1 |
rodolico |
1 |
#! /usr/bin/perl -w
|
|
|
2 |
|
|
|
3 |
# process_sysinfo.pl
|
|
|
4 |
# Author: R. W. Rodolico
|
|
|
5 |
# Part of sysinfo package. This application takes the output from sysinfo.pl as input, parsing
|
|
|
6 |
# it and populating a database (see sysinfo.sql) with the results.
|
|
|
7 |
# Application will bypass anything up to the line beginning with [sysinfo version], allowing
|
|
|
8 |
# the output of sysinfo.pl to be e-mailed to a central server, where this app will correctly parse
|
|
|
9 |
# it.
|
|
|
10 |
# Application also has limited sensing capabilities, and will respond to certain conditions with a
|
|
|
11 |
# message on STDOUT indicating conditions, such as disk usage above an alarm value, new Operating Systems
|
|
|
12 |
# installed, etc... The goal is to allow this program to be called by a cron job, and the result (if any)
|
|
|
13 |
# returned in the cron specified e-mail.
|
|
|
14 |
# OVERVIEW:
|
|
|
15 |
# The input file contains two types of information, both beginning with a tag surrounded by square
|
|
|
16 |
# brackets. The single line data has the value for that tag immediately following the tag, ie:
|
|
|
17 |
# [tagname]value
|
|
|
18 |
# Multiline data has a beginning tag, then data following on a line by line basis, until the next
|
|
|
19 |
# tag (denoted by a tag name surrounded by square brackets) is reached. Every line between the first
|
|
|
20 |
# and subsequent tag are considered data for the previous tag, ie:
|
|
|
21 |
# [tagname1]
|
|
|
22 |
# value 1
|
|
|
23 |
# value 2
|
|
|
24 |
# [tagname2]
|
|
|
25 |
# thus, value 1, and value 2 are data for tagname1. Multiline data is stored in its own hash, and handled
|
|
|
26 |
# by separate routines.
|
|
|
27 |
# See bottom of this app for main routine; subroutines are in between here and the beginning of the main
|
|
|
28 |
# code
|
|
|
29 |
|
|
|
30 |
# Required Libraries:
|
|
|
31 |
# GenericSQL - Home Grown MySQL access routine, included with package
|
|
|
32 |
# GenericTemplates - Home Grown routine, included with package
|
|
|
33 |
# Logging - Home Grown logging routine, included with package
|
|
|
34 |
# Date (uses format and parse)
|
|
|
35 |
|
|
|
36 |
# Suggested Uses: See process_sysinfo.sh. This is an example that looks for all messages in a directory
|
|
|
37 |
# (in this case, a maildir directory), processes each e-mail, then moves the e-mail
|
|
|
38 |
# to the Processed folder. The advantage to this is that it can be called by a nightly
|
|
|
39 |
# cron job, with any warnings e-mailed back to the sysadmin
|
|
|
40 |
|
|
|
41 |
# version 0.10 20071103
|
|
|
42 |
# Ready for distribution. Database fairly normalized.
|
|
|
43 |
# version 0.11 20071104
|
|
|
44 |
# Bug fix, and set up for "uninstalled" software
|
|
|
45 |
# version 0.12 20071104
|
|
|
46 |
# Adjusted so it will ignore blank package lines, and will handle directories_to_watch
|
|
|
47 |
# version 0.13 20071120
|
|
|
48 |
# Fixed problem where a file that was NOT a valid sysinfo file would cause a run-away
|
|
|
49 |
# version 1.00 20071206
|
|
|
50 |
# Modified for new database format
|
|
|
51 |
# version 1.01 20071208
|
|
|
52 |
# Modified for report_date to be a date/time stamp instead of just the date
|
|
|
53 |
# version 2.0.0b 20081208
|
|
|
54 |
# converted to read XML data. Also uses older style data, but converts it to standard hash to mimic xml
|
|
|
55 |
# Adds requirement for libxml-simple-perl
|
|
|
56 |
# version 2.0.1 20090416
|
|
|
57 |
# Bug fix
|
|
|
58 |
|
|
|
59 |
|
|
|
60 |
my $VERSION = '2.0.1';
|
|
|
61 |
|
|
|
62 |
# only required if reports are sent from process_sysinfo. Not the norm.
|
|
|
63 |
my $iMailResults;
|
|
|
64 |
my $mailTo;
|
|
|
65 |
my $mailCC;
|
|
|
66 |
my $mailBCC;
|
|
|
67 |
my $mailServer;
|
|
|
68 |
my $mailServerPort;
|
|
|
69 |
my $mailFrom;
|
|
|
70 |
my $SENDMAIL;
|
|
|
71 |
my $DiskUsageAlert = 90; # will generate a warning if any disk has more than this percent capacity used
|
|
|
72 |
|
|
|
73 |
# information for the database
|
|
|
74 |
#my $DSN = 'DBI:mysql:camp'; # and, set up the database access
|
|
|
75 |
#my $DB_USER = 'test';
|
|
|
76 |
#my $DB_PASS = 'test';
|
|
|
77 |
|
|
|
78 |
#my $LIBRARIES = '/home/www/common-cgi/'; # where the extra libraries are stored
|
|
|
79 |
|
|
|
80 |
# global variables (not configuration information)
|
|
|
81 |
my $dbh; # global variable for database handle
|
|
|
82 |
my @warnings; # variable to hold errors and warnings
|
|
|
83 |
my $FATAL = ''; # in case of a fatal error, this variable will hold the reason
|
|
|
84 |
|
|
|
85 |
|
|
|
86 |
# Globals that hold information after parsing by readAndParseInput
|
|
|
87 |
my $datafileVersion;# store the data file version here.
|
|
|
88 |
my $reportDate; # global for the report date
|
|
|
89 |
my $clientID;
|
|
|
90 |
my $computerID;
|
|
|
91 |
my $clientName;
|
|
|
92 |
my $computerName;
|
|
|
93 |
|
|
|
94 |
|
|
|
95 |
|
|
|
96 |
#my %info; # stores any single line tag/value pair
|
|
|
97 |
#my $ipAddresses; # stores IP's for this machine
|
|
|
98 |
#my @diskInfo; # stores the disk info
|
|
|
99 |
#my @packages; # stores package info
|
|
|
100 |
#my @directoriesToWatch; # stores directories to watch
|
|
|
101 |
#my @pciInfo; # stores pci info
|
|
|
102 |
|
|
|
103 |
|
|
|
104 |
# safe check for equality. Handles undefined
|
|
|
105 |
sub checkEquals {
|
|
|
106 |
my ($first, $second) = @_;
|
|
|
107 |
return ($first eq $second) if (defined $first) and (defined $second);
|
|
|
108 |
return 1 if (! defined $first) and (! defined $second); # both undefined, so equal
|
|
|
109 |
return 0;
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
# standard find and load configuration file
|
|
|
113 |
sub loadConfigurationFile {
|
|
|
114 |
my $configuration_file = shift;
|
|
|
115 |
|
|
|
116 |
use File::Basename;
|
|
|
117 |
use Cwd qw(realpath);
|
|
|
118 |
|
|
|
119 |
my $filename = realpath($0); # get my real path
|
|
|
120 |
my $directories;
|
|
|
121 |
my $suffix;
|
|
|
122 |
#print "$configuration_file\n";
|
|
|
123 |
$configuration_file = $filename unless $configuration_file;
|
|
|
124 |
#print "$configuration_file\n";
|
|
|
125 |
|
|
|
126 |
if ( $configuration_file !~ m/\// ) { # no path information
|
|
|
127 |
($filename, $directories, $suffix) = fileparse($filename,qr/\.[^.]*/); # break filename apart
|
|
|
128 |
#print "No Path Given\n";
|
|
|
129 |
} else {
|
|
|
130 |
($filename, $directories, $suffix) = fileparse($configuration_file,qr/\.[^.]*/); # break filename apart
|
|
|
131 |
$configuration_file = '';
|
|
|
132 |
#print "Path included\n";
|
|
|
133 |
}
|
|
|
134 |
unless (-e $directories . ($configuration_file ? $configuration_file : $filename) . '.conf' ) {
|
|
|
135 |
$lookingIn = $directories;
|
|
|
136 |
while ($lookingIn) {
|
|
|
137 |
$lookingIn =~ m/^(.*\/)[^\/]+\//;
|
|
|
138 |
$lookingIn = $1;
|
|
|
139 |
#print "$lookingIn\n";
|
|
|
140 |
if (-e $lookingIn . ($configuration_file ? $configuration_file : $filename) . '.conf' ) {
|
|
|
141 |
$directories = $lookingIn;
|
|
|
142 |
$lookingIn = '';
|
|
|
143 |
}
|
|
|
144 |
}
|
|
|
145 |
}
|
|
|
146 |
$configuration_file = $directories . ($configuration_file ? $configuration_file : $filename) . '.conf'; # add the .conf
|
|
|
147 |
# print "$configuration_file\n";
|
|
|
148 |
# die;
|
|
|
149 |
open CONFFILE, "<$configuration_file" or die "Can not open configuration file $configuration_file";
|
|
|
150 |
my $confFileContents = join( '', <CONFFILE> );
|
|
|
151 |
close CONFFILE;
|
|
|
152 |
return $confFileContents;
|
|
|
153 |
}
|
|
|
154 |
|
|
|
155 |
# just a nice place to format any warnings/errors. Just prepend the client and computer name
|
|
|
156 |
sub createLogMessage {
|
|
|
157 |
my $message = shift;
|
|
|
158 |
$message = "$clientName - $computerName: " . $message;
|
|
|
159 |
return $message;
|
|
|
160 |
}
|
|
|
161 |
|
|
|
162 |
# generic routine to send an e-mail
|
|
|
163 |
sub sendmessage {
|
|
|
164 |
my ( $from, $to, $subject, $message, $cc, $bcc, $server, $port ) = @_;
|
|
|
165 |
|
|
|
166 |
open SENDMAIL, "|$SENDMAIL" or die "Could not open sendmail";
|
|
|
167 |
print SENDMAIL "From: $from\nTo: $to\nSubject: $subject\n";
|
|
|
168 |
print SENDMAIL "cc: $cc\n" if $cc;
|
|
|
169 |
print SENDMAIL "bcc: $bcc\n" if $bcc;
|
|
|
170 |
print SENDMAIL "$message\n";
|
|
|
171 |
print SENDMAIL ".\n";
|
|
|
172 |
close SENDMAIL;
|
|
|
173 |
}
|
|
|
174 |
|
|
|
175 |
# simply used to get an attrib_id. If it does not exit, will create it
|
|
|
176 |
|
|
|
177 |
sub getAttributeID {
|
|
|
178 |
my ($attributeName ) = @_;
|
|
|
179 |
my $sql = qq/select attrib_id from attrib where name = $attributeName and removed_date is null/;
|
|
|
180 |
my $result = &GenericSQL::getOneValue($dbh,$sql);
|
|
|
181 |
unless ( $result ) {
|
|
|
182 |
my $insertSQL = qq/insert into attrib (name,added_date) values ($attributeName,$reportDate)/;
|
|
|
183 |
&GenericSQL::doSQL( $dbh,$insertSQL );
|
|
|
184 |
$result = &GenericSQL::getOneValue($dbh,$sql);
|
|
|
185 |
push @warnings, "Added a new attribute type [$attributeName]";
|
|
|
186 |
}
|
|
|
187 |
return $result;
|
|
|
188 |
}
|
|
|
189 |
|
|
|
190 |
sub fixDatabaseValue {
|
|
|
191 |
# just return NULL if the parameter is invalid
|
|
|
192 |
return 'NULL' unless defined $_[0];
|
|
|
193 |
|
|
|
194 |
my ($value,$alwaysQuote) = @_;
|
|
|
195 |
# remove leading and trailing blank spaces
|
|
|
196 |
$value =~ s/^ +//gi;
|
|
|
197 |
$value =~ s/ +$//gi;
|
|
|
198 |
if ($alwaysQuote or ($value !~ m/^\d+$/)) { # Not a numeric value
|
|
|
199 |
$value = &GenericSQL::fixStringValue($dbh, $value); # so get it ready for SQL (ie, put quotes around it, etc...
|
|
|
200 |
}
|
|
|
201 |
#$value = "'$value'" if $alwaysQuote && $value ;
|
|
|
202 |
#print "AlwaysQuote [$alwaysQuote], value [$value]\n";
|
|
|
203 |
return $value;
|
|
|
204 |
}
|
|
|
205 |
|
|
|
206 |
sub checkAndUpdateAttribute {
|
|
|
207 |
my ($ID,$attribute,$value ) = @_;
|
|
|
208 |
unless ($attribute && $value) {
|
|
|
209 |
push @warnings, "Error: attempt to use null value for [$attribute], value [$value] for ID $ID in checkAndUPdateAttribute";
|
|
|
210 |
return 0;
|
|
|
211 |
}
|
|
|
212 |
$value = &fixDatabaseValue($value, 1); # we want to always quote the value on this particular one
|
|
|
213 |
#print "\tcheckAndUpdateAttribute:attribute/value = [$attribute][$value]\n";
|
|
|
214 |
$attribute = &fixDatabaseValue( $attribute );
|
|
|
215 |
my $attrib_id = &getAttributeID( $attribute, $reportDate );
|
|
|
216 |
my $sql = qq/
|
|
|
217 |
select device_attrib.value
|
|
|
218 |
from device_attrib join attrib using (attrib_id)
|
|
|
219 |
where device_attrib.device_id = $ID
|
|
|
220 |
and device_attrib.removed_date is null
|
|
|
221 |
and attrib.attrib_id = $attrib_id
|
|
|
222 |
/;
|
|
|
223 |
$result = &GenericSQL::getOneValue($dbh,$sql);
|
|
|
224 |
$result = &fixDatabaseValue( $result, 1 ); # we must do this since we are comparing to $value which has had this done
|
|
|
225 |
if ( $result ) { # got it, now see if it compares ok
|
|
|
226 |
if ( $value ne $result ) { # nope, this has changed. Note, must use fixDatabaseValue for escapes already in $value
|
|
|
227 |
# first, set the removed_date to now on the old part
|
|
|
228 |
#die "[$reportDate][$ID][$attrib_id]\n";
|
|
|
229 |
#print "\tresult = [$result], value = [$value]\n";
|
|
|
230 |
$sql = qq/
|
|
|
231 |
update device_attrib
|
|
|
232 |
set removed_date = $reportDate
|
|
|
233 |
where device_id = $ID
|
|
|
234 |
and attrib_id = $attrib_id
|
|
|
235 |
and removed_date is null
|
|
|
236 |
/;
|
|
|
237 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
238 |
undef $result; # this will force the insert in the next block of code
|
|
|
239 |
} # if $result ne $value
|
|
|
240 |
} # if ($result)
|
|
|
241 |
unless ( $result ) { # we have no valid entry for this attribute
|
|
|
242 |
$sql = qq/
|
|
|
243 |
insert into device_attrib(device_id,attrib_id,value,added_date)
|
|
|
244 |
values ($ID,$attrib_id,$value,$reportDate)
|
|
|
245 |
/;
|
|
|
246 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
247 |
return 1;
|
|
|
248 |
}
|
|
|
249 |
return 0;
|
|
|
250 |
}
|
|
|
251 |
|
|
|
252 |
# main subroutine that reads and parses the input
|
|
|
253 |
# it will place the appropriate values into the arrays/hashes
|
|
|
254 |
sub readAndParseInput {
|
|
|
255 |
my @lines = <STDIN>; # suck all data lines into this array
|
|
|
256 |
chomp @lines; # and remove eol chars
|
|
|
257 |
while ( @lines && ( $lines[0] !~ m/^[\[\<]sysinfo/ ) ) { # skip any header lines that may exist
|
|
|
258 |
shift @lines;
|
|
|
259 |
}
|
|
|
260 |
unless ( @lines ) {
|
|
|
261 |
$FATAL = 'Invalid Report File';
|
|
|
262 |
return;
|
|
|
263 |
}
|
|
|
264 |
if ( $lines[0] =~ m/<sysinfo([^>]*)>/ ) { # this is xml data
|
|
|
265 |
$datafileVersion = $1;
|
|
|
266 |
use XML::Simple;
|
|
|
267 |
my $output = XML::Simple->new();
|
|
|
268 |
my $theXML = $output->XMLin(join( "\n",@lines));
|
|
|
269 |
# XML::Simple is inconsistent in that the resulting hashes are different depending
|
|
|
270 |
# on if they only have one instance of a block or multiples. This is ok most of the
|
|
|
271 |
# time, but for diskinfo, network, etc..., our code assumes the resulting hashes
|
|
|
272 |
# will have a key into a hash reference, not simply a hash reference. The following
|
|
|
273 |
# kludge fixes that until I figure out the "right" way to do it.
|
|
|
274 |
%toFix = ( 'diskinfo' => 'name', 'pci' => 'name' ); # , 'network'
|
|
|
275 |
for $key ( keys %toFix ) {
|
|
|
276 |
#print "\n\nChecking $key\n";
|
|
|
277 |
#print Data::Dumper->Dump([$$theXML{$key}],[$key]);
|
|
|
278 |
my $test = $$theXML{$key};
|
|
|
279 |
if ( defined $$test{ $toFix{$key}} ) { # This occurs if there is only one entry
|
|
|
280 |
#print "We found a single entry hash\n";
|
|
|
281 |
my $thisName = $$test{$toFix{$key}};
|
|
|
282 |
delete $$test{$toFix{$key}};
|
|
|
283 |
foreach $thisKey ( keys %$test ) {
|
|
|
284 |
$$test{$thisName}{$thisKey} = $$test{$thisKey};
|
|
|
285 |
delete $$test{$thisKey};
|
|
|
286 |
}
|
|
|
287 |
#print Data::Dumper->Dump([$$theXML{$key}],[$key]);
|
|
|
288 |
}
|
|
|
289 |
}
|
|
|
290 |
# another problem, this time if the hostname is blank it the library sets it to an empty hash
|
|
|
291 |
#print STDERR ref($$theXML{'system'}{'hostname'});
|
|
|
292 |
# if the hostname is anything other than a pure scalar, just make it empty
|
|
|
293 |
$$theXML{'system'}{'hostname'} = '' if ( ref($$theXML{'system'}{'hostname'}) );
|
|
|
294 |
#print "\n\nAfter the check\n";
|
|
|
295 |
#print Data::Dumper->Dump([$$theXML{'system'}],['system']);
|
|
|
296 |
#die;
|
|
|
297 |
return $theXML;
|
|
|
298 |
} elsif ($lines[0] =~ m/\[sysinfo version\]/i) { # old style data file
|
|
|
299 |
my %returnValue;
|
|
|
300 |
my $linecount = 0; # just a pointer into the array
|
|
|
301 |
# ok, we are in the actual data (this can be an e-mail)
|
|
|
302 |
while ($linecount < @lines ) {
|
|
|
303 |
if ($lines[$linecount++] =~ m/^\[([^\]]+)\](.*)$/ ) { # Assume single line entry, ie [tag]value
|
|
|
304 |
$section = lc $1; # tag, section, whatever you want to call it. The thing inside the square brackets
|
|
|
305 |
$value = $2; # this is what we are working with
|
|
|
306 |
#$value = &fixDatabaseValue($2); # this is what we are working with
|
|
|
307 |
# first, look for anything we want to place in global variables
|
|
|
308 |
if ( $section eq 'sysinfo version' ) {
|
|
|
309 |
$returnValue{'report'}{'version'} = $value;
|
|
|
310 |
$datafileVersion = $value;
|
|
|
311 |
} elsif ($section eq 'client name') {
|
|
|
312 |
$returnValue{'report'}{'client'} = $value;
|
|
|
313 |
} elsif ($section eq 'hostname' ) {
|
|
|
314 |
$returnValue{'system'}{'hostname'} = $value;
|
|
|
315 |
} elsif ($section eq 'report date') { # ok, so we put quotes around this and we don't want them
|
|
|
316 |
$value =~ s/'//gi; # remove the quotes
|
|
|
317 |
my $reportDate = substr($value,0,12); # and grab only the date, hours and minutes portion of the value
|
|
|
318 |
$reportDate = substr($value,0,4) .'-' . substr($value,4,2) .'-' . substr($value,6,2) .' ' . substr($value,8,2) .':' . substr($value,10,2);
|
|
|
319 |
$returnValue{'report'}{'date'} = $reportDate;
|
|
|
320 |
} elsif ($section eq 'ip addresses' ) { # A machine can have multiple IP's
|
|
|
321 |
@ipAddresses = split(' ', $value);
|
|
|
322 |
my $counter = 0;
|
|
|
323 |
while (my $ip = shift @ipAddresses) {
|
|
|
324 |
$returnValue{'network'}{$counter++}{'address'} = $ip;
|
|
|
325 |
}
|
|
|
326 |
# following are processing the multi-line tags. We just look through them and get the start and end
|
|
|
327 |
# indicies of the section so they can be processed by the appropriate routines
|
|
|
328 |
} elsif ($section eq 'disk info') {
|
|
|
329 |
# disk info can be one or more line, so work until we find the next tag
|
|
|
330 |
while ($linecount < @lines && $lines[$linecount] !~ m/^\[([^\]]+)\](.*)$/) {
|
|
|
331 |
my ( $device, $fstype, $size, $used, $mount ) = split( "\t",$lines[$linecount++] );
|
|
|
332 |
$returnValue{'diskinfo'}{$device}{'fstype'} = $fstype if $fstype;
|
|
|
333 |
$returnValue{'diskinfo'}{$device}{'size'} = $size if $size;
|
|
|
334 |
$returnValue{'diskinfo'}{$device}{'used'} = $used if $used;
|
|
|
335 |
$returnValue{'diskinfo'}{$device}{'mount'} = $mount if $mount;
|
|
|
336 |
# push @diskInfo, $lines[$linecount++];
|
|
|
337 |
}
|
|
|
338 |
} elsif ($section eq 'packages installed') { # and, of course, software packages
|
|
|
339 |
while ($linecount < @lines && $lines[$linecount] !~ m/^\[([^\]]+)\](.*)$/) {
|
|
|
340 |
if ( $lines[$linecount] =~ m/^\s*$/) {
|
|
|
341 |
$linecount++;
|
|
|
342 |
next;
|
|
|
343 |
}
|
|
|
344 |
my ($package, $version, $description) = split( "\t", $lines[$linecount++]);
|
|
|
345 |
$returnValue{'software'}{$package}{'version'} = $version;
|
|
|
346 |
$returnValue{'software'}{$package}{'description'} = $description;
|
|
|
347 |
#push @packages, $lines[$linecount++];
|
|
|
348 |
}
|
|
|
349 |
} elsif ($section eq 'pci info') { # this gives us some hardware info
|
|
|
350 |
my $x = 0;
|
|
|
351 |
while ($linecount < @lines && $lines[$linecount] !~ m/^\[([^\]]+)\](.*)$/) {
|
|
|
352 |
if ($lines[$linecount] =~ m/^\s*([0-9.:]+[\S])\s+(.*\S)\s*$/) { # we have a slot/name pair
|
|
|
353 |
$returnValue{'pci'}{$x}{'slot'} = $1;
|
|
|
354 |
$returnValue{'pci'}{$x}{'name'} = $2;
|
|
|
355 |
} else {
|
|
|
356 |
$returnValue{'pci'}{$x}{'slot'} = 'v1.x-unk';
|
|
|
357 |
$returnValue{'pci'}{$x}{'name'} = $lines[$linecount];
|
|
|
358 |
}
|
|
|
359 |
$x++;
|
|
|
360 |
$linecount++;
|
|
|
361 |
}
|
|
|
362 |
# we have a single line tag, so just store it in the right place
|
|
|
363 |
} elsif ($value) {
|
|
|
364 |
if ( $section eq 'client name' ) {
|
|
|
365 |
$returnValue{'report'}{'client'} = $value;
|
|
|
366 |
} elsif ( $section eq 'distro_name' ) {
|
|
|
367 |
$returnValue{'operatingsystem'}{'distribution'} = $value;
|
|
|
368 |
} elsif ( $section eq 'distro_description' ) {
|
|
|
369 |
$returnValue{'operatingsystem'}{'description'} = $value;
|
|
|
370 |
} elsif ( $section eq 'distro_release' ) {
|
|
|
371 |
$returnValue{'operatingsystem'}{'release'} = $value;
|
|
|
372 |
} elsif ( $section eq 'distro_codename' ) {
|
|
|
373 |
$returnValue{'operatingsystem'}{'codename'} = $value;
|
|
|
374 |
} elsif ( $section eq 'hostname' ) {
|
|
|
375 |
$returnValue{'system'}{'hostname'} = $value;
|
|
|
376 |
} elsif ( $section eq 'memory' ) {
|
|
|
377 |
$returnValue{'system'}{'memory'} = $value;
|
|
|
378 |
} elsif ( $section eq 'num_cpu' ) {
|
|
|
379 |
$returnValue{'system'}{'num_cpu'} = $value;
|
|
|
380 |
} elsif ( $section eq 'cpu_speed' ) {
|
|
|
381 |
$returnValue{'system'}{'cpu_speed'} = $value;
|
|
|
382 |
} elsif ( $section eq 'cpu_type' ) {
|
|
|
383 |
$returnValue{'system'}{'cpu_type'} = $value;
|
|
|
384 |
} elsif ( $section eq 'cpu_sub' ) {
|
|
|
385 |
$returnValue{'system'}{'cpu_sub'} = $value;
|
|
|
386 |
} elsif ( $section eq 'os_name' ) {
|
|
|
387 |
$returnValue{'operatingsystem'}{'os_name'} = $value;
|
|
|
388 |
} elsif ( $section eq 'os_version' ) {
|
|
|
389 |
$returnValue{'operatingsystem'}{'os_version'} = $value;
|
|
|
390 |
} elsif ( $section eq 'kernel' ) {
|
|
|
391 |
$returnValue{'operatingsystem'}{'kernel'} = $value;
|
|
|
392 |
} elsif ( $section eq 'boot' ) {
|
|
|
393 |
$returnValue{'system'}{'last_boot'} = $value;
|
|
|
394 |
} elsif ( $section eq 'uptime' ) {
|
|
|
395 |
$returnValue{'system'}{'uptime'} = $value;
|
|
|
396 |
}
|
|
|
397 |
#$info{$section} = $value;
|
|
|
398 |
} else { # we should never get here, a single line with no value is BAD
|
|
|
399 |
push @warnings, &createLogMessage( "Unknown line '$lines[$linecount-1]'" ) . "\n";
|
|
|
400 |
}
|
|
|
401 |
} else { # this is also an error, no [tagname]. Maybe a space inserted?
|
|
|
402 |
push @warnings, &createLogMessage( "unknown line '$lines[$linecount-1]'" ) . "\n";
|
|
|
403 |
}
|
|
|
404 |
} # while
|
|
|
405 |
return \%returnValue;
|
|
|
406 |
}
|
|
|
407 |
}
|
|
|
408 |
|
|
|
409 |
# tries to figure out the client. If the client does not exist, will create a null record
|
|
|
410 |
# for them. Stores result in $client_id (reading $clientName)
|
|
|
411 |
sub getClientID {
|
|
|
412 |
# let's see if the client exists
|
|
|
413 |
$client = &fixDatabaseValue($clientName);
|
|
|
414 |
$sql = qq/select client_id from client where name = $client and removed_date is null/;
|
|
|
415 |
my $client_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
416 |
unless ($client_id) { # no entry, check the alias table
|
|
|
417 |
$sql = qq/select client_id from client_alias where alias=$client and removed_date is null/;
|
|
|
418 |
$client_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
419 |
}
|
|
|
420 |
# the following has been changed to simply return a message
|
|
|
421 |
unless ( $client_id ) { # nope, client does not exist, so add them
|
|
|
422 |
$device = &fixDatabaseValue($computerName);
|
|
|
423 |
$sql = qq/select report_date from unknown_entry where client_name = $client and device_name = $device/;
|
|
|
424 |
#$report = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
425 |
#print STDERR "Report Date $report\n";
|
|
|
426 |
if ($report = &GenericSQL::getOneValue( $dbh, $sql )) {
|
|
|
427 |
$FATAL = "New Client detected, but entry already made. You must update Camp before this can be processed";
|
|
|
428 |
} else {
|
|
|
429 |
$sql = qq/insert into unknown_entry(client_name,device_name,report_date) values ($client, $device, $reportDate)/;
|
|
|
430 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
431 |
$FATAL = "Warning, new client $client found with new device $device. You must update Camp before this report can be processed\n";
|
|
|
432 |
}
|
|
|
433 |
}
|
|
|
434 |
return $client_id;
|
|
|
435 |
}
|
|
|
436 |
|
|
|
437 |
# get a device type id from the device table. Create it if it does not exist
|
|
|
438 |
sub getDeviceTypeID {
|
|
|
439 |
my $typeDescription = &GenericSQL::fixStringValue( $dbh, shift );
|
|
|
440 |
my $reportDate = shift;
|
|
|
441 |
$sql = qq/select device_type_id from device_type where name = $typeDescription and removed_date is null/;
|
|
|
442 |
my $id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
443 |
unless ($id) {
|
|
|
444 |
my $sql_insert = qq/insert into device_type ( name,added_date ) values ($typeDescription, $reportDate) /;
|
|
|
445 |
&GenericSQL::doSQL( $dbh, $sql_insert );
|
|
|
446 |
$id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
447 |
}
|
|
|
448 |
return $id;
|
|
|
449 |
}
|
|
|
450 |
|
|
|
451 |
# gets the computer ID. If the computerID does not exist, creates it.
|
|
|
452 |
# returns the id of the computer.
|
|
|
453 |
sub getComputerID {
|
|
|
454 |
# ok, does this computer name exist (each computer name per site must be unique)
|
|
|
455 |
$computer = &fixDatabaseValue($computerName);
|
|
|
456 |
my $sql = qq/
|
|
|
457 |
select device_id
|
|
|
458 |
from device join site using (site_id)
|
|
|
459 |
where site.client_id = $clientID
|
|
|
460 |
and device.name = $computer
|
|
|
461 |
and device.removed_date is null
|
|
|
462 |
/;
|
|
|
463 |
my $computer_id = &GenericSQL::getOneValue( $dbh, $sql ); # actually, result of query above
|
|
|
464 |
unless ( $computer_id ) { # didn't find it. Let's see if it is in the alias table
|
|
|
465 |
$sql = qq/select device_id from device_alias join device using (device_id) join site using (site_id) where device_alias.alias = $computer and site.client_id = $clientID/;
|
|
|
466 |
$computer_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
467 |
}
|
|
|
468 |
# changed to just give a warning
|
|
|
469 |
unless ( $computer_id ) { # nope, computer does not exist so create it
|
|
|
470 |
$client = &fixDatabaseValue($clientName);
|
|
|
471 |
$sql = qq/select report_date from unknown_entry where client_name = $client and device_name = $computer and processed_date is null/;
|
|
|
472 |
#$report = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
473 |
#print STDERR "Report Date $report\n";
|
|
|
474 |
if ($report = &GenericSQL::getOneValue( $dbh, $sql )) {
|
|
|
475 |
$FATAL = "New Device detected, but entry already made. You must update Camp before this can be processed";
|
|
|
476 |
} else {
|
|
|
477 |
$sql = qq/insert into unknown_entry(client_name,device_name,report_date) values ($client, $computer, $reportDate)/;
|
|
|
478 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
479 |
$FATAL = "Warning, new device $computer found associated with client $client. You must update Camp before this report can be processed\n";
|
|
|
480 |
}
|
|
|
481 |
}
|
|
|
482 |
return $computer_id;
|
|
|
483 |
}
|
|
|
484 |
|
|
|
485 |
# checks for a duplicate report, ie one that has already been run.
|
|
|
486 |
# the only thing that always changes is disk space usage, so just look to see
|
|
|
487 |
# if this computer has a report already for this date/time
|
|
|
488 |
sub recordReport {
|
|
|
489 |
my $sql = qq/select count(*) from sysinfo_report where device_id = $computerID and report_date = $reportDate/;
|
|
|
490 |
if (&GenericSQL::getOneValue( $dbh, $sql ) > 0) {
|
|
|
491 |
my $thisDevice = &GenericSQL::getOneValue( $dbh, "select name from device where device_id = $computerID" );
|
|
|
492 |
$FATAL = qq/Duplicate Report for $thisDevice (id $computerID) on $reportDate/;
|
|
|
493 |
return;
|
|
|
494 |
}
|
|
|
495 |
$version = &fixDatabaseValue($datafileVersion);
|
|
|
496 |
# if we made it this far, we are ok, so just add the report id
|
|
|
497 |
$sql = qq/insert into sysinfo_report(device_id,version,report_date,added_date) values ($computerID,$version,$reportDate,now())/;
|
|
|
498 |
&GenericSQL::doSQL($dbh,$sql);
|
|
|
499 |
$sql = qq/select sysinfo_report_id from sysinfo_report where device_id = $computerID and report_date = $reportDate/;
|
|
|
500 |
return &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
501 |
}
|
|
|
502 |
|
|
|
503 |
# gets operating system ID. If it does not exist, creates it
|
|
|
504 |
sub getOSID {
|
|
|
505 |
my ($osHash) = shift;
|
|
|
506 |
my $os_id;
|
|
|
507 |
my $osName = &fixDatabaseValue($$osHash{'os_name'});
|
|
|
508 |
my $kernel = &fixDatabaseValue($$osHash{'kernel'});
|
|
|
509 |
my $distro_name = &fixDatabaseValue($$osHash{'distribution'});
|
|
|
510 |
my $release = &fixDatabaseValue($$osHash{'release'});
|
|
|
511 |
my $version = &fixDatabaseValue($$osHash{'os_version'});
|
|
|
512 |
my $description = &fixDatabaseValue($$osHash{'description'});
|
|
|
513 |
my $codename = &fixDatabaseValue($$osHash{'codename'});
|
|
|
514 |
|
|
|
515 |
$sql = qq/select operating_system_id from operating_system
|
|
|
516 |
where name = $osName
|
|
|
517 |
and kernel = $kernel
|
|
|
518 |
and distro = $distro_name
|
|
|
519 |
and distro_release = $release /;
|
|
|
520 |
unless ( $os_id = &GenericSQL::getOneValue( $dbh, $sql ) ) {
|
|
|
521 |
$sql = qq/insert into operating_system (name,version,kernel,distro,distro_description,distro_release,distro_codename, added_date) values
|
|
|
522 |
($osName,$version,$kernel,$distro_name,$description,$release,$codename, $reportDate)/;
|
|
|
523 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
524 |
$sql = qq/select operating_system_id from operating_system
|
|
|
525 |
where name = $osName
|
|
|
526 |
and kernel = $kernel
|
|
|
527 |
and distro = $distro_name
|
|
|
528 |
and distro_release = $release /;
|
|
|
529 |
$os_id = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
530 |
}
|
|
|
531 |
return $os_id;
|
|
|
532 |
}
|
|
|
533 |
|
|
|
534 |
|
|
|
535 |
# simply verifies some attributes of the computer
|
|
|
536 |
sub updateComputerMakeup {
|
|
|
537 |
my ($systemHash) = @_;
|
|
|
538 |
#print "[$$systemHash{'memory'}]\n";
|
|
|
539 |
&checkAndUpdateAttribute($computerID,'Memory',$$systemHash{'memory'});
|
|
|
540 |
#print "[$$systemHash{'num_cpu'}]\n";
|
|
|
541 |
&checkAndUpdateAttribute($computerID,'Number of CPUs',$$systemHash{'num_cpu'});
|
|
|
542 |
#die;
|
|
|
543 |
&checkAndUpdateAttribute($computerID,'CPU Type',$$systemHash{'cpu_type'});
|
|
|
544 |
&checkAndUpdateAttribute($computerID,'CPU SubType',$$systemHash{'cpu_sub'});
|
|
|
545 |
&checkAndUpdateAttribute($computerID,'CPU Speed',$$systemHash{'cpu_speed'});
|
|
|
546 |
}
|
|
|
547 |
|
|
|
548 |
sub updateOS {
|
|
|
549 |
my ($osHash) = @_;
|
|
|
550 |
# verify the operating system
|
|
|
551 |
my $os_id = &getOSID($osHash, $reportDate);
|
|
|
552 |
$sql = qq/select operating_system_id from device_operating_system where device_id = $computerID and removed_date is null/;
|
|
|
553 |
$registeredOS = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
554 |
unless ($registeredOS && $registeredOS eq $os_id ) {
|
|
|
555 |
if ( $registeredOS ) { #we have the same computer, but a new OS???
|
|
|
556 |
$sql = qq/update device_operating_system set removed_date = $reportDate where device_id = $computerID and removed_date is null/;
|
|
|
557 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
558 |
push @warnings, &createLogMessage("Computer $computerName has a new OS" );
|
|
|
559 |
$os_id = &getOSID($osHash, $reportDate);
|
|
|
560 |
}
|
|
|
561 |
$sql = qq/insert into device_operating_system( device_id,operating_system_id,added_date) values ($computerID,$os_id,$reportDate)/;
|
|
|
562 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
563 |
}
|
|
|
564 |
}
|
|
|
565 |
|
|
|
566 |
sub dateToMySQL {
|
|
|
567 |
my $date = shift;
|
|
|
568 |
# print "Date In $date\n";
|
|
|
569 |
$date =~ s/'//g; # some of the older reports put quotes around this
|
|
|
570 |
return &fixDatabaseValue($date) if $date =~ m/\d{4}[-\/]\d{2}[-\/]\d{2} \d{2}:\d{2}/; # this is already in the correct format
|
|
|
571 |
|
|
|
572 |
my ($ss,$mm,$hh,$day,$month,$year,$zone);
|
|
|
573 |
unless ( $date =~ m/^\d+$/ ) { # If it is not a unix time stamp
|
|
|
574 |
$date = str2time($date); # try to parse it
|
|
|
575 |
}
|
|
|
576 |
return '' unless defined $date && $date; # bail if date is not defined or zero
|
|
|
577 |
# standard processing of a date
|
|
|
578 |
($ss,$mm,$hh,$day,$month,$year,$zone) = localtime($date);
|
|
|
579 |
$year += 1900;
|
|
|
580 |
++$month;
|
|
|
581 |
# printf( "Answer Is: %4d-%02d-%02d %02d:%02d\n", $year,$month,$day,$hh,$mm);
|
|
|
582 |
return &fixDatabaseValue(sprintf( '%4d-%02d-%02d %02d:%02d', $year,$month,$day,$hh,$mm));
|
|
|
583 |
}
|
|
|
584 |
|
|
|
585 |
# every time we get a report, we need to see if the computer was rebooted
|
|
|
586 |
# if last reboot date is not the same as what our report shows, we will
|
|
|
587 |
# remove the existing entry, then add a new one
|
|
|
588 |
sub updateBootTime {
|
|
|
589 |
my ($systemHash) = @_;
|
|
|
590 |
my $lastReboot;
|
|
|
591 |
if ($$systemHash{'last_boot'}) {
|
|
|
592 |
#print "Checking Boot Time\n";
|
|
|
593 |
if ($lastReboot = &dateToMySQL($$systemHash{'last_boot'})) {
|
|
|
594 |
my $sql = qq/select computer_uptime_id from computer_uptime where device_id = $computerID and last_reboot = $lastReboot/;
|
|
|
595 |
unless ( &GenericSQL::getOneValue( $dbh, $sql ) ) {
|
|
|
596 |
push @warnings, &createLogMessage("Computer was rebooted at $lastReboot");
|
|
|
597 |
my $sql_insert = qq/update computer_uptime set removed_date = $reportDate where device_id = $computerID and removed_date is null/;
|
|
|
598 |
&GenericSQL::doSQL( $dbh, $sql_insert );
|
|
|
599 |
$sql_insert = qq/insert into computer_uptime (device_id,added_date,last_reboot) values ($computerID,$reportDate,$lastReboot)/;
|
|
|
600 |
&GenericSQL::doSQL( $dbh, $sql_insert );
|
|
|
601 |
}
|
|
|
602 |
} else {
|
|
|
603 |
push @warnings, &createLogMessage('Invalid reboot time [' . $$systemHash{'last_boot'} . ']');
|
|
|
604 |
}
|
|
|
605 |
} else {
|
|
|
606 |
push @warnings, &createLogMessage('No Boot time given');
|
|
|
607 |
}
|
|
|
608 |
}
|
|
|
609 |
|
|
|
610 |
# routine will check for all IP addresses reported and check against those recorded in the
|
|
|
611 |
# database. It will remove any no longer in the database, and add any new ones
|
|
|
612 |
sub doIPAddresses {
|
|
|
613 |
my ( $networkHash ) = @_;
|
|
|
614 |
# delete $$networkHash{'lo'}; # we don't process lo
|
|
|
615 |
# first, remove any interfaces that no longer exist
|
|
|
616 |
my $interfaces = join ',', (map { &fixDatabaseValue($_) } keys %$networkHash); # get a list of interfaces being passed in
|
|
|
617 |
if ( $interfaces ) {
|
|
|
618 |
my $sql = qq/update network set removed_date = $reportDate where device_id = $computerID and removed_date is null and interface not in ($interfaces)/;
|
|
|
619 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
620 |
}
|
|
|
621 |
# let's get all remaining network information
|
|
|
622 |
$sql = qq/select network_id,interface,address,netmask,ip6,ip6net,mac,mtu from network where device_id = $computerID and removed_date is null/;
|
|
|
623 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
624 |
while (my $thisRow = &GenericSQL::getNextRow($sth)) {
|
|
|
625 |
if ( defined $$thisRow{'interface'} ) { # pre 2.0 versions did not have an interface object
|
|
|
626 |
# long drawn out thing to check if they are the same
|
|
|
627 |
if ( &checkEquals($$networkHash{$$thisRow{'interface'}}{'address'}, $$thisRow{'address'}) &&
|
|
|
628 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'ip6address'}, $$thisRow{'ip6'}) &&
|
|
|
629 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'ip6networkbits'}, $$thisRow{'ip6net'}) &&
|
|
|
630 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'mac'}, $$thisRow{'mac'}) &&
|
|
|
631 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'mtu'}, $$thisRow{'mtu'}) &&
|
|
|
632 |
&checkEquals($$networkHash{$$thisRow{'interface'}}{'netmask'}, $$thisRow{'netmask'}) ) {
|
|
|
633 |
# they are the same, so just mark it off the list
|
|
|
634 |
delete $$networkHash{$$thisRow{'interface'}};
|
|
|
635 |
} else { # it has changed, so invalidate the current line in the database
|
|
|
636 |
$sql = qq/update network set removed_date = $reportDate where network_id = $$thisRow{'network_id'}/;
|
|
|
637 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
638 |
}
|
|
|
639 |
} else { # the database is still using pre 2.0 values, so we must see if we need to upgrade this
|
|
|
640 |
if ($datafileVersion =~ m/^2/) { # version 2.x, so we will need to update this record
|
|
|
641 |
# in this case, we are going to just "remove" all current entries and reload them below.
|
|
|
642 |
# this code will only be run once for each machine that needs to conver to the new format
|
|
|
643 |
$sql = qq/update network set removed_date = $reportDate where removed_date is null and device_id = $computerID/;
|
|
|
644 |
last;
|
|
|
645 |
}
|
|
|
646 |
}
|
|
|
647 |
}
|
|
|
648 |
# at this point, the only items left are either new or have changed, so just insert them.
|
|
|
649 |
foreach my $device ( keys %$networkHash ) {
|
|
|
650 |
$sql = qq/insert into network (device_id,added_date,interface,address,netmask,ip6,ip6net,mtu,mac) values /;
|
|
|
651 |
$sql .= '( ' . join(',',
|
|
|
652 |
$computerID,
|
|
|
653 |
$reportDate,
|
|
|
654 |
&fixDatabaseValue($device),
|
|
|
655 |
&fixDatabaseValue($$networkHash{$device}{'address'}),
|
|
|
656 |
&fixDatabaseValue($$networkHash{$device}{'netmask'}),
|
|
|
657 |
&fixDatabaseValue($$networkHash{$device}{'ip6address'}),
|
|
|
658 |
&fixDatabaseValue($$networkHash{$device}{'ip6networkbits'}),
|
|
|
659 |
&fixDatabaseValue($$networkHash{$device}{'mtu'}),
|
|
|
660 |
&fixDatabaseValue($$networkHash{$device}{'mac'})
|
|
|
661 |
) .
|
|
|
662 |
')';
|
|
|
663 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
664 |
push @warnings,&createLogMessage("Network Device $device was added/modified");
|
|
|
665 |
}
|
|
|
666 |
} # sub doIPAddresses
|
|
|
667 |
|
|
|
668 |
|
|
|
669 |
sub processDisks {
|
|
|
670 |
my ($diskHash) = @_;
|
|
|
671 |
#print Data::Dumper->Dump([$diskHash],['$diskHash']);
|
|
|
672 |
#print "Upon entry, we have " . (scalar keys %$diskHash) . " Items in hash\n";
|
|
|
673 |
# first, see if there are any alerts
|
|
|
674 |
foreach my $partition (keys %$diskHash) {
|
|
|
675 |
if ($$diskHash{$partition}{'size'}) {
|
|
|
676 |
my $usedPercent = sprintf('%4.2f', ($$diskHash{$partition}{'used'}/$$diskHash{$partition}{'size'}) * 100);
|
|
|
677 |
push @warnings, &createLogMessage("Partition $partition at $usedPercent%% capacity") if $usedPercent > $DiskUsageAlert;
|
|
|
678 |
}
|
|
|
679 |
}
|
|
|
680 |
# now, remove any that are no longer reported
|
|
|
681 |
my $temp = join ',', (map { &fixDatabaseValue($_) } keys %$diskHash); # get a list of interfaces being passed in
|
|
|
682 |
my $sql = qq/select disk_info_id from disk_info where removed_date is null and device_id = $computerID and disk_device not in ($temp)/;
|
|
|
683 |
#print "\n$sql\n";
|
|
|
684 |
# die;
|
|
|
685 |
my @idsToDelete = &GenericSQL::getArrayOfValues( $dbh, $sql );
|
|
|
686 |
# print '[' . join ('][', @idsToDelete) . "]\n";
|
|
|
687 |
foreach my $id ( @idsToDelete ) {
|
|
|
688 |
next unless $id;
|
|
|
689 |
push @warnings,&createLogMessage("Disk Partition removed");
|
|
|
690 |
$sql = qq/update disk_info set removed_date = $reportDate where removed_date is null and disk_info_id = $id/;
|
|
|
691 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
692 |
$sql = qq/update disk_space set removed_date = $reportDate where removed_date is null and disk_info_id = $id/;
|
|
|
693 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
694 |
}
|
|
|
695 |
# now, we have a "clean" database
|
|
|
696 |
# do a query to retrieve all disk entries for this device
|
|
|
697 |
$sql = qq/select disk_info.disk_info_id,disk_space_id,disk_device,filesystem,mount_point,capacity
|
|
|
698 |
from disk_info join disk_space using (disk_info_id)
|
|
|
699 |
where disk_space.removed_date is null and disk_info.removed_date is null and device_id = $computerID/;
|
|
|
700 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
701 |
#print "Before we start processing, we have " . (scalar keys %$diskHash) . " Items in hash\n";
|
|
|
702 |
while (my $thisDBRow = &GenericSQL::getNextRow($sth)) {
|
|
|
703 |
my $thisHashRow = $$diskHash{$$thisDBRow{'disk_device'}} ; # just for convenience
|
|
|
704 |
# Always invalidate the disk space entry. We'll either add a new row, or it is changed too much
|
|
|
705 |
$sql = "update disk_space set removed_date = $reportDate where removed_date is null and disk_info_id = " . $$thisDBRow{'disk_info_id'};
|
|
|
706 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
707 |
# we know this exists in both
|
|
|
708 |
#print "\n\n" . $$thisDBRow{'disk_device'} . "\n";
|
|
|
709 |
#print Data::Dumper->Dump([$thisDBRow],['thisRow']);
|
|
|
710 |
#print Data::Dumper->Dump([$thisHashRow],['thisHashRow']);
|
|
|
711 |
#print $$thisHashRow{'fstype'} . "\n";
|
|
|
712 |
#print $$thisHashRow{'size'} . "\n";
|
|
|
713 |
#print $$thisHashRow{'mount'} . "\n";
|
|
|
714 |
|
|
|
715 |
# is it a partition, or a directory to watch. This is defined as a directory to watch does not contain a size,
|
|
|
716 |
# mount point or file system type.
|
|
|
717 |
my $diskPartition = (exists ($$thisHashRow{'fstype'}) && exists ($$thisHashRow{'size'}) && exists ($$thisHashRow{'mount'}) );
|
|
|
718 |
# now, determine if we need to update the disk_info for some reason
|
|
|
719 |
# this condition is based upon two types of entries
|
|
|
720 |
# Type #1 (top) is a standard partition, so we see if fstype, mount and capacity are the same
|
|
|
721 |
# type #2 (after ||) us a "directory to watch" (with no fstype, size or mount)
|
|
|
722 |
if ( $diskPartition ) { # it is a partition, so check if something has changed in the entry
|
|
|
723 |
#print "\n\nDevice: [" . $$thisDBRow{'disk_device'} . "] is a partition\n";
|
|
|
724 |
#print "thisHashRow fstype [" . $$thisHashRow{'fstype'} . "]\n";
|
|
|
725 |
#print "thisHashRow size [" . $$thisHashRow{'size'} . "]\n";
|
|
|
726 |
#print "thisHashRow mount [" . $$thisHashRow{'mount'} . "]\n";
|
|
|
727 |
#print "thisRow filesystem [" .
|
|
|
728 |
unless ( &checkEquals($$thisHashRow{'fstype'}, $$thisDBRow{'filesystem'}) and
|
|
|
729 |
&checkEquals($$thisHashRow{'mount'}, $$thisDBRow{'mount_point'}) and
|
|
|
730 |
&checkEquals($$thisHashRow{'size'}, $$thisDBRow{'capacity'}) ) {
|
|
|
731 |
# yes, a change. If we just remove this entry, the add loop (below) will set it as a new device
|
|
|
732 |
$sql = "update disk_info set removed_date = $reportDate where disk_info_id = " . $$thisDBRow{'disk_info_id'};
|
|
|
733 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
734 |
#print "$sql\n";
|
|
|
735 |
next;
|
|
|
736 |
}
|
|
|
737 |
}
|
|
|
738 |
$usedSpace = $$diskHash{$$thisDBRow{'disk_device'}}{'used'};
|
|
|
739 |
#print "\tupdating entry, looking at disk has => $$thisDBRow{'disk_device'} with space $usedSpace\n";
|
|
|
740 |
$sql = "insert into disk_space (disk_info_id,space_used,added_date) values ";
|
|
|
741 |
$sql .= '(' . join (',', ($$thisDBRow{'disk_info_id'}, &fixDatabaseValue($usedSpace), $reportDate)) . ')';
|
|
|
742 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
743 |
# and delete the hash entry so we don't process it as a change
|
|
|
744 |
delete $$diskHash{$$thisDBRow{'disk_device'}};
|
|
|
745 |
}
|
|
|
746 |
# at this point, all we have left are additions and changes
|
|
|
747 |
foreach my $partition ( keys %$diskHash ) {
|
|
|
748 |
$sql = 'insert into disk_info(device_id,added_date,disk_device,filesystem,mount_point,capacity) values ';
|
|
|
749 |
$sql .= '(' . join( ',', ( $computerID,
|
|
|
750 |
$reportDate,
|
|
|
751 |
&fixDatabaseValue($partition),
|
|
|
752 |
&fixDatabaseValue($$diskHash{$partition}{'fstype'}),
|
|
|
753 |
&fixDatabaseValue($$diskHash{$partition}{'mount'}),
|
|
|
754 |
&fixDatabaseValue($$diskHash{$partition}{'size'})
|
|
|
755 |
)
|
|
|
756 |
) . ')';
|
|
|
757 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
758 |
$sql = "select disk_info_id from disk_info where removed_date is null and device_id = $computerID and disk_device = " . &fixDatabaseValue($partition);
|
|
|
759 |
$temp = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
760 |
$sql = 'insert into disk_space(disk_info_id,added_date,space_used) values (';
|
|
|
761 |
$sql .= join( ',', ($temp, $reportDate, fixDatabaseValue($$diskHash{$partition}{'used'}))) . ')';
|
|
|
762 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
763 |
}
|
|
|
764 |
}
|
|
|
765 |
|
|
|
766 |
# routine to ensure the hardware returned as PCI hardware is in the attributes area
|
|
|
767 |
sub processPCI {
|
|
|
768 |
my ($pciHash) = @_;
|
|
|
769 |
# print "Entering processPCI\n";
|
|
|
770 |
#print Data::Dumper->Dump([$pciHash],[$key]);
|
|
|
771 |
return unless $pciHash && keys %$pciHash;
|
|
|
772 |
|
|
|
773 |
#my %attributeMappings = ('class' => 'Class', # v2 database has these items, but we want to have a pretty name
|
|
|
774 |
# 'device' => 'Device Name',
|
|
|
775 |
# 'sdevice' => 'Subsystem Device',
|
|
|
776 |
# 'svendor' => 'Subsystem Vendor',
|
|
|
777 |
# 'vendor' => 'Vendor',
|
|
|
778 |
# 'name' => 'Name',
|
|
|
779 |
# 'slot' => 'Slot'
|
|
|
780 |
# );
|
|
|
781 |
|
|
|
782 |
# The two keys we'll check for uniquness are device.name and device_type with a key value of 'slot'. If these
|
|
|
783 |
# are the same, we assume this is the same record
|
|
|
784 |
|
|
|
785 |
# print Data::Dumper->Dump([$pciHash]);
|
|
|
786 |
|
|
|
787 |
my $key;
|
|
|
788 |
# normalize the data
|
|
|
789 |
foreach $key ( keys %$pciHash ) {
|
|
|
790 |
unless ( defined ($$pciHash{$key}{'slot'}) ) { # doesn't have a slot field
|
|
|
791 |
my $slotField = '';
|
|
|
792 |
my $test = $$pciHash{$key};
|
|
|
793 |
foreach $subkey ( keys %$test) { # scan through all keys and see if there is something with a "slot looking" value in it
|
|
|
794 |
$slotField = $key if $$test{$subkey} =~ m/^[0-9a-f:.]+$/;
|
|
|
795 |
}
|
|
|
796 |
if ( $slotField ) {
|
|
|
797 |
$$pciHash{$key}{$subkey}{'slot'} = $$pciHash{$key}{$subkey}{$slotField};
|
|
|
798 |
} else {
|
|
|
799 |
$$pciHash{$key}{'slot'} = 'Unknown';
|
|
|
800 |
}
|
|
|
801 |
}
|
|
|
802 |
# Each entry must have a name. Use 'device' if it doesn't exist
|
|
|
803 |
$$pciHash{$key}{'name'} = $$pciHash{$key}{'device'} unless defined($$pciHash{$key}{'name'}) && $$pciHash{$key}{'name'};
|
|
|
804 |
$$pciHash{$key}{'name'} = $$pciHash{$key}{'sdevice'} unless defined($$pciHash{$key}{'name'}) && $$pciHash{$key}{'name'};
|
|
|
805 |
$$pciHash{$key}{'name'} =~ s/^ +//;
|
|
|
806 |
unless ( $$pciHash{$key}{'name'} ) {
|
|
|
807 |
push @warnings, &createLogMessage("No name given for one or more PCI devices at normalize, Computer ID: [$computerID], Report Date: [$reportDate]");
|
|
|
808 |
return;
|
|
|
809 |
}
|
|
|
810 |
# Following is what will actually be put in the device table, ie device.name
|
|
|
811 |
$$pciHash{$key}{'keyFieldValue'} = $$pciHash{$key}{'slot'} . ' - ' . $$pciHash{$key}{'name'};
|
|
|
812 |
}
|
|
|
813 |
# at this point, we should have a slot and a name field in all pci devices
|
|
|
814 |
|
|
|
815 |
# print Data::Dumper->Dump([$pciHash]);
|
|
|
816 |
# die;
|
|
|
817 |
# Get list of all PCI cards in database for this computer
|
|
|
818 |
my @toDelete;
|
|
|
819 |
$sql = qq/select device_id,
|
|
|
820 |
device.name name
|
|
|
821 |
from device join device_type using (device_type_id)
|
|
|
822 |
where device_type.name = 'PCI Card'
|
|
|
823 |
and device.removed_date is null
|
|
|
824 |
and device.part_of = $computerID/;
|
|
|
825 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
826 |
while (my $thisRow = &GenericSQL::getNextRow($sth)) { # for each row in the database
|
|
|
827 |
my $deleteMe = $$thisRow{'device_id'}; # assume we will delete it
|
|
|
828 |
foreach $key (keys %$pciHash ) { # look for it in the hash
|
|
|
829 |
#print "Checking [$$pciHash{$key}{'name'}] eq [$$thisRow{'name'}]\n";
|
|
|
830 |
#print " [$$pciHash{$key}{'slot'}] eq [$$thisRow{'slot'}]\n\n";
|
|
|
831 |
if (
|
|
|
832 |
($$pciHash{$key}{'keyFieldValue'} eq $$thisRow{'name'})
|
|
|
833 |
&&
|
|
|
834 |
! defined ($$pciHash{$key}{'device_id'}) # this keeps us from ignoring a card when two are installed
|
|
|
835 |
) { # it is in the database and in pciHash
|
|
|
836 |
$deleteMe = ''; # so let's keep it
|
|
|
837 |
$$pciHash{$key}{'device_id'} = $$thisRow{'device_id'}; # and mark it as there
|
|
|
838 |
#print "\tfound equality at $$thisRow{'device_id'}\n";
|
|
|
839 |
last; # and exit the foreach loop
|
|
|
840 |
}
|
|
|
841 |
}
|
|
|
842 |
push @toDelete, $deleteMe if $deleteMe; # if we did not find it, mark for deletion
|
|
|
843 |
}
|
|
|
844 |
# remove stale items from the database
|
|
|
845 |
if (@toDelete) {
|
|
|
846 |
my $toDelete = join ",", @toDelete; # this is a list of device_id's
|
|
|
847 |
push @warnings, &createLogMessage( scalar(@toDelete) . " PCI Devices removed");
|
|
|
848 |
# remove from the device_attrib table
|
|
|
849 |
$sql = qq/update device_attrib set removed_date = $reportDate where device_id in ($toDelete)/;
|
|
|
850 |
# print "$sql\n";
|
|
|
851 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
852 |
# and from the device table itself
|
|
|
853 |
$sql = qq/update device set removed_date = $reportDate where device_id in ($toDelete)/;
|
|
|
854 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
855 |
}
|
|
|
856 |
undef @toDelete; # don't need this anymore
|
|
|
857 |
|
|
|
858 |
my $added = 0;
|
|
|
859 |
my $updated = 0;
|
|
|
860 |
# now, we have either inserts or updates
|
|
|
861 |
foreach $key (keys %$pciHash) {
|
|
|
862 |
unless ( $$pciHash{$key}{'device_id'} ) { # we did not find it in the database, so it is an insert
|
|
|
863 |
my $thisKey = &fixDatabaseValue($$pciHash{$key}{'keyFieldValue'});
|
|
|
864 |
$sql = qq/insert into device (site_id,device_type_id,name,part_of,added_date)
|
|
|
865 |
select site_id,device_type.device_type_id, $thisKey, device_id, $reportDate
|
|
|
866 |
from device,device_type
|
|
|
867 |
where device.device_id = $computerID
|
|
|
868 |
and device_type.name = 'PCI Card'/;
|
|
|
869 |
&GenericSQL::doSQL($dbh, $sql);
|
|
|
870 |
# get the inserted key
|
|
|
871 |
$$pciHash{$key}{'device_id'} = &GenericSQL::getOneValue($dbh, qq/select max(device_id) from device where part_of = $computerID and name = $thisKey and added_date = $reportDate/);
|
|
|
872 |
$added++;
|
|
|
873 |
} # unless
|
|
|
874 |
my $thisEntry = $$pciHash{$key};
|
|
|
875 |
$value = 0;
|
|
|
876 |
foreach my $subkey ( keys %$thisEntry ) {
|
|
|
877 |
# $test = $attributeMappings{$subkey} ? $attributeMappings{$subkey} : $subkey;
|
|
|
878 |
# print "checking $subkey [$$thisEntry{$subkey}]\n";
|
|
|
879 |
$value += &checkAndUpdateAttribute($$pciHash{$key}{'device_id'},
|
|
|
880 |
$attributeMappings{$subkey} ? $attributeMappings{$subkey} : $subkey,
|
|
|
881 |
$$thisEntry{$subkey} )
|
|
|
882 |
unless ($subkey eq 'device_id') or ($subkey eq 'keyFieldValue');
|
|
|
883 |
}
|
|
|
884 |
$updated++ if $value;
|
|
|
885 |
}
|
|
|
886 |
push @warnings, &createLogMessage("$added PCI Devices added") if $added;
|
|
|
887 |
push @warnings, &createLogMessage("$updated PCI Devices modified") if $updated;
|
|
|
888 |
}
|
|
|
889 |
|
|
|
890 |
|
|
|
891 |
# figure out the software_id and software_version_id of a package. Adds the package/version if
|
|
|
892 |
# it doesn't exist in the database
|
|
|
893 |
sub getSoftwareID {
|
|
|
894 |
my ( $packageName,$versionInfo,$description ) = @_;
|
|
|
895 |
#print "In getSoftwareID, paramters are [$packageName][$versionInfo][$description]\n";
|
|
|
896 |
#return;
|
|
|
897 |
# escape and quote the values for SQL
|
|
|
898 |
$packageName = &GenericSQL::fixStringValue($dbh, $packageName );
|
|
|
899 |
$versionInfo = &GenericSQL::fixStringValue($dbh, $versionInfo );
|
|
|
900 |
# does the package exist?
|
|
|
901 |
my $sql = qq/select software_id from software where package_name = $packageName and removed_date is null/;
|
|
|
902 |
my $result = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
903 |
unless ( $result ) { # NO, package doesn't exist, so add it to the database
|
|
|
904 |
$description = &GenericSQL::fixStringValue($dbh, $description );
|
|
|
905 |
$sql = qq/insert into software (package_name,description, added_date) values ($packageName,$description, $reportDate)/;
|
|
|
906 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
907 |
$sql = qq/select software_id from software where package_name = $packageName and removed_date is null/;
|
|
|
908 |
$result = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
909 |
}
|
|
|
910 |
# does this version number exist?
|
|
|
911 |
$sql = qq/select software_version_id from software_version where version = $versionInfo and removed_date is null/;
|
|
|
912 |
my $version = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
913 |
unless ( $version ) { # nope, so add it
|
|
|
914 |
$sql = qq/insert into software_version ( version,added_date ) values ($versionInfo,$reportDate)/;
|
|
|
915 |
&GenericSQL::doSQL( $dbh, $sql );
|
|
|
916 |
$sql = qq/select software_version_id from software_version where version = $versionInfo and removed_date is null/;
|
|
|
917 |
$version = &GenericSQL::getOneValue( $dbh, $sql );
|
|
|
918 |
}
|
|
|
919 |
return ($result,$version);
|
|
|
920 |
}
|
|
|
921 |
|
|
|
922 |
# process each package. We will only add entries if a package has changed, either version number
|
|
|
923 |
# added, or deleted. Deleted packages are not handled well right now.
|
|
|
924 |
sub processPackages {
|
|
|
925 |
my ($softwareHash) = @_;
|
|
|
926 |
my %softwareIDs;
|
|
|
927 |
my $count;
|
|
|
928 |
# since we go by software and version id's, let's just precalculate them
|
|
|
929 |
foreach my $package (keys %$softwareHash) {
|
|
|
930 |
# this will also insert the package and/or version in the software or software_version tables
|
|
|
931 |
($$softwareHash{$package}{'softwareid'},$$softwareHash{$package}{'versionid'}) =
|
|
|
932 |
&getSoftwareID( $package, $$softwareHash{$package}{'version'}, $$softwareHash{$package}{'description'}, $reportDate );
|
|
|
933 |
# this is just a shortcut for when we need to query
|
|
|
934 |
#$$softwareHash{$package}{'complexkey'} = $$softwareHash{$package}{'softwareid'} . '-' . $$softwareHash{$package}{'versionid'};
|
|
|
935 |
#push @installedPackages,$$softwareHash{$package}{'softwareid'};
|
|
|
936 |
$softwareIDs{$$softwareHash{$package}{'softwareid'}} = $$softwareHash{$package}{'versionid'};
|
|
|
937 |
}
|
|
|
938 |
# remove any software for this machine that no longer exists
|
|
|
939 |
my $temp = join( ',', grep { /^\d+$/ } keys %softwareIDs); # make sure we only have numerics
|
|
|
940 |
my $sql = "update installed_packages set removed_date = $reportDate where device_id = $computerID and removed_date is null and software_id not in ($temp)";
|
|
|
941 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
942 |
# ok, at this point, all software in the database exists in the computer
|
|
|
943 |
# now, lets see if there are any modified versions or something
|
|
|
944 |
$sql = qq/select installed_packages_id,software_id,software_version_id from installed_packages where device_id = $computerID and removed_date is null/;
|
|
|
945 |
my $sth = &GenericSQL::startQuery( $dbh, $sql );
|
|
|
946 |
#print "Before we start processing, we have " . (scalar keys %$diskHash) . " Items in hash\n";
|
|
|
947 |
while (my $thisRow = &GenericSQL::getNextRow($sth)) {
|
|
|
948 |
# if the version is the same, just do the next one
|
|
|
949 |
if ( $softwareIDs{$$thisRow{'software_id'}} == $$thisRow{'software_version_id'}) {
|
|
|
950 |
delete $softwareIDs{$$thisRow{'software_id'}};
|
|
|
951 |
} else { # we have a change. We simply remove the entry and let the "add new packages" section take care of it
|
|
|
952 |
$sql = qq/update installed_packages set removed_date = $reportDate where installed_packages_id = $$thisRow{'installed_packages_id'}/;
|
|
|
953 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
954 |
}
|
|
|
955 |
}
|
|
|
956 |
# at this point, the only items left in $softwareIDs are the packages that have changed or been added
|
|
|
957 |
$count = 0;
|
|
|
958 |
foreach my $softwareID ( keys %softwareIDs ) {
|
|
|
959 |
$count++;
|
|
|
960 |
$sql = qq/insert into installed_packages( device_id,software_id,software_version_id,added_date ) values
|
|
|
961 |
($computerID,$softwareID,$softwareIDs{$softwareID},$reportDate)/;
|
|
|
962 |
&GenericSQL::doSQL( $dbh, $sql);
|
|
|
963 |
}
|
|
|
964 |
push @warnings, &createLogMessage("$count Software Packages changed or added") if $count;
|
|
|
965 |
}
|
|
|
966 |
|
|
|
967 |
###############################################################################
|
|
|
968 |
# BEGIN MAIN ROUTINE
|
|
|
969 |
###############################################################################
|
|
|
970 |
BEGIN{
|
|
|
971 |
# load the configuration file
|
|
|
972 |
eval ( &loadConfigurationFile );
|
|
|
973 |
push @INC, $LIBRARIES;
|
|
|
974 |
}
|
|
|
975 |
|
|
|
976 |
use strict;
|
|
|
977 |
no strict 'vars';
|
|
|
978 |
use Data::Dumper;
|
|
|
979 |
use GenericSQL; # generic, home grown MySQL access routines
|
|
|
980 |
#use GenericTemplates;
|
|
|
981 |
use Logging; # generic, home grown logging routines
|
|
|
982 |
use Date::Format; # allows us to format our dates nicely
|
|
|
983 |
use Date::Parse; # VERY KEWL, parses out a huge number of date formats
|
|
|
984 |
|
|
|
985 |
|
|
|
986 |
$dbh = DBI->connect( $DSN, $DB_USER , $DB_PASS ) or die $DBI::errstr; # try to connect to db first
|
|
|
987 |
|
|
|
988 |
|
|
|
989 |
# read the input, parse it into useable information
|
|
|
990 |
my $data = &readAndParseInput;
|
|
|
991 |
#print Data::Dumper->Dump([$data]);
|
|
|
992 |
#die;
|
|
|
993 |
$reportDate = &dateToMySQL($$data{'report'}{'date'});
|
|
|
994 |
$clientName = $$data{'report'}{'client'};
|
|
|
995 |
$FATAL = 'No client name' unless $clientName;
|
|
|
996 |
$computerName = $$data{'system'}{'hostname'} unless $FATAL;
|
|
|
997 |
$FATAL = 'No computer name' unless $computerName;
|
|
|
998 |
# print STDERR "[$computerName]\n";
|
|
|
999 |
|
|
|
1000 |
|
|
|
1001 |
# try to figure out who the client is, creating if necessary
|
|
|
1002 |
$clientID = &getClientID( ) unless $FATAL;
|
|
|
1003 |
# try to figure out the computer ID, creating an entry if necessary
|
|
|
1004 |
$computerID = &getComputerID( ) unless $FATAL;
|
|
|
1005 |
# Ok, we have enough info, now let's make sure we aren't re-runing a report and record the current one.
|
|
|
1006 |
my $reportID = &recordReport( ) unless $FATAL;
|
|
|
1007 |
# we will simply verify memory, cpu, etc...
|
|
|
1008 |
&updateComputerMakeup($$data{'system'}) unless $FATAL;
|
|
|
1009 |
# check if the operating system has changed
|
|
|
1010 |
&updateOS( $$data{'operatingsystem'} ) unless $FATAL;
|
|
|
1011 |
# see if the machine has been rebooted and, if so, record it
|
|
|
1012 |
&updateBootTime ($$data{'system'}) unless $FATAL;
|
|
|
1013 |
# see what IP's this machine has
|
|
|
1014 |
&doIPAddresses($$data{'network'}) unless $FATAL;
|
|
|
1015 |
# Look at the disk usage, and report if they are above limits
|
|
|
1016 |
&processDisks($$data{'diskinfo'}) unless $FATAL;
|
|
|
1017 |
# and also if any hardware has changed
|
|
|
1018 |
&processPCI($$data{'pci'}) unless $FATAL;
|
|
|
1019 |
# see if any software packages have changed
|
|
|
1020 |
&processPackages($$data{'software'}) unless $FATAL;
|
|
|
1021 |
if ($FATAL) { # we had a fatal error, so just return it
|
|
|
1022 |
print "ERROR: $FATAL\n";
|
|
|
1023 |
exit 0;
|
|
|
1024 |
}
|
|
|
1025 |
# ok, work is done. If there are any values in $warnings, they should be either printed or e-mailed
|
|
|
1026 |
# to whoever the sysadmin is.
|
|
|
1027 |
if (@warnings) {
|
|
|
1028 |
my $warnings = join ("\n", @warnings);
|
|
|
1029 |
if ($iMailResults) {
|
|
|
1030 |
&sendmail( $mailFrom, $mailTo, 'Process Sysinfo Warnings', $warnings, $mailCC, $mailBCC, $mailServer, $mailServerPort );
|
|
|
1031 |
} else {
|
|
|
1032 |
print "$warnings\n";
|
|
|
1033 |
}
|
|
|
1034 |
}
|
|
|
1035 |
|
|
|
1036 |
exit 1;
|