]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
CLI: Improved auto-completion behavior cli-completion
authorPavel Tvrdik <pawel.tvrdik@gmail.com>
Fri, 15 Apr 2016 09:41:08 +0000 (11:41 +0200)
committerPavel Tvrdik <pawel.tvrdik@gmail.com>
Thu, 30 Jun 2016 14:42:04 +0000 (16:42 +0200)
Auto-complete keywords (for, where, filter, ...) and symbol names (names
of protocols, tables, ...). Client can request daemon for list of all
symbols using new cli command `refresh symbols`.

Next changes:
  - Behavior is configured by *.Y files using flags CLI_SF_*
  - The file doc/reply_codes was moved to header file
    client/reply_codes.h.
  - Share birdcl input_read() function code for birdc non-interactive
    mode.
  - BIRD daemon notifies the client about new symbol set.
  - BIRD pushes notification to the client about new symbol set and then
    the client should request a package with all symbols (`refresh
    symbols`).
  - File-based history of previous commands(). In interactive mode in
    birdc is stored history of all commands in ~/.birdc_history file.
  - BIRD daemon sends notification to clients about interface updates
  - Maintains a list of all connected cli clients to daemon. Daemon
    sends to all cli clients notification about interfaces states up and
    down.

17 files changed:
client/birdc.c
client/birdcl.c
client/client.c
client/client.h
client/commands.c
client/reply_codes.h [new file with mode: 0644]
conf/conf.c
conf/conf.h
conf/gen_commands.m4
nest/cli.c
nest/cli.h
nest/cmds.c
nest/cmds.h
nest/config.Y
nest/iface.c
proto/ospf/config.Y
sysdep/unix/main.c

index 8aa01c17ffa7ac2b9c50f352b9e0f39328395e36..f14c333c34a2e3d05611c331b2586776349c275e 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <stdio.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <termios.h>
@@ -20,6 +21,8 @@
 #include "lib/string.h"
 #include "client/client.h"
 
+#define BIRDC_HISTORY_FILENAME         ".birdc_history"
+
 static int input_hidden_end;
 static int prompt_active;
 
@@ -78,7 +81,7 @@ static int
 input_complete(int arg UNUSED, int key UNUSED)
 {
   static int complete_flag;
-  char buf[256];
+  char buf[256] = {};
 
   if (rl_last_func != input_complete)
     complete_flag = 0;
@@ -137,20 +140,48 @@ input_help(int arg, int key UNUSED)
   return 0;
 }
 
+/**
+ * get_rl_history_filepath - return the string with path to readline history file
+ * @buf: buffer for writing
+ * @size: size of @buf including a |NULL| terminator at the end
+ *
+ * This function returns the string with path to readline history file ($HOME/.birdc_history).
+ * A function is copied from readline repository histfile.c from function history_filename()
+ */
+static char *
+get_rl_history_filepath(char *buf, size_t size)
+{
+  const char *home = getenv("HOME");
+  if (!home)
+    return NULL;
+
+  if (snprintf(buf, size, "%s/%s", home, BIRDC_HISTORY_FILENAME) >= size)
+    return NULL;
+
+  return buf;
+}
+
 void
 input_init(void)
 {
-  rl_readline_name = "birdc";
-  rl_add_defun("bird-complete", input_complete, '\t');
-  rl_add_defun("bird-help", input_help, '?');
-  rl_callback_handler_install("bird> ", input_got_line);
+  char path[PATH_MAX];
+  prompt_active = 0;
+
+  if (interactive)
+  {
+    prompt_active = 1;
+
+    rl_readline_name = "birdc";
+    rl_add_defun("bird-complete", input_complete, '\t');
+    rl_add_defun("bird-help", input_help, '?');
+    read_history(get_rl_history_filepath(path, sizeof(path)));
+    rl_callback_handler_install("bird> ", input_got_line);
+  }
 
   // rl_get_screen_size();
   term_lns = LINES;
   term_cls = COLS;
 
-  prompt_active = 1;
-
   // readline library does strange things when stdin is nonblocking.
   // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
   //   DIE("fcntl");
@@ -197,7 +228,10 @@ input_notify(int prompt)
 void
 input_read(void)
 {
-  rl_callback_read_char();
+  if (interactive)
+    rl_callback_read_char();
+  else
+    simple_input_read();
 }
 
 void
@@ -213,9 +247,14 @@ more_end(void)
 void
 cleanup(void)
 {
+  char path[PATH_MAX];
+
   if (init)
     return;
 
+  if (interactive)
+    write_history(get_rl_history_filepath(path, sizeof(path)));
+
   input_hide();
   rl_callback_handler_remove();
 }
index 7b567a9ff01f1051e00bf8a4c73f28d5032f96d5..5fdec87a9beffa3bb9b16db88b60a646ca6c52b0 100644 (file)
@@ -22,8 +22,6 @@
 #include "client/client.h"
 #include "sysdep/unix/unix.h"
 
-#define INPUT_BUF_LEN 2048
-
 struct termios tty_save;
 
 void
@@ -42,59 +40,17 @@ void
 input_notify(int prompt)
 {
   /* No ncurses -> no status to reveal/hide, print prompt manually. */
-  if (!prompt)
+  if (!prompt || !interactive)
     return;
 
-  printf("bird> ");
+  printf("\rbird> ");
   fflush(stdout);
 }
 
-
-static int
-lastnb(char *str, int i)
-{
-  while (i--)
-    if ((str[i] != ' ') && (str[i] != '\t'))
-      return str[i];
-
-  return 0;
-}
-
 void
 input_read(void)
 {
-  char buf[INPUT_BUF_LEN];
-
-  if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0))
-  {
-    putchar('\n');
-    cleanup();
-    exit(0);
-  }
-
-  int l = strlen(buf);
-  if ((l+1) == INPUT_BUF_LEN)
-    {
-      printf("Input too long.\n");
-      return;
-    }
-
-  if (buf[l-1] == '\n')
-    buf[--l] = '\0';
-
-  if (!interactive)
-    printf("%s\n", buf);
-
-  if (l == 0)
-    return;
-
-  if (lastnb(buf, l) == '?')
-    {
-      cmd_help(buf, strlen(buf));
-      return;
-    }
-
-  submit_command(buf);
+  simple_input_read();
 }
 
 static struct termios stored_tty;
