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;
64 #define SYSLOG_OPT_small (1)
65 #define SYSLOG_OPT_remotelog (2)
66 #define SYSLOG_OPT_locallog (4)
67 #define SYSLOG_OPT_circularlog (8)
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
102 static void ipcsyslog_cleanup(void)
104 printf("Exiting Syslogd!\n");
110 shmctl(shmid, IPC_RMID, NULL);
113 semctl(s_semid, 0, IPC_RMID, 0);
117 static void ipcsyslog_init(void)
120 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
121 bb_perror_msg_and_die("shmget");
124 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
125 bb_perror_msg_and_die("shmat");
128 buf->size = shm_size - sizeof(*buf);
129 buf->head = buf->tail = 0;
131 // we'll trust the OS to set initial semval to 0 (let's hope)
132 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
133 if (errno == EEXIST) {
134 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
135 bb_perror_msg_and_die("semget");
138 bb_perror_msg_and_die("semget");
142 printf("Buffer already allocated just grab the semaphore?");
146 /* write message to buffer */
147 static void circ_message(const char *msg)
149 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
150 const char * const fail_msg = "Can't find the terminator token%s?\n";
152 if (semop(s_semid, SMwdn, 3) == -1) {
153 bb_perror_msg_and_die("SMwdn");
157 * Circular Buffer Algorithm:
158 * --------------------------
160 * Start-off w/ empty buffer of specific size SHM_SIZ
161 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
162 * This is also very handy since we can do printf on message.
164 * Once the buffer is full we need to get rid of the first message in buffer and
165 * insert the new message. (Note: if the message being added is >1 message then
166 * we will need to "remove" >1 old message from the buffer). The way this is done
168 * When we reach the end of the buffer we set a mark and start from the beginning.
169 * Now what about the beginning and end of the buffer? Well we have the "head"
170 * index/pointer which is the starting point for the messages and we have "tail"
171 * index/pointer which is the ending point for the messages. When we "display" the
172 * messages we start from the beginning and continue until we reach "tail". If we
173 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
174 * "tail" are actually offsets from the beginning of the buffer.
176 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
177 * a threadsafe way of handling shared memory operations.
179 if ((buf->tail + l) < buf->size) {
180 /* before we append the message we need to check the HEAD so that we won't
181 overwrite any of the message that we still need and adjust HEAD to point
182 to the next message! */
183 if (buf->tail < buf->head) {
184 if ((buf->tail + l) >= buf->head) {
185 /* we need to move the HEAD to point to the next message
186 * Theoretically we have enough room to add the whole message to the
187 * buffer, because of the first outer IF statement, so we don't have
188 * to worry about overflows here!
190 int k = buf->tail + l - buf->head; /* we need to know how many bytes
191 we are overwriting to make
194 memchr(buf->data + buf->head + k, '\0',
195 buf->size - (buf->head + k));
196 if (c != NULL) { /* do a sanity check just in case! */
197 buf->head = c - buf->data + 1; /* we need to convert pointer to
198 offset + skip the '\0' since
199 we need to point to the beginning
200 of the next message */
201 /* Note: HEAD is only used to "retrieve" messages, it's not used
202 when writing messages into our buffer */
203 } else { /* show an error message to know we messed up? */
210 /* in other cases no overflows have been done yet, so we don't care! */
211 /* we should be ok to append the message now */
212 strncpy(buf->data + buf->tail, msg, l); /* append our message */
213 buf->tail += l; /* count full message w/ '\0' terminating char */
215 /* we need to break up the message and "circle" it around */
217 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
219 /* We need to move HEAD! This is always the case since we are going
220 * to "circle" the message.
222 c = memchr(buf->data + k, '\0', buf->size - k);
224 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
225 /* move head pointer */
226 buf->head = c - buf->data + 1;
228 /* now write the first part of the message */
229 strncpy(buf->data + buf->tail, msg, l - k - 1);
231 /* ALWAYS terminate end of buffer w/ '\0' */
232 buf->data[buf->size - 1] = '\0';
234 /* now write out the rest of the string to the beginning of the buffer */
235 strcpy(buf->data, &msg[l - k - 1]);
237 /* we need to place the TAIL at the end of the message */
240 printf(fail_msg, " from the beginning");
241 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
245 if (semop(s_semid, SMwup, 1) == -1) {
246 bb_perror_msg_and_die("SMwup");
251 void ipcsyslog_cleanup(void);
252 void ipcsyslog_init(void);
253 void circ_message(const char *msg);
254 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
256 /* Note: There is also a function called "message()" in init.c */
257 /* Print a message to the log file. */
258 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
259 static void message(char *fmt, ...)
265 fl.l_whence = SEEK_SET;
269 #ifdef CONFIG_FEATURE_IPC_SYSLOG
270 if ((opts & SYSLOG_OPT_circularlog) && (buf != NULL)) {
273 va_start(arguments, fmt);
274 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
280 if ((fd = device_open(logFilePath,
281 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
284 fcntl(fd, F_SETLKW, &fl);
286 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
287 if (ENABLE_FEATURE_ROTATE_LOGFILE && logFileSize > 0 ) {
289 int r = fstat(fd, &statf);
290 if( !r && (statf.st_mode & S_IFREG)
291 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
292 if(logFileRotate > 0) {
294 char oldFile[(strlen(logFilePath)+4)], newFile[(strlen(logFilePath)+4)];
295 for(i=logFileRotate-1;i>0;i--) {
296 sprintf(oldFile, "%s.%d", logFilePath, i-1);
297 sprintf(newFile, "%s.%d", logFilePath, i);
298 rename(oldFile, newFile);
300 sprintf(newFile, "%s.%d", logFilePath, 0);
302 fcntl (fd, F_SETLKW, &fl);
304 rename(logFilePath, newFile);
305 fd = device_open (logFilePath,
306 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
309 fcntl (fd, F_SETLKW, &fl);
316 va_start(arguments, fmt);
317 vdprintf(fd, fmt, arguments);
320 fcntl(fd, F_SETLKW, &fl);
323 /* Always send console messages to /dev/console so people will see them. */
324 if ((fd = device_open(_PATH_CONSOLE,
325 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
326 va_start(arguments, fmt);
327 vdprintf(fd, fmt, arguments);
331 fprintf(stderr, "Bummer, can't print: ");
332 va_start(arguments, fmt);
333 vfprintf(stderr, fmt, arguments);
340 #ifdef CONFIG_FEATURE_REMOTE_LOG
341 static void init_RemoteLog(void)
343 memset(&remoteaddr, 0, sizeof(remoteaddr));
344 remotefd = xsocket(AF_INET, SOCK_DGRAM, 0);
345 remoteaddr.sin_family = AF_INET;
346 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
347 remoteaddr.sin_port = htons(RemotePort);
350 void init_RemoteLog(void);
353 static void logMessage(int pri, char *msg)
361 for (c_fac = facilitynames;
362 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
363 for (c_pri = prioritynames;
364 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
365 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
366 snprintf(res, sizeof(res), "<%d>", pri);
368 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
372 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
373 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
375 timestamp = ctime(&now) + 4;
376 timestamp[15] = '\0';
379 timestamp[15] = '\0';
383 /* todo: supress duplicates */
385 #ifdef CONFIG_FEATURE_REMOTE_LOG
386 if (opts & SYSLOG_OPT_remotelog) {
387 char line[MAXLINE + 1];
388 /* trying connect the socket */
389 if (-1 == remotefd) {
393 /* if we have a valid socket, send the message */
394 if (-1 != remotefd) {
396 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
399 /* send message to remote logger */
400 if(( -1 == sendto(remotefd, line, strlen(line), 0,
401 (struct sockaddr *) &remoteaddr,
402 sizeof(remoteaddr))) && (errno == EINTR)) {
403 /* sleep now seconds and retry (with now * 2) */
411 if (opts & SYSLOG_OPT_locallog)
414 /* now spew out the message to wherever it is supposed to go */
415 if (opts & SYSLOG_OPT_small)
416 message("%s %s\n", timestamp, msg);
418 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
422 static void quit_signal(int sig)
424 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
426 if (ENABLE_FEATURE_IPC_SYSLOG)
432 static void domark(int sig)
434 if (MarkInterval > 0) {
435 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
440 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
441 * enabled, we otherwise get a "storage size isn't constant error. */
442 static int serveConnection(char *tmpbuf, int n_read)
446 while (p < tmpbuf + n_read) {
448 int pri = (LOG_USER | LOG_NOTICE);
450 char line[MAXLINE + 1];
454 while ((c = *p) && q < &line[sizeof(line) - 1]) {
455 if (c == '<' && num_lt == 0) {
456 /* Parse the magic priority number. */
459 while (isdigit(*(++p))) {
460 pri = 10 * pri + (*p - '0');
462 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
463 pri = (LOG_USER | LOG_NOTICE);
465 } else if (c == '\n') {
467 } else if (iscntrl(c) && (c < 0177)) {
478 logMessage(pri, line);
483 static void doSyslogd(void) ATTRIBUTE_NORETURN;
484 static void doSyslogd(void)
486 struct sockaddr_un sunx;
487 socklen_t addrLength;
492 /* Set up signal handlers. */
493 signal(SIGINT, quit_signal);
494 signal(SIGTERM, quit_signal);
495 signal(SIGQUIT, quit_signal);
496 signal(SIGHUP, SIG_IGN);
497 signal(SIGCHLD, SIG_IGN);
499 signal(SIGCLD, SIG_IGN);
501 signal(SIGALRM, domark);
504 /* Create the syslog file so realpath() can work. */
505 if (realpath(_PATH_LOG, lfile) != NULL) {
509 memset(&sunx, 0, sizeof(sunx));
510 sunx.sun_family = AF_UNIX;
511 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
512 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
513 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
514 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
515 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
518 if (chmod(lfile, 0666) < 0) {
519 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
521 if (ENABLE_FEATURE_IPC_SYSLOG && opts & SYSLOG_OPT_circularlog) {
525 if (ENABLE_FEATURE_REMOTE_LOG && opts & SYSLOG_OPT_remotelog) {
529 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
534 FD_SET(sock_fd, &fds);
536 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
537 if (errno == EINTR) {
538 /* alarm may have happened. */
541 bb_perror_msg_and_die("select error");
544 if (FD_ISSET(sock_fd, &fds)) {
547 # define TMP_BUF_SZ BUFSIZ
549 # define TMP_BUF_SZ MAXLINE
551 #define tmpbuf bb_common_bufsiz1
553 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
555 serveConnection(tmpbuf, i);
557 bb_perror_msg_and_die("UNIX socket error");
560 } /* for main loop */
563 int syslogd_main(int argc, char **argv)
571 /* do normal option parsing */
572 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
575 MarkInterval = atoi(optarg) * 60;
581 logFilePath = optarg;
583 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
585 logFileSize = atoi(optarg) * 1024;
588 logFileRotate = atoi(optarg);
589 if( logFileRotate > 99 ) logFileRotate = 99;
592 #ifdef CONFIG_FEATURE_REMOTE_LOG
594 RemoteHost = xstrdup(optarg);
595 if ((p = strchr(RemoteHost, ':'))) {
596 RemotePort = atoi(p + 1);
599 opts |= SYSLOG_OPT_remotelog;
602 opts |= SYSLOG_OPT_locallog;
605 #ifdef CONFIG_FEATURE_IPC_SYSLOG
608 int buf_size = atoi(optarg);
610 shm_size = buf_size * 1024;
613 opts |= SYSLOG_OPT_circularlog;
617 opts |= SYSLOG_OPT_small;
624 /* If they have not specified remote logging, then log locally */
625 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & SYSLOG_OPT_remotelog))
626 opts |= SYSLOG_OPT_locallog;
629 /* Store away localhost's name before the fork */
630 gethostname(LocalHostName, sizeof(LocalHostName));
631 if ((p = strchr(LocalHostName, '.'))) {
637 if (doFork == TRUE) {
639 vfork_daemon_rexec(0, 1, argc, argv, "-n");