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
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 int data_size = 16000; // data size
110 int shm_size = 16000 + sizeof(*buf); // our buffer size
111 static int circular_logging = FALSE;
114 * sem_up - up()'s a semaphore.
116 static inline void sem_up(int semid)
118 if (semop(semid, SMwup, 1) == -1) {
119 bb_perror_msg_and_die("semop[SMwup]");
124 * sem_down - down()'s a semaphore
126 static inline void sem_down(int semid)
128 if (semop(semid, SMwdn, 3) == -1) {
129 bb_perror_msg_and_die("semop[SMwdn]");
134 void ipcsyslog_cleanup(void)
136 printf("Exiting Syslogd!\n");
142 shmctl(shmid, IPC_RMID, NULL);
145 semctl(s_semid, 0, IPC_RMID, 0);
149 void ipcsyslog_init(void)
152 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
153 bb_perror_msg_and_die("shmget");
156 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
157 bb_perror_msg_and_die("shmat");
160 buf->size = data_size;
161 buf->head = buf->tail = 0;
163 // we'll trust the OS to set initial semval to 0 (let's hope)
164 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
165 if (errno == EEXIST) {
166 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
167 bb_perror_msg_and_die("semget");
170 bb_perror_msg_and_die("semget");
174 printf("Buffer already allocated just grab the semaphore?");
178 /* write message to buffer */
179 void circ_message(const char *msg)
181 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
186 * Circular Buffer Algorithm:
187 * --------------------------
189 * Start-off w/ empty buffer of specific size SHM_SIZ
190 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
191 * This is also very handy since we can do printf on message.
193 * Once the buffer is full we need to get rid of the first message in buffer and
194 * insert the new message. (Note: if the message being added is >1 message then
195 * we will need to "remove" >1 old message from the buffer). The way this is done
197 * When we reach the end of the buffer we set a mark and start from the beginning.
198 * Now what about the beginning and end of the buffer? Well we have the "head"
199 * index/pointer which is the starting point for the messages and we have "tail"
200 * index/pointer which is the ending point for the messages. When we "display" the
201 * messages we start from the beginning and continue until we reach "tail". If we
202 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
203 * "tail" are actually offsets from the beginning of the buffer.
205 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
206 * a threasafe way of handling shared memory operations.
208 if ((buf->tail + l) < buf->size) {
209 /* before we append the message we need to check the HEAD so that we won't
210 overwrite any of the message that we still need and adjust HEAD to point
211 to the next message! */
212 if (buf->tail < buf->head) {
213 if ((buf->tail + l) >= buf->head) {
214 /* we need to move the HEAD to point to the next message
215 * Theoretically we have enough room to add the whole message to the
216 * buffer, because of the first outer IF statement, so we don't have
217 * to worry about overflows here!
219 int k = buf->tail + l - buf->head; /* we need to know how many bytes
220 we are overwriting to make
223 memchr(buf->data + buf->head + k, '\0',
224 buf->size - (buf->head + k));
225 if (c != NULL) { /* do a sanity check just in case! */
226 buf->head = c - buf->data + 1; /* we need to convert pointer to
227 offset + skip the '\0' since
228 we need to point to the beginning
229 of the next message */
230 /* Note: HEAD is only used to "retrieve" messages, it's not used
231 when writing messages into our buffer */
232 } else { /* show an error message to know we messed up? */
233 printf("Weird! Can't find the terminator token??? \n");
239 /* in other cases no overflows have been done yet, so we don't care! */
240 /* we should be ok to append the message now */
241 strncpy(buf->data + buf->tail, msg, l); /* append our message */
242 buf->tail += l; /* count full message w/ '\0' terminating char */
244 /* we need to break up the message and "circle" it around */
246 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
248 /* We need to move HEAD! This is always the case since we are going
249 * to "circle" the message.
251 c = memchr(buf->data + k, '\0', buf->size - k);
253 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
254 /* move head pointer */
255 buf->head = c - buf->data + 1;
257 /* now write the first part of the message */
258 strncpy(buf->data + buf->tail, msg, l - k - 1);
260 /* ALWAYS terminate end of buffer w/ '\0' */
261 buf->data[buf->size - 1] = '\0';
263 /* now write out the rest of the string to the beginning of the buffer */
264 strcpy(buf->data, &msg[l - k - 1]);
266 /* we need to place the TAIL at the end of the message */
270 ("Weird! Can't find the terminator token from the beginning??? \n");
271 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
277 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
279 /* Note: There is also a function called "message()" in init.c */
280 /* Print a message to the log file. */
281 static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
282 static void message(char *fmt, ...)
288 fl.l_whence = SEEK_SET;
292 #ifdef CONFIG_FEATURE_IPC_SYSLOG
293 if ((circular_logging == TRUE) && (buf != NULL)) {
296 va_start(arguments, fmt);
297 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
304 device_open(logFilePath,
305 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
308 fcntl(fd, F_SETLKW, &fl);
309 va_start(arguments, fmt);
310 vdprintf(fd, fmt, arguments);
313 fcntl(fd, F_SETLKW, &fl);
316 /* Always send console messages to /dev/console so people will see them. */
318 device_open(_PATH_CONSOLE,
319 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
320 va_start(arguments, fmt);
321 vdprintf(fd, fmt, arguments);
325 fprintf(stderr, "Bummer, can't print: ");
326 va_start(arguments, fmt);
327 vfprintf(stderr, fmt, arguments);
334 static void logMessage(int pri, char *msg)
338 static char res[20] = "";
342 for (c_fac = facilitynames;
343 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
344 for (c_pri = prioritynames;
345 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
346 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
347 snprintf(res, sizeof(res), "<%d>", pri);
349 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
353 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
354 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
356 timestamp = ctime(&now) + 4;
357 timestamp[15] = '\0';
360 timestamp[15] = '\0';
364 /* todo: supress duplicates */
366 #ifdef CONFIG_FEATURE_REMOTE_LOG
367 /* send message to remote logger */
368 if (-1 != remotefd) {
369 static const int IOV_COUNT = 2;
370 struct iovec iov[IOV_COUNT];
371 struct iovec *v = iov;
373 memset(&res, 0, sizeof(res));
374 snprintf(res, sizeof(res), "<%d>", pri);
376 v->iov_len = strlen(res);
380 v->iov_len = strlen(msg);
382 if ((-1 == writev(remotefd, iov, IOV_COUNT)) && (errno == EINTR)) {
386 if (local_logging == TRUE)
388 /* now spew out the message to wherever it is supposed to go */
389 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
392 static void quit_signal(int sig)
394 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
396 #ifdef CONFIG_FEATURE_IPC_SYSLOG
403 static void domark(int sig)
405 if (MarkInterval > 0) {
406 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
411 /* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
412 * enabled, we otherwise get a "storage size isn't constant error. */
413 static int serveConnection(char *tmpbuf, int n_read)
417 while (p < tmpbuf + n_read) {
419 int pri = (LOG_USER | LOG_NOTICE);
421 char line[MAXLINE + 1];
425 while ((c = *p) && q < &line[sizeof(line) - 1]) {
426 if (c == '<' && num_lt == 0) {
427 /* Parse the magic priority number. */
430 while (isdigit(*(++p))) {
431 pri = 10 * pri + (*p - '0');
433 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
434 pri = (LOG_USER | LOG_NOTICE);
436 } else if (c == '\n') {
438 } else if (iscntrl(c) && (c < 0177)) {
449 logMessage(pri, line);
455 #ifdef CONFIG_FEATURE_REMOTE_LOG
456 static void init_RemoteLog(void)
459 struct sockaddr_in remoteaddr;
460 struct hostent *hostinfo;
461 int len = sizeof(remoteaddr);
463 memset(&remoteaddr, 0, len);
465 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
468 bb_error_msg_and_die("cannot create socket");
471 hostinfo = xgethostbyname(RemoteHost);
473 remoteaddr.sin_family = AF_INET;
474 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
475 remoteaddr.sin_port = htons(RemotePort);
477 /* Since we are using UDP sockets, connect just sets the default host and port
478 * for future operations
480 if (0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))) {
481 bb_error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost,
488 static void doSyslogd(void) __attribute__ ((noreturn));
489 static void doSyslogd(void)
491 struct sockaddr_un sunx;
492 socklen_t addrLength;
497 /* Set up signal handlers. */
498 signal(SIGINT, quit_signal);
499 signal(SIGTERM, quit_signal);
500 signal(SIGQUIT, quit_signal);
501 signal(SIGHUP, SIG_IGN);
502 signal(SIGCHLD, SIG_IGN);
504 signal(SIGCLD, SIG_IGN);
506 signal(SIGALRM, domark);
509 /* Create the syslog file so realpath() can work. */
510 if (realpath(_PATH_LOG, lfile) != NULL) {
514 memset(&sunx, 0, sizeof(sunx));
515 sunx.sun_family = AF_UNIX;
516 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
517 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
518 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
522 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
523 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
524 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
527 if (chmod(lfile, 0666) < 0) {
528 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
530 #ifdef CONFIG_FEATURE_IPC_SYSLOG
531 if (circular_logging == TRUE) {
536 #ifdef CONFIG_FEATURE_REMOTE_LOG
537 if (doRemoteLog == TRUE) {
542 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
547 FD_SET(sock_fd, &fds);
549 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
550 if (errno == EINTR) {
551 /* alarm may have happened. */
554 bb_perror_msg_and_die("select error");
557 if (FD_ISSET(sock_fd, &fds)) {
560 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
562 memset(tmpbuf, '\0', MAXLINE + 1);
563 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
564 serveConnection(tmpbuf, i);
566 bb_perror_msg_and_die("UNIX socket error");
568 RELEASE_CONFIG_BUFFER(tmpbuf);
570 } /* for main loop */
573 extern int syslogd_main(int argc, char **argv)
577 #if ! defined(__uClinux__)
583 /* do normal option parsing */
584 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
587 MarkInterval = atoi(optarg) * 60;
589 #if ! defined(__uClinux__)
595 logFilePath = bb_xstrdup(optarg);
597 #ifdef CONFIG_FEATURE_REMOTE_LOG
599 RemoteHost = bb_xstrdup(optarg);
600 if ((p = strchr(RemoteHost, ':'))) {
601 RemotePort = atoi(p + 1);
607 local_logging = TRUE;
610 #ifdef CONFIG_FEATURE_IPC_SYSLOG
612 circular_logging = TRUE;
620 #ifdef CONFIG_FEATURE_REMOTE_LOG
621 /* If they have not specified remote logging, then log locally */
622 if (doRemoteLog == FALSE)
623 local_logging = TRUE;
627 /* Store away localhost's name before the fork */
628 gethostname(LocalHostName, sizeof(LocalHostName));
629 if ((p = strchr(LocalHostName, '.'))) {
635 if ((doFork == TRUE) && (daemon(0, 1) < 0)) {
636 bb_perror_msg_and_die("daemon");
637 #if ! defined(__uClinux__)
638 vfork_daemon_rexec(argc, argv, "-n");
648 c-file-style: "linux"