index 0d4bdf3e54f17065d3d9b553b37caf00530f173e..cab5601e6e8105515966cdf2d9ee7eb71b48ef41 100644 (file)
 #include "lib/string.h"
 #include "client/client.h"
 #include "sysdep/unix/unix.h"
+#include "client/reply_codes.h"
 
-#define SERVER_READ_BUF_LEN 4096
+#define SERVER_READ_BUF_LEN    4096
+#define INPUT_BUF_LEN          2048
+#define REFRESH_SYMBOLS_CMD    "refresh symbols" /* Name of cli command for retrieve new symbols from daemon */
 
 static char *opt_list = "s:vrl";
 static int verbose, restricted, once;
@@ -47,12 +50,16 @@ static byte server_read_buf[SERVER_READ_BUF_LEN];
 static byte *server_read_pos = server_read_buf;
 
 int init = 1;          /* During intial sequence */
-int busy = 1;          /* Executing BIRD command */
+int busy = 0;          /* Executing BIRD command */
 int interactive;       /* Whether stdin is terminal */
+int welcomed = 0;      /* Was welcome message with BIRD version printed out? */
 
 static int num_lines, skip_input;
 int term_lns, term_cls;
 
+static list symbols;
+static uint longest_symbol_len;
+
 
 /*** Parsing of arguments ***/
 
@@ -107,7 +114,7 @@ parse_args(int argc, char **argv)
          tmp += strlen(tmp);
          *tmp++ = ' ';
        }
-      tmp[-1] = 0;
+      tmp[-1] = 0; /* Put ending null-terminator */
 
       once = 1;
       interactive = 0;
@@ -132,6 +139,12 @@ handle_internal_command(char *cmd)
       puts("Press `?' for context sensitive help.");
       return 1;
     }
+  if (!strncmp(cmd, REFRESH_SYMBOLS_CMD, sizeof(REFRESH_SYMBOLS_CMD)-1))
+  {
+    retrieve_symbols();
+    return 1;
+  }
+
   return 0;
 }
 
@@ -172,6 +185,41 @@ submit_command(char *cmd_raw)
   free(cmd);
 }
 
+static void
+add_to_symbols(int flag, const char *name)
+{
+  struct cli_symbol *sym = malloc(sizeof(struct cli_symbol));
+  sym->flags = flag;
+
+  sym->len = strlen(name);
+  char *name_ = malloc(sym->len + 1);
+  memcpy(name_, name, sym->len + 1);
+  sym->name = name_;
+  add_tail(&symbols, &sym->n);
+
+  if (longest_symbol_len < sym->len)
+    longest_symbol_len = sym->len;
+}
+
+void
+retrieve_symbols(void)
+{
+  /* Purge old symbols */
+  list *syms = cli_get_symbol_list();
+  struct cli_symbol *sym, *next;
+  WALK_LIST_DELSAFE(sym, next, *syms)
+  {
+    rem_node(&sym->n);
+    free((char *) sym->name);
+    free(sym);
+  }
+
+  add_to_symbols(CLI_SF_KW_ALL, "all");
+  add_to_symbols(CLI_SF_KW_OFF, "off");
+
+  submit_server_command(REFRESH_SYMBOLS_CMD);
+}
+
 static void
 init_commands(void)
 {
@@ -198,6 +246,13 @@ init_commands(void)
       exit(0);
     }
 
+  init_list(&symbols);
+  longest_symbol_len = 1; /* Be careful, it's used as denominator! */
+
+  /* In symbol list is a BIRD version for welcome message too */
+  if (interactive)
+    retrieve_symbols();
+
   input_init();
 
   term_lns = (term_lns > 0) ? term_lns : 25;
@@ -262,31 +317,89 @@ server_connect(void)
     DIE("fcntl");
 }
 
+list *
+cli_get_symbol_list(void)
+{
+  return &symbols;
+}
+
+uint
+cli_get_symbol_maxlen(void)
+{
+  return longest_symbol_len;
+}
+
+static void
+process_internal_message(int reply_code, const char *name)
+{
+  u32 flag = 0;
+
+  switch (reply_code)
+  {
+  case RC_BIRD_VERSION_NUM:
+    if (interactive && !welcomed)
+    {
+      welcomed = 1;
+      printf("BIRD %s ready.\n", name);
+    }
+    return;
+
+  case RC_NOTIFY:
+    if (interactive)
+      retrieve_symbols();
+    return;
+
+  /* Symbols */
+  case RC_CONSTANT_NAME:       flag = CLI_SF_CONSTANT; break;
+  case RC_VARIABLE_NAME:       flag = CLI_SF_VARIABLE; break;
+  case RC_FILTER_NAME:         flag = CLI_SF_FILTER; break;
+  case RC_FUNCTION_NAME:       flag = CLI_SF_FUNCTION; break;
+  case RC_PROTOCOL_NAME:       flag = CLI_SF_PROTOCOL; break;
+  case RC_TABLE_NAME:          flag = CLI_SF_TABLE; break;
+  case RC_TEMPLATE_NAME:       flag = CLI_SF_TEMPLATE; break;
+  case RC_INTERFACE_NAME:      flag = CLI_SF_INTERFACE; break;
+  default:
+    printf("Undefined %d: %s", reply_code, name);
+    return;
+  }
+
+  if (flag && name && *name)
+    add_to_symbols(flag, name);
+}
 
 #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0)
 
 static void
 server_got_reply(char *x)
 {
-  int code;
+  int code = 0;
   int len = 0;
 
-  if (*x == '+')                        /* Async reply */
+  if (*x == '+') {                       /* Async reply */
+    busy = 1;
+    input_notify(0);
     PRINTF(len, ">>> %s\n", x+1);
+  }
   else if (x[0] == ' ')                 /* Continuation */
     PRINTF(len, "%s%s\n", verbose ? "     " : "", x+1);
   else if (strlen(x) > 4 &&
            sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 &&
            (x[4] == ' ' || x[4] == '-'))
     {
-      if (code)
-        PRINTF(len, "%s\n", verbose ? x : x+5);
+      if (code >= 3000 && code < 4000)
+      {
+       process_internal_message(code, x+5);
+      }
+      else if (code)
+      {
+       PRINTF(len, "%s\n", verbose ? x : x+5);
+      }
 
       if (x[4] == ' ')
       {
-        busy = 0;
-        skip_input = 0;
-        return;
+       busy = 0;
+       skip_input = 0;
+       return;
       }
     }
   else
@@ -341,6 +454,51 @@ server_read(void)
     }
 }
 
