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
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
98 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
99 #error Sorry, you must set the syslogd buffer size to at least 4KB.
100 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
108 static const long KEY_ID = 0x414e4547; /*"GENA" */
110 // Semaphore operation structures
111 static struct shbuf_ds {
112 int size; // size of data written
113 int head; // start of message list
114 int tail; // end of message list
115 char data[1]; // data/messages
116 } *buf = NULL; // shared memory pointer
118 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
119 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
121 static int shmid = -1; // ipc shared memory id
122 static int s_semid = -1; // ipc semaphore id
123 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
124 static int circular_logging = FALSE;
127 * sem_up - up()'s a semaphore.
129 static inline void sem_up(int semid)
131 if (semop(semid, SMwup, 1) == -1) {
132 bb_perror_msg_and_die("semop[SMwup]");
137 * sem_down - down()'s a semaphore
139 static inline void sem_down(int semid)
141 if (semop(semid, SMwdn, 3) == -1) {
142 bb_perror_msg_and_die("semop[SMwdn]");
147 void ipcsyslog_cleanup(void)
149 printf("Exiting Syslogd!\n");
155 shmctl(shmid, IPC_RMID, NULL);
158 semctl(s_semid, 0, IPC_RMID, 0);
162 void ipcsyslog_init(void)
165 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
166 bb_perror_msg_and_die("shmget");
169 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
170 bb_perror_msg_and_die("shmat");
173 buf->size = shm_size - sizeof(*buf);
174 buf->head = buf->tail = 0;
176 // we'll trust the OS to set initial semval to 0 (let's hope)
177 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
178 if (errno == EEXIST) {
179 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
180 bb_perror_msg_and_die("semget");
183 bb_perror_msg_and_die("semget");
187 printf("Buffer already allocated just grab the semaphore?");
191 /* write message to buffer */
192 void circ_message(const char *msg)
194 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
199 * Circular Buffer Algorithm:
200 * --------------------------
202 * Start-off w/ empty buffer of specific size SHM_SIZ
203 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
204 * This is also very handy since we can do printf on message.
206 * Once the buffer is full we need to get rid of the first message in buffer and
207 * insert the new message. (Note: if the message being added is >1 message then
208 * we will need to "remove" >1 old message from the buffer). The way this is done
210 * When we reach the end of the buffer we set a mark and start from the beginning.
211 * Now what about the beginning and end of the buffer? Well we have the "head"
212 * index/pointer which is the starting point for the messages and we have "tail"
213 * index/pointer which is the ending point for the messages. When we "display" the
214 * messages we start from the beginning and continue until we reach "tail". If we
215 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
216 * "tail" are actually offsets from the beginning of the buffer.
218 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
219 * a threasafe way of handling shared memory operations.
221 if ((buf->tail + l) < buf->size) {
222 /* before we append the message we need to check the HEAD so that we won't
223 overwrite any of the message that we still need and adjust HEAD to point
224 to the next message! */
225 if (buf->tail < buf->head) {
226 if ((buf->tail + l) >= buf->head) {
227 /* we need to move the HEAD to point to the next message
228 * Theoretically we have enough room to add the whole message to the
229 * buffer, because of the first outer IF statement, so we don't have
230 * to worry about overflows here!
232 int k = buf->tail + l - buf->head; /* we need to know how many bytes
233 we are overwriting to make
236 memchr(buf->data + buf->head + k, '\0',
237 buf->size - (buf->head + k));
238 if (c != NULL) { /* do a sanity check just in case! */
239 buf->head = c - buf->data + 1; /* we need to convert pointer to
240 offset + skip the '\0' since
241 we need to point to the beginning
242 of the next message */
243 /* Note: HEAD is only used to "retrieve" messages, it's not used
244 when writing messages into our buffer */
245 } else { /* show an error message to know we messed up? */
246 printf("Weird! Can't find the terminator token??? \n");
252 /* in other cases no overflows have been done yet, so we don't care! */
253 /* we should be ok to append the message now */
254 strncpy(buf->data + buf->tail, msg, l); /* append our message */
255 buf->tail += l; /* count full message w/ '\0' terminating char */
257 /* we need to break up the message and "circle" it around */
259 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
261 /* We need to move HEAD! This is always the case since we are going
262 * to "circle" the message.
264 c = memchr(buf->data + k, '\0', buf->size - k);
266 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
267 /* move head pointer */
268 buf->head = c - buf->data + 1;
270 /* now write the first part of the message */
271 strncpy(buf->data + buf->tail, msg, l - k - 1);
273 /* ALWAYS terminate end of buffer w/ '\0' */
274 buf->data[buf->size - 1] = '\0';
276 /* now write out the rest of the string to the beginning of the buffer */
277 strcpy(buf->data, &msg[l - k - 1]);
279 /* we need to place the TAIL at the end of the message */
283 ("Weird! Can't find the terminator token from the beginning??? \n");
284 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
290 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
292 /* Note: There is also a function called "message()" in init.c */
293 /* Print a message to the log file. */
294 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
295 static void message(char *fmt, ...)
301 fl.l_whence = SEEK_SET;
305 #ifdef CONFIG_FEATURE_IPC_SYSLOG
306 if ((circular_logging == TRUE) && (buf != NULL)) {
309 va_start(arguments, fmt);
310 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
317 device_open(logFilePath,
318 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
321 fcntl(fd, F_SETLKW, &fl);
322 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
323 if ( logFileSize > 0 ) {
325 int r = fstat(fd, &statf);
326 if( !r && (statf.st_mode & S_IFREG)
327 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
328 if(logFileRotate > 0) {
330 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
331 for(i=logFileRotate-1;i>0;i--) {
332 sprintf(oldFile, "%s.%d", logFilePath, i-1);
333 sprintf(newFile, "%s.%d", logFilePath, i);
334 rename(oldFile, newFile);
336 sprintf(newFile, "%s.%d", logFilePath, 0);
338 fcntl (fd, F_SETLKW, &fl);
340 rename(logFilePath, newFile);
341 fd = device_open (logFilePath,
342 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
345 fcntl (fd, F_SETLKW, &fl);
352 va_start(arguments, fmt);
353 vdprintf(fd, fmt, arguments);
356 fcntl(fd, F_SETLKW, &fl);
359 /* Always send console messages to /dev/console so people will see them. */
361 device_open(_PATH_CONSOLE,
362 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
363 va_start(arguments, fmt);
364 vdprintf(fd, fmt, arguments);
368 fprintf(stderr, "Bummer, can't print: ");
369 va_start(arguments, fmt);
370 vfprintf(stderr, fmt, arguments);
377 static void logMessage(int pri, char *msg)
381 static char res[20] = "";
385 for (c_fac = facilitynames;
386 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
387 for (c_pri = prioritynames;
388 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
389 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
390 snprintf(res, sizeof(res), "<%d>", pri);
392 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
396 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
397 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
399 timestamp = ctime(&now) + 4;
400 timestamp[15] = '\0';
403 timestamp[15] = '\0';
407 /* todo: supress duplicates */
409 #ifdef CONFIG_FEATURE_REMOTE_LOG
410 /* send message to remote logger */
411 if (-1 != remotefd) {
412 static const int IOV_COUNT = 2;
413 struct iovec iov[IOV_COUNT];
414 struct iovec *v = iov;
416 memset(&res, 0, sizeof(res));
417 snprintf(res, sizeof(res), "<%d>", pri);
419 v->iov_len = strlen(res);
423 v->iov_len = strlen(msg);
425 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
429 if (local_logging == TRUE)
431 /* now spew out the message to wherever it is supposed to go */
432 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
435 static void quit_signal(int sig)
437 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
439 #ifdef CONFIG_FEATURE_IPC_SYSLOG
446 static void domark(int sig)
448 if (MarkInterval > 0) {
449 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
454 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
455 * enabled, we otherwise get a "storage size isn't constant error. */
456 static int serveConnection(char *tmpbuf, int n_read)
460 while (p < tmpbuf + n_read) {
462 int pri = (LOG_USER | LOG_NOTICE);
464 char line[MAXLINE + 1];
468 while ((c = *p) && q < &line[sizeof(line) - 1]) {
469 if (c == '<' && num_lt == 0) {
470 /* Parse the magic priority number. */
473 while (isdigit(*(++p))) {
474 pri = 10 * pri + (*p - '0');
476 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
477 pri = (LOG_USER | LOG_NOTICE);
479 } else if (c == '\n') {
481 } else if (iscntrl(c) && (c < 0177)) {
492 logMessage(pri, line);
498 #ifdef CONFIG_FEATURE_REMOTE_LOG
499 static void init_RemoteLog(void)
502 struct sockaddr_in remoteaddr;
503 struct hostent *hostinfo;
504 int len = sizeof(remoteaddr);
506 memset(&remoteaddr, 0, len);
508 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
511 bb_error_msg_and_die("cannot create socket");
514 hostinfo = xgethostbyname(RemoteHost);
516 remoteaddr.sin_family = AF_INET;
517 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
518 remoteaddr.sin_port = htons(RemotePort);
520 /* Since we are using UDP sockets, connect just sets the default host and port
521 * for future operations
523 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
524 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
531 static void doSyslogd(void) __attribute__ ((noreturn));
532 static void doSyslogd(void)
534 struct sockaddr_un sunx;
535 socklen_t addrLength;
540 /* Set up signal handlers. */
541 signal(SIGINT, quit_signal);
542 signal(SIGTERM, quit_signal);
543 signal(SIGQUIT, quit_signal);
544 signal(SIGHUP, SIG_IGN);
545 signal(SIGCHLD, SIG_IGN);
547 signal(SIGCLD, SIG_IGN);
549 signal(SIGALRM, domark);
552 /* Create the syslog file so realpath() can work. */
553 if (realpath(_PATH_LOG, lfile) != NULL) {
557 memset(&sunx, 0, sizeof(sunx));
558 sunx.sun_family = AF_UNIX;
559 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
560 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
561 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
565 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
566 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
567 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
570 if (chmod(lfile, 0666) < 0) {
571 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
573 #ifdef CONFIG_FEATURE_IPC_SYSLOG
574 if (circular_logging == TRUE) {
579 #ifdef CONFIG_FEATURE_REMOTE_LOG
580 if (doRemoteLog == TRUE) {
585 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
590 FD_SET(sock_fd, &fds);
592 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
593 if (errno == EINTR) {
594 /* alarm may have happened. */
597 bb_perror_msg_and_die("select error");
600 if (FD_ISSET(sock_fd, &fds)) {
603 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
605 memset(tmpbuf, '\0', MAXLINE + 1);
606 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
607 serveConnection(tmpbuf, i);
609 bb_perror_msg_and_die("UNIX socket error");
611 RELEASE_CONFIG_BUFFER(tmpbuf);
613 } /* for main loop */
616 extern int syslogd_main(int argc, char **argv)
624 /* do normal option parsing */
625 while ((opt = getopt(argc, argv, "m:nO:s:b:R:LC::")) > 0) {
628 MarkInterval = atoi(optarg) * 60;
634 logFilePath = optarg;
636 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
638 logFileSize = atoi(optarg) * 1024;
641 logFileRotate = atoi(optarg);
642 if( logFileRotate > 99 ) logFileRotate = 99;
645 #ifdef CONFIG_FEATURE_REMOTE_LOG
647 RemoteHost = bb_xstrdup(optarg);
648 if ((p = strchr(RemoteHost, ':'))) {
649 RemotePort = atoi(p + 1);
655 local_logging = TRUE;
658 #ifdef CONFIG_FEATURE_IPC_SYSLOG
661 int buf_size = atoi(optarg);
663 shm_size = buf_size * 1024;
666 circular_logging = TRUE;
674 #ifdef CONFIG_FEATURE_REMOTE_LOG
675 /* If they have not specified remote logging, then log locally */
676 if (doRemoteLog == FALSE)
677 local_logging = TRUE;
681 /* Store away localhost's name before the fork */
682 gethostname(LocalHostName, sizeof(LocalHostName));
683 if ((p = strchr(LocalHostName, '.'))) {
689 if (doFork == TRUE) {
690 #if defined(__uClinux__)
691 vfork_daemon_rexec(0, 1, argc, argv, "-n");
692 #else /* __uClinux__ */
694 bb_perror_msg_and_die("daemon");
695 #endif /* __uClinux__ */
704 c-file-style: "linux"