Coverage Report

Created: 2025-10-23 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/command.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * CLI backend interface.
4
 *
5
 * --
6
 * Copyright (C) 2016 Cumulus Networks, Inc.
7
 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
8
 * Copyright (C) 2013 by Open Source Routing.
9
 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
10
 */
11
#define FUZZING 1
12
13
#include <zebra.h>
14
#include <lib/version.h>
15
16
#include "command.h"
17
#include "frrstr.h"
18
#include "memory.h"
19
#include "log.h"
20
#include "log_vty.h"
21
#include "frrevent.h"
22
#include "vector.h"
23
#include "linklist.h"
24
#include "vty.h"
25
#include "workqueue.h"
26
#include "vrf.h"
27
#include "command_match.h"
28
#include "command_graph.h"
29
#include "qobj.h"
30
#include "defaults.h"
31
#include "libfrr.h"
32
#include "jhash.h"
33
#include "hook.h"
34
#include "lib_errors.h"
35
#include "mgmt_be_client.h"
36
#include "mgmt_fe_client.h"
37
#include "northbound_cli.h"
38
#include "network.h"
39
#include "routemap.h"
40
41
#include "frrscript.h"
42
43
8
DEFINE_MTYPE_STATIC(LIB, HOST, "Host config");
44
8
DEFINE_MTYPE(LIB, COMPLETION, "Completion item");
45
8
46
8
#define item(x)                                                                \
47
8
  {                                                                      \
48
8
    x, #x                                                          \
49
8
  }
50
8
51
8
/* clang-format off */
52
8
const struct message tokennames[] = {
53
8
  item(WORD_TKN),
54
8
  item(VARIABLE_TKN),
55
8
  item(RANGE_TKN),
56
8
  item(IPV4_TKN),
57
8
  item(IPV4_PREFIX_TKN),
58
8
  item(IPV6_TKN),
59
8
  item(IPV6_PREFIX_TKN),
60
8
  item(MAC_TKN),
61
8
  item(MAC_PREFIX_TKN),
62
8
  item(ASNUM_TKN),
63
8
  item(FORK_TKN),
64
8
  item(JOIN_TKN),
65
8
  item(START_TKN),
66
8
  item(END_TKN),
67
8
  item(NEG_ONLY_TKN),
68
8
  {0},
69
8
};
70
8
/* clang-format on */
71
8
72
8
/* Command vector which includes some level of command lists. Normally
73
8
   each daemon maintains each own cmdvec. */
74
8
vector cmdvec = NULL;
75
8
76
8
/* Host information structure. */
77
8
struct host host;
78
8
79
8
/* for vtysh, put together CLI trees only when switching into node */
80
8
static bool defer_cli_tree;
81
8
82
8
/*
83
8
 * Returns host.name if any, otherwise
84
8
 * it returns the system hostname.
85
8
 */
86
8
const char *cmd_hostname_get(void)
87
8
{
88
2
  return host.name;
89
2
}
90
91
/*
92
 * Returns unix domainname
93
 */
94
const char *cmd_domainname_get(void)
95
1
{
96
1
  return host.domainname;
97
1
}
98
99
const char *cmd_system_get(void)
100
0
{
101
0
  return host.system;
102
0
}
103
104
const char *cmd_release_get(void)
105
0
{
106
0
  return host.release;
107
0
}
108
109
const char *cmd_version_get(void)
110
0
{
111
0
  return host.version;
112
0
}
113
114
bool cmd_allow_reserved_ranges_get(void)
115
1.03k
{
116
1.03k
  return host.allow_reserved_ranges;
117
1.03k
}
118
119
const char *cmd_software_version_get(void)
120
0
{
121
0
  return FRR_FULL_NAME "/" FRR_VERSION;
122
0
}
123
124
static int root_on_exit(struct vty *vty);
125
126
/* Standard command node structures. */
127
static struct cmd_node auth_node = {
128
  .name = "auth",
129
  .node = AUTH_NODE,
130
  .prompt = "Password: ",
131
};
132
133
static struct cmd_node view_node = {
134
  .name = "view",
135
  .node = VIEW_NODE,
136
  .prompt = "%s> ",
137
  .node_exit = root_on_exit,
138
};
139
140
static struct cmd_node auth_enable_node = {
141
  .name = "auth enable",
142
  .node = AUTH_ENABLE_NODE,
143
  .prompt = "Password: ",
144
};
145
146
static struct cmd_node enable_node = {
147
  .name = "enable",
148
  .node = ENABLE_NODE,
149
  .prompt = "%s# ",
150
  .node_exit = root_on_exit,
151
};
152
153
static int config_write_host(struct vty *vty);
154
static struct cmd_node config_node = {
155
  .name = "config",
156
  .node = CONFIG_NODE,
157
  .parent_node = ENABLE_NODE,
158
  .prompt = "%s(config)# ",
159
  .config_write = config_write_host,
160
  .node_exit = vty_config_node_exit,
161
};
162
163
/* This is called from main when a daemon is invoked with -v or --version. */
164
void print_version(const char *progname)
165
0
{
166
0
  printf("%s version %s\n", progname, FRR_VERSION);
167
0
  printf("%s\n", FRR_COPYRIGHT);
168
0
#ifdef ENABLE_VERSION_BUILD_CONFIG
169
0
  printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
170
0
#endif
171
0
}
172
173
char *argv_concat(struct cmd_token **argv, int argc, int shift)
174
0
{
175
0
  int cnt = MAX(argc - shift, 0);
176
0
  const char *argstr[cnt + 1];
177
178
0
  if (!cnt)
179
0
    return NULL;
180
181
0
  for (int i = 0; i < cnt; i++)
182
0
    argstr[i] = argv[i + shift]->arg;
183
184
0
  return frrstr_join(argstr, cnt, " ");
185
0
}
186
187
vector cmd_make_strvec(const char *string)
188
0
{
189
0
  if (!string)
190
0
    return NULL;
191
192
0
  const char *copy = string;
193
194
  /* skip leading whitespace */
195
0
  while (isspace((unsigned char)*copy) && *copy != '\0')
196
0
    copy++;
197
198
  /* if the entire string was whitespace or a comment, return */
199
0
  if (*copy == '\0' || *copy == '!' || *copy == '#')
200
0
    return NULL;
201
202
0
  vector result = frrstr_split_vec(copy, "\n\r\t ");
203
204
0
  for (unsigned int i = 0; i < vector_active(result); i++) {
205
0
    if (strlen(vector_slot(result, i)) == 0) {
206
0
      XFREE(MTYPE_TMP, vector_slot(result, i));
207
0
      vector_unset(result, i);
208
0
    }
209
0
  }
210
211
0
  vector_compact(result);
212
213
0
  return result;
214
0
}
215
216
void cmd_free_strvec(vector v)
217
0
{
218
0
  frrstr_strvec_free(v);
219
0
}
220
221
/**
222
 * Convenience function for accessing argv data.
223
 *
224
 * @param argc
225
 * @param argv
226
 * @param text definition snippet of the desired token
227
 * @param index the starting index, and where to store the
228
 *        index of the found token if it exists
229
 * @return 1 if found, 0 otherwise
230
 */
231
int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
232
0
{
233
0
  int found = 0;
234
0
  for (int i = *index; i < argc && found == 0; i++)
235
0
    if ((found = strmatch(text, argv[i]->text)))
236
0
      *index = i;
237
0
  return found;
238
0
}
239
240
static unsigned int cmd_hash_key(const void *p)
241
0
{
242
0
  int size = sizeof(p);
243
244
0
  return jhash(p, size, 0);
245
0
}
246
247
static bool cmd_hash_cmp(const void *a, const void *b)
248
0
{
249
0
  return a == b;
250
0
}
251
252
/* Install top node of command vector. */
253
void install_node(struct cmd_node *node)
254
75
{
255
75
#define CMD_HASH_STR_SIZE 256
256
75
  char hash_name[CMD_HASH_STR_SIZE];
257
258
75
  vector_set_index(cmdvec, node->node, node);
259
75
  node->cmdgraph = graph_new();
260
75
  node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
261
  // add start node
262
75
  struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
263
75
  graph_new_node(node->cmdgraph, token,
264
75
           (void (*)(void *)) & cmd_token_del);
265
266
75
  snprintf(hash_name, sizeof(hash_name), "Command Hash: %s", node->name);
267
75
  node->cmd_hash =
268
75
    hash_create_size(16, cmd_hash_key, cmd_hash_cmp, hash_name);
269
75
}
270
271
/* Return prompt character of specified node. */
272
const char *cmd_prompt(enum node_type node)
273
0
{
274
0
  struct cmd_node *cnode;
275
276
0
  cnode = vector_slot(cmdvec, node);
277
0
  return cnode->prompt;
278
0
}
279
280
void cmd_defer_tree(bool val)
281
0
{
282
0
  defer_cli_tree = val;
283
0
}
284
285
/* Install a command into a node. */
286
void _install_element(enum node_type ntype, const struct cmd_element *cmd)
287
2.05k
{
288
2.05k
#ifdef FUZZING
289
2.05k
  return;
290
0
#endif
291
0
  struct cmd_node *cnode;
292
293
  /* cmd_init hasn't been called */
294
0
  if (!cmdvec) {
295
0
    fprintf(stderr, "%s called before cmd_init, breakage likely\n",
296
0
      __func__);
297
0
    return;
298
0
  }
299
300
0
  cnode = vector_lookup(cmdvec, ntype);
301
302
0
  if (cnode == NULL) {
303
0
    fprintf(stderr,
304
0
      "%s[%s]:\n"
305
0
      "\tnode %d does not exist.\n"
306
0
      "\tplease call install_node() before install_element()\n",
307
0
      cmd->name, cmd->string, ntype);
308
0
    exit(EXIT_FAILURE);
309
0
  }
310
311
0
  if (hash_lookup(cnode->cmd_hash, (void *)cmd) != NULL) {
312
0
    fprintf(stderr,
313
0
      "%s[%s]:\n"
314
0
      "\tnode %d (%s) already has this command installed.\n"
315
0
      "\tduplicate install_element call?\n",
316
0
      cmd->name, cmd->string, ntype, cnode->name);
317
0
    return;
318
0
  }
319
320
0
  (void)hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern);
321
322
0
  if (cnode->graph_built || !defer_cli_tree) {
323
0
    struct graph *graph = graph_new();
324
0
    struct cmd_token *token =
325
0
      cmd_token_new(START_TKN, 0, NULL, NULL);
326
0
    graph_new_node(graph, token,
327
0
             (void (*)(void *)) & cmd_token_del);
328
329
0
    cmd_graph_parse(graph, cmd);
330
0
    cmd_graph_names(graph);
331
0
    cmd_graph_merge(cnode->cmdgraph, graph, +1);
332
0
    graph_delete_graph(graph);
333
334
0
    cnode->graph_built = true;
335
0
  }
336
337
0
  vector_set(cnode->cmd_vector, (void *)cmd);
338
339
0
  if (ntype == VIEW_NODE)
340
0
    _install_element(ENABLE_NODE, cmd);