+static int
+lastnb(char *str, int i)
+{
+  while (i--)
+    if ((str[i] != ' ') && (str[i] != '\t'))
+      return str[i];
+
+  return 0;
+}
+
+void
+simple_input_read(void)
+{
+  char buf[INPUT_BUF_LEN];
+
+  if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0))
+  {
+    if (interactive)
+      putchar('\n');
+    cleanup();
+    exit(0);
+  }
+
+  int l = strlen(buf);
+  if ((l+1) == INPUT_BUF_LEN)
+    {
+      printf("Input too long.\n");
+      return;
+    }
+
+  if (buf[l-1] == '\n')
+    buf[--l] = '\0';
+
+  if (l == 0)
+    return;
+
+  if (lastnb(buf, l) == '?')
+    {
+      cmd_help(buf, strlen(buf));
+      return;
+    }
+
+  submit_command(buf);
+}
+
 static void
 select_loop(void)
 {
index f9693def0491ce56bc6ea23cec6dc116b21ff51c..b4a45cc5ce90b7f24f23d307c07c8aba26989eec 100644 (file)
@@ -6,6 +6,8 @@
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
+#ifndef _BIRD_CLIENT_H_
+#define _BIRD_CLIENT_H_
 
 extern int init, busy, interactive;
 extern int term_lns, term_cls;
@@ -33,7 +35,41 @@ char *cmd_expand(char *cmd);
 
 /* client.c */
 
+/* Client Symbol Flags: Types */
+#define CLI_SF_CONSTANT                (1 << 0)
+#define CLI_SF_VARIABLE                (1 << 1)
+#define CLI_SF_FILTER          (1 << 2)
+#define CLI_SF_FUNCTION                (1 << 3)
+#define CLI_SF_PROTOCOL                (1 << 4)
+#define CLI_SF_TABLE           (1 << 5)
+#define CLI_SF_TEMPLATE                (1 << 6)
+#define CLI_SF_INTERFACE       (1 << 7)
+
+#define CLI_SF_OPTIONAL                (1 << 8) /* This node is optional not mandatory */
+#define CLI_SF_PARAMETER       (1 << 9) /* A parameter/word will follow after this node */
+
+/* Client Symbol Flags: Keywords */
+#define CLI_SF_KW_ALL          (1 << 10)
+#define CLI_SF_KW_OFF          (1 << 11)
+
+
+struct cli_symbol
+{
+  node n;
+  const char *name;
+  uint len;
+  u32 flags;                   /* CLI_SF_* */
+};
+
 void submit_command(char *cmd_raw);
 
 /* die() with system error messages */
 #define DIE(x, y...) die(x ": %s", ##y, strerror(errno))
+
+void retrieve_symbols(void);
+void add_keywords_to_symbols(void);
+list *cli_get_symbol_list(void);
+uint cli_get_symbol_maxlen(void);
+void simple_input_read(void);
+
+#endif
index f2134c1bb98a465d8043894c557f7f546cb00b30..13e0a07554d773b0cf214cad89e49861b75ba55d 100644 (file)
 #include "client/client.h"
 
 struct cmd_info {
+  /* use for build cmd tree and cli commands */
   char *command;
   char *args;
   char *help;
+
+  /* only for build tree */
   int is_real_cmd;
+  u32 flags;                   /* Mask of (CLI_SF_*) */
 };
 
 static struct cmd_info command_table[] = {
@@ -27,11 +31,15 @@ static struct cmd_info command_table[] = {
 };
 
 struct cmd_node {
-  struct cmd_node *sibling, *son, **plastson;
-  struct cmd_info *cmd, *help;
-  int len;
-  signed char prio;
-  char token[1];
+  struct cmd_node *sibling;
+  struct cmd_node *son;
+  struct cmd_node **plastson;  /* Helping pointer to son */
+  struct cmd_info *cmd;                /* Short info */
+  struct cmd_info *help;       /* Detailed info */
+  signed char prio;            /* Priority */
+  u32 flags;                   /* Mask of (CLI_SF_*) */
+  int len;                     /* Length of string in token */
+  char token[1];               /* Name of command */
 };
 
 static struct cmd_node cmd_root;
@@ -78,6 +86,7 @@ cmd_build_tree(void)
        old->cmd = cmd;
       else
        old->help = cmd;
+      old->flags |= cmd->flags;
     }
 }
 
@@ -134,6 +143,14 @@ cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len)
   for(m=root->son; m; m=m->sibling)
     if (m->len > len && !memcmp(m->token, cmd, len))   
       cmd_display_help(m->help, m->cmd);
+
+  struct cli_symbol *sym;
+  list *syms = cli_get_symbol_list();
+  WALK_LIST(sym, *syms)
+  {
+    if ((sym->flags & root->flags) && sym->len > len && memcmp(sym->name, cmd, len) == 0)
+      printf("%s\n", sym->name);
+  }
 }
 
 void
@@ -170,14 +187,43 @@ cmd_help(char *cmd, int len)
     cmd_display_help(m->help, m->cmd);
 }
 
+/*
+ * Return length of common prefix of all matches,
+ * Write common prefix string into buf
+ */
+static int
+cmd_merge_match_with_others(int max_common_len, const char *token_name, int token_len, char *buf, int from)
+{
+  if (max_common_len < 0)
+  {
+    /* For a case that we'll have exactly one match */
+    strcpy(buf, token_name + from);
+    max_common_len = token_len - from;
+  }
+  else
+  {
+    int i = 0;
+    while (i < max_common_len && i < token_len - from && buf[i] == token_name[from+i])
+      i++;
+    max_common_len = i;
+  }
+  return max_common_len;
+}
+
+/*
+ * Return length of common prefix of all matches,
+ * Write count of all matches into pcount,
+ * Write common prefix string into buf
+ */
 static int
 cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf)
 {
   struct cmd_node *m;
-  int best, best_prio, i;
+  int max_common_len;
+  int best_prio;
 
   *pcount = 0;
-  best = -1;
+  max_common_len = -1;
   best_prio = -1;
   for(m=root->son; m; m=m->sibling)
     {
@@ -190,25 +236,31 @@ cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, ch
       if (best_prio < m->prio)
        {
          *pcount = 0;
-         best = -1;
+         max_common_len = -1;
        }
 
+      if (max_common_len < 0)
+       best_prio = m->prio;
+
       (*pcount)++;
-      if (best < 0)
-       {
-         strcpy(buf, m->token + len);
-         best = m->len - len;
-         best_prio = m->prio;
-       }
-      else
-       {
-         i = 0;
-         while (i < best && i < m->len - len && buf[i] == m->token[len+i])
-           i++;
-         best = i;
-       }
+      max_common_len = cmd_merge_match_with_others(max_common_len, m->token, m->len, buf, len);
     }
-  return best;
+
+  list *syms = cli_get_symbol_list();
+  struct cli_symbol *sym;
+  WALK_LIST(sym, *syms)
+  {
+    if (!(sym->flags & root->flags))
+      continue;
+
+    if (sym->len < len || memcmp(sym->name, cmd, len))
+      continue;
+
+    (*pcount)++;
+    max_common_len = cmd_merge_match_with_others(max_common_len, sym->name, sym->len, buf, len);
+  }
+
+  return max_common_len;
 }
 
 int
@@ -217,7 +269,7 @@ cmd_complete(char *cmd, int len, char *buf, int again)
   char *start = cmd;
   char *end = cmd + len;
   char *fin;
-  struct cmd_node *n, *m;
+  struct cmd_node *n, *m = NULL;
   char *z;
   int ambig, cnt = 0, common;
 
@@ -227,7 +279,7 @@ cmd_complete(char *cmd, int len, char *buf, int again)
 
   /* Find the context */
   n = &cmd_root;
-  while (cmd < fin && n->son)
+  while (cmd < fin)
     {
       if (isspace(*cmd))
        {
@@ -249,12 +301,39 @@ cmd_complete(char *cmd, int len, char *buf, int again)
        }
       if (!m)
        return -1;
-      n = m;
+
+      /* Try skip a parameter/word */
+      if (m->flags & CLI_SF_PARAMETER)
+      {
+       z = cmd;
+
+       /* Skip spaces before parameter */
+       while (cmd < fin && isspace(*cmd))
+         cmd++;
+
+       /* Skip one parameter/word */
+       while (cmd < fin && !isspace(*cmd))
+         cmd++;
+
+       /* Check ending of parameter */
+       if (isspace(*cmd))
+       {
+         if (m->flags & CLI_SF_OPTIONAL)
+           m = n;
+         continue;
+       }
+       else
+         cmd = z;
+      }
+
+      /* Do not enter to optional command nodes */
+      if (!(m->flags & CLI_SF_OPTIONAL))
+       n = m;
     }
 
-  /* Completion of parameters is not yet supported */
-  if (!n->son)
-    return -1;
+  /* Enter to the last command node */
+  if (m && (m->flags & CLI_SF_PARAMETER))
+    n = m;
 
   /* We know the context, let's try to complete */
   common = cmd_find_common_match(n, fin, end-fin, &cnt, buf);
@@ -282,8 +361,8 @@ cmd_complete(char *cmd, int len, char *buf, int again)
 char *
 cmd_expand(char *cmd)
 {
-  struct cmd_node *n, *m;
-  char *c, *b, *args;
+  struct cmd_node *n, *m, *last_real_cmd = NULL;
+  char *c, *b, *args, *lrc_args = NULL;
   int ambig;
 
   args = c = cmd;
@@ -307,14 +386,28 @@ cmd_expand(char *cmd)
          cmd_list_ambiguous(n, b, c-b);
          return NULL;
        }
+
       args = c;
       n = m;
+
+      if (m->cmd)
+      {
+       last_real_cmd = m;
+       lrc_args = c;
+      }
     }
-  if (!n->cmd)
+
+  if (!n->cmd && !last_real_cmd)
     {
       puts("No such command. Press `?' for help.");
       return NULL;
     }
+
+  if (last_real_cmd && last_real_cmd != n)
+  {
+    n = last_real_cmd;
+    args = lrc_args;
+  }
   b = malloc(strlen(n->cmd->command) + strlen(args) + 1);
   sprintf(b, "%s%s", n->cmd->command, args);
   return b;
