3 * (c) Peter Roozemaal, feb 2003
5 * $Id: logrunner.cpp,v 1.1 2003-08-11 16:56:16 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 static const char* confname = "logrunner.conf";
53 static const char* statusname = "logrunner.status";
54 static const char* newstatusname = "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)
146 write(2, str, strlen(str));
149 void output_error(const char* str)
152 sprintf(error, " errno=%d\n", errno);
157 void write_status_file(struct fileinfo* fl)
161 dumpfile = fopen(newstatusname, "w");
165 sprintf(error, ", errno=%d\n", errno);
166 output("!!! dumpstatus: open failed: ");
167 output(newstatusname);
169 something_has_changed = 0;
174 if ( fprintf(dumpfile, "%s %li %lu\n",
175 fl->name, (long) fl->inode,
176 (unsigned long) fl->position) < 0 )
178 output_error("!!! dumpstatus: write failed");
184 if ( fclose(dumpfile) && localerror == 0 )
186 output_error("!!! dumpstatus: close failed");
189 if ( localerror == 0 )
191 if ( rename(newstatusname, statusname) )
193 output_error("!!! dumpstatus: rename failed");
196 something_has_changed = 0;
208 statusfile = fopen(statusname, "r");
209 if ( statusfile == NULL )
211 fprintf(stderr, "logrunner: can\'t open status file \'%s\': ",
216 while ( fgets(buffer, sizeof(buffer), statusfile ) )
218 xp = strchr(buffer, ' ');
222 sscanf(xp+1, "%ld %lu", &ino, &pos);
226 if ( strcmp(buffer, fp->name) == 0 )
239 struct fileinfo* new_info()
242 rv = (struct fileinfo *)malloc(sizeof(struct fileinfo));
257 /* The config file now is a simple list of type, filenames;
258 * it should be more user-friendly in the future!
263 conffile = fopen(confname, "r");
267 "logrunner: can\'t open configuration file \'%s\': ",
272 while ( fgets(buffer, sizeof(buffer), conffile) )
274 int len = strlen(buffer);
276 if ( buffer[len-1] == '\n' )
280 fname = strchr(buffer, '\t');
288 fi->name = strdup(fname);
289 fi->type = strdup(buffer);
295 fprintf(stderr, "logrunner: out of memory\n");
301 fprintf(stderr, "logrunner: WARNING: bad config line: %s\n", buffer);
307 void copy_data(struct fileinfo *f)
312 /* read data and dump to output */
313 ndata = read(f->fd, buffer, sizeof(buffer));
319 // Make a separate <gcmt:raw> element from each line
321 char *line, *nextline;
326 while (nextline < buffer + ndata)
328 while (*nextline != '\n' && nextline < buffer + ndata)
332 if (*nextline == '\n')
334 // Another line found - make the split.
336 write(out_stream, "<gcmt:raw>", 10);
338 String logline(line);
339 logline = XML_Entities(logline);
340 write(out_stream, logline, ~logline);
341 write(out_stream, "</gcmt:raw>\n", 12);
345 if (line != nextline)
347 // There is still an incomplete line in the buffer.
348 memmove(buffer, line, nextline - line);
350 f->position += ndata - (nextline - line);
351 ndata -= line - buffer;
352 ndata += read(f->fd, buffer + (nextline - line), sizeof(buffer) - (nextline - line));
355 something_has_changed = 1;
360 sprintf(error, ", errno=%d\n", errno);
361 output("!!! logfile: read failed: ");
367 void do_file(struct fileinfo *f)
369 struct stat statinfo;
373 if ( stat(f->name, &statinfo) )
375 sprintf(buffer, ", errno=%d\n", errno);
376 output("!!! logfile: stat failed: ");
382 if ( statinfo.st_ino != f->inode )
390 output("@@@ logfile: logrotate detected: ");
393 f->inode = statinfo.st_ino;
399 /* attempt to open the file */
400 f->fd = open(f->name, O_RDONLY);
403 sprintf(buffer, ", errno=%d\n", errno);
404 output("!!! logfile: open failed: ");
409 output("*** logfile: opened: ");
412 "\n*** logfile: resumed read from position %ld\n",
413 (long) lseek(f->fd, f->position, SEEK_SET) );
420 void process_options(int argc, char* argv[])
422 const char* const options = "1c:p:";
425 opt = getopt(argc, argv, options);
434 confname = strdup(optarg);
440 fputs(usage, stderr);
443 opt = getopt(argc, argv, options);
445 switch ( argc-optind )
450 hostname = argv[optind];
453 fputs(usage, stderr);
458 int main(int argc, char* argv[])
460 process_options(argc, argv);
466 set_signal_handler();
468 while ( sig_seen == 0 )
470 struct fileinfo* fip;
475 if (something_has_changed && oneshot)
481 if ( something_has_changed )
483 write_status_file(filelist);
492 fprintf(stderr, "logrunner: stopped by signal %d\n", sig_seen);
493 /* shouldn't we close files and release memory here? */