341
0
}
342
343
static void cmd_finalize_iter(struct hash_bucket *hb, void *arg)
344
0
{
345
0
  struct cmd_node *cnode = arg;
346
0
  const struct cmd_element *cmd = hb->data;
347
0
  struct graph *graph = graph_new();
348
0
  struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
349
350
0
  graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
351
352
0
  cmd_graph_parse(graph, cmd);
353
0
  cmd_graph_names(graph);
354
0
  cmd_graph_merge(cnode->cmdgraph, graph, +1);
355
0
  graph_delete_graph(graph);
356
0
}
357
358
void cmd_finalize_node(struct cmd_node *cnode)
359
0
{
360
0
  if (cnode->graph_built)
361
0
    return;
362
363
0
  hash_iterate(cnode->cmd_hash, cmd_finalize_iter, cnode);
364
0
  cnode->graph_built = true;
365
0
}
366
367
void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
368
0
{
369
0
  struct cmd_node *cnode;
370
371
  /* cmd_init hasn't been called */
372
0
  if (!cmdvec) {
373
0
    fprintf(stderr, "%s called before cmd_init, breakage likely\n",
374
0
      __func__);
375
0
    return;
376
0
  }
377
378
0
  cnode = vector_lookup(cmdvec, ntype);
379
380
0
  if (cnode == NULL) {
381
0
    fprintf(stderr,
382
0
      "%s[%s]:\n"
383
0
      "\tnode %d does not exist.\n"
384
0
      "\tplease call install_node() before uninstall_element()\n",
385
0
      cmd->name, cmd->string, ntype);
386
0
    exit(EXIT_FAILURE);
387
0
  }
388
389
0
  if (hash_release(cnode->cmd_hash, (void *)cmd) == NULL) {
390
0
    fprintf(stderr,
391
0
      "%s[%s]:\n"
392
0
      "\tnode %d (%s) does not have this command installed.\n"
393
0
      "\tduplicate uninstall_element call?\n",
394
0
      cmd->name, cmd->string, ntype, cnode->name);
395
0
    return;
396
0
  }
397
398
0
  vector_unset_value(cnode->cmd_vector, (void *)cmd);
399
400
0
  if (cnode->graph_built) {
401
0
    struct graph *graph = graph_new();
402
0
    struct cmd_token *token =
403
0
      cmd_token_new(START_TKN, 0, NULL, NULL);
404
0
    graph_new_node(graph, token,
405
0
             (void (*)(void *)) & cmd_token_del);
406
407
0
    cmd_graph_parse(graph, cmd);
408
0
    cmd_graph_names(graph);
409
0
    cmd_graph_merge(cnode->cmdgraph, graph, -1);
410
0
    graph_delete_graph(graph);
411
0
  }
412
413
0
  if (ntype == VIEW_NODE)
414
0
    uninstall_element(ENABLE_NODE, cmd);
415
0
}
416
417
418
static const unsigned char itoa64[] =
419
  "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
420
421
static void to64(char *s, long v, int n)
422
0
{
423
0
  while (--n >= 0) {
424
0
    *s++ = itoa64[v & 0x3f];
425
0
    v >>= 6;
426
0
  }
427
0
}
428
429
static char *zencrypt(const char *passwd)
430
0
{
431
0
  char salt[6];
432
0
  struct timeval tv;
433
434
0
  gettimeofday(&tv, 0);
435
436
0
  to64(&salt[0], frr_weak_random(), 3);
437
0
  to64(&salt[3], tv.tv_usec, 3);
438
0
  salt[5] = '\0';
439
440
0
  return crypt(passwd, salt);
441
0
}
442
443
static bool full_cli;
444
445
/* This function write configuration of this host. */
446
static int config_write_host(struct vty *vty)
447
0
{
448
0
  const char *name;
449
450
0
  name = cmd_hostname_get();
451
0
  if (name && name[0] != '\0')
452
0
    vty_out(vty, "hostname %s\n", name);
453
454
0
  name = cmd_domainname_get();
455
0
  if (name && name[0] != '\0')
456
0
    vty_out(vty, "domainname %s\n", name);
457
458
0
  if (cmd_allow_reserved_ranges_get())
459
0
    vty_out(vty, "allow-reserved-ranges\n");
460
461
  /* The following are all configuration commands that are not sent to
462
   * watchfrr.  For instance watchfrr is hardcoded to log to syslog so
463
   * we would always display 'log syslog informational' in the config
464
   * which would cause other daemons to then switch to syslog when they
465
   * parse frr.conf.
466
   */
467
0
  if (full_cli) {
468
0
    if (host.encrypt) {
469
0
      if (host.password_encrypt)
470
0
        vty_out(vty, "password 8 %s\n",
471
0
          host.password_encrypt);
472
0
      if (host.enable_encrypt)
473
0
        vty_out(vty, "enable password 8 %s\n",
474
0
          host.enable_encrypt);
475
0
    } else {
476
0
      if (host.password)
477
0
        vty_out(vty, "password %s\n", host.password);
478
0
      if (host.enable)
479
0
        vty_out(vty, "enable password %s\n",
480
0
          host.enable);
481
0
    }
482
0
    log_config_write(vty);
483
484
    /* print disable always, but enable only if default is flipped
485
     * => prep for future removal of compile-time knob
486
     */
487
0
    if (!cputime_enabled)
488
0
      vty_out(vty, "no service cputime-stats\n");
489
#ifdef EXCLUDE_CPU_TIME
490
    else
491
      vty_out(vty, "service cputime-stats\n");
492
#endif
493
494
0
    if (!cputime_threshold)
495
0
      vty_out(vty, "no service cputime-warning\n");
496
#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
497
    else /* again, always print non-default */
498
#else
499
0
    else if (cputime_threshold != 5000000)
500
0
#endif
501
0
      vty_out(vty, "service cputime-warning %lu\n",
502
0
        cputime_threshold / 1000);
503
504
0
    if (!walltime_threshold)
505
0
      vty_out(vty, "no service walltime-warning\n");
506
#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
507
    else /* again, always print non-default */
508
#else
509
0
    else if (walltime_threshold != 5000000)
510
0
#endif
511
0
      vty_out(vty, "service walltime-warning %lu\n",
512
0
        walltime_threshold / 1000);
513
514
0
    if (host.advanced)
515
0
      vty_out(vty, "service advanced-vty\n");
516
517
0
    if (host.encrypt)
518
0
      vty_out(vty, "service password-encryption\n");
519
520
0
    if (host.lines >= 0)
521
0
      vty_out(vty, "service terminal-length %d\n",
522
0
        host.lines);
523
524
0
    if (host.motdfile)
525
0
      vty_out(vty, "banner motd file %s\n", host.motdfile);
526
0
    else if (host.motd
527
0
       && strncmp(host.motd, FRR_DEFAULT_MOTD,
528
0
            strlen(host.motd)))
529
0
      vty_out(vty, "banner motd line %s\n", host.motd);
530
0
    else if (!host.motd)
531
0
      vty_out(vty, "no banner motd\n");
532
0
  }
533
534
0
  if (debug_memstats_at_exit)
535
0
    vty_out(vty, "!\ndebug memstats-at-exit\n");
536
537
0
  return 1;
538
0
}
539
540
/* Utility function for getting command graph. */
541
static struct graph *cmd_node_graph(vector v, enum node_type ntype)
542
0
{
543
0
  struct cmd_node *cnode = vector_slot(v, ntype);
544
545
0
  cmd_finalize_node(cnode);
546
0
  return cnode->cmdgraph;
547
0
}
548
549
static int cmd_try_do_shortcut(enum node_type node, char *first_word)
550
0
{
551
0
  if (first_word != NULL && node != AUTH_NODE && node != VIEW_NODE
552
0
      && node != AUTH_ENABLE_NODE && 0 == strcmp("do", first_word))
553
0
    return 1;
554
0
  return 0;
555
0
}
556
557
/**
558
 * Compare function for cmd_token.
559
 * Used with qsort to sort command completions.
560
 */
561
static int compare_completions(const void *fst, const void *snd)
562
0
{
563
0
  const struct cmd_token *first = *(const struct cmd_token * const *)fst,
564
0
             *secnd = *(const struct cmd_token * const *)snd;
565
0
  return strcmp(first->text, secnd->text);
566
0
}
567
568
/**
569
 * Takes a list of completions returned by command_complete,
570
 * dedeuplicates them based on both text and description,
571
 * sorts them, and returns them as a vector.
572
 *
573
 * @param completions linked list of cmd_token
574
 * @return deduplicated and sorted vector with
575
 */
576
vector completions_to_vec(struct list *completions)
577
0
{
578
0
  vector comps = vector_init(VECTOR_MIN_SIZE);
579
580
0
  struct listnode *ln;
581
0
  struct cmd_token *token, *cr = NULL;
582
0
  unsigned int i, exists;
583
0
  for (ALL_LIST_ELEMENTS_RO(completions, ln, token)) {
584
0
    if (token->type == END_TKN && (cr = token))
585
0
      continue;
586
587
    // linear search for token in completions vector
588
0
    exists = 0;
589
0
    for (i = 0; i < vector_active(comps) && !exists; i++) {
590
0
      struct cmd_token *curr = vector_slot(comps, i);
591
#ifdef VTYSH_DEBUG
592
      exists = !strcmp(curr->text, token->text)
593
         && !strcmp(curr->desc, token->desc);
594
#else
595
0
      exists = !strcmp(curr->text, token->text);
596
0
#endif /* VTYSH_DEBUG */
597
0
    }
598
599
0
    if (!exists)
600
0
      vector_set(comps, token);
601
0
  }
602
603
  // sort completions
604
0
  qsort(comps->index, vector_active(comps), sizeof(void *),
605
0
        &compare_completions);
606
607
  // make <cr> the first element, if it is present
608
0
  if (cr) {
609
0
    vector_set_index(comps, vector_active(comps), NULL);
610
0
    memmove(comps->index + 1, comps->index,
611
0
      (comps->alloced - 1) * sizeof(void *));
612
0
    vector_set_index(comps, 0, cr);
613
0
  }
614
615
0
  return comps;
616
0
}
617
/**
618
 * Generates a vector of cmd_token representing possible completions
619
 * on the current input.
620
 *
621
 * @param vline the vectorized input line
622
 * @param vty the vty with the node to match on
623
 * @param status pointer to matcher status code
624
 * @return vector of struct cmd_token * with possible completions
625
 */
626
static vector cmd_complete_command_real(vector vline, struct vty *vty,
627
          int *status)
628
0
{
629
0
  struct list *completions;
630
0
  struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
631
632
0
  enum matcher_rv rv = command_complete(cmdgraph, vline, &completions);
633
634
0
  if (MATCHER_ERROR(rv)) {
635
0
    *status = CMD_ERR_NO_MATCH;
636
0
    return NULL;
637
0
  }
638
639
0
  vector comps = completions_to_vec(completions);
640
0
  list_delete(&completions);
641
642
  // set status code appropriately
643
0
  switch (vector_active(comps)) {
644
0
  case 0:
645
0
    *status = CMD_ERR_NO_MATCH;
646
0
    break;
647
0
  case 1:
648
0
    *status = CMD_COMPLETE_FULL_MATCH;
649
0
    break;
650
0
  default:
651
0
    *status = CMD_COMPLETE_LIST_MATCH;
652
0
  }
653
654
0
  return comps;
655
0
}
656
657
vector cmd_describe_command(vector vline, struct vty *vty, int *status)
658
0
{
659
0
  vector ret;
660
661
0
  if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
662
0
    enum node_type onode;
663
0
    int orig_xpath_index;
664
0
    vector shifted_vline;
665
0
    unsigned int index;
666
667
0
    onode = vty->node;
668
0
    orig_xpath_index = vty->xpath_index;
669
0
    vty->node = ENABLE_NODE;
670
0
    vty->xpath_index = 0;
671
    /* We can try it on enable node, cos' the vty is authenticated
672
     */
673
674
0
    shifted_vline = vector_init(vector_count(vline));
675
    /* use memcpy? */
676
0
    for (index = 1; index < vector_active(vline); index++) {
677
0
      vector_set_index(shifted_vline, index - 1,
678
0
           vector_lookup(vline, index));
679
0
    }
680
681
0
    ret = cmd_complete_command_real(shifted_vline, vty, status);
682
683
0
    vector_free(shifted_vline);
684
0
    vty->node = onode;
685
0
    vty->xpath_index = orig_xpath_index;
686
0
    return ret;
687
0
  }
688
689
0
  return cmd_complete_command_real(vline, vty, status);
690
0
}
691
692
static struct list *varhandlers = NULL;
693
694
void cmd_variable_complete(struct cmd_token *token, const char *arg,
695
         vector comps)
696
0
{
697
0
  struct listnode *ln;
698
0
  const struct cmd_variable_handler *cvh;
699
0
  size_t i, argsz;
700
0
  vector tmpcomps;
701
702
0
  tmpcomps = arg ? vector_init(VECTOR_MIN_SIZE) : comps;
703
704
0
  for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh)) {
705
0
    if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
706
0
      continue;
707
0
    if (cvh->varname && (!token->varname
708
0
             || strcmp(cvh->varname, token->varname)))
709
0
      continue;
710
0
    cvh->completions(tmpcomps, token);
711
0
    break;
712
0
  }
