]>
Commit | Line | Data |
---|---|---|
e143e9d8 DM |
1 | package PVE::CLIHandler; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::Exception qw(raise raise_param_exc); | |
7 | use PVE::RESTHandler; | |
8 | use PVE::PodParser; | |
9 | ||
10 | use base qw(PVE::RESTHandler); | |
11 | ||
12 | my $cmddef; | |
13 | my $exename; | |
14 | ||
15 | my $expand_command_name = sub { | |
16 | my ($def, $cmd) = @_; | |
17 | ||
18 | if (!$def->{$cmd}) { | |
19 | my $expanded; | |
20 | for my $k (keys(%$def)) { | |
21 | if ($k =~ m/^$cmd/) { | |
22 | if ($expanded) { | |
23 | $expanded = undef; # more than one match | |
24 | last; | |
25 | } else { | |
26 | $expanded = $k; | |
27 | } | |
28 | } | |
29 | } | |
30 | $cmd = $expanded if $expanded; | |
31 | } | |
32 | return $cmd; | |
33 | }; | |
34 | ||
35 | __PACKAGE__->register_method ({ | |
36 | name => 'help', | |
37 | path => 'help', | |
38 | method => 'GET', | |
39 | description => "Get help about specified command.", | |
40 | parameters => { | |
41 | additionalProperties => 0, | |
42 | properties => { | |
43 | cmd => { | |
44 | description => "Command name", | |
45 | type => 'string', | |
46 | optional => 1, | |
47 | }, | |
48 | verbose => { | |
49 | description => "Verbose output format.", | |
50 | type => 'boolean', | |
51 | optional => 1, | |
52 | }, | |
53 | }, | |
54 | }, | |
55 | returns => { type => 'null' }, | |
56 | ||
57 | code => sub { | |
58 | my ($param) = @_; | |
59 | ||
60 | die "not initialized" if !($cmddef && $exename); | |
61 | ||
62 | my $cmd = $param->{cmd}; | |
63 | ||
64 | my $verbose = defined($cmd) && $cmd; | |
65 | $verbose = $param->{verbose} if defined($param->{verbose}); | |
66 | ||
67 | if (!$cmd) { | |
68 | if ($verbose) { | |
69 | print_usage_verbose(); | |
70 | } else { | |
71 | print_usage_short(\*STDOUT); | |
72 | } | |
73 | return undef; | |
74 | } | |
75 | ||
76 | $cmd = &$expand_command_name($cmddef, $cmd); | |
77 | ||
78 | my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd} || []}; | |
79 | ||
80 | raise_param_exc({ cmd => "no such command '$cmd'"}) if !$class; | |
81 | ||
82 | ||
83 | my $str = $class->usage_str($name, "$exename $cmd", $arg_param, $uri_param, $verbose ? 'full' : 'short'); | |
84 | if ($verbose) { | |
85 | print "$str\n"; | |
86 | } else { | |
87 | print "USAGE: $str\n"; | |
88 | } | |
89 | ||
90 | return undef; | |
91 | ||
92 | }}); | |
93 | ||
94 | sub print_pod_manpage { | |
95 | my ($podfn) = @_; | |
96 | ||
97 | die "not initialized" if !($cmddef && $exename); | |
98 | die "no pod file specified" if !$podfn; | |
99 | ||
100 | my $synopsis = ""; | |
101 | ||
102 | $synopsis .= " $exename <COMMAND> [ARGS] [OPTIONS]\n\n"; | |
103 | ||
104 | my $style = 'full'; # or should we use 'short'? | |
105 | my $oldclass; | |
106 | foreach my $cmd (sorted_commands()) { | |
107 | my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}}; | |
108 | my $str = $class->usage_str($name, "$exename $cmd", $arg_param, | |
109 | $uri_param, $style); | |
110 | $str =~ s/^USAGE: //; | |
111 | ||
112 | $synopsis .= "\n" if $oldclass && $oldclass ne $class; | |
113 | $str =~ s/\n/\n /g; | |
114 | $synopsis .= " $str\n\n"; | |
115 | $oldclass = $class; | |
116 | } | |
117 | ||
118 | $synopsis .= "\n"; | |
119 | ||
120 | my $parser = PVE::PodParser->new(); | |
121 | $parser->{include}->{synopsis} = $synopsis; | |
122 | $parser->parse_from_file($podfn); | |
123 | } | |
124 | ||
125 | sub print_usage_verbose { | |
126 | ||
127 | die "not initialized" if !($cmddef && $exename); | |
128 | ||
129 | print "USAGE: $exename <COMMAND> [ARGS] [OPTIONS]\n\n"; | |
130 | ||
131 | foreach my $cmd (sort keys %$cmddef) { | |
132 | my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}}; | |
133 | my $str = $class->usage_str($name, "$exename $cmd", $arg_param, $uri_param, 'full'); | |
134 | print "$str\n\n"; | |
135 | } | |
136 | } | |
137 | ||
138 | sub sorted_commands { | |
139 | return sort { ($cmddef->{$a}->[0] cmp $cmddef->{$b}->[0]) || ($a cmp $b)} keys %$cmddef; | |
140 | } | |
141 | ||
142 | sub print_usage_short { | |
143 | my ($fd, $msg) = @_; | |
144 | ||
145 | die "not initialized" if !($cmddef && $exename); | |
146 | ||
147 | print $fd "ERROR: $msg\n" if $msg; | |
148 | print $fd "USAGE: $exename <COMMAND> [ARGS] [OPTIONS]\n"; | |
149 | ||
150 | my $oldclass; | |
151 | foreach my $cmd (sorted_commands()) { | |
152 | my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}}; | |
153 | my $str = $class->usage_str($name, "$exename $cmd", $arg_param, $uri_param, 'short'); | |
154 | print $fd "\n" if $oldclass && $oldclass ne $class; | |
155 | print $fd " $str"; | |
156 | $oldclass = $class; | |
157 | } | |
158 | } | |
159 | ||
160 | sub handle_cmd { | |
161 | my ($def, $cmdname, $cmd, $args, $pwcallback, $podfn) = @_; | |
162 | ||
163 | $cmddef = $def; | |
164 | $exename = $cmdname; | |
165 | ||
166 | $cmddef->{help} = [ __PACKAGE__, 'help', ['cmd'] ]; | |
167 | ||
168 | if (!$cmd) { | |
169 | print_usage_short (\*STDERR, "no command specified"); | |
170 | exit (-1); | |
171 | } elsif ($cmd eq 'verifyapi') { | |
172 | PVE::RESTHandler::validate_method_schemas(); | |
173 | return; | |
174 | } elsif ($cmd eq 'printmanpod') { | |
175 | print_pod_manpage($podfn); | |
176 | return; | |
177 | } | |
178 | ||
179 | $cmd = &$expand_command_name($cmddef, $cmd); | |
180 | ||
181 | my ($class, $name, $arg_param, $uri_param, $outsub) = @{$cmddef->{$cmd} || []}; | |
182 | ||
183 | if (!$class) { | |
184 | print_usage_short (\*STDERR, "unknown command '$cmd'"); | |
185 | exit (-1); | |
186 | } | |
187 | ||
188 | my $prefix = "$exename $cmd"; | |
189 | my $res = $class->cli_handler($prefix, $name, \@ARGV, $arg_param, $uri_param, $pwcallback); | |
2026f4b5 DM |
190 | |
191 | &$outsub($res) if $outsub; | |
192 | } | |
193 | ||
194 | sub handle_simple_cmd { | |
195 | my ($def, $args, $pwcallback, $podfn) = @_; | |
196 | ||
197 | my ($class, $name, $arg_param, $uri_param, $outsub) = @{$def}; | |
198 | die "no class specified" if !$class; | |
199 | ||
200 | if (scalar(@$args) == 1) { | |
201 | if ($args->[0] eq 'help') { | |
202 | my $str = "USAGE: $name help\n"; | |
203 | $str .= $class->usage_str($name, $name, $arg_param, $uri_param, 'long'); | |
204 | print STDERR "$str\n\n"; | |
205 | return; | |
206 | } elsif ($args->[0] eq 'verifyapi') { | |
207 | PVE::RESTHandler::validate_method_schemas(); | |
208 | return; | |
209 | } elsif ($args->[0] eq 'printmanpod') { | |
210 | my $synopsis = " $name help\n\n"; | |
211 | my $str = $class->usage_str($name, $name, $arg_param, $uri_param, 'long'); | |
212 | $str =~ s/^USAGE://; | |
213 | $str =~ s/\n/\n /g; | |
214 | $synopsis .= $str; | |
215 | ||
216 | my $parser = PVE::PodParser->new(); | |
217 | $parser->{include}->{synopsis} = $synopsis; | |
218 | $parser->parse_from_file($podfn); | |
219 | return; | |
220 | } | |
e143e9d8 | 221 | } |
2026f4b5 DM |
222 | |
223 | my $res = $class->cli_handler($name, $name, \@ARGV, $arg_param, $uri_param, $pwcallback); | |
224 | ||
225 | &$outsub($res) if $outsub; | |
e143e9d8 DM |
226 | } |
227 | ||
228 | 1; |