This file is indexed.

/usr/share/perl5/Rex/Commands/Run.pm is in rex 1.4.1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:

=head1 NAME

Rex::Commands::Run - Execute a remote command

=head1 DESCRIPTION

With this module you can run a command.

=head1 SYNOPSIS

 my $output = run "ls -l";
 sudo "id";


=head1 EXPORTED FUNCTIONS

=cut

package Rex::Commands::Run;

use strict;
use warnings;

our $VERSION = '1.4.1'; # VERSION

#require Exporter;
require Rex::Exporter;
use Data::Dumper;
use Rex;
use Rex::Logger;
use Rex::Helper::SSH2;
use Rex::Helper::Run;
use Rex::Helper::SSH2::Expect;
use Rex::Config;
use Rex::Interface::Exec;
use Rex::Interface::Fs;

BEGIN {
  if ( $^O !~ m/^MSWin/ ) {
    eval "use Expect";
  }
  else {
    # this fails sometimes on windows...
    eval {
      Rex::Logger::debug("Running under windows, Expect not supported."); };
  }
}

use vars qw(@EXPORT);
use base qw(Rex::Exporter);

@EXPORT = qw(run can_run sudo);

=head2 run($command [, $callback])

=head2 run($command_description, command => $command, %options)

This function will execute the given command and returns the output. In
scalar context it returns the raw output as is, and in list context it
returns the list of output lines. The exit value of the command is stored
in the $? variable.


 task "uptime", "server01", sub {
   say run "uptime";
   run "uptime", sub {
     my ($stdout, $stderr) = @_;
     my $server = Rex::get_current_connection()->{server};
     say "[$server] $stdout\n";
   };
 };

Supported options are:

  cwd           => $path
    sets the working directory of the executed command to $path
  only_if       => $condition_command
    executes the command only if $condition_command completes successfully
  unless        => $condition_command
    executes the command unless $condition_command completes successfully
  only_notified => TRUE
    queues the command, to be executed upon notification (see below)
  env           => { var1 => $value1, ..., varN => $valueN }
    sets environment variables in the environment of the command
  timeout       => value
    sets the timeout for the command to be run
  auto_die      => TRUE
    die if the command returns with a non-zero exit code
    it can be set globally via the exec_autodie feature flag
  command       => $command_to_run
    if set, run tries to execute the specified command and the first argument
    becomes an identifier for the run block (e.g. to be triggered with notify)
  creates       => $file_to_create
    tries to create $file_to_create upon execution
    skips execution if the file already exists

Examples:

If you only want to run a command in special cases, you can queue the command
and notify it when you want to run it.

 task "prepare", sub {
   run "extract-something",
     command     => "tar -C /foo -xzf /tmp/foo.tgz",
     only_notified => TRUE;

   # some code ...

   notify "run", "extract-something";  # now the command gets executed
 };

If you only want to run a command if another command succeeds or fails, you can use
I<only_if> or I<unless> option.

 run "some-command",
   only_if => "ps -ef | grep -q httpd";   # only run if httpd is running

 run "some-other-command",
   unless => "ps -ef | grep -q httpd";    # only run if httpd is not running

If you want to set custom environment variables you can do it like this:

 run "my_command",

    env => {
     env_var_1 => "the value for 1",
     env_var_2 => "the value for 2",
   };

If you want to end the command upon receiving a certain output:
 run "my_command",
   end_if_matched => qr/PATTERN/;
   
=cut

our $LAST_OUTPUT; # this variable stores the last output of a run.
 # so that it is possible to get for example the output of an apt-get update
 # that is called through >> install "foo" <<