713
714
0
  if (!arg)
715
0
    return;
716
717
0
  argsz = strlen(arg);
718
0
  for (i = vector_active(tmpcomps); i; i--) {
719
0
    char *item = vector_slot(tmpcomps, i - 1);
720
0
    if (strlen(item) >= argsz && !strncmp(item, arg, argsz))
721
0
      vector_set(comps, item);
722
0
    else
723
0
      XFREE(MTYPE_COMPLETION, item);
724
0
  }
725
0
  vector_free(tmpcomps);
726
0
}
727
728
0
#define AUTOCOMP_INDENT 5
729
730
char *cmd_variable_comp2str(vector comps, unsigned short cols)
731
0
{
732
0
  size_t bsz = 16;
733
0
  char *buf = XCALLOC(MTYPE_TMP, bsz);
734
0
  int lc = AUTOCOMP_INDENT;
735
0
  size_t cs = AUTOCOMP_INDENT;
736
0
  size_t itemlen;
737
0
  snprintf(buf, bsz, "%*s", AUTOCOMP_INDENT, "");
738
0
  for (size_t j = 0; j < vector_active(comps); j++) {
739
0
    char *item = vector_slot(comps, j);
740
0
    itemlen = strlen(item);
741
742
0
    size_t next_sz = cs + itemlen + AUTOCOMP_INDENT + 3;
743
744
0
    if (next_sz > bsz) {
745
      /* Make sure the buf size is large enough */
746
0
      bsz = next_sz;
747
0
      buf = XREALLOC(MTYPE_TMP, buf, bsz);
748
0
    }
749
0
    if (lc + itemlen + 1 >= cols) {
750
0
      cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
751
0
               AUTOCOMP_INDENT, "");
752
0
      lc = AUTOCOMP_INDENT;
753
0
    }
754
755
0
    size_t written = snprintf(&buf[cs], bsz - cs, "%s ", item);
756
0
    lc += written;
757
0
    cs += written;
758
0
    XFREE(MTYPE_COMPLETION, item);
759
0
    vector_set_index(comps, j, NULL);
760
0
  }
761
0
  return buf;
762
0
}
763
764
void cmd_variable_handler_register(const struct cmd_variable_handler *cvh)
765
22
{
766
22
  if (!varhandlers)
767
0
    return;
768
769
78
  for (; cvh->completions; cvh++)
770
56
    listnode_add(varhandlers, (void *)cvh);
771
22
}
772
773
DEFUN_HIDDEN (autocomplete,
774
              autocomplete_cmd,
775
              "autocomplete TYPE TEXT VARNAME",
776
              "Autocompletion handler (internal, for vtysh)\n"
777
              "cmd_token->type\n"
778
              "cmd_token->text\n"
779
              "cmd_token->varname\n")
780
0
{
781
0
  struct cmd_token tok;
782
0
  vector comps = vector_init(32);
783
0
  size_t i;
784
785
0
  memset(&tok, 0, sizeof(tok));
786
0
  tok.type = atoi(argv[1]->arg);
787
0
  tok.text = argv[2]->arg;
788
0
  tok.varname = argv[3]->arg;
789
0
  if (!strcmp(tok.varname, "-"))
790
0
    tok.varname = NULL;
791
792
0
  cmd_variable_complete(&tok, NULL, comps);
793
794
0
  for (i = 0; i < vector_active(comps); i++) {
795
0
    char *text = vector_slot(comps, i);
796
0
    vty_out(vty, "%s\n", text);
797
0
    XFREE(MTYPE_COMPLETION, text);
798
0
  }
799
800
0
  vector_free(comps);
801
0
  return CMD_SUCCESS;
802
0
}
803
804
/**
805
 * Generate possible tab-completions for the given input. This function only
806
 * returns results that would result in a valid command if used as Readline
807
 * completions (as is the case in vtysh). For instance, if the passed vline ends
808
 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
809
 *
810
 * @param vline vectorized input line
811
 * @param vty the vty
812
 * @param status location to store matcher status code in
813
 * @return set of valid strings for use with Readline as tab-completions.
814
 */
815
816
char **cmd_complete_command(vector vline, struct vty *vty, int *status)
817
0
{
818
0
  char **ret = NULL;
819
0
  int original_node = vty->node;
820
0
  vector input_line = vector_init(vector_count(vline));
821
822
  // if the first token is 'do' we'll want to execute the command in the
823
  // enable node
824
0
  int do_shortcut = cmd_try_do_shortcut(vty->node, vector_slot(vline, 0));
825
0
  vty->node = do_shortcut ? ENABLE_NODE : original_node;
826
827
  // construct the input line we'll be matching on
828
0
  unsigned int offset = (do_shortcut) ? 1 : 0;
829
0
  for (unsigned index = 0; index + offset < vector_active(vline); index++)
830
0
    vector_set_index(input_line, index,
831
0
         vector_lookup(vline, index + offset));
832
833
  // get token completions -- this is a copying operation
834
0
  vector comps = NULL, initial_comps;
835
0
  initial_comps = cmd_complete_command_real(input_line, vty, status);
836
837
0
  if (!MATCHER_ERROR(*status)) {
838
0
    assert(initial_comps);
839
    // filter out everything that is not suitable for a
840
    // tab-completion
841
0
    comps = vector_init(VECTOR_MIN_SIZE);
842
0
    for (unsigned int i = 0; i < vector_active(initial_comps);
843
0
         i++) {
844
0
      struct cmd_token *token = vector_slot(initial_comps, i);
845
0
      if (token->type == WORD_TKN)
846
0
        vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
847
0
                token->text));
848
0
      else if (IS_VARYING_TOKEN(token->type)) {
849
0
        const char *ref = vector_lookup(
850
0
          vline, vector_active(vline) - 1);
851
0
        cmd_variable_complete(token, ref, comps);
852
0
      }
853
0
    }
854
0
    vector_free(initial_comps);
855
856
    // since we filtered results, we need to re-set status code
857
0
    switch (vector_active(comps)) {
858
0
    case 0:
859
0
      *status = CMD_ERR_NO_MATCH;
860
0
      break;
861
0
    case 1:
862
0
      *status = CMD_COMPLETE_FULL_MATCH;
863
0
      break;
864
0
    default:
865
0
      *status = CMD_COMPLETE_LIST_MATCH;
866
0
    }
867
868
    // copy completions text into an array of char*
869
0
    ret = XMALLOC(MTYPE_TMP,
870
0
            (vector_active(comps) + 1) * sizeof(char *));
871
0
    unsigned int i;
872
0
    for (i = 0; i < vector_active(comps); i++) {
873
0
      ret[i] = vector_slot(comps, i);
874
0
    }
875
    // set the last element to NULL, because this array is used in
876
    // a Readline completion_generator function which expects NULL
877
    // as a sentinel value
878
0
    ret[i] = NULL;
879
0
    vector_free(comps);
880
0
    comps = NULL;
881
0
  } else if (initial_comps)
882
0
    vector_free(initial_comps);
883
884
  // comps should always be null here
885
0
  assert(!comps);
886
887
  // free the adjusted input line
888
0
  vector_free(input_line);
889
890
  // reset vty->node to its original value
891
0
  vty->node = original_node;
892
893
0
  return ret;
894
0
}
895
896
/* return parent node */
897
/* MUST eventually converge on CONFIG_NODE */
898
enum node_type node_parent(enum node_type node)
899
0
{
900
0
  struct cmd_node *cnode;
901
902
0
  assert(node > CONFIG_NODE);
903
904
0
  cnode = vector_lookup(cmdvec, node);
905
906
0
  return cnode->parent_node;
907
0
}
908
909
/* Execute command by argument vline vector. */
910
static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
911
            struct vty *vty,
912
            const struct cmd_element **cmd,
913
            unsigned int up_level)
914
0
{
915
0
  struct list *argv_list;
916
0
  enum matcher_rv status;
917
0
  const struct cmd_element *matched_element = NULL;
918
0
  unsigned int i;
919
0
  int xpath_index = vty->xpath_index;
920
0
  int node = vty->node;
921
922
  /* only happens for legacy split config file load;  need to check for
923
   * a match before calling node_exit handlers below
924
   */
925
0
  for (i = 0; i < up_level; i++) {
926
0
    struct cmd_node *cnode;
927
928
0
    if (node <= CONFIG_NODE)
929
0
      return CMD_NO_LEVEL_UP;
930
931
0
    cnode = vector_slot(cmdvec, node);
932
0
    node = node_parent(node);
933
934
0
    if (xpath_index > 0 && !cnode->no_xpath)
935
0
      xpath_index--;
936
0
  }
937
938
0
  struct graph *cmdgraph = cmd_node_graph(cmdvec, node);
939
0
  status = command_match(cmdgraph, vline, &argv_list, &matched_element);
940
941
0
  if (cmd)
942
0
    *cmd = matched_element;
943
944
  // if matcher error, return corresponding CMD_ERR
945
0
  if (MATCHER_ERROR(status)) {
946
0
    if (argv_list)
947
0
      list_delete(&argv_list);
948
0
    switch (status) {
949
0
    case MATCHER_INCOMPLETE:
950
0
      return CMD_ERR_INCOMPLETE;
951
0
    case MATCHER_AMBIGUOUS:
952
0
      return CMD_ERR_AMBIGUOUS;
953
0
    case MATCHER_NO_MATCH:
954
0
    case MATCHER_OK:
955
0
      return CMD_ERR_NO_MATCH;
956
0
    }
957
0
  }
958
959
0
  for (i = 0; i < up_level; i++)
960
0
    cmd_exit(vty);
961
962
  // build argv array from argv list
963
0
  struct cmd_token **argv = XMALLOC(
964
0
    MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *));
965
0
  struct listnode *ln;
966
0
  struct cmd_token *token;
967
968
0
  i = 0;
969
0
  for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token))
970
0
    argv[i++] = token;
971
972
0
  int argc = argv_list->count;
973
974
0
  int ret;
975
0
  if (matched_element->daemon)
976
0
    ret = CMD_SUCCESS_DAEMON;
977
0
  else {
978
0
    if (vty->config) {
979
      /* Clear array of enqueued configuration changes. */
980
0
      vty->num_cfg_changes = 0;
981
0
      memset(&vty->cfg_changes, 0, sizeof(vty->cfg_changes));
982
983
      /* Regenerate candidate configuration if necessary. */
984
0
      if (frr_get_cli_mode() == FRR_CLI_CLASSIC
985
0
          && running_config->version
986
0
               > vty->candidate_config->version)
987
0
        nb_config_replace(vty->candidate_config,
988
0
              running_config, true);
989
990
      /*
991
       * Perform pending commit (if any) before executing
992
       * non-YANG command.
993
       */
994
0
      if (!(matched_element->attr & CMD_ATTR_YANG))
995
0
        (void)nb_cli_pending_commit_check(vty);
996
0
    }
997
998
0
    ret = matched_element->func(matched_element, vty, argc, argv);
999
0
  }
1000
1001
  // delete list and cmd_token's in it
1002
0
  list_delete(&argv_list);
1003
0
  XFREE(MTYPE_TMP, argv);
1004
1005
0
  return ret;
1006
0
}
1007
1008
/**
1009
 * Execute a given command, handling things like "do ..." and checking
1010
 * whether the given command might apply at a parent node if doesn't
1011
 * apply for the current node.
1012
 *
1013
 * @param vline Command line input, vector of char* where each element is
1014
 *              one input token.
1015
 * @param vty The vty context in which the command should be executed.
1016
 * @param cmd Pointer where the struct cmd_element of the matched command
1017
 *            will be stored, if any. May be set to NULL if this info is
1018
 *            not needed.
1019
 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1020
 * @return The status of the command that has been executed or an error code
1021
 *         as to why no command could be executed.
1022
 */
