diff src/wflogs-config.cpp @ 0:0aa1171aebd2 stable-1-0-0

initial version
author Carl Byington <carl@five-ten-sg.com>
date Wed, 15 May 2013 13:15:59 -0700
parents
children 400b1de6e1c6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/wflogs-config.cpp	Wed May 15 13:15:59 2013 -0700
@@ -0,0 +1,358 @@
+/*
+
+Copyright (c) 2007 Carl Byington - 510 Software Group, released under
+the GPL version 3 or any later version at your choice available at
+http://www.gnu.org/licenses/gpl-3.0.txt
+
+*/
+
+#include "includes.h"
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <limits.h>
+
+const char *token_context;
+const char *token_file;
+const char *token_include;
+const char *token_lbrace;
+const char *token_output;
+const char *token_pattern;
+const char *token_period;
+const char *token_rbrace;
+const char *token_semi;
+const char *token_tempin;
+const char *token_versions;
+const char *token_wflogs;
+
+string_set      all_strings;// owns all the strings, only modified by the config loader thread
+const int maxlen = 1000;    // used for snprintf buffers
+
+
+
+////////////////////////////////////////////////
+//
+CONFIG::CONFIG() {
+    reference_count    = 0;
+    fd                 = -1;
+    len                = 0;
+    fdo                = -1;
+    generation         = 0;
+    load_time          = 0;
+    period             = 120;
+    versions           = 3;
+    output             = NULL;
+    tempin             = NULL;
+    wflogs             = NULL;
+    fn                 = NULL;
+    pattern            = NULL;
+}
+
+
+CONFIG::~CONFIG() {
+}
+
+
+void CONFIG::sleep(int duration, time_t &previous) {
+    ::sleep(duration);
+    time_t now = time(NULL);
+    previous = now;
+}
+
+
+void CONFIG::free_all() {
+    regfree(&re);
+    close();
+    closeo();
+}
+
+
+void CONFIG::openo(bool msg) {
+    open_time = time(NULL);
+    fdo = ::creat(tempin, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    if (fdo == -1) {
+        if (msg) {
+            char buf[maxlen];
+            snprintf(buf, sizeof(buf), "wflogs tempin file %s not writeable", tempin);
+            tokp->token_error(buf);
+        }
+    }
+}
+
+
+void CONFIG::open(bool msg) {
+    fd        = ::open(fn, O_RDONLY);
+    len       = 0;
+    if (fd == -1) {
+        if (msg) {
+            char buf[maxlen];
+            snprintf(buf, sizeof(buf), "syslog file %s not readable", fn);
+            tokp->token_error(buf);
+        }
+    }
+    else {
+        if (debug_syslog > 1) {
+            snprintf(buf, sizeof(buf), "syslog file %s opened", fn);
+            my_syslog(buf);
+        }
+        if (msg) lseek(fd, 0, SEEK_END);
+        if (fstat(fd, &openfdstat)) {
+            close();
+            snprintf(buf, sizeof(buf), "syslog file %s cannot stat after open", fn);
+            tokp->token_error(buf);
+        }
+        // specify that this fd gets closed on exec, so that selinux
+        // won't complain about iptables trying to read log files.
+        int oldflags = fcntl(fd, F_GETFD, 0);
+        if (oldflags >= 0) {
+            fcntl(fd, F_SETFD, oldflags | FD_CLOEXEC);
+        }
+    }
+}
+
+
+bool CONFIG::write(char *p) {
+    // p points to \0 at end of buf, may be destroyed
+    if (failedo()) {
+        openo(false);
+        if (failedo()) return false;
+    }
+    *p = '\n';
+    ::write(fdo, buf, p-buf+1);
+}
+
+
+bool CONFIG::read() {
+    if (failed()) {
+        open(false);
+        if (failed()) return false;
+    }
+    int n = ::read(fd, buf+len, buflen-len);
+    bool have = (n > 0);
+    if (have) {
+        len += n;
+        while (true) {
+            char *p = (char*)memchr(buf, '\n', len);
+            if (!p) break;
+            n = p-buf;
+            *p = '\0';
+            process(p);     // process null terminated string, but may destroy the null
+            len -= n+1;
+            memmove(buf, p+1, len);
+        }
+        // no <lf> in a full buffer
+        if (len == buflen) len = 0;
+    }
+    else {
+        // check for file close
+        struct stat filenamest;
+        if (0 == stat(fn, &filenamest)) {
+            if ((filenamest.st_dev != openfdstat.st_dev) ||
+                (filenamest.st_ino != openfdstat.st_ino)) {
+                close();
+            }
+        }
+        else {
+            // filename no longer exists
+            close();
+        }
+    }
+    check_wflog();
+    return have;
+}
+
+
+void CONFIG::closeo() {
+    if (fdo != -1) ::close(fdo);
+    fdo = -1;
+}
+
+
+void CONFIG::close() {
+    if (debug_syslog > 1) {
+        snprintf(buf, sizeof(buf), "syslog file %s closed", fn);
+        my_syslog(buf);
+    }
+    if (fd != -1) ::close(fd);
+    fd = -1;
+}
+
+
+void CONFIG::process(char *p) {
+    // p points to \0 at end of buf, may be destroyed
+    if (pattern) {
+        if (0 == regexec(&re, buf, 0, NULL, 0)) {
+            if (debug_syslog > 2) {
+                my_syslog(buf); // show lines with matches
+            }
+            write(p);
+        }
+    }
+}
+
+
+void CONFIG::check_wflog() {
+    time_t now = time(NULL);
+    if ((fdo != -1) && (open_time + period < now)) {
+        closeo();
+        // rename previous wflog html output files
+        char buf[maxlen];
+        char fn1[maxlen];
+        char fn2[maxlen];
+        for (int i=1; i<versions; i++) {
+            int j = versions - 1 - i;
+            int k = j + 1;
+            snprintf(fn1, maxlen, output, j);
+            snprintf(fn2, maxlen, output, k);
+            snprintf(buf, maxlen, "mv \"%s\" \"%s\"", fn1, fn2);
+            system(buf);
+        }
+        snprintf(buf, maxlen, wflogs, fn1);
+        system(buf);
+        openo(false);
+    }
+}
+
+
+void CONFIG::dump() {
+    int level = 0;
+    char indent[maxlen];
+    int i = min(maxlen-1, level*4);
+    memset(indent, ' ', i);
+    indent[i] = '\0';
+    printf("%s period   %d; \n", indent, period);
+    printf("%s versions %d; \n", indent, versions);
+    printf("%s output   \"%s\";\n", indent, output);
+    printf("%s tempin   \"%s\";\n", indent, tempin);
+    printf("%s wflogs   \"%s\";\n", indent, wflogs);
+    printf("%s file     \"%s\";\n", indent, fn);
+    printf("%s pattern  \"%s\";\n", indent, pattern);
+}
+
+
+////////////////////////////////////////////////
+// helper to discard the strings held by a string_set
+//
+void discard(string_set &s) {
+    for (string_set::iterator i=s.begin(); i!=s.end(); i++) {
+        free((void*)*i);
+    }
+    s.clear();
+}
+
+
+////////////////////////////////////////////////
+// helper to register a string in a string set
+//
+const char* register_string(string_set &s, const char *name) {
+    string_set::const_iterator i = s.find(name);
+    if (i != s.end()) return *i;
+    char *x = strdup(name);
+    s.insert(x);
+    return x;
+}
+
+
+////////////////////////////////////////////////
+// register a global string
+//
+const char* register_string(const char *name) {
+    return register_string(all_strings, name);
+}
+
+
+////////////////////////////////////////////////
+// clear all global strings, helper for valgrind checking
+//
+void clear_strings() {
+	discard(all_strings);
+}
+
+
+////////////////////////////////////////////////
+//
+bool tsa(TOKEN &tok, const char *token);
+bool tsa(TOKEN &tok, const char *token) {
+    const char *have = tok.next();
+    if (have == token) return true;
+    tok.token_error(token, have);
+    return false;
+}
+
+
+////////////////////////////////////////////////
+// parse a config file
+//
+bool load_conf(CONFIG &dc, const char *fn) {
+    TOKEN tok(fn, &dc.config_files);
+    dc.set_token(tok);
+    while (true) {
+        const char *have = tok.next();
+        if (!have) break;
+        if (have == token_period) {
+            have = tok.next();
+            dc.set_period(atoi(have));
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_versions) {
+            have = tok.next();
+            dc.set_versions(atoi(have));
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_output) {
+            dc.set_output(tok.next());
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_tempin) {
+            dc.set_tempin(tok.next());
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_wflogs) {
+            dc.set_wflogs(tok.next());
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_file) {
+            dc.set_file(tok.next());
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_pattern) {
+            dc.pattern = tok.next();
+            int rc = regcomp(&dc.re, dc.pattern, REG_ICASE | REG_EXTENDED);
+            if (rc) {
+                char bu[maxlen];
+                regerror(rc, &dc.re, bu, maxlen);
+                char buf[maxlen];
+                snprintf(buf, sizeof(buf), "pattern %s not valid - %s", dc.pattern, bu);
+                tok.token_error(buf);
+                dc.pattern = NULL;
+            }
+
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else {
+            tok.token_error("statement", have);
+            return false;
+        }
+    }
+    return true;
+}
+
+
+////////////////////////////////////////////////
+// init the tokens
+//
+void token_init() {
+    token_file       = register_string("file");
+    token_include    = register_string("include");
+    token_lbrace     = register_string("{");
+    token_output     = register_string("output");
+    token_pattern    = register_string("pattern");
+    token_period     = register_string("period");
+    token_rbrace     = register_string("}");
+    token_semi       = register_string(";");
+    token_tempin     = register_string("tempin");
+    token_versions   = register_string("versions");
+    token_wflogs     = register_string("wflogs");
+}