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