From 7e796a4c3815f623617e0fdbb5987be7ac135db6 Mon Sep 17 00:00:00 2001 From: arjen Date: Sun, 6 May 2007 08:35:16 +0000 Subject: [PATCH] Rewrote most functions and datastructures into C++ Use the Gnucomo (XML) configuration file --- src/gcm_input/logrunner.cpp | 461 ++++++++++++++++++++++++-------------------- 1 file changed, 252 insertions(+), 209 deletions(-) diff --git a/src/gcm_input/logrunner.cpp b/src/gcm_input/logrunner.cpp index f4d0ecd..2e610dd 100644 --- a/src/gcm_input/logrunner.cpp +++ b/src/gcm_input/logrunner.cpp @@ -2,16 +2,26 @@ * logrunner.c * (c) Peter Roozemaal, feb 2003 * - * $Id: logrunner.cpp,v 1.2 2005-05-31 05:47:33 arjen Exp $ + * $Id: logrunner.cpp,v 1.3 2007-05-06 08:35:16 arjen Exp $ * * 1) compile, - * 2) create 'logrunner.conf' containing a list of filenames and types of the - * logfiles to scan; one file per line: - * TAB + * 2) Add 'logfile' elements to the gnucomo configuration file + * defining the name and type of each logfile to scan. + * 'filter' elements can be addes to pre-filter lines from the log. + * * example: - * syslog /var/log/messages - * syslog /var/log/secure - * 3) run executable + * + * /var/log/httpd/access_log + * apache access log + * + * + * /var/log/messages + * system log + * open_scanner + * session closed + * + * + * 3) run executable and feed the output to gcm_input */ #include #include @@ -27,8 +37,13 @@ #include #include +#include +#include + #include +#include "gnucomo_config.h" + extern String XML_Entities(String s); static const char* const usage = @@ -40,16 +55,65 @@ static const char* const usage = " -c : specify alternative configuration file, default is \'logrunner.conf\'\n" "If no hostname is specified, logrunner sends output to stdout\n"; -struct fileinfo { - struct fileinfo* next; - char* name; /* strdupped name */ - char* type; /* strdupped logtype */ +class LogFile { + String name; + String type; int fd; ino_t inode; off_t position; + + std::list filter; + + void copy_data(); + +public: + LogFile(String n, String t) + { + name = n; + type = t; + inode = 0; + position = 0; + fd = -1; + } + + LogFile(const LogFile &lf) + { + name = lf.name; + type = lf.type; + fd = lf.fd; + inode = lf.inode; + position = lf.position; + filter = lf.filter; + } + + String pathname() + { + return name; + } + + void update_status(ino_t i, off_t p) + { + inode = i; + position = p; + } + + String status() + { + return name + " " + String(inode) + " " + String(position); + } + + void add_filter(String filter_expression) + { + filter.push_back(filter_expression); + std::cerr << "add_filter, filter size is " << filter.size() << "\n"; + } + + void do_file(); }; -String confname = "/etc/logrunner.conf"; +static std::list logs_to_run; + +String confname = "gnucomo"; static const char* statusname = "/var/lib/logrunner.status"; static const char* newstatusname = "/var/lib/logrunner.status.new"; @@ -59,8 +123,6 @@ static int out_stream = 1; static int oneshot = 0; -static struct fileinfo* filelist = NULL; - static int something_has_changed = 0; static volatile sig_atomic_t sig_seen = 0; @@ -81,6 +143,7 @@ static void set_signal_handler(void) signal(SIGTERM, sighandler); } + void open_output() { struct hostent* hostptr; @@ -121,7 +184,7 @@ void xsend(const char* str) write(out_stream, str, strlen(str)); } -void xml_header(struct fileinfo *f) +void xml_header(String type) { char buffer[256]; *buffer = 0; @@ -131,7 +194,7 @@ void xml_header(struct fileinfo *f) xsend("\n"); xsend(buffer); xsend("\n"); - xsend(f->type); + xsend(type); xsend("\n\n"); xsend(""); } @@ -151,164 +214,59 @@ void output_error(const char* str) std::cerr << str << " errno=" << errno << "\n"; } -void write_status_file(struct fileinfo* fl) -{ - FILE* dumpfile; - int localerror = 0; - dumpfile = fopen(newstatusname, "w"); - if ( !dumpfile ) - { - char error[80]; - sprintf(error, ", errno=%d\n", errno); - output("!!! dumpstatus: open failed: "); - output(newstatusname); - output(error); - something_has_changed = 0; - return; - } - while ( fl ) - { - if ( fprintf(dumpfile, "%s %li %lu\n", - fl->name, (long) fl->inode, - (unsigned long) fl->position) < 0 ) - { - output_error("!!! dumpstatus: write failed"); - localerror = 1; - break; - } - fl = fl->next; - } - if ( fclose(dumpfile) && localerror == 0 ) - { - output_error("!!! dumpstatus: close failed"); - localerror = 1; - } - if ( localerror == 0 ) - { - if ( rename(newstatusname, statusname) ) - { - output_error("!!! dumpstatus: rename failed"); - } - } - something_has_changed = 0; -} - -void read_status() +void LogFile::do_file() { - FILE* statusfile; - char buffer[4096]; - char* xp; - long ino; - unsigned long pos; - struct fileinfo* fp; + struct stat statinfo; + char buffer[80]; - statusfile = fopen(statusname, "r"); - if ( statusfile == NULL ) + /* inode check */ + if ( stat(name, &statinfo) ) { - fprintf(stderr, "logrunner: can\'t open status file \'%s\': ", - statusname); - perror(""); - return; + std::cerr << "!!! logfile: stat failed: " << name << ", errno=" << errno << "\n"; } - while ( fgets(buffer, sizeof(buffer), statusfile ) ) + else { - xp = strchr(buffer, ' '); - if ( xp ) + if ( statinfo.st_ino != inode ) { - *xp = 0; - sscanf(xp+1, "%ld %lu", &ino, &pos); - fp = filelist; - while ( fp ) + if ( fd >= 0 ) { - if ( strcmp(buffer, fp->name) == 0 ) - { - fp->inode = ino; - fp->position = pos; - break; - } - fp = fp->next; + copy_data(); + close(fd); + fd = -1; } + std::cerr << "@@@ logfile: logrotate detected: " << name << "\n"; + inode = statinfo.st_ino; + position = 0; } } - fclose(statusfile); -} - -struct fileinfo* new_info() -{ - struct fileinfo* rv; - rv = (struct fileinfo *)malloc(sizeof(struct fileinfo)); - if ( rv ) + if ( fd < 0 ) { - rv->next = NULL; - rv->name = NULL; - rv->type = NULL; - rv->fd = -1; - rv->position = 0; - rv->inode = 0; - } - return rv; -} - -void read_config() -{ - /* The config file now is a simple list of type, filenames; - * it should be more user-friendly in the future! - */ - FILE* conffile; - char buffer[4096]; - - conffile = fopen(confname, "r"); - if ( !conffile ) - { - std::cerr << "logrunner: can\'t open configuration file \'" << confname << "\': "; - perror(""); - exit(2); - } - while ( fgets(buffer, sizeof(buffer), conffile) ) - { - int len = strlen(buffer); - char *fname; - if ( buffer[len-1] == '\n' ) - { - buffer[--len] = 0; - } - fname = strchr(buffer, '\t'); - if ( fname ) - { - struct fileinfo* fi; - fi = new_info(); - *(fname++) = 0; - if ( fi ) - { - fi->name = strdup(fname); - fi->type = strdup(buffer); - fi->next = filelist; - filelist = fi; - } - else - { - fprintf(stderr, "logrunner: out of memory\n"); - exit(2); - } - } - else if ( len > 0 ) + /* attempt to open the file */ + fd = open(name, O_RDONLY); + if ( fd < 0 ) { - fprintf(stderr, "logrunner: WARNING: bad config line: %s\n", buffer); + std::cerr << "!!! logfile: open failed: " << name << ", " << strerror(errno) << "\n"; + return; } + std::cerr << "*** logfile: opened: " << name; + std::cerr << "\n*** logfile: resumed read from position "; + std::cerr << (long) lseek(fd, position, SEEK_SET) << "\n"; + std::cerr << "This logfile has " << filter.size() << " filters.\n"; } - fclose(conffile); + + copy_data(); } -void copy_data(struct fileinfo *f) +void LogFile::copy_data() { char buffer[4096]; int ndata; /* read data and dump to output */ - ndata = read(f->fd, buffer, sizeof(buffer)); + ndata = read(fd, buffer, sizeof(buffer)); if ( ndata > 0 ) { - xml_header(f); + xml_header(type); while ( ndata > 0 ) { // Make a separate element from each line @@ -328,12 +286,35 @@ void copy_data(struct fileinfo *f) { // Another line found - make the split. *nextline++ = '\0'; - write(out_stream, "", 10); String logline(line); - logline = XML_Entities(logline); - write(out_stream, logline, ~logline); - write(out_stream, "\n", 12); + + // Apply filters to this log entry + + std::list::iterator f = filter.begin(); + bool filtered_out = false; + while (f != filter.end()) + { + if (logline == *f) + { + filtered_out = true; + } + f++; + } + + if (!filtered_out) + { + logline = XML_Entities(logline); + + write(out_stream, "", 10); + write(out_stream, logline, ~logline); + write(out_stream, "\n", 12); + } + else + { + std::cerr << logline << " is filtred out.\n"; + } + line = nextline; } } @@ -342,74 +323,130 @@ void copy_data(struct fileinfo *f) // There is still an incomplete line in the buffer. memmove(buffer, line, nextline - line); } - f->position += ndata - (nextline - line); + position += ndata - (nextline - line); ndata -= line - buffer; - ndata += read(f->fd, buffer + (nextline - line), sizeof(buffer) - (nextline - line)); + ndata += read(fd, buffer + (nextline - line), sizeof(buffer) - (nextline - line)); } xml_footer(); something_has_changed = 1; } if ( ndata < 0 ) { - char error[80]; - sprintf(error, ", errno=%d\n", errno); - output("!!! logfile: read failed: "); - output(f->name); - output(error); + std::cerr << "!!! logfile: read failed: " << name << ", " << strerror(errno) << "\n"; + std::cerr << " file descriptor = " << fd << "\n"; } } -void do_file(struct fileinfo *f) +void write_status_file() { - struct stat statinfo; - char buffer[80]; + FILE* dumpfile; + int localerror = 0; - /* inode check */ - if ( stat(f->name, &statinfo) ) + std::ofstream statusfile(newstatusname); + std::list::iterator lf = logs_to_run.begin(); + while (lf != logs_to_run.end()) { - sprintf(buffer, ", errno=%d\n", errno); - output("!!! logfile: stat failed: "); - output(f->name); - output(buffer); + std::cerr << "Write status for " << lf->pathname() << "\n"; + statusfile << lf->status() << "\n"; + lf++; } - else + + if ( localerror == 0 ) { - if ( statinfo.st_ino != f->inode ) + if ( rename(newstatusname, statusname) ) { - if ( f->fd >= 0 ) + output_error("!!! dumpstatus: rename failed"); + } + } + something_has_changed = 0; +} + +void read_status() +{ + FILE* statusfile; + char buffer[4096]; + char* xp; + long ino; + unsigned long pos; + struct fileinfo* fp; + + statusfile = fopen(statusname, "r"); + if ( statusfile == NULL ) + { + fprintf(stderr, "logrunner: can\'t open status file \'%s\': ", + statusname); + perror(""); + return; + } + while ( fgets(buffer, sizeof(buffer), statusfile ) ) + { + xp = strchr(buffer, ' '); + if ( xp ) + { + *xp = 0; + sscanf(xp+1, "%ld %lu", &ino, &pos); + + // Search for the logfile in the list of logs to run + + std::list::iterator lf = logs_to_run.begin(); + while (lf != logs_to_run.end()) { - copy_data(f); - close(f->fd); - f->fd = -1; + if (lf->pathname() == String(buffer)) + { + std::cerr << "Read status for " << lf->pathname() << "\n"; + lf->update_status(ino, pos); + } + lf++; } - output("@@@ logfile: logrotate detected: "); - output(f->name); - output("\n"); - f->inode = statinfo.st_ino; - f->position = 0; + } } - if ( f->fd < 0 ) + fclose(statusfile); +} + +void read_config(gnucomo_config cfg) +{ + /* + * The configuration for logrunner is stored in the central Gnucomo + * configuration file. Multiple 'logfile' elements can be put in this + * XML file, one for each logfile to scan. + * Each 'logfile' element has at least a 'name' and a 'type' element + * that denote the pathname of the logfile and the type of its content. + */ + + String logfilename; + String logfiletype; + + int l = 0; + + logfilename = cfg.find_parameter("logfile", "name", l); + while (logfilename != String("")) { - /* attempt to open the file */ - f->fd = open(f->name, O_RDONLY); - if ( f->fd < 0 ) + std::cerr << "Configuration for logfile " << logfilename << "\n"; + logfiletype = cfg.find_parameter("logfile", "type", l); + std::cerr << "LogFile " << logfilename << " of type " << logfiletype << "\n"; + + LogFile lf(logfilename, logfiletype); + + int f = 0; + String exp = cfg.find_parameter("logfile", "filter", l, f); + while (exp != "") { - sprintf(buffer, ", errno=%d\n", errno); - output("!!! logfile: open failed: "); - output(f->name); - output(buffer); - return; + std::cerr << "Adding filter " << exp << "\n"; + lf.add_filter(exp); + + f++; + exp = cfg.find_parameter("logfile", "filter", l, f); } - output("*** logfile: opened: "); - output(f->name); - sprintf(buffer, - "\n*** logfile: resumed read from position %ld\n", - (long) lseek(f->fd, f->position, SEEK_SET) ); - output(buffer); + + logs_to_run.push_back(lf); + std::cerr << "Logfile added to list.\n"; + + l++; + logfilename = cfg.find_parameter("logfile", "name", l); + std::cerr << "Next logfile = " << logfilename << "\n"; } - copy_data(f); } void process_options(int argc, char* argv[]) @@ -452,30 +489,36 @@ void process_options(int argc, char* argv[]) int main(int argc, char* argv[]) { + + gnucomo_config cfg; + process_options(argc, argv); open_output(); - read_config(); + if (!cfg.read(confname)) + { + std::cerr << "Can not read Gnucomo configuration file for " << confname << ".\n"; + exit(1); + } + + read_config(cfg); read_status(); set_signal_handler(); while ( sig_seen == 0 ) { - struct fileinfo* fip; - fip = filelist; - while ( fip ) + std::list::iterator lf = logs_to_run.begin(); + while (lf != logs_to_run.end() && !(something_has_changed && oneshot)) { - do_file(fip); - if (something_has_changed && oneshot) - { - break; - } - fip = fip->next; + std::cerr << "Scanning logfile " << lf->pathname() << "\n"; + lf->do_file(); + lf++; } + if ( something_has_changed ) { - write_status_file(filelist); + write_status_file(); } if ( oneshot ) { -- 2.11.0