1 /* vi: set sw=4 ts=4: */
3 * Mini syslogd implementation for busybox
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
9 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
11 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
13 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
20 /* SYSLOG_NAMES defined to pull prioritynames[] and facilitynames[]
21 * from syslog.h. Grrrr - glibc puts those in _rwdata_! :( */
23 #define SYSLOG_NAMES_CONST /* uclibc is saner :) */
24 #include <sys/syslog.h>
27 #if ENABLE_FEATURE_REMOTE_LOG
28 #include <netinet/in.h>
31 #if ENABLE_FEATURE_IPC_SYSLOG
40 /* MARK code is not very useful, is bloat, and broken:
41 * can deadlock if alarmed to make MARK while writing to IPC buffer
42 * (semaphores are down but do_mark routine tries to down them again) */
45 enum { MAX_READ = 256 };
47 /* Semaphore operation structures */
49 int32_t size; /* size of data - 1 */
50 int32_t tail; /* end of message list */
51 char data[1]; /* data/messages */
54 /* Allows us to have smaller initializer. Ugly. */
56 const char *logFilePath; \
58 /* interval between marks in seconds */ \
59 /*int markInterval;*/ \
60 /* level of messages to be logged */ \
62 USE_FEATURE_ROTATE_LOGFILE( \
63 /* max size of file before rotation */ \
64 unsigned logFileSize; \
65 /* number of rotated message files */ \
66 unsigned logFileRotate; \
67 unsigned curFileSize; \
70 USE_FEATURE_REMOTE_LOG( \
71 /* udp socket for remote logging */ \
73 len_and_sockaddr* remoteAddr; \
75 USE_FEATURE_IPC_SYSLOG( \
76 int shmid; /* ipc shared memory id */ \
77 int s_semid; /* ipc semaphore id */ \
79 struct sembuf SMwup[1]; \
80 struct sembuf SMwdn[3]; \
89 #if ENABLE_FEATURE_IPC_SYSLOG
90 struct shbuf_ds *shbuf;
93 /* localhost's name */
94 char localHostName[64];
96 /* We recv into recvbuf... */
97 char recvbuf[MAX_READ];
98 /* ...then copy to parsebuf, escaping control chars */
99 /* (can grow x2 max) */
100 char parsebuf[MAX_READ*2];
101 /* ...then sprintf into printbuf, adding timestamp (15 chars),
102 * host (64), fac.prio (20) to the message */
103 /* (growth by: 15 + 64 + 20 + delims = ~110) */
104 char printbuf[MAX_READ*2 + 128];
107 static const struct init_globals init_data = {
108 .logFilePath = "/var/log/messages",
111 .markInterval = 20 * 60,
114 #if ENABLE_FEATURE_ROTATE_LOGFILE
115 .logFileSize = 200 * 1024,
118 #if ENABLE_FEATURE_REMOTE_LOG
121 #if ENABLE_FEATURE_IPC_SYSLOG
124 .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), // default shm size
125 .SMwup = { {1, -1, IPC_NOWAIT} },
126 .SMwdn = { {0, 0}, {1, 0}, {1, +1} },
130 #define G (*ptr_to_globals)
135 OPTBIT_mark = 0, // -m
137 OPTBIT_outfile, // -O
138 OPTBIT_loglevel, // -l
140 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
141 USE_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
142 USE_FEATURE_REMOTE_LOG( OPTBIT_remote ,) // -R
143 USE_FEATURE_REMOTE_LOG( OPTBIT_localtoo ,) // -L
144 USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
146 OPT_mark = 1 << OPTBIT_mark ,
147 OPT_nofork = 1 << OPTBIT_nofork ,
148 OPT_outfile = 1 << OPTBIT_outfile ,
149 OPT_loglevel = 1 << OPTBIT_loglevel,
150 OPT_small = 1 << OPTBIT_small ,
151 OPT_filesize = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
152 OPT_rotatecnt = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
153 OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remote )) + 0,
154 OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_localtoo )) + 0,
155 OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
157 #define OPTION_STR "m:nO:l:S" \
158 USE_FEATURE_ROTATE_LOGFILE("s:" ) \
159 USE_FEATURE_ROTATE_LOGFILE("b:" ) \
160 USE_FEATURE_REMOTE_LOG( "R:" ) \
161 USE_FEATURE_REMOTE_LOG( "L" ) \
162 USE_FEATURE_IPC_SYSLOG( "C::")
163 #define OPTION_DECL *opt_m, *opt_l \
164 USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
165 USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
166 USE_FEATURE_REMOTE_LOG( ,*opt_R) \
167 USE_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
168 #define OPTION_PARAM &opt_m, &G.logFilePath, &opt_l \
169 USE_FEATURE_ROTATE_LOGFILE(,&opt_s) \
170 USE_FEATURE_ROTATE_LOGFILE(,&opt_b) \
171 USE_FEATURE_REMOTE_LOG( ,&opt_R) \
172 USE_FEATURE_IPC_SYSLOG( ,&opt_C)
175 /* circular buffer variables/structures */
176 #if ENABLE_FEATURE_IPC_SYSLOG
178 #if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
179 #error Sorry, you must set the syslogd buffer size to at least 4KB.
180 #error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
184 #define KEY_ID ((long)0x414e4547) /* "GENA" */
186 static void ipcsyslog_cleanup(void)
192 shmctl(G.shmid, IPC_RMID, NULL);
194 if (G.s_semid != -1) {
195 semctl(G.s_semid, 0, IPC_RMID, 0);
199 static void ipcsyslog_init(void)
202 printf("shmget(%lx, %d,...)\n", KEY_ID, G.shm_size);
204 G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
206 bb_perror_msg_and_die("shmget");
209 G.shbuf = shmat(G.shmid, NULL, 0);
211 bb_perror_msg_and_die("shmat");
214 memset(G.shbuf, 0, G.shm_size);
215 G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1;
216 /*G.shbuf->tail = 0;*/
218 // we'll trust the OS to set initial semval to 0 (let's hope)
219 G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
220 if (G.s_semid == -1) {
221 if (errno == EEXIST) {
222 G.s_semid = semget(KEY_ID, 2, 0);
226 bb_perror_msg_and_die("semget");
230 /* Write message to shared mem buffer */
231 static void log_to_shmem(const char *msg, int len)
233 int old_tail, new_tail;
235 if (semop(G.s_semid, G.SMwdn, 3) == -1) {
236 bb_perror_msg_and_die("SMwdn");
239 /* Circular Buffer Algorithm:
240 * --------------------------
241 * tail == position where to store next syslog message.
242 * tail's max value is (shbuf->size - 1)
243 * Last byte of buffer is never used and remains NUL.
245 len++; /* length with NUL included */
247 old_tail = G.shbuf->tail;
248 new_tail = old_tail + len;
249 if (new_tail < G.shbuf->size) {
250 /* store message, set new tail */
251 memcpy(G.shbuf->data + old_tail, msg, len);
252 G.shbuf->tail = new_tail;
254 /* k == available buffer space ahead of old tail */
255 int k = G.shbuf->size - old_tail;
256 /* copy what fits to the end of buffer, and repeat */
257 memcpy(G.shbuf->data + old_tail, msg, k);
263 if (semop(G.s_semid, G.SMwup, 1) == -1) {
264 bb_perror_msg_and_die("SMwup");
267 printf("tail:%d\n", G.shbuf->tail);
270 void ipcsyslog_cleanup(void);
271 void ipcsyslog_init(void);
272 void log_to_shmem(const char *msg);
273 #endif /* FEATURE_IPC_SYSLOG */
276 /* Print a message to the log file. */
277 static void log_locally(char *msg)
280 int len = strlen(msg);
282 #if ENABLE_FEATURE_IPC_SYSLOG
283 if ((option_mask32 & OPT_circularlog) && G.shbuf) {
284 log_to_shmem(msg, len);
291 if (G.last_log_time != cur) {
292 G.last_log_time = cur; /* reopen log file every second */
298 G.logFD = device_open(G.logFilePath, O_WRONLY | O_CREAT
299 | O_NOCTTY | O_APPEND | O_NONBLOCK);
301 /* cannot open logfile? - print to /dev/console then */
302 int fd = device_open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
304 fd = 2; /* then stderr, dammit */
305 full_write(fd, msg, len);
310 #if ENABLE_FEATURE_ROTATE_LOGFILE
313 G.isRegular = (fstat(G.logFD, &statf) == 0 && S_ISREG(statf.st_mode));
314 /* bug (mostly harmless): can wrap around if file > 4gb */
315 G.curFileSize = statf.st_size;
320 fl.l_whence = SEEK_SET;
324 fcntl(G.logFD, F_SETLKW, &fl);
326 #if ENABLE_FEATURE_ROTATE_LOGFILE
327 if (G.logFileSize && G.isRegular && G.curFileSize > G.logFileSize) {
328 if (G.logFileRotate) { /* always 0..99 */
329 int i = strlen(G.logFilePath) + 3 + 1;
332 i = G.logFileRotate - 1;
333 /* rename: f.8 -> f.9; f.7 -> f.8; ... */
335 sprintf(newFile, "%s.%d", G.logFilePath, i);
337 sprintf(oldFile, "%s.%d", G.logFilePath, --i);
338 rename(oldFile, newFile);
340 /* newFile == "f.0" now */
341 rename(G.logFilePath, newFile);
343 fcntl(G.logFD, F_SETLKW, &fl);
347 ftruncate(G.logFD, 0);
351 full_write(G.logFD, msg, len);
353 fcntl(G.logFD, F_SETLKW, &fl);
356 static void parse_fac_prio_20(int pri, char *res20)
358 const CODE *c_pri, *c_fac;
361 c_fac = facilitynames;
362 while (c_fac->c_name) {
363 if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
366 /* facility is found, look for prio */
367 c_pri = prioritynames;
368 while (c_pri->c_name) {
369 if (c_pri->c_val != LOG_PRI(pri)) {
372 snprintf(res20, 20, "%s.%s",
373 c_fac->c_name, c_pri->c_name);
376 /* prio not found, bail out */
379 snprintf(res20, 20, "<%d>", pri);
383 /* len parameter is used only for "is there a timestamp?" check.
384 * NB: some callers cheat and supply 0 when they know
385 * that there is no timestamp, short-cutting the test. */
386 static void timestamp_and_log(int pri, char *msg, int len)
390 if (len < 16 || msg[3] != ' ' || msg[6] != ' '
391 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
395 timestamp = ctime(&now) + 4;
400 timestamp[15] = '\0';
402 /* Log message locally (to file or shared mem) */
403 if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
404 if (LOG_PRI(pri) < G.logLevel) {
405 if (option_mask32 & OPT_small)
406 sprintf(G.printbuf, "%s %s\n", timestamp, msg);
409 parse_fac_prio_20(pri, res);
410 sprintf(G.printbuf, "%s %s %s %s\n", timestamp, G.localHostName, res, msg);
412 log_locally(G.printbuf);
417 static void split_escape_and_log(char *tmpbuf, int len)
424 char *q = G.parsebuf;
425 int pri = (LOG_USER | LOG_NOTICE);
428 /* Parse the magic priority number */
429 pri = bb_strtou(p + 1, &p, 10);
431 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
432 pri = (LOG_USER | LOG_NOTICE);
439 if (!(c & ~0x1f) && c != '\t') {
441 c += '@'; /* ^@, ^A, ^B... */
447 timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
451 static void quit_signal(int sig)
453 timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)"syslogd exiting", 0);
454 puts("syslogd exiting");
455 if (ENABLE_FEATURE_IPC_SYSLOG)
461 static void do_mark(int sig)
463 if (G.markInterval) {
464 timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)"-- MARK --", 0);
465 alarm(G.markInterval);
470 static void do_syslogd(void) ATTRIBUTE_NORETURN;
471 static void do_syslogd(void)
473 struct sockaddr_un sunx;
477 /* Set up signal handlers */
478 signal(SIGINT, quit_signal);
479 signal(SIGTERM, quit_signal);
480 signal(SIGQUIT, quit_signal);
481 signal(SIGHUP, SIG_IGN);
482 signal(SIGCHLD, SIG_IGN);
484 signal(SIGCLD, SIG_IGN);
487 signal(SIGALRM, do_mark);
488 alarm(G.markInterval);
490 remove_pidfile("/var/run/syslogd.pid");
492 memset(&sunx, 0, sizeof(sunx));
493 sunx.sun_family = AF_UNIX;
494 strcpy(sunx.sun_path, "/dev/log");
496 /* Unlink old /dev/log or object it points to. */
497 /* (if it exists, bind will fail) */
498 logmode = LOGMODE_NONE;
499 dev_log_name = xmalloc_readlink_or_warn("/dev/log");
500 logmode = LOGMODE_STDIO;
502 int fd = xopen(".", O_NONBLOCK);
504 /* we do not check whether this is a link also */
505 unlink(dev_log_name);
508 safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
514 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
515 xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
517 if (chmod("/dev/log", 0666) < 0) {
518 bb_perror_msg_and_die("cannot set permission on /dev/log");
520 if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
524 timestamp_and_log(LOG_SYSLOG | LOG_INFO,
525 (char*)"syslogd started: BusyBox v" BB_VER, 0);
530 sz = read(sock_fd, G.recvbuf, MAX_READ - 1);
533 continue; /* EOF from unix socket??? */
534 if (errno == EINTR) /* alarm may have happened */
536 bb_perror_msg_and_die("read from /dev/log");
539 /* TODO: maybe suppress duplicates? */
540 #if ENABLE_FEATURE_REMOTE_LOG
541 /* We are not modifying log messages in any way before send */
542 /* Remote site cannot trust _us_ anyway and need to do validation again */
544 if (-1 == G.remoteFD) {
545 G.remoteFD = socket(G.remoteAddr->sa.sa_family, SOCK_DGRAM, 0);
547 if (-1 != G.remoteFD) {
548 /* send message to remote logger, ignore possible error */
549 sendto(G.remoteFD, G.recvbuf, sz, MSG_DONTWAIT,
550 &G.remoteAddr->sa, G.remoteAddr->len);
554 G.recvbuf[sz] = '\0';
555 split_escape_and_log(G.recvbuf, sz);
559 int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
560 int syslogd_main(int argc, char **argv)
565 PTR_TO_GLOBALS = memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data));
567 /* do normal option parsing */
568 opt_complementary = "=0"; /* no non-option params */
569 getopt32(argv, OPTION_STR, OPTION_PARAM);
571 if (option_mask32 & OPT_mark) // -m
572 G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60;
574 //if (option_mask32 & OPT_nofork) // -n
575 //if (option_mask32 & OPT_outfile) // -O
576 if (option_mask32 & OPT_loglevel) // -l
577 G.logLevel = xatou_range(opt_l, 1, 8);
578 //if (option_mask32 & OPT_small) // -S
579 #if ENABLE_FEATURE_ROTATE_LOGFILE
580 if (option_mask32 & OPT_filesize) // -s
581 G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024;
582 if (option_mask32 & OPT_rotatecnt) // -b
583 G.logFileRotate = xatou_range(opt_b, 0, 99);
585 #if ENABLE_FEATURE_REMOTE_LOG
586 if (option_mask32 & OPT_remotelog) { // -R
587 G.remoteAddr = xhost2sockaddr(opt_R, 514);
589 //if (option_mask32 & OPT_locallog) // -L
591 #if ENABLE_FEATURE_IPC_SYSLOG
593 G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
596 /* If they have not specified remote logging, then log locally */
597 if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_remotelog))
598 option_mask32 |= OPT_locallog;
600 /* Store away localhost's name before the fork */
601 gethostname(G.localHostName, sizeof(G.localHostName));
602 p = strchr(G.localHostName, '.');
607 if (!(option_mask32 & OPT_nofork)) {
608 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
611 write_pidfile("/var/run/syslogd.pid");
613 /* return EXIT_SUCCESS; */