#! /usr/bin/perl # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # # # Licensed Materials - Property of IBM # # (C) COPYRIGHT International Business Machines Corp. 2008,2022 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG # sccsid = "@(#)77 1.15 src/rsct/trace/trcspl/lstrsp.perl, trace, rsct_rady, radys004a 1/19/22 15:29:16" # Search on "sub help" and/or "sub usage" for script description. use Getopt::Long; use Fcntl ':mode'; use Time::Local; # Get a basic info my $osname = `/bin/uname -s 2> /dev/null`; chomp $osname; my $pwd = `pwd`; chomp $pwd; my $total_spool_files = 0; my $total_spool_files_bytes = 0; my @unsorted_hits = (); # trace entry list my @entry_files = (); my $rc; my $spool_dir, $cluster_name, $cluster_id, $node_name, $node_id; my $daemon_name, $megabytes, $from, $to, $previous, $tar_outfile, $gzip; my $help, $tar_ticks, $no_usage, $show_file_bytes; my $tar_stdout, $tar_stderr; my $tar_timeout; if(!GetOptions("spool_dir=s" => \$spool_dir, "cluster_name=s" => \$cluster_name, "cluster_id=s" => \$cluster_id, "node_name=s" => \$node_name, "node_id=s" => \$node_id, "daemon_name=s" => \$daemon_name, "megabytes=i" => \$megabytes, "from=s" => \$from, "to=s" => \$to, "previous=i" => \$previous, "tar=s" => \$tar_outfile, "gzip" => \$gzip, "help" => \$help, "h" =>\$help, "tar_stdout=s" => \$tar_stdout, "tar_stderr=s" => \$tar_stderr, "tar_timeout=i" => \$tar_timeout, "tar_ticks" => \$tar_ticks, "no_usage" => \$no_usage, "show_file_bytes" => \$show_file_bytes)) { usage(1); } if (defined $help) { help(); } # set the default values $spool_dir = "." unless defined($spool_dir); $cluster_name = ".*" unless defined($cluster_name); $cluster_id = ".*" unless defined($cluster_id); $node_name = ".*" unless defined($node_name); $node_id = ".*" unless defined($node_id); $daemon_name = ".*" unless defined($daemon_name); $tar_timeout = 3600 unless defined($tar_timeout); if ((defined $previous) && ((defined $from) || (defined $to))) { usage(1); } if (defined $from) { if (! ($from =~ /^\d{4}-\d{2}-\d{2}/)) { usage(1); } } if (defined $to) { if (! ($to =~ /^\d{4}-\d{2}-\d{2}/)) { usage(1); } } if( (defined $gzip) && !defined($tar_outfile) ) { # gzip is found but no tar outfile specified usage(1); } # Check parameters my $gzip_enabled = 1; if(!defined $gzip) { $rc = system("which gzip 1>/dev/null 2>&1"); #$rc = system("which gzip"); $gzip_enabled = ($rc eq 0); # gzip is defined } my $tar_ticks_enabled = 0; if (defined $tar_ticks) { $tar_ticks_enabled = 1; if (!defined $tar_outfile) { usage(1); } # Redirect tar stdout if(!defined($tar_stdout)) { # Redirect tar_stdout to /dev/null $tar_stdout = "/dev/null"; } } my $tar_path_prefix = ""; my $chdir_root_done = 0; if ("/" eq substr($spool_dir, 0, 1)) { # The current dir = "/" and "spool start with "/" $tar_path_prefix = "."; chdir("/"); $chdir_root_done = 1; if(defined($tar_stdout) && ("/" ne substr($tar_stdout,0,1))) { $tar_stdout = "$pwd/$tar_stdout"; # Prepend PWD to tar_stdout } if(defined($tar_stderr) && ("/" ne substr($tar_stderr,0,1))) { $tar_stderr = "$pwd/$tar_stderr"; # Prepend PWD to tar_stderr } if(defined($tar_outfile) && ("/" ne substr($tar_outfile,0,1))) { $tar_outfile = "$pwd/$tar_outfile"; # prepend PWD to tar_outfile } } # Traverse spool_dir to collect $entry_list descend($spool_dir, 5); # capture the spool information for IW dir as well if # cluster_name or cluster_id is given as input. if ((defined $cluster_name) || (defined $cluster_id)) { if ($cluster_name ne "IW") { $cluster_name = "IW"; descend($spool_dir, 5); } } if ((defined $megabytes) || (defined $show_file_bytes)) { my @sorted_hits = (); if (!defined $show_file_bytes) { @sorted_hits = sort { (@$b[1] =~ /^.+\.(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+)$/)[0] cmp (@$a[1] =~ /^.+\.(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+)$/)[0]; } @unsorted_hits; } else { @sorted_hits = sort { (@$a[1] =~ /^.+\.(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+)$/)[0] cmp (@$b[1] =~ /^.+\.(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+)$/)[0]; } @unsorted_hits; } foreach $arrayref (@sorted_hits) { if ((defined $megabytes) && ($total_spool_files_bytes + @$arrayref[2] > $megabytes*1024*1024)) { last; } process_entry(@$arrayref[0], @$arrayref[1], @$arrayref[2]); if (defined $megabytes) { $total_spool_files_bytes += @$arrayref[2]; } } } if (($tar_ticks_enabled ne 0) && ($total_spool_files)) { print "\n"; } # 127 - timeout my $exitrc = 0; local $timeout_happened = 0; if( scalar(@entry_files) > 0 ) { # To deal with stdout/stderr outputs from "tar" command open OLDSTDOUT, ">&STDOUT"; open OLDSTDERR, ">&STDERR"; # Ignore stdout of tar if(defined($tar_stdout)) { open STDOUT, ">$tar_stdout" or warn "Can't open tar_stdout=$tar_stdout: $!"; } if(defined($tar_stderr)) { open STDERR, ">$tar_stderr" or warn "Can't open tar_stderr=$tar_stderr: $!"; } # Run the tar command with the timeout eval { local $SIG{ALRM} = sub {die "tar_alarm\n"}; alarm $tar_timeout; # set the timeout # tar all collected 'trace' files under spool tar_trace_entry_files(); alarm 0; # reset alarm }; if ($@) { die unless $@ eq "tar_alarm\n"; $timeout_happened = 1; $exitrc = 127; } # close redirected stdout/stderr close STDOUT if defined($tar_stdout); close STDERR if defined($tar_stderr); # Restore stdout, stderr open STDOUT, '>&OLDSTDOUT' or die "Can't restore original stdout: $!"; open STDERR, '>&OLDSTDERR' or die "Can't restore original stderr: $!"; close OLDSTDOUT or warn "Can't close old stdout: $!"; close OLDSTDERR or warn "Can't close old stderr: $!"; } # end-of-tar operation if ($chdir_root_done) { chdir("$pwd"); } if($timeout_happened != 0) { my $datestring = localtime(); print STDERR "2 $datestring: $0 - tar command executed is timed out (timeout=$tar_timeout)\n"; } exit $exitrc; ####################################################################################### sub show_tick_mark { if ($tar_ticks_enabled > 0) { print "."; } } sub help { print STDERR <] [--cluster_name ] [--cluster_id ] [--node_name ] [--node_id ] [--daemon_name ] [--previous ] | [--from [--to ]] [--megabytes ] [--tar ] [--tar_ticks] [--tar_timeout ] [--tar_stdout ] [--tar_stderr ] [--show_file_bytes] [--gzip] [--no_usage] Notes: - expected patterns are quoted Perl regular expressions - pattern defaults are '.*' - --tar_ticks prints a '.' instead of a file name for each matching file - --no_usage suppresses usage statement errata - --show_file_bytes forces an "oldest to latest" sort USAGE } if (defined $_[0]) { exit $_[0]; } } # args: parent_directory, recurse_depth (counts down to 0) # returns: 0=okay 1=hit_megabytes_limit sub descend { local (*DIR); # makes the handle distinct across recursive calls my $match_expression; if (5 == $_[1]) { $match_expression = sprintf("^%s\$", $cluster_name); } elsif (4 == $_[1]) { $match_expression = sprintf("^%s\$", $cluster_id); } elsif (3 == $_[1]) { $match_expression = sprintf("^%s\$", $node_name); } elsif (2 == $_[1]) { $match_expression = sprintf("^%s\$", $node_id); } elsif (1 == $_[1]) { $match_expression = sprintf("^%s\$", $daemon_name); } elsif (0 == $_[1]) { if ((defined $megabytes) || (defined $show_file_bytes)) { # include all files ending in timestamps $match_expression = "^.+\\.\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}\\.\\d+\$"; } else { # include only trace spool files ending in timestamps $match_expression = "^.+\\.\\d+\\.sp\\.\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}\\.\\d+\$"; } } if (! opendir DIR, "$_[0]") { printf(STDERR "Error: cannot open spool directory(%s).\n", $_[0]); exit 2; } while (my $entry = readdir(DIR)) { if (("." ne $entry) && (".." ne $entry)) { if ($entry =~ /$match_expression/) { my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat("$_[0]/$entry"); # on 0th recurse_depth we're checking spool file names if ((0 == $_[1]) && (S_ISREG($mode))) { if ((defined $previous) || (defined $from) || (defined $to)) { my $file_timedate_stamp; # split out file name from timedate stamp because file name may contain periods if ((defined $megabytes) || (defined $show_file_bytes)) { $entry =~ /^.+\.(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+)$/; $file_timedate_stamp = $1; } else { $entry =~ /^.+\.\d+\.sp\.(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+)$/; $file_timedate_stamp = $1; } my ($file_date, $file_microseconds) = split(/\./, $file_timedate_stamp, 4); my ($file_year, $file_month, $file_day, $file_hour, $file_minute, $file_second) = split(/_/, $file_date, 6); my $specified_from_epoch_seconds = 0; if (defined $previous) { $specified_from_epoch_seconds = todays_midnight_epoch_seconds() - $previous*24*60*60; } elsif (defined $from) { $specified_from_epoch_seconds = epoch_seconds(constituent_date_time_elements($from)); } if (epoch_seconds($file_year, $file_month, $file_day, $file_hour, $file_minute, $file_second) < $specified_from_epoch_seconds) { next; } if (defined $to) { if (epoch_seconds($file_year, $file_month, $file_day, $file_hour, $file_minute, $file_second) > epoch_seconds(constituent_date_time_elements($to))) { next; } } } if ((defined $megabytes) || (defined $show_file_bytes)) { push(@unsorted_hits, [$_[0], $entry, $size]); } else { process_entry($_[0], $entry, $size); } } elsif (S_ISDIR($mode)) { if (my $rc = descend("$_[0]/$entry", $_[1]-1)) { return $rc; } } } } } close DIR; return 0; } sub todays_midnight_epoch_seconds { my ($current_second, $current_minute, $current_hour, $current_day, $current_month, $current_year, $wday, $yday, $isdst) = localtime(time); return timelocal(0, 0, 0, $current_day, $current_month, $current_year); } # arguments: year, month (1-12), day (1-31), hour (0-23), minute (0-59), second (0-59) sub epoch_seconds { # force any undefined or blank values to zero # (NOTE: this has been seen to change the passed variables by reference) for (my $i=0; $i<6; $i++) { if (! $_[$i]) { $_[$i] = 0; } } # if month was specified, subtract 1 from it to fit # timelocal()'s argument requirements return timelocal($_[5], $_[4], $_[3], $_[2], ($_[1] ? $_[1] - 1 : $_[1]), $_[0]); } sub process_entry { my $path = shift; my $entry = shift; my $entry_bytes = shift; if (!defined $tar_outfile) { if (!defined $show_file_bytes) { print "$path/$entry\n"; } else { print "$entry_bytes $path/$entry\n"; } } else { # push the trace entry file to list push ( @entry_files, "$tar_path_prefix$path/$entry" ); show_tick_mark(); } $total_spool_files++; } sub constituent_date_time_elements { my $datetime = shift; my ($date, $time) = split /\./, $datetime; my $year, $month, $day, $hour, $minute, $seconds; if ($date) { ($year, $month, $day) = split /-/, $date; } if ($time) { ($hour, $minute, $seconds) = split /:/, $time; } return ($year, $month, $day, $hour, $minute, $seconds); } # # tar the files (@entry_files) to $tar_outfile sub tar_trace_entry_files { if( scalar(@entry_files) == 0 ) { return; } # Create a initial tar file with 1 spool file, so that we can add files to it via 'tar -uf' my $first_file = shift @entry_files; system("tar -cvf $tar_outfile $first_file") == 0 or do { print STDERR "Failled to create $tar_outfile, $!, RC: $?\n"; $exitrc = 11; return; }; while( scalar(@entry_files) > 0 ) { my @entry_files_subset = splice @entry_files, 0, 100; # Extract 100 files out each time if( $osname =~ "Linux" ) { my $tar_cmd = "tar --ignore-failed-read -uvf $tar_outfile @entry_files_subset"; $exitrc = system($tar_cmd); if ($exitrc != 0) { printf STDERR "Tar command failled with error, $!, RC: $exitrc\n"; $exitrc = 12; last; } } else { # AIX or other platforms my $tar_cmd = "tar -uvf $tar_outfile @entry_files_subset"; $exitrc = system($tar_cmd); if ($exitrc != 0) { printf STDERR "Tar command failled with error, $!, $exitrc\n"; $exitrc = 13; last; } } } #end-of-while # Compress whatever has been tarred so far, may or may not be all spool files if($gzip_enabled) { system("gzip -f $tar_outfile"); } } # end-of-tar operation