1023
int cmd_execute_command(vector vline, struct vty *vty,
1024
      const struct cmd_element **cmd, int vtysh)
1025
0
{
1026
0
  int ret, saved_ret = 0;
1027
0
  enum node_type onode, try_node;
1028
0
  int orig_xpath_index;
1029
1030
0
  onode = try_node = vty->node;
1031
0
  orig_xpath_index = vty->xpath_index;
1032
1033
0
  if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1034
0
    vector shifted_vline;
1035
0
    unsigned int index;
1036
1037
0
    vty->node = ENABLE_NODE;
1038
0
    vty->xpath_index = 0;
1039
    /* We can try it on enable node, cos' the vty is authenticated
1040
     */
1041
1042
0
    shifted_vline = vector_init(vector_count(vline));
1043
    /* use memcpy? */
1044
0
    for (index = 1; index < vector_active(vline); index++)
1045
0
      vector_set_index(shifted_vline, index - 1,
1046
0
           vector_lookup(vline, index));
1047
1048
0
    ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
1049
0
                 vty, cmd, 0);
1050
1051
0
    vector_free(shifted_vline);
1052
0
    vty->node = onode;
1053
0
    vty->xpath_index = orig_xpath_index;
1054
0
    return ret;
1055
0
  }
1056
1057
0
  saved_ret = ret =
1058
0
    cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd, 0);
1059
1060
0
  if (vtysh)
1061
0
    return saved_ret;
1062
1063
0
  if (ret != CMD_SUCCESS && ret != CMD_WARNING
1064
0
      && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
1065
0
      && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
1066
    /* This assumes all nodes above CONFIG_NODE are childs of
1067
     * CONFIG_NODE */
1068
0
    while (vty->node > CONFIG_NODE) {
1069
0
      struct cmd_node *cnode = vector_slot(cmdvec, try_node);
1070
1071
0
      try_node = node_parent(try_node);
1072
0
      vty->node = try_node;
1073
0
      if (vty->xpath_index > 0 && !cnode->no_xpath)
1074
0
        vty->xpath_index--;
1075
1076
0
      ret = cmd_execute_command_real(vline, FILTER_RELAXED,
1077
0
                   vty, cmd, 0);
1078
0
      if (ret == CMD_SUCCESS || ret == CMD_WARNING
1079
0
          || ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE
1080
0
          || ret == CMD_NOT_MY_INSTANCE
1081
0
          || ret == CMD_WARNING_CONFIG_FAILED)
1082
0
        return ret;
1083
0
    }
1084
    /* no command succeeded, reset the vty to the original node */
1085
0
    vty->node = onode;
1086
0
    vty->xpath_index = orig_xpath_index;
1087
0
  }
1088
1089
  /* return command status for original node */
1090
0
  return saved_ret;
1091
0
}
1092
1093
/**
1094
 * Execute a given command, matching it strictly against the current node.
1095
 * This mode is used when reading config files.
1096
 *
1097
 * @param vline Command line input, vector of char* where each element is
1098
 *              one input token.
1099
 * @param vty The vty context in which the command should be executed.
1100
 * @param cmd Pointer where the struct cmd_element* of the matched command
1101
 *            will be stored, if any. May be set to NULL if this info is
1102
 *            not needed.
1103
 * @return The status of the command that has been executed or an error code
1104
 *         as to why no command could be executed.
1105
 */
1106
int cmd_execute_command_strict(vector vline, struct vty *vty,
1107
             const struct cmd_element **cmd)
1108
0
{
1109
0
  return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, 0);
1110
0
}
1111
1112
/*
1113
 * Hook for preprocessing command string before executing.
1114
 *
1115
 * All subscribers are called with the raw command string that is to be
1116
 * executed. If any changes are to be made, a new string should be allocated
1117
 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1118
 * is then responsible for freeing this string.
1119
 *
1120
 * All processing functions must be mutually exclusive in their action, i.e. if
1121
 * one subscriber decides to modify the command, all others must not modify it
1122
 * when called. Feeding the output of one processing command into a subsequent
1123
 * one is not supported.
1124
 *
1125
 * This hook is intentionally internal to the command processing system.
1126
 *
1127
 * cmd_in
1128
 *    The raw command string.
1129
 *
1130
 * cmd_out
1131
 *    The result of any processing.
1132
 */
1133
DECLARE_HOOK(cmd_execute,
1134
       (struct vty *vty, const char *cmd_in, char **cmd_out),
1135
       (vty, cmd_in, cmd_out));
1136
DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
1137
      (vty, cmd_in, cmd_out));
1138
1139
/* Hook executed after a CLI command. */
1140
DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1141
       (vty, cmd_exec));
1142
DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1143
      (vty, cmd_exec));
1144
1145
/*
1146
 * cmd_execute hook subscriber to handle `|` actions.
1147
 */
1148
static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1149
            char **cmd_out)
1150
0
{
1151
  /* look for `|` */
1152
0
  char *orig, *working, *token, *u;
1153
0
  char *pipe = strstr(cmd_in, "| ");
1154
0
  int ret = 0;
1155
1156
0
  if (!pipe)
1157
0
    return 0;
1158
1159
  /* duplicate string for processing purposes, not including pipe */
1160
0
  orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1161
1162
  /* retrieve action */
1163
0
  token = strsep(&working, " ");
1164
0
  assert(token);
1165
1166
  /* match result to known actions */
1167
0
  if (strmatch(token, "include")) {
1168
    /* the remaining text should be a regexp */
1169
0
    char *regexp = working;
1170
1171
0
    if (!regexp) {
1172
0
      vty_out(vty, "%% Need a regexp to filter with\n");
1173
0
      ret = 1;
1174
0
      goto fail;
1175
0
    }
1176
1177
0
    bool succ = vty_set_include(vty, regexp);
1178
1179
0
    if (!succ) {
1180
0
      vty_out(vty, "%% Bad regexp '%s'\n", regexp);
1181
0
      ret = 1;
1182
0
      goto fail;
1183
0
    }
1184
0
    *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
1185
0
    u = *cmd_out;
1186
0
    strsep(&u, "|");
1187
0
  } else {
1188
0
    vty_out(vty, "%% Unknown action '%s'\n", token);
1189
0
    ret = 1;
1190
0
    goto fail;
1191
0
  }
1192
1193
0
fail:
1194
0
  XFREE(MTYPE_TMP, orig);
1195
0
  return ret;
1196
0
}
1197
1198
static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1199
0
{
1200
0
  if (vty->filter)
1201
0
    vty_set_include(vty, NULL);
1202
1203
0
  return 0;
1204
0
}
1205
1206
int cmd_execute(struct vty *vty, const char *cmd,
1207
    const struct cmd_element **matched, int vtysh)
1208
0
{
1209
0
  int ret;
1210
0
  char *cmd_out = NULL;
1211
0
  const char *cmd_exec = NULL;
1212
0
  vector vline;
1213
1214
0
  ret = hook_call(cmd_execute, vty, cmd, &cmd_out);
1215
0
  if (ret) {
1216
0
    ret = CMD_WARNING;
1217
0
    goto free;
1218
0
  }
1219
1220
0
  cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1221
1222
0
  vline = cmd_make_strvec(cmd_exec);
1223
1224
0
  if (vline) {
1225
0
    ret = cmd_execute_command(vline, vty, matched, vtysh);
1226
0
    cmd_free_strvec(vline);
1227
0
  } else {
1228
0
    ret = CMD_SUCCESS;
1229
0
  }
1230
1231
0
free:
1232
0
  hook_call(cmd_execute_done, vty, cmd_exec);
1233
1234
0
  XFREE(MTYPE_TMP, cmd_out);
1235
1236
0
  return ret;
1237
0
}
1238
1239
1240
/**
1241
 * Parse one line of config, walking up the parse tree attempting to find a
1242
 * match
1243
 *
1244
 * @param vty The vty context in which the command should be executed.
1245
 * @param cmd Pointer where the struct cmd_element* of the match command
1246
 *            will be stored, if any.  May be set to NULL if this info is
1247
 *            not needed.
1248
 * @param use_daemon Boolean to control whether or not we match on
1249
 * CMD_SUCCESS_DAEMON
1250
 *                   or not.
1251
 * @return The status of the command that has been executed or an error code
1252
 *         as to why no command could be executed.
1253
 */
1254
int command_config_read_one_line(struct vty *vty,
1255
         const struct cmd_element **cmd,
1256
         uint32_t line_num, int use_daemon)
1257
0
{
1258
0
  vector vline;
1259
0
  int ret;
1260
0
  unsigned up_level = 0;
1261
1262
0
  vline = cmd_make_strvec(vty->buf);
1263
1264
  /* In case of comment line */
1265
0
  if (vline == NULL)
1266
0
    return CMD_SUCCESS;
1267
1268
  /* Execute configuration command : this is strict match */
1269
0
  ret = cmd_execute_command_strict(vline, vty, cmd);
1270
1271
  /* The logic for trying parent nodes is in cmd_execute_command_real()
1272
   * since calling ->node_exit() correctly is a bit involved.  This is
1273
   * also the only reason CMD_NO_LEVEL_UP exists.
1274
   */
1275
0
  while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1276
0
         && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1277
0
         && ret != CMD_SUCCESS && ret != CMD_WARNING
1278
0
         && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
1279
0
         && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1280
0
         && ret != CMD_NO_LEVEL_UP)
1281
0
    ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd,
1282
0
                 ++up_level);
1283
1284
0
  if (ret == CMD_NO_LEVEL_UP)
1285
0
    ret = CMD_ERR_NO_MATCH;
1286
1287
0
  if (ret != CMD_SUCCESS &&
1288
0
      ret != CMD_WARNING &&
1289
0
      ret != CMD_SUCCESS_DAEMON) {
1290
0
    struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1291
1292
0
    memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1293
0
    ve->line_num = line_num;
1294
0
    ve->cmd_ret = ret;
1295
0
    if (!vty->error)
1296
0
      vty->error = list_new();
1297
1298
0
    listnode_add(vty->error, ve);
1299
0
  }
1300
1301
0
  cmd_free_strvec(vline);
1302
1303
0
  return ret;
1304
0
}
1305
1306
/* Configuration make from file. */
1307
int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
1308
0
{
1309
0
  int ret, error_ret = 0;
1310
0
  *line_num = 0;
1311
1312
0
  while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1313
0
    ++(*line_num);
1314
1315
0
    if (vty_log_commands) {
1316
0
      int len = strlen(vty->buf);
1317
1318
      /* now log the command */
1319
0
      zlog_notice("config-from-file# %.*s", len ? len - 1 : 0,
1320
0
            vty->buf);
1321
0
    }
1322
1323
0
    ret = command_config_read_one_line(vty, NULL, *line_num, 0);
1324
1325
0
    if (ret != CMD_SUCCESS && ret != CMD_WARNING
1326
0
        && ret != CMD_ERR_NOTHING_TODO)
1327
0
      error_ret = ret;
1328
0
  }
1329
1330
0
  if (error_ret) {
1331
0
    return error_ret;
1332
0
  }
1333
1334
0
  return CMD_SUCCESS;
1335
0
}
1336
1337
/* Configuration from terminal */
1338
DEFUN (config_terminal,
1339
       config_terminal_cmd,
1340
       "configure [terminal [file-lock]]",
1341
       "Configuration from vty interface\n"
1342
       "Configuration with locked datastores\n"
1343
       "Configuration terminal\n")
1344
0
{
1345
0
  return vty_config_enter(vty, false, false, argc == 3);
1346
0
}
1347
1348
/* Enable command */
1349
DEFUN (enable,
1350
       config_enable_cmd,
1351
       "enable",
1352
       "Turn on privileged mode command\n")
1353
0
{
1354
  /* If enable password is NULL, change to ENABLE_NODE */
1355
0
  if ((host.enable == NULL && host.enable_encrypt == NULL)
1356
0
      || vty->type == VTY_SHELL_SERV)
1357
0
    vty->node = ENABLE_NODE;
1358
0
  else
1359
0
    vty->node = AUTH_ENABLE_NODE;
1360
1361
0
  return CMD_SUCCESS;
1362
0
}
1363
1364
/* Disable command */
1365
DEFUN (disable,
1366
       config_disable_cmd,
1367
       "disable",
1368
       "Turn off privileged mode command\n")
