1 /* vi: set sw=4 ts=4: */
3 * Mini syslogd implementation for busybox
5 * Copyright (C) 1999,2000,2001 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <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 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/param.h>
47 /* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
49 #include <sys/syslog.h>
52 /* Path for the file where all log messages are written */
53 #define __LOG_FILE "/var/log/messages"
55 /* Path to the unix socket */
56 char lfile[BUFSIZ] = "";
58 static char *logFilePath = __LOG_FILE;
60 /* interval between marks in seconds */
61 static int MarkInterval = 20 * 60;
63 /* localhost's name */
64 static char LocalHostName[32];
66 #ifdef BB_FEATURE_REMOTE_LOG
67 #include <netinet/in.h>
68 /* udp socket for logging to remote host */
69 static int remotefd = -1;
70 /* where do we log? */
71 static char *RemoteHost;
72 /* what port to log to? */
73 static int RemotePort = 514;
74 /* To remote log or not to remote log, that is the question. */
75 static int doRemoteLog = FALSE;
76 static int local_logging = FALSE;
79 /* circular buffer variables/structures */
80 #ifdef BB_FEATURE_IPC_SYSLOG
87 static const long KEY_ID = 0x414e4547; /*"GENA"*/
89 // Semaphore operation structures
90 static struct shbuf_ds {
91 int size; // size of data written
92 int head; // start of message list
93 int tail; // end of message list
94 char data[1]; // data/messages
95 } *buf = NULL; // shared memory pointer
97 static struct sembuf SMwup[1] = {{1, -1, IPC_NOWAIT}}; // set SMwup
98 static struct sembuf SMwdn[3] = {{0, 0}, {1, 0}, {1, +1}}; // set SMwdn
100 static int shmid = -1; // ipc shared memory id
101 static int s_semid = -1; // ipc semaphore id
102 int data_size = 16000; // data size
103 int shm_size = 16000 + sizeof(*buf); // our buffer size
104 static int circular_logging = FALSE;
107 * sem_up - up()'s a semaphore.
109 static inline void sem_up(int semid)
111 if ( semop(semid, SMwup, 1) == -1 )
112 perror_msg_and_die("semop[SMwup]");
116 * sem_down - down()'s a semaphore
118 static inline void sem_down(int semid)
120 if ( semop(semid, SMwdn, 2) == -1 )
121 perror_msg_and_die("semop[SMwdn]");
125 void ipcsyslog_cleanup(void){
126 printf("Exiting Syslogd!\n");
131 shmctl(shmid, IPC_RMID, NULL);
133 semctl(s_semid, 0, IPC_RMID, 0);
136 void ipcsyslog_init(void){
138 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
139 perror_msg_and_die("shmget");
142 if ((buf = shmat(shmid, NULL, 0)) == NULL)
143 perror_msg_and_die("shmat");
147 buf->head=buf->tail=0;
149 // we'll trust the OS to set initial semval to 0 (let's hope)
150 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
151 if (errno == EEXIST){
152 if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
153 perror_msg_and_die("semget");
155 perror_msg_and_die("semget");
158 printf("Buffer already allocated just grab the semaphore?");
162 /* write message to buffer */
163 void circ_message(const char *msg){
164 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
169 * Circular Buffer Algorithm:
170 * --------------------------
172 * Start-off w/ empty buffer of specific size SHM_SIZ
173 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
174 * This is also very handy since we can do printf on message.
176 * Once the buffer is full we need to get rid of the first message in buffer and
177 * insert the new message. (Note: if the message being added is >1 message then
178 * we will need to "remove" >1 old message from the buffer). The way this is done
180 * When we reach the end of the buffer we set a mark and start from the beginning.
181 * Now what about the beginning and end of the buffer? Well we have the "head"
182 * index/pointer which is the starting point for the messages and we have "tail"
183 * index/pointer which is the ending point for the messages. When we "display" the
184 * messages we start from the beginning and continue until we reach "tail". If we
185 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
186 * "tail" are actually offsets from the beginning of the buffer.
188 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
189 * a threasafe way of handling shared memory operations.
191 if ( (buf->tail + l) < buf->size ){
192 /* before we append the message we need to check the HEAD so that we won't
193 overwrite any of the message that we still need and adjust HEAD to point
194 to the next message! */
195 if ( buf->tail < buf->head){
196 if ( (buf->tail + l) >= buf->head ){
197 /* we need to move the HEAD to point to the next message
198 * Theoretically we have enough room to add the whole message to the
199 * buffer, because of the first outer IF statement, so we don't have
200 * to worry about overflows here!
202 int k= buf->tail + l - buf->head; /* we need to know how many bytes
203 we are overwriting to make
205 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
206 if (c != NULL) {/* do a sanity check just in case! */
207 buf->head = c - buf->data + 1; /* we need to convert pointer to
208 offset + skip the '\0' since
209 we need to point to the beginning
210 of the next message */
211 /* Note: HEAD is only used to "retrieve" messages, it's not used
212 when writing messages into our buffer */
213 }else{ /* show an error message to know we messed up? */
214 printf("Weird! Can't find the terminator token??? \n");
218 } /* in other cases no overflows have been done yet, so we don't care! */
220 /* we should be ok to append the message now */
221 strncpy(buf->data + buf->tail,msg,l); /* append our message */
222 buf->tail+=l; /* count full message w/ '\0' terminating char */
224 /* we need to break up the message and "circle" it around */
226 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
228 /* We need to move HEAD! This is always the case since we are going
229 * to "circle" the message.
231 c=memchr(buf->data + k ,'\0', buf->size - k);
233 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
234 /* move head pointer*/
235 buf->head=c-buf->data+1;
237 /* now write the first part of the message */
238 strncpy(buf->data + buf->tail, msg, l - k - 1);
240 /* ALWAYS terminate end of buffer w/ '\0' */
241 buf->data[buf->size-1]='\0';
243 /* now write out the rest of the string to the beginning of the buffer */
244 strcpy(buf->data, &msg[l-k-1]);
246 /* we need to place the TAIL at the end of the message */
249 printf("Weird! Can't find the terminator token from the beginning??? \n");
250 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
257 /* Note: There is also a function called "message()" in init.c */
258 /* Print a message to the log file. */
259 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
260 static void message (char *fmt, ...)
266 fl.l_whence = SEEK_SET;
270 #ifdef BB_FEATURE_IPC_SYSLOG
271 if ((circular_logging == TRUE) && (buf != NULL)){
273 va_start (arguments, fmt);
274 vsprintf (b, fmt, arguments);
280 if ((fd = device_open (logFilePath,
281 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
284 fcntl (fd, F_SETLKW, &fl);
285 va_start (arguments, fmt);
286 vdprintf (fd, fmt, arguments);
289 fcntl (fd, F_SETLKW, &fl);
292 /* Always send console messages to /dev/console so people will see them. */
293 if ((fd = device_open (_PATH_CONSOLE,
294 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
295 va_start (arguments, fmt);
296 vdprintf (fd, fmt, arguments);
300 fprintf (stderr, "Bummer, can't print: ");
301 va_start (arguments, fmt);
302 vfprintf (stderr, fmt, arguments);
309 static void logMessage (int pri, char *msg)
313 static char res[20] = "";
317 for (c_fac = facilitynames;
318 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
319 for (c_pri = prioritynames;
320 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
321 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
322 snprintf(res, sizeof(res), "<%d>", pri);
324 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
327 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
328 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
330 timestamp = ctime(&now) + 4;
331 timestamp[15] = '\0';
334 timestamp[15] = '\0';
338 /* todo: supress duplicates */
340 #ifdef BB_FEATURE_REMOTE_LOG
341 /* send message to remote logger */
342 if ( -1 != remotefd){
343 static const int IOV_COUNT = 2;
344 struct iovec iov[IOV_COUNT];
345 struct iovec *v = iov;
347 bzero(&res, sizeof(res));
348 snprintf(res, sizeof(res), "<%d>", pri);
350 v->iov_len = strlen(res);
354 v->iov_len = strlen(msg);
356 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
357 error_msg_and_die("syslogd: cannot write to remote file handle on"
358 "%s:%d",RemoteHost,RemotePort);
361 if (local_logging == TRUE)
363 /* now spew out the message to wherever it is supposed to go */
364 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
369 static void quit_signal(int sig)
371 logMessage(0, "System log daemon exiting.");
373 #ifdef BB_FEATURE_IPC_SYSLOG
380 static void domark(int sig)
382 if (MarkInterval > 0) {
383 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
388 static const int BUFSIZE = 1023;
389 static int serveConnection (int conn)
391 RESERVE_BB_BUFFER(buf, BUFSIZE + 1);
394 while ((n_read = read (conn, buf, BUFSIZE )) > 0) {
396 int pri = (LOG_USER | LOG_NOTICE);
397 char line[ BUFSIZE + 1 ];
400 char *p = buf, *q = line;
402 buf[ n_read - 1 ] = '\0';
404 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
406 /* Parse the magic priority number. */
408 while (isdigit (*(++p))) {
409 pri = 10 * pri + (*p - '0');
411 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
412 pri = (LOG_USER | LOG_NOTICE);
414 } else if (c == '\n') {
416 } else if (iscntrl (c) && (c < 0177)) {
426 logMessage (pri, line);
432 #ifdef BB_FEATURE_REMOTE_LOG
433 static void init_RemoteLog (void){
435 struct sockaddr_in remoteaddr;
436 struct hostent *hostinfo;
437 int len = sizeof(remoteaddr);
439 bzero(&remoteaddr, len);
441 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
444 error_msg_and_die("syslogd: cannot create socket");
447 hostinfo = (struct hostent *) gethostbyname(RemoteHost);
450 error_msg_and_die("syslogd: cannot resolve remote host name [%s]", RemoteHost);
453 remoteaddr.sin_family = AF_INET;
454 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
455 remoteaddr.sin_port = htons(RemotePort);
458 Since we are using UDP sockets, connect just sets the default host and port
459 for future operations
461 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
462 error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
468 static void doSyslogd (void) __attribute__ ((noreturn));
469 static void doSyslogd (void)
471 struct sockaddr_un sunx;
472 socklen_t addrLength;
478 RESERVE_BB_BUFFER(lfile, BUFSIZ);
480 /* Set up signal handlers. */
481 signal (SIGINT, quit_signal);
482 signal (SIGTERM, quit_signal);
483 signal (SIGQUIT, quit_signal);
484 signal (SIGHUP, SIG_IGN);
485 signal (SIGCHLD, SIG_IGN);
487 signal (SIGCLD, SIG_IGN);
489 signal (SIGALRM, domark);
490 alarm (MarkInterval);
492 /* Create the syslog file so realpath() can work. */
493 close (open (_PATH_LOG, O_RDWR | O_CREAT, 0644));
494 if (realpath (_PATH_LOG, lfile) == NULL)
495 perror_msg_and_die ("Could not resolve path to " _PATH_LOG);
499 memset (&sunx, 0, sizeof (sunx));
500 sunx.sun_family = AF_UNIX;
501 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
502 if ((sock_fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
503 perror_msg_and_die ("Couldn't obtain descriptor for socket " _PATH_LOG);
505 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
506 if ((bind (sock_fd, (struct sockaddr *) &sunx, addrLength)) || (listen (sock_fd, 5)))
507 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
509 if (chmod (lfile, 0666) < 0)
510 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
513 FD_SET (sock_fd, &fds);
515 #ifdef BB_FEATURE_REMOTE_LOG
516 if (doRemoteLog == TRUE){
521 logMessage (0, "syslogd started: BusyBox v" BB_VER " (" BB_BT ")");
529 memcpy (&readfds, &fds, sizeof (fds));
531 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
532 if (errno == EINTR) continue; /* alarm may have happened. */
533 perror_msg_and_die ("select error");
536 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
537 if (FD_ISSET (fd, &readfds)) {
544 //printf("New Connection request.\n");
545 if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) {
546 perror_msg_and_die ("accept error");
550 //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE);
552 //printf("Serving connection: %i\n",fd);
553 serveConnection (fd);
556 } /* fd == sock_fd */
559 } /* for main loop */
562 static void daemon_init (char **argv, char *dz, void fn (void))
566 strncpy(argv[0], dz, strlen(argv[0]));
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 = strdup(optarg);
590 #ifdef BB_FEATURE_REMOTE_LOG
592 RemoteHost = strdup(optarg);
593 if ( (p = strchr(RemoteHost, ':'))){
594 RemotePort = atoi(p+1);
600 local_logging = TRUE;
603 #ifdef BB_FEATURE_IPC_SYSLOG
605 circular_logging = TRUE;
613 #ifdef BB_FEATURE_REMOTE_LOG
614 /* If they have not specified remote logging, then log locally */
615 if (doRemoteLog == FALSE)
616 local_logging = TRUE;
620 /* Store away localhost's name before the fork */
621 gethostname(LocalHostName, sizeof(LocalHostName));
622 if ((p = strchr(LocalHostName, '.'))) {
628 #ifdef BB_FEATURE_IPC_SYSLOG
629 if (circular_logging == TRUE ){
634 if (doFork == TRUE) {
639 daemon_init (argv, "syslogd", doSyslogd);
650 c-file-style: "linux"