Bugfix: Segmentation fault when reading an rpm package list
[gnucomo.git] / src / gcm_input / gcm_input.cpp
index 51fa9b7..4717f22 100644 (file)
@@ -1,12 +1,13 @@
-
 /**************************************************************************
 **  (c) Copyright 2002, Andromeda Technology & Automation
+** This is free software; you can redistribute it and/or modify it under the
+** terms of the GNU General Public License, see the file COPYING.
 ***************************************************************************
 ** MODULE INFORMATION *
 ***********************
 **      FILE NAME      : gcm_input.cpp
 **      SYSTEM NAME    : Gnucomo - Gnu Computer Monitoring
-**      VERSION NUMBER : $Revision: 1.1 $
+**      VERSION NUMBER : $Revision: 1.15 $
 **
 **  DESCRIPTION      :  Application to store client messages into the database
 **                      The client message contains a log file from one of the
@@ -33,6 +34,7 @@
 **                      -c <name>        Configuration name (default = gnucomo).
 **                      -d <date>        Date and time of log arrival.
 **                      -h <hostname>    FQDN of the client.
+**                      -i               Incremental - partial list of parameters.
 **                      -s <service>     Service that created the log.
 **                      -T               Test mode. Do not alter the database.
 **                      -v               Verbose (debug) output.
 ********************************
 **      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
 **      CREATION DATE   : Aug 29, 2002
-**      LAST UPDATE     : Sep 30, 2002
+**      LAST UPDATE     : Nov 26, 2003
 **      MODIFICATIONS   : 
 **************************************************************************/
 
 /*****************************
    $Log: gcm_input.cpp,v $
-   Revision 1.1  2002-10-05 10:25:49  arjen
+   Revision 1.15  2007-11-14 16:19:25  arjen
+   Bugfix: Segmentation fault when reading an rpm package list
+   with empty lines.
+
+   Revision 1.14  2007/11/03 10:26:13  arjen
+   Added a new filter which can directly read the output
+   of the UNIX df command. A brief description is added in
+   the user manual.
+
+   Revision 1.13  2003/12/22 10:20:21  arjen
+   Report if the message type can not be detected.
+
+   Revision 1.12  2003/12/04 10:38:09  arjen
+   Major redesign. All input is handled through XML. Raw input data is first
+   transformed into an XML document for further processing.
+   A collection of polymorphic classes handle the transformation of various
+   input formats into XML.
+   Classifying input data is done with a finite improbability calculation.
+
+   Revision 1.11  2003/10/27 13:00:15  arjen
+   Catch exceptions from the database library
+
+   Revision 1.10  2003/09/03 06:58:31  arjen
+   Changed version string to 0.0.8
+
+   Revision 1.9  2003/09/01 06:59:26  arjen
+   A date without the time for the '-d <date> option will
+   assume midnight on that date.
+
+   Revision 1.8  2003/08/16 15:30:19  arjen
+   Fixed a gcc 2 vs. gcc 3 problem
+
+   Revision 1.7  2003/08/14 10:28:37  arjen
+   Use parameters from a new section 'logging' with three configuration parameters:
+      method       - Output method to use for logging.
+      destination  - Name of the log output destination.
+      level        - Log level: Verbose output if greater than 0.
+
+   Revision 1.6  2003/08/11 16:56:16  arjen
+   Different kinds of log files are parsed by a collection of objects
+   of different classes, derived from the base class line_cooker
+   Depending on the message content or the message_type element in
+   XML, one of these objects is selected.
+
+   Logrunner is integrated with gcm_input. Although its functionality
+   is still limited, a connection between logrunner and gcm_input
+   is beginning to form.
+
+   Revision 1.5  2003/08/05 08:11:06  arjen
+   Added two configuration parameters:
+      logfile   - Log to this file instead of stderr.
+      verbosity - Verbose output if greater than 0.
+   Added '-i' option for incremental parameter updates
+
+   Revision 1.4  2003/03/29 08:42:00  arjen
+   Exit without reading any input if the database connection fails.
+
+   Revision 1.3  2002/11/09 08:04:27  arjen
+   Added a reference to the GPL
+
+   Revision 1.2  2002/11/04 10:13:36  arjen
+   Use proper namespace for iostream classes
+
+   Revision 1.1  2002/10/05 10:25:49  arjen
    Creation of gcm_input and a first approach to a web interface
 
 *****************************/
 
-static const char *RCSID = "$Id: gcm_input.cpp,v 1.1 2002-10-05 10:25:49 arjen Exp $";
+static const char *RCSID = "$Id: gcm_input.cpp,v 1.15 2007-11-14 16:19:25 arjen Exp $";
+
+#include <fstream>
 
 #include <getopt.h>
 
 #include "message.h"
+#include "log_filter.h"
+#include "rpm_filter.h"
+#include "df_filter.h"
+
+#include "xml_cooker.h"
+#include "syslog_cooker.h"
+#include "irix_syslog_cooker.h"
+#include "access_cooker.h"
+#include "error_cooker.h"
 
 bool verbose = false;
 bool testmode = false;
+bool incremental = false;
+std::ostream *Log = &std::cerr;
 
