From 6712e77271fb3cb4a3c48cd7b027b39c5cea00a2 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 14 Nov 2018 17:16:05 +0100 Subject: [PATCH] Unix: Implement log file size limit / log rotation Allow to specify log file size limit and ensure that log file is rotated to secondary name to avoid exceeding of log size limit. The patch also fixes a bug related to keeping old fds open after reconfiguration and using old fds after 'configure undo'. --- sysdep/unix/config.Y | 34 +++++++++++------ sysdep/unix/log.c | 88 ++++++++++++++++++++++++++++++++++++++++++-- sysdep/unix/unix.h | 5 +++ 3 files changed, 111 insertions(+), 16 deletions(-) diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index b8572c90..e7ecd735 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -11,13 +11,16 @@ CF_HDR #include "sysdep/unix/unix.h" #include +CF_DEFINES + +static struct log_config *this_log; + CF_DECLS CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING) %type log_mask log_mask_list log_cat cfg_timeout -%type log_file %type cfg_name %type timeformat_which %type syslog_name @@ -26,11 +29,11 @@ CF_GRAMMAR conf: log_config ; -log_config: LOG log_file log_mask ';' { - struct log_config *c = cfg_allocz(sizeof(struct log_config)); - c->fh = $2; - c->mask = $3; - add_tail(&new_config->logfiles, &c->n); +log_begin: { this_log = cfg_allocz(sizeof(struct log_config)); }; + +log_config: LOG log_begin log_file log_mask ';' { + this_log->mask = $4; + add_tail(&new_config->logfiles, &this_log->n); } ; @@ -39,14 +42,21 @@ syslog_name: | { $$ = bird_name; } ; +log_limit: + /* empty */ + | expr text { this_log->limit = $1; this_log->backup = $2; } + ; + log_file: - text { - struct rfile *f = rf_open(new_config->pool, $1, "a"); - if (!f) cf_error("Unable to open log file '%s': %m", $1); - $$ = rf_file(f); + text log_limit { + this_log->rf = rf_open(new_config->pool, $1, "a"); + if (!this_log->rf) cf_error("Unable to open log file '%s': %m", $1); + this_log->fh = rf_file(this_log->rf); + this_log->pos = -1; + this_log->filename = $1; } - | SYSLOG syslog_name { $$ = NULL; new_config->syslog_name = $2; } - | STDERR { $$ = stderr; } + | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; } + | STDERR { this_log->fh = stderr; } ; log_mask: diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index f9dccc39..960b4c1c 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -86,6 +88,54 @@ static char *class_names[] = { "BUG" }; +static inline off_t +log_size(struct log_config *l) +{ + struct stat st; + return (!fstat(rf_fileno(l->rf), &st) && S_ISREG(st.st_mode)) ? st.st_size : 0; +} + +static void +log_close(struct log_config *l) +{ + rfree(l->rf); + l->rf = NULL; + l->fh = NULL; +} + +static int +log_open(struct log_config *l) +{ + l->rf = rf_open(config->pool, l->filename, "a"); + if (!l->rf) + { + /* Well, we cannot do much in case of error as log is closed */ + l->mask = 0; + return -1; + } + + l->fh = rf_file(l->rf); + l->pos = log_size(l); + + return 0; +} + +static int +log_rotate(struct log_config *l) +{ + log_close(l); + + /* If we cannot rename the logfile, we at least try to delete it + in order to continue logging and not exceeding logfile size */ + if ((rename(l->filename, l->backup) < 0) && + (unlink(l->filename) < 0)) + { + l->mask = 0; + return -1; + } + + return log_open(l); +} /** * log_commit - commit a log message @@ -121,6 +171,22 @@ log_commit(int class, buffer *buf) { byte tbuf[TM_DATETIME_BUFFER_SIZE]; tm_format_real_time(tbuf, config->tf_log.fmt1, current_real_time()); + + if (l->limit) + { + off_t msg_len = strlen(tbuf) + strlen(class_names[class]) + + (buf->pos - buf->start) + 5; + + if (l->pos < 0) + l->pos = log_size(l); + + if (l->pos + msg_len > l->limit) + if (log_rotate(l) < 0) + continue; + + l->pos += msg_len; + } + fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]); } fputs(buf->start, l->fh); @@ -279,12 +345,26 @@ default_log_list(int debug, int init, char **syslog_name) } void -log_switch(int debug, list *l, char *new_syslog_name) +log_switch(int debug, list *logs, char *new_syslog_name) { - if (!l || EMPTY_LIST(*l)) - l = default_log_list(debug, !l, &new_syslog_name); + struct log_config *l; - current_log_list = l; + if (!logs || EMPTY_LIST(*logs)) + logs = default_log_list(debug, !logs, &new_syslog_name); + + /* Close the logs to avoid pinning them on disk when deleted */ + if (current_log_list) + WALK_LIST(l, *current_log_list) + if (l->rf) + log_close(l); + + /* Reopen the logs, needed for 'configure undo' */ + if (logs) + WALK_LIST(l, *logs) + if (l->filename && !l->rf) + log_open(l); + + current_log_list = logs; #ifdef HAVE_SYSLOG_H if (current_syslog_name && new_syslog_name && diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 64b146ee..12306167 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -122,6 +122,11 @@ struct log_config { node n; uint mask; /* Classes to log */ void *fh; /* FILE to log to, NULL=syslog */ + struct rfile *rf; /* Resource for log file */ + char *filename; /* Log filename */ + char *backup; /* Secondary filename (for log rotation) */ + off_t pos; /* Position/size of current log */ + off_t limit; /* Log size limit */ int terminal_flag; };