]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Conf: use coroutine to read new config
authorJan Maria Matejka <mq@ucw.cz>
Thu, 30 Aug 2018 14:45:45 +0000 (16:45 +0200)
committerJan Maria Matejka <mq@ucw.cz>
Fri, 14 Sep 2018 12:50:49 +0000 (14:50 +0200)
conf/cf-lex.l
conf/conf.c
conf/conf.h
conf/confbase.Y
conf/parser.h
doc/reply_codes
lib/resource.h
nest/cli.c
nest/cli.h
sysdep/unix/conf.c

index 32dc576a62623bd395704fd23a0c115f58b1c6c6..895e5afaa1dc4522d1806d29b4941875f30f9a7f 100644 (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);
index 1048296cf239e1d08c54d519208925db69ea4dd9..99d98c2802a22707fd2c776c14f33272b62ab02f 100644 (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;
+
+  rfree(ctx->coro);
+
+  cf_free_context(ctx);
+  order->ctx = NULL;
+
+  if (order->flags & CO_CLI)
+    {
+      config_free(order->new_config);
+      order->new_config = NULL;
+    }
 
-  struct cf_context *ctx = cf_new_context(0, order);
-  int ret;
+  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);
index 999e09963acb0d552dec701c9d0d8f396dce33c3..038ef0e77dc165d15044f66468fe448e63f1fd23 100644 (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
index d9bcbad5664b490e261ec29201823817070fdbe1..bad8b0a10738760ecd090910bcc757b5596cf411 100644 (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
 
index 1d858cf16016d24aa336c456e499e9353b543883..112632cab5bc41f4cc643cb60dfd95fd45025080 100644 (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 */
index 3a7f2c907b17c5926f477a1ed8f359be09997f22..04b000c8835e7d6459536ffa1cd3b58d5163a727 100644 (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
index d9d4bb8f10e265e07e107b174561fdc55f09a8bd..1455c474b83f6fd660e090fa5e898fde7619f51c 100644 (file)
@@ -11,6 +11,8 @@
 
 #include "lib/lists.h"
 
+#include <stdlib.h>
+
 /* Resource */
 
 typedef struct resource {
index aa69e71e2a00c1ead47e1dc9c2d599e3da92c70e..2fefb08a7eb0f0df32c36103206938ad2fe89a50 100644 (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);
index 80aea375519daf14a5b7705b9970afd068106e7e..c0470a2336b63a44bec403db0aaafa6fbf1621a0 100644 (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;
index 571909b2609719cc044a83317add93d6d65a9f42..785044734dcbcd4f325070534b174a1a3f143b28 100644 (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 *order, const char *msg, va_list args);
+typedef void (*cf_error_type)(struct conf_order *co, const char *msg, va_list args);
+typedef void (*cf_done_type)(struct conf_order *co);
 
-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_ifs uifs = {
-    .state = &state,
-    .depth = MAX_INCLUDE_DEPTH,
-    .fd = -1,
-  };
-
-  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;
+  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;
+
+  uco->ifs_lp = lp_new_default(p);
+
+  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)
 {
+  struct unix_conf_order *uco = unix_new_conf_order(&root_pool);
+
+  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");
-  struct config *conf = unix_read_config(config_name, unix_cf_error_log);
+  config_parse(&(uco->co));
+}
 
-  if (conf)
-    config_commit(conf, RECONFIG_HARD, 0);
+static void
+unix_cf_error_cli(struct conf_order *co, const char *msg, va_list args)
+{
+  cli_msg(8002, "%s, line %d: %s", co->state->name, co->state->lino, msg, &args);
 }
 
+static void cmd_reconfig_msg(cli *c, int r);
+
+/* Hack for scheduled undo notification */
+cli *cmd_reconfig_stored_cli;
+
 static void
-unix_cf_error_cli(struct conf_order *order, const char *msg, va_list args)
+unix_cf_done_cli(struct conf_order *co)
 {
-  cli_msg(8002, "%s, line %d: %s", order->state->name, order->state->lino, msg, &args);
+  DBG("unix_cf_done_cli\n");
+  UCO;
+  cli_wakeup(uco->cli);
 }
 
-static struct config *
-cmd_read_config(char *name)
+static void
+cmd_done_config(struct unix_conf_order *uco)
 {
-  if (!name)
-    name = config_name;
+  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;
 
-  cli_msg(-2, "Reading configuration from %s", name);
-  return unix_read_config(name, unix_cf_error_cli);
+  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);
+
+  uco->co.buf = name;
+  uco->type = RECONFIG_CHECK;
 
-  cli_msg(20, "Configuration OK");
-  config_free(conf);
+  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;
-
-  int r = config_commit(conf, type, timeout);
+  struct unix_conf_order *uco = unix_new_conf_order(&root_pool);
 
-  if ((r >= 0) && (timeout > 0))
-    {
-      cmd_reconfig_stored_cli = this_cli;
-      cli_msg(-22, "Undo scheduled in %d s", timeout);
-    }
+  uco->co.buf = name;
+  uco->type = type;
+  uco->timeout = 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);
 }