File Coverage

lib/Rex/Commands/Run.pm
Criterion Covered Total %
statement 93 163 57.0
branch 37 74 50.0
condition 8 24 33.3
subroutine 18 21 85.7
pod 3 3 100.0
total 159 285 55.7


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             =head1 NAME
6              
7             Rex::Commands::Run - Execute a remote command
8              
9             =head1 DESCRIPTION
10              
11             With this module you can run a command.
12              
13             =head1 SYNOPSIS
14              
15             my $output = run 'ls -l';
16             sudo 'id';
17              
18             =head1 CONFIGURATION AND ENVIRONMENT
19              
20             Please note that Rex may set the C environment variable when executing commands on the user's behalf to a different value compared to executing the same commands manually. The following are available to control the related behavior:
21              
22             =over 4
23              
24             =item L command
25              
26             =item L configuration option
27              
28             =item L feature flag
29              
30             =item L feature flag
31              
32             =item L feature flag
33              
34             =back
35              
36             =head1 EXPORTED FUNCTIONS
37              
38             =cut
39              
40             package Rex::Commands::Run;
41              
42 67     67   298507 use v5.12.5;
  67         285  
43 67     67   379 use warnings;
  67         120  
  67         3729  
44              
45             our $VERSION = '1.14.2.2'; # TRIAL VERSION
46              
47             #require Exporter;
48             require Rex::Exporter;
49 67     67   3361 use Net::OpenSSH::ShellQuoter;
  67         41997  
  67         929  
50 67     67   4294 use Data::Dumper;
  67         28404  
  67         3225  
51 67     67   3701 use Rex;
  67         152  
  67         428  
52 67     67   444 use Rex::Logger;
  67         162  
  67         444  
53 67     67   1844 use Rex::Helper::SSH2;
  67         170  
  67         4256  
54 67     67   671 use Rex::Helper::Run;
  67         160  
  67         4186  
55 67     67   790 use Rex::Helper::SSH2::Expect;
  67         180  
  67         897  
56 67     67   1964 use Rex::Config;
  67         187  
  67         437  
57 67     67   459 use Rex::Interface::Exec;
  67         164  
  67         476  
58 67     67   1377 use Rex::Interface::Fs;
  67         144  
  67         324  
59              
60             BEGIN {
61 67 50   67   4621 if ( $^O !~ m/^MSWin/ ) {
62 67     67   5109 eval "use Expect";
  67         773  
  0         0  
  0         0  
63             }
64             else {
65             # this fails sometimes on windows...
66 0         0 eval { Rex::Logger::debug("Running under windows, Expect not supported."); };
  0         0  
67             }
68             }
69              
70 67     67   493 use vars qw(@EXPORT);
  67         161  
  67         2888  
71 67     67   412 use base qw(Rex::Exporter);
  67         159  
  67         99703  
