Updated the patch for keeping state in the file. Moved the read/write
functions to sysdep/unix/main.c and made better parsing. So it is not a
draft anymore, but something more or less "stable". I can add documentation
patch in case there is interest to include that into upstream.

On Tue, Jan 24, 2023 at 8:03 AM Ondrej Zajicek <santi...@crfreenet.org>
wrote:

> On Mon, Jan 23, 2023 at 03:19:43AM +0100, Alexander Zubkov wrote:
> > Hello,
> >
> > Not sure if those are forgotten or are unwanted modifications. Please let
> > me know.
> >
> > And I also have another idea regarding the subject. The idea is to
> > configure the file where bird will keep the states of the protocols (I
> mean
> > enabled/disabled states). Then during loading the configuration it alters
> > the states to reflect the saved ones. So the states are persistent during
> > reconfiguration and also during restart of a daemon. The draft patch is
> > attached. Please take a look.
>
> Hello
>
> Forgot about it, will check it.
>
> --
> Elen sila lumenn' omentielvo
>
> Ondrej 'Santiago' Zajicek (email: santi...@crfreenet.org)
> OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net)
> "To err is human -- to blame it on a computer is even more so."
>
commit 65a25826c9b8678cbd97b4229714d5145b47c839
Author: Alexander Zubkov <gr...@qrator.net>
Date:   Fri Jan 27 02:19:39 2023 +0100

    Config: define file for keeping persistent protocol states
    
    Persist protocols enabled/disabled states by keeping them in a file, if
    its name is defined in the config file. Upon loading the config file
    the states of the protocols are updated according to the file. The file
    is rewritten when some protocol is enabled/disabled by CLI.

diff --git a/conf/conf.c b/conf/conf.c
index 4e31de29..da42a78a 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -287,6 +287,8 @@ config_do_commit(struct config *c, int type)
   if (old_config)
     old_config->obstacle_count++;
 
+  DBG("proto_states_read\n");
+  proto_states_read(c);
   DBG("filter_commit\n");
   filter_commit(c, old_config);
   DBG("sysdep_commit\n");
diff --git a/conf/conf.h b/conf/conf.h
index b409750e..ecbe9408 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -28,6 +28,7 @@ struct config {
 
   int mrtdump_file;			/* Configured MRTDump file (sysdep, fd in unix) */
   const char *syslog_name;		/* Name used for syslog (NULL -> no syslog) */
+  void *states_file;			/* Protocol states file (sysdep, FILE *) */
   struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */
   struct iface_patt *router_id_from;	/* Configured list of router ID iface patterns */
 
diff --git a/nest/proto.c b/nest/proto.c
index 885a0b75..4e70f585 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -21,6 +21,7 @@
 #include "nest/cli.h"
 #include "filter/filter.h"
 #include "filter/f-inst.h"
+#include "sysdep/unix/unix.h"
 
 pool *proto_pool;
 list STATIC_LIST_INIT(proto_list);
@@ -2105,6 +2106,7 @@ proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
   p->down_code = PDC_CMD_DISABLE;
   proto_set_message(p, (char *) arg, -1);
   proto_rethink_goal(p);
+  proto_states_write(p);
   cli_msg(-9, "%s: disabled", p->name);
 }
 
@@ -2121,6 +2123,7 @@ proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED)
   p->disabled = 0;
   proto_set_message(p, (char *) arg, -1);
   proto_rethink_goal(p);
+  proto_states_write(p);
   cli_msg(-11, "%s: enabled", p->name);
 }
 
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index 5c4b5bef..cc7813c8 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -101,6 +101,20 @@ mrtdump_base:
  ;
 
 
+conf: keep_states ;
+
+keep_states: KEEP STATES text ';' {
+    if (!parse_and_exit)
+    {
+      if (new_config->states_file) cf_error("Repeating definition of protocol states file");
+      struct rfile *f = rf_open(new_config->pool, $3, "a+");
+      if (!f) cf_error("Unable to open protocol states file '%s': %m", $3);
+      new_config->states_file = rf_file(f);
+    }
+  }
+;
+
+
 conf: debug_unix ;
 
 debug_unix:
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index 627d7a4a..c4a8ed42 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -389,6 +389,83 @@ cmd_reconfig_status(void)
   cli_msg(0, "");
 }
 
+/*
+ *	Protocol states reading/writing
+ */
+
+void
+proto_states_read(struct config *c)
+{
+  FILE *f = c->states_file;
+  if (!f) return;
+
+  log(L_INFO "Reading protocol states file");
+
+  /* Move to the start of the file */
+  fseek(f, 0, SEEK_SET);
+  fflush(f);
+
+  const char *fmt = "%" XSTR1(SYM_MAX_LEN) "s %u";
+  char buf[512];
+
+  /* Use longer buffer to check for too long names */
+  char name[SYM_MAX_LEN + 1];
+  uint enabled;
+
+  int longline = 0, line = 0;
+  while (fgets(buf, sizeof(buf), f))
+  {
+    int len = strlen(buf);
+    int eol = len && buf[len - 1] == '\n';
+
+    /* Skip long line tail */
+    if (longline)
+    {
+      if (eol) longline = 0;
+      continue;
+    }
+
+    ++line;
+    if (!eol) longline = 1;
+
+    if (sscanf(buf, fmt, name, &enabled) != 2 || strlen(name) >= SYM_MAX_LEN)
+    {
+      log(L_ERR "Format error in states file on line %d", line);
+      continue;
+    }
+
+    struct symbol *sym = cf_find_symbol(c, name);
+    if (sym && sym->class == SYM_PROTO)
+    {
+      struct proto_config *pc = sym->proto;
+      log(L_INFO "Applying state to protocol %s: %d", pc->name, enabled);
+      pc->disabled = !enabled;
+    }
+  }
+}
+
+void
+proto_states_write(struct proto *proto_changed)
+{
+  FILE *f = proto_changed->cf->global->states_file;
+  if (!f) return;
+
+  log(L_INFO "Rewriting protocol states file");
+
+  /* Move to the start and truncate the file for rewriting */
+  fseek(f, 0, SEEK_SET);
+  fflush(f);
+  ftruncate(fileno(f), 0);
+
+  struct proto *p;
+  WALK_LIST(p, proto_list)
+  {
+    fprintf(f, "%s %d\n", p->name, !p->disabled);
+  }
+
+  fflush(f);
+}
+
 
 /*
  *	Command-Line Interface
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index ad85d1ea..ea69914b 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -16,6 +16,8 @@ struct pool;
 struct iface;
 struct birdsock;
 struct rfile;
+struct config;
+struct proto;
 
 /* main.c */
 
@@ -33,6 +35,9 @@ void cmd_reconfig_status(void);
 void cmd_shutdown(void);
 void cmd_graceful_restart(void);
 
+void proto_states_read(struct config *c);
+void proto_states_write(struct proto *proto_changed);
+
 #define UNIX_DEFAULT_CONFIGURE_TIMEOUT	300
 
 #define UNIX_DEFAULT_LATENCY_LIMIT	(1 S_)

Reply via email to