File Coverage

lib/Rex/CLI.pm
Criterion Covered Total %
statement 131 509 25.7
branch 14 182 7.6
condition 0 38 0.0
subroutine 29 45 64.4
pod 0 8 0.0
total 174 782 22.2


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             package Rex::CLI;
6              
7 15     15   114605 use v5.12.5;
  15         74  
8 15     15   76 use warnings;
  15         30  
  15         646  
9              
10             our $VERSION = '1.14.2.3'; # TRIAL VERSION
11              
12 15     15   691 use English qw(-no_match_vars);
  15         3915  
  15         117  
13 15     15   6427 use FindBin;
  15         15557  
  15         716  
14 15     15   102 use File::Basename qw(basename dirname);
  15         36  
  15         977  
15 15     15   1140 use Time::HiRes qw(gettimeofday tv_interval);
  15         2830  
  15         77  
16 15     15   1618 use Cwd qw(getcwd);
  15         28  
  15         580  
17 15     15   66 use List::Util qw(max);
  15         27  
  15         1806  
18 15     15   1274 use Text::Wrap;
  15         27365  
  15         805  
19 15     15   1443 use Term::ANSIColor;
  15         17031  
  15         925  
20 15     15   1113 use Term::ReadKey;
  15         30870  
  15         1369  
21 15     15   1391 use Sort::Naturally;
  15         46586  
  15         1175  
22              
23 15     15   1286 use if $OSNAME eq 'MSWin32', 'Win32::Console::ANSI';
  15         53  
  15         277  
24              
25 15     15   1715 use Rex;
  15         32  
  15         79  
26 15     15   92 use Rex::Args;
  15         30  
  15         175  
27 15     15   340 use Rex::Config;
  15         30  
  15         109  
28 15     15   152 use Rex::Group;
  15         41  
  15         183  
29 15     15   514 use Rex::Batch;
  15         31  
  15         158  
30 15     15   448 use Rex::TaskList;
  15         31  
  15         47  
31 15     15   293 use Rex::Logger;
  15         24  
  15         53  
32 15     15   286 use YAML;
  15         20  
  15         888  
33              
34 15     15   87 use Data::Dumper;
  15         30  
  15         884  
35              
36             my $no_color = 0;
37              
38             # preload some modules
39 15     15   99 use Rex -base;
  15         34  
  15         101  
