]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Unix: Implement log file size limit / log rotation
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 14 Nov 2018 16:16:05 +0000 (17:16 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 18 Nov 2018 13:03:50 +0000 (14:03 +0100)
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
sysdep/unix/log.c
sysdep/unix/unix.h

index b8572c907a73628553c9d34f13dfe0de52c190e4..e7ecd735ba5872f27d571aefb78c534e7f042a43 100644 (file)
@@ -11,13 +11,16 @@ CF_HDR
 #include "sysdep/unix/unix.h"
 #include <stdio.h>
 
+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 <i> log_mask log_mask_list log_cat cfg_timeout
-%type <g> log_file
 %type <t> cfg_name
 %type <tf> timeformat_which
 %type <t> 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:
index f9dccc390279fa7a9d0f494e692f7d40d7d6a9a1..960b4c1c7c538486bc44e55f4878567f762086f2 100644 (file)
@@ -19,6 +19,8 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <errno.h>
 
@@ -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;
+
+  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 = l;
+  current_log_list = logs;
 
 #ifdef HAVE_SYSLOG_H
   if (current_syslog_name && new_syslog_name &&
index 64b146ee6e501389fffdcb01bc71c66c4914096a..1230616704a7f17a1ae4d346b1d5c7de6714d929 100644 (file)
@@ -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;
 };