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.
30 #include <sys/socket.h>
31 #include <sys/types.h>
33 #include <sys/param.h>
37 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
39 #include <sys/syslog.h>
42 /* Path for the file where all log messages are written */
43 #define __LOG_FILE "/var/log/messages"
45 /* Path to the unix socket */
46 static char lfile[MAXPATHLEN];
48 static const char *logFilePath = __LOG_FILE;
50 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
51 /* max size of message file before being rotated */
52 static int logFileSize = 200 * 1024;
54 /* number of rotated message files */
55 static int logFileRotate = 1;
58 /* interval between marks in seconds */
59 static int MarkInterval = 20 * 60;
61 /* localhost's name */
62 static char LocalHostName[64];
64 #ifdef CONFIG_FEATURE_REMOTE_LOG
65 #include <netinet/in.h>
66 /* udp socket for logging to remote host */
67 static int remotefd = -1;
68 static struct sockaddr_in remoteaddr;
70 /* where do we log? */
71 static char *RemoteHost;
73 /* what port to log to? */
74 static int RemotePort = 514;
76 /* To remote log or not to remote log, that is the question. */
77 static int doRemoteLog = FALSE;
78 static int local_logging = FALSE;
81 /* Make loging output smaller. */
82 static bool small = false;
85 #define MAXLINE 1024 /* maximum line length */
88 /* circular buffer variables/structures */
89 #ifdef CONFIG_FEATURE_IPC_SYSLOG
91 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
92 #error Sorry, you must set the syslogd buffer size to at least 4KB.
93 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
101 static const long KEY_ID = 0x414e4547; /*"GENA" */
103 // Semaphore operation structures
104 static struct shbuf_ds {
105 int size; // size of data written
106 int head; // start of message list
107 int tail; // end of message list
108 char data[1]; // data/messages
109 } *buf = NULL; // shared memory pointer
111 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
112 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
114 static int shmid = -1; // ipc shared memory id
115 static int s_semid = -1; // ipc semaphore id
116 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
117 static int circular_logging = FALSE;
120 * sem_up - up()'s a semaphore.
122 static inline void sem_up(int semid)
124 if (semop(semid, SMwup, 1) == -1) {
125 bb_perror_msg_and_die("semop[SMwup]");
130 * sem_down - down()'s a semaphore
132 static inline void sem_down(int semid)
134 if (semop(semid, SMwdn, 3) == -1) {
135 bb_perror_msg_and_die("semop[SMwdn]");
140 static void ipcsyslog_cleanup(void)
142 printf("Exiting Syslogd!\n");
148 shmctl(shmid, IPC_RMID, NULL);
151 semctl(s_semid, 0, IPC_RMID, 0);
155 static void ipcsyslog_init(void)
158 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
159 bb_perror_msg_and_die("shmget");
162 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
163 bb_perror_msg_and_die("shmat");
166 buf->size = shm_size - sizeof(*buf);
167 buf->head = buf->tail = 0;
169 // we'll trust the OS to set initial semval to 0 (let's hope)
170 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
171 if (errno == EEXIST) {
172 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
173 bb_perror_msg_and_die("semget");
176 bb_perror_msg_and_die("semget");
180 printf("Buffer already allocated just grab the semaphore?");
184 /* write message to buffer */
185 static void circ_message(const char *msg)
187 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
192 * Circular Buffer Algorithm:
193 * --------------------------
195 * Start-off w/ empty buffer of specific size SHM_SIZ
196 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
197 * This is also very handy since we can do printf on message.
199 * Once the buffer is full we need to get rid of the first message in buffer and
200 * insert the new message. (Note: if the message being added is >1 message then
201 * we will need to "remove" >1 old message from the buffer). The way this is done
203 * When we reach the end of the buffer we set a mark and start from the beginning.
204 * Now what about the beginning and end of the buffer? Well we have the "head"
205 * index/pointer which is the starting point for the messages and we have "tail"
206 * index/pointer which is the ending point for the messages. When we "display" the
207 * messages we start from the beginning and continue until we reach "tail". If we
208 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
209 * "tail" are actually offsets from the beginning of the buffer.
211 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
212 * a threasafe way of handling shared memory operations.
214 if ((buf->tail + l) < buf->size) {
215 /* before we append the message we need to check the HEAD so that we won't
216 overwrite any of the message that we still need and adjust HEAD to point
217 to the next message! */
218 if (buf->tail < buf->head) {
219 if ((buf->tail + l) >= buf->head) {
220 /* we need to move the HEAD to point to the next message
221 * Theoretically we have enough room to add the whole message to the
222 * buffer, because of the first outer IF statement, so we don't have
223 * to worry about overflows here!
225 int k = buf->tail + l - buf->head; /* we need to know how many bytes
226 we are overwriting to make
229 memchr(buf->data + buf->head + k, '\0',
230 buf->size - (buf->head + k));
231 if (c != NULL) { /* do a sanity check just in case! */
232 buf->head = c - buf->data + 1; /* we need to convert pointer to
233 offset + skip the '\0' since
234 we need to point to the beginning
235 of the next message */
236 /* Note: HEAD is only used to "retrieve" messages, it's not used
237 when writing messages into our buffer */
238 } else { /* show an error message to know we messed up? */
239 printf("Weird! Can't find the terminator token??? \n");
245 /* in other cases no overflows have been done yet, so we don't care! */
246 /* we should be ok to append the message now */
247 strncpy(buf->data + buf->tail, msg, l); /* append our message */
248 buf->tail += l; /* count full message w/ '\0' terminating char */
250 /* we need to break up the message and "circle" it around */
252 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
254 /* We need to move HEAD! This is always the case since we are going
255 * to "circle" the message.
257 c = memchr(buf->data + k, '\0', buf->size - k);
259 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
260 /* move head pointer */
261 buf->head = c - buf->data + 1;
263 /* now write the first part of the message */
264 strncpy(buf->data + buf->tail, msg, l - k - 1);
266 /* ALWAYS terminate end of buffer w/ '\0' */
267 buf->data[buf->size - 1] = '\0';
269 /* now write out the rest of the string to the beginning of the buffer */
270 strcpy(buf->data, &msg[l - k - 1]);
272 /* we need to place the TAIL at the end of the message */
276 ("Weird! Can't find the terminator token from the beginning??? \n");
277 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
283 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
285 /* Note: There is also a function called "message()" in init.c */
286 /* Print a message to the log file. */
287 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
288 static void message(char *fmt, ...)
294 fl.l_whence = SEEK_SET;
298 #ifdef CONFIG_FEATURE_IPC_SYSLOG
299 if ((circular_logging == TRUE) && (buf != NULL)) {
302 va_start(arguments, fmt);
303 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
309 if ((fd = device_open(logFilePath,
310 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
313 fcntl(fd, F_SETLKW, &fl);
314 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
315 if ( logFileSize > 0 ) {
317 int r = fstat(fd, &statf);
318 if( !r && (statf.st_mode & S_IFREG)
319 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
320 if(logFileRotate > 0) {
322 char oldFile[(strlen(logFilePath)+4)], newFile[(strlen(logFilePath)+4)];
323 for(i=logFileRotate-1;i>0;i--) {
324 sprintf(oldFile, "%s.%d", logFilePath, i-1);
325 sprintf(newFile, "%s.%d", logFilePath, i);
326 rename(oldFile, newFile);
328 sprintf(newFile, "%s.%d", logFilePath, 0);
330 fcntl (fd, F_SETLKW, &fl);
332 rename(logFilePath, newFile);
333 fd = device_open (logFilePath,
334 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
337 fcntl (fd, F_SETLKW, &fl);
344 va_start(arguments, fmt);
345 vdprintf(fd, fmt, arguments);
348 fcntl(fd, F_SETLKW, &fl);
351 /* Always send console messages to /dev/console so people will see them. */
352 if ((fd = device_open(_PATH_CONSOLE,
353 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
354 va_start(arguments, fmt);
355 vdprintf(fd, fmt, arguments);
359 fprintf(stderr, "Bummer, can't print: ");
360 va_start(arguments, fmt);
361 vfprintf(stderr, fmt, arguments);
368 #ifdef CONFIG_FEATURE_REMOTE_LOG
369 static void init_RemoteLog(void)
371 memset(&remoteaddr, 0, sizeof(remoteaddr));
372 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
375 bb_error_msg("cannot create socket");
378 remoteaddr.sin_family = AF_INET;
379 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
380 remoteaddr.sin_port = htons(RemotePort);
384 static void logMessage(int pri, char *msg)
389 #ifdef CONFIG_FEATURE_REMOTE_LOG
390 static char line[MAXLINE + 1];
395 for (c_fac = facilitynames;
396 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
397 for (c_pri = prioritynames;
398 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
399 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
400 snprintf(res, sizeof(res), "<%d>", pri);
402 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
406 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
407 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
409 timestamp = ctime(&now) + 4;
410 timestamp[15] = '\0';
413 timestamp[15] = '\0';
417 /* todo: supress duplicates */
419 #ifdef CONFIG_FEATURE_REMOTE_LOG
420 if (doRemoteLog == TRUE) {
421 /* trying connect the socket */
422 if (-1 == remotefd) {
426 /* if we have a valid socket, send the message */
427 if (-1 != remotefd) {
429 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
432 /* send message to remote logger */
433 if(( -1 == sendto(remotefd, line, strlen(line), 0,
434 (struct sockaddr *) &remoteaddr,
435 sizeof(remoteaddr))) && (errno == EINTR)) {
436 /* sleep now seconds and retry (with now * 2) */
444 if (local_logging == TRUE)
447 /* now spew out the message to wherever it is supposed to go */
449 message("%s %s\n", timestamp, msg);
451 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
455 static void quit_signal(int sig)
457 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
459 #ifdef CONFIG_FEATURE_IPC_SYSLOG
466 static void domark(int sig)
468 if (MarkInterval > 0) {
469 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
474 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
475 * enabled, we otherwise get a "storage size isn't constant error. */
476 static int serveConnection(char *tmpbuf, int n_read)
480 while (p < tmpbuf + n_read) {
482 int pri = (LOG_USER | LOG_NOTICE);
484 char line[MAXLINE + 1];
488 while ((c = *p) && q < &line[sizeof(line) - 1]) {
489 if (c == '<' && num_lt == 0) {
490 /* Parse the magic priority number. */
493 while (isdigit(*(++p))) {
494 pri = 10 * pri + (*p - '0');
496 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
497 pri = (LOG_USER | LOG_NOTICE);
499 } else if (c == '\n') {
501 } else if (iscntrl(c) && (c < 0177)) {
512 logMessage(pri, line);
517 static void doSyslogd(void) ATTRIBUTE_NORETURN;
518 static void doSyslogd(void)
520 struct sockaddr_un sunx;
521 socklen_t addrLength;
526 /* Set up signal handlers. */
527 signal(SIGINT, quit_signal);
528 signal(SIGTERM, quit_signal);
529 signal(SIGQUIT, quit_signal);
530 signal(SIGHUP, SIG_IGN);
531 signal(SIGCHLD, SIG_IGN);
533 signal(SIGCLD, SIG_IGN);
535 signal(SIGALRM, domark);
538 /* Create the syslog file so realpath() can work. */
539 if (realpath(_PATH_LOG, lfile) != NULL) {
543 memset(&sunx, 0, sizeof(sunx));
544 sunx.sun_family = AF_UNIX;
545 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
546 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
547 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
551 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
552 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
553 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
556 if (chmod(lfile, 0666) < 0) {
557 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
559 #ifdef CONFIG_FEATURE_IPC_SYSLOG
560 if (circular_logging == TRUE) {
565 #ifdef CONFIG_FEATURE_REMOTE_LOG
566 if (doRemoteLog == TRUE) {
571 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " "BusyBox v" BB_VER );
576 FD_SET(sock_fd, &fds);
578 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
579 if (errno == EINTR) {
580 /* alarm may have happened. */
583 bb_perror_msg_and_die("select error");
586 if (FD_ISSET(sock_fd, &fds)) {
589 # define TMP_BUF_SZ BUFSIZ
591 # define TMP_BUF_SZ MAXLINE
593 #define tmpbuf bb_common_bufsiz1
595 if ((i = recv(sock_fd, tmpbuf, TMP_BUF_SZ, 0)) > 0) {
597 serveConnection(tmpbuf, i);
599 bb_perror_msg_and_die("UNIX socket error");
602 } /* for main loop */
605 int syslogd_main(int argc, char **argv)
613 /* do normal option parsing */
614 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
617 MarkInterval = atoi(optarg) * 60;
623 logFilePath = optarg;
625 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
627 logFileSize = atoi(optarg) * 1024;
630 logFileRotate = atoi(optarg);
631 if( logFileRotate > 99 ) logFileRotate = 99;
634 #ifdef CONFIG_FEATURE_REMOTE_LOG
636 RemoteHost = bb_xstrdup(optarg);
637 if ((p = strchr(RemoteHost, ':'))) {
638 RemotePort = atoi(p + 1);
644 local_logging = TRUE;
647 #ifdef CONFIG_FEATURE_IPC_SYSLOG
650 int buf_size = atoi(optarg);
652 shm_size = buf_size * 1024;
655 circular_logging = TRUE;
666 #ifdef CONFIG_FEATURE_REMOTE_LOG
667 /* If they have not specified remote logging, then log locally */
668 if (doRemoteLog == FALSE)
669 local_logging = TRUE;
673 /* Store away localhost's name before the fork */
674 gethostname(LocalHostName, sizeof(LocalHostName));
675 if ((p = strchr(LocalHostName, '.'))) {
681 if (doFork == TRUE) {
682 #if defined(__uClinux__)
683 vfork_daemon_rexec(0, 1, argc, argv, "-n");
684 #else /* __uClinux__ */
686 bb_perror_msg_and_die("daemon");
687 #endif /* __uClinux__ */
696 c-file-style: "linux"