]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements undo command and optional timeout for configuration
authorOndrej Zajicek <santiago@crfreenet.org>
Wed, 26 Dec 2012 11:40:48 +0000 (12:40 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Wed, 26 Dec 2012 11:40:48 +0000 (12:40 +0100)
Several new configure command variants:

configure undo - undo last reconfiguration
configure timeout - configure with scheduled undo if not confirmed in timeout
configure confirm - confirm last configuration
configure check - just parse and validate config file

16 files changed:
conf/conf.c
conf/conf.h
conf/gen_commands.m4
conf/gen_parser.m4
doc/bird.sgml
doc/reply_codes
nest/cli.c
nest/cli.h
nest/cmds.c
nest/proto.c
sysdep/unix/config.Y
sysdep/unix/io.c
sysdep/unix/krt.c
sysdep/unix/main.c
sysdep/unix/timer.h
sysdep/unix/unix.h

index 9375861fbf741ce8bffbb4a1511c44ca875ea914..6dfa369136a0f1fe0b9d92594033d6dae97e962a 100644 (file)
  * There can exist up to four different configurations at one time: an active
  * one (pointed to by @config), configuration we are just switching from
  * (@old_config), one queued for the next reconfiguration (@future_config;
- * if it's non-%NULL and the user wants to reconfigure once again, we just
+ * if there is one and the user wants to reconfigure once again, we just
  * free the previous queued config and replace it with the new one) and
- * finally a config being parsed (@new_config).
+ * finally a config being parsed (@new_config). The stored @old_config 
+ * is also used for undo reconfiguration, which works in a similar way.
+ * Reconfiguration could also have timeout (using @config_timer) and undo
+ * is automatically called if the new configuration is not confirmed later.
  *
  * Loading of new configuration is very simple: just call config_alloc()
  * to get a new &config structure, then use config_parse() to parse a
 
 static jmp_buf conf_jmpbuf;
 
-struct config *config, *new_config, *old_config, *future_config;
-static event *config_event;
-int shutting_down, future_type;
-bird_clock_t boot_time;
+struct config *config, *new_config;
+
+static struct config *old_config;      /* Old configuration */
+static struct config *future_config;   /* New config held here if recon requested during recon */
+static int old_cftype;                 /* Type of transition old_config -> config (RECONFIG_SOFT/HARD) */
+static int future_cftype;              /* Type of scheduled transition, may also be RECONFIG_UNDO */
+/* Note that when future_cftype is RECONFIG_UNDO, then future_config is NULL,
+   therefore proper check for future scheduled config checks future_cftype */
+
+static event *config_event;            /* Event for finalizing reconfiguration */
+static timer *config_timer;            /* Timer for scheduled configuration rollback */
+
+/* These are public just for cmd_show_status(), should not be accessed elsewhere */
+int shutting_down;                     /* Shutdown requested, do not accept new config changes */
+int configuring;                       /* Reconfiguration is running */
+int undo_available;                    /* Undo was not requested from last reconfiguration */
+/* Note that both shutting_down and undo_available are related to requests, not processing */
 
 /**
  * config_alloc - allocate a new configuration
@@ -82,8 +98,6 @@ config_alloc(byte *name)
   c->load_time = now;
   c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T";
 
-  if (!boot_time)
-    boot_time = now;
   return c;
 }
 
@@ -154,7 +168,8 @@ cli_parse(struct config *c)
 void
 config_free(struct config *c)
 {
-  rfree(c->pool);
+  if (c)
+    rfree(c->pool);
 }
 
 void
@@ -170,10 +185,7 @@ config_del_obstacle(struct config *c)
   DBG("+++ deleting obstacle %d\n", c->obstacle_count);
   c->obstacle_count--;
   if (!c->obstacle_count)
-    {
-      ASSERT(config_event);
-      ev_schedule(config_event);
-    }
+    ev_schedule(config_event);
 }
 
 static int
@@ -197,16 +209,31 @@ global_commit(struct config *new, struct config *old)
 static int
 config_do_commit(struct config *c, int type)
 {
-  int force_restart, nobs;
+  if (type == RECONFIG_UNDO)
+    {
+      c = old_config;
+      type = old_cftype;
+    }
+  else
+    config_free(old_config);
 
-  DBG("do_commit\n");
   old_config = config;
-  config = new_config = c;
+  old_cftype = type;
+  config = c;
+
+  configuring = 1;
+  if (old_config && !config->shutdown)
+    log(L_INFO "Reconfiguring");
+
+  /* This should not be necessary, but it seems there are some
+     functions that access new_config instead of config */
+  new_config = config;
+
   if (old_config)
     old_config->obstacle_count++;
 
   DBG("sysdep_commit\n");
-  force_restart = sysdep_commit(c, old_config);
+  int force_restart = sysdep_commit(c, old_config);
   DBG("global_commit\n");
   force_restart |= global_commit(c, old_config);
   DBG("rt_commit\n");
@@ -214,38 +241,38 @@ config_do_commit(struct config *c, int type)
   roa_commit(c, old_config);
   DBG("protos_commit\n");
   protos_commit(c, old_config, force_restart, type);
-  new_config = NULL;                   /* Just to be sure nobody uses that now */
+
+  /* Just to be sure nobody uses that now */
+  new_config = NULL;
+
+  int obs = 0;
   if (old_config)
-    nobs = --old_config->obstacle_count;
-  else
-    nobs = 0;
-  DBG("do_commit finished with %d obstacles remaining\n", nobs);
-  return !nobs;
+    obs = --old_config->obstacle_count;
+
+  DBG("do_commit finished with %d obstacles remaining\n", obs);
+  return !obs;
 }
 
 static void
 config_done(void *unused UNUSED)
 {
-  struct config *c;
+  if (config->shutdown)
+    sysdep_shutdown_done();
+
+  configuring = 0;
+  if (old_config)
+    log(L_INFO "Reconfigured");
 
-  DBG("config_done\n");
-  for(;;)
+  if (future_cftype)
     {
-      if (config->shutdown)
-       sysdep_shutdown_done();
-      log(L_INFO "Reconfigured");
-      if (old_config)
-       {
-         config_free(old_config);
-         old_config = NULL;
-       }
-      if (!future_config)
-       break;
-      c = future_config;
+      int type = future_cftype;
+      struct config *conf = future_config;
+      future_cftype = RECONFIG_NONE;
       future_config = NULL;
+
       log(L_INFO "Reconfiguring to queued configuration");
-      if (!config_do_commit(c, future_type))
-       break;
+      if (config_do_commit(conf, type))
+       config_done(NULL);
     }
 }
 
