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 void ipcsyslog_cleanup(void){
133 printf("Exiting Syslogd!\n");
138 shmctl(shmid, IPC_RMID, NULL);
140 semctl(s_semid, 0, IPC_RMID, 0);
143 void ipcsyslog_init(void){
145 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
146 perror_msg_and_die("shmget");
149 if ((buf = shmat(shmid, NULL, 0)) == NULL)
150 perror_msg_and_die("shmat");
154 buf->head=buf->tail=0;
156 // we'll trust the OS to set initial semval to 0 (let's hope)
157 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
158 if (errno == EEXIST){
159 if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
160 perror_msg_and_die("semget");
162 perror_msg_and_die("semget");
165 printf("Buffer already allocated just grab the semaphore?");
169 /* write message to buffer */
170 void circ_message(const char *msg){
171 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
176 * Circular Buffer Algorithm:
177 * --------------------------
179 * Start-off w/ empty buffer of specific size SHM_SIZ
180 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
181 * This is also very handy since we can do printf on message.
183 * Once the buffer is full we need to get rid of the first message in buffer and
184 * insert the new message. (Note: if the message being added is >1 message then
185 * we will need to "remove" >1 old message from the buffer). The way this is done
187 * When we reach the end of the buffer we set a mark and start from the beginning.
188 * Now what about the beginning and end of the buffer? Well we have the "head"
189 * index/pointer which is the starting point for the messages and we have "tail"
190 * index/pointer which is the ending point for the messages. When we "display" the
191 * messages we start from the beginning and continue until we reach "tail". If we
192 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
193 * "tail" are actually offsets from the beginning of the buffer.
195 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
196 * a threasafe way of handling shared memory operations.
198 if ( (buf->tail + l) < buf->size ){
199 /* before we append the message we need to check the HEAD so that we won't
200 overwrite any of the message that we still need and adjust HEAD to point
201 to the next message! */
202 if ( buf->tail < buf->head){
203 if ( (buf->tail + l) >= buf->head ){
204 /* we need to move the HEAD to point to the next message
205 * Theoretically we have enough room to add the whole message to the
206 * buffer, because of the first outer IF statement, so we don't have
207 * to worry about overflows here!
209 int k= buf->tail + l - buf->head; /* we need to know how many bytes
210 we are overwriting to make
212 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
213 if (c != NULL) {/* do a sanity check just in case! */
214 buf->head = c - buf->data + 1; /* we need to convert pointer to
215 offset + skip the '\0' since
216 we need to point to the beginning
217 of the next message */
218 /* Note: HEAD is only used to "retrieve" messages, it's not used
219 when writing messages into our buffer */
220 }else{ /* show an error message to know we messed up? */
221 printf("Weird! Can't find the terminator token??? \n");
225 } /* in other cases no overflows have been done yet, so we don't care! */
227 /* we should be ok to append the message now */
228 strncpy(buf->data + buf->tail,msg,l); /* append our message */
229 buf->tail+=l; /* count full message w/ '\0' terminating char */
231 /* we need to break up the message and "circle" it around */
233 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
235 /* We need to move HEAD! This is always the case since we are going
236 * to "circle" the message.
238 c=memchr(buf->data + k ,'\0', buf->size - k);
240 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
241 /* move head pointer*/
242 buf->head=c-buf->data+1;
244 /* now write the first part of the message */
245 strncpy(buf->data + buf->tail, msg, l - k - 1);
247 /* ALWAYS terminate end of buffer w/ '\0' */
248 buf->data[buf->size-1]='\0';
250 /* now write out the rest of the string to the beginning of the buffer */
251 strcpy(buf->data, &msg[l-k-1]);
253 /* we need to place the TAIL at the end of the message */
256 printf("Weird! Can't find the terminator token from the beginning??? \n");
257 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
264 /* Note: There is also a function called "message()" in init.c */
265 /* Print a message to the log file. */
266 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
267 static void message (char *fmt, ...)
273 fl.l_whence = SEEK_SET;
277 #ifdef CONFIG_FEATURE_IPC_SYSLOG
278 if ((circular_logging) && (buf != NULL)){
280 va_start (arguments, fmt);
281 vsprintf (b, fmt, arguments);
287 if ((fd = device_open (logFilePath,
288 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
291 fcntl (fd, F_SETLKW, &fl);
292 va_start (arguments, fmt);
293 vdprintf (fd, fmt, arguments);
296 fcntl (fd, F_SETLKW, &fl);
299 /* Always send console messages to /dev/console so people will see them. */
300 if ((fd = device_open (_PATH_CONSOLE,
301 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
302 va_start (arguments, fmt);
303 vdprintf (fd, fmt, arguments);
307 fprintf (stderr, "Bummer, can't print: ");
308 va_start (arguments, fmt);
309 vfprintf (stderr, fmt, arguments);
316 static void logMessage (int pri, char *msg)
320 static char res[20] = "";
324 for (c_fac = facilitynames;
325 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
326 for (c_pri = prioritynames;
327 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
328 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
329 snprintf(res, sizeof(res), "<%d>", pri);
331 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
334 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
335 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
337 timestamp = ctime(&now) + 4;
338 timestamp[15] = '\0';
341 timestamp[15] = '\0';
345 /* todo: supress duplicates */
347 #ifdef CONFIG_FEATURE_REMOTE_LOG
348 /* send message to remote logger */
349 if ( -1 != remotefd){
350 static const int IOV_COUNT = 2;
351 struct iovec iov[IOV_COUNT];
352 struct iovec *v = iov;
354 memset(&res, 0, sizeof(res));
355 snprintf(res, sizeof(res), "<%d>", pri);
357 v->iov_len = strlen(res);
361 v->iov_len = strlen(msg);
364 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
365 if (errno == EINTR) goto writev_retry;
366 error_msg_and_die("syslogd: cannot write to remote file handle on"
367 "%s:%d",RemoteHost,RemotePort);
372 /* now spew out the message to wherever it is supposed to go */
373 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
378 static void quit_signal(int sig)
380 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
382 #ifdef CONFIG_FEATURE_IPC_SYSLOG
389 static void domark(int sig)
391 if (MarkInterval > 0) {
392 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
397 /* This must be a #define, since when DODEBUG and BUFFERS_GO_IN_BSS are
398 * enabled, we otherwise get a "storage size isn't constant error. */
400 static int serveConnection (char* tmpbuf, int n_read)
404 while (p < tmpbuf + n_read) {
406 int pri = (LOG_USER | LOG_NOTICE);
407 char line[ BUFSIZE + 1 ];
413 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
414 if ((c == '<') && !gotpri && isdigit(p[1])) {
415 /* Parse the magic priority number. */
418 while (isdigit (*(++p))) {
419 pri = 10 * pri + (*p - '0');
421 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
422 pri = (LOG_USER | LOG_NOTICE);
424 } else if (c == '\n') {
426 } else if (iscntrl (c) && (c < 0177)) {
437 logMessage (pri, line);
443 #ifdef CONFIG_FEATURE_REMOTE_LOG
444 static void init_RemoteLog (void){
446 struct sockaddr_in remoteaddr;
447 struct hostent *hostinfo;
448 int len = sizeof(remoteaddr);
450 memset(&remoteaddr, 0, len);
452 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
455 error_msg_and_die("syslogd: cannot create socket");
458 hostinfo = xgethostbyname(RemoteHost);
460 remoteaddr.sin_family = AF_INET;
461 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
462 remoteaddr.sin_port = htons(RemotePort);
465 Since we are using UDP sockets, connect just sets the default host and port
466 for future operations
468 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
469 error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
475 #define MAXLINE 1024 /* maximum line length */
477 static void doSyslogd (void) __attribute__ ((noreturn));
478 static void doSyslogd (void)
480 struct sockaddr_un sunx;
481 socklen_t addrLength;
487 /* Set up signal handlers. */
488 signal (SIGINT, quit_signal);
489 signal (SIGTERM, quit_signal);
490 signal (SIGQUIT, quit_signal);
491 signal (SIGHUP, SIG_IGN);
492 signal (SIGCHLD, SIG_IGN);
494 signal (SIGCLD, SIG_IGN);
496 signal (SIGALRM, domark);
497 alarm (MarkInterval);
499 /* Create the syslog file so realpath() can work. */
500 if (realpath (_PATH_LOG, lfile) != NULL)
503 memset (&sunx, 0, sizeof (sunx));
504 sunx.sun_family = AF_UNIX;
505 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
506 if ((sock_fd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
507 perror_msg_and_die ("Couldn't get file descriptor for socket " _PATH_LOG);
509 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
510 if (bind (sock_fd, (struct sockaddr *) &sunx, addrLength) < 0)
511 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
513 if (chmod (lfile, 0666) < 0)
514 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
517 FD_SET (sock_fd, &fds);
519 #ifdef CONFIG_FEATURE_IPC_SYSLOG
520 if (circular_logging ){
525 #ifdef CONFIG_FEATURE_REMOTE_LOG
531 logMessage (LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
539 memcpy (&readfds, &fds, sizeof (fds));
541 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
542 if (errno == EINTR) continue; /* alarm may have happened. */
543 perror_msg_and_die ("select error");
546 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
547 if (FD_ISSET (fd, &readfds)) {
553 RESERVE_CONFIG_BUFFER(tmpbuf, BUFSIZE + 1);
555 memset(tmpbuf, '\0', BUFSIZE+1);
556 if ( (i = recv(fd, tmpbuf, MAXLINE - 2, 0)) > 0) {
557 if ( serveConnection(tmpbuf, i) <= 0 ) {
562 perror_msg_and_die ("UNIX socket error");
564 RELEASE_CONFIG_BUFFER (tmpbuf);
565 } /* fd == sock_fd */
568 } /* for main loop */
571 extern int syslogd_main(int argc, char **argv)
578 /* do normal option parsing */
579 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
582 MarkInterval = atoi(optarg) * 60;
588 logFilePath = xstrdup(optarg);
590 #ifdef CONFIG_FEATURE_REMOTE_LOG
592 RemoteHost = xstrdup(optarg);
593 if ( (p = strchr(RemoteHost, ':'))){
594 RemotePort = atoi(p+1);
600 local_logging = TRUE;
603 #ifdef CONFIG_FEATURE_IPC_SYSLOG
605 circular_logging = TRUE;
613 #ifdef CONFIG_FEATURE_REMOTE_LOG
614 /* If they have not specified remote logging, then log locally */
616 local_logging = TRUE;
620 /* Store away localhost's name before the fork */
621 gethostname(LocalHostName, sizeof(LocalHostName));
622 if ((p = strchr(LocalHostName, '.'))) {
629 #if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
630 if (daemon(0, 1) < 0)
631 perror_msg_and_die("daemon");
633 error_msg_and_die("daemon not supported");
643 c-file-style: "linux"