1 /* vi: set sw=4 ts=4: */
3 * Mini syslogd implementation for busybox
5 * Copyright (C) 1999-2003 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
41 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/param.h>
48 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
50 #include <sys/syslog.h>
53 /* Path for the file where all log messages are written */
54 #define __LOG_FILE "/var/log/messages"
56 /* Path to the unix socket */
57 static char lfile[MAXPATHLEN];
59 static const char *logFilePath = __LOG_FILE;
61 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
62 /* max size of message file bevor being rotated */
63 static int logFileSize = 200 * 1024;
65 /* number of rotated message files */
66 static int logFileRotate = 1;
69 /* interval between marks in seconds */
70 static int MarkInterval = 20 * 60;
72 /* localhost's name */
73 static char LocalHostName[64];
75 #ifdef CONFIG_FEATURE_REMOTE_LOG
76 #include <netinet/in.h>
77 /* udp socket for logging to remote host */
78 static int remotefd = -1;
80 /* where do we log? */
81 static char *RemoteHost;
83 /* what port to log to? */
84 static int RemotePort = 514;
86 /* To remote log or not to remote log, that is the question. */
87 static int doRemoteLog = FALSE;
88 static int local_logging = FALSE;
92 #define MAXLINE 1024 /* maximum line length */
95 /* circular buffer variables/structures */
96 #ifdef CONFIG_FEATURE_IPC_SYSLOG
102 static const long KEY_ID = 0x414e4547; /*"GENA" */
104 // Semaphore operation structures
105 static struct shbuf_ds {
106 int size; // size of data written
107 int head; // start of message list
108 int tail; // end of message list
109 char data[1]; // data/messages
110 } *buf = NULL; // shared memory pointer
112 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
113 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
115 static int shmid = -1; // ipc shared memory id
116 static int s_semid = -1; // ipc semaphore id
117 static int data_size = 16000; // default data size
118 static int circular_logging = FALSE;
121 * sem_up - up()'s a semaphore.
123 static inline void sem_up(int semid)
125 if (semop(semid, SMwup, 1) == -1) {
126 bb_perror_msg_and_die("semop[SMwup]");
131 * sem_down - down()'s a semaphore
133 static inline void sem_down(int semid)
135 if (semop(semid, SMwdn, 3) == -1) {
136 bb_perror_msg_and_die("semop[SMwdn]");
141 void ipcsyslog_cleanup(void)
143 printf("Exiting Syslogd!\n");
149 shmctl(shmid, IPC_RMID, NULL);
152 semctl(s_semid, 0, IPC_RMID, 0);
156 void ipcsyslog_init(void)
159 if ((shmid = shmget(KEY_ID, data_size, IPC_CREAT | 1023)) == -1) {
160 bb_perror_msg_and_die("shmget");
163 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
164 bb_perror_msg_and_die("shmat");
167 buf->size = data_size - sizeof(*buf);
168 buf->head = buf->tail = 0;
170 // we'll trust the OS to set initial semval to 0 (let's hope)
171 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
172 if (errno == EEXIST) {
173 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
174 bb_perror_msg_and_die("semget");
177 bb_perror_msg_and_die("semget");
181 printf("Buffer already allocated just grab the semaphore?");
185 /* write message to buffer */
186 void circ_message(const char *msg)
188 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
193 * Circular Buffer Algorithm:
194 * --------------------------
196 * Start-off w/ empty buffer of specific size SHM_SIZ
197 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
198 * This is also very handy since we can do printf on message.
200 * Once the buffer is full we need to get rid of the first message in buffer and
201 * insert the new message. (Note: if the message being added is >1 message then
202 * we will need to "remove" >1 old message from the buffer). The way this is done
204 * When we reach the end of the buffer we set a mark and start from the beginning.
205 * Now what about the beginning and end of the buffer? Well we have the "head"
206 * index/pointer which is the starting point for the messages and we have "tail"
207 * index/pointer which is the ending point for the messages. When we "display" the
208 * messages we start from the beginning and continue until we reach "tail". If we
209 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
210 * "tail" are actually offsets from the beginning of the buffer.
212 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
213 * a threasafe way of handling shared memory operations.
215 if ((buf->tail + l) < buf->size) {
216 /* before we append the message we need to check the HEAD so that we won't
217 overwrite any of the message that we still need and adjust HEAD to point
218 to the next message! */
219 if (buf->tail < buf->head) {
220 if ((buf->tail + l) >= buf->head) {
221 /* we need to move the HEAD to point to the next message
222 * Theoretically we have enough room to add the whole message to the
223 * buffer, because of the first outer IF statement, so we don't have
224 * to worry about overflows here!
226 int k = buf->tail + l - buf->head; /* we need to know how many bytes
227 we are overwriting to make
230 memchr(buf->data + buf->head + k, '\0',
231 buf->size - (buf->head + k));
232 if (c != NULL) { /* do a sanity check just in case! */
233 buf->head = c - buf->data + 1; /* we need to convert pointer to
234 offset + skip the '\0' since
235 we need to point to the beginning
236 of the next message */
237 /* Note: HEAD is only used to "retrieve" messages, it's not used
238 when writing messages into our buffer */
239 } else { /* show an error message to know we messed up? */
240 printf("Weird! Can't find the terminator token??? \n");
246 /* in other cases no overflows have been done yet, so we don't care! */
247 /* we should be ok to append the message now */
248 strncpy(buf->data + buf->tail, msg, l); /* append our message */
249 buf->tail += l; /* count full message w/ '\0' terminating char */
251 /* we need to break up the message and "circle" it around */
253 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
255 /* We need to move HEAD! This is always the case since we are going
256 * to "circle" the message.
258 c = memchr(buf->data + k, '\0', buf->size - k);
260 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
261 /* move head pointer */
262 buf->head = c - buf->data + 1;
264 /* now write the first part of the message */
265 strncpy(buf->data + buf->tail, msg, l - k - 1);
267 /* ALWAYS terminate end of buffer w/ '\0' */
268 buf->data[buf->size - 1] = '\0';
270 /* now write out the rest of the string to the beginning of the buffer */
271 strcpy(buf->data, &msg[l - k - 1]);
273 /* we need to place the TAIL at the end of the message */
277 ("Weird! Can't find the terminator token from the beginning??? \n");
278 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
284 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
286 /* Note: There is also a function called "message()" in init.c */
287 /* Print a message to the log file. */
288 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
289 static void message(char *fmt, ...)
295 fl.l_whence = SEEK_SET;
299 #ifdef CONFIG_FEATURE_IPC_SYSLOG
300 if ((circular_logging == TRUE) && (buf != NULL)) {
303 va_start(arguments, fmt);
304 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
311 device_open(logFilePath,
312 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
315 fcntl(fd, F_SETLKW, &fl);
316 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
317 if ( logFileSize > 0 ) {
319 int r = fstat(fd, &statf);
320 if( !r && (statf.st_mode & S_IFREG)
321 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
322 if(logFileRotate > 0) {
324 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
325 for(i=logFileRotate-1;i>0;i--) {
326 sprintf(oldFile, "%s.%d", logFilePath, i-1);
327 sprintf(newFile, "%s.%d", logFilePath, i);
328 rename(oldFile, newFile);
330 sprintf(newFile, "%s.%d", logFilePath, 0);
332 fcntl (fd, F_SETLKW, &fl);
334 rename(logFilePath, newFile);
335 fd = device_open (logFilePath,
336 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
339 fcntl (fd, F_SETLKW, &fl);
346 va_start(arguments, fmt);
347 vdprintf(fd, fmt, arguments);
350 fcntl(fd, F_SETLKW, &fl);
353 /* Always send console messages to /dev/console so people will see them. */
355 device_open(_PATH_CONSOLE,
356 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
357 va_start(arguments, fmt);
358 vdprintf(fd, fmt, arguments);
362 fprintf(stderr, "Bummer, can't print: ");
363 va_start(arguments, fmt);
364 vfprintf(stderr, fmt, arguments);
371 static void logMessage(int pri, char *msg)
375 static char res[20] = "";
379 for (c_fac = facilitynames;
380 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
381 for (c_pri = prioritynames;
382 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
383 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
384 snprintf(res, sizeof(res), "<%d>", pri);
386 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
390 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
391 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
393 timestamp = ctime(&now) + 4;
394 timestamp[15] = '\0';
397 timestamp[15] = '\0';
401 /* todo: supress duplicates */
403 #ifdef CONFIG_FEATURE_REMOTE_LOG
404 /* send message to remote logger */
405 if (-1 != remotefd) {
406 static const int IOV_COUNT = 2;
407 struct iovec iov[IOV_COUNT];
408 struct iovec *v = iov;
410 memset(&res, 0, sizeof(res));
411 snprintf(res, sizeof(res), "<%d>", pri);
413 v->iov_len = strlen(res);
417 v->iov_len = strlen(msg);
419 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
423 if (local_logging == TRUE)
425 /* now spew out the message to wherever it is supposed to go */
426 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
429 static void quit_signal(int sig)
431 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
433 #ifdef CONFIG_FEATURE_IPC_SYSLOG
440 static void domark(int sig)
442 if (MarkInterval > 0) {
443 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
448 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
449 * enabled, we otherwise get a "storage size isn't constant error. */
450 static int serveConnection(char *tmpbuf, int n_read)
454 while (p < tmpbuf + n_read) {
456 int pri = (LOG_USER | LOG_NOTICE);
458 char line[MAXLINE + 1];
462 while ((c = *p) && q < &line[sizeof(line) - 1]) {
463 if (c == '<' && num_lt == 0) {
464 /* Parse the magic priority number. */
467 while (isdigit(*(++p))) {
468 pri = 10 * pri + (*p - '0');
470 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
471 pri = (LOG_USER | LOG_NOTICE);
473 } else if (c == '\n') {
475 } else if (iscntrl(c) && (c < 0177)) {
486 logMessage(pri, line);
492 #ifdef CONFIG_FEATURE_REMOTE_LOG
493 static void init_RemoteLog(void)
496 struct sockaddr_in remoteaddr;
497 struct hostent *hostinfo;
498 int len = sizeof(remoteaddr);
500 memset(&remoteaddr, 0, len);
502 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
505 bb_error_msg_and_die("cannot create socket");
508 hostinfo = xgethostbyname(RemoteHost);
510 remoteaddr.sin_family = AF_INET;
511 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
512 remoteaddr.sin_port = htons(RemotePort);
514 /* Since we are using UDP sockets, connect just sets the default host and port
515 * for future operations
517 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
518 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
525 static void doSyslogd(void) __attribute__ ((noreturn));
526 static void doSyslogd(void)
528 struct sockaddr_un sunx;
529 socklen_t addrLength;
534 /* Set up signal handlers. */
535 signal(SIGINT, quit_signal);
536 signal(SIGTERM, quit_signal);
537 signal(SIGQUIT, quit_signal);
538 signal(SIGHUP, SIG_IGN);
539 signal(SIGCHLD, SIG_IGN);
541 signal(SIGCLD, SIG_IGN);
543 signal(SIGALRM, domark);
546 /* Create the syslog file so realpath() can work. */
547 if (realpath(_PATH_LOG, lfile) != NULL) {
551 memset(&sunx, 0, sizeof(sunx));
552 sunx.sun_family = AF_UNIX;
553 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
554 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
555 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
559 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
560 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
561 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
564 if (chmod(lfile, 0666) < 0) {
565 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
567 #ifdef CONFIG_FEATURE_IPC_SYSLOG
568 if (circular_logging == TRUE) {
573 #ifdef CONFIG_FEATURE_REMOTE_LOG
574 if (doRemoteLog == TRUE) {
579 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
584 FD_SET(sock_fd, &fds);
586 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
587 if (errno == EINTR) {
588 /* alarm may have happened. */
591 bb_perror_msg_and_die("select error");
594 if (FD_ISSET(sock_fd, &fds)) {
597 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
599 memset(tmpbuf, '\0', MAXLINE + 1);
600 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
601 serveConnection(tmpbuf, i);
603 bb_perror_msg_and_die("UNIX socket error");
605 RELEASE_CONFIG_BUFFER(tmpbuf);
607 } /* for main loop */
610 extern int syslogd_main(int argc, char **argv)
618 /* do normal option parsing */
619 while ((opt = getopt(argc, argv, "m:nO:s:b:R:LC::")) > 0) {
622 MarkInterval = atoi(optarg) * 60;
628 logFilePath = optarg;
630 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
632 logFileSize = atoi(optarg) * 1024;
635 logFileRotate = atoi(optarg);
636 if( logFileRotate > 99 ) logFileRotate = 99;
639 #ifdef CONFIG_FEATURE_REMOTE_LOG
641 RemoteHost = bb_xstrdup(optarg);
642 if ((p = strchr(RemoteHost, ':'))) {
643 RemotePort = atoi(p + 1);
649 local_logging = TRUE;
652 #ifdef CONFIG_FEATURE_IPC_SYSLOG
655 int buf_size = atoi(optarg);
657 data_size = buf_size;
660 circular_logging = TRUE;
668 #ifdef CONFIG_FEATURE_REMOTE_LOG
669 /* If they have not specified remote logging, then log locally */
670 if (doRemoteLog == FALSE)
671 local_logging = TRUE;
675 /* Store away localhost's name before the fork */
676 gethostname(LocalHostName, sizeof(LocalHostName));
677 if ((p = strchr(LocalHostName, '.'))) {
683 if (doFork == TRUE) {
684 #if defined(__uClinux__)
685 vfork_daemon_rexec(0, 1, argc, argv, "-n");
686 #else /* __uClinux__ */
688 bb_perror_msg_and_die("daemon");
689 #endif /* __uClinux__ */
698 c-file-style: "linux"