@@ -253,6 +280,7 @@ config_done(void *unused UNUSED)
  * config_commit - commit a configuration
  * @c: new configuration
  * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
+ * @timeout: timeout for undo (or 0 for no timeout)
  *
  * When a configuration is parsed and prepared for use, the
  * config_commit() function starts the process of reconfiguration.
@@ -265,6 +293,10 @@ config_done(void *unused UNUSED)
  * using config_del_obstacle(), the old configuration is freed and
  * everything runs according to the new one.
  *
+ * When @timeout is nonzero, the undo timer is activated with given
+ * timeout. The timer is deactivated when config_commit(),
+ * config_confirm() or config_undo() is called.
+ *
  * Result: %CONF_DONE if the configuration has been accepted immediately,
  * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED
  * if it's been queued due to another reconfiguration being in progress now
@@ -272,49 +304,147 @@ config_done(void *unused UNUSED)
  * are accepted.
  */
 int
-config_commit(struct config *c, int type)
+config_commit(struct config *c, int type, int timeout)
 {
-  if (!config)                         /* First-time configuration */
+  if (shutting_down)
     {
-      config_do_commit(c, RECONFIG_HARD);
-      return CONF_DONE;
+      config_free(c);
+      return CONF_SHUTDOWN;
     }
-  if (old_config)                      /* Reconfiguration already in progress */
+
+  undo_available = 1;
+  if (timeout > 0)
+    tm_start(config_timer, timeout);
+  else
+    tm_stop(config_timer);
+
+  if (configuring)
     {
-      if (shutting_down == 2)
-       {
-         log(L_INFO "New configuration discarded due to shutdown");
-         config_free(c);
-         return CONF_SHUTDOWN;
-       }
-      if (future_config)
+      if (future_cftype)
        {
          log(L_INFO "Queueing new configuration, ignoring the one already queued");
          config_free(future_config);
        }
       else
-       log(L_INFO "Queued new configuration");
+       log(L_INFO "Queueing new configuration");
+
+      future_cftype = type;
       future_config = c;
-      future_type = type;
       return CONF_QUEUED;
     }
 
-  if (!shutting_down)
-    log(L_INFO "Reconfiguring");
-
   if (config_do_commit(c, type))
     {
       config_done(NULL);
       return CONF_DONE;
     }
-  if (!config_event)
+  return CONF_PROGRESS;
+}
+
+/**
+ * config_confirm - confirm a commited configuration
+ *
+ * When the undo timer is activated by config_commit() with nonzero timeout,
+ * this function can be used to deactivate it and therefore confirm
+ * the current configuration.
+ *
+ * Result: %CONF_CONFIRM when the current configuration is confirmed,
+ * %CONF_NONE when there is nothing to confirm (i.e. undo timer is not active).
+ */
+int
+config_confirm(void)
+{
+  if (config_timer->expires == 0)
+    return CONF_NOTHING;
+
+  tm_stop(config_timer);
+
+  return CONF_CONFIRM;
+}
+
+/**
+ * config_undo - undo a configuration
+ *
+ * Function config_undo() can be used to change the current
+ * configuration back to stored %old_config. If no reconfiguration is
+ * running, this stored configuration is commited in the same way as a
+ * new configuration in config_commit(). If there is already a
+ * reconfiguration in progress and no next reconfiguration is
+ * scheduled, then the undo is scheduled for later processing as
+ * usual, but if another reconfiguration is already scheduled, then
+ * such reconfiguration is removed instead (i.e. undo is applied on
+ * the last commit that scheduled it).
+ *
+ * Result: %CONF_DONE if the configuration has been accepted immediately,
+ * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED
+ * if it's been queued due to another reconfiguration being in progress now,
+ * %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING
+ * if there is no relevant configuration to undo (the previous config request
+ * was config_undo() too)  or %CONF_SHUTDOWN if BIRD is in shutdown mode and 
+ * no new configuration changes  are accepted.
+ */
+int
+config_undo(void)
+{
+  if (shutting_down)
+    return CONF_SHUTDOWN;
+
+  if (!undo_available || !old_config)
+    return CONF_NOTHING;
+
+  undo_available = 0;
+  tm_stop(config_timer);
+
+  if (configuring)
     {
-      config_event = ev_new(&root_pool);
-      config_event->hook = config_done;
+      if (future_cftype)
+       {
+         config_free(future_config);
+         future_config = NULL;
+
+         log(L_INFO "Removing queued configuration");
+         future_cftype = RECONFIG_NONE;
+         return CONF_UNQUEUED;
+       }
+      else
+       {
+         log(L_INFO "Queueing undo configuration");
+         future_cftype = RECONFIG_UNDO;
+         return CONF_QUEUED;
+       }
+    }
+
+  if (config_do_commit(NULL, RECONFIG_UNDO))
+    {
+      config_done(NULL);
+      return CONF_DONE;
     }
   return CONF_PROGRESS;
 }
 
