Subversion Repositories camp_sysinfo_client_3

Rev

Rev 252 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 252 Rev 256
Line 154... Line 154...
154
#
154
#
155
# Version 3.3.0 20190419 RWR
155
# Version 3.3.0 20190419 RWR
156
# Converted to use YAML config file
156
# Converted to use YAML config file
157
#
157
#
158
# Version 3.4.0 20191111 RWR
158
# Version 3.4.0 20191111 RWR
159
# adding logging with priority. logging is a hash inside of %cvonfiguration which contains the following
159
# adding logging with priority. logging is a hash inside of %configuration which contains the following
160
# $configuration{ 'logging' } = {
160
# $configuration{ 'logging' } = {
161
#    'log type'  => 'string',
161
#    'log type'  => 'string',
162
#    'log level' => #,
162
#    'log level' => #,
163
#    'other params' => something,
163
#    'other params' => something,
164
# };
164
# };
Line 206... Line 206...
206
#
206
#
207
# Version 3.7.2 20240601 RWR
207
# Version 3.7.2 20240601 RWR
208
# Added code to undefine &doit if it is already defined, ie if one transport fails and we want to try another one,
208
# Added code to undefine &doit if it is already defined, ie if one transport fails and we want to try another one,
209
# we were getting a "can not redefine doit" and the transports would fail. The line added is
209
# we were getting a "can not redefine doit" and the transports would fail. The line added is
210
# undef &doit if exists &doit;
210
# undef &doit if exists &doit;
211
 
211
#
-
 
212
# Version 4.0.0 20250331 RWR
-
 
213
# Adding support for MS Windows
-
 
214
# Removed requirement for modules to be executable
212
 
215
 
