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:
parent
80a9cadc76
commit
a92cf57dd6
16 changed files with 407 additions and 115 deletions
253
conf/conf.c
253
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
29
conf/conf.h
29
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 */
|
||||
|
||||
|
|
|
@ -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)')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
20
nest/cli.c
20
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 *, ...);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue