Cleaned up a lot of printing.
[distributedratelimiting.git] / ulogd.c
1 /* ulogd, Version $LastChangedRevision: 5373 $
2  *
3  * $Id: ulogd.c 5373 2005-05-04 01:23:16Z laforge $
4  *
5  * userspace logging daemon for the iptables ULOG target
6  * of the linux 2.4 netfilter subsystem.
7  *
8  * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2 
12  *  as published by the Free Software Foundation
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * $Id: ulogd.c 5373 2005-05-04 01:23:16Z laforge $
24  *
25  * Modifications:
26  *      14 Jun 2001 Martin Josefsson <gandalf@wlug.westbo.se>
27  *              - added SIGHUP handler for logfile cycling
28  *
29  *      10 Feb 2002 Alessandro Bono <a.bono@libero.it>
30  *              - added support for non-fork mode
31  *              - added support for logging to stdout
32  *
33  *      09 Sep 2003 Magnus Boden <sarek@ozaba.cx>
34  *              - added support for more flexible multi-section conffile
35  *
36  *      20 Apr 2004 Nicolas Pougetoux <nicolas.pougetoux@edelweb.fr>
37  *              - added suppurt for seteuid()
38  */
39
40 #define ULOGD_VERSION   "1.23"
41
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <stdarg.h>
47 #include <time.h>
48 #include <ctype.h>
49 #include <signal.h>
50 #include <dlfcn.h>
51 #include <sys/types.h>
52 #include <dirent.h>
53 #include <getopt.h>
54 #include <pwd.h>
55 #include <grp.h>
56 #include <syslog.h>
57 #include <libipulog/libipulog.h>
58 #include <ulogd/conffile.h>
59 #include <ulogd/ulogd.h>
60
61 /* Size of the socket recevive memory.  Should be at least the same size as the
62  * 'nlbufsiz' module loadtime parameter of ipt_ULOG.o
63  * If you have _big_ in-kernel queues, you may have to increase this number.  (
64  * --qthreshold 100 * 1500 bytes/packet = 150kB  */
65 #define ULOGD_RMEM_DEFAULT      131071
66
67 /* Size of the receive buffer for the netlink socket.  Should be at least of
68  * RMEM_DEFAULT size.  */
69 #define ULOGD_BUFSIZE_DEFAULT   150000
70
71 #ifdef DEBUG
72 #define DEBUGP(format, args...) fprintf(stderr, format, ## args)
73 #else
74 #define DEBUGP(format, args...) 
75 #endif
76
77 /* default config parameters, if not changed in configfile */
78 #ifndef ULOGD_LOGFILE_DEFAULT
79 #define ULOGD_LOGFILE_DEFAULT   "/var/log/ulogd.log"
80 #endif
81 #ifndef ULOGD_NLGROUP_DEFAULT
82 #define ULOGD_NLGROUP_DEFAULT   32
83 #endif
84
85 /* where to look for the config file */
86 #ifndef ULOGD_CONFIGFILE
87 #define ULOGD_CONFIGFILE        "/etc/ulogd.conf"
88 #endif
89
90 /* global variables */
91 static struct ipulog_handle *libulog_h; /* our libipulog handle */
92 static unsigned char* libulog_buf;      /* the receive buffer */
93 static FILE *logfile = NULL;            /* logfile pointer */
94 static char *ulogd_configfile = ULOGD_CONFIGFILE;
95
96 /* linked list for all registered interpreters */
97 static ulog_interpreter_t *ulogd_interpreters;
98
99 /* linked list for all registered output targets */
100 static ulog_output_t *ulogd_outputs;
101
102 /***********************************************************************
103  * INTERPRETER AND KEY HASH FUNCTIONS                   (new in 0.9)
104  ***********************************************************************/
105
106 /* We keep hashtables of interpreters and registered keys. The hash-tables
107  * are allocated dynamically at program load time. You may control the
108  * allocation granularity of both hashes (i.e. the amount of hashtable
109  * entries are allocated at one time) through modification of the constants
110  * INTERH_ALLOC_GRAN and KEYH_ALLOC_GRAN 
111  */
112
113 /* allocation granularith */
114 #define INTERH_ALLOC_GRAN       5
115
116 /* hashtable for all registered interpreters */
117 static ulog_interpreter_t **ulogd_interh;
118
119 /* current hashtable size */
120 static unsigned int ulogd_interh_ids_alloc;
121
122 /* total number of registered ids */
123 static unsigned int ulogd_interh_ids;
124
125 /* allocate a new interpreter id and write it into the interpreter struct */
126 static unsigned int interh_allocid(ulog_interpreter_t *ip)
127 {
128         unsigned int id;
129
130         id = ++ulogd_interh_ids;
131         
132         if (id >= ulogd_interh_ids_alloc) {
133                 if (!ulogd_interh)
134                         ulogd_interh = (ulog_interpreter_t **) 
135                                 malloc(INTERH_ALLOC_GRAN *
136                                         sizeof(ulog_interpreter_t));
137                 else
138                         ulogd_interh = (ulog_interpreter_t **)
139                                 realloc(ulogd_interh, 
140                                         (INTERH_ALLOC_GRAN +
141                                          ulogd_interh_ids_alloc) *
142                                         sizeof(ulog_interpreter_t));
143
144                 ulogd_interh_ids_alloc += INTERH_ALLOC_GRAN;
145         }
146
147         ip->id = id;
148         ulogd_interh[id] = ip;
149         return id;
150 }
151
152 /* get interpreter id by name */
153 unsigned int interh_getid(const char *name)
154 {
155         unsigned int i;
156         for (i = 1; i <= ulogd_interh_ids; i++)
157                 if (!strcmp(name, (ulogd_interh[i])->name))
158                         return i;
159
160         return 0;
161 }
162
163 #ifdef DEBUG
164 /* dump out the contents of the interpreter hash */
165 static void interh_dump(void)
166 {
167         unsigned int i;
168
169         for (i = 1; i <= ulogd_interh_ids; i++)
170                 ulogd_log(ULOGD_DEBUG, "ulogd_interh[%d] = %s\n", 
171                         i, (ulogd_interh[i])->name);
172
173 }
174 #endif
175
176 /* key hash allocation granularity */
177 #define KEYH_ALLOC_GRAN 20
178
179 /* hash table for key ids */
180 struct ulogd_keyh_entry *ulogd_keyh;
181
182 /* current size of the hashtable */
183 static unsigned int ulogd_keyh_ids_alloc;
184
185 /* total number of registered keys */
186 static unsigned int ulogd_keyh_ids;
187
188 /* allocate a new key_id */
189 static unsigned int keyh_allocid(ulog_interpreter_t *ip, unsigned int offset,
190                                 const char *name)
191 {
192         unsigned int id;
193
194         id = ++ulogd_keyh_ids;
195
196         if (id >= ulogd_keyh_ids_alloc) {
197                 if (!ulogd_keyh) {
198                         ulogd_keyh = (struct ulogd_keyh_entry *)
199                                 malloc(KEYH_ALLOC_GRAN * 
200                                         sizeof(struct ulogd_keyh_entry));
201                         if (!ulogd_keyh) {
202                                 ulogd_log(ULOGD_ERROR, "OOM!\n");
203                                 return 0;
204                         }
205                 } else {
206                         ulogd_keyh = (struct ulogd_keyh_entry *)
207                                 realloc(ulogd_keyh, (KEYH_ALLOC_GRAN
208                                                 +ulogd_keyh_ids_alloc) *
209                                         sizeof(struct ulogd_keyh_entry));
210
211                         if (!ulogd_keyh) {
212                                 ulogd_log(ULOGD_ERROR, "OOM!\n");
213                                 return 0;
214                         }
215                 }
216
217                 ulogd_keyh_ids_alloc += KEYH_ALLOC_GRAN;
218         }
219
220         ulogd_keyh[id].interp = ip;
221         ulogd_keyh[id].offset = offset;
222         ulogd_keyh[id].name = name;
223
224         return id;
225 }
226
227 #ifdef DEBUG
228 /* dump the keyhash to standard output */
229 static void keyh_dump(void)
230 {
231         unsigned int i;
232
233         printf("dumping keyh\n");
234         for (i = 1; i <= ulogd_keyh_ids; i++)
235                 printf("ulogd_keyh[%lu] = %s:%u\n", i, 
236                         ulogd_keyh[i].interp->name, ulogd_keyh[i].offset);
237 }
238 #endif
239
240 /* get keyid by name */
241 unsigned int keyh_getid(const char *name)
242 {
243         unsigned int i;
244         for (i = 1; i <= ulogd_keyh_ids; i++)
245                 if (!strcmp(name, ulogd_keyh[i].name))
246                         return i;
247
248         return 0;
249 }
250
251 /* get key name by keyid */
252 char *keyh_getname(unsigned int id)
253 {
254         if (id > ulogd_keyh_ids) {
255                 ulogd_log(ULOGD_NOTICE, 
256                         "keyh_getname called with invalid id%u\n", id);
257                 return NULL;
258         }
259                 
260         return ulogd_keyh[id].interp->name;
261 }
262
263 /* get result for given key id. does not check if result valid */
264 ulog_iret_t *keyh_getres(unsigned int id)
265 {
266         ulog_iret_t *ret;
267
268         if (id > ulogd_keyh_ids) {
269                 ulogd_log(ULOGD_NOTICE,
270                         "keyh_getres called with invalid id %d\n", id);
271                 return NULL;
272         }
273
274         ret = &ulogd_keyh[id].interp->result[ulogd_keyh[id].offset];
275
276         return ret;
277 }
278
279 /***********************************************************************
280  * INTERPRETER MANAGEMENT 
281  ***********************************************************************/
282
283 /* try to lookup a registered interpreter for a given name */
284 static ulog_interpreter_t *find_interpreter(const char *name)
285 {
286         unsigned int id;
287         
288         id = interh_getid(name);
289         if (!id)
290                 return NULL;
291
292         return ulogd_interh[id];
293 }
294
295 /* the function called by all interpreter plugins for registering their
296  * target. */ 
297 void register_interpreter(ulog_interpreter_t *me)
298 {
299         unsigned int i;
300
301         /* check if we already have an interpreter with this name */
302         if (find_interpreter(me->name)) {
303                 ulogd_log(ULOGD_NOTICE, 
304                         "interpreter `%s' already registered\n", me->name);
305                 return;
306         }
307
308         ulogd_log(ULOGD_INFO, "registering interpreter `%s'\n", me->name);
309
310         /* allocate a new interpreter id for it */
311         if (!interh_allocid(me)) {
312                 ulogd_log(ULOGD_ERROR, "unable to obtain interh_id for "
313                         "interpreter '%s'\n", me->name);
314                 return;
315         }
316
317         /* - allocate one keyh_id for each result of this interpreter 
318          * - link the elements to each other */
319         for (i = 0; i < me->key_num; i++) {
320                 if (!keyh_allocid(me, i, me->result[i].key)) {
321                         ulogd_log(ULOGD_ERROR, "unable to obtain keyh_id "
322                                 "for interpreter %s, key %d", me->name,
323                                 me->result[i].key);
324                         continue;
325                 }
326                 if (i != me->key_num - 1)
327                         me->result[i].next = &me->result[i+1];
328         }
329
330         /* all work done, we can prepend the new interpreter to the list */
331         if (ulogd_interpreters)
332                 me->result[me->key_num - 1].next = 
333                                         &ulogd_interpreters->result[0];
334         me->next = ulogd_interpreters;
335         ulogd_interpreters = me;
336 }
337
338 /***********************************************************************
339  * OUTPUT MANAGEMENT 
340  ***********************************************************************/
341
342 /* try to lookup a registered output plugin for a given name */
343 static ulog_output_t *find_output(const char *name)
344 {
345         ulog_output_t *ptr;
346
347         for (ptr = ulogd_outputs; ptr; ptr = ptr->next) {
348                 if (strcmp(name, ptr->name) == 0)
349                                 return ptr;
350         }
351
352         return NULL;
353 }
354
355 /* the function called by all output plugins for registering themselves */
356 void register_output(ulog_output_t *me)
357 {
358         if (find_output(me->name)) {
359                 ulogd_log(ULOGD_NOTICE, "output `%s' already registered\n",
360                                 me->name);
361                 exit(EXIT_FAILURE);
362         }
363         ulogd_log(ULOGD_INFO, "registering output `%s'\n", me->name);
364         me->next = ulogd_outputs;
365         ulogd_outputs = me;
366 }
367
368 /***********************************************************************
369  * MAIN PROGRAM
370  ***********************************************************************/
371
372 static FILE syslog_dummy;
373
374 static inline int ulogd2syslog_level(int level)
375 {
376         int syslog_level = LOG_WARNING;
377
378         switch (level) {
379                 case ULOGD_DEBUG:
380                         syslog_level = LOG_DEBUG;
381                         break;
382                 case ULOGD_INFO:
383                         syslog_level = LOG_INFO;
384                         break;
385                 case ULOGD_NOTICE:
386                         syslog_level = LOG_NOTICE;
387                         break;
388                 case ULOGD_ERROR:
389                         syslog_level = LOG_ERR;
390                         break;
391                 case ULOGD_FATAL:
392                         syslog_level = LOG_CRIT;
393                         break;
394         }
395         return syslog_level;
396 }
397 /* propagate results to all registered output plugins */
398 static void propagate_results(ulog_iret_t *ret)
399 {
400         ulog_output_t *p;
401
402         for (p = ulogd_outputs; p; p = p->next) {
403                 (*p->output)(ret);
404         }
405 }
406
407 /* clean results (set all values to 0 and free pointers) */
408 static void clean_results(ulog_iret_t *ret)
409 {
410         ulog_iret_t *r;
411
412         for (r = ret; r; r = r->next) {
413                 if (r->flags & ULOGD_RETF_FREE) {
414                         free(r->value.ptr);
415                         r->value.ptr = NULL;
416                 }
417                 memset(&r->value, 0, sizeof(r->value));
418                 r->flags &= ~ULOGD_RETF_VALID;
419         }
420 }
421
422 /* call all registered interpreters and hand the results over to 
423  * propagate_results */
424 static void handle_packet(ulog_packet_msg_t *pkt)
425 {
426         ulog_iret_t *ret;
427         ulog_iret_t *allret = NULL;
428         ulog_interpreter_t *ip;
429
430         unsigned int i,j;
431
432         /* If there are no interpreters registered yet,
433          * ignore this packet */
434         if (!ulogd_interh_ids) {
435                 ulogd_log(ULOGD_NOTICE, 
436                           "packet received, but no interpreters found\n");
437                 return;
438         }
439
440         for (i = 1; i <= ulogd_interh_ids; i++) {
441                 ip = ulogd_interh[i];
442                 /* call interpreter */
443                 if ((ret = ((ip)->interp)(ip, pkt))) {
444                         /* create references for result linked-list */
445                         for (j = 0; j < ip->key_num; j++) {
446                                 if (IS_VALID(ip->result[j])) {
447                                         ip->result[j].cur_next = allret;
448                                         allret = &ip->result[j];
449                                 }
450                         }
451                 }
452         }
453         propagate_results(allret);
454         clean_results(ulogd_interpreters->result);
455 }
456
457 /* plugin loader to dlopen() a plugins */
458 static int load_plugin(char *file)
459 {
460         if (!dlopen(file, RTLD_NOW)) {
461                 ulogd_log(ULOGD_ERROR, "load_plugins: '%s': %s\n", file,
462                           dlerror());
463                 return 1;
464         }
465         return 0;
466 }
467
468 /* open the logfile */
469 static int logfile_open(const char *name)
470 {
471         if (!strcmp(name, "syslog")) {
472                 openlog("ulogd", LOG_PID, LOG_DAEMON);
473                 logfile = &syslog_dummy;
474         } else if (!strcmp(name,"stdout"))
475                 logfile = stdout;
476         else {
477                 logfile = fopen(name, "a");
478                 if (!logfile) {
479                         fprintf(stderr, "ERROR: can't open logfile %s: %s\n", 
480                                 name, strerror(errno));
481                         exit(2);
482                 }
483         }
484         ulogd_log(ULOGD_INFO, "ulogd Version %s starting\n", ULOGD_VERSION);
485         return 0;
486 }
487
488 /* wrapper to handle conffile error codes */
489 static int parse_conffile(const char *section, config_entry_t *ce)
490 {
491         int err;
492
493         err = config_parse_file(section, ce);
494
495         switch(err) {
496                 case 0:
497                         return 0;
498                         break;
499                 case -ERROPEN:
500                         ulogd_log(ULOGD_ERROR,
501                                 "unable to open configfile: %s\n",
502                                 ulogd_configfile);
503                         break;
504                 case -ERRMAND:
505                         ulogd_log(ULOGD_ERROR,
506                                 "mandatory option \"%s\" not found\n",
507                                 config_errce->key);
508                         break;
509                 case -ERRMULT:
510                         ulogd_log(ULOGD_ERROR,
511                                 "option \"%s\" occurred more than once\n",
512                                 config_errce->key);
513                         break;
514                 case -ERRUNKN:
515                         ulogd_log(ULOGD_ERROR,
516                                 "unknown config key \"%s\"\n",
517                                 config_errce->key);
518                         break;
519                 case -ERRSECTION:
520                         ulogd_log(ULOGD_ERROR,
521                                 "section \"%s\" not found\n", section);
522                         break;
523         }
524         return 1;
525
526 }
527
528 /* configuration directives of the main program */
529 static config_entry_t logf_ce = { NULL, "logfile", CONFIG_TYPE_STRING, 
530                                   CONFIG_OPT_NONE, 0, 
531                                   { string: ULOGD_LOGFILE_DEFAULT } };
532
533 static config_entry_t bufsiz_ce = { &logf_ce, "bufsize", CONFIG_TYPE_INT,       
534                                    CONFIG_OPT_NONE, 0,
535                                    { value: ULOGD_BUFSIZE_DEFAULT } }; 
536
537 static config_entry_t plugin_ce = { &bufsiz_ce, "plugin", CONFIG_TYPE_CALLBACK,
538                                     CONFIG_OPT_MULTI, 0, 
539                                     { parser: &load_plugin } };
540
541 static config_entry_t nlgroup_ce = { &plugin_ce, "nlgroup", CONFIG_TYPE_INT,
542                                      CONFIG_OPT_NONE, 0,
543                                      { value: ULOGD_NLGROUP_DEFAULT } };
544
545 static config_entry_t loglevel_ce = { &nlgroup_ce, "loglevel", CONFIG_TYPE_INT,
546                                       CONFIG_OPT_NONE, 0, 
547                                       { value: ULOGD_NOTICE } };
548 static config_entry_t rmem_ce = { &loglevel_ce, "rmem", CONFIG_TYPE_INT,
549                                   CONFIG_OPT_NONE, 0, 
550                                   { value: ULOGD_RMEM_DEFAULT } };
551
552 /* log message to the logfile */
553 void __ulogd_log(int level, char *file, int line, const char *format, ...)
554 {
555         char *timestr;
556         va_list ap;
557         time_t tm;
558         FILE *outfd;
559
560         /* log only messages which have level at least as high as loglevel */
561         if (level < loglevel_ce.u.value)
562                 return;
563
564         if (logfile == &syslog_dummy) {
565                 /* FIXME: this omit's the 'file' string */
566                 va_start(ap, format);
567                 vsyslog(ulogd2syslog_level(level), format, ap);
568                 va_end(ap);
569         } else {
570                 if (logfile)
571                         outfd = logfile;
572                 else
573                         outfd = stderr;
574
575                 va_start(ap, format);
576
577                 tm = time(NULL);
578                 timestr = ctime(&tm);
579                 timestr[strlen(timestr)-1] = '\0';
580                 fprintf(outfd, "%s <%1.1d> %s:%d ", timestr, level, file, line);
581                 
582                 vfprintf(outfd, format, ap);
583                 va_end(ap);
584
585                 /* flush glibc's buffer */
586                 fflush(outfd);
587         }
588 }
589
590 static void sigterm_handler(int signal)
591 {
592         ulog_output_t *p;
593         
594         ulogd_log(ULOGD_NOTICE, "sigterm received, exiting\n");
595
596         ipulog_destroy_handle(libulog_h);
597         free(libulog_buf);
598         if (logfile != stdout && logfile != &syslog_dummy)
599                 fclose(logfile);
600
601         for (p = ulogd_outputs; p; p = p->next) {
602                 if (p->fini)
603                         (*p->fini)();
604         }
605
606         exit(0);
607 }
608
609 static void sighup_handler(int signal)
610 {
611         ulog_output_t *p;
612
613         if (logfile != stdout && logfile != &syslog_dummy) {
614                 fclose(logfile);
615                 logfile = fopen(logf_ce.u.string, "a");
616                 if (!logfile)
617                         sigterm_handler(signal);
618         }
619
620         ulogd_log(ULOGD_NOTICE, "sighup received, calling plugin handlers\n");
621         
622         for (p = ulogd_outputs; p; p = p->next) {
623                 if (p->signal)
624                         (*p->signal)(SIGHUP);
625         }
626 }
627
628 static void print_usage(void)
629 {
630         /* FIXME */
631         printf("ulogd Version %s\n", ULOGD_VERSION);
632         printf("Copyright (C) 2000-2005 Harald Welte "
633                "<laforge@gnumonks.org>\n");
634         printf("This is free software with ABSOLUTELY NO WARRANTY.\n\n");
635         printf("Parameters:\n");
636         printf("\t-h --help\tThis help page\n");
637         printf("\t-V --version\tPrint version information\n");
638         printf("\t-d --daemon\tDaemonize (fork into background)\n");
639         printf("\t-c --configfile\tUse alternative Configfile\n");
640         printf("\t-u --uid\tChange UID/GID\n");
641 }
642
643 static struct option opts[] = {
644         { "version", 0, NULL, 'V' },
645         { "daemon", 0, NULL, 'd' },
646         { "help", 0, NULL, 'h' },
647         { "configfile", 1, NULL, 'c'},
648         { "uid", 1, NULL, 'u' },
649         { 0 }
650 };
651
652 int main(int argc, char* argv[])
653 {
654         int len;
655         int argch;
656         int daemonize = 0;
657         int change_uid = 0;
658         char *user = NULL;
659         struct passwd *pw;
660         uid_t uid = 0;
661         gid_t gid = 0;
662         ulog_packet_msg_t *upkt;
663         ulog_output_t *p;
664
665
666         while ((argch = getopt_long(argc, argv, "c:dh::Vu:", opts, NULL)) != -1) {
667                 switch (argch) {
668                 default:
669                 case '?':
670                         if (isprint(optopt))
671                                 fprintf(stderr, "Unknown option `-%c'.\n", optopt);
672                         else
673                                 fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
674
675                         print_usage();
676                         exit(1);
677                         break;
678                 case 'h':
679                         print_usage();
680                         exit(0);
681                         break;
682                 case 'd':
683                         daemonize = 1;
684                         break;
685                 case 'V':
686                         printf("ulogd Version %s\n", ULOGD_VERSION);
687                         printf("Copyright (C) 2000-2005 Harald Welte "
688                                "<laforge@gnumonks.org>\n");
689                         exit(0);
690                         break;
691                 case 'c':
692                         ulogd_configfile = optarg;
693                         break;
694                 case 'u':
695                         change_uid = 1;
696                         user = strdup(optarg);
697                         pw = getpwnam(user);
698                         if (!pw) {
699                                 printf("Unknown user %s.\n", user);
700                                 free(user);
701                                 exit(1);
702                         }
703                         uid = pw->pw_uid;
704                         gid = pw->pw_gid;
705                         break;
706                 }
707         }
708
709         if (config_register_file(ulogd_configfile)) {
710                 ulogd_log(ULOGD_FATAL, "error registering configfile \"%s\"\n",
711                           ulogd_configfile);
712                 exit(1);
713         }
714         
715         /* parse config file */
716         if (parse_conffile("global", &rmem_ce)) {
717                 ulogd_log(ULOGD_FATAL, "parse_conffile\n");
718                 exit(1);
719         }
720
721         /* allocate a receive buffer */
722         libulog_buf = (unsigned char *) malloc(bufsiz_ce.u.value);
723
724         if (!libulog_buf) {
725                 ulogd_log(ULOGD_FATAL, "unable to allocate receive buffer"
726                           "of %d bytes\n", bufsiz_ce.u.value);
727                 ipulog_perror(NULL);
728                 exit(1);
729         }
730
731         /* create ipulog handle */
732         libulog_h = ipulog_create_handle(ipulog_group2gmask(nlgroup_ce.u.value),
733                                          rmem_ce.u.value);
734
735         if (!libulog_h) {
736                 /* if some error occurrs, print it to stderr */
737                 ulogd_log(ULOGD_FATAL, "unable to create ipulogd handle\n");
738                 ipulog_perror(NULL);
739                 exit(1);
740         }
741
742
743         if (change_uid) {
744                 ulogd_log(ULOGD_NOTICE, "Changing UID / GID\n");
745                 if (setgid(gid)) {
746                         ulogd_log(ULOGD_FATAL, "can't set GID\n");
747                         ipulog_perror(NULL);
748                         exit(1);
749                 }
750                 if (setegid(gid)) {
751                         ulogd_log(ULOGD_FATAL, "can't sett effective GID\n");
752                         ipulog_perror(NULL);
753                         exit(1);
754                 }
755                 if (initgroups(user, gid)) {
756                         ulogd_log(ULOGD_FATAL, "can't set user secondary GID\n");
757                         ipulog_perror(NULL);
758                         exit(1);
759                 }
760                 if (setuid(uid)) {
761                         ulogd_log(ULOGD_FATAL, "can't set UID\n");
762                         ipulog_perror(NULL);
763                         exit(1);
764                 }
765                 if (seteuid(uid)) {
766                         ulogd_log(ULOGD_FATAL, "can't set effective UID\n");
767                         ipulog_perror(NULL);
768                         exit(1);
769                 }
770         }
771
772         logfile_open(logf_ce.u.string);
773
774         for (p = ulogd_outputs; p; p = p->next) {
775                 if (p->init)
776                         (*p->init)();
777         }
778
779 #ifdef DEBUG
780         /* dump key and interpreter hash */
781         interh_dump();
782         keyh_dump();
783 #endif
784         if (daemonize){
785                 if (fork()) {
786                         exit(0);
787                 }
788                 if (logfile != stdout)
789                         fclose(stdout);
790                 fclose(stderr);
791                 fclose(stdin);
792                 setsid();
793         }
794
795         /* send SIGINT to the term handler, since they hit CTRL-C */
796         signal(SIGINT, &sigterm_handler);
797         signal(SIGHUP, &sighup_handler);
798         signal(SIGTERM, &sigterm_handler);
799
800         ulogd_log(ULOGD_INFO, 
801                   "initialization finished, entering main loop\n");
802
803         /* endless loop receiving packets and handling them over to
804          * handle_packet */
805         while ((len = ipulog_read(libulog_h, libulog_buf, 
806                                  bufsiz_ce.u.value, 1))) {
807
808                 if (len <= 0) {
809                         /* this is not supposed to happen */
810                         ulogd_log(ULOGD_ERROR, "ipulog_read == %d! "
811                                   "ipulog_errno == %d, errno = %d\n",
812                                   len, ipulog_errno, errno);
813                 } else {
814                         while ((upkt = ipulog_get_packet(libulog_h,
815                                                libulog_buf, len))) {
816                                 DEBUGP("==> packet received\n");
817                                 handle_packet(upkt);
818                         }
819                 }
820         }
821
822         /* hackish, but result is the same */
823         sigterm_handler(SIGTERM);       
824         return(0);
825 }