+extern void cmd_reconfig_undo_notify(void);
+
+static void
+config_timeout(struct timer *t)
+{
+  log(L_INFO "Config timeout expired, starting undo");
+  cmd_reconfig_undo_notify();
+
+  int r = config_undo();
+  if (r < 0)
+    log(L_ERR "Undo request failed");
+}
+
+void
+config_init(void)
+{
+  config_event = ev_new(&root_pool);
+  config_event->hook = config_done;
+
+  config_timer = tm_new(&root_pool);
+  config_timer->hook = config_timeout;
+}
+
 /**
  * order_shutdown - order BIRD shutdown
  *
@@ -328,15 +458,16 @@ order_shutdown(void)
 
   if (shutting_down)
     return;
+
   log(L_INFO "Shutting down");
   c = lp_alloc(config->mem, sizeof(struct config));
   memcpy(c, config, sizeof(struct config));
   init_list(&c->protos);
   init_list(&c->tables);
   c->shutdown = 1;
+
+  config_commit(c, RECONFIG_HARD, 0);
   shutting_down = 1;
-  config_commit(c, RECONFIG_HARD);
-  shutting_down = 2;
 }
 
 /**
index c76832b607c85f1c5cbb95c9ef565f5aa46f9c41..19300f54bab6ec3478dd7ff945191acc17a55403 100644 (file)
@@ -54,28 +54,33 @@ struct config {
 /* Please don't use these variables in protocols. Use proto_config->global instead. */
 extern struct config *config;          /* Currently active configuration */
 extern struct config *new_config;      /* Configuration being parsed */
