Current Path : /usr/bin/ |
Current File : //usr/bin/webmin |
#!/usr/bin/env perl # Webmin CLI - Allows performing a variety of common Webmin-related # functions on the command line. use strict; use warnings; BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Color'; } use 5.010; # Version in CentOS 6 use Getopt::Long qw(:config permute pass_through); use Pod::Usage; use Term::ANSIColor qw(:constants); use File::Spec; use File::Basename; sub main { my ( %opt, $subcmd ); GetOptions( 'help|h' => \$opt{'help'}, 'config|c=s' => \$opt{'config'}, 'list-commands|l' => \$opt{'list'}, 'describe|d' => \$opt{'describe'}, 'man|m' => \$opt{'man'}, '<>' => sub { # Handle unrecognized options, inc. subcommands. my($arg) = @_; if ($arg =~ m{^-}) { say "Usage error: Unknown option $arg."; pod2usage(0); } else { # It must be a subcommand. $subcmd = $arg; die "!FINISH"; } } ); $opt{'config'} ||= "/etc/webmin"; my @remain = @ARGV; # List commands? if ($opt{'list'}) { list_commands(\%opt); exit 0; } elsif ($opt{'man'} || $opt{'help'} || !defined($remain[0])) { # Show the full manual page man_command(\%opt, $subcmd); exit 0; } elsif ($subcmd) { run_command( \%opt, $subcmd, \@remain ); } exit 0; } exit main( \@ARGV ) if !caller(0); # run_command - Run a subcommand # $optref is a reference to an options object passed down from global options # like --help or a --config path. sub run_command { my ( $optref, $subcmd, $remainref ) = @_; # Figure out the Webmin root directory my $root = root($optref->{'config'}); my (@commands) = list_commands($optref); if (! grep( /^$subcmd$/, @commands ) ) { say RED, "Error: ", RESET, "Command \`$subcmd\` doesn't exist", RESET; exit 1; } my $command_path = get_command_path($root, $subcmd); # Merge the options # Only handling config, right now... # XXX Should we do this with libraries instead of commands? # Maybe detect .pm for that possibility. my @allopts = ("--config", "$optref->{'config'}", @$remainref); # Run that binch system($command_path, @allopts); # Try to exit with the passed through exit code (rarely used, but # why not?) if ($? == -1) { say RED, "Error: ", RESET, "Failed to execute \`$command_path\`: $!"; exit 1; } else { exit $? >> 8; } } sub get_command_path { my ($root, $subcmd) = @_; # Check for a root-level command (in "$root/bin") my $command_path; if ($subcmd) { $command_path = File::Spec->catfile($root, 'bin', $subcmd); } else { $command_path = File::Spec->catfile($root, 'bin', 'webmin'); } my $module_name; my $command; if ( -x $command_path) { $command = $command_path; } else { # Try to extract a module name from the command # Get list of directories opendir (my $DIR, $root); my @module_dirs = grep { -d "$root/$_" } readdir($DIR); # See if any of them are a substring of $subcmd for my $dir (@module_dirs) { if (index($subcmd, $dir) == 0) { $module_name = $dir; my $barecmd = substr($subcmd, -(length($subcmd)-length($module_name)-1)); $command = File::Spec->catfile($root, $dir, 'bin', $barecmd); # Could be .pl or no extension if ( -x $command ) { last; } elsif ( -x $command . ".pl" ) { $command = $command . ".pl"; last; } } } } if ($command) { return $command; } else { die RED, "Unrecognized subcommand: $subcmd", RESET; } } sub list_commands { my ($optref) = @_; my $root = root($optref->{'config'}); my @commands; # Find and list global commands for my $command (glob ("$root/bin/*")) { my ($bin, $path) = fileparse($command); if ($bin =~ "webmin") { next; } if ($optref->{'describe'}) { # Display name and description say YELLOW, "$bin", RESET; pod2usage( -verbose => 99, -sections => [ qw(DESCRIPTION) ], -input => $command, -exitval => "NOEXIT"); } else { if (wantarray) { push(@commands, $bin); } else { # Just list the names say "$bin"; } } } my @modules; # Find all module directories with something in bin for my $command (glob ("$root/*/bin/*")) { my ($bin, $path) = fileparse($command); my $module = (split /\//, $path)[-2]; if ($optref->{'describe'}) { # Display name and description say YELLOW, "$module-$bin", RESET; pod2usage( -verbose => 99, -sections => [ qw(DESCRIPTION) ], -input => $command, -exitval => "NOEXIT"); } else { if (wantarray) { push(@modules, "$module-$bin"); } else { # Just list the names say "$module-$bin"; } } } if (wantarray) { return (@commands, @modules); } } # Display either a short usage message (--help) or a full manual (--man) sub man_command { my ($optref, $subcmd) = @_; my $root = root($optref->{'config'}); my $command_path = get_command_path($root, $subcmd); $ENV{'PAGER'} ||= "more"; open(my $PAGER, "|-", "$ENV{'PAGER'}"); if ($optref->{'help'}) { pod2usage( -input => $command_path ); } else { pod2usage( -verbose => 99, -input => $command_path, -output => $PAGER); } } sub root { my ($config) = @_; open(my $CONF, "<", "$config/miniserv.conf") || die RED, "Failed to open $config/miniserv.conf", RESET; my $root; while (<$CONF>) { if (/^root=(.*)/) { $root = $1; } } close($CONF); # Does the Webmin root exist? if ( $root ) { die "$root is not a directory. Is --config correct?" unless (-d $root); } else { die "Unable to determine Webmin installation directory from $ENV{'WEBMIN_CONFIG'}"; } return $root; } 1; =pod =head1 NAME webmin =head1 DESCRIPTION Webmin CLI command to perform many common Webmin tasks from the command line or from scripts. =head1 SYNOPSIS webmin [options] subcommand [subcommand options] =head1 OPTIONS =over =item --help, -h Print this usage summary and exit. Subcommands may also have a usage summary. =item --config <path>, -c <path> Specify the full path to the Webmin configuration directory. Defaults to C</etc/webmin>. =item --list-commands, -l List available subcommands. =item --describe, -d When listing commands, briefly describe what they do. =item --man <command>, -m <command> Display the manual page for the given subcommand. =back =head1 EXIT CODES 0 on success non-0 on error =head1 LICENSE AND COPYRIGHT Copyright 2020 Jamie Cameron <jcameron@webmin.com>, Joe Cooper <joe@virtualmin.com>.