Commit 3d1d8fc1 authored by Maria Matejka's avatar Maria Matejka
Browse files

Unlocked CLI parser

Now the CLI locks at command execution and the commands are parsed
unlocked. This allows for re-enabling the long-running command split.

Fixed also a handful of other bugs in the coroutine system.
parent 3c6d356e
Loading
Loading
Loading
Loading
+18 −6
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
#include <stdarg.h>

#undef LOCAL_DEBUG
#define LOCAL_DEBUG

#include "nest/bird.h"
#include "nest/route.h"
@@ -110,6 +111,7 @@ config_parse(struct conf_order *order)
  DBG("Parsing configuration named `%s'\n", order->state->name);

  if (!order->new_config)
    THE_BIRD_LOCKED_NOFAIL
      order->new_config = config_alloc(order->pool, order->lp);

  struct cf_context *ctx = cf_new_context(order);
@@ -150,25 +152,35 @@ cli_parse(struct conf_order *order)
{
  DBG("Parsing command line\n");

  struct config cc = {}, *gc = config;
  struct config cc = {}, *gc;
  ASSERT_DIE(order->pool);
  ASSERT_DIE(order->lp);

  cc.pool = rp_new(order->pool, "CLI Dummy Config");
  cc.mem = order->lp;
  THE_BIRD_LOCKED_NOFAIL
  {
    config_add_obstacle(gc = config);
    cc.cli_sym = &(config->sym_hash);
  }

  config_add_obstacle(gc);
  cc.cli_sym = &(gc->sym_hash);
  init_list(&cc.symbols);

  order->new_config = &cc;

  struct cf_context *ctx = cf_new_context(order);

  /* We don't need to handle the parse errors in any specific ways,
   * we just do nothing in that case but we have to set the return point. */
  if (!setjmp(ctx->jmpbuf))
    cfx_parse(ctx, ctx->yyscanner);

  cf_free_context(ctx);
  config_free(&cc);

  THE_BIRD_LOCKED_NOFAIL
    config_del_obstacle(gc);

  order->new_config = NULL;
  order->ctx = NULL;
}
+1 −0
Original line number Diff line number Diff line
@@ -137,6 +137,7 @@ extern DOMAIN(the_bird) the_bird_domain;
#define the_bird_unlock()	do_unlock(the_bird_domain.the_bird, &locking_stack.the_bird)

#define THE_BIRD_LOCKED(cleanup)  LOCKED_DO(the_bird, the_bird_domain, cleanup)
#define THE_BIRD_LOCKED_NOFAIL	  LOCKED_DO_NOFAIL(the_bird, the_bird_domain)

#define assert_bird_lock() ASSERT_DIE(SUPER_LOCK(the_bird).the_bird == the_bird_domain.the_bird)

+2 −0
Original line number Diff line number Diff line
@@ -358,6 +358,8 @@ cli_connect(sock *s, byte *buf UNUSED, uint size UNUSED)
  rmove(s, c->pool);
  sk_set_rbsize(s, 1024);

  s->flags |= SKF_NOLOCK;

  cli_hello(c);

  sk_schedule_rx(s);
+26 −21
Original line number Diff line number Diff line
@@ -560,13 +560,13 @@ bfd_opts:
CF_CLI_HELP(SHOW, ..., [[Show status information]])

CF_CLI(SHOW STATUS,,, [[Show router status]])
{ cmd_show_status(); } ;
{ THE_BIRD_LOCKED_NOFAIL cmd_show_status(); } ;

CF_CLI(SHOW MEMORY,,, [[Show memory usage]])
{ cmd_show_memory(); } ;
{ THE_BIRD_LOCKED_NOFAIL cmd_show_memory(); } ;

CF_CLI(SHOW THREADS,,, [[Show thread information]])
{ cmd_show_threads(); } ;
{ THE_BIRD_LOCKED_NOFAIL cmd_show_threads(); } ;

CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]])
{ proto_apply_cmd($3, proto_cmd_show, 0, 0); } ;
@@ -580,21 +580,26 @@ optproto:
 ;

CF_CLI(SHOW INTERFACES,,, [[Show network interfaces]])
{ if_show(); } ;
{ THE_BIRD_LOCKED_NOFAIL if_show(); } ;

CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]])
{ if_show_summary(); } ;
{ THE_BIRD_LOCKED_NOFAIL if_show_summary(); } ;

CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
{ rt_show($3); } ;
{
  /* Filtered routes are neither exported nor have sensible ordering */
  if ($3->filtered && ($3->export_mode || $3->primary_only))
    cf_error("Incompatible show route options");

  rt_show(ctx->cfg_mem, $3);
};

r_args:
   /* empty */ {
     $$ = cfg_allocz(sizeof(struct rt_show_data));
     init_list(&($$->tables));
     $$->filter = FILTER_ACCEPT;
     $$->ctx = ctx;
   }
 | r_args net_any {
     $$ = $1;
@@ -610,7 +615,7 @@ r_args:
 | r_args TABLE CF_SYM_KNOWN {
     cf_assert_symbol(ctx, $3, SYM_TABLE);
     $$ = $1;
     rt_show_add_table($$, $3->table->table);
     rt_show_add_table(ctx->cfg_mem, $$, $3->table->table);
     $$->tables_defined_by = RSD_TDB_DIRECT;
   }
 | r_args TABLE ALL {
@@ -622,17 +627,17 @@ r_args:
       cf_error("All tables have been already released as we're shutting down");
     }
     WALK_LIST(t, config->tables)
       rt_show_add_table($$, t->table);
       rt_show_add_table(ctx->cfg_mem, $$, t->table);
     $$->tables_defined_by = RSD_TDB_ALL;
   }
 | r_args IMPORT TABLE channel_arg {
     if (!$4->in_table) cf_error("No import table in channel %s.%s", $4->proto->name, $4->name);
     rt_show_add_table($$, $4->in_table);
     rt_show_add_table(ctx->cfg_mem, $$, $4->in_table);
     $$->tables_defined_by = RSD_TDB_DIRECT;
   }
 | r_args EXPORT TABLE channel_arg {
     if (!$4->out_table) cf_error("No export table in channel %s.%s", $4->proto->name, $4->name);
     rt_show_add_table($$, $4->out_table);
     rt_show_add_table(ctx->cfg_mem, $$, $4->out_table);
     $$->tables_defined_by = RSD_TDB_DIRECT;
   }
 | r_args FILTER filter {
@@ -775,7 +780,7 @@ channel_arg:

CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]])
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]])
{ cmd_show_symbols($3); } ;
{ THE_BIRD_LOCKED_NOFAIL cmd_show_symbols($3); } ;