-extern struct config *old_config;      /* Old configuration when reconfiguration is in progress */
-extern struct config *future_config;   /* New config held here if recon requested during recon */
-
-extern int shutting_down;
-extern bird_clock_t boot_time;
 
 struct config *config_alloc(byte *name);
 int config_parse(struct config *);
 int cli_parse(struct config *);
 void config_free(struct config *);
-int config_commit(struct config *, int type);
-#define RECONFIG_HARD 0
-#define RECONFIG_SOFT 1
+int config_commit(struct config *, int type, int timeout);
+int config_confirm(void);
+int config_undo(void);
+void config_init(void);
 void cf_error(char *msg, ...) NORET;
 void config_add_obstacle(struct config *);
 void config_del_obstacle(struct config *);
 void order_shutdown(void);
 
-#define CONF_DONE 0
-#define CONF_PROGRESS 1
-#define CONF_QUEUED 2
-#define CONF_SHUTDOWN 3
+#define RECONFIG_NONE  0
+#define RECONFIG_HARD  1
+#define RECONFIG_SOFT  2
+#define RECONFIG_UNDO  3
+
+#define CONF_DONE      0
+#define CONF_PROGRESS  1
+#define CONF_QUEUED    2
+#define CONF_UNQUEUED  3
+#define CONF_CONFIRM   4
+#define CONF_SHUTDOWN  -1
+#define CONF_NOTHING   -2
+
 
 /* Pools */
 
index a88ba014c5c3862d9cad23afd5b3a366186fd761..3ed21f135bc310d6b32bb29e810546159d7e49aa 100644 (file)
@@ -10,6 +10,9 @@ m4_divert(-1)m4_dnl
 m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 },
 m4_divert(-1)')
 
+m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 },
+m4_divert(-1)')
+
 m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 },
 m4_divert(-1)')
 
index 74385f3210a8fdb498db17672d4d16dabd8549b1..00b55023766177f55c86e5c1ade9bdcc3f039027 100644 (file)
@@ -44,6 +44,7 @@ m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL
 m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
 m4_divert(3)CF_ADDTO(cli_cmd, CF_cmd)
 CF_cmd: $1 $2 END')
