b1c50e67b8314e6090f414d14c3e9b62e607d81a
[gnucomo.git] / src / gcm_input / gcm_input.cpp
1 /**************************************************************************
2 **  (c) Copyright 2002, Andromeda Technology & Automation
3 ** This is free software; you can redistribute it and/or modify it under the
4 ** terms of the GNU General Public License, see the file COPYING.
5 ***************************************************************************
6 ** MODULE INFORMATION *
7 ***********************
8 **      FILE NAME      : gcm_input.cpp
9 **      SYSTEM NAME    : Gnucomo - Gnu Computer Monitoring
10 **      VERSION NUMBER : $Revision: 1.13 $
11 **
12 **  DESCRIPTION      :  Application to store client messages into the database
13 **                      The client message contains a log file from one of the
14 **                      system logs. Gcm_input parses the log file and enters
15 **                      the raw data into the 'log' table of the gnucomo database.
16 **
17 **                      The system log may arrive in one of several forms:
18 **                      1. Log file (archived or not) from a client machine.
19 **                      2. Log file from a client system, arriving in a clear Email.
20 **                      3. Log file from a client machine, arriving in an
21 **                         encrypted Email.
22 **
23 **                      additional information we need that may not be availble in
24 **                      the content of the log is:
25 **                      - FQDN of the client.
26 **                      - Time of arrival of the log
27 **                      - Service that created the log.
28 **
29 **                      If the log arrives in an Email, these items probably are
30 **                      available in the mail header. Otherwise, they have to be
31 **                      provided as command line arguments.
32 **
33 **                      Command line arguments:
34 **                      -c <name>        Configuration name (default = gnucomo).
35 **                      -d <date>        Date and time of log arrival.
36 **                      -h <hostname>    FQDN of the client.
37 **                      -i               Incremental - partial list of parameters.
38 **                      -s <service>     Service that created the log.
39 **                      -T               Test mode. Do not alter the database.
40 **                      -v               Verbose (debug) output.
41 **                      -V               Print version and exit.
42 **
43 **  EXPORTED OBJECTS : 
44 **  LOCAL    OBJECTS : 
45 **  MODULES  USED    :
46 ***************************************************************************
47 **  ADMINISTRATIVE INFORMATION *
48 ********************************
49 **      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
50 **      CREATION DATE   : Aug 29, 2002
51 **      LAST UPDATE     : Nov 26, 2003
52 **      MODIFICATIONS   : 
53 **************************************************************************/
54
55 /*****************************
56    $Log: gcm_input.cpp,v $
57    Revision 1.13  2003-12-22 10:20:21  arjen
58    Report if the message type can not be detected.
59
60    Revision 1.12  2003/12/04 10:38:09  arjen
61    Major redesign. All input is handled through XML. Raw input data is first
62    transformed into an XML document for further processing.
63    A collection of polymorphic classes handle the transformation of various
64    input formats into XML.
65    Classifying input data is done with a finite improbability calculation.
66
67    Revision 1.11  2003/10/27 13:00:15  arjen
68    Catch exceptions from the database library
69
70    Revision 1.10  2003/09/03 06:58:31  arjen
71    Changed version string to 0.0.8
72
73    Revision 1.9  2003/09/01 06:59:26  arjen
74    A date without the time for the '-d <date> option will
75    assume midnight on that date.
76
77    Revision 1.8  2003/08/16 15:30:19  arjen
78    Fixed a gcc 2 vs. gcc 3 problem
79
80    Revision 1.7  2003/08/14 10:28:37  arjen
81    Use parameters from a new section 'logging' with three configuration parameters:
82       method       - Output method to use for logging.
83       destination  - Name of the log output destination.
84       level        - Log level: Verbose output if greater than 0.
85
86    Revision 1.6  2003/08/11 16:56:16  arjen
87    Different kinds of log files are parsed by a collection of objects
88    of different classes, derived from the base class line_cooker
89    Depending on the message content or the message_type element in
90    XML, one of these objects is selected.
91
92    Logrunner is integrated with gcm_input. Although its functionality
93    is still limited, a connection between logrunner and gcm_input
94    is beginning to form.
95
96    Revision 1.5  2003/08/05 08:11:06  arjen
97    Added two configuration parameters:
98       logfile   - Log to this file instead of stderr.
99       verbosity - Verbose output if greater than 0.
100    Added '-i' option for incremental parameter updates
101
102    Revision 1.4  2003/03/29 08:42:00  arjen
103    Exit without reading any input if the database connection fails.
104
105    Revision 1.3  2002/11/09 08:04:27  arjen
106    Added a reference to the GPL
107
108    Revision 1.2  2002/11/04 10:13:36  arjen
109    Use proper namespace for iostream classes
110
111    Revision 1.1  2002/10/05 10:25:49  arjen
112    Creation of gcm_input and a first approach to a web interface
113
114 *****************************/
115
116 static const char *RCSID = "$Id: gcm_input.cpp,v 1.13 2003-12-22 10:20:21 arjen Exp $";
117
118 #include <fstream>
119
120 #include <getopt.h>
121
122 #include "message.h"
123 #include "log_filter.h"
124 #include "rpm_filter.h"
125
126 #include "xml_cooker.h"
127 #include "syslog_cooker.h"
128 #include "irix_syslog_cooker.h"
129 #include "access_cooker.h"
130 #include "error_cooker.h"
131
132 bool verbose = false;
133 bool testmode = false;
134 bool incremental = false;
135 std::ostream *Log = &std::cerr;
136
137 static char *Version = "gcm_input version 0.0.9 - Dec 22, 2003";
138
139
140 /*=========================================================================
141 **  NAME           : main
142 **  SYNOPSIS       : int main(int argc, char *argv[])
143 **  PARAMETERS     : 
144 **  RETURN VALUE   : 
145 **
146 **  DESCRIPTION    : Parse command line arguments and establish a connection
147 **                   to the database.
148 **                   When we have a database connection, parse the log file
149 **                   from stdin.
150 **
151 **  VARS USED      :
152 **  VARS CHANGED   :
153 **  FUNCTIONS USED :
154 **  SEE ALSO       :
155 **  LAST MODIFIED  : Aug 11, 2003
156 **=========================================================================
157 */
158
159 int main(int argc, char *argv[])
160 {
161    const char *usage = "Usage: gcm_input [-c configname] [-h hostname] [-i] [-d date]"
162                        " [-s service] [-T] [-v] [-V]\n";
163
164    gnucomo_config    cfg;
165    char             *config_name = "gnucomo";
166    std::ofstream    logfile;
167
168
169    /*   Parse command line arguments */
170
171    UTC    arrival = Now();
172    String  hostname(""), service("");
173    int     option;
174
175
176    while ((option = getopt(argc, argv, "c:h:d:s:iTvV")) != -1)
177    {
178       switch (option)
179       {
180       case 'c':
181          config_name = optarg;
182          break;
183
184       case 'h':
185          hostname = optarg;
186          break;
187
188       case 'd':
189          arrival = String(optarg);
190          if (!date(arrival).proper())
191          {
192             std::cerr << "gcm_input: Invalid date string: " << optarg
193                       << "(" << arrival << ").\n";
194             exit(1);
195          }
196          else if (!hour(arrival).proper())
197          {
198             arrival = UTC(date(arrival), hour(0,0,0));
199          }
200          break;
201
202       case 's':
203          service = optarg;
204          break;
205
206       case 'i':
207          incremental = true;
208          break;
209
210       case 'T':
211          testmode = true;
212          break;
213
214       case 'v':
215          verbose = true;
216          break;
217
218       case 'V':
219          std::cout << Version << "\n";
220          exit(0);
221
222       case '?':
223       case ':':
224          std::cerr << usage;
225          exit(1);
226       }
227    }
228    /*  Get the configuration file */
229
230    if (!cfg.read(config_name))
231    {
232       std::cerr << "Can not read Gnucomo configuration file for " << config_name << ".\n";
233       exit(1);
234    }
235
236    String log_method      = cfg.find_parameter("logging", "method");
237    String log_destination = cfg.find_parameter("logging", "destination");
238    int    level           = cfg.find_parameter("logging", "level");
239
240    if (log_method == "file" && log_destination != "")
241    {
242 #if __GNUC__ == 2
243       logfile.open(log_destination, _IO_APPEND); // for gcc 2
244 #else
245       logfile.open(log_destination, std::ios_base::app); // for gcc 3
246 #endif
247       if (!logfile)
248       {
249          std::cerr << "Can't open logfile " << log_destination << " for writing.\n";
250       }
251       else
252       {
253          Log = &logfile;
254       }
255    }
256    verbose = verbose || level > 0;
257
258    if (verbose)
259    {
260       *Log << "Hostname = " << hostname;
261       *Log << " Arrival = " << arrival;
262       *Log << " Service = " << service << "\n";
263       *Log << "Config OK.\n";
264    }
265
266    /*  Try to connect to the database */
267
268    gnucomo_database db(&cfg);
269
270    if (db.is_connected())
271    {
272
273       client_message      msg(&std::cin, db);
274
275       message_filter      shortcircuit(hostname, arrival, service);
276       log_filter          lf(hostname, arrival, service);
277       rpm_filter          rf(hostname, arrival, service);
278
279       syslog_cooker       slc;
280       irix_syslog_cooker  islc;
281       access_cooker       alc;
282       error_cooker        elc;
283       xml_cooker          xlc;
284       rpm_cooker          rlc;
285
286       msg.add_cooker(&xlc,  &shortcircuit);
287       msg.add_cooker(&slc,  &lf);
288       msg.add_cooker(&islc, &lf);
289       msg.add_cooker(&alc,  &lf);
290       msg.add_cooker(&elc,  &lf);
291       msg.add_cooker(&rlc,  &rf);
292
293       if (msg.classify(hostname, arrival, service) > 0.9)
294       {
295          try
296          {
297             msg.enter();
298          }
299          catch (std::exception &e)
300          {
301             *Log << "Caught an exception: " << e.what() << "\n";
302          }
303       }
304       else
305       {
306          *Log << "Cannot determine message type with sufficient certainty.\n";
307       }
308       return 0;
309    }
310    else
311    {
312       *Log << "gcm_input: Can not connect to database.\n";
313       *Log << "Gcm_input finished at " << Now() << ".\n";
314       return 1;
315    }
316 }
317