72              
73             @EXPORT = qw(run can_run sudo);
74              
75             =head2 run($command [, $callback], %options)
76              
77             This function will execute the given C<$command> and returns the output. In
78             scalar context it returns the raw output as is, and in list context it
79             returns the list of output lines. The exit value of the command is stored
80             in the C<$?> variable.
81              
82             run 'uptime';
83             my $output = run 'uptime';
84             my @output_lines = run 'uptime';
85              
86             Please note when the C> feature flag is enabled the
87             combined output containing both C and C is returned
88             via C. When using the C> feature flag, or
89             the C> feature bundle (or newer), then C returns
90             only the C output of the command.
91              
92             To access separate C and C output, use a callback
93             subroutine, for example:
94              
95             run 'uptime', sub {
96             my ( $stdout, $stderr ) = @_;
97             my $server = Rex::get_current_connection()->{server};
98             say "[$server] $stdout\n";
99             };
100              
101             It also takes further options in a form of a hash. Supported options are:
102              
103             =over 4
104              
105             =item cwd => $path
106              
107             Sets the working directory of the executed command to C<$path>.
108              
109             =item only_if => $condition_command
110              
111             Executes the command only if C<$condition_command> returns success.
112              
113             =item unless => $condition_command
114              
115             Executes the command if C<$condition_command> returns failure.
116              
117             =item only_notified => TRUE
118              
119             Queues the command to be executed later upon notification.
120              
121             =item env => { var1 => $value1, ..., varN => $valueN }
122              
123             Sets environment variables for the given command.
124              
125             =item timeout => value
126              
127             Sets the timeout for the command to be run.
128              
129             =item auto_die => TRUE
130              
131             Die if the command returns with an exit code indicating failure. It can be set globally via the L feature flag.
132              
133             =item command => $command_to_run
134              
135             If present, Rex will execute C<$command_to_run>, and treat the first arugment as an identifier for the given C block (e.g. to be triggered with notify).
136              
137             =item creates => $file_to_create
138              
139             Tries to create C<$file_to_create> upon execution, and skips execution if the file already exists.
140              
141             =item continuous_read => $callback
142              
143             Calls C<$callback> subroutine reference for each line of the command's output, passing the line as an argument.
144              
145             =item end_if_matched => qr{$pattern}
146              
147             End execution early as soon as C<$pattern> is detected in the command's output.
148              
149             =back
150              
151             Examples:
152              
153             If you only want to run a command if another command succeeds or fails, use the C or C options.
154              
155             run 'some-command',
156             only_if => 'pgrep httpd'; # only run if httpd is running
157              
158             run 'some-other-command',
159             unless => 'pgrep httpd'; # only run if httpd is _not_ running
160              
161             If you want to set custom environment variables you can do it like this:
162              
163             run 'my_command',
164             env => {
165             env_var_1 => 'the value for 1',
166             env_var_2 => 'the value for 2',
167             };
168              
169             If you want to end the command upon receiving a certain output:
170              
171             run 'my_command',
172             end_if_matched => qr{$pattern};
173              
174             =head2 run($command, $arguments, %options)
175              
176             This form will execute C<$command> with the given C<$arguments> pass as an array reference.
177             All arguments will be quoted by Rex with Cquoter()> according to the managed host's shell.
178              
179             run 'ls', [ '-l', '-t', '-r', '-a' ];
180             run 'ls', [ '/tmp', '-l' ], auto_die => TRUE;
181              
182             =head2 run($command_description, command => $command, %options)
183              
184             If you only want to run a command in certain cases, you can queue the command
185             and notify it to trigger its execution.
186              
187             run 'extract-something',
188             command => 'tar -C /foo -xzf /tmp/foo.tgz',
189             only_notified => TRUE;
190              
191             # some code ...
192              
193             notify 'run', 'extract-something'; # now the command gets executed
194              
195             =cut
196              
197             our $LAST_OUTPUT; # this variable stores the last output of a run.
198             # so that it is possible to get for example the output of an apt-get update
199             # that is called through >> install "foo" <<
200              
201             sub run {
202 11     11 1 3493 my $cmd = shift;
203              
204 11 50       101 if ( ref $cmd eq "ARRAY" ) {
205 0         0 for my $_cmd ( @{$cmd} ) {
  0         0  
206 0         0 &run( $_cmd, @_ );
207             }
208 0         0 return;
209             }
210              
211 11         52 my ( $code, $option );
212 11 50       58 if ( ref $_[0] eq "CODE" ) {
213 0         0 $code = shift;
214             }
215              
216 11         21 my ($args);
217 11 50       50 if ( ref $_[0] eq "ARRAY" ) {
218 0         0 $args = shift;
219             }
220              
221 11 100       49 if ( scalar @_ > 0 ) {
222 2         15 $option = {@_};
223             }
224              
225             $option->{auto_die} = Rex::Config->get_exec_autodie()
226 11 100       124 if !exists $option->{auto_die};
227              
228 11         68 my $res_cmd = $cmd;
229              
230 11 0 33     94 if ( exists $option->{only_notified} && $option->{only_notified} ) {
231 0         0 Rex::Logger::debug(
232             "This command runs only if notified. Passing by. ($cmd, $option->{command})"
233             );
234 0         0 my $notify = Rex::get_current_connection()->{notify};
235             $notify->add(
236             type => "run",
237             name => $cmd,
238             options => $option,
239             cb => sub {
240 0     0   0 my ($option) = shift;
241 0         0 Rex::Logger::debug(
242             "Running notified command: $cmd ($option->{command})");
243 0         0 run( $option->{command} );
244             }
245 0         0 );
246              
247 0         0 return;
248             }
249              
250 11 50       58 if ( exists $option->{command} ) {
251 0         0 $cmd = $option->{command};
252             }
253              
254             Rex::get_current_connection()->{reporter}
255 11         61 ->report_resource_start( type => "run", name => $res_cmd );
256              
257 11         42 my $changed = 1; # default for run() is 1
258              
259 11 50       77 if ( exists $option->{creates} ) {
260 0         0 my $fs = Rex::Interface::Fs->create();
261 0 0       0 if ( $fs->is_file( $option->{creates} ) ) {
262 0         0 Rex::Logger::debug(
263             "File $option->{creates} already exists. Not executing $cmd.");
264 0         0 $changed = 0;
265             }
266             }
267              
268 11 50       45 if ( exists $option->{only_if} ) {
269 0         0 run( $option->{only_if}, auto_die => 0 );
270 0 0       0 if ( $? != 0 ) {
271 0         0 Rex::Logger::debug(
272             "Don't executing $cmd because $option->{only_if} return $?.");
273 0         0 $changed = 0;
274 0         0 $? = 0; # reset $?
275             }
276             }
277              
278 11 50       49 if ( exists $option->{unless} ) {
279 0         0 run( $option->{unless}, auto_die => 0 );
280 0 0       0 if ( $? == 0 ) {
281 0         0 Rex::Logger::debug(
282             "Don't executing $cmd because $option->{unless} return $?.");
283 0         0 $changed = 0;
284             }
285             }
286              
287 11         22 my $out_ret;
288 11         45 my ( $out, $err );
289              
290 11 50       228 if ($changed) {
291 11         174 my $path;
292              
293 11 50       308 if ( !Rex::Config->get_no_path_cleanup() ) {
294 11         126 $path = join( ":", Rex::Config->get_path() );
295             }
296              
297 11         213 my $exec = Rex::Interface::Exec->create;
298              
299 11 50 33     61 if ( $args && ref($args) eq "ARRAY" ) {
300 0         0 my $quoter = Net::OpenSSH::ShellQuoter->quoter( $exec->shell->name );
301 0         0 $cmd = "$cmd " . join( " ", map { $quoter->quote($_) } @{$args} );
  0         0  
  0         0  
302             }
303              
304 11 50 33     74 if ( exists $option->{timeout} && $option->{timeout} > 0 ) {
305 0         0 eval {
306 0     0   0 local $SIG{ALRM} = sub { die("timeout"); };
  0         0  
307 0         0 alarm $option->{timeout};
308 0         0 ( $out, $err ) = $exec->exec( $cmd, $path, $option );
309 0         0 alarm 0;
310             };
311              
312 0 0       0 if ( $@ =~ m/^timeout at/ ) {
313 0         0 Rex::Logger::info( "Timeout executing $cmd.", "error" );
314 0         0 $? = 300;
315             }
316             }
317             else {
318 11         72 ( $out, $err ) = $exec->exec( $cmd, $path, $option );
319             }
320              
321 11 100       334 chomp $out if $out;
322 11 100       171 chomp $err if $err;
323              
324 11         180 $LAST_OUTPUT = [ $out, $err ];
325              
326 11 50       130 if ( !defined $out ) {
327 0         0 $out = "";
328             }
329              
330 11 100       114 if ( !defined $err ) {
331 10         122 $err = "";
332             }
333              
334 11 100 66     346 if ( $? == 127 ) {
    100          
    50          
335 1 50       29 Rex::Logger::info( "$cmd: Command not found.", "error" )
336             if ( Rex::Config->get_verbose_run );
337             }
338             elsif ( $? != 0 && $? != 300 ) {
339 5 50       178 Rex::Logger::info( "Error executing $cmd: Return code: $?", "warn" )
340             if ( Rex::Config->get_verbose_run );
341             }
342             elsif ( $? == 0 ) {
343 5 50       206 Rex::Logger::info("Successfully executed $cmd.")
344             if ( Rex::Config->get_verbose_run );
345             }
346              
347 11 50       160 if ($code) {
348 0         0 $out_ret = &$code( $out, $err );
349             }
350              
351             else {
352 11         81 $out_ret = $out;
353             }
354              
355             Rex::get_current_connection()->{reporter}->report(
356 11         124 changed => 1,
357             message => "Command ($cmd) executed. Return code: $?"
358             );
359             }
360             else {
361 0         0 Rex::get_current_connection()->{reporter}->report( changed => 0, );
362             }
363              
364             Rex::get_current_connection()->{reporter}
365 11         94 ->report_resource_end( type => "run", name => $res_cmd );
366              
367 11 100 66     279 if ( exists $option->{auto_die} && $option->{auto_die} ) {
368 3 100       41 if ( $? != 0 ) {
369 2         175 die("Error executing: $cmd.\nSTDOUT:\n$out\nSTDERR:\n$err");
370             }
371             }
372              
373 9 50 33     74 if ( wantarray && defined $out_ret ) {
374 0         0 return split( /\r?\n/, $out_ret );
375             }
376              
377 9         293 return $out_ret;
378             }
379              
380             =head2 can_run($command)
381              
382             This function checks if a command is available in the path. It accepts a list of commands, and returns the full path to the first command found.
383              
384             task 'uptime', sub {
385             if ( my $cmd = can_run( 'uptime', 'downtime' ) ) {
386             say run $cmd;
387             }
388             };
389              
390             =cut
391              
392             sub can_run {
393 436     436 1 6541 my @commands = @_;
394 436         11062 my $exec = Rex::Interface::Exec->create;
395 436         4184 $exec->can_run( [@commands] ); # use a new anon ref, so that we don't have drawbacks if some lower layers will manipulate things.
396             }
397              
398             =head2 sudo
399              
400             Run a single command, a code block, or all commands with C. You need perl to be available on the remote systems to use C.
401              
402             Depending on your remote sudo configuration, you may need to define a sudo password with I first:
403              
404             sudo_password 'my_sudo_password'; # hardcoding
405              
406             Or alternatively, since Rexfile is plain perl, you can read the password from terminal at the start:
407              
408             use Term::ReadKey;
409              
410             print 'I need sudo password: ';
411             ReadMode('noecho');
412             sudo_password ReadLine(0);
413             ReadMode('restore');
414              
415             Similarly, it is also possible to read it from a secret file, database, etc.
416              
417             You can turn sudo on globally with:
418              
419             sudo TRUE; # run _everything_ with sudo
420              
421             To run only a specific command with sudo, use :
422              
423             say sudo 'id'; # passing a remote command directly
424             say sudo { command => 'id' }; # passing anonymous hashref
425              
426             say sudo { command => 'id', user => 'different' }; # run a single command with sudo as different user
427              
428             To run multiple commands with C, either use an anonymous code reference directly:
429              
430             sudo sub {
431             service 'nginx' => 'restart';
432             say run 'id';
433             };
434              
435             or pass it via C (optionally along a different user):
436              
437             sudo {
438             command => sub {
439             say run 'id';
440             say run 'pwd', cwd => '/home/different';
441             },
442             user => 'different',
443             };
444              
445             B that some users receive the error C
446             to run sudo>. In this case you have to disable C for this user.
447             You can do this in your sudoers file with the following code:
448              
449             Defaults:$username !requiretty
450              
451             =cut
452              
453             sub sudo {
454 0     0 1   my ($cmd) = @_;
455              
456 0           my $options;
457 0 0         if ( ref $cmd eq "HASH" ) {
458 0           $options = $cmd;
459 0           $cmd = $options->{command};
460             }
461              
462 0 0 0       if ( $cmd eq "on" || $cmd eq "-on" || $cmd eq "1" ) {
    0 0        
463 0           Rex::Logger::debug("Turning sudo globally on");
464 0           Rex::global_sudo(1);
465 0           return;
466             }
467             elsif ( $cmd eq "0" ) {
468 0           Rex::Logger::debug("Turning sudo globally off");
469 0           Rex::global_sudo(0);
470 0           return;
471             }
472              
473 0           Rex::get_current_connection_object()->push_use_sudo(1);
474 0           Rex::get_current_connection_object()->push_sudo_options( %{$options} );
  0            
475              
476 0           my $ret;
477              
478             # if sudo is used with a code block
479 0 0         if ( ref($cmd) eq "CODE" ) {
480 0           $ret = &$cmd();
481             }
482             else {
483 0           $ret = i_run( $cmd, fail_ok => 1 );
484             }
485              
486 0           Rex::get_current_connection_object()->pop_use_sudo();
487 0           Rex::get_current_connection_object()->pop_sudo_options();
488              
489 0           return $ret;
490             }
491              
492             1;