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