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 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
43 #include <sys/socket.h>
44 #include <sys/types.h>
46 #include <sys/param.h>
50 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
52 #include <sys/syslog.h>
55 /* Path for the file where all log messages are written */
56 #define __LOG_FILE "/var/log/messages"
58 /* Path to the unix socket */
59 static char lfile[MAXPATHLEN];
61 static const char *logFilePath = __LOG_FILE;
63 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
64 /* max size of message file before being rotated */
65 static int logFileSize = 200 * 1024;
67 /* number of rotated message files */
68 static int logFileRotate = 1;
71 /* interval between marks in seconds */
72 static int MarkInterval = 20 * 60;
74 /* localhost's name */
75 static char LocalHostName[64];
77 #ifdef CONFIG_FEATURE_REMOTE_LOG
78 #include <netinet/in.h>
79 /* udp socket for logging to remote host */
80 static int remotefd = -1;
82 /* where do we log? */
83 static char *RemoteHost;
85 /* what port to log to? */
86 static int RemotePort = 514;
88 /* To remote log or not to remote log, that is the question. */
89 static int doRemoteLog = FALSE;
90 static int local_logging = FALSE;
93 /* Make loging output smaller. */
94 static bool small = false;
97 #define MAXLINE 1024 /* maximum line length */
100 /* circular buffer variables/structures */
101 #ifdef CONFIG_FEATURE_IPC_SYSLOG
103 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
104 #error Sorry, you must set the syslogd buffer size to at least 4KB.
105 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
113 static const long KEY_ID = 0x414e4547; /*"GENA" */
115 // Semaphore operation structures
116 static struct shbuf_ds {
117 int size; // size of data written
118 int head; // start of message list
119 int tail; // end of message list
120 char data[1]; // data/messages
121 } *buf = NULL; // shared memory pointer
123 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
124 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
126 static int shmid = -1; // ipc shared memory id
127 static int s_semid = -1; // ipc semaphore id
128 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
129 static int circular_logging = FALSE;
132 * sem_up - up()'s a semaphore.
134 static inline void sem_up(int semid)
136 if (semop(semid, SMwup, 1) == -1) {
137 bb_perror_msg_and_die("semop[SMwup]");
142 * sem_down - down()'s a semaphore
144 static inline void sem_down(int semid)
146 if (semop(semid, SMwdn, 3) == -1) {
147 bb_perror_msg_and_die("semop[SMwdn]");
152 void ipcsyslog_cleanup(void)
154 printf("Exiting Syslogd!\n");
160 shmctl(shmid, IPC_RMID, NULL);
163 semctl(s_semid, 0, IPC_RMID, 0);
167 void ipcsyslog_init(void)
170 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
171 bb_perror_msg_and_die("shmget");
174 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
175 bb_perror_msg_and_die("shmat");
178 buf->size = shm_size - sizeof(*buf);
179 buf->head = buf->tail = 0;
181 // we'll trust the OS to set initial semval to 0 (let's hope)
182 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
183 if (errno == EEXIST) {
184 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
185 bb_perror_msg_and_die("semget");
188 bb_perror_msg_and_die("semget");
192 printf("Buffer already allocated just grab the semaphore?");
196 /* write message to buffer */
197 void circ_message(const char *msg)
199 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
204 * Circular Buffer Algorithm:
205 * --------------------------
207 * Start-off w/ empty buffer of specific size SHM_SIZ
208 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
209 * This is also very handy since we can do printf on message.
211 * Once the buffer is full we need to get rid of the first message in buffer and
212 * insert the new message. (Note: if the message being added is >1 message then
213 * we will need to "remove" >1 old message from the buffer). The way this is done
215 * When we reach the end of the buffer we set a mark and start from the beginning.
216 * Now what about the beginning and end of the buffer? Well we have the "head"
217 * index/pointer which is the starting point for the messages and we have "tail"
218 * index/pointer which is the ending point for the messages. When we "display" the
219 * messages we start from the beginning and continue until we reach "tail". If we
220 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
221 * "tail" are actually offsets from the beginning of the buffer.
223 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
224 * a threasafe way of handling shared memory operations.
226 if ((buf->tail + l) < buf->size) {
227 /* before we append the message we need to check the HEAD so that we won't
228 overwrite any of the message that we still need and adjust HEAD to point
229 to the next message! */
230 if (buf->tail < buf->head) {
231 if ((buf->tail + l) >= buf->head) {
232 /* we need to move the HEAD to point to the next message
233 * Theoretically we have enough room to add the whole message to the
234 * buffer, because of the first outer IF statement, so we don't have
235 * to worry about overflows here!
237 int k = buf->tail + l - buf->head; /* we need to know how many bytes
238 we are overwriting to make
241 memchr(buf->data + buf->head + k, '\0',
242 buf->size - (buf->head + k));
243 if (c != NULL) { /* do a sanity check just in case! */
244 buf->head = c - buf->data + 1; /* we need to convert pointer to
245 offset + skip the '\0' since
246 we need to point to the beginning
247 of the next message */
248 /* Note: HEAD is only used to "retrieve" messages, it's not used
249 when writing messages into our buffer */
250 } else { /* show an error message to know we messed up? */
251 printf("Weird! Can't find the terminator token??? \n");
257 /* in other cases no overflows have been done yet, so we don't care! */
258 /* we should be ok to append the message now */
259 strncpy(buf->data + buf->tail, msg, l); /* append our message */
260 buf->tail += l; /* count full message w/ '\0' terminating char */
262 /* we need to break up the message and "circle" it around */
264 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
266 /* We need to move HEAD! This is always the case since we are going
267 * to "circle" the message.
269 c = memchr(buf->data + k, '\0', buf->size - k);
271 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
272 /* move head pointer */
273 buf->head = c - buf->data + 1;
275 /* now write the first part of the message */
276 strncpy(buf->data + buf->tail, msg, l - k - 1);
278 /* ALWAYS terminate end of buffer w/ '\0' */
279 buf->data[buf->size - 1] = '\0';
281 /* now write out the rest of the string to the beginning of the buffer */
282 strcpy(buf->data, &msg[l - k - 1]);
284 /* we need to place the TAIL at the end of the message */
288 ("Weird! Can't find the terminator token from the beginning??? \n");
289 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
295 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
297 /* Note: There is also a function called "message()" in init.c */
298 /* Print a message to the log file. */
299 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
300 static void message(char *fmt, ...)
306 fl.l_whence = SEEK_SET;
310 #ifdef CONFIG_FEATURE_IPC_SYSLOG
311 if ((circular_logging == TRUE) && (buf != NULL)) {
314 va_start(arguments, fmt);
315 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
322 device_open(logFilePath,
323 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
326 fcntl(fd, F_SETLKW, &fl);
327 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
328 if ( logFileSize > 0 ) {
330 int r = fstat(fd, &statf);
331 if( !r && (statf.st_mode & S_IFREG)
332 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
333 if(logFileRotate > 0) {
335 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
336 for(i=logFileRotate-1;i>0;i--) {
337 sprintf(oldFile, "%s.%d", logFilePath, i-1);
338 sprintf(newFile, "%s.%d", logFilePath, i);
339 rename(oldFile, newFile);
341 sprintf(newFile, "%s.%d", logFilePath, 0);
343 fcntl (fd, F_SETLKW, &fl);
345 rename(logFilePath, newFile);
346 fd = device_open (logFilePath,
347 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
350 fcntl (fd, F_SETLKW, &fl);
357 va_start(arguments, fmt);
358 vdprintf(fd, fmt, arguments);
361 fcntl(fd, F_SETLKW, &fl);
364 /* Always send console messages to /dev/console so people will see them. */
366 device_open(_PATH_CONSOLE,
367 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
368 va_start(arguments, fmt);
369 vdprintf(fd, fmt, arguments);
373 fprintf(stderr, "Bummer, can't print: ");
374 va_start(arguments, fmt);
375 vfprintf(stderr, fmt, arguments);
382 static void logMessage(int pri, char *msg)
386 static char res[20] = "";
390 for (c_fac = facilitynames;
391 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
392 for (c_pri = prioritynames;
393 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
394 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
395 snprintf(res, sizeof(res), "<%d>", pri);
397 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
401 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
402 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
404 timestamp = ctime(&now) + 4;
405 timestamp[15] = '\0';
408 timestamp[15] = '\0';
412 /* todo: supress duplicates */
414 #ifdef CONFIG_FEATURE_REMOTE_LOG
415 /* send message to remote logger */
416 if (-1 != remotefd) {
417 static const int IOV_COUNT = 2;
418 struct iovec iov[IOV_COUNT];
419 struct iovec *v = iov;
421 memset(&res, 0, sizeof(res));
422 snprintf(res, sizeof(res), "<%d>", pri);
424 v->iov_len = strlen(res);
428 v->iov_len = strlen(msg);
430 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
434 if (local_logging == TRUE)
437 /* now spew out the message to wherever it is supposed to go */
439 message("%s %s\n", timestamp, msg);
441 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
445 static void quit_signal(int sig)
447 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
449 #ifdef CONFIG_FEATURE_IPC_SYSLOG
456 static void domark(int sig)
458 if (MarkInterval > 0) {
459 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
464 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
465 * enabled, we otherwise get a "storage size isn't constant error. */
466 static int serveConnection(char *tmpbuf, int n_read)
470 while (p < tmpbuf + n_read) {
472 int pri = (LOG_USER | LOG_NOTICE);
474 char line[MAXLINE + 1];
478 while ((c = *p) && q < &line[sizeof(line) - 1]) {
479 if (c == '<' && num_lt == 0) {
480 /* Parse the magic priority number. */
483 while (isdigit(*(++p))) {
484 pri = 10 * pri + (*p - '0');
486 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
487 pri = (LOG_USER | LOG_NOTICE);
489 } else if (c == '\n') {
491 } else if (iscntrl(c) && (c < 0177)) {
502 logMessage(pri, line);
508 #ifdef CONFIG_FEATURE_REMOTE_LOG
509 static void init_RemoteLog(void)
512 struct sockaddr_in remoteaddr;
513 struct hostent *hostinfo;
514 int len = sizeof(remoteaddr);
516 memset(&remoteaddr, 0, len);
518 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
521 bb_error_msg_and_die("cannot create socket");
524 hostinfo = xgethostbyname(RemoteHost);
526 remoteaddr.sin_family = AF_INET;
527 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
528 remoteaddr.sin_port = htons(RemotePort);
530 /* Since we are using UDP sockets, connect just sets the default host and port
531 * for future operations
533 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
534 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
541 static void doSyslogd(void) __attribute__ ((noreturn));
542 static void doSyslogd(void)
544 struct sockaddr_un sunx;
545 socklen_t addrLength;
550 /* Set up signal handlers. */
551 signal(SIGINT, quit_signal);
552 signal(SIGTERM, quit_signal);
553 signal(SIGQUIT, quit_signal);
554 signal(SIGHUP, SIG_IGN);
555 signal(SIGCHLD, SIG_IGN);
557 signal(SIGCLD, SIG_IGN);
559 signal(SIGALRM, domark);
562 /* Create the syslog file so realpath() can work. */
563 if (realpath(_PATH_LOG, lfile) != NULL) {
567 memset(&sunx, 0, sizeof(sunx));
568 sunx.sun_family = AF_UNIX;
569 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
570 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
571 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
575 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
576 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
577 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
580 if (chmod(lfile, 0666) < 0) {
581 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
583 #ifdef CONFIG_FEATURE_IPC_SYSLOG
584 if (circular_logging == TRUE) {
589 #ifdef CONFIG_FEATURE_REMOTE_LOG
590 if (doRemoteLog == TRUE) {
595 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
600 FD_SET(sock_fd, &fds);
602 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
603 if (errno == EINTR) {
604 /* alarm may have happened. */
607 bb_perror_msg_and_die("select error");
610 if (FD_ISSET(sock_fd, &fds)) {
613 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
615 memset(tmpbuf, '\0', MAXLINE + 1);
616 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
617 serveConnection(tmpbuf, i);
619 bb_perror_msg_and_die("UNIX socket error");
621 RELEASE_CONFIG_BUFFER(tmpbuf);
623 } /* for main loop */
626 extern int syslogd_main(int argc, char **argv)
634 /* do normal option parsing */
635 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
638 MarkInterval = atoi(optarg) * 60;
644 logFilePath = optarg;
646 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
648 logFileSize = atoi(optarg) * 1024;
651 logFileRotate = atoi(optarg);
652 if( logFileRotate > 99 ) logFileRotate = 99;
655 #ifdef CONFIG_FEATURE_REMOTE_LOG
657 RemoteHost = bb_xstrdup(optarg);
658 if ((p = strchr(RemoteHost, ':'))) {
659 RemotePort = atoi(p + 1);
665 local_logging = TRUE;
668 #ifdef CONFIG_FEATURE_IPC_SYSLOG
671 int buf_size = atoi(optarg);
673 shm_size = buf_size * 1024;
676 circular_logging = TRUE;
687 #ifdef CONFIG_FEATURE_REMOTE_LOG
688 /* If they have not specified remote logging, then log locally */
689 if (doRemoteLog == FALSE)
690 local_logging = TRUE;
694 /* Store away localhost's name before the fork */
695 gethostname(LocalHostName, sizeof(LocalHostName));
696 if ((p = strchr(LocalHostName, '.'))) {
702 if (doFork == TRUE) {
703 #if defined(__uClinux__)
704 vfork_daemon_rexec(0, 1, argc, argv, "-n");
705 #else /* __uClinux__ */
707 bb_perror_msg_and_die("daemon");
708 #endif /* __uClinux__ */
717 c-file-style: "linux"