1 /* vi: set sw=4 ts=4: */
3 * Mini syslogd implementation for busybox
5 * Copyright (C) 1999-2003 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
41 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/param.h>
48 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
50 #include <sys/syslog.h>
53 /* Path for the file where all log messages are written */
54 #define __LOG_FILE "/var/log/messages"
56 /* Path to the unix socket */
57 static char lfile[MAXPATHLEN];
59 static const char *logFilePath = __LOG_FILE;
61 /* interval between marks in seconds */
62 static int MarkInterval = 20 * 60;
64 /* localhost's name */
65 static char LocalHostName[64];
67 #ifdef CONFIG_FEATURE_REMOTE_LOG
68 #include <netinet/in.h>
69 /* udp socket for logging to remote host */
70 static int remotefd = -1;
72 /* where do we log? */
73 static char *RemoteHost;
75 /* what port to log to? */
76 static int RemotePort = 514;
78 /* To remote log or not to remote log, that is the question. */
79 static int doRemoteLog = FALSE;
80 static int local_logging = FALSE;
84 #define MAXLINE 1024 /* maximum line length */
87 /* circular buffer variables/structures */
88 #ifdef CONFIG_FEATURE_IPC_SYSLOG
94 static const long KEY_ID = 0x414e4547; /*"GENA" */
96 // Semaphore operation structures
97 static struct shbuf_ds {
98 int size; // size of data written
99 int head; // start of message list
100 int tail; // end of message list
101 char data[1]; // data/messages
102 } *buf = NULL; // shared memory pointer
104 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
105 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
107 static int shmid = -1; // ipc shared memory id
108 static int s_semid = -1; // ipc semaphore id
109 static int data_size = 16000; // default data size
110 static int circular_logging = FALSE;
113 * sem_up - up()'s a semaphore.
115 static inline void sem_up(int semid)
117 if (semop(semid, SMwup, 1) == -1) {
118 bb_perror_msg_and_die("semop[SMwup]");
123 * sem_down - down()'s a semaphore
125 static inline void sem_down(int semid)
127 if (semop(semid, SMwdn, 3) == -1) {
128 bb_perror_msg_and_die("semop[SMwdn]");
133 void ipcsyslog_cleanup(void)
135 printf("Exiting Syslogd!\n");
141 shmctl(shmid, IPC_RMID, NULL);
144 semctl(s_semid, 0, IPC_RMID, 0);
148 void ipcsyslog_init(void)
151 if ((shmid = shmget(KEY_ID, data_size, IPC_CREAT | 1023)) == -1) {
152 bb_perror_msg_and_die("shmget");
155 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
156 bb_perror_msg_and_die("shmat");
159 buf->size = data_size - sizeof(*buf);
160 buf->head = buf->tail = 0;
162 // we'll trust the OS to set initial semval to 0 (let's hope)
163 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
164 if (errno == EEXIST) {
165 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
166 bb_perror_msg_and_die("semget");
169 bb_perror_msg_and_die("semget");
173 printf("Buffer already allocated just grab the semaphore?");
177 /* write message to buffer */
178 void circ_message(const char *msg)
180 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
185 * Circular Buffer Algorithm:
186 * --------------------------
188 * Start-off w/ empty buffer of specific size SHM_SIZ
189 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
190 * This is also very handy since we can do printf on message.
192 * Once the buffer is full we need to get rid of the first message in buffer and
193 * insert the new message. (Note: if the message being added is >1 message then
194 * we will need to "remove" >1 old message from the buffer). The way this is done
196 * When we reach the end of the buffer we set a mark and start from the beginning.
197 * Now what about the beginning and end of the buffer? Well we have the "head"
198 * index/pointer which is the starting point for the messages and we have "tail"
199 * index/pointer which is the ending point for the messages. When we "display" the
200 * messages we start from the beginning and continue until we reach "tail". If we
201 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
202 * "tail" are actually offsets from the beginning of the buffer.
204 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
205 * a threasafe way of handling shared memory operations.
207 if ((buf->tail + l) < buf->size) {
208 /* before we append the message we need to check the HEAD so that we won't
209 overwrite any of the message that we still need and adjust HEAD to point
210 to the next message! */
211 if (buf->tail < buf->head) {
212 if ((buf->tail + l) >= buf->head) {
213 /* we need to move the HEAD to point to the next message
214 * Theoretically we have enough room to add the whole message to the
215 * buffer, because of the first outer IF statement, so we don't have
216 * to worry about overflows here!
218 int k = buf->tail + l - buf->head; /* we need to know how many bytes
219 we are overwriting to make
222 memchr(buf->data + buf->head + k, '\0',
223 buf->size - (buf->head + k));
224 if (c != NULL) { /* do a sanity check just in case! */
225 buf->head = c - buf->data + 1; /* we need to convert pointer to
226 offset + skip the '\0' since
227 we need to point to the beginning
228 of the next message */
229 /* Note: HEAD is only used to "retrieve" messages, it's not used
230 when writing messages into our buffer */
231 } else { /* show an error message to know we messed up? */
232 printf("Weird! Can't find the terminator token??? \n");
238 /* in other cases no overflows have been done yet, so we don't care! */
239 /* we should be ok to append the message now */
240 strncpy(buf->data + buf->tail, msg, l); /* append our message */
241 buf->tail += l; /* count full message w/ '\0' terminating char */
243 /* we need to break up the message and "circle" it around */
245 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
247 /* We need to move HEAD! This is always the case since we are going
248 * to "circle" the message.
250 c = memchr(buf->data + k, '\0', buf->size - k);
252 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
253 /* move head pointer */
254 buf->head = c - buf->data + 1;
256 /* now write the first part of the message */
257 strncpy(buf->data + buf->tail, msg, l - k - 1);
259 /* ALWAYS terminate end of buffer w/ '\0' */
260 buf->data[buf->size - 1] = '\0';
262 /* now write out the rest of the string to the beginning of the buffer */
263 strcpy(buf->data, &msg[l - k - 1]);
265 /* we need to place the TAIL at the end of the message */
269 ("Weird! Can't find the terminator token from the beginning??? \n");
270 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
276 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
278 /* Note: There is also a function called "message()" in init.c */
279 /* Print a message to the log file. */
280 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
281 static void message(char *fmt, ...)
287 fl.l_whence = SEEK_SET;
291 #ifdef CONFIG_FEATURE_IPC_SYSLOG
292 if ((circular_logging == TRUE) && (buf != NULL)) {
295 va_start(arguments, fmt);
296 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
303 device_open(logFilePath,
304 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
307 fcntl(fd, F_SETLKW, &fl);
308 va_start(arguments, fmt);
309 vdprintf(fd, fmt, arguments);
312 fcntl(fd, F_SETLKW, &fl);
315 /* Always send console messages to /dev/console so people will see them. */
317 device_open(_PATH_CONSOLE,
318 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
319 va_start(arguments, fmt);
320 vdprintf(fd, fmt, arguments);
324 fprintf(stderr, "Bummer, can't print: ");
325 va_start(arguments, fmt);
326 vfprintf(stderr, fmt, arguments);
333 static void logMessage(int pri, char *msg)
337 static char res[20] = "";
341 for (c_fac = facilitynames;
342 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
343 for (c_pri = prioritynames;
344 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
345 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
346 snprintf(res, sizeof(res), "<%d>", pri);
348 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
352 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
353 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
355 timestamp = ctime(&now) + 4;
356 timestamp[15] = '\0';
359 timestamp[15] = '\0';
363 /* todo: supress duplicates */
365 #ifdef CONFIG_FEATURE_REMOTE_LOG
366 /* send message to remote logger */
367 if (-1 != remotefd) {
368 static const int IOV_COUNT = 2;
369 struct iovec iov[IOV_COUNT];
370 struct iovec *v = iov;
372 memset(&res, 0, sizeof(res));
373 snprintf(res, sizeof(res), "<%d>", pri);
375 v->iov_len = strlen(res);
379 v->iov_len = strlen(msg);
381 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
385 if (local_logging == TRUE)
387 /* now spew out the message to wherever it is supposed to go */
388 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
391 static void quit_signal(int sig)
393 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
395 #ifdef CONFIG_FEATURE_IPC_SYSLOG
402 static void domark(int sig)
404 if (MarkInterval > 0) {
405 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
410 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
411 * enabled, we otherwise get a "storage size isn't constant error. */
412 static int serveConnection(char *tmpbuf, int n_read)
416 while (p < tmpbuf + n_read) {
418 int pri = (LOG_USER | LOG_NOTICE);
420 char line[MAXLINE + 1];
424 while ((c = *p) && q < &line[sizeof(line) - 1]) {
425 if (c == '<' && num_lt == 0) {
426 /* Parse the magic priority number. */
429 while (isdigit(*(++p))) {
430 pri = 10 * pri + (*p - '0');
432 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
433 pri = (LOG_USER | LOG_NOTICE);
435 } else if (c == '\n') {
437 } else if (iscntrl(c) && (c < 0177)) {
448 logMessage(pri, line);
454 #ifdef CONFIG_FEATURE_REMOTE_LOG
455 static void init_RemoteLog(void)
458 struct sockaddr_in remoteaddr;
459 struct hostent *hostinfo;
460 int len = sizeof(remoteaddr);
462 memset(&remoteaddr, 0, len);
464 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
467 bb_error_msg_and_die("cannot create socket");
470 hostinfo = xgethostbyname(RemoteHost);
472 remoteaddr.sin_family = AF_INET;
473 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
474 remoteaddr.sin_port = htons(RemotePort);
476 /* Since we are using UDP sockets, connect just sets the default host and port
477 * for future operations
479 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
480 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
487 static void doSyslogd(void) __attribute__ ((noreturn));
488 static void doSyslogd(void)
490 struct sockaddr_un sunx;
491 socklen_t addrLength;
496 /* Set up signal handlers. */
497 signal(SIGINT, quit_signal);
498 signal(SIGTERM, quit_signal);
499 signal(SIGQUIT, quit_signal);
500 signal(SIGHUP, SIG_IGN);
501 signal(SIGCHLD, SIG_IGN);
503 signal(SIGCLD, SIG_IGN);
505 signal(SIGALRM, domark);
508 /* Create the syslog file so realpath() can work. */
509 if (realpath(_PATH_LOG, lfile) != NULL) {
513 memset(&sunx, 0, sizeof(sunx));
514 sunx.sun_family = AF_UNIX;
515 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
516 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
517 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
521 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
522 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
523 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
526 if (chmod(lfile, 0666) < 0) {
527 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
529 #ifdef CONFIG_FEATURE_IPC_SYSLOG
530 if (circular_logging == TRUE) {
535 #ifdef CONFIG_FEATURE_REMOTE_LOG
536 if (doRemoteLog == TRUE) {
541 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
546 FD_SET(sock_fd, &fds);
548 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
549 if (errno == EINTR) {
550 /* alarm may have happened. */
553 bb_perror_msg_and_die("select error");
556 if (FD_ISSET(sock_fd, &fds)) {
559 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
561 memset(tmpbuf, '\0', MAXLINE + 1);
562 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
563 serveConnection(tmpbuf, i);
565 bb_perror_msg_and_die("UNIX socket error");
567 RELEASE_CONFIG_BUFFER(tmpbuf);
569 } /* for main loop */
572 extern int syslogd_main(int argc, char **argv)
580 /* do normal option parsing */
581 while ((opt = getopt(argc, argv, "m:nO:R:LC::")) > 0) {
584 MarkInterval = atoi(optarg) * 60;
590 logFilePath = optarg;
592 #ifdef CONFIG_FEATURE_REMOTE_LOG
594 RemoteHost = bb_xstrdup(optarg);
595 if ((p = strchr(RemoteHost, ':'))) {
596 RemotePort = atoi(p + 1);
602 local_logging = TRUE;
605 #ifdef CONFIG_FEATURE_IPC_SYSLOG
608 int buf_size = atoi(optarg);
610 data_size = buf_size;
613 circular_logging = TRUE;
621 #ifdef CONFIG_FEATURE_REMOTE_LOG
622 /* If they have not specified remote logging, then log locally */
623 if (doRemoteLog == FALSE)
624 local_logging = TRUE;
628 /* Store away localhost's name before the fork */
629 gethostname(LocalHostName, sizeof(LocalHostName));
630 if ((p = strchr(LocalHostName, '.'))) {
636 if (doFork == TRUE) {
638 bb_perror_msg_and_die("daemon");
639 #if defined(__uClinux__)
640 vfork_daemon_rexec(argc, argv, "-n");
650 c-file-style: "linux"