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 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
89 #if __GNU_LIBRARY__ < 5
90 #error Sorry. Looks like you are using libc5.
91 #error libc5 shm support isnt good enough.
92 #error Please disable CONFIG_FEATURE_IPC_SYSLOG
100 static const long KEY_ID = 0x414e4547; /*"GENA" */
102 // Semaphore operation structures
103 static struct shbuf_ds {
104 int size; // size of data written
105 int head; // start of message list
106 int tail; // end of message list
107 char data[1]; // data/messages
108 } *buf = NULL; // shared memory pointer
110 static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
111 static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
113 static int shmid = -1; // ipc shared memory id
114 static int s_semid = -1; // ipc semaphore id
115 int data_size = 16000; // data size
116 int shm_size = 16000 + sizeof(*buf); // our buffer size
117 static int circular_logging = FALSE;
120 * sem_up - up()'s a semaphore.
122 static inline void sem_up(int semid)
124 if (semop(semid, SMwup, 1) == -1) {
125 bb_perror_msg_and_die("semop[SMwup]");
130 * sem_down - down()'s a semaphore
132 static inline void sem_down(int semid)
134 if (semop(semid, SMwdn, 3) == -1) {
135 bb_perror_msg_and_die("semop[SMwdn]");
140 void ipcsyslog_cleanup(void)
142 printf("Exiting Syslogd!\n");
148 shmctl(shmid, IPC_RMID, NULL);
151 semctl(s_semid, 0, IPC_RMID, 0);
155 void ipcsyslog_init(void)
158 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
159 bb_perror_msg_and_die("shmget");
162 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
163 bb_perror_msg_and_die("shmat");
166 buf->size = data_size;
167 buf->head = buf->tail = 0;
169 // we'll trust the OS to set initial semval to 0 (let's hope)
170 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
171 if (errno == EEXIST) {
172 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
173 bb_perror_msg_and_die("semget");
176 bb_perror_msg_and_die("semget");
180 printf("Buffer already allocated just grab the semaphore?");
184 /* write message to buffer */
185 void circ_message(const char *msg)
187 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
192 * Circular Buffer Algorithm:
193 * --------------------------
195 * Start-off w/ empty buffer of specific size SHM_SIZ
196 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
197 * This is also very handy since we can do printf on message.
199 * Once the buffer is full we need to get rid of the first message in buffer and
200 * insert the new message. (Note: if the message being added is >1 message then
201 * we will need to "remove" >1 old message from the buffer). The way this is done
203 * When we reach the end of the buffer we set a mark and start from the beginning.
204 * Now what about the beginning and end of the buffer? Well we have the "head"
205 * index/pointer which is the starting point for the messages and we have "tail"
206 * index/pointer which is the ending point for the messages. When we "display" the
207 * messages we start from the beginning and continue until we reach "tail". If we
208 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
209 * "tail" are actually offsets from the beginning of the buffer.
211 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
212 * a threasafe way of handling shared memory operations.
214 if ((buf->tail + l) < buf->size) {
215 /* before we append the message we need to check the HEAD so that we won't
216 overwrite any of the message that we still need and adjust HEAD to point
217 to the next message! */
218 if (buf->tail < buf->head) {
219 if ((buf->tail + l) >= buf->head) {
220 /* we need to move the HEAD to point to the next message
221 * Theoretically we have enough room to add the whole message to the
222 * buffer, because of the first outer IF statement, so we don't have
223 * to worry about overflows here!
225 int k = buf->tail + l - buf->head; /* we need to know how many bytes
226 we are overwriting to make
229 memchr(buf->data + buf->head + k, '\0',
230 buf->size - (buf->head + k));
231 if (c != NULL) { /* do a sanity check just in case! */
232 buf->head = c - buf->data + 1; /* we need to convert pointer to
233 offset + skip the '\0' since
234 we need to point to the beginning
235 of the next message */
236 /* Note: HEAD is only used to "retrieve" messages, it's not used
237 when writing messages into our buffer */
238 } else { /* show an error message to know we messed up? */
239 printf("Weird! Can't find the terminator token??? \n");
245 /* in other cases no overflows have been done yet, so we don't care! */
246 /* we should be ok to append the message now */
247 strncpy(buf->data + buf->tail, msg, l); /* append our message */
248 buf->tail += l; /* count full message w/ '\0' terminating char */
250 /* we need to break up the message and "circle" it around */
252 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
254 /* We need to move HEAD! This is always the case since we are going
255 * to "circle" the message.
257 c = memchr(buf->data + k, '\0', buf->size - k);
259 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
260 /* move head pointer */
261 buf->head = c - buf->data + 1;
263 /* now write the first part of the message */
264 strncpy(buf->data + buf->tail, msg, l - k - 1);
266 /* ALWAYS terminate end of buffer w/ '\0' */
267 buf->data[buf->size - 1] = '\0';
269 /* now write out the rest of the string to the beginning of the buffer */
270 strcpy(buf->data, &msg[l - k - 1]);
272 /* we need to place the TAIL at the end of the message */
276 ("Weird! Can't find the terminator token from the beginning??? \n");
277 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
283 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
285 /* Note: There is also a function called "message()" in init.c */
286 /* Print a message to the log file. */
287 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
288 static void message(char *fmt, ...)
294 fl.l_whence = SEEK_SET;
298 #ifdef CONFIG_FEATURE_IPC_SYSLOG
299 if ((circular_logging == TRUE) && (buf != NULL)) {
302 va_start(arguments, fmt);
303 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
310 device_open(logFilePath,
311 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
314 fcntl(fd, F_SETLKW, &fl);
315 va_start(arguments, fmt);
316 vdprintf(fd, fmt, arguments);
319 fcntl(fd, F_SETLKW, &fl);
322 /* Always send console messages to /dev/console so people will see them. */
324 device_open(_PATH_CONSOLE,
325 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
326 va_start(arguments, fmt);
327 vdprintf(fd, fmt, arguments);
331 fprintf(stderr, "Bummer, can't print: ");
332 va_start(arguments, fmt);
333 vfprintf(stderr, fmt, arguments);
340 static void logMessage(int pri, char *msg)
344 static char res[20] = "";
348 for (c_fac = facilitynames;
349 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
350 for (c_pri = prioritynames;
351 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
352 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
353 snprintf(res, sizeof(res), "<%d>", pri);
355 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
359 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
360 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
362 timestamp = ctime(&now) + 4;
363 timestamp[15] = '\0';
366 timestamp[15] = '\0';
370 /* todo: supress duplicates */
372 #ifdef CONFIG_FEATURE_REMOTE_LOG
373 /* send message to remote logger */
374 if (-1 != remotefd) {
375 static const int IOV_COUNT = 2;
376 struct iovec iov[IOV_COUNT];
377 struct iovec *v = iov;
379 memset(&res, 0, sizeof(res));
380 snprintf(res, sizeof(res), "<%d>", pri);
382 v->iov_len = strlen(res);
386 v->iov_len = strlen(msg);
388 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
392 if (local_logging == TRUE)
394 /* now spew out the message to wherever it is supposed to go */
395 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
398 static void quit_signal(int sig)
400 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
402 #ifdef CONFIG_FEATURE_IPC_SYSLOG
409 static void domark(int sig)
411 if (MarkInterval > 0) {
412 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
417 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
418 * enabled, we otherwise get a "storage size isn't constant error. */
419 static int serveConnection(char *tmpbuf, int n_read)
423 while (p < tmpbuf + n_read) {
425 int pri = (LOG_USER | LOG_NOTICE);
427 char line[MAXLINE + 1];
431 while ((c = *p) && q < &line[sizeof(line) - 1]) {
432 if (c == '<' && num_lt == 0) {
433 /* Parse the magic priority number. */
436 while (isdigit(*(++p))) {
437 pri = 10 * pri + (*p - '0');
439 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
440 pri = (LOG_USER | LOG_NOTICE);
442 } else if (c == '\n') {
444 } else if (iscntrl(c) && (c < 0177)) {
455 logMessage(pri, line);
461 #ifdef CONFIG_FEATURE_REMOTE_LOG
462 static void init_RemoteLog(void)
465 struct sockaddr_in remoteaddr;
466 struct hostent *hostinfo;
467 int len = sizeof(remoteaddr);
469 memset(&remoteaddr, 0, len);
471 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
474 bb_error_msg_and_die("cannot create socket");
477 hostinfo = xgethostbyname(RemoteHost);
479 remoteaddr.sin_family = AF_INET;
480 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
481 remoteaddr.sin_port = htons(RemotePort);
483 /* Since we are using UDP sockets, connect just sets the default host and port
484 * for future operations
486 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
487 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
494 static void doSyslogd(void) __attribute__ ((noreturn));
495 static void doSyslogd(void)
497 struct sockaddr_un sunx;
498 socklen_t addrLength;
503 /* Set up signal handlers. */
504 signal(SIGINT, quit_signal);
505 signal(SIGTERM, quit_signal);
506 signal(SIGQUIT, quit_signal);
507 signal(SIGHUP, SIG_IGN);
508 signal(SIGCHLD, SIG_IGN);
510 signal(SIGCLD, SIG_IGN);
512 signal(SIGALRM, domark);
515 /* Create the syslog file so realpath() can work. */
516 if (realpath(_PATH_LOG, lfile) != NULL) {
520 memset(&sunx, 0, sizeof(sunx));
521 sunx.sun_family = AF_UNIX;
522 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
523 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
524 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
528 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
529 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
530 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
533 if (chmod(lfile, 0666) < 0) {
534 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
536 #ifdef CONFIG_FEATURE_IPC_SYSLOG
537 if (circular_logging == TRUE) {
542 #ifdef CONFIG_FEATURE_REMOTE_LOG
543 if (doRemoteLog == TRUE) {
548 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
553 FD_SET(sock_fd, &fds);
555 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
556 if (errno == EINTR) {
557 /* alarm may have happened. */
560 bb_perror_msg_and_die("select error");
563 if (FD_ISSET(sock_fd, &fds)) {
566 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
568 memset(tmpbuf, '\0', MAXLINE + 1);
569 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
570 serveConnection(tmpbuf, i);
572 bb_perror_msg_and_die("UNIX socket error");
574 RELEASE_CONFIG_BUFFER(tmpbuf);
576 } /* for main loop */
579 extern int syslogd_main(int argc, char **argv)
583 #if ! defined(__uClinux__)
589 /* do normal option parsing */
590 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
593 MarkInterval = atoi(optarg) * 60;
595 #if ! defined(__uClinux__)
601 logFilePath = bb_xstrdup(optarg);
603 #ifdef CONFIG_FEATURE_REMOTE_LOG
605 RemoteHost = bb_xstrdup(optarg);
606 if ((p = strchr(RemoteHost, ':'))) {
607 RemotePort = atoi(p + 1);
613 local_logging = TRUE;
616 #ifdef CONFIG_FEATURE_IPC_SYSLOG
618 circular_logging = TRUE;
626 #ifdef CONFIG_FEATURE_REMOTE_LOG
627 /* If they have not specified remote logging, then log locally */
628 if (doRemoteLog == FALSE)
629 local_logging = TRUE;
633 /* Store away localhost's name before the fork */
634 gethostname(LocalHostName, sizeof(LocalHostName));
635 if ((p = strchr(LocalHostName, '.'))) {
641 #if ! defined(__uClinux__)
642 if ((doFork == TRUE) && (daemon(0, 1) < 0)) {
643 bb_perror_msg_and_die("daemon");
653 c-file-style: "linux"