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;
83 /* where do we log? */
84 static char *RemoteHost;
86 /* what port to log to? */
87 static int RemotePort = 514;
89 /* To remote log or not to remote log, that is the question. */
90 static int doRemoteLog = FALSE;
91 static int local_logging = FALSE;
94 /* Make loging output smaller. */
95 static bool small = false;
98 #define MAXLINE 1024 /* maximum line length */
101 /* circular buffer variables/structures */
102 #ifdef CONFIG_FEATURE_IPC_SYSLOG
104 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
105 #error Sorry, you must set the syslogd buffer size to at least 4KB.
106 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
114 static const long KEY_ID = 0x414e4547; /*"GENA" */
116 // Semaphore operation structures
117 static struct shbuf_ds {
118 int size; // size of data written
119 int head; // start of message list
120 int tail; // end of message list
121 char data[1]; // data/messages
122 } *buf = NULL; // shared memory pointer
124 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
125 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
127 static int shmid = -1; // ipc shared memory id
128 static int s_semid = -1; // ipc semaphore id
129 static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
130 static int circular_logging = FALSE;
133 * sem_up - up()'s a semaphore.
135 static inline void sem_up(int semid)
137 if (semop(semid, SMwup, 1) == -1) {
138 bb_perror_msg_and_die("semop[SMwup]");
143 * sem_down - down()'s a semaphore
145 static inline void sem_down(int semid)
147 if (semop(semid, SMwdn, 3) == -1) {
148 bb_perror_msg_and_die("semop[SMwdn]");
153 static void ipcsyslog_cleanup(void)
155 printf("Exiting Syslogd!\n");
161 shmctl(shmid, IPC_RMID, NULL);
164 semctl(s_semid, 0, IPC_RMID, 0);
168 static void ipcsyslog_init(void)
171 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
172 bb_perror_msg_and_die("shmget");
175 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
176 bb_perror_msg_and_die("shmat");
179 buf->size = shm_size - sizeof(*buf);
180 buf->head = buf->tail = 0;
182 // we'll trust the OS to set initial semval to 0 (let's hope)
183 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
184 if (errno == EEXIST) {
185 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
186 bb_perror_msg_and_die("semget");
189 bb_perror_msg_and_die("semget");
193 printf("Buffer already allocated just grab the semaphore?");
197 /* write message to buffer */
198 static void circ_message(const char *msg)
200 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
205 * Circular Buffer Algorithm:
206 * --------------------------
208 * Start-off w/ empty buffer of specific size SHM_SIZ
209 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
210 * This is also very handy since we can do printf on message.
212 * Once the buffer is full we need to get rid of the first message in buffer and
213 * insert the new message. (Note: if the message being added is >1 message then
214 * we will need to "remove" >1 old message from the buffer). The way this is done
216 * When we reach the end of the buffer we set a mark and start from the beginning.
217 * Now what about the beginning and end of the buffer? Well we have the "head"
218 * index/pointer which is the starting point for the messages and we have "tail"
219 * index/pointer which is the ending point for the messages. When we "display" the
220 * messages we start from the beginning and continue until we reach "tail". If we
221 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
222 * "tail" are actually offsets from the beginning of the buffer.
224 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
225 * a threasafe way of handling shared memory operations.
227 if ((buf->tail + l) < buf->size) {
228 /* before we append the message we need to check the HEAD so that we won't
229 overwrite any of the message that we still need and adjust HEAD to point
230 to the next message! */
231 if (buf->tail < buf->head) {
232 if ((buf->tail + l) >= buf->head) {
233 /* we need to move the HEAD to point to the next message
234 * Theoretically we have enough room to add the whole message to the
235 * buffer, because of the first outer IF statement, so we don't have
236 * to worry about overflows here!
238 int k = buf->tail + l - buf->head; /* we need to know how many bytes
239 we are overwriting to make
242 memchr(buf->data + buf->head + k, '\0',
243 buf->size - (buf->head + k));
244 if (c != NULL) { /* do a sanity check just in case! */
245 buf->head = c - buf->data + 1; /* we need to convert pointer to
246 offset + skip the '\0' since
247 we need to point to the beginning
248 of the next message */
249 /* Note: HEAD is only used to "retrieve" messages, it's not used
250 when writing messages into our buffer */
251 } else { /* show an error message to know we messed up? */
252 printf("Weird! Can't find the terminator token??? \n");
258 /* in other cases no overflows have been done yet, so we don't care! */
259 /* we should be ok to append the message now */
260 strncpy(buf->data + buf->tail, msg, l); /* append our message */
261 buf->tail += l; /* count full message w/ '\0' terminating char */
263 /* we need to break up the message and "circle" it around */
265 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
267 /* We need to move HEAD! This is always the case since we are going
268 * to "circle" the message.
270 c = memchr(buf->data + k, '\0', buf->size - k);
272 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
273 /* move head pointer */
274 buf->head = c - buf->data + 1;
276 /* now write the first part of the message */
277 strncpy(buf->data + buf->tail, msg, l - k - 1);
279 /* ALWAYS terminate end of buffer w/ '\0' */
280 buf->data[buf->size - 1] = '\0';
282 /* now write out the rest of the string to the beginning of the buffer */
283 strcpy(buf->data, &msg[l - k - 1]);
285 /* we need to place the TAIL at the end of the message */
289 ("Weird! Can't find the terminator token from the beginning??? \n");
290 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
296 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
298 /* Note: There is also a function called "message()" in init.c */
299 /* Print a message to the log file. */
300 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
301 static void message(char *fmt, ...)
307 fl.l_whence = SEEK_SET;
311 #ifdef CONFIG_FEATURE_IPC_SYSLOG
312 if ((circular_logging == TRUE) && (buf != NULL)) {
315 va_start(arguments, fmt);
316 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
323 device_open(logFilePath,
324 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
327 fcntl(fd, F_SETLKW, &fl);
328 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
329 if ( logFileSize > 0 ) {
331 int r = fstat(fd, &statf);
332 if( !r && (statf.st_mode & S_IFREG)
333 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
334 if(logFileRotate > 0) {
336 char oldFile[(strlen(logFilePath)+4)], newFile[(strlen(logFilePath)+4)];
337 for(i=logFileRotate-1;i>0;i--) {
338 sprintf(oldFile, "%s.%d", logFilePath, i-1);
339 sprintf(newFile, "%s.%d", logFilePath, i);
340 rename(oldFile, newFile);
342 sprintf(newFile, "%s.%d", logFilePath, 0);
344 fcntl (fd, F_SETLKW, &fl);
346 rename(logFilePath, newFile);
347 fd = device_open (logFilePath,
348 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
351 fcntl (fd, F_SETLKW, &fl);
358 va_start(arguments, fmt);
359 vdprintf(fd, fmt, arguments);
362 fcntl(fd, F_SETLKW, &fl);
365 /* Always send console messages to /dev/console so people will see them. */
367 device_open(_PATH_CONSOLE,
368 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
369 va_start(arguments, fmt);
370 vdprintf(fd, fmt, arguments);
374 fprintf(stderr, "Bummer, can't print: ");
375 va_start(arguments, fmt);
376 vfprintf(stderr, fmt, arguments);
383 #ifdef CONFIG_FEATURE_REMOTE_LOG
384 static void init_RemoteLog(void)
386 memset(&remoteaddr, 0, sizeof(remoteaddr));
387 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
390 bb_error_msg("cannot create socket");
393 remoteaddr.sin_family = AF_INET;
394 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
395 remoteaddr.sin_port = htons(RemotePort);
399 static void logMessage(int pri, char *msg)
403 static char res[20] = "";
404 #ifdef CONFIG_FEATURE_REMOTE_LOG
405 static char line[MAXLINE + 1];
410 for (c_fac = facilitynames;
411 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
412 for (c_pri = prioritynames;
413 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
414 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
415 snprintf(res, sizeof(res), "<%d>", pri);
417 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
421 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
422 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
424 timestamp = ctime(&now) + 4;
425 timestamp[15] = '\0';
428 timestamp[15] = '\0';
432 /* todo: supress duplicates */
434 #ifdef CONFIG_FEATURE_REMOTE_LOG
435 if (doRemoteLog == TRUE) {
436 /* trying connect the socket */
437 if (-1 == remotefd) {
441 /* if we have a valid socket, send the message */
442 if (-1 != remotefd) {
444 snprintf(line, sizeof(line), "<%d>%s", pri, msg);
447 /* send message to remote logger */
448 if(( -1 == sendto(remotefd, line, strlen(line), 0,
449 (struct sockaddr *) &remoteaddr,
450 sizeof(remoteaddr))) && (errno == EINTR)) {
451 /* sleep now seconds and retry (with now * 2) */
459 if (local_logging == TRUE)
462 /* now spew out the message to wherever it is supposed to go */
464 message("%s %s\n", timestamp, msg);
466 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
470 static void quit_signal(int sig)
472 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
474 #ifdef CONFIG_FEATURE_IPC_SYSLOG
481 static void domark(int sig)
483 if (MarkInterval > 0) {
484 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
489 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
490 * enabled, we otherwise get a "storage size isn't constant error. */
491 static int serveConnection(char *tmpbuf, int n_read)
495 while (p < tmpbuf + n_read) {
497 int pri = (LOG_USER | LOG_NOTICE);
499 char line[MAXLINE + 1];
503 while ((c = *p) && q < &line[sizeof(line) - 1]) {
504 if (c == '<' && num_lt == 0) {
505 /* Parse the magic priority number. */
508 while (isdigit(*(++p))) {
509 pri = 10 * pri + (*p - '0');
511 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
512 pri = (LOG_USER | LOG_NOTICE);
514 } else if (c == '\n') {
516 } else if (iscntrl(c) && (c < 0177)) {
527 logMessage(pri, line);
532 static void doSyslogd(void) __attribute__ ((noreturn));
533 static void doSyslogd(void)
535 struct sockaddr_un sunx;
536 socklen_t addrLength;
541 /* Set up signal handlers. */
542 signal(SIGINT, quit_signal);
543 signal(SIGTERM, quit_signal);
544 signal(SIGQUIT, quit_signal);
545 signal(SIGHUP, SIG_IGN);
546 signal(SIGCHLD, SIG_IGN);
548 signal(SIGCLD, SIG_IGN);
550 signal(SIGALRM, domark);
553 /* Create the syslog file so realpath() can work. */
554 if (realpath(_PATH_LOG, lfile) != NULL) {
558 memset(&sunx, 0, sizeof(sunx));
559 sunx.sun_family = AF_UNIX;
560 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
561 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
562 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
566 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
567 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
568 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
571 if (chmod(lfile, 0666) < 0) {
572 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
574 #ifdef CONFIG_FEATURE_IPC_SYSLOG
575 if (circular_logging == TRUE) {
580 #ifdef CONFIG_FEATURE_REMOTE_LOG
581 if (doRemoteLog == TRUE) {
586 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
591 FD_SET(sock_fd, &fds);
593 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
594 if (errno == EINTR) {
595 /* alarm may have happened. */
598 bb_perror_msg_and_die("select error");
601 if (FD_ISSET(sock_fd, &fds)) {
604 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
606 memset(tmpbuf, '\0', MAXLINE + 1);
607 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
608 serveConnection(tmpbuf, i);
610 bb_perror_msg_and_die("UNIX socket error");
612 RELEASE_CONFIG_BUFFER(tmpbuf);
614 } /* for main loop */
617 extern int syslogd_main(int argc, char **argv)
625 /* do normal option parsing */
626 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
629 MarkInterval = atoi(optarg) * 60;
635 logFilePath = optarg;
637 #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
639 logFileSize = atoi(optarg) * 1024;
642 logFileRotate = atoi(optarg);
643 if( logFileRotate > 99 ) logFileRotate = 99;
646 #ifdef CONFIG_FEATURE_REMOTE_LOG
648 RemoteHost = bb_xstrdup(optarg);
649 if ((p = strchr(RemoteHost, ':'))) {
650 RemotePort = atoi(p + 1);
656 local_logging = TRUE;
659 #ifdef CONFIG_FEATURE_IPC_SYSLOG
662 int buf_size = atoi(optarg);
664 shm_size = buf_size * 1024;
667 circular_logging = TRUE;
678 #ifdef CONFIG_FEATURE_REMOTE_LOG
679 /* If they have not specified remote logging, then log locally */
680 if (doRemoteLog == FALSE)
681 local_logging = TRUE;
685 /* Store away localhost's name before the fork */
686 gethostname(LocalHostName, sizeof(LocalHostName));
687 if ((p = strchr(LocalHostName, '.'))) {
693 if (doFork == TRUE) {
694 #if defined(__uClinux__)
695 vfork_daemon_rexec(0, 1, argc, argv, "-n");
696 #else /* __uClinux__ */
698 bb_perror_msg_and_die("daemon");
699 #endif /* __uClinux__ */
708 c-file-style: "linux"