]> git.proxmox.com Git - mirror_frr.git/blobdiff - lib/command.c
lib: remove remaining struct zlog * args
[mirror_frr.git] / lib / command.c
index 4ac1d1abe5ffb492b7ef993fac9be198ba3e339b..597e5a06c88b2e112e5c74a6066c354474441042 100644 (file)
 
 DEFINE_MTYPE(       LIB, HOST,       "Host config")
 DEFINE_MTYPE(       LIB, STRVEC,     "String vector")
-DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
+DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
+DEFINE_MTYPE_STATIC(LIB, CMD_DESC,   "Command Token Text")
+DEFINE_MTYPE_STATIC(LIB, CMD_TEXT,   "Command Token Help")
+DEFINE_MTYPE(       LIB, CMD_ARG,    "Command Argument")
 
 /* Command vector which includes some level of command lists. Normally
    each daemon maintains each own cmdvec. */
@@ -291,27 +294,6 @@ cmd_free_strvec (vector v)
   vector_free (v);
 }
 
-char *
-cmd_concat_strvec (vector v)
-{
-  size_t strsize = 0;
-  for (unsigned int i = 0; i < vector_active (v); i++)
-    if (vector_slot (v, i))
-      strsize += strlen ((char *) vector_slot (v, i)) + 1;
-
-  if (strsize == 0)
-    return XSTRDUP (MTYPE_TMP, "");
-
-  char *concatenated = calloc (sizeof (char), strsize);
-  for (unsigned int i = 0; i < vector_active (v); i++)
-  {
-    strlcat (concatenated, (char *) vector_slot (v, i), strsize);
-    strlcat (concatenated, " ", strsize);
-  }
-
-  return concatenated;
-}
-
 /* Return prompt character of specified node. */
 const char *
 cmd_prompt (enum node_type node)
@@ -322,6 +304,272 @@ cmd_prompt (enum node_type node)
   return cnode->prompt;
 }
 
