Check the log table against the servies running on an object and
[gnucomo.git] / src / gcm_daemon / gcm_daemon.php
1 #!/usr/bin/php
2 <?PHP
3 /**********************************************************************************
4 **  (c) Copyright 2002, Brenno J.S.A.A.F. de Winter, De Winter Information Solutions
5 ** This is free software; you can redistribute it and/or modify it under the
6 ** terms of the GNU General Public License, see the file COPYING.
7 ***********************************************************************************/
8
9
10 /*
11    NAME          : gcm_daemon
12    AUTHOR        : Brenno J.S.A.A.F. de Winter
13                    De Winter Information Solutions
14    COPYRIGHT     : 2002 - De Winter Information Solutions,
15                    Brenno J.S.A.A.F. de Winter
16
17    * DATES *
18    First        : November 8th 2002
19    Gnucomo-0.0.3: December 6th 2002
20    Gnucomo-0.0.8: September 4th 2003
21
22  $Log: gcm_daemon.php,v $
23  Revision 1.16  2003-09-03 12:48:48  arjen
24  Check the log table against the servies running on an object and
25  create notifications if a service is not supposed to be available
26  or is not known at all.
27
28  Revision 1.15  2003/09/02 12:48:09  arjen
29  BUGFIX: Secondary indices on log_notification were unique.
30  Additional information in the 'usr' table: 'display_name' and 'email'.
31  Added new issues and services.
32
33  Revision 1.14  2003/09/01 06:51:07  arjen
34  Accept command argument '-c config' to use an alternate
35  gnucomo configuration.
36
37  Revision 1.13  2003/08/14 10:22:42  arjen
38  Disabled DEBUG output
39
40  Revision 1.12  2003/08/05 07:46:37  arjen
41  BUGFIX: Print an error message if a parameter does not have
42  any history.
43
44  Revision 1.11  2003/07/09 07:25:02  arjen
45  Gcm_daemon gathers statistics on parameters, notifications, etc. for all objects.
46
47  Revision 1.10  2003/03/29 08:33:58  arjen
48  In phpclasses/db.class.php: Added the database connection string as
49  an argument to the function copy_db_class.
50  Fixed the PHP member function db::db_connect(). The Postgres connection
51  string is now passed as an argument to that function.
52
53  Revision 1.9  2003/02/21 08:37:59  arjen
54  Added new table to the database: log_adv_daemon_email.
55
56
57 */
58
59 // $Id: gcm_daemon.php,v 1.16 2003-09-03 12:48:48 arjen Exp $
60
61 ini_set('include_path', '.:./classes:../phpclasses');
62 ini_set('html_errors', 'false');
63
64 //Tell the log that we're up.
65 define_syslog_variables();
66
67 require_once "gnucomo_config.php";
68 require_once "db.class.php";
69 require_once "gnucomo.process_log.php";
70
71 // Set the standard variables //
72
73 $project_name   = "gnucomo";    // name of the entire project
74 $app_name       = "gcm_daemon"; // name of the application running
75 $developrelease = "FALSE";      // Indicates if special debug settings are needed
76 $db_version     = 43;           // The db_version indicates what the level of
77                                 // the database should be. If the database is
78                                 // old an update will be generated.
79 $gcmd_version   = 5;            // This value indicates the active version of
80                                 // the gcm_daemon, which is saved in the database.
81                                 // Log records that were not recognized before
82                                 // will now be recognized. The version doesn't
83                                 // mean anything in the overall gnucomo project.
84
85 //Avoid time-limit issues
86 set_time_limit(0);
87
88 //  Scan the command arguments
89
90 for ($argi = 1; $argi < $argc; $argi++)
91 {
92    switch ($argv[$argi])
93    {
94    case "-c":
95       $argi++;
96       $project_name = $argv[$argi];
97       break;
98
99    default:
100       echo "Usage: gcm_daemon [-c configname]\n";
101       exit();
102       break;
103    }
104 }
105
106 // Read the database settings //
107 $class_settings = new gnucomo_config();
108 if (!$class_settings->read($project_name))
109 {
110    echo "Can not read Gnucomo configuration file for $project_name.\n";
111    exit();
112 }
113
114 openlog("gnucomo", LOG_PID, LOG_DAEMON);
115 syslog(LOG_INFO, "gcm_daemon started");
116
117 //Open an connection to the database
118 $dbms_type = $class_settings->find_parameter("database", "type");
119 $dbms_host = $class_settings->find_parameter("database", "host");
120 $dbms_name = $class_settings->find_parameter("database", "name");
121 $dbms_user = $class_settings->find_parameter("gcm_daemon", "user");
122 $dbms_password = $class_settings->find_parameter("gcm_daemon", "password");
123
124 db_select($dbms_type);
125 $dbms = new db();
126 $dbms->db_host = $dbms_host;
127 $dbms->db_name = $dbms_name;
128 $dbms->db_user = $dbms_user;
129 $dbms->db_password = $dbms_password;
130 $dbms->db_connect($class_settings->database());
131
132 if ($dbms->have_db_connection() == "FALSE")
133 {
134    exit ("Database connection failed.");
135 }
136 else
137 {
138    // The database connection has been made.
139    $dbms_working = copy_db_class($dbms, $class_settings->database());
140 }
141
142 // Verify if the database is up-to-date by checking the versionnumber
143
144 $local_sql = "SELECT setting_value FROM db_value WHERE setting = 'db_version' ";
145 $dbms->query($local_sql);
146
147 if ($dbms->fetch_row() == "TRUE")
148 {
149   $active_version = $dbms->db_result_row[0];
150
151   // Update the database to the most recent version.
152
153   if ($active_version < $db_version)
154   {
155      include ("gnucomo_db_version.php");
156   }
157 }
158 else
159 {
160   syslog (LOG_INFO, "Couldn't initialize database version. Is this a gnucomo database?");
161   die ("Couldn't initialize database version.\n");
162 }
163
164 // If there is a new gcm_daemon_version the logrecords that couldn't be
165 // understood can be reprocessed. For this reason processed is now changed
166 // to false again for not recognized records.
167
168 $local_sql = "SELECT setting_value FROM db_value
169               WHERE setting = 'gcm_daemon_version'";
170 $dbms->query($local_sql);
171
172 if ($dbms->fetch_row() == "TRUE")
173 {
174    if ($dbms->db_result_row[0] < $gcmd_version)
175    {
176       //Reactive log-records that weren't understood earlier.
177
178       $local_sql = "UPDATE log SET processed = false
179                     WHERE logid NOT IN (SELECT DISTINCT logid FROM log_adv)";
180       $dbms->query($local_sql);
181
182       //Update de gcm_daemon version in the database
183       $local_sql = "UPDATE db_value SET setting_value = '".$gcmd_version;
184       $local_sql .= "' WHERE setting = 'gcm_daemon_version'";
185       $dbms->query($local_sql);
186
187    }
188
189 }
190
191 // Now we loop the tasks that we have to do.
192
193
194 do
195 {
196
197    //At this place we start processing new log-lines
198
199    process_log ();
200    service_check();
201    find_notifications();
202
203    //  Gather the statistics for each object
204
205    $obj_result = $dbms->query("SELECT objectid FROM object");
206    for ($obj = 0; $obj < $dbms->num_rows($obj_result); $obj++)
207    {
208       $object = $dbms->fetch_object($obj_result, $obj);
209       echo "Gathering statistics for object " . $object->objectid . "\n";
210       GatherStatistics($object->objectid);
211    }
212
213    $keep_running = false;
214
215 } while ($keep_running == true);
216
217 //Tell the log that we're ending our efforts in a nice way
218
219 syslog (LOG_INFO, "gcm_daemon ended nicely");
220
221 function process_log ()
222 {
223
224  /* This function will walk through the log-records that haven't been processed
225   * first a snapshot will be created of a the non-processed records.
226   * sequentially each record will dealt with. By doing that changes will be made
227   * in several log_adv_xxx tables
228   * INPUT  : NONE
229   * OUTPUT : NONE
230   */
231
232   global $dbms;
233   global $dbms_working;
234   global $class_settings;
235
236   $last_log = 0;
237
238   // Find records in log that still have to be processed.
239
240   $local_sql = "SELECT setting_value FROM db_value WHERE setting = 'log_processing'";
241   $dbms->query($local_sql);
242
243   if ($dbms->fetch_row() == "TRUE")
244   {
245      $last_log = $dbms->db_result_row[0];
246   }
247
248   //Query the log-table
249   $local_sql = "SELECT * FROM log WHERE logid > CAST(".$last_log." AS BIGINT)
250                 ORDER BY logid";
251   $dbms->query($local_sql);
252
253   //Update the log-statistics in the object-table
254   $local_statistics_db = copy_db_class($dbms, $class_settings->database());
255   $local_findobject_db = copy_db_class($dbms, $class_settings->database());
256
257   //Make totals
258   $local_upper_row = $dbms->num_rows() + $last_log + 1;
259   $local_sql = "SELECT COUNT(logid), objectid from log WHERE logid > CAST(". $last_log .
260       " AS BIGINT) AND logid < CAST (" . $local_upper_row . " AS BIGINT) GROUP BY objectid";
261   $local_statistics_db->query ($local_sql);
262
263   // Loop the objects
264   for ($i = 1; $i <= $local_statistics_db->num_rows(); $i++)
265   {
266       $local_object_row = $local_statistics_db->fetch_row();
267
268       $local_sql = "UPDATE object SET log_count = log_count + " .
269           $local_statistics_db->db_result_row[0] . " WHERE objectid = '" .
270           $local_statistics_db->db_result_row[1] . "'";
271
272       $local_findobject_db->query($local_sql);
273   }
274
275   $local_counter = 0;
276
277   if ($dbms->num_rows() > 0)
278   {
279
280     //Create a database connection for changes in the database.
281     $dbms_changes = copy_db_class($dbms, $class_settings->database());
282     if ($dbms_changes->have_db_connection() == 'TRUE')
283     {
284
285        $local_sql               = 0 ;
286        $local_sql_statistics    = "";
287        $local_object_os         = "";
288        $local_object_os_version = "";
289
290        while ($local_counter < $dbms->num_rows())
291        {
292
293          $local_return_row = $dbms->fetch_row();
294          if ($local_return_row == 'TRUE')
295          {
296             // Work on active rows
297             $local_log_id = $dbms->db_result_row[0];
298
299             $local_sql_findobject = "SELECT os, os_version FROM object
300                                 WHERE objectid = '".$dbms->db_result_row[1]."'";
301             $local_findobject_db->query($local_sql_findobject);
302             $local_findobject_result = $local_findobject_db->fetch_row();
303             if ($local_findobject_result == 'TRUE')
304             {
305
306                 // Now work on the OS again
307                 $local_object_os = $local_findobject_db->db_result_row[0];
308                 if  ($local_object_os == "")
309                 {
310                     $local_object_os = "Linux";
311                     $local_object_os_version = "Unknown assuming Linux";
312                 }
313                 else
314                 {
315                   $local_object_os_version = $local_findobject_db->db_result_row[1];
316                 }
317              }
318
319             switch (strtolower($local_object_os))
320             {
321             case "linux":
322                 $local_process_return = linux_log ();
323                 break;
324             default:
325                 syslog (LOG_INFO, "Couldn't find suitable OS for processing the logline");
326                 break;
327             }
328
329             if ($local_process_return != 'TRUE')
330             {
331               $local_process_return = 'FALSE';
332             }
333
334          }
335          else
336          {
337
338            break;
339
340          }
341          $local_counter++;
342        }
343
344        // Register that the logrecords have been processed.
345        $local_sql = "UPDATE db_value SET setting_value = '"
346                    .$local_log_id."' where setting = 'log_processing'";
347        $dbms->query($local_sql);
348
349
350        // Update the statistics for the object-table
351
352
353      }
354      else
355      {
356        syslog (LOG_INFO, "Couldn't clone database connection.");
357        die ("Couldn't reconnect to the database.\n");
358      }
359   }
360
361 }
362
363 /*
364  *   Update a single statistic for some object.
365  *   If it does not yet exist, it will be created.
366  */
367
368 function UpdateStatistic($objectid, $name, $value)
369 {
370    global $dbms;
371
372    $result = $dbms->query("SELECT objectid FROM object_statistics WHERE
373              objectid='$objectid' AND statname='$name'");
374    if ($dbms->num_rows() == 0)
375    {
376       $dbms->query("INSERT INTO object_statistics VALUES
377                     ('$objectid', '$name', '$value')");
378    }
379    else
380    {
381       $dbms->query("UPDATE object_statistics SET statvalue='$value' WHERE
382            statname='$name' AND objectid='$objectid'");
383    }
384 }
385
386 /*
387  *   Gather the statistics for a single object ($objectid).
388  *   We count the number of parameters, removed parameters, notifications
389  *   closed notifications and log entries. The totals of these are
390  *   maintained in a separate table: object_statistics.
391  */
392
393 function GatherStatistics($objectid)
394 {
395    global $dbms;
396
397    //  Gather statistics on parameters
398
399    $r = $dbms->query("SELECT paramid FROM parameter WHERE objectid=CAST('"
400                         . $objectid . "' AS BIGINT)");
401    $nr_parameters = $dbms->num_rows($r);
402
403    $removed_parameters = 0;
404    for ($p = 0; $p < $nr_parameters; $p++)
405    {
406       $param = pg_fetch_object($r, $p);
407       $qry ="select change_nature from history where paramid= CAST('";
408       $qry .= $param->paramid . "' AS BIGINT) order by modified desc";
409       $rhist = $dbms->query($qry);
410       if ($dbms->num_rows($rhist) == 0)
411       {
412          echo "ERROR: No history for parameter id " . $param->paramid . "\n";
413       }
414       else
415       {
416          $hist = $dbms->fetch_object($rhist, 0);
417          if ($hist->change_nature == "REMOVED")
418          {
419             $removed_parameters++;
420          }
421       }
422    }
423
424    UpdateStatistic($objectid, 'parameters', $nr_parameters);
425    UpdateStatistic($objectid, 'removed_parameters', $removed_parameters);
426
427    //  Gather statistics on notifications
428
429    $r = $dbms->query("SELECT count(notificationid) FROM notification WHERE
430                        objectid = CAST('" . $objectid . "' AS BIGINT)");
431    $cnt = $dbms->fetch_object($r, 0);
432    UpdateStatistic($objectid, 'notifications', $cnt->count);
433
434    $r = $dbms->query("SELECT count(notificationid) FROM notification WHERE
435                        objectid = CAST('" . $objectid . "' AS BIGINT) AND statuscode ='cls'");
436    $cnt = $dbms->fetch_object($r, 0);
437    UpdateStatistic($objectid, 'closed_notifications', $cnt->count);
438
439    //  Gather statistics on log entries
440
441    $r = $dbms->query("SELECT count(logid) FROM log WHERE
442                        objectid = CAST('" . $objectid . "' AS BIGINT)");
443    $cnt = $dbms->fetch_object($r, 0);
444    UpdateStatistic($objectid, 'logs', $cnt->count);
445 }
446
447 /*
448  *   Service_check - Check the log entries if there are any unknown
449  *   services.
450  */
451
452 function service_check()
453 {
454    global  $dbms;
455
456    $unknown_notification = 0;
457    $unused_notification  = 0;
458    $last_log             = 0;
459
460    //  How far did we get last time ?
461
462    $lastlogres = $dbms->query("SELECT setting_value FROM db_value
463                                WHERE setting = 'log_servicecheck'");
464
465    if ($dbms->num_rows($lastlogres) == 1)
466    {
467      $last_log = $dbms->Field($lastlogres, 0, 'setting_value');
468    }
469    else
470    {
471       $dbms->query("INSERT INTO db_value (setting, setting_value)
472                             VALUES ('log_servicecheck', '0')");
473    }
474
475    // Query the log-table
476
477    $qry = "SELECT logid, objectid, servicecode FROM log
478            WHERE logid > CAST(".$last_log." AS BIGINT) ORDER BY logid";
479    $log_res = $dbms->query($qry);
480    //$log_res = $dbms->query("SELECT logid, objectid, servicecode,rawdata FROM log");
481
482    for ($log_row = 0; $log_row < $dbms->num_rows($log_res); $log_row++)
483    {
484       $log_entry = $dbms->fetch_object($log_res, $log_row);
485       $last_log  = $log_entry->logid;
486
487       //   Check if the service is used on the object.
488
489       $qry = "SELECT * FROM object_service WHERE objectid='";
490       $qry .= $log_entry->objectid . "' AND servicecode='";
491       $qry .= $log_entry->servicecode . "'";
492
493       $os_res = $dbms->query($qry);
494       if ($dbms->num_rows($os_res) == 0)
495       {
496          //   Service is not found for the object, check if the service
497          //   exists at all.
498
499          $qry = "SELECT * FROM service WHERE servicecode='";
500          $qry .= $log_entry->servicecode . "'";
501
502          if ($dbms->num_rows($dbms->query($qry)) == 0)
503          {
504             if ($unknown_notification == 0)
505             {
506                $remark = "One or more log entries from a service that is not in the database";
507                $unknown_notification = $dbms->new_notification($log_entry->objectid, 'service unknown', $remark);
508             }
509             if ($unknown_notification != 0)
510             {
511                $insertion = "INSERT INTO log_notification (notificationid, logid) VALUES ('";
512                $insertion .= $unknown_notification . "', '";
513                $insertion .= $log_entry->logid . "')";
514                $dbms->query($insertion);
515             }
516          }
517          else
518          {
519             if ($unused_notification == 0)
520             {
521                $remark = "One or more log entries from a service not running on this object";
522                $unused_notification = $dbms->new_notification($log_entry->objectid, 'service not used', $remark);
523             }
524             if ($unused_notification != 0)
525             {
526                $insertion = "INSERT INTO log_notification (notificationid, logid) VALUES ('";
527                $insertion .= $unused_notification . "', '";
528                $insertion .= $log_entry->logid . "')";
529                $dbms->query($insertion);
530             }
531          }
532       }
533    }
534
535    $qry = "UPDATE db_value SET setting_value = '"
536                    . $last_log . "' WHERE setting = 'log_servicecheck'";
537    $dbms->query($qry);
538 }
539
540 function find_notifications ()
541 {
542
543 /*
544  *  Do something with notification checks.
545  *
546  * INPUT  : NONE
547  * OUTPUT : NONE
548  */
549
550    global $dbms;
551
552    // Find checks that have to be executed.
553    $local_sql = "select * from notification_check where
554                     age(last_execution) > time_between_executions";
555    $dbms->query($local_sql);
556
557    for ($i=0; $i<$dbms->num_rows(); $i++)
558    {
559       // A check has been found that has to be executed
560       $dbms->fetch_row();
561    }
562 }
563
564 ?>
565