source: MondoRescue/trunk/mindi-busybox/sysklogd/syslogd.c@ 904

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

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

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