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'.
This commit is contained in:
Ondrej Zajicek (work) 2018-11-14 17:16:05 +01:00
parent c68ba7d093
commit 6712e77271
3 changed files with 111 additions and 16 deletions

View file

@ -11,13 +11,16 @@ CF_HDR
#include "sysdep/unix/unix.h" #include "sysdep/unix/unix.h"
#include <stdio.h> #include <stdio.h>
CF_DEFINES
static struct log_config *this_log;
CF_DECLS CF_DECLS
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) 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) CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING)
%type <i> log_mask log_mask_list log_cat cfg_timeout %type <i> log_mask log_mask_list log_cat cfg_timeout
%type <g> log_file
%type <t> cfg_name %type <t> cfg_name
%type <tf> timeformat_which %type <tf> timeformat_which
%type <t> syslog_name %type <t> syslog_name
@ -26,11 +29,11 @@ CF_GRAMMAR
conf: log_config ; conf: log_config ;
log_config: LOG log_file log_mask ';' { log_begin: { this_log = cfg_allocz(sizeof(struct log_config)); };
struct log_config *c = cfg_allocz(sizeof(struct log_config));
c->fh = $2; log_config: LOG log_begin log_file log_mask ';' {
c->mask = $3; this_log->mask = $4;
add_tail(&new_config->logfiles, &c->n); add_tail(&new_config->logfiles, &this_log->n);
} }
; ;
@ -39,14 +42,21 @@ syslog_name:
| { $$ = bird_name; } | { $$ = bird_name; }
; ;
log_limit:
/* empty */
| expr text { this_log->limit = $1; this_log->backup = $2; }
;
log_file: log_file:
text { text log_limit {
struct rfile *f = rf_open(new_config->pool, $1, "a"); this_log->rf = rf_open(new_config->pool, $1, "a");
if (!f) cf_error("Unable to open log file '%s': %m", $1); if (!this_log->rf) cf_error("Unable to open log file '%s': %m", $1);
$$ = rf_file(f); 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; } | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; }
| STDERR { $$ = stderr; } | STDERR { this_log->fh = stderr; }
; ;
log_mask: log_mask:

View file

@ -19,6 +19,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <time.h> #include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@ -86,6 +88,54 @@ static char *class_names[] = {
"BUG" "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 * log_commit - commit a log message
@ -121,6 +171,22 @@ log_commit(int class, buffer *buf)
{ {
byte tbuf[TM_DATETIME_BUFFER_SIZE]; byte tbuf[TM_DATETIME_BUFFER_SIZE];
tm_format_real_time(tbuf, config->tf_log.fmt1, current_real_time()); 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]); fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]);
} }
fputs(buf->start, l->fh); fputs(buf->start, l->fh);
@ -279,12 +345,26 @@ default_log_list(int debug, int init, char **syslog_name)
} }
void 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)) struct log_config *l;
l = default_log_list(debug, !l, &new_syslog_name);
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 #ifdef HAVE_SYSLOG_H
if (current_syslog_name && new_syslog_name && if (current_syslog_name && new_syslog_name &&

View file

@ -122,6 +122,11 @@ struct log_config {
node n; node n;
uint mask; /* Classes to log */ uint mask; /* Classes to log */
void *fh; /* FILE to log to, NULL=syslog */ 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; int terminal_flag;
}; };