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 static unsigned option_mask;
59 /* Correct regardless of combination of CONFIG_xxx */
61 OPTBIT_mark = 0, // -m
64 OPTBIT_loglevel, // -l
66 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
67 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
68 USE_FEATURE_REMOTE_LOG( OPTBIT_remote ,) // -R
69 USE_FEATURE_REMOTE_LOG( OPTBIT_localtoo ,) // -L
70 USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
72 OPT_mark = 1 << OPTBIT_mark ,
73 OPT_nofork = 1 << OPTBIT_nofork ,
74 OPT_outfile = 1 << OPTBIT_outfile ,
75 OPT_loglevel = 1 << OPTBIT_loglevel,
76 OPT_small = 1 << OPTBIT_small ,
77 OPT_filesize = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
78 OPT_rotatecnt = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
79 OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remote )) + 0,
80 OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_localtoo )) + 0,
81 OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
83 #define OPTION_STR "m:nO:l:S" \
84 USE_FEATURE_ROTATE_LOGFILE("s:" ) \
85 USE_FEATURE_ROTATE_LOGFILE("b:" ) \
86 USE_FEATURE_REMOTE_LOG( "R:" ) \
87 USE_FEATURE_REMOTE_LOG( "L" ) \
88 USE_FEATURE_IPC_SYSLOG( "C::")
89 #define OPTION_DECL *opt_m, *opt_l \
90 USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
91 USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
92 USE_FEATURE_REMOTE_LOG( ,*opt_R) \
93 USE_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
94 #define OPTION_PARAM &opt_m, &logFilePath, &opt_l \
95 USE_FEATURE_ROTATE_LOGFILE(,&opt_s) \
96 USE_FEATURE_ROTATE_LOGFILE(,&opt_b) \
97 USE_FEATURE_REMOTE_LOG( ,&opt_R) \
98 USE_FEATURE_IPC_SYSLOG( ,&opt_C)
100 #define MAXLINE 1024 /* maximum line length */
102 /* circular buffer variables/structures */
103 #ifdef CONFIG_FEATURE_IPC_SYSLOG
105 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
106 #error Sorry, you must set the syslogd buffer size to at least 4KB.
107 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
115 #define KEY_ID ((long)0x414e4547) /* "GENA" */
117 // Semaphore operation structures
118 static struct shbuf_ds {
119 int size; // size of data written
120 int head; // start of message list
121 int tail; // end of message list
122 char data[1]; // data/messages
123 } *shbuf = NULL; // shared memory pointer
125 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
126 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
128 static int shmid = -1; // ipc shared memory id
129 static int s_semid = -1; // ipc semaphore id
130 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
132 static void ipcsyslog_cleanup(void)
134 puts("Exiting syslogd!");
140 shmctl(shmid, IPC_RMID, NULL);
143 semctl(s_semid, 0, IPC_RMID, 0);
147 static void ipcsyslog_init(void)
150 shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023);
152 bb_perror_msg_and_die("shmget");
155 shbuf = shmat(shmid, NULL, 0);
157 bb_perror_msg_and_die("shmat");
160 shbuf->size = shm_size - sizeof(*shbuf);
161 shbuf->head = shbuf->tail = 0;
163 // we'll trust the OS to set initial semval to 0 (let's hope)
164 s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
166 if (errno == EEXIST) {
167 s_semid = semget(KEY_ID, 2, 0);
169 bb_perror_msg_and_die("semget");
172 bb_perror_msg_and_die("semget");
176 printf("Buffer already allocated just grab the semaphore?");
180 /* write message to buffer */
181 static void circ_message(const char *msg)
183 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
184 const char * const fail_msg = "Can't find the terminator token%s?\n";
186 if (semop(s_semid, SMwdn, 3) == -1) {
187 bb_perror_msg_and_die("SMwdn");
191 * Circular Buffer Algorithm:
192 * --------------------------
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.
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
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.
210 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
211 * a threadsafe way of handling shared memory operations.
213 if ((shbuf->tail + l) < shbuf->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 (shbuf->tail < shbuf->head) {
218 if ((shbuf->tail + l) >= shbuf->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!
224 /* we need to know how many bytes we are overwriting to make enough room */
225 int k = shbuf->tail + l - shbuf->head;
227 memchr(shbuf->data + shbuf->head + k, '\0',
228 shbuf->size - (shbuf->head + k));
229 if (c != NULL) { /* do a sanity check just in case! */
230 /* we need to convert pointer to offset + skip the '\0'
231 since we need to point to the beginning of the next message */
232 shbuf->head = c - shbuf->data + 1;
233 /* Note: HEAD is only used to "retrieve" messages, it's not used
234 when writing messages into our buffer */
235 } else { /* show an error message to know we messed up? */
242 /* in other cases no overflows have been done yet, so we don't care! */
243 /* we should be ok to append the message now */
244 strncpy(shbuf->data + shbuf->tail, msg, l); /* append our message */
245 shbuf->tail += l; /* count full message w/ '\0' terminating char */
247 /* we need to break up the message and "circle" it around */
249 int k = shbuf->tail + l - shbuf->size; /* count # of bytes we don't fit */
251 /* We need to move HEAD! This is always the case since we are going
252 * to "circle" the message. */
253 c = memchr(shbuf->data + k, '\0', shbuf->size - k);
255 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
256 /* move head pointer */
257 shbuf->head = c - shbuf->data + 1;
259 /* now write the first part of the message */
260 strncpy(shbuf->data + shbuf->tail, msg, l - k - 1);
262 /* ALWAYS terminate end of buffer w/ '\0' */
263 shbuf->data[shbuf->size - 1] = '\0';
265 /* now write out the rest of the string to the beginning of the buffer */
266 strcpy(shbuf->data, &msg[l - k - 1]);
268 /* we need to place the TAIL at the end of the message */
271 printf(fail_msg, " from the beginning");
272 shbuf->head = shbuf->tail = 0; /* reset buffer, since it's probably corrupted */
276 if (semop(s_semid, SMwup, 1) == -1) {
277 bb_perror_msg_and_die("SMwup");
282 void ipcsyslog_cleanup(void);
283 void ipcsyslog_init(void);
284 void circ_message(const char *msg);
285 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
287 /* Note: There is also a function called "message()" in init.c */
288 /* Print a message to the log file. */
289 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
290 static void message(char *fmt, ...)
296 fl.l_whence = SEEK_SET;
300 #ifdef CONFIG_FEATURE_IPC_SYSLOG
301 if ((option_mask & OPT_circularlog) && shbuf) {
304 va_start(arguments, fmt);
305 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
311 fd = device_open(logFilePath, O_WRONLY | O_CREAT
312 | O_NOCTTY | O_APPEND | O_NONBLOCK);
315 fcntl(fd, F_SETLKW, &fl);
317 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
318 if (ENABLE_FEATURE_ROTATE_LOGFILE && logFileSize > 0 ) {
320 int r = fstat(fd, &statf);
321 if (!r && (statf.st_mode & S_IFREG)
322 && (lseek(fd,0,SEEK_END) > logFileSize)) {
323 if (logFileRotate > 0) {
324 int i = strlen(logFilePath) + 4;
327 for (i=logFileRotate-1; i>0; i--) {
328 sprintf(oldFile, "%s.%d", logFilePath, i-1);
329 sprintf(newFile, "%s.%d", logFilePath, i);
330 rename(oldFile, newFile);
332 sprintf(newFile, "%s.%d", logFilePath, 0);
334 fcntl(fd, F_SETLKW, &fl);
336 rename(logFilePath, newFile);
337 fd = device_open(logFilePath,
338 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
341 fcntl(fd, F_SETLKW, &fl);
348 va_start(arguments, fmt);
349 vdprintf(fd, fmt, arguments);
352 fcntl(fd, F_SETLKW, &fl);
355 /* Always send console messages to /dev/console so people will see them. */
356 fd = device_open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
358 va_start(arguments, fmt);
359 vdprintf(fd, fmt, arguments);
363 fprintf(stderr, "Bummer, can't print: ");
364 va_start(arguments, fmt);
365 vfprintf(stderr, fmt, arguments);
372 static void logMessage(int pri, char *msg)
380 c_fac = facilitynames;
381 while (c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3))
383 c_pri = prioritynames;
384 while (c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)))
386 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
387 snprintf(res, sizeof(res), "<%d>", pri);
389 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
393 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
394 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
396 timestamp = ctime(&now) + 4;
397 timestamp[15] = '\0';
400 timestamp[15] = '\0';
404 /* todo: supress duplicates */
406 #ifdef CONFIG_FEATURE_REMOTE_LOG
407 if (option_mask & OPT_remotelog) {
408 char line[MAXLINE + 1];
409 /* trying connect the socket */
410 if (-1 == remotefd) {
411 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
413 /* if we have a valid socket, send the message */
414 if (-1 != remotefd) {
415 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
416 /* send message to remote logger, ignore possible error */
417 sendto(remotefd, line, strlen(line), 0,
418 (struct sockaddr *) &remoteaddr, sizeof(remoteaddr));
422 if (option_mask & OPT_locallog)
425 /* now spew out the message to wherever it is supposed to go */
426 if (pri == 0 || LOG_PRI(pri) < logLevel) {
427 if (option_mask & OPT_small)
428 message("%s %s\n", timestamp, msg);
430 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
435 static void quit_signal(int sig)
437 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
439 if (ENABLE_FEATURE_IPC_SYSLOG)
445 static void domark(int sig)
447 if (MarkInterval > 0) {
448 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
453 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
454 * enabled, we otherwise get a "storage size isn't constant error. */
455 static int serveConnection(char *tmpbuf, int n_read)
459 while (p < tmpbuf + n_read) {
461 int pri = (LOG_USER | LOG_NOTICE);
463 char line[MAXLINE + 1];
467 while ((c = *p) && q < &line[sizeof(line) - 1]) {
468 if (c == '<' && num_lt == 0) {
469 /* Parse the magic priority number. */
472 while (isdigit(*(++p))) {
473 pri = 10 * pri + (*p - '0');
475 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
476 pri = (LOG_USER | LOG_NOTICE);
478 } else if (c == '\n') {
480 } else if (iscntrl(c) && (c < 0177)) {
491 logMessage(pri, line);
496 static void doSyslogd(void) ATTRIBUTE_NORETURN;
497 static void doSyslogd(void)
499 struct sockaddr_un sunx;
500 socklen_t addrLength;
505 /* Set up signal handlers. */
506 signal(SIGINT, quit_signal);
507 signal(SIGTERM, quit_signal);
508 signal(SIGQUIT, quit_signal);
509 signal(SIGHUP, SIG_IGN);
510 signal(SIGCHLD, SIG_IGN);
512 signal(SIGCLD, SIG_IGN);
514 signal(SIGALRM, domark);
517 /* Create the syslog file so realpath() can work. */
518 if (realpath(_PATH_LOG, lfile) != NULL) {
522 memset(&sunx, 0, sizeof(sunx));
523 sunx.sun_family = AF_UNIX;
524 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
525 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
526 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
527 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
528 bb_perror_msg_and_die("cannot connect to socket %s", lfile);
531 if (chmod(lfile, 0666) < 0) {
532 bb_perror_msg_and_die("cannot set permission on %s", lfile);
534 if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask & OPT_circularlog)) {
538 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
542 FD_SET(sock_fd, &fds);
544 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
545 if (errno == EINTR) {
546 /* alarm may have happened. */
549 bb_perror_msg_and_die("select");
552 if (FD_ISSET(sock_fd, &fds)) {
555 # define TMP_BUF_SZ BUFSIZ
557 # define TMP_BUF_SZ MAXLINE
559 #define tmpbuf bb_common_bufsiz1
561 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
563 serveConnection(tmpbuf, i);
565 bb_perror_msg_and_die("UNIX socket error");
568 } /* for main loop */
572 int syslogd_main(int argc, char **argv)
577 /* do normal option parsing */
578 option_mask = bb_getopt_ulflags(argc, argv, OPTION_STR, OPTION_PARAM);
579 if (option_mask & OPT_mark) MarkInterval = atoi(opt_m) * 60; // -m
580 //if (option_mask & OPT_nofork) // -n
581 //if (option_mask & OPT_outfile) // -O
582 if (option_mask & OPT_loglevel) { // -l
583 logLevel = atoi(opt_l);
584 /* Valid levels are between 1 and 8 */
585 if (logLevel < 1 || logLevel > 8)
588 //if (option_mask & OPT_small) // -S
589 #if ENABLE_FEATURE_ROTATE_LOGFILE
590 if (option_mask & OPT_filesize) logFileSize = atoi(opt_s) * 1024; // -s
591 if (option_mask & OPT_rotatecnt) { // -b
592 logFileRotate = atoi(opt_b);
593 if (logFileRotate > 99) logFileRotate = 99;
596 #if ENABLE_FEATURE_REMOTE_LOG
597 if (option_mask & OPT_remotelog) { // -R
599 char *host = xstrdup(opt_R);
600 p = strchr(host, ':');
605 remoteaddr.sin_family = AF_INET;
606 /* FIXME: looks ip4-specific. need to do better */
607 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(host)->h_addr_list);
608 remoteaddr.sin_port = htons(port);
611 //if (option_mask & OPT_locallog) // -L
613 #if ENABLE_FEATURE_IPC_SYSLOG
614 if (option_mask & OPT_circularlog) { // -C
616 int buf_size = atoi(opt_C);
618 shm_size = buf_size * 1024;
623 /* If they have not specified remote logging, then log locally */
624 if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask & OPT_remotelog))
625 option_mask |= OPT_locallog;
627 /* Store away localhost's name before the fork */
628 gethostname(LocalHostName, sizeof(LocalHostName));
629 p = strchr(LocalHostName, '.');
636 if (!(option_mask & OPT_nofork)) {
638 vfork_daemon_rexec(0, 1, argc, argv, "-n");