Mercurial > syslog2iptables
view src/syslog2iptables.cpp @ 20:0d65c3de34fd
add better logging
author | carl |
---|---|
date | Sun, 08 Jan 2006 12:36:57 -0800 |
parents | 1134c6a1d692 |
children | 00bd0b0ef015 |
line wrap: on
line source
/*************************************************************************** * Copyright (C) 2005 by 510 Software Group * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ // debug levels: // 4 - show syslog lines that match regex // 3 - show addresses being dropped/released from the filter // 2 - show files open/close // 1 - show config files loading #include "includes.h" #include <iostream> #include <cstdlib> #include <errno.h> #include <sysexits.h> #include <pthread.h> #include <syslog.h> #include <sys/wait.h> /* header for waitpid() and various macros */ #include <signal.h> /* header for signal functions */ static char* syslog2iptables_version = "$Id$"; extern "C" { void sig_chld(int signo); } int debug_syslog = 0; bool syslog_opened = false; bool use_syslog = true; // false to printf bool loader_run = true; // used to stop the config loader thread CONFIG *config = NULL; // protected by the config_mutex int generation = 0; // protected by the config_mutex const int maxlen = 1000; // used for snprintf buffers pthread_mutex_t config_mutex; pthread_mutex_t syslog_mutex; //////////////////////////////////////////////// // syslog a message // void my_syslog(char *text) { if (use_syslog) { pthread_mutex_lock(&syslog_mutex); if (!syslog_opened) { openlog("syslog2iptables", LOG_PID, LOG_AUTHPRIV); syslog_opened = true; } syslog(LOG_NOTICE, "%s", text); pthread_mutex_unlock(&syslog_mutex); } else { printf("%s\n", text); } } //////////////////////////////////////////////// // reload the config // CONFIG* new_conf(); CONFIG* new_conf() { CONFIG *newc = new CONFIG; pthread_mutex_lock(&config_mutex); newc->generation = generation++; pthread_mutex_unlock(&config_mutex); if (debug_syslog) { char buf[maxlen]; snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); my_syslog(buf); } if (load_conf(*newc, "syslog2iptables.conf")) { newc->load_time = time(NULL); return newc; } delete newc; return NULL; } //////////////////////////////////////////////// // thread to watch the old config files for changes // and reload when needed. we also cleanup old // configs whose reference count has gone to zero. // void* config_loader(void *arg); void* config_loader(void *arg) { typedef set<CONFIG *> configp_set; configp_set old_configs; while (loader_run) { sleep(180); // look for modifications every 3 minutes if (!loader_run) break; CONFIG &dc = *config; time_t then = dc.load_time; struct stat st; bool reload = false; for (string_set::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { char *fn = *i; if (stat(fn, &st)) reload = true; // file disappeared else if (st.st_mtime > then) reload = true; // file modified if (reload) break; } if (reload) { CONFIG *newc = new_conf(); if (newc) { // replace the global config pointer pthread_mutex_lock(&config_mutex); CONFIG *old = config; config = newc; pthread_mutex_unlock(&config_mutex); if (old) old_configs.insert(old); } else { // failed to load new config my_syslog("failed to load new configuration"); system("echo 'failed to load new /etc/syslog2iptables.conf' | mail -s 'error in /etc/syslog2iptables.conf' root"); // update the load time on the current config to prevent complaining every 3 minutes dc.load_time = time(NULL); } } // now look for old configs with zero ref counts for (configp_set::iterator i=old_configs.begin(); i!=old_configs.end(); ) { CONFIG *old = *i; if (!old->reference_count) { if (debug_syslog) { char buf[maxlen]; snprintf(buf, sizeof(buf), "freeing memory for old configuration generation %d", old->generation); my_syslog(buf); } delete old; // destructor does all the work old_configs.erase(i++); } else i++; } } return NULL; } //////////////////////////////////////////////// // The signal handler function -- only gets called when a SIGCHLD // is received, ie when a child terminates // void sig_chld(int signo) { int status; /* Wait for any child without blocking */ while (waitpid(-1, &status, WNOHANG) > 0) { // ignore child exit status, we only do this to cleanup zombies } } void usage(char *prog); void usage(char *prog) { fprintf(stderr, "Usage: %s [-d [level]] [-c]\n", prog); fprintf(stderr, "-c will load and dump the config to stdout\n"); fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n"); } void worker(); void worker() { time_t t = time(NULL); CONFIG *c; pthread_mutex_lock(&config_mutex); c = config; c->reference_count++; pthread_mutex_unlock(&config_mutex); while (true) { if (c != config) { pthread_mutex_lock(&config_mutex); c->reference_count--; c = config; c->reference_count++; pthread_mutex_unlock(&config_mutex); } c->read(); c->sleep(10, t); } } int main(int argc, char *argv[]) { token_init(); bool check = false; int c; const char *args = "d:ch"; extern char *optarg; // Process command line options while ((c = getopt(argc, argv, args)) != -1) { switch (c) { case 'c': check = true; break; case 'd': if (optarg == NULL || *optarg == '\0') debug_syslog = 1; else debug_syslog = atoi(optarg); break; case 'h': default: usage(argv[0]); exit(EX_USAGE); } } if (check) { use_syslog = false; debug_syslog = 10; config = new_conf(); if (config) { config->dump(); delete config; return 0; } else { return 1; // config failed to load } } // switch to background mode if (daemon(1,0) < 0) { fprintf(stderr, "daemon() call failed\n"); exit(EX_UNAVAILABLE); } // write the pid const char *pidpath = "/var/run/syslog2iptables.pid"; unlink(pidpath); FILE *f = fopen(pidpath, "w"); if (f) { #ifdef linux // from a comment in the DCC source code: // Linux threads are broken. Signals given the // original process are delivered to only the // thread that happens to have that PID. The // sendmail libmilter thread that needs to hear // SIGINT and other signals does not, and that breaks // scripts that need to stop milters. // However, signaling the process group works. fprintf(f, "-%d\n", (u_int)getpgrp()); #else fprintf(f, "%d\n", (u_int)getpid()); #endif fclose(f); } // initialize the thread sync objects pthread_mutex_init(&config_mutex, 0); pthread_mutex_init(&syslog_mutex, 0); // load the initial config config = new_conf(); if (!config) { my_syslog("failed to load initial configuration, quitting"); exit(1); } // setup sigchld handler to prevent zombies struct sigaction act; act.sa_handler = sig_chld; // Assign sig_chld as our SIGCHLD handler sigemptyset(&act.sa_mask); // We don't want to block any other signals in this example act.sa_flags = SA_NOCLDSTOP; // only want children that have terminated if (sigaction(SIGCHLD, &act, NULL) < 0) { my_syslog("failed to setup SIGCHLD handler"); exit(1); } // only create threads after the fork() in daemon pthread_t tid; if (pthread_create(&tid, 0, config_loader, 0)) my_syslog("failed to create config loader thread"); if (pthread_detach(tid)) my_syslog("failed to detach config loader thread"); worker(); return EXIT_SUCCESS; }