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