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
This commit is contained in:
Ondrej Zajicek 2012-12-26 12:40:48 +01:00
parent 80a9cadc76
commit a92cf57dd6
16 changed files with 407 additions and 115 deletions

View file

@ -21,9 +21,12 @@
* There can exist up to four different configurations at one time: an active * There can exist up to four different configurations at one time: an active
* one (pointed to by @config), configuration we are just switching from * one (pointed to by @config), configuration we are just switching from
* (@old_config), one queued for the next reconfiguration (@future_config; * (@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 * 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() * Loading of new configuration is very simple: just call config_alloc()
* to get a new &config structure, then use config_parse() to parse a * to get a new &config structure, then use config_parse() to parse a
@ -55,10 +58,23 @@
static jmp_buf conf_jmpbuf; static jmp_buf conf_jmpbuf;
struct config *config, *new_config, *old_config, *future_config; struct config *config, *new_config;
static event *config_event;
int shutting_down, future_type; static struct config *old_config; /* Old configuration */
bird_clock_t boot_time; 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 * config_alloc - allocate a new configuration
@ -82,8 +98,6 @@ config_alloc(byte *name)
c->load_time = now; c->load_time = now;
c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T"; c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T";
if (!boot_time)
boot_time = now;
return c; return c;
} }
@ -154,6 +168,7 @@ cli_parse(struct config *c)
void void
config_free(struct config *c) config_free(struct config *c)
{ {
if (c)
rfree(c->pool); rfree(c->pool);
} }
@ -170,11 +185,8 @@ config_del_obstacle(struct config *c)
DBG("+++ deleting obstacle %d\n", c->obstacle_count); DBG("+++ deleting obstacle %d\n", c->obstacle_count);
c->obstacle_count--; c->obstacle_count--;
if (!c->obstacle_count) if (!c->obstacle_count)
{
ASSERT(config_event);
ev_schedule(config_event); ev_schedule(config_event);
} }
}
static int static int
global_commit(struct config *new, struct config *old) global_commit(struct config *new, struct config *old)
@ -197,16 +209,31 @@ global_commit(struct config *new, struct config *old)
static int static int
config_do_commit(struct config *c, int type) 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; 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) if (old_config)
old_config->obstacle_count++; old_config->obstacle_count++;
DBG("sysdep_commit\n"); DBG("sysdep_commit\n");
force_restart = sysdep_commit(c, old_config); int force_restart = sysdep_commit(c, old_config);
DBG("global_commit\n"); DBG("global_commit\n");
force_restart |= global_commit(c, old_config); force_restart |= global_commit(c, old_config);
DBG("rt_commit\n"); DBG("rt_commit\n");
@ -214,38 +241,38 @@ config_do_commit(struct config *c, int type)
roa_commit(c, old_config); roa_commit(c, old_config);
DBG("protos_commit\n"); DBG("protos_commit\n");
protos_commit(c, old_config, force_restart, type); 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) if (old_config)
nobs = --old_config->obstacle_count; obs = --old_config->obstacle_count;
else
nobs = 0; DBG("do_commit finished with %d obstacles remaining\n", obs);
DBG("do_commit finished with %d obstacles remaining\n", nobs); return !obs;
return !nobs;
} }
static void static void
config_done(void *unused UNUSED) config_done(void *unused UNUSED)
{
struct config *c;
DBG("config_done\n");
for(;;)
{ {
if (config->shutdown) if (config->shutdown)
sysdep_shutdown_done(); sysdep_shutdown_done();
log(L_INFO "Reconfigured");
configuring = 0;
if (old_config) if (old_config)
log(L_INFO "Reconfigured");
if (future_cftype)
{ {
config_free(old_config); int type = future_cftype;
old_config = NULL; struct config *conf = future_config;
} future_cftype = RECONFIG_NONE;
if (!future_config)
break;
c = future_config;
future_config = NULL; future_config = NULL;
log(L_INFO "Reconfiguring to queued configuration"); log(L_INFO "Reconfiguring to queued configuration");
if (!config_do_commit(c, future_type)) if (config_do_commit(conf, type))
break; config_done(NULL);
} }
} }
@ -253,6 +280,7 @@ config_done(void *unused UNUSED)
* config_commit - commit a configuration * config_commit - commit a configuration
* @c: new configuration * @c: new configuration
* @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) * @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 * When a configuration is parsed and prepared for use, the
* config_commit() function starts the process of reconfiguration. * 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 * using config_del_obstacle(), the old configuration is freed and
* everything runs according to the new one. * 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, * Result: %CONF_DONE if the configuration has been accepted immediately,
* %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED * %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 * if it's been queued due to another reconfiguration being in progress now
@ -272,47 +304,145 @@ config_done(void *unused UNUSED)
* are accepted. * are accepted.
*/ */
int 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;
}
if (old_config) /* Reconfiguration already in progress */
{
if (shutting_down == 2)
{
log(L_INFO "New configuration discarded due to shutdown");
config_free(c); config_free(c);
return CONF_SHUTDOWN; return CONF_SHUTDOWN;
} }
if (future_config)
undo_available = 1;
if (timeout > 0)
tm_start(config_timer, timeout);
else
tm_stop(config_timer);
if (configuring)
{
if (future_cftype)
{ {
log(L_INFO "Queueing new configuration, ignoring the one already queued"); log(L_INFO "Queueing new configuration, ignoring the one already queued");
config_free(future_config); config_free(future_config);
} }
else else
log(L_INFO "Queued new configuration"); log(L_INFO "Queueing new configuration");
future_cftype = type;
future_config = c; future_config = c;
future_type = type;
return CONF_QUEUED; return CONF_QUEUED;
} }
if (!shutting_down)
log(L_INFO "Reconfiguring");
if (config_do_commit(c, type)) if (config_do_commit(c, type))
{ {
config_done(NULL); config_done(NULL);
return CONF_DONE; 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)
{
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 = ev_new(&root_pool);
config_event->hook = config_done; config_event->hook = config_done;
}
return CONF_PROGRESS; config_timer = tm_new(&root_pool);
config_timer->hook = config_timeout;
} }
/** /**
@ -328,15 +458,16 @@ order_shutdown(void)
if (shutting_down) if (shutting_down)
return; return;
log(L_INFO "Shutting down"); log(L_INFO "Shutting down");
c = lp_alloc(config->mem, sizeof(struct config)); c = lp_alloc(config->mem, sizeof(struct config));
memcpy(c, config, sizeof(struct config)); memcpy(c, config, sizeof(struct config));
init_list(&c->protos); init_list(&c->protos);
init_list(&c->tables); init_list(&c->tables);
c->shutdown = 1; c->shutdown = 1;
config_commit(c, RECONFIG_HARD, 0);
shutting_down = 1; shutting_down = 1;
config_commit(c, RECONFIG_HARD);
shutting_down = 2;
} }
/** /**

View file

@ -54,28 +54,33 @@ struct config {
/* Please don't use these variables in protocols. Use proto_config->global instead. */ /* Please don't use these variables in protocols. Use proto_config->global instead. */
extern struct config *config; /* Currently active configuration */ extern struct config *config; /* Currently active configuration */
extern struct config *new_config; /* Configuration being parsed */ 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); struct config *config_alloc(byte *name);
int config_parse(struct config *); int config_parse(struct config *);
int cli_parse(struct config *); int cli_parse(struct config *);
void config_free(struct config *); void config_free(struct config *);
int config_commit(struct config *, int type); int config_commit(struct config *, int type, int timeout);
#define RECONFIG_HARD 0 int config_confirm(void);
#define RECONFIG_SOFT 1 int config_undo(void);
void config_init(void);
void cf_error(char *msg, ...) NORET; void cf_error(char *msg, ...) NORET;
void config_add_obstacle(struct config *); void config_add_obstacle(struct config *);
void config_del_obstacle(struct config *); void config_del_obstacle(struct config *);
void order_shutdown(void); void order_shutdown(void);
#define RECONFIG_NONE 0
#define RECONFIG_HARD 1
#define RECONFIG_SOFT 2
#define RECONFIG_UNDO 3
#define CONF_DONE 0 #define CONF_DONE 0
#define CONF_PROGRESS 1 #define CONF_PROGRESS 1
#define CONF_QUEUED 2 #define CONF_QUEUED 2
#define CONF_SHUTDOWN 3 #define CONF_UNQUEUED 3
#define CONF_CONFIRM 4
#define CONF_SHUTDOWN -1
#define CONF_NOTHING -2
/* Pools */ /* Pools */