-static char *Version = "gcm_input version 0.0.3 - Sep 30, 2002";
+static char *Version = "gcm_input version 0.0.11 - Nov 22, 2007";
 
 
 /*=========================================================================
@@ -84,17 +162,19 @@ static char *Version = "gcm_input version 0.0.3 - Sep 30, 2002";
 **  VARS CHANGED   :
 **  FUNCTIONS USED :
 **  SEE ALSO       :
-**  LAST MODIFIED  : Sep 30, 2002
+**  LAST MODIFIED  : Aug 11, 2003
 **=========================================================================
 */
 
 int main(int argc, char *argv[])
 {
-   const char *usage = "Usage: gcm_input [-c configname] [-h hostname] [-d date]"
+   const char *usage = "Usage: gcm_input [-c configname] [-h hostname] [-i] [-d date]"
                        " [-s service] [-T] [-v] [-V]\n";
 
    gnucomo_config    cfg;
    char             *config_name = "gnucomo";
+   std::ofstream    logfile;
+
 
    /*   Parse command line arguments */
 
@@ -102,8 +182,8 @@ int main(int argc, char *argv[])
    String  hostname(""), service("");
    int     option;
 
-   while ((option = getopt(argc, argv, "c:h:d:s:TvV")) != -1)
+
+   while ((option = getopt(argc, argv, "c:h:d:s:iTvV")) != -1)
    {
       switch (option)
       {
@@ -117,17 +197,26 @@ int main(int argc, char *argv[])
 
       case 'd':
          arrival = String(optarg);
-         if (!arrival.proper())
+         if (!date(arrival).proper())
          {
-            cerr << "gcm_input: Invalid date string: " << optarg << ".\n";
+            std::cerr << "gcm_input: Invalid date string: " << optarg
+                      << "(" << arrival << ").\n";
             exit(1);
          }
+         else if (!hour(arrival).proper())
+         {
+            arrival = UTC(date(arrival), hour(0,0,0));
+         }
          break;
 
       case 's':
          service = optarg;
          break;
 
+      case 'i':
+         incremental = true;
+         break;
+
       case 'T':
          testmode = true;
          break;
@@ -137,47 +226,108 @@ int main(int argc, char *argv[])
          break;
 
       case 'V':
-         cout << Version << "\n";
+         std::cout << Version << "\n";
          exit(0);
 
       case '?':
       case ':':
-         cerr << usage;
+         std::cerr << usage;
          exit(1);
       }
    }
+   /*  Get the configuration file */
 
-   if (verbose)
+   if (!cfg.read(config_name))
    {
-      cout << "Hostname = " << hostname;
-      cout << " Arrival = " << arrival;
-      cout << " Service = " << service << "\n";
+      std::cerr << "Can not read Gnucomo configuration file for " << config_name << ".\n";
+      exit(1);
    }
 
-   /*  Get the configuration file */
+   String log_method      = cfg.find_parameter("logging", "method");
+   String log_destination = cfg.find_parameter("logging", "destination");
+   int    level           = cfg.find_parameter("logging", "level");
 
-   if (!cfg.read(config_name))
+   if (log_method == "file" && log_destination != "")
    {
-      cerr << "Can not read Gnucomo configuration file for " << config_name << ".\n";
-      exit(1);
+#if __GNUC__ == 2
+      logfile.open(log_destination, _IO_APPEND); // for gcc 2
+#else
+      logfile.open(log_destination, std::ios_base::app); // for gcc 3
+#endif
+      if (!logfile)
+      {
+         std::cerr << "Can't open logfile " << log_destination << " for writing.\n";
+      }
+      else
+      {
+         Log = &logfile;
+      }
    }
+   verbose = verbose || level > 0;
 
    if (verbose)
    {
-      cout << "Config OK.\n";
+      *Log << "Hostname = " << hostname;
+      *Log << " Arrival = " << arrival;
+      *Log << " Service = " << service << "\n";
+      *Log << "Config OK.\n";
    }
 
    /*  Try to connect to the database */
 
    gnucomo_database db(&cfg);
 
-   client_message  msg(&cin, db);
+   if (db.is_connected())
+   {
 
-   if (msg.classify(hostname, arrival, service) > 0.9)
+      client_message      msg(&std::cin, db);
+
+      double              message_probability;
+
+      message_filter      shortcircuit(hostname, arrival, service);
+      log_filter          lf(hostname, arrival, service);
+      rpm_filter          rf(hostname, arrival, service);
+      df_filter           df(hostname, arrival, service);
+
+      syslog_cooker       slc;
+      irix_syslog_cooker  islc;
+      access_cooker       alc;
+      error_cooker        elc;
+      xml_cooker          xlc;
+      rpm_cooker          rlc;
+      df_cooker           dlc;
+
+      msg.add_cooker(&xlc,  &shortcircuit);
+      msg.add_cooker(&slc,  &lf);
+      msg.add_cooker(&islc, &lf);
+      msg.add_cooker(&alc,  &lf);
+      msg.add_cooker(&elc,  &lf);
+      msg.add_cooker(&rlc,  &rf);
+      msg.add_cooker(&dlc,  &df);
+
+      message_probability = msg.classify(hostname, arrival, service);
+      if (message_probability > 0.75)
+      {
+         try
+         {
+            msg.enter();
+         }
+         catch (std::exception &e)
+         {
+            *Log << "Caught an exception: " << e.what() << "\n";
+         }
+      }
+      else
+      {
+         *Log << "Cannot determine message type with sufficient certainty.\n";
+      }
+      return 0;
+   }
+   else
    {
-      msg.enter();
+      *Log << "gcm_input: Can not connect to database.\n";
+      *Log << "Gcm_input finished at " << Now() << ".\n";
+      return 1;
    }
-
-   return 0;
 }