1 /* vi: set sw=4 ts=4: */
3 * Mini syslogd implementation for busybox
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
9 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
11 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
13 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
21 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
23 #include <sys/syslog.h>
26 /* Path to the unix socket */
27 static char lfile[MAXPATHLEN];
29 /* Path for the file where all log messages are written */
30 static const char *logFilePath = "/var/log/messages";
32 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
33 /* max size of message file before being rotated */
34 static int logFileSize = 200 * 1024;
36 /* number of rotated message files */
37 static int logFileRotate = 1;
40 /* interval between marks in seconds */
41 static int MarkInterval = 20 * 60;
43 /* level of messages to be locally logged */
44 static int logLevel = 8;
46 /* localhost's name */
47 static char LocalHostName[64];
49 #ifdef CONFIG_FEATURE_REMOTE_LOG
50 #include <netinet/in.h>
51 /* udp socket for logging to remote host */
52 static int remotefd = -1;
53 static struct sockaddr_in remoteaddr;
58 /* Correct regardless of combination of CONFIG_xxx */
60 OPTBIT_mark = 0, // -m
63 OPTBIT_loglevel, // -l
65 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
66 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
67 USE_FEATURE_REMOTE_LOG( OPTBIT_remote ,) // -R
68 USE_FEATURE_REMOTE_LOG( OPTBIT_localtoo ,) // -L
69 USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
71 OPT_mark = 1 << OPTBIT_mark ,
72 OPT_nofork = 1 << OPTBIT_nofork ,
73 OPT_outfile = 1 << OPTBIT_outfile ,
74 OPT_loglevel = 1 << OPTBIT_loglevel,
75 OPT_small = 1 << OPTBIT_small ,
76 OPT_filesize = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
77 OPT_rotatecnt = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
78 OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remote )) + 0,
79 OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_localtoo )) + 0,
80 OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
82 #define OPTION_STR "m:nO:l:S" \
83 USE_FEATURE_ROTATE_LOGFILE("s:" ) \
84 USE_FEATURE_ROTATE_LOGFILE("b:" ) \
85 USE_FEATURE_REMOTE_LOG( "R:" ) \
86 USE_FEATURE_REMOTE_LOG( "L" ) \
87 USE_FEATURE_IPC_SYSLOG( "C::")
88 #define OPTION_DECL *opt_m, *opt_l \
89 USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
90 USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
91 USE_FEATURE_REMOTE_LOG( ,*opt_R) \
92 USE_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
93 #define OPTION_PARAM &opt_m, &logFilePath, &opt_l \
94 USE_FEATURE_ROTATE_LOGFILE(,&opt_s) \
95 USE_FEATURE_ROTATE_LOGFILE(,&opt_b) \
96 USE_FEATURE_REMOTE_LOG( ,&opt_R) \
97 USE_FEATURE_IPC_SYSLOG( ,&opt_C)
99 #define MAXLINE 1024 /* maximum line length */
101 /* circular buffer variables/structures */
102 #ifdef CONFIG_FEATURE_IPC_SYSLOG
104 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
105 #error Sorry, you must set the syslogd buffer size to at least 4KB.
106 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
114 #define KEY_ID ((long)0x414e4547) /* "GENA" */
116 // Semaphore operation structures
117 static struct shbuf_ds {
118 int size; // size of data written
119 int head; // start of message list
120 int tail; // end of message list
121 char data[1]; // data/messages
122 } *shbuf = NULL; // shared memory pointer
124 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
125 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
127 static int shmid = -1; // ipc shared memory id
128 static int s_semid = -1; // ipc semaphore id
129 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
131 static void ipcsyslog_cleanup(void)
133 puts("Exiting syslogd!");
139 shmctl(shmid, IPC_RMID, NULL);
142 semctl(s_semid, 0, IPC_RMID, 0);
146 static void ipcsyslog_init(void)
149 shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023);
151 bb_perror_msg_and_die("shmget");
154 shbuf = shmat(shmid, NULL, 0);
156 bb_perror_msg_and_die("shmat");
159 shbuf->size = shm_size - sizeof(*shbuf);
160 shbuf->head = shbuf->tail = 0;
162 // we'll trust the OS to set initial semval to 0 (let's hope)
163 s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
165 if (errno == EEXIST) {
166 s_semid = semget(KEY_ID, 2, 0);
168 bb_perror_msg_and_die("semget");
171 bb_perror_msg_and_die("semget");
175 printf("Buffer already allocated just grab the semaphore?");
179 /* write message to buffer */
180 static void circ_message(const char *msg)
182 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
183 const char * const fail_msg = "Can't find the terminator token%s?\n";
185 if (semop(s_semid, SMwdn, 3) == -1) {
186 bb_perror_msg_and_die("SMwdn");
190 * Circular Buffer Algorithm:
191 * --------------------------
193 * Start-off w/ empty buffer of specific size SHM_SIZ
194 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
195 * This is also very handy since we can do printf on message.
197 * Once the buffer is full we need to get rid of the first message in buffer and
198 * insert the new message. (Note: if the message being added is >1 message then
199 * we will need to "remove" >1 old message from the buffer). The way this is done
201 * When we reach the end of the buffer we set a mark and start from the beginning.
202 * Now what about the beginning and end of the buffer? Well we have the "head"
203 * index/pointer which is the starting point for the messages and we have "tail"
204 * index/pointer which is the ending point for the messages. When we "display" the
205 * messages we start from the beginning and continue until we reach "tail". If we
206 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
207 * "tail" are actually offsets from the beginning of the buffer.
209 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
210 * a threadsafe way of handling shared memory operations.
212 if ((shbuf->tail + l) < shbuf->size) {
213 /* before we append the message we need to check the HEAD so that we won't
214 overwrite any of the message that we still need and adjust HEAD to point
215 to the next message! */
216 if (shbuf->tail < shbuf->head) {
217 if ((shbuf->tail + l) >= shbuf->head) {
218 /* we need to move the HEAD to point to the next message
219 * Theoretically we have enough room to add the whole message to the
220 * buffer, because of the first outer IF statement, so we don't have
221 * to worry about overflows here!
223 /* we need to know how many bytes we are overwriting to make enough room */
224 int k = shbuf->tail + l - shbuf->head;
226 memchr(shbuf->data + shbuf->head + k, '\0',
227 shbuf->size - (shbuf->head + k));
228 if (c != NULL) { /* do a sanity check just in case! */
229 /* we need to convert pointer to offset + skip the '\0'
230 since we need to point to the beginning of the next message */
231 shbuf->head = c - shbuf->data + 1;
232 /* Note: HEAD is only used to "retrieve" messages, it's not used
233 when writing messages into our buffer */
234 } else { /* show an error message to know we messed up? */
241 /* in other cases no overflows have been done yet, so we don't care! */
242 /* we should be ok to append the message now */
243 strncpy(shbuf->data + shbuf->tail, msg, l); /* append our message */
244 shbuf->tail += l; /* count full message w/ '\0' terminating char */
246 /* we need to break up the message and "circle" it around */
248 int k = shbuf->tail + l - shbuf->size; /* count # of bytes we don't fit */
250 /* We need to move HEAD! This is always the case since we are going
251 * to "circle" the message. */
252 c = memchr(shbuf->data + k, '\0', shbuf->size - k);
254 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
255 /* move head pointer */
256 shbuf->head = c - shbuf->data + 1;
258 /* now write the first part of the message */
259 strncpy(shbuf->data + shbuf->tail, msg, l - k - 1);
261 /* ALWAYS terminate end of buffer w/ '\0' */
262 shbuf->data[shbuf->size - 1] = '\0';
264 /* now write out the rest of the string to the beginning of the buffer */
265 strcpy(shbuf->data, &msg[l - k - 1]);
267 /* we need to place the TAIL at the end of the message */
270 printf(fail_msg, " from the beginning");
271 shbuf->head = shbuf->tail = 0; /* reset buffer, since it's probably corrupted */
275 if (semop(s_semid, SMwup, 1) == -1) {
276 bb_perror_msg_and_die("SMwup");
281 void ipcsyslog_cleanup(void);
282 void ipcsyslog_init(void);
283 void circ_message(const char *msg);
284 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
286 /* Note: There is also a function called "message()" in init.c */
287 /* Print a message to the log file. */
288 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
289 static void message(char *fmt, ...)
295 fl.l_whence = SEEK_SET;
299 #ifdef CONFIG_FEATURE_IPC_SYSLOG
300 if ((option_mask32 & OPT_circularlog) && shbuf) {
303 va_start(arguments, fmt);
304 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
310 fd = device_open(logFilePath, O_WRONLY | O_CREAT
311 | O_NOCTTY | O_APPEND | O_NONBLOCK);
314 fcntl(fd, F_SETLKW, &fl);
316 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
317 if (ENABLE_FEATURE_ROTATE_LOGFILE && logFileSize > 0 ) {
319 int r = fstat(fd, &statf);
320 if (!r && (statf.st_mode & S_IFREG)
321 && (lseek(fd,0,SEEK_END) > logFileSize)) {
322 if (logFileRotate > 0) {
323 int i = strlen(logFilePath) + 4;
326 for (i=logFileRotate-1; i>0; i--) {
327 sprintf(oldFile, "%s.%d", logFilePath, i-1);
328 sprintf(newFile, "%s.%d", logFilePath, i);
329 rename(oldFile, newFile);
331 sprintf(newFile, "%s.%d", logFilePath, 0);
333 fcntl(fd, F_SETLKW, &fl);
335 rename(logFilePath, newFile);
336 fd = device_open(logFilePath,
337 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
340 fcntl(fd, F_SETLKW, &fl);
347 va_start(arguments, fmt);
348 vdprintf(fd, fmt, arguments);
351 fcntl(fd, F_SETLKW, &fl);
354 /* Always send console messages to /dev/console so people will see them. */
355 fd = device_open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
357 va_start(arguments, fmt);
358 vdprintf(fd, fmt, arguments);
362 fprintf(stderr, "Bummer, can't print: ");
363 va_start(arguments, fmt);
364 vfprintf(stderr, fmt, arguments);
371 static void logMessage(int pri, char *msg)
379 c_fac = facilitynames;
380 while (c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3))
382 c_pri = prioritynames;
383 while (c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)))
385 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
386 snprintf(res, sizeof(res), "<%d>", pri);
388 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
392 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
393 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
395 timestamp = ctime(&now) + 4;
396 timestamp[15] = '\0';
399 timestamp[15] = '\0';
403 /* todo: supress duplicates */
405 #ifdef CONFIG_FEATURE_REMOTE_LOG
406 if (option_mask32 & OPT_remotelog) {
407 char line[MAXLINE + 1];
408 /* trying connect the socket */
409 if (-1 == remotefd) {
410 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
412 /* if we have a valid socket, send the message */
413 if (-1 != remotefd) {
414 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
415 /* send message to remote logger, ignore possible error */
416 sendto(remotefd, line, strlen(line), 0,
417 (struct sockaddr *) &remoteaddr, sizeof(remoteaddr));
421 if (option_mask32 & OPT_locallog)
424 /* now spew out the message to wherever it is supposed to go */
425 if (pri == 0 || LOG_PRI(pri) < logLevel) {
426 if (option_mask32 & OPT_small)
427 message("%s %s\n", timestamp, msg);
429 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
434 static void quit_signal(int sig)
436 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
438 if (ENABLE_FEATURE_IPC_SYSLOG)
444 static void domark(int sig)
446 if (MarkInterval > 0) {
447 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
452 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
453 * enabled, we otherwise get a "storage size isn't constant error. */
454 static int serveConnection(char *tmpbuf, int n_read)
458 while (p < tmpbuf + n_read) {
460 int pri = (LOG_USER | LOG_NOTICE);
462 char line[MAXLINE + 1];
466 while ((c = *p) && q < &line[sizeof(line) - 1]) {
467 if (c == '<' && num_lt == 0) {
468 /* Parse the magic priority number. */
471 while (isdigit(*(++p))) {
472 pri = 10 * pri + (*p - '0');
474 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
475 pri = (LOG_USER | LOG_NOTICE);
477 } else if (c == '\n') {
479 } else if (iscntrl(c) && (c < 0177)) {
490 logMessage(pri, line);
495 static void doSyslogd(void) ATTRIBUTE_NORETURN;
496 static void doSyslogd(void)
498 struct sockaddr_un sunx;
499 socklen_t addrLength;
504 /* Set up signal handlers. */
505 signal(SIGINT, quit_signal);
506 signal(SIGTERM, quit_signal);
507 signal(SIGQUIT, quit_signal);
508 signal(SIGHUP, SIG_IGN);
509 signal(SIGCHLD, SIG_IGN);
511 signal(SIGCLD, SIG_IGN);
513 signal(SIGALRM, domark);
516 /* Create the syslog file so realpath() can work. */
517 if (realpath(_PATH_LOG, lfile) != NULL) {
521 memset(&sunx, 0, sizeof(sunx));
522 sunx.sun_family = AF_UNIX;
523 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
524 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
525 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
526 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
527 bb_perror_msg_and_die("cannot connect to socket %s", lfile);
530 if (chmod(lfile, 0666) < 0) {
531 bb_perror_msg_and_die("cannot set permission on %s", lfile);
533 if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
537 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
541 FD_SET(sock_fd, &fds);
543 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
544 if (errno == EINTR) {
545 /* alarm may have happened. */
548 bb_perror_msg_and_die("select");
551 if (FD_ISSET(sock_fd, &fds)) {
554 # define TMP_BUF_SZ BUFSIZ
556 # define TMP_BUF_SZ MAXLINE
558 #define tmpbuf bb_common_bufsiz1
560 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
562 serveConnection(tmpbuf, i);
564 bb_perror_msg_and_die("UNIX socket error");
567 } /* for main loop */
571 int syslogd_main(int argc, char **argv)
576 /* do normal option parsing */
577 getopt32(argc, argv, OPTION_STR, OPTION_PARAM);
578 if (option_mask32 & OPT_mark) MarkInterval = atoi(opt_m) * 60; // -m
579 //if (option_mask32 & OPT_nofork) // -n
580 //if (option_mask32 & OPT_outfile) // -O
581 if (option_mask32 & OPT_loglevel) { // -l
582 logLevel = atoi(opt_l);
583 /* Valid levels are between 1 and 8 */
584 if (logLevel < 1 || logLevel > 8)
587 //if (option_mask32 & OPT_small) // -S
588 #if ENABLE_FEATURE_ROTATE_LOGFILE
589 if (option_mask32 & OPT_filesize) logFileSize = atoi(opt_s) * 1024; // -s
590 if (option_mask32 & OPT_rotatecnt) { // -b
591 logFileRotate = atoi(opt_b);
592 if (logFileRotate > 99) logFileRotate = 99;
595 #if ENABLE_FEATURE_REMOTE_LOG
596 if (option_mask32 & OPT_remotelog) { // -R
598 char *host = xstrdup(opt_R);
599 p = strchr(host, ':');
604 remoteaddr.sin_family = AF_INET;
605 /* FIXME: looks ip4-specific. need to do better */
606 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(host)->h_addr_list);
607 remoteaddr.sin_port = htons(port);
610 //if (option_mask32 & OPT_locallog) // -L
612 #if ENABLE_FEATURE_IPC_SYSLOG
613 if (option_mask32 & OPT_circularlog) { // -C
615 int buf_size = atoi(opt_C);
617 shm_size = buf_size * 1024;
622 /* If they have not specified remote logging, then log locally */
623 if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_remotelog))
624 option_mask32 |= OPT_locallog;
626 /* Store away localhost's name before the fork */
627 gethostname(LocalHostName, sizeof(LocalHostName));
628 p = strchr(LocalHostName, '.');
635 if (!(option_mask32 & OPT_nofork)) {
637 vfork_daemon_rexec(0, 1, argc, argv, "-n");