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@cachier.com>
12 * Maintainer: Gennady Feldman <gena01@cachier.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[BUFSIZ];
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[32];
68 #ifdef CONFIG_FEATURE_REMOTE_LOG
69 #include <netinet/in.h>
70 /* udp socket for logging to remote host */
71 static int remotefd = -1;
72 /* where do we log? */
73 static char *RemoteHost;
74 /* what port to log to? */
75 static int RemotePort = 514;
76 /* To remote log or not to remote log, that is the question. */
77 static int doRemoteLog = FALSE;
78 static int local_logging = FALSE;
81 /* circular buffer variables/structures */
82 #ifdef CONFIG_FEATURE_IPC_SYSLOG
84 #if __GNU_LIBRARY__ < 5
85 #error Sorry. Looks like you are using libc5.
86 #error libc5 shm support isnt good enough.
87 #error Please disable CONFIG_FEATURE_IPC_SYSLOG
95 static const long KEY_ID = 0x414e4547; /*"GENA"*/
97 // Semaphore operation structures
98 static struct shbuf_ds {
99 int size; // size of data written
100 int head; // start of message list
101 int tail; // end of message list
102 char data[1]; // data/messages
103 } *buf = NULL; // shared memory pointer
105 static struct sembuf SMwup[1] = {{1, -1, IPC_NOWAIT}}; // set SMwup
106 static struct sembuf SMwdn[3] = {{0, 0}, {1, 0}, {1, +1}}; // set SMwdn
108 static int shmid = -1; // ipc shared memory id
109 static int s_semid = -1; // ipc semaphore id
110 int data_size = 16000; // data size
111 int shm_size = 16000 + sizeof(*buf); // our buffer size
112 static int circular_logging = FALSE;
115 * sem_up - up()'s a semaphore.
117 static inline void sem_up(int semid)
119 if ( semop(semid, SMwup, 1) == -1 )
120 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 perror_msg_and_die("semop[SMwdn]");
132 #define MAXLINE 1024 /* maximum line length */
135 void ipcsyslog_cleanup(void){
136 printf("Exiting Syslogd!\n");
141 shmctl(shmid, IPC_RMID, NULL);
143 semctl(s_semid, 0, IPC_RMID, 0);
146 void ipcsyslog_init(void){
148 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
149 perror_msg_and_die("shmget");
152 if ((buf = shmat(shmid, NULL, 0)) == NULL)
153 perror_msg_and_die("shmat");
157 buf->head=buf->tail=0;
159 // we'll trust the OS to set initial semval to 0 (let's hope)
160 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
161 if (errno == EEXIST){
162 if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
163 perror_msg_and_die("semget");
165 perror_msg_and_die("semget");
168 printf("Buffer already allocated just grab the semaphore?");
172 /* write message to buffer */
173 void circ_message(const char *msg){
174 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
179 * Circular Buffer Algorithm:
180 * --------------------------
182 * Start-off w/ empty buffer of specific size SHM_SIZ
183 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
184 * This is also very handy since we can do printf on message.
186 * Once the buffer is full we need to get rid of the first message in buffer and
187 * insert the new message. (Note: if the message being added is >1 message then
188 * we will need to "remove" >1 old message from the buffer). The way this is done
190 * When we reach the end of the buffer we set a mark and start from the beginning.
191 * Now what about the beginning and end of the buffer? Well we have the "head"
192 * index/pointer which is the starting point for the messages and we have "tail"
193 * index/pointer which is the ending point for the messages. When we "display" the
194 * messages we start from the beginning and continue until we reach "tail". If we
195 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
196 * "tail" are actually offsets from the beginning of the buffer.
198 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
199 * a threasafe way of handling shared memory operations.
201 if ( (buf->tail + l) < buf->size ){
202 /* before we append the message we need to check the HEAD so that we won't
203 overwrite any of the message that we still need and adjust HEAD to point
204 to the next message! */
205 if ( buf->tail < buf->head){
206 if ( (buf->tail + l) >= buf->head ){
207 /* we need to move the HEAD to point to the next message
208 * Theoretically we have enough room to add the whole message to the
209 * buffer, because of the first outer IF statement, so we don't have
210 * to worry about overflows here!
212 int k= buf->tail + l - buf->head; /* we need to know how many bytes
213 we are overwriting to make
215 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
216 if (c != NULL) {/* do a sanity check just in case! */
217 buf->head = c - buf->data + 1; /* we need to convert pointer to
218 offset + skip the '\0' since
219 we need to point to the beginning
220 of the next message */
221 /* Note: HEAD is only used to "retrieve" messages, it's not used
222 when writing messages into our buffer */
223 }else{ /* show an error message to know we messed up? */
224 printf("Weird! Can't find the terminator token??? \n");
228 } /* in other cases no overflows have been done yet, so we don't care! */
230 /* we should be ok to append the message now */
231 strncpy(buf->data + buf->tail,msg,l); /* append our message */
232 buf->tail+=l; /* count full message w/ '\0' terminating char */
234 /* we need to break up the message and "circle" it around */
236 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
238 /* We need to move HEAD! This is always the case since we are going
239 * to "circle" the message.
241 c=memchr(buf->data + k ,'\0', buf->size - k);
243 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
244 /* move head pointer*/
245 buf->head=c-buf->data+1;
247 /* now write the first part of the message */
248 strncpy(buf->data + buf->tail, msg, l - k - 1);
250 /* ALWAYS terminate end of buffer w/ '\0' */
251 buf->data[buf->size-1]='\0';
253 /* now write out the rest of the string to the beginning of the buffer */
254 strcpy(buf->data, &msg[l-k-1]);
256 /* we need to place the TAIL at the end of the message */
259 printf("Weird! Can't find the terminator token from the beginning??? \n");
260 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
267 /* Note: There is also a function called "message()" in init.c */
268 /* Print a message to the log file. */
269 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
270 static void message (char *fmt, ...)
276 fl.l_whence = SEEK_SET;
280 #ifdef CONFIG_FEATURE_IPC_SYSLOG
281 if ((circular_logging) && (buf != NULL)){
283 va_start (arguments, fmt);
284 vsprintf (b, fmt, arguments);
290 if ((fd = device_open (logFilePath,
291 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
294 fcntl (fd, F_SETLKW, &fl);
295 va_start (arguments, fmt);
296 vdprintf (fd, fmt, arguments);
299 fcntl (fd, F_SETLKW, &fl);
302 /* Always send console messages to /dev/console so people will see them. */
303 if ((fd = device_open (_PATH_CONSOLE,
304 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
305 va_start (arguments, fmt);
306 vdprintf (fd, fmt, arguments);
310 fprintf (stderr, "Bummer, can't print: ");
311 va_start (arguments, fmt);
312 vfprintf (stderr, fmt, arguments);
319 static void logMessage (int pri, char *msg)
323 static char res[20] = "";
327 for (c_fac = facilitynames;
328 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
329 for (c_pri = prioritynames;
330 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
331 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
332 snprintf(res, sizeof(res), "<%d>", pri);
334 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
337 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
338 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
340 timestamp = ctime(&now) + 4;
341 timestamp[15] = '\0';
344 timestamp[15] = '\0';
348 /* todo: supress duplicates */
350 #ifdef CONFIG_FEATURE_REMOTE_LOG
351 /* send message to remote logger */
352 if ( -1 != remotefd){
353 static const int IOV_COUNT = 2;
354 struct iovec iov[IOV_COUNT];
355 struct iovec *v = iov;
357 memset(&res, 0, sizeof(res));
358 snprintf(res, sizeof(res), "<%d>", pri);
360 v->iov_len = strlen(res);
364 v->iov_len = strlen(msg);
367 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
368 if (errno == EINTR) goto writev_retry;
369 error_msg_and_die("syslogd: cannot write to remote file handle on"
370 "%s:%d",RemoteHost,RemotePort);
375 /* now spew out the message to wherever it is supposed to go */
376 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
381 static void quit_signal(int sig)
383 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
385 #ifdef CONFIG_FEATURE_IPC_SYSLOG
392 static void domark(int sig)
394 if (MarkInterval > 0) {
395 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
400 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
401 * enabled, we otherwise get a "storage size isn't constant error. */
403 static int serveConnection (char* tmpbuf, int n_read)
407 while (p < tmpbuf + n_read) {
409 int pri = (LOG_USER | LOG_NOTICE);
410 char line[ BUFSIZE + 1 ];
416 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
417 if ((c == '<') && !gotpri && isdigit(p[1])) {
418 /* Parse the magic priority number. */
421 while (isdigit (*(++p))) {
422 pri = 10 * pri + (*p - '0');
424 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
425 pri = (LOG_USER | LOG_NOTICE);
427 } else if (c == '\n') {
429 } else if (iscntrl (c) && (c < 0177)) {
440 logMessage (pri, line);
446 #ifdef CONFIG_FEATURE_REMOTE_LOG
447 static void init_RemoteLog (void){
449 struct sockaddr_in remoteaddr;
450 struct hostent *hostinfo;
451 int len = sizeof(remoteaddr);
453 memset(&remoteaddr, 0, len);
455 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
458 error_msg_and_die("syslogd: cannot create socket");
461 hostinfo = xgethostbyname(RemoteHost);
463 remoteaddr.sin_family = AF_INET;
464 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
465 remoteaddr.sin_port = htons(RemotePort);
468 Since we are using UDP sockets, connect just sets the default host and port
469 for future operations
471 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
472 error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
478 static void doSyslogd (void) __attribute__ ((noreturn));
479 static void doSyslogd (void)
481 struct sockaddr_un sunx;
482 socklen_t addrLength;
488 /* Set up signal handlers. */
489 signal (SIGINT, quit_signal);
490 signal (SIGTERM, quit_signal);
491 signal (SIGQUIT, quit_signal);
492 signal (SIGHUP, SIG_IGN);
493 signal (SIGCHLD, SIG_IGN);
495 signal (SIGCLD, SIG_IGN);
497 signal (SIGALRM, domark);
498 alarm (MarkInterval);
500 /* Create the syslog file so realpath() can work. */
501 if (realpath (_PATH_LOG, lfile) != NULL)
504 memset (&sunx, 0, sizeof (sunx));
505 sunx.sun_family = AF_UNIX;
506 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
507 if ((sock_fd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
508 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG);
510 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
511 if (bind (sock_fd, (struct sockaddr *) &sunx, addrLength) < 0)
512 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
514 if (chmod (lfile, 0666) < 0)
515 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
518 FD_SET (sock_fd, &fds);
520 #ifdef CONFIG_FEATURE_IPC_SYSLOG
521 if (circular_logging ){
526 #ifdef CONFIG_FEATURE_REMOTE_LOG
532 logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
540 memcpy (&readfds, &fds, sizeof (fds));
542 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
543 if (errno == EINTR) continue; /* alarm may have happened. */
544 perror_msg_and_die ("select error");
547 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
548 if (FD_ISSET (fd, &readfds)) {
554 RESERVE_CONFIG_BUFFER(tmpbuf, BUFSIZE + 1);
556 memset(tmpbuf, '\0', BUFSIZE+1);
557 if ( (i = recv(fd, tmpbuf, MAXLINE - 2, 0)) > 0) {
558 if ( serveConnection(tmpbuf, i) <= 0 ) {
563 perror_msg_and_die ("UNIX socket error");
565 RELEASE_CONFIG_BUFFER (tmpbuf);
566 } /* fd == sock_fd */
569 } /* for main loop */
572 extern int syslogd_main(int argc, char **argv)
579 /* do normal option parsing */
580 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
583 MarkInterval = atoi(optarg) * 60;
589 logFilePath = xstrdup(optarg);
591 #ifdef CONFIG_FEATURE_REMOTE_LOG
593 RemoteHost = xstrdup(optarg);
594 if ( (p = strchr(RemoteHost, ':'))){
595 RemotePort = atoi(p+1);
601 local_logging = TRUE;
604 #ifdef CONFIG_FEATURE_IPC_SYSLOG
606 circular_logging = TRUE;
614 #ifdef CONFIG_FEATURE_REMOTE_LOG
615 /* If they have not specified remote logging, then log locally */
617 local_logging = TRUE;
621 /* Store away localhost's name before the fork */
622 gethostname(LocalHostName, sizeof(LocalHostName));
623 if ((p = strchr(LocalHostName, '.'))) {
630 #if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
631 if (daemon(0, 1) < 0)
632 perror_msg_and_die("daemon");
634 error_msg_and_die("daemon not supported");
644 c-file-style: "linux"