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>
57 int remoteLogLevel=-1;
60 /* Path for the file where all log messages are written */
61 #define __LOG_FILE "/var/log/messages"
63 /* Path to the unix socket */
64 static char lfile[MAXPATHLEN];
66 static const char *logFilePath = __LOG_FILE;
68 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
69 /* max size of message file before being rotated */
70 static int logFileSize = 200 * 1024;
72 /* number of rotated message files */
73 static int logFileRotate = 1;
76 /* interval between marks in seconds */
78 /*static int MarkInterval = 20 * 60; */
79 static int MarkInterval = 60 * 60;
82 /* localhost's name */
83 static char LocalHostName[64];
85 #ifdef CONFIG_FEATURE_REMOTE_LOG
86 #include <netinet/in.h>
87 /* udp socket for logging to remote host */
88 static int remotefd = -1;
89 static struct sockaddr_in remoteaddr;
91 /* where do we log? */
92 static char *RemoteHost;
94 /* what port to log to? */
95 static int RemotePort = 514;
97 /* To remote log or not to remote log, that is the question. */
98 static int doRemoteLog = FALSE;
99 static int local_logging = FALSE;
102 /* Make loging output smaller. */
103 static bool small = false;
105 #define MAXLINE 1024 /* maximum line length */
108 /* circular buffer variables/structures */
109 #ifdef CONFIG_FEATURE_IPC_SYSLOG
111 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
112 #error Sorry, you must set the syslogd buffer size to at least 4KB.
113 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
121 static const long KEY_ID = 0x414e4547; /*"GENA" */
123 // Semaphore operation structures
124 static struct shbuf_ds {
125 int size; // size of data written
126 int head; // start of message list
127 int tail; // end of message list
128 char data[1]; // data/messages
129 } *buf = NULL; // shared memory pointer
131 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
132 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
134 static int shmid = -1; // ipc shared memory id
135 static int s_semid = -1; // ipc semaphore id
136 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
137 static int circular_logging = FALSE;
139 static void init_RemoteLog(void);
142 * sem_up - up()'s a semaphore.
144 static inline void sem_up(int semid)
146 if (semop(semid, SMwup, 1) == -1) {
147 bb_perror_msg_and_die("semop[SMwup]");
152 * sem_down - down()'s a semaphore
154 static inline void sem_down(int semid)
156 if (semop(semid, SMwdn, 3) == -1) {
157 bb_perror_msg_and_die("semop[SMwdn]");
162 void ipcsyslog_cleanup(void)
164 printf("Exiting Syslogd!\n");
170 shmctl(shmid, IPC_RMID, NULL);
173 semctl(s_semid, 0, IPC_RMID, 0);
177 void ipcsyslog_init(void)
180 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
181 bb_perror_msg_and_die("shmget");
184 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
185 bb_perror_msg_and_die("shmat");
188 buf->size = shm_size - sizeof(*buf);
189 buf->head = buf->tail = 0;
191 // we'll trust the OS to set initial semval to 0 (let's hope)
192 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
193 if (errno == EEXIST) {
194 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
195 bb_perror_msg_and_die("semget");
198 bb_perror_msg_and_die("semget");
202 printf("Buffer already allocated just grab the semaphore?");
206 /* write message to buffer */
207 void circ_message(const char *msg)
209 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
214 * Circular Buffer Algorithm:
215 * --------------------------
217 * Start-off w/ empty buffer of specific size SHM_SIZ
218 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
219 * This is also very handy since we can do printf on message.
221 * Once the buffer is full we need to get rid of the first message in buffer and
222 * insert the new message. (Note: if the message being added is >1 message then
223 * we will need to "remove" >1 old message from the buffer). The way this is done
225 * When we reach the end of the buffer we set a mark and start from the beginning.
226 * Now what about the beginning and end of the buffer? Well we have the "head"
227 * index/pointer which is the starting point for the messages and we have "tail"
228 * index/pointer which is the ending point for the messages. When we "display" the
229 * messages we start from the beginning and continue until we reach "tail". If we
230 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
231 * "tail" are actually offsets from the beginning of the buffer.
233 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
234 * a threasafe way of handling shared memory operations.
236 if ((buf->tail + l) < buf->size) {
237 /* before we append the message we need to check the HEAD so that we won't
238 overwrite any of the message that we still need and adjust HEAD to point
239 to the next message! */
240 if (buf->tail < buf->head) {
241 if ((buf->tail + l) >= buf->head) {
242 /* we need to move the HEAD to point to the next message
243 * Theoretically we have enough room to add the whole message to the
244 * buffer, because of the first outer IF statement, so we don't have
245 * to worry about overflows here!
247 int k = buf->tail + l - buf->head; /* we need to know how many bytes
248 we are overwriting to make
251 memchr(buf->data + buf->head + k, '\0',
252 buf->size - (buf->head + k));
253 if (c != NULL) { /* do a sanity check just in case! */
254 buf->head = c - buf->data + 1; /* we need to convert pointer to
255 offset + skip the '\0' since
256 we need to point to the beginning
257 of the next message */
258 /* Note: HEAD is only used to "retrieve" messages, it's not used
259 when writing messages into our buffer */
260 } else { /* show an error message to know we messed up? */
261 printf("Weird! Can't find the terminator token??? \n");
267 /* in other cases no overflows have been done yet, so we don't care! */
268 /* we should be ok to append the message now */
269 strncpy(buf->data + buf->tail, msg, l); /* append our message */
270 buf->tail += l; /* count full message w/ '\0' terminating char */
272 /* we need to break up the message and "circle" it around */
274 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
276 /* We need to move HEAD! This is always the case since we are going
277 * to "circle" the message.
279 c = memchr(buf->data + k, '\0', buf->size - k);
281 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
282 /* move head pointer */
283 buf->head = c - buf->data + 1;
285 /* now write the first part of the message */
286 strncpy(buf->data + buf->tail, msg, l - k - 1);
288 /* ALWAYS terminate end of buffer w/ '\0' */
289 buf->data[buf->size - 1] = '\0';
291 /* now write out the rest of the string to the beginning of the buffer */
292 strcpy(buf->data, &msg[l - k - 1]);
294 /* we need to place the TAIL at the end of the message */
298 ("Weird! Can't find the terminator token from the beginning??? \n");
299 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
305 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
307 /* Note: There is also a function called "message()" in init.c */
308 /* Print a message to the log file. */
309 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
310 static void message(char *fmt, ...)
316 fl.l_whence = SEEK_SET;
320 #ifdef CONFIG_FEATURE_IPC_SYSLOG
321 if ((circular_logging == TRUE) && (buf != NULL)) {
324 va_start(arguments, fmt);
325 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
332 device_open(logFilePath,
333 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
336 fcntl(fd, F_SETLKW, &fl);
337 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
338 if ( logFileSize > 0 ) {
340 int r = fstat(fd, &statf);
341 if( !r && (statf.st_mode & S_IFREG)
342 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
343 if(logFileRotate > 0) {
345 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
346 for(i=logFileRotate-1;i>0;i--) {
347 sprintf(oldFile, "%s.%d", logFilePath, i-1);
348 sprintf(newFile, "%s.%d", logFilePath, i);
349 rename(oldFile, newFile);
351 sprintf(newFile, "%s.%d", logFilePath, 0);
353 fcntl (fd, F_SETLKW, &fl);
355 rename(logFilePath, newFile);
356 fd = device_open (logFilePath,
357 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
360 fcntl (fd, F_SETLKW, &fl);
367 va_start(arguments, fmt);
368 vdprintf(fd, fmt, arguments);
371 fcntl(fd, F_SETLKW, &fl);
374 /* Always send console messages to /dev/console so people will see them. */
376 device_open(_PATH_CONSOLE,
377 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
378 va_start(arguments, fmt);
379 vdprintf(fd, fmt, arguments);
383 fprintf(stderr, "Bummer, can't print: ");
384 va_start(arguments, fmt);
385 vfprintf(stderr, fmt, arguments);
392 static void logMessage(int pri, char *msg)
396 static char res[20] = "";
397 #ifdef CONFIG_FEATURE_REMOTE_LOG
398 static char line[MAXLINE + 1];
408 for (c_fac = facilitynames;
409 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
410 for (c_pri = prioritynames;
411 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
412 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
413 snprintf(res, sizeof(res), "<%d>", pri);
415 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
420 if ((pri != 0) && (c_pri->c_name != NULL)) {
421 if (c_pri->c_val > localLogLevel)
423 if (c_pri->c_val > remoteLogLevel)
426 if (!localLog && !remoteLog)
430 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
431 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
433 timestamp = ctime(&now) + 4;
434 timestamp[15] = '\0';
437 timestamp[15] = '\0';
441 /* todo: supress duplicates */
443 #ifdef CONFIG_FEATURE_REMOTE_LOG
444 if (doRemoteLog == TRUE) {
445 /* trying connect the socket */
446 if (-1 == remotefd) {
450 /* if we have a valid socket, send the message */
451 if (-1 != remotefd) {
453 snprintf(line, sizeof(line), "<%d> %s", pri, msg);
456 /* send message to remote logger */
457 if(( -1 == sendto(remotefd, line, strlen(line), 0,
458 (struct sockaddr *) &remoteaddr,
459 sizeof(remoteaddr))) && (errno == EINTR)) {
460 /* sleep now seconds and retry (with now * 2) */
469 // if (local_logging == TRUE)
470 /* bug fix ; circular_logging should do too; it only checked
471 * -L local logging. */
472 if (((local_logging == TRUE) || (circular_logging)) && localLog) {
474 // /* now spew out the message to wherever it is supposed to go */
475 // message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
476 /* brcm, add len of string to do log display with latestest event displayed first */
477 /* now spew out the message to wherever it is supposed to go; 4 spaces+ \0 + \n + len(3bytes) */
478 len = (strlen(timestamp)+strlen(LocalHostName)+strlen(res)+strlen(msg)+9);
479 message("%s %s %s %s %3i\n", timestamp, LocalHostName, res, msg, len);
480 #ifdef CONFIG_FEATURE_REMOTE_LOG
486 static void quit_signal(int sig)
489 char pidfilename[30];
490 /* change to priority emerg to be consistent with klogd */
491 logMessage(LOG_SYSLOG | LOG_EMERG, "System log daemon exiting.");
492 // logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
495 sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "syslogd");
496 unlink (pidfilename);
500 #ifdef CONFIG_FEATURE_IPC_SYSLOG
507 static void domark(int sig)
509 if (MarkInterval > 0) {
510 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
515 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
516 * enabled, we otherwise get a "storage size isn't constant error. */
517 static int serveConnection(char *tmpbuf, int n_read)
521 while (p < tmpbuf + n_read) {
523 int pri = (LOG_USER | LOG_NOTICE);
525 char line[MAXLINE + 1];
529 while ((c = *p) && q < &line[sizeof(line) - 1]) {
530 if (c == '<' && num_lt == 0) {
531 /* Parse the magic priority number. */
534 while (isdigit(*(++p))) {
535 pri = 10 * pri + (*p - '0');
537 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
538 pri = (LOG_USER | LOG_NOTICE);
540 } else if (c == '\n') {
542 } else if (iscntrl(c) && (c < 0177)) {
553 logMessage(pri, line);
559 #ifdef CONFIG_FEATURE_REMOTE_LOG
560 static void init_RemoteLog(void)
562 memset(&remoteaddr, 0, sizeof(remoteaddr));
563 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
567 if ((local_logging == FALSE) && (circular_logging == FALSE))
568 bb_error_msg_and_die("cannot create socket");
570 bb_perror_msg("cannot create socket");
574 remoteaddr.sin_family = AF_INET;
575 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
576 remoteaddr.sin_port = htons(RemotePort);
580 static void doSyslogd(void) __attribute__ ((noreturn));
581 static void doSyslogd(void)
583 struct sockaddr_un sunx;
584 socklen_t addrLength;
587 char pidfilename[30];
588 /* All the access to /dev/log will be redirected to /var/log/log
589 * * which is TMPFS, memory file system.
591 #define BRCM_PATH_LOG "/var/log/log"
597 /* Set up signal handlers. */
598 signal(SIGINT, quit_signal);
599 signal(SIGTERM, quit_signal);
600 signal(SIGQUIT, quit_signal);
601 signal(SIGHUP, SIG_IGN);
602 signal(SIGCHLD, SIG_IGN);
604 signal(SIGCLD, SIG_IGN);
606 signal(SIGALRM, domark);
609 /* Create the syslog file so realpath() can work. */
612 if (realpath (_PATH_LOG, lfile) != NULL) {
614 if (realpath (BRCM_PATH_LOG, lfile) != NULL) {
619 memset(&sunx, 0, sizeof(sunx));
620 sunx.sun_family = AF_UNIX;
621 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
622 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
624 /* bb_perror_msg_and_die("Couldn't get file descriptor for socket "
627 bb_error_msg_and_die ("Couldn't get file descriptor for socket " BRCM_PATH_LOG);
631 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
632 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
634 /* bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
636 bb_error_msg_and_die ("Could not connect to socket " BRCM_PATH_LOG);
640 if (chmod(lfile, 0666) < 0) {
642 /* bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
644 bb_error_msg_and_die ("Could not set permission on " BRCM_PATH_LOG);
647 #ifdef CONFIG_FEATURE_IPC_SYSLOG
648 if (circular_logging == TRUE) {
653 #ifdef CONFIG_FEATURE_REMOTE_LOG
654 if (doRemoteLog == TRUE) {
660 if (localLogLevel < LOG_EMERG)
661 localLogLevel = LOG_DEBUG;
662 if (remoteLogLevel < LOG_EMERG)
663 remoteLogLevel = LOG_ERR;
665 /* change to priority emerg to be consistent with klogd */
666 /* logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER); */
667 logMessage (LOG_SYSLOG | LOG_EMERG, "BCM96345 started: " BB_BANNER);
669 sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "syslogd");
670 if ((pidfile = fopen (pidfilename, "w")) != NULL) {
671 fprintf (pidfile, "%d\n", getpid ());
672 (void) fclose (pidfile);
675 logMessage ((LOG_SYSLOG | LOG_ERR),("Failed to create pid file %s", pidfilename));
682 FD_SET(sock_fd, &fds);
684 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
685 if (errno == EINTR) {
686 /* alarm may have happened. */
689 bb_perror_msg_and_die("select error");
692 if (FD_ISSET(sock_fd, &fds)) {
695 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
697 memset(tmpbuf, '\0', MAXLINE + 1);
698 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
699 serveConnection(tmpbuf, i);
701 bb_perror_msg_and_die("UNIX socket error");
703 RELEASE_CONFIG_BUFFER(tmpbuf);
705 } /* for main loop */
708 extern int syslogd_main(int argc, char **argv)
716 /* do normal option parsing */
719 while ((opt = getopt(argc, argv, "m:nO:s:b:R:LC::")) > 0) {
721 /* brcm, l - local log level, r - remote log level */
722 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:l:r:LC")) > 0) {
726 MarkInterval = atoi(optarg) * 60;
732 logFilePath = optarg;
734 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
736 logFileSize = atoi(optarg) * 1024;
739 logFileRotate = atoi(optarg);
740 if( logFileRotate > 99 ) logFileRotate = 99;
743 #ifdef CONFIG_FEATURE_REMOTE_LOG
745 RemoteHost = bb_xstrdup(optarg);
746 if ((p = strchr(RemoteHost, ':'))) {
747 RemotePort = atoi(p + 1);
753 local_logging = TRUE;
756 #ifdef CONFIG_FEATURE_IPC_SYSLOG
759 int buf_size = atoi(optarg);
761 shm_size = buf_size * 1024;
764 circular_logging = TRUE;
772 localLogLevel = atoi(optarg);
775 remoteLogLevel = atoi(optarg);
783 #ifdef CONFIG_FEATURE_REMOTE_LOG
784 /* If they have not specified remote logging, then log locally */
785 if (doRemoteLog == FALSE)
786 local_logging = TRUE;
790 /* Store away localhost's name before the fork */
791 gethostname(LocalHostName, sizeof(LocalHostName));
792 if ((p = strchr(LocalHostName, '.'))) {
798 if (doFork == TRUE) {
799 #if defined(__uClinux__)
800 vfork_daemon_rexec(0, 1, argc, argv, "-n");
801 #else /* __uClinux__ */
803 bb_perror_msg_and_die("daemon");
804 #endif /* __uClinux__ */
813 c-file-style: "linux"