diff --git a/client/reply_codes.h b/client/reply_codes.h
new file mode 100644 (file)
index 0000000..20b108b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *     BIRD Client -- Reply codes for communication between client and daemon
+ *
+ *     (c) 2016 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_REPLY_CODES_H_
+#define _BIRD_REPLY_CODES_H_
+
+/*
+Reply codes of BIRD command-line interface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0xxx   Action suceessfully completed
+1xxx   Table entry
+2xxx   Table heading
+3xxx   Internal messages
+8xxx   Run-time error
+9xxx   Parse-time error
+<space>        Continuation
++      Spontaneous printout
+*/
+
+enum reply_code {
+  RC_OK = 0,
+  RC_WELCOME = 1,
+  RC_READING_CONFIGURATION = 2,
+  RC_RECONFIGURED = 3,
+  RC_RECONFIGURATION_IN_PROGRESS = 4,
+  RC_RECONFIGURATION_ALREADY_IN_PROGRESS, QUEUEING = 5,
+  RC_RECONFIGURATION_IGNORED_SHUTTING_DOWN = 6,
+  RC_SHUTDOWN_ORDERED = 7,
+  RC_ALREADY_DISABLED = 8,
+  RC_DISABLED = 9,
+  RC_ALREADY_ENABLED = 10,
+  RC_ENABLED = 11,
+  RC_RESTARTED = 12,
+  RC_STATUS_REPORT = 13,
+  RC_ROUTE_COUNT = 14,
+  RC_RELOADING = 15,
+  RC_ACCESS_RESTRICTED = 16,
+  RC_RECONFIGURATION_ALREADY_IN_PROGRESS_REMOVING_QUEUED_CONFIG = 17,
+  RC_RECONFIGURATION_CONFIRMED = 18,
+  RC_NOTHING_TO_DO_CONFIGURE_UNDO_CONFIRM = 19,
+  RC_CONFIGURATION_OK = 20,
+  RC_UNDO_REQUESTED = 21,
+  RC_UNDO_SCHEDULED = 22,
+  RC_EVALUATION_OF_EXPRESSION = 23,
+  RC_GRACEFUL_RESTART_STATUS_REPORT = 24,
+
+  RC_BIRD_VERSION = 1000,
+  RC_INTERFACE_LIST = 1001,
+  RC_PROTOCOL_LIST = 1002,
+  RC_INTERFACE_ADDRESS = 1003,
+  RC_INTERFACE_FLAGS = 1004,
+  RC_INTERFACE_SUMMARY = 1005,
+  RC_PROTOCOL_DETAILS = 1006,
+  RC_ROUTE_LIST = 1007,
+  RC_ROUTE_DETAILS = 1008,
+  RC_STATIC_ROUTE_LIST = 1009,
+  RC_SYMBOL_LIST = 1010,
+  RC_UPTIME = 1011,
+  RC_ROUTE_EXTENDED_ATTRIBUTE_LIST = 1012,
+  RC_SHOW_OSPF_NEIGHBORS = 1013,
+  RC_SHOW_OSPF = 1014,
+  RC_SHOW_OSPF_INTERFACE = 1015,
+  RC_SHOW_OSPF_STATE_TOPOLOGY = 1016,
+  RC_SHOW_OSPF_LSADB = 1017,
+  RC_SHOW_MEMORY = 1018,
+  RC_SHOW_ROA_LIST = 1019,
+  RC_SHOW_BFD_SESSIONS = 1020,
+  RC_SHOW_RIP_INTERFACE = 1021,
+  RC_SHOW_RIP_NEIGHBORS = 1022,
+
+  RC_BIRD_VERSION_NUM = 3000,
+  RC_TABLE_NAME = 3001,
+  RC_PROTOCOL_NAME = 3002,
+  RC_FILTER_NAME = 3003,
+  RC_FUNCTION_NAME = 3004,
+  RC_CONSTANT_NAME = 3005,
+  RC_VARIABLE_NAME = 3006,
+  RC_TEMPLATE_NAME = 3007,
+  RC_INTERFACE_NAME = 3008,
+  RC_NOTIFY = 3009,            /* Daemon notifies the client about new symbol set */
+
+  RC_REPLY_TOO_LONG = 8000,
+  RC_ROUTE_NOT_FOUND = 8001,
+  RC_CONFIGURATION_FILE_ERROR = 8002,
+  RC_NO_PROTOCOLS_MATCH = 8003,
+  RC_STOPPED_DUE_TO_RECONFIGURATION = 8004,
+  RC_PROTOCOL_IS_DOWN_CANNOT_DUMP = 8005,
+  RC_RELOAD_FAILED = 8006,
+  RC_ACCESS_DENIED = 8007,
+  RC_EVALUATION_RUNTIME_ERROR = 8008,
+
+  RC_COMMAND_TOO_LONG = 9000,
+  RC_PARSE_ERROR = 9001,
+  RC_INVALID_SYMBOL_TYPE = 9002,
+};
+
+#endif
+
index 8d4d28e38e46616eef68ac4fc62b9025aa463f6e..cc09eb719dd4ae61142326282b582b8b4fd6feb0 100644 (file)
@@ -448,6 +448,7 @@ config_undo(void)
 }
 
 extern void cmd_reconfig_undo_notify(void);
+extern void cmd_reconfig_msg(int r);
 
 static void
 config_timeout(struct timer *t)
@@ -458,6 +459,8 @@ config_timeout(struct timer *t)
   int r = config_undo();
   if (r < 0)
     log(L_ERR "Undo request failed");
+
+  cmd_reconfig_msg(r);
 }
 
 void
index 03fecd32a024ad7900bd05443ebba065ff58e718..4f3eea58eff468dadce5c9c18c85615a325d4c6d 100644 (file)
@@ -158,6 +158,9 @@ char *cf_symbol_class_name(struct symbol *sym);
 static inline int cf_symbol_is_constant(struct symbol *sym)
 { return (sym->class & 0xff00) == SYM_CONSTANT; }
 
+static inline int cf_symbol_is_variable(struct symbol *sym)
+{ return (sym->class & 0xff00) == SYM_VARIABLE; }
+
 
 /* Parser */
 
