0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-08 12:18:42 +00:00

Conf: use coroutine to read new config

This commit is contained in:
Jan Maria Matejka 2018-08-30 16:45:45 +02:00
parent 642acc1b59
commit 4ec43aa137
10 changed files with 318 additions and 164 deletions

View File

@ -330,12 +330,6 @@ cf_hash(byte *c)
return h;
}
static void
cf_init_state(struct cf_context *ctx, struct conf_state *cs)
{
cs->buffer = yy_create_buffer(NULL, YY_BUF_SIZE, ctx->yyscanner);
}
struct conf_state *
cf_new_state(struct cf_context *ctx, const char *name)
{
@ -344,7 +338,7 @@ cf_new_state(struct cf_context *ctx, const char *name)
.name = cfg_strdup(name),
.lino = 1,
};
cf_init_state(ctx, cs);
cs->buffer = yy_create_buffer(NULL, YY_BUF_SIZE, ctx->yyscanner);
return cs;
}
@ -400,7 +394,7 @@ cf_init_kh(void)
* parsing of a new input.
*/
struct cf_context *
cf_new_context(int is_cli, struct conf_order *order)
cf_new_context(struct conf_order *order)
{
struct cf_context *ctx = order->ctx = lp_allocz(order->new_config->mem, sizeof(struct cf_context));
*ctx = (struct cf_context) {
@ -412,10 +406,7 @@ cf_new_context(int is_cli, struct conf_order *order)
cfx_lex_init_extra(ctx, &(ctx->yyscanner));
struct yyguts_t *yyg = ctx->yyscanner;
cf_init_state(ctx, order->state);
yy_switch_to_buffer(order->state->buffer, yyg);
if (is_cli)
if (order->flags & CO_CLI)
BEGIN(CLI);
else
BEGIN(INITIAL);

View File

@ -103,16 +103,42 @@ config_alloc(struct pool *pp, struct linpool *lp)
return c;
}
int
config_parse(struct conf_order *order)
static void config_event_resume(void *arg)
{
DBG("Parsing configuration named `%s'\n", order->state->name);
DBG("config event resume\n");
struct cf_context *ctx = arg;
coro_resume(ctx->coro);
}
if (!order->new_config)
order->new_config = config_alloc(order->pool, order->lp);
static void config_event_cleanup(void *arg)
{
DBG("config event cleanup\n");
struct cf_context *ctx = arg;
struct conf_order *order = ctx->order;
struct cf_context *ctx = cf_new_context(0, order);
int ret;
rfree(ctx->coro);
cf_free_context(ctx);
order->ctx = NULL;
if (order->flags & CO_CLI)
{
config_free(order->new_config);
order->new_config = NULL;
}
return order->cf_done(order);
}
static void
config_parse_coro(void *arg)
{
struct cf_context *ctx = arg;
struct conf_order *order = ctx->order;
DBG("%s parse coroutine started in %s mode\n",
(order->flags & CO_CLI) ? "Cli" : "Conf",
(order->flags & CO_SYNC) ? "sync" : "async"
);
if (setjmp(ctx->jmpbuf))
{
@ -120,58 +146,80 @@ config_parse(struct conf_order *order)
while (! order->cf_outclude(order))
;
ret = 0;
config_free(ctx->new_config);
order->new_config = NULL;
goto cleanup;
}
sysdep_preconfig(ctx);
protos_preconfig(ctx->new_config);
rt_preconfig(ctx);
cfx_parse(ctx, ctx->yyscanner);
if (EMPTY_LIST((ctx->new_config)->protos))
if (!(order->flags & CO_CLI) && EMPTY_LIST(ctx->new_config->protos))
cf_error(ctx, "No protocol is specified in the config file");
ret = 1;
cleanup:
cf_free_context(ctx);
order->ctx = NULL;
return ret;
if (order->flags & CO_SYNC)
return;
DBG("config parse coroutine done, scheduling thread join\n");
coro_done(ctx->ev_cleanup);
bug("Resumed config when done.");
}
int
cli_parse(struct conf_order *order)
void
config_parse(struct conf_order *order)
{
DBG("Parsing command line\n");
DBG("Parsing configuration\n");
struct config cc = {};
cc.pool = rp_new(order->pool ?: &root_pool, "CLI Dummy Config");
cc.mem = order->lp ?: lp_new_default(cc.pool);
if (!order->new_config)
order->new_config = config_alloc(order->pool, order->lp);
order->new_config = &cc;
pool *p = order->new_config->pool;
struct cf_context *ctx = cf_new_context(1, order);
struct cf_context *ctx = cf_new_context(order);
cf_scan_bytes(ctx, order->buf, order->len);
/* CLI does no preconfig */
if (!(order->flags & CO_CLI))
{
sysdep_preconfig(ctx);
protos_preconfig(ctx->new_config);
rt_preconfig(ctx);
}
int ok = 0;
if (setjmp(ctx->jmpbuf))
goto done;
if (!(order->flags & CO_SYNC))
{
ctx->ev_resume = ev_new(p);
ctx->ev_resume->hook = config_event_resume;
ctx->ev_resume->data = ctx;
cfx_parse(ctx, ctx->yyscanner);
ok = 1;
ctx->ev_cleanup = ev_new(p);
ctx->ev_cleanup->hook = config_event_cleanup;
ctx->ev_cleanup->data = ctx;
}
done:
cf_free_context(ctx);
config_free(&cc);
order->new_config = NULL;
order->ctx = NULL;
return ok;
if (order->flags & CO_FILENAME)
if (order->cf_include)
order->cf_include(order, order->buf, order->len);
else
bug("Include handler must be set to config from file");
else
cf_scan_bytes(ctx, order->buf, order->len);
/* Warning: Return from include will fail badly if you start with a buffer.
* Currently it's not supported to supply cf_include hook without CO_FILENAME flag.
*/
if (order->flags & CO_SYNC)
return config_parse_coro(ctx);
ctx->coro = coro_new(p, config_parse_coro, ctx);
coro_resume(ctx->coro);
}
void config_yield(struct cf_context *ctx)
{
DBG("Conf: Yield\n");
ev_schedule(ctx->ev_resume);
DBG("Conf: Yield resumed\n");
}
/**
@ -319,6 +367,7 @@ config_done(void *unused UNUSED)
int
config_commit(struct config *c, int type, uint timeout)
{
ASSERT(type != RECONFIG_CHECK);
if (shutting_down)
{
config_free(c);

View File

@ -59,18 +59,30 @@ struct conf_state {
uint lino; /* Current line */
};
enum conf_order_flag {
CO_CLI = 1, /* Parse CLI, not regular config */
CO_SYNC = 2, /* Run parser synchronously */
CO_FILENAME = 4, /* Use the order buffer as filename */
} PACKED;
/* This struct is meant to be inherited and customized by caller */
struct conf_order {
struct config *new_config; /* Store the allocated config here */
resource r;
struct config *new_config; /* Outputs the allocated config here */
struct cf_context *ctx; /* Internal config context, do not set */
struct conf_state *state;
struct conf_state *state; /* Internal config state, do not set */
struct pool *pool; /* If set, use this resource pool */
struct linpool *lp; /* If set, use this linpool */
const char *buf;
uint len;
const char *buf; /* Buffer to parse or filename */
uint len; /* Buffer length */
enum conf_order_flag flags;
int (*cf_read_hook)(struct conf_order *order, byte *buf, uint max);
void (*cf_include)(struct conf_order *order, char *name, uint len);
void (*cf_include)(struct conf_order *order, const char *name, uint len);
int (*cf_outclude)(struct conf_order *order);
void (*cf_error)(struct conf_order *order, const char *msg, va_list args);
void (*cf_done)(struct conf_order *order);
};
/* Please don't use these variables in protocols. Use proto_config->global instead. */
@ -82,25 +94,12 @@ extern struct config *config; /* Currently active configuration */
* Arguments:
* @order provides callbacks to read config files
*
* This function queues
* Return value:
* 1 on success; order->new_config is then set to the parsed config
* 0 on fail; order->new_config is undefined
**/
int config_parse(struct conf_order *order);
/**
* Parse CLI command
*
* Arguments:
* @order provides callbacks to read command line
*
* Return value:
* 1 on success
* 0 on fail
*
* Parsed config is never kept, order->new_config should be zero after return.
**/
int cli_parse(struct conf_order *order);
void config_parse(struct conf_order *order);
/** Callback for returning error from parser hooks */
void cf_error(struct cf_context *, const char *msg, ...) NORET;
@ -118,6 +117,7 @@ void order_shutdown(void);
#define RECONFIG_HARD 1
#define RECONFIG_SOFT 2
#define RECONFIG_UNDO 3
#define RECONFIG_CHECK 4
#define CONF_DONE 0
#define CONF_PROGRESS 1

