From a92cf57dd6ba021a495fe7268c86dc8e6aeecbb2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 26 Dec 2012 12:40:48 +0100 Subject: [PATCH] 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 --- conf/conf.c | 253 ++++++++++++++++++++++++++++++++----------- conf/conf.h | 29 +++-- conf/gen_commands.m4 | 3 + conf/gen_parser.m4 | 1 + doc/bird.sgml | 37 ++++++- doc/reply_codes | 6 + nest/cli.c | 20 +++- nest/cli.h | 2 + nest/cmds.c | 6 +- nest/proto.c | 4 +- sysdep/unix/config.Y | 33 ++++-- sysdep/unix/io.c | 5 +- sysdep/unix/krt.c | 2 +- sysdep/unix/main.c | 113 +++++++++++++++---- sysdep/unix/timer.h | 1 + sysdep/unix/unix.h | 7 +- 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(); - 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;