3 * (c) Peter Roozemaal, feb 2003
5 * $Id: logrunner.cpp,v 1.2 2005-05-31 05:47:33 arjen Exp $
8 * 2) create 'logrunner.conf' containing a list of filenames and types of the
9 * logfiles to scan; one file per line:
10 * <gnucomo_messagetype> TAB <logfilename>
12 * syslog /var/log/messages
13 * syslog /var/log/secure
16 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
30 #include <AXE/String.h>
32 extern String XML_Entities(String s);
34 static const char* const usage =
35 "Usage: logrunner [<options>] [<hostname>]\n\n"
36 "<hostname> is the gnucomo data collection server\n"
38 " -1: one-shot; stop after a single pass over one of the configured logfiles\n"
39 " -p <number>: set IP port number to connect to\n"
40 " -c <file>: specify alternative configuration file, default is \'logrunner.conf\'\n"
41 "If no hostname is specified, logrunner sends output to stdout\n";
44 struct fileinfo* next;
45 char* name; /* strdupped name */
46 char* type; /* strdupped logtype */
52 String confname = "/etc/logrunner.conf";
53 static const char* statusname = "/var/lib/logrunner.status";
54 static const char* newstatusname = "/var/lib/logrunner.status.new";
56 static const char *hostname = NULL;
57 static int port = 2996; /* random magic number */
58 static int out_stream = 1;
60 static int oneshot = 0;
62 static struct fileinfo* filelist = NULL;
64 static int something_has_changed = 0;
66 static volatile sig_atomic_t sig_seen = 0;
68 static void sighandler(int sig)
73 static void set_signal_handler(void)
75 /* These aren't all; but it's a nice set to start with */
76 signal(SIGHUP, sighandler);
77 signal(SIGINT, sighandler);
78 signal(SIGQUIT, sighandler);
79 signal(SIGABRT, sighandler);
80 signal(SIGPIPE, sighandler);
81 signal(SIGTERM, sighandler);
86 struct hostent* hostptr;
87 struct sockaddr_in addr;
88 unsigned int namelen = sizeof(addr);
92 hostptr = gethostbyname(hostname);
95 fprintf(stderr, "logrunner: FATAL: cannot resolve %s\n", hostname);
98 out_stream = socket(PF_INET, SOCK_STREAM, 0);
101 fprintf(stderr, "logrunner: FATAL: Socket creation failed\n");
104 addr.sin_family = AF_INET;
105 addr.sin_addr.s_addr = *((long*)(hostptr->h_addr));
106 addr.sin_port = htons(port);
107 if ( connect(out_stream, (struct sockaddr*) &addr, namelen) < 0 )
109 fprintf(stderr, "logrunner: FATAL: connect to %s failed\n", hostname);
119 void xsend(const char* str)
121 write(out_stream, str, strlen(str));
124 void xml_header(struct fileinfo *f)
128 gethostname(buffer, sizeof(buffer));
129 xsend("<?xml version='1.0'?>\n");
130 xsend("<gcmt:message xmlns:gcmt=\"http://gnucomo.org/transport/\">\n");
131 xsend("<gcmt:header>\n<gcmt:hostname>");
133 xsend("</gcmt:hostname>\n<gcmt:messagetype>");
135 xsend("</gcmt:messagetype>\n</gcmt:header>\n");
136 xsend("<gcmt:data><gcmt:log>");
139 void xml_footer(void)
141 xsend("</gcmt:log></gcmt:data>\n</gcmt:message>\n");
144 void output(const char* str)
149 void output_error(const char* str)
151 std::cerr << str << " errno=" << errno << "\n";
154 void write_status_file(struct fileinfo* fl)
158 dumpfile = fopen(newstatusname, "w");
162 sprintf(error, ", errno=%d\n", errno);
163 output("!!! dumpstatus: open failed: ");
164 output(newstatusname);
166 something_has_changed = 0;
171 if ( fprintf(dumpfile, "%s %li %lu\n",
172 fl->name, (long) fl->inode,
173 (unsigned long) fl->position) < 0 )
175 output_error("!!! dumpstatus: write failed");
181 if ( fclose(dumpfile) && localerror == 0 )
183 output_error("!!! dumpstatus: close failed");
186 if ( localerror == 0 )
188 if ( rename(newstatusname, statusname) )
190 output_error("!!! dumpstatus: rename failed");
193 something_has_changed = 0;
205 statusfile = fopen(statusname, "r");
206 if ( statusfile == NULL )
208 fprintf(stderr, "logrunner: can\'t open status file \'%s\': ",
213 while ( fgets(buffer, sizeof(buffer), statusfile ) )
215 xp = strchr(buffer, ' ');
219 sscanf(xp+1, "%ld %lu", &ino, &pos);
223 if ( strcmp(buffer, fp->name) == 0 )
236 struct fileinfo* new_info()
239 rv = (struct fileinfo *)malloc(sizeof(struct fileinfo));
254 /* The config file now is a simple list of type, filenames;
255 * it should be more user-friendly in the future!
260 conffile = fopen(confname, "r");
263 std::cerr << "logrunner: can\'t open configuration file \'" << confname << "\': ";
267 while ( fgets(buffer, sizeof(buffer), conffile) )
269 int len = strlen(buffer);
271 if ( buffer[len-1] == '\n' )
275 fname = strchr(buffer, '\t');
283 fi->name = strdup(fname);
284 fi->type = strdup(buffer);
290 fprintf(stderr, "logrunner: out of memory\n");
296 fprintf(stderr, "logrunner: WARNING: bad config line: %s\n", buffer);
302 void copy_data(struct fileinfo *f)
307 /* read data and dump to output */
308 ndata = read(f->fd, buffer, sizeof(buffer));
314 // Make a separate <gcmt:raw> element from each line
316 char *line, *nextline;
321 while (nextline < buffer + ndata)
323 while (*nextline != '\n' && nextline < buffer + ndata)
327 if (*nextline == '\n')
329 // Another line found - make the split.
331 write(out_stream, "<gcmt:raw>", 10);
333 String logline(line);
334 logline = XML_Entities(logline);
335 write(out_stream, logline, ~logline);
336 write(out_stream, "</gcmt:raw>\n", 12);
340 if (line != nextline)
342 // There is still an incomplete line in the buffer.
343 memmove(buffer, line, nextline - line);
345 f->position += ndata - (nextline - line);
346 ndata -= line - buffer;
347 ndata += read(f->fd, buffer + (nextline - line), sizeof(buffer) - (nextline - line));
350 something_has_changed = 1;
355 sprintf(error, ", errno=%d\n", errno);
356 output("!!! logfile: read failed: ");
362 void do_file(struct fileinfo *f)
364 struct stat statinfo;
368 if ( stat(f->name, &statinfo) )
370 sprintf(buffer, ", errno=%d\n", errno);
371 output("!!! logfile: stat failed: ");
377 if ( statinfo.st_ino != f->inode )
385 output("@@@ logfile: logrotate detected: ");
388 f->inode = statinfo.st_ino;
394 /* attempt to open the file */
395 f->fd = open(f->name, O_RDONLY);
398 sprintf(buffer, ", errno=%d\n", errno);
399 output("!!! logfile: open failed: ");
404 output("*** logfile: opened: ");
407 "\n*** logfile: resumed read from position %ld\n",
408 (long) lseek(f->fd, f->position, SEEK_SET) );
415 void process_options(int argc, char* argv[])
417 const char* const options = "1c:p:";
420 opt = getopt(argc, argv, options);
429 confname = strdup(optarg);
435 fputs(usage, stderr);
438 opt = getopt(argc, argv, options);
440 switch ( argc-optind )
445 hostname = argv[optind];
448 fputs(usage, stderr);
453 int main(int argc, char* argv[])
455 process_options(argc, argv);
461 set_signal_handler();
463 while ( sig_seen == 0 )
465 struct fileinfo* fip;
470 if (something_has_changed && oneshot)
476 if ( something_has_changed )
478 write_status_file(filelist);
487 fprintf(stderr, "logrunner: stopped by signal %d\n", sig_seen);
488 /* shouldn't we close files and release memory here? */