View File

@ -22,6 +22,7 @@ CF_HDR
#include "nest/route.h"
#include "nest/cli.h"
#include "filter/filter.h"
#include "lib/coroutine.h"
/* FIXME: Turn on YYERROR_VERBOSE and work around lots of bison bugs? */
@ -33,6 +34,9 @@ struct config *new_config;
jmp_buf jmpbuf;
linpool *cfg_mem;
struct conf_order *order;
coroutine *coro;
event *ev_resume;
event *ev_cleanup;
CF_DEFINES

View File

@ -27,7 +27,7 @@ union YYSTYPE;
int cfx_lex(union YYSTYPE *, yyscan_t);
/* Config context alloc and free */
struct cf_context *cf_new_context(int, struct conf_order *);
struct cf_context *cf_new_context(struct conf_order *);
void cf_free_context(struct cf_context *);
/* Lexer state alloc and free */

View File

@ -33,6 +33,7 @@ Reply codes of BIRD command-line interface
0022 Undo scheduled
0023 Evaluation of expression
0024 Graceful restart status report
0025 Reconfiguration rejected, another config not parsed yet
1000 BIRD version
1001 Interface list

View File

@ -11,6 +11,8 @@
#include "lib/lists.h"
#include <stdlib.h>
/* Resource */
typedef struct resource {

View File

@ -442,6 +442,7 @@ cli_command(struct cli *c)
.cf_error = cli_cmd_error,
.lp = c->parser_pool,
.pool = c->pool,
.flags = CO_CLI | CO_SYNC,
},
.cli = c,
};
@ -451,7 +452,7 @@ cli_command(struct cli *c)
lp_flush(c->parser_pool);
this_cli = c;
cli_parse(&(o.co));
config_parse(&(o.co));
}
/*
@ -468,8 +469,6 @@ cli_event(void *data)
c->async_msg_size < CLI_MAX_ASYNC_QUEUE)
cli_copy_message(c);
cli_write_trigger(c);
if (c->state == CLI_STATE_YIELD ||
c->state == CLI_STATE_WAIT_TX && !c->tx_pos)
coro_resume(c->coro);

View File

@ -66,6 +66,7 @@ typedef struct cli {
uint log_mask; /* Mask of allowed message levels */
uint log_threshold; /* When free < log_threshold, store only important messages */
uint async_msg_size; /* Total size of async messages queued in tx_buf */
struct conf_order *running_config; /* Asynchronous config pointer */
} cli;
extern pool *cli_pool;

