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