X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FCLIHandler.pm;h=e6fd415b3ef4fd237c03db93eb038e835e7d1bed;hp=6ccfcd2c041e6a4039bbfb372d8fa012059d4597;hb=2a8ced7bacb7da8ab4143a59720e74a2897cf714;hpb=1f130ba6410638eec2ba5bf7b7c0ac3ccacd5615 diff --git a/src/PVE/CLIHandler.pm b/src/PVE/CLIHandler.pm index 6ccfcd2..e6fd415 100644 --- a/src/PVE/CLIHandler.pm +++ b/src/PVE/CLIHandler.pm @@ -4,6 +4,7 @@ use strict; use warnings; use Data::Dumper; +use PVE::SafeSyslog; use PVE::Exception qw(raise raise_param_exc); use PVE::RESTHandler; use PVE::PodParser; @@ -33,6 +34,19 @@ my $expand_command_name = sub { return $cmd; }; +my $complete_command_names = sub { + my $res = []; + + return if ref($cmddef) ne 'HASH'; + + foreach my $cmd (keys %$cmddef) { + next if $cmd eq 'help'; + push @$res, $cmd; + } + + return $res; +}; + __PACKAGE__->register_method ({ name => 'help', path => 'help', @@ -45,6 +59,7 @@ __PACKAGE__->register_method ({ description => "Command name", type => 'string', optional => 1, + completion => $complete_command_names, }, verbose => { description => "Verbose output format.", @@ -92,6 +107,20 @@ __PACKAGE__->register_method ({ }}); +sub print_simple_pod_manpage { + my ($podfn, $class, $name, $arg_param, $uri_param) = @_; + + my $synopsis = " $name help\n\n"; + my $str = $class->usage_str($name, $name, $arg_param, $uri_param, 'long'); + $str =~ s/^USAGE://; + $str =~ s/\n/\n /g; + $synopsis .= $str; + + my $parser = PVE::PodParser->new(); + $parser->{include}->{synopsis} = $synopsis; + $parser->parse_from_file($podfn); +} + sub print_pod_manpage { my ($podfn) = @_; @@ -268,30 +297,105 @@ sub verify_api { PVE::RESTHandler::validate_method_schemas(); } -sub generate_pod_manpage { - my ($class, $podfn) = @_; +sub generate_bash_completions { + my ($class) = @_; - no strict 'refs'; - $cmddef = ${"${class}::cmddef"}; + # generate bash completion config $exename = $class; $exename =~ s/^.*:://; - if (!defined($podfn)) { - my $cpath = "$class.pm"; - $cpath =~ s/::/\//g; - foreach my $p (@INC) { - my $testfn = "$p/$cpath"; + print <<__EOD__; +# $exename bash completion + +# see http://tiswww.case.edu/php/chet/bash/FAQ +# and __ltrim_colon_completions() in /usr/share/bash-completion/bash_completion +# this modifies global var, but I found no better way +COMP_WORDBREAKS=\${COMP_WORDBREAKS//:} + +complete -C '$exename bashcomplete' $exename +__EOD__ +} + +sub find_cli_class_source { + my ($exename) = @_; + + my $filename; + + my $cpath = "PVE/CLI/${exename}.pm"; + my $spath = "PVE/Service/${exename}.pm"; + foreach my $p (@INC) { + foreach my $s (($cpath, $spath)) { + my $testfn = "$p/$s"; if (-f $testfn) { - $podfn = $testfn; + $filename = $testfn; last; } } + last if defined($filename); } + return $filename; +} + +sub generate_pod_manpage { + my ($class, $podfn) = @_; + + $exename = $class; + $exename =~ s/^.*:://; + + $podfn = find_cli_class_source($exename) if !defined($podfn); + die "unable to find source for class '$class'" if !$podfn; - print_pod_manpage($podfn); + no strict 'refs'; + my $def = ${"${class}::cmddef"}; + + if (ref($def) eq 'ARRAY') { + print_simple_pod_manpage($podfn, @$def); + } else { + $cmddef = $def; + + $cmddef->{help} = [ __PACKAGE__, 'help', ['cmd'] ]; + + print_pod_manpage($podfn); + } +} + +sub run_cli { + my ($class, $pwcallback, $podfn, $preparefunc) = @_; + + $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; + + $exename = $class; + $exename =~ s/^.*:://; + + initlog($exename); + + + if ($class !~ m/^PVE::Service::/) { + die "please run as root\n" if $> != 0; + + PVE::INotify::inotify_init(); + + my $rpcenv = PVE::RPCEnvironment->init('cli'); + $rpcenv->init_request(); + $rpcenv->set_language($ENV{LANG}); + $rpcenv->set_user('root@pam'); + } + + no strict 'refs'; + my $def = ${"${class}::cmddef"}; + + if (ref($def) eq 'ARRAY') { + handle_simple_cmd($def, \@ARGV, $pwcallback, $podfn, $preparefunc); + } else { + $cmddef = $def; + my $cmd = shift @ARGV; + handle_cmd($cmddef, $exename, $cmd, \@ARGV, $pwcallback, $podfn, $preparefunc); + } + + exit 0; } sub handle_cmd { @@ -309,6 +413,7 @@ sub handle_cmd { PVE::RESTHandler::validate_method_schemas(); return; } elsif ($cmd eq 'printmanpod') { + $podfn = find_cli_class_source($exename) if !defined($podfn); print_pod_manpage($podfn); return; } elsif ($cmd eq 'bashcomplete') { @@ -334,7 +439,7 @@ sub handle_cmd { } sub handle_simple_cmd { - my ($def, $args, $pwcallback, $podfn) = @_; + my ($def, $args, $pwcallback, $podfn, $preparefunc) = @_; my ($class, $name, $arg_param, $uri_param, $outsub) = @{$def}; die "no class specified" if !$class; @@ -353,19 +458,14 @@ sub handle_simple_cmd { PVE::RESTHandler::validate_method_schemas(); return; } elsif ($args->[0] eq 'printmanpod') { - my $synopsis = " $name help\n\n"; - my $str = $class->usage_str($name, $name, $arg_param, $uri_param, 'long'); - $str =~ s/^USAGE://; - $str =~ s/\n/\n /g; - $synopsis .= $str; - - my $parser = PVE::PodParser->new(); - $parser->{include}->{synopsis} = $synopsis; - $parser->parse_from_file($podfn); + $podfn = find_cli_class_source($name) if !defined($podfn); + print_simple_pod_manpage($podfn, @$def); return; } } + &$preparefunc() if $preparefunc; + my $res = $class->cli_handler($name, $name, \@ARGV, $arg_param, $uri_param, $pwcallback); &$outsub($res) if $outsub;