aboutsummaryrefslogtreecommitdiff
path: root/busybox/sysklogd/syslogd.c
diff options
context:
space:
mode:
Diffstat (limited to 'busybox/sysklogd/syslogd.c')
-rw-r--r--busybox/sysklogd/syslogd.c712
1 files changed, 712 insertions, 0 deletions
diff --git a/busybox/sysklogd/syslogd.c b/busybox/sysklogd/syslogd.c
new file mode 100644
index 000000000..8c6c44ee0
--- /dev/null
+++ b/busybox/sysklogd/syslogd.c
@@ -0,0 +1,712 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini syslogd implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
8 *
9 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
10 *
11 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <getopt.h>
35#include <netdb.h>
36#include <paths.h>
37#include <signal.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <time.h>
41#include <string.h>
42#include <unistd.h>
43#include <sys/socket.h>
44#include <sys/types.h>
45#include <sys/un.h>
46#include <sys/param.h>
47
48#include "busybox.h"
49
50/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
51#define SYSLOG_NAMES
52#include <sys/syslog.h>
53#include <sys/uio.h>
54
55/* Path for the file where all log messages are written */
56#define __LOG_FILE "/var/log/messages"
57
58/* Path to the unix socket */
59static char lfile[MAXPATHLEN];
60
61static const char *logFilePath = __LOG_FILE;
62
63#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
64/* max size of message file before being rotated */
65static int logFileSize = 200 * 1024;
66
67/* number of rotated message files */
68static int logFileRotate = 1;
69#endif
70
71/* interval between marks in seconds */
72static int MarkInterval = 20 * 60;
73
74/* localhost's name */
75static char LocalHostName[64];
76
77#ifdef CONFIG_FEATURE_REMOTE_LOG
78#include <netinet/in.h>
79/* udp socket for logging to remote host */
80static int remotefd = -1;
81static struct sockaddr_in remoteaddr;
82
83/* where do we log? */
84static char *RemoteHost;
85
86/* what port to log to? */
87static int RemotePort = 514;
88
89/* To remote log or not to remote log, that is the question. */
90static int doRemoteLog = FALSE;
91static int local_logging = FALSE;
92#endif
93
94/* Make loging output smaller. */
95static bool small = false;
96
97
98#define MAXLINE 1024 /* maximum line length */
99
100
101/* circular buffer variables/structures */
102#ifdef CONFIG_FEATURE_IPC_SYSLOG
103
104#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
105#error Sorry, you must set the syslogd buffer size to at least 4KB.
106#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
107#endif
108
109#include <sys/ipc.h>
110#include <sys/sem.h>
111#include <sys/shm.h>
112
113/* our shared key */
114static const long KEY_ID = 0x414e4547; /*"GENA" */
115
116// Semaphore operation structures
117static struct shbuf_ds {
118 int size; // size of data written
119 int head; // start of message list
120 int tail; // end of message list
121 char data[1]; // data/messages
122} *buf = NULL; // shared memory pointer
123
124static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
125static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
126
127static int shmid = -1; // ipc shared memory id
128static int s_semid = -1; // ipc semaphore id
129static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
130static int circular_logging = FALSE;
131
132/*
133 * sem_up - up()'s a semaphore.
134 */
135static inline void sem_up(int semid)
136{
137 if (semop(semid, SMwup, 1) == -1) {
138 bb_perror_msg_and_die("semop[SMwup]");
139 }
140}
141
142/*
143 * sem_down - down()'s a semaphore
144 */
145static inline void sem_down(int semid)
146{
147 if (semop(semid, SMwdn, 3) == -1) {
148 bb_perror_msg_and_die("semop[SMwdn]");
149 }
150}
151
152
153void ipcsyslog_cleanup(void)
154{
155 printf("Exiting Syslogd!\n");
156 if (shmid != -1) {
157 shmdt(buf);
158 }
159
160 if (shmid != -1) {
161 shmctl(shmid, IPC_RMID, NULL);
162 }
163 if (s_semid != -1) {
164 semctl(s_semid, 0, IPC_RMID, 0);
165 }
166}
167
168void ipcsyslog_init(void)
169{
170 if (buf == NULL) {
171 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
172 bb_perror_msg_and_die("shmget");
173 }
174
175 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
176 bb_perror_msg_and_die("shmat");
177 }
178
179 buf->size = shm_size - sizeof(*buf);
180 buf->head = buf->tail = 0;
181
182 // we'll trust the OS to set initial semval to 0 (let's hope)
183 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
184 if (errno == EEXIST) {
185 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
186 bb_perror_msg_and_die("semget");
187 }
188 } else {
189 bb_perror_msg_and_die("semget");
190 }
191 }
192 } else {
193 printf("Buffer already allocated just grab the semaphore?");
194 }
195}
196
197/* write message to buffer */
198void circ_message(const char *msg)
199{
200 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
201
202 sem_down(s_semid);
203
204 /*
205 * Circular Buffer Algorithm:
206 * --------------------------
207 *
208 * Start-off w/ empty buffer of specific size SHM_SIZ
209 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
210 * This is also very handy since we can do printf on message.
211 *
212 * Once the buffer is full we need to get rid of the first message in buffer and
213 * insert the new message. (Note: if the message being added is >1 message then
214 * we will need to "remove" >1 old message from the buffer). The way this is done
215 * is the following:
216 * When we reach the end of the buffer we set a mark and start from the beginning.
217 * Now what about the beginning and end of the buffer? Well we have the "head"
218 * index/pointer which is the starting point for the messages and we have "tail"
219 * index/pointer which is the ending point for the messages. When we "display" the
220 * messages we start from the beginning and continue until we reach "tail". If we
221 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
222 * "tail" are actually offsets from the beginning of the buffer.
223 *
224 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
225 * a threasafe way of handling shared memory operations.
226 */
227 if ((buf->tail + l) < buf->size) {
228 /* before we append the message we need to check the HEAD so that we won't
229 overwrite any of the message that we still need and adjust HEAD to point
230 to the next message! */
231 if (buf->tail < buf->head) {
232 if ((buf->tail + l) >= buf->head) {
233 /* we need to move the HEAD to point to the next message
234 * Theoretically we have enough room to add the whole message to the
235 * buffer, because of the first outer IF statement, so we don't have
236 * to worry about overflows here!
237 */
238 int k = buf->tail + l - buf->head; /* we need to know how many bytes
239 we are overwriting to make
240 enough room */
241 char *c =
242 memchr(buf->data + buf->head + k, '\0',
243 buf->size - (buf->head + k));
244 if (c != NULL) { /* do a sanity check just in case! */
245 buf->head = c - buf->data + 1; /* we need to convert pointer to
246 offset + skip the '\0' since
247 we need to point to the beginning
248 of the next message */
249 /* Note: HEAD is only used to "retrieve" messages, it's not used
250 when writing messages into our buffer */
251 } else { /* show an error message to know we messed up? */
252 printf("Weird! Can't find the terminator token??? \n");
253 buf->head = 0;
254 }
255 }
256 }
257
258 /* in other cases no overflows have been done yet, so we don't care! */
259 /* we should be ok to append the message now */
260 strncpy(buf->data + buf->tail, msg, l); /* append our message */
261 buf->tail += l; /* count full message w/ '\0' terminating char */
262 } else {
263 /* we need to break up the message and "circle" it around */
264 char *c;
265 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
266
267 /* We need to move HEAD! This is always the case since we are going
268 * to "circle" the message.
269 */
270 c = memchr(buf->data + k, '\0', buf->size - k);
271
272 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
273 /* move head pointer */
274 buf->head = c - buf->data + 1;
275
276 /* now write the first part of the message */
277 strncpy(buf->data + buf->tail, msg, l - k - 1);
278
279 /* ALWAYS terminate end of buffer w/ '\0' */
280 buf->data[buf->size - 1] = '\0';
281
282 /* now write out the rest of the string to the beginning of the buffer */
283 strcpy(buf->data, &msg[l - k - 1]);
284
285 /* we need to place the TAIL at the end of the message */
286 buf->tail = k + 1;
287 } else {
288 printf
289 ("Weird! Can't find the terminator token from the beginning??? \n");
290 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
291 }
292
293 }
294 sem_up(s_semid);
295}
296#endif /* CONFIG_FEATURE_IPC_SYSLOG */
297
298/* Note: There is also a function called "message()" in init.c */
299/* Print a message to the log file. */
300static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
301static void message(char *fmt, ...)
302{
303 int fd;
304 struct flock fl;
305 va_list arguments;
306
307 fl.l_whence = SEEK_SET;
308 fl.l_start = 0;
309 fl.l_len = 1;
310
311#ifdef CONFIG_FEATURE_IPC_SYSLOG
312 if ((circular_logging == TRUE) && (buf != NULL)) {
313 char b[1024];
314
315 va_start(arguments, fmt);
316 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
317 va_end(arguments);
318 circ_message(b);
319
320 } else
321#endif
322 if ((fd =
323 device_open(logFilePath,
324 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
325 O_NONBLOCK)) >= 0) {
326 fl.l_type = F_WRLCK;
327 fcntl(fd, F_SETLKW, &fl);
328#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
329 if ( logFileSize > 0 ) {
330 struct stat statf;
331 int r = fstat(fd, &statf);
332 if( !r && (statf.st_mode & S_IFREG)
333 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
334 if(logFileRotate > 0) {
335 int i;
336 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
337 for(i=logFileRotate-1;i>0;i--) {
338 sprintf(oldFile, "%s.%d", logFilePath, i-1);
339 sprintf(newFile, "%s.%d", logFilePath, i);
340 rename(oldFile, newFile);
341 }
342 sprintf(newFile, "%s.%d", logFilePath, 0);
343 fl.l_type = F_UNLCK;
344 fcntl (fd, F_SETLKW, &fl);
345 close(fd);
346 rename(logFilePath, newFile);
347 fd = device_open (logFilePath,
348 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
349 O_NONBLOCK);
350 fl.l_type = F_WRLCK;
351 fcntl (fd, F_SETLKW, &fl);
352 } else {
353 ftruncate( fd, 0 );
354 }
355 }
356 }
357#endif
358 va_start(arguments, fmt);
359 vdprintf(fd, fmt, arguments);
360 va_end(arguments);
361 fl.l_type = F_UNLCK;
362 fcntl(fd, F_SETLKW, &fl);
363 close(fd);
364 } else {
365 /* Always send console messages to /dev/console so people will see them. */
366 if ((fd =
367 device_open(_PATH_CONSOLE,
368 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
369 va_start(arguments, fmt);
370 vdprintf(fd, fmt, arguments);
371 va_end(arguments);
372 close(fd);
373 } else {
374 fprintf(stderr, "Bummer, can't print: ");
375 va_start(arguments, fmt);
376 vfprintf(stderr, fmt, arguments);
377 fflush(stderr);
378 va_end(arguments);
379 }
380 }
381}
382
383#ifdef CONFIG_FEATURE_REMOTE_LOG
384static void init_RemoteLog(void)
385{
386 memset(&remoteaddr, 0, sizeof(remoteaddr));
387 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
388
389 if (remotefd < 0) {
390 bb_error_msg("cannot create socket");
391 }
392
393 remoteaddr.sin_family = AF_INET;
394 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
395 remoteaddr.sin_port = htons(RemotePort);
396}
397#endif
398
399static void logMessage(int pri, char *msg)
400{
401 time_t now;
402 char *timestamp;
403 static char res[20] = "";
404#ifdef CONFIG_FEATURE_REMOTE_LOG
405 static char line[MAXLINE + 1];
406#endif
407 CODE *c_pri, *c_fac;
408
409 if (pri != 0) {
410 for (c_fac = facilitynames;
411 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
412 for (c_pri = prioritynames;
413 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
414 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
415 snprintf(res, sizeof(res), "<%d>", pri);
416 } else {
417 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
418 }
419 }
420
421 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
422 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
423 time(&now);
424 timestamp = ctime(&now) + 4;
425 timestamp[15] = '\0';
426 } else {
427 timestamp = msg;
428 timestamp[15] = '\0';
429 msg += 16;
430 }
431
432 /* todo: supress duplicates */
433
434#ifdef CONFIG_FEATURE_REMOTE_LOG
435 if (doRemoteLog == TRUE) {
436 /* trying connect the socket */
437 if (-1 == remotefd) {
438 init_RemoteLog();
439 }
440
441 /* if we have a valid socket, send the message */
442 if (-1 != remotefd) {
443 now = 1;
444 snprintf(line, sizeof(line), "<%d> %s", pri, msg);
445
446 retry:
447 /* send message to remote logger */
448 if(( -1 == sendto(remotefd, line, strlen(line), 0,
449 (struct sockaddr *) &remoteaddr,
450 sizeof(remoteaddr))) && (errno == EINTR)) {
451 /* sleep now seconds and retry (with now * 2) */
452 sleep(now);
453 now *= 2;
454 goto retry;
455 }
456 }
457 }
458
459 if (local_logging == TRUE)
460#endif
461 {
462 /* now spew out the message to wherever it is supposed to go */
463 if (small)
464 message("%s %s\n", timestamp, msg);
465 else
466 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
467 }
468}
469
470static void quit_signal(int sig)
471{
472 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
473 unlink(lfile);
474#ifdef CONFIG_FEATURE_IPC_SYSLOG
475 ipcsyslog_cleanup();
476#endif
477
478 exit(TRUE);
479}
480
481static void domark(int sig)
482{
483 if (MarkInterval > 0) {
484 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
485 alarm(MarkInterval);
486 }
487}
488
489/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
490 * enabled, we otherwise get a "storage size isn't constant error. */
491static int serveConnection(char *tmpbuf, int n_read)
492{
493 char *p = tmpbuf;
494
495 while (p < tmpbuf + n_read) {
496
497 int pri = (LOG_USER | LOG_NOTICE);
498 int num_lt = 0;
499 char line[MAXLINE + 1];
500 unsigned char c;
501 char *q = line;
502
503 while ((c = *p) && q < &line[sizeof(line) - 1]) {
504 if (c == '<' && num_lt == 0) {
505 /* Parse the magic priority number. */
506 num_lt++;
507 pri = 0;
508 while (isdigit(*(++p))) {
509 pri = 10 * pri + (*p - '0');
510 }
511 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
512 pri = (LOG_USER | LOG_NOTICE);
513 }
514 } else if (c == '\n') {
515 *q++ = ' ';
516 } else if (iscntrl(c) && (c < 0177)) {
517 *q++ = '^';
518 *q++ = c ^ 0100;
519 } else {
520 *q++ = c;
521 }
522 p++;
523 }
524 *q = '\0';
525 p++;
526 /* Now log it */
527 logMessage(pri, line);
528 }
529 return n_read;
530}
531
532static void doSyslogd(void) __attribute__ ((noreturn));
533static void doSyslogd(void)
534{
535 struct sockaddr_un sunx;
536 socklen_t addrLength;
537
538 int sock_fd;
539 fd_set fds;
540
541 /* Set up signal handlers. */
542 signal(SIGINT, quit_signal);
543 signal(SIGTERM, quit_signal);
544 signal(SIGQUIT, quit_signal);
545 signal(SIGHUP, SIG_IGN);
546 signal(SIGCHLD, SIG_IGN);
547#ifdef SIGCLD
548 signal(SIGCLD, SIG_IGN);
549#endif
550 signal(SIGALRM, domark);
551 alarm(MarkInterval);
552
553 /* Create the syslog file so realpath() can work. */
554 if (realpath(_PATH_LOG, lfile) != NULL) {
555 unlink(lfile);
556 }
557
558 memset(&sunx, 0, sizeof(sunx));
559 sunx.sun_family = AF_UNIX;
560 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
561 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
562 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
563 _PATH_LOG);
564 }
565
566 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
567 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
568 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
569 }
570
571 if (chmod(lfile, 0666) < 0) {
572 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
573 }
574#ifdef CONFIG_FEATURE_IPC_SYSLOG
575 if (circular_logging == TRUE) {
576 ipcsyslog_init();
577 }
578#endif
579
580#ifdef CONFIG_FEATURE_REMOTE_LOG
581 if (doRemoteLog == TRUE) {
582 init_RemoteLog();
583 }
584#endif
585
586 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
587
588 for (;;) {
589
590 FD_ZERO(&fds);
591 FD_SET(sock_fd, &fds);
592
593 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
594 if (errno == EINTR) {
595 /* alarm may have happened. */
596 continue;
597 }
598 bb_perror_msg_and_die("select error");
599 }
600
601 if (FD_ISSET(sock_fd, &fds)) {
602 int i;
603
604 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
605
606 memset(tmpbuf, '\0', MAXLINE + 1);
607 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
608 serveConnection(tmpbuf, i);
609 } else {
610 bb_perror_msg_and_die("UNIX socket error");
611 }
612 RELEASE_CONFIG_BUFFER(tmpbuf);
613 } /* FD_ISSET() */
614 } /* for main loop */
615}
616
617extern int syslogd_main(int argc, char **argv)
618{
619 int opt;
620
621 int doFork = TRUE;
622
623 char *p;
624
625 /* do normal option parsing */
626 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
627 switch (opt) {
628 case 'm':
629 MarkInterval = atoi(optarg) * 60;
630 break;
631 case 'n':
632 doFork = FALSE;
633 break;
634 case 'O':
635 logFilePath = optarg;
636 break;
637#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
638 case 's':
639 logFileSize = atoi(optarg) * 1024;
640 break;
641 case 'b':
642 logFileRotate = atoi(optarg);
643 if( logFileRotate > 99 ) logFileRotate = 99;
644 break;
645#endif
646#ifdef CONFIG_FEATURE_REMOTE_LOG
647 case 'R':
648 RemoteHost = bb_xstrdup(optarg);
649 if ((p = strchr(RemoteHost, ':'))) {
650 RemotePort = atoi(p + 1);
651 *p = '\0';
652 }
653 doRemoteLog = TRUE;
654 break;
655 case 'L':
656 local_logging = TRUE;
657 break;
658#endif
659#ifdef CONFIG_FEATURE_IPC_SYSLOG
660 case 'C':
661 if (optarg) {
662 int buf_size = atoi(optarg);
663 if (buf_size >= 4) {
664 shm_size = buf_size * 1024;
665 }
666 }
667 circular_logging = TRUE;
668 break;
669#endif
670 case 'S':
671 small = true;
672 break;
673 default:
674 bb_show_usage();
675 }
676 }
677
678#ifdef CONFIG_FEATURE_REMOTE_LOG
679 /* If they have not specified remote logging, then log locally */
680 if (doRemoteLog == FALSE)
681 local_logging = TRUE;
682#endif
683
684
685 /* Store away localhost's name before the fork */
686 gethostname(LocalHostName, sizeof(LocalHostName));
687 if ((p = strchr(LocalHostName, '.'))) {
688 *p = '\0';
689 }
690
691 umask(0);
692
693 if (doFork == TRUE) {
694#if defined(__uClinux__)
695 vfork_daemon_rexec(0, 1, argc, argv, "-n");
696#else /* __uClinux__ */
697 if(daemon(0, 1) < 0)
698 bb_perror_msg_and_die("daemon");
699#endif /* __uClinux__ */
700 }
701 doSyslogd();
702
703 return EXIT_SUCCESS;
704}
705
706/*
707Local Variables
708c-file-style: "linux"
709c-basic-offset: 4
710tab-width: 4
711End:
712*/