1369
0
{
1370
0
  if (vty->node == ENABLE_NODE)
1371
0
    vty->node = VIEW_NODE;
1372
0
  return CMD_SUCCESS;
1373
0
}
1374
1375
/* Down vty node level. */
1376
DEFUN (config_exit,
1377
       config_exit_cmd,
1378
       "exit",
1379
       "Exit current mode and down to previous mode\n")
1380
0
{
1381
0
  cmd_exit(vty);
1382
0
  return CMD_SUCCESS;
1383
0
}
1384
1385
static int root_on_exit(struct vty *vty)
1386
0
{
1387
0
  if (vty_shell(vty))
1388
0
    exit(0);
1389
0
  else
1390
0
    vty->status = VTY_CLOSE;
1391
0
  return 0;
1392
0
}
1393
1394
void cmd_exit(struct vty *vty)
1395
0
{
1396
0
  struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1397
1398
0
  if (cnode->node_exit) {
1399
0
    if (!cnode->node_exit(vty))
1400
0
      return;
1401
0
  }
1402
0
  if (cnode->parent_node)
1403
0
    vty->node = cnode->parent_node;
1404
0
  if (vty->xpath_index > 0 && !cnode->no_xpath)
1405
0
    vty->xpath_index--;
1406
0
}
1407
1408
/* ALIAS_FIXME */
1409
DEFUN (config_quit,
1410
       config_quit_cmd,
1411
       "quit",
1412
       "Exit current mode and down to previous mode\n")
1413
0
{
1414
0
  return config_exit(self, vty, argc, argv);
1415
0
}
1416
1417
1418
/* End of configuration. */
1419
DEFUN (config_end,
1420
       config_end_cmd,
1421
       "end",
1422
       "End current mode and change to enable mode.\n")
1423
0
{
1424
0
  if (vty->config) {
1425
0
    vty_config_exit(vty);
1426
0
    vty->node = ENABLE_NODE;
1427
0
  }
1428
0
  return CMD_SUCCESS;
1429
0
}
1430
1431
/* Show version. */
1432
DEFUN (show_version,
1433
       show_version_cmd,
1434
       "show version",
1435
       SHOW_STR
1436
       "Displays zebra version\n")
1437
0
{
1438
0
  vty_out(vty, "%s %s (%s) on %s(%s).\n", FRR_FULL_NAME, FRR_VERSION,
1439
0
    cmd_hostname_get() ? cmd_hostname_get() : "", cmd_system_get(),
1440
0
    cmd_release_get());
1441
0
  vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1442
0
#ifdef ENABLE_VERSION_BUILD_CONFIG
1443
0
  vty_out(vty, "configured with:\n    %s\n", FRR_CONFIG_ARGS);
1444
0
#endif
1445
0
  return CMD_SUCCESS;
1446
0
}
1447
1448
/* Help display function for all node. */
1449
DEFUN (config_help,
1450
       config_help_cmd,
1451
       "help",
1452
       "Description of the interactive help system\n")
1453
0
{
1454
0
  vty_out(vty,
1455
0
    "Quagga VTY provides advanced help feature.  When you need help,\n\
1456
0
anytime at the command line please press '?'.\n\
1457
0
\n\
1458
0
If nothing matches, the help list will be empty and you must backup\n\
1459
0
 until entering a '?' shows the available options.\n\
1460
0
Two styles of help are provided:\n\
1461
0
1. Full help is available when you are ready to enter a\n\
1462
0
command argument (e.g. 'show ?') and describes each possible\n\
1463
0
argument.\n\
1464
0
2. Partial help is provided when an abbreviated argument is entered\n\
1465
0
   and you want to know what arguments match the input\n\
1466
0
   (e.g. 'show me?'.)\n\n");
1467
0
  return CMD_SUCCESS;
1468
0
}
1469
1470
static void permute(struct graph_node *start, struct vty *vty)
1471
0
{
1472
0
  static struct list *position = NULL;
1473
0
  if (!position)
1474
0
    position = list_new();
1475
1476
0
  struct cmd_token *stok = start->data;
1477
0
  struct graph_node *gnn;
1478
0
  struct listnode *ln;
1479
1480
  // recursive dfs
1481
0
  listnode_add(position, start);
1482
0
  for (unsigned int i = 0; i < vector_active(start->to); i++) {
1483
0
    struct graph_node *gn = vector_slot(start->to, i);
1484
0
    struct cmd_token *tok = gn->data;
1485
0
    if (tok->attr & CMD_ATTR_HIDDEN)
1486
0
      continue;
1487
0
    else if (tok->type == END_TKN || gn == start) {
1488
0
      vty_out(vty, " ");
1489
0
      for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1490
0
        struct cmd_token *tt = gnn->data;
1491
0
        if (tt->type < SPECIAL_TKN)
1492
0
          vty_out(vty, " %s", tt->text);
1493
0
      }
1494
0
      if (gn == start)
1495
0
        vty_out(vty, "...");
1496
0
      vty_out(vty, "\n");
1497
0
    } else {
1498
0
      bool skip = false;
1499
0
      if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1500
0
        for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1501
0
          if (gnn == gn) {
1502
0
            skip = true;
1503
0
            break;
1504
0
          }
1505
0
      if (!skip)
1506
0
        permute(gn, vty);
1507
0
    }
1508
0
  }
1509
0
  list_delete_node(position, listtail(position));
1510
0
}
1511
1512
static void print_cmd(struct vty *vty, const char *cmd)
1513
0
{
1514
0
  int i, j, len = strlen(cmd);
1515
0
  char buf[len + 1];
1516
0
  bool skip = false;
1517
1518
0
  j = 0;
1519
0
  for (i = 0; i < len; i++) {
1520
    /* skip varname */
1521
0
    if (cmd[i] == '$')
1522
0
      skip = true;
1523
0
    else if (strchr(" ()<>[]{}|", cmd[i]))
1524
0
      skip = false;
1525
1526
0
    if (skip)
1527
0
      continue;
1528
1529
0
    if (isspace(cmd[i])) {
1530
      /* skip leading whitespace */
1531
0
      if (i == 0)
1532
0
        continue;
1533
      /* skip trailing whitespace */
1534
0
      if (i == len - 1)
1535
0
        continue;
1536
      /* skip all whitespace after opening brackets or pipe */
1537
0
      if (strchr("(<[{|", cmd[i - 1])) {
1538
0
        while (isspace(cmd[i + 1]))
1539
0
          i++;
1540
0
        continue;
1541
0
      }
1542
      /* skip repeated whitespace */
1543
0
      if (isspace(cmd[i + 1]))
1544
0
        continue;
1545
      /* skip whitespace before closing brackets or pipe */
1546
0
      if (strchr(")>]}|", cmd[i + 1]))
1547
0
        continue;
1548
      /* convert tabs to spaces */
1549
0
      if (cmd[i] == '\t') {
1550
0
        buf[j++] = ' ';
1551
0
        continue;
1552
0
      }
1553
0
    }
1554
1555
0
    buf[j++] = cmd[i];
1556
0
  }
1557
0
  buf[j] = 0;
1558
1559
0
  vty_out(vty, "%s\n", buf);
1560
0
}
1561
1562
int cmd_list_cmds(struct vty *vty, int do_permute)
1563
0
{
1564
0
  struct cmd_node *node = vector_slot(cmdvec, vty->node);
1565
1566
0
  if (do_permute) {
1567
0
    cmd_finalize_node(node);
1568
0
    permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1569
0
  } else {
1570
    /* loop over all commands at this node */
1571
0
    const struct cmd_element *element = NULL;
1572
0
    for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1573
0
         i++)
1574
0
      if ((element = vector_slot(node->cmd_vector, i)) &&
1575
0
          !(element->attr & CMD_ATTR_HIDDEN)) {
1576
0
        vty_out(vty, "    ");
1577
0
        print_cmd(vty, element->string);
1578
0
      }
1579
0
  }
1580
0
  return CMD_SUCCESS;
1581
0
}
1582
1583
/* Help display function for all node. */
1584
DEFUN (config_list,
1585
       config_list_cmd,
1586
       "list [permutations]",
1587
       "Print command list\n"
1588
       "Print all possible command permutations\n")
1589
0
{
1590
0
  return cmd_list_cmds(vty, argc == 2);
1591
0
}
1592
1593
DEFUN (show_commandtree,
1594
       show_commandtree_cmd,
1595
       "show commandtree [permutations]",
1596
       SHOW_STR
1597
       "Show command tree\n"
1598
       "Permutations that we are interested in\n")
1599
0
{
1600
0
  return cmd_list_cmds(vty, argc == 3);
1601
0
}
1602
1603
DEFUN_HIDDEN(show_cli_graph,
1604
             show_cli_graph_cmd,
1605
             "show cli graph",
1606
             SHOW_STR
1607
             "CLI reflection\n"
1608
             "Dump current command space as DOT graph\n")
1609
0
{
1610
0
  struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1611
0
  char *dot;
1612
1613
0
  cmd_finalize_node(cn);
1614
0
  dot = cmd_graph_dump_dot(cn->cmdgraph);
1615
1616
0
  vty_out(vty, "%s\n", dot);
1617
0
  XFREE(MTYPE_TMP, dot);
1618
0
  return CMD_SUCCESS;
1619
0
}
1620
1621
static int vty_write_config(struct vty *vty)
1622
0
{
1623
0
  size_t i;
1624
0
  struct cmd_node *node;
1625
1626
0
  if (host.noconfig)
1627
0
    return CMD_SUCCESS;
1628
1629
0
  nb_cli_show_config_prepare(running_config, false);
1630
1631
0
  if (vty->type == VTY_TERM) {
1632
0
    vty_out(vty, "\nCurrent configuration:\n");
1633
0
    vty_out(vty, "!\n");
1634
0
  }
1635
1636
0
  if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1637
0
    vty_out(vty, "! loaded from %s\n", frr_defaults_version());
1638
0
  vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1639
0
  vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
1640
0
  vty_out(vty, "!\n");
1641
1642
0
  for (i = 0; i < vector_active(cmdvec); i++)
1643
0
    if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1644
0
      if ((*node->config_write)(vty))
1645
0
        vty_out(vty, "!\n");
1646
0
    }
1647
1648
0
  if (vty->type == VTY_TERM) {
1649
0
    vty_out(vty, "end\n");
1650
0
  }
1651
1652
0
  return CMD_SUCCESS;
1653
0
}
1654
1655
static int file_write_config(struct vty *vty)
1656
0
{
1657
0
  int fd, dirfd;
1658
0
  char *config_file, *slash;
1659
0
  char *config_file_tmp = NULL;
1660
0
  char *config_file_sav = NULL;
1661
0
  int ret = CMD_WARNING;
1662
0
  struct vty *file_vty;
1663
0
  struct stat conf_stat;
1664
1665
0
  if (host.noconfig)
1666
0
    return CMD_SUCCESS;
1667
1668
  /* Check and see if we are operating under vtysh configuration */
1669
0
  if (host.config == NULL) {
1670
0
    vty_out(vty,
1671
0
      "Can't save to configuration file, using vtysh.\n");
1672
0
    return CMD_WARNING;
1673
0
  }
1674
1675
  /* Get filename. */
1676
0
  config_file = host.config;
1677
1678
#ifndef O_DIRECTORY
1679
#define O_DIRECTORY 0
1680
#endif
1681
0
  slash = strrchr(config_file, '/');
1682
0
  if (slash) {
1683
0
    char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1684
0
    config_dir[slash - config_file] = '\0';
1685
0
    dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1686
0
    XFREE(MTYPE_TMP, config_dir);
1687
0
  } else
1688
0
    dirfd = open(".", O_DIRECTORY | O_RDONLY);
1689
  /* if dirfd is invalid, directory sync fails, but we're still OK */
1690
1691
0
  size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1692
0
  config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1693
0
  strlcpy(config_file_sav, config_file, config_file_sav_sz);
1694
0
  strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
1695
1696
1697
0
  config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1698
0
  snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX",
1699
0
     config_file);
