source: MondoRescue/branches/3.3/mindi-busybox/sysklogd/syslogd.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 32.1 KB
RevLine 
[821]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 *
[2725]13 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]14 */
[3621]15//config:config SYSLOGD
16//config: bool "syslogd"
17//config: default y
18//config: help
19//config: The syslogd utility is used to record logs of all the
20//config: significant events that occur on a system. Every
21//config: message that is logged records the date and time of the
22//config: event, and will generally also record the name of the
23//config: application that generated the message. When used in
24//config: conjunction with klogd, messages from the Linux kernel
25//config: can also be recorded. This is terribly useful,
26//config: especially for finding what happened when something goes
27//config: wrong. And something almost always will go wrong if
28//config: you wait long enough....
29//config:
30//config:config FEATURE_ROTATE_LOGFILE
31//config: bool "Rotate message files"
32//config: default y
33//config: depends on SYSLOGD
34//config: help
35//config: This enables syslogd to rotate the message files
36//config: on his own. No need to use an external rotate script.
37//config:
38//config:config FEATURE_REMOTE_LOG
39//config: bool "Remote Log support"
40//config: default y
41//config: depends on SYSLOGD
42//config: help
43//config: When you enable this feature, the syslogd utility can
44//config: be used to send system log messages to another system
45//config: connected via a network. This allows the remote
46//config: machine to log all the system messages, which can be
47//config: terribly useful for reducing the number of serial
48//config: cables you use. It can also be a very good security
49//config: measure to prevent system logs from being tampered with
50//config: by an intruder.
51//config:
52//config:config FEATURE_SYSLOGD_DUP
53//config: bool "Support -D (drop dups) option"
54//config: default y
55//config: depends on SYSLOGD
56//config: help
57//config: Option -D instructs syslogd to drop consecutive messages
58//config: which are totally the same.
59//config:
60//config:config FEATURE_SYSLOGD_CFG
61//config: bool "Support syslog.conf"
62//config: default y
63//config: depends on SYSLOGD
64//config: help
65//config: Supports restricted syslogd config. See docs/syslog.conf.txt
66//config:
67//config:config FEATURE_SYSLOGD_READ_BUFFER_SIZE
68//config: int "Read buffer size in bytes"
69//config: default 256
70//config: range 256 20000
71//config: depends on SYSLOGD
72//config: help
73//config: This option sets the size of the syslog read buffer.
74//config: Actual memory usage increases around five times the
75//config: change done here.
76//config:
77//config:config FEATURE_IPC_SYSLOG
78//config: bool "Circular Buffer support"
79//config: default y
80//config: depends on SYSLOGD
81//config: help
82//config: When you enable this feature, the syslogd utility will
83//config: use a circular buffer to record system log messages.
84//config: When the buffer is filled it will continue to overwrite
85//config: the oldest messages. This can be very useful for
86//config: systems with little or no permanent storage, since
87//config: otherwise system logs can eventually fill up your
88//config: entire filesystem, which may cause your system to
89//config: break badly.
90//config:
91//config:config FEATURE_IPC_SYSLOG_BUFFER_SIZE
92//config: int "Circular buffer size in Kbytes (minimum 4KB)"
93//config: default 16
94//config: range 4 2147483647
95//config: depends on FEATURE_IPC_SYSLOG
96//config: help
97//config: This option sets the size of the circular buffer
98//config: used to record system log messages.
99//config:
100//config:config FEATURE_KMSG_SYSLOG
101//config: bool "Linux kernel printk buffer support"
102//config: default y
103//config: depends on SYSLOGD
104//config: select PLATFORM_LINUX
105//config: help
106//config: When you enable this feature, the syslogd utility will
107//config: write system log message to the Linux kernel's printk buffer.
108//config: This can be used as a smaller alternative to the syslogd IPC
109//config: support, as klogd and logread aren't needed.
110//config:
111//config: NOTICE: Syslog facilities in log entries needs kernel 3.5+.
[821]112
[3621]113//applet:IF_SYSLOGD(APPLET(syslogd, BB_DIR_SBIN, BB_SUID_DROP))
114
115//kbuild:lib-$(CONFIG_SYSLOGD) += syslogd_and_logger.o
116
[3232]117//usage:#define syslogd_trivial_usage
118//usage: "[OPTIONS]"
119//usage:#define syslogd_full_usage "\n\n"
120//usage: "System logging utility\n"
121//usage: IF_NOT_FEATURE_SYSLOGD_CFG(
122//usage: "(this version of syslogd ignores /etc/syslog.conf)\n"
123//usage: )
124//usage: "\n -n Run in foreground"
[3621]125//usage: IF_FEATURE_REMOTE_LOG(
126//usage: "\n -R HOST[:PORT] Log to HOST:PORT (default PORT:514)"
127//usage: "\n -L Log locally and via network (default is network only if -R)"
128//usage: )
129//usage: IF_FEATURE_IPC_SYSLOG(
130/* NB: -Csize shouldn't have space (because size is optional) */
131//usage: "\n -C[size_kb] Log to shared mem buffer (use logread to read it)"
132//usage: )
133//usage: IF_FEATURE_KMSG_SYSLOG(
134//usage: "\n -K Log to kernel printk buffer (use dmesg to read it)"
135//usage: )
136//usage: "\n -O FILE Log to FILE (default: /var/log/messages, stdout if -)"
[3232]137//usage: IF_FEATURE_ROTATE_LOGFILE(
138//usage: "\n -s SIZE Max size (KB) before rotation (default:200KB, 0=off)"
139//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)"
140//usage: )
[3621]141//usage: "\n -l N Log only messages more urgent than prio N (1-8)"
142//usage: "\n -S Smaller output"
[3232]143//usage: IF_FEATURE_SYSLOGD_DUP(
144//usage: "\n -D Drop duplicates"
145//usage: )
146//usage: IF_FEATURE_SYSLOGD_CFG(
147//usage: "\n -f FILE Use FILE as config (default:/etc/syslog.conf)"
148//usage: )
149/* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */
150//usage:
151//usage:#define syslogd_example_usage
152//usage: "$ syslogd -R masterlog:514\n"
153//usage: "$ syslogd -R 192.168.1.1:601\n"
154
[2725]155/*
156 * Done in syslogd_and_logger.c:
[1765]157#include "libbb.h"
[2725]158#define SYSLOG_NAMES
159#define SYSLOG_NAMES_CONST
160#include <syslog.h>
161*/
[3621]162#ifndef _PATH_LOG
163#define _PATH_LOG "/dev/log"
164#endif
[2725]165
[821]166#include <sys/un.h>
167#include <sys/uio.h>
168
[1765]169#if ENABLE_FEATURE_REMOTE_LOG
170#include <netinet/in.h>
171#endif
[821]172
[1765]173#if ENABLE_FEATURE_IPC_SYSLOG
174#include <sys/ipc.h>
175#include <sys/sem.h>
176#include <sys/shm.h>
177#endif
[821]178
179
[1765]180#define DEBUG 0
[821]181
[1765]182/* MARK code is not very useful, is bloat, and broken:
183 * can deadlock if alarmed to make MARK while writing to IPC buffer
184 * (semaphores are down but do_mark routine tries to down them again) */
185#undef SYSLOGD_MARK
[821]186
[2725]187/* Write locking does not seem to be useful either */
188#undef SYSLOGD_WRLOCK
[821]189
[2725]190enum {
191 MAX_READ = CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE,
192 DNS_WAIT_SEC = 2 * 60,
193};
194
[1765]195/* Semaphore operation structures */
196struct shbuf_ds {
197 int32_t size; /* size of data - 1 */
198 int32_t tail; /* end of message list */
199 char data[1]; /* data/messages */
200};
[821]201
[2725]202#if ENABLE_FEATURE_REMOTE_LOG
203typedef struct {
204 int remoteFD;
205 unsigned last_dns_resolve;
206 len_and_sockaddr *remoteAddr;
207 const char *remoteHostname;
208} remoteHost_t;
209#endif
210
[3232]211typedef struct logFile_t {
212 const char *path;
213 int fd;
[3621]214 time_t last_log_time;
[3232]215#if ENABLE_FEATURE_ROTATE_LOGFILE
216 unsigned size;
217 uint8_t isRegular;
218#endif
219} logFile_t;
220
221#if ENABLE_FEATURE_SYSLOGD_CFG
222typedef struct logRule_t {
223 uint8_t enabled_facility_priomap[LOG_NFACILITIES];
224 struct logFile_t *file;
225 struct logRule_t *next;
226} logRule_t;
227#endif
228
[1765]229/* Allows us to have smaller initializer. Ugly. */
230#define GLOBALS \
[3232]231 logFile_t logFile; \
[1765]232 /* interval between marks in seconds */ \
233 /*int markInterval;*/ \
234 /* level of messages to be logged */ \
235 int logLevel; \
[2725]236IF_FEATURE_ROTATE_LOGFILE( \
[1765]237 /* max size of file before rotation */ \
238 unsigned logFileSize; \
239 /* number of rotated message files */ \
240 unsigned logFileRotate; \
241) \
[2725]242IF_FEATURE_IPC_SYSLOG( \
[1765]243 int shmid; /* ipc shared memory id */ \
244 int s_semid; /* ipc semaphore id */ \
245 int shm_size; \
246 struct sembuf SMwup[1]; \
247 struct sembuf SMwdn[3]; \
[3232]248) \
249IF_FEATURE_SYSLOGD_CFG( \
250 logRule_t *log_rules; \
251) \
252IF_FEATURE_KMSG_SYSLOG( \
253 int kmsgfd; \
254 int primask; \
[1765]255)
[821]256
[1765]257struct init_globals {
258 GLOBALS
259};
[821]260
[1765]261struct globals {
262 GLOBALS
[2725]263
264#if ENABLE_FEATURE_REMOTE_LOG
265 llist_t *remoteHosts;
266#endif
[1765]267#if ENABLE_FEATURE_IPC_SYSLOG
268 struct shbuf_ds *shbuf;
269#endif
[2725]270 /* localhost's name. We print only first 64 chars */
271 char *hostname;
[821]272
[1765]273 /* We recv into recvbuf... */
[2725]274 char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)];
[1765]275 /* ...then copy to parsebuf, escaping control chars */
276 /* (can grow x2 max) */
277 char parsebuf[MAX_READ*2];
278 /* ...then sprintf into printbuf, adding timestamp (15 chars),
279 * host (64), fac.prio (20) to the message */
280 /* (growth by: 15 + 64 + 20 + delims = ~110) */
281 char printbuf[MAX_READ*2 + 128];
282};
283
284static const struct init_globals init_data = {
[3232]285 .logFile = {
286 .path = "/var/log/messages",
287 .fd = -1,
288 },
[1765]289#ifdef SYSLOGD_MARK
290 .markInterval = 20 * 60,
[821]291#endif
[1765]292 .logLevel = 8,
293#if ENABLE_FEATURE_ROTATE_LOGFILE
294 .logFileSize = 200 * 1024,
295 .logFileRotate = 1,
296#endif
297#if ENABLE_FEATURE_IPC_SYSLOG
298 .shmid = -1,
299 .s_semid = -1,
[3232]300 .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), /* default shm size */
[1765]301 .SMwup = { {1, -1, IPC_NOWAIT} },
302 .SMwdn = { {0, 0}, {1, 0}, {1, +1} },
303#endif
304};
[821]305
[1765]306#define G (*ptr_to_globals)
[2725]307#define INIT_G() do { \
308 SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \
309} while (0)
[821]310
311
[1765]312/* Options */
313enum {
314 OPTBIT_mark = 0, // -m
315 OPTBIT_nofork, // -n
316 OPTBIT_outfile, // -O
317 OPTBIT_loglevel, // -l
318 OPTBIT_small, // -S
[2725]319 IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
320 IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
321 IF_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,) // -R
322 IF_FEATURE_REMOTE_LOG( OPTBIT_locallog ,) // -L
323 IF_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
324 IF_FEATURE_SYSLOGD_DUP( OPTBIT_dup ,) // -D
[3232]325 IF_FEATURE_SYSLOGD_CFG( OPTBIT_cfg ,) // -f
326 IF_FEATURE_KMSG_SYSLOG( OPTBIT_kmsg ,) // -K
[821]327
[1765]328 OPT_mark = 1 << OPTBIT_mark ,
329 OPT_nofork = 1 << OPTBIT_nofork ,
330 OPT_outfile = 1 << OPTBIT_outfile ,
331 OPT_loglevel = 1 << OPTBIT_loglevel,
332 OPT_small = 1 << OPTBIT_small ,
[2725]333 OPT_filesize = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
334 OPT_rotatecnt = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
335 OPT_remotelog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0,
336 OPT_locallog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_locallog )) + 0,
337 OPT_circularlog = IF_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
338 OPT_dup = IF_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0,
[3232]339 OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0,
340 OPT_kmsg = IF_FEATURE_KMSG_SYSLOG( (1 << OPTBIT_kmsg )) + 0,
[1765]341};
342#define OPTION_STR "m:nO:l:S" \
[2725]343 IF_FEATURE_ROTATE_LOGFILE("s:" ) \
344 IF_FEATURE_ROTATE_LOGFILE("b:" ) \
345 IF_FEATURE_REMOTE_LOG( "R:" ) \
346 IF_FEATURE_REMOTE_LOG( "L" ) \
347 IF_FEATURE_IPC_SYSLOG( "C::") \
[3232]348 IF_FEATURE_SYSLOGD_DUP( "D" ) \
349 IF_FEATURE_SYSLOGD_CFG( "f:" ) \
350 IF_FEATURE_KMSG_SYSLOG( "K" )
[1765]351#define OPTION_DECL *opt_m, *opt_l \
[2725]352 IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \
353 IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \
[3232]354 IF_FEATURE_IPC_SYSLOG( ,*opt_C = NULL) \
355 IF_FEATURE_SYSLOGD_CFG( ,*opt_f = NULL)
356#define OPTION_PARAM &opt_m, &(G.logFile.path), &opt_l \
[2725]357 IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \
358 IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \
[3232]359 IF_FEATURE_REMOTE_LOG( ,&remoteAddrList) \
360 IF_FEATURE_IPC_SYSLOG( ,&opt_C) \
361 IF_FEATURE_SYSLOGD_CFG( ,&opt_f)
[821]362
[1765]363
[3232]364#if ENABLE_FEATURE_SYSLOGD_CFG
365static const CODE* find_by_name(char *name, const CODE* c_set)
366{
367 for (; c_set->c_name; c_set++) {
368 if (strcmp(name, c_set->c_name) == 0)
369 return c_set;
370 }
371 return NULL;
372}
373#endif
374static const CODE* find_by_val(int val, const CODE* c_set)
375{
376 for (; c_set->c_name; c_set++) {
377 if (c_set->c_val == val)
378 return c_set;
379 }
380 return NULL;
381}
382
383#if ENABLE_FEATURE_SYSLOGD_CFG
384static void parse_syslogdcfg(const char *file)
385{
386 char *t;
387 logRule_t **pp_rule;
388 /* tok[0] set of selectors */
389 /* tok[1] file name */
390 /* tok[2] has to be NULL */
391 char *tok[3];
392 parser_t *parser;
393
394 parser = config_open2(file ? file : "/etc/syslog.conf",
395 file ? xfopen_for_read : fopen_for_read);
396 if (!parser)
397 /* didn't find default /etc/syslog.conf */
398 /* proceed as if we built busybox without config support */
399 return;
400
401 /* use ptr to ptr to avoid checking whether head was initialized */
402 pp_rule = &G.log_rules;
403 /* iterate through lines of config, skipping comments */
404 while (config_read(parser, tok, 3, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
405 char *cur_selector;
406 logRule_t *cur_rule;
407
408 /* unexpected trailing token? */
409 if (tok[2])
410 goto cfgerr;
411
412 cur_rule = *pp_rule = xzalloc(sizeof(*cur_rule));
413
414 cur_selector = tok[0];
415 /* iterate through selectors: "kern.info;kern.!err;..." */
416 do {
417 const CODE *code;
418 char *next_selector;
419 uint8_t negated_prio; /* "kern.!err" */
420 uint8_t single_prio; /* "kern.=err" */
421 uint32_t facmap; /* bitmap of enabled facilities */
422 uint8_t primap; /* bitmap of enabled priorities */
423 unsigned i;
424
425 next_selector = strchr(cur_selector, ';');
426 if (next_selector)
427 *next_selector++ = '\0';
428
429 t = strchr(cur_selector, '.');
430 if (!t)
431 goto cfgerr;
432 *t++ = '\0'; /* separate facility from priority */
433
434 negated_prio = 0;
435 single_prio = 0;
436 if (*t == '!') {
437 negated_prio = 1;
438 ++t;
439 }
440 if (*t == '=') {
441 single_prio = 1;
442 ++t;
443 }
444
445 /* parse priority */
446 if (*t == '*')
447 primap = 0xff; /* all 8 log levels enabled */
448 else {
449 uint8_t priority;
450 code = find_by_name(t, prioritynames);
451 if (!code)
452 goto cfgerr;
453 primap = 0;
454 priority = code->c_val;
455 if (priority == INTERNAL_NOPRI) {
456 /* ensure we take "enabled_facility_priomap[fac] &= 0" branch below */
457 negated_prio = 1;
458 } else {
459 priority = 1 << priority;
460 do {
461 primap |= priority;
462 if (single_prio)
463 break;
464 priority >>= 1;
465 } while (priority);
466 if (negated_prio)
467 primap = ~primap;
468 }
469 }
470
471 /* parse facility */
472 if (*cur_selector == '*')
473 facmap = (1<<LOG_NFACILITIES) - 1;
474 else {
475 char *next_facility;
476 facmap = 0;
477 t = cur_selector;
478 /* iterate through facilities: "kern,daemon.<priospec>" */
479 do {
480 next_facility = strchr(t, ',');
481 if (next_facility)
482 *next_facility++ = '\0';
483 code = find_by_name(t, facilitynames);
484 if (!code)
485 goto cfgerr;
486 /* "mark" is not a real facility, skip it */
487 if (code->c_val != INTERNAL_MARK)
488 facmap |= 1<<(LOG_FAC(code->c_val));
489 t = next_facility;
490 } while (t);
491 }
492
493 /* merge result with previous selectors */
494 for (i = 0; i < LOG_NFACILITIES; ++i) {
495 if (!(facmap & (1<<i)))
496 continue;
497 if (negated_prio)
498 cur_rule->enabled_facility_priomap[i] &= primap;
499 else
500 cur_rule->enabled_facility_priomap[i] |= primap;
501 }
502
503 cur_selector = next_selector;
504 } while (cur_selector);
505
506 /* check whether current file name was mentioned in previous rules or
507 * as global logfile (G.logFile).
508 */
509 if (strcmp(G.logFile.path, tok[1]) == 0) {
510 cur_rule->file = &G.logFile;
511 goto found;
512 }
513 /* temporarily use cur_rule as iterator, but *pp_rule still points
514 * to currently processing rule entry.
515 * NOTE: *pp_rule points to the current (and last in the list) rule.
516 */
517 for (cur_rule = G.log_rules; cur_rule != *pp_rule; cur_rule = cur_rule->next) {
518 if (strcmp(cur_rule->file->path, tok[1]) == 0) {
519 /* found - reuse the same file structure */
520 (*pp_rule)->file = cur_rule->file;
521 cur_rule = *pp_rule;
522 goto found;
523 }
524 }
525 cur_rule->file = xzalloc(sizeof(*cur_rule->file));
526 cur_rule->file->fd = -1;
527 cur_rule->file->path = xstrdup(tok[1]);
528 found:
529 pp_rule = &cur_rule->next;
530 }
531 config_close(parser);
532 return;
533
534 cfgerr:
535 bb_error_msg_and_die("error in '%s' at line %d",
536 file ? file : "/etc/syslog.conf",
537 parser->lineno);
538}
539#endif
540
[821]541/* circular buffer variables/structures */
[1765]542#if ENABLE_FEATURE_IPC_SYSLOG
[821]543
544#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
545#error Sorry, you must set the syslogd buffer size to at least 4KB.
546#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
547#endif
548
[2725]549/* our shared key (syslogd.c and logread.c must be in sync) */
550enum { KEY_ID = 0x414e4547 }; /* "GENA" */
[821]551
552static void ipcsyslog_cleanup(void)
553{
[1765]554 if (G.shmid != -1) {
555 shmdt(G.shbuf);
[821]556 }
[1765]557 if (G.shmid != -1) {
558 shmctl(G.shmid, IPC_RMID, NULL);
[821]559 }
[1765]560 if (G.s_semid != -1) {
561 semctl(G.s_semid, 0, IPC_RMID, 0);
[821]562 }
563}
564
565static void ipcsyslog_init(void)
566{
[1765]567 if (DEBUG)
[2725]568 printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size);
[821]569
[1765]570 G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
571 if (G.shmid == -1) {
572 bb_perror_msg_and_die("shmget");
573 }
[821]574
[1765]575 G.shbuf = shmat(G.shmid, NULL, 0);
[2725]576 if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */
[1765]577 bb_perror_msg_and_die("shmat");
578 }
[821]579
[1765]580 memset(G.shbuf, 0, G.shm_size);
581 G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1;
582 /*G.shbuf->tail = 0;*/
583
[3232]584 /* we'll trust the OS to set initial semval to 0 (let's hope) */
[1765]585 G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
586 if (G.s_semid == -1) {
587 if (errno == EEXIST) {
588 G.s_semid = semget(KEY_ID, 2, 0);
589 if (G.s_semid != -1)
590 return;
[821]591 }
[1765]592 bb_perror_msg_and_die("semget");
[821]593 }
594}
595
[1765]596/* Write message to shared mem buffer */
[3232]597static void log_to_shmem(const char *msg)
[821]598{
[1765]599 int old_tail, new_tail;
[3232]600 int len;
[821]601
[1765]602 if (semop(G.s_semid, G.SMwdn, 3) == -1) {
603 bb_perror_msg_and_die("SMwdn");
604 }
[821]605
[1765]606 /* Circular Buffer Algorithm:
[821]607 * --------------------------
[1765]608 * tail == position where to store next syslog message.
609 * tail's max value is (shbuf->size - 1)
610 * Last byte of buffer is never used and remains NUL.
[821]611 */
[3232]612 len = strlen(msg) + 1; /* length with NUL included */
[1765]613 again:
614 old_tail = G.shbuf->tail;
615 new_tail = old_tail + len;
616 if (new_tail < G.shbuf->size) {
617 /* store message, set new tail */
618 memcpy(G.shbuf->data + old_tail, msg, len);
619 G.shbuf->tail = new_tail;
[821]620 } else {
[1765]621 /* k == available buffer space ahead of old tail */
622 int k = G.shbuf->size - old_tail;
623 /* copy what fits to the end of buffer, and repeat */
624 memcpy(G.shbuf->data + old_tail, msg, k);
625 msg += k;
626 len -= k;
627 G.shbuf->tail = 0;
628 goto again;
[821]629 }
[1765]630 if (semop(G.s_semid, G.SMwup, 1) == -1) {
631 bb_perror_msg_and_die("SMwup");
632 }
633 if (DEBUG)
634 printf("tail:%d\n", G.shbuf->tail);
[821]635}
[1765]636#else
[3232]637static void ipcsyslog_cleanup(void) {}
638static void ipcsyslog_init(void) {}
[1765]639void log_to_shmem(const char *msg);
640#endif /* FEATURE_IPC_SYSLOG */
[821]641
[3232]642#if ENABLE_FEATURE_KMSG_SYSLOG
643static void kmsg_init(void)
644{
645 G.kmsgfd = xopen("/dev/kmsg", O_WRONLY);
[1765]646
[3232]647 /*
648 * kernel < 3.5 expects single char printk KERN_* priority prefix,
649 * from 3.5 onwards the full syslog facility/priority format is supported
650 */
651 if (get_linux_version_code() < KERNEL_VERSION(3,5,0))
652 G.primask = LOG_PRIMASK;
653 else
654 G.primask = -1;
655}
656
657static void kmsg_cleanup(void)
658{
659 if (ENABLE_FEATURE_CLEAN_UP)
660 close(G.kmsgfd);
661}
662
663/* Write message to /dev/kmsg */
664static void log_to_kmsg(int pri, const char *msg)
665{
666 /*
667 * kernel < 3.5 expects single char printk KERN_* priority prefix,
668 * from 3.5 onwards the full syslog facility/priority format is supported
669 */
670 pri &= G.primask;
671
[3621]672 full_write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg));
[3232]673}
674#else
675static void kmsg_init(void) {}
676static void kmsg_cleanup(void) {}
677static void log_to_kmsg(int pri UNUSED_PARAM, const char *msg UNUSED_PARAM) {}
678#endif /* FEATURE_KMSG_SYSLOG */
679
[821]680/* Print a message to the log file. */
[3232]681static void log_locally(time_t now, char *msg, logFile_t *log_file)
[821]682{
[2725]683#ifdef SYSLOGD_WRLOCK
[821]684 struct flock fl;
[2725]685#endif
[1765]686 int len = strlen(msg);
[821]687
[3621]688 /* fd can't be 0 (we connect fd 0 to /dev/log socket) */
689 /* fd is 1 if "-O -" is in use */
690 if (log_file->fd > 1) {
691 /* Reopen log files every second. This allows admin
692 * to delete the files and not worry about restarting us.
[2725]693 * This costs almost nothing since it happens
[3621]694 * _at most_ once a second for each file, and happens
695 * only when each file is actually written.
[2725]696 */
697 if (!now)
698 now = time(NULL);
[3621]699 if (log_file->last_log_time != now) {
700 log_file->last_log_time = now;
[3232]701 close(log_file->fd);
[1765]702 goto reopen;
703 }
[3621]704 }
705 else if (log_file->fd == 1) {
706 /* We are logging to stdout: do nothing */
707 }
708 else {
709 if (LONE_DASH(log_file->path)) {
710 log_file->fd = 1;
711 /* log_file->isRegular = 0; - already is */
712 } else {
[1765]713 reopen:
[3621]714 log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
[2725]715 | O_NOCTTY | O_APPEND | O_NONBLOCK,
716 0666);
[3621]717 if (log_file->fd < 0) {
718 /* cannot open logfile? - print to /dev/console then */
719 int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
720 if (fd < 0)
721 fd = 2; /* then stderr, dammit */
722 full_write(fd, msg, len);
723 if (fd != 2)
724 close(fd);
725 return;
726 }
[1765]727#if ENABLE_FEATURE_ROTATE_LOGFILE
[3621]728 {
729 struct stat statf;
730 log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
731 /* bug (mostly harmless): can wrap around if file > 4gb */
732 log_file->size = statf.st_size;
733 }
734#endif
[1765]735 }
736 }
737
[2725]738#ifdef SYSLOGD_WRLOCK
[821]739 fl.l_whence = SEEK_SET;
740 fl.l_start = 0;
741 fl.l_len = 1;
[1765]742 fl.l_type = F_WRLCK;
[3232]743 fcntl(log_file->fd, F_SETLKW, &fl);
[2725]744#endif
[821]745
[1765]746#if ENABLE_FEATURE_ROTATE_LOGFILE
[3232]747 if (G.logFileSize && log_file->isRegular && log_file->size > G.logFileSize) {
[1765]748 if (G.logFileRotate) { /* always 0..99 */
[3232]749 int i = strlen(log_file->path) + 3 + 1;
[1765]750 char oldFile[i];
751 char newFile[i];
752 i = G.logFileRotate - 1;
753 /* rename: f.8 -> f.9; f.7 -> f.8; ... */
754 while (1) {
[3232]755 sprintf(newFile, "%s.%d", log_file->path, i);
[1765]756 if (i == 0) break;
[3232]757 sprintf(oldFile, "%s.%d", log_file->path, --i);
[2725]758 /* ignore errors - file might be missing */
[1765]759 rename(oldFile, newFile);
760 }
761 /* newFile == "f.0" now */
[3232]762 rename(log_file->path, newFile);
[3621]763 }
764
765 /* We may or may not have just renamed the file away;
766 * if we didn't rename because we aren't keeping any backlog,
767 * then it's time to clobber the file. If we did rename it...,
768 * incredibly, if F and F.0 are hardlinks, POSIX _demands_
769 * that rename returns 0 but does not remove F!!!
770 * (hardlinked F/F.0 pair was observed after
771 * power failure during rename()).
772 * So ensure old file is gone in any case:
773 */
774 unlink(log_file->path);
[2725]775#ifdef SYSLOGD_WRLOCK
[3621]776 fl.l_type = F_UNLCK;
777 fcntl(log_file->fd, F_SETLKW, &fl);
[2725]778#endif
[3621]779 close(log_file->fd);
780 goto reopen;
[1765]781 }
[3621]782/* TODO: what to do on write errors ("disk full")? */
783 len = full_write(log_file->fd, msg, len);
784 if (len > 0)
785 log_file->size += len;
786#else
787 full_write(log_file->fd, msg, len);
[1765]788#endif
[3621]789
[2725]790#ifdef SYSLOGD_WRLOCK
[1765]791 fl.l_type = F_UNLCK;
[3232]792 fcntl(log_file->fd, F_SETLKW, &fl);
[2725]793#endif
[1765]794}
[821]795
[1765]796static void parse_fac_prio_20(int pri, char *res20)
797{
798 const CODE *c_pri, *c_fac;
[821]799
[3232]800 c_fac = find_by_val(LOG_FAC(pri) << 3, facilitynames);
801 if (c_fac) {
802 c_pri = find_by_val(LOG_PRI(pri), prioritynames);
803 if (c_pri) {
804 snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name);
805 return;
[821]806 }
807 }
[3232]808 snprintf(res20, 20, "<%d>", pri);
[821]809}
810
[1765]811/* len parameter is used only for "is there a timestamp?" check.
[2725]812 * NB: some callers cheat and supply len==0 when they know
813 * that there is no timestamp, short-circuiting the test. */
[1765]814static void timestamp_and_log(int pri, char *msg, int len)
[821]815{
816 char *timestamp;
[2725]817 time_t now;
[821]818
[2725]819 /* Jan 18 00:11:22 msg... */
820 /* 01234567890123456 */
[1765]821 if (len < 16 || msg[3] != ' ' || msg[6] != ' '
822 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
823 ) {
[821]824 time(&now);
[2725]825 timestamp = ctime(&now) + 4; /* skip day of week */
[821]826 } else {
[2725]827 now = 0;
[821]828 timestamp = msg;
829 msg += 16;
830 }
[1765]831 timestamp[15] = '\0';
[821]832
[3232]833 if (option_mask32 & OPT_kmsg) {
834 log_to_kmsg(pri, msg);
835 return;
836 }
837
[2725]838 if (option_mask32 & OPT_small)
839 sprintf(G.printbuf, "%s %s\n", timestamp, msg);
840 else {
841 char res[20];
842 parse_fac_prio_20(pri, res);
843 sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg);
844 }
845
[1765]846 /* Log message locally (to file or shared mem) */
[3232]847#if ENABLE_FEATURE_SYSLOGD_CFG
848 {
849 bool match = 0;
850 logRule_t *rule;
851 uint8_t facility = LOG_FAC(pri);
852 uint8_t prio_bit = 1 << LOG_PRI(pri);
853
854 for (rule = G.log_rules; rule; rule = rule->next) {
855 if (rule->enabled_facility_priomap[facility] & prio_bit) {
856 log_locally(now, G.printbuf, rule->file);
857 match = 1;
858 }
859 }
860 if (match)
861 return;
862 }
863#endif
864 if (LOG_PRI(pri) < G.logLevel) {
865#if ENABLE_FEATURE_IPC_SYSLOG
866 if ((option_mask32 & OPT_circularlog) && G.shbuf) {
867 log_to_shmem(G.printbuf);
868 return;
869 }
870#endif
871 log_locally(now, G.printbuf, &G.logFile);
872 }
[821]873}
874
[2725]875static void timestamp_and_log_internal(const char *msg)
876{
877 /* -L, or no -R */
878 if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_locallog))
879 return;
880 timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0);
881}
882
883/* tmpbuf[len] is a NUL byte (set by caller), but there can be other,
884 * embedded NULs. Split messages on each of these NULs, parse prio,
885 * escape control chars and log each locally. */
[1765]886static void split_escape_and_log(char *tmpbuf, int len)
[821]887{
888 char *p = tmpbuf;
889
[1765]890 tmpbuf += len;
891 while (p < tmpbuf) {
892 char c;
893 char *q = G.parsebuf;
[821]894 int pri = (LOG_USER | LOG_NOTICE);
895
[1765]896 if (*p == '<') {
897 /* Parse the magic priority number */
898 pri = bb_strtou(p + 1, &p, 10);
[2725]899 if (*p == '>')
900 p++;
901 if (pri & ~(LOG_FACMASK | LOG_PRIMASK))
[1765]902 pri = (LOG_USER | LOG_NOTICE);
903 }
904
905 while ((c = *p++)) {
906 if (c == '\n')
907 c = ' ';
908 if (!(c & ~0x1f) && c != '\t') {
[821]909 *q++ = '^';
[1765]910 c += '@'; /* ^@, ^A, ^B... */
[821]911 }
[1765]912 *q++ = c;
[821]913 }
914 *q = '\0';
[2725]915
[821]916 /* Now log it */
[3232]917 timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
[821]918 }
919}
920
[1765]921#ifdef SYSLOGD_MARK
922static void do_mark(int sig)
923{
924 if (G.markInterval) {
[2725]925 timestamp_and_log_internal("-- MARK --");
[1765]926 alarm(G.markInterval);
927 }
928}
929#endif
930
[2725]931/* Don't inline: prevent struct sockaddr_un to take up space on stack
932 * permanently */
933static NOINLINE int create_socket(void)
[1765]934{
[821]935 struct sockaddr_un sunx;
936 int sock_fd;
[1765]937 char *dev_log_name;
[821]938
939 memset(&sunx, 0, sizeof(sunx));
940 sunx.sun_family = AF_UNIX;
[1765]941
942 /* Unlink old /dev/log or object it points to. */
943 /* (if it exists, bind will fail) */
[3621]944 strcpy(sunx.sun_path, _PATH_LOG);
945 dev_log_name = xmalloc_follow_symlinks(_PATH_LOG);
[1765]946 if (dev_log_name) {
947 safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
948 free(dev_log_name);
[821]949 }
[2725]950 unlink(sunx.sun_path);
[821]951
[1765]952 sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
953 xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
[3621]954 chmod(_PATH_LOG, 0666);
[1765]955
[2725]956 return sock_fd;
957}
958
959#if ENABLE_FEATURE_REMOTE_LOG
960static int try_to_resolve_remote(remoteHost_t *rh)
961{
962 if (!rh->remoteAddr) {
963 unsigned now = monotonic_sec();
964
965 /* Don't resolve name too often - DNS timeouts can be big */
966 if ((now - rh->last_dns_resolve) < DNS_WAIT_SEC)
967 return -1;
968 rh->last_dns_resolve = now;
969 rh->remoteAddr = host2sockaddr(rh->remoteHostname, 514);
970 if (!rh->remoteAddr)
971 return -1;
[821]972 }
[2725]973 return xsocket(rh->remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
974}
975#endif
976
977static void do_syslogd(void) NORETURN;
978static void do_syslogd(void)
979{
980#if ENABLE_FEATURE_REMOTE_LOG
981 llist_t *item;
982#endif
983#if ENABLE_FEATURE_SYSLOGD_DUP
984 int last_sz = -1;
985 char *last_buf;
986 char *recvbuf = G.recvbuf;
987#else
988#define recvbuf (G.recvbuf)
989#endif
990
991 /* Set up signal handlers (so that they interrupt read()) */
992 signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
993 signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
994 //signal_no_SA_RESTART_empty_mask(SIGQUIT, record_signo);
995 signal(SIGHUP, SIG_IGN);
996#ifdef SYSLOGD_MARK
997 signal(SIGALRM, do_mark);
998 alarm(G.markInterval);
999#endif
[3621]1000 xmove_fd(create_socket(), STDIN_FILENO);
[2725]1001
[3232]1002 if (option_mask32 & OPT_circularlog)
[821]1003 ipcsyslog_init();
1004
[3232]1005 if (option_mask32 & OPT_kmsg)
1006 kmsg_init();
1007
[2725]1008 timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
[821]1009
[2725]1010 while (!bb_got_signal) {
1011 ssize_t sz;
[821]1012
[2725]1013#if ENABLE_FEATURE_SYSLOGD_DUP
1014 last_buf = recvbuf;
1015 if (recvbuf == G.recvbuf)
1016 recvbuf = G.recvbuf + MAX_READ;
1017 else
1018 recvbuf = G.recvbuf;
1019#endif
1020 read_again:
[3621]1021 sz = read(STDIN_FILENO, recvbuf, MAX_READ - 1);
[2725]1022 if (sz < 0) {
1023 if (!bb_got_signal)
[3621]1024 bb_perror_msg("read from %s", _PATH_LOG);
[2725]1025 break;
1026 }
1027
1028 /* Drop trailing '\n' and NULs (typically there is one NUL) */
1029 while (1) {
1030 if (sz == 0)
1031 goto read_again;
1032 /* man 3 syslog says: "A trailing newline is added when needed".
1033 * However, neither glibc nor uclibc do this:
1034 * syslog(prio, "test") sends "test\0" to /dev/log,
1035 * syslog(prio, "test\n") sends "test\n\0".
1036 * IOW: newline is passed verbatim!
1037 * I take it to mean that it's syslogd's job
1038 * to make those look identical in the log files. */
1039 if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
1040 break;
1041 sz--;
1042 }
1043#if ENABLE_FEATURE_SYSLOGD_DUP
1044 if ((option_mask32 & OPT_dup) && (sz == last_sz))
1045 if (memcmp(last_buf, recvbuf, sz) == 0)
[821]1046 continue;
[2725]1047 last_sz = sz;
1048#endif
1049#if ENABLE_FEATURE_REMOTE_LOG
1050 /* Stock syslogd sends it '\n'-terminated
1051 * over network, mimic that */
1052 recvbuf[sz] = '\n';
1053
1054 /* We are not modifying log messages in any way before send */
1055 /* Remote site cannot trust _us_ anyway and need to do validation again */
1056 for (item = G.remoteHosts; item != NULL; item = item->link) {
1057 remoteHost_t *rh = (remoteHost_t *)item->data;
1058
1059 if (rh->remoteFD == -1) {
1060 rh->remoteFD = try_to_resolve_remote(rh);
1061 if (rh->remoteFD == -1)
1062 continue;
[821]1063 }
1064
[2725]1065 /* Send message to remote logger.
1066 * On some errors, close and set remoteFD to -1
1067 * so that DNS resolution is retried.
1068 */
1069 if (sendto(rh->remoteFD, recvbuf, sz+1,
1070 MSG_DONTWAIT | MSG_NOSIGNAL,
1071 &(rh->remoteAddr->u.sa), rh->remoteAddr->len) == -1
1072 ) {
1073 switch (errno) {
1074 case ECONNRESET:
1075 case ENOTCONN: /* paranoia */
1076 case EPIPE:
1077 close(rh->remoteFD);
1078 rh->remoteFD = -1;
1079 free(rh->remoteAddr);
1080 rh->remoteAddr = NULL;
[1765]1081 }
[821]1082 }
[2725]1083 }
[1765]1084#endif
[2725]1085 if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
1086 recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */
1087 split_escape_and_log(recvbuf, sz);
1088 }
1089 } /* while (!bb_got_signal) */
1090
1091 timestamp_and_log_internal("syslogd exiting");
[3232]1092 remove_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid");
1093 ipcsyslog_cleanup();
1094 if (option_mask32 & OPT_kmsg)
1095 kmsg_cleanup();
[2725]1096 kill_myself_with_sig(bb_got_signal);
1097#undef recvbuf
[821]1098}
1099
[2725]1100int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1101int syslogd_main(int argc UNUSED_PARAM, char **argv)
[821]1102{
[2725]1103 int opts;
[1765]1104 char OPTION_DECL;
[2725]1105#if ENABLE_FEATURE_REMOTE_LOG
1106 llist_t *remoteAddrList = NULL;
1107#endif
[821]1108
[2725]1109 INIT_G();
[821]1110
[2725]1111 /* No non-option params, -R can occur multiple times */
1112 opt_complementary = "=0" IF_FEATURE_REMOTE_LOG(":R::");
1113 opts = getopt32(argv, OPTION_STR, OPTION_PARAM);
1114#if ENABLE_FEATURE_REMOTE_LOG
1115 while (remoteAddrList) {
1116 remoteHost_t *rh = xzalloc(sizeof(*rh));
1117 rh->remoteHostname = llist_pop(&remoteAddrList);
1118 rh->remoteFD = -1;
1119 rh->last_dns_resolve = monotonic_sec() - DNS_WAIT_SEC - 1;
1120 llist_add_to(&G.remoteHosts, rh);
1121 }
1122#endif
1123
[1765]1124#ifdef SYSLOGD_MARK
[2725]1125 if (opts & OPT_mark) // -m
[1765]1126 G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60;
[821]1127#endif
[2725]1128 //if (opts & OPT_nofork) // -n
1129 //if (opts & OPT_outfile) // -O
1130 if (opts & OPT_loglevel) // -l
[1765]1131 G.logLevel = xatou_range(opt_l, 1, 8);
[2725]1132 //if (opts & OPT_small) // -S
[1765]1133#if ENABLE_FEATURE_ROTATE_LOGFILE
[2725]1134 if (opts & OPT_filesize) // -s
[1765]1135 G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024;
[2725]1136 if (opts & OPT_rotatecnt) // -b
[1765]1137 G.logFileRotate = xatou_range(opt_b, 0, 99);
[821]1138#endif
[1765]1139#if ENABLE_FEATURE_IPC_SYSLOG
1140 if (opt_C) // -Cn
1141 G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
1142#endif
[821]1143 /* If they have not specified remote logging, then log locally */
[2725]1144 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R
[1765]1145 option_mask32 |= OPT_locallog;
[3232]1146#if ENABLE_FEATURE_SYSLOGD_CFG
1147 parse_syslogdcfg(opt_f);
1148#endif
[821]1149
1150 /* Store away localhost's name before the fork */
[2725]1151 G.hostname = safe_gethostname();
1152 *strchrnul(G.hostname, '.') = '\0';
[821]1153
[2725]1154 if (!(opts & OPT_nofork)) {
[1765]1155 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
1156 }
[3232]1157
[2725]1158 //umask(0); - why??
[3232]1159 write_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid");
1160
[1765]1161 do_syslogd();
1162 /* return EXIT_SUCCESS; */
[821]1163}
[2725]1164
1165/* Clean up. Needed because we are included from syslogd_and_logger.c */
1166#undef DEBUG
1167#undef SYSLOGD_MARK
1168#undef SYSLOGD_WRLOCK
1169#undef G
1170#undef GLOBALS
1171#undef INIT_G
1172#undef OPTION_STR
1173#undef OPTION_DECL
1174#undef OPTION_PARAM
Note: See TracBrowser for help on using the repository browser.