+static bool
+cmd_nodes_link (struct graph_node *from, struct graph_node *to)
+{
+  for (size_t i = 0; i < vector_active (from->to); i++)
+    if (vector_slot (from->to, i) == to)
+      return true;
+  return false;
+}
+
+static bool cmd_nodes_equal (struct graph_node *ga, struct graph_node *gb);
+
+/* returns a single node to be excluded as "next" from iteration
+ * - for JOIN_TKN, never continue back to the FORK_TKN
+ * - in all other cases, don't try the node itself (in case of "...")
+ */
+static inline struct graph_node *
+cmd_loopstop(struct graph_node *gn)
+{
+  struct cmd_token *tok = gn->data;
+  if (tok->type == JOIN_TKN)
+    return tok->forkjoin;
+  else
+    return gn;
+}
+
+static bool
+cmd_subgraph_equal (struct graph_node *ga, struct graph_node *gb,
+                    struct graph_node *a_join)
+{
+  size_t i, j;
+  struct graph_node *a_fork, *b_fork;
+  a_fork = cmd_loopstop (ga);
+  b_fork = cmd_loopstop (gb);
+
+  if (vector_active (ga->to) != vector_active (gb->to))
+    return false;
+  for (i = 0; i < vector_active (ga->to); i++)
+    {
+      struct graph_node *cga = vector_slot (ga->to, i);
+
+      for (j = 0; j < vector_active (gb->to); j++)
+        {
+          struct graph_node *cgb = vector_slot (gb->to, i);
+
+          if (cga == a_fork && cgb != b_fork)
+            continue;
+          if (cga == a_fork && cgb == b_fork)
+            break;
+
+          if (cmd_nodes_equal (cga, cgb))
+            {
+              if (cga == a_join)
+                break;
+              if (cmd_subgraph_equal (cga, cgb, a_join))
+                break;
+            }
+        }
+      if (j == vector_active (gb->to))
+        return false;
+    }
+  return true;
+}
+
+/* deep compare -- for FORK_TKN, the entire subgraph is compared.
+ * this is what's needed since we're not currently trying to partially
+ * merge subgraphs */
+static bool
+cmd_nodes_equal (struct graph_node *ga, struct graph_node *gb)
+{
+  struct cmd_token *a = ga->data, *b = gb->data;
+
+  if (a->type != b->type || a->allowrepeat != b->allowrepeat)
+    return false;
+  if (a->type < SPECIAL_TKN && strcmp (a->text, b->text))
+    return false;
+  /* one a ..., the other not. */
+  if (cmd_nodes_link (ga, ga) != cmd_nodes_link (gb, gb))
+    return false;
+
+  switch (a->type)
+    {
+    case RANGE_TKN:
+      return a->min == b->min && a->max == b->max;
+
+    case FORK_TKN:
+      /* one is keywords, the other just option or selector ... */
+      if (cmd_nodes_link (a->forkjoin, ga) != cmd_nodes_link (b->forkjoin, gb))
+        return false;
+      if (cmd_nodes_link (ga, a->forkjoin) != cmd_nodes_link (gb, b->forkjoin))
+        return false;
+      return cmd_subgraph_equal (ga, gb, a->forkjoin);
+
+    default:
+      return true;
+    }
+}
+
+static void
+cmd_fork_bump_attr (struct graph_node *gn, struct graph_node *join,
+                    u_char attr)
+{
+  size_t i;
+  struct cmd_token *tok = gn->data;
+  struct graph_node *stop = cmd_loopstop (gn);
+
+  tok->attr = attr;
+  for (i = 0; i < vector_active (gn->to); i++)
+    {
+      struct graph_node *next = vector_slot (gn->to, i);
+      if (next == stop || next == join)
+        continue;
+      cmd_fork_bump_attr (next, join, attr);
+    }
+}
+
+/* move an entire subtree from the temporary graph resulting from
+ * parse() into the permanent graph for the command node.
+ *
+ * this touches rather deeply into the graph code unfortunately.
+ */
+static void
+cmd_reparent_tree (struct graph *fromgraph, struct graph *tograph,
+                   struct graph_node *node)
+{
+  struct graph_node *stop = cmd_loopstop (node);
+  size_t i;
+
+  for (i = 0; i < vector_active (fromgraph->nodes); i++)
+    if (vector_slot (fromgraph->nodes, i) == node)
+      {
+        /* agressive iteration punching through subgraphs - may hit some
+         * nodes twice.  reparent only if found on old graph */
+        vector_unset (fromgraph->nodes, i);
+        vector_set (tograph->nodes, node);
+        break;
+      }
+
+  for (i = 0; i < vector_active (node->to); i++)
+    {
+      struct graph_node *next = vector_slot (node->to, i);
+      if (next != stop)
+        cmd_reparent_tree (fromgraph, tograph, next);
+    }
+}
+
+static void
+cmd_free_recur (struct graph *graph, struct graph_node *node,
+                struct graph_node *stop)
+{
+  struct graph_node *next, *nstop;
+
+  for (size_t i = vector_active (node->to); i; i--)
+    {
+      next = vector_slot (node->to, i - 1);
+      if (next == stop)
+        continue;
+      nstop = cmd_loopstop (next);
+      if (nstop != next)
+        cmd_free_recur (graph, next, nstop);
+      cmd_free_recur (graph, nstop, stop);
+    }
+  graph_delete_node (graph, node);
+}
+
+static void
+cmd_free_node (struct graph *graph, struct graph_node *node)
+{
+  struct cmd_token *tok = node->data;
+  if (tok->type == JOIN_TKN)
+    cmd_free_recur (graph, tok->forkjoin, node);
+  graph_delete_node (graph, node);
+}
+
+/* recursive graph merge.  call with
+ *   old ~= new
+ * (which holds true for old == START_TKN, new == START_TKN)
+ */
+static void
+cmd_merge_nodes (struct graph *oldgraph, struct graph *newgraph,
+                 struct graph_node *old, struct graph_node *new,
+                 int direction)
+{
+  struct cmd_token *tok;
+  struct graph_node *old_skip, *new_skip;
+  old_skip = cmd_loopstop (old);
+  new_skip = cmd_loopstop (new);
+
+  assert (direction == 1 || direction == -1);
+
+  tok = old->data;
+  tok->refcnt += direction;
+
+  size_t j, i;
+  for (j = 0; j < vector_active (new->to); j++)
+    {
+      struct graph_node *cnew = vector_slot (new->to, j);
+      if (cnew == new_skip)
+        continue;
+
+      for (i = 0; i < vector_active (old->to); i++)
+        {
+          struct graph_node *cold = vector_slot (old->to, i);
+          if (cold == old_skip)
+            continue;
+
+          if (cmd_nodes_equal (cold, cnew))
+            {
+              struct cmd_token *told = cold->data, *tnew = cnew->data;
+
+              if (told->type == END_TKN)
+                {
+                  if (direction < 0)
+                    {
+                      graph_delete_node (oldgraph, vector_slot (cold->to, 0));
+                      graph_delete_node (oldgraph, cold);
+                    }
+                  else
+                    /* force no-match handling to install END_TKN */
+                    i = vector_active (old->to);
+                  break;
+                }
+
+              /* the entire fork compared as equal, we continue after it. */
+              if (told->type == FORK_TKN)
+                {
+                  if (tnew->attr < told->attr && direction > 0)
+                    cmd_fork_bump_attr (cold, told->forkjoin, tnew->attr);
+                  /* XXX: no reverse bump on uninstall */
+                  told = (cold = told->forkjoin)->data;
+                  tnew = (cnew = tnew->forkjoin)->data;
+                }
+              if (tnew->attr < told->attr)
+                told->attr = tnew->attr;
+
+              cmd_merge_nodes (oldgraph, newgraph, cold, cnew, direction);
+              break;
+            }
+        }
+      /* nothing found => add new to old */
+      if (i == vector_active (old->to) && direction > 0)
+        {
+          assert (vector_count (cnew->from) ==
+                          cmd_nodes_link (cnew, cnew) ? 2 : 1);
+          graph_remove_edge (new, cnew);
+
+          cmd_reparent_tree (newgraph, oldgraph, cnew);
+
+          graph_add_edge (old, cnew);
+        }
+    }
+
+  if (!tok->refcnt)
+    cmd_free_node (oldgraph, old);
+}
+
+void
+cmd_merge_graphs (struct graph *old, struct graph *new, int direction)
+{
+  assert (vector_active (old->nodes) >= 1);
+  assert (vector_active (new->nodes) >= 1);
+
+  cmd_merge_nodes (old, new,
+                   vector_slot (old->nodes, 0), vector_slot (new->nodes, 0),
+                   direction);
+}
+
 /* Install a command into a node. */
 void
 install_element (enum node_type ntype, struct cmd_element *cmd)
