commit 89af0c12d4a2f40bc522b64c61daea6dac8ab3a5
Author: Alexander Zubkov <green@qrator.net>
Date:   Mon Jan 23 03:10:19 2023 +0100

    Keep protocol enabled states to configured file and restore them during reconfigure

diff --git a/conf/conf.c b/conf/conf.c
index 4e31de29..d4ea0ca2 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("read_states\n");
+  protos_read_state(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..608e5c31 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;			/* Configured 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..93c75ae7 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -8,6 +8,8 @@
 
 #undef LOCAL_DEBUG
 
+#include <stdio.h>
+#include <unistd.h>
 #include "nest/bird.h"
 #include "nest/protocol.h"
 #include "lib/resource.h"
@@ -2091,6 +2093,55 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
   }
 }
 
+void
+protos_read_state(struct config *c)
+{
+  FILE *f = c->states_file;
+  if (!f) return;
+
+  fseek(f, 0, SEEK_SET);
+  fflush(f);
+
+  while (1)
+  {
+    uint enabled;
+    char name[4096];
+    if (fscanf(f, "%4095s %d\n", name, &enabled) != 2)
+    {
+      if (!feof(f))
+	log(L_ERR "Error reading states file: %m");
+      break;
+    }
+
+    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
+protos_write_state(struct proto *mp)
+{
+  FILE *f = mp->cf->global->states_file;
+  if (!f) return;
+
+  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);
+}
+
 void
 proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
 {
@@ -2105,6 +2156,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);
+  protos_write_state(p);
   cli_msg(-9, "%s: disabled", p->name);
 }
 
@@ -2121,6 +2173,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);
+  protos_write_state(p);
   cli_msg(-11, "%s: enabled", p->name);
 }
 
diff --git a/nest/protocol.h b/nest/protocol.h
index fcbf0539..76fa5f09 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -90,6 +90,7 @@ void protos_preconfig(struct config *);
 void protos_commit(struct config *new, struct config *old, int force_restart, int type);
 struct proto * proto_spawn(struct proto_config *cf, uint disabled);
 void protos_dump_all(void);
+void protos_read_state(struct config *c);
 
 #define GA_UNKNOWN	0		/* Attribute not recognized */
 #define GA_NAME		1		/* Result = name */
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index 5c4b5bef..e99068cf 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("Redefine of states file");
+      struct rfile *f = rf_open(new_config->pool, $3, "a+");
+      if (!f) cf_error("Unable to open states file '%s': %m", $3);
+      new_config->states_file = rf_file(f);
+    }
+  }
+;
+
+
 conf: debug_unix ;
 
 debug_unix:
