source: branches/stable/mindi-busybox/sysklogd/syslogd.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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