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;
81 static struct sockaddr_in remoteaddr;
82 static int remoteaddrlen;
84 /* where do we log? */
85 static char *RemoteHost;
87 /* what port to log to? */
88 static int RemotePort = 514;
90 /* To remote log or not to remote log, that is the question. */
91 static int doRemoteLog = FALSE;
92 static int local_logging = FALSE;
95 /* Make loging output smaller. */
96 static bool small = false;
99 #define MAXLINE 1024 /* maximum line length */
102 /* circular buffer variables/structures */
103 #ifdef CONFIG_FEATURE_IPC_SYSLOG
105 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
106 #error Sorry, you must set the syslogd buffer size to at least 4KB.
107 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
115 static const long KEY_ID = 0x414e4547; /*"GENA" */
117 // Semaphore operation structures
118 static struct shbuf_ds {
119 int size; // size of data written
120 int head; // start of message list
121 int tail; // end of message list
122 char data[1]; // data/messages
123 } *buf = NULL; // shared memory pointer
125 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
126 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
128 static int shmid = -1; // ipc shared memory id
129 static int s_semid = -1; // ipc semaphore id
130 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
131 static int circular_logging = FALSE;
134 * sem_up - up()'s a semaphore.
136 static inline void sem_up(int semid)
138 if (semop(semid, SMwup, 1) == -1) {
139 bb_perror_msg_and_die("semop[SMwup]");
144 * sem_down - down()'s a semaphore
146 static inline void sem_down(int semid)
148 if (semop(semid, SMwdn, 3) == -1) {
149 bb_perror_msg_and_die("semop[SMwdn]");
154 void ipcsyslog_cleanup(void)
156 printf("Exiting Syslogd!\n");
162 shmctl(shmid, IPC_RMID, NULL);
165 semctl(s_semid, 0, IPC_RMID, 0);
169 void ipcsyslog_init(void)
172 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
173 bb_perror_msg_and_die("shmget");
176 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
177 bb_perror_msg_and_die("shmat");
180 buf->size = shm_size - sizeof(*buf);
181 buf->head = buf->tail = 0;
183 // we'll trust the OS to set initial semval to 0 (let's hope)
184 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
185 if (errno == EEXIST) {
186 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
187 bb_perror_msg_and_die("semget");
190 bb_perror_msg_and_die("semget");
194 printf("Buffer already allocated just grab the semaphore?");
198 /* write message to buffer */
199 void circ_message(const char *msg)
201 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
206 * Circular Buffer Algorithm:
207 * --------------------------
209 * Start-off w/ empty buffer of specific size SHM_SIZ
210 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
211 * This is also very handy since we can do printf on message.
213 * Once the buffer is full we need to get rid of the first message in buffer and
214 * insert the new message. (Note: if the message being added is >1 message then
215 * we will need to "remove" >1 old message from the buffer). The way this is done
217 * When we reach the end of the buffer we set a mark and start from the beginning.
218 * Now what about the beginning and end of the buffer? Well we have the "head"
219 * index/pointer which is the starting point for the messages and we have "tail"
220 * index/pointer which is the ending point for the messages. When we "display" the
221 * messages we start from the beginning and continue until we reach "tail". If we
222 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
223 * "tail" are actually offsets from the beginning of the buffer.
225 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
226 * a threasafe way of handling shared memory operations.
228 if ((buf->tail + l) < buf->size) {
229 /* before we append the message we need to check the HEAD so that we won't
230 overwrite any of the message that we still need and adjust HEAD to point
231 to the next message! */
232 if (buf->tail < buf->head) {
233 if ((buf->tail + l) >= buf->head) {
234 /* we need to move the HEAD to point to the next message
235 * Theoretically we have enough room to add the whole message to the
236 * buffer, because of the first outer IF statement, so we don't have
237 * to worry about overflows here!
239 int k = buf->tail + l - buf->head; /* we need to know how many bytes
240 we are overwriting to make
243 memchr(buf->data + buf->head + k, '\0',
244 buf->size - (buf->head + k));
245 if (c != NULL) { /* do a sanity check just in case! */
246 buf->head = c - buf->data + 1; /* we need to convert pointer to
247 offset + skip the '\0' since
248 we need to point to the beginning
249 of the next message */
250 /* Note: HEAD is only used to "retrieve" messages, it's not used
251 when writing messages into our buffer */
252 } else { /* show an error message to know we messed up? */
253 printf("Weird! Can't find the terminator token??? \n");
259 /* in other cases no overflows have been done yet, so we don't care! */
260 /* we should be ok to append the message now */
261 strncpy(buf->data + buf->tail, msg, l); /* append our message */
262 buf->tail += l; /* count full message w/ '\0' terminating char */
264 /* we need to break up the message and "circle" it around */
266 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
268 /* We need to move HEAD! This is always the case since we are going
269 * to "circle" the message.
271 c = memchr(buf->data + k, '\0', buf->size - k);
273 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
274 /* move head pointer */
275 buf->head = c - buf->data + 1;
277 /* now write the first part of the message */
278 strncpy(buf->data + buf->tail, msg, l - k - 1);
280 /* ALWAYS terminate end of buffer w/ '\0' */
281 buf->data[buf->size - 1] = '\0';
283 /* now write out the rest of the string to the beginning of the buffer */
284 strcpy(buf->data, &msg[l - k - 1]);
286 /* we need to place the TAIL at the end of the message */
290 ("Weird! Can't find the terminator token from the beginning??? \n");
291 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
297 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
299 /* Note: There is also a function called "message()" in init.c */
300 /* Print a message to the log file. */
301 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
302 static void message(char *fmt, ...)
308 fl.l_whence = SEEK_SET;
312 #ifdef CONFIG_FEATURE_IPC_SYSLOG
313 if ((circular_logging == TRUE) && (buf != NULL)) {
316 va_start(arguments, fmt);
317 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
324 device_open(logFilePath,
325 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
328 fcntl(fd, F_SETLKW, &fl);
329 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
330 if ( logFileSize > 0 ) {
332 int r = fstat(fd, &statf);
333 if( !r && (statf.st_mode & S_IFREG)
334 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
335 if(logFileRotate > 0) {
337 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
338 for(i=logFileRotate-1;i>0;i--) {
339 sprintf(oldFile, "%s.%d", logFilePath, i-1);
340 sprintf(newFile, "%s.%d", logFilePath, i);
341 rename(oldFile, newFile);
343 sprintf(newFile, "%s.%d", logFilePath, 0);
345 fcntl (fd, F_SETLKW, &fl);
347 rename(logFilePath, newFile);
348 fd = device_open (logFilePath,
349 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
352 fcntl (fd, F_SETLKW, &fl);
359 va_start(arguments, fmt);
360 vdprintf(fd, fmt, arguments);
363 fcntl(fd, F_SETLKW, &fl);
366 /* Always send console messages to /dev/console so people will see them. */
368 device_open(_PATH_CONSOLE,
369 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
370 va_start(arguments, fmt);
371 vdprintf(fd, fmt, arguments);
375 fprintf(stderr, "Bummer, can't print: ");
376 va_start(arguments, fmt);
377 vfprintf(stderr, fmt, arguments);
384 static void logMessage(int pri, char *msg)
388 static char res[20] = "";
389 #ifdef CONFIG_FEATURE_REMOTE_LOG
390 static char line[512];
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 /* send message to remote logger */
421 if (-1 != remotefd) {
423 memset(&line, 0, sizeof(line));
424 snprintf(line, sizeof(line), "<%d> <%s>", pri, msg);
427 if(( -1 == sendto(remotefd, line, strlen(line), 0,
428 (struct sockaddr *) &remoteaddr,
429 remoteaddrlen)) && (errno == EINTR)) {
433 if (local_logging == TRUE)
436 /* now spew out the message to wherever it is supposed to go */
438 message("%s %s\n", timestamp, msg);
440 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
444 static void quit_signal(int sig)
446 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
448 #ifdef CONFIG_FEATURE_IPC_SYSLOG
455 static void domark(int sig)
457 if (MarkInterval > 0) {
458 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
463 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
464 * enabled, we otherwise get a "storage size isn't constant error. */
465 static int serveConnection(char *tmpbuf, int n_read)
469 while (p < tmpbuf + n_read) {
471 int pri = (LOG_USER | LOG_NOTICE);
473 char line[MAXLINE + 1];
477 while ((c = *p) && q < &line[sizeof(line) - 1]) {
478 if (c == '<' && num_lt == 0) {
479 /* Parse the magic priority number. */
482 while (isdigit(*(++p))) {
483 pri = 10 * pri + (*p - '0');
485 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
486 pri = (LOG_USER | LOG_NOTICE);
488 } else if (c == '\n') {
490 } else if (iscntrl(c) && (c < 0177)) {
501 logMessage(pri, line);
507 #ifdef CONFIG_FEATURE_REMOTE_LOG
508 static void init_RemoteLog(void)
510 struct hostent *hostinfo;
511 remoteaddrlen = sizeof(remoteaddr);
513 memset(&remoteaddr, 0, remoteaddrlen);
515 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
518 bb_error_msg_and_die("cannot create socket");
521 hostinfo = xgethostbyname(RemoteHost);
523 remoteaddr.sin_family = AF_INET;
524 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
525 remoteaddr.sin_port = htons(RemotePort);
529 static void doSyslogd(void) __attribute__ ((noreturn));
530 static void doSyslogd(void)
532 struct sockaddr_un sunx;
533 socklen_t addrLength;
538 /* Set up signal handlers. */
539 signal(SIGINT, quit_signal);
540 signal(SIGTERM, quit_signal);
541 signal(SIGQUIT, quit_signal);
542 signal(SIGHUP, SIG_IGN);
543 signal(SIGCHLD, SIG_IGN);
545 signal(SIGCLD, SIG_IGN);
547 signal(SIGALRM, domark);
550 /* Create the syslog file so realpath() can work. */
551 if (realpath(_PATH_LOG, lfile) != NULL) {
555 memset(&sunx, 0, sizeof(sunx));
556 sunx.sun_family = AF_UNIX;
557 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
558 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
559 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
563 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
564 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
565 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
568 if (chmod(lfile, 0666) < 0) {
569 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
571 #ifdef CONFIG_FEATURE_IPC_SYSLOG
572 if (circular_logging == TRUE) {
577 #ifdef CONFIG_FEATURE_REMOTE_LOG
578 if (doRemoteLog == TRUE) {
583 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
588 FD_SET(sock_fd, &fds);
590 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
591 if (errno == EINTR) {
592 /* alarm may have happened. */
595 bb_perror_msg_and_die("select error");
598 if (FD_ISSET(sock_fd, &fds)) {
601 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
603 memset(tmpbuf, '\0', MAXLINE + 1);
604 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
605 serveConnection(tmpbuf, i);
607 bb_perror_msg_and_die("UNIX socket error");
609 RELEASE_CONFIG_BUFFER(tmpbuf);
611 } /* for main loop */
614 extern int syslogd_main(int argc, char **argv)
622 /* do normal option parsing */
623 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
626 MarkInterval = atoi(optarg) * 60;
632 logFilePath = optarg;
634 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
636 logFileSize = atoi(optarg) * 1024;
639 logFileRotate = atoi(optarg);
640 if( logFileRotate > 99 ) logFileRotate = 99;
643 #ifdef CONFIG_FEATURE_REMOTE_LOG
645 RemoteHost = bb_xstrdup(optarg);
646 if ((p = strchr(RemoteHost, ':'))) {
647 RemotePort = atoi(p + 1);
653 local_logging = TRUE;
656 #ifdef CONFIG_FEATURE_IPC_SYSLOG
659 int buf_size = atoi(optarg);
661 shm_size = buf_size * 1024;
664 circular_logging = TRUE;
675 #ifdef CONFIG_FEATURE_REMOTE_LOG
676 /* If they have not specified remote logging, then log locally */
677 if (doRemoteLog == FALSE)
678 local_logging = TRUE;
682 /* Store away localhost's name before the fork */
683 gethostname(LocalHostName, sizeof(LocalHostName));
684 if ((p = strchr(LocalHostName, '.'))) {
690 if (doFork == TRUE) {
691 #if defined(__uClinux__)
692 vfork_daemon_rexec(0, 1, argc, argv, "-n");
693 #else /* __uClinux__ */
695 bb_perror_msg_and_die("daemon");
696 #endif /* __uClinux__ */
705 c-file-style: "linux"