1 /* vi: set sw=4 ts=4: */
3 * Mini syslogd implementation for busybox
5 * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen
6 * Copyright (C) 1999,2000,2001 by Erik Andersen <andersee@debian.org>
8 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
10 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
12 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * 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 char *logFilePath = __LOG_FILE;
62 /* interval between marks in seconds */
63 static int MarkInterval = 20 * 60;
65 /* localhost's name */
66 static char LocalHostName[64];
68 #ifdef CONFIG_FEATURE_REMOTE_LOG
69 #include <netinet/in.h>
70 /* udp socket for logging to remote host */
71 static int remotefd = -1;
73 /* where do we log? */
74 static char *RemoteHost;
76 /* what port to log to? */
77 static int RemotePort = 514;
79 /* To remote log or not to remote log, that is the question. */
80 static int doRemoteLog = FALSE;
81 static int local_logging = FALSE;
85 #define MAXLINE 1024 /* maximum line length */
88 /* circular buffer variables/structures */
89 #ifdef CONFIG_FEATURE_IPC_SYSLOG
90 #if __GNU_LIBRARY__ < 5
91 #error Sorry. Looks like you are using libc5.
92 #error libc5 shm support isnt good enough.
93 #error Please disable CONFIG_FEATURE_IPC_SYSLOG
101 static const long KEY_ID = 0x414e4547; /*"GENA" */
103 // Semaphore operation structures
104 static struct shbuf_ds {
105 int size; // size of data written
106 int head; // start of message list
107 int tail; // end of message list
108 char data[1]; // data/messages
109 } *buf = NULL; // shared memory pointer
111 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
112 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
114 static int shmid = -1; // ipc shared memory id
115 static int s_semid = -1; // ipc semaphore id
116 int data_size = 16000; // data size
117 int shm_size = 16000 + sizeof(*buf); // our buffer size
118 static int circular_logging = FALSE;
121 * sem_up - up()'s a semaphore.
123 static inline void sem_up(int semid)
125 if (semop(semid, SMwup, 1) == -1) {
126 bb_perror_msg_and_die("semop[SMwup]");
131 * sem_down - down()'s a semaphore
133 static inline void sem_down(int semid)
135 if (semop(semid, SMwdn, 3) == -1) {
136 bb_perror_msg_and_die("semop[SMwdn]");
141 void ipcsyslog_cleanup(void)
143 printf("Exiting Syslogd!\n");
149 shmctl(shmid, IPC_RMID, NULL);
152 semctl(s_semid, 0, IPC_RMID, 0);
156 void ipcsyslog_init(void)
159 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
160 bb_perror_msg_and_die("shmget");
163 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
164 bb_perror_msg_and_die("shmat");
167 buf->size = data_size;
168 buf->head = buf->tail = 0;
170 // we'll trust the OS to set initial semval to 0 (let's hope)
171 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
172 if (errno == EEXIST) {
173 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
174 bb_perror_msg_and_die("semget");
177 bb_perror_msg_and_die("semget");
181 printf("Buffer already allocated just grab the semaphore?");
185 /* write message to buffer */
186 void circ_message(const char *msg)
188 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
193 * Circular Buffer Algorithm:
194 * --------------------------
196 * Start-off w/ empty buffer of specific size SHM_SIZ
197 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
198 * This is also very handy since we can do printf on message.
200 * Once the buffer is full we need to get rid of the first message in buffer and
201 * insert the new message. (Note: if the message being added is >1 message then
202 * we will need to "remove" >1 old message from the buffer). The way this is done
204 * When we reach the end of the buffer we set a mark and start from the beginning.
205 * Now what about the beginning and end of the buffer? Well we have the "head"
206 * index/pointer which is the starting point for the messages and we have "tail"
207 * index/pointer which is the ending point for the messages. When we "display" the
208 * messages we start from the beginning and continue until we reach "tail". If we
209 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
210 * "tail" are actually offsets from the beginning of the buffer.
212 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
213 * a threasafe way of handling shared memory operations.
215 if ((buf->tail + l) < buf->size) {
216 /* before we append the message we need to check the HEAD so that we won't
217 overwrite any of the message that we still need and adjust HEAD to point
218 to the next message! */
219 if (buf->tail < buf->head) {
220 if ((buf->tail + l) >= buf->head) {
221 /* we need to move the HEAD to point to the next message
222 * Theoretically we have enough room to add the whole message to the
223 * buffer, because of the first outer IF statement, so we don't have
224 * to worry about overflows here!
226 int k = buf->tail + l - buf->head; /* we need to know how many bytes
227 we are overwriting to make
230 memchr(buf->data + buf->head + k, '\0',
231 buf->size - (buf->head + k));
232 if (c != NULL) { /* do a sanity check just in case! */
233 buf->head = c - buf->data + 1; /* we need to convert pointer to
234 offset + skip the '\0' since
235 we need to point to the beginning
236 of the next message */
237 /* Note: HEAD is only used to "retrieve" messages, it's not used
238 when writing messages into our buffer */
239 } else { /* show an error message to know we messed up? */
240 printf("Weird! Can't find the terminator token??? \n");
246 /* in other cases no overflows have been done yet, so we don't care! */
247 /* we should be ok to append the message now */
248 strncpy(buf->data + buf->tail, msg, l); /* append our message */
249 buf->tail += l; /* count full message w/ '\0' terminating char */
251 /* we need to break up the message and "circle" it around */
253 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
255 /* We need to move HEAD! This is always the case since we are going
256 * to "circle" the message.
258 c = memchr(buf->data + k, '\0', buf->size - k);
260 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
261 /* move head pointer */
262 buf->head = c - buf->data + 1;
264 /* now write the first part of the message */
265 strncpy(buf->data + buf->tail, msg, l - k - 1);
267 /* ALWAYS terminate end of buffer w/ '\0' */
268 buf->data[buf->size - 1] = '\0';
270 /* now write out the rest of the string to the beginning of the buffer */
271 strcpy(buf->data, &msg[l - k - 1]);
273 /* we need to place the TAIL at the end of the message */
277 ("Weird! Can't find the terminator token from the beginning??? \n");
278 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
284 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
286 /* Note: There is also a function called "message()" in init.c */
287 /* Print a message to the log file. */
288 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
289 static void message(char *fmt, ...)
295 fl.l_whence = SEEK_SET;
299 #ifdef CONFIG_FEATURE_IPC_SYSLOG
300 if ((circular_logging == TRUE) && (buf != NULL)) {
303 va_start(arguments, fmt);
304 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
311 device_open(logFilePath,
312 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
315 fcntl(fd, F_SETLKW, &fl);
316 va_start(arguments, fmt);
317 vdprintf(fd, fmt, arguments);
320 fcntl(fd, F_SETLKW, &fl);
323 /* Always send console messages to /dev/console so people will see them. */
325 device_open(_PATH_CONSOLE,
326 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
327 va_start(arguments, fmt);
328 vdprintf(fd, fmt, arguments);
332 fprintf(stderr, "Bummer, can't print: ");
333 va_start(arguments, fmt);
334 vfprintf(stderr, fmt, arguments);
341 static void logMessage(int pri, char *msg)
345 static char res[20] = "";
349 for (c_fac = facilitynames;
350 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
351 for (c_pri = prioritynames;
352 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
353 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
354 snprintf(res, sizeof(res), "<%d>", pri);
356 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
360 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
361 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
363 timestamp = ctime(&now) + 4;
364 timestamp[15] = '\0';
367 timestamp[15] = '\0';
371 /* todo: supress duplicates */
373 #ifdef CONFIG_FEATURE_REMOTE_LOG
374 /* send message to remote logger */
375 if (-1 != remotefd) {
376 static const int IOV_COUNT = 2;
377 struct iovec iov[IOV_COUNT];
378 struct iovec *v = iov;
380 memset(&res, 0, sizeof(res));
381 snprintf(res, sizeof(res), "<%d>", pri);
383 v->iov_len = strlen(res);
387 v->iov_len = strlen(msg);
389 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
393 if (local_logging == TRUE)
395 /* now spew out the message to wherever it is supposed to go */
396 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
399 static void quit_signal(int sig)
401 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
403 #ifdef CONFIG_FEATURE_IPC_SYSLOG
410 static void domark(int sig)
412 if (MarkInterval > 0) {
413 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
418 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
419 * enabled, we otherwise get a "storage size isn't constant error. */
420 static int serveConnection(char *tmpbuf, int n_read)
424 while (p < tmpbuf + n_read) {
426 int pri = (LOG_USER | LOG_NOTICE);
427 char line[MAXLINE + 1];
433 while ((c = *p) && q < &line[sizeof(line) - 1]) {
434 if (c == '<' && p1 == 0) {
435 /* Parse the magic priority number. */
439 while (isdigit(*(++p))) {
440 pri = 10 * pri + (*p - '0');
448 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
449 pri = (LOG_USER | LOG_NOTICE);
452 } else if (c == '\n') {
454 } else if (iscntrl(c) && (c < 0177)) {
465 logMessage(pri, line);
471 #ifdef CONFIG_FEATURE_REMOTE_LOG
472 static void init_RemoteLog(void)
475 struct sockaddr_in remoteaddr;
476 struct hostent *hostinfo;
477 int len = sizeof(remoteaddr);
479 memset(&remoteaddr, 0, len);
481 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
484 bb_error_msg_and_die("cannot create socket");
487 hostinfo = xgethostbyname(RemoteHost);
489 remoteaddr.sin_family = AF_INET;
490 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
491 remoteaddr.sin_port = htons(RemotePort);
493 /* Since we are using UDP sockets, connect just sets the default host and port
494 * for future operations
496 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
497 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
504 static void doSyslogd(void) __attribute__ ((noreturn));
505 static void doSyslogd(void)
507 struct sockaddr_un sunx;
508 socklen_t addrLength;
513 /* Set up signal handlers. */
514 signal(SIGINT, quit_signal);
515 signal(SIGTERM, quit_signal);
516 signal(SIGQUIT, quit_signal);
517 signal(SIGHUP, SIG_IGN);
518 signal(SIGCHLD, SIG_IGN);
520 signal(SIGCLD, SIG_IGN);
522 signal(SIGALRM, domark);
525 /* Create the syslog file so realpath() can work. */
526 if (realpath(_PATH_LOG, lfile) != NULL) {
530 memset(&sunx, 0, sizeof(sunx));
531 sunx.sun_family = AF_UNIX;
532 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
533 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
534 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
538 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
539 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
540 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
543 if (chmod(lfile, 0666) < 0) {
544 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
546 #ifdef CONFIG_FEATURE_IPC_SYSLOG
547 if (circular_logging == TRUE) {
552 #ifdef CONFIG_FEATURE_REMOTE_LOG
553 if (doRemoteLog == TRUE) {
558 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
563 FD_SET(sock_fd, &fds);
565 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
566 if (errno == EINTR) {
567 /* alarm may have happened. */
570 bb_perror_msg_and_die("select error");
573 if (FD_ISSET(sock_fd, &fds)) {
576 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
578 memset(tmpbuf, '\0', MAXLINE + 1);
579 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
580 serveConnection(tmpbuf, i);
582 bb_perror_msg_and_die("UNIX socket error");
584 RELEASE_CONFIG_BUFFER(tmpbuf);
586 } /* for main loop */
589 extern int syslogd_main(int argc, char **argv)
593 #if ! defined(__uClinux__)
599 /* do normal option parsing */
600 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
603 MarkInterval = atoi(optarg) * 60;
605 #if ! defined(__uClinux__)
611 logFilePath = bb_xstrdup(optarg);
613 #ifdef CONFIG_FEATURE_REMOTE_LOG
615 RemoteHost = bb_xstrdup(optarg);
616 if ((p = strchr(RemoteHost, ':'))) {
617 RemotePort = atoi(p + 1);
623 local_logging = TRUE;
626 #ifdef CONFIG_FEATURE_IPC_SYSLOG
628 circular_logging = TRUE;
636 #ifdef CONFIG_FEATURE_REMOTE_LOG
637 /* If they have not specified remote logging, then log locally */
638 if (doRemoteLog == FALSE)
639 local_logging = TRUE;
643 /* Store away localhost's name before the fork */
644 gethostname(LocalHostName, sizeof(LocalHostName));
645 if ((p = strchr(LocalHostName, '.'))) {
651 #if ! defined(__uClinux__)
652 if ((doFork == TRUE) && (daemon(0, 1) < 0)) {
653 bb_perror_msg_and_die("daemon");
663 c-file-style: "linux"