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
42 #include <sys/socket.h>
43 #include <sys/types.h>
45 #include <sys/param.h>
49 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
51 #include <sys/syslog.h>
54 /* Path for the file where all log messages are written */
55 #define __LOG_FILE "/var/log/messages"
57 /* Path to the unix socket */
58 static char lfile[MAXPATHLEN];
60 static const char *logFilePath = __LOG_FILE;
62 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
63 /* max size of message file before being rotated */
64 static int logFileSize = 200 * 1024;
66 /* number of rotated message files */
67 static int logFileRotate = 1;
70 /* interval between marks in seconds */
71 static int MarkInterval = 20 * 60;
73 /* localhost's name */
74 static char LocalHostName[64];
76 #ifdef CONFIG_FEATURE_REMOTE_LOG
77 #include <netinet/in.h>
78 /* udp socket for logging to remote host */
79 static int remotefd = -1;
81 /* where do we log? */
82 static char *RemoteHost;
84 /* what port to log to? */
85 static int RemotePort = 514;
87 /* To remote log or not to remote log, that is the question. */
88 static int doRemoteLog = FALSE;
89 static int local_logging = FALSE;
92 /* Make loging output smaller. */
93 static bool small = false;
96 #define MAXLINE 1024 /* maximum line length */
99 /* circular buffer variables/structures */
100 #ifdef CONFIG_FEATURE_IPC_SYSLOG
102 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
103 #error Sorry, you must set the syslogd buffer size to at least 4KB.
104 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
112 static const long KEY_ID = 0x414e4547; /*"GENA" */
114 // Semaphore operation structures
115 static struct shbuf_ds {
116 int size; // size of data written
117 int head; // start of message list
118 int tail; // end of message list
119 char data[1]; // data/messages
120 } *buf = NULL; // shared memory pointer
122 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
123 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
125 static int shmid = -1; // ipc shared memory id
126 static int s_semid = -1; // ipc semaphore id
127 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
128 static int circular_logging = FALSE;
131 * sem_up - up()'s a semaphore.
133 static inline void sem_up(int semid)
135 if (semop(semid, SMwup, 1) == -1) {
136 bb_perror_msg_and_die("semop[SMwup]");
141 * sem_down - down()'s a semaphore
143 static inline void sem_down(int semid)
145 if (semop(semid, SMwdn, 3) == -1) {
146 bb_perror_msg_and_die("semop[SMwdn]");
151 void ipcsyslog_cleanup(void)
153 printf("Exiting Syslogd!\n");
159 shmctl(shmid, IPC_RMID, NULL);
162 semctl(s_semid, 0, IPC_RMID, 0);
166 void ipcsyslog_init(void)
169 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
170 bb_perror_msg_and_die("shmget");
173 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
174 bb_perror_msg_and_die("shmat");
177 buf->size = shm_size - sizeof(*buf);
178 buf->head = buf->tail = 0;
180 // we'll trust the OS to set initial semval to 0 (let's hope)
181 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
182 if (errno == EEXIST) {
183 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
184 bb_perror_msg_and_die("semget");
187 bb_perror_msg_and_die("semget");
191 printf("Buffer already allocated just grab the semaphore?");
195 /* write message to buffer */
196 void circ_message(const char *msg)
198 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
203 * Circular Buffer Algorithm:
204 * --------------------------
206 * Start-off w/ empty buffer of specific size SHM_SIZ
207 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
208 * This is also very handy since we can do printf on message.
210 * Once the buffer is full we need to get rid of the first message in buffer and
211 * insert the new message. (Note: if the message being added is >1 message then
212 * we will need to "remove" >1 old message from the buffer). The way this is done
214 * When we reach the end of the buffer we set a mark and start from the beginning.
215 * Now what about the beginning and end of the buffer? Well we have the "head"
216 * index/pointer which is the starting point for the messages and we have "tail"
217 * index/pointer which is the ending point for the messages. When we "display" the
218 * messages we start from the beginning and continue until we reach "tail". If we
219 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
220 * "tail" are actually offsets from the beginning of the buffer.
222 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
223 * a threasafe way of handling shared memory operations.
225 if ((buf->tail + l) < buf->size) {
226 /* before we append the message we need to check the HEAD so that we won't
227 overwrite any of the message that we still need and adjust HEAD to point
228 to the next message! */
229 if (buf->tail < buf->head) {
230 if ((buf->tail + l) >= buf->head) {
231 /* we need to move the HEAD to point to the next message
232 * Theoretically we have enough room to add the whole message to the
233 * buffer, because of the first outer IF statement, so we don't have
234 * to worry about overflows here!
236 int k = buf->tail + l - buf->head; /* we need to know how many bytes
237 we are overwriting to make
240 memchr(buf->data + buf->head + k, '\0',
241 buf->size - (buf->head + k));
242 if (c != NULL) { /* do a sanity check just in case! */
243 buf->head = c - buf->data + 1; /* we need to convert pointer to
244 offset + skip the '\0' since
245 we need to point to the beginning
246 of the next message */
247 /* Note: HEAD is only used to "retrieve" messages, it's not used
248 when writing messages into our buffer */
249 } else { /* show an error message to know we messed up? */
250 printf("Weird! Can't find the terminator token??? \n");
256 /* in other cases no overflows have been done yet, so we don't care! */
257 /* we should be ok to append the message now */
258 strncpy(buf->data + buf->tail, msg, l); /* append our message */
259 buf->tail += l; /* count full message w/ '\0' terminating char */
261 /* we need to break up the message and "circle" it around */
263 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
265 /* We need to move HEAD! This is always the case since we are going
266 * to "circle" the message.
268 c = memchr(buf->data + k, '\0', buf->size - k);
270 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
271 /* move head pointer */
272 buf->head = c - buf->data + 1;
274 /* now write the first part of the message */
275 strncpy(buf->data + buf->tail, msg, l - k - 1);
277 /* ALWAYS terminate end of buffer w/ '\0' */
278 buf->data[buf->size - 1] = '\0';
280 /* now write out the rest of the string to the beginning of the buffer */
281 strcpy(buf->data, &msg[l - k - 1]);
283 /* we need to place the TAIL at the end of the message */
287 ("Weird! Can't find the terminator token from the beginning??? \n");
288 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
294 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
296 /* Note: There is also a function called "message()" in init.c */
297 /* Print a message to the log file. */
298 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
299 static void message(char *fmt, ...)
305 fl.l_whence = SEEK_SET;
309 #ifdef CONFIG_FEATURE_IPC_SYSLOG
310 if ((circular_logging == TRUE) && (buf != NULL)) {
313 va_start(arguments, fmt);
314 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
321 device_open(logFilePath,
322 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
325 fcntl(fd, F_SETLKW, &fl);
326 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
327 if ( logFileSize > 0 ) {
329 int r = fstat(fd, &statf);
330 if( !r && (statf.st_mode & S_IFREG)
331 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
332 if(logFileRotate > 0) {
334 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
335 for(i=logFileRotate-1;i>0;i--) {
336 sprintf(oldFile, "%s.%d", logFilePath, i-1);
337 sprintf(newFile, "%s.%d", logFilePath, i);
338 rename(oldFile, newFile);
340 sprintf(newFile, "%s.%d", logFilePath, 0);
342 fcntl (fd, F_SETLKW, &fl);
344 rename(logFilePath, newFile);
345 fd = device_open (logFilePath,
346 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
349 fcntl (fd, F_SETLKW, &fl);
356 va_start(arguments, fmt);
357 vdprintf(fd, fmt, arguments);
360 fcntl(fd, F_SETLKW, &fl);
363 /* Always send console messages to /dev/console so people will see them. */
365 device_open(_PATH_CONSOLE,
366 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
367 va_start(arguments, fmt);
368 vdprintf(fd, fmt, arguments);
372 fprintf(stderr, "Bummer, can't print: ");
373 va_start(arguments, fmt);
374 vfprintf(stderr, fmt, arguments);
381 static void logMessage(int pri, char *msg)
385 static char res[20] = "";
389 for (c_fac = facilitynames;
390 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
391 for (c_pri = prioritynames;
392 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
393 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
394 snprintf(res, sizeof(res), "<%d>", pri);
396 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
400 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
401 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
403 timestamp = ctime(&now) + 4;
404 timestamp[15] = '\0';
407 timestamp[15] = '\0';
411 /* todo: supress duplicates */
413 #ifdef CONFIG_FEATURE_REMOTE_LOG
414 /* send message to remote logger */
415 if (-1 != remotefd) {
416 static const int IOV_COUNT = 2;
417 struct iovec iov[IOV_COUNT];
418 struct iovec *v = iov;
420 memset(&res, 0, sizeof(res));
421 snprintf(res, sizeof(res), "<%d>", pri);
423 v->iov_len = strlen(res);
427 v->iov_len = strlen(msg);
429 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (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)
511 struct sockaddr_in remoteaddr;
512 struct hostent *hostinfo;
513 int len = sizeof(remoteaddr);
515 memset(&remoteaddr, 0, len);
517 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
520 bb_error_msg_and_die("cannot create socket");
523 hostinfo = xgethostbyname(RemoteHost);
525 remoteaddr.sin_family = AF_INET;
526 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
527 remoteaddr.sin_port = htons(RemotePort);
529 /* Since we are using UDP sockets, connect just sets the default host and port
530 * for future operations
532 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
533 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
540 static void doSyslogd(void) __attribute__ ((noreturn));
541 static void doSyslogd(void)
543 struct sockaddr_un sunx;
544 socklen_t addrLength;
549 /* Set up signal handlers. */
550 signal(SIGINT, quit_signal);
551 signal(SIGTERM, quit_signal);
552 signal(SIGQUIT, quit_signal);
553 signal(SIGHUP, SIG_IGN);
554 signal(SIGCHLD, SIG_IGN);
556 signal(SIGCLD, SIG_IGN);
558 signal(SIGALRM, domark);
561 /* Create the syslog file so realpath() can work. */
562 if (realpath(_PATH_LOG, lfile) != NULL) {
566 memset(&sunx, 0, sizeof(sunx));
567 sunx.sun_family = AF_UNIX;
568 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
569 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
570 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
574 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
575 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
576 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
579 if (chmod(lfile, 0666) < 0) {
580 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
582 #ifdef CONFIG_FEATURE_IPC_SYSLOG
583 if (circular_logging == TRUE) {
588 #ifdef CONFIG_FEATURE_REMOTE_LOG
589 if (doRemoteLog == TRUE) {
594 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
599 FD_SET(sock_fd, &fds);
601 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
602 if (errno == EINTR) {
603 /* alarm may have happened. */
606 bb_perror_msg_and_die("select error");
609 if (FD_ISSET(sock_fd, &fds)) {
612 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
614 memset(tmpbuf, '\0', MAXLINE + 1);
615 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
616 serveConnection(tmpbuf, i);
618 bb_perror_msg_and_die("UNIX socket error");
620 RELEASE_CONFIG_BUFFER(tmpbuf);
622 } /* for main loop */
625 extern int syslogd_main(int argc, char **argv)
633 /* do normal option parsing */
634 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC:")) > 0) {
637 MarkInterval = atoi(optarg) * 60;
643 logFilePath = optarg;
645 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
647 logFileSize = atoi(optarg) * 1024;
650 logFileRotate = atoi(optarg);
651 if( logFileRotate > 99 ) logFileRotate = 99;
654 #ifdef CONFIG_FEATURE_REMOTE_LOG
656 RemoteHost = bb_xstrdup(optarg);
657 if ((p = strchr(RemoteHost, ':'))) {
658 RemotePort = atoi(p + 1);
664 local_logging = TRUE;
667 #ifdef CONFIG_FEATURE_IPC_SYSLOG
670 int buf_size = atoi(optarg);
672 shm_size = buf_size * 1024;
675 circular_logging = TRUE;
686 #ifdef CONFIG_FEATURE_REMOTE_LOG
687 /* If they have not specified remote logging, then log locally */
688 if (doRemoteLog == FALSE)
689 local_logging = TRUE;
693 /* Store away localhost's name before the fork */
694 gethostname(LocalHostName, sizeof(LocalHostName));
695 if ((p = strchr(LocalHostName, '.'))) {
701 if (doFork == TRUE) {
702 #if defined(__uClinux__)
703 vfork_daemon_rexec(0, 1, argc, argv, "-n");
704 #else /* __uClinux__ */
706 bb_perror_msg_and_die("daemon");
707 #endif /* __uClinux__ */
716 c-file-style: "linux"