index 3ed21f135bc310d6b32bb29e810546159d7e49aa..d5779ffe4a3a39fdcfb8b857e3e6c0d1a49a9158 100644 (file)
@@ -7,13 +7,13 @@ m4_divert(-1)m4_dnl
 #      Can be freely distributed and used under the terms of the GNU GPL.
 #
 
-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, $5 },
 m4_divert(-1)')
 
-m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 },
+m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1, 0 },
 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, $4 },
 m4_divert(-1)')
 
 # As we are processing C source, we must access all M4 primitives via
index 83e79616f8c42d02f45ff9e16fe352a087072c37..464b5941bcb8b4eb6c7dd5d69bbb0fc111be14ab 100644 (file)
@@ -27,7 +27,8 @@
  * white space character.
  *
  * Reply codes starting with 0 stand for `action successfully completed' messages,
- * 1 means `table entry', 8 `runtime error' and 9 `syntax error'.
+ * 1 means `table entry', 3 means `internal message`, 8 `runtime error' and 9
+ * `syntax error'.
  *
  * Each CLI session is internally represented by a &cli structure and a
  * resource pool containing all resources associated with the connection,
 #include "nest/cli.h"
 #include "conf/conf.h"
 #include "lib/string.h"
+#include "client/reply_codes.h"
 
 pool *cli_pool;
+static list cli_client_list;
 
 static byte *
 cli_alloc_out(cli *c, int size)
@@ -196,13 +199,6 @@ cli_copy_message(cli *c)
   q[-1] = '\n';
 }
 
-static void
-cli_hello(cli *c)
-{
-  cli_printf(c, 1, "BIRD " BIRD_VERSION " ready.");
-  c->cont = NULL;
-}
-
 static void
 cli_free_out(cli *c)
 {
@@ -228,7 +224,7 @@ cli_written(cli *c)
   ev_schedule(c->event);
 }
 
-
+/* cli read hooks variables */
 static byte *cli_rh_pos;
 static uint cli_rh_len;
 static int cli_rh_trick_flag;
@@ -304,18 +300,18 @@ cli *
 cli_new(void *priv)
 {
   pool *p = rp_new(cli_pool, "CLI");
-  cli *c = mb_alloc(p, sizeof(cli));
+  cli *c = mb_allocz(p, sizeof(cli));
 
-  bzero(c, sizeof(cli));
   c->pool = p;
   c->priv = priv;
   c->event = ev_new(p);
   c->event->hook = cli_event;
   c->event->data = c;
-  c->cont = cli_hello;
+  c->cont = NULL;
   c->parser_pool = lp_new(c->pool, 4096);
   c->rx_buf = mb_alloc(c->pool, CLI_RX_BUF_SIZE);
   ev_schedule(c->event);
+  add_tail(&cli_client_list, &c->cli_client_node);
   return c;
 }
 
@@ -407,6 +403,7 @@ cli_free(cli *c)
     c->cleanup(c);
   if (c == cmd_reconfig_stored_cli)
     cmd_reconfig_stored_cli = NULL;
+  rem_node(&c->cli_client_node);
   rfree(c->pool);
 }
 
@@ -422,4 +419,23 @@ cli_init(void)
   cli_pool = rp_new(&root_pool, "CLI");
   init_list(&cli_log_hooks);
   cli_log_inited = 1;
+  init_list(&cli_client_list);
+}
+
+/**
+ * cli_notify_all_clients - send push notification to all cli clients
+ *
+ * Send a notification to all command line clients about some news.
+ * Client could then send a request for pulling symbols.
+ */
+void
+cli_notify_all_clients(void)
+{
+  struct cli *cli;
+  node *n;
+  WALK_LIST2(cli, n, cli_client_list, cli_client_node)
+  {
+    cli_printf(cli, RC_NOTIFY, "");
+    cli_write_trigger(cli);
+  }
 }
index 92f3c3d75262486c0560b20af940abbf6e8bfce2..b1ed821f6c19be1c6c2d89d9db36e860a60b7139 100644 (file)
@@ -27,6 +27,7 @@ struct cli_out {
 
 typedef struct cli {
   node n;                              /* Node in list of all log hooks */
+  node cli_client_node;                        /* Node in list of all cli clients */
   pool *pool;
   void *priv;                          /* Private to sysdep layer */
   byte *rx_buf, *rx_pos, *rx_aux;      /* sysdep */
@@ -65,6 +66,7 @@ void cli_free(cli *);
 void cli_kick(cli *);
 void cli_written(cli *);
 void cli_echo(uint class, byte *msg);
+void cli_notify_all_clients(void);
 
 static inline int cli_access_restricted(void)
 {
index 82fdca6674525375dbbe31daabb1adfdc568a283..1035a5e98f0b2e6fd240e2d940c2616fd1e2a7de 100644 (file)
@@ -15,6 +15,8 @@
 #include "lib/string.h"
 #include "lib/resource.h"
 #include "filter/filter.h"
+#include "client/reply_codes.h"
+#include "nest/iface.h"
 
 extern int shutting_down;
 extern int configuring;
@@ -64,6 +66,56 @@ cmd_show_symbols(struct sym_show_data *sd)
     }
 }
 
+static int
+get_cli_code_for_sym(struct symbol *sym)
+{
+  if (cf_symbol_is_constant(sym))
+    return RC_CONSTANT_NAME;
+
+  if (cf_symbol_is_variable(sym))
+    return RC_VARIABLE_NAME;
+
+  switch (sym->class & 0xff)
+  {
+  case SYM_PROTO:      return RC_PROTOCOL_NAME;
+  case SYM_TEMPLATE:   return RC_TEMPLATE_NAME;
+  case SYM_FUNCTION:   return RC_FUNCTION_NAME;
+  case SYM_FILTER:     return RC_FILTER_NAME;
+  case SYM_TABLE:      return RC_TABLE_NAME;
+  default:
+    log(L_ERR "Undefined class %d of %s", sym->class, sym->name);
+  }
+  return 0;
+}
+
+/**
+ * cmd_send_symbols - send all symbols for auto-completion interactive CLI
+ *
+ * This function sends all known symbols for auto-completion interactive BIRD's
+ * CLI. The first symbol is version of BIRD.
+ */
+void
+cmd_send_symbols(void)
+{
+  int code, pos = 0;
+  struct symbol *sym = NULL;
+
+  cli_msg(RC_BIRD_VERSION_NUM, "%s", BIRD_VERSION);
+
+  while (sym = cf_walk_symbols(config, sym, &pos))
+  {
+    code = get_cli_code_for_sym(sym);
+    cli_msg(code, "%s", sym->name);
+  }
+
+  struct iface *i;
+  WALK_LIST(i, iface_list)
+    if (!(i->flags & IF_SHUTDOWN))
+      cli_msg(RC_INTERFACE_NAME, "\"%s\"", i->name);
+
+  cli_msg(0, "");
+}
+
 static void
 print_size(char *dsc, size_t val)
 {
index 4cf8fb1b8b03469fca0eb18cd46e1b09ac6e855f..7191166c90b6d601219d98ced71fbda3c6f8bd9b 100644 (file)
@@ -15,5 +15,6 @@ struct f_inst;
 
 void cmd_show_status(void);
 void cmd_show_symbols(struct sym_show_data *sym);
+void cmd_send_symbols(void);
 void cmd_show_memory(void);
 void cmd_eval(struct f_inst *expr);
index 2961dafb0c466c48e49bdfc18c6f41f70d8702be..b106499269f2588bc55c018e73907f2b2c1bba37 100644 (file)
@@ -72,6 +72,7 @@ CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
 CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
 CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
 CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
+CF_KEYWORDS(REFRESH)
 
 CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
        RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@@ -467,10 +468,10 @@ CF_CLI(SHOW STATUS,,, [[Show router status]])
 CF_CLI(SHOW MEMORY,,, [[Show memory usage]])
 { cmd_show_memory(); } ;
 
-CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]])
+CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]], CLI_SF_PROTOCOL)
 { proto_apply_cmd($3, proto_cmd_show, 0, 0); } ;
 
-CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]])
+CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]], CLI_SF_PROTOCOL)
 { proto_apply_cmd($4, proto_cmd_show, 0, 1); } ;
 
 optsym:
@@ -488,6 +489,20 @@ 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); } ;
 
+CF_CLI_HELP(SHOW ROUTE FOR, <prefix> | <ip> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
+CF_CLI_HELP(SHOW ROUTE TABLE, <table> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_TABLE)
+CF_CLI_HELP(SHOW ROUTE FILTER, <filter> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_FILTER)
+CF_CLI_HELP(SHOW ROUTE WHERE, <condition> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
+CF_CLI_HELP(SHOW ROUTE ALL, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW ROUTE PRIMARY, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW ROUTE FILTRED, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW ROUTE EXPORT, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
+CF_CLI_HELP(SHOW ROUTE PREEXPORT, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
+CF_CLI_HELP(SHOW ROUTE NOEXPORT, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
+CF_CLI_HELP(SHOW ROUTE PROTOCOL, <protocol> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER | CLI_SF_PROTOCOL)
+CF_CLI_HELP(SHOW ROUTE STATS, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW ROUTE COUNT, ...,, CLI_SF_OPTIONAL)
+
 r_args:
    /* empty */ {
      $$ = cfg_allocz(sizeof(struct rt_show_data));
@@ -567,9 +582,13 @@ export_mode:
 
 
 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]])
+CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]], ~CLI_SF_OPTIONAL)
 { cmd_show_symbols($3); } ;
 
+CF_CLI_HELP(REFRESH, symbols, [[Check out new symbols from daemon for autocomplete in BIRD Client]])
+CF_CLI(REFRESH SYMBOLS,,, [[Check out new symbols from daemon for autocomplete in BIRD Client]])
+{ cmd_send_symbols(); } ;
+
 sym_args:
    /* empty */ {
      $$ = cfg_allocz(sizeof(struct sym_show_data));
@@ -601,11 +620,11 @@ CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
 CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
 { protos_dump_all(); cli_msg(0, ""); } ;
 
-CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
+CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]], CLI_SF_CONSTANT | CLI_SF_VARIABLE)
 { cmd_eval($2); } ;
 
 CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
-CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug | trace | info | remote | warning | error | auth }) [<buffer-size>], [[Control echoing of log messages]]) {
+CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug | trace | info | remote | warning | error | auth }) [<buffer-size>], [[Control echoing of log messages]], CLI_SF_KW_ALL | CLI_SF_KW_OFF) {
   cli_set_log_echo(this_cli, $2, $3);
   cli_msg(0, "");
 } ;
@@ -624,25 +643,25 @@ echo_size:
    }
  ;
 
-CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]])
+CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
 { proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ;
-CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]])
+CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
 { proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ;
-CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
+CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
 { proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ;
-CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
+CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
 { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
-CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
+CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
 { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_IN); } ;
-CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]])
+CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL)
 { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
 
 CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]])
-CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | interfaces | events | packets }), [[Control protocol debugging via BIRD logs]])
+CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | interfaces | events | packets }), [[Control protocol debugging via BIRD logs]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL | CLI_SF_KW_OFF | CLI_SF_PARAMETER)
 { proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
 
 CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
-CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]])
+CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]], CLI_SF_PROTOCOL | CLI_SF_KW_ALL | CLI_SF_KW_OFF | CLI_SF_PARAMETER)
 { proto_apply_cmd($2, proto_cmd_mrtdump, 1, $3); } ;
 
 CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
index 00af5052926ff8343cafbaf812224fd6648c073a..2c89e31ee06961e2da0f5be50a05a26b116d3a72 100644 (file)
@@ -206,27 +206,32 @@ if_notify_change(unsigned c, struct iface *i)
 #endif
 
   if (c & IF_CHANGE_DOWN)
+  {
     neigh_if_down(i);
 
-  if (c & IF_CHANGE_DOWN)
     WALK_LIST(a, i->addrs)
       {
        a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
        ifa_notify_change_(IF_CHANGE_DOWN, a);
       }
 
+    cli_notify_all_clients();
+  }
+
   WALK_LIST(p, proto_list)
     if_send_notify(p, c, i);
 
   if (c & IF_CHANGE_UP)
+  {
     WALK_LIST(a, i->addrs)
       {
        a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
        ifa_notify_change_(IF_CHANGE_UP, a);
       }
 
-  if (c & IF_CHANGE_UP)
     neigh_if_up(i);
+    cli_notify_all_clients();
+  }
 
   if ((c & (IF_CHANGE_UP | IF_CHANGE_DOWN | IF_CHANGE_LINK)) == IF_CHANGE_LINK)
     neigh_if_link(i);
index 297774b5a39b840aec33ae151f894ce13fb7bf9e..522ab52b20790d9eeeea8a2be6b742e10acc8c9e 100644 (file)
@@ -417,13 +417,13 @@ CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEM
 CF_ADDTO(dynamic_attr, OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID | EAF_TEMP, T_QUAD, EA_OSPF_ROUTER_ID); })
 
 CF_CLI_HELP(SHOW OSPF, ..., [[Show information about OSPF protocol]]);
-CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol XXX]])
+CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol XXX]], CLI_SF_PROTOCOL)
 { ospf_sh(proto_get_named($3, &proto_ospf)); };
 
-CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]])
+CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]], CLI_SF_PROTOCOL | CLI_SF_INTERFACE | CLI_SF_PARAMETER)
 { ospf_sh_neigh(proto_get_named($4, &proto_ospf), $5); };
 
-CF_CLI(SHOW OSPF INTERFACE, optsym opttext, [<name>] [\"<interface>\"], [[Show information about interface]])
+CF_CLI(SHOW OSPF INTERFACE, optsym opttext, [<name>] [\"<interface>\"], [[Show information about interface]], CLI_SF_PROTOCOL | CLI_SF_INTERFACE | CLI_SF_PARAMETER)
 { ospf_sh_iface(proto_get_named($4, &proto_ospf), $5); };
 
 CF_CLI_HELP(SHOW OSPF TOPOLOGY, [all] [<name>], [[Show information about OSPF network topology]])
@@ -431,20 +431,27 @@ CF_CLI_HELP(SHOW OSPF TOPOLOGY, [all] [<name>], [[Show information about OSPF ne
 CF_CLI(SHOW OSPF TOPOLOGY, optsym opttext, [<name>], [[Show information about reachable OSPF network topology]])
 { ospf_sh_state(proto_get_named($4, &proto_ospf), 0, 1); };
 
-CF_CLI(SHOW OSPF TOPOLOGY ALL, optsym opttext, [<name>], [[Show information about all OSPF network topology]])
+CF_CLI(SHOW OSPF TOPOLOGY ALL, optsym opttext, [<name>], [[Show information about all OSPF network topology]], CLI_SF_PROTOCOL)
 { ospf_sh_state(proto_get_named($5, &proto_ospf), 0, 0); };
 
 CF_CLI_HELP(SHOW OSPF STATE, [all] [<name>], [[Show information about OSPF network state]])
 
-CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about reachable OSPF network state]])
+CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about reachable OSPF network state]], CLI_SF_PROTOCOL)
 { ospf_sh_state(proto_get_named($4, &proto_ospf), 1, 1); };
 
-CF_CLI(SHOW OSPF STATE ALL, optsym opttext, [<name>], [[Show information about all OSPF network state]])
+CF_CLI(SHOW OSPF STATE ALL, optsym opttext, [<name>], [[Show information about all OSPF network state]], CLI_SF_PROTOCOL)
 { ospf_sh_state(proto_get_named($5, &proto_ospf), 1, 0); };
 
-CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]]);
-CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [lsid <id>] [self | router <id>] [<proto>], [[Show content of OSPF LSA database]])
+CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]])
+CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [lsid <id>] [self | router <id>] [<proto>], [[Show content of OSPF LSA database]], CLI_SF_PROTOCOL)
 { ospf_sh_lsadb($4); };
+CF_CLI_HELP(SHOW OSPF LSADB GLOBAL, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW OSPF LSADB AREA, <id> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
+CF_CLI_HELP(SHOW OSPF LSADB LINK, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW OSPF LSADB TYPE, <num> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
+CF_CLI_HELP(SHOW OSPF LSADB LSID, <num> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
+CF_CLI_HELP(SHOW OSPF LSADB SELF, ...,, CLI_SF_OPTIONAL)
+CF_CLI_HELP(SHOW OSPF LSADB ROUTER, <num> ...,, CLI_SF_OPTIONAL | CLI_SF_PARAMETER)
 
 lsadb_args:
    /* empty */ {
index 1f47680e53351cf398118194cb653368a9e2db5d..6b12e7d1970894e85c04215b5c19fb240242d8d5 100644 (file)
@@ -33,6 +33,7 @@
 #include "nest/locks.h"
 #include "conf/conf.h"
 #include "filter/filter.h"
+#include "client/reply_codes.h"
 
 #include "unix.h"
 #include "krt.h"
@@ -269,12 +270,12 @@ cmd_check_config(char *name)
   config_free(conf);
 }
 
-static void
+void
 cmd_reconfig_msg(int r)
 {
   switch (r)
     {
-    case CONF_DONE:    cli_msg( 3, "Reconfigured"); break;
+    case CONF_DONE:    cli_msg( 3, "Reconfigured"); cli_msg(RC_NOTIFY, ""); cli_msg(19, "Nothing to do"); 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;