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]");
133 void ipcsyslog_cleanup(void){
134 printf("Exiting Syslogd!\n");
139 shmctl(shmid, IPC_RMID, NULL);
141 semctl(s_semid, 0, IPC_RMID, 0);
144 void ipcsyslog_init(void){
146 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
147 perror_msg_and_die("shmget");
150 if ((buf = shmat(shmid, NULL, 0)) == NULL)
151 perror_msg_and_die("shmat");
155 buf->head=buf->tail=0;
157 // we'll trust the OS to set initial semval to 0 (let's hope)
158 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
159 if (errno == EEXIST){
160 if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
161 perror_msg_and_die("semget");
163 perror_msg_and_die("semget");
166 printf("Buffer already allocated just grab the semaphore?");
170 /* write message to buffer */
171 void circ_message(const char *msg){
172 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
177 * Circular Buffer Algorithm:
178 * --------------------------
180 * Start-off w/ empty buffer of specific size SHM_SIZ
181 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
182 * This is also very handy since we can do printf on message.
184 * Once the buffer is full we need to get rid of the first message in buffer and
185 * insert the new message. (Note: if the message being added is >1 message then
186 * we will need to "remove" >1 old message from the buffer). The way this is done
188 * When we reach the end of the buffer we set a mark and start from the beginning.
189 * Now what about the beginning and end of the buffer? Well we have the "head"
190 * index/pointer which is the starting point for the messages and we have "tail"
191 * index/pointer which is the ending point for the messages. When we "display" the
192 * messages we start from the beginning and continue until we reach "tail". If we
193 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
194 * "tail" are actually offsets from the beginning of the buffer.
196 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
197 * a threasafe way of handling shared memory operations.
199 if ( (buf->tail + l) < buf->size ){
200 /* before we append the message we need to check the HEAD so that we won't
201 overwrite any of the message that we still need and adjust HEAD to point
202 to the next message! */
203 if ( buf->tail < buf->head){
204 if ( (buf->tail + l) >= buf->head ){
205 /* we need to move the HEAD to point to the next message
206 * Theoretically we have enough room to add the whole message to the
207 * buffer, because of the first outer IF statement, so we don't have
208 * to worry about overflows here!
210 int k= buf->tail + l - buf->head; /* we need to know how many bytes
211 we are overwriting to make
213 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
214 if (c != NULL) {/* do a sanity check just in case! */
215 buf->head = c - buf->data + 1; /* we need to convert pointer to
216 offset + skip the '\0' since
217 we need to point to the beginning
218 of the next message */
219 /* Note: HEAD is only used to "retrieve" messages, it's not used
220 when writing messages into our buffer */
221 }else{ /* show an error message to know we messed up? */
222 printf("Weird! Can't find the terminator token??? \n");
226 } /* in other cases no overflows have been done yet, so we don't care! */
228 /* we should be ok to append the message now */
229 strncpy(buf->data + buf->tail,msg,l); /* append our message */
230 buf->tail+=l; /* count full message w/ '\0' terminating char */
232 /* we need to break up the message and "circle" it around */
234 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
236 /* We need to move HEAD! This is always the case since we are going
237 * to "circle" the message.
239 c=memchr(buf->data + k ,'\0', buf->size - k);
241 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
242 /* move head pointer*/
243 buf->head=c-buf->data+1;
245 /* now write the first part of the message */
246 strncpy(buf->data + buf->tail, msg, l - k - 1);
248 /* ALWAYS terminate end of buffer w/ '\0' */
249 buf->data[buf->size-1]='\0';
251 /* now write out the rest of the string to the beginning of the buffer */
252 strcpy(buf->data, &msg[l-k-1]);
254 /* we need to place the TAIL at the end of the message */
257 printf("Weird! Can't find the terminator token from the beginning??? \n");
258 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
265 /* Note: There is also a function called "message()" in init.c */
266 /* Print a message to the log file. */
267 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
268 static void message (char *fmt, ...)
274 fl.l_whence = SEEK_SET;
278 #ifdef CONFIG_FEATURE_IPC_SYSLOG
279 if ((circular_logging) && (buf != NULL)){
281 va_start (arguments, fmt);
282 vsprintf (b, fmt, arguments);
288 if ((fd = device_open (logFilePath,
289 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
292 fcntl (fd, F_SETLKW, &fl);
293 va_start (arguments, fmt);
294 vdprintf (fd, fmt, arguments);
297 fcntl (fd, F_SETLKW, &fl);
300 /* Always send console messages to /dev/console so people will see them. */
301 if ((fd = device_open (_PATH_CONSOLE,
302 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
303 va_start (arguments, fmt);
304 vdprintf (fd, fmt, arguments);
308 fprintf (stderr, "Bummer, can't print: ");
309 va_start (arguments, fmt);
310 vfprintf (stderr, fmt, arguments);
317 static void logMessage (int pri, char *msg)
321 static char res[20] = "";
325 for (c_fac = facilitynames;
326 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
327 for (c_pri = prioritynames;
328 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
329 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
330 snprintf(res, sizeof(res), "<%d>", pri);
332 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
335 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
336 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
338 timestamp = ctime(&now) + 4;
339 timestamp[15] = '\0';
342 timestamp[15] = '\0';
346 /* todo: supress duplicates */
348 #ifdef CONFIG_FEATURE_REMOTE_LOG
349 /* send message to remote logger */
350 if ( -1 != remotefd){
351 static const int IOV_COUNT = 2;
352 struct iovec iov[IOV_COUNT];
353 struct iovec *v = iov;
355 memset(&res, 0, sizeof(res));
356 snprintf(res, sizeof(res), "<%d>", pri);
358 v->iov_len = strlen(res);
362 v->iov_len = strlen(msg);
365 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
366 if (errno == EINTR) goto writev_retry;
367 error_msg_and_die("syslogd: cannot write to remote file handle on"
368 "%s:%d",RemoteHost,RemotePort);
373 /* now spew out the message to wherever it is supposed to go */
374 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
379 static void quit_signal(int sig)
381 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
383 #ifdef CONFIG_FEATURE_IPC_SYSLOG
390 static void domark(int sig)
392 if (MarkInterval > 0) {
393 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
398 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
399 * enabled, we otherwise get a "storage size isn't constant error. */
401 static int serveConnection (int conn)
403 RESERVE_CONFIG_BUFFER(tmpbuf, BUFSIZE + 1);
407 n_read = read (conn, tmpbuf, BUFSIZE );
409 while (p < tmpbuf + n_read) {
411 int pri = (LOG_USER | LOG_NOTICE);
412 char line[ BUFSIZE + 1 ];
418 tmpbuf[ n_read - 1 ] = '\0';
420 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
421 if ((c == '<') && !gotpri && isdigit(p[1])) {
422 /* Parse the magic priority number. */
425 while (isdigit (*(++p))) {
426 pri = 10 * pri + (*p - '0');
428 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
429 pri = (LOG_USER | LOG_NOTICE);
431 } else if (c == '\n') {
433 } else if (iscntrl (c) && (c < 0177)) {
444 logMessage (pri, line);
446 RELEASE_CONFIG_BUFFER (tmpbuf);
451 #ifdef CONFIG_FEATURE_REMOTE_LOG
452 static void init_RemoteLog (void){
454 struct sockaddr_in remoteaddr;
455 struct hostent *hostinfo;
456 int len = sizeof(remoteaddr);
458 memset(&remoteaddr, 0, len);
460 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
463 error_msg_and_die("syslogd: cannot create socket");
466 hostinfo = xgethostbyname(RemoteHost);
468 remoteaddr.sin_family = AF_INET;
469 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
470 remoteaddr.sin_port = htons(RemotePort);
473 Since we are using UDP sockets, connect just sets the default host and port
474 for future operations
476 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
477 error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
483 static void doSyslogd (void) __attribute__ ((noreturn));
484 static void doSyslogd (void)
486 struct sockaddr_un sunx;
487 socklen_t addrLength;
493 /* Set up signal handlers. */
494 signal (SIGINT, quit_signal);
495 signal (SIGTERM, quit_signal);
496 signal (SIGQUIT, quit_signal);
497 signal (SIGHUP, SIG_IGN);
498 signal (SIGCHLD, SIG_IGN);
500 signal (SIGCLD, SIG_IGN);
502 signal (SIGALRM, domark);
503 alarm (MarkInterval);
505 /* Create the syslog file so realpath() can work. */
506 if (realpath (_PATH_LOG, lfile) != NULL)
509 memset (&sunx, 0, sizeof (sunx));
510 sunx.sun_family = AF_UNIX;
511 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
512 if ((sock_fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
513 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG);
515 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
516 if ((bind (sock_fd, (struct sockaddr *) &sunx, addrLength)) || (listen (sock_fd, 5)))
517 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
519 if (chmod (lfile, 0666) < 0)
520 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
523 FD_SET (sock_fd, &fds);
525 #ifdef CONFIG_FEATURE_IPC_SYSLOG
526 if (circular_logging ){
531 #ifdef CONFIG_FEATURE_REMOTE_LOG
537 logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
545 memcpy (&readfds, &fds, sizeof (fds));
547 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
548 if (errno == EINTR) continue; /* alarm may have happened. */
549 perror_msg_and_die ("select error");
552 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
553 if (FD_ISSET (fd, &readfds)) {
560 //printf("New Connection request.\n");
561 if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) {
562 perror_msg_and_die ("accept error");
566 //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE);
568 //printf("Serving connection: %i\n",fd);
569 if ( serveConnection(fd) <= 0 ) {
573 } /* fd == sock_fd */
576 } /* for main loop */
579 extern int syslogd_main(int argc, char **argv)
586 /* do normal option parsing */
587 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
590 MarkInterval = atoi(optarg) * 60;
596 logFilePath = xstrdup(optarg);
598 #ifdef CONFIG_FEATURE_REMOTE_LOG
600 RemoteHost = xstrdup(optarg);
601 if ( (p = strchr(RemoteHost, ':'))){
602 RemotePort = atoi(p+1);
608 local_logging = TRUE;
611 #ifdef CONFIG_FEATURE_IPC_SYSLOG
613 circular_logging = TRUE;
621 #ifdef CONFIG_FEATURE_REMOTE_LOG
622 /* If they have not specified remote logging, then log locally */
624 local_logging = TRUE;
628 /* Store away localhost's name before the fork */
629 gethostname(LocalHostName, sizeof(LocalHostName));
630 if ((p = strchr(LocalHostName, '.'))) {
637 #if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
638 if (daemon(0, 1) < 0)
639 perror_msg_and_die("daemon");
641 error_msg_and_die("daemon not supported");
651 c-file-style: "linux"