- add platform.h.
[oweals/busybox.git] / sysklogd / syslogd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini syslogd implementation for busybox
4  *
5  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6  *
7  * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
8  *
9  * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
10  *
11  * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
12  *
13  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <netdb.h>
23 #include <paths.h>
24 #include <signal.h>
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include <time.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <sys/un.h>
33 #include <sys/param.h>
34
35 #include "busybox.h"
36
37 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
38 #define SYSLOG_NAMES
39 #include <sys/syslog.h>
40 #include <sys/uio.h>
41
42 /* Path for the file where all log messages are written */
43 #define __LOG_FILE "/var/log/messages"
44
45 /* Path to the unix socket */
46 static char lfile[MAXPATHLEN];
47
48 static const char *logFilePath = __LOG_FILE;
49
50 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
51 /* max size of message file before being rotated */
52 static int logFileSize = 200 * 1024;
53
54 /* number of rotated message files */
55 static int logFileRotate = 1;
56 #endif
57
58 /* interval between marks in seconds */
59 static int MarkInterval = 20 * 60;
60
61 /* localhost's name */
62 static char LocalHostName[64];
63
64 #ifdef CONFIG_FEATURE_REMOTE_LOG
65 #include <netinet/in.h>
66 /* udp socket for logging to remote host */
67 static int remotefd = -1;
68 static struct sockaddr_in remoteaddr;
69
70 /* where do we log? */
71 static char *RemoteHost;
72
73 /* what port to log to? */
74 static int RemotePort = 514;
75
76 /* To remote log or not to remote log, that is the question. */
77 static int doRemoteLog = FALSE;
78 static int local_logging = FALSE;
79 #endif
80
81 /* Make loging output smaller. */
82 static bool small = false;
83
84
85 #define MAXLINE         1024    /* maximum line length */
86
87
88 /* circular buffer variables/structures */
89 #ifdef CONFIG_FEATURE_IPC_SYSLOG
90
91 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
92 #error Sorry, you must set the syslogd buffer size to at least 4KB.
93 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
94 #endif
95
96 #include <sys/ipc.h>
97 #include <sys/sem.h>
98 #include <sys/shm.h>
99
100 /* our shared key */
101 static const long KEY_ID = 0x414e4547;  /*"GENA" */
102
103 // Semaphore operation structures
104 static struct shbuf_ds {
105         int size;                       // size of data written
106         int head;                       // start of message list
107         int tail;                       // end of message list
108         char data[1];           // data/messages
109 } *buf = NULL;                  // shared memory pointer
110
111 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} };        // set SMwup
112 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} };    // set SMwdn
113
114 static int shmid = -1;  // ipc shared memory id
115 static int s_semid = -1;        // ipc semaphore id
116 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024);   // default shm size
117 static int circular_logging = FALSE;
118
119 /*
120  * sem_up - up()'s a semaphore.
121  */
122 static inline void sem_up(int semid)
123 {
124         if (semop(semid, SMwup, 1) == -1) {
125                 bb_perror_msg_and_die("semop[SMwup]");
126         }
127 }
128
129 /*
130  * sem_down - down()'s a semaphore
131  */
132 static inline void sem_down(int semid)
133 {
134         if (semop(semid, SMwdn, 3) == -1) {
135                 bb_perror_msg_and_die("semop[SMwdn]");
136         }
137 }
138
139
140 static void ipcsyslog_cleanup(void)
141 {
142         printf("Exiting Syslogd!\n");
143         if (shmid != -1) {
144                 shmdt(buf);
145         }
146
147         if (shmid != -1) {
148                 shmctl(shmid, IPC_RMID, NULL);
149         }
150         if (s_semid != -1) {
151                 semctl(s_semid, 0, IPC_RMID, 0);
152         }
153 }
154
155 static void ipcsyslog_init(void)
156 {
157         if (buf == NULL) {
158                 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
159                         bb_perror_msg_and_die("shmget");
160                 }
161
162                 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
163                         bb_perror_msg_and_die("shmat");
164                 }
165
166                 buf->size = shm_size - sizeof(*buf);
167                 buf->head = buf->tail = 0;
168
169                 // we'll trust the OS to set initial semval to 0 (let's hope)
170                 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
171                         if (errno == EEXIST) {
172                                 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
173                                         bb_perror_msg_and_die("semget");
174                                 }
175                         } else {
176                                 bb_perror_msg_and_die("semget");
177                         }
178                 }
179         } else {
180                 printf("Buffer already allocated just grab the semaphore?");
181         }
182 }
183
184 /* write message to buffer */
185 static void circ_message(const char *msg)
186 {
187         int l = strlen(msg) + 1;        /* count the whole message w/ '\0' included */
188
189         sem_down(s_semid);
190
191         /*
192          * Circular Buffer Algorithm:
193          * --------------------------
194          *
195          * Start-off w/ empty buffer of specific size SHM_SIZ
196          * Start filling it up w/ messages. I use '\0' as separator to break up messages.
197          * This is also very handy since we can do printf on message.
198          *
199          * Once the buffer is full we need to get rid of the first message in buffer and
200          * insert the new message. (Note: if the message being added is >1 message then
201          * we will need to "remove" >1 old message from the buffer). The way this is done
202          * is the following:
203          *      When we reach the end of the buffer we set a mark and start from the beginning.
204          *      Now what about the beginning and end of the buffer? Well we have the "head"
205          *      index/pointer which is the starting point for the messages and we have "tail"
206          *      index/pointer which is the ending point for the messages. When we "display" the
207          *      messages we start from the beginning and continue until we reach "tail". If we
208          *      reach end of buffer, then we just start from the beginning (offset 0). "head" and
209          *      "tail" are actually offsets from the beginning of the buffer.
210          *
211          * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
212          *       a threasafe way of handling shared memory operations.
213          */
214         if ((buf->tail + l) < buf->size) {
215                 /* before we append the message we need to check the HEAD so that we won't
216                    overwrite any of the message that we still need and adjust HEAD to point
217                    to the next message! */
218                 if (buf->tail < buf->head) {
219                         if ((buf->tail + l) >= buf->head) {
220                                 /* we need to move the HEAD to point to the next message
221                                  * Theoretically we have enough room to add the whole message to the
222                                  * buffer, because of the first outer IF statement, so we don't have
223                                  * to worry about overflows here!
224                                  */
225                                 int k = buf->tail + l - buf->head;      /* we need to know how many bytes
226                                                                                                            we are overwriting to make
227                                                                                                            enough room */
228                                 char *c =
229                                         memchr(buf->data + buf->head + k, '\0',
230                                                    buf->size - (buf->head + k));
231                                 if (c != NULL) {        /* do a sanity check just in case! */
232                                         buf->head = c - buf->data + 1;  /* we need to convert pointer to
233                                                                                                            offset + skip the '\0' since
234                                                                                                            we need to point to the beginning
235                                                                                                            of the next message */
236                                         /* Note: HEAD is only used to "retrieve" messages, it's not used
237                                            when writing messages into our buffer */
238                                 } else {        /* show an error message to know we messed up? */
239                                         printf("Weird! Can't find the terminator token??? \n");
240                                         buf->head = 0;
241                                 }
242                         }
243                 }
244
245                 /* in other cases no overflows have been done yet, so we don't care! */
246                 /* we should be ok to append the message now */
247                 strncpy(buf->data + buf->tail, msg, l); /* append our message */
248                 buf->tail += l; /* count full message w/ '\0' terminating char */
249         } else {
250                 /* we need to break up the message and "circle" it around */
251                 char *c;
252                 int k = buf->tail + l - buf->size;      /* count # of bytes we don't fit */
253
254                 /* We need to move HEAD! This is always the case since we are going
255                  * to "circle" the message.
256                  */
257                 c = memchr(buf->data + k, '\0', buf->size - k);
258
259                 if (c != NULL) {        /* if we don't have '\0'??? weird!!! */
260                         /* move head pointer */
261                         buf->head = c - buf->data + 1;
262
263                         /* now write the first part of the message */
264                         strncpy(buf->data + buf->tail, msg, l - k - 1);
265
266                         /* ALWAYS terminate end of buffer w/ '\0' */
267                         buf->data[buf->size - 1] = '\0';
268
269                         /* now write out the rest of the string to the beginning of the buffer */
270                         strcpy(buf->data, &msg[l - k - 1]);
271
272                         /* we need to place the TAIL at the end of the message */
273                         buf->tail = k + 1;
274                 } else {
275                         printf
276                                 ("Weird! Can't find the terminator token from the beginning??? \n");
277                         buf->head = buf->tail = 0;      /* reset buffer, since it's probably corrupted */
278                 }
279
280         }
281         sem_up(s_semid);
282 }
283 #endif                                                  /* CONFIG_FEATURE_IPC_SYSLOG */
284
285 /* Note: There is also a function called "message()" in init.c */
286 /* Print a message to the log file. */
287 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
288 static void message(char *fmt, ...)
289 {
290         int fd;
291         struct flock fl;
292         va_list arguments;
293
294         fl.l_whence = SEEK_SET;
295         fl.l_start = 0;
296         fl.l_len = 1;
297
298 #ifdef CONFIG_FEATURE_IPC_SYSLOG
299         if ((circular_logging == TRUE) && (buf != NULL)) {
300                 char b[1024];
301
302                 va_start(arguments, fmt);
303                 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
304                 va_end(arguments);
305                 circ_message(b);
306
307         } else
308 #endif
309         if ((fd = device_open(logFilePath,
310                                         O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
311                                                          O_NONBLOCK)) >= 0) {
312                 fl.l_type = F_WRLCK;
313                 fcntl(fd, F_SETLKW, &fl);
314 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
315                 if ( logFileSize > 0 ) {
316                         struct stat statf;
317                         int r = fstat(fd, &statf);
318                         if( !r && (statf.st_mode & S_IFREG)
319                                 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
320                                 if(logFileRotate > 0) {
321                                         int i;
322                                         char oldFile[(strlen(logFilePath)+4)], newFile[(strlen(logFilePath)+4)];
323                                         for(i=logFileRotate-1;i>0;i--) {
324                                                 sprintf(oldFile, "%s.%d", logFilePath, i-1);
325                                                 sprintf(newFile, "%s.%d", logFilePath, i);
326                                                 rename(oldFile, newFile);
327                                         }
328                                         sprintf(newFile, "%s.%d", logFilePath, 0);
329                                         fl.l_type = F_UNLCK;
330                                         fcntl (fd, F_SETLKW, &fl);
331                                         close(fd);
332                                         rename(logFilePath, newFile);
333                                         fd = device_open (logFilePath,
334                                                    O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
335                                                    O_NONBLOCK);
336                                         fl.l_type = F_WRLCK;
337                                         fcntl (fd, F_SETLKW, &fl);
338                                 } else {
339                                         ftruncate( fd, 0 );
340                                 }
341                         }
342                 }
343 #endif
344                 va_start(arguments, fmt);
345                 vdprintf(fd, fmt, arguments);
346                 va_end(arguments);
347                 fl.l_type = F_UNLCK;
348                 fcntl(fd, F_SETLKW, &fl);
349                 close(fd);
350         } else {
351                 /* Always send console messages to /dev/console so people will see them. */
352                 if ((fd = device_open(_PATH_CONSOLE,
353                                                  O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
354                         va_start(arguments, fmt);
355                         vdprintf(fd, fmt, arguments);
356                         va_end(arguments);
357                         close(fd);
358                 } else {
359                         fprintf(stderr, "Bummer, can't print: ");
360                         va_start(arguments, fmt);
361                         vfprintf(stderr, fmt, arguments);
362                         fflush(stderr);
363                         va_end(arguments);
364                 }
365         }
366 }
367
368 #ifdef CONFIG_FEATURE_REMOTE_LOG
369 static void init_RemoteLog(void)
370 {
371         memset(&remoteaddr, 0, sizeof(remoteaddr));
372         remotefd = socket(AF_INET, SOCK_DGRAM, 0);
373
374         if (remotefd < 0) {
375                 bb_error_msg("cannot create socket");
376         }
377
378         remoteaddr.sin_family = AF_INET;
379         remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
380         remoteaddr.sin_port = htons(RemotePort);
381 }
382 #endif
383
384 static void logMessage(int pri, char *msg)
385 {
386         time_t now;
387         char *timestamp;
388         static char res[20];
389 #ifdef CONFIG_FEATURE_REMOTE_LOG        
390         static char line[MAXLINE + 1];
391 #endif
392         CODE *c_pri, *c_fac;
393
394         if (pri != 0) {
395                 for (c_fac = facilitynames;
396                          c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
397                 for (c_pri = prioritynames;
398                          c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
399                 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
400                         snprintf(res, sizeof(res), "<%d>", pri);
401                 } else {
402                         snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
403                 }
404         }
405
406         if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
407                 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
408                 time(&now);
409                 timestamp = ctime(&now) + 4;
410                 timestamp[15] = '\0';
411         } else {
412                 timestamp = msg;
413                 timestamp[15] = '\0';
414                 msg += 16;
415         }
416
417         /* todo: supress duplicates */
418
419 #ifdef CONFIG_FEATURE_REMOTE_LOG
420         if (doRemoteLog == TRUE) {
421                 /* trying connect the socket */
422                 if (-1 == remotefd) {
423                         init_RemoteLog();
424                 }
425
426                 /* if we have a valid socket, send the message */
427                 if (-1 != remotefd) {
428                         now = 1;
429                         snprintf(line, sizeof(line), "<%d>%s", pri, msg);
430
431                 retry:
432                         /* send message to remote logger */
433                         if(( -1 == sendto(remotefd, line, strlen(line), 0,
434                                                         (struct sockaddr *) &remoteaddr,
435                                                         sizeof(remoteaddr))) && (errno == EINTR)) {
436                                 /* sleep now seconds and retry (with now * 2) */
437                                 sleep(now);
438                                 now *= 2;
439                                 goto retry;
440                         }
441                 }
442         }
443
444         if (local_logging == TRUE)
445 #endif
446         {
447                 /* now spew out the message to wherever it is supposed to go */
448                 if (small)
449                         message("%s %s\n", timestamp, msg);
450                 else
451                         message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
452         }
453 }
454
455 static void quit_signal(int sig)
456 {
457         logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
458         unlink(lfile);
459 #ifdef CONFIG_FEATURE_IPC_SYSLOG
460         ipcsyslog_cleanup();
461 #endif
462
463         exit(TRUE);
464 }
465
466 static void domark(int sig)
467 {
468         if (MarkInterval > 0) {
469                 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
470                 alarm(MarkInterval);
471         }
472 }
473
474 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
475  * enabled, we otherwise get a "storage size isn't constant error. */
476 static int serveConnection(char *tmpbuf, int n_read)
477 {
478         char *p = tmpbuf;
479
480         while (p < tmpbuf + n_read) {
481
482                 int pri = (LOG_USER | LOG_NOTICE);
483                 int num_lt = 0;
484                 char line[MAXLINE + 1];
485                 unsigned char c;
486                 char *q = line;
487
488                 while ((c = *p) && q < &line[sizeof(line) - 1]) {
489                         if (c == '<' && num_lt == 0) {
490                                 /* Parse the magic priority number. */
491                                 num_lt++;
492                                 pri = 0;
493                                 while (isdigit(*(++p))) {
494                                         pri = 10 * pri + (*p - '0');
495                                 }
496                                 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
497                                         pri = (LOG_USER | LOG_NOTICE);
498                                 }
499                         } else if (c == '\n') {
500                                 *q++ = ' ';
501                         } else if (iscntrl(c) && (c < 0177)) {
502                                 *q++ = '^';
503                                 *q++ = c ^ 0100;
504                         } else {
505                                 *q++ = c;
506                         }
507                         p++;
508                 }
509                 *q = '\0';
510                 p++;
511                 /* Now log it */
512                 logMessage(pri, line);
513         }
514         return n_read;
515 }
516
517 static void doSyslogd(void) ATTRIBUTE_NORETURN;
518 static void doSyslogd(void)
519 {
520         struct sockaddr_un sunx;
521         socklen_t addrLength;
522
523         int sock_fd;
524         fd_set fds;
525
526         /* Set up signal handlers. */
527         signal(SIGINT, quit_signal);
528         signal(SIGTERM, quit_signal);
529         signal(SIGQUIT, quit_signal);
530         signal(SIGHUP, SIG_IGN);
531         signal(SIGCHLD, SIG_IGN);
532 #ifdef SIGCLD
533         signal(SIGCLD, SIG_IGN);
534 #endif
535         signal(SIGALRM, domark);
536         alarm(MarkInterval);
537
538         /* Create the syslog file so realpath() can work. */
539         if (realpath(_PATH_LOG, lfile) != NULL) {
540                 unlink(lfile);
541         }
542
543         memset(&sunx, 0, sizeof(sunx));
544         sunx.sun_family = AF_UNIX;
545         strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
546         if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
547                 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
548                                                    _PATH_LOG);
549         }
550
551         addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
552         if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
553                 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
554         }
555
556         if (chmod(lfile, 0666) < 0) {
557                 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
558         }
559 #ifdef CONFIG_FEATURE_IPC_SYSLOG
560         if (circular_logging == TRUE) {
561                 ipcsyslog_init();
562         }
563 #endif
564
565 #ifdef CONFIG_FEATURE_REMOTE_LOG
566         if (doRemoteLog == TRUE) {
567                 init_RemoteLog();
568         }
569 #endif
570
571         logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
572
573         for (;;) {
574
575                 FD_ZERO(&fds);
576                 FD_SET(sock_fd, &fds);
577
578                 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
579                         if (errno == EINTR) {
580                                 /* alarm may have happened. */
581                                 continue;
582                         }
583                         bb_perror_msg_and_die("select error");
584                 }
585
586                 if (FD_ISSET(sock_fd, &fds)) {
587                         int i;
588 #if MAXLINE > BUFSIZ
589 # define TMP_BUF_SZ BUFSIZ
590 #else
591 # define TMP_BUF_SZ MAXLINE
592 #endif
593 #define tmpbuf bb_common_bufsiz1
594
595                         if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
596                                 tmpbuf[i] = '\0';
597                                 serveConnection(tmpbuf, i);
598                         } else {
599                                 bb_perror_msg_and_die("UNIX socket error");
600                         }
601                 }                               /* FD_ISSET() */
602         }                                       /* for main loop */
603 }
604
605 extern int syslogd_main(int argc, char **argv)
606 {
607         int opt;
608
609         int doFork = TRUE;
610
611         char *p;
612
613         /* do normal option parsing */
614         while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
615                 switch (opt) {
616                 case 'm':
617                         MarkInterval = atoi(optarg) * 60;
618                         break;
619                 case 'n':
620                         doFork = FALSE;
621                         break;
622                 case 'O':
623                         logFilePath = optarg;
624                         break;
625 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
626                 case 's':
627                         logFileSize = atoi(optarg) * 1024;
628                         break;
629                 case 'b':
630                         logFileRotate = atoi(optarg);
631                         if( logFileRotate > 99 ) logFileRotate = 99;
632                         break;
633 #endif
634 #ifdef CONFIG_FEATURE_REMOTE_LOG
635                 case 'R':
636                         RemoteHost = bb_xstrdup(optarg);
637                         if ((p = strchr(RemoteHost, ':'))) {
638                                 RemotePort = atoi(p + 1);
639                                 *p = '\0';
640                         }
641                         doRemoteLog = TRUE;
642                         break;
643                 case 'L':
644                         local_logging = TRUE;
645                         break;
646 #endif
647 #ifdef CONFIG_FEATURE_IPC_SYSLOG
648                 case 'C':
649                         if (optarg) {
650                                 int buf_size = atoi(optarg);
651                                 if (buf_size >= 4) {
652                                         shm_size = buf_size * 1024;
653                                 }
654                         }
655                         circular_logging = TRUE;
656                         break;
657 #endif
658                 case 'S':
659                         small = true;
660                         break;
661                 default:
662                         bb_show_usage();
663                 }
664         }
665
666 #ifdef CONFIG_FEATURE_REMOTE_LOG
667         /* If they have not specified remote logging, then log locally */
668         if (doRemoteLog == FALSE)
669                 local_logging = TRUE;
670 #endif
671
672
673         /* Store away localhost's name before the fork */
674         gethostname(LocalHostName, sizeof(LocalHostName));
675         if ((p = strchr(LocalHostName, '.'))) {
676                 *p = '\0';
677         }
678
679         umask(0);
680
681         if (doFork == TRUE) {
682 #if defined(__uClinux__)
683                 vfork_daemon_rexec(0, 1, argc, argv, "-n");
684 #else /* __uClinux__ */
685                 if(daemon(0, 1) < 0)
686                         bb_perror_msg_and_die("daemon");
687 #endif /* __uClinux__ */
688         }
689         doSyslogd();
690
691         return EXIT_SUCCESS;
692 }
693
694 /*
695 Local Variables
696 c-file-style: "linux"
697 c-basic-offset: 4
698 tab-width: 4
699 End:
700 */