File Coverage

blib/lib/Metabrik/Shell/Command.pm
Criterion Covered Total %
statement 9 183 4.9
branch 0 82 0.0
condition 0 34 0.0
subroutine 3 16 18.7
pod 1 11 9.0
total 13 326 3.9


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # shell::command Brik
5             #
6             package Metabrik::Shell::Command;
7 24     24   746 use strict;
  24         59  
  24         687  
8 24     24   122 use warnings;
  24         45  
  24         612  
9              
10 24     24   125 use base qw(Metabrik);
  24         44  
  24         29857  
11              
12             sub brik_properties {
13             return {
14 0     0 1   revision => '$Revision$',
15             tags => [ qw(exec execute) ],
16             attributes => {
17             as_array => [ qw(0|1) ],
18             as_matrix => [ qw(0|1) ],
19             capture_stderr => [ qw(0|1) ],
20             capture_mode => [ qw(0|1) ],
21             capture_system => [ qw(0|1) ],
22             ignore_error => [ qw(0|1) ],
23             use_sudo => [ qw(0|1) ],
24             use_pager => [ qw(0|1) ],
25             use_globbing => [ qw(0|1) ],
26             sudo_args => [ qw(args) ],
27             },
28             attributes_default => {
29             as_array => 1,
30             as_matrix => 0,
31             capture_stderr => 1,
32             capture_mode => 0,
33             capture_system => 0,
34             ignore_error => 1,
35             use_sudo => 0,
36             use_pager => 0,
37             use_globbing => 0,
38             #sudo_args => '-E', # Keep environment
39             sudo_args => '', # Do not keep env by default.
40             # Not needed anymore cause Metabrik is installed
41             # system-wide.
42             },
43             commands => {
44             system => [ qw(command args|OPTIONAL) ],
45             sudo_system => [ qw(command args|OPTIONAL) ],
46             system_in_background => [ qw(command args|OPTIONAL) ],
47             sudo_system_in_background => [ qw(command args|OPTIONAL) ],
48             capture => [ qw(command args|OPTIONAL) ],
49             sudo_capture => [ qw(command args|OPTIONAL) ],
50             system_capture => [ qw(command args|OPTIONAL) ],
51             sudo_system_capture => [ qw(command args|OPTIONAL) ],
52             execute => [ qw(command args|OPTIONAL) ],
53             sudo_execute => [ qw(command args|OPTIONAL) ],
54             },
55             require_binaries => {
56             script => [ ],
57             },
58             require_modules => {
59             'IPC::Run3' => [ ],
60             'Metabrik::System::Os' => [ ],
61             'Metabrik::System::Process' => [ ],
62             },
63             need_packages => {
64             ubuntu => [ qw(bsdutils) ],
65             debian => [ qw(bsdutils) ],
66             kali => [ qw(bsdutils) ],
67             },
68             };
69             }
70              
71             sub system {
72 0     0 0   my $self = shift;
73 0           my ($cmd, @args) = @_;
74              
75 0 0         $self->brik_help_run_undef_arg('system', $cmd) or return;
76              
77             # Remove undefined values from arguments
78 0           my @new;
79 0           for (@args) {
80 0 0         if (defined($_)) {
81 0           push @new, $_;
82             }
83             }
84              
85 0           my $command = join(' ', $cmd, @new);
86 0           my @toks = split(/\s+/, $command);
87 0           my $bin = $toks[0];
88              
89 0           my @path = split(':', $ENV{PATH});
90 0 0         if (! -f $bin) { # If file is not directly found
91 0           for my $path (@path) {
92 0 0         if (-f "$path/$bin") {
93 0           $bin = "$path/$bin";
94 0           last;
95             }
96             }
97             }
98 0           $toks[0] = $bin;
99              
100 0 0         if (! -f $bin) {
101 0           return $self->log->error("system: program [$bin] not found in PATH");
102             }
103              
104 0           $command = join(' ', @toks);
105              
106             # Use sudo only when not root
107 0 0 0       if ($self->use_sudo && $< != 0) {
108 0           my @sudo = ( "sudo" );
109 0 0 0       if (! ref($self->sudo_args) && length($self->sudo_args)) {
110 0           my @args = split(/\s+/, $self->sudo_args);
111 0           push @sudo, @args;
112             }
113 0           $command = join(' ', @sudo)." $command";
114 0           @toks = ( @sudo, @toks );
115             }
116              
117 0 0         if ($self->use_pager) {
118 0   0       my $pager = $ENV{PAGER} || 'less';
119 0           $command .= " | $pager";
120             }
121              
122             # Also capture output to terminal to a file
123 0 0         my $so = Metabrik::System::Os->new_from_brik_init($self) or return;
124 0           my $output_file = 'capture_system.script';
125 0 0         if ($self->capture_system) {
126 0 0         if ($so->is_freebsd) {
127 0           $command = "script -q $output_file $command";
128             }
129             else {
130 0           $command = "script -q --command '".$command."' $output_file";
131             }
132             }
133              
134 0           $self->log->verbose("system: command[$command]");
135              
136 0           my $r = CORE::system($command);
137              
138 0           $self->log->debug("system: command returned code [$r] with status [$?]");
139              
140 0 0 0       if (! $self->ignore_error && $? != 0) {
141 0           $self->log->verbose("system: exit code[$?]");
142             # Failure, we return the program exit code
143 0           $self->log->debug("system: program exit code [$?]");
144 0           return $?;
145             }
146              
147 0           $self->log->debug("system: program exit with success");
148              
149             # Program succeeded, we only return full path to output file,
150             # maybe the caller will have some optimization to not process ths full
151             # file content afterwards.
152 0 0         if ($self->capture_system) {
153 0   0       my $pwd = defined($self->shell) && $self->shell->full_pwd || '/tmp';
154             my $homedir = defined($self->global) && $self->global->homedir
155 0   0       || defined($ENV{HOME}) && $ENV{HOME} || '/tmp';
156 0           $pwd =~ s{^~}{$homedir};
157 0           return $pwd.'/'.$output_file;
158             }
159              
160 0           return 1;
161             }
162              
163             sub sudo_system {
164 0     0 0   my $self = shift;
165 0           my ($cmd, @args) = @_;
166              
167 0 0         $self->brik_help_run_undef_arg('sudo_system', $cmd) or return;
168              
169 0           my $prev = $self->use_sudo;
170 0           $self->use_sudo(1);
171 0           my $r = $self->system($cmd, @args);
172 0           $self->use_sudo($prev);
173              
174 0           return $r;
175             }
176              
177             sub system_in_background {
178 0     0 0   my $self = shift;
179 0           my ($cmd, @args) = @_;
180              
181 0 0         $self->brik_help_run_undef_arg('system_in_background', $cmd) or return;
182              
183 0 0         my $sp = Metabrik::System::Process->new_from_brik_init($self) or return;
184 0           $sp->close_output_on_start(1);
185              
186             $sp->start(sub {
187 0     0     $self->system($cmd, @args);
188 0           });
189              
190 0           return 1;
191             }
192              
193             sub sudo_system_in_background {
194 0     0 0   my $self = shift;
195 0           my ($cmd, @args) = @_;
196              
197 0 0         $self->brik_help_run_undef_arg('sudo_system_in_background', $cmd) or return;
198              
199 0 0         my $sp = Metabrik::System::Process->new_from_brik_init($self) or return;
200 0           $sp->close_output_on_start(1);
201              
202             $sp->start(sub {
203 0     0     $self->sudo_system($cmd, @args);
204 0           });
205              
206 0           return 1;
207             }
208              
209             sub capture {
210 0     0 0   my $self = shift;
211 0           my ($cmd, @args) = @_;
212              
213 0 0         $self->brik_help_run_undef_arg('capture', $cmd) or return;
214              
215             # Remove undefined values from arguments
216 0           my @new;
217 0           for (@args) {
218 0 0         if (defined($_)) {
219 0           push @new, $_;
220             }
221             }
222              
223 0           my $command = join(' ', $cmd, @new);
224 0           my @toks = split(/\s+/, $command);
225 0           my $bin = $toks[0];
226              
227 0           my @path = split(':', $ENV{PATH});
228 0 0         if (! -f $bin) { # If file is not directly found
229 0           for my $path (@path) {
230 0 0         if (-f "$path/$bin") {
231 0           $bin = "$path/$bin";
232 0           last;
233             }
234             }
235             }
236 0           $toks[0] = $bin;
237              
238 0 0         if (! -f $bin) {
239 0           return $self->log->error("capture: program [$bin] not found in PATH");
240             }
241              
242             # Perform file globbing, if any
243 0 0         if ($self->use_globbing) {
244 0           my @globbed = ();
245 0           for (@toks) {
246 0           push @globbed, glob($_);
247             }
248 0           @toks = @globbed;
249             }
250              
251 0           $command = join(' ', @toks);
252              
253             # Use sudo only when not root
254 0 0 0       if ($self->use_sudo && $< != 0) {
255 0           my @sudo = ( "sudo" );
256 0 0 0       if (! ref($self->sudo_args) && length($self->sudo_args)) {
257 0           my @args = split(/\s+/, $self->sudo_args);
258 0           push @sudo, @args;
259             }
260 0           $command = join(' ', @sudo)." $command";
261 0           @toks = ( @sudo, @toks );
262             }
263              
264 0           my $out;
265             my $err;
266 0           eval {
267 0           my $cmd = join(' ', @toks);
268 0           $self->log->verbose("capture: command[$cmd]");
269 0           IPC::Run3::run3($cmd, undef, \$out, \$err);
270             };
271             # Error in executing run3()
272 0 0         if ($@) {
    0          
273 0           chomp($@);
274 0           return $self->log->error("capture: unable to execute command [$command]: $@");
275             }
276             # Error in command execution
277             elsif ($?) {
278 0           chomp($err);
279 0           chomp($out);
280 0   0       $err ||= $out; # Sometimes, the error is printed on stdout instead of stderr
281 0 0         if ($self->ignore_error) {
282 0           $self->log->warning("capture: command execution had errors [$command]: $err");
283             }
284             else {
285 0           return $self->log->error("capture: command execution failed [$command]: $err");
286             }
287             }
288              
289 0   0       $out ||= 'undef';
290 0   0       $err ||= 'undef';
291 0           chomp($out);
292 0           chomp($err);
293              
294             # If we also wanted stderr, we put it at the end of output
295 0 0 0       if ($self->capture_stderr && $err ne 'undef') {
296 0           $out .= "\n\nSTDERR:\n$err";
297             }
298              
299             # as_matrix has precedence over as_array (because as_array is the default)
300 0 0 0       if (! $self->as_matrix && $self->as_array) {
    0          
301 0           $out = [ split(/\n/, $out) ];
302             }
303             elsif ($self->as_matrix) {
304 0           my @matrix = ();
305 0           for my $this (split(/\n/, $out)) {
306 0           push @matrix, [ split(/\s+/, $this) ];
307             }
308 0           $out = \@matrix;
309             }
310              
311 0           return $out;
312             }
313              
314             sub sudo_capture {
315 0     0 0   my $self = shift;
316 0           my ($cmd, @args) = @_;
317              
318 0 0         $self->brik_help_run_undef_arg('sudo_capture', $cmd) or return;
319              
320 0           my $prev = $self->use_sudo;
321 0           $self->use_sudo(1);
322 0           my $r = $self->capture($cmd, @args);
323 0           $self->use_sudo($prev);
324              
325 0           return $r;
326             }
327              
328             sub system_capture {
329 0     0 0   my $self = shift;
330 0           my ($cmd, @args) = @_;
331              
332 0 0         $self->brik_help_run_undef_arg('system_capture', $cmd) or return;
333              
334 0           my $prev = $self->capture_system;
335 0           $self->capture_system(1);
336 0           my $r = $self->system($cmd, @args);
337 0           $self->capture_system($prev);
338              
339 0           return $r;
340             }
341              
342             sub sudo_system_capture {
343 0     0 0   my $self = shift;
344 0           my ($cmd, @args) = @_;
345              
346 0 0         $self->brik_help_run_undef_arg('sudo_system_capture', $cmd) or return;
347              
348 0           my $prev = $self->capture_system;
349 0           $self->capture_system(1);
350 0           my $r = $self->sudo_system($cmd, @args);
351 0           $self->capture_system($prev);
352              
353 0           return $r;
354             }
355              
356             sub execute {
357 0     0 0   my $self = shift;
358 0           my ($cmd, @args) = @_;
359            
360 0 0         $self->brik_help_run_undef_arg('execute', $cmd) or return;
361              
362 0 0         if ($self->capture_system) {
    0          
363 0           return $self->system_capture($cmd, @args);
364             }
365             elsif ($self->capture_mode) {
366 0           return $self->capture($cmd, @args);
367             }
368             else { # non-capture mode
369 0           return $self->system($cmd, @args);
370             }
371              
372             # Unknown error
373 0           return;
374             }
375              
376             sub sudo_execute {
377 0     0 0   my $self = shift;
378 0           my ($cmd, @args) = @_;
379              
380 0 0         $self->brik_help_run_undef_arg('sudo_execute', $cmd) or return;
381              
382 0 0         if ($self->capture_system) {
    0          
383 0           return $self->sudo_system_capture($cmd, @args);
384             }
385             elsif ($self->capture_mode) {
386 0           return $self->sudo_capture($cmd, @args);
387             }
388             else { # non-capture mode
389 0           return $self->sudo_system($cmd, @args);
390             }
391              
392             # Unknown error
393 0           return;
394             }
395              
396             1;
397              
398             __END__