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