View 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_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 },
m4_divert(-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_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 },
m4_divert(-1)') m4_divert(-1)')

View 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(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
m4_divert(3)CF_ADDTO(cli_cmd, CF_cmd) m4_divert(3)CF_ADDTO(cli_cmd, CF_cmd)
CF_cmd: $1 $2 END') CF_cmd: $1 $2 END')
m4_define(CF_CLI_CMD, `')
m4_define(CF_CLI_HELP, `') m4_define(CF_CLI_HELP, `')
# ENUM declarations are ignored # ENUM declarations are ignored

View file

@ -702,19 +702,48 @@ This argument can be omitted if there exists only a single instance.
<tag>flush roa [table <m/t/>]</tag> <tag>flush roa [table <m/t/>]</tag>
Remove all dynamic ROA entries from a ROA table. 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 Reload configuration from a given file. BIRD will smoothly
switch itself to the new configuration, protocols are switch itself to the new configuration, protocols are
reconfigured if possible, restarted otherwise. Changes in reconfigured if possible, restarted otherwise. Changes in
filters usually lead to restart of affected protocols. If filters usually lead to restart of affected protocols.
<cf/soft/ option is used, changes in filters does not cause
If <cf/soft/ option is used, changes in filters does not cause
BIRD to restart affected protocols, therefore already accepted BIRD to restart affected protocols, therefore already accepted
routes (according to old filters) would be still propagated, routes (according to old filters) would be still propagated,
but new routes would be processed according to the new but new routes would be processed according to the new
filters. 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> <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> <tag>reload [in|out] <m/name/|"<m/pattern/"|all</tag>