+m4_define(CF_CLI_CMD, `')
 m4_define(CF_CLI_HELP, `')
 
 # ENUM declarations are ignored
index d351cedc1203449dc8ae0cfb2f849d9a2b812b86..615ced986ded876f795d8ef6ce22e523ef0ae53a 100644 (file)
@@ -702,19 +702,48 @@ This argument can be omitted if there exists only a single instance.
        <tag>flush roa [table <m/t/>]</tag>
        Remove all dynamic ROA entries from a ROA table.
 
-       <tag>configure [soft] ["<m/config file/"]</tag>
+       <tag>configure [soft] ["<m/config file/"] [timeout [<m/num/]]</tag>
        Reload configuration from a given file. BIRD will smoothly
        switch itself to the new configuration, protocols are
        reconfigured if possible, restarted otherwise. Changes in
-       filters usually lead to restart of affected protocols. If
-       <cf/soft/ option is used, changes in filters does not cause
+       filters usually lead to restart of affected protocols.
+
+       If <cf/soft/ option is used, changes in filters does not cause
        BIRD to restart affected protocols, therefore already accepted
        routes (according to old filters) would be still propagated,
        but new routes would be processed according to the new
        filters.
 
+       If <cf/timeout/ option is used, config timer is activated. The
+       new configuration could be either confirmed using
+       <cf/configure confirm/ command, or it will be reverted to the
+       old one when the config timer expires. This is useful for cases
+       when reconfiguration breaks current routing and a router becames
+       inaccessible for an administrator. The config timeout expiration is
+       equivalent to <cf/configure undo/ command. The timeout duration
+       could be specified, default is 300 s.
+
+       <tag>configure confirm</tag>
+       Deactivate the config undo timer and therefore confirm the current
+       configuration.
+
+       <tag>configure undo</tag>
+       Undo the last configuration change and smoothly switch back to
+       the previous (stored) configuration. If the last configuration
+       change was soft, the undo change is also soft. There is only
+       one level of undo, but in some specific cases when several
+       reconfiguration requests are given immediately in a row and
+       the intermediate ones are skipped then the undo also skips them back.
+
+       <tag>configure check ["<m/config file/"]</tag>
+       Read and parse given config file, but do not use it. useful
+       for checking syntactic and some semantic validity of an config
+       file.
+
        <tag>enable|disable|restart <m/name/|"<m/pattern/"|all</tag>
-       Enable, disable or restart a given protocol instance, instances matching the <cf><m/pattern/</cf> or <cf/all/ instances.
+       Enable, disable or restart a given protocol instance,
+       instances matching the <cf><m/pattern/</cf> or
+       <cf/all/ instances.
 
        <tag>reload [in|out] <m/name/|"<m/pattern/"|all</tag>
        
index 7ec2e27dd1cac0d4e15a7b02e0d65d99819fb6a0..58807241b23a26080b35f9f2a194f7aa54bb5b02 100644 (file)
@@ -25,6 +25,12 @@ Reply codes of BIRD command-line interface
 0014   Route count
 0015   Reloading
 0016   Access restricted
+0017   Reconfiguration already in progress, removing queued config
+0018   Reconfiguration confirmed
+0019   Nothing to do (configure undo/confirm)
+0020   Configuration OK
+0021   Undo requested
+0022   Undo scheduled
 
 1000   BIRD version
 1001   Interface list
index d245790b01d09aaea66290f2941614c4db300a6a..11f98794d82a6c840fc9c7bbaab3962d50fccb2f 100644 (file)
@@ -122,6 +122,7 @@ cli_printf(cli *c, int code, char *msg, ...)
   va_list args;
   byte buf[CLI_LINE_SIZE];
   int cd = code;
+  int errcode;
   int size, cnt;
 
   if (cd < 0)
@@ -131,16 +132,26 @@ cli_printf(cli *c, int code, char *msg, ...)
        size = bsprintf(buf, " ");
       else
        size = bsprintf(buf, "%04d-", cd);
+      errcode = -8000;
+    }
+  else if (cd == CLI_ASYNC_CODE)
+    {
+      size = 1; buf[0] = '+'; 
+      errcode = cd;
     }
   else
-    size = bsprintf(buf, "%04d ", cd);
+    {
+      size = bsprintf(buf, "%04d ", cd);
+      errcode = 8000;
+    }
+
   c->last_reply = cd;
   va_start(args, msg);
   cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args);
   va_end(args);
   if (cnt < 0)
     {
-      cli_printf(c, code < 0 ? -8000 : 8000, "<line overflow>");
+      cli_printf(c, errcode, "<line overflow>");
       return;
     }
   size += cnt;
@@ -385,12 +396,17 @@ cli_echo(unsigned int class, byte *msg)
     }
 }
 
