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();
- DBG("config_done\n");
- for(;;)
+ configuring = 0;
+ if (old_config)
+ log(L_INFO "Reconfigured");
+
+ 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.
flush roa [table ]
Remove all dynamic ROA entries from a ROA table.
- configure [soft] ["
+ configure [soft] ["
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
- configure confirm
+ Deactivate the config undo timer and therefore confirm the current
+ configuration.
+
+ configure undo
+ 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.
+
+ configure check ["
+ Read and parse given config file, but do not use it. useful
+ for checking syntactic and some semantic validity of an config
+ file.
+
enable|disable|restart
- Enable, disable or restart a given protocol instance, instances matching the or or
+ reload [in|out]
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, "");
+ cli_printf(c, errcode, "");
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 log_mask log_mask_list log_cat
+%type log_mask log_mask_list log_cat cfg_timeout
%type log_file
%type cfg_name
%type timeformat_which
@@ -104,13 +104,26 @@ timeformat_base:
/* Unix specific commands */
-CF_CLI_HELP(CONFIGURE, [soft] [\"\"], [[Reload configuration]])
+CF_CLI_HELP(CONFIGURE, ..., [[Reload configuration]])
-CF_CLI(CONFIGURE, cfg_name, [\"\"], [[Reload configuration]])
-{ cmd_reconfig($2, RECONFIG_HARD); } ;
+CF_CLI(CONFIGURE, cfg_name cfg_timeout, [\"\"] [timeout []], [[Reload configuration]])
+{ cmd_reconfig($2, RECONFIG_HARD, $3); } ;
-CF_CLI(CONFIGURE SOFT, cfg_name, [\"\"], [[Reload configuration and ignore changes in filters]])
-{ cmd_reconfig($3, RECONFIG_SOFT); } ;
+CF_CLI(CONFIGURE SOFT, cfg_name cfg_timeout, [\"\"] [timeout []], [[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, [], [[Reload configuration with undo timeout]])
+CF_CLI_CMD(CONFIGURE SOFT TIMEOUT, [], [[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, [\"\"], [[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,26 +247,96 @@ 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);
+}
+
/*
* Command-Line Interface
*/
@@ -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;