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 for the file where all log messages are written */
27 #define __LOG_FILE "/var/log/messages"
29 /* Path to the unix socket */
30 static char lfile[MAXPATHLEN];
32 static const char *logFilePath = __LOG_FILE;
34 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
35 /* max size of message file before being rotated */
36 static int logFileSize = 200 * 1024;
38 /* number of rotated message files */
39 static int logFileRotate = 1;
42 /* interval between marks in seconds */
43 static int MarkInterval = 20 * 60;
45 /* localhost's name */
46 static char LocalHostName[64];
48 #ifdef CONFIG_FEATURE_REMOTE_LOG
49 #include <netinet/in.h>
50 /* udp socket for logging to remote host */
51 static int remotefd = -1;
52 static struct sockaddr_in remoteaddr;
54 /* where do we log? */
55 static char *RemoteHost;
57 /* what port to log to? */
58 static int RemotePort = 514;
60 /* To remote log or not to remote log, that is the question. */
61 static int doRemoteLog = FALSE;
62 static int local_logging = FALSE;
65 /* Make loging output smaller. */
66 static bool small = false;
69 #define MAXLINE 1024 /* maximum line length */
72 /* circular buffer variables/structures */
73 #ifdef CONFIG_FEATURE_IPC_SYSLOG
75 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
76 #error Sorry, you must set the syslogd buffer size to at least 4KB.
77 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
85 static const long KEY_ID = 0x414e4547; /*"GENA" */
87 // Semaphore operation structures
88 static struct shbuf_ds {
89 int size; // size of data written
90 int head; // start of message list
91 int tail; // end of message list
92 char data[1]; // data/messages
93 } *buf = NULL; // shared memory pointer
95 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
96 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
98 static int shmid = -1; // ipc shared memory id
99 static int s_semid = -1; // ipc semaphore id
100 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
101 static int circular_logging = FALSE;
104 * sem_up - up()'s a semaphore.
106 static inline void sem_up(int semid)
108 if (semop(semid, SMwup, 1) == -1) {
109 bb_perror_msg_and_die("semop[SMwup]");
114 * sem_down - down()'s a semaphore
116 static inline void sem_down(int semid)
118 if (semop(semid, SMwdn, 3) == -1) {
119 bb_perror_msg_and_die("semop[SMwdn]");
124 static void ipcsyslog_cleanup(void)
126 printf("Exiting Syslogd!\n");
132 shmctl(shmid, IPC_RMID, NULL);
135 semctl(s_semid, 0, IPC_RMID, 0);
139 static void ipcsyslog_init(void)
142 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
143 bb_perror_msg_and_die("shmget");
146 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
147 bb_perror_msg_and_die("shmat");
150 buf->size = shm_size - sizeof(*buf);
151 buf->head = buf->tail = 0;
153 // we'll trust the OS to set initial semval to 0 (let's hope)
154 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
155 if (errno == EEXIST) {
156 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
157 bb_perror_msg_and_die("semget");
160 bb_perror_msg_and_die("semget");
164 printf("Buffer already allocated just grab the semaphore?");
168 /* write message to buffer */
169 static void circ_message(const char *msg)
171 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
176 * Circular Buffer Algorithm:
177 * --------------------------
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.
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
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.
195 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
196 * a threadsafe way of handling shared memory operations.
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!
209 int k = buf->tail + l - buf->head; /* we need to know how many bytes
210 we are overwriting to make
213 memchr(buf->data + buf->head + k, '\0',
214 buf->size - (buf->head + k));
215 if (c != NULL) { /* do a sanity check just in case! */
216 buf->head = c - buf->data + 1; /* we need to convert pointer to
217 offset + skip the '\0' since
218 we need to point to the beginning
219 of the next message */
220 /* Note: HEAD is only used to "retrieve" messages, it's not used
221 when writing messages into our buffer */
222 } else { /* show an error message to know we messed up? */
223 printf("Weird! Can't find the terminator token?\n");
229 /* in other cases no overflows have been done yet, so we don't care! */
230 /* we should be ok to append the message now */
231 strncpy(buf->data + buf->tail, msg, l); /* append our message */
232 buf->tail += l; /* count full message w/ '\0' terminating char */
234 /* we need to break up the message and "circle" it around */
236 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
238 /* We need to move HEAD! This is always the case since we are going
239 * to "circle" the message.
241 c = memchr(buf->data + k, '\0', buf->size - k);
243 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
244 /* move head pointer */
245 buf->head = c - buf->data + 1;
247 /* now write the first part of the message */
248 strncpy(buf->data + buf->tail, msg, l - k - 1);
250 /* ALWAYS terminate end of buffer w/ '\0' */
251 buf->data[buf->size - 1] = '\0';
253 /* now write out the rest of the string to the beginning of the buffer */
254 strcpy(buf->data, &msg[l - k - 1]);
256 /* we need to place the TAIL at the end of the message */
260 ("Weird! Can't find the terminator token from the beginning?\n");
261 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
267 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
269 /* Note: There is also a function called "message()" in init.c */
270 /* Print a message to the log file. */
271 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
272 static void message(char *fmt, ...)
278 fl.l_whence = SEEK_SET;
282 #ifdef CONFIG_FEATURE_IPC_SYSLOG
283 if ((circular_logging == TRUE) && (buf != NULL)) {
286 va_start(arguments, fmt);
287 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
293 if ((fd = device_open(logFilePath,
294 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
297 fcntl(fd, F_SETLKW, &fl);
298 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
299 if ( logFileSize > 0 ) {
301 int r = fstat(fd, &statf);
302 if( !r && (statf.st_mode & S_IFREG)
303 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
304 if(logFileRotate > 0) {
306 char oldFile[(strlen(logFilePath)+4)], newFile[(strlen(logFilePath)+4)];
307 for(i=logFileRotate-1;i>0;i--) {
308 sprintf(oldFile, "%s.%d", logFilePath, i-1);
309 sprintf(newFile, "%s.%d", logFilePath, i);
310 rename(oldFile, newFile);
312 sprintf(newFile, "%s.%d", logFilePath, 0);
314 fcntl (fd, F_SETLKW, &fl);
316 rename(logFilePath, newFile);
317 fd = device_open (logFilePath,
318 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
321 fcntl (fd, F_SETLKW, &fl);
328 va_start(arguments, fmt);
329 vdprintf(fd, fmt, arguments);
332 fcntl(fd, F_SETLKW, &fl);
335 /* Always send console messages to /dev/console so people will see them. */
336 if ((fd = device_open(_PATH_CONSOLE,
337 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
338 va_start(arguments, fmt);
339 vdprintf(fd, fmt, arguments);
343 fprintf(stderr, "Bummer, can't print: ");
344 va_start(arguments, fmt);
345 vfprintf(stderr, fmt, arguments);
352 #ifdef CONFIG_FEATURE_REMOTE_LOG
353 static void init_RemoteLog(void)
355 memset(&remoteaddr, 0, sizeof(remoteaddr));
356 remotefd = xsocket(AF_INET, SOCK_DGRAM, 0);
357 remoteaddr.sin_family = AF_INET;
358 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
359 remoteaddr.sin_port = htons(RemotePort);
363 static void logMessage(int pri, char *msg)
368 #ifdef CONFIG_FEATURE_REMOTE_LOG
369 static char line[MAXLINE + 1];
374 for (c_fac = facilitynames;
375 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
376 for (c_pri = prioritynames;
377 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
378 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
379 snprintf(res, sizeof(res), "<%d>", pri);
381 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
385 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
386 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
388 timestamp = ctime(&now) + 4;
389 timestamp[15] = '\0';
392 timestamp[15] = '\0';
396 /* todo: supress duplicates */
398 #ifdef CONFIG_FEATURE_REMOTE_LOG
399 if (doRemoteLog == TRUE) {
400 /* trying connect the socket */
401 if (-1 == remotefd) {
405 /* if we have a valid socket, send the message */
406 if (-1 != remotefd) {
408 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
411 /* send message to remote logger */
412 if(( -1 == sendto(remotefd, line, strlen(line), 0,
413 (struct sockaddr *) &remoteaddr,
414 sizeof(remoteaddr))) && (errno == EINTR)) {
415 /* sleep now seconds and retry (with now * 2) */
423 if (local_logging == TRUE)
426 /* now spew out the message to wherever it is supposed to go */
428 message("%s %s\n", timestamp, msg);
430 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 #ifdef CONFIG_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("Could not connect to socket " _PATH_LOG);
531 if (chmod(lfile, 0666) < 0) {
532 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
534 #ifdef CONFIG_FEATURE_IPC_SYSLOG
535 if (circular_logging == TRUE) {
540 #ifdef CONFIG_FEATURE_REMOTE_LOG
541 if (doRemoteLog == TRUE) {
546 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
551 FD_SET(sock_fd, &fds);
553 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
554 if (errno == EINTR) {
555 /* alarm may have happened. */
558 bb_perror_msg_and_die("select error");
561 if (FD_ISSET(sock_fd, &fds)) {
564 # define TMP_BUF_SZ BUFSIZ
566 # define TMP_BUF_SZ MAXLINE
568 #define tmpbuf bb_common_bufsiz1
570 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
572 serveConnection(tmpbuf, i);
574 bb_perror_msg_and_die("UNIX socket error");
577 } /* for main loop */
580 int syslogd_main(int argc, char **argv)
588 /* do normal option parsing */
589 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
592 MarkInterval = atoi(optarg) * 60;
598 logFilePath = optarg;
600 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
602 logFileSize = atoi(optarg) * 1024;
605 logFileRotate = atoi(optarg);
606 if( logFileRotate > 99 ) logFileRotate = 99;
609 #ifdef CONFIG_FEATURE_REMOTE_LOG
611 RemoteHost = xstrdup(optarg);
612 if ((p = strchr(RemoteHost, ':'))) {
613 RemotePort = atoi(p + 1);
619 local_logging = TRUE;
622 #ifdef CONFIG_FEATURE_IPC_SYSLOG
625 int buf_size = atoi(optarg);
627 shm_size = buf_size * 1024;
630 circular_logging = TRUE;
641 #ifdef CONFIG_FEATURE_REMOTE_LOG
642 /* If they have not specified remote logging, then log locally */
643 if (doRemoteLog == FALSE)
644 local_logging = TRUE;
648 /* Store away localhost's name before the fork */
649 gethostname(LocalHostName, sizeof(LocalHostName));
650 if ((p = strchr(LocalHostName, '.'))) {
656 if (doFork == TRUE) {
658 vfork_daemon_rexec(0, 1, argc, argv, "-n");