summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2012-12-26 12:40:48 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2012-12-26 12:40:48 +0100
commita92cf57dd6ba021a495fe7268c86dc8e6aeecbb2 (patch)
tree1df35ec36e661dbdcd3b6065d86df651b2d240c7
parent80a9cadc76101157707aecc0b482ad88ad702fc3 (diff)
Implements undo command and optional timeout for configuration
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
-rw-r--r--conf/conf.c253
-rw-r--r--conf/conf.h29
-rw-r--r--conf/gen_commands.m43
-rw-r--r--conf/gen_parser.m41
-rw-r--r--doc/bird.sgml37
-rw-r--r--doc/reply_codes6
-rw-r--r--nest/cli.c20
-rw-r--r--nest/cli.h2
-rw-r--r--nest/cmds.c6
-rw-r--r--nest/proto.c4
-rw-r--r--sysdep/unix/config.Y33
-rw-r--r--sysdep/unix/io.c5
-rw-r--r--sysdep/unix/krt.c2
-rw-r--r--sysdep/unix/main.c113
-rw-r--r--sysdep/unix/timer.h1
-rw-r--r--sysdep/unix/unix.h7
16 files changed, 407 insertions, 115 deletions
diff --git a/conf/conf.c b/conf/conf.c
index 9375861f..6dfa3691 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -21,9 +21,12 @@
* 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
@@ -55,10 +58,23 @@
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;
}
/**
diff --git a/conf/conf.h b/conf/conf.h
index c76832b6..19300f54 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -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 */
diff --git a/conf/gen_commands.m4 b/conf/gen_commands.m4
index a88ba014..3ed21f13 100644
--- a/conf/gen_commands.m4
+++ b/conf/gen_commands.m4
@@ -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)')
diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4
index 74385f32..00b55023 100644
--- a/conf/gen_parser.m4
+++ b/conf/gen_parser.m4
@@ -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
diff --git a/doc/bird.sgml b/doc/bird.sgml
index d351cedc..615ced98 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -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>
diff --git a/doc/reply_codes b/doc/reply_codes
index 7ec2e27d..58807241 100644
--- a/doc/reply_codes
+++ b/doc/reply_codes
@@ -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
diff --git a/nest/cli.c b/nest/cli.c
index d245790b..11f98794 100644
--- a/nest/cli.c
+++ b/nest/cli.c
@@ -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);
}
diff --git a/nest/cli.h b/nest/cli.h
index ea64680a..396656e8 100644
--- a/nest/cli.h
+++ b/nest/cli.h
@@ -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 *, ...);
diff --git a/nest/cmds.c b/nest/cmds.c
index 2a803930..54ace169 100644
--- a/nest/cmds.c
+++ b/nest/cmds.c
@@ -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");
diff --git a/nest/proto.c b/nest/proto.c
index e9afa2fe..1334884e 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -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);
}
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index 844f53df..7bade918 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -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
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index f91b5278..80914afe 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -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)
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 6c0e5e91..3761ace6 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -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;
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index f0344a8f..23040e54 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -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);
diff --git a/sysdep/unix/timer.h b/sysdep/unix/timer.h
index a788ae27..17450322 100644
--- a/sysdep/unix/timer.h
+++ b/sysdep/unix/timer.h
@@ -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)
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index 3e85c85c..1fc26db2 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -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;