sub run {
  my $cmd = shift;

  if ( ref $cmd eq "ARRAY" ) {
    for my $_cmd ( @{$cmd} ) {
      &run( $_cmd, @_ );
    }
    return;
  }

  my ( $code, $option );
  if ( ref $_[0] eq "CODE" ) {
    $code = shift;
  }
  if ( scalar @_ > 0 ) {
    $option = {@_};
  }

  $option->{auto_die} = Rex::Config->get_exec_autodie()
    if !exists $option->{auto_die};

  my $res_cmd = $cmd;

  if ( exists $option->{only_notified} && $option->{only_notified} ) {
    Rex::Logger::debug(
      "This command runs only if notified. Passing by. ($cmd, $option->{command})"
    );
    my $notify = Rex::get_current_connection()->{notify};
    $notify->add(
      type    => "run",
      name    => $cmd,
      options => $option,
      cb      => sub {
        my ($option) = shift;
        Rex::Logger::debug(
          "Running notified command: $cmd ($option->{command})");
        run( $option->{command} );
      }
    );

    return;
  }

  if ( exists $option->{command} ) {
    $cmd = $option->{command};
  }

  Rex::get_current_connection()->{reporter}
    ->report_resource_start( type => "run", name => $res_cmd );

  my $changed = 1; # default for run() is 1

  if ( exists $option->{creates} ) {
    my $fs = Rex::Interface::Fs->create();
    if ( $fs->is_file( $option->{creates} ) ) {
      Rex::Logger::debug(
        "File $option->{creates} already exists. Not executing $cmd.");
      $changed = 0;
    }
  }

  if ( exists $option->{only_if} ) {
    run( $option->{only_if}, auto_die => 0 );
    if ( $? != 0 ) {
      Rex::Logger::debug(
        "Don't executing $cmd because $option->{only_if} return $?.");
      $changed = 0;
      $?       = 0; # reset $?
    }
  }

  if ( exists $option->{unless} ) {
    run( $option->{unless}, auto_die => 0 );
    if ( $? == 0 ) {
      Rex::Logger::debug(
        "Don't executing $cmd because $option->{unless} return $?.");
      $changed = 0;
    }
  }

  my $out_ret;
  my ( $out, $err );

  if ($changed) {
    my $path;

    if ( !Rex::Config->get_no_path_cleanup() ) {
      $path = join( ":", Rex::Config->get_path() );
    }

    my $exec = Rex::Interface::Exec->create;

    if ( exists $option->{timeout} && $option->{timeout} > 0 ) {
      eval {
        local $SIG{ALRM} = sub { die("timeout"); };
        alarm $option->{timeout};
        ( $out, $err ) = $exec->exec( $cmd, $path, $option );
        alarm 0;
      };

      if ( $@ =~ m/^timeout at/ ) {
        Rex::Logger::info( "Timeout executing $cmd.", "error" );
        $? = 300;
      }
    }
    else {
      ( $out, $err ) = $exec->exec( $cmd, $path, $option );
    }

    chomp $out if $out;
    chomp $err if $err;

    $LAST_OUTPUT = [ $out, $err ];

    if ( !defined $out ) {
      $out = "";
    }

    if ( !defined $err ) {
      $err = "";
    }

    if ( $? == 127 ) {
      Rex::Logger::info( "$cmd: Command not found.", "error" )
        if ( Rex::Config->get_verbose_run );
    }
    elsif ( $? != 0 && $? != 300 ) {
      Rex::Logger::info( "Error executing $cmd: Return code: $?", "warn" )
        if ( Rex::Config->get_verbose_run );
    }
    elsif ( $? == 0 ) {
      Rex::Logger::info("Successfully executed $cmd.")
        if ( Rex::Config->get_verbose_run );
    }

    if ($code) {
      $out_ret = &$code( $out, $err );
    }

    else {
      $out_ret = $out;
    }

    Rex::get_current_connection()->{reporter}->report(
      changed => 1,
      message => "Command ($cmd) executed. Return code: $?"
    );
  }
  else {
    Rex::get_current_connection()->{reporter}->report( changed => 0, );
  }

  Rex::get_current_connection()->{reporter}
    ->report_resource_end( type => "run", name => $res_cmd );

  if ( exists $option->{auto_die} && $option->{auto_die} ) {
    if ( $? != 0 ) {
      die("Error executing: $cmd.\nSTDOUT:\n$out\nSTDERR:\n$err");
    }
  }

  if ( wantarray && defined $out_ret ) {
    return split( /\r?\n/, $out_ret );
  }

  return $out_ret;
}

=head2 can_run($command)

This function checks if a command is in the path or is available. You can
specify multiple commands, the first command found will be returned.

 task "uptime", sub {
   if( my $cmd = can_run("uptime", "downtime") ) {
     say run $cmd;
   }
 };

=cut

sub can_run {
  my @commands = @_;
  my $exec     = Rex::Interface::Exec->create;
  $exec->can_run( [@commands] ); # use a new anon ref, so that we don't have drawbacks if some lower layers will manipulate things.
}

=head2 sudo

Run a single command, a code block, or all commands with C<sudo>. You need perl to be available on the remote systems to use C<sudo>.

Depending on your remote sudo configuration, you may need to define a sudo password with I<sudo_password> first:

 sudo_password 'my_sudo_password'; # hardcoding

Or alternatively, since Rexfile is plain perl, you can read the password from terminal at the start:

 use Term::ReadKey;
 
 print 'I need sudo password: ';
 ReadMode('noecho');
 sudo_password ReadLine(0);
 ReadMode('restore');

Similarly, it is also possible to read it from a secret file, database, etc.

You can turn sudo on globally with:

 sudo TRUE; # run _everything_ with sudo

To run only a specific command with sudo, use :

 say sudo 'id';                # passing a remote command directly
 say sudo { command => 'id' }; # passing anonymous hashref
 
 say sudo { command => 'id', user => 'different' }; # run a single command with sudo as different user
 
 # running a single command with sudo as different user, and `cd` to another directory too
 say sudo { command => 'id', user => 'different', cwd => '/home/different' };

Passing an anonymous I<coderef> to C<sudo> allows for running the commands in the sub with sudo:

 sudo sub {
     service 'nginx' => 'restart';
     say run 'id';
 };

=cut

sub sudo {
  my ($cmd) = @_;

  my $options;
  if ( ref $cmd eq "HASH" ) {
    $options = $cmd;
    $cmd     = $options->{command};
  }

  if ( $cmd eq "on" || $cmd eq "-on" || $cmd eq "1" ) {
    Rex::Logger::debug("Turning sudo globally on");
    Rex::global_sudo(1);
    return;
  }
  elsif ( $cmd eq "0" ) {
    Rex::Logger::debug("Turning sudo globally off");
    Rex::global_sudo(0);
    return;
  }

  Rex::get_current_connection_object()->push_use_sudo(1);
  Rex::get_current_connection_object()->push_sudo_options( %{$options} );

  my $ret;

  # if sudo is used with a code block
  if ( ref($cmd) eq "CODE" ) {
    $ret = &$cmd();
  }
  else {
    $ret = i_run($cmd);
  }

  Rex::get_current_connection_object()->pop_use_sudo();
  Rex::get_current_connection_object()->pop_sudo_options();

  return $ret;
}

1;