213
# find our location and use it for searching for libraries
216
# find our location and use it for searching for libraries
214
BEGIN {
217
BEGIN {
215
   use FindBin;
218
   use FindBin;
216
   use File::Spec;
219
   use File::Spec;
Line 224... Line 227...
224
# contains the directory our script is in
227
# contains the directory our script is in
225
my $sourceDir = dirname( abs_path( __FILE__ ) );
228
my $sourceDir = dirname( abs_path( __FILE__ ) );
226
 
229
 
227
# define the version number
230
# define the version number
228
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
231
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
229
use version;
-
 
230
our $VERSION = version->declare("v3.7.2");
232
use version 0.77; our $VERSION = version->declare("v4.0.0");
231
our $DATA_VERSION = version->declare( 'v3.7.0' ); # used in sending the data file. sets version of XML/YAML data file
233
our $DATA_VERSION = version->declare( 'v3.7.2' ); # used in sending the data file. sets version of XML/YAML data file
232
 
234
 
233
# see https://perldoc.perl.org/Getopt/Long.html
235
# see https://perldoc.perl.org/Getopt/Long.html
234
use Getopt::Long;
236
use Getopt::Long;
235
# allow -vvn (ie, --verbose --verbose --dryrun)
237
# allow -vvn (ie, --verbose --verbose --dryrun)
236
Getopt::Long::Configure ("bundling");
238
Getopt::Long::Configure ("bundling");
Line 292... Line 294...
292
# would then be sent via e-mail to an administrative account, possibly root
294
# would then be sent via e-mail to an administrative account, possibly root
293
#
295
#
294
#######################################################
296
#######################################################
295
sub sendResults {
297
sub sendResults {
296
   my ( $globals, $transports, $message, $scriptDirectory ) = @_;
298
   my ( $globals, $transports, $message, $scriptDirectory ) = @_;
297
   &logIt( 3, "Entering sendResults" );
299
   &logIt( \%configuration,  3, "Entering sendResults" );
298
   foreach my $key ( sort { $a <=> $b } %$transports ) {
300
   foreach my $key ( sort { $a <=> $b } %$transports ) {
299
      if ( $transports->{$key}->{'sendScript'} ) {
301
      if ( $transports->{$key}->{'sendScript'} ) {
300
         &logIt( 3, "Trying to find file " . $transports->{$key}->{'sendScript'} . " in " . join( "\n\t", @{$scriptDirectory} ) );
302
         &logIt( \%configuration,  3, "Trying to find file " . $transports->{$key}->{'sendScript'} . " in " . join( "\n\t", @{$scriptDirectory} ) );
301
         my $sendScript = &findFile( $transports->{$key}->{'sendScript'}, $scriptDirectory );
303
         my $sendScript = &findFile( $transports->{$key}->{'sendScript'}, $scriptDirectory );
302
         if ( $sendScript ) {
304
         if ( $sendScript ) {
303
            # check if we have doit defined from previous iteration and, if so, undefine it
305
            # check if we have doit defined from previous iteration and, if so, undefine it
304
            undef &doit if exists &doit;
306
            undef &doit if exists &doit;
305
            # load the chosen script into memory
307
            # load the chosen script into memory
Line 308... Line 310...
308
            while ( my ( $gkey, $value ) = each %$globals ) { 
310
            while ( my ( $gkey, $value ) = each %$globals ) { 
309
               $transports->{$key}->{$gkey} = $value; 
311
               $transports->{$key}->{$gkey} = $value; 
310
            }
312
            }
311
            # do variable substitution for any values which need it
313
            # do variable substitution for any values which need it
312
            foreach my $thisOne ( keys %{$transports->{$key}} ) {
314
            foreach my $thisOne ( keys %{$transports->{$key}} ) {
313
               &logIt( 4, "$thisOne" );
315
               &logIt( \%configuration,  4, "$thisOne" );
314
               if ( $transports->{$key}->{$thisOne} =~ m/(\$configuration\{'hostname'\})|(\$reportDate)|(\$configuration\{'clientName'\})|(\$configuration\{'serialNumber'\})/ ) {
316
               if ( $transports->{$key}->{$thisOne} =~ m/(\$configuration\{'hostname'\})|(\$reportDate)|(\$configuration\{'clientName'\})|(\$configuration\{'serialNumber'\})/ ) {
315
                  $transports->{$key}->{$thisOne} = eval "\"$transports->{$key}->{$thisOne}\"";
317
                  $transports->{$key}->{$thisOne} = eval "\"$transports->{$key}->{$thisOne}\"";
316
               }
318
               }
317
            }
319
            }
318
            
320
            
319
            #%$transports{$key}{keys %$globals} = values %$globals;
321
            #%$transports{$key}{keys %$globals} = values %$globals;
320
            #print Dumper( $$transports[$key] );
322
            #print Dumper( $$transports[$key] );
321
            #next;
323
            #next;
322
            # execute the "doit" sub from that script
324
            # execute the "doit" sub from that script
323
            &logIt( 3, $message );
325
            &logIt( \%configuration,  3, $message );
324
            my $return = &doit( $transports->{$key}, $message );
326
            my $return = &doit( $transports->{$key}, $message );
325
            return $return if ( $return == 1 );
327
            return $return if ( $return == 1 );
326
         } else {
328
         } else {
327
            &logIt( 0,"Could not find " . $$transports[$key]{'sendScript'} . ", trying next transport" );
329
            &logIt( \%configuration,  0,"Could not find " . $$transports[$key]{'sendScript'} . ", trying next transport" );
328
         } # if..else
330
         } # if..else
329
      } # if
331
      } # if
330
   } # foreach
332
   } # foreach
331
   # if we made it here, we have not sent the report, so just return it to the user
333
   # if we made it here, we have not sent the report, so just return it to the user
332
   # if called from a cron job, it will (hopefully) be sent to root
334
   # if called from a cron job, it will (hopefully) be sent to root
333
   &logIt( 0, 'Error, reached ' . __LINE__ . " which should not happen, message was\n$message" );
335
   &logIt( \%configuration,  0, 'Error, reached ' . __LINE__ . " which should not happen, message was\n$message" );
334
   print $message;
336
   print $message;
335
   return 1;
337
   return 1;
336
}
338
}
337
 
339
 
338
#######################################################
340
#######################################################
Line 341... Line 343...
341
#
343
#
342
# return hostname from hostname -f
344
# return hostname from hostname -f
343
#
345
#
344
#######################################################
346
#######################################################
345
sub getHostName {
347
sub getHostName {
346
   &logIt( 3, "Entering getHostName" );
348
   &logIt( \%configuration,  3, "Entering getHostName" );
347
   my $hostname = lc $^O eq 'mswin32' ? `hostname` : `hostname -f`;
349
   my $hostname = lc $^O eq 'mswin32' ? `hostname` : `hostname -f`;
348
   chomp $hostname;
350
   chomp $hostname;
349
   return $hostname;
351
   return $hostname;
350
}
352
}
351
 
353
 
Line 434... Line 436...
434
#
436
#
435
#
437
#
436
#######################################################
438
#######################################################
437
sub tabDelimitedToHash {
439
sub tabDelimitedToHash {
438
   my ($hashRef, $tabdelim) = @_;
440
   my ($hashRef, $tabdelim) = @_;
439
   &logIt( 3, "Entering tabDelimitedToHash" );
441
   &logIt( \%configuration,  3, "Entering tabDelimitedToHash" );
440
   
442
   
441
   utf8::encode( $tabdelim ); # ensure this is all utf8, convert if necessary
443
   utf8::encode( $tabdelim ); # ensure this is all utf8, convert if necessary
442
 
444
 
443
   foreach my $line ( split( "\n", $tabdelim ) ) { # split on newlines, then process each line in turn
445
   foreach my $line ( split( "\n", $tabdelim ) ) { # split on newlines, then process each line in turn
444
      $line =~ s/'/\\'/gi; # escape single quotes
446
      $line =~ s/'/\\'/gi; # escape single quotes
Line 469... Line 471...
469
#
471
#
470
#######################################################
472
#######################################################
471
 
473
 
472
sub validatePermission {
474
sub validatePermission {
473
   my $file = shift;
475
   my $file = shift;
-
 
476
   # on Windows system, we can not look for ownership and exect status
-
 
477
   return '' if lc $^O eq 'mswin32'; 
474
   &logIt( 3, "Entering validatePermission with $file" );
478
   &logIt( \%configuration,  3, "Entering validatePermission with $file" );
-
 
479
   return "$file - Not a file" unless -f $file;
-
 
480
   # in test mode, do not check permissions
-
 
481
   return '' if $TESTING;
475
   my $return;
482
   my $return;
476
   # must be owned by root
483
   # must be owned by root
477
   my $owner = (stat($file))[4];
484
   my $owner = (stat($file))[4];
-
 
485
   # print "\tOwner = $owner\n";
478
   $return .= " - Bad Owner [$owner]" if $owner;
486
   $return .= " - Bad Owner [$owner]" if $owner;
479
   # must not have any permissions for group or world
487
   # must not have any permissions for group or world
480
   # ie, 0700 or less
488
   # ie, 0700 or less
481
   my $mode = (stat($file))[2];
489
   my $mode = sprintf( '%04o', (stat($file))[2] & 07777 );
482
   $mode = sprintf( '%04o', $mode & 07777 );
490
   # print "\tMode = $mode\n";
483
   $return .= " - Bad Permission [$mode]" unless $mode =~ m/0.00/;
491
   $return .= " - Bad Permission [$mode]" unless $mode =~ m/0.00/;
484
   return $return ? $file . $return : '';
492
   return $return ? $file . $return : '';
485
}
493
}
486
 
494
 
-
 
495
sub getModulesFromDir {
-
 
496
   my $moduleDir = shift;
-
 
497
   $moduleDir .= '/' unless substr( $moduleDir, -1 ) eq '/';
-
 
498
   # open the module directory
-
 
499
   return unless -d $moduleDir;
-
 
500
   opendir( my $dh, $moduleDir ) || die "Module Directory $moduleDir can not be opened: $!\n";
-
 
501
   # and get all files which are nothing but alpha-numerics and underscores (must begin with alpha-numeric)
-
 
502
   # ignore anything else, including directories
-
 
503
   # then, prepend the directory name (map), then validate they are ready to be used (validatePermissions)
-
 
504
   my @modules = grep{ ! &validatePermission( $_ ) } map { "$moduleDir$_" } grep { /^[a-zA-Z0-9][a-zA-Z0-9_]*$/ } readdir( $dh );
-
 
505
   closedir $dh;
-
 
506
   return \@modules;
-
 
507
   
-
 
508
}
-
 
509
 
487
#######################################################
510
#######################################################
488
#
511
#
489
# ProcessModules ( $system, $moduleDir )
512
# ProcessModules ( $system, $moduleDir )
490
#
513
#
491
# Processes all modules in $moduleDir, adding result to $system hash
514
# Processes all modules in $moduleDir, adding result to $system hash
Line 503... Line 526...
503
# on failure, the returned output of the script is assumed to be an error message
526
# on failure, the returned output of the script is assumed to be an error message
504
# and is displayed on STDERR
527
# and is displayed on STDERR
505
#######################################################
528
#######################################################
506
sub ProcessModules {
529
sub ProcessModules {
507
   my ( $system, $moduleDir ) = @_;
530
   my ( $system, $moduleDir ) = @_;
508
   &logIt( 3, "Entering processModules" );
531
   &logIt( \%configuration,  3, "Entering processModules" );
-
 
532
   foreach my $modFile ( sort @{ &getModulesFromDir( $moduleDir ) } ) { # for each valid script
509
   # open the module directory
533
      next unless -f $modFile; # skip any directories, symbolic links, etc...
510
   return unless -d $moduleDir;
534
      my $output = do $modFile;
511
   opendir( my $dh, $moduleDir ) || die "Module Directory $moduleDir can not be opened: $!\n";
535
      &logIt( \%configuration, 4, "Output of module $modFile is\n$output\n" );
-
 
536
      if ( defined $output && $output ) {
512
   # and get all files which are executable and contain nothing but alpha-numerics and underscores (must begin with alpha-numeric)
537
         if ( substr( $output, 1,6) eq 'error:' ) { # we have an error
513
   my @modules = grep { /^[a-zA-Z0-9][a-zA-Z0-9_]+$/ && -x "$moduleDir/$_" } readdir( $dh );
538
            &logIt( \%configuration,  1, $output );
514
   closedir $dh;
539
         } else {
515
   foreach my $modFile ( sort @modules ) { # for each valid script
-
 
516
      if ( my $error = &validatePermission( "$moduleDir$modFile" ) ) {
540
            &tabDelimitedToHash( $system, $output );
517
         print STDERR "Not Processed: $error\n";
541
         }
518
         next;
542
      } else {
-
 
543
         print "Script $modFile failed to compile with error\n$@\n";
519
      }
544
      }
520
      &logIt( 3, "Processing module $moduleDir$modFile");
545
      &logIt( \%configuration,  3, "Processing module $moduleDir$modFile");
521
      my $output = qx/$moduleDir$modFile $moduleDir/; # execute it and grab the output
-
 
522
      my $exitCode = $? >> 8; # process the exitCode
-
 
523
      # exitCode 0 - processed normally
-
 
524
      # exitCode 1 - not applicable to this machine
-
 
525
      if ( $exitCode && $exitCode > 1) { # if non-zero, error, so show an error message
-
 
526
         warn "Error in $moduleDir$modFile, [$output]\n";
-
 
527
         &logIt( 0, "Error in $moduleDir$modFile, [$output]" );
-
 
528
      } else { # otherwise, call tabDelimitedToHash to save the data
-
 
529
         &tabDelimitedToHash( $system, $output );
-
 
530
      } # if
-
 
531
   } # foreach
546
   } # foreach
532
   # add sysinfo-client (me) to the software list, since we're obviously installed
547
   # add sysinfo-client (me) to the software list, since we're obviously installed
533
   &tabDelimitedToHash( $system, "software\tsysinfo-client\tversion\t$main::VERSION\n" );
548
   &tabDelimitedToHash( $system, "software\tsysinfo-client\tversion\t$main::VERSION\n" );
534
}
549
}
535
 
550
 
Line 606... Line 621...
606
}
621
}
607
 
622
 
608
# simple display if --help is passed
623
# simple display if --help is passed
609
sub help {
624
sub help {
610
   use File::Basename;
625
   use File::Basename;
611
   print basename($0) . " $VERSION\n";
626
   print basename($0) . " $VERSION (data $DATA_VERSION)\n";
612
   print <<END
627
   print <<END
613
$0 [options]
628
$0 [options]
614
Options:
629
Options:
615
   -i,
630
   -i,
616
   --interactive    - do not read configuration file
631
   --interactive    - do not read configuration file
Line 624... Line 639...
624
   -m,
639
   -m,
625
   --modules=/path/ - override path to modules
640
   --modules=/path/ - override path to modules
626
   --scripts=/path/ - override path to scripts
641
   --scripts=/path/ - override path to scripts
627
   -p,
642
   -p,
628
   --periodic       - runs modules designed to be run only weekly, monthly, etc...
643
   --periodic       - runs modules designed to be run only weekly, monthly, etc...
-
 
644
   -t,
-
 
645
   --test           - If non-zero, runs but places output in /tmp
629
END
646
END
630
}
647
}
631
 
648
 
632
 
649
 
633
# handle any command line parameters that may have been passed in
650
# handle any command line parameters that may have been passed in
Line 645... Line 662...
645
            'test|t=s'      => \$TESTING
662
            'test|t=s'      => \$TESTING
646
            ) or die "Error parsing command line\n";
663
            ) or die "Error parsing command line\n";
647
 
664
 
648
                  
665
                  
649
if ( $help ) { &help() ; exit; }
666
if ( $help ) { &help() ; exit; }
650
if ( $version ) { use File::Basename; print basename($0) . " $VERSION\n"; exit; }
667
if ( $version ) { use File::Basename; print basename($0) . " $VERSION (data $DATA_VERSION)\n"; exit; }
651
 
668
 
652
if ( $interactive ) {
669
if ( $interactive ) {
653
   %configuration = %{ &interactiveConfig( \%configuration ) };
670
   %configuration = %{ &interactiveConfig( \%configuration ) };
654
} else {
671
} else {
655
   # load the configuration file
672
   # load the configuration file
Line 663... Line 680...
663
# user did not define a serial number, so make something up
680
# user did not define a serial number, so make something up
664
$configuration{'serialNumber'} = '' unless $configuration{'serialNumber'};
681
$configuration{'serialNumber'} = '' unless $configuration{'serialNumber'};
665
# oops, no client name (required) so tell them and exit
682
# oops, no client name (required) so tell them and exit
666
die "No client name defined in $configurationFile" unless $configuration{'clientName'};
683
die "No client name defined in $configurationFile" unless $configuration{'clientName'};
667
 
684
 
668
&logIt( 0, 'Starting sysinfo Run' );
685
&logIt( \%configuration,  0, 'Starting sysinfo Run' );
669
&logIt( 3, "Configuration is\n" . Data::Dumper->Dump( [\%configuration], [ qw($configuration) ] ) );
686
&logIt( \%configuration,  3, "Configuration is\n" . Data::Dumper->Dump( [\%configuration], [ qw($configuration) ] ) );
670
 
687
 
671
$TESTING = $configuration{'TESTING'} if defined $configuration{'TESTING'};
688
$TESTING = $configuration{'TESTING'} if defined $configuration{'TESTING'};
672
 
689
 
673
&logIt( 0, "Testing => $TESTING" ) if $TESTING;
690
&logIt( \%configuration,  0, "Testing => $TESTING" ) if $TESTING;
674
 
691
 
675
# hash reference that will store all info we are going to send to the server
692
# hash reference that will store all info we are going to send to the server
676
my $System = &initReport( \%configuration );
693
my $System = &initReport( \%configuration );
677
 
694
 
678
&logIt( 3, "Initial System\n" . Data::Dumper->Dump( [$System], [qw( $System )] ) );
695
&logIt( \%configuration,  3, "Initial System\n" . Data::Dumper->Dump( [$System], [qw( $System )] ) );
679
 
696
 
680
# process any modules in the system
697
# process any modules in the system
681
foreach my $moduleDir ( @{$configuration{'moduleDirs'}} ) {
698
foreach my $moduleDir ( @{$configuration{'moduleDirs'}} ) {
682
   &logIt( 3, "Processing modules from $moduleDir" );
699
   &logIt( \%configuration,  3, "Processing modules from $moduleDir" );
683
   &ProcessModules( $System, "$moduleDir/" );
700
   &ProcessModules( $System, "$moduleDir/" );
684
}
701
}
685
 
702
 
686
&logIt( 4, "After processing modules\n" . Data::Dumper->Dump( [$System], [qw( $System )] ) );
703
&logIt( \%configuration,  4, "After processing modules\n" . Data::Dumper->Dump( [$System], [qw( $System )] ) );
687
 
704
 
688
my $out =  sprintf( "#sysinfo: %s YAML\n", $VERSION->normal ) . &Dump( $System );
705
my $out =  sprintf( "#sysinfo: %s (data: %s) YAML\n", $VERSION, $DATA_VERSION ) . &Dump( $System );
689
 
706
 
690
&logIt( 4, 'At line number ' . __LINE__ . "\n" . Data::Dumper->Dump([$System],[qw($System)]) );
707
&logIt( \%configuration,  4, 'At line number ' . __LINE__ . "\n" . Data::Dumper->Dump([$System],[qw($System)]) );
691
 
708
 
692
# load some global values for use in the script, if required
709
# load some global values for use in the script, if required
693
my $globals = { 
710
my $globals = { 
694
      'upload_type'  => 'sysinfo',
711
      'upload_type'  => 'sysinfo',
695
      'data version' => $DATA_VERSION->normal,
712
      'data version' => $DATA_VERSION->normal,
Line 698... Line 715...
698
      'host name'    => $configuration{'hostname'},
715
      'host name'    => $configuration{'hostname'},
699
      'serial number'=> $configuration{'serialNumber'},
716
      'serial number'=> $configuration{'serialNumber'},
700
      'UUID'         => $configuration{'UUID'}
717
      'UUID'         => $configuration{'UUID'}
701
      };
718
      };
702
 
719
 
703
&logIt( 4, "Globals initialized\n" . Data::Dumper->Dump([$globals],[qw($globals)]) );
720
&logIt( \%configuration,  4, "Globals initialized\n" . Data::Dumper->Dump([$globals],[qw($globals)]) );
704
 
721
 
705
if ( $TESTING ) {
722
if ( $TESTING ) {
706
   open DATA, ">/tmp/sysinfo.testing.yaml" or die "Could not write to /tmp/sysinfo.testing.yaml: $!\n";
723
   open DATA, ">/tmp/sysinfo.testing.yaml" or die "Could not write to /tmp/sysinfo.testing.yaml: $!\n";
707
   print DATA $out;
724
   print DATA $out;
708
   close DATA;
725
   close DATA;
709
} else {
726
} else {
710
   # and send the results to the server
727
   # and send the results to the server
711
   if ( my $success = &sendResults( $globals, $configuration{'transports'}, $out, $configuration{'scriptDirs'} ) != 1 ) {
728
   if ( my $success = &sendResults( $globals, $configuration{'transports'}, $out, $configuration{'scriptDirs'} ) != 1 ) {
712
      &logIt( 0, "Error $success while sending report from $configuration{'hostname'}" );
729
      &logIt( \%configuration,  0, "Error $success while sending report from $configuration{'hostname'}" );
713
   }
730
   }
714
}
731
}
715
 
732
 
716
unlink ( $periodicOverrideFile ) if -e $periodicOverrideFile;
733
unlink ( $periodicOverrideFile ) if -e $periodicOverrideFile;
717
&logIt( 0, 'Ending sysinfo Run' );
734
&logIt( \%configuration,  0, 'Ending sysinfo Run' );
718
 
735
 
719
if ( $configuration{'postRunScript'}{'script name'} ) {
736
if ( $configuration{'postRunScript'}{'script name'} ) {
720
   my $script = $sourceDir . '/' . $configuration{'postRunScript'}{'script name'};
737
   my $script = $sourceDir . '/' . $configuration{'postRunScript'}{'script name'};
721
   exec ( "$script $configurationFile" ) if -x $script;
738
   exec ( "$script $configurationFile" ) if -x $script;
722
}
739
}