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 * 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 BB_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 BB_FEATURE_IPC_SYSLOG
89 static const long KEY_ID = 0x414e4547; /*"GENA"*/
91 // Semaphore operation structures
92 static struct shbuf_ds {
93 int size; // size of data written
94 int head; // start of message list
95 int tail; // end of message list
96 char data[1]; // data/messages
97 } *buf = NULL; // shared memory pointer
99 static struct sembuf SMwup[1] = {{1, -1, IPC_NOWAIT}}; // set SMwup
100 static struct sembuf SMwdn[3] = {{0, 0}, {1, 0}, {1, +1}}; // set SMwdn
102 static int shmid = -1; // ipc shared memory id
103 static int s_semid = -1; // ipc semaphore id
104 int data_size = 16000; // data size
105 int shm_size = 16000 + sizeof(*buf); // our buffer size
106 static int circular_logging = FALSE;
109 * sem_up - up()'s a semaphore.
111 static inline void sem_up(int semid)
113 if ( semop(semid, SMwup, 1) == -1 )
114 perror_msg_and_die("semop[SMwup]");
118 * sem_down - down()'s a semaphore
120 static inline void sem_down(int semid)
122 if ( semop(semid, SMwdn, 2) == -1 )
123 perror_msg_and_die("semop[SMwdn]");
127 void ipcsyslog_cleanup(void){
128 printf("Exiting Syslogd!\n");
133 shmctl(shmid, IPC_RMID, NULL);
135 semctl(s_semid, 0, IPC_RMID, 0);
138 void ipcsyslog_init(void){
140 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1)
141 perror_msg_and_die("shmget");
144 if ((buf = shmat(shmid, NULL, 0)) == NULL)
145 perror_msg_and_die("shmat");
149 buf->head=buf->tail=0;
151 // we'll trust the OS to set initial semval to 0 (let's hope)
152 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1){
153 if (errno == EEXIST){
154 if ((s_semid = semget(KEY_ID, 2, 0)) == -1)
155 perror_msg_and_die("semget");
157 perror_msg_and_die("semget");
160 printf("Buffer already allocated just grab the semaphore?");
164 /* write message to buffer */
165 void circ_message(const char *msg){
166 int l=strlen(msg)+1; /* count the whole message w/ '\0' included */
171 * Circular Buffer Algorithm:
172 * --------------------------
174 * Start-off w/ empty buffer of specific size SHM_SIZ
175 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
176 * This is also very handy since we can do printf on message.
178 * Once the buffer is full we need to get rid of the first message in buffer and
179 * insert the new message. (Note: if the message being added is >1 message then
180 * we will need to "remove" >1 old message from the buffer). The way this is done
182 * When we reach the end of the buffer we set a mark and start from the beginning.
183 * Now what about the beginning and end of the buffer? Well we have the "head"
184 * index/pointer which is the starting point for the messages and we have "tail"
185 * index/pointer which is the ending point for the messages. When we "display" the
186 * messages we start from the beginning and continue until we reach "tail". If we
187 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
188 * "tail" are actually offsets from the beginning of the buffer.
190 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
191 * a threasafe way of handling shared memory operations.
193 if ( (buf->tail + l) < buf->size ){
194 /* before we append the message we need to check the HEAD so that we won't
195 overwrite any of the message that we still need and adjust HEAD to point
196 to the next message! */
197 if ( buf->tail < buf->head){
198 if ( (buf->tail + l) >= buf->head ){
199 /* we need to move the HEAD to point to the next message
200 * Theoretically we have enough room to add the whole message to the
201 * buffer, because of the first outer IF statement, so we don't have
202 * to worry about overflows here!
204 int k= buf->tail + l - buf->head; /* we need to know how many bytes
205 we are overwriting to make
207 char *c=memchr(buf->data+buf->head + k,'\0',buf->size - (buf->head + k));
208 if (c != NULL) {/* do a sanity check just in case! */
209 buf->head = c - buf->data + 1; /* we need to convert pointer to
210 offset + skip the '\0' since
211 we need to point to the beginning
212 of the next message */
213 /* Note: HEAD is only used to "retrieve" messages, it's not used
214 when writing messages into our buffer */
215 }else{ /* show an error message to know we messed up? */
216 printf("Weird! Can't find the terminator token??? \n");
220 } /* in other cases no overflows have been done yet, so we don't care! */
222 /* we should be ok to append the message now */
223 strncpy(buf->data + buf->tail,msg,l); /* append our message */
224 buf->tail+=l; /* count full message w/ '\0' terminating char */
226 /* we need to break up the message and "circle" it around */
228 int k=buf->tail + l - buf->size; /* count # of bytes we don't fit */
230 /* We need to move HEAD! This is always the case since we are going
231 * to "circle" the message.
233 c=memchr(buf->data + k ,'\0', buf->size - k);
235 if (c != NULL) /* if we don't have '\0'??? weird!!! */{
236 /* move head pointer*/
237 buf->head=c-buf->data+1;
239 /* now write the first part of the message */
240 strncpy(buf->data + buf->tail, msg, l - k - 1);
242 /* ALWAYS terminate end of buffer w/ '\0' */
243 buf->data[buf->size-1]='\0';
245 /* now write out the rest of the string to the beginning of the buffer */
246 strcpy(buf->data, &msg[l-k-1]);
248 /* we need to place the TAIL at the end of the message */
251 printf("Weird! Can't find the terminator token from the beginning??? \n");
252 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
259 /* Note: There is also a function called "message()" in init.c */
260 /* Print a message to the log file. */
261 static void message (char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
262 static void message (char *fmt, ...)
268 fl.l_whence = SEEK_SET;
272 #ifdef BB_FEATURE_IPC_SYSLOG
273 if ((circular_logging == TRUE) && (buf != NULL)){
275 va_start (arguments, fmt);
276 vsprintf (b, fmt, arguments);
282 if ((fd = device_open (logFilePath,
283 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
286 fcntl (fd, F_SETLKW, &fl);
287 va_start (arguments, fmt);
288 vdprintf (fd, fmt, arguments);
291 fcntl (fd, F_SETLKW, &fl);
294 /* Always send console messages to /dev/console so people will see them. */
295 if ((fd = device_open (_PATH_CONSOLE,
296 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
297 va_start (arguments, fmt);
298 vdprintf (fd, fmt, arguments);
302 fprintf (stderr, "Bummer, can't print: ");
303 va_start (arguments, fmt);
304 vfprintf (stderr, fmt, arguments);
311 static void logMessage (int pri, char *msg)
315 static char res[20] = "";
319 for (c_fac = facilitynames;
320 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
321 for (c_pri = prioritynames;
322 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
323 if (c_fac->c_name == NULL || c_pri->c_name == NULL)
324 snprintf(res, sizeof(res), "<%d>", pri);
326 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
329 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
330 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
332 timestamp = ctime(&now) + 4;
333 timestamp[15] = '\0';
336 timestamp[15] = '\0';
340 /* todo: supress duplicates */
342 #ifdef BB_FEATURE_REMOTE_LOG
343 /* send message to remote logger */
344 if ( -1 != remotefd){
345 static const int IOV_COUNT = 2;
346 struct iovec iov[IOV_COUNT];
347 struct iovec *v = iov;
349 bzero(&res, sizeof(res));
350 snprintf(res, sizeof(res), "<%d>", pri);
352 v->iov_len = strlen(res);
356 v->iov_len = strlen(msg);
358 if ( -1 == writev(remotefd,iov, IOV_COUNT)){
359 error_msg_and_die("syslogd: cannot write to remote file handle on"
360 "%s:%d",RemoteHost,RemotePort);
363 if (local_logging == TRUE)
365 /* now spew out the message to wherever it is supposed to go */
366 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
371 static void quit_signal(int sig)
373 logMessage(0, "System log daemon exiting.");
375 #ifdef BB_FEATURE_IPC_SYSLOG
382 static void domark(int sig)
384 if (MarkInterval > 0) {
385 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
390 static const int BUFSIZE = 1023;
391 static int serveConnection (int conn)
393 RESERVE_BB_BUFFER(tmpbuf, BUFSIZE + 1);
396 n_read = read (conn, tmpbuf, BUFSIZE );
400 int pri = (LOG_USER | LOG_NOTICE);
401 char line[ BUFSIZE + 1 ];
404 char *p = tmpbuf, *q = line;
406 tmpbuf[ n_read - 1 ] = '\0';
408 while (p && (c = *p) && q < &line[ sizeof (line) - 1 ]) {
410 /* Parse the magic priority number. */
412 while (isdigit (*(++p))) {
413 pri = 10 * pri + (*p - '0');
415 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)){
416 pri = (LOG_USER | LOG_NOTICE);
418 } else if (c == '\n') {
420 } else if (iscntrl (c) && (c < 0177)) {
430 logMessage (pri, line);
436 #ifdef BB_FEATURE_REMOTE_LOG
437 static void init_RemoteLog (void){
439 struct sockaddr_in remoteaddr;
440 struct hostent *hostinfo;
441 int len = sizeof(remoteaddr);
443 bzero(&remoteaddr, len);
445 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
448 error_msg_and_die("syslogd: cannot create socket");
451 hostinfo = (struct hostent *) gethostbyname(RemoteHost);
454 error_msg_and_die("syslogd: cannot resolve remote host name [%s]", RemoteHost);
457 remoteaddr.sin_family = AF_INET;
458 remoteaddr.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
459 remoteaddr.sin_port = htons(RemotePort);
462 Since we are using UDP sockets, connect just sets the default host and port
463 for future operations
465 if ( 0 != (connect(remotefd, (struct sockaddr *) &remoteaddr, len))){
466 error_msg_and_die("syslogd: cannot connect to remote host %s:%d", RemoteHost, RemotePort);
472 static void doSyslogd (void) __attribute__ ((noreturn));
473 static void doSyslogd (void)
475 struct sockaddr_un sunx;
476 socklen_t addrLength;
482 /* Set up signal handlers. */
483 signal (SIGINT, quit_signal);
484 signal (SIGTERM, quit_signal);
485 signal (SIGQUIT, quit_signal);
486 signal (SIGHUP, SIG_IGN);
487 signal (SIGCHLD, SIG_IGN);
489 signal (SIGCLD, SIG_IGN);
491 signal (SIGALRM, domark);
492 alarm (MarkInterval);
494 /* Create the syslog file so realpath() can work. */
495 close (open (_PATH_LOG, O_RDWR | O_CREAT, 0644));
496 if (realpath (_PATH_LOG, lfile) == NULL)
497 perror_msg_and_die ("Could not resolve path to " _PATH_LOG);
501 memset (&sunx, 0, sizeof (sunx));
502 sunx.sun_family = AF_UNIX;
503 strncpy (sunx.sun_path, lfile, sizeof (sunx.sun_path));
504 if ((sock_fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
505 perror_msg_and_die ("Couldn't obtain descriptor for socket " _PATH_LOG);
507 addrLength = sizeof (sunx.sun_family) + strlen (sunx.sun_path);
508 if ((bind (sock_fd, (struct sockaddr *) &sunx, addrLength)) || (listen (sock_fd, 5)))
509 perror_msg_and_die ("Could not connect to socket " _PATH_LOG);
511 if (chmod (lfile, 0666) < 0)
512 perror_msg_and_die ("Could not set permission on " _PATH_LOG);
515 FD_SET (sock_fd, &fds);
517 #ifdef BB_FEATURE_REMOTE_LOG
518 if (doRemoteLog == TRUE){
523 logMessage (0, "syslogd started: BusyBox v" BB_VER " (" BB_BT ")");
531 memcpy (&readfds, &fds, sizeof (fds));
533 if ((n_ready = select (FD_SETSIZE, &readfds, NULL, NULL, NULL)) < 0) {
534 if (errno == EINTR) continue; /* alarm may have happened. */
535 perror_msg_and_die ("select error");
538 for (fd = 0; (n_ready > 0) && (fd < FD_SETSIZE); fd++) {
539 if (FD_ISSET (fd, &readfds)) {
546 //printf("New Connection request.\n");
547 if ((conn = accept (sock_fd, (struct sockaddr *) &sunx, &addrLength)) < 0) {
548 perror_msg_and_die ("accept error");
552 //printf("conn: %i, set_size: %i\n",conn,FD_SETSIZE);
554 //printf("Serving connection: %i\n",fd);
555 if ( serveConnection(fd) <= 0 ) {
559 } /* fd == sock_fd */
562 } /* for main loop */
565 static void daemon_init (char **argv, char *dz, void fn (void))
569 strncpy(argv[0], dz, strlen(argv[0]));
574 extern int syslogd_main(int argc, char **argv)
581 /* do normal option parsing */
582 while ((opt = getopt(argc, argv, "m:nO:R:LC")) > 0) {
585 MarkInterval = atoi(optarg) * 60;
591 logFilePath = strdup(optarg);
593 #ifdef BB_FEATURE_REMOTE_LOG
595 RemoteHost = strdup(optarg);
596 if ( (p = strchr(RemoteHost, ':'))){
597 RemotePort = atoi(p+1);
603 local_logging = TRUE;
606 #ifdef BB_FEATURE_IPC_SYSLOG
608 circular_logging = TRUE;
616 #ifdef BB_FEATURE_REMOTE_LOG
617 /* If they have not specified remote logging, then log locally */
618 if (doRemoteLog == FALSE)
619 local_logging = TRUE;
623 /* Store away localhost's name before the fork */
624 gethostname(LocalHostName, sizeof(LocalHostName));
625 if ((p = strchr(LocalHostName, '.'))) {
631 #ifdef BB_FEATURE_IPC_SYSLOG
632 if (circular_logging == TRUE ){
637 if (doFork == TRUE) {
642 daemon_init (argv, "syslogd", doSyslogd);
653 c-file-style: "linux"