+/* Hack for scheduled undo notification */
+extern cli *cmd_reconfig_stored_cli;
+
 void
 cli_free(cli *c)
 {
   cli_set_log_echo(c, 0, 0);
   if (c->cleanup)
     c->cleanup(c);
+  if (c == cmd_reconfig_stored_cli)
+    cmd_reconfig_stored_cli = NULL;
   rfree(c->pool);
 }
 
index ea64680abdc40adb03cdb739169e022de3c706e8..396656e8417d88f4a65280af17e89a2e15824e3b 100644 (file)
@@ -49,6 +49,8 @@ typedef struct cli {
 extern pool *cli_pool;
 extern struct cli *this_cli;           /* Used during parsing */
 
+#define CLI_ASYNC_CODE 10000
+
 /* Functions to be called by command handlers */
 
 void cli_printf(cli *, int, char *, ...);
index 2a8039306acc66f2590a00c83fa784bfda4d592f..54ace169af3e3db92e09af2c1dfda8a35b399f7c 100644 (file)
@@ -14,6 +14,9 @@
 #include "lib/string.h"
 #include "lib/resource.h"
 
+extern int shutting_down;
+extern int configuring;
+
 void
 cmd_show_status(void)
 {
@@ -27,9 +30,10 @@ cmd_show_status(void)
   cli_msg(-1011, "Last reboot on %s", tim);
   tm_format_datetime(tim, &config->tf_base, config->load_time);
   cli_msg(-1011, "Last reconfiguration on %s", tim);
+
   if (shutting_down)
     cli_msg(13, "Shutdown in progress");
-  else if (old_config)
+  else if (configuring)
     cli_msg(13, "Reconfiguration in progress");
   else
     cli_msg(13, "Daemon is up and running");
index e9afa2fe29ed72f7e19ea332f7cb94fad3685a43..1334884e6f7e35c1e4ee915581cdbdad7635bb85 100644 (file)
@@ -516,7 +516,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
              p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
              p->cf_new = nc;
            }
-         else if (!shutting_down)
+         else if (!new->shutdown)
            {
              log(L_INFO "Removing protocol %s", p->name);
              p->down_code = PDC_CF_REMOVE;
@@ -537,7 +537,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
   WALK_LIST(nc, new->protos)
     if (!nc->proto)
       {
-       if (old_config)         /* Not a first-time configuration */
+       if (old)                /* Not a first-time configuration */
          log(L_INFO "Adding protocol %s", nc->name);
        proto_init(nc);
       }
index 844f53df052faa7db218032c47904a13957e9ffc..7bade9181fdd98236a23d416257efa6a7569f81d 100644 (file)
@@ -14,9 +14,9 @@ CF_HDR
 CF_DECLS
 
 CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT)
-CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME)
+CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT)
 
-%type <i> log_mask log_mask_list log_cat
+%type <i> log_mask log_mask_list log_cat cfg_timeout
 %type <g> log_file
 %type <t> cfg_name
 %type <tf> timeformat_which
@@ -104,13 +104,26 @@ timeformat_base:
 
 /* Unix specific commands */
 