View File

@ -16,6 +16,8 @@
#include <sys/stat.h>
#include <unistd.h>
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "conf/conf.h"
#include "conf/parser.h"
@ -106,9 +108,33 @@ sysdep_commit(struct config *new, struct config *old UNUSED)
return 0;
}
#define MAX_INCLUDE_DEPTH 8
#define UCO struct unix_conf_order *uco = (struct unix_conf_order *) co
struct unix_conf_order {
struct conf_order co;
struct unix_ifs *ifs;
struct conf_order co; /* First field of struct conf_order is resource r; */
struct unix_ifs *ifs; /* Input file stack; initially NULL, is inited inside config_parse() */
struct linpool *ifs_lp; /* Where to allocate IFS from */
struct cli *cli; /* CLI if called from CLI */
event *ev; /* Start event if called from CLI */
int type; /* Type of reconfig */
uint timeout; /* Config timeout */
};
static void
unix_conf_order_free(resource *r)
{
struct unix_conf_order *uco = (struct unix_conf_order *) r;
rfree(uco->ifs_lp);
}
static struct resclass unix_conf_order_class = {
"Unix Conf Order",
sizeof(struct unix_conf_order),
unix_conf_order_free,
NULL,
NULL,
NULL,
};
struct unix_ifs {
@ -123,7 +149,7 @@ struct unix_ifs {
static int
unix_cf_read(struct conf_order *co, byte *dest, uint len)
{
struct unix_conf_order *uco = (struct unix_conf_order *) co;
UCO;
ASSERT(uco->ifs->state == co->state);
@ -138,7 +164,7 @@ unix_cf_read(struct conf_order *co, byte *dest, uint len)
cf_error(co->ctx, "Unable to open included file %s: %m", fn);
}
else
cf_error(co->ctx, "Unable to open configuration file %s: %m", co->state->name);
cf_error(co->ctx, "Unable to open: %m");
int l = read(uco->ifs->fd, dest, len);
if (l < 0)
@ -147,28 +173,26 @@ unix_cf_read(struct conf_order *co, byte *dest, uint len)
}
static void
unix_cf_include(struct conf_order *co, char *name, uint len)
unix_cf_include(struct conf_order *co, const char *name, uint len)
{
struct unix_conf_order *uco = (struct unix_conf_order *) co;
UCO;
if (!uco->ifs)
cf_error(co->ctx, "Max include depth reached");
byte new_depth = uco->ifs->depth - 1;
byte new_depth = uco->ifs ? (uco->ifs->depth - 1) : MAX_INCLUDE_DEPTH;
/* Includes are relative to the current file unless the path is absolute.
* Joining the current file dirname with the include relative path. */
char *patt;
if (*name != '/')
const char *patt;
if (co->state && *name != '/')
{
/* dlen is upper bound of current file dirname length */
int dlen = strlen(co->state->name);
char *dir = alloca(dlen + 1);
patt = alloca(dlen + len + 2);
char *npatt = alloca(dlen + len + 2);
/* dirname() may overwrite its argument */
memcpy(dir, co->state->name, dlen + 1);
sprintf(patt, "%s/%s", dirname(dir), name);
sprintf(npatt, "%s/%s", dirname(dir), name);
patt = npatt;
}
else
patt = name;
@ -177,7 +201,7 @@ unix_cf_include(struct conf_order *co, char *name, uint len)
response when the included config file is missing */
if (!strpbrk(name, "?*["))
{
struct unix_ifs *uifs = cf_alloc(co->ctx, sizeof(struct unix_ifs));
struct unix_ifs *uifs = lp_alloc(uco->ifs_lp, sizeof(struct unix_ifs));
*uifs = (struct unix_ifs) {
.next = uco->ifs,
@ -223,7 +247,7 @@ unix_cf_include(struct conf_order *co, char *name, uint len)
continue;
/* Prepare new stack item */
struct unix_ifs *uifs = cf_alloc(co->ctx, sizeof(struct unix_ifs));
struct unix_ifs *uifs = lp_alloc(uco->ifs_lp, sizeof(struct unix_ifs));
*uifs = (struct unix_ifs) {
.next = last_uifs,
@ -247,7 +271,7 @@ unix_cf_include(struct conf_order *co, char *name, uint len)
static int
unix_cf_outclude(struct conf_order *co)
{
struct unix_conf_order *uco = (struct unix_conf_order *) co;
UCO;
close(uco->ifs->fd);
cf_free_state(co->ctx, uco->ifs->state);
@ -261,112 +285,201 @@ unix_cf_outclude(struct conf_order *co)
return 0;
}
#define MAX_INCLUDE_DEPTH 8
typedef void (*cf_error_type)(struct conf_order *co, const char *msg, va_list args);
typedef void (*cf_done_type)(struct conf_order *co);
typedef void (*cf_error_type)(struct conf_order *order, const char *msg, va_list args);
static struct config *
unix_read_config(char *name, cf_error_type arg_cf_error)
static struct unix_conf_order *
unix_new_conf_order(pool *p)
{
struct conf_state state = { .name = name };
struct unix_conf_order *uco = ralloc(p, &unix_conf_order_class);
uco->co.flags = CO_FILENAME;
uco->co.cf_read_hook = unix_cf_read;
uco->co.cf_include = unix_cf_include;
uco->co.cf_outclude = unix_cf_outclude;
struct unix_ifs uifs = {
.state = &state,
.depth = MAX_INCLUDE_DEPTH,
.fd = -1,
};
uco->ifs_lp = lp_new_default(p);
struct unix_conf_order uco = {
.co = {
.cf_read_hook = unix_cf_read,
.cf_include = unix_cf_include,
.cf_outclude = unix_cf_outclude,
.cf_error = arg_cf_error,
.state = &state,
},
.ifs = &uifs,
};
if (config_parse(&uco.co))
return uco.co.new_config;
else
return NULL;
return uco;
}
static void
unix_cf_error_die(struct conf_order *order, const char *msg, va_list args)
unix_cf_error_die(struct conf_order *co, const char *msg, va_list args)
{
die("%s, line %u: %V", order->state->name, order->state->lino, msg, &args);
va_list cargs;
va_copy(cargs, args);
die("%s, line %u: %V", co->state->name, co->state->lino, msg, &cargs);
va_end(cargs);
}
struct config *
read_config(void)
{
return unix_read_config(config_name, unix_cf_error_die);
struct unix_conf_order *uco = unix_new_conf_order(&root_pool);
uco->co.buf = config_name;
uco->co.len = strlen(config_name);
uco->co.flags |= CO_SYNC;
uco->co.cf_error = unix_cf_error_die;
config_parse(&(uco->co));
struct config *c = uco->co.new_config;
rfree(uco);
return c;
}
static void
unix_cf_error_log(struct conf_order *order, const char *msg, va_list args)
unix_cf_error_log(struct conf_order *co, const char *msg, va_list args)
{
log(L_ERR "%s, line %u: %V", order->state->name, order->state->lino, msg, &args);
va_list cargs;
va_copy(cargs, args);
log(L_ERR "%s, line %u: %V", co->state->name, co->state->lino, msg, &cargs);
va_end(cargs);
}
static void
unix_cf_done_async(struct conf_order *co)
{
UCO;
struct config *c = co->new_config;
if (c)
config_commit(c, RECONFIG_HARD, 0);
rfree(uco);
}
void
async_config(void)
{
log(L_INFO "Reconfiguration requested by SIGHUP");
struct config *conf = unix_read_config(config_name, unix_cf_error_log);
struct unix_conf_order *uco = unix_new_conf_order(&root_pool);
if (conf)
config_commit(conf, RECONFIG_HARD, 0);
uco->co.buf = config_name;
uco->co.len = strlen(config_name);
uco->co.cf_error = unix_cf_error_log;
uco->co.cf_done = unix_cf_done_async;
log(L_INFO "Reconfiguration requested by SIGHUP");
config_parse(&(uco->co));
}
static void
unix_cf_error_cli(struct conf_order *order, const char *msg, va_list args)
unix_cf_error_cli(struct conf_order *co, const char *msg, va_list args)
{
cli_msg(8002, "%s, line %d: %s", order->state->name, order->state->lino, msg, &args);
cli_msg(8002, "%s, line %d: %s", co->state->name, co->state->lino, msg, &args);
}
static struct config *
cmd_read_config(char *name)
{
if (!name)
name = config_name;
static void cmd_reconfig_msg(cli *c, int r);
cli_msg(-2, "Reading configuration from %s", name);
return unix_read_config(name, unix_cf_error_cli);
/* Hack for scheduled undo notification */
cli *cmd_reconfig_stored_cli;
static void
unix_cf_done_cli(struct conf_order *co)
{
DBG("unix_cf_done_cli\n");
UCO;
cli_wakeup(uco->cli);
}
static void
cmd_done_config(struct unix_conf_order *uco)
{
DBG("config done handler\n");
if (uco->type == RECONFIG_CHECK)
{
if (!uco->co.new_config)
goto cleanup;
cli_printf(uco->cli, 20, "Configuration OK");
config_free(uco->co.new_config);
}
else
{
struct config *c = uco->co.new_config;
if (!c)
goto cleanup;
int r = config_commit(c, uco->type, uco->timeout);
if ((r >= 0) && (uco->timeout > 0))
{
cmd_reconfig_stored_cli = uco->cli;
cli_printf(uco->cli, -22, "Undo scheduled in %d s", uco->timeout);
}
cmd_reconfig_msg(uco->cli, r);
}
cleanup:
DBG("config done handler: freeing the config order\n");
rfree(uco);
}
static void
cmd_read_config_ev(void *data)
{
struct unix_conf_order *uco = data;
log(L_INFO "Reading configuration from %s on CLI request: begin", uco->co.buf);
return config_parse(&(uco->co));
}
static void
cmd_read_config(struct unix_conf_order *uco)
{
DBG("cmd_read_config\n");
uco->co.buf = uco->co.buf ?: config_name;
uco->co.len = strlen(uco->co.buf);
uco->cli = this_cli;
cli_msg(-2, "Reading configuration from %s", uco->co.buf);
cli_write_trigger(uco->cli);
uco->co.cf_error = unix_cf_error_cli;
uco->co.cf_done = unix_cf_done_cli;
uco->ev = ev_new(uco->co.pool);
uco->ev->hook = cmd_read_config_ev;
uco->ev->data = uco;
ev_schedule(uco->ev);
DBG("cmd_read_config: sleeping\n");
cli_sleep(uco->cli);
DBG("cmd_read_config: woken up\n");
cmd_done_config(uco);
}
void
cmd_check_config(char *name)
{
struct config *conf = cmd_read_config(name);
if (!conf)
return;
struct unix_conf_order *uco = unix_new_conf_order(&root_pool);
cli_msg(20, "Configuration OK");
config_free(conf);
uco->co.buf = name;
uco->type = RECONFIG_CHECK;
cmd_read_config(uco);
}
static void
cmd_reconfig_msg(int r)
cmd_reconfig_msg(cli *c, int r)
{
switch (r)
{
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;
case CONF_DONE: cli_printf(c, 3, "Reconfigured"); break;
case CONF_PROGRESS: cli_printf(c, 4, "Reconfiguration in progress"); break;
case CONF_QUEUED: cli_printf(c, 5, "Reconfiguration already in progress, queueing new config"); break;
case CONF_UNQUEUED: cli_printf(c, 17, "Reconfiguration already in progress, removing queued config"); break;
case CONF_CONFIRM: cli_printf(c, 18, "Reconfiguration confirmed"); break;
case CONF_SHUTDOWN: cli_printf(c, 6, "Reconfiguration ignored, shutting down"); break;
case CONF_NOTHING: cli_printf(c, 19, "Nothing to do"); break;
default: break;
}
}
/* Hack for scheduled undo notification */
cli *cmd_reconfig_stored_cli;
void
cmd_reconfig_undo_notify(void)
{
@ -384,19 +497,13 @@ cmd_reconfig(char *name, int type, uint timeout)
if (cli_access_restricted())
return;
struct config *conf = cmd_read_config(name);
if (!conf)
return;
struct unix_conf_order *uco = unix_new_conf_order(&root_pool);
int r = config_commit(conf, type, timeout);
uco->co.buf = name;
uco->type = type;
uco->timeout = 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);
return cmd_read_config(uco);
}
void
@ -406,7 +513,7 @@ cmd_reconfig_confirm(void)
return;
int r = config_confirm();
cmd_reconfig_msg(r);
cmd_reconfig_msg(this_cli, r);
}
void
@ -418,6 +525,6 @@ cmd_reconfig_undo(void)
cli_msg(-21, "Undo requested");
int r = config_undo();
cmd_reconfig_msg(r);
cmd_reconfig_msg(this_cli, r);
}