@@ -355,13 +603,65 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
 
   assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern));
 
-  command_parse_format (cnode->cmdgraph, cmd);
+  struct graph *graph = graph_new();
+  struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+  graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token);
+
+  command_parse_format (graph, cmd);
+  cmd_merge_graphs (cnode->cmdgraph, graph, +1);
+  graph_delete_graph (graph);
+
   vector_set (cnode->cmd_vector, cmd);
 
   if (ntype == VIEW_NODE)
     install_element (ENABLE_NODE, cmd);
 }
 
+void
+uninstall_element (enum node_type ntype, struct cmd_element *cmd)
+{
+  struct cmd_node *cnode;
+
+  /* cmd_init hasn't been called */
+  if (!cmdvec)
+    {
+      fprintf (stderr, "%s called before cmd_init, breakage likely\n",
+               __func__);
+      return;
+    }
+
+  cnode = vector_slot (cmdvec, ntype);
+
+  if (cnode == NULL)
+    {
+      fprintf (stderr, "Command node %d doesn't exist, please check it\n",
+               ntype);
+      exit (EXIT_FAILURE);
+    }
+
+  if (hash_release (cnode->cmd_hash, cmd) == NULL)
+    {
+      fprintf (stderr,
+               "Trying to uninstall non-installed command (node %d):\n%s\n",
+               ntype, cmd->string);
+      return;
+    }
+
+  vector_unset_value (cnode->cmd_vector, cmd);
+
+  struct graph *graph = graph_new();
+  struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+  graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token);
+
+  command_parse_format (graph, cmd);
+  cmd_merge_graphs (cnode->cmdgraph, graph, -1);
+  graph_delete_graph (graph);
+
+  if (ntype == VIEW_NODE)
+    uninstall_element (ENABLE_NODE, cmd);
+}
+
+
 static const unsigned char itoa64[] =
 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
@@ -523,7 +823,7 @@ compare_completions (const void *fst, const void *snd)
  * @param completions linked list of cmd_token
  * @return deduplicated and sorted vector with
  */