-CF_CLI_HELP(CONFIGURE, [soft] [\"<file>\"], [[Reload configuration]])
+CF_CLI_HELP(CONFIGURE, ..., [[Reload configuration]])
 
-CF_CLI(CONFIGURE, cfg_name, [\"<file>\"], [[Reload configuration]])
-{ cmd_reconfig($2, RECONFIG_HARD); } ;
+CF_CLI(CONFIGURE, cfg_name cfg_timeout, [\"<file>\"] [timeout [<sec>]], [[Reload configuration]])
+{ cmd_reconfig($2, RECONFIG_HARD, $3); } ;
 
-CF_CLI(CONFIGURE SOFT, cfg_name, [\"<file>\"], [[Reload configuration and ignore changes in filters]])
-{ cmd_reconfig($3, RECONFIG_SOFT); } ;
+CF_CLI(CONFIGURE SOFT, cfg_name cfg_timeout, [\"<file>\"] [timeout [<sec>]], [[Reload configuration and ignore changes in filters]])
+{ cmd_reconfig($3, RECONFIG_SOFT, $4); } ;
+
+/* Hack to get input completion for 'timeout' */
+CF_CLI_CMD(CONFIGURE TIMEOUT, [<sec>], [[Reload configuration with undo timeout]])
+CF_CLI_CMD(CONFIGURE SOFT TIMEOUT, [<sec>], [[Reload configuration with undo timeout]])
+
+CF_CLI(CONFIGURE CONFIRM,,, [[Confirm last configuration change - deactivate undo timeout]])
+{ cmd_reconfig_confirm(); } ;
+
+CF_CLI(CONFIGURE UNDO,,, [[Undo last configuration change]])
+{ cmd_reconfig_undo(); } ;
+
+CF_CLI(CONFIGURE CHECK, cfg_name, [\"<file>\"], [[Parse configuration and check its validity]])
+{ cmd_check_config($3); } ;
 
 CF_CLI(DOWN,,, [[Shut the daemon down]])
 { cmd_shutdown(); } ;
@@ -120,6 +133,12 @@ cfg_name:
  | TEXT
  ;
 
+cfg_timeout:
+   /* empty */ { $$ = 0; }
+ | TIMEOUT { $$ = UNIX_DEFAULT_CONFIGURE_TIMEOUT; }
+ | TIMEOUT expr { $$ = $2; }
+ ;
+
 CF_CODE
 
 CF_END
index f91b527859816c33365845ff459992ac6f362b67..80914afe92f71ce8aa5db1d73d0f930ee449214d 100644 (file)
@@ -121,7 +121,7 @@ static list near_timers, far_timers;
 static bird_clock_t first_far_timer = TIME_INFINITY;
 
 /* now must be different from 0, because 0 is a special value in timer->expires */
-bird_clock_t now = 1, now_real;
+bird_clock_t now = 1, now_real, boot_time;
 
 static void
 update_times_plain(void)
@@ -1530,6 +1530,7 @@ io_init(void)
   krt_io_init();
   init_times();
   update_times();
+  boot_time = now;
   srandom((int) now_real);
 }
 
@@ -1557,7 +1558,7 @@ io_loop(void)
          tm_shot();
          continue;
        }
-      timo.tv_sec = events ? 0 : tout - now;
+      timo.tv_sec = events ? 0 : MIN(tout - now, 3);
       timo.tv_usec = 0;
 
       if (sock_recalc_fdsets_p)
index 6c0e5e9167e63e105d20524682c56e4ccc8af25e..3761ace610dda321f7719155a7dcdc0d5e28bb5e 100644 (file)
@@ -900,7 +900,7 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
 {
   struct krt_proto *p = (struct krt_proto *) P;
 
-  if (shutting_down)
+  if (config->shutdown)
     return;
   if (!(net->n.flags & KRF_INSTALLED))
     old = NULL;
index f0344a8f4136d75bdb0701dcd6097af35f856680..23040e5408c9a73c25208ad43554ce3731b4165e 100644 (file)
@@ -210,7 +210,7 @@ read_config(void)
       else
        die("Unable to open configuration file %s: %m", config_name);
     }
-  config_commit(conf, RECONFIG_HARD);
+  config_commit(conf, RECONFIG_HARD, 0);
 }
 
 void
@@ -228,19 +228,17 @@ async_config(void)
       config_free(conf);
     }
   else
-    config_commit(conf, RECONFIG_HARD);
+    config_commit(conf, RECONFIG_HARD, 0);
 }
 
-void
-cmd_reconfig(char *name, int type)
+static struct config *
+cmd_read_config(char *name)
 {
   struct config *conf;
 
-  if (cli_access_restricted())
-    return;
-
   if (!name)
     name = config_name;
+
   cli_msg(-2, "Reading configuration from %s", name);
   if (!unix_read_config(&conf, name))
     {
@@ -249,24 +247,94 @@ cmd_reconfig(char *name, int type)
       else
        cli_msg(8002, "%s: %m", name);
       config_free(conf);
+      conf = NULL;
     }
-  else
+
+  return conf;
+}
+
+void
+cmd_check_config(char *name)
+{
+  struct config *conf = cmd_read_config(name);
+  if (!conf)
+    return;
+
+  cli_msg(20, "Configuration OK");
+  config_free(conf);
+}
+
+static void
+cmd_reconfig_msg(int r)
+{
+  switch (r)
     {
-      switch (config_commit(conf, type))
-       {
-       case CONF_DONE:
-         cli_msg(3, "Reconfigured.");
-         break;
-       case CONF_PROGRESS:
-         cli_msg(4, "Reconfiguration in progress.");
-         break;
-       case CONF_SHUTDOWN:
-         cli_msg(6, "Reconfiguration ignored, shutting down.");
-         break;
-       default:
-         cli_msg(5, "Reconfiguration already in progress, queueing new config");
-       }
+    case CONF_DONE:    cli_msg( 3, "Reconfigured"); break;
+    case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break;
+    case CONF_QUEUED:  cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break;
+    case CONF_UNQUEUED:        cli_msg(17, "Reconfiguration already in progress, removing queued config"); break;
+    case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break;
+    case CONF_SHUTDOWN:        cli_msg( 6, "Reconfiguration ignored, shutting down"); break;
+    case CONF_NOTHING: cli_msg(19, "Nothing to do"); break;
+    default:           break;
+    }
+}
+
+/* Hack for scheduled undo notification */
+cli *cmd_reconfig_stored_cli;
+
+void
+cmd_reconfig_undo_notify(void)
+{
+  if (cmd_reconfig_stored_cli)
+    {
+      cli *c = cmd_reconfig_stored_cli;
+      cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo");
+      cli_write_trigger(c);
+    }
+}
+
+void
+cmd_reconfig(char *name, int type, int timeout)
+{
+  if (cli_access_restricted())
+    return;
+
+  struct config *conf = cmd_read_config(name);
+  if (!conf)
+    return;
+
+  int r = config_commit(conf, type, timeout);
+
+  if ((r >= 0) && (timeout > 0))
+    {
+      cmd_reconfig_stored_cli = this_cli;
+      cli_msg(-22, "Undo scheduled in %d s", timeout);
     }
+
+  cmd_reconfig_msg(r);
+}
+
+void
+cmd_reconfig_confirm(void)
+{
+  if (cli_access_restricted())
+    return;
+
+  int r = config_confirm();
+  cmd_reconfig_msg(r);
+}
+
+void
+cmd_reconfig_undo(void)
+{
+  if (cli_access_restricted())
+    return;
+
+  cli_msg(-21, "Undo requested");
+
+  int r = config_undo();
+  cmd_reconfig_msg(r);
 }
 
 /*
@@ -623,6 +691,7 @@ main(int argc, char **argv)
   rt_init();
   if_init();
   roa_init();
+  config_init();
 
   uid_t use_uid = get_uid(use_user);
   gid_t use_gid = get_gid(use_group);
index a788ae270791454a7952e2b8f4c7075e3b0ec510..17450322a73beaed7c466f43517f4aeccd292ff6 100644 (file)
@@ -32,6 +32,7 @@ void tm_dump_all(void);
 
 extern bird_clock_t now;               /* Relative, monotonic time in seconds */
 extern bird_clock_t now_real;          /* Time in seconds since fixed known epoch */
+extern bird_clock_t boot_time;
 
 static inline bird_clock_t
 tm_remains(timer *t)
index 3e85c85c9b7f9929636d9c93ed4edec363675256..1fc26db2a774bd11a47b821e1d5acc941b5ac9a3 100644 (file)
@@ -19,9 +19,14 @@ extern char *bird_name;
 void async_config(void);
 void async_dump(void);
 void async_shutdown(void);
-void cmd_reconfig(char *name, int type);
+void cmd_check_config(char *name);
+void cmd_reconfig(char *name, int type, int timeout);
+void cmd_reconfig_confirm(void);
+void cmd_reconfig_undo(void);
 void cmd_shutdown(void);
 
+#define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300
+
 /* io.c */
 
 volatile int async_config_flag;