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[MAXPATHLEN];
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[64];
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;
82 #define MAXLINE 1024 /* maximum line length */
85 /* circular buffer variables/structures */
86 #ifdef CONFIG_FEATURE_IPC_SYSLOG
87 #if __GNU_LIBRARY__ < 5
88 #error Sorry. Looks like you are using libc5.
89 #error libc5 shm support isnt good enough.
90 #error Please disable CONFIG_FEATURE_IPC_SYSLOG
98 static const long KEY_ID = 0x414e4547; /*"GENA"*/
100 // Semaphore operation structures
101 static struct shbuf_ds {
102 int size; // size of data written
103 int head; // start of message list
104 int tail; // end of message list
105 char data[1]; // data/messages
106 } *buf = NULL; // shared memory pointer
108 static struct sembuf SMwup[1] = {{1, -1, IPC_NOWAIT}}; // set SMwup
109 static struct sembuf SMwdn[3] = {{0, 0}, {1, 0}, {1, +1}}; // set SMwdn
111 static int shmid = -1; // ipc shared memory id
112 static int s_semid = -1; // ipc semaphore id
113 int data_size = 16000; // data size
114 int shm_size = 16000 + sizeof(*buf); // our buffer size
115 static int circular_logging = FALSE;
118 * sem_up - up()'s a semaphore.
120 static inline void sem_up(int semid)
122 if ( semop(semid, SMwup, 1) == -1 )
123 perror_msg_and_die("semop[SMwup]");
127 * sem_down - down()'s a semaphore
129 static inline void sem_down(int semid)
131 if ( semop(semid, SMwdn, 3) == -1 )
132 perror_msg_and_die("semop[SMwdn]");
136 void ipcsyslog_cleanup(void){
137 printf("Exiting Syslogd!\n");
142 shmctl(shmid, IPC_RMID, NULL);
144 semctl(s_semid, 0, IPC_RMID, 0);
147 void ipcsyslog_init(void){
149 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
150 perror_msg_and_die("shmget");
153 if ((buf = shmat(shmid, NULL, 0)) == NULL)
154 perror_msg_and_die("shmat");
158 buf->head=buf->tail=0;
160 // we'll trust the OS to set initial semval to 0 (let's hope)
161 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
162 if (errno == EEXIST){
163 if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
164 perror_msg_and_die("semget");
166 perror_msg_and_die("semget");
169 printf("Buffer already allocated just grab the semaphore?");
173 /* write message to buffer */
174 void circ_message(const char *msg){
175 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
180 * Circular Buffer Algorithm:
181 * --------------------------
183 * Start-off w/ empty buffer of specific size SHM_SIZ
184 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
185 * This is also very handy since we can do printf on message.
187 * Once the buffer is full we need to get rid of the first message in buffer and
188 * insert the new message. (Note: if the message being added is >1 message then
189 * we will need to "remove" >1 old message from the buffer). The way this is done
191 * When we reach the end of the buffer we set a mark and start from the beginning.
192 * Now what about the beginning and end of the buffer? Well we have the "head"
193 * index/pointer which is the starting point for the messages and we have "tail"
194 * index/pointer which is the ending point for the messages. When we "display" the
195 * messages we start from the beginning and continue until we reach "tail". If we
196 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
197 * "tail" are actually offsets from the beginning of the buffer.
199 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
200 * a threasafe way of handling shared memory operations.
202 if ( (buf->tail + l) < buf->size ){
203 /* before we append the message we need to check the HEAD so that we won't
204 overwrite any of the message that we still need and adjust HEAD to point
205 to the next message! */
206 if ( buf->tail < buf->head){
207 if ( (buf->tail + l) >= buf->head ){
208 /* we need to move the HEAD to point to the next message
209 * Theoretically we have enough room to add the whole message to the
210 * buffer, because of the first outer IF statement, so we don't have
211 * to worry about overflows here!
213 int k= buf->tail + l - buf->head; /* we need to know how many bytes
214 we are overwriting to make
216 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
217 if (c != NULL) {/* do a sanity check just in case! */
218 buf->head = c - buf->data + 1; /* we need to convert pointer to
219 offset + skip the '\0' since
220 we need to point to the beginning
221 of the next message */
222 /* Note: HEAD is only used to "retrieve" messages, it's not used
223 when writing messages into our buffer */
224 }else{ /* show an error message to know we messed up? */
225 printf("Weird! Can't find the terminator token??? \n");
229 } /* in other cases no overflows have been done yet, so we don't care! */
231 /* we should be ok to append the message now */
232 strncpy(buf->data + buf->tail,msg,l); /* append our message */
233 buf->tail+=l; /* count full message w/ '\0' terminating char */
235 /* we need to break up the message and "circle" it around */
237 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
239 /* We need to move HEAD! This is always the case since we are going
240 * to "circle" the message.
242 c=memchr(buf->data + k ,'\0', buf->size - k);
244 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
245 /* move head pointer*/
246 buf->head=c-buf->data+1;
248 /* now write the first part of the message */
249 strncpy(buf->data + buf->tail, msg, l - k - 1);
251 /* ALWAYS terminate end of buffer w/ '\0' */
252 buf->data[buf->size-1]='\0';
254 /* now write out the rest of the string to the beginning of the buffer */
255 strcpy(buf->data, &msg[l-k-1]);
257 /* we need to place the TAIL at the end of the message */
260 printf("Weird! Can't find the terminator token from the beginning??? \n");
261 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
267 #endif /* CONFIG_FEATURE_IPC_SYSLOG */
269 /* Note: There is also a function called "message()" in init.c */
270 /* Print a message to the log file. */
271 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
272 static void message (char *fmt, ...)
278 fl.l_whence = SEEK_SET;
282 #ifdef CONFIG_FEATURE_IPC_SYSLOG
283 if ((circular_logging == TRUE) && (buf != NULL)){
285 va_start (arguments, fmt);
286 vsnprintf (b, sizeof(b)-1, fmt, arguments);
292 if ((fd = device_open (logFilePath,
293 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
296 fcntl (fd, F_SETLKW, &fl);
297 va_start (arguments, fmt);
298 vdprintf (fd, fmt, arguments);
301 fcntl (fd, F_SETLKW, &fl);
304 /* Always send console messages to /dev/console so people will see them. */
305 if ((fd = device_open (_PATH_CONSOLE,
306 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
307 va_start (arguments, fmt);
308 vdprintf (fd, fmt, arguments);
312 fprintf (stderr, "Bummer, can't print: ");
313 va_start (arguments, fmt);
314 vfprintf (stderr, fmt, arguments);
321 static void logMessage (int pri, char *msg)
325 static char res[20] = "";
329 for (c_fac = facilitynames;
330 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
331 for (c_pri = prioritynames;
332 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
333 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
334 snprintf(res, sizeof(res), "<%d>", pri);
336 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
339 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
340 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
342 timestamp = ctime(&now) + 4;
343 timestamp[15] = '\0';
346 timestamp[15] = '\0';
350 /* todo: supress duplicates */
352 #ifdef CONFIG_FEATURE_REMOTE_LOG
353 /* send message to remote logger */
354 if ( -1 != remotefd){
355 static const int IOV_COUNT = 2;
356 struct iovec iov[IOV_COUNT];
357 struct iovec *v = iov;
359 memset(&res, 0, sizeof(res));
360 snprintf(res, sizeof(res), "<%d>", pri);
362 v->iov_len = strlen(res);
366 v->iov_len = strlen(msg);
368 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
369 if (errno == EINTR) goto writev_retry;
370 error_msg_and_die("cannot write to remote file handle on"
371 "%s:%d",RemoteHost,RemotePort);
374 if (local_logging == TRUE)
376 /* now spew out the message to wherever it is supposed to go */
377 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
382 static void quit_signal(int sig)
384 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
386 #ifdef CONFIG_FEATURE_IPC_SYSLOG
393 static void domark(int sig)
395 if (MarkInterval > 0) {
396 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
401 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
402 * 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[ MAXLINE + 1 ];
415 while ( (c = *p) && q < &line[ sizeof (line) - 1 ]) {
417 /* Parse the magic priority number. */
419 while (isdigit (*(++p))) {
420 pri = 10 * pri + (*p - '0');
422 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
423 pri = (LOG_USER | LOG_NOTICE);
425 } else if (c == '\n') {
427 } else if (iscntrl (c) && (c < 0177)) {
438 logMessage (pri, line);
444 #ifdef CONFIG_FEATURE_REMOTE_LOG
445 static void init_RemoteLog (void)
448 struct sockaddr_in remoteaddr;
449 struct hostent *hostinfo;
450 int len = sizeof(remoteaddr);
452 memset(&remoteaddr, 0, len);
454 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
457 error_msg_and_die("cannot create socket");
460 hostinfo = xgethostbyname(RemoteHost);
462 remoteaddr.sin_family = AF_INET;
463 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
464 remoteaddr.sin_port = htons(RemotePort);
467 Since we are using UDP sockets, connect just sets the default host and port
468 for future operations
470 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
471 error_msg_and_die("cannot connect to remote host %s:%d", RemoteHost, RemotePort);
477 static void doSyslogd (void) __attribute__ ((noreturn));
478 static void doSyslogd (void)
480 struct sockaddr_un sunx;
481 socklen_t addrLength;
486 /* Set up signal handlers. */
487 signal (SIGINT, quit_signal);
488 signal (SIGTERM, quit_signal);
489 signal (SIGQUIT, quit_signal);
490 signal (SIGHUP, SIG_IGN);
491 signal (SIGCHLD, SIG_IGN);
493 signal (SIGCLD, SIG_IGN);
495 signal (SIGALRM, domark);
496 alarm (MarkInterval);
498 /* Create the syslog file so realpath() can work. */
499 if (realpath (_PATH_LOG, lfile) != NULL)
502 memset (&sunx, 0, sizeof (sunx));
503 sunx.sun_family = AF_UNIX;
504 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
505 if ((sock_fd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
506 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG);
508 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
509 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0)
510 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
512 if (chmod (lfile, 0666) < 0)
513 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
516 #ifdef CONFIG_FEATURE_IPC_SYSLOG
517 if (circular_logging == TRUE ){
522 #ifdef CONFIG_FEATURE_REMOTE_LOG
523 if (doRemoteLog == TRUE){
528 logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
533 FD_SET (sock_fd, &fds);
535 if (select (sock_fd+1, &fds, NULL, NULL, NULL) < 0) {
536 if (errno == EINTR) {
537 /* alarm may have happened. */
540 perror_msg_and_die ("select error");
543 if (FD_ISSET (sock_fd, &fds)) {
545 RESERVE_CONFIG_BUFFER(tmpbuf, BUFSIZ + 1);
547 memset(tmpbuf, '\0', BUFSIZ+1);
548 if ( (i = recv(sock_fd, tmpbuf, BUFSIZ, 0)) > 0) {
549 serveConnection(tmpbuf, i);
551 perror_msg_and_die ("UNIX socket error");
553 RELEASE_CONFIG_BUFFER (tmpbuf);
555 } /* for main loop */
558 extern int syslogd_main(int argc, char **argv)
561 #if ! defined(__uClinux__)
567 /* do normal option parsing */
568 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
571 MarkInterval = atoi(optarg) * 60;
573 #if ! defined(__uClinux__)
579 logFilePath = xstrdup(optarg);
581 #ifdef CONFIG_FEATURE_REMOTE_LOG
583 RemoteHost = xstrdup(optarg);
584 if ( (p = strchr(RemoteHost, ':'))){
585 RemotePort = atoi(p+1);
591 local_logging = TRUE;
594 #ifdef CONFIG_FEATURE_IPC_SYSLOG
596 circular_logging = TRUE;
604 #ifdef CONFIG_FEATURE_REMOTE_LOG
605 /* If they have not specified remote logging, then log locally */
606 if (doRemoteLog == FALSE)
607 local_logging = TRUE;
611 /* Store away localhost's name before the fork */
612 gethostname(LocalHostName, sizeof(LocalHostName));
613 if ((p = strchr(LocalHostName, '.'))) {
619 #if ! defined(__uClinux__)
620 if (doFork == TRUE) {
621 if (daemon(0, 1) < 0)
622 perror_msg_and_die("daemon");
632 c-file-style: "linux"