1700
1701
  /* Open file to configuration write. */
1702
0
  fd = mkstemp(config_file_tmp);
1703
0
  if (fd < 0) {
1704
0
    vty_out(vty, "Can't open configuration file %s.\n",
1705
0
      config_file_tmp);
1706
0
    goto finished;
1707
0
  }
1708
0
  if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1709
0
    vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1710
0
      config_file_tmp, safe_strerror(errno), errno);
1711
0
    goto finished;
1712
0
  }
1713
1714
  /* Make vty for configuration file. */
1715
0
  file_vty = vty_new();
1716
0
  file_vty->wfd = fd;
1717
0
  file_vty->type = VTY_FILE;
1718
1719
  /* Config file header print. */
1720
0
  vty_out(file_vty, "!\n! Zebra configuration saved from vty\n!   ");
1721
0
  vty_time_print(file_vty, 1);
1722
0
  vty_out(file_vty, "!\n");
1723
0
  vty_write_config(file_vty);
1724
0
  vty_close(file_vty);
1725
1726
0
  if (stat(config_file, &conf_stat) >= 0) {
1727
0
    if (unlink(config_file_sav) != 0)
1728
0
      if (errno != ENOENT) {
1729
0
        vty_out(vty,
1730
0
          "Can't unlink backup configuration file %s.\n",
1731
0
          config_file_sav);
1732
0
        goto finished;
1733
0
      }
1734
0
    if (link(config_file, config_file_sav) != 0) {
1735
0
      vty_out(vty,
1736
0
        "Can't backup old configuration file %s.\n",
1737
0
        config_file_sav);
1738
0
      goto finished;
1739
0
    }
1740
0
    if (dirfd >= 0)
1741
0
      fsync(dirfd);
1742
0
  }
1743
0
  if (rename(config_file_tmp, config_file) != 0) {
1744
0
    vty_out(vty, "Can't save configuration file %s.\n",
1745
0
      config_file);
1746
0
    goto finished;
1747
0
  }
1748
0
  if (dirfd >= 0)
1749
0
    fsync(dirfd);
1750
1751
0
  vty_out(vty, "Configuration saved to %s\n", config_file);
1752
0
  ret = CMD_SUCCESS;
1753
1754
0
finished:
1755
0
  if (ret != CMD_SUCCESS)
1756
0
    unlink(config_file_tmp);
1757
0
  if (dirfd >= 0)
1758
0
    close(dirfd);
1759
0
  XFREE(MTYPE_TMP, config_file_tmp);
1760
0
  XFREE(MTYPE_TMP, config_file_sav);
1761
0
  return ret;
1762
0
}
1763
1764
/* Write current configuration into file. */
1765
1766
DEFUN (config_write,
1767
       config_write_cmd,
1768
       "write [<file|memory|terminal>]",
1769
       "Write running configuration to memory, network, or terminal\n"
1770
       "Write to configuration file\n"
1771
       "Write configuration currently in memory\n"
1772
       "Write configuration to terminal\n")
1773
0
{
1774
0
  const int idx_type = 1;
1775
1776
  // if command was 'write terminal' or 'write memory'
1777
0
  if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1778
0
    return vty_write_config(vty);
1779
0
  }
1780
1781
0
  return file_write_config(vty);
1782
0
}
1783
1784
/* ALIAS_FIXME for 'write <terminal|memory>' */
1785
DEFUN (show_running_config,
1786
       show_running_config_cmd,
1787
       "show running-config",
1788
       SHOW_STR
1789
       "running configuration (same as write terminal)\n")
1790
0
{
1791
0
  return vty_write_config(vty);
1792
0
}
1793
1794
/* ALIAS_FIXME for 'write file' */
1795
DEFUN (copy_runningconf_startupconf,
1796
       copy_runningconf_startupconf_cmd,
1797
       "copy running-config startup-config",
1798
       "Copy configuration\n"
1799
       "Copy running config to... \n"
1800
       "Copy running config to startup config (same as write file/memory)\n")
1801
0
{
1802
0
  return file_write_config(vty);
1803
0
}
1804
/** -- **/
1805
1806
/* Write startup configuration into the terminal. */
1807
DEFUN (show_startup_config,
1808
       show_startup_config_cmd,
1809
       "show startup-config",
1810
       SHOW_STR
1811
       "Contents of startup configuration\n")
1812
0
{
1813
0
  char buf[BUFSIZ];
1814
0
  FILE *confp;
1815
1816
0
  if (host.noconfig)
1817
0
    return CMD_SUCCESS;
1818
0
  if (host.config == NULL)
1819
0
    return CMD_WARNING;
1820
1821
0
  confp = fopen(host.config, "r");
1822
0
  if (confp == NULL) {
1823
0
    vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1824
0
      host.config, safe_strerror(errno));
1825
0
    return CMD_WARNING;
1826
0
  }
1827
1828
0
  while (fgets(buf, BUFSIZ, confp)) {
1829
0
    char *cp = buf;
1830
1831
0
    while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1832
0
      cp++;
1833
0
    *cp = '\0';
1834
1835
0
    vty_out(vty, "%s\n", buf);
1836
0
  }
1837
1838
0
  fclose(confp);
1839
1840
0
  return CMD_SUCCESS;
1841
0
}
1842
1843
int cmd_domainname_set(const char *domainname)
1844
0
{
1845
0
  XFREE(MTYPE_HOST, host.domainname);
1846
0
  host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1847
0
  return CMD_SUCCESS;
1848
0
}
1849
1850
/* Hostname configuration */
1851
DEFUN(config_domainname,
1852
      domainname_cmd,
1853
      "domainname WORD",
1854
      "Set system's domain name\n"
1855
      "This system's domain name\n")
1856
0
{
1857
0
  struct cmd_token *word = argv[1];
1858
1859
0
  if (!isalpha((unsigned char)word->arg[0])) {
1860
0
    vty_out(vty, "Please specify string starting with alphabet\n");
1861
0
    return CMD_WARNING_CONFIG_FAILED;
1862
0
  }
1863
1864
0
  return cmd_domainname_set(word->arg);
1865
0
}
1866
1867
DEFUN(config_no_domainname,
1868
      no_domainname_cmd,
1869
      "no domainname [DOMAINNAME]",
1870
      NO_STR
1871
      "Reset system's domain name\n"
1872
      "domain name of this router\n")
1873
0
{
1874
0
  return cmd_domainname_set(NULL);
1875
0
}
1876
1877
int cmd_hostname_set(const char *hostname)
1878
0
{
1879
0
  XFREE(MTYPE_HOST, host.name);
1880
0
  host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1881
0
  return CMD_SUCCESS;
1882
0
}
1883
1884
/* Hostname configuration */
1885
DEFUN (config_hostname,
1886
       hostname_cmd,
1887
       "hostname WORD",
1888
       "Set system's network name\n"
1889
       "This system's network name\n")
1890
0
{
1891
0
  struct cmd_token *word = argv[1];
1892
1893
0
  if (!isalnum((unsigned char)word->arg[0])) {
1894
0
    vty_out(vty,
1895
0
        "Please specify string starting with alphabet or number\n");
1896
0
    return CMD_WARNING_CONFIG_FAILED;
1897
0
  }
1898
1899
  /* With reference to RFC 1123 Section 2.1 */
1900
0
  if (strlen(word->arg) > HOSTNAME_LEN) {
1901
0
    vty_out(vty, "Hostname length should be less than %d chars\n",
1902
0
      HOSTNAME_LEN);
1903
0
    return CMD_WARNING_CONFIG_FAILED;
1904
0
  }
1905
1906
0
  return cmd_hostname_set(word->arg);
1907
0
}
1908
1909
DEFUN (config_no_hostname,
1910
       no_hostname_cmd,
1911
       "no hostname [HOSTNAME]",
1912
       NO_STR
1913
       "Reset system's network name\n"
1914
       "Host name of this router\n")
1915
0
{
1916
0
  return cmd_hostname_set(NULL);
1917
0
}
1918
1919
/* VTY interface password set. */
1920
DEFUN (config_password,
1921
       password_cmd,
1922
       "password [(8-8)] WORD",
1923
       "Modify the terminal connection password\n"
1924
       "Specifies a HIDDEN password will follow\n"
1925
       "The password string\n")
1926
0
{
1927
0
  int idx_8 = 1;
1928
0
  int idx_word = 2;
1929
0
  if (argc == 3) // '8' was specified
1930
0
  {
1931
0
    if (host.password)
1932
0
      XFREE(MTYPE_HOST, host.password);
1933
0
    host.password = NULL;
1934
0
    if (host.password_encrypt)
1935
0
      XFREE(MTYPE_HOST, host.password_encrypt);
1936
0
    host.password_encrypt =
1937
0
      XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1938
0
    return CMD_SUCCESS;
1939
0
  }
1940
1941
0
  if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1942
0
    vty_out(vty,
1943
0
      "Please specify string starting with alphanumeric\n");
1944
0
    return CMD_WARNING_CONFIG_FAILED;
1945
0
  }
1946
1947
0
  if (host.password)
1948
0
    XFREE(MTYPE_HOST, host.password);
1949
0
  host.password = NULL;
1950
1951
0
  if (host.encrypt) {
1952
0
    if (host.password_encrypt)
1953
0
      XFREE(MTYPE_HOST, host.password_encrypt);
1954
0
    host.password_encrypt =
1955
0
      XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1956
0
  } else
1957
0
    host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1958
1959
0
  return CMD_SUCCESS;
1960
0
}
1961
1962
/* VTY interface password delete. */
1963
DEFUN (no_config_password,
1964
       no_password_cmd,
1965
       "no password",
1966
       NO_STR
1967
       "Modify the terminal connection password\n")
1968
0
{
1969
0
  bool warned = false;
1970
1971
0
  if (host.password) {
1972
0
    if (!vty_shell_serv(vty)) {
1973
0
      vty_out(vty, NO_PASSWD_CMD_WARNING);
1974
0
      warned = true;
1975
0
    }
1976
0
    XFREE(MTYPE_HOST, host.password);
1977
0
  }
1978
0
  host.password = NULL;
1979
1980
0
  if (host.password_encrypt) {
1981
0
    if (!warned && !vty_shell_serv(vty))
1982
0
      vty_out(vty, NO_PASSWD_CMD_WARNING);
1983
0
    XFREE(MTYPE_HOST, host.password_encrypt);
1984
0
  }
1985
0
  host.password_encrypt = NULL;
1986
1987
0
  return CMD_SUCCESS;
1988
0
}
1989
1990
/* VTY enable password set. */
1991
DEFUN (config_enable_password,
1992
       enable_password_cmd,
1993
       "enable password [(8-8)] WORD",
1994
       "Modify enable password parameters\n"
1995
       "Assign the privileged level password\n"
1996
       "Specifies a HIDDEN password will follow\n"
1997
       "The HIDDEN 'enable' password string\n")
1998
0
{
1999
0
  int idx_8 = 2;
2000
0
  int idx_word = 3;
2001
2002
  /* Crypt type is specified. */
2003
0
  if (argc == 4) {
2004
0
    if (argv[idx_8]->arg[0] == '8') {
2005
0
      if (host.enable)
2006
0
        XFREE(MTYPE_HOST, host.enable);
2007
0
      host.enable = NULL;
2008
2009
0
      if (host.enable_encrypt)
2010
0
        XFREE(MTYPE_HOST, host.enable_encrypt);
2011
0
      host.enable_encrypt =
2012
0
        XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
2013
2014
0
      return CMD_SUCCESS;
2015
0
    } else {
2016
0
      vty_out(vty, "Unknown encryption type.\n");
2017
0
      return CMD_WARNING_CONFIG_FAILED;
2018
0
    }
2019
0
  }
2020
2021
0
  if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
2022
0
    vty_out(vty,
2023
0
      "Please specify string starting with alphanumeric\n");
2024
0
    return CMD_WARNING_CONFIG_FAILED;
2025
0
  }