-static vector
+vector
 completions_to_vec (struct list *completions)
 {
   vector comps = vector_init (VECTOR_MIN_SIZE);
@@ -541,8 +841,12 @@ completions_to_vec (struct list *completions)
     for (i = 0; i < vector_active (comps) && !exists; i++)
     {
       struct cmd_token *curr = vector_slot (comps, i);
+#ifdef VTYSH_DEBUG
       exists = !strcmp (curr->text, token->text) &&
                !strcmp (curr->desc, token->desc);
+#else
+      exists = !strcmp (curr->text, token->text);
+#endif /* VTYSH_DEBUG */
     }
 
     if (!exists)
@@ -713,6 +1017,8 @@ cmd_complete_command (vector vline, struct vty *vty, int *status)
     vector_free (comps);
     comps = NULL;
   }
+  else if (initial_comps)
+    vector_free (initial_comps);
 
   // comps should always be null here
   assert (!comps);
@@ -749,6 +1055,7 @@ node_parent ( enum node_type node )
     case BGP_IPV4M_NODE:
     case BGP_IPV6_NODE:
     case BGP_IPV6M_NODE:
+    case BGP_EVPN_NODE:
       ret = BGP_NODE;
       break;
     case KEYCHAIN_KEY_NODE:
@@ -798,6 +1105,8 @@ cmd_execute_command_real (vector vline,
   // if matcher error, return corresponding CMD_ERR
   if (MATCHER_ERROR(status))
   {
+    if (argv_list)
+      list_delete (argv_list);
     switch (status)
     {
       case MATCHER_INCOMPLETE:
@@ -1113,6 +1422,7 @@ cmd_exit (struct vty *vty)
     case BGP_VNC_L2_GROUP_NODE:
     case BGP_IPV6_NODE:
     case BGP_IPV6M_NODE:
+    case BGP_EVPN_NODE:
       vty->node = BGP_NODE;
       break;
     case LDP_IPV4_NODE:
@@ -1181,6 +1491,7 @@ DEFUN (config_end,
     case BGP_IPV4M_NODE:
     case BGP_IPV6_NODE:
     case BGP_IPV6M_NODE:
+    case BGP_EVPN_NODE:
     case RMAP_NODE:
     case OSPF_NODE:
     case OSPF6_NODE:
@@ -1271,7 +1582,7 @@ permute (struct graph_node *start, struct vty *vty)
       for (ALL_LIST_ELEMENTS_RO (position,ln,gnn))
       {
         struct cmd_token *tt = gnn->data;
-        if (tt->type < SELECTOR_TKN)
+        if (tt->type < SPECIAL_TKN)
           vty_out (vty, " %s", tt->text);
       }
       if (gn == start)
@@ -1318,7 +1629,8 @@ DEFUN (show_commandtree,
        show_commandtree_cmd,
        "show commandtree [permutations]",
        SHOW_STR
-       "Show command tree\n")
+       "Show command tree\n"
+       "Permutations that we are interested in\n")
 {
   return cmd_list_cmds (vty, argc == 3);
 }
@@ -1335,9 +1647,9 @@ DEFUN (config_write,
 {
   int idx_type = 1;
   unsigned int i;
-  int fd;
+  int fd, dirfd;
   struct cmd_node *node;
-  char *config_file;
+  char *config_file, *slash;
   char *config_file_tmp = NULL;
   char *config_file_sav = NULL;
   int ret = CMD_WARNING;
@@ -1388,6 +1700,21 @@ DEFUN (config_write,
   /* Get filename. */
   config_file = host.config;
 
+#ifndef O_DIRECTORY
+#define O_DIRECTORY 0
+#endif
+  slash = strrchr (config_file, '/');
+  if (slash)
+    {
+      char *config_dir = XSTRDUP (MTYPE_TMP, config_file);
+      config_dir[slash - config_file] = '\0';
+      dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
+      XFREE (MTYPE_TMP, config_dir);
+    }
+  else
+    dirfd = open(".", O_DIRECTORY | O_RDONLY);
+  /* if dirfd is invalid, directory sync fails, but we're still OK */
+
   config_file_sav =
     XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
   strcpy (config_file_sav, config_file);
@@ -1405,6 +1732,12 @@ DEFUN (config_write,
                VTY_NEWLINE);
       goto finished;
     }
+  if (fchmod (fd, CONFIGFILE_MASK) != 0)
+    {
+      vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
+        config_file_tmp, safe_strerror(errno), errno, VTY_NEWLINE);
+      goto finished;
+    }
 
   /* Make vty for configuration file. */
   file_vty = vty_new ();
@@ -1439,35 +1772,27 @@ DEFUN (config_write,
                    VTY_NEWLINE);
           goto finished;
         }
-      sync ();
-      if (unlink (config_file) != 0)
-        {
-          vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
-                   VTY_NEWLINE);
-          goto finished;
-        }
+      if (dirfd >= 0)
+        fsync (dirfd);
     }
-  if (link (config_file_tmp, config_file) != 0)
+  if (rename (config_file_tmp, config_file) != 0)
     {
       vty_out (vty, "Can't save configuration file %s.%s", config_file,
                VTY_NEWLINE);
       goto finished;
     }
-  sync ();
-
-  if (chmod (config_file, CONFIGFILE_MASK) != 0)
-    {
-      vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
-        config_file, safe_strerror(errno), errno, VTY_NEWLINE);
-      goto finished;
-    }
+  if (dirfd >= 0)
+    fsync (dirfd);
 
   vty_out (vty, "Configuration saved to %s%s", config_file,
            VTY_NEWLINE);
   ret = CMD_SUCCESS;
 
 finished:
-  unlink (config_file_tmp);
+  if (ret != CMD_SUCCESS)
+    unlink (config_file_tmp);
+  if (dirfd >= 0)
+    close (dirfd);
   XFREE (MTYPE_TMP, config_file_tmp);
   XFREE (MTYPE_TMP, config_file_sav);
   return ret;
@@ -1534,6 +1859,14 @@ DEFUN (show_startup_config,
   return CMD_SUCCESS;
 }
 
+int
+cmd_hostname_set (const char *hostname)
+{
+  XFREE (MTYPE_HOST, host.name);
+  host.name = hostname ? XSTRDUP (MTYPE_HOST, hostname) : NULL;
+  return CMD_SUCCESS;
+}
+
 /* Hostname configuration */
 DEFUN (config_hostname,
        hostname_cmd,
@@ -1549,11 +1882,7 @@ DEFUN (config_hostname,
       return CMD_WARNING;
     }
 
-  if (host.name)
-    XFREE (MTYPE_HOST, host.name);
-
-  host.name = XSTRDUP (MTYPE_HOST, word->arg);
-  return CMD_SUCCESS;
+  return cmd_hostname_set (word->arg);
 }
 
 DEFUN (config_no_hostname,
@@ -1563,10 +1892,7 @@ DEFUN (config_no_hostname,
        "Reset system's network name\n"
        "Host name of this router\n")
 {
-  if (host.name)
-    XFREE (MTYPE_HOST, host.name);
-  host.name = NULL;
-  return CMD_SUCCESS;
+  return cmd_hostname_set (NULL);
 }
 
 /* VTY interface password set. */
@@ -1837,7 +2163,7 @@ DEFUN (config_logmsg,
   if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
     return CMD_ERR_NO_MATCH;
 
-  zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
+  zlog(level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
   if (message)
     XFREE(MTYPE_TMP, message);
 
@@ -1888,7 +2214,7 @@ DEFUN (show_logging,
   vty_out (vty, "%s", VTY_NEWLINE);
 
   vty_out (vty, "Protocol name: %s%s",
-           zlog_proto_names[zl->protocol], VTY_NEWLINE);
+           zl->protoname, VTY_NEWLINE);
   vty_out (vty, "Record priority: %s%s",
            (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
   vty_out (vty, "Timestamp precision: %d%s",
@@ -1908,14 +2234,14 @@ DEFUN (config_log_stdout,
 
   if (argc == idx_log_level)
   {
-    zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+    zlog_set_level (ZLOG_DEST_STDOUT, zlog_default->default_lvl);
     return CMD_SUCCESS;
   }
   int level;
 
   if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
     return CMD_ERR_NO_MATCH;
-  zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
+  zlog_set_level (ZLOG_DEST_STDOUT, level);
   return CMD_SUCCESS;
 }
 
@@ -1927,7 +2253,7 @@ DEFUN (no_config_log_stdout,
        "Cancel logging to stdout\n"
        LOG_LEVEL_DESC)
 {
-  zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+  zlog_set_level (ZLOG_DEST_STDOUT, ZLOG_DISABLED);
   return CMD_SUCCESS;
 }
 
@@ -1942,14 +2268,14 @@ DEFUN (config_log_monitor,
 
   if (argc == idx_log_level)
   {
-    zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+    zlog_set_level (ZLOG_DEST_MONITOR, zlog_default->default_lvl);
     return CMD_SUCCESS;
   }
   int level;
 
   if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
     return CMD_ERR_NO_MATCH;
-  zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
+  zlog_set_level (ZLOG_DEST_MONITOR, level);
   return CMD_SUCCESS;
 }
 
@@ -1961,7 +2287,7 @@ DEFUN (no_config_log_monitor,
        "Disable terminal line (monitor) logging\n"
        LOG_LEVEL_DESC)
 {
-  zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+  zlog_set_level (ZLOG_DEST_MONITOR, ZLOG_DISABLED);
   return CMD_SUCCESS;
 }
 
@@ -1996,7 +2322,7 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)
   else
     fullpath = fname;
 
-  ret = zlog_set_file (NULL, fullpath, loglevel);
+  ret = zlog_set_file (fullpath, loglevel);
 
   if (p)
     XFREE (MTYPE_TMP, p);
@@ -2050,7 +2376,7 @@ DEFUN (no_config_log_file,
        "Logging file name\n"
        "Logging level\n")
 {
-  zlog_reset_file (NULL);
+  zlog_reset_file ();
 
   if (host.logfile)
     XFREE (MTYPE_HOST, host.logfile);
@@ -2073,12 +2399,12 @@ DEFUN (config_log_syslog,
     int level;
     if ((level = level_match (argv[idx_log_levels]->arg)) == ZLOG_DISABLED)
       return CMD_ERR_NO_MATCH;
-    zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
+    zlog_set_level (ZLOG_DEST_SYSLOG, level);
     return CMD_SUCCESS;
   }
   else
   {
-    zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+    zlog_set_level (ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
     return CMD_SUCCESS;
   }
 }
@@ -2092,7 +2418,7 @@ DEFUN (no_config_log_syslog,
        LOG_FACILITY_DESC
        LOG_LEVEL_DESC)
 {
-  zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+  zlog_set_level (ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
   return CMD_SUCCESS;
 }
 
@@ -2400,17 +2726,22 @@ cmd_init (int terminal)
 
       vrf_install_commands ();
     }
-  srandom(time(NULL));
+
+#ifdef DEV_BUILD
+  grammar_sandbox_init();
+#endif
 }
 
 struct cmd_token *
-new_cmd_token (enum cmd_token_type type, u_char attr, char *text, char *desc)
+new_cmd_token (enum cmd_token_type type, u_char attr,
+               const char *text, const char *desc)
 {
   struct cmd_token *token = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token));
   token->type = type;
   token->attr = attr;
-  token->text = text;
-  token->desc = desc;
+  token->text = text ? XSTRDUP (MTYPE_CMD_TEXT, text) : NULL;
+  token->desc = desc ? XSTRDUP (MTYPE_CMD_DESC, desc) : NULL;
+  token->refcnt = 1;
   token->arg  = NULL;
   token->allowrepeat = false;
 
@@ -2423,11 +2754,11 @@ del_cmd_token (struct cmd_token *token)
   if (!token) return;
 
   if (token->text)
-    XFREE (MTYPE_CMD_TOKENS, token->text);
+    XFREE (MTYPE_CMD_TEXT, token->text);
   if (token->desc)
-    XFREE (MTYPE_CMD_TOKENS, token->desc);
+    XFREE (MTYPE_CMD_DESC, token->desc);
   if (token->arg)
-    XFREE (MTYPE_CMD_TOKENS, token->arg);
+    XFREE (MTYPE_CMD_ARG, token->arg);
 
   XFREE (MTYPE_CMD_TOKENS, token);
 }
@@ -2438,9 +2769,9 @@ copy_cmd_token (struct cmd_token *token)
   struct cmd_token *copy = new_cmd_token (token->type, token->attr, NULL, NULL);
   copy->max   = token->max;
   copy->min   = token->min;
-  copy->text  = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL;
-  copy->desc  = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL;
-  copy->arg   = token->arg  ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL;
+  copy->text  = token->text ? XSTRDUP (MTYPE_CMD_TEXT, token->text) : NULL;
+  copy->desc  = token->desc ? XSTRDUP (MTYPE_CMD_DESC, token->desc) : NULL;
+  copy->arg   = token->arg  ? XSTRDUP (MTYPE_CMD_ARG, token->arg) : NULL;
 
   return copy;
 }