aac37b11ee5d5ba5ea7341964b48a1300d879f74
[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 #define MAXLINE         1024            /* maximum line length */
133
134
135 void ipcsyslog_cleanup(void){
136         printf("Exiting Syslogd!\n");
137         if (shmid != -1)
138                 shmdt(buf);
139
140         if (shmid != -1)
141                 shmctl(shmid, IPC_RMID, NULL);
142         if (s_semid != -1)
143                 semctl(s_semid, 0, IPC_RMID, 0);
144 }
145
146 void ipcsyslog_init(void){
147         if (buf == NULL){
148             if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
149                         perror_msg_and_die("shmget");
150
151
152             if ((buf = shmat(shmid, NULL, 0)) == NULL)
153                         perror_msg_and_die("shmat");
154
155
156             buf->size=data_size;
157             buf->head=buf->tail=0;
158
159             // we'll trust the OS to set initial semval to 0 (let's hope)
160             if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
161                 if (errno == EEXIST){
162                    if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
163                     perror_msg_and_die("semget");
164                 }else
165                         perror_msg_and_die("semget");
166             }
167         }else{
168                 printf("Buffer already allocated just grab the semaphore?");
169         }
170 }
171
172 /* write message to buffer */
173 void circ_message(const char *msg){
174         int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
175
176         sem_down(s_semid);
177
178         /*
179          * Circular Buffer Algorithm:
180          * --------------------------
181          *
182          * Start-off w/ empty buffer of specific size SHM_SIZ
183          * Start filling it up w/ messages. I use '\0' as separator to break up messages.
184          * This is also very handy since we can do printf on message.
185          *
186          * Once the buffer is full we need to get rid of the first message in buffer and
187          * insert the new message. (Note: if the message being added is >1 message then
188          * we will need to "remove" >1 old message from the buffer). The way this is done
189          * is the following:
190          *      When we reach the end of the buffer we set a mark and start from the beginning.
191          *      Now what about the beginning and end of the buffer? Well we have the "head"
192          *      index/pointer which is the starting point for the messages and we have "tail"
193          *      index/pointer which is the ending point for the messages. When we "display" the
194          *      messages we start from the beginning and continue until we reach "tail". If we
195          *      reach end of buffer, then we just start from the beginning (offset 0). "head" and
196          *      "tail" are actually offsets from the beginning of the buffer.
197          *
198          * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
199          *       a threasafe way of handling shared memory operations.
200          */
201         if ( (buf->tail + l) < buf->size ){
202                 /* before we append the message we need to check the HEAD so that we won't
203                    overwrite any of the message that we still need and adjust HEAD to point
204                    to the next message! */
205                 if ( buf->tail < buf->head){
206                         if ( (buf->tail + l) >= buf->head ){
207                           /* we need to move the HEAD to point to the next message
208                            * Theoretically we have enough room to add the whole message to the
209                            * buffer, because of the first outer IF statement, so we don't have
210                            * to worry about overflows here!
211                            */
212                            int k= buf->tail + l - buf->head; /* we need to know how many bytes
213                                                                 we are overwriting to make
214                                                                 enough room */
215                            char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
216                            if (c != NULL) {/* do a sanity check just in case! */
217                                 buf->head = c - buf->data + 1; /* we need to convert pointer to
218                                                                   offset + skip the '\0' since
219                                                                   we need to point to the beginning
220                                                                   of the next message */
221                                 /* Note: HEAD is only used to "retrieve" messages, it's not used
222                                         when writing messages into our buffer */
223                            }else{ /* show an error message to know we messed up? */
224                                 printf("Weird! Can't find the terminator token??? \n");
225                                 buf->head=0;
226                            }
227                         }
228                 } /* in other cases no overflows have been done yet, so we don't care! */
229
230                 /* we should be ok to append the message now */
231                 strncpy(buf->data + buf->tail,msg,l); /* append our message */
232                 buf->tail+=l; /* count full message w/ '\0' terminating char */
233         }else{
234                 /* we need to break up the message and "circle" it around */
235                 char *c;
236                 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
237                 
238                 /* We need to move HEAD! This is always the case since we are going
239                  * to "circle" the message.
240                  */
241                 c=memchr(buf->data + k ,'\0', buf->size - k);
242                 
243                 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
244                         /* move head pointer*/
245                         buf->head=c-buf->data+1; 
246                         
247                         /* now write the first part of the message */                   
248                         strncpy(buf->data + buf->tail, msg, l - k - 1);
249                         
250                         /* ALWAYS terminate end of buffer w/ '\0' */
251                         buf->data[buf->size-1]='\0'; 
252                         
253                         /* now write out the rest of the string to the beginning of the buffer */
254                         strcpy(buf->data, &msg[l-k-1]);
255
256                         /* we need to place the TAIL at the end of the message */
257                         buf->tail = k + 1;
258                 }else{
259                         printf("Weird! Can't find the terminator token from the beginning??? \n");
260                         buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
261                 }
262                 
263         }
264         sem_up(s_semid);
265 }
266 #endif
267 /* Note: There is also a function called "message()" in init.c */
268 /* Print a message to the log file. */
269 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
270 static void message (char *fmt, ...)
271 {
272         int fd;
273         struct flock fl;
274         va_list arguments;
275
276         fl.l_whence = SEEK_SET;
277         fl.l_start  = 0;
278         fl.l_len    = 1;
279
280 #ifdef CONFIG_FEATURE_IPC_SYSLOG
281         if ((circular_logging) && (buf != NULL)){
282                         char b[1024];
283                         va_start (arguments, fmt);
284                         vsprintf (b, fmt, arguments);
285                         va_end (arguments);
286                         circ_message(b);
287
288         }else
289 #endif
290         if ((fd = device_open (logFilePath,
291                                                    O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
292                                                    O_NONBLOCK)) >= 0) {
293                 fl.l_type = F_WRLCK;
294                 fcntl (fd, F_SETLKW, &fl);
295                 va_start (arguments, fmt);
296                 vdprintf (fd, fmt, arguments);
297                 va_end (arguments);
298                 fl.l_type = F_UNLCK;
299                 fcntl (fd, F_SETLKW, &fl);
300                 close (fd);
301         } else {
302                 /* Always send console messages to /dev/console so people will see them. */
303                 if ((fd = device_open (_PATH_CONSOLE,
304                                                            O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
305                         va_start (arguments, fmt);
306                         vdprintf (fd, fmt, arguments);
307                         va_end (arguments);
308                         close (fd);
309                 } else {
310                         fprintf (stderr, "Bummer, can't print: ");
311                         va_start (arguments, fmt);
312                         vfprintf (stderr, fmt, arguments);
313                         fflush (stderr);
314                         va_end (arguments);
315                 }
316         }
317 }
318
319 static void logMessage (int pri, char *msg)
320 {
321         time_t now;
322         char *timestamp;
323         static char res[20] = "";
324         CODE *c_pri, *c_fac;
325
326         if (pri != 0) {
327                 for (c_fac = facilitynames;
328                                 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
329                 for (c_pri = prioritynames;
330                                 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
331                 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
332                         snprintf(res, sizeof(res), "<%d>", pri);
333                 else
334                         snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
335         }
336
337         if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
338                         msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
339                 time(&now);
340                 timestamp = ctime(&now) + 4;
341                 timestamp[15] = '\0';
342         } else {
343                 timestamp = msg;
344                 timestamp[15] = '\0';
345                 msg += 16;
346         }
347
348         /* todo: supress duplicates */
349
350 #ifdef CONFIG_FEATURE_REMOTE_LOG
351         /* send message to remote logger */
352         if ( -1 != remotefd){
353 static const int IOV_COUNT = 2;
354                 struct iovec iov[IOV_COUNT];
355                 struct iovec *v = iov;
356
357                 memset(&res, 0, sizeof(res));
358                 snprintf(res, sizeof(res), "<%d>", pri);
359                 v->iov_base = res ;
360                 v->iov_len = strlen(res);          
361                 v++;
362
363                 v->iov_base = msg;
364                 v->iov_len = strlen(msg);          
365
366 writev_retry:
367                 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
368                         if (errno == EINTR) goto writev_retry;
369                         error_msg_and_die("syslogd: cannot write to remote file handle on"
370                                         "%s:%d",RemoteHost,RemotePort);
371                 }
372         }
373         if (local_logging)
374 #endif
375                 /* now spew out the message to wherever it is supposed to go */
376                 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
377
378
379 }
380
381 static void quit_signal(int sig)
382 {
383         logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
384         unlink(lfile);
385 #ifdef CONFIG_FEATURE_IPC_SYSLOG
386         ipcsyslog_cleanup();
387 #endif
388
389         exit(TRUE);
390 }
391
392 static void domark(int sig)
393 {
394         if (MarkInterval > 0) {
395                 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
396                 alarm(MarkInterval);
397         }
398 }
399
400 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
401  * enabled, we otherwise get a "storage size isn't constant error. */
402 #define BUFSIZE 1023
403 static int serveConnection (char* tmpbuf, int n_read)
404 {
405         char *p = tmpbuf;
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                 int           gotpri = 0;
413
414                 char *q = line;
415
416                 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
417                         if ((c == '<') && !gotpri && isdigit(p[1])) {
418                         /* Parse the magic priority number. */
419                                 gotpri = 1;
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         return n_read;
443 }
444
445
446 #ifdef CONFIG_FEATURE_REMOTE_LOG
447 static void init_RemoteLog (void){
448
449   struct sockaddr_in remoteaddr;
450   struct hostent *hostinfo;
451   int len = sizeof(remoteaddr);
452
453   memset(&remoteaddr, 0, len);
454
455   remotefd = socket(AF_INET, SOCK_DGRAM, 0);
456
457   if (remotefd < 0) {
458     error_msg_and_die("syslogd: cannot create socket");
459   }
460
461   hostinfo = xgethostbyname(RemoteHost);
462
463   remoteaddr.sin_family = AF_INET;
464   remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
465   remoteaddr.sin_port = htons(RemotePort);
466
467   /* 
468      Since we are using UDP sockets, connect just sets the default host and port 
469      for future operations
470   */
471   if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
472     error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
473   }
474
475 }
476 #endif
477
478 static void doSyslogd (void) __attribute__ ((noreturn));
479 static void doSyslogd (void)
480 {
481         struct sockaddr_un sunx;
482         socklen_t addrLength;
483
484
485         int sock_fd;
486         fd_set fds;
487
488         /* Set up signal handlers. */
489         signal (SIGINT,  quit_signal);
490         signal (SIGTERM, quit_signal);
491         signal (SIGQUIT, quit_signal);
492         signal (SIGHUP,  SIG_IGN);
493         signal (SIGCHLD,  SIG_IGN);
494 #ifdef SIGCLD
495         signal (SIGCLD,  SIG_IGN);
496 #endif
497         signal (SIGALRM, domark);
498         alarm (MarkInterval);
499
500         /* Create the syslog file so realpath() can work. */
501         if (realpath (_PATH_LOG, lfile) != NULL)
502                 unlink (lfile);
503
504         memset (&sunx, 0, sizeof (sunx));
505         sunx.sun_family = AF_UNIX;
506         strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
507         if ((sock_fd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
508                 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG);
509
510         addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
511         if (bind (sock_fd, (struct sockaddr *) &sunx, addrLength) < 0)
512                 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
513
514         if (chmod (lfile, 0666) < 0)
515                 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
516
517         FD_ZERO (&fds);
518         FD_SET (sock_fd, &fds);
519
520 #ifdef CONFIG_FEATURE_IPC_SYSLOG
521         if (circular_logging ){
522            ipcsyslog_init();
523         }
524 #endif
525
526         #ifdef CONFIG_FEATURE_REMOTE_LOG
527         if (doRemoteLog){
528           init_RemoteLog();
529         }
530         #endif
531
532         logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
533
534         for (;;) {
535
536                 fd_set readfds;
537                 int    n_ready;
538                 int    fd;
539
540                 memcpy (&readfds, &fds, sizeof (fds));
541
542                 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
543                         if (errno == EINTR) continue; /* alarm may have happened. */
544                         perror_msg_and_die ("select error");
545                 }
546
547                 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
548                         if (FD_ISSET (fd, &readfds)) {
549
550                                 --n_ready;
551
552                                 if (fd == sock_fd) {
553                                         int   i;
554                                         RESERVE_CONFIG_BUFFER(tmpbuf, BUFSIZE + 1);
555
556                                         memset(tmpbuf, '\0', BUFSIZE+1);
557                                         if ( (i = recv(fd, tmpbuf, MAXLINE - 2, 0)) > 0) {
558                                                 if ( serveConnection(tmpbuf, i) <= 0 ) {
559                                                         close (fd);
560                                                         FD_CLR(fd, &fds);
561                                                 }
562                                         } else {
563                                                 perror_msg_and_die ("UNIX socket error");
564                                         }
565                                         RELEASE_CONFIG_BUFFER (tmpbuf);
566                                 } /* fd == sock_fd */
567                         }/* FD_ISSET() */
568                 }/* for */
569         } /* for main loop */
570 }
571
572 extern int syslogd_main(int argc, char **argv)
573 {
574         int opt;
575         int doFork = TRUE;
576
577         char *p;
578
579         /* do normal option parsing */
580         while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
581                 switch (opt) {
582                         case 'm':
583                                 MarkInterval = atoi(optarg) * 60;
584                                 break;
585                         case 'n':
586                                 doFork = FALSE;
587                                 break;
588                         case 'O':
589                                 logFilePath = xstrdup(optarg);
590                                 break;
591 #ifdef CONFIG_FEATURE_REMOTE_LOG
592                         case 'R':
593                                 RemoteHost = xstrdup(optarg);
594                                 if ( (p = strchr(RemoteHost, ':'))){
595                                         RemotePort = atoi(p+1);
596                                         *p = '\0';
597                                 }
598                                 doRemoteLog = TRUE;
599                                 break;
600                         case 'L':
601                                 local_logging = TRUE;
602                                 break;
603 #endif
604 #ifdef CONFIG_FEATURE_IPC_SYSLOG
605                         case 'C':
606                                 circular_logging = TRUE;
607                                 break;
608 #endif
609                         default:
610                                 show_usage();
611                 }
612         }
613
614 #ifdef CONFIG_FEATURE_REMOTE_LOG
615         /* If they have not specified remote logging, then log locally */
616         if (! doRemoteLog)
617                 local_logging = TRUE;
618 #endif
619
620
621         /* Store away localhost's name before the fork */
622         gethostname(LocalHostName, sizeof(LocalHostName));
623         if ((p = strchr(LocalHostName, '.'))) {
624                 *p++ = '\0';
625         }
626
627         umask(0);
628
629         if (doFork) {
630 #if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
631                 if (daemon(0, 1) < 0)
632                         perror_msg_and_die("daemon");
633 #else
634                         error_msg_and_die("daemon not supported");
635 #endif
636         }
637         doSyslogd();
638
639         return EXIT_SUCCESS;
640 }
641
642 /*
643 Local Variables
644 c-file-style: "linux"
645 c-basic-offset: 4
646 tab-width: 4
647 End:
648 */