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 /* level of messages to be locally logged */
46 static int logLevel = 8;
48 /* localhost's name */
49 static char LocalHostName[64];
51 #ifdef CONFIG_FEATURE_REMOTE_LOG
52 #include <netinet/in.h>
53 /* udp socket for logging to remote host */
54 static int remotefd = -1;
55 static struct sockaddr_in remoteaddr;
57 /* where do we log? */
58 static char *RemoteHost;
60 /* what port to log to? */
61 static int RemotePort = 514;
67 #define SYSLOG_OPT_small (1)
68 #define SYSLOG_OPT_remotelog (2)
69 #define SYSLOG_OPT_locallog (4)
70 #define SYSLOG_OPT_circularlog (8)
72 #define MAXLINE 1024 /* maximum line length */
75 /* circular buffer variables/structures */
76 #ifdef CONFIG_FEATURE_IPC_SYSLOG
78 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
79 #error Sorry, you must set the syslogd buffer size to at least 4KB.
80 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
88 static const long KEY_ID = 0x414e4547; /*"GENA" */
90 // Semaphore operation structures
91 static struct shbuf_ds {
92 int size; // size of data written
93 int head; // start of message list
94 int tail; // end of message list
95 char data[1]; // data/messages
96 } *buf = NULL; // shared memory pointer
98 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
99 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
101 static int shmid = -1; // ipc shared memory id
102 static int s_semid = -1; // ipc semaphore id
103 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
105 static void ipcsyslog_cleanup(void)
107 printf("Exiting Syslogd!\n");
113 shmctl(shmid, IPC_RMID, NULL);
116 semctl(s_semid, 0, IPC_RMID, 0);
120 static void ipcsyslog_init(void)
123 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
124 bb_perror_msg_and_die("shmget");
127 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
128 bb_perror_msg_and_die("shmat");
131 buf->size = shm_size - sizeof(*buf);
132 buf->head = buf->tail = 0;
134 // we'll trust the OS to set initial semval to 0 (let's hope)
135 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
136 if (errno == EEXIST) {
137 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
138 bb_perror_msg_and_die("semget");
141 bb_perror_msg_and_die("semget");
145 printf("Buffer already allocated just grab the semaphore?");
149 /* write message to buffer */
150 static void circ_message(const char *msg)
152 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
153 const char * const fail_msg = "Can't find the terminator token%s?\n";
155 if (semop(s_semid, SMwdn, 3) == -1) {
156 bb_perror_msg_and_die("SMwdn");
160 * Circular Buffer Algorithm:
161 * --------------------------
163 * Start-off w/ empty buffer of specific size SHM_SIZ
164 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
165 * This is also very handy since we can do printf on message.
167 * Once the buffer is full we need to get rid of the first message in buffer and
168 * insert the new message. (Note: if the message being added is >1 message then
169 * we will need to "remove" >1 old message from the buffer). The way this is done
171 * When we reach the end of the buffer we set a mark and start from the beginning.
172 * Now what about the beginning and end of the buffer? Well we have the "head"
173 * index/pointer which is the starting point for the messages and we have "tail"
174 * index/pointer which is the ending point for the messages. When we "display" the
175 * messages we start from the beginning and continue until we reach "tail". If we
176 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
177 * "tail" are actually offsets from the beginning of the buffer.
179 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
180 * a threadsafe way of handling shared memory operations.
182 if ((buf->tail + l) < buf->size) {
183 /* before we append the message we need to check the HEAD so that we won't
184 overwrite any of the message that we still need and adjust HEAD to point
185 to the next message! */
186 if (buf->tail < buf->head) {
187 if ((buf->tail + l) >= buf->head) {
188 /* we need to move the HEAD to point to the next message
189 * Theoretically we have enough room to add the whole message to the
190 * buffer, because of the first outer IF statement, so we don't have
191 * to worry about overflows here!
193 int k = buf->tail + l - buf->head; /* we need to know how many bytes
194 we are overwriting to make
197 memchr(buf->data + buf->head + k, '\0',
198 buf->size - (buf->head + k));
199 if (c != NULL) { /* do a sanity check just in case! */
200 buf->head = c - buf->data + 1; /* we need to convert pointer to
201 offset + skip the '\0' since
202 we need to point to the beginning
203 of the next message */
204 /* Note: HEAD is only used to "retrieve" messages, it's not used
205 when writing messages into our buffer */
206 } else { /* show an error message to know we messed up? */
213 /* in other cases no overflows have been done yet, so we don't care! */
214 /* we should be ok to append the message now */
215 strncpy(buf->data + buf->tail, msg, l); /* append our message */
216 buf->tail += l; /* count full message w/ '\0' terminating char */
218 /* we need to break up the message and "circle" it around */
220 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
222 /* We need to move HEAD! This is always the case since we are going
223 * to "circle" the message.
225 c = memchr(buf->data + k, '\0', buf->size - k);
227 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
228 /* move head pointer */
229 buf->head = c - buf->data + 1;
231 /* now write the first part of the message */
232 strncpy(buf->data + buf->tail, msg, l - k - 1);
234 /* ALWAYS terminate end of buffer w/ '\0' */
235 buf->data[buf->size - 1] = '\0';
237 /* now write out the rest of the string to the beginning of the buffer */
238 strcpy(buf->data, &msg[l - k - 1]);
240 /* we need to place the TAIL at the end of the message */
243 printf(fail_msg, " from the beginning");
244 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
248 if (semop(s_semid, SMwup, 1) == -1) {
249 bb_perror_msg_and_die("SMwup");
254 void ipcsyslog_cleanup(void);
255 void ipcsyslog_init(void);
256 void circ_message(const char *msg);
257 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
259 /* Note: There is also a function called "message()" in init.c */
260 /* Print a message to the log file. */
261 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
262 static void message(char *fmt, ...)
268 fl.l_whence = SEEK_SET;
272 #ifdef CONFIG_FEATURE_IPC_SYSLOG
273 if ((opts & SYSLOG_OPT_circularlog) && (buf != NULL)) {
276 va_start(arguments, fmt);
277 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
283 fd = device_open(logFilePath, O_WRONLY | O_CREAT
284 | O_NOCTTY | O_APPEND | O_NONBLOCK);
287 fcntl(fd, F_SETLKW, &fl);
289 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
290 if (ENABLE_FEATURE_ROTATE_LOGFILE && logFileSize > 0 ) {
292 int r = fstat(fd, &statf);
293 if( !r && (statf.st_mode & S_IFREG)
294 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
295 if(logFileRotate > 0) {
297 char oldFile[(strlen(logFilePath)+4)];
298 char newFile[(strlen(logFilePath)+4)];
299 for(i=logFileRotate-1;i>0;i--) {
300 sprintf(oldFile, "%s.%d", logFilePath, i-1);
301 sprintf(newFile, "%s.%d", logFilePath, i);
302 rename(oldFile, newFile);
304 sprintf(newFile, "%s.%d", logFilePath, 0);
306 fcntl (fd, F_SETLKW, &fl);
308 rename(logFilePath, newFile);
309 fd = device_open (logFilePath,
310 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
313 fcntl (fd, F_SETLKW, &fl);
320 va_start(arguments, fmt);
321 vdprintf(fd, fmt, arguments);
324 fcntl(fd, F_SETLKW, &fl);
327 /* Always send console messages to /dev/console so people will see them. */
328 fd = device_open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
330 va_start(arguments, fmt);
331 vdprintf(fd, fmt, arguments);
335 fprintf(stderr, "Bummer, can't print: ");
336 va_start(arguments, fmt);
337 vfprintf(stderr, fmt, arguments);
344 #ifdef CONFIG_FEATURE_REMOTE_LOG
345 static void init_RemoteLog(void)
347 memset(&remoteaddr, 0, sizeof(remoteaddr));
348 remotefd = xsocket(AF_INET, SOCK_DGRAM, 0);
349 remoteaddr.sin_family = AF_INET;
350 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
351 remoteaddr.sin_port = htons(RemotePort);
354 void init_RemoteLog(void);
357 static void logMessage(int pri, char *msg)
365 for (c_fac = facilitynames;
366 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
367 for (c_pri = prioritynames;
368 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
369 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
370 snprintf(res, sizeof(res), "<%d>", pri);
372 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
376 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
377 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
379 timestamp = ctime(&now) + 4;
380 timestamp[15] = '\0';
383 timestamp[15] = '\0';
387 /* todo: supress duplicates */
389 #ifdef CONFIG_FEATURE_REMOTE_LOG
390 if (opts & SYSLOG_OPT_remotelog) {
391 char line[MAXLINE + 1];
392 /* trying connect the socket */
393 if (-1 == remotefd) {
397 /* if we have a valid socket, send the message */
398 if (-1 != remotefd) {
400 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
403 /* send message to remote logger */
404 if(( -1 == sendto(remotefd, line, strlen(line), 0,
405 (struct sockaddr *) &remoteaddr,
406 sizeof(remoteaddr))) && (errno == EINTR)) {
407 /* sleep now seconds and retry (with now * 2) */
415 if (opts & SYSLOG_OPT_locallog)
418 /* now spew out the message to wherever it is supposed to go */
419 if (pri == 0 || LOG_PRI(pri) < logLevel) {
420 if (opts & SYSLOG_OPT_small)
421 message("%s %s\n", timestamp, msg);
423 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
428 static void quit_signal(int sig)
430 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
432 if (ENABLE_FEATURE_IPC_SYSLOG)
438 static void domark(int sig)
440 if (MarkInterval > 0) {
441 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
446 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
447 * enabled, we otherwise get a "storage size isn't constant error. */
448 static int serveConnection(char *tmpbuf, int n_read)
452 while (p < tmpbuf + n_read) {
454 int pri = (LOG_USER | LOG_NOTICE);
456 char line[MAXLINE + 1];
460 while ((c = *p) && q < &line[sizeof(line) - 1]) {
461 if (c == '<' && num_lt == 0) {
462 /* Parse the magic priority number. */
465 while (isdigit(*(++p))) {
466 pri = 10 * pri + (*p - '0');
468 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
469 pri = (LOG_USER | LOG_NOTICE);
471 } else if (c == '\n') {
473 } else if (iscntrl(c) && (c < 0177)) {
484 logMessage(pri, line);
489 static void doSyslogd(void) ATTRIBUTE_NORETURN;
490 static void doSyslogd(void)
492 struct sockaddr_un sunx;
493 socklen_t addrLength;
498 /* Set up signal handlers. */
499 signal(SIGINT, quit_signal);
500 signal(SIGTERM, quit_signal);
501 signal(SIGQUIT, quit_signal);
502 signal(SIGHUP, SIG_IGN);
503 signal(SIGCHLD, SIG_IGN);
505 signal(SIGCLD, SIG_IGN);
507 signal(SIGALRM, domark);
510 /* Create the syslog file so realpath() can work. */
511 if (realpath(_PATH_LOG, lfile) != NULL) {
515 memset(&sunx, 0, sizeof(sunx));
516 sunx.sun_family = AF_UNIX;
517 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
518 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
519 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
520 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
521 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
524 if (chmod(lfile, 0666) < 0) {
525 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
527 if (ENABLE_FEATURE_IPC_SYSLOG && opts & SYSLOG_OPT_circularlog) {
531 if (ENABLE_FEATURE_REMOTE_LOG && opts & SYSLOG_OPT_remotelog) {
535 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
540 FD_SET(sock_fd, &fds);
542 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
543 if (errno == EINTR) {
544 /* alarm may have happened. */
547 bb_perror_msg_and_die("select error");
550 if (FD_ISSET(sock_fd, &fds)) {
553 # define TMP_BUF_SZ BUFSIZ
555 # define TMP_BUF_SZ MAXLINE
557 #define tmpbuf bb_common_bufsiz1
559 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
561 serveConnection(tmpbuf, i);
563 bb_perror_msg_and_die("UNIX socket error");
566 } /* for main loop */
569 int syslogd_main(int argc, char **argv)
577 /* do normal option parsing */
578 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
581 MarkInterval = atoi(optarg) * 60;
587 logFilePath = optarg;
590 logLevel = atoi(optarg);
591 /* Valid levels are between 1 and 8 */
592 if (logLevel < 1 || logLevel > 8) {
596 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
598 logFileSize = atoi(optarg) * 1024;
601 logFileRotate = atoi(optarg);
602 if( logFileRotate > 99 ) logFileRotate = 99;
605 #ifdef CONFIG_FEATURE_REMOTE_LOG
607 RemoteHost = xstrdup(optarg);
608 if ((p = strchr(RemoteHost, ':'))) {
609 RemotePort = atoi(p + 1);
612 opts |= SYSLOG_OPT_remotelog;
615 opts |= SYSLOG_OPT_locallog;
618 #ifdef CONFIG_FEATURE_IPC_SYSLOG
621 int buf_size = atoi(optarg);
623 shm_size = buf_size * 1024;
626 opts |= SYSLOG_OPT_circularlog;
630 opts |= SYSLOG_OPT_small;
637 /* If they have not specified remote logging, then log locally */
638 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & SYSLOG_OPT_remotelog))
639 opts |= SYSLOG_OPT_locallog;
642 /* Store away localhost's name before the fork */
643 gethostname(LocalHostName, sizeof(LocalHostName));
644 if ((p = strchr(LocalHostName, '.'))) {
650 if (doFork == TRUE) {
652 vfork_daemon_rexec(0, 1, argc, argv, "-n");