View file

@ -25,6 +25,12 @@ Reply codes of BIRD command-line interface
0014 Route count 0014 Route count
0015 Reloading 0015 Reloading
0016 Access restricted 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 1000 BIRD version
1001 Interface list 1001 Interface list

View file

@ -122,6 +122,7 @@ cli_printf(cli *c, int code, char *msg, ...)
va_list args; va_list args;
byte buf[CLI_LINE_SIZE]; byte buf[CLI_LINE_SIZE];
int cd = code; int cd = code;
int errcode;
int size, cnt; int size, cnt;
if (cd < 0) if (cd < 0)
@ -131,16 +132,26 @@ cli_printf(cli *c, int code, char *msg, ...)
size = bsprintf(buf, " "); size = bsprintf(buf, " ");
else else
size = bsprintf(buf, "%04d-", cd); size = bsprintf(buf, "%04d-", cd);
errcode = -8000;
}
else if (cd == CLI_ASYNC_CODE)
{
size = 1; buf[0] = '+';
errcode = cd;
} }
else else
{
size = bsprintf(buf, "%04d ", cd); size = bsprintf(buf, "%04d ", cd);
errcode = 8000;
}
c->last_reply = cd; c->last_reply = cd;
va_start(args, msg); va_start(args, msg);
cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args); cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args);
va_end(args); va_end(args);
if (cnt < 0) if (cnt < 0)
{ {
cli_printf(c, code < 0 ? -8000 : 8000, "<line overflow>"); cli_printf(c, errcode, "<line overflow>");
return; return;
} }
size += cnt; 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 void
cli_free(cli *c) cli_free(cli *c)
{ {
cli_set_log_echo(c, 0, 0); cli_set_log_echo(c, 0, 0);
if (c->cleanup) if (c->cleanup)
c->cleanup(c); c->cleanup(c);
if (c == cmd_reconfig_stored_cli)
cmd_reconfig_stored_cli = NULL;
rfree(c->pool); rfree(c->pool);
} }