2026
2027
0
  if (host.enable)
2028
0
    XFREE(MTYPE_HOST, host.enable);
2029
0
  host.enable = NULL;
2030
2031
  /* Plain password input. */
2032
0
  if (host.encrypt) {
2033
0
    if (host.enable_encrypt)
2034
0
      XFREE(MTYPE_HOST, host.enable_encrypt);
2035
0
    host.enable_encrypt =
2036
0
      XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
2037
0
  } else
2038
0
    host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
2039
2040
0
  return CMD_SUCCESS;
2041
0
}
2042
2043
/* VTY enable password delete. */
2044
DEFUN (no_config_enable_password,
2045
       no_enable_password_cmd,
2046
       "no enable password",
2047
       NO_STR
2048
       "Modify enable password parameters\n"
2049
       "Assign the privileged level password\n")
2050
0
{
2051
0
  bool warned = false;
2052
2053
0
  if (host.enable) {
2054
0
    if (!vty_shell_serv(vty)) {
2055
0
      vty_out(vty, NO_PASSWD_CMD_WARNING);
2056
0
      warned = true;
2057
0
    }
2058
0
    XFREE(MTYPE_HOST, host.enable);
2059
0
  }
2060
0
  host.enable = NULL;
2061
2062
0
  if (host.enable_encrypt) {
2063
0
    if (!warned && !vty_shell_serv(vty))
2064
0
      vty_out(vty, NO_PASSWD_CMD_WARNING);
2065
0
    XFREE(MTYPE_HOST, host.enable_encrypt);
2066
0
  }
2067
0
  host.enable_encrypt = NULL;
2068
2069
0
  return CMD_SUCCESS;
2070
0
}
2071
2072
DEFUN (service_password_encrypt,
2073
       service_password_encrypt_cmd,
2074
       "service password-encryption",
2075
       "Set up miscellaneous service\n"
2076
       "Enable encrypted passwords\n")
2077
0
{
2078
0
  if (host.encrypt)
2079
0
    return CMD_SUCCESS;
2080
2081
0
  host.encrypt = 1;
2082
2083
0
  if (host.password) {
2084
0
    if (host.password_encrypt)
2085
0
      XFREE(MTYPE_HOST, host.password_encrypt);
2086
0
    host.password_encrypt =
2087
0
      XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2088
0
  }
2089
0
  if (host.enable) {
2090
0
    if (host.enable_encrypt)
2091
0
      XFREE(MTYPE_HOST, host.enable_encrypt);
2092
0
    host.enable_encrypt =
2093
0
      XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2094
0
  }
2095
2096
0
  return CMD_SUCCESS;
2097
0
}
2098
2099
DEFUN (no_service_password_encrypt,
2100
       no_service_password_encrypt_cmd,
2101
       "no service password-encryption",
2102
       NO_STR
2103
       "Set up miscellaneous service\n"
2104
       "Enable encrypted passwords\n")
2105
0
{
2106
0
  if (!host.encrypt)
2107
0
    return CMD_SUCCESS;
2108
2109
0
  host.encrypt = 0;
2110
2111
0
  if (host.password_encrypt)
2112
0
    XFREE(MTYPE_HOST, host.password_encrypt);
2113
0
  host.password_encrypt = NULL;
2114
2115
0
  if (host.enable_encrypt)
2116
0
    XFREE(MTYPE_HOST, host.enable_encrypt);
2117
0
  host.enable_encrypt = NULL;
2118
2119
0
  return CMD_SUCCESS;
2120
0
}
2121
2122
DEFUN (config_terminal_length,
2123
       config_terminal_length_cmd,
2124
       "terminal length (0-512)",
2125
       "Set terminal line parameters\n"
2126
       "Set number of lines on a screen\n"
2127
       "Number of lines on screen (0 for no pausing)\n")
2128
0
{
2129
0
  int idx_number = 2;
2130
2131
0
  vty->lines = atoi(argv[idx_number]->arg);
2132
2133
0
  return CMD_SUCCESS;
2134
0
}
2135
2136
DEFUN (config_terminal_no_length,
2137
       config_terminal_no_length_cmd,
2138
       "terminal no length",
2139
       "Set terminal line parameters\n"
2140
       NO_STR
2141
       "Set number of lines on a screen\n")
2142
0
{
2143
0
  vty->lines = -1;
2144
0
  return CMD_SUCCESS;
2145
0
}
2146
2147
DEFUN (service_terminal_length,
2148
       service_terminal_length_cmd,
2149
       "service terminal-length (0-512)",
2150
       "Set up miscellaneous service\n"
2151
       "System wide terminal length configuration\n"
2152
       "Number of lines of VTY (0 means no line control)\n")
2153
0
{
2154
0
  int idx_number = 2;
2155
2156
0
  host.lines = atoi(argv[idx_number]->arg);
2157
2158
0
  return CMD_SUCCESS;
2159
0
}
2160
2161
DEFUN (no_service_terminal_length,
2162
       no_service_terminal_length_cmd,
2163
       "no service terminal-length [(0-512)]",
2164
       NO_STR
2165
       "Set up miscellaneous service\n"
2166
       "System wide terminal length configuration\n"
2167
       "Number of lines of VTY (0 means no line control)\n")
2168
0
{
2169
0
  host.lines = -1;
2170
0
  return CMD_SUCCESS;
2171
0
}
2172
2173
DEFUN_HIDDEN (do_echo,
2174
              echo_cmd,
2175
              "echo MESSAGE...",
2176
              "Echo a message back to the vty\n"
2177
              "The message to echo\n")
2178
0
{
2179
0
  char *message;
2180
2181
0
  vty_out(vty, "%s\n",
2182
0
    ((message = argv_concat(argv, argc, 1)) ? message : ""));
2183
0
  if (message)
2184
0
    XFREE(MTYPE_TMP, message);
2185
0
  return CMD_SUCCESS;
2186
0
}
2187
2188
DEFUN (config_logmsg,
2189
       config_logmsg_cmd,
2190
       "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2191
       "Send a message to enabled logging destinations\n"
2192
       LOG_LEVEL_DESC
2193
       "The message to send\n")
2194
0
{
2195
0
  int idx_log_level = 1;
2196
0
  int idx_message = 2;
2197
0
  int level;
2198
0
  char *message;
2199
2200
0
  level = log_level_match(argv[idx_log_level]->arg);
2201
0
  if (level == ZLOG_DISABLED)
2202
0
    return CMD_ERR_NO_MATCH;
2203
2204
0
  zlog(level, "%s",
2205
0
       ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2206
0
  if (message)
2207
0
    XFREE(MTYPE_TMP, message);
2208
2209
0
  return CMD_SUCCESS;
2210
0
}
2211
2212
DEFUN (debug_memstats,
2213
       debug_memstats_cmd,
2214
       "[no] debug memstats-at-exit",
2215
       NO_STR
2216
       DEBUG_STR
2217
       "Print memory type statistics at exit\n")
2218
0
{
2219
0
  debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2220
0
  return CMD_SUCCESS;
2221
0
}
2222
2223
int cmd_banner_motd_file(const char *file)
2224
0
{
2225
0
  int success = CMD_SUCCESS;
2226
0
  char p[PATH_MAX];
2227
0
  char *rpath;
2228
0
  char *in;
2229
2230
0
  rpath = realpath(file, p);
2231
0
  if (!rpath)
2232
0
    return CMD_ERR_NO_FILE;
2233
0
  in = strstr(rpath, SYSCONFDIR);
2234
0
  if (in == rpath) {
2235
0
    XFREE(MTYPE_HOST, host.motdfile);
2236
0
    host.motdfile = XSTRDUP(MTYPE_HOST, file);
2237
0
  } else
2238
0
    success = CMD_WARNING_CONFIG_FAILED;
2239
2240
0
  return success;
2241
0
}
2242
2243
void cmd_banner_motd_line(const char *line)
2244
4
{
2245
4
  XFREE(MTYPE_HOST, host.motd);
2246
4
  host.motd = XSTRDUP(MTYPE_HOST, line);
2247
4
}
2248
2249
DEFUN (banner_motd_file,
2250
       banner_motd_file_cmd,
2251
       "banner motd file FILE",
2252
       "Set banner\n"
2253
       "Banner for motd\n"
2254
       "Banner from a file\n"
2255
       "Filename\n")
2256
0
{
2257
0
  int idx_file = 3;
2258
0
  const char *filename = argv[idx_file]->arg;
2259
0
  int cmd = cmd_banner_motd_file(filename);
2260
2261
0
  if (cmd == CMD_ERR_NO_FILE)
2262
0
    vty_out(vty, "%s does not exist\n", filename);
2263
0
  else if (cmd == CMD_WARNING_CONFIG_FAILED)
2264
0
    vty_out(vty, "%s must be in %s\n", filename, SYSCONFDIR);
2265
2266
0
  return cmd;
2267
0
}
2268
2269
DEFUN (banner_motd_line,
2270
       banner_motd_line_cmd,
2271
       "banner motd line LINE...",
2272
       "Set banner\n"
2273
       "Banner for motd\n"
2274
       "Banner from an input\n"
2275
       "Text\n")
2276
0
{
2277
0
  int idx = 0;
2278
0
  char *motd;
2279
2280
0
  argv_find(argv, argc, "LINE", &idx);
2281
0
  motd = argv_concat(argv, argc, idx);
2282
2283
0
  cmd_banner_motd_line(motd);
2284
0
  XFREE(MTYPE_TMP, motd);
2285
2286
0
  return CMD_SUCCESS;
2287
0
}
2288
2289
DEFUN (banner_motd_default,
2290
       banner_motd_default_cmd,
2291
       "banner motd default",
2292
       "Set banner string\n"
2293
       "Strings for motd\n"
2294
       "Default string\n")
2295
0
{
2296
0
  cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2297
0
  return CMD_SUCCESS;
2298
0
}
2299
2300
DEFUN (no_banner_motd,
2301
       no_banner_motd_cmd,
2302
       "no banner motd",
2303
       NO_STR
2304
       "Set banner string\n"
2305
       "Strings for motd\n")
2306
0
{
2307
0
  host.motd = NULL;
2308
0
  if (host.motdfile)
2309
0
    XFREE(MTYPE_HOST, host.motdfile);
2310
0
  host.motdfile = NULL;
2311
0
  return CMD_SUCCESS;
2312
0
}
2313
2314
DEFUN(allow_reserved_ranges, allow_reserved_ranges_cmd, "allow-reserved-ranges",
2315
      "Allow using IPv4 (Class E) reserved IP space\n")
2316
0
{
2317
0
  host.allow_reserved_ranges = true;
2318
0
  return CMD_SUCCESS;
2319
0
}
2320
2321
DEFUN(no_allow_reserved_ranges, no_allow_reserved_ranges_cmd,
2322
      "no allow-reserved-ranges",
2323
      NO_STR "Allow using IPv4 (Class E) reserved IP space\n")
2324
0
{
2325
0
  host.allow_reserved_ranges = false;
2326
0
  return CMD_SUCCESS;
2327
0
}
2328
2329
int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc)
2330
0
{
2331
0
  const struct cmd_node *node;
2332
0
  const struct cmd_element *cli;
2333
0
  vector clis;
2334
2335
0
  regex_t exp = {};
2336
2337
0
  char *pattern = argv_concat(argv, argc, 1);
2338
0
  int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
2339
0
  XFREE(MTYPE_TMP, pattern);
2340
2341
0
  if (cr != 0) {
2342
0
    switch (cr) {
2343
0
    case REG_BADBR:
2344
0
      vty_out(vty, "%% Invalid {...} expression\n");
2345
0
      break;
2346
0
    case REG_BADRPT:
2347
0
      vty_out(vty, "%% Bad repetition operator\n");
2348
0
      break;
2349
0
    case REG_BADPAT:
2350
0
      vty_out(vty, "%% Regex syntax error\n");
2351
0
      break;
2352
0
    case REG_ECOLLATE:
2353
0
      vty_out(vty, "%% Invalid collating element\n");
2354
0
      break;
2355
0
    case REG_ECTYPE:
2356
0
      vty_out(vty, "%% Invalid character class name\n");
2357
0
      break;
2358
0
    case REG_EESCAPE:
2359
0
      vty_out(vty,
2360
0
        "%% Regex ended with escape character (\\)\n");
2361
0
      break;
2362
0
    case REG_ESUBREG:
2363
0
      vty_out(vty,
2364
0
        "%% Invalid number in \\digit construction\n");
2365
0
      break;
2366
0
    case REG_EBRACK:
2367
0
      vty_out(vty, "%% Unbalanced square brackets\n");
2368
0
      break;
2369
0
    case REG_EPAREN:
2370
0
      vty_out(vty, "%% Unbalanced parentheses\n");
2371
0
      break;
2372
0
    case REG_EBRACE:
2373
0
      vty_out(vty, "%% Unbalanced braces\n");
2374
0
      break;
2375
0
    case REG_ERANGE:
2376
0
      vty_out(vty,
2377
0
        "%% Invalid endpoint in range expression\n");
2378
0
      break;
2379
0
    case REG_ESPACE:
2380
0
      vty_out(vty, "%% Failed to compile (out of memory)\n");
2381
0
      break;
2382
0
    }
2383
2384
0
    goto done;
2385
0
  }
2386
2387
2388
0
  for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2389
0
    node = vector_slot(cmdvec, i);
2390
0
    if (!node)
2391
0
      continue;
2392
0
    clis = node->cmd_vector;
2393
0
    for (unsigned int j = 0; j < vector_active(clis); j++) {
2394
0
      cli = vector_slot(clis, j);
2395
2396
0
      if (regexec(&exp, cli->string, 0, NULL, 0) == 0) {
2397
0
        vty_out(vty, "  (%s)  ", node->name);
2398
0
        print_cmd(vty, cli->string);
2399
0
      }
2400
0
    }
2401
0
  }
