| Line 1... |
Line 1... |
| 1 |
#! /usr/bin/env perl
|
1 |
#! /usr/bin/env perl
|
| 2 |
|
2 |
|
| - |
|
3 |
# Copyright (c) 2025, R. W. Rodolico
|
| - |
|
4 |
# All rights reserved.
|
| - |
|
5 |
#
|
| - |
|
6 |
# Redistribution and use in source and binary forms, with or without
|
| - |
|
7 |
# modification, are permitted provided that the following conditions are met:
|
| - |
|
8 |
#
|
| - |
|
9 |
# 1. Redistributions of source code must retain the above copyright notice, this
|
| - |
|
10 |
# list of conditions and the following disclaimer.
|
| - |
|
11 |
#
|
| - |
|
12 |
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
| - |
|
13 |
# this list of conditions and the following disclaimer in the documentation
|
| - |
|
14 |
# and/or other materials provided with the distribution.
|
| - |
|
15 |
#
|
| - |
|
16 |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| - |
|
17 |
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| - |
|
18 |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| - |
|
19 |
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
| - |
|
20 |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
| - |
|
21 |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
| - |
|
22 |
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| - |
|
23 |
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
| - |
|
24 |
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| - |
|
25 |
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| - |
|
26 |
|
| 3 |
use warnings;
|
27 |
use warnings;
|
| 4 |
use strict;
|
28 |
use strict;
|
| 5 |
use open ':std', ':encoding(utf8)' ; # force all I/O, including disk and STD?, to use utf-8
|
29 |
use open ':std', ':encoding(utf8)' ; # force all I/O, including disk and STD?, to use utf-8
|
| 6 |
use utf8; # Source code encoded using UTF-8, used to ensure output from modules is UTF-8, see tabDelimitedToHash
|
30 |
use utf8; # Source code encoded using UTF-8, used to ensure output from modules is UTF-8, see tabDelimitedToHash
|
| 7 |
|
31 |
|
| Line 141... |
Line 165... |
| 141 |
# On freeBSD systems, was looking in wrong place for configuration file
|
165 |
# On freeBSD systems, was looking in wrong place for configuration file
|
| 142 |
#
|
166 |
#
|
| 143 |
# Version 3.2.0 20180320 RWR
|
167 |
# Version 3.2.0 20180320 RWR
|
| 144 |
# Major change in the configuration file format; All entries are loaded into
|
168 |
# Major change in the configuration file format; All entries are loaded into
|
| 145 |
# hash %configuration, so clientname is no longer $clientname, but is now
|
169 |
# hash %configuration, so clientname is no longer $clientname, but is now
|
| 146 |
# $configuration{'clientname'}
|
170 |
# $config{'clientname'}
|
| 147 |
# NOT backwards compatible
|
171 |
# NOT backwards compatible
|
| 148 |
# changed configuration to be loaded into hash (vs directly loaded into variables)
|
172 |
# changed configuration to be loaded into hash (vs directly loaded into variables)
|
| 149 |
# added UUID to configuration file
|
173 |
# added UUID to configuration file
|
| 150 |
#
|
174 |
#
|
| 151 |
# Version 3.2.1 20180424 RWR
|
175 |
# Version 3.2.1 20180424 RWR
|
| Line 155... |
Line 179... |
| 155 |
# Version 3.3.0 20190419 RWR
|
179 |
# Version 3.3.0 20190419 RWR
|
| 156 |
# Converted to use YAML config file
|
180 |
# Converted to use YAML config file
|
| 157 |
#
|
181 |
#
|
| 158 |
# Version 3.4.0 20191111 RWR
|
182 |
# Version 3.4.0 20191111 RWR
|
| 159 |
# adding logging with priority. logging is a hash inside of %configuration which contains the following
|
183 |
# adding logging with priority. logging is a hash inside of %configuration which contains the following
|
| 160 |
# $configuration{ 'logging' } = {
|
184 |
# $config{ 'logging' } = {
|
| 161 |
# 'log type' => 'string',
|
185 |
# 'log type' => 'string',
|
| 162 |
# 'log level' => #,
|
186 |
# 'log level' => #,
|
| 163 |
# 'other params' => something,
|
187 |
# 'other params' => something,
|
| 164 |
# };
|
188 |
# };
|
| 165 |
#
|
189 |
#
|
| Line 227... |
Line 251... |
| 227 |
# contains the directory our script is in
|
251 |
# contains the directory our script is in
|
| 228 |
my $sourceDir = dirname( abs_path( __FILE__ ) );
|
252 |
my $sourceDir = dirname( abs_path( __FILE__ ) );
|
| 229 |
|
253 |
|
| 230 |
# define the version number
|
254 |
# define the version number
|
| 231 |
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
|
255 |
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
|
| - |
|
256 |
use version 0.77;
|
| 232 |
use version 0.77; our $VERSION = version->declare("v4.0.0");
|
257 |
our $VERSION = version->declare("v4.0.0");
|
| 233 |
our $DATA_VERSION = version->declare( 'v3.7.2' ); # used in sending the data file. sets version of XML/YAML data file
|
258 |
our $DATA_VERSION = version->declare( 'v3.7.2' ); # used in sending the data file. sets version of XML/YAML data file
|
| 234 |
|
259 |
|
| 235 |
# see https://perldoc.perl.org/Getopt/Long.html
|
260 |
# see https://perldoc.perl.org/Getopt/Long.html
|
| 236 |
use Getopt::Long;
|
261 |
use Getopt::Long;
|
| 237 |
# allow -vvn (ie, --verbose --verbose --dryrun)
|
262 |
# allow -vvn (ie, --verbose --verbose --dryrun)
|
| Line 245... |
Line 270... |
| 245 |
|
270 |
|
| 246 |
my $indentLevel = 2; # number of spaces to indent per level in XML or YAML
|
271 |
my $indentLevel = 2; # number of spaces to indent per level in XML or YAML
|
| 247 |
|
272 |
|
| 248 |
my $reportDate = &timeStamp(); # set report date
|
273 |
my $reportDate = &timeStamp(); # set report date
|
| 249 |
|
274 |
|
| 250 |
my $interactive = 0; # if set to 1, will go into interactive mode and output to local file
|
- |
|
| 251 |
my $periodicOverrideFile = '/tmp/sysinfo.firstrun'; # if this file exists, library.pm will tell all periodic modules to run anyway
|
275 |
my $periodicOverrideFile = '/tmp/sysinfo.firstrun'; # if this file exists, library.pm will tell all periodic modules to run anyway
|
| 252 |
my $periodic = 0; # if set to 1, will do modules which are only supposed to run weekly, monthly, etc...
|
276 |
my $periodic = 0; # if set to 1, will do modules which are only supposed to run weekly, monthly, etc...
|
| 253 |
|
277 |
|
| 254 |
my $version;
|
278 |
my $version;
|
| 255 |
my $help;
|
279 |
my $help;
|
| - |
|
280 |
my $configFile; # optional path to configuration file specified via command line
|
| - |
|
281 |
my $configType; # optional config file type: yaml, json, or datatransport
|
| - |
|
282 |
my $outputType; # optional output type: yaml, json, datatransport
|
| 256 |
|
283 |
|
| 257 |
my %configuration = (
|
284 |
my $config = {
|
| 258 |
'logging' => { 'log type' => 'cache', 'log level' => 0 }, # if set, will point to logging
|
285 |
'logging' => { 'log type' => 'cache', 'log level' => 0 }, # if set, will point to logging
|
| 259 |
'moduleDirs' => ["$sourceDir/modules"], # search paths for modules
|
286 |
'moduleDirs' => ["$sourceDir/modules"], # search paths for modules
|
| 260 |
'scriptDirs' => ["$sourceDir/scripts"], # search paths for scripts
|
287 |
'scriptDirs' => ["$sourceDir/scripts"], # search paths for scripts
|
| 261 |
'clientName' => '', # Required!! Must be set in conf file (no defaults)
|
288 |
'clientName' => '', # Required!! Must be set in conf file (no defaults)
|
| 262 |
'serialNumber' => '', # serial number of machine
|
289 |
'serialNumber' => '', # serial number of machine
|
| 263 |
'UUID' => '', # UUID of machine
|
290 |
'UUID' => '', # UUID of machine
|
| 264 |
'transports' => {'3' => { '-name-' => 'saveLocal', 'sendScript' => 'save_local', 'output directory' => "$sourceDir/reports" } }, # hash with various transports
|
291 |
'transports' => {'3' => { '-name-' => 'saveLocal', 'sendScript' => 'save_local', 'output directory' => "$sourceDir/reports" } }, # hash with various transports
|
| 265 |
'hostname' => &getHostName() # fully qualified host name of machine
|
292 |
'hostname' => &getHostName() # fully qualified host name of machine
|
| 266 |
);
|
293 |
};
|
| 267 |
|
294 |
|
| 268 |
|
295 |
|
| 269 |
|
296 |
|
| 270 |
#######################################################
|
297 |
#######################################################
|
| 271 |
#
|
298 |
#
|
| Line 294... |
Line 321... |
| 294 |
# would then be sent via e-mail to an administrative account, possibly root
|
321 |
# would then be sent via e-mail to an administrative account, possibly root
|
| 295 |
#
|
322 |
#
|
| 296 |
#######################################################
|
323 |
#######################################################
|
| 297 |
sub sendResults {
|
324 |
sub sendResults {
|
| 298 |
my ( $globals, $transports, $message, $scriptDirectory ) = @_;
|
325 |
my ( $globals, $transports, $message, $scriptDirectory ) = @_;
|
| 299 |
&logIt( \%configuration, 3, "Entering sendResults" );
|
326 |
&logIt( $config, 3, "Entering sendResults" );
|
| 300 |
foreach my $key ( sort { $a <=> $b } %$transports ) {
|
327 |
foreach my $key ( sort { $a <=> $b } %$transports ) {
|
| 301 |
if ( $transports->{$key}->{'sendScript'} ) {
|
328 |
if ( $transports->{$key}->{'sendScript'} ) {
|
| 302 |
&logIt( \%configuration, 3, "Trying to find file " . $transports->{$key}->{'sendScript'} . " in " . join( "\n\t", @{$scriptDirectory} ) );
|
329 |
&logIt( $config, 3, "Trying to find file " . $transports->{$key}->{'sendScript'} . " in " . join( "\n\t", @{$scriptDirectory} ) );
|
| 303 |
my $sendScript = &findFile( $transports->{$key}->{'sendScript'}, $scriptDirectory );
|
330 |
my $sendScript = &findFile( $transports->{$key}->{'sendScript'}, $scriptDirectory );
|
| 304 |
if ( $sendScript ) {
|
331 |
if ( $sendScript ) {
|
| 305 |
# check if we have doit defined from previous iteration and, if so, undefine it
|
332 |
# check if we have doit defined from previous iteration and, if so, undefine it
|
| 306 |
undef &doit if exists &doit;
|
333 |
undef &doit if exists &doit;
|
| 307 |
# load the chosen script into memory
|
334 |
# load the chosen script into memory
|
| Line 310... |
Line 337... |
| 310 |
while ( my ( $gkey, $value ) = each %$globals ) {
|
337 |
while ( my ( $gkey, $value ) = each %$globals ) {
|
| 311 |
$transports->{$key}->{$gkey} = $value;
|
338 |
$transports->{$key}->{$gkey} = $value;
|
| 312 |
}
|
339 |
}
|
| 313 |
# do variable substitution for any values which need it
|
340 |
# do variable substitution for any values which need it
|
| 314 |
foreach my $thisOne ( keys %{$transports->{$key}} ) {
|
341 |
foreach my $thisOne ( keys %{$transports->{$key}} ) {
|
| 315 |
&logIt( \%configuration, 4, "$thisOne" );
|
342 |
&logIt( $config, 4, "$thisOne" );
|
| 316 |
if ( $transports->{$key}->{$thisOne} =~ m/(\$configuration\{'hostname'\})|(\$reportDate)|(\$configuration\{'clientName'\})|(\$configuration\{'serialNumber'\})/ ) {
|
343 |
if ( $transports->{$key}->{$thisOne} =~ m/(\$config\{'hostname'\})|(\$reportDate)|(\$config\{'clientName'\})|(\$config\{'serialNumber'\})/ ) {
|
| 317 |
$transports->{$key}->{$thisOne} = eval "\"$transports->{$key}->{$thisOne}\"";
|
344 |
$transports->{$key}->{$thisOne} = eval "\"$transports->{$key}->{$thisOne}\"";
|
| 318 |
}
|
345 |
}
|
| 319 |
}
|
346 |
}
|
| 320 |
|
347 |
# add all global keys/values into transport hash
|
| 321 |
#%$transports{$key}{keys %$globals} = values %$globals;
|
348 |
$transports->{$key}->{keys %$globals} = values %$globals;
|
| 322 |
#print Dumper( $$transports[$key] );
|
349 |
#print Dumper( $$transports[$key] );
|
| 323 |
#next;
|
350 |
#next;
|
| 324 |
# execute the "doit" sub from that script
|
351 |
# execute the "doit" sub from that script
|
| 325 |
&logIt( \%configuration, 3, $message );
|
352 |
&logIt( $config, 3, $message );
|
| 326 |
my $return = &doit( $transports->{$key}, $message );
|
353 |
my $return = &doit( $transports->{$key}, $message );
|
| 327 |
return $return if ( $return == 1 );
|
354 |
return $return if ( $return == 1 );
|
| 328 |
} else {
|
355 |
} else {
|
| 329 |
&logIt( \%configuration, 0,"Could not find " . $$transports[$key]{'sendScript'} . ", trying next transport" );
|
356 |
&logIt( $config, 0,"Could not find " . $$transports[$key]{'sendScript'} . ", trying next transport" );
|
| 330 |
} # if..else
|
357 |
} # if..else
|
| 331 |
} # if
|
358 |
} # if
|
| 332 |
} # foreach
|
359 |
} # foreach
|
| 333 |
# if we made it here, we have not sent the report, so just return it to the user
|
360 |
# if we made it here, we have not sent the report, so just return it to the user
|
| 334 |
# if called from a cron job, it will (hopefully) be sent to root
|
361 |
# if called from a cron job, it will (hopefully) be sent to root
|
| 335 |
&logIt( \%configuration, 0, 'Error, reached ' . __LINE__ . " which should not happen, message was\n$message" );
|
362 |
&logIt( $config, 0, 'Error, reached ' . __LINE__ . " which should not happen, message was\n$message" );
|
| 336 |
print $message;
|
363 |
print $message;
|
| 337 |
return 1;
|
364 |
return 1;
|
| 338 |
}
|
365 |
}
|
| 339 |
|
366 |
|
| 340 |
#######################################################
|
367 |
#######################################################
|
| Line 343... |
Line 370... |
| 343 |
#
|
370 |
#
|
| 344 |
# return hostname from hostname -f
|
371 |
# return hostname from hostname -f
|
| 345 |
#
|
372 |
#
|
| 346 |
#######################################################
|
373 |
#######################################################
|
| 347 |
sub getHostName {
|
374 |
sub getHostName {
|
| 348 |
&logIt( \%configuration, 3, "Entering getHostName" );
|
375 |
&logIt( $config, 3, "Entering getHostName" );
|
| 349 |
my $hostname = lc $^O eq 'mswin32' ? `hostname` : `hostname -f`;
|
376 |
my $hostname = lc $^O eq 'mswin32' ? `hostname` : `hostname -f`;
|
| 350 |
chomp $hostname;
|
377 |
chomp $hostname;
|
| 351 |
return $hostname;
|
378 |
return $hostname;
|
| 352 |
}
|
379 |
}
|
| 353 |
|
380 |
|
| 354 |
#######################################################
|
381 |
#######################################################
|
| 355 |
#
|
382 |
#
|
| 356 |
# escapeForYAML
|
- |
|
| 357 |
#
|
- |
|
| 358 |
# Escapes values put into YAML report
|
383 |
# validatePermission ( $file )
|
| 359 |
#
|
384 |
#
|
| - |
|
385 |
# Checks that file is owned by root, and has permission
|
| - |
|
386 |
# 0700 or less
|
| - |
|
387 |
#
|
| 360 |
# DEPRECATED AS OF VERSION 3.3.0
|
388 |
# Returns empty string on success, error message
|
| 361 |
# uses YAML::Tiny
|
389 |
# on failure
|
| 362 |
#
|
390 |
#
|
| 363 |
#######################################################
|
391 |
#######################################################
|
| 364 |
#sub escapeForYAML {
|
- |
|
| 365 |
# my $value = shift;
|
- |
|
| 366 |
# $value =~ s/'/\\'/gi; # escape single quotes
|
- |
|
| 367 |
# $value =~ s/"/\\"/gi; # escape double quotes
|
- |
|
| 368 |
# # pound sign indicates start of a comment and thus loses part
|
- |
|
| 369 |
# # of strings. Surrounding it by double quotes in next statement
|
- |
|
| 370 |
# # allows
|
- |
|
| 371 |
# $value = '"' . $value . '"' if ( $value =~ m/[#:]/ );
|
- |
|
| 372 |
# return $value;
|
- |
|
| 373 |
#}
|
- |
|
| 374 |
|
- |
|
| 375 |
#######################################################
|
- |
|
| 376 |
#
|
- |
|
| 377 |
# hashToYAML( $hashRef, $indent )
|
- |
|
| 378 |
#
|
- |
|
| 379 |
# Converts a hash to a YAML string
|
- |
|
| 380 |
#
|
- |
|
| 381 |
# NOTE: This routine recursively calls itself for every level
|
- |
|
| 382 |
# in the hash
|
- |
|
| 383 |
#
|
- |
|
| 384 |
# Parameters
|
- |
|
| 385 |
# $hashref - reference (address) of a hash
|
- |
|
| 386 |
# $indent - current indent level, defaults to 0
|
- |
|
| 387 |
#
|
- |
|
| 388 |
# Even though there are some very good libraries that do this
|
- |
|
| 389 |
# I chose to hand-code it so sysinfo can be run with no libraries
|
- |
|
| 390 |
# loaded. I chose to NOT do a full implementation, so special chars
|
- |
|
| 391 |
# that would normally be escaped are not in here.
|
- |
|
| 392 |
# However, I followed all the RFC for the values that were given, so
|
- |
|
| 393 |
# assume any YAML reader can parse this
|
- |
|
| 394 |
# NOTE: YAML appears to give a resulting file 1/3 smaller than the above
|
- |
|
| 395 |
# XML, and compresses down in like manner
|
- |
|
| 396 |
#
|
- |
|
| 397 |
# DEPRECATED AS OF VERSION 3.3.0
|
- |
|
| 398 |
# uses YAML::Tiny
|
- |
|
| 399 |
#
|
- |
|
| 400 |
#######################################################
|
- |
|
| 401 |
#sub hashToYAML {
|
- |
|
| 402 |
# my ($hashRef, $indent) = @_;
|
- |
|
| 403 |
# $indent = 0 unless $indent; # default to 0 if not defined
|
- |
|
| 404 |
#
|
- |
|
| 405 |
# my $output; # where the output is stored
|
- |
|
| 406 |
# foreach my $key ( keys %$hashRef ) { # for each key in the current reference
|
- |
|
| 407 |
# print "Looking at $key\n" if $TESTING > 3;
|
- |
|
| 408 |
# # see http://www.perlmonks.org/?node_id=175651 for isa function
|
- |
|
| 409 |
# if ( UNIVERSAL::isa( $$hashRef{$key}, 'HASH' ) ) { # is the value another hash?
|
- |
|
| 410 |
# # NOTE: unlike xml, indentation is NOT optional in YAML, so the following line verifies $indentlevel is non-zero
|
- |
|
| 411 |
# # and, if it is, uses a default 3 character indentation
|
- |
|
| 412 |
# $output .= (' ' x $indent ) . &escapeForYAML($key) . ":\n" . # key, plus colon, plus newline
|
- |
|
| 413 |
# &hashToYAML( $$hashRef{$key}, $indent+($indentLevel ? $indentLevel : 3) ) . # add results of recursive call
|
- |
|
| 414 |
# "\n";
|
- |
|
| 415 |
# } elsif ( UNIVERSAL::isa( $$hashRef{$key}, 'ARRAY' ) ) { # is it an array? ignore it
|
- |
|
| 416 |
# } else { # it is a scalar, so just do <key>value</key>
|
- |
|
| 417 |
# $output .= (' ' x $indent ) . &escapeForYAML($key) . ': ' . &escapeForYAML($$hashRef{$key}) . "\n";
|
- |
|
| 418 |
# }
|
- |
|
| 419 |
# }
|
- |
|
| 420 |
# return $output;
|
- |
|
| 421 |
#}
|
- |
|
| 422 |
|
392 |
|
| - |
|
393 |
sub validatePermission {
|
| - |
|
394 |
my $file = shift;
|
| - |
|
395 |
# on Windows system, we can not look for ownership and exect status
|
| - |
|
396 |
return '' if lc $^O eq 'mswin32';
|
| - |
|
397 |
&logIt( $config, 3, "Entering validatePermission with $file" );
|
| - |
|
398 |
return "$file - Not a file" unless -f $file;
|
| - |
|
399 |
# in test mode, do not check permissions
|
| - |
|
400 |
return '' if $TESTING;
|
| - |
|
401 |
my $return;
|
| - |
|
402 |
# must be owned by root
|
| - |
|
403 |
my $owner = (stat($file))[4];
|
| - |
|
404 |
# print "\tOwner = $owner\n";
|
| - |
|
405 |
$return .= " - Bad Owner [$owner]" if $owner;
|
| - |
|
406 |
# must not have any permissions for group or world
|
| - |
|
407 |
# ie, 0700 or less
|
| - |
|
408 |
my $mode = sprintf( '%04o', (stat($file))[2] & 07777 );
|
| - |
|
409 |
# print "\tMode = $mode\n";
|
| - |
|
410 |
$return .= " - Bad Permission [$mode]" unless $mode =~ m/0.00/;
|
| - |
|
411 |
&logIt( $config, 4, "validatePermission: $file permissions check result: " . ($return ? $return : "OK") );
|
| - |
|
412 |
return $return ? $file . $return : '';
|
| - |
|
413 |
}
|
| 423 |
|
414 |
|
| 424 |
#######################################################
|
415 |
#######################################################
|
| 425 |
#
|
416 |
#
|
| 426 |
# tabDelimitedToHash ($hashRef, $tabdelim)
|
417 |
# tabDelimitedToHash ($hashRef, $tabdelim)
|
| 427 |
#
|
418 |
#
|
| Line 436... |
Line 427... |
| 436 |
#
|
427 |
#
|
| 437 |
#
|
428 |
#
|
| 438 |
#######################################################
|
429 |
#######################################################
|
| 439 |
sub tabDelimitedToHash {
|
430 |
sub tabDelimitedToHash {
|
| 440 |
my ($hashRef, $tabdelim) = @_;
|
431 |
my ($hashRef, $tabdelim) = @_;
|
| 441 |
&logIt( \%configuration, 3, "Entering tabDelimitedToHash" );
|
432 |
&logIt( $config, 3, "Entering tabDelimitedToHash" );
|
| 442 |
|
433 |
|
| 443 |
utf8::encode( $tabdelim ); # ensure this is all utf8, convert if necessary
|
434 |
utf8::encode( $tabdelim ); # ensure this is all utf8, convert if necessary
|
| 444 |
|
435 |
|
| 445 |
foreach my $line ( split( "\n", $tabdelim ) ) { # split on newlines, then process each line in turn
|
436 |
foreach my $line ( split( "\n", $tabdelim ) ) { # split on newlines, then process each line in turn
|
| 446 |
$line =~ s/'/\\'/gi; # escape single quotes
|
437 |
$line =~ s/'/\\'/gi; # escape single quotes
|
| Line 457... |
Line 448... |
| 457 |
#print STDERR "$command\n";
|
448 |
#print STDERR "$command\n";
|
| 458 |
eval $command; # eval the string to make the actual assignment
|
449 |
eval $command; # eval the string to make the actual assignment
|
| 459 |
}
|
450 |
}
|
| 460 |
}
|
451 |
}
|
| 461 |
|
452 |
|
| - |
|
453 |
|
| - |
|
454 |
|
| 462 |
#######################################################
|
455 |
#######################################################
|
| 463 |
#
|
456 |
#
|
| 464 |
# validatePermission ( $file )
|
457 |
# getModulesFromDir( $moduleDir )
|
| 465 |
#
|
458 |
#
|
| 466 |
# Checks that file is owned by root, and has permission
|
459 |
# Retrieves a list of valid module files from the specified
|
| - |
|
460 |
# module directory
|
| - |
|
461 |
#
|
| 467 |
# 0700 or less
|
462 |
# Parameters
|
| - |
|
463 |
# $moduleDir - full path to directory containing modules
|
| 468 |
#
|
464 |
#
|
| - |
|
465 |
# Returns
|
| 469 |
# Returns empty string on success, error message
|
466 |
# Reference to an array of valid module file paths
|
| - |
|
467 |
#
|
| 470 |
# on failure
|
468 |
# Module files must:
|
| - |
|
469 |
# - Have names consisting only of alphanumerics and underscores
|
| - |
|
470 |
# - Begin with an alphanumeric character
|
| - |
|
471 |
# - Pass permission validation (owned by root, 0700 or less)
|
| 471 |
#
|
472 |
#
|
| 472 |
#######################################################
|
473 |
#######################################################
|
| 473 |
|
- |
|
| 474 |
sub validatePermission {
|
- |
|
| 475 |
my $file = shift;
|
- |
|
| 476 |
# on Windows system, we can not look for ownership and exect status
|
- |
|
| 477 |
return '' if lc $^O eq 'mswin32';
|
- |
|
| 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;
|
- |
|
| 482 |
my $return;
|
- |
|
| 483 |
# must be owned by root
|
- |
|
| 484 |
my $owner = (stat($file))[4];
|
- |
|
| 485 |
# print "\tOwner = $owner\n";
|
- |
|
| 486 |
$return .= " - Bad Owner [$owner]" if $owner;
|
- |
|
| 487 |
# must not have any permissions for group or world
|
- |
|
| 488 |
# ie, 0700 or less
|
- |
|
| 489 |
my $mode = sprintf( '%04o', (stat($file))[2] & 07777 );
|
- |
|
| 490 |
# print "\tMode = $mode\n";
|
- |
|
| 491 |
$return .= " - Bad Permission [$mode]" unless $mode =~ m/0.00/;
|
- |
|
| 492 |
return $return ? $file . $return : '';
|
- |
|
| 493 |
}
|
- |
|
| 494 |
|
- |
|
| 495 |
sub getModulesFromDir {
|
474 |
sub getModulesFromDir {
|
| 496 |
my $moduleDir = shift;
|
475 |
my $moduleDir = shift;
|
| 497 |
$moduleDir .= '/' unless substr( $moduleDir, -1 ) eq '/';
|
476 |
$moduleDir .= '/' unless substr( $moduleDir, -1 ) eq '/';
|
| 498 |
# open the module directory
|
477 |
# open the module directory
|
| 499 |
return unless -d $moduleDir;
|
478 |
return unless -d $moduleDir;
|
| Line 501... |
Line 480... |
| 501 |
# and get all files which are nothing but alpha-numerics and underscores (must begin with alpha-numeric)
|
480 |
# and get all files which are nothing but alpha-numerics and underscores (must begin with alpha-numeric)
|
| 502 |
# ignore anything else, including directories
|
481 |
# ignore anything else, including directories
|
| 503 |
# then, prepend the directory name (map), then validate they are ready to be used (validatePermissions)
|
482 |
# 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 );
|
483 |
my @modules = grep{ ! &validatePermission( $_ ) } map { "$moduleDir$_" } grep { /^[a-zA-Z0-9][a-zA-Z0-9_]*$/ } readdir( $dh );
|
| 505 |
closedir $dh;
|
484 |
closedir $dh;
|
| - |
|
485 |
&logIt( $config, 4, "getModulesFromDir: Modules found in $moduleDir: " . join(", ", @modules) );
|
| 506 |
return \@modules;
|
486 |
return \@modules;
|
| 507 |
|
487 |
|
| 508 |
}
|
488 |
}
|
| 509 |
|
489 |
|
| 510 |
#######################################################
|
490 |
#######################################################
|
| Line 526... |
Line 506... |
| 526 |
# on failure, the returned output of the script is assumed to be an error message
|
506 |
# on failure, the returned output of the script is assumed to be an error message
|
| 527 |
# and is displayed on STDERR
|
507 |
# and is displayed on STDERR
|
| 528 |
#######################################################
|
508 |
#######################################################
|
| 529 |
sub ProcessModules {
|
509 |
sub ProcessModules {
|
| 530 |
my ( $system, $moduleDir ) = @_;
|
510 |
my ( $system, $moduleDir ) = @_;
|
| 531 |
&logIt( \%configuration, 3, "Entering processModules" );
|
511 |
&logIt( $config, 3, "Entering processModules using $moduleDir" );
|
| 532 |
foreach my $modFile ( sort @{ &getModulesFromDir( $moduleDir ) } ) { # for each valid script
|
512 |
foreach my $modFile ( sort @{ &getModulesFromDir( $moduleDir ) } ) { # for each valid script
|
| 533 |
next unless -f $modFile; # skip any directories, symbolic links, etc...
|
513 |
next unless -f $modFile; # skip any directories, symbolic links, etc...
|
| 534 |
my $output = do $modFile;
|
514 |
my $output = do $modFile;
|
| 535 |
&logIt( \%configuration, 4, "Output of module $modFile is\n$output\n" );
|
515 |
&logIt( $config, 4, "Output of module $modFile is\n$output\n" );
|
| 536 |
if ( defined $output && $output ) {
|
516 |
if ( defined $output && $output ) {
|
| 537 |
if ( substr( $output, 1,6) eq 'error:' ) { # we have an error
|
517 |
if ( substr( $output, 1,6) eq 'error:' ) { # we have an error
|
| 538 |
&logIt( \%configuration, 1, $output );
|
518 |
&logIt( $config, 1, $output );
|
| 539 |
} else {
|
519 |
} else {
|
| 540 |
&tabDelimitedToHash( $system, $output );
|
520 |
&tabDelimitedToHash( $system, $output );
|
| 541 |
}
|
521 |
}
|
| 542 |
} else {
|
522 |
} else {
|
| 543 |
print "Script $modFile failed to compile with error\n$@\n";
|
523 |
print "Script $modFile failed to compile with error\n$@\n";
|
| 544 |
}
|
524 |
}
|
| 545 |
&logIt( \%configuration, 3, "Processing module $moduleDir$modFile");
|
525 |
&logIt( $config, 3, "Processing module $moduleDir$modFile");
|
| 546 |
} # foreach
|
526 |
} # foreach
|
| 547 |
# add sysinfo-client (me) to the software list, since we're obviously installed
|
527 |
# add sysinfo-client (me) to the software list, since we're obviously installed
|
| 548 |
&tabDelimitedToHash( $system, "software\tsysinfo-client\tversion\t$main::VERSION\n" );
|
528 |
&tabDelimitedToHash( $system, "software\tsysinfo-client\tversion\t$main::VERSION\n" );
|
| 549 |
}
|
529 |
}
|
| 550 |
|
530 |
|
| 551 |
sub getDMIDecode {
|
- |
|
| 552 |
my ( $key, $type ) = @_;
|
- |
|
| 553 |
my $command = 'dmidecode ';
|
- |
|
| 554 |
$command .= "-t $type " if $type;
|
531 |
#######################################################
|
| 555 |
$command .= " | grep -i '$key'";
|
- |
|
| 556 |
my $value = `$command`;
|
532 |
# initReport( $config )
|
| 557 |
chomp $value;
|
- |
|
| 558 |
if ( $value =~ m/:\s*(.*)\s*$/ ) {
|
- |
|
| 559 |
return $1;
|
- |
|
| 560 |
} else {
|
- |
|
| 561 |
return '';
|
- |
|
| 562 |
}
|
- |
|
| 563 |
}
|
533 |
#
|
| - |
|
534 |
# Initialize the report structure with configuration data
|
| 564 |
|
535 |
#
|
| 565 |
sub interactiveConfig {
|
536 |
# Parameters
|
| 566 |
my $config = shift;
|
- |
|
| 567 |
$config->{'moduleDirs'} = $config->{'moduleDirs'}[0];
|
- |
|
| 568 |
$config->{'scriptDirs'} = $config->{'scriptDirs'}[0];
|
537 |
# $config - reference to configuration hash
|
| 569 |
$config->{'UUID'} = getDMIDecode( 'uuid', 'system' ) unless $config->{'UUID'};
|
- |
|
| 570 |
$config->{'serialNumber'} = getDMIDecode( 'serial number', 'system' ) unless $config->{'serialNumber'};
|
- |
|
| 571 |
|
538 |
#
|
| 572 |
my %menu = (
|
539 |
# Returns
|
| 573 |
1 => {'prompt' => 'Host Name', 'key' => 'hostname' },
|
- |
|
| 574 |
2 => {'prompt' => 'Client Name', 'key' => 'clientName' },
|
- |
|
| 575 |
3 => {'prompt' => 'Serial Number', 'key' => 'serialNumber' },
|
- |
|
| 576 |
4 => {'prompt' => 'UUID', 'key' => 'UUID' },
|
540 |
# Reference to initialized report hash
|
| 577 |
5 => {'prompt' => 'Modules Directory', 'key' => 'moduleDirs' },
|
- |
|
| 578 |
6 => {'prompt' => 'Scripts Directory', 'key' => 'scriptDirs' }
|
- |
|
| 579 |
);
|
541 |
#
|
| 580 |
my $choice = 'quit';
|
- |
|
| 581 |
while ( $choice ) {
|
- |
|
| 582 |
foreach my $menuItem ( sort keys %menu ) {
|
542 |
# Creates the initial report structure with:
|
| 583 |
print "$menuItem\. " . $menu{$menuItem}{'prompt'} . ': ' . $config->{$menu{$menuItem}{'key'}} . "\n";
|
- |
|
| 584 |
}
|
- |
|
| 585 |
print "Enter Menu Item to change, or press Enter to proceed ";
|
543 |
# - Report metadata (version, date, client)
|
| 586 |
$choice = <>;
|
- |
|
| 587 |
chomp $choice;
|
- |
|
| 588 |
last unless $choice;
|
- |
|
| 589 |
print $menu{$choice}{'prompt'} . ' [' . $config->{$menu{$choice}{'key'}} . '] : ';
|
- |
|
| 590 |
my $value = <>;
|
- |
|
| 591 |
chomp $value;
|
- |
|
| 592 |
$config->{$menu{$choice}{'key'}} = $value if ($value);
|
544 |
# - System information (hostname, serial, UUID)
|
| 593 |
}
|
- |
|
| 594 |
$config->{'moduleDirs'} = [ $config->{'moduleDirs'} ];
|
545 |
# - Additional configuration values (excluding moduleDirs,
|
| 595 |
$config->{'scriptDirs'} = [ $config->{'scriptDirs'} ];
|
546 |
# transports, scriptDirs, and logging)
|
| 596 |
return $config;
|
- |
|
| 597 |
}
|
547 |
#
|
| 598 |
|
- |
|
| 599 |
# Initialize the report with some stuff from the program itself and the configuration file
|
548 |
#######################################################
|
| 600 |
sub initReport {
|
549 |
sub initReport {
|
| 601 |
my $config = shift;
|
550 |
my $config = shift;
|
| 602 |
my %ignore = ( # list of config keys to ignore. Using hash for fast lookup
|
551 |
my %ignore = ( # list of config keys to ignore. Using hash for fast lookup
|
| 603 |
'moduleDirs' => 1,
|
552 |
'moduleDirs' => 1,
|
| 604 |
'transports' => 1,
|
553 |
'transports' => 1,
|
| Line 618... |
Line 567... |
| 618 |
$report->{'system'}->{$key} = $config->{$key}; # simply copy to the system part of the report
|
567 |
$report->{'system'}->{$key} = $config->{$key}; # simply copy to the system part of the report
|
| 619 |
}
|
568 |
}
|
| 620 |
return $report;
|
569 |
return $report;
|
| 621 |
}
|
570 |
}
|
| 622 |
|
571 |
|
| - |
|
572 |
#######################################################
|
| - |
|
573 |
#
|
| - |
|
574 |
# detectConfigType( $filename )
|
| - |
|
575 |
#
|
| - |
|
576 |
# Detects the configuration file type by examining content
|
| - |
|
577 |
# json and DataTransport have unique starting lines
|
| - |
|
578 |
# yaml will usually start with --- or key: value, but may be
|
| - |
|
579 |
# comments in the first few lines, so checks up to 5 lines
|
| - |
|
580 |
#
|
| - |
|
581 |
# Parameters:
|
| - |
|
582 |
# $lines - array reference containing lines of configuration content
|
| - |
|
583 |
#
|
| - |
|
584 |
# Returns:
|
| - |
|
585 |
# 'yaml', 'json', 'datatransport', or undef
|
| - |
|
586 |
#
|
| - |
|
587 |
#######################################################
|
| - |
|
588 |
sub detectConfigType {
|
| - |
|
589 |
my $lines = shift;
|
| - |
|
590 |
# first look at first line for unique identifiers. First line comments
|
| - |
|
591 |
# may contain type info
|
| - |
|
592 |
if ( $lines->[0] =~ m/^.*(yaml|datatransport).*/i ) {
|
| - |
|
593 |
&logIt( $config, 3, "Configuration file type hint found in first line comment" );
|
| - |
|
594 |
return lc $1;
|
| - |
|
595 |
}
|
| - |
|
596 |
# look through first five lines, there should be some indicator
|
| - |
|
597 |
for ( my $line = 0; $line < 5 && $lines->[$line]; $line++ ) {
|
| - |
|
598 |
return 'yaml' if $lines->[$line] =~ /^#.*YAML/;
|
| - |
|
599 |
return 'yaml' if $lines->[$line] =~ /^---/; # YAML document start
|
| - |
|
600 |
return 'yaml' if $lines->[$line] =~ /^\s*\w+:\s*/; # YAML key: value
|
| - |
|
601 |
return 'json' if $lines->[$line] =~ /^#.*JSON/; # skip comments
|
| - |
|
602 |
return 'json' if $lines->[$line] =~ /^\s*[{\[]/; # JSON starts with { or [
|
| - |
|
603 |
}
|
| - |
|
604 |
return undef;
|
| - |
|
605 |
}
|
| - |
|
606 |
|
| - |
|
607 |
#######################################################
|
| - |
|
608 |
#
|
| - |
|
609 |
# loadConfig( $confStr, $type )
|
| - |
|
610 |
#
|
| - |
|
611 |
# Load configuration file based on specified or detected type
|
| - |
|
612 |
# adds 'dataType' key to returned hashref indicating type used
|
| - |
|
613 |
#
|
| - |
|
614 |
# Parameters:
|
| - |
|
615 |
# $confStr - configuration content as an arrayref of string
|
| - |
|
616 |
# $type - optional type: 'yaml', 'json', 'datatransport'
|
| - |
|
617 |
#
|
| - |
|
618 |
# Returns:
|
| - |
|
619 |
# hashref of configuration
|
| - |
|
620 |
#
|
| - |
|
621 |
#######################################################
|
| - |
|
622 |
sub loadConfig {
|
| - |
|
623 |
my ($confStr, $type) = @_;
|
| - |
|
624 |
|
| - |
|
625 |
my $return = {};
|
| - |
|
626 |
|
| 623 |
# simple display if --help is passed
|
627 |
# Auto-detect if type not specified
|
| - |
|
628 |
$type = &detectConfigType($confStr) unless $type;
|
| - |
|
629 |
|
| - |
|
630 |
die "Cannot determine configuration file type\n" unless $type;
|
| - |
|
631 |
|
| - |
|
632 |
$type = lc($type); # normalize to lowercase
|
| - |
|
633 |
$confStr = join("\n", @$confStr); # join arrayref into single string for processing
|
| - |
|
634 |
|
| - |
|
635 |
if ($type eq 'datatransport') {
|
| - |
|
636 |
# Try to load DataTransport module
|
| - |
|
637 |
eval { require DataTransport; };
|
| - |
|
638 |
if ($@) {
|
| - |
|
639 |
die "DataTransport module not available: $@\n";
|
| - |
|
640 |
}
|
| - |
|
641 |
|
| - |
|
642 |
my $dt = DataTransport->new();
|
| - |
|
643 |
$return = $dt->decode($confStr);
|
| - |
|
644 |
die "Failed to read DataTransport content\n" unless $return;
|
| - |
|
645 |
} elsif ($type eq 'json') {
|
| - |
|
646 |
# Try to load JSON module
|
| - |
|
647 |
eval { require JSON; };
|
| - |
|
648 |
if ($@) {
|
| - |
|
649 |
die "JSON module not available: $@\n";
|
| - |
|
650 |
}
|
| - |
|
651 |
$return = JSON::decode_json($confStr);
|
| - |
|
652 |
} elsif ($type eq 'yaml') {
|
| - |
|
653 |
# Use YAML::Tiny directly for single file loading
|
| - |
|
654 |
eval { require YAML::Tiny; };
|
| - |
|
655 |
if ($@) {
|
| - |
|
656 |
die "YAML::Tiny module not available: $@\n";
|
| - |
|
657 |
}
|
| - |
|
658 |
|
| - |
|
659 |
$return = YAML::Tiny->read_string($confStr);
|
| - |
|
660 |
die "Failed to read YAML content\n" unless $return;
|
| - |
|
661 |
$return = $return->[0]; # only return the first document
|
| - |
|
662 |
} else {
|
| - |
|
663 |
die "Unknown configuration type: $type\n";
|
| - |
|
664 |
}
|
| - |
|
665 |
$return->{'dataType'} = $type; # store the type used
|
| - |
|
666 |
return $return;
|
| - |
|
667 |
}
|
| - |
|
668 |
|
| - |
|
669 |
########################################################
|
| - |
|
670 |
# hashToReportString( $report, $outputType )
|
| - |
|
671 |
#
|
| - |
|
672 |
# Converts report hash to specified output format string
|
| - |
|
673 |
## Parameters:
|
| - |
|
674 |
# $report - reference to report hash
|
| - |
|
675 |
# $outputType - output format: 'yaml', 'json', 'datatransport'
|
| - |
|
676 |
# Returns:
|
| - |
|
677 |
# string containing report in specified format
|
| - |
|
678 |
########################################################
|
| - |
|
679 |
sub hashToReportString {
|
| - |
|
680 |
my ( $report, $outputType ) = @_;
|
| - |
|
681 |
&logIt( $config, 3, "Entering hashToReportString with outputType $outputType" );
|
| - |
|
682 |
my $output;
|
| - |
|
683 |
if ( $outputType eq 'yaml' ) {
|
| - |
|
684 |
eval { require YAML::Tiny; };
|
| - |
|
685 |
if ($@) {
|
| - |
|
686 |
die "YAML::Tiny module not available: $@\n";
|
| - |
|
687 |
}
|
| - |
|
688 |
$output = YAML::Tiny->new( $report )->write_string();
|
| - |
|
689 |
} elsif ( $outputType eq 'json' ) {
|
| - |
|
690 |
eval { require JSON; };
|
| - |
|
691 |
if ($@) {
|
| - |
|
692 |
die "JSON module not available: $@\n";
|
| - |
|
693 |
}
|
| - |
|
694 |
$output = JSON::encode_json( $report );
|
| - |
|
695 |
} elsif ( $outputType eq 'datatransport' ) {
|
| - |
|
696 |
eval { require DataTransport; };
|
| - |
|
697 |
if ($@) {
|
| - |
|
698 |
die "DataTransport module not available: $@\n";
|
| - |
|
699 |
}
|
| - |
|
700 |
my $dt = DataTransport->new();
|
| - |
|
701 |
$output = $dt->encode( $report );
|
| - |
|
702 |
} else {
|
| - |
|
703 |
die "Unknown output type: $outputType\n";
|
| - |
|
704 |
}
|
| - |
|
705 |
return $output;
|
| - |
|
706 |
}
|
| - |
|
707 |
|
| - |
|
708 |
#######################################################
|
| - |
|
709 |
#
|
| - |
|
710 |
# help()
|
| - |
|
711 |
#
|
| - |
|
712 |
# Display help message showing command-line options
|
| - |
|
713 |
#
|
| - |
|
714 |
# Parameters
|
| - |
|
715 |
# None
|
| - |
|
716 |
#
|
| - |
|
717 |
# Returns
|
| - |
|
718 |
# None (prints to STDOUT)
|
| - |
|
719 |
#
|
| - |
|
720 |
# Shows program version and available command-line options:
|
| - |
|
721 |
# -f, --config : Specify configuration file path
|
| - |
|
722 |
# --configtype : Specify config format (yaml/json/datatransport)
|
| - |
|
723 |
# --version : Display version
|
| - |
|
724 |
# --help : Display help message
|
| - |
|
725 |
# -p, --periodic : Run periodic modules
|
| - |
|
726 |
# -t, --test : Test mode (output to /tmp)
|
| - |
|
727 |
#
|
| - |
|
728 |
# Note: For interactive configuration, use sysinfo-client-interactive
|
| - |
|
729 |
#
|
| - |
|
730 |
#######################################################
|
| 624 |
sub help {
|
731 |
sub help {
|
| 625 |
use File::Basename;
|
732 |
use File::Basename;
|
| 626 |
print basename($0) . " $VERSION (data $DATA_VERSION)\n";
|
733 |
print basename($0) . " $VERSION (data $DATA_VERSION)\n";
|
| 627 |
print <<END
|
734 |
print <<END
|
| 628 |
$0 [options]
|
735 |
$0 [options]
|
| - |
|
736 |
|
| - |
|
737 |
For interactive configuration, use sysinfo-client-interactive instead.
|
| - |
|
738 |
|
| 629 |
Options:
|
739 |
Options:
|
| 630 |
-i,
|
- |
|
| 631 |
--interactive - do not read configuration file
|
- |
|
| 632 |
--version - display version and exit
|
740 |
--version - display version and exit
|
| 633 |
-c,
|
- |
|
| 634 |
--client='xxx' - Client name for interactive mode
|
741 |
--help - display this help message
|
| 635 |
-s,
|
742 |
-f,
|
| 636 |
--serial='xxx' - Serial Number for interactive mode
|
- |
|
| 637 |
-h,
|
- |
|
| 638 |
--hostname='xxx' - override hostname
|
- |
|
| 639 |
-m,
|
- |
|
| 640 |
--modules=/path/ - override path to modules
|
743 |
--config=/path/ - path to configuration file (overrides default search)
|
| 641 |
--scripts=/path/ - override path to scripts
|
744 |
--configtype=xxx - config file type: yaml, json, or datatransport (autodetects if not specified)
|
| 642 |
-p,
|
745 |
-p,
|
| 643 |
--periodic - runs modules designed to be run only weekly, monthly, etc...
|
746 |
--periodic - runs modules designed to be run only weekly, monthly, etc...
|
| 644 |
-t,
|
747 |
-t,
|
| 645 |
--test - If non-zero, runs but places output in /tmp
|
748 |
--test - If set, runs but places output in /tmp
|
| 646 |
END
|
749 |
END
|
| 647 |
}
|
750 |
}
|
| 648 |
|
751 |
|
| 649 |
|
752 |
|
| 650 |
# handle any command line parameters that may have been passed in
|
753 |
# Process command line options
|
| 651 |
|
- |
|
| 652 |
GetOptions (
|
754 |
GetOptions (
|
| 653 |
'interactive|i' => \$interactive, # ask questions instead of using config file
|
- |
|
| 654 |
'periodic|p' => \$periodic, # will do modules which are marked as periodic
|
755 |
'periodic|p' => \$periodic, # will do modules which are marked as periodic
|
| - |
|
756 |
'config|f=s' => \$configFile, # path to configuration file
|
| - |
|
757 |
'configtype=s' => \$configType, # config file type: yaml, json, datatransport
|
| - |
|
758 |
'outputType|o=s' => \$outputType, # output type: xml, yaml
|
| 655 |
'help|h' => \$help,
|
759 |
'help' => \$help,
|
| 656 |
'version' => \$version,
|
760 |
'version' => \$version,
|
| 657 |
'client|c=s' => \$configuration{clientName},
|
- |
|
| 658 |
'serial|s=s' => \$configuration{serialNumber},
|
- |
|
| 659 |
'hostname=s' => \$configuration{hostname},
|
- |
|
| 660 |
'modules|m=s' => \$configuration{moduleDirs},
|
- |
|
| 661 |
'scripts=s' => \$configuration{scriptDirs},
|
- |
|
| 662 |
'test|t=s' => \$TESTING
|
761 |
'test|t' => \$TESTING
|
| 663 |
) or die "Error parsing command line\n";
|
762 |
) or die "Error parsing command line\n";
|
| 664 |
|
763 |
|
| 665 |
|
- |
|
| 666 |
if ( $help ) { &help() ; exit; }
|
764 |
if ( $help ) { &help() ; exit; }
|
| 667 |
if ( $version ) { use File::Basename; print basename($0) . " $VERSION (data $DATA_VERSION)\n"; exit; }
|
765 |
if ( $version ) { use File::Basename; print basename($0) . " $VERSION (data $DATA_VERSION)\n"; exit; }
|
| 668 |
|
766 |
|
| 669 |
if ( $interactive ) {
|
- |
|
| 670 |
%configuration = %{ &interactiveConfig( \%configuration ) };
|
- |
|
| 671 |
} else {
|
- |
|
| 672 |
# load the configuration file
|
767 |
# if periodic flag set, create the override file
|
| 673 |
%configuration = %{ &loadConfigurationFile( \$configurationFile, @confFileSearchPath) };
|
- |
|
| 674 |
}
|
- |
|
| 675 |
|
- |
|
| 676 |
`touch $periodicOverrideFile` if $periodic; # tells periodic modules to run
|
768 |
`touch $periodicOverrideFile` if $periodic; # tells periodic modules to run
|
| 677 |
|
769 |
|
| 678 |
#die Dumper (\%configuration );
|
770 |
$configFile //= 'sysinfo-client.yaml'; # default configuration file name
|
| 679 |
|
- |
|
| - |
|
771 |
# load the configuration file. Note, loadConfigurationFile returns an arrayref of lines
|
| 680 |
# user did not define a serial number, so make something up
|
772 |
# then loadConfig processes that based on type
|
| 681 |
$configuration{'serialNumber'} = '' unless $configuration{'serialNumber'};
|
773 |
$config = loadConfig( loadConfigurationFile( $configFile ), $configType);
|
| - |
|
774 |
|
| 682 |
# oops, no client name (required) so tell them and exit
|
775 |
# oops, no client name (required) so tell them and exit
|
| 683 |
die "No client name defined in $configurationFile" unless $configuration{'clientName'};
|
776 |
die "No client name defined in $configFile" unless $config->{'clientName'};
|
| 684 |
|
777 |
|
| 685 |
&logIt( \%configuration, 0, 'Starting sysinfo Run' );
|
778 |
# clean up some missing values
|
| 686 |
&logIt( \%configuration, 3, "Configuration is\n" . Data::Dumper->Dump( [\%configuration], [ qw($configuration) ] ) );
|
779 |
# output type is either what user passed on command line, or what is in config file, or defaults to yaml
|
| - |
|
780 |
# the config->{'dataType'} is set by loadConfig, so we can use that as last resort
|
| - |
|
781 |
$config->{'outputType'} //= $outputType //= $config->{'dataType'} //= 'yaml'; # default to yaml output
|
| - |
|
782 |
# user did not define a serial number, so make something up. Just take an md5sum of hostname-clientname
|
| - |
|
783 |
$config->{'serialNumber'} //= $config->{UUID} ? $config->{UUID} : `echo \`$config->{'hostname'}\`-$config->{'clientName'} | md5sum | awk '{print \$1}'`;
|
| - |
|
784 |
chomp $config->{'serialNumber'};
|
| 687 |
|
785 |
|
| - |
|
786 |
&logIt( $config, 0, 'Starting sysinfo Run' );
|
| 688 |
$TESTING = $configuration{'TESTING'} if defined $configuration{'TESTING'};
|
787 |
&logIt( $config, 3, "Configuration is\n" . Data::Dumper->Dump( [$config], [ qw($config) ] ) );
|
| 689 |
|
788 |
|
| - |
|
789 |
$TESTING //= $config->{'TESTING'} //= 0; # default to 0 if not defined
|
| 690 |
&logIt( \%configuration, 0, "Testing => $TESTING" ) if $TESTING;
|
790 |
&logIt( $config, 0, "Testing => $TESTING" ) if $TESTING;
|
| 691 |
|
791 |
|
| 692 |
# hash reference that will store all info we are going to send to the server
|
792 |
# hash reference that will store all info we are going to send to the server
|
| - |
|
793 |
# initialize it with some basic info from configuration file
|
| 693 |
my $System = &initReport( \%configuration );
|
794 |
my $report = &initReport( $config );
|
| 694 |
|
- |
|
| 695 |
&logIt( \%configuration, 3, "Initial System\n" . Data::Dumper->Dump( [$System], [qw( $System )] ) );
|
795 |
&logIt( $config, 3, "Initial System\n" . Data::Dumper->Dump( [$report], [qw( $report )] ) );
|
| 696 |
|
796 |
|
| 697 |
# process any modules in the system
|
797 |
# process any modules in the system
|
| 698 |
foreach my $moduleDir ( @{$configuration{'moduleDirs'}} ) {
|
798 |
foreach my $moduleDir ( @{$config->{'moduleDirs'}} ) {
|
| 699 |
&logIt( \%configuration, 3, "Processing modules from $moduleDir" );
|
799 |
&logIt( $config, 3, "Processing modules from $moduleDir" );
|
| 700 |
&ProcessModules( $System, "$moduleDir/" );
|
800 |
&ProcessModules( $report, "$moduleDir/" );
|
| 701 |
}
|
801 |
}
|
| 702 |
|
- |
|
| 703 |
&logIt( \%configuration, 4, "After processing modules\n" . Data::Dumper->Dump( [$System], [qw( $System )] ) );
|
802 |
&logIt( $config, 4, "After processing modules\n" . Data::Dumper->Dump( [$report], [qw( $report )] ) );
|
| 704 |
|
- |
|
| 705 |
my $out = sprintf( "#sysinfo: %s (data: %s) YAML\n", $VERSION, $DATA_VERSION ) . &Dump( $System );
|
- |
|
| 706 |
|
- |
|
| 707 |
&logIt( \%configuration, 4, 'At line number ' . __LINE__ . "\n" . Data::Dumper->Dump([$System],[qw($System)]) );
|
- |
|
| 708 |
|
803 |
|
| 709 |
# load some global values for use in the script, if required
|
804 |
# convert the report hash to the desired output type
|
| 710 |
my $globals = {
|
- |
|
| 711 |
'upload_type' => 'sysinfo',
|
- |
|
| 712 |
'data version' => $DATA_VERSION->normal,
|
- |
|
| 713 |
'report date' => $reportDate,
|
- |
|
| 714 |
'client name' => $configuration{'clientName'},
|
805 |
my $out = hashToReportString( $report, $config->{'outputType'} ); # test if we can convert to output type
|
| 715 |
'host name' => $configuration{'hostname'},
|
806 |
# add comment to top of file except for json
|
| 716 |
'serial number'=> $configuration{'serialNumber'},
|
807 |
$out = "# sysinfo " . $VERSION->normal . " (data " . $DATA_VERSION->normal . ") $config->{outputType}\n" . $out unless $config->{'outputType'} eq 'json';
|
| 717 |
'UUID' => $configuration{'UUID'}
|
- |
|
| 718 |
};
|
- |
|
| 719 |
|
- |
|
| 720 |
&logIt( \%configuration, 4, "Globals initialized\n" . Data::Dumper->Dump([$globals],[qw($globals)]) );
|
808 |
&logIt( $config, 4, 'At line number ' . __LINE__ . "\n" . $out );
|
| 721 |
|
809 |
|
| - |
|
810 |
# actually writing the report
|
| 722 |
if ( $TESTING ) {
|
811 |
if ( $TESTING ) {
|
| 723 |
&logIt( \%configuration, 0, "Sending report to sysinfo.testing.yaml" );
|
812 |
&logIt( $config, 0, "Sending report to sysinfo.testing.yaml" );
|
| 724 |
open DATA, ">/tmp/sysinfo.testing.yaml" or die "Could not write to /tmp/sysinfo.testing.yaml: $!\n";
|
813 |
open DATA, ">/tmp/sysinfo.testing.yaml" or die "Could not write to /tmp/sysinfo.testing.yaml: $!\n";
|
| 725 |
print DATA $out;
|
814 |
print DATA $out;
|
| 726 |
close DATA;
|
815 |
close DATA;
|
| 727 |
} else {
|
816 |
} else {
|
| - |
|
817 |
# load some global values for use in the script, if required
|
| - |
|
818 |
my $globals = {
|
| - |
|
819 |
'upload_type' => 'sysinfo',
|
| - |
|
820 |
'data version' => $DATA_VERSION->normal,
|
| 728 |
# and send the results to the server
|
821 |
'report date' => $reportDate,
|
| - |
|
822 |
'client name' => $config->{'clientName'},
|
| - |
|
823 |
'host name' => $config->{'hostname'},
|
| - |
|
824 |
'serial number'=> $config->{'serialNumber'},
|
| - |
|
825 |
'UUID' => $config->{'UUID'},
|
| - |
|
826 |
'fileType' => $config->{'outputType'}
|
| - |
|
827 |
};
|
| - |
|
828 |
|
| - |
|
829 |
&logIt( $config, 4, "Globals initialized\n" . Data::Dumper->Dump([$globals],[qw($globals)]) );
|
| 729 |
&logIt( \%configuration, 0, "Sending report to remote transport" );
|
830 |
&logIt( $config, 0, "Sending report to remote transport" );
|
| 730 |
if ( my $success = &sendResults( $globals, $configuration{'transports'}, $out, $configuration{'scriptDirs'} ) != 1 ) {
|
831 |
if ( my $success = &sendResults( $globals, $config->{'transports'}, $out, $config->{'scriptDirs'} ) != 1 ) {
|
| 731 |
&logIt( \%configuration, 0, "Error $success while sending report from $configuration{'hostname'}" );
|
832 |
&logIt( $config, 0, "Error $success while sending report from $config->{'hostname'}" );
|
| 732 |
}
|
833 |
}
|
| 733 |
}
|
834 |
}
|
| 734 |
|
835 |
|
| 735 |
unlink ( $periodicOverrideFile ) if -e $periodicOverrideFile;
|
836 |
unlink ( $periodicOverrideFile ) if -e $periodicOverrideFile;
|
| 736 |
&logIt( \%configuration, 0, 'Ending sysinfo Run' );
|
837 |
&logIt( $config, 0, 'Ending sysinfo Run' );
|
| 737 |
|
838 |
|
| 738 |
if ( $configuration{'postRunScript'}{'script name'} ) {
|
839 |
if ( $config->{'postRunScript'}{'script name'} ) {
|
| 739 |
my $script = $sourceDir . '/' . $configuration{'postRunScript'}{'script name'};
|
840 |
my $script = $sourceDir . '/' . $config->{'postRunScript'}{'script name'};
|
| 740 |
exec ( "$script $configurationFile" ) if -x $script;
|
841 |
exec ( "$script $configFile" ) if -x $script;
|
| 741 |
}
|
842 |
}
|
| 742 |
|
843 |
|
| 743 |
1;
|
844 |
1;
|