aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sysklogd/Config.src7
-rw-r--r--sysklogd/syslogd.c333
2 files changed, 275 insertions, 65 deletions
diff --git a/sysklogd/Config.src b/sysklogd/Config.src
index d62dc5f5c..b3e13d7c0 100644
--- a/sysklogd/Config.src
+++ b/sysklogd/Config.src
@@ -52,6 +52,13 @@ config FEATURE_SYSLOGD_DUP
Option -D instructs syslogd to drop consecutive messages
which are totally the same.
+config FEATURE_SYSLOGD_CFG
+ bool "Support syslog.conf"
+ default y
+ depends on SYSLOGD
+ help
+ Supports restricted syslogd config.
+
config FEATURE_SYSLOGD_READ_BUFFER_SIZE
int "Read buffer size in bytes"
default 256
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 24cab3b6c..f179dc5ac 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -66,10 +66,26 @@ typedef struct {
} remoteHost_t;
#endif
+typedef struct logFile_t {
+ const char *path;
+ int fd;
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+ unsigned size;
+ uint8_t isRegular;
+#endif
+} logFile_t;
+
+#if ENABLE_FEATURE_SYSLOGD_CFG
+typedef struct logRule_t {
+ uint8_t enabled_facility_priomap[LOG_NFACILITIES];
+ struct logFile_t *file;
+ struct logRule_t *next;
+} logRule_t;
+#endif
+
/* Allows us to have smaller initializer. Ugly. */
#define GLOBALS \
- const char *logFilePath; \
- int logFD; \
+ logFile_t logFile; \
/* interval between marks in seconds */ \
/*int markInterval;*/ \
/* level of messages to be logged */ \
@@ -79,8 +95,6 @@ IF_FEATURE_ROTATE_LOGFILE( \
unsigned logFileSize; \
/* number of rotated message files */ \
unsigned logFileRotate; \
- unsigned curFileSize; \
- smallint isRegular; \
) \
IF_FEATURE_IPC_SYSLOG( \
int shmid; /* ipc shared memory id */ \
@@ -88,6 +102,9 @@ IF_FEATURE_IPC_SYSLOG( \
int shm_size; \
struct sembuf SMwup[1]; \
struct sembuf SMwdn[3]; \
+) \
+IF_FEATURE_SYSLOGD_CFG( \
+ logRule_t *log_rules; \
)
struct init_globals {
@@ -119,8 +136,10 @@ struct globals {
};
static const struct init_globals init_data = {
- .logFilePath = "/var/log/messages",
- .logFD = -1,
+ .logFile = {
+ .path = "/var/log/messages",
+ .fd = -1,
+ },
#ifdef SYSLOGD_MARK
.markInterval = 20 * 60,
#endif
@@ -132,7 +151,7 @@ static const struct init_globals init_data = {
#if ENABLE_FEATURE_IPC_SYSLOG
.shmid = -1,
.s_semid = -1,
- .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), // default shm size
+ .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), /* default shm size */
.SMwup = { {1, -1, IPC_NOWAIT} },
.SMwdn = { {0, 0}, {1, 0}, {1, +1} },
#endif
@@ -157,6 +176,7 @@ enum {
IF_FEATURE_REMOTE_LOG( OPTBIT_locallog ,) // -L
IF_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
IF_FEATURE_SYSLOGD_DUP( OPTBIT_dup ,) // -D
+ IF_FEATURE_SYSLOGD_CFG( OPTBIT_cfg ,) // -f
OPT_mark = 1 << OPTBIT_mark ,
OPT_nofork = 1 << OPTBIT_nofork ,
@@ -169,6 +189,7 @@ enum {
OPT_locallog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_locallog )) + 0,
OPT_circularlog = IF_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
OPT_dup = IF_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0,
+ OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0,
};
#define OPTION_STR "m:nO:l:S" \
IF_FEATURE_ROTATE_LOGFILE("s:" ) \
@@ -176,18 +197,194 @@ enum {
IF_FEATURE_REMOTE_LOG( "R:" ) \
IF_FEATURE_REMOTE_LOG( "L" ) \
IF_FEATURE_IPC_SYSLOG( "C::") \
- IF_FEATURE_SYSLOGD_DUP( "D" )
+ IF_FEATURE_SYSLOGD_DUP( "D" ) \
+ IF_FEATURE_SYSLOGD_CFG( "f:" )
#define OPTION_DECL *opt_m, *opt_l \
IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \
IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \
- IF_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
-#define OPTION_PARAM &opt_m, &G.logFilePath, &opt_l \
+ IF_FEATURE_IPC_SYSLOG( ,*opt_C = NULL) \
+ IF_FEATURE_SYSLOGD_CFG( ,*opt_f = NULL)
+#define OPTION_PARAM &opt_m, &(G.logFile.path), &opt_l \
IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \
IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \
IF_FEATURE_REMOTE_LOG( ,&remoteAddrList) \
- IF_FEATURE_IPC_SYSLOG( ,&opt_C)
+ IF_FEATURE_IPC_SYSLOG( ,&opt_C) \
+ IF_FEATURE_SYSLOGD_CFG( ,&opt_f)
+#if ENABLE_FEATURE_SYSLOGD_CFG
+static const CODE* find_by_name(char *name, const CODE* c_set)
+{
+ for (; c_set->c_name; c_set++) {
+ if (strcmp(name, c_set->c_name) == 0)
+ return c_set;
+ }
+ return NULL;
+}
+#endif
+static const CODE* find_by_val(int val, const CODE* c_set)
+{
+ for (; c_set->c_name; c_set++) {
+ if (c_set->c_val == val)
+ return c_set;
+ }
+ return NULL;
+}
+
+#if ENABLE_FEATURE_SYSLOGD_CFG
+static void parse_syslogdcfg(const char *file)
+{
+ char *t;
+ logRule_t **pp_rule;
+ /* tok[0] set of selectors */
+ /* tok[1] file name */
+ /* tok[2] has to be NULL */
+ char *tok[3];
+ parser_t *parser;
+
+ parser = config_open2(file ? file : "/etc/syslog.conf",
+ file ? xfopen_for_read : fopen_or_warn_stdin);
+ if (!parser)
+ /* didn't find default /etc/syslog.conf */
+ /* proceed as if we built busybox without config support */
+ return;
+
+ /* use ptr to ptr to avoid checking whether head was initialized */
+ pp_rule = &G.log_rules;
+ /* iterate through lines of config, skipping comments */
+ while (config_read(parser, tok, 3, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
+ char *cur_selector;
+ logRule_t *cur_rule;
+
+ /* unexpected trailing token? */
+ if (tok[2]) {
+ t = tok[2];
+ goto cfgerr;
+ }
+
+ cur_rule = *pp_rule = xzalloc(sizeof(*cur_rule));
+
+ cur_selector = tok[0];
+ /* iterate through selectors: "kern.info;kern.!err;..." */
+ do {
+ const CODE *code;
+ char *next_selector;
+ uint8_t negated_prio; /* "kern.!err" */
+ uint8_t single_prio; /* "kern.=err" */
+ uint32_t facmap; /* bitmap of enabled facilities */
+ uint8_t primap; /* bitmap of enabled priorities */
+ unsigned i;
+
+ next_selector = strchr(cur_selector, ';');
+ if (next_selector)
+ *next_selector++ = '\0';
+
+ t = strchr(cur_selector, '.');
+ if (!t) {
+ t = cur_selector;
+ goto cfgerr;
+ }
+ *t++ = '\0'; /* separate facility from priority */
+
+ negated_prio = 0;
+ single_prio = 0;
+ if (*t == '!') {
+ negated_prio = 1;
+ ++t;
+ }
+ if (*t == '=') {
+ single_prio = 1;
+ ++t;
+ }
+
+ /* parse priority */
+ if (*t == '*')
+ primap = 0xff; /* all 8 log levels enabled */
+ else {
+ uint8_t priority;
+ code = find_by_name(t, prioritynames);
+ if (!code)
+ goto cfgerr;
+ primap = 0;
+ priority = code->c_val;
+ if (priority == INTERNAL_NOPRI) {
+ /* ensure we take "enabled_facility_priomap[fac] &= 0" branch below */
+ negated_prio = 1;
+ } else {
+ priority = 1 << priority;
+ do {
+ primap |= priority;
+ if (single_prio)
+ break;
+ priority >>= 1;
+ } while (priority);
+ if (negated_prio)
+ primap = ~primap;
+ }
+ }
+
+ /* parse facility */
+ if (*cur_selector == '*')
+ facmap = (1<<LOG_NFACILITIES) - 1;
+ else {
+ char *next_facility;
+ facmap = 0;
+ t = cur_selector;
+ /* iterate through facilities: "kern,daemon.<priospec>" */
+ do {
+ next_facility = strchr(t, ',');
+ if (next_facility)
+ *next_facility++ = '\0';
+ code = find_by_name(t, facilitynames);
+ if (!code)
+ goto cfgerr;
+ /* "mark" is not a real facility, skip it */
+ if (code->c_val != INTERNAL_MARK)
+ facmap |= 1<<(LOG_FAC(code->c_val));
+ t = next_facility;
+ } while (t);
+ }
+
+ /* merge result with previous selectors */
+ for (i = 0; i < LOG_NFACILITIES; ++i) {
+ if (!(facmap & (1<<i)))
+ continue;
+ if (negated_prio)
+ cur_rule->enabled_facility_priomap[i] &= primap;
+ else
+ cur_rule->enabled_facility_priomap[i] |= primap;
+ }
+
+ cur_selector = next_selector;
+ } while (cur_selector);
+
+ /* check whether current file name was mentioned in previous rules.
+ * temporarily use cur_rule as iterator, but *pp_rule still points to
+ * currently processing rule entry.
+ * NOTE: *pp_rule points to the current (and last in the list) rule.
+ */
+ for (cur_rule = G.log_rules; cur_rule != *pp_rule; cur_rule = cur_rule->next) {
+ if (strcmp(cur_rule->file->path, tok[1]) == 0) {
+ /* found - reuse the same file structure */
+ (*pp_rule)->file = cur_rule->file;
+ cur_rule = *pp_rule;
+ goto found;
+ }
+ }
+ cur_rule->file = xzalloc(sizeof(*cur_rule->file));
+ cur_rule->file->fd = -1;
+ cur_rule->file->path = xstrdup(tok[1]);
+ found:
+ pp_rule = &cur_rule->next;
+ }
+ config_close(parser);
+ return;
+
+ cfgerr:
+ bb_error_msg_and_die("bad line %d: wrong token '%s'", parser->lineno, t);
+}
+#endif
+
/* circular buffer variables/structures */
#if ENABLE_FEATURE_IPC_SYSLOG
@@ -231,7 +428,7 @@ static void ipcsyslog_init(void)
G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1;
/*G.shbuf->tail = 0;*/
- // we'll trust the OS to set initial semval to 0 (let's hope)
+ /* we'll trust the OS to set initial semval to 0 (let's hope) */
G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
if (G.s_semid == -1) {
if (errno == EEXIST) {
@@ -244,9 +441,10 @@ static void ipcsyslog_init(void)
}
/* Write message to shared mem buffer */
-static void log_to_shmem(const char *msg, int len)
+static void log_to_shmem(const char *msg)
{
int old_tail, new_tail;
+ int len;
if (semop(G.s_semid, G.SMwdn, 3) == -1) {
bb_perror_msg_and_die("SMwdn");
@@ -258,7 +456,7 @@ static void log_to_shmem(const char *msg, int len)
* tail's max value is (shbuf->size - 1)
* Last byte of buffer is never used and remains NUL.
*/
- len++; /* length with NUL included */
+ len = strlen(msg) + 1; /* length with NUL included */
again:
old_tail = G.shbuf->tail;
new_tail = old_tail + len;
@@ -288,22 +486,15 @@ void ipcsyslog_init(void);
void log_to_shmem(const char *msg);
#endif /* FEATURE_IPC_SYSLOG */
-
/* Print a message to the log file. */
-static void log_locally(time_t now, char *msg)
+static void log_locally(time_t now, char *msg, logFile_t *log_file)
{
#ifdef SYSLOGD_WRLOCK
struct flock fl;
#endif
int len = strlen(msg);
-#if ENABLE_FEATURE_IPC_SYSLOG
- if ((option_mask32 & OPT_circularlog) && G.shbuf) {
- log_to_shmem(msg, len);
- return;
- }
-#endif
- if (G.logFD >= 0) {
+ if (log_file->fd >= 0) {
/* Reopen log file every second. This allows admin
* to delete the file and not worry about restarting us.
* This costs almost nothing since it happens
@@ -313,15 +504,15 @@ static void log_locally(time_t now, char *msg)
now = time(NULL);
if (G.last_log_time != now) {
G.last_log_time = now;
- close(G.logFD);
+ close(log_file->fd);
goto reopen;
}
} else {
reopen:
- G.logFD = open(G.logFilePath, O_WRONLY | O_CREAT
+ log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
| O_NOCTTY | O_APPEND | O_NONBLOCK,
0666);
- if (G.logFD < 0) {
+ if (log_file->fd < 0) {
/* cannot open logfile? - print to /dev/console then */
int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
if (fd < 0)
@@ -334,9 +525,9 @@ static void log_locally(time_t now, char *msg)
#if ENABLE_FEATURE_ROTATE_LOGFILE
{
struct stat statf;
- G.isRegular = (fstat(G.logFD, &statf) == 0 && S_ISREG(statf.st_mode));
+ log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
/* bug (mostly harmless): can wrap around if file > 4gb */
- G.curFileSize = statf.st_size;
+ log_file->size = statf.st_size;
}
#endif
}
@@ -346,41 +537,41 @@ static void log_locally(time_t now, char *msg)
fl.l_start = 0;
fl.l_len = 1;
fl.l_type = F_WRLCK;
- fcntl(G.logFD, F_SETLKW, &fl);
+ fcntl(log_file->fd, F_SETLKW, &fl);
#endif
#if ENABLE_FEATURE_ROTATE_LOGFILE
- if (G.logFileSize && G.isRegular && G.curFileSize > G.logFileSize) {
+ if (G.logFileSize && log_file->isRegular && log_file->size > G.logFileSize) {
if (G.logFileRotate) { /* always 0..99 */
- int i = strlen(G.logFilePath) + 3 + 1;
+ int i = strlen(log_file->path) + 3 + 1;
char oldFile[i];
char newFile[i];
i = G.logFileRotate - 1;
/* rename: f.8 -> f.9; f.7 -> f.8; ... */
while (1) {
- sprintf(newFile, "%s.%d", G.logFilePath, i);
+ sprintf(newFile, "%s.%d", log_file->path, i);
if (i == 0) break;
- sprintf(oldFile, "%s.%d", G.logFilePath, --i);
+ sprintf(oldFile, "%s.%d", log_file->path, --i);
/* ignore errors - file might be missing */
rename(oldFile, newFile);
}
/* newFile == "f.0" now */
- rename(G.logFilePath, newFile);
+ rename(log_file->path, newFile);
#ifdef SYSLOGD_WRLOCK
fl.l_type = F_UNLCK;
- fcntl(G.logFD, F_SETLKW, &fl);
+ fcntl(log_file->fd, F_SETLKW, &fl);
#endif
- close(G.logFD);
+ close(log_file->fd);
goto reopen;
}
- ftruncate(G.logFD, 0);
+ ftruncate(log_file->fd, 0);
}
- G.curFileSize +=
+ log_file->size +=
#endif
- full_write(G.logFD, msg, len);
+ full_write(log_file->fd, msg, len);
#ifdef SYSLOGD_WRLOCK
fl.l_type = F_UNLCK;
- fcntl(G.logFD, F_SETLKW, &fl);
+ fcntl(log_file->fd, F_SETLKW, &fl);
#endif
}
@@ -388,29 +579,15 @@ static void parse_fac_prio_20(int pri, char *res20)
{
const CODE *c_pri, *c_fac;
- if (pri != 0) {
- c_fac = facilitynames;
- while (c_fac->c_name) {
- if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
- c_fac++;
- continue;
- }
- /* facility is found, look for prio */
- c_pri = prioritynames;
- while (c_pri->c_name) {
- if (c_pri->c_val != LOG_PRI(pri)) {
- c_pri++;
- continue;
- }
- snprintf(res20, 20, "%s.%s",
- c_fac->c_name, c_pri->c_name);
- return;
- }
- /* prio not found, bail out */
- break;
+ c_fac = find_by_val(LOG_FAC(pri) << 3, facilitynames);
+ if (c_fac) {
+ c_pri = find_by_val(LOG_PRI(pri), prioritynames);
+ if (c_pri) {
+ snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name);
+ return;
}
- snprintf(res20, 20, "<%d>", pri);
}
+ snprintf(res20, 20, "<%d>", pri);
}
/* len parameter is used only for "is there a timestamp?" check.
@@ -444,7 +621,32 @@ static void timestamp_and_log(int pri, char *msg, int len)
}
/* Log message locally (to file or shared mem) */
- log_locally(now, G.printbuf);
+#if ENABLE_FEATURE_SYSLOGD_CFG
+ {
+ bool match = 0;
+ logRule_t *rule;
+ uint8_t facility = LOG_FAC(pri);
+ uint8_t prio_bit = 1 << LOG_PRI(pri);
+
+ for (rule = G.log_rules; rule; rule = rule->next) {
+ if (rule->enabled_facility_priomap[facility] & prio_bit) {
+ log_locally(now, G.printbuf, rule->file);
+ match = 1;
+ }
+ }
+ if (match)
+ return;
+ }
+#endif
+ if (LOG_PRI(pri) < G.logLevel) {
+#if ENABLE_FEATURE_IPC_SYSLOG
+ if ((option_mask32 & OPT_circularlog) && G.shbuf) {
+ log_to_shmem(msg);
+ return;
+ }
+#endif
+ log_locally(now, G.printbuf, &G.logFile);
+ }
}
static void timestamp_and_log_internal(const char *msg)
@@ -489,8 +691,7 @@ static void split_escape_and_log(char *tmpbuf, int len)
*q = '\0';
/* Now log it */
- if (LOG_PRI(pri) < G.logLevel)
- timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
+ timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
}
}
@@ -719,10 +920,12 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
if (opt_C) // -Cn
G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
#endif
-
/* If they have not specified remote logging, then log locally */
if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R
option_mask32 |= OPT_locallog;
+#if ENABLE_FEATURE_SYSLOGD_CFG
+ parse_syslogdcfg(opt_f);
+#endif
/* Store away localhost's name before the fork */
G.hostname = safe_gethostname();