2402
2403
0
done:
2404
0
  regfree(&exp);
2405
0
  return CMD_SUCCESS;
2406
0
}
2407
2408
DEFUN(find,
2409
      find_cmd,
2410
      "find REGEX...",
2411
      "Find CLI command matching a regular expression\n"
2412
      "Search pattern (POSIX regex)\n")
2413
0
{
2414
0
  return cmd_find_cmds(vty, argv, argc);
2415
0
}
2416
2417
#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2418
DEFUN(script, script_cmd, "script SCRIPT FUNCTION",
2419
      "Test command - execute a function in a script\n"
2420
      "Script name (same as filename in /etc/frr/scripts/)\n"
2421
      "Function name (in the script)\n")
2422
{
2423
  struct prefix p;
2424
2425
  (void)str2prefix("1.2.3.4/24", &p);
2426
  struct frrscript *fs = frrscript_new(argv[1]->arg);
2427
2428
  if (frrscript_load(fs, argv[2]->arg, NULL)) {
2429
    vty_out(vty,
2430
      "/etc/frr/scripts/%s.lua or function '%s' not found\n",
2431
      argv[1]->arg, argv[2]->arg);
2432
  }
2433
2434
  int ret = frrscript_call(fs, argv[2]->arg, ("p", &p));
2435
  char buf[40];
2436
  prefix2str(&p, buf, sizeof(buf));
2437
  vty_out(vty, "p: %s\n", buf);
2438
  vty_out(vty, "Script result: %d\n", ret);
2439
2440
  frrscript_delete(fs);
2441
2442
  return CMD_SUCCESS;
2443
}
2444
#endif
2445
2446
/* Set config filename.  Called from vty.c */
2447
void host_config_set(const char *filename)
2448
0
{
2449
0
  XFREE(MTYPE_HOST, host.config);
2450
0
  host.config = XSTRDUP(MTYPE_HOST, filename);
2451
0
}
2452
2453
const char *host_config_get(void)
2454
0
{
2455
0
  return host.config;
2456
0
}
2457
2458
void cmd_show_lib_debugs(struct vty *vty)
2459
0
{
2460
0
  route_map_show_debug(vty);
2461
0
  mgmt_debug_be_client_show_debug(vty);
2462
0
  mgmt_debug_fe_client_show_debug(vty);
2463
0
}
2464
2465
void install_default(enum node_type node)
2466
21
{
2467
21
  _install_element(node, &config_exit_cmd);
2468
21
  _install_element(node, &config_quit_cmd);
2469
21
  _install_element(node, &config_end_cmd);
2470
21
  _install_element(node, &config_help_cmd);
2471
21
  _install_element(node, &config_list_cmd);
2472
21
  _install_element(node, &show_cli_graph_cmd);
2473
21
  _install_element(node, &find_cmd);
2474
2475
21
  _install_element(node, &config_write_cmd);
2476
21
  _install_element(node, &show_running_config_cmd);
2477
2478
21
  _install_element(node, &autocomplete_cmd);
2479
2480
21
  nb_cli_install_default(node);
2481
21
}
2482
2483
/* Initialize command interface. Install basic nodes and commands.
2484
 *
2485
 * terminal = 0 -- vtysh / no logging, no config control
2486
 * terminal = 1 -- normal daemon
2487
 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2488
void cmd_init(int terminal)
2489
4
{
2490
4
  struct utsname names;
2491
2492
4
  uname(&names);
2493
4
  qobj_init();
2494
2495
  /* register command preprocessors */
2496
4
  hook_register(cmd_execute, handle_pipe_action);
2497
4
  hook_register(cmd_execute_done, handle_pipe_action_done);
2498
2499
4
  varhandlers = list_new();
2500
2501
  /* Allocate initial top vector of commands. */
2502
4
  cmdvec = vector_init(VECTOR_MIN_SIZE);
2503
2504
  /* Default host value settings. */
2505
4
  host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2506
4
  host.system = XSTRDUP(MTYPE_HOST, names.sysname);
2507
4
  host.release = XSTRDUP(MTYPE_HOST, names.release);
2508
4
  host.version = XSTRDUP(MTYPE_HOST, names.version);
2509
2510
4
#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2511
4
  if ((strcmp(names.domainname, "(none)") == 0))
2512
4
    host.domainname = NULL;
2513
0
  else
2514
0
    host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2515
#else
2516
  host.domainname = NULL;
2517
#endif
2518
4
  host.password = NULL;
2519
4
  host.enable = NULL;
2520
4
  host.config = NULL;
2521
4
  host.noconfig = (terminal < 0);
2522
4
  host.lines = -1;
2523
4
  cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2524
4
  host.motdfile = NULL;
2525
4
  host.allow_reserved_ranges = false;
2526
2527
  /* Install top nodes. */
2528
4
  install_node(&view_node);
2529
4
  install_node(&enable_node);
2530
4
  install_node(&auth_node);
2531
4
  install_node(&auth_enable_node);
2532
4
  install_node(&config_node);
2533
2534
  /* Each node's basic commands. */
2535
4
  install_element(VIEW_NODE, &show_version_cmd);
2536
4
  install_element(ENABLE_NODE, &show_startup_config_cmd);
2537
2538
4
  if (terminal) {
2539
4
    install_element(ENABLE_NODE, &debug_memstats_cmd);
2540
2541
4
    install_element(VIEW_NODE, &config_list_cmd);
2542
4
    install_element(VIEW_NODE, &config_exit_cmd);
2543
4
    install_element(VIEW_NODE, &config_quit_cmd);
2544
4
    install_element(VIEW_NODE, &config_help_cmd);
2545
4
    install_element(VIEW_NODE, &config_enable_cmd);
2546
4
    install_element(VIEW_NODE, &config_terminal_length_cmd);
2547
4
    install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2548
4
    install_element(VIEW_NODE, &show_commandtree_cmd);
2549
4
    install_element(VIEW_NODE, &echo_cmd);
2550
4
    install_element(VIEW_NODE, &autocomplete_cmd);
2551
4
    install_element(VIEW_NODE, &find_cmd);
2552
#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
2553
    install_element(VIEW_NODE, &script_cmd);
2554
#endif
2555
2556
2557
4
    install_element(ENABLE_NODE, &config_end_cmd);
2558
4
    install_element(ENABLE_NODE, &config_disable_cmd);
2559
4
    install_element(ENABLE_NODE, &config_terminal_cmd);
2560
4
    install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2561
4
    install_element(ENABLE_NODE, &config_write_cmd);
2562
4
    install_element(ENABLE_NODE, &show_running_config_cmd);
2563
4
    install_element(ENABLE_NODE, &config_logmsg_cmd);
2564
2565
4
    install_default(CONFIG_NODE);
2566
2567
4
    event_cmd_init();
2568
4
    workqueue_cmd_init();
2569
4
    hash_cmd_init();
2570
4
  }
2571
2572
4
  install_element(CONFIG_NODE, &hostname_cmd);
2573
4
  install_element(CONFIG_NODE, &no_hostname_cmd);
2574
4
  install_element(CONFIG_NODE, &domainname_cmd);
2575
4
  install_element(CONFIG_NODE, &no_domainname_cmd);
2576
2577
4
  if (terminal > 0) {
2578
4
    full_cli = true;
2579
2580
4
    install_element(CONFIG_NODE, &debug_memstats_cmd);
2581
2582
4
    install_element(CONFIG_NODE, &password_cmd);
2583
4
    install_element(CONFIG_NODE, &no_password_cmd);
2584
4
    install_element(CONFIG_NODE, &enable_password_cmd);
2585
4
    install_element(CONFIG_NODE, &no_enable_password_cmd);
2586
2587
4
    install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2588
4
    install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2589
4
    install_element(CONFIG_NODE, &banner_motd_default_cmd);
2590
4
    install_element(CONFIG_NODE, &banner_motd_file_cmd);
2591
4
    install_element(CONFIG_NODE, &banner_motd_line_cmd);
2592
4
    install_element(CONFIG_NODE, &no_banner_motd_cmd);
2593
4
    install_element(CONFIG_NODE, &service_terminal_length_cmd);
2594
4
    install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2595
4
    install_element(CONFIG_NODE, &allow_reserved_ranges_cmd);
2596
4
    install_element(CONFIG_NODE, &no_allow_reserved_ranges_cmd);
2597
2598
4
    log_cmd_init();
2599
4
    vrf_install_commands();
2600
4
  }
2601
2602
#ifdef DEV_BUILD
2603
  grammar_sandbox_init();
2604
#endif
2605
4
}
2606
2607
void cmd_terminate(void)
2608
0
{
2609
0
  struct cmd_node *cmd_node;
2610
2611
0
  hook_unregister(cmd_execute, handle_pipe_action);
2612
0
  hook_unregister(cmd_execute_done, handle_pipe_action_done);
2613
2614
0
  if (cmdvec) {
2615
0
    for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2616
0
      if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2617
        // deleting the graph delets the cmd_element as
2618
        // well
2619
0
        graph_delete_graph(cmd_node->cmdgraph);
2620
0
        vector_free(cmd_node->cmd_vector);
2621
0
        hash_clean_and_free(&cmd_node->cmd_hash, NULL);
2622
0
      }
2623
2624
0
    vector_free(cmdvec);
2625
0
    cmdvec = NULL;
2626
0
  }
2627
2628
0
  XFREE(MTYPE_HOST, host.name);
2629
0
  XFREE(MTYPE_HOST, host.system);
2630
0
  XFREE(MTYPE_HOST, host.release);
2631
0
  XFREE(MTYPE_HOST, host.version);
2632
0
  XFREE(MTYPE_HOST, host.domainname);
2633
0
  XFREE(MTYPE_HOST, host.password);
2634
0
  XFREE(MTYPE_HOST, host.password_encrypt);
2635
0
  XFREE(MTYPE_HOST, host.enable);
2636
0
  XFREE(MTYPE_HOST, host.enable_encrypt);
2637
0
  XFREE(MTYPE_HOST, host.motdfile);
2638
0
  XFREE(MTYPE_HOST, host.config);
2639
0
  XFREE(MTYPE_HOST, host.motd);
2640
2641
0
  list_delete(&varhandlers);
2642
0
  qobj_finish();
2643
0
}