* archival/bunzip2.c (bunzip2_main): Do not remove files if writing to standard
[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) && (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 writev_retry:
365                 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
366                         if (errno == EINTR) goto writev_retry;
367                         error_msg_and_die("syslogd: cannot write to remote file handle on"
368                                         "%s:%d",RemoteHost,RemotePort);
369                 }
370         }
371         if (local_logging)
372 #endif
373                 /* now spew out the message to wherever it is supposed to go */
374                 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
375
376
377 }
378
379 static void quit_signal(int sig)
380 {
381         logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
382         unlink(lfile);
383 #ifdef CONFIG_FEATURE_IPC_SYSLOG
384         ipcsyslog_cleanup();
385 #endif
386
387         exit(TRUE);
388 }
389
390 static void domark(int sig)
391 {
392         if (MarkInterval > 0) {
393                 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
394                 alarm(MarkInterval);
395         }
396 }
397
398 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
399  * enabled, we otherwise get a "storage size isn't constant error. */
400 #define BUFSIZE 1023
401 static int serveConnection (int conn)
402 {
403         RESERVE_CONFIG_BUFFER(tmpbuf, BUFSIZE + 1);
404         int    n_read;
405         char *p = tmpbuf;
406
407         n_read = read (conn, tmpbuf, BUFSIZE );
408
409         while (p < tmpbuf + n_read) {
410
411                 int           pri = (LOG_USER | LOG_NOTICE);
412                 char          line[ BUFSIZE + 1 ];
413                 unsigned char c;
414
415                 char *q = line;
416
417                 tmpbuf[ n_read - 1 ] = '\0';
418
419                 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
420                         if (c == '<') {
421                         /* Parse the magic priority number. */
422                                 pri = 0;
423                                 while (isdigit (*(++p))) {
424                                         pri = 10 * pri + (*p - '0');
425                                 }
426                                 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
427                                         pri = (LOG_USER | LOG_NOTICE);
428                                 }
429                         } else if (c == '\n') {
430                                 *q++ = ' ';
431                         } else if (iscntrl (c) && (c < 0177)) {
432                                 *q++ = '^';
433                                 *q++ = c ^ 0100;
434                         } else {
435                                 *q++ = c;
436                         }
437                         p++;
438                 }
439                 *q = '\0';
440                 p++;
441                 /* Now log it */
442                 logMessage (pri, line);
443         }
444         RELEASE_CONFIG_BUFFER (tmpbuf);
445         return n_read;
446 }
447
448
449 #ifdef CONFIG_FEATURE_REMOTE_LOG
450 static void init_RemoteLog (void){
451
452   struct sockaddr_in remoteaddr;
453   struct hostent *hostinfo;
454   int len = sizeof(remoteaddr);
455
456   memset(&remoteaddr, 0, len);
457
458   remotefd = socket(AF_INET, SOCK_DGRAM, 0);
459
460   if (remotefd < 0) {
461     error_msg_and_die("syslogd: cannot create socket");
462   }
463
464   hostinfo = xgethostbyname(RemoteHost);
465
466   remoteaddr.sin_family = AF_INET;
467   remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
468   remoteaddr.sin_port = htons(RemotePort);
469
470   /* 
471      Since we are using UDP sockets, connect just sets the default host and port 
472      for future operations
473   */
474   if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
475     error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
476   }
477
478 }
479 #endif
480
481 static void doSyslogd (void) __attribute__ ((noreturn));
482 static void doSyslogd (void)
483 {
484         struct sockaddr_un sunx;
485         socklen_t addrLength;
486
487
488         int sock_fd;
489         fd_set fds;
490
491         /* Set up signal handlers. */
492         signal (SIGINT,  quit_signal);
493         signal (SIGTERM, quit_signal);
494         signal (SIGQUIT, quit_signal);
495         signal (SIGHUP,  SIG_IGN);
496         signal (SIGCHLD,  SIG_IGN);
497 #ifdef SIGCLD
498         signal (SIGCLD,  SIG_IGN);
499 #endif
500         signal (SIGALRM, domark);
501         alarm (MarkInterval);
502
503         /* Create the syslog file so realpath() can work. */
504         if (realpath (_PATH_LOG, lfile) != NULL)
505                 unlink (lfile);
506
507         memset (&sunx, 0, sizeof (sunx));
508         sunx.sun_family = AF_UNIX;
509         strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
510         if ((sock_fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
511                 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG);
512
513         addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
514         if ((bind (sock_fd, (struct sockaddr *) &sunx, addrLength)) || (listen (sock_fd, 5)))
515                 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
516
517         if (chmod (lfile, 0666) < 0)
518                 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
519
520         FD_ZERO (&fds);
521         FD_SET (sock_fd, &fds);
522
523 #ifdef CONFIG_FEATURE_IPC_SYSLOG
524         if (circular_logging ){
525            ipcsyslog_init();
526         }
527 #endif
528
529         #ifdef CONFIG_FEATURE_REMOTE_LOG
530         if (doRemoteLog){
531           init_RemoteLog();
532         }
533         #endif
534
535         logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
536
537         for (;;) {
538
539                 fd_set readfds;
540                 int    n_ready;
541                 int    fd;
542
543                 memcpy (&readfds, &fds, sizeof (fds));
544
545                 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
546                         if (errno == EINTR) continue; /* alarm may have happened. */
547                         perror_msg_and_die ("select error");
548                 }
549
550                 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
551                         if (FD_ISSET (fd, &readfds)) {
552
553                                 --n_ready;
554
555                                 if (fd == sock_fd) {
556                                         int   conn;
557
558                                         //printf("New Connection request.\n");
559                                         if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) {
560                                                 perror_msg_and_die ("accept error");
561                                         }
562
563                                         FD_SET(conn, &fds);
564                                         //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE);
565                                 } else {
566                                         //printf("Serving connection: %i\n",fd);
567                                           if ( serveConnection(fd) <= 0 ) {
568                                             close (fd);
569                                             FD_CLR(fd, &fds);
570             }
571                                 } /* fd == sock_fd */
572                         }/* FD_ISSET() */
573                 }/* for */
574         } /* for main loop */
575 }
576
577 extern int syslogd_main(int argc, char **argv)
578 {
579         int opt;
580         int doFork = TRUE;
581
582         char *p;
583
584         /* do normal option parsing */
585         while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
586                 switch (opt) {
587                         case 'm':
588                                 MarkInterval = atoi(optarg) * 60;
589                                 break;
590                         case 'n':
591                                 doFork = FALSE;
592                                 break;
593                         case 'O':
594                                 logFilePath = xstrdup(optarg);
595                                 break;
596 #ifdef CONFIG_FEATURE_REMOTE_LOG
597                         case 'R':
598                                 RemoteHost = xstrdup(optarg);
599                                 if ( (p = strchr(RemoteHost, ':'))){
600                                         RemotePort = atoi(p+1);
601                                         *p = '\0';
602                                 }
603                                 doRemoteLog = TRUE;
604                                 break;
605                         case 'L':
606                                 local_logging = TRUE;
607                                 break;
608 #endif
609 #ifdef CONFIG_FEATURE_IPC_SYSLOG
610                         case 'C':
611                                 circular_logging = TRUE;
612                                 break;
613 #endif
614                         default:
615                                 show_usage();
616                 }
617         }
618
619 #ifdef CONFIG_FEATURE_REMOTE_LOG
620         /* If they have not specified remote logging, then log locally */
621         if (! doRemoteLog)
622                 local_logging = TRUE;
623 #endif
624
625
626         /* Store away localhost's name before the fork */
627         gethostname(LocalHostName, sizeof(LocalHostName));
628         if ((p = strchr(LocalHostName, '.'))) {
629                 *p++ = '\0';
630         }
631
632         umask(0);
633
634         if (doFork) {
635 #if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
636                 if (daemon(0, 1) < 0)
637                         perror_msg_and_die("daemon");
638 #else
639                         error_msg_and_die("daemon not supported");
640 #endif
641         }
642         doSyslogd();
643
644         return EXIT_SUCCESS;
645 }
646
647 /*
648 Local Variables
649 c-file-style: "linux"
650 c-basic-offset: 4
651 tab-width: 4
652 End:
653 */