]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Unix: Implement log file size limit / log rotation
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sat, 24 Nov 2018 00:52:13 +0000 (01:52 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sat, 24 Nov 2018 00:52:13 +0000 (01:52 +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/io.c
sysdep/unix/log.c
sysdep/unix/unix.h

index ccca4a6271a18ccf41d5d048e198ee780b103624..253127a7f1189bf1625ec8eee25b20eb87cd6708 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
 
 CF_ADDTO(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 {
-     FILE *f = tracked_fopen(new_config->pool, $1, "a");
-     if (!f) cf_error("Unable to open log file `%s': %m", $1);
-     $$ = 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:
@@ -77,9 +87,9 @@ CF_ADDTO(conf, mrtdump_base)
 mrtdump_base:
    MRTDUMP PROTOCOLS mrtdump_mask ';' { new_config->proto_default_mrtdump = $3; }
  | MRTDUMP text ';' {
-     FILE *f = tracked_fopen(new_config->pool, $2, "a");
+     struct rfile *f = rf_open(new_config->pool, $2, "a");
      if (!f) cf_error("Unable to open MRTDump file '%s': %m", $2);
-     new_config->mrtdump_file = fileno(f);
+     new_config->mrtdump_file = rf_fileno(f);
    }
  ;
 
index 012deaf0aa7319ef64fc92eb6ca8b4413d52578c..670cdd3755ba5567357291ac0bed6ddf07f1e8cb 100644 (file)
@@ -55,6 +55,7 @@
    this to gen small latencies */
 #define MAX_RX_STEPS 4
 
+
 /*
  *     Tracked Files
  */
@@ -89,17 +90,29 @@ static struct resclass rf_class = {
   NULL
 };
 
-void *
-tracked_fopen(pool *p, char *name, char *mode)
+struct rfile *
+rf_open(pool *p, char *name, char *mode)
 {
   FILE *f = fopen(name, mode);
 
-  if (f)
-    {
-      struct rfile *r = ralloc(p, &rf_class);
-      r->f = f;
-    }
-  return f;
+  if (!f)
+    return NULL;
+
+  struct rfile *r = ralloc(p, &rf_class);
+  r->f = f;
+  return r;
+}
+
+void *
+rf_file(struct rfile *f)
+{
+  return f->f;
+}
+
+int
+rf_fileno(struct rfile *f)
+{
+  return fileno(f->f);
 }
 
 
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 cb12fad844bb65da77b3d95960e982e0452ca817..1230616704a7f17a1ae4d346b1d5c7de6714d929 100644 (file)
@@ -14,6 +14,7 @@
 struct pool;
 struct iface;
 struct birdsock;
+struct rfile;
 
 /* main.c */
 
@@ -102,7 +103,9 @@ void io_init(void);
 void io_loop(void);
 void io_log_dump(void);
 int sk_open_unix(struct birdsock *s, char *name);
-void *tracked_fopen(struct pool *, char *name, char *mode);
+struct rfile *rf_open(struct pool *, char *name, char *mode);
+void *rf_file(struct rfile *f);
+int rf_fileno(struct rfile *f);
 void test_old_bird(char *path);
 
 /* krt.c bits */
@@ -119,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;
 };