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;
55 /* where do we log? */
56 static char *RemoteHost;
58 /* what port to log to? */
59 static int RemotePort = 514;
64 static unsigned option_mask;
65 /* Correct regardless of combination of CONFIG_xxx */
67 OPTBIT_mark = 0, // -m
70 OPTBIT_loglevel, // -l
72 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
73 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
74 USE_FEATURE_REMOTE_LOG( OPTBIT_remote ,) // -R
75 USE_FEATURE_REMOTE_LOG( OPTBIT_localtoo ,) // -L
76 USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
78 OPT_mark = 1 << OPTBIT_mark ,
79 OPT_nofork = 1 << OPTBIT_nofork ,
80 OPT_outfile = 1 << OPTBIT_outfile ,
81 OPT_loglevel = 1 << OPTBIT_loglevel,
82 OPT_small = 1 << OPTBIT_small ,
83 OPT_filesize = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
84 OPT_rotatecnt = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
85 OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remote )) + 0,
86 OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_localtoo )) + 0,
87 OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
89 #define OPTION_STR "m:nO:l:S" \
90 USE_FEATURE_ROTATE_LOGFILE("s:" ) \
91 USE_FEATURE_ROTATE_LOGFILE("b:" ) \
92 USE_FEATURE_REMOTE_LOG( "R:" ) \
93 USE_FEATURE_REMOTE_LOG( "L" ) \
94 USE_FEATURE_IPC_SYSLOG( "C::")
95 #define OPTION_DECL *opt_m, *opt_l \
96 USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
97 USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
98 USE_FEATURE_REMOTE_LOG( ,*opt_R) \
99 USE_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
100 #define OPTION_PARAM &opt_m, &logFilePath, &opt_l \
101 USE_FEATURE_ROTATE_LOGFILE(,&opt_s) \
102 USE_FEATURE_ROTATE_LOGFILE(,&opt_b) \
103 USE_FEATURE_REMOTE_LOG( ,&opt_R) \
104 USE_FEATURE_IPC_SYSLOG( ,&opt_C)
106 #define MAXLINE 1024 /* maximum line length */
108 /* circular buffer variables/structures */
109 #ifdef CONFIG_FEATURE_IPC_SYSLOG
111 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
112 #error Sorry, you must set the syslogd buffer size to at least 4KB.
113 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
121 #define KEY_ID ((long)0x414e4547) /* "GENA" */
123 // Semaphore operation structures
124 static struct shbuf_ds {
125 int size; // size of data written
126 int head; // start of message list
127 int tail; // end of message list
128 char data[1]; // data/messages
129 } *shbuf = NULL; // shared memory pointer
131 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
132 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
134 static int shmid = -1; // ipc shared memory id
135 static int s_semid = -1; // ipc semaphore id
136 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
138 static void ipcsyslog_cleanup(void)
140 puts("Exiting syslogd!");
146 shmctl(shmid, IPC_RMID, NULL);
149 semctl(s_semid, 0, IPC_RMID, 0);
153 static void ipcsyslog_init(void)
156 shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023);
158 bb_perror_msg_and_die("shmget");
161 shbuf = shmat(shmid, NULL, 0);
163 bb_perror_msg_and_die("shmat");
166 shbuf->size = shm_size - sizeof(*shbuf);
167 shbuf->head = shbuf->tail = 0;
169 // we'll trust the OS to set initial semval to 0 (let's hope)
170 s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
172 if (errno == EEXIST) {
173 s_semid = semget(KEY_ID, 2, 0);
175 bb_perror_msg_and_die("semget");
178 bb_perror_msg_and_die("semget");
182 printf("Buffer already allocated just grab the semaphore?");
186 /* write message to buffer */
187 static void circ_message(const char *msg)
189 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
190 const char * const fail_msg = "Can't find the terminator token%s?\n";
192 if (semop(s_semid, SMwdn, 3) == -1) {
193 bb_perror_msg_and_die("SMwdn");
197 * Circular Buffer Algorithm:
198 * --------------------------
200 * Start-off w/ empty buffer of specific size SHM_SIZ
201 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
202 * This is also very handy since we can do printf on message.
204 * Once the buffer is full we need to get rid of the first message in buffer and
205 * insert the new message. (Note: if the message being added is >1 message then
206 * we will need to "remove" >1 old message from the buffer). The way this is done
208 * When we reach the end of the buffer we set a mark and start from the beginning.
209 * Now what about the beginning and end of the buffer? Well we have the "head"
210 * index/pointer which is the starting point for the messages and we have "tail"
211 * index/pointer which is the ending point for the messages. When we "display" the
212 * messages we start from the beginning and continue until we reach "tail". If we
213 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
214 * "tail" are actually offsets from the beginning of the buffer.
216 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
217 * a threadsafe way of handling shared memory operations.
219 if ((shbuf->tail + l) < shbuf->size) {
220 /* before we append the message we need to check the HEAD so that we won't
221 overwrite any of the message that we still need and adjust HEAD to point
222 to the next message! */
223 if (shbuf->tail < shbuf->head) {
224 if ((shbuf->tail + l) >= shbuf->head) {
225 /* we need to move the HEAD to point to the next message
226 * Theoretically we have enough room to add the whole message to the
227 * buffer, because of the first outer IF statement, so we don't have
228 * to worry about overflows here!
230 /* we need to know how many bytes we are overwriting to make enough room */
231 int k = shbuf->tail + l - shbuf->head;
233 memchr(shbuf->data + shbuf->head + k, '\0',
234 shbuf->size - (shbuf->head + k));
235 if (c != NULL) { /* do a sanity check just in case! */
236 /* we need to convert pointer to offset + skip the '\0'
237 since we need to point to the beginning of the next message */
238 shbuf->head = c - shbuf->data + 1;
239 /* Note: HEAD is only used to "retrieve" messages, it's not used
240 when writing messages into our buffer */
241 } else { /* show an error message to know we messed up? */
248 /* in other cases no overflows have been done yet, so we don't care! */
249 /* we should be ok to append the message now */
250 strncpy(shbuf->data + shbuf->tail, msg, l); /* append our message */
251 shbuf->tail += l; /* count full message w/ '\0' terminating char */
253 /* we need to break up the message and "circle" it around */
255 int k = shbuf->tail + l - shbuf->size; /* count # of bytes we don't fit */
257 /* We need to move HEAD! This is always the case since we are going
258 * to "circle" the message. */
259 c = memchr(shbuf->data + k, '\0', shbuf->size - k);
261 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
262 /* move head pointer */
263 shbuf->head = c - shbuf->data + 1;
265 /* now write the first part of the message */
266 strncpy(shbuf->data + shbuf->tail, msg, l - k - 1);
268 /* ALWAYS terminate end of buffer w/ '\0' */
269 shbuf->data[shbuf->size - 1] = '\0';
271 /* now write out the rest of the string to the beginning of the buffer */
272 strcpy(shbuf->data, &msg[l - k - 1]);
274 /* we need to place the TAIL at the end of the message */
277 printf(fail_msg, " from the beginning");
278 shbuf->head = shbuf->tail = 0; /* reset buffer, since it's probably corrupted */
282 if (semop(s_semid, SMwup, 1) == -1) {
283 bb_perror_msg_and_die("SMwup");
288 void ipcsyslog_cleanup(void);
289 void ipcsyslog_init(void);
290 void circ_message(const char *msg);
291 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
293 /* Note: There is also a function called "message()" in init.c */
294 /* Print a message to the log file. */
295 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
296 static void message(char *fmt, ...)
302 fl.l_whence = SEEK_SET;
306 #ifdef CONFIG_FEATURE_IPC_SYSLOG
307 if ((option_mask & OPT_circularlog) && shbuf) {
310 va_start(arguments, fmt);
311 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
317 fd = device_open(logFilePath, O_WRONLY | O_CREAT
318 | O_NOCTTY | O_APPEND | O_NONBLOCK);
321 fcntl(fd, F_SETLKW, &fl);
323 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
324 if (ENABLE_FEATURE_ROTATE_LOGFILE && logFileSize > 0 ) {
326 int r = fstat(fd, &statf);
327 if (!r && (statf.st_mode & S_IFREG)
328 && (lseek(fd,0,SEEK_END) > logFileSize)) {
329 if (logFileRotate > 0) {
330 int i = strlen(logFilePath) + 4;
333 for (i=logFileRotate-1; i>0; i--) {
334 sprintf(oldFile, "%s.%d", logFilePath, i-1);
335 sprintf(newFile, "%s.%d", logFilePath, i);
336 rename(oldFile, newFile);
338 sprintf(newFile, "%s.%d", logFilePath, 0);
340 fcntl(fd, F_SETLKW, &fl);
342 rename(logFilePath, newFile);
343 fd = device_open(logFilePath,
344 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
347 fcntl(fd, F_SETLKW, &fl);
354 va_start(arguments, fmt);
355 vdprintf(fd, fmt, arguments);
358 fcntl(fd, F_SETLKW, &fl);
361 /* Always send console messages to /dev/console so people will see them. */
362 fd = device_open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
364 va_start(arguments, fmt);
365 vdprintf(fd, fmt, arguments);
369 fprintf(stderr, "Bummer, can't print: ");
370 va_start(arguments, fmt);
371 vfprintf(stderr, fmt, arguments);
378 #ifdef CONFIG_FEATURE_REMOTE_LOG
379 static void init_RemoteLog(void)
381 memset(&remoteaddr, 0, sizeof(remoteaddr));
382 remotefd = xsocket(AF_INET, SOCK_DGRAM, 0);
383 remoteaddr.sin_family = AF_INET;
384 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
385 remoteaddr.sin_port = htons(RemotePort);
388 void init_RemoteLog(void);
391 static void logMessage(int pri, char *msg)
399 c_fac = facilitynames;
400 while (c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3))
402 c_pri = prioritynames;
403 while (c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)))
405 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
406 snprintf(res, sizeof(res), "<%d>", pri);
408 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
412 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
413 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
415 timestamp = ctime(&now) + 4;
416 timestamp[15] = '\0';
419 timestamp[15] = '\0';
423 /* todo: supress duplicates */
425 #ifdef CONFIG_FEATURE_REMOTE_LOG
426 if (option_mask & OPT_remotelog) {
427 char line[MAXLINE + 1];
428 /* trying connect the socket */
429 if (-1 == remotefd) {
433 /* if we have a valid socket, send the message */
434 if (-1 != remotefd) {
436 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
439 /* send message to remote logger */
440 if ((-1 == sendto(remotefd, line, strlen(line), 0,
441 (struct sockaddr *) &remoteaddr,
442 sizeof(remoteaddr))) && (errno == EINTR)) {
443 /* sleep now seconds and retry (with now * 2) */
451 if (option_mask & OPT_locallog)
454 /* now spew out the message to wherever it is supposed to go */
455 if (pri == 0 || LOG_PRI(pri) < logLevel) {
456 if (option_mask & OPT_small)
457 message("%s %s\n", timestamp, msg);
459 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
464 static void quit_signal(int sig)
466 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
468 if (ENABLE_FEATURE_IPC_SYSLOG)
474 static void domark(int sig)
476 if (MarkInterval > 0) {
477 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
482 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
483 * enabled, we otherwise get a "storage size isn't constant error. */
484 static int serveConnection(char *tmpbuf, int n_read)
488 while (p < tmpbuf + n_read) {
490 int pri = (LOG_USER | LOG_NOTICE);
492 char line[MAXLINE + 1];
496 while ((c = *p) && q < &line[sizeof(line) - 1]) {
497 if (c == '<' && num_lt == 0) {
498 /* Parse the magic priority number. */
501 while (isdigit(*(++p))) {
502 pri = 10 * pri + (*p - '0');
504 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
505 pri = (LOG_USER | LOG_NOTICE);
507 } else if (c == '\n') {
509 } else if (iscntrl(c) && (c < 0177)) {
520 logMessage(pri, line);
525 static void doSyslogd(void) ATTRIBUTE_NORETURN;
526 static void doSyslogd(void)
528 struct sockaddr_un sunx;
529 socklen_t addrLength;
534 /* Set up signal handlers. */
535 signal(SIGINT, quit_signal);
536 signal(SIGTERM, quit_signal);
537 signal(SIGQUIT, quit_signal);
538 signal(SIGHUP, SIG_IGN);
539 signal(SIGCHLD, SIG_IGN);
541 signal(SIGCLD, SIG_IGN);
543 signal(SIGALRM, domark);
546 /* Create the syslog file so realpath() can work. */
547 if (realpath(_PATH_LOG, lfile) != NULL) {
551 memset(&sunx, 0, sizeof(sunx));
552 sunx.sun_family = AF_UNIX;
553 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
554 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
555 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
556 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
557 bb_perror_msg_and_die("cannot connect to socket %s", lfile);
560 if (chmod(lfile, 0666) < 0) {
561 bb_perror_msg_and_die("cannot set permission on %s", lfile);
563 if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask & OPT_circularlog)) {
567 if (ENABLE_FEATURE_REMOTE_LOG && (option_mask & OPT_remotelog)) {
571 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
575 FD_SET(sock_fd, &fds);
577 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
578 if (errno == EINTR) {
579 /* alarm may have happened. */
582 bb_perror_msg_and_die("select");
585 if (FD_ISSET(sock_fd, &fds)) {
588 # define TMP_BUF_SZ BUFSIZ
590 # define TMP_BUF_SZ MAXLINE
592 #define tmpbuf bb_common_bufsiz1
594 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
596 serveConnection(tmpbuf, i);
598 bb_perror_msg_and_die("UNIX socket error");
601 } /* for main loop */
605 int syslogd_main(int argc, char **argv)
610 /* do normal option parsing */
611 option_mask = bb_getopt_ulflags(argc, argv, OPTION_STR, OPTION_PARAM);
612 if (option_mask & OPT_mark) MarkInterval = atoi(opt_m) * 60; // -m
613 //if (option_mask & OPT_nofork) // -n
614 //if (option_mask & OPT_outfile) // -O
615 if (option_mask & OPT_loglevel) { // -l
616 logLevel = atoi(opt_l);
617 /* Valid levels are between 1 and 8 */
618 if (logLevel < 1 || logLevel > 8)
621 //if (option_mask & OPT_small) // -S
622 #if ENABLE_FEATURE_ROTATE_LOGFILE
623 if (option_mask & OPT_filesize) logFileSize = atoi(opt_s) * 1024; // -s
624 if (option_mask & OPT_rotatecnt) { // -b
625 logFileRotate = atoi(opt_b);
626 if (logFileRotate > 99) logFileRotate = 99;
629 #if ENABLE_FEATURE_REMOTE_LOG
630 if (option_mask & OPT_remotelog) { // -R
631 RemoteHost = xstrdup(opt_R);
632 p = strchr(RemoteHost, ':');
634 RemotePort = atoi(p + 1);
638 //if (option_mask & OPT_locallog) // -L
640 #if ENABLE_FEATURE_IPC_SYSLOG
641 if (option_mask & OPT_circularlog) { // -C
643 int buf_size = atoi(opt_C);
645 shm_size = buf_size * 1024;
650 /* If they have not specified remote logging, then log locally */
651 if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask & OPT_remotelog))
652 option_mask |= OPT_locallog;
654 /* Store away localhost's name before the fork */
655 gethostname(LocalHostName, sizeof(LocalHostName));
656 p = strchr(LocalHostName, '.');
663 if (!(option_mask & OPT_nofork)) {
665 vfork_daemon_rexec(0, 1, argc, argv, "-n");