sym_args:
   /* empty */ {
@@ -792,26 +797,26 @@ sym_args:

CF_CLI_HELP(DUMP, ..., [[Dump debugging information]])
CF_CLI(DUMP RESOURCES,,, [[Dump all allocated resource]])
{ rdump(&root_pool); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL rdump(&root_pool); cli_msg(0, ""); } ;
/*
CF_CLI(DUMP EVENTS,,, [[Dump event log]])
{ io_log_dump(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL io_log_dump(); cli_msg(0, ""); } ;
*/
CF_CLI(DUMP INTERFACES,,, [[Dump interface information]])
{ if_dump_all(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL if_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP NEIGHBORS,,, [[Dump neighbor cache]])
{ neigh_dump_all(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL neigh_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ATTRIBUTES,,, [[Dump attribute cache]])
{ rta_dump_all(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL rta_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
{ rt_dump_all(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL rt_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL protos_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
{ filters_dump_all(); cli_msg(0, ""); } ;
{ THE_BIRD_LOCKED_NOFAIL filters_dump_all(); cli_msg(0, ""); } ;

CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
{ cmd_eval(f_linearize(ctx, $2)); } ;
{ THE_BIRD_LOCKED_NOFAIL cmd_eval(f_linearize(ctx, $2)); } ;

CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])

+79 −41
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
 */

#undef LOCAL_DEBUG
#define LOCAL_DEBUG

#include "nest/bird.h"
#include "nest/protocol.h"
@@ -251,17 +252,34 @@ channel_schedule_feed(struct channel *c, int initial)
  c->export_state = ES_FEEDING;
  c->refeeding = !initial;

  c->refeed_lastmod_min = c->refeed_lastmod_max = 0;
  c->feed_done_lastmod = c->feed_seen_lastmod = 0;

  ev_schedule(c->feed_event);
}

static void
channel_feed_finished(struct channel *c, _Bool partial)
{
  c->export_state = ES_READY;
  channel_log_state_change(c);

  if (!partial && c->proto->feed_end)
    c->proto->feed_end(c);

  if (c->table->total_updates > c->last_export)
    channel_run_exports(c);
}

static void
channel_feed_loop(void *ptr)
{
  struct channel *c = ptr;
  _Bool loop = 1;
  _Bool wait = 1;
  _Bool partial = 0;

  /* There is only one case when this event may be cancelled
   * and it is when the channel is going down. */

  /* The protocol has not yet started fully, wait for the proto_event to complete */
  while (loop && wait)
@@ -277,48 +295,59 @@ channel_feed_loop(void *ptr)
  THE_BIRD_LOCKED({ return; })
  {
    ASSERT(!c->feed_active);
    if (c->proto->feed_begin)

    if (c->feed_done_lastmod > 0)
      partial = 1;

    if (!partial && c->proto->feed_begin)
      c->proto->feed_begin(c, !c->refeeding);

    DBG("Channel %s.%s feed loop init: last_export=%lu, total_updates=%lu\n",
	c->proto->name, c->name, c->last_export, c->table->total_updates);

    /* Jump over empty partial feed */
    if (partial && (c->last_export == c->table->total_updates))
    {
      loop = 0;
      channel_feed_finished(c, partial);
    }
    else
      rt_feed_channel_prepare(c);

    DBG("Channel %s.%s feed loop init: partial=%d, loop=%d\n", c->proto->name, c->name, partial, loop);
  }

  _Bool repeated_feed = 0;

  while (loop)
  {
    THE_BIRD_LOCKED({ return; })
      loop = rt_feed_channel(c);

  /* Reset export limit if the feed ended with acceptable number of exported routes */
    {
      struct channel_limit *l = &c->out_limit;
  THE_BIRD_LOCKED({ return; })
    if (loop = c->refeeding &&
      /* Feed is finished? */
      if (!(loop = rt_feed_channel(c)))
      {
	/* Reset export limit if the feed ended with acceptable number of exported routes */
	if (!repeated_feed &&
	    (loop = c->refeeding &&
	    (l->state == PLS_BLOCKED) &&
	    (c->refeed_count <= l->limit) &&
	(c->stats.exp_routes <= l->limit))
	    (c->stats.exp_routes <= l->limit)))
	{
	  log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit);
	  channel_reset_limit(&c->out_limit);

	  /* Continue in feed - it will process routing table again from beginning */
	  repeated_feed = 1;
	  c->refeed_count = 0;
	  c->feed_done_lastmod = c->feed_seen_lastmod = 0;
	  rt_feed_channel_prepare(c);
	}

  while (loop)
    THE_BIRD_LOCKED({ return; })
      loop = rt_feed_channel(c);

  THE_BIRD_LOCKED({ return; })
  {
    // DBG("Feeding protocol %s finished\n", p->name);
    c->export_state = ES_READY;
    channel_log_state_change(c);
    if (c->table->total_updates > c->last_export)
      channel_run_exports(c);

    // proto_log_state_change(p);

    if (c->proto->feed_end)
      c->proto->feed_end(c);
	/* Otherwise we're really finished */
	else
	  channel_feed_finished(c, partial);
      }
    }
  }
}

@@ -2183,6 +2212,7 @@ proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, in
  if (restricted && cli_access_restricted())
    return;

  THE_BIRD_LOCKED_NOFAIL
    if (ps.patt)
      proto_apply_cmd_patt(ps.ptr, cmd, arg);
    else
@@ -2206,15 +2236,23 @@ proto_get_named(struct cf_context *ctx, struct symbol *sym, struct protocol *pr)
  else
  {
    p = NULL;
    WALK_LIST(q, proto_list)
    _Bool multiple = 1;

    THE_BIRD_LOCKED_NOFAIL WALK_LIST(q, proto_list)
      if ((q->proto == pr) && (q->proto_state != PS_DOWN))
      {
	if (p)
	  cf_error("There are multiple %s protocols running", pr->name);
	{
	  multiple = 1;
	  break;
	}
	p = q;
      }

    if (!p)
      cf_error("There is no %s protocol running", pr->name);
    else if (multiple)
      cf_error("There are multiple %s protocols running", pr->name);
  }

  return p;
Loading