View file

@ -49,6 +49,8 @@ typedef struct cli {
extern pool *cli_pool; extern pool *cli_pool;
extern struct cli *this_cli; /* Used during parsing */ extern struct cli *this_cli; /* Used during parsing */
#define CLI_ASYNC_CODE 10000
/* Functions to be called by command handlers */ /* Functions to be called by command handlers */
void cli_printf(cli *, int, char *, ...); void cli_printf(cli *, int, char *, ...);

View file

@ -14,6 +14,9 @@
#include "lib/string.h" #include "lib/string.h"
#include "lib/resource.h" #include "lib/resource.h"
extern int shutting_down;
extern int configuring;
void void
cmd_show_status(void) cmd_show_status(void)
{ {
@ -27,9 +30,10 @@ cmd_show_status(void)
cli_msg(-1011, "Last reboot on %s", tim); cli_msg(-1011, "Last reboot on %s", tim);
tm_format_datetime(tim, &config->tf_base, config->load_time); tm_format_datetime(tim, &config->tf_base, config->load_time);
cli_msg(-1011, "Last reconfiguration on %s", tim); cli_msg(-1011, "Last reconfiguration on %s", tim);
if (shutting_down) if (shutting_down)
cli_msg(13, "Shutdown in progress"); cli_msg(13, "Shutdown in progress");
else if (old_config) else if (configuring)
cli_msg(13, "Reconfiguration in progress"); cli_msg(13, "Reconfiguration in progress");
else else
cli_msg(13, "Daemon is up and running"); cli_msg(13, "Daemon is up and running");

View 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->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
p->cf_new = nc; p->cf_new = nc;
} }
else if (!shutting_down) else if (!new->shutdown)
{ {
log(L_INFO "Removing protocol %s", p->name); log(L_INFO "Removing protocol %s", p->name);
p->down_code = PDC_CF_REMOVE; 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) WALK_LIST(nc, new->protos)
if (!nc->proto) 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); log(L_INFO "Adding protocol %s", nc->name);
proto_init(nc); proto_init(nc);
} }

View file

@ -14,9 +14,9 @@ CF_HDR
CF_DECLS CF_DECLS
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) 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 <g> log_file
%type <t> cfg_name %type <t> cfg_name
%type <tf> timeformat_which %type <tf> timeformat_which
@ -104,13 +104,26 @@ timeformat_base:
/* Unix specific commands */ /* 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]]) CF_CLI(CONFIGURE, cfg_name cfg_timeout, [\"<file>\"] [timeout [<sec>]], [[Reload configuration]])
{ cmd_reconfig($2, RECONFIG_HARD); } ; { cmd_reconfig($2, RECONFIG_HARD, $3); } ;
CF_CLI(CONFIGURE SOFT, cfg_name, [\"<file>\"], [[Reload configuration and ignore changes in filters]]) CF_CLI(CONFIGURE SOFT, cfg_name cfg_timeout, [\"<file>\"] [timeout [<sec>]], [[Reload configuration and ignore changes in filters]])
{ cmd_reconfig($3, RECONFIG_SOFT); } ; { 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]]) CF_CLI(DOWN,,, [[Shut the daemon down]])
{ cmd_shutdown(); } ; { cmd_shutdown(); } ;
@ -120,6 +133,12 @@ cfg_name:
| TEXT | TEXT
; ;
cfg_timeout:
/* empty */ { $$ = 0; }
| TIMEOUT { $$ = UNIX_DEFAULT_CONFIGURE_TIMEOUT; }
| TIMEOUT expr { $$ = $2; }
;
CF_CODE CF_CODE
CF_END CF_END

View file

