1 /* ulogd, Version $LastChangedRevision: 5373 $
3 * $Id: ulogd.c 5373 2005-05-04 01:23:16Z laforge $
5 * userspace logging daemon for the iptables ULOG target
6 * of the linux 2.4 netfilter subsystem.
8 * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org>
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
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.
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
23 * $Id: ulogd.c 5373 2005-05-04 01:23:16Z laforge $
26 * 14 Jun 2001 Martin Josefsson <gandalf@wlug.westbo.se>
27 * - added SIGHUP handler for logfile cycling
29 * 10 Feb 2002 Alessandro Bono <a.bono@libero.it>
30 * - added support for non-fork mode
31 * - added support for logging to stdout
33 * 09 Sep 2003 Magnus Boden <sarek@ozaba.cx>
34 * - added support for more flexible multi-section conffile
36 * 20 Apr 2004 Nicolas Pougetoux <nicolas.pougetoux@edelweb.fr>
37 * - added suppurt for seteuid()
40 #define ULOGD_VERSION "1.23"
51 #include <sys/types.h>
57 #include <libipulog/libipulog.h>
58 #include <ulogd/conffile.h>
59 #include <ulogd/ulogd.h>
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
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
72 #define DEBUGP(format, args...) fprintf(stderr, format, ## args)
74 #define DEBUGP(format, args...)
77 /* default config parameters, if not changed in configfile */
78 #ifndef ULOGD_LOGFILE_DEFAULT
79 #define ULOGD_LOGFILE_DEFAULT "/var/log/ulogd.log"
81 #ifndef ULOGD_NLGROUP_DEFAULT
82 #define ULOGD_NLGROUP_DEFAULT 32
85 /* where to look for the config file */
86 #ifndef ULOGD_CONFIGFILE
87 #define ULOGD_CONFIGFILE "/etc/ulogd.conf"
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;
96 /* linked list for all registered interpreters */
97 static ulog_interpreter_t *ulogd_interpreters;
99 /* linked list for all registered output targets */
100 static ulog_output_t *ulogd_outputs;
102 /***********************************************************************
103 * INTERPRETER AND KEY HASH FUNCTIONS (new in 0.9)
104 ***********************************************************************/
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
113 /* allocation granularith */
114 #define INTERH_ALLOC_GRAN 5
116 /* hashtable for all registered interpreters */
117 static ulog_interpreter_t **ulogd_interh;
119 /* current hashtable size */
120 static unsigned int ulogd_interh_ids_alloc;
122 /* total number of registered ids */
123 static unsigned int ulogd_interh_ids;
125 /* allocate a new interpreter id and write it into the interpreter struct */
126 static unsigned int interh_allocid(ulog_interpreter_t *ip)
130 id = ++ulogd_interh_ids;
132 if (id >= ulogd_interh_ids_alloc) {
134 ulogd_interh = (ulog_interpreter_t **)
135 malloc(INTERH_ALLOC_GRAN *
136 sizeof(ulog_interpreter_t));
138 ulogd_interh = (ulog_interpreter_t **)
139 realloc(ulogd_interh,
141 ulogd_interh_ids_alloc) *
142 sizeof(ulog_interpreter_t));
144 ulogd_interh_ids_alloc += INTERH_ALLOC_GRAN;
148 ulogd_interh[id] = ip;
152 /* get interpreter id by name */
153 unsigned int interh_getid(const char *name)
156 for (i = 1; i <= ulogd_interh_ids; i++)
157 if (!strcmp(name, (ulogd_interh[i])->name))
164 /* dump out the contents of the interpreter hash */
165 static void interh_dump(void)
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);
176 /* key hash allocation granularity */
177 #define KEYH_ALLOC_GRAN 20
179 /* hash table for key ids */
180 struct ulogd_keyh_entry *ulogd_keyh;
182 /* current size of the hashtable */
183 static unsigned int ulogd_keyh_ids_alloc;
185 /* total number of registered keys */
186 static unsigned int ulogd_keyh_ids;
188 /* allocate a new key_id */
189 static unsigned int keyh_allocid(ulog_interpreter_t *ip, unsigned int offset,
194 id = ++ulogd_keyh_ids;
196 if (id >= ulogd_keyh_ids_alloc) {
198 ulogd_keyh = (struct ulogd_keyh_entry *)
199 malloc(KEYH_ALLOC_GRAN *
200 sizeof(struct ulogd_keyh_entry));
202 ulogd_log(ULOGD_ERROR, "OOM!\n");
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));
212 ulogd_log(ULOGD_ERROR, "OOM!\n");
217 ulogd_keyh_ids_alloc += KEYH_ALLOC_GRAN;
220 ulogd_keyh[id].interp = ip;
221 ulogd_keyh[id].offset = offset;
222 ulogd_keyh[id].name = name;
228 /* dump the keyhash to standard output */
229 static void keyh_dump(void)
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);
240 /* get keyid by name */
241 unsigned int keyh_getid(const char *name)
244 for (i = 1; i <= ulogd_keyh_ids; i++)
245 if (!strcmp(name, ulogd_keyh[i].name))
251 /* get key name by keyid */
252 char *keyh_getname(unsigned int id)
254 if (id > ulogd_keyh_ids) {
255 ulogd_log(ULOGD_NOTICE,
256 "keyh_getname called with invalid id%u\n", id);
260 return ulogd_keyh[id].interp->name;
263 /* get result for given key id. does not check if result valid */
264 ulog_iret_t *keyh_getres(unsigned int id)
268 if (id > ulogd_keyh_ids) {
269 ulogd_log(ULOGD_NOTICE,
270 "keyh_getres called with invalid id %d\n", id);
274 ret = &ulogd_keyh[id].interp->result[ulogd_keyh[id].offset];
279 /***********************************************************************
280 * INTERPRETER MANAGEMENT
281 ***********************************************************************/
283 /* try to lookup a registered interpreter for a given name */
284 static ulog_interpreter_t *find_interpreter(const char *name)
288 id = interh_getid(name);
292 return ulogd_interh[id];
295 /* the function called by all interpreter plugins for registering their
297 void register_interpreter(ulog_interpreter_t *me)
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);
308 ulogd_log(ULOGD_INFO, "registering interpreter `%s'\n", me->name);
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);
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,
326 if (i != me->key_num - 1)
327 me->result[i].next = &me->result[i+1];
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;
338 /***********************************************************************
340 ***********************************************************************/
342 /* try to lookup a registered output plugin for a given name */
343 static ulog_output_t *find_output(const char *name)
347 for (ptr = ulogd_outputs; ptr; ptr = ptr->next) {
348 if (strcmp(name, ptr->name) == 0)
355 /* the function called by all output plugins for registering themselves */
356 void register_output(ulog_output_t *me)
358 if (find_output(me->name)) {
359 ulogd_log(ULOGD_NOTICE, "output `%s' already registered\n",
363 ulogd_log(ULOGD_INFO, "registering output `%s'\n", me->name);
364 me->next = ulogd_outputs;
368 /***********************************************************************
370 ***********************************************************************/
372 static FILE syslog_dummy;
374 static inline int ulogd2syslog_level(int level)
376 int syslog_level = LOG_WARNING;
380 syslog_level = LOG_DEBUG;
383 syslog_level = LOG_INFO;
386 syslog_level = LOG_NOTICE;
389 syslog_level = LOG_ERR;
392 syslog_level = LOG_CRIT;
397 /* propagate results to all registered output plugins */
398 static void propagate_results(ulog_iret_t *ret)
402 for (p = ulogd_outputs; p; p = p->next) {
407 /* clean results (set all values to 0 and free pointers) */
408 static void clean_results(ulog_iret_t *ret)
412 for (r = ret; r; r = r->next) {
413 if (r->flags & ULOGD_RETF_FREE) {
417 memset(&r->value, 0, sizeof(r->value));
418 r->flags &= ~ULOGD_RETF_VALID;
422 /* call all registered interpreters and hand the results over to
423 * propagate_results */
424 static void handle_packet(ulog_packet_msg_t *pkt)
427 ulog_iret_t *allret = NULL;
428 ulog_interpreter_t *ip;
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");
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];
453 propagate_results(allret);
454 clean_results(ulogd_interpreters->result);
457 /* plugin loader to dlopen() a plugins */
458 static int load_plugin(char *file)
460 if (!dlopen(file, RTLD_NOW)) {
461 ulogd_log(ULOGD_ERROR, "load_plugins: '%s': %s\n", file,
468 /* open the logfile */
469 static int logfile_open(const char *name)
471 if (!strcmp(name, "syslog")) {
472 openlog("ulogd", LOG_PID, LOG_DAEMON);
473 logfile = &syslog_dummy;
474 } else if (!strcmp(name,"stdout"))
477 logfile = fopen(name, "a");
479 fprintf(stderr, "ERROR: can't open logfile %s: %s\n",
480 name, strerror(errno));
484 ulogd_log(ULOGD_INFO, "ulogd Version %s starting\n", ULOGD_VERSION);
488 /* wrapper to handle conffile error codes */
489 static int parse_conffile(const char *section, config_entry_t *ce)
493 err = config_parse_file(section, ce);
500 ulogd_log(ULOGD_ERROR,
501 "unable to open configfile: %s\n",
505 ulogd_log(ULOGD_ERROR,
506 "mandatory option \"%s\" not found\n",
510 ulogd_log(ULOGD_ERROR,
511 "option \"%s\" occurred more than once\n",
515 ulogd_log(ULOGD_ERROR,
516 "unknown config key \"%s\"\n",
520 ulogd_log(ULOGD_ERROR,
521 "section \"%s\" not found\n", section);
528 /* configuration directives of the main program */
529 static config_entry_t logf_ce = { NULL, "logfile", CONFIG_TYPE_STRING,
531 { string: ULOGD_LOGFILE_DEFAULT } };
533 static config_entry_t bufsiz_ce = { &logf_ce, "bufsize", CONFIG_TYPE_INT,
535 { value: ULOGD_BUFSIZE_DEFAULT } };
537 static config_entry_t plugin_ce = { &bufsiz_ce, "plugin", CONFIG_TYPE_CALLBACK,
539 { parser: &load_plugin } };
541 static config_entry_t nlgroup_ce = { &plugin_ce, "nlgroup", CONFIG_TYPE_INT,
543 { value: ULOGD_NLGROUP_DEFAULT } };
545 static config_entry_t loglevel_ce = { &nlgroup_ce, "loglevel", CONFIG_TYPE_INT,
547 { value: ULOGD_NOTICE } };
548 static config_entry_t rmem_ce = { &loglevel_ce, "rmem", CONFIG_TYPE_INT,
550 { value: ULOGD_RMEM_DEFAULT } };
552 /* log message to the logfile */
553 void __ulogd_log(int level, char *file, int line, const char *format, ...)
560 /* log only messages which have level at least as high as loglevel */
561 if (level < loglevel_ce.u.value)
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);
575 va_start(ap, format);
578 timestr = ctime(&tm);
579 timestr[strlen(timestr)-1] = '\0';
580 fprintf(outfd, "%s <%1.1d> %s:%d ", timestr, level, file, line);
582 vfprintf(outfd, format, ap);
585 /* flush glibc's buffer */
590 static void sigterm_handler(int signal)
594 ulogd_log(ULOGD_NOTICE, "sigterm received, exiting\n");
596 ipulog_destroy_handle(libulog_h);
598 if (logfile != stdout && logfile != &syslog_dummy)
601 for (p = ulogd_outputs; p; p = p->next) {
609 static void sighup_handler(int signal)
613 if (logfile != stdout && logfile != &syslog_dummy) {
615 logfile = fopen(logf_ce.u.string, "a");
617 sigterm_handler(signal);
620 ulogd_log(ULOGD_NOTICE, "sighup received, calling plugin handlers\n");
622 for (p = ulogd_outputs; p; p = p->next) {
624 (*p->signal)(SIGHUP);
628 static void print_usage(void)
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");
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' },
652 int main(int argc, char* argv[])
662 ulog_packet_msg_t *upkt;
666 while ((argch = getopt_long(argc, argv, "c:dh::Vu:", opts, NULL)) != -1) {
671 fprintf(stderr, "Unknown option `-%c'.\n", optopt);
673 fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
686 printf("ulogd Version %s\n", ULOGD_VERSION);
687 printf("Copyright (C) 2000-2005 Harald Welte "
688 "<laforge@gnumonks.org>\n");
692 ulogd_configfile = optarg;
696 user = strdup(optarg);
699 printf("Unknown user %s.\n", user);
709 if (config_register_file(ulogd_configfile)) {
710 ulogd_log(ULOGD_FATAL, "error registering configfile \"%s\"\n",
715 /* parse config file */
716 if (parse_conffile("global", &rmem_ce)) {
717 ulogd_log(ULOGD_FATAL, "parse_conffile\n");
721 /* allocate a receive buffer */
722 libulog_buf = (unsigned char *) malloc(bufsiz_ce.u.value);
725 ulogd_log(ULOGD_FATAL, "unable to allocate receive buffer"
726 "of %d bytes\n", bufsiz_ce.u.value);
731 /* create ipulog handle */
732 libulog_h = ipulog_create_handle(ipulog_group2gmask(nlgroup_ce.u.value),
736 /* if some error occurrs, print it to stderr */
737 ulogd_log(ULOGD_FATAL, "unable to create ipulogd handle\n");
744 ulogd_log(ULOGD_NOTICE, "Changing UID / GID\n");
746 ulogd_log(ULOGD_FATAL, "can't set GID\n");
751 ulogd_log(ULOGD_FATAL, "can't sett effective GID\n");
755 if (initgroups(user, gid)) {
756 ulogd_log(ULOGD_FATAL, "can't set user secondary GID\n");
761 ulogd_log(ULOGD_FATAL, "can't set UID\n");
766 ulogd_log(ULOGD_FATAL, "can't set effective UID\n");
776 if (logfile != stdout)
783 logfile_open(logf_ce.u.string);
785 for (p = ulogd_outputs; p; p = p->next) {
791 /* dump key and interpreter hash */
796 /* send SIGINT to the term handler, since they hit CTRL-C */
797 signal(SIGINT, &sigterm_handler);
798 signal(SIGHUP, &sighup_handler);
799 signal(SIGTERM, &sigterm_handler);
801 ulogd_log(ULOGD_INFO,
802 "initialization finished, entering main loop\n");
804 /* endless loop receiving packets and handling them over to
806 while ((len = ipulog_read(libulog_h, libulog_buf,
807 bufsiz_ce.u.value, 1))) {
810 /* this is not supposed to happen */
811 ulogd_log(ULOGD_ERROR, "ipulog_read == %d! "
812 "ipulog_errno == %d, errno = %d\n",
813 len, ipulog_errno, errno);
815 while ((upkt = ipulog_get_packet(libulog_h,
816 libulog_buf, len))) {
817 DEBUGP("==> packet received\n");
823 /* hackish, but result is the same */
824 sigterm_handler(SIGTERM);