Importing all of DRL, including ulogd and all of its files.
[distributedratelimiting.git] / ulogd.c
diff --git a/ulogd.c b/ulogd.c
new file mode 100644 (file)
index 0000000..9b998a9
--- /dev/null
+++ b/ulogd.c
@@ -0,0 +1,825 @@
+/* ulogd, Version $LastChangedRevision: 5373 $
+ *
+ * $Id: ulogd.c 5373 2005-05-04 01:23:16Z laforge $
+ *
+ * userspace logging daemon for the iptables ULOG target
+ * of the linux 2.4 netfilter subsystem.
+ *
+ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 
+ *  as published by the Free Software Foundation
+ *
+ *  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
+ *
+ * $Id: ulogd.c 5373 2005-05-04 01:23:16Z laforge $
+ *
+ * Modifications:
+ *     14 Jun 2001 Martin Josefsson <gandalf@wlug.westbo.se>
+ *             - added SIGHUP handler for logfile cycling
+ *
+ *     10 Feb 2002 Alessandro Bono <a.bono@libero.it>
+ *             - added support for non-fork mode
+ *             - added support for logging to stdout
+ *
+ *     09 Sep 2003 Magnus Boden <sarek@ozaba.cx>
+ *             - added support for more flexible multi-section conffile
+ *
+ *     20 Apr 2004 Nicolas Pougetoux <nicolas.pougetoux@edelweb.fr>
+ *             - added suppurt for seteuid()
+ */
+
+#define ULOGD_VERSION  "1.23"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <ctype.h>
+#include <signal.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+#include <libipulog/libipulog.h>
+#include <ulogd/conffile.h>
+#include <ulogd/ulogd.h>
+
+/* Size of the socket recevive memory.  Should be at least the same size as the
+ * 'nlbufsiz' module loadtime parameter of ipt_ULOG.o
+ * If you have _big_ in-kernel queues, you may have to increase this number.  (
+ * --qthreshold 100 * 1500 bytes/packet = 150kB  */
+#define ULOGD_RMEM_DEFAULT     131071
+
+/* Size of the receive buffer for the netlink socket.  Should be at least of
+ * RMEM_DEFAULT size.  */
+#define ULOGD_BUFSIZE_DEFAULT  150000
+
+#ifdef DEBUG
+#define DEBUGP(format, args...) fprintf(stderr, format, ## args)
+#else
+#define DEBUGP(format, args...) 
+#endif
+
+/* default config parameters, if not changed in configfile */
+#ifndef ULOGD_LOGFILE_DEFAULT
+#define ULOGD_LOGFILE_DEFAULT  "/var/log/ulogd.log"
+#endif
+#ifndef ULOGD_NLGROUP_DEFAULT
+#define ULOGD_NLGROUP_DEFAULT  32
+#endif
+
+/* where to look for the config file */
+#ifndef ULOGD_CONFIGFILE
+#define ULOGD_CONFIGFILE       "/etc/ulogd.conf"
+#endif
+
+/* global variables */
+static struct ipulog_handle *libulog_h;        /* our libipulog handle */
+static unsigned char* libulog_buf;     /* the receive buffer */
+static FILE *logfile = NULL;           /* logfile pointer */
+static char *ulogd_configfile = ULOGD_CONFIGFILE;
+
+/* linked list for all registered interpreters */
+static ulog_interpreter_t *ulogd_interpreters;
+
+/* linked list for all registered output targets */
+static ulog_output_t *ulogd_outputs;
+
+/***********************************************************************
+ * INTERPRETER AND KEY HASH FUNCTIONS                  (new in 0.9)
+ ***********************************************************************/
+
+/* We keep hashtables of interpreters and registered keys. The hash-tables
+ * are allocated dynamically at program load time. You may control the
+ * allocation granularity of both hashes (i.e. the amount of hashtable
+ * entries are allocated at one time) through modification of the constants
+ * INTERH_ALLOC_GRAN and KEYH_ALLOC_GRAN 
+ */
+
+/* allocation granularith */
+#define INTERH_ALLOC_GRAN      5
+
+/* hashtable for all registered interpreters */
+static ulog_interpreter_t **ulogd_interh;
+
+/* current hashtable size */
+static unsigned int ulogd_interh_ids_alloc;
+
+/* total number of registered ids */
+static unsigned int ulogd_interh_ids;
+
+/* allocate a new interpreter id and write it into the interpreter struct */
+static unsigned int interh_allocid(ulog_interpreter_t *ip)
+{
+       unsigned int id;
+
+       id = ++ulogd_interh_ids;
+       
+       if (id >= ulogd_interh_ids_alloc) {
+               if (!ulogd_interh)
+                       ulogd_interh = (ulog_interpreter_t **) 
+                               malloc(INTERH_ALLOC_GRAN *
+                                       sizeof(ulog_interpreter_t));
+               else
+                       ulogd_interh = (ulog_interpreter_t **)
+                               realloc(ulogd_interh, 
+                                       (INTERH_ALLOC_GRAN +
+                                        ulogd_interh_ids_alloc) *
+                                       sizeof(ulog_interpreter_t));
+
+               ulogd_interh_ids_alloc += INTERH_ALLOC_GRAN;
+       }
+
+       ip->id = id;
+       ulogd_interh[id] = ip;
+       return id;
+}
+
+/* get interpreter id by name */
+unsigned int interh_getid(const char *name)
+{
+       unsigned int i;
+       for (i = 1; i <= ulogd_interh_ids; i++)
+               if (!strcmp(name, (ulogd_interh[i])->name))
+                       return i;
+
+       return 0;
+}
+
+#ifdef DEBUG
+/* dump out the contents of the interpreter hash */
+static void interh_dump(void)
+{
+       unsigned int i;
+
+       for (i = 1; i <= ulogd_interh_ids; i++)
+               ulogd_log(ULOGD_DEBUG, "ulogd_interh[%d] = %s\n", 
+                       i, (ulogd_interh[i])->name);
+
+}
+#endif
+
+/* key hash allocation granularity */
+#define KEYH_ALLOC_GRAN 20
+
+/* hash table for key ids */
+struct ulogd_keyh_entry *ulogd_keyh;
+
+/* current size of the hashtable */
+static unsigned int ulogd_keyh_ids_alloc;
+
+/* total number of registered keys */
+static unsigned int ulogd_keyh_ids;
+
+/* allocate a new key_id */
+static unsigned int keyh_allocid(ulog_interpreter_t *ip, unsigned int offset,
+                               const char *name)
+{
+       unsigned int id;
+
+       id = ++ulogd_keyh_ids;
+
+       if (id >= ulogd_keyh_ids_alloc) {
+               if (!ulogd_keyh) {
+                       ulogd_keyh = (struct ulogd_keyh_entry *)
+                               malloc(KEYH_ALLOC_GRAN * 
+                                       sizeof(struct ulogd_keyh_entry));
+                       if (!ulogd_keyh) {
+                               ulogd_log(ULOGD_ERROR, "OOM!\n");
+                               return 0;
+                       }
+               } else {
+                       ulogd_keyh = (struct ulogd_keyh_entry *)
+                               realloc(ulogd_keyh, (KEYH_ALLOC_GRAN
+                                               +ulogd_keyh_ids_alloc) *
+                                       sizeof(struct ulogd_keyh_entry));
+
+                       if (!ulogd_keyh) {
+                               ulogd_log(ULOGD_ERROR, "OOM!\n");
+                               return 0;
+                       }
+               }
+
+               ulogd_keyh_ids_alloc += KEYH_ALLOC_GRAN;
+       }
+
+       ulogd_keyh[id].interp = ip;
+       ulogd_keyh[id].offset = offset;
+       ulogd_keyh[id].name = name;
+
+       return id;
+}
+
+#ifdef DEBUG
+/* dump the keyhash to standard output */
+static void keyh_dump(void)
+{
+       unsigned int i;
+
+       printf("dumping keyh\n");
+       for (i = 1; i <= ulogd_keyh_ids; i++)
+               printf("ulogd_keyh[%lu] = %s:%u\n", i, 
+                       ulogd_keyh[i].interp->name, ulogd_keyh[i].offset);
+}
+#endif
+
+/* get keyid by name */
+unsigned int keyh_getid(const char *name)
+{
+       unsigned int i;
+       for (i = 1; i <= ulogd_keyh_ids; i++)
+               if (!strcmp(name, ulogd_keyh[i].name))
+                       return i;
+
+       return 0;
+}
+
+/* get key name by keyid */
+char *keyh_getname(unsigned int id)
+{
+       if (id > ulogd_keyh_ids) {
+               ulogd_log(ULOGD_NOTICE, 
+                       "keyh_getname called with invalid id%u\n", id);
+               return NULL;
+       }
+               
+       return ulogd_keyh[id].interp->name;
+}
+
+/* get result for given key id. does not check if result valid */
+ulog_iret_t *keyh_getres(unsigned int id)
+{
+       ulog_iret_t *ret;
+
+       if (id > ulogd_keyh_ids) {
+               ulogd_log(ULOGD_NOTICE,
+                       "keyh_getres called with invalid id %d\n", id);
+               return NULL;
+       }
+
+       ret = &ulogd_keyh[id].interp->result[ulogd_keyh[id].offset];
+
+       return ret;
+}
+
+/***********************************************************************
+ * INTERPRETER MANAGEMENT 
+ ***********************************************************************/
+
+/* try to lookup a registered interpreter for a given name */
+static ulog_interpreter_t *find_interpreter(const char *name)
+{
+       unsigned int id;
+       
+       id = interh_getid(name);
+       if (!id)
+               return NULL;
+
+       return ulogd_interh[id];
+}
+
+/* the function called by all interpreter plugins for registering their
+ * target. */ 
+void register_interpreter(ulog_interpreter_t *me)
+{
+       unsigned int i;
+
+       /* check if we already have an interpreter with this name */
+       if (find_interpreter(me->name)) {
+               ulogd_log(ULOGD_NOTICE, 
+                       "interpreter `%s' already registered\n", me->name);
+               return;
+       }
+
+       ulogd_log(ULOGD_INFO, "registering interpreter `%s'\n", me->name);
+
+       /* allocate a new interpreter id for it */
+       if (!interh_allocid(me)) {
+               ulogd_log(ULOGD_ERROR, "unable to obtain interh_id for "
+                       "interpreter '%s'\n", me->name);
+               return;
+       }
+
+       /* - allocate one keyh_id for each result of this interpreter 
+        * - link the elements to each other */
+       for (i = 0; i < me->key_num; i++) {
+               if (!keyh_allocid(me, i, me->result[i].key)) {
+                       ulogd_log(ULOGD_ERROR, "unable to obtain keyh_id "
+                               "for interpreter %s, key %d", me->name,
+                               me->result[i].key);
+                       continue;
+               }
+               if (i != me->key_num - 1)
+                       me->result[i].next = &me->result[i+1];
+       }
+
+       /* all work done, we can prepend the new interpreter to the list */
+       if (ulogd_interpreters)
+               me->result[me->key_num - 1].next = 
+                                       &ulogd_interpreters->result[0];
+       me->next = ulogd_interpreters;
+       ulogd_interpreters = me;
+}
+
+/***********************************************************************
+ * OUTPUT MANAGEMENT 
+ ***********************************************************************/
+
+/* try to lookup a registered output plugin for a given name */
+static ulog_output_t *find_output(const char *name)
+{
+       ulog_output_t *ptr;
+
+       for (ptr = ulogd_outputs; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                               return ptr;
+       }
+
+       return NULL;
+}
+
+/* the function called by all output plugins for registering themselves */
+void register_output(ulog_output_t *me)
+{
+       if (find_output(me->name)) {
+               ulogd_log(ULOGD_NOTICE, "output `%s' already registered\n",
+                               me->name);
+               exit(EXIT_FAILURE);
+       }
+       ulogd_log(ULOGD_INFO, "registering output `%s'\n", me->name);
+       me->next = ulogd_outputs;
+       ulogd_outputs = me;
+}
+
+/***********************************************************************
+ * MAIN PROGRAM
+ ***********************************************************************/
+
+static FILE syslog_dummy;
+
+static inline int ulogd2syslog_level(int level)
+{
+       int syslog_level = LOG_WARNING;
+
+       switch (level) {
+               case ULOGD_DEBUG:
+                       syslog_level = LOG_DEBUG;
+                       break;
+               case ULOGD_INFO:
+                       syslog_level = LOG_INFO;
+                       break;
+               case ULOGD_NOTICE:
+                       syslog_level = LOG_NOTICE;
+                       break;
+               case ULOGD_ERROR:
+                       syslog_level = LOG_ERR;
+                       break;
+               case ULOGD_FATAL:
+                       syslog_level = LOG_CRIT;
+                       break;
+       }
+       return syslog_level;
+}
+/* propagate results to all registered output plugins */
+static void propagate_results(ulog_iret_t *ret)
+{
+       ulog_output_t *p;
+
+       for (p = ulogd_outputs; p; p = p->next) {
+               (*p->output)(ret);
+       }
+}
+
+/* clean results (set all values to 0 and free pointers) */
+static void clean_results(ulog_iret_t *ret)
+{
+       ulog_iret_t *r;
+
+       for (r = ret; r; r = r->next) {
+               if (r->flags & ULOGD_RETF_FREE) {
+                       free(r->value.ptr);
+                       r->value.ptr = NULL;
+               }
+               memset(&r->value, 0, sizeof(r->value));
+               r->flags &= ~ULOGD_RETF_VALID;
+       }
+}
+
+/* call all registered interpreters and hand the results over to 
+ * propagate_results */
+static void handle_packet(ulog_packet_msg_t *pkt)
+{
+       ulog_iret_t *ret;
+        ulog_iret_t *allret = NULL;
+       ulog_interpreter_t *ip;
+
+       unsigned int i,j;
+
+       /* If there are no interpreters registered yet,
+        * ignore this packet */
+       if (!ulogd_interh_ids) {
+               ulogd_log(ULOGD_NOTICE, 
+                         "packet received, but no interpreters found\n");
+               return;
+       }
+
+       for (i = 1; i <= ulogd_interh_ids; i++) {
+               ip = ulogd_interh[i];
+               /* call interpreter */
+               if ((ret = ((ip)->interp)(ip, pkt))) {
+                       /* create references for result linked-list */
+                       for (j = 0; j < ip->key_num; j++) {
+                               if (IS_VALID(ip->result[j])) {
+                                       ip->result[j].cur_next = allret;
+                                       allret = &ip->result[j];
+                               }
+                       }
+               }
+       }
+       propagate_results(allret);
+       clean_results(ulogd_interpreters->result);
+}
+
+/* plugin loader to dlopen() a plugins */
+static int load_plugin(char *file)
+{
+       if (!dlopen(file, RTLD_NOW)) {
+               ulogd_log(ULOGD_ERROR, "load_plugins: '%s': %s\n", file,
+                         dlerror());
+               return 1;
+       }
+       return 0;
+}
+
+/* open the logfile */
+static int logfile_open(const char *name)
+{
+       if (!strcmp(name, "syslog")) {
+               openlog("ulogd", LOG_PID, LOG_DAEMON);
+               logfile = &syslog_dummy;
+       } else if (!strcmp(name,"stdout"))
+               logfile = stdout;
+       else {
+               logfile = fopen(name, "a");
+               if (!logfile) {
+                       fprintf(stderr, "ERROR: can't open logfile %s: %s\n", 
+                               name, strerror(errno));
+                       exit(2);
+               }
+       }
+       ulogd_log(ULOGD_INFO, "ulogd Version %s starting\n", ULOGD_VERSION);
+       return 0;
+}
+
+/* wrapper to handle conffile error codes */
+static int parse_conffile(const char *section, config_entry_t *ce)
+{
+       int err;
+
+       err = config_parse_file(section, ce);
+
+       switch(err) {
+               case 0:
+                       return 0;
+                       break;
+               case -ERROPEN:
+                       ulogd_log(ULOGD_ERROR,
+                               "unable to open configfile: %s\n",
+                               ulogd_configfile);
+                       break;
+               case -ERRMAND:
+                       ulogd_log(ULOGD_ERROR,
+                               "mandatory option \"%s\" not found\n",
+                               config_errce->key);
+                       break;
+               case -ERRMULT:
+                       ulogd_log(ULOGD_ERROR,
+                               "option \"%s\" occurred more than once\n",
+                               config_errce->key);
+                       break;
+               case -ERRUNKN:
+                       ulogd_log(ULOGD_ERROR,
+                               "unknown config key \"%s\"\n",
+                               config_errce->key);
+                       break;
+               case -ERRSECTION:
+                       ulogd_log(ULOGD_ERROR,
+                               "section \"%s\" not found\n", section);
+                       break;
+       }
+       return 1;
+
+}
+
+/* configuration directives of the main program */
+static config_entry_t logf_ce = { NULL, "logfile", CONFIG_TYPE_STRING, 
+                                 CONFIG_OPT_NONE, 0, 
+                                 { string: ULOGD_LOGFILE_DEFAULT } };
+
+static config_entry_t bufsiz_ce = { &logf_ce, "bufsize", CONFIG_TYPE_INT,       
+                                  CONFIG_OPT_NONE, 0,
+                                  { value: ULOGD_BUFSIZE_DEFAULT } }; 
+
+static config_entry_t plugin_ce = { &bufsiz_ce, "plugin", CONFIG_TYPE_CALLBACK,
+                                   CONFIG_OPT_MULTI, 0, 
+                                   { parser: &load_plugin } };
+
+static config_entry_t nlgroup_ce = { &plugin_ce, "nlgroup", CONFIG_TYPE_INT,
+                                    CONFIG_OPT_NONE, 0,
+                                    { value: ULOGD_NLGROUP_DEFAULT } };
+
+static config_entry_t loglevel_ce = { &nlgroup_ce, "loglevel", CONFIG_TYPE_INT,
+                                     CONFIG_OPT_NONE, 0, 
+                                     { value: ULOGD_NOTICE } };
+static config_entry_t rmem_ce = { &loglevel_ce, "rmem", CONFIG_TYPE_INT,
+                                 CONFIG_OPT_NONE, 0, 
+                                 { value: ULOGD_RMEM_DEFAULT } };
+
+/* log message to the logfile */
+void __ulogd_log(int level, char *file, int line, const char *format, ...)
+{
+       char *timestr;
+       va_list ap;
+       time_t tm;
+       FILE *outfd;
+
+       /* log only messages which have level at least as high as loglevel */
+       if (level < loglevel_ce.u.value)
+               return;
+
+       if (logfile == &syslog_dummy) {
+               /* FIXME: this omit's the 'file' string */
+               va_start(ap, format);
+               vsyslog(ulogd2syslog_level(level), format, ap);
+               va_end(ap);
+       } else {
+               if (logfile)
+                       outfd = logfile;
+               else
+                       outfd = stderr;
+
+               va_start(ap, format);
+
+               tm = time(NULL);
+               timestr = ctime(&tm);
+               timestr[strlen(timestr)-1] = '\0';
+               fprintf(outfd, "%s <%1.1d> %s:%d ", timestr, level, file, line);
+               
+               vfprintf(outfd, format, ap);
+               va_end(ap);
+
+               /* flush glibc's buffer */
+               fflush(outfd);
+       }
+}
+
+static void sigterm_handler(int signal)
+{
+       ulog_output_t *p;
+       
+       ulogd_log(ULOGD_NOTICE, "sigterm received, exiting\n");
+
+       ipulog_destroy_handle(libulog_h);
+       free(libulog_buf);
+       if (logfile != stdout && logfile != &syslog_dummy)
+               fclose(logfile);
+
+       for (p = ulogd_outputs; p; p = p->next) {
+               if (p->fini)
+                       (*p->fini)();
+       }
+
+       exit(0);
+}
+
+static void sighup_handler(int signal)
+{
+       ulog_output_t *p;
+
+       if (logfile != stdout && logfile != &syslog_dummy) {
+               fclose(logfile);
+               logfile = fopen(logf_ce.u.string, "a");
+               if (!logfile)
+                       sigterm_handler(signal);
+       }
+
+       ulogd_log(ULOGD_NOTICE, "sighup received, calling plugin handlers\n");
+       
+       for (p = ulogd_outputs; p; p = p->next) {
+               if (p->signal)
+                       (*p->signal)(SIGHUP);
+       }
+}
+
+static void print_usage(void)
+{
+       /* FIXME */
+       printf("ulogd Version %s\n", ULOGD_VERSION);
+       printf("Copyright (C) 2000-2005 Harald Welte "
+              "<laforge@gnumonks.org>\n");
+       printf("This is free software with ABSOLUTELY NO WARRANTY.\n\n");
+       printf("Parameters:\n");
+       printf("\t-h --help\tThis help page\n");
+       printf("\t-V --version\tPrint version information\n");
+       printf("\t-d --daemon\tDaemonize (fork into background)\n");
+       printf("\t-c --configfile\tUse alternative Configfile\n");
+       printf("\t-u --uid\tChange UID/GID\n");
+}
+
+static struct option opts[] = {
+       { "version", 0, NULL, 'V' },
+       { "daemon", 0, NULL, 'd' },
+       { "help", 0, NULL, 'h' },
+       { "configfile", 1, NULL, 'c'},
+       { "uid", 1, NULL, 'u' },
+       { 0 }
+};
+
+int main(int argc, char* argv[])
+{
+       int len;
+       int argch;
+       int daemonize = 0;
+       int change_uid = 0;
+       char *user = NULL;
+       struct passwd *pw;
+       uid_t uid = 0;
+       gid_t gid = 0;
+       ulog_packet_msg_t *upkt;
+       ulog_output_t *p;
+
+
+       while ((argch = getopt_long(argc, argv, "c:dh::Vu:", opts, NULL)) != -1) {
+               switch (argch) {
+               default:
+               case '?':
+                       if (isprint(optopt))
+                               fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+                       else
+                               fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+
+                       print_usage();
+                       exit(1);
+                       break;
+               case 'h':
+                       print_usage();
+                       exit(0);
+                       break;
+               case 'd':
+                       daemonize = 1;
+                       break;
+               case 'V':
+                       printf("ulogd Version %s\n", ULOGD_VERSION);
+                       printf("Copyright (C) 2000-2005 Harald Welte "
+                              "<laforge@gnumonks.org>\n");
+                       exit(0);
+                       break;
+               case 'c':
+                       ulogd_configfile = optarg;
+                       break;
+               case 'u':
+                       change_uid = 1;
+                       user = strdup(optarg);
+                       pw = getpwnam(user);
+                       if (!pw) {
+                               printf("Unknown user %s.\n", user);
+                               free(user);
+                               exit(1);
+                       }
+                       uid = pw->pw_uid;
+                       gid = pw->pw_gid;
+                       break;
+               }
+       }
+
+       if (config_register_file(ulogd_configfile)) {
+               ulogd_log(ULOGD_FATAL, "error registering configfile \"%s\"\n",
+                         ulogd_configfile);
+               exit(1);
+       }
+       
+       /* parse config file */
+       if (parse_conffile("global", &rmem_ce)) {
+               ulogd_log(ULOGD_FATAL, "parse_conffile\n");
+               exit(1);
+       }
+
+       /* allocate a receive buffer */
+       libulog_buf = (unsigned char *) malloc(bufsiz_ce.u.value);
+
+       if (!libulog_buf) {
+               ulogd_log(ULOGD_FATAL, "unable to allocate receive buffer"
+                         "of %d bytes\n", bufsiz_ce.u.value);
+               ipulog_perror(NULL);
+               exit(1);
+       }
+
+       /* create ipulog handle */
+       libulog_h = ipulog_create_handle(ipulog_group2gmask(nlgroup_ce.u.value),
+                                        rmem_ce.u.value);
+
+       if (!libulog_h) {
+               /* if some error occurrs, print it to stderr */
+               ulogd_log(ULOGD_FATAL, "unable to create ipulogd handle\n");
+               ipulog_perror(NULL);
+               exit(1);
+       }
+
+
+       if (change_uid) {
+               ulogd_log(ULOGD_NOTICE, "Changing UID / GID\n");
+               if (setgid(gid)) {
+                       ulogd_log(ULOGD_FATAL, "can't set GID\n");
+                       ipulog_perror(NULL);
+                       exit(1);
+               }
+               if (setegid(gid)) {
+                       ulogd_log(ULOGD_FATAL, "can't sett effective GID\n");
+                       ipulog_perror(NULL);
+                       exit(1);
+               }
+               if (initgroups(user, gid)) {
+                       ulogd_log(ULOGD_FATAL, "can't set user secondary GID\n");
+                       ipulog_perror(NULL);
+                       exit(1);
+               }
+               if (setuid(uid)) {
+                       ulogd_log(ULOGD_FATAL, "can't set UID\n");
+                       ipulog_perror(NULL);
+                       exit(1);
+               }
+               if (seteuid(uid)) {
+                       ulogd_log(ULOGD_FATAL, "can't set effective UID\n");
+                       ipulog_perror(NULL);
+                       exit(1);
+               }
+       }
+
+       logfile_open(logf_ce.u.string);
+
+       for (p = ulogd_outputs; p; p = p->next) {
+               if (p->init)
+                       (*p->init)();
+       }
+
+#ifdef DEBUG
+       /* dump key and interpreter hash */
+       interh_dump();
+       keyh_dump();
+#endif
+       if (daemonize){
+               if (fork()) {
+                       exit(0);
+               }
+               if (logfile != stdout)
+                       fclose(stdout);
+               fclose(stderr);
+               fclose(stdin);
+               setsid();
+       }
+
+       /* send SIGINT to the term handler, since they hit CTRL-C */
+       signal(SIGINT, &sigterm_handler);
+       signal(SIGHUP, &sighup_handler);
+       signal(SIGTERM, &sigterm_handler);
+
+       ulogd_log(ULOGD_INFO, 
+                 "initialization finished, entering main loop\n");
+
+       /* endless loop receiving packets and handling them over to
+        * handle_packet */
+       while ((len = ipulog_read(libulog_h, libulog_buf, 
+                                bufsiz_ce.u.value, 1))) {
+
+               if (len <= 0) {
+                       /* this is not supposed to happen */
+                       ulogd_log(ULOGD_ERROR, "ipulog_read == %d! "
+                                 "ipulog_errno == %d, errno = %d\n",
+                                 len, ipulog_errno, errno);
+               } else {
+                       while ((upkt = ipulog_get_packet(libulog_h,
+                                              libulog_buf, len))) {
+                               DEBUGP("==> packet received\n");
+                               handle_packet(upkt);
+                       }
+               }
+       }
+
+       /* hackish, but result is the same */
+       sigterm_handler(SIGTERM);       
+       return(0);
+}