@ -121,7 +121,7 @@ static list near_timers, far_timers;
static bird_clock_t first_far_timer = TIME_INFINITY; static bird_clock_t first_far_timer = TIME_INFINITY;
/* now must be different from 0, because 0 is a special value in timer->expires */ /* 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 static void
update_times_plain(void) update_times_plain(void)
@ -1530,6 +1530,7 @@ io_init(void)
krt_io_init(); krt_io_init();
init_times(); init_times();
update_times(); update_times();
boot_time = now;
srandom((int) now_real); srandom((int) now_real);
} }
@ -1557,7 +1558,7 @@ io_loop(void)
tm_shot(); tm_shot();
continue; continue;
} }
timo.tv_sec = events ? 0 : tout - now; timo.tv_sec = events ? 0 : MIN(tout - now, 3);
timo.tv_usec = 0; timo.tv_usec = 0;
if (sock_recalc_fdsets_p) if (sock_recalc_fdsets_p)

View file

@ -900,7 +900,7 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
{ {
struct krt_proto *p = (struct krt_proto *) P; struct krt_proto *p = (struct krt_proto *) P;
if (shutting_down) if (config->shutdown)
return; return;
if (!(net->n.flags & KRF_INSTALLED)) if (!(net->n.flags & KRF_INSTALLED))
old = NULL; old = NULL;

View file

@ -210,7 +210,7 @@ read_config(void)
else else
die("Unable to open configuration file %s: %m", config_name); die("Unable to open configuration file %s: %m", config_name);
} }
config_commit(conf, RECONFIG_HARD); config_commit(conf, RECONFIG_HARD, 0);
} }
void void
@ -228,19 +228,17 @@ async_config(void)
config_free(conf); config_free(conf);
} }
else else
config_commit(conf, RECONFIG_HARD); config_commit(conf, RECONFIG_HARD, 0);
} }
void static struct config *
cmd_reconfig(char *name, int type) cmd_read_config(char *name)
{ {
struct config *conf; struct config *conf;
if (cli_access_restricted())
return;
if (!name) if (!name)
name = config_name; name = config_name;
cli_msg(-2, "Reading configuration from %s", name); cli_msg(-2, "Reading configuration from %s", name);
if (!unix_read_config(&conf, name)) if (!unix_read_config(&conf, name))
{ {
@ -249,24 +247,94 @@ cmd_reconfig(char *name, int type)
else else
cli_msg(8002, "%s: %m", name); cli_msg(8002, "%s: %m", name);
config_free(conf); config_free(conf);
conf = NULL;
} }
else
return conf;
}
void
cmd_check_config(char *name)
{ {
switch (config_commit(conf, type)) 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)
{ {
case CONF_DONE: switch (r)
cli_msg(3, "Reconfigured."); {
break; case CONF_DONE: cli_msg( 3, "Reconfigured"); break;
case CONF_PROGRESS: case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break;
cli_msg(4, "Reconfiguration in progress."); case CONF_QUEUED: cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break;
break; case CONF_UNQUEUED: cli_msg(17, "Reconfiguration already in progress, removing queued config"); break;
case CONF_SHUTDOWN: case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break;
cli_msg(6, "Reconfiguration ignored, shutting down."); case CONF_SHUTDOWN: cli_msg( 6, "Reconfiguration ignored, shutting down"); break;
break; case CONF_NOTHING: cli_msg(19, "Nothing to do"); break;
default: default: break;
cli_msg(5, "Reconfiguration already in progress, queueing new config");
} }
} }
/* 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(); rt_init();
if_init(); if_init();
roa_init(); roa_init();
config_init();
uid_t use_uid = get_uid(use_user); uid_t use_uid = get_uid(use_user);
gid_t use_gid = get_gid(use_group); gid_t use_gid = get_gid(use_group);

View 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; /* Relative, monotonic time in seconds */
extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */ extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */
extern bird_clock_t boot_time;
static inline bird_clock_t static inline bird_clock_t
tm_remains(timer *t) tm_remains(timer *t)

View file

@ -19,9 +19,14 @@ extern char *bird_name;
void async_config(void); void async_config(void);
void async_dump(void); void async_dump(void);
void async_shutdown(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); void cmd_shutdown(void);
#define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300
/* io.c */ /* io.c */
volatile int async_config_flag; volatile int async_config_flag;