40              
41             $OUTPUT_AUTOFLUSH++;
42              
43             my ( %opts, @help, @exit );
44              
45             if ( $#ARGV < 0 ) {
46             @ARGV = qw(-h);
47             }
48              
49             sub new {
50 0     0 0 0 my $that = shift;
51 0   0     0 my $proto = ref($that) || $that;
52 0         0 my $self = {@_};
53              
54 0         0 bless( $self, $proto );
55              
56 0         0 return $self;
57             }
58              
59             sub __run__ {
60 0     0   0 my ( $self, %more_args ) = @_;
61              
62 0         0 Rex::Args->parse_rex_opts;
63 0         0 %opts = Rex::Args->getopts;
64              
65 0 0       0 if ( $opts{'Q'} ) {
66 0         0 my $stdout;
67 0         0 open( my $newout, '>', \$stdout );
68 0         0 select $newout;
69 0         0 close(STDERR);
70             }
71              
72 0 0       0 if ( $opts{'m'} ) {
73 0         0 $no_color = 1;
74 0         0 $Rex::Logger::no_color = 1;
75             }
76              
77 0 0       0 if ( $opts{'d'} ) {
78 0         0 $Rex::Logger::debug = $opts{'d'};
79 0         0 $Rex::Logger::silent = 0;
80             }
81              
82 0         0 Rex::Config->set_use_cache(1);
83 0 0       0 if ( $opts{"c"} ) {
    0          
84              
85             # Rex::Config->set_use_cache(1);
86             # since 0.46 just a pseudo option
87             # cache is enabled by default
88             }
89             elsif ( $opts{"C"} ) {
90 0         0 Rex::Config->set_use_cache(0);
91             }
92              
93 0         0 Rex::Logger::debug("This is Rex version: $Rex::VERSION");
94 0         0 Rex::Logger::debug("Command Line Parameters");
95 0         0 for my $param ( keys %opts ) {
96 0         0 Rex::Logger::debug( "\t$param = " . $opts{$param} );
97             }
98              
99 0 0 0     0 if ( $opts{'h'} ) {
    0          
100 0         0 $self->__help__;
101             }
102             elsif ( $opts{'v'} && !$opts{'T'} ) {
103 0         0 $self->__version__;
104             }
105              
106 0 0       0 if ( $opts{'q'} ) {
107 0         0 $::QUIET = 1;
108 0 0       0 if ( $opts{'w'} ) {
109 0         0 $::QUIET = 2;
110             }
111             }
112              
113 0         0 $::rexfile = "Rexfile";
114 0 0       0 if ( $opts{'f'} ) {
115 0         0 Rex::Logger::debug( "Using Rexfile: " . $opts{'f'} );
116 0         0 $::rexfile = $opts{'f'};
117             }
118             else {
119 0 0 0     0 if ( ( !-e $::rexfile ) && ( $ARGV[0] && $ARGV[0] =~ /:/ ) ) {
      0        
120              
121             #if there is no Rexfile, and the user asks for a longer path task, see if we can use it as the Rexfile
122             #eg: rex -H $host Misc:Example:prepare --bar=baz
123 0         0 $::rexfile = $ARGV[0];
124 0         0 $::rexfile =~ s/:[^:]*$//;
125 0         0 $::rexfile =~ s{:}{/}g;
126 0         0 $::rexfile = 'Rex/' . $::rexfile . '.pm';
127             }
128             }
129              
130             FORCE_SERVER: {
131              
132 0 0       0 if ( $opts{'H'} ) {
  0         0  
133 0 0       0 if ( $opts{'H'} =~ m/^perl:(.*)/ ) {
134 0         0 my $host_eval = eval($1);
135              
136 0 0       0 if ( ref($host_eval) eq "ARRAY" ) {
137 0         0 $::FORCE_SERVER = join( " ", @{$host_eval} );
  0         0  
138             }
139             else {
140 0         0 die("Perl Code have to return an array reference.");
141             }
142             }
143             else {
144 0         0 $::FORCE_SERVER = $opts{'H'};
145             }
146             }
147              
148             }
149              
150 0 0       0 if ( $opts{'z'} ) {
151 0         0 my $host_eval = eval { `$opts{'z'}`; };
  0         0  
152 0 0       0 if ( $host_eval =~ m/\S/xms ) {
153 0         0 $::FORCE_SERVER = join( " ", split /\n|,|;/, $host_eval );
154             }
155             else {
156 0         0 $::FORCE_SERVER = $opts{'H'};
157             }
158             }
159              
160 0 0       0 if ( $opts{'z'} ) {
161 0         0 my $host_eval = eval { `$opts{'z'}`; };
  0         0  
162 0 0       0 if ( $host_eval =~ m/\S/xms ) {
163 0         0 $::FORCE_SERVER = join( " ", split /\n|,|;/, $host_eval );
164             }
165             else {
166 0         0 Rex::Logger::info("You must give a valid command.");
167             }
168             }
169              
170 0 0       0 if ( $opts{'o'} ) {
171 0         0 Rex::Output->require;
172 0         0 Rex::Output->get( $opts{'o'} );
173             }
174              
175 0         0 handle_lock_file($::rexfile);
176              
177 0 0       0 Rex::Config->set_environment( $opts{"E"} ) if ( $opts{"E"} );
178              
179 0 0 0     0 if ( $opts{'g'} || $opts{'G'} ) {
180              
181             #$::FORCE_SERVER = "\0" . $opts{'g'};
182 0   0     0 $opts{'g'} ||= $opts{'G'};
183              
184 0 0       0 if ( ref $opts{'g'} ne "ARRAY" ) {
185 0         0 $::FORCE_SERVER = [ $opts{'g'} ];
186             }
187             else {
188 0         0 $::FORCE_SERVER = $opts{'g'};
189             }
190             }
191              
192 0         0 load_server_ini_file($::rexfile);
193 0         0 load_rexfile($::rexfile);
194              
195             #### check if some parameters should be overwritten from the command line
196             CHECK_OVERWRITE: {
197              
198 0         0 my $pass_auth = 0;
  0         0  
199              
200 0 0       0 if ( $opts{'u'} ) {
201 0         0 Rex::Commands::user( $opts{'u'} );
202 0         0 for my $task ( Rex::TaskList->create()->get_tasks ) {
203 0         0 Rex::TaskList->create()->get_task($task)->set_user( $opts{'u'} );
204             }
205             }
206              
207 0 0       0 if ( $opts{'p'} ) {
208 0         0 Rex::Commands::password( $opts{'p'} );
209              
210 0 0       0 unless ( $opts{'P'} ) {
211 0         0 $pass_auth = 1;
212             }
213              
214 0         0 for my $task ( Rex::TaskList->create()->get_tasks ) {
215 0         0 Rex::TaskList->create()->get_task($task)->set_password( $opts{'p'} );
216             }
217              
218             }
219              
220 0 0       0 if ( $opts{'P'} ) {
221 0         0 Rex::Commands::private_key( $opts{'P'} );
222              
223 0         0 for my $task ( Rex::TaskList->create()->get_tasks ) {
224             Rex::TaskList->create()->get_task($task)
225 0         0 ->set_auth( "private_key", $opts{'P'} );
226             }
227             }
228              
229 0 0       0 if ( $opts{'K'} ) {
230 0         0 Rex::Commands::public_key( $opts{'K'} );
231              
232 0         0 for my $task ( Rex::TaskList->create()->get_tasks ) {
233             Rex::TaskList->create()->get_task($task)
234 0         0 ->set_auth( "public_key", $opts{'K'} );
235             }
236             }
237              
238 0 0       0 if ($pass_auth) {
239 0         0 pass_auth;
240             }
241              
242             }
243              
244 0         0 Rex::Logger::debug("Initializing Logger from parameters found in $::rexfile");
245              
246 0 0 0     0 if ( $opts{'T'} && $opts{'m'} ) {
    0 0        
    0          
247              
248             # create machine readable tasklist
249 0         0 my @tasks = Rex::TaskList->create()->get_tasks;
250 0         0 for my $task (@tasks) {
251 0         0 my $desc = Rex::TaskList->create()->get_desc($task);
252 0         0 $desc =~ s/'/\\'/gms;
253 0         0 print "'$task'" . " = '$desc'\n";
254             }
255             }
256             elsif ( $opts{'T'} && $opts{'y'} ) {
257 0         0 my @tasks = Rex::TaskList->create()->get_tasks;
258 0         0 my @envs = Rex::Commands->get_environments();
259 0         0 my %groups = Rex::Group->get_groups;
260              
261 0         0 my %real_groups;
262              
263 0         0 for my $group ( keys %groups ) {
264 0         0 my @servers = map { $_->get_servers }
  0         0  
265             Rex::Group->get_group_object($group)->get_servers;
266 0         0 $real_groups{$group} = \@servers;
267             }
268              
269 0         0 print YAML::Dump(
270             {
271             tasks => \@tasks,
272             envs => \@envs,
273             groups => \%real_groups,
274             }
275             );
276             }
277             elsif ( $opts{'T'} ) {
278 0         0 _handle_T(%opts);
279              
280 0         0 Rex::global_sudo(0);
281 0 0       0 Rex::Logger::debug("Removing lockfile") if ( !exists $opts{'F'} );
282 0 0       0 CORE::unlink("$::rexfile.lock") if ( !exists $opts{'F'} );
283 0         0 CORE::exit 0;
284             }
285              
286             # turn sudo on with cli option s is used
287 0 0       0 if ( exists $opts{'s'} ) {
288 0         0 sudo("on");
289             }
290 0 0       0 if ( exists $opts{'S'} ) {
291 0         0 sudo_password( $opts{'S'} );
292             }
293              
294 0 0       0 if ( exists $opts{'t'} ) {
295 0         0 parallelism( $opts{'t'} );
296             }
297              
298 0 0       0 if ( $opts{'e'} ) {
    0          
299 0         0 Rex::Logger::debug("Executing command line code");
300 0         0 Rex::Logger::debug( "\t" . $opts{'e'} );
301              
302             # execute the given code
303 0         0 my $code = "sub { \n";
304 0         0 $code .= $opts{'e'} . "\n";
305 0         0 $code .= "}";
306              
307 0         0 $code = eval($code);
308              
309 0 0       0 if ($EVAL_ERROR) {
310 0         0 Rex::Logger::info( "Error in eval line: $EVAL_ERROR\n", "warn" );
311 0         0 exit 1;
312             }
313              
314 0 0       0 if ( exists $opts{'t'} ) {
315 0         0 parallelism( $opts{'t'} );
316             }
317              
318 0         0 my $pass_auth = 0;
319              
320 0 0       0 if ( $opts{'u'} ) {
321 0         0 Rex::Commands::user( $opts{'u'} );
322             }
323              
324 0 0       0 if ( $opts{'p'} ) {
325 0         0 Rex::Commands::password( $opts{'p'} );
326              
327 0 0       0 unless ( $opts{'P'} ) {
328 0         0 $pass_auth = 1;
329             }
330             }
331              
332 0 0       0 if ( $opts{'P'} ) {
333 0         0 Rex::Commands::private_key( $opts{'P'} );
334             }
335              
336 0 0       0 if ( $opts{'K'} ) {
337 0         0 Rex::Commands::public_key( $opts{'K'} );
338             }
339              
340 0 0       0 if ($pass_auth) {
341 0         0 pass_auth;
342             }
343              
344 0         0 my @params = ();
345 0 0       0 if ( $opts{'H'} ) {
346 0         0 push @params, split( /\s+/, $opts{'H'} );
347             }
348 0         0 push @params, $code;
349 0         0 push @params, "eval-line-desc";
350 0         0 push @params, {};
351              
352 0         0 Rex::TaskList->create()->create_task( "eval-line", @params );
353 0         0 Rex::Commands::do_task("eval-line");
354 0         0 exit_rex();
355             }
356             elsif ( $opts{'M'} ) {
357 0         0 Rex::Logger::debug( "Loading Rex-Module: " . $opts{'M'} );
358 0         0 my $mod = $opts{'M'};
359 0         0 $mod =~ s{::}{/}g;
360 0         0 $mod .= ".pm";
361 0         0 require $mod;
362             }
363              
364 0         0 my $run_list = Rex::RunList->instance;
365              
366 0 0       0 if ( $opts{'b'} ) {
367 0         0 my $batch = $opts{'b'};
368 0         0 Rex::Logger::debug("Running batch: $batch");
369 0         0 $run_list->add_task($_) for Rex::Batch->get_batch($batch);
370             }
371              
372 0         0 $run_list->parse_opts(@ARGV);
373              
374 0         0 eval { $run_list->run_tasks };
  0         0  
375 0 0       0 if ($EVAL_ERROR) {
376              
377             # this is always the child
378 0         0 Rex::Logger::info( "Error running task/batch: $EVAL_ERROR", "warn" );
379 0         0 CORE::exit(0);
380             }
381              
382 0         0 exit_rex();
383             }
384              
385             sub _print_color {
386 0     0   0 my ( $msg, $color ) = @_;
387 0 0       0 $color = 'green' if !defined($color);
388              
389 0 0       0 if ($no_color) {
390 0         0 print $msg;
391             }
392             else {
393 0         0 print colored( [$color], $msg );
394             }
395             }
396              
397             sub __help__ {
398              
399 0     0   0 my $fmt = " %-6s %s\n";
400              
401 0         0 print "usage: \n";
402 0         0 print " rex [] [-H ] [-G ] []\n";
403 0         0 print " rex -T[m|y|v] []\n";
404 0         0 print "\n";
405 0         0 printf $fmt, "-b", "Run batch";
406 0         0 printf $fmt, "-e", "Run the given code fragment";
407 0         0 printf $fmt, "-E", "Execute a task on the given environment";
408 0         0 printf $fmt, "-G|-g", "Execute a task on the given server groups";
409 0         0 printf $fmt, "-H", "Execute a task on the given hosts (space delimited)";
410 0         0 printf $fmt, "-z", "Execute a task on hosts from this command's output";
411 0         0 print "\n";
412 0         0 printf $fmt, "-K", "Public key file for the ssh connection";
413 0         0 printf $fmt, "-P", "Private key file for the ssh connection";
414 0         0 printf $fmt, "-p", "Password for the ssh connection";
415 0         0 printf $fmt, "-u", "Username for the ssh connection";
416 0         0 print "\n";
417 0         0 printf $fmt, "-d", "Show debug output";
418 0         0 printf $fmt, "-ddd", "Show more debug output (includes profiling output)";
419 0         0 printf $fmt, "-m", "Monochrome output: no colors";
420 0         0 printf $fmt, "-o", "Output format";
421 0         0 printf $fmt, "-q", "Quiet mode: no log output";
422 0         0 printf $fmt, "-qw", "Quiet mode: only output warnings and errors";
423 0         0 printf $fmt, "-Q", "Really quiet: output nothing";
424 0         0 print "\n";
425 0         0 printf $fmt, "-T", "List tasks";
426 0         0 printf $fmt, "-Ta", "List all tasks, including hidden";
427 0         0 printf $fmt, "-Tm", "List tasks in machine-readable format";
428 0         0 printf $fmt, "-Tv", "List tasks verbosely";
429 0         0 printf $fmt, "-Ty", "List tasks in YAML format";
430 0         0 print "\n";
431 0         0 printf $fmt, "-c", "Turn cache ON";
432 0         0 printf $fmt, "-C", "Turn cache OFF";
433 0         0 printf $fmt, "-f", "Use this file instead of Rexfile";
434 0         0 printf $fmt, "-F", "Force: disregard lock file";
435 0         0 printf $fmt, "-h", "Display this help message";
436 0         0 printf $fmt, "-M", "Load this module instead of Rexfile";
437 0         0 printf $fmt, "-O", "Pass additional options, like CMDB path";
438 0         0 printf $fmt, "-s", "Use sudo for every command";
439 0         0 printf $fmt, "-S", "Password for sudo";
440 0         0 printf $fmt, "-t", "Number of threads to use (aka 'parallelism' param)";
441 0         0 printf $fmt, "-v", "Display (R)?ex version";
442 0         0 print "\n";
443              
444 0         0 for my $code (@help) {
445 0         0 &$code();
446             }
447              
448 0         0 CORE::exit 0;
449              
450             }
451              
452             sub add_help {
453 0     0 0 0 my ( $self, $code ) = @_;
454 0         0 push( @help, $code );
455             }
456              
457             sub add_exit {
458 1     1 0 2 my ( $self, $code ) = @_;
459 1         4 push( @exit, $code );
460             }
461              
462             sub __version__ {
463 0     0   0 print "(R)?ex " . $Rex::VERSION . "\n";
464 0         0 CORE::exit 0;
465             }
466              
467             sub _handle_T {
468 0     0   0 my %opts = @_;
469              
470 0         0 my ($cols) = Term::ReadKey::GetTerminalSize(*STDOUT);
471 0   0     0 $Text::Wrap::columns = $cols || 80;
472              
473 0         0 _list_tasks();
474 0         0 _list_batches();
475 0         0 _list_envs();
476 0         0 _list_groups();
477             }
478              
479             sub _list_tasks {
480 0     0   0 Rex::Logger::debug("Listing Tasks");
481              
482 0         0 my @tasks;
483 0 0       0 if ( $opts{'a'} ) {
484 0         0 @tasks = sort Rex::TaskList->create()->get_all_tasks(qr/.*/);
485             }
486             else {
487 0         0 @tasks = Rex::TaskList->create()->get_tasks;
488             }
489              
490 0 0       0 if ( defined $ARGV[0] ) {
491 0         0 @tasks = grep { $_ =~ /^$ARGV[0]/ } @tasks;
  0         0  
492              
493             # Warn if the user passed args to '-T' and no matching task names were found
494 0 0       0 Rex::Logger::info( "No tasks matching '$ARGV[0]' found.", "error" )
495             unless @tasks;
496             }
497              
498 0 0       0 return unless @tasks;
499              
500             # fancy sorting of tasks -- put tasks from Rexfile first
501 0         0 my @root_tasks = grep { !/:/ } @tasks;
  0         0  
502 0         0 my @other_tasks = grep { /:/ } @tasks;
  0         0  
503 0         0 @tasks = ( sort(@root_tasks), sort(@other_tasks) );
504              
505 0         0 _print_color( "Tasks\n", "yellow" );
506 0         0 my $max_task_len = max map { length } @tasks;
  0         0  
507 0         0 my $fmt = " %-" . $max_task_len . "s %s\n";
508 0         0 my $last_namespace = _namespace( $tasks[0] );
509              
510 0         0 for my $task (@tasks) {
511 0 0       0 print "\n" if $last_namespace ne _namespace($task);
512 0         0 $last_namespace = _namespace($task);
513              
514 0         0 my $description = Rex::TaskList->create()->get_desc($task);
515 0         0 my $output = sprintf $fmt, $task, $description;
516 0         0 my $indent = " " x $max_task_len . " ";
517              
518 0         0 print wrap( "", $indent, $output );
519              
520 0 0       0 if ( $opts{'v'} ) {
521 0         0 my @servers = sort @{ Rex::TaskList->create()->get_task($task)->server };
  0         0  
522 0         0 _print_color( " Servers: " . join( ", ", @servers ) . "\n" );
523             }
524             }
525             }
526              
527             sub _namespace {
528 0     0   0 my ($full_task_name) = @_;
529 0 0       0 return "" unless $full_task_name =~ /:/;
530 0         0 my ($namespace) = split /:/, $full_task_name;
531 0         0 return $namespace;
532             }
533              
534             sub _list_batches {
535 0     0   0 Rex::Logger::debug("Listing Batches");
536              
537 0         0 my @batchs = sort Rex::Batch->get_batchs;
538 0 0       0 return unless Rex::Batch->get_batchs;
539              
540 0         0 _print_color( "Batches\n", 'yellow' );
541 0         0 my $max_batch_len = max map { length } @batchs;
  0         0  
542 0         0 my $fmt = " %-" . $max_batch_len . "s %s\n";
543              
544 0         0 for my $batch ( sort @batchs ) {
545 0         0 my $description = Rex::Batch->get_desc($batch);
546 0         0 my $output = sprintf $fmt, $batch, $description;
547 0         0 my $indent = " " x $max_batch_len . " ";
548              
549 0         0 print wrap( "", $indent, $output );
550              
551 0 0       0 if ( $opts{'v'} ) {
552 0         0 my @tasks = Rex::Batch->get_batch($batch);
553 0         0 _print_color( " " . join( " ", @tasks ) . "\n" );
554             }
555             }
556             }
557              
558             sub _list_envs {
559 0     0   0 Rex::Logger::debug("Listing Envs");
560              
561             my @envs =
562 0         0 map { Rex::Commands->get_environment($_) }
  0         0  
563             sort Rex::Commands->get_environments();
564 0 0       0 return unless @envs;
565              
566 0 0       0 _print_color( "Environments\n", "yellow" ) if scalar @envs;
567 0         0 my $max_env_len = max map { length $_->{name} } @envs;
  0         0  
568 0         0 my $fmt = " %-" . $max_env_len . "s %s\n";
569              
570 0         0 for my $e ( sort @envs ) {
571 0         0 my $output = sprintf $fmt, $e->{name}, $e->{description};
572 0         0 my $indent = " " x $max_env_len . " ";
573 0         0 print wrap( "", $indent, $output );
574             }
575             }
576              
577             sub _list_groups {
578 0     0   0 Rex::Logger::debug("Listing Groups");
579              
580 0         0 my %groups = Rex::Group->get_groups;
581 0         0 my @group_names = sort keys %groups;
582              
583 0 0       0 return unless @group_names;
584              
585 0         0 _print_color( "Server Groups\n", "yellow" );
586 0         0 my $max_group_len = max map { length } @group_names;
  0         0  
587 0         0 my $fmt = " %-" . $max_group_len . "s %s\n";
588              
589 0         0 for my $group_name (@group_names) {
590 0         0 my $hosts = join( ", ", sort @{ $groups{$group_name} } );
  0         0  
591 0         0 my $output = sprintf $fmt, $group_name, $hosts;
592 0         0 my $indent = " " x $max_group_len . " ";
593 0         0 print wrap( "", $indent, $output );
594             }
595             }
596              
597             sub summarize {
598 0     0 0 0 my ($signal) = @_;
599 0         0 my %opts = Rex::Args->getopts;
600 0 0       0 return if $opts{'T'};
601              
602 0         0 my @summary = Rex::TaskList->create()->get_summary();
603 0 0       0 return unless @summary; # no tasks ran -- nothing to summarize
604              
605 0         0 my @failures = grep { $_->{exit_code} != 0 } @summary;
  0         0  
606              
607 0 0       0 if ( !@failures ) {
608 0         0 Rex::Logger::info("All tasks successful on all hosts");
609 0         0 return;
610             }
611              
612 0         0 Rex::Logger::info( @failures . " out of " . @summary . " task(s) failed:",
613             "error" );
614              
615 0         0 foreach (
616             sort {
617             ncmp( $a->{task}, $b->{task} )
618             || ncmp( $a->{server}, $b->{server} )
619 0 0       0 } @failures
620             )
621             {
622 0         0 Rex::Logger::info( "\t$_->{task} failed on $_->{server}", "error" );
623 0 0       0 if ( $_->{error_message} ) {
624 0         0 for my $line ( split( $INPUT_RECORD_SEPARATOR, $_->{error_message} ) ) {
625 0         0 Rex::Logger::info( "\t\t$line", "error" );
626             }
627             }
628             }
629             }
630              
631             sub handle_lock_file {
632 0     0 0 0 my $rexfile = shift;
633              
634 0 0       0 if ( $OSNAME !~ m/^MSWin/ ) {
635 0 0 0     0 if ( -f "$rexfile.lock" && !exists $opts{'F'} ) {
636 0         0 Rex::Logger::debug("Found $rexfile.lock");
637 0         0 my $pid = eval {
638 0         0 local ( @ARGV, $INPUT_RECORD_SEPARATOR ) = ("$rexfile.lock");
639 0         0 <>;
640             };
641 0         0 system(
642             "ps aux | awk -F' ' ' { print \$2 } ' | grep $pid >/dev/null 2>&1");
643 0 0       0 if ( $CHILD_ERROR == 0 ) {
644 0         0 Rex::Logger::info("Rexfile is in use by $pid.");
645 0         0 CORE::exit 1;
646             }
647             else {
648 0         0 Rex::Logger::debug("Found stale lock file. Removing it.");
649 0         0 Rex::global_sudo(0);
650 0         0 CORE::unlink("$rexfile.lock");
651             }
652             }
653              
654 0         0 Rex::Logger::debug("Creating lock-file ($rexfile.lock)");
655 0 0       0 open( my $f, ">", "$rexfile.lock" ) or die($OS_ERROR);
656 0         0 print $f $PID;
657 0         0 close($f);
658             }
659             else {
660 0         0 Rex::Logger::debug("Running on windows. Disabled lock file support.");
661             }
662             }
663              
664             sub load_server_ini_file {
665 0     0 0 0 my $rexfile = shift;
666              
667             # load server ini file
668 0         0 my $env = environment;
669 0         0 my $ini_dir = dirname($rexfile);
670 0         0 my $server_ini_file = "$ini_dir/server.$env.ini";
671 0 0       0 $server_ini_file = "$ini_dir/server.ini" unless -f $server_ini_file;
672              
673 0 0 0     0 if ( -f $server_ini_file && Rex::Group::Lookup::INI->is_loadable ) {
674 0         0 Rex::Logger::debug("Loading $server_ini_file");
675 0         0 Rex::Group::Lookup::INI::groups_file($server_ini_file);
676             }
677             }
678              
679             sub load_rexfile {
680 9     9 0 13491 my $rexfile = shift;
681 9         46 Rex::Logger::debug("Loading $rexfile");
682              
683 9 100       199 if ( !-f $rexfile ) {
684 1 50       9 if ( !exists $opts{'e'} ) {
685 1         8 Rex::Logger::info( "No Rexfile found.", "warn" );
686 1         4 Rex::Logger::info( "Create a file named 'Rexfile' in this directory,",
687             "warn" );
688 1         16 Rex::Logger::info( "or specify the file you want to use with:", "warn" );
689 1         17 Rex::Logger::info( " rex -f file_to_use task_to_run", "warn" );
690             }
691 1         8 return;
692             }
693              
694 8         391 my $rexfile_dir = dirname $rexfile;
695 8         43 my @new_inc = Rex::generate_inc($rexfile_dir);
696 8         62 @INC = @new_inc;
697              
698             # load Rexfile
699 8         21 eval {
700              
701             # add a true return value at the end of $rexfile.
702             # we need to do this because perl want a "true" value
703             # at the end of a file that is loaded.
704             unshift @INC, sub {
705 28     28   49179 my $load_file = $_[1];
706 28 100       183 if ( $load_file eq "__Rexfile__.pm" ) {
707 7 50       284 open( my $fh, "<", $rexfile )
708             or die("Error can't open $rexfile: $OS_ERROR");
709 7         288 my @content = <$fh>;
710 7         83 close($fh);
711 7         28 chomp @content;
712              
713 7         21 my $i = 0;
714 7         9 my $found_end = 0;
715              
716             # some rexfile has a __DATA__ or __END__ section
717             # and we need to add the true value before those sections.
718 7         14 for my $line (@content) {
719 47 50       89 if ( $line =~ m/^__(DATA|END)__$/ ) {
720 0         0 splice( @content, $i, 0, "42;" );
721 0         0 $found_end++;
722 0         0 last;
723             }
724 47         58 $i++;
725             }
726              
727             # we didn't found __DATA__ or __END__ so we just add
728             # it at the end.
729 7 50       16 if ( $found_end == 0 ) {
730 7         19 push @content, "42;";
731             }
732              
733             # we can't remove this load from @INC because on perl 5.8
734             # this causes a crash
735             #shift @INC; # remove this loader from @INC
736              
737             # we can't directly return a scalar reference because perl 5.8
738             # needs a filehandle. so we create a virtual filehandle...
739 7         30 my $c = join( "\n", @content );
740 7     2   136 open( my $rex_fh, "<", \$c );
  2         13  
  2         4  
  2         18  
741 7         2122 return $rex_fh;
742             }
743 8         50 };
744              
745             # we don't want to see the
746             # normal perl warning message on the screen. Instead we print
747             # the warning message in the catch-if below
748 8         16 my @warnings;
749 8     5   53 local $SIG{__WARN__} = sub { push @warnings, $_[0] };
  5         288  
750              
751             # we can't use $rexfile here, because if the variable contains dots
752             # the perl interpreter try to load the file directly without using @INC
753             # so we just fake a module name.
754 8         78 require __Rexfile__;
755              
756             # update %INC so that we can later use it to find the rexfile
757 6         63 $INC{"__Rexfile__.pm"} = $rexfile;
758              
759 6 100       59 if (@warnings) {
760 2         11 Rex::Logger::info( "You have some code warnings:", 'warn' );
761 2         8 for (@warnings) {
762 4         12 chomp;
763              
764 4         11 my $message = _tidy_loading_message( $_, $rexfile );
765              
766 4         14 Rex::Logger::info( "\t$message", 'warn' );
767             }
768             }
769              
770 6         43 1;
771             };
772              
773 8 100       61 if ($EVAL_ERROR) {
774 2         9 my $e = $EVAL_ERROR;
775 2         5 chomp $e;
776              
777 2         7 $e = _tidy_loading_message( $e, $rexfile );
778              
779 2         6 my ( @error_lines, @debug_lines );
780              
781 2         36 for my $line ( split $INPUT_RECORD_SEPARATOR, $e ) {
782 8 100       30 $line =~ m{CLI[.]pm[ ]line[ ]\d+}msx
783             ? push @debug_lines, $line
784             : push @error_lines, $line;
785             }
786              
787 2         11 Rex::Logger::info( "Compile time errors:", 'error' );
788              
789 2         6 for my $error_line (@error_lines) {
790 6         24 Rex::Logger::info( "\t$error_line", 'error' );
791             }
792              
793 2         9 for my $debug_line (@debug_lines) {
794 2         9 Rex::Logger::debug("\t$debug_line");
795             }
796              
797 2         10 exit 1;
798             }
799             }
800              
801             sub _tidy_loading_message {
802 6     6   15 my ( $message, $rexfile ) = @_;
803              
804 6         51 $message =~ s{/loader/[^/]+/__Rexfile__[.]pm}{$rexfile}gmsx;
805 6         19 return $message;
806             }
807              
808             sub exit_rex {
809 0     0 0 0 my ( $exit_code_override, $signal ) = @_;
810              
811 0 0       0 summarize($signal) if !$signal;
812              
813 0         0 Rex::global_sudo(0);
814 0 0       0 Rex::Logger::debug("Removing lockfile") if !exists $opts{'F'};
815 0 0       0 unlink("$::rexfile.lock") if !exists $opts{'F'};
816              
817 0         0 select STDOUT;
818              
819 0 0 0     0 if ( !$signal && $opts{'o'} && defined( Rex::Output->get ) ) {
      0        
820 0         0 Rex::Output->get->write();
821 0         0 IPC::Shareable->clean_up_all();
822             }
823              
824 0         0 for my $exit_hook (@exit) {
825 0         0 $exit_hook->( $exit_code_override, $signal );
826             }
827              
828 0 0       0 if ($Rex::WITH_EXIT_STATUS) {
829 0 0       0 CORE::exit($exit_code_override) if defined $exit_code_override;
830              
831 0         0 my @exit_codes = Rex::TaskList->create()->get_exit_codes();
832 0         0 for my $exit_code (@exit_codes) {
833 0 0       0 $exit_code = $exit_code >> 8 if $exit_code > 255;
834 0 0       0 CORE::exit($exit_code) if $exit_code != 0;
835             }
836             }
837              
838 0         0 CORE::exit(0);
839             }
840              
841             # we capture CTRL+C so we can cleanup vars files
842             # and give modules the chance to also do cleanup
843             $SIG{INT} = sub {
844             exit_rex( 1, "INT" );
845             };
846              
847             1;