Coverage Report

Created: 2026-01-21 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/vty.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Virtual terminal [aka TeletYpe] interface routine.
4
 * Copyright (C) 1997, 98 Kunihiro Ishiguro
5
 */
6
7
#include <zebra.h>
8
9
#include <lib/version.h>
10
#include <sys/types.h>
11
#include <sys/types.h>
12
#ifdef HAVE_LIBPCRE2_POSIX
13
#ifndef _FRR_PCRE2_POSIX
14
#define _FRR_PCRE2_POSIX
15
#include <pcre2posix.h>
16
#endif /* _FRR_PCRE2_POSIX */
17
#elif defined(HAVE_LIBPCREPOSIX)
18
#include <pcreposix.h>
19
#else
20
#include <regex.h>
21
#endif /* HAVE_LIBPCRE2_POSIX */
22
#include <stdio.h>
23
24
#include "debug.h"
25
#include "linklist.h"
26
#include "frrevent.h"
27
#include "buffer.h"
28
#include "command.h"
29
#include "sockunion.h"
30
#include "memory.h"
31
#include "log.h"
32
#include "prefix.h"
33
#include "filter.h"
34
#include "vty.h"
35
#include "privs.h"
36
#include "network.h"
37
#include "libfrr.h"
38
#include "frrstr.h"
39
#include "lib_errors.h"
40
#include "northbound_cli.h"
41
#include "printfrr.h"
42
#include "json.h"
43
44
#include <arpa/telnet.h>
45
#include <termios.h>
46
47
#include "lib/vty_clippy.c"
48
49
8
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
50
8
DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server");
51
8
DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer");
52
8
DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history");
53
8
54
8
DECLARE_DLIST(vtys, struct vty, itm);
Unexecuted instantiation: vty.c:vtys_del
Unexecuted instantiation: vty.c:vtys_next_safe
55
56
/* Vty events */
57
enum vty_event {
58
  VTY_SERV,
59
  VTY_READ,
60
  VTY_WRITE,
61
  VTY_TIMEOUT_RESET,
62
#ifdef VTYSH
63
  VTYSH_SERV,
64
  VTYSH_READ,
65
  VTYSH_WRITE
66
#endif /* VTYSH */
67
};
68
69
struct nb_config *vty_mgmt_candidate_config;
70
71
static struct mgmt_fe_client *mgmt_fe_client;
72
static bool mgmt_fe_connected;
73
static uint64_t mgmt_client_id_next;
74
static uint64_t mgmt_last_req_id = UINT64_MAX;
75
76
PREDECL_DLIST(vtyservs);
77
78
struct vty_serv {
79
  struct vtyservs_item itm;
80
81
  int sock;
82
  bool vtysh;
83
84
  struct event *t_accept;
85
};
86
87
DECLARE_DLIST(vtyservs, struct vty_serv, itm);
88
89
static void vty_event_serv(enum vty_event event, struct vty_serv *);
90
static void vty_event(enum vty_event, struct vty *);
91
static int vtysh_flush(struct vty *vty);
92
93
/* Extern host structure from command.c */
94
extern struct host host;
95
96
/* active listeners */
97
static struct vtyservs_head vty_servs[1] = {INIT_DLIST(vty_servs[0])};
98
99
/* active connections */
100
static struct vtys_head vty_sessions[1] = {INIT_DLIST(vty_sessions[0])};
101
static struct vtys_head vtysh_sessions[1] = {INIT_DLIST(vtysh_sessions[0])};
102
103
/* Vty timeout value. */
104
static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
105
106
/* Vty access-class command */
107
static char *vty_accesslist_name = NULL;
108
109
/* Vty access-calss for IPv6. */
110
static char *vty_ipv6_accesslist_name = NULL;
111
112
/* Current directory. */
113
static char vty_cwd[MAXPATHLEN];
114
115
/* Login password check. */
116
static int no_password_check = 0;
117
118
/* Integrated configuration file path */
119
static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
120
121
bool vty_log_commands;
122
static bool vty_log_commands_perm;
123
124
char const *const mgmt_daemons[] = {
125
#ifdef HAVE_STATICD
126
  "staticd",
127
#endif
128
};
129
uint mgmt_daemons_count = array_size(mgmt_daemons);
130
131
132
static int vty_mgmt_lock_candidate_inline(struct vty *vty)
133
0
{
134
0
  assert(!vty->mgmt_locked_candidate_ds);
135
0
  (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, true, true);
136
0
  return vty->mgmt_locked_candidate_ds ? 0 : -1;
137
0
}
138
139
static int vty_mgmt_unlock_candidate_inline(struct vty *vty)
140
0
{
141
0
  assert(vty->mgmt_locked_candidate_ds);
142
0
  (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false, true);
143
0
  return vty->mgmt_locked_candidate_ds ? -1 : 0;
144
0
}
145
146
static int vty_mgmt_lock_running_inline(struct vty *vty)
147
0
{
148
0
  assert(!vty->mgmt_locked_running_ds);
149
0
  (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, true, true);
150
0
  return vty->mgmt_locked_running_ds ? 0 : -1;
151
0
}
152
153
static int vty_mgmt_unlock_running_inline(struct vty *vty)
154
0
{
155
0
  assert(vty->mgmt_locked_running_ds);
156
0
  (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, false, true);
157
0
  return vty->mgmt_locked_running_ds ? -1 : 0;
158
0
}
159
160
void vty_mgmt_resume_response(struct vty *vty, bool success)
161
0
{
162
0
  uint8_t header[4] = {0, 0, 0, 0};
163
0
  int ret = CMD_SUCCESS;
164
165
0
  if (!vty->mgmt_req_pending_cmd) {
166
0
    zlog_err(
167
0
      "vty resume response called without mgmt_req_pending_cmd");
168
0
    return;
169
0
  }
170
171
0
  if (!success)
172
0
    ret = CMD_WARNING_CONFIG_FAILED;
173
174
0
  MGMTD_FE_CLIENT_DBG(
175
0
    "resuming CLI cmd after %s on vty session-id: %" PRIu64
176
0
    " with '%s'",
177
0
    vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
178
0
    success ? "succeeded" : "failed");
179
180
0
  vty->mgmt_req_pending_cmd = NULL;
181
182
0
  if (vty->type != VTY_FILE) {
183
0
    header[3] = ret;
184
0
    buffer_put(vty->obuf, header, 4);
185
0
    if (!vty->t_write && (vtysh_flush(vty) < 0)) {
186
0
      zlog_err("failed to vtysh_flush");
187
      /* Try to flush results; exit if a write error occurs */
188
0
      return;
189
0
    }
190
0
  }
191
192
0
  if (vty->status == VTY_CLOSE)
193
0
    vty_close(vty);
194
0
  else if (vty->type != VTY_FILE)
195
0
    vty_event(VTYSH_READ, vty);
196
0
  else
197
    /* should we assert here? */
198
0
    zlog_err("mgmtd: unexpected resume while reading config file");
199
0
}
200
201
void vty_frame(struct vty *vty, const char *format, ...)
202
0
{
203
0
  va_list args;
204
205
0
  va_start(args, format);
206
0
  vsnprintfrr(vty->frame + vty->frame_pos,
207
0
        sizeof(vty->frame) - vty->frame_pos, format, args);
208
0
  vty->frame_pos = strlen(vty->frame);
209
0
  va_end(args);
210
0
}
211
212
void vty_endframe(struct vty *vty, const char *endtext)
213
0
{
214
0
  if (vty->frame_pos == 0 && endtext)
215
0
    vty_out(vty, "%s", endtext);
216
0
  vty->frame_pos = 0;
217
0
}
218
219
bool vty_set_include(struct vty *vty, const char *regexp)
220
0
{
221
0
  int errcode;
222
0
  bool ret = true;
223
0
  char errbuf[256];
224
225
0
  if (!regexp) {
226
0
    if (vty->filter) {
227
0
      regfree(&vty->include);
228
0
      vty->filter = false;
229
0
    }
230
0
    return true;
231
0
  }
232
233
0
  errcode = regcomp(&vty->include, regexp,
234
0
        REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
235
0
  if (errcode) {
236
0
    ret = false;
237
0
    regerror(errcode, &vty->include, errbuf, sizeof(errbuf));
238
0
    vty_out(vty, "%% Regex compilation error: %s\n", errbuf);
239
0
  } else {
240
0
    vty->filter = true;
241
0
  }
242
243
0
  return ret;
244
0
}
245
246
/* VTY standard output function. */
247
int vty_out(struct vty *vty, const char *format, ...)
248
0
{
249
0
  va_list args;
250
0
  ssize_t len;
251
0
  char buf[1024];
252
0
  char *p = NULL;
253
0
  char *filtered;
254
  /* format string may contain %m, keep errno intact for printfrr */
255
0
  int saved_errno = errno;
256
257
0
  if (vty->frame_pos) {
258
0
    vty->frame_pos = 0;
259
0
    vty_out(vty, "%s", vty->frame);
260
0
  }
261
262
0
  va_start(args, format);
263
0
  errno = saved_errno;
264
0
  p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args);
265
0
  va_end(args);
266
267
0
  len = strlen(p);
268
269
  /* filter buffer */
270
0
  if (vty->filter) {
271
0
    vector lines = frrstr_split_vec(p, "\n");
272
273
    /* Place first value in the cache */
274
0
    char *firstline = vector_slot(lines, 0);
275
0
    buffer_put(vty->lbuf, (uint8_t *) firstline, strlen(firstline));
276
277
    /* If our split returned more than one entry, time to filter */
278
0
    if (vector_active(lines) > 1) {
279
      /*
280
       * returned string is MTYPE_TMP so it matches the MTYPE
281
       * of everything else in the vector
282
       */
283
0
      char *bstr = buffer_getstr(vty->lbuf);
284
0
      buffer_reset(vty->lbuf);
285
0
      XFREE(MTYPE_TMP, lines->index[0]);
286
0
      vector_set_index(lines, 0, bstr);
287
0
      frrstr_filter_vec(lines, &vty->include);
288
0
      vector_compact(lines);
289
      /*
290
       * Consider the string "foo\n". If the regex is an empty string
291
       * and the line ended with a newline, then the vector will look
292
       * like:
293
       *
294
       * [0]: 'foo'
295
       * [1]: ''
296
       *
297
       * If the regex isn't empty, the vector will look like:
298
       *
299
       * [0]: 'foo'
300
       *
301
       * In this case we'd like to preserve the newline, so we add
302
       * the empty string [1] as in the first example.
303
       */
304
0
      if (p[strlen(p) - 1] == '\n' && vector_active(lines) > 0
305
0
          && strlen(vector_slot(lines, vector_active(lines) - 1)))
306
0
        vector_set(lines, XSTRDUP(MTYPE_TMP, ""));
307
308
0
      filtered = frrstr_join_vec(lines, "\n");
309
0
    }
310
0
    else {
311
0
      filtered = NULL;
312
0
    }
313
314
0
    frrstr_strvec_free(lines);
315
316
0
  } else {
317
0
    filtered = p;
318
0
  }
319
320
0
  if (!filtered)
321
0
    goto done;
322
323
0
  switch (vty->type) {
324
0
  case VTY_TERM:
325
    /* print with crlf replacement */
326
0
    buffer_put_crlf(vty->obuf, (uint8_t *)filtered,
327
0
        strlen(filtered));
328
0
    break;
329
0
  case VTY_SHELL:
330
0
    if (vty->of) {
331
0
      fprintf(vty->of, "%s", filtered);
332
0
      fflush(vty->of);
333
0
    } else if (vty->of_saved) {
334
0
      fprintf(vty->of_saved, "%s", filtered);
335
0
      fflush(vty->of_saved);
336
0
    }
337
0
    break;
338
0
  case VTY_SHELL_SERV:
339
0
  case VTY_FILE:
340
0
  default:
341
    /* print without crlf replacement */
342
0
    buffer_put(vty->obuf, (uint8_t *)filtered, strlen(filtered));
343
0
    break;
344
0
  }
345
346
0
done:
347
348
0
  if (vty->filter && filtered)
349
0
    XFREE(MTYPE_TMP, filtered);
350
351
  /* If p is not different with buf, it is allocated buffer.  */
352
0
  if (p != buf)
353
0
    XFREE(MTYPE_VTY_OUT_BUF, p);
354
355
0
  return len;
356
0
}
357
358
static int vty_json_helper(struct vty *vty, struct json_object *json,
359
         uint32_t options)
360
0
{
361
0
  const char *text;
362
363
0
  if (!json)
364
0
    return CMD_SUCCESS;
365
366
0
  text = json_object_to_json_string_ext(
367
0
    json, options);
368
0
  vty_out(vty, "%s\n", text);
369
0
  json_object_free(json);
370
371
0
  return CMD_SUCCESS;
372
0
}
373
374
int vty_json(struct vty *vty, struct json_object *json)
375
0
{
376
0
  return vty_json_helper(vty, json,
377
0
             JSON_C_TO_STRING_PRETTY |
378
0
               JSON_C_TO_STRING_NOSLASHESCAPE);
379
0
}
380
381
int vty_json_no_pretty(struct vty *vty, struct json_object *json)
382
0
{
383
0
  return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
384
0
}
385
386
void vty_json_empty(struct vty *vty)
387
0
{
388
0
  json_object *json = json_object_new_object();
389
390
0
  vty_json(vty, json);
391
0
}
392
393
/* Output current time to the vty. */
394
void vty_time_print(struct vty *vty, int cr)
395
0
{
396
0
  char buf[FRR_TIMESTAMP_LEN];
397
398
0
  if (frr_timestamp(0, buf, sizeof(buf)) == 0) {
399
0
    zlog_info("frr_timestamp error");
400
0
    return;
401
0
  }
402
0
  if (cr)
403
0
    vty_out(vty, "%s\n", buf);
404
0
  else
405
0
    vty_out(vty, "%s ", buf);
406
407
0
  return;
408
0
}
409
410
/* Say hello to vty interface. */
411
void vty_hello(struct vty *vty)
412
0
{
413
0
  if (host.motdfile) {
414
0
    FILE *f;
415
0
    char buf[4096];
416
417
0
    f = fopen(host.motdfile, "r");
418
0
    if (f) {
419
0
      while (fgets(buf, sizeof(buf), f)) {
420
0
        char *s;
421
        /* work backwards to ignore trailling isspace()
422
         */
423
0
        for (s = buf + strlen(buf);
424
0
             (s > buf) && isspace((unsigned char)s[-1]);
425
0
             s--)
426
0
          ;
427
0
        *s = '\0';
428
0
        vty_out(vty, "%s\n", buf);
429
0
      }
430
0
      fclose(f);
431
0
    } else
432
0
      vty_out(vty, "MOTD file not found\n");
433
0
  } else if (host.motd)
434
0
    vty_out(vty, "%s", host.motd);
435
0
}
436
437
#pragma GCC diagnostic push
438
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
439
/* prompt formatting has a %s in the cmd_node prompt string.
440
 *
441
 * Also for some reason GCC emits the warning on the end of the function
442
 * (optimization maybe?) rather than on the vty_out line, so this pragma
443
 * wraps the entire function rather than just the vty_out line.
444
 */
445
446
/* Put out prompt and wait input from user. */
447
static void vty_prompt(struct vty *vty)
448
0
{
449
0
  if (vty->type == VTY_TERM) {
450
0
    vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
451
0
  }
452
0
}
453
#pragma GCC diagnostic pop
454
455
/* Send WILL TELOPT_ECHO to remote server. */
456
static void vty_will_echo(struct vty *vty)
457
0
{
458
0
  unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
459
0
  vty_out(vty, "%s", cmd);
460
0
}
461
462
/* Make suppress Go-Ahead telnet option. */
463
static void vty_will_suppress_go_ahead(struct vty *vty)
464
0
{
465
0
  unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
466
0
  vty_out(vty, "%s", cmd);
467
0
}
468
469
/* Make don't use linemode over telnet. */
470
static void vty_dont_linemode(struct vty *vty)
471
0
{
472
0
  unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
473
0
  vty_out(vty, "%s", cmd);
474
0
}
475
476
/* Use window size. */
477
static void vty_do_window_size(struct vty *vty)
478
0
{
479
0
  unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
480
0
  vty_out(vty, "%s", cmd);
481
0
}
482
483
/* Authentication of vty */
484
static void vty_auth(struct vty *vty, char *buf)
485
0
{
486
0
  char *passwd = NULL;
487
0
  enum node_type next_node = 0;
488
0
  int fail;
489
0
490
0
  switch (vty->node) {
491
0
  case AUTH_NODE:
492
0
    if (host.encrypt)
493
0
      passwd = host.password_encrypt;
494
0
    else
495
0
      passwd = host.password;
496
0
    if (host.advanced)
497
0
      next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
498
0
    else
499
0
      next_node = VIEW_NODE;
500
0
    break;
501
0
  case AUTH_ENABLE_NODE:
502
0
    if (host.encrypt)
503
0
      passwd = host.enable_encrypt;
504
0
    else
505
0
      passwd = host.enable;
506
0
    next_node = ENABLE_NODE;
507
0
    break;
508
0
  }
509
0
510
0
  if (passwd) {
511
0
    if (host.encrypt)
512
0
      fail = strcmp(crypt(buf, passwd), passwd);
513
0
    else
514
0
      fail = strcmp(buf, passwd);
515
0
  } else
516
0
    fail = 1;
517
0
518
0
  if (!fail) {
519
0
    vty->fail = 0;
520
0
    vty->node = next_node; /* Success ! */
521
0
  } else {
522
0
    vty->fail++;
523
0
    if (vty->fail >= 3) {
524
0
      if (vty->node == AUTH_NODE) {
525
0
        vty_out(vty,
526
0
          "%% Bad passwords, too many failures!\n");
527
0
        vty->status = VTY_CLOSE;
528
0
      } else {
529
0
        /* AUTH_ENABLE_NODE */
530
0
        vty->fail = 0;
531
0
        vty_out(vty,
532
0
          "%% Bad enable passwords, too many failures!\n");
533
0
        vty->status = VTY_CLOSE;
534
0
      }
535
0
    }
536
0
  }
537
0
}
538
539
/* Command execution over the vty interface. */
540
static int vty_command(struct vty *vty, char *buf)
541
0
{
542
0
  int ret;
543
0
  const char *protocolname;
544
0
  char *cp = NULL;
545
0
546
0
  assert(vty);
547
0
548
0
  /*
549
0
   * Log non empty command lines
550
0
   */
551
0
  if (vty_log_commands &&
552
0
      strncmp(buf, "echo PING", strlen("echo PING")) != 0)
553
0
    cp = buf;
554
0
  if (cp != NULL) {
555
0
    /* Skip white spaces. */
556
0
    while (isspace((unsigned char)*cp) && *cp != '\0')
557
0
      cp++;
558
0
  }
559
0
  if (cp != NULL && *cp != '\0') {
560
0
    char vty_str[VTY_BUFSIZ];
561
0
    char prompt_str[VTY_BUFSIZ];
562
0
563
0
    /* format the base vty info */
564
0
    snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", vty->fd,
565
0
       vty->address);
566
0
567
0
    /* format the prompt */
568
0
#pragma GCC diagnostic push
569
0
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
570
0
    /* prompt formatting has a %s in the cmd_node prompt string */
571
0
    snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
572
0
       vty_str);
573
0
#pragma GCC diagnostic pop
574
0
575
0
    /* now log the command */
576
0
    zlog_notice("%s%s", prompt_str, buf);
577
0
  }
578
0
579
0
  RUSAGE_T before;
580
0
  RUSAGE_T after;
581
0
  unsigned long walltime, cputime;
582
0
583
0
  /* cmd_execute() may change cputime_enabled if we're executing the
584
0
   * "service cputime-stats" command, which can result in nonsensical
585
0
   * and very confusing warnings
586
0
   */
587
0
  bool cputime_enabled_here = cputime_enabled;
588
0
589
0
  GETRUSAGE(&before);
590
0
591
0
  ret = cmd_execute(vty, buf, NULL, 0);
592
0
593
0
  GETRUSAGE(&after);
594
0
595
0
  walltime = event_consumed_time(&after, &before, &cputime);
596
0
597
0
  if (cputime_enabled_here && cputime_enabled && cputime_threshold
598
0
      && cputime > cputime_threshold)
599
0
    /* Warn about CPU hog that must be fixed. */
600
0
    flog_warn(EC_LIB_SLOW_THREAD_CPU,
601
0
        "CPU HOG: command took %lums (cpu time %lums): %s",
602
0
        walltime / 1000, cputime / 1000, buf);
603
0
  else if (walltime_threshold && walltime > walltime_threshold)
604
0
    flog_warn(EC_LIB_SLOW_THREAD_WALL,
605
0
        "STARVATION: command took %lums (cpu time %lums): %s",
606
0
        walltime / 1000, cputime / 1000, buf);
607
0
608
0
  /* Get the name of the protocol if any */
609
0
  protocolname = frr_protoname;
610
0
611
0
  if (ret != CMD_SUCCESS)
612
0
    switch (ret) {
613
0
    case CMD_WARNING:
614
0
      if (vty->type == VTY_FILE)
615
0
        vty_out(vty, "Warning...\n");
616
0
      break;
617
0
    case CMD_ERR_AMBIGUOUS:
618
0
      vty_out(vty, "%% Ambiguous command.\n");
619
0
      break;
620
0
    case CMD_ERR_NO_MATCH:
621
0
      vty_out(vty, "%% [%s] Unknown command: %s\n",
622
0
        protocolname, buf);
623
0
      break;
624
0
    case CMD_ERR_INCOMPLETE:
625
0
      vty_out(vty, "%% Command incomplete.\n");
626
0
      break;
627
0
    }
628
0
629
0
  return ret;
630
0
}
631
632
static const char telnet_backward_char = 0x08;
633
static const char telnet_space_char = ' ';
634
635
/* Basic function to write buffer to vty. */
636
static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
637
0
{
638
0
  if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
639
0
    return;
640
0
641
0
  /* Should we do buffering here ?  And make vty_flush (vty) ? */
642
0
  buffer_put(vty->obuf, buf, nbytes);
643
0
}
644
645
/* Basic function to insert character into vty. */
646
static void vty_self_insert(struct vty *vty, char c)
647
0
{
648
0
  int i;
649
0
  int length;
650
0
651
0
  if (vty->length + 1 >= VTY_BUFSIZ)
652
0
    return;
653
0
654
0
  length = vty->length - vty->cp;
655
0
  memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
656
0
  vty->buf[vty->cp] = c;
657
0
658
0
  vty_write(vty, &vty->buf[vty->cp], length + 1);
659
0
  for (i = 0; i < length; i++)
660
0
    vty_write(vty, &telnet_backward_char, 1);
661
0
662
0
  vty->cp++;
663
0
  vty->length++;
664
0
665
0
  vty->buf[vty->length] = '\0';
666
0
}
667
668
/* Self insert character 'c' in overwrite mode. */
669
static void vty_self_insert_overwrite(struct vty *vty, char c)
670
0
{
671
0
  if (vty->cp == vty->length) {
672
0
    vty_self_insert(vty, c);
673
0
    return;
674
0
  }
675
0
676
0
  vty->buf[vty->cp++] = c;
677
0
  vty_write(vty, &c, 1);
678
0
}
679
680
/**
681
 * Insert a string into vty->buf at the current cursor position.
682
 *
683
 * If the resultant string would be larger than VTY_BUFSIZ it is
684
 * truncated to fit.
685
 */
686
static void vty_insert_word_overwrite(struct vty *vty, char *str)
687
0
{
688
0
  if (vty->cp == VTY_BUFSIZ)
689
0
    return;
690
0
691
0
  size_t nwrite = MIN((int)strlen(str), VTY_BUFSIZ - vty->cp - 1);
692
0
  memcpy(&vty->buf[vty->cp], str, nwrite);
693
0
  vty->cp += nwrite;
694
0
  vty->length = MAX(vty->cp, vty->length);
695
0
  vty->buf[vty->length] = '\0';
696
0
  vty_write(vty, str, nwrite);
697
0
}
698
699
/* Forward character. */
700
static void vty_forward_char(struct vty *vty)
701
0
{
702
0
  if (vty->cp < vty->length) {
703
0
    vty_write(vty, &vty->buf[vty->cp], 1);
704
0
    vty->cp++;
705
0
  }
706
0
}
707
708
/* Backward character. */
709
static void vty_backward_char(struct vty *vty)
710
0
{
711
0
  if (vty->cp > 0) {
712
0
    vty->cp--;
713
0
    vty_write(vty, &telnet_backward_char, 1);
714
0
  }
715
0
}
716
717
/* Move to the beginning of the line. */
718
static void vty_beginning_of_line(struct vty *vty)
719
0
{
720
0
  while (vty->cp)
721
0
    vty_backward_char(vty);
722
0
}
723
724
/* Move to the end of the line. */
725
static void vty_end_of_line(struct vty *vty)
726
0
{
727
0
  while (vty->cp < vty->length)
728
0
    vty_forward_char(vty);
729
0
}
730
731
static void vty_kill_line_from_beginning(struct vty *);
732
static void vty_redraw_line(struct vty *);
733
734
/* Print command line history.  This function is called from
735
   vty_next_line and vty_previous_line. */
736
static void vty_history_print(struct vty *vty)
737
0
{
738
0
  int length;
739
0
740
0
  vty_kill_line_from_beginning(vty);
741
0
742
0
  /* Get previous line from history buffer */
743
0
  length = strlen(vty->hist[vty->hp]);
744
0
  memcpy(vty->buf, vty->hist[vty->hp], length);
745
0
  vty->cp = vty->length = length;
746
0
  vty->buf[vty->length] = '\0';
747
0
748
0
  /* Redraw current line */
749
0
  vty_redraw_line(vty);
750
0
}
751
752
/* Show next command line history. */
753
static void vty_next_line(struct vty *vty)
754
0
{
755
0
  int try_index;
756
0
757
0
  if (vty->hp == vty->hindex)
758
0
    return;
759
0
760
0
  /* Try is there history exist or not. */
761
0
  try_index = vty->hp;
762
0
  if (try_index == (VTY_MAXHIST - 1))
763
0
    try_index = 0;
764
0
  else
765
0
    try_index++;
766
0
767
0
  /* If there is not history return. */
768
0
  if (vty->hist[try_index] == NULL)
769
0
    return;
770
0
  else
771
0
    vty->hp = try_index;
772
0
773
0
  vty_history_print(vty);
774
0
}
775
776
/* Show previous command line history. */
777
static void vty_previous_line(struct vty *vty)
778
0
{
779
0
  int try_index;
780
0
781
0
  try_index = vty->hp;
782
0
  if (try_index == 0)
783
0
    try_index = VTY_MAXHIST - 1;
784
0
  else
785
0
    try_index--;
786
0
787
0
  if (vty->hist[try_index] == NULL)
788
0
    return;
789
0
  else
790
0
    vty->hp = try_index;
791
0
792
0
  vty_history_print(vty);
793
0
}
794
795
/* This function redraw all of the command line character. */
796
static void vty_redraw_line(struct vty *vty)
797
0
{
798
0
  vty_write(vty, vty->buf, vty->length);
799
0
  vty->cp = vty->length;
800
0
}
801
802
/* Forward word. */
803
static void vty_forward_word(struct vty *vty)
804
0
{
805
0
  while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
806
0
    vty_forward_char(vty);
807
0
808
0
  while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
809
0
    vty_forward_char(vty);
810
0
}
811
812
/* Backward word without skipping training space. */
813
static void vty_backward_pure_word(struct vty *vty)
814
0
{
815
0
  while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
816
0
    vty_backward_char(vty);
817
0
}
818
819
/* Backward word. */
820
static void vty_backward_word(struct vty *vty)
821
0
{
822
0
  while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
823
0
    vty_backward_char(vty);
824
0
825
0
  while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
826
0
    vty_backward_char(vty);
827
0
}
828
829
/* When '^D' is typed at the beginning of the line we move to the down
830
   level. */
831
static void vty_down_level(struct vty *vty)
832
0
{
833
0
  vty_out(vty, "\n");
834
0
  cmd_exit(vty);
835
0
  vty_prompt(vty);
836
0
  vty->cp = 0;
837
0
}
838
839
/* When '^Z' is received from vty, move down to the enable mode. */
840
static void vty_end_config(struct vty *vty)
841
0
{
842
0
  vty_out(vty, "\n");
843
0
844
0
  if (vty->config) {
845
0
    vty_config_exit(vty);
846
0
    vty->node = ENABLE_NODE;
847
0
  }
848
0
849
0
  vty_prompt(vty);
850
0
  vty->cp = 0;
851
0
}
852
853
/* Delete a character at the current point. */
854
static void vty_delete_char(struct vty *vty)
855
0
{
856
0
  int i;
857
0
  int size;
858
0
859
0
  if (vty->length == 0) {
860
0
    vty_down_level(vty);
861
0
    return;
862
0
  }
863
0
864
0
  if (vty->cp == vty->length)
865
0
    return; /* completion need here? */
866
0
867
0
  size = vty->length - vty->cp;
868
0
869
0
  vty->length--;
870
0
  memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
871
0
  vty->buf[vty->length] = '\0';
872
0
873
0
  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
874
0
    return;
875
0
876
0
  vty_write(vty, &vty->buf[vty->cp], size - 1);
877
0
  vty_write(vty, &telnet_space_char, 1);
878
0
879
0
  for (i = 0; i < size; i++)
880
0
    vty_write(vty, &telnet_backward_char, 1);
881
0
}
882
883
/* Delete a character before the point. */
884
static void vty_delete_backward_char(struct vty *vty)
885
0
{
886
0
  if (vty->cp == 0)
887
0
    return;
888
0
889
0
  vty_backward_char(vty);
890
0
  vty_delete_char(vty);
891
0
}
892
893
/* Kill rest of line from current point. */
894
static void vty_kill_line(struct vty *vty)
895
0
{
896
0
  int i;
897
0
  int size;
898
0
899
0
  size = vty->length - vty->cp;
900
0
901
0
  if (size == 0)
902
0
    return;
903
0
904
0
  for (i = 0; i < size; i++)
905
0
    vty_write(vty, &telnet_space_char, 1);
906
0
  for (i = 0; i < size; i++)
907
0
    vty_write(vty, &telnet_backward_char, 1);
908
0
909
0
  memset(&vty->buf[vty->cp], 0, size);
910
0
  vty->length = vty->cp;
911
0
}
912
913
/* Kill line from the beginning. */
914
static void vty_kill_line_from_beginning(struct vty *vty)
915
0
{
916
0
  vty_beginning_of_line(vty);
917
0
  vty_kill_line(vty);
918
0
}
919
920
/* Delete a word before the point. */
921
static void vty_forward_kill_word(struct vty *vty)
922
0
{
923
0
  while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
924
0
    vty_delete_char(vty);
925
0
  while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
926
0
    vty_delete_char(vty);
927
0
}
928
929
/* Delete a word before the point. */
930
static void vty_backward_kill_word(struct vty *vty)
931
0
{
932
0
  while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
933
0
    vty_delete_backward_char(vty);
934
0
  while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
935
0
    vty_delete_backward_char(vty);
936
0
}
937
938
/* Transpose chars before or at the point. */
939
static void vty_transpose_chars(struct vty *vty)
940
0
{
941
0
  char c1, c2;
942
0
943
0
  /* If length is short or point is near by the beginning of line then
944
0
     return. */
945
0
  if (vty->length < 2 || vty->cp < 1)
946
0
    return;
947
0
948
0
  /* In case of point is located at the end of the line. */
949
0
  if (vty->cp == vty->length) {
950
0
    c1 = vty->buf[vty->cp - 1];
951
0
    c2 = vty->buf[vty->cp - 2];
952
0
953
0
    vty_backward_char(vty);
954
0
    vty_backward_char(vty);
955
0
    vty_self_insert_overwrite(vty, c1);
956
0
    vty_self_insert_overwrite(vty, c2);
957
0
  } else {
958
0
    c1 = vty->buf[vty->cp];
959
0
    c2 = vty->buf[vty->cp - 1];
960
0
961
0
    vty_backward_char(vty);
962
0
    vty_self_insert_overwrite(vty, c1);
963
0
    vty_self_insert_overwrite(vty, c2);
964
0
  }
965
0
}
966
967
/* Do completion at vty interface. */
968
static void vty_complete_command(struct vty *vty)
969
0
{
970
0
  int i;
971
0
  int ret;
972
0
  char **matched = NULL;
973
0
  vector vline;
974
0
975
0
  if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
976
0
    return;
977
0
978
0
  vline = cmd_make_strvec(vty->buf);
979
0
  if (vline == NULL)
980
0
    return;
981
0
982
0
  /* In case of 'help \t'. */
983
0
  if (isspace((unsigned char)vty->buf[vty->length - 1]))
984
0
    vector_set(vline, NULL);
985
0
986
0
  matched = cmd_complete_command(vline, vty, &ret);
987
0
988
0
  cmd_free_strvec(vline);
989
0
990
0
  vty_out(vty, "\n");
991
0
  switch (ret) {
992
0
  case CMD_ERR_AMBIGUOUS:
993
0
    vty_out(vty, "%% Ambiguous command.\n");
994
0
    vty_prompt(vty);
995
0
    vty_redraw_line(vty);
996
0
    break;
997
0
  case CMD_ERR_NO_MATCH:
998
0
    /* vty_out (vty, "%% There is no matched command.\n"); */
999
0
    vty_prompt(vty);
1000
0
    vty_redraw_line(vty);
1001
0
    break;
1002
0
  case CMD_COMPLETE_FULL_MATCH:
1003
0
    if (!matched[0]) {
1004
0
      /* 2016-11-28 equinox -- need to debug, SEGV here */
1005
0
      vty_out(vty, "%% CLI BUG: FULL_MATCH with NULL str\n");
1006
0
      vty_prompt(vty);
1007
0
      vty_redraw_line(vty);
1008
0
      break;
1009
0
    }
1010
0
    vty_prompt(vty);
1011
0
    vty_redraw_line(vty);
1012
0
    vty_backward_pure_word(vty);
1013
0
    vty_insert_word_overwrite(vty, matched[0]);
1014
0
    vty_self_insert(vty, ' ');
1015
0
    XFREE(MTYPE_COMPLETION, matched[0]);
1016
0
    break;
1017
0
  case CMD_COMPLETE_MATCH:
1018
0
    vty_prompt(vty);
1019
0
    vty_redraw_line(vty);
1020
0
    vty_backward_pure_word(vty);
1021
0
    vty_insert_word_overwrite(vty, matched[0]);
1022
0
    XFREE(MTYPE_COMPLETION, matched[0]);
1023
0
    break;
1024
0
  case CMD_COMPLETE_LIST_MATCH:
1025
0
    for (i = 0; matched[i] != NULL; i++) {
1026
0
      if (i != 0 && ((i % 6) == 0))
1027
0
        vty_out(vty, "\n");
1028
0
      vty_out(vty, "%-10s ", matched[i]);
1029
0
      XFREE(MTYPE_COMPLETION, matched[i]);
1030
0
    }
1031
0
    vty_out(vty, "\n");
1032
0
1033
0
    vty_prompt(vty);
1034
0
    vty_redraw_line(vty);
1035
0
    break;
1036
0
  case CMD_ERR_NOTHING_TODO:
1037
0
    vty_prompt(vty);
1038
0
    vty_redraw_line(vty);
1039
0
    break;
1040
0
  default:
1041
0
    break;
1042
0
  }
1043
0
  XFREE(MTYPE_TMP, matched);
1044
0
}
1045
1046
static void vty_describe_fold(struct vty *vty, int cmd_width,
1047
            unsigned int desc_width, struct cmd_token *token)
1048
0
{
1049
0
  char *buf;
1050
0
  const char *cmd, *p;
1051
0
  int pos;
1052
0
1053
0
  cmd = token->text;
1054
0
1055
0
  if (desc_width <= 0) {
1056
0
    vty_out(vty, "  %-*s  %s\n", cmd_width, cmd, token->desc);
1057
0
    return;
1058
0
  }
1059
0
1060
0
  buf = XCALLOC(MTYPE_TMP, strlen(token->desc) + 1);
1061
0
1062
0
  for (p = token->desc; strlen(p) > desc_width; p += pos + 1) {
1063
0
    for (pos = desc_width; pos > 0; pos--)
1064
0
      if (*(p + pos) == ' ')
1065
0
        break;
1066
0
1067
0
    if (pos == 0)
1068
0
      break;
1069
0
1070
0
    memcpy(buf, p, pos);
1071
0
    buf[pos] = '\0';
1072
0
    vty_out(vty, "  %-*s  %s\n", cmd_width, cmd, buf);
1073
0
1074
0
    cmd = "";
1075
0
  }
1076
0
1077
0
  vty_out(vty, "  %-*s  %s\n", cmd_width, cmd, p);
1078
0
1079
0
  XFREE(MTYPE_TMP, buf);
1080
0
}
1081
1082
/* Describe matched command function. */
1083
static void vty_describe_command(struct vty *vty)
1084
0
{
1085
0
  int ret;
1086
0
  vector vline;
1087
0
  vector describe;
1088
0
  unsigned int i, width, desc_width;
1089
0
  struct cmd_token *token, *token_cr = NULL;
1090
0
1091
0
  vline = cmd_make_strvec(vty->buf);
1092
0
1093
0
  /* In case of '> ?'. */
1094
0
  if (vline == NULL) {
1095
0
    vline = vector_init(1);
1096
0
    vector_set(vline, NULL);
1097
0
  } else if (isspace((unsigned char)vty->buf[vty->length - 1]))
1098
0
    vector_set(vline, NULL);
1099
0
1100
0
  describe = cmd_describe_command(vline, vty, &ret);
1101
0
1102
0
  vty_out(vty, "\n");
1103
0
1104
0
  /* Ambiguous error. */
1105
0
  switch (ret) {
1106
0
  case CMD_ERR_AMBIGUOUS:
1107
0
    vty_out(vty, "%% Ambiguous command.\n");
1108
0
    goto out;
1109
0
    break;
1110
0
  case CMD_ERR_NO_MATCH:
1111
0
    vty_out(vty, "%% There is no matched command.\n");
1112
0
    goto out;
1113
0
    break;
1114
0
  }
1115
0
1116
0
  /* Get width of command string. */
1117
0
  width = 0;
1118
0
  for (i = 0; i < vector_active(describe); i++)
1119
0
    if ((token = vector_slot(describe, i)) != NULL) {
1120
0
      unsigned int len;
1121
0
1122
0
      if (token->text[0] == '\0')
1123
0
        continue;
1124
0
1125
0
      len = strlen(token->text);
1126
0
1127
0
      if (width < len)
1128
0
        width = len;
1129
0
    }
1130
0
1131
0
  /* Get width of description string. */
1132
0
  desc_width = vty->width - (width + 6);
1133
0
1134
0
  /* Print out description. */
1135
0
  for (i = 0; i < vector_active(describe); i++)
1136
0
    if ((token = vector_slot(describe, i)) != NULL) {
1137
0
      if (token->text[0] == '\0')
1138
0
        continue;
1139
0
1140
0
      if (strcmp(token->text, CMD_CR_TEXT) == 0) {
1141
0
        token_cr = token;
1142
0
        continue;
1143
0
      }
1144
0
1145
0
      if (!token->desc)
1146
0
        vty_out(vty, "  %-s\n", token->text);
1147
0
      else if (desc_width >= strlen(token->desc))
1148
0
        vty_out(vty, "  %-*s  %s\n", width, token->text,
1149
0
          token->desc);
1150
0
      else
1151
0
        vty_describe_fold(vty, width, desc_width,
1152
0
              token);
1153
0
1154
0
      if (IS_VARYING_TOKEN(token->type)) {
1155
0
        const char *ref = vector_slot(
1156
0
          vline, vector_active(vline) - 1);
1157
0
1158
0
        vector varcomps = vector_init(VECTOR_MIN_SIZE);
1159
0
        cmd_variable_complete(token, ref, varcomps);
1160
0
1161
0
        if (vector_active(varcomps) > 0) {
1162
0
          char *ac = cmd_variable_comp2str(
1163
0
            varcomps, vty->width);
1164
0
          vty_out(vty, "%s\n", ac);
1165
0
          XFREE(MTYPE_TMP, ac);
1166
0
        }
1167
0
1168
0
        vector_free(varcomps);
1169
0
      }
1170
0
    }
1171
0
1172
0
  if ((token = token_cr)) {
1173
0
    if (!token->desc)
1174
0
      vty_out(vty, "  %-s\n", token->text);
1175
0
    else if (desc_width >= strlen(token->desc))
1176
0
      vty_out(vty, "  %-*s  %s\n", width, token->text,
1177
0
        token->desc);
1178
0
    else
1179
0
      vty_describe_fold(vty, width, desc_width, token);
1180
0
  }
1181
0
1182
0
out:
1183
0
  cmd_free_strvec(vline);
1184
0
  if (describe)
1185
0
    vector_free(describe);
1186
0
1187
0
  vty_prompt(vty);
1188
0
  vty_redraw_line(vty);
1189
0
}
1190
1191
static void vty_clear_buf(struct vty *vty)
1192
0
{
1193
0
  memset(vty->buf, 0, vty->max);
1194
0
}
1195
1196
/* ^C stop current input and do not add command line to the history. */
1197
static void vty_stop_input(struct vty *vty)
1198
0
{
1199
0
  vty->cp = vty->length = 0;
1200
0
  vty_clear_buf(vty);
1201
0
  vty_out(vty, "\n");
1202
0
1203
0
  if (vty->config) {
1204
0
    vty_config_exit(vty);
1205
0
    vty->node = ENABLE_NODE;
1206
0
  }
1207
0
1208
0
  vty_prompt(vty);
1209
0
1210
0
  /* Set history pointer to the latest one. */
1211
0
  vty->hp = vty->hindex;
1212
0
}
1213
1214
/* Add current command line to the history buffer. */
1215
static void vty_hist_add(struct vty *vty)
1216
0
{
1217
0
  int index;
1218
0
1219
0
  if (vty->length == 0)
1220
0
    return;
1221
0
1222
0
  index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1223
0
1224
0
  /* Ignore the same string as previous one. */
1225
0
  if (vty->hist[index])
1226
0
    if (strcmp(vty->buf, vty->hist[index]) == 0) {
1227
0
      vty->hp = vty->hindex;
1228
0
      return;
1229
0
    }
1230
0
1231
0
  /* Insert history entry. */
1232
0
  XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1233
0
  vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
1234
0
1235
0
  /* History index rotation. */
1236
0
  vty->hindex++;
1237
0
  if (vty->hindex == VTY_MAXHIST)
1238
0
    vty->hindex = 0;
1239
0
1240
0
  vty->hp = vty->hindex;
1241
0
}
1242
1243
/* #define TELNET_OPTION_DEBUG */
1244
1245
/* Get telnet window size. */
1246
static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
1247
0
{
1248
0
#ifdef TELNET_OPTION_DEBUG
1249
0
  int i;
1250
0
1251
0
  for (i = 0; i < nbytes; i++) {
1252
0
    switch (buf[i]) {
1253
0
    case IAC:
1254
0
      vty_out(vty, "IAC ");
1255
0
      break;
1256
0
    case WILL:
1257
0
      vty_out(vty, "WILL ");
1258
0
      break;
1259
0
    case WONT:
1260
0
      vty_out(vty, "WONT ");
1261
0
      break;
1262
0
    case DO:
1263
0
      vty_out(vty, "DO ");
1264
0
      break;
1265
0
    case DONT:
1266
0
      vty_out(vty, "DONT ");
1267
0
      break;
1268
0
    case SB:
1269
0
      vty_out(vty, "SB ");
1270
0
      break;
1271
0
    case SE:
1272
0
      vty_out(vty, "SE ");
1273
0
      break;
1274
0
    case TELOPT_ECHO:
1275
0
      vty_out(vty, "TELOPT_ECHO \n");
1276
0
      break;
1277
0
    case TELOPT_SGA:
1278
0
      vty_out(vty, "TELOPT_SGA \n");
1279
0
      break;
1280
0
    case TELOPT_NAWS:
1281
0
      vty_out(vty, "TELOPT_NAWS \n");
1282
0
      break;
1283
0
    default:
1284
0
      vty_out(vty, "%x ", buf[i]);
1285
0
      break;
1286
0
    }
1287
0
  }
1288
0
  vty_out(vty, "\n");
1289
0
1290
0
#endif /* TELNET_OPTION_DEBUG */
1291
0
1292
0
  switch (buf[0]) {
1293
0
  case SB:
1294
0
    vty->sb_len = 0;
1295
0
    vty->iac_sb_in_progress = 1;
1296
0
    return 0;
1297
0
  case SE: {
1298
0
    if (!vty->iac_sb_in_progress)
1299
0
      return 0;
1300
0
1301
0
    if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) {
1302
0
      vty->iac_sb_in_progress = 0;
1303
0
      return 0;
1304
0
    }
1305
0
    switch (vty->sb_buf[0]) {
1306
0
    case TELOPT_NAWS:
1307
0
      if (vty->sb_len != TELNET_NAWS_SB_LEN)
1308
0
        flog_err(
1309
0
          EC_LIB_SYSTEM_CALL,
1310
0
          "RFC 1073 violation detected: telnet NAWS option should send %d characters, but we received %lu",
1311
0
          TELNET_NAWS_SB_LEN,
1312
0
          (unsigned long)vty->sb_len);
1313
0
      else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1314
0
        flog_err(
1315
0
          EC_LIB_DEVELOPMENT,
1316
0
          "Bug detected: sizeof(vty->sb_buf) %lu < %d, too small to handle the telnet NAWS option",
1317
0
          (unsigned long)sizeof(vty->sb_buf),
1318
0
          TELNET_NAWS_SB_LEN);
1319
0
      else {
1320
0
        vty->width = ((vty->sb_buf[1] << 8)
1321
0
                | vty->sb_buf[2]);
1322
0
        vty->height = ((vty->sb_buf[3] << 8)
1323
0
                 | vty->sb_buf[4]);
1324
0
#ifdef TELNET_OPTION_DEBUG
1325
0
        vty_out(vty,
1326
0
          "TELNET NAWS window size negotiation completed: width %d, height %d\n",
1327
0
          vty->width, vty->height);
1328
0
#endif
1329
0
      }
1330
0
      break;
1331
0
    }
1332
0
    vty->iac_sb_in_progress = 0;
1333
0
    return 0;
1334
0
  }
1335
0
  default:
1336
0
    break;
1337
0
  }
1338
0
  return 1;
1339
0
}
1340
1341
/* Execute current command line. */
1342
static int vty_execute(struct vty *vty)
1343
0
{
1344
0
  int ret;
1345
0
1346
0
  ret = CMD_SUCCESS;
1347
0
1348
0
  switch (vty->node) {
1349
0
  case AUTH_NODE:
1350
0
  case AUTH_ENABLE_NODE:
1351
0
    vty_auth(vty, vty->buf);
1352
0
    break;
1353
0
  default:
1354
0
    ret = vty_command(vty, vty->buf);
1355
0
    if (vty->type == VTY_TERM)
1356
0
      vty_hist_add(vty);
1357
0
    break;
1358
0
  }
1359
0
1360
0
  /* Clear command line buffer. */
1361
0
  vty->cp = vty->length = 0;
1362
0
  vty_clear_buf(vty);
1363
0
1364
0
  if (vty->status != VTY_CLOSE)
1365
0
    vty_prompt(vty);
1366
0
1367
0
  return ret;
1368
0
}
1369
1370
#define CONTROL(X)  ((X) - '@')
1371
0
#define VTY_NORMAL     0
1372
#define VTY_PRE_ESCAPE 1
1373
#define VTY_ESCAPE     2
1374
#define VTY_CR         3
1375
1376
/* Escape character command map. */
1377
static void vty_escape_map(unsigned char c, struct vty *vty)
1378
0
{
1379
0
  switch (c) {
1380
0
  case ('A'):
1381
0
    vty_previous_line(vty);
1382
0
    break;
1383
0
  case ('B'):
1384
0
    vty_next_line(vty);
1385
0
    break;
1386
0
  case ('C'):
1387
0
    vty_forward_char(vty);
1388
0
    break;
1389
0
  case ('D'):
1390
0
    vty_backward_char(vty);
1391
0
    break;
1392
0
  default:
1393
0
    break;
1394
0
  }
1395
0
1396
0
  /* Go back to normal mode. */
1397
0
  vty->escape = VTY_NORMAL;
1398
0
}
1399
1400
/* Quit print out to the buffer. */
1401
static void vty_buffer_reset(struct vty *vty)
1402
0
{
1403
0
  buffer_reset(vty->obuf);
1404
0
  buffer_reset(vty->lbuf);
1405
0
  vty_prompt(vty);
1406
0
  vty_redraw_line(vty);
1407
0
}
1408
1409
/* Read data via vty socket. */
1410
static void vty_read(struct event *thread)
1411
0
{
1412
0
  int i;
1413
0
  int nbytes;
1414
0
  unsigned char buf[VTY_READ_BUFSIZ];
1415
0
1416
0
  struct vty *vty = EVENT_ARG(thread);
1417
0
1418
0
  /* Read raw data from socket */
1419
0
  if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1420
0
    if (nbytes < 0) {
1421
0
      if (ERRNO_IO_RETRY(errno)) {
1422
0
        vty_event(VTY_READ, vty);
1423
0
        return;
1424
0
      }
1425
0
      flog_err(
1426
0
        EC_LIB_SOCKET,
1427
0
        "%s: read error on vty client fd %d, closing: %s",
1428
0
        __func__, vty->fd, safe_strerror(errno));
1429
0
      buffer_reset(vty->obuf);
1430
0
      buffer_reset(vty->lbuf);
1431
0
    }
1432
0
    vty->status = VTY_CLOSE;
1433
0
  }
1434
0
1435
0
  for (i = 0; i < nbytes; i++) {
1436
0
    if (buf[i] == IAC) {
1437
0
      if (!vty->iac) {
1438
0
        vty->iac = 1;
1439
0
        continue;
1440
0
      } else {
1441
0
        vty->iac = 0;
1442
0
      }
1443
0
    }
1444
0
1445
0
    if (vty->iac_sb_in_progress && !vty->iac) {
1446
0
      if (vty->sb_len < sizeof(vty->sb_buf))
1447
0
        vty->sb_buf[vty->sb_len] = buf[i];
1448
0
      vty->sb_len++;
1449
0
      continue;
1450
0
    }
1451
0
1452
0
    if (vty->iac) {
1453
0
      /* In case of telnet command */
1454
0
      int ret = 0;
1455
0
      ret = vty_telnet_option(vty, buf + i, nbytes - i);
1456
0
      vty->iac = 0;
1457
0
      i += ret;
1458
0
      continue;
1459
0
    }
1460
0
1461
0
1462
0
    if (vty->status == VTY_MORE) {
1463
0
      switch (buf[i]) {
1464
0
      case CONTROL('C'):
1465
0
      case 'q':
1466
0
      case 'Q':
1467
0
        vty_buffer_reset(vty);
1468
0
        break;
1469
0
      default:
1470
0
        break;
1471
0
      }
1472
0
      continue;
1473
0
    }
1474
0
1475
0
    /* Escape character. */
1476
0
    if (vty->escape == VTY_ESCAPE) {
1477
0
      vty_escape_map(buf[i], vty);
1478
0
      continue;
1479
0
    }
1480
0
1481
0
    /* Pre-escape status. */
1482
0
    if (vty->escape == VTY_PRE_ESCAPE) {
1483
0
      switch (buf[i]) {
1484
0
      case '[':
1485
0
        vty->escape = VTY_ESCAPE;
1486
0
        break;
1487
0
      case 'b':
1488
0
        vty_backward_word(vty);
1489
0
        vty->escape = VTY_NORMAL;
1490
0
        break;
1491
0
      case 'f':
1492
0
        vty_forward_word(vty);
1493
0
        vty->escape = VTY_NORMAL;
1494
0
        break;
1495
0
      case 'd':
1496
0
        vty_forward_kill_word(vty);
1497
0
        vty->escape = VTY_NORMAL;
1498
0
        break;
1499
0
      case CONTROL('H'):
1500
0
      case 0x7f:
1501
0
        vty_backward_kill_word(vty);
1502
0
        vty->escape = VTY_NORMAL;
1503
0
        break;
1504
0
      default:
1505
0
        vty->escape = VTY_NORMAL;
1506
0
        break;
1507
0
      }
1508
0
      continue;
1509
0
    }
1510
0
1511
0
    if (vty->escape == VTY_CR) {
1512
0
      /* if we get CR+NL, the NL results in an extra empty
1513
0
       * prompt line being printed without this;  just drop
1514
0
       * the NL if it immediately follows CR.
1515
0
       */
1516
0
      vty->escape = VTY_NORMAL;
1517
0
1518
0
      if (buf[i] == '\n')
1519
0
        continue;
1520
0
    }
1521
0
1522
0
    switch (buf[i]) {
1523
0
    case CONTROL('A'):
1524
0
      vty_beginning_of_line(vty);
1525
0
      break;
1526
0
    case CONTROL('B'):
1527
0
      vty_backward_char(vty);
1528
0
      break;
1529
0
    case CONTROL('C'):
1530
0
      vty_stop_input(vty);
1531
0
      break;
1532
0
    case CONTROL('D'):
1533
0
      vty_delete_char(vty);
1534
0
      break;
1535
0
    case CONTROL('E'):
1536
0
      vty_end_of_line(vty);
1537
0
      break;
1538
0
    case CONTROL('F'):
1539
0
      vty_forward_char(vty);
1540
0
      break;
1541
0
    case CONTROL('H'):
1542
0
    case 0x7f:
1543
0
      vty_delete_backward_char(vty);
1544
0
      break;
1545
0
    case CONTROL('K'):
1546
0
      vty_kill_line(vty);
1547
0
      break;
1548
0
    case CONTROL('N'):
1549
0
      vty_next_line(vty);
1550
0
      break;
1551
0
    case CONTROL('P'):
1552
0
      vty_previous_line(vty);
1553
0
      break;
1554
0
    case CONTROL('T'):
1555
0
      vty_transpose_chars(vty);
1556
0
      break;
1557
0
    case CONTROL('U'):
1558
0
      vty_kill_line_from_beginning(vty);
1559
0
      break;
1560
0
    case CONTROL('W'):
1561
0
      vty_backward_kill_word(vty);
1562
0
      break;
1563
0
    case CONTROL('Z'):
1564
0
      vty_end_config(vty);
1565
0
      break;
1566
0
    case '\r':
1567
0
      vty->escape = VTY_CR;
1568
0
      /* fallthru */
1569
0
    case '\n':
1570
0
      vty_out(vty, "\n");
1571
0
      buffer_flush_available(vty->obuf, vty->wfd);
1572
0
      vty_execute(vty);
1573
0
1574
0
      if (vty->pass_fd != -1) {
1575
0
        close(vty->pass_fd);
1576
0
        vty->pass_fd = -1;
1577
0
      }
1578
0
      break;
1579
0
    case '\t':
1580
0
      vty_complete_command(vty);
1581
0
      break;
1582
0
    case '?':
1583
0
      if (vty->node == AUTH_NODE
1584
0
          || vty->node == AUTH_ENABLE_NODE)
1585
0
        vty_self_insert(vty, buf[i]);
1586
0
      else
1587
0
        vty_describe_command(vty);
1588
0
      break;
1589
0
    case '\033':
1590
0
      if (i + 1 < nbytes && buf[i + 1] == '[') {
1591
0
        vty->escape = VTY_ESCAPE;
1592
0
        i++;
1593
0
      } else
1594
0
        vty->escape = VTY_PRE_ESCAPE;
1595
0
      break;
1596
0
    default:
1597
0
      if (buf[i] > 31 && buf[i] < 127)
1598
0
        vty_self_insert(vty, buf[i]);
1599
0
      break;
1600
0
    }
1601
0
  }
1602
0
1603
0
  /* Check status. */
1604
0
  if (vty->status == VTY_CLOSE)
1605
0
    vty_close(vty);
1606
0
  else {
1607
0
    vty_event(VTY_WRITE, vty);
1608
0
    vty_event(VTY_READ, vty);
1609
0
  }
1610
0
}
1611
1612
/* Flush buffer to the vty. */
1613
static void vty_flush(struct event *thread)
1614
0
{
1615
0
  int erase;
1616
0
  buffer_status_t flushrc;
1617
0
  struct vty *vty = EVENT_ARG(thread);
1618
0
1619
0
  /* Tempolary disable read thread. */
1620
0
  if (vty->lines == 0)
1621
0
    EVENT_OFF(vty->t_read);
1622
0
1623
0
  /* Function execution continue. */
1624
0
  erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
1625
0
1626
0
  /* N.B. if width is 0, that means we don't know the window size. */
1627
0
  if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
1628
0
    flushrc = buffer_flush_available(vty->obuf, vty->wfd);
1629
0
  else if (vty->status == VTY_MORELINE)
1630
0
    flushrc = buffer_flush_window(vty->obuf, vty->wfd, vty->width,
1631
0
                1, erase, 0);
1632
0
  else
1633
0
    flushrc = buffer_flush_window(
1634
0
      vty->obuf, vty->wfd, vty->width,
1635
0
      vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
1636
0
  switch (flushrc) {
1637
0
  case BUFFER_ERROR:
1638
0
    zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
1639
0
        vty->fd, vty->wfd);
1640
0
    buffer_reset(vty->lbuf);
1641
0
    buffer_reset(vty->obuf);
1642
0
    vty_close(vty);
1643
0
    return;
1644
0
  case BUFFER_EMPTY:
1645
0
    if (vty->status == VTY_CLOSE)
1646
0
      vty_close(vty);
1647
0
    else {
1648
0
      vty->status = VTY_NORMAL;
1649
0
      if (vty->lines == 0)
1650
0
        vty_event(VTY_READ, vty);
1651
0
    }
1652
0
    break;
1653
0
  case BUFFER_PENDING:
1654
0
    /* There is more data waiting to be written. */
1655
0
    vty->status = VTY_MORE;
1656
0
    if (vty->lines == 0)
1657
0
      vty_event(VTY_WRITE, vty);
1658
0
    break;
1659
0
  }
1660
0
}
1661
1662
/* Allocate new vty struct. */
1663
struct vty *vty_new(void)
1664
0
{
1665
0
  struct vty *new = XCALLOC(MTYPE_VTY, sizeof(struct vty));
1666
1667
0
  new->fd = new->wfd = -1;
1668
0
  new->of = stdout;
1669
0
  new->lbuf = buffer_new(0);
1670
0
  new->obuf = buffer_new(0); /* Use default buffer size. */
1671
0
  new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
1672
0
  new->max = VTY_BUFSIZ;
1673
0
  new->pass_fd = -1;
1674
1675
0
  if (mgmt_fe_client) {
1676
0
    if (!mgmt_client_id_next)
1677
0
      mgmt_client_id_next++;
1678
0
    new->mgmt_client_id = mgmt_client_id_next++;
1679
0
    new->mgmt_session_id = 0;
1680
0
    mgmt_fe_create_client_session(
1681
0
      mgmt_fe_client, new->mgmt_client_id, (uintptr_t) new);
1682
    /* we short-circuit create the session so it must be set now */
1683
0
    assertf(new->mgmt_session_id != 0,
1684
0
      "Failed to create client session for VTY");
1685
0
  }
1686
1687
0
  return new;
1688
0
}
1689
1690
1691
/* allocate and initialise vty */
1692
static struct vty *vty_new_init(int vty_sock)
1693
0
{
1694
0
  struct vty *vty;
1695
1696
0
  vty = vty_new();
1697
0
  vty->fd = vty_sock;
1698
0
  vty->wfd = vty_sock;
1699
0
  vty->type = VTY_TERM;
1700
0
  vty->node = AUTH_NODE;
1701
0
  vty->fail = 0;
1702
0
  vty->cp = 0;
1703
0
  vty_clear_buf(vty);
1704
0
  vty->length = 0;
1705
0
  memset(vty->hist, 0, sizeof(vty->hist));
1706
0
  vty->hp = 0;
1707
0
  vty->hindex = 0;
1708
0
  vty->xpath_index = 0;
1709
0
  memset(vty->xpath, 0, sizeof(vty->xpath));
1710
0
  vty->private_config = false;
1711
0
  vty->candidate_config = vty_shared_candidate_config;
1712
0
  vty->status = VTY_NORMAL;
1713
0
  vty->lines = -1;
1714
0
  vty->iac = 0;
1715
0
  vty->iac_sb_in_progress = 0;
1716
0
  vty->sb_len = 0;
1717
1718
0
  vtys_add_tail(vty_sessions, vty);
1719
1720
0
  return vty;
1721
0
}
1722
1723
/* Create new vty structure. */
1724
static struct vty *vty_create(int vty_sock, union sockunion *su)
1725
0
{
1726
0
  char buf[SU_ADDRSTRLEN];
1727
0
  struct vty *vty;
1728
0
1729
0
  sockunion2str(su, buf, SU_ADDRSTRLEN);
1730
0
1731
0
  /* Allocate new vty structure and set up default values. */
1732
0
  vty = vty_new_init(vty_sock);
1733
0
1734
0
  /* configurable parameters not part of basic init */
1735
0
  vty->v_timeout = vty_timeout_val;
1736
0
  strlcpy(vty->address, buf, sizeof(vty->address));
1737
0
  if (no_password_check) {
1738
0
    if (host.advanced)
1739
0
      vty->node = ENABLE_NODE;
1740
0
    else
1741
0
      vty->node = VIEW_NODE;
1742
0
  }
1743
0
  if (host.lines >= 0)
1744
0
    vty->lines = host.lines;
1745
0
1746
0
  if (!no_password_check) {
1747
0
    /* Vty is not available if password isn't set. */
1748
0
    if (host.password == NULL && host.password_encrypt == NULL) {
1749
0
      vty_out(vty, "Vty password is not set.\n");
1750
0
      vty->status = VTY_CLOSE;
1751
0
      vty_close(vty);
1752
0
      return NULL;
1753
0
    }
1754
0
  }
1755
0
1756
0
  /* Say hello to the world. */
1757
0
  vty_hello(vty);
1758
0
  if (!no_password_check)
1759
0
    vty_out(vty, "\nUser Access Verification\n\n");
1760
0
1761
0
  /* Setting up terminal. */
1762
0
  vty_will_echo(vty);
1763
0
  vty_will_suppress_go_ahead(vty);
1764
0
1765
0
  vty_dont_linemode(vty);
1766
0
  vty_do_window_size(vty);
1767
0
  /* vty_dont_lflow_ahead (vty); */
1768
0
1769
0
  vty_prompt(vty);
1770
0
1771
0
  /* Add read/write thread. */
1772
0
  vty_event(VTY_WRITE, vty);
1773
0
  vty_event(VTY_READ, vty);
1774
0
1775
0
  return vty;
1776
0
}
1777
1778
/* create vty for stdio */
1779
static struct termios stdio_orig_termios;
1780
static struct vty *stdio_vty = NULL;
1781
static bool stdio_termios = false;
1782
static void (*stdio_vty_atclose)(int isexit);
1783
1784
static void vty_stdio_reset(int isexit)
1785
4
{
1786
4
  if (stdio_vty) {
1787
0
    if (stdio_termios)
1788
0
      tcsetattr(0, TCSANOW, &stdio_orig_termios);
1789
0
    stdio_termios = false;
1790
1791
0
    stdio_vty = NULL;
1792
1793
0
    if (stdio_vty_atclose)
1794
0
      stdio_vty_atclose(isexit);
1795
0
    stdio_vty_atclose = NULL;
1796
0
  }
1797
4
}
1798
1799
static void vty_stdio_atexit(void)
1800
4
{
1801
4
  vty_stdio_reset(1);
1802
4
}
1803
1804
void vty_stdio_suspend(void)
1805
0
{
1806
0
  if (!stdio_vty)
1807
0
    return;
1808
1809
0
  EVENT_OFF(stdio_vty->t_write);
1810
0
  EVENT_OFF(stdio_vty->t_read);
1811
0
  EVENT_OFF(stdio_vty->t_timeout);
1812
1813
0
  if (stdio_termios)
1814
0
    tcsetattr(0, TCSANOW, &stdio_orig_termios);
1815
0
  stdio_termios = false;
1816
0
}
1817
1818
void vty_stdio_resume(void)
1819
0
{
1820
0
  if (!stdio_vty)
1821
0
    return;
1822
1823
0
  if (!tcgetattr(0, &stdio_orig_termios)) {
1824
0
    struct termios termios;
1825
1826
0
    termios = stdio_orig_termios;
1827
0
    termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
1828
0
             | IGNCR | ICRNL | IXON);
1829
0
    termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
1830
0
    termios.c_cflag &= ~(CSIZE | PARENB);
1831
0
    termios.c_cflag |= CS8;
1832
0
    tcsetattr(0, TCSANOW, &termios);
1833
0
    stdio_termios = true;
1834
0
  }
1835
1836
0
  vty_prompt(stdio_vty);
1837
1838
  /* Add read/write thread. */
1839
0
  vty_event(VTY_WRITE, stdio_vty);
1840
0
  vty_event(VTY_READ, stdio_vty);
1841
0
}
1842
1843
void vty_stdio_close(void)
1844
0
{
1845
0
  if (!stdio_vty)
1846
0
    return;
1847
0
  vty_close(stdio_vty);
1848
0
}
1849
1850
struct vty *vty_stdio(void (*atclose)(int isexit))
1851
0
{
1852
0
  struct vty *vty;
1853
1854
  /* refuse creating two vtys on stdio */
1855
0
  if (stdio_vty)
1856
0
    return NULL;
1857
1858
0
  vty = stdio_vty = vty_new_init(0);
1859
0
  stdio_vty_atclose = atclose;
1860
0
  vty->wfd = 1;
1861
1862
  /* always have stdio vty in a known _unchangeable_ state, don't want
1863
   * config
1864
   * to have any effect here to make sure scripting this works as intended
1865
   */
1866
0
  vty->node = ENABLE_NODE;
1867
0
  vty->v_timeout = 0;
1868
0
  strlcpy(vty->address, "console", sizeof(vty->address));
1869
1870
0
  vty_stdio_resume();
1871
0
  return vty;
1872
0
}
1873
1874
/* Accept connection from the network. */
1875
static void vty_accept(struct event *thread)
1876
0
{
1877
0
  struct vty_serv *vtyserv = EVENT_ARG(thread);
1878
0
  int vty_sock;
1879
0
  union sockunion su;
1880
0
  int ret;
1881
0
  unsigned int on;
1882
0
  int accept_sock = vtyserv->sock;
1883
0
  struct prefix p;
1884
0
  struct access_list *acl = NULL;
1885
0
1886
0
  /* We continue hearing vty socket. */
1887
0
  vty_event_serv(VTY_SERV, vtyserv);
1888
0
1889
0
  memset(&su, 0, sizeof(union sockunion));
1890
0
1891
0
  /* We can handle IPv4 or IPv6 socket. */
1892
0
  vty_sock = sockunion_accept(accept_sock, &su);
1893
0
  if (vty_sock < 0) {
1894
0
    flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
1895
0
       safe_strerror(errno));
1896
0
    return;
1897
0
  }
1898
0
  set_nonblocking(vty_sock);
1899
0
  set_cloexec(vty_sock);
1900
0
1901
0
  if (!sockunion2hostprefix(&su, &p)) {
1902
0
    close(vty_sock);
1903
0
    zlog_info("Vty unable to convert prefix from sockunion %pSU",
1904
0
        &su);
1905
0
    return;
1906
0
  }
1907
0
1908
0
  /* VTY's accesslist apply. */
1909
0
  if (p.family == AF_INET && vty_accesslist_name) {
1910
0
    if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
1911
0
        && (access_list_apply(acl, &p) == FILTER_DENY)) {
1912
0
      zlog_info("Vty connection refused from %pSU", &su);
1913
0
      close(vty_sock);
1914
0
      return;
1915
0
    }
1916
0
  }
1917
0
1918
0
  /* VTY's ipv6 accesslist apply. */
1919
0
  if (p.family == AF_INET6 && vty_ipv6_accesslist_name) {
1920
0
    if ((acl = access_list_lookup(AFI_IP6,
1921
0
                vty_ipv6_accesslist_name))
1922
0
        && (access_list_apply(acl, &p) == FILTER_DENY)) {
1923
0
      zlog_info("Vty connection refused from %pSU", &su);
1924
0
      close(vty_sock);
1925
0
      return;
1926
0
    }
1927
0
  }
1928
0
1929
0
  on = 1;
1930
0
  ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on,
1931
0
       sizeof(on));
1932
0
  if (ret < 0)
1933
0
    zlog_info("can't set sockopt to vty_sock : %s",
1934
0
        safe_strerror(errno));
1935
0
1936
0
  zlog_info("Vty connection from %pSU", &su);
1937
0
1938
0
  vty_create(vty_sock, &su);
1939
0
}
1940
1941
static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
1942
0
{
1943
0
  int ret;
1944
0
  struct addrinfo req;
1945
0
  struct addrinfo *ainfo;
1946
0
  struct addrinfo *ainfo_save;
1947
0
  int sock;
1948
0
  char port_str[BUFSIZ];
1949
1950
0
  memset(&req, 0, sizeof(req));
1951
0
  req.ai_flags = AI_PASSIVE;
1952
0
  req.ai_family = AF_UNSPEC;
1953
0
  req.ai_socktype = SOCK_STREAM;
1954
0
  snprintf(port_str, sizeof(port_str), "%d", port);
1955
0
  port_str[sizeof(port_str) - 1] = '\0';
1956
1957
0
  ret = getaddrinfo(hostname, port_str, &req, &ainfo);
1958
1959
0
  if (ret != 0) {
1960
0
    flog_err_sys(EC_LIB_SYSTEM_CALL, "getaddrinfo failed: %s",
1961
0
           gai_strerror(ret));
1962
0
    exit(1);
1963
0
  }
1964
1965
0
  ainfo_save = ainfo;
1966
1967
0
  do {
1968
0
    struct vty_serv *vtyserv;
1969
1970
0
    if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
1971
0
      continue;
1972
1973
0
    sock = socket(ainfo->ai_family, ainfo->ai_socktype,
1974
0
            ainfo->ai_protocol);
1975
0
    if (sock < 0)
1976
0
      continue;
1977
1978
0
    sockopt_v6only(ainfo->ai_family, sock);
1979
0
    sockopt_reuseaddr(sock);
1980
0
    sockopt_reuseport(sock);
1981
0
    set_cloexec(sock);
1982
1983
0
    ret = bind(sock, ainfo->ai_addr, ainfo->ai_addrlen);
1984
0
    if (ret < 0) {
1985
0
      close(sock); /* Avoid sd leak. */
1986
0
      continue;
1987
0
    }
1988
1989
0
    ret = listen(sock, 3);
1990
0
    if (ret < 0) {
1991
0
      close(sock); /* Avoid sd leak. */
1992
0
      continue;
1993
0
    }
1994
1995
0
    vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv));
1996
0
    vtyserv->sock = sock;
1997
0
    vtyservs_add_tail(vty_servs, vtyserv);
1998
1999
0
    vty_event_serv(VTY_SERV, vtyserv);
2000
0
  } while ((ainfo = ainfo->ai_next) != NULL);
2001
2002
0
  freeaddrinfo(ainfo_save);
2003
0
}
2004
2005
#ifdef VTYSH
2006
/* For sockaddr_un. */
2007
#include <sys/un.h>
2008
2009
/* VTY shell UNIX domain socket. */
2010
static void vty_serv_un(const char *path)
2011
0
{
2012
0
  struct vty_serv *vtyserv;
2013
0
  int ret;
2014
0
  int sock, len;
2015
0
  struct sockaddr_un serv;
2016
0
  mode_t old_mask;
2017
0
  struct zprivs_ids_t ids;
2018
2019
  /* First of all, unlink existing socket */
2020
0
  unlink(path);
2021
2022
  /* Set umask */
2023
0
  old_mask = umask(0007);
2024
2025
  /* Make UNIX domain socket. */
2026
0
  sock = socket(AF_UNIX, SOCK_STREAM, 0);
2027
0
  if (sock < 0) {
2028
0
    flog_err_sys(EC_LIB_SOCKET,
2029
0
           "Cannot create unix stream socket: %s",
2030
0
           safe_strerror(errno));
2031
0
    return;
2032
0
  }
2033
2034
  /* Make server socket. */
2035
0
  memset(&serv, 0, sizeof(serv));
2036
0
  serv.sun_family = AF_UNIX;
2037
0
  strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
2038
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
2039
  len = serv.sun_len = SUN_LEN(&serv);
2040
#else
2041
0
  len = sizeof(serv.sun_family) + strlen(serv.sun_path);
2042
0
#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
2043
2044
0
  set_cloexec(sock);
2045
2046
0
  ret = bind(sock, (struct sockaddr *)&serv, len);
2047
0
  if (ret < 0) {
2048
0
    flog_err_sys(EC_LIB_SOCKET, "Cannot bind path %s: %s", path,
2049
0
           safe_strerror(errno));
2050
0
    close(sock); /* Avoid sd leak. */
2051
0
    return;
2052
0
  }
2053
2054
0
  ret = listen(sock, 5);
2055
0
  if (ret < 0) {
2056
0
    flog_err_sys(EC_LIB_SOCKET, "listen(fd %d) failed: %s", sock,
2057
0
           safe_strerror(errno));
2058
0
    close(sock); /* Avoid sd leak. */
2059
0
    return;
2060
0
  }
2061
2062
0
  umask(old_mask);
2063
2064
0
  zprivs_get_ids(&ids);
2065
2066
  /* Hack: ids.gid_vty is actually a uint, but we stored -1 in it
2067
     earlier for the case when we don't need to chown the file
2068
     type casting it here to make a compare */
2069
0
  if ((int)ids.gid_vty > 0) {
2070
    /* set group of socket */
2071
0
    if (chown(path, -1, ids.gid_vty)) {
2072
0
      flog_err_sys(EC_LIB_SYSTEM_CALL,
2073
0
             "vty_serv_un: could chown socket, %s",
2074
0
             safe_strerror(errno));
2075
0
    }
2076
0
  }
2077
2078
0
  vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv));
2079
0
  vtyserv->sock = sock;
2080
0
  vtyserv->vtysh = true;
2081
0
  vtyservs_add_tail(vty_servs, vtyserv);
2082
2083
0
  vty_event_serv(VTYSH_SERV, vtyserv);
2084
0
}
2085
2086
/* #define VTYSH_DEBUG 1 */
2087
2088
static void vtysh_accept(struct event *thread)
2089
0
{
2090
0
  struct vty_serv *vtyserv = EVENT_ARG(thread);
2091
0
  int accept_sock = vtyserv->sock;
2092
0
  int sock;
2093
0
  int client_len;
2094
0
  struct sockaddr_un client;
2095
0
  struct vty *vty;
2096
0
2097
0
  vty_event_serv(VTYSH_SERV, vtyserv);
2098
0
2099
0
  memset(&client, 0, sizeof(client));
2100
0
  client_len = sizeof(struct sockaddr_un);
2101
0
2102
0
  sock = accept(accept_sock, (struct sockaddr *)&client,
2103
0
          (socklen_t *)&client_len);
2104
0
2105
0
  if (sock < 0) {
2106
0
    flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
2107
0
       safe_strerror(errno));
2108
0
    return;
2109
0
  }
2110
0
2111
0
  if (set_nonblocking(sock) < 0) {
2112
0
    flog_err(
2113
0
      EC_LIB_SOCKET,
2114
0
      "vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
2115
0
      sock, safe_strerror(errno));
2116
0
    close(sock);
2117
0
    return;
2118
0
  }
2119
0
  set_cloexec(sock);
2120
0
2121
0
#ifdef VTYSH_DEBUG
2122
0
  printf("VTY shell accept\n");
2123
0
#endif /* VTYSH_DEBUG */
2124
0
2125
0
  vty = vty_new();
2126
0
  vty->fd = sock;
2127
0
  vty->wfd = sock;
2128
0
  vty->type = VTY_SHELL_SERV;
2129
0
  vty->node = VIEW_NODE;
2130
0
  vtys_add_tail(vtysh_sessions, vty);
2131
0
2132
0
  vty_event(VTYSH_READ, vty);
2133
0
}
2134
2135
static int vtysh_do_pass_fd(struct vty *vty)
2136
0
{
2137
0
  struct iovec iov[1] = {
2138
0
    {
2139
0
      .iov_base = vty->pass_fd_status,
2140
0
      .iov_len = sizeof(vty->pass_fd_status),
2141
0
    },
2142
0
  };
2143
0
  union {
2144
0
    uint8_t buf[CMSG_SPACE(sizeof(int))];
2145
0
    struct cmsghdr align;
2146
0
  } u;
2147
0
  struct msghdr mh = {
2148
0
    .msg_iov = iov,
2149
0
    .msg_iovlen = array_size(iov),
2150
0
    .msg_control = u.buf,
2151
0
    .msg_controllen = sizeof(u.buf),
2152
0
  };
2153
0
  struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh);
2154
0
  ssize_t ret;
2155
2156
0
  memset(&u.buf, 0, sizeof(u.buf));
2157
0
  cmh->cmsg_level = SOL_SOCKET;
2158
0
  cmh->cmsg_type = SCM_RIGHTS;
2159
0
  cmh->cmsg_len = CMSG_LEN(sizeof(int));
2160
0
  memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int));
2161
2162
0
  ret = sendmsg(vty->wfd, &mh, 0);
2163
0
  if (ret < 0 && ERRNO_IO_RETRY(errno))
2164
0
    return BUFFER_PENDING;
2165
2166
0
  close(vty->pass_fd);
2167
0
  vty->pass_fd = -1;
2168
0
  vty->status = VTY_NORMAL;
2169
2170
0
  if (ret <= 0)
2171
0
    return BUFFER_ERROR;
2172
2173
  /* resume accepting commands (suspended in vtysh_read) */
2174
0
  vty_event(VTYSH_READ, vty);
2175
2176
0
  if ((size_t)ret < sizeof(vty->pass_fd_status)) {
2177
0
    size_t remains = sizeof(vty->pass_fd_status) - ret;
2178
2179
0
    buffer_put(vty->obuf, vty->pass_fd_status + ret, remains);
2180
0
    return BUFFER_PENDING;
2181
0
  }
2182
0
  return BUFFER_EMPTY;
2183
0
}
2184
2185
static int vtysh_flush(struct vty *vty)
2186
0
{
2187
0
  int ret;
2188
2189
0
  ret = buffer_flush_available(vty->obuf, vty->wfd);
2190
0
  if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD)
2191
0
    ret = vtysh_do_pass_fd(vty);
2192
2193
0
  switch (ret) {
2194
0
  case BUFFER_PENDING:
2195
0
    vty_event(VTYSH_WRITE, vty);
2196
0
    break;
2197
0
  case BUFFER_ERROR:
2198
0
    flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
2199
0
       __func__, vty->fd);
2200
0
    buffer_reset(vty->lbuf);
2201
0
    buffer_reset(vty->obuf);
2202
0
    vty_close(vty);
2203
0
    return -1;
2204
0
  case BUFFER_EMPTY:
2205
0
    break;
2206
0
  }
2207
0
  return 0;
2208
0
}
2209
2210
void vty_pass_fd(struct vty *vty, int fd)
2211
0
{
2212
0
  if (vty->pass_fd != -1)
2213
0
    close(vty->pass_fd);
2214
2215
0
  vty->pass_fd = fd;
2216
0
}
2217
2218
bool mgmt_vty_read_configs(void)
2219
0
{
2220
0
  char path[PATH_MAX];
2221
0
  struct vty *vty;
2222
0
  FILE *confp;
2223
0
  uint line_num = 0;
2224
0
  uint count = 0;
2225
0
  uint index;
2226
2227
0
  vty = vty_new();
2228
0
  vty->wfd = STDERR_FILENO;
2229
0
  vty->type = VTY_FILE;
2230
0
  vty->node = CONFIG_NODE;
2231
0
  vty->config = true;
2232
0
  vty->pending_allowed = true;
2233
2234
0
  vty->candidate_config = vty_shared_candidate_config;
2235
2236
0
  vty_mgmt_lock_candidate_inline(vty);
2237
0
  vty_mgmt_lock_running_inline(vty);
2238
2239
0
  for (index = 0; index < array_size(mgmt_daemons); index++) {
2240
0
    snprintf(path, sizeof(path), "%s/%s.conf", frr_sysconfdir,
2241
0
       mgmt_daemons[index]);
2242
2243
0
    confp = vty_open_config(path, config_default);
2244
0
    if (!confp)
2245
0
      continue;
2246
2247
0
    zlog_info("mgmtd: reading config file: %s", path);
2248
2249
    /* Execute configuration file */
2250
0
    line_num = 0;
2251
0
    (void)config_from_file(vty, confp, &line_num);
2252
0
    count++;
2253
2254
0
    fclose(confp);
2255
0
  }
2256
2257
0
  snprintf(path, sizeof(path), "%s/mgmtd.conf", frr_sysconfdir);
2258
0
  confp = vty_open_config(path, config_default);
2259
0
  if (!confp) {
2260
0
    char *orig;
2261
2262
0
    snprintf(path, sizeof(path), "%s/zebra.conf", frr_sysconfdir);
2263
0
    orig = XSTRDUP(MTYPE_TMP, host_config_get());
2264
2265
0
    zlog_info("mgmtd: trying backup config file: %s", path);
2266
0
    confp = vty_open_config(path, config_default);
2267
2268
0
    host_config_set(path);
2269
0
    XFREE(MTYPE_TMP, orig);
2270
0
  }
2271
2272
0
  if (confp) {
2273
0
    zlog_info("mgmtd: reading config file: %s", path);
2274
2275
0
    line_num = 0;
2276
0
    (void)config_from_file(vty, confp, &line_num);
2277
0
    count++;
2278
2279
0
    fclose(confp);
2280
0
  }
2281
2282
  /* Conditionally unlock as the config file may have "exit"d early which
2283
   * would then have unlocked things.
2284
   */
2285
0
  if (vty->mgmt_locked_running_ds)
2286
0
    vty_mgmt_unlock_running_inline(vty);
2287
0
  if (vty->mgmt_locked_candidate_ds)
2288
0
    vty_mgmt_unlock_candidate_inline(vty);
2289
2290
0
  vty->pending_allowed = false;
2291
2292
0
  if (!count)
2293
0
    vty_close(vty);
2294
0
  else
2295
0
    vty_read_file_finish(vty, NULL);
2296
2297
0
  zlog_info("mgmtd: finished reading config files");
2298
2299
0
  return true;
2300
0
}
2301
2302
static void vtysh_read(struct event *thread)
2303
0
{
2304
0
  int ret;
2305
0
  int sock;
2306
0
  int nbytes;
2307
0
  struct vty *vty;
2308
0
  unsigned char buf[VTY_READ_BUFSIZ];
2309
0
  unsigned char *p;
2310
0
  uint8_t header[4] = {0, 0, 0, 0};
2311
0
2312
0
  sock = EVENT_FD(thread);
2313
0
  vty = EVENT_ARG(thread);
2314
0
2315
0
  /*
2316
0
   * This code looks like it can read multiple commands from the `buf`
2317
0
   * value returned by read(); however, it cannot in some cases.
2318
0
   *
2319
0
   * There are multiple paths out of the "copying to vty->buf" loop, which
2320
0
   * lose any content not yet copied from the stack `buf`, `passfd`,
2321
0
   * `CMD_SUSPEND` and finally if a front-end for mgmtd (generally this
2322
0
   * would be mgmtd itself). So these code paths are counting on vtysh not
2323
0
   * sending us more than 1 command line before waiting on the reply to
2324
0
   * that command.
2325
0
   */
2326
0
  assert(vty->type == VTY_SHELL_SERV);
2327
0
2328
0
  if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
2329
0
    if (nbytes < 0) {
2330
0
      if (ERRNO_IO_RETRY(errno)) {
2331
0
        vty_event(VTYSH_READ, vty);
2332
0
        return;
2333
0
      }
2334
0
      flog_err(
2335
0
        EC_LIB_SOCKET,
2336
0
        "%s: read failed on vtysh client fd %d, closing: %s",
2337
0
        __func__, sock, safe_strerror(errno));
2338
0
    }
2339
0
    buffer_reset(vty->lbuf);
2340
0
    buffer_reset(vty->obuf);
2341
0
    vty_close(vty);
2342
0
#ifdef VTYSH_DEBUG
2343
0
    printf("close vtysh\n");
2344
0
#endif /* VTYSH_DEBUG */
2345
0
    return;
2346
0
  }
2347
0
2348
0
#ifdef VTYSH_DEBUG
2349
0
  printf("line: %.*s\n", nbytes, buf);
2350
0
#endif /* VTYSH_DEBUG */
2351
0
2352
0
  if (vty->length + nbytes >= VTY_BUFSIZ) {
2353
0
    /* Clear command line buffer. */
2354
0
    vty->cp = vty->length = 0;
2355
0
    vty_clear_buf(vty);
2356
0
    vty_out(vty, "%% Command is too long.\n");
2357
0
  } else {
2358
0
    for (p = buf; p < buf + nbytes; p++) {
2359
0
      vty->buf[vty->length++] = *p;
2360
0
      if (*p == '\0') {
2361
0
        /* Pass this line to parser. */
2362
0
        ret = vty_execute(vty);
2363
0
/* Note that vty_execute clears the command buffer and resets
2364
0
   vty->length to 0. */
2365
0
2366
0
/* Return result. */
2367
0
#ifdef VTYSH_DEBUG
2368
0
        printf("result: %d\n", ret);
2369
0
        printf("vtysh node: %d\n", vty->node);
2370
0
#endif /* VTYSH_DEBUG */
2371
0
2372
0
        if (vty->pass_fd != -1) {
2373
0
          memset(vty->pass_fd_status, 0, 4);
2374
0
          vty->pass_fd_status[3] = ret;
2375
0
          vty->status = VTY_PASSFD;
2376
0
2377
0
          if (!vty->t_write)
2378
0
            vty_event(VTYSH_WRITE, vty);
2379
0
2380
0
          /* this introduces a "sequence point"
2381
0
           * command output is written normally,
2382
0
           * read processing is suspended until
2383
0
           * buffer is empty
2384
0
           * then retcode + FD is written
2385
0
           * then normal processing resumes
2386
0
           *
2387
0
           * => skip vty_event(VTYSH_READ, vty)!
2388
0
           */
2389
0
          return;
2390
0
        }
2391
0
2392
0
        /* hack for asynchronous "write integrated"
2393
0
         * - other commands in "buf" will be ditched
2394
0
         * - input during pending config-write is
2395
0
         * "unsupported" */
2396
0
        if (ret == CMD_SUSPEND)
2397
0
          break;
2398
0
2399
0
        /* with new infra we need to stop response till
2400
0
         * we get response through callback.
2401
0
         */
2402
0
        if (vty->mgmt_req_pending_cmd) {
2403
0
          MGMTD_FE_CLIENT_DBG(
2404
0
            "postpone CLI response pending mgmtd %s on vty session-id %" PRIu64,
2405
0
            vty->mgmt_req_pending_cmd,
2406
0
            vty->mgmt_session_id);
2407
0
          return;
2408
0
        }
2409
0
2410
0
        /* warning: watchfrr hardcodes this result write
2411
0
         */
2412
0
        header[3] = ret;
2413
0
        buffer_put(vty->obuf, header, 4);
2414
0
2415
0
        if (!vty->t_write && (vtysh_flush(vty) < 0))
2416
0
          /* Try to flush results; exit if a write
2417
0
           * error occurs. */
2418
0
          return;
2419
0
      }
2420
0
    }
2421
0
  }
2422
0
2423
0
  if (vty->status == VTY_CLOSE)
2424
0
    vty_close(vty);
2425
0
  else
2426
0
    vty_event(VTYSH_READ, vty);
2427
0
}
2428
2429
static void vtysh_write(struct event *thread)
2430
0
{
2431
0
  struct vty *vty = EVENT_ARG(thread);
2432
0
2433
0
  vtysh_flush(vty);
2434
0
}
2435
2436
#endif /* VTYSH */
2437
2438
/* Determine address family to bind. */
2439
void vty_serv_start(const char *addr, unsigned short port, const char *path)
2440
0
{
2441
  /* If port is set to 0, do not listen on TCP/IP at all! */
2442
0
  if (port)
2443
0
    vty_serv_sock_addrinfo(addr, port);
2444
2445
0
#ifdef VTYSH
2446
0
  vty_serv_un(path);
2447
0
#endif /* VTYSH */
2448
0
}
2449
2450
void vty_serv_stop(void)
2451
0
{
2452
0
  struct vty_serv *vtyserv;
2453
2454
0
  while ((vtyserv = vtyservs_pop(vty_servs))) {
2455
0
    EVENT_OFF(vtyserv->t_accept);
2456
0
    close(vtyserv->sock);
2457
0
    XFREE(MTYPE_VTY_SERV, vtyserv);
2458
0
  }
2459
2460
0
  vtyservs_fini(vty_servs);
2461
0
  vtyservs_init(vty_servs);
2462
0
}
2463
2464
static void vty_error_delete(void *arg)
2465
0
{
2466
0
  struct vty_error *ve = arg;
2467
2468
0
  XFREE(MTYPE_TMP, ve);
2469
0
}
2470
2471
/* Close vty interface.  Warning: call this only from functions that
2472
   will be careful not to access the vty afterwards (since it has
2473
   now been freed).  This is safest from top-level functions (called
2474
   directly by the thread dispatcher). */
2475
void vty_close(struct vty *vty)
2476
0
{
2477
0
  int i;
2478
0
  bool was_stdio = false;
2479
2480
0
  vty->status = VTY_CLOSE;
2481
2482
  /*
2483
   * If we reach here with pending config to commit we will be losing it
2484
   * so warn the user.
2485
   */
2486
0
  if (vty->mgmt_num_pending_setcfg)
2487
0
    MGMTD_FE_CLIENT_ERR(
2488
0
      "vty closed, uncommitted config will be lost.");
2489
2490
  /* Drop out of configure / transaction if needed. */
2491
0
  vty_config_exit(vty);
2492
2493
0
  if (mgmt_fe_client && vty->mgmt_session_id) {
2494
0
    MGMTD_FE_CLIENT_DBG("closing vty session");
2495
0
    mgmt_fe_destroy_client_session(mgmt_fe_client,
2496
0
                 vty->mgmt_client_id);
2497
0
    vty->mgmt_session_id = 0;
2498
0
  }
2499
2500
  /* Cancel threads.*/
2501
0
  EVENT_OFF(vty->t_read);
2502
0
  EVENT_OFF(vty->t_write);
2503
0
  EVENT_OFF(vty->t_timeout);
2504
2505
0
  if (vty->pass_fd != -1) {
2506
0
    close(vty->pass_fd);
2507
0
    vty->pass_fd = -1;
2508
0
  }
2509
0
  zlog_live_close(&vty->live_log);
2510
2511
  /* Flush buffer. */
2512
0
  buffer_flush_all(vty->obuf, vty->wfd);
2513
2514
  /* Free input buffer. */
2515
0
  buffer_free(vty->obuf);
2516
0
  buffer_free(vty->lbuf);
2517
2518
  /* Free command history. */
2519
0
  for (i = 0; i < VTY_MAXHIST; i++) {
2520
0
    XFREE(MTYPE_VTY_HIST, vty->hist[i]);
2521
0
  }
2522
2523
  /* Unset vector. */
2524
0
  if (vty->fd != -1) {
2525
0
    if (vty->type == VTY_SHELL_SERV)
2526
0
      vtys_del(vtysh_sessions, vty);
2527
0
    else if (vty->type == VTY_TERM)
2528
0
      vtys_del(vty_sessions, vty);
2529
0
  }
2530
2531
0
  if (vty->wfd > 0 && vty->type == VTY_FILE)
2532
0
    fsync(vty->wfd);
2533
2534
  /* Close socket.
2535
   * note check is for fd > STDERR_FILENO, not fd != -1.
2536
   * We never close stdin/stdout/stderr here, because we may be
2537
   * running in foreground mode with logging to stdout.  Also,
2538
   * additionally, we'd need to replace these fds with /dev/null. */
2539
0
  if (vty->wfd > STDERR_FILENO && vty->wfd != vty->fd)
2540
0
    close(vty->wfd);
2541
0
  if (vty->fd > STDERR_FILENO)
2542
0
    close(vty->fd);
2543
0
  if (vty->fd == STDIN_FILENO)
2544
0
    was_stdio = true;
2545
2546
0
  XFREE(MTYPE_TMP, vty->pending_cmds_buf);
2547
0
  XFREE(MTYPE_VTY, vty->buf);
2548
2549
0
  if (vty->error) {
2550
0
    vty->error->del = vty_error_delete;
2551
0
    list_delete(&vty->error);
2552
0
  }
2553
2554
  /* OK free vty. */
2555
0
  XFREE(MTYPE_VTY, vty);
2556
2557
0
  if (was_stdio)
2558
0
    vty_stdio_reset(0);
2559
0
}
2560
2561
/* When time out occur output message then close connection. */
2562
static void vty_timeout(struct event *thread)
2563
0
{
2564
0
  struct vty *vty;
2565
0
2566
0
  vty = EVENT_ARG(thread);
2567
0
  vty->v_timeout = 0;
2568
0
2569
0
  /* Clear buffer*/
2570
0
  buffer_reset(vty->lbuf);
2571
0
  buffer_reset(vty->obuf);
2572
0
  vty_out(vty, "\nVty connection is timed out.\n");
2573
0
2574
0
  /* Close connection. */
2575
0
  vty->status = VTY_CLOSE;
2576
0
  vty_close(vty);
2577
0
}
2578
2579
/* Read up configuration file from file_name. */
2580
void vty_read_file(struct nb_config *config, FILE *confp)
2581
0
{
2582
0
  struct vty *vty;
2583
0
  unsigned int line_num = 0;
2584
2585
0
  vty = vty_new();
2586
  /* vty_close won't close stderr;  if some config command prints
2587
   * something it'll end up there.  (not ideal; it'd be better if output
2588
   * from a file-load went to logging instead.  Also note that if this
2589
   * function is called after daemonizing, stderr will be /dev/null.)
2590
   *
2591
   * vty->fd will be -1 from vty_new()
2592
   */
2593
0
  vty->wfd = STDERR_FILENO;
2594
0
  vty->type = VTY_FILE;
2595
0
  vty->node = CONFIG_NODE;
2596
0
  vty->config = true;
2597
0
  if (config)
2598
0
    vty->candidate_config = config;
2599
0
  else {
2600
0
    vty->private_config = true;
2601
0
    vty->candidate_config = nb_config_new(NULL);
2602
0
  }
2603
2604
  /* Execute configuration file */
2605
0
  (void)config_from_file(vty, confp, &line_num);
2606
2607
0
  vty_read_file_finish(vty, config);
2608
0
}
2609
2610
void vty_read_file_finish(struct vty *vty, struct nb_config *config)
2611
0
{
2612
0
  struct vty_error *ve;
2613
0
  struct listnode *node;
2614
2615
  /* Flush any previous errors before printing messages below */
2616
0
  buffer_flush_all(vty->obuf, vty->wfd);
2617
2618
0
  for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
2619
0
    const char *message = NULL;
2620
0
    char *nl;
2621
2622
0
    switch (ve->cmd_ret) {
2623
0
    case CMD_SUCCESS:
2624
0
      message = "Command succeeded";
2625
0
      break;
2626
0
    case CMD_ERR_NOTHING_TODO:
2627
0
      message = "Nothing to do";
2628
0
      break;
2629
0
    case CMD_ERR_AMBIGUOUS:
2630
0
      message = "Ambiguous command";
2631
0
      break;
2632
0
    case CMD_ERR_NO_MATCH:
2633
0
      message = "No such command";
2634
0
      break;
2635
0
    case CMD_WARNING:
2636
0
      message = "Command returned Warning";
2637
0
      break;
2638
0
    case CMD_WARNING_CONFIG_FAILED:
2639
0
      message = "Command returned Warning Config Failed";
2640
0
      break;
2641
0
    case CMD_ERR_INCOMPLETE:
2642
0
      message = "Command returned Incomplete";
2643
0
      break;
2644
0
    case CMD_ERR_EXEED_ARGC_MAX:
2645
0
      message =
2646
0
        "Command exceeded maximum number of Arguments";
2647
0
      break;
2648
0
    default:
2649
0
      message = "Command returned unhandled error message";
2650
0
      break;
2651
0
    }
2652
2653
0
    nl = strchr(ve->error_buf, '\n');
2654
0
    if (nl)
2655
0
      *nl = '\0';
2656
0
    flog_err(EC_LIB_VTY, "%s on config line %u: %s", message,
2657
0
       ve->line_num, ve->error_buf);
2658
0
  }
2659
2660
  /*
2661
   * Automatically commit the candidate configuration after
2662
   * reading the configuration file.
2663
   */
2664
0
  if (config == NULL) {
2665
0
    struct nb_context context = {};
2666
0
    char errmsg[BUFSIZ] = {0};
2667
0
    int ret;
2668
2669
0
    context.client = NB_CLIENT_CLI;
2670
0
    context.user = vty;
2671
0
    ret = nb_candidate_commit(context, vty->candidate_config, true,
2672
0
            "Read configuration file", NULL,
2673
0
            errmsg, sizeof(errmsg));
2674
0
    if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
2675
0
      zlog_err(
2676
0
        "%s: failed to read configuration file: %s (%s)",
2677
0
        __func__, nb_err_name(ret), errmsg);
2678
0
  }
2679
2680
0
  vty_close(vty);
2681
0
}
2682
2683
static FILE *vty_use_backup_config(const char *fullpath)
2684
0
{
2685
0
  char *fullpath_sav, *fullpath_tmp;
2686
0
  FILE *ret = NULL;
2687
0
  int tmp, sav;
2688
0
  int c;
2689
0
  char buffer[512];
2690
2691
0
  size_t fullpath_sav_sz = strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1;
2692
0
  fullpath_sav = malloc(fullpath_sav_sz);
2693
0
  strlcpy(fullpath_sav, fullpath, fullpath_sav_sz);
2694
0
  strlcat(fullpath_sav, CONF_BACKUP_EXT, fullpath_sav_sz);
2695
2696
0
  sav = open(fullpath_sav, O_RDONLY);
2697
0
  if (sav < 0) {
2698
0
    free(fullpath_sav);
2699
0
    return NULL;
2700
0
  }
2701
2702
0
  fullpath_tmp = malloc(strlen(fullpath) + 8);
2703
0
  snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath);
2704
2705
  /* Open file to configuration write. */
2706
0
  tmp = mkstemp(fullpath_tmp);
2707
0
  if (tmp < 0)
2708
0
    goto out_close_sav;
2709
2710
0
  if (fchmod(tmp, CONFIGFILE_MASK) != 0)
2711
0
    goto out_close;
2712
2713
0
  while ((c = read(sav, buffer, 512)) > 0) {
2714
0
    if (write(tmp, buffer, c) <= 0)
2715
0
      goto out_close;
2716
0
  }
2717
0
  close(sav);
2718
0
  close(tmp);
2719
2720
0
  if (rename(fullpath_tmp, fullpath) == 0)
2721
0
    ret = fopen(fullpath, "r");
2722
0
  else
2723
0
    unlink(fullpath_tmp);
2724
2725
0
  if (0) {
2726
0
  out_close:
2727
0
    close(tmp);
2728
0
    unlink(fullpath_tmp);
2729
0
  out_close_sav:
2730
0
    close(sav);
2731
0
  }
2732
2733
0
  free(fullpath_sav);
2734
0
  free(fullpath_tmp);
2735
0
  return ret;
2736
0
}
2737
2738
FILE *vty_open_config(const char *config_file, char *config_default_dir)
2739
0
{
2740
0
  char cwd[MAXPATHLEN];
2741
0
  FILE *confp = NULL;
2742
0
  const char *fullpath;
2743
0
  char *tmp = NULL;
2744
2745
  /* If -f flag specified. */
2746
0
  if (config_file != NULL) {
2747
0
    if (!IS_DIRECTORY_SEP(config_file[0])) {
2748
0
      if (getcwd(cwd, MAXPATHLEN) == NULL) {
2749
0
        flog_err_sys(
2750
0
          EC_LIB_SYSTEM_CALL,
2751
0
          "%s: failure to determine Current Working Directory %d!",
2752
0
          __func__, errno);
2753
0
        goto tmp_free_and_out;
2754
0
      }
2755
0
      size_t tmp_len = strlen(cwd) + strlen(config_file) + 2;
2756
0
      tmp = XMALLOC(MTYPE_TMP, tmp_len);
2757
0
      snprintf(tmp, tmp_len, "%s/%s", cwd, config_file);
2758
0
      fullpath = tmp;
2759
0
    } else
2760
0
      fullpath = config_file;
2761
2762
0
    confp = fopen(fullpath, "r");
2763
2764
0
    if (confp == NULL) {
2765
0
      flog_warn(
2766
0
        EC_LIB_BACKUP_CONFIG,
2767
0
        "%s: failed to open configuration file %s: %s, checking backup",
2768
0
        __func__, fullpath, safe_strerror(errno));
2769
2770
0
      confp = vty_use_backup_config(fullpath);
2771
0
      if (confp)
2772
0
        flog_warn(EC_LIB_BACKUP_CONFIG,
2773
0
            "using backup configuration file!");
2774
0
      else {
2775
0
        flog_err(
2776
0
          EC_LIB_VTY,
2777
0
          "%s: can't open configuration file [%s]",
2778
0
          __func__, config_file);
2779
0
        goto tmp_free_and_out;
2780
0
      }
2781
0
    }
2782
0
  } else {
2783
2784
0
    host_config_set(config_default_dir);
2785
2786
0
#ifdef VTYSH
2787
0
    int ret;
2788
0
    struct stat conf_stat;
2789
2790
    /* !!!!PLEASE LEAVE!!!!
2791
     * This is NEEDED for use with vtysh -b, or else you can get
2792
     * a real configuration food fight with a lot garbage in the
2793
     * merged configuration file it creates coming from the per
2794
     * daemon configuration files.  This also allows the daemons
2795
     * to start if there default configuration file is not
2796
     * present or ignore them, as needed when using vtysh -b to
2797
     * configure the daemons at boot - MAG
2798
     */
2799
2800
    /* Stat for vtysh Zebra.conf, if found startup and wait for
2801
     * boot configuration
2802
     */
2803
2804
0
    if (strstr(config_default_dir, "vtysh") == NULL) {
2805
0
      ret = stat(integrate_default, &conf_stat);
2806
0
      if (ret >= 0)
2807
0
        goto tmp_free_and_out;
2808
0
    }
2809
0
#endif /* VTYSH */
2810
0
    confp = fopen(config_default_dir, "r");
2811
0
    if (confp == NULL) {
2812
0
      flog_err(
2813
0
        EC_LIB_SYSTEM_CALL,
2814
0
        "%s: failed to open configuration file %s: %s, checking backup",
2815
0
        __func__, config_default_dir,
2816
0
        safe_strerror(errno));
2817
2818
0
      confp = vty_use_backup_config(config_default_dir);
2819
0
      if (confp) {
2820
0
        flog_warn(EC_LIB_BACKUP_CONFIG,
2821
0
            "using backup configuration file!");
2822
0
        fullpath = config_default_dir;
2823
0
      } else {
2824
0
        flog_err(EC_LIB_VTY,
2825
0
           "can't open configuration file [%s]",
2826
0
           config_default_dir);
2827
0
        goto tmp_free_and_out;
2828
0
      }
2829
0
    } else
2830
0
      fullpath = config_default_dir;
2831
0
  }
2832
2833
0
  host_config_set(fullpath);
2834
2835
0
tmp_free_and_out:
2836
0
  XFREE(MTYPE_TMP, tmp);
2837
2838
0
  return confp;
2839
0
}
2840
2841
2842
bool vty_read_config(struct nb_config *config, const char *config_file,
2843
         char *config_default_dir)
2844
0
{
2845
0
  FILE *confp;
2846
2847
0
  confp = vty_open_config(config_file, config_default_dir);
2848
0
  if (!confp)
2849
0
    return false;
2850
2851
0
  vty_read_file(config, confp);
2852
2853
0
  fclose(confp);
2854
2855
0
  return true;
2856
0
}
2857
2858
int vty_config_enter(struct vty *vty, bool private_config, bool exclusive,
2859
         bool file_lock)
2860
0
{
2861
0
  if (exclusive && !vty_mgmt_fe_enabled() &&
2862
0
      nb_running_lock(NB_CLIENT_CLI, vty)) {
2863
0
    vty_out(vty, "%% Configuration is locked by other client\n");
2864
0
    return CMD_WARNING;
2865
0
  }
2866
2867
  /*
2868
   * We only need to do a lock when reading a config file as we will be
2869
   * sending a batch of setcfg changes followed by a single commit
2870
   * message. For user interactive mode we are doing implicit commits
2871
   * those will obtain the lock (or not) when they try and commit.
2872
   */
2873
0
  if (file_lock && vty_mgmt_fe_enabled() && !private_config) {
2874
0
    if (vty_mgmt_lock_candidate_inline(vty)) {
2875
0
      vty_out(vty,
2876
0
        "%% Can't enter config; candidate datastore locked by another session\n");
2877
0
      return CMD_WARNING_CONFIG_FAILED;
2878
0
    }
2879
0
    if (vty_mgmt_lock_running_inline(vty)) {
2880
0
      vty_out(vty,
2881
0
        "%% Can't enter config; running datastore locked by another session\n");
2882
0
      vty_mgmt_unlock_candidate_inline(vty);
2883
0
      return CMD_WARNING_CONFIG_FAILED;
2884
0
    }
2885
0
    assert(vty->mgmt_locked_candidate_ds);
2886
0
    assert(vty->mgmt_locked_running_ds);
2887
0
  }
2888
2889
0
  vty->node = CONFIG_NODE;
2890
0
  vty->config = true;
2891
0
  vty->private_config = private_config;
2892
0
  vty->xpath_index = 0;
2893
2894
0
  if (private_config) {
2895
0
    vty->candidate_config = nb_config_dup(running_config);
2896
0
    vty->candidate_config_base = nb_config_dup(running_config);
2897
0
    vty_out(vty,
2898
0
      "Warning: uncommitted changes will be discarded on exit.\n\n");
2899
0
    return CMD_SUCCESS;
2900
0
  }
2901
2902
  /*
2903
   * NOTE: On the MGMTD daemon we point the VTY candidate DS to
2904
   * the global MGMTD candidate DS. Else we point to the VTY
2905
   * Shared Candidate Config.
2906
   */
2907
0
  vty->candidate_config = vty_mgmt_candidate_config
2908
0
          ? vty_mgmt_candidate_config
2909
0
          : vty_shared_candidate_config;
2910
0
  if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
2911
0
    vty->candidate_config_base = nb_config_dup(running_config);
2912
2913
0
  return CMD_SUCCESS;
2914
0
}
2915
2916
2917
void vty_config_exit(struct vty *vty)
2918
0
{
2919
0
  enum node_type node = vty->node;
2920
0
  struct cmd_node *cnode;
2921
2922
  /* unlock and jump up to ENABLE_NODE if -and only if- we're
2923
   * somewhere below CONFIG_NODE */
2924
0
  while (node && node != CONFIG_NODE) {
2925
0
    cnode = vector_lookup(cmdvec, node);
2926
0
    node = cnode->parent_node;
2927
0
  }
2928
0
  if (node != CONFIG_NODE)
2929
    /* called outside config, e.g. vty_close() in ENABLE_NODE */
2930
0
    return;
2931
2932
0
  while (vty->node != ENABLE_NODE)
2933
    /* will call vty_config_node_exit() below */
2934
0
    cmd_exit(vty);
2935
0
}
2936
2937
int vty_config_node_exit(struct vty *vty)
2938
0
{
2939
0
  vty->xpath_index = 0;
2940
2941
  /* TODO: could we check for un-commited changes here? */
2942
2943
0
  if (vty->mgmt_locked_running_ds)
2944
0
    vty_mgmt_unlock_running_inline(vty);
2945
2946
0
  if (vty->mgmt_locked_candidate_ds)
2947
0
    vty_mgmt_unlock_candidate_inline(vty);
2948
2949
  /* Perform any pending commits. */
2950
0
  (void)nb_cli_pending_commit_check(vty);
2951
2952
  /* Check if there's a pending confirmed commit. */
2953
0
  if (vty->t_confirmed_commit_timeout) {
2954
0
    vty_out(vty,
2955
0
      "exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
2956
0
    nb_cli_confirmed_commit_rollback(vty);
2957
0
    nb_cli_confirmed_commit_clean(vty);
2958
0
  }
2959
2960
0
  (void)nb_running_unlock(NB_CLIENT_CLI, vty);
2961
2962
0
  if (vty->candidate_config) {
2963
0
    if (vty->private_config)
2964
0
      nb_config_free(vty->candidate_config);
2965
0
    vty->candidate_config = NULL;
2966
0
  }
2967
0
  if (vty->candidate_config_base) {
2968
0
    nb_config_free(vty->candidate_config_base);
2969
0
    vty->candidate_config_base = NULL;
2970
0
  }
2971
2972
0
  vty->config = false;
2973
2974
  /*
2975
   * If this is a config file and we are dropping out of config end
2976
   * parsing.
2977
   */
2978
0
  if (vty->type == VTY_FILE && vty->status != VTY_CLOSE) {
2979
0
    vty_out(vty, "exit from config node while reading config file");
2980
0
    vty->status = VTY_CLOSE;
2981
0
  }
2982
2983
0
  return 1;
2984
0
}
2985
2986
/* Master of the threads. */
2987
static struct event_loop *vty_master;
2988
2989
static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv)
2990
0
{
2991
0
  switch (event) {
2992
0
  case VTY_SERV:
2993
0
    event_add_read(vty_master, vty_accept, vty_serv, vty_serv->sock,
2994
0
             &vty_serv->t_accept);
2995
0
    break;
2996
0
#ifdef VTYSH
2997
0
  case VTYSH_SERV:
2998
0
    event_add_read(vty_master, vtysh_accept, vty_serv,
2999
0
             vty_serv->sock, &vty_serv->t_accept);
3000
0
    break;
3001
0
#endif /* VTYSH */
3002
0
  case VTY_READ:
3003
0
  case VTY_WRITE:
3004
0
  case VTY_TIMEOUT_RESET:
3005
0
  case VTYSH_READ:
3006
0
  case VTYSH_WRITE:
3007
0
    assert(!"vty_event_serv() called incorrectly");
3008
0
  }
3009
0
}
3010
3011
static void vty_event(enum vty_event event, struct vty *vty)
3012
0
{
3013
0
  switch (event) {
3014
0
#ifdef VTYSH
3015
0
  case VTYSH_READ:
3016
0
    event_add_read(vty_master, vtysh_read, vty, vty->fd,
3017
0
             &vty->t_read);
3018
0
    break;
3019
0
  case VTYSH_WRITE:
3020
0
    event_add_write(vty_master, vtysh_write, vty, vty->wfd,
3021
0
        &vty->t_write);
3022
0
    break;
3023
0
#endif /* VTYSH */
3024
0
  case VTY_READ:
3025
0
    event_add_read(vty_master, vty_read, vty, vty->fd,
3026
0
             &vty->t_read);
3027
3028
    /* Time out treatment. */
3029
0
    if (vty->v_timeout) {
3030
0
      EVENT_OFF(vty->t_timeout);
3031
0
      event_add_timer(vty_master, vty_timeout, vty,
3032
0
          vty->v_timeout, &vty->t_timeout);
3033
0
    }
3034
0
    break;
3035
0
  case VTY_WRITE:
3036
0
    event_add_write(vty_master, vty_flush, vty, vty->wfd,
3037
0
        &vty->t_write);
3038
0
    break;
3039
0
  case VTY_TIMEOUT_RESET:
3040
0
    EVENT_OFF(vty->t_timeout);
3041
0
    if (vty->v_timeout)
3042
0
      event_add_timer(vty_master, vty_timeout, vty,
3043
0
          vty->v_timeout, &vty->t_timeout);
3044
0
    break;
3045
0
  case VTY_SERV:
3046
0
  case VTYSH_SERV:
3047
0
    assert(!"vty_event() called incorrectly");
3048
0
  }
3049
0
}
3050
3051
DEFUN_NOSH (config_who,
3052
       config_who_cmd,
3053
       "who",
3054
       "Display who is on vty\n")
3055
0
{
3056
0
  struct vty *v;
3057
3058
0
  frr_each (vtys, vty_sessions, v)
3059
0
    vty_out(vty, "%svty[%d] connected from %s%s.\n",
3060
0
      v->config ? "*" : " ", v->fd, v->address,
3061
0
      zlog_live_is_null(&v->live_log) ? "" : ", live log");
3062
0
  return CMD_SUCCESS;
3063
0
}
3064
3065
/* Move to vty configuration mode. */
3066
DEFUN_NOSH (line_vty,
3067
       line_vty_cmd,
3068
       "line vty",
3069
       "Configure a terminal line\n"
3070
       "Virtual terminal\n")
3071
0
{
3072
0
  vty->node = VTY_NODE;
3073
0
  return CMD_SUCCESS;
3074
0
}
3075
3076
/* Set time out value. */
3077
static int exec_timeout(struct vty *vty, const char *min_str,
3078
      const char *sec_str)
3079
0
{
3080
0
  unsigned long timeout = 0;
3081
3082
  /* min_str and sec_str are already checked by parser.  So it must be
3083
     all digit string. */
3084
0
  if (min_str) {
3085
0
    timeout = strtol(min_str, NULL, 10);
3086
0
    timeout *= 60;
3087
0
  }
3088
0
  if (sec_str)
3089
0
    timeout += strtol(sec_str, NULL, 10);
3090
3091
0
  vty_timeout_val = timeout;
3092
0
  vty->v_timeout = timeout;
3093
0
  vty_event(VTY_TIMEOUT_RESET, vty);
3094
3095
3096
0
  return CMD_SUCCESS;
3097
0
}
3098
3099
DEFUN (exec_timeout_min,
3100
       exec_timeout_min_cmd,
3101
       "exec-timeout (0-35791)",
3102
       "Set timeout value\n"
3103
       "Timeout value in minutes\n")
3104
0
{
3105
0
  int idx_number = 1;
3106
0
  return exec_timeout(vty, argv[idx_number]->arg, NULL);
3107
0
}
3108
3109
DEFUN (exec_timeout_sec,
3110
       exec_timeout_sec_cmd,
3111
       "exec-timeout (0-35791) (0-2147483)",
3112
       "Set the EXEC timeout\n"
3113
       "Timeout in minutes\n"
3114
       "Timeout in seconds\n")
3115
0
{
3116
0
  int idx_number = 1;
3117
0
  int idx_number_2 = 2;
3118
0
  return exec_timeout(vty, argv[idx_number]->arg,
3119
0
          argv[idx_number_2]->arg);
3120
0
}
3121
3122
DEFUN (no_exec_timeout,
3123
       no_exec_timeout_cmd,
3124
       "no exec-timeout",
3125
       NO_STR
3126
       "Set the EXEC timeout\n")
3127
0
{
3128
0
  return exec_timeout(vty, NULL, NULL);
3129
0
}
3130
3131
/* Set vty access class. */
3132
DEFUN (vty_access_class,
3133
       vty_access_class_cmd,
3134
       "access-class WORD",
3135
       "Filter connections based on an IP access list\n"
3136
       "IP access list\n")
3137
0
{
3138
0
  int idx_word = 1;
3139
0
  if (vty_accesslist_name)
3140
0
    XFREE(MTYPE_VTY, vty_accesslist_name);
3141
3142
0
  vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
3143
3144
0
  return CMD_SUCCESS;
3145
0
}
3146
3147
/* Clear vty access class. */
3148
DEFUN (no_vty_access_class,
3149
       no_vty_access_class_cmd,
3150
       "no access-class [WORD]",
3151
       NO_STR
3152
       "Filter connections based on an IP access list\n"
3153
       "IP access list\n")
3154
0
{
3155
0
  int idx_word = 2;
3156
0
  const char *accesslist = (argc == 3) ? argv[idx_word]->arg : NULL;
3157
0
  if (!vty_accesslist_name
3158
0
      || (argc == 3 && strcmp(vty_accesslist_name, accesslist))) {
3159
0
    vty_out(vty, "Access-class is not currently applied to vty\n");
3160
0
    return CMD_WARNING_CONFIG_FAILED;
3161
0
  }
3162
3163
0
  XFREE(MTYPE_VTY, vty_accesslist_name);
3164
3165
0
  vty_accesslist_name = NULL;
3166
3167
0
  return CMD_SUCCESS;
3168
0
}
3169
3170
/* Set vty access class. */
3171
DEFUN (vty_ipv6_access_class,
3172
       vty_ipv6_access_class_cmd,
3173
       "ipv6 access-class WORD",
3174
       IPV6_STR
3175
       "Filter connections based on an IP access list\n"
3176
       "IPv6 access list\n")
3177
0
{
3178
0
  int idx_word = 2;
3179
0
  if (vty_ipv6_accesslist_name)
3180
0
    XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3181
3182
0
  vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
3183
3184
0
  return CMD_SUCCESS;
3185
0
}
3186
3187
/* Clear vty access class. */
3188
DEFUN (no_vty_ipv6_access_class,
3189
       no_vty_ipv6_access_class_cmd,
3190
       "no ipv6 access-class [WORD]",
3191
       NO_STR
3192
       IPV6_STR
3193
       "Filter connections based on an IP access list\n"
3194
       "IPv6 access list\n")
3195
0
{
3196
0
  int idx_word = 3;
3197
0
  const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL;
3198
3199
0
  if (!vty_ipv6_accesslist_name
3200
0
      || (argc == 4 && strcmp(vty_ipv6_accesslist_name, accesslist))) {
3201
0
    vty_out(vty,
3202
0
      "IPv6 access-class is not currently applied to vty\n");
3203
0
    return CMD_WARNING_CONFIG_FAILED;
3204
0
  }
3205
3206
0
  XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3207
3208
0
  vty_ipv6_accesslist_name = NULL;
3209
3210
0
  return CMD_SUCCESS;
3211
0
}
3212
3213
/* vty login. */
3214
DEFUN (vty_login,
3215
       vty_login_cmd,
3216
       "login",
3217
       "Enable password checking\n")
3218
0
{
3219
0
  no_password_check = 0;
3220
0
  return CMD_SUCCESS;
3221
0
}
3222
3223
DEFUN (no_vty_login,
3224
       no_vty_login_cmd,
3225
       "no login",
3226
       NO_STR
3227
       "Enable password checking\n")
3228
0
{
3229
0
  no_password_check = 1;
3230
0
  return CMD_SUCCESS;
3231
0
}
3232
3233
DEFUN (service_advanced_vty,
3234
       service_advanced_vty_cmd,
3235
       "service advanced-vty",
3236
       "Set up miscellaneous service\n"
3237
       "Enable advanced mode vty interface\n")
3238
0
{
3239
0
  host.advanced = 1;
3240
0
  return CMD_SUCCESS;
3241
0
}
3242
3243
DEFUN (no_service_advanced_vty,
3244
       no_service_advanced_vty_cmd,
3245
       "no service advanced-vty",
3246
       NO_STR
3247
       "Set up miscellaneous service\n"
3248
       "Enable advanced mode vty interface\n")
3249
0
{
3250
0
  host.advanced = 0;
3251
0
  return CMD_SUCCESS;
3252
0
}
3253
3254
DEFUN_NOSH(terminal_monitor,
3255
     terminal_monitor_cmd,
3256
     "terminal monitor [detach]",
3257
     "Set terminal line parameters\n"
3258
     "Copy debug output to the current terminal line\n"
3259
     "Keep logging feed open independent of VTY session\n")
3260
0
{
3261
0
  int fd_ret = -1;
3262
3263
0
  if (vty->type != VTY_SHELL_SERV) {
3264
0
    vty_out(vty, "%% not supported\n");
3265
0
    return CMD_WARNING;
3266
0
  }
3267
3268
0
  if (argc == 3) {
3269
0
    struct zlog_live_cfg detach_log = {};
3270
3271
0
    zlog_live_open(&detach_log, LOG_DEBUG, &fd_ret);
3272
0
    zlog_live_disown(&detach_log);
3273
0
  } else
3274
0
    zlog_live_open(&vty->live_log, LOG_DEBUG, &fd_ret);
3275
3276
0
  if (fd_ret == -1) {
3277
0
    vty_out(vty, "%% error opening live log: %m\n");
3278
0
    return CMD_WARNING;
3279
0
  }
3280
3281
0
  vty_pass_fd(vty, fd_ret);
3282
0
  return CMD_SUCCESS;
3283
0
}
3284
3285
DEFUN_NOSH(no_terminal_monitor,
3286
     no_terminal_monitor_cmd,
3287
     "no terminal monitor",
3288
     NO_STR
3289
     "Set terminal line parameters\n"
3290
     "Copy debug output to the current terminal line\n")
3291
0
{
3292
0
  zlog_live_close(&vty->live_log);
3293
0
  return CMD_SUCCESS;
3294
0
}
3295
3296
DEFUN_NOSH(terminal_no_monitor,
3297
     terminal_no_monitor_cmd,
3298
     "terminal no monitor",
3299
     "Set terminal line parameters\n"
3300
     NO_STR
3301
     "Copy debug output to the current terminal line\n")
3302
0
{
3303
0
  return no_terminal_monitor(self, vty, argc, argv);
3304
0
}
3305
3306
3307
DEFUN_NOSH (show_history,
3308
       show_history_cmd,
3309
       "show history",
3310
       SHOW_STR
3311
       "Display the session command history\n")
3312
0
{
3313
0
  int index;
3314
3315
0
  for (index = vty->hindex + 1; index != vty->hindex;) {
3316
0
    if (index == VTY_MAXHIST) {
3317
0
      index = 0;
3318
0
      continue;
3319
0
    }
3320
3321
0
    if (vty->hist[index] != NULL)
3322
0
      vty_out(vty, "  %s\n", vty->hist[index]);
3323
3324
0
    index++;
3325
0
  }
3326
3327
0
  return CMD_SUCCESS;
3328
0
}
3329
3330
/* vty login. */
3331
DEFPY (log_commands,
3332
       log_commands_cmd,
3333
       "[no] log commands",
3334
       NO_STR
3335
       "Logging control\n"
3336
       "Log all commands\n")
3337
0
{
3338
0
  if (no) {
3339
0
    if (vty_log_commands_perm) {
3340
0
      vty_out(vty,
3341
0
        "Daemon started with permanent logging turned on for commands, ignoring\n");
3342
0
      return CMD_WARNING;
3343
0
    }
3344
3345
0
    vty_log_commands = false;
3346
0
  } else
3347
0
    vty_log_commands = true;
3348
3349
0
  return CMD_SUCCESS;
3350
0
}
3351
3352
/* Display current configuration. */
3353
static int vty_config_write(struct vty *vty)
3354
0
{
3355
0
  vty_frame(vty, "line vty\n");
3356
3357
0
  if (vty_accesslist_name)
3358
0
    vty_out(vty, " access-class %s\n", vty_accesslist_name);
3359
3360
0
  if (vty_ipv6_accesslist_name)
3361
0
    vty_out(vty, " ipv6 access-class %s\n",
3362
0
      vty_ipv6_accesslist_name);
3363
3364
  /* exec-timeout */
3365
0
  if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
3366
0
    vty_out(vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
3367
0
      vty_timeout_val % 60);
3368
3369
  /* login */
3370
0
  if (no_password_check)
3371
0
    vty_out(vty, " no login\n");
3372
3373
0
  vty_endframe(vty, "exit\n");
3374
3375
0
  if (vty_log_commands)
3376
0
    vty_out(vty, "log commands\n");
3377
3378
0
  vty_out(vty, "!\n");
3379
3380
0
  return CMD_SUCCESS;
3381
0
}
3382
3383
static int vty_config_write(struct vty *vty);
3384
struct cmd_node vty_node = {
3385
  .name = "vty",
3386
  .node = VTY_NODE,
3387
  .parent_node = CONFIG_NODE,
3388
  .prompt = "%s(config-line)# ",
3389
  .config_write = vty_config_write,
3390
};
3391
3392
/* Reset all VTY status. */
3393
void vty_reset(void)
3394
0
{
3395
0
  struct vty *vty;
3396
3397
0
  frr_each_safe (vtys, vty_sessions, vty) {
3398
0
    buffer_reset(vty->lbuf);
3399
0
    buffer_reset(vty->obuf);
3400
0
    vty->status = VTY_CLOSE;
3401
0
    vty_close(vty);
3402
0
  }
3403
3404
0
  vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3405
3406
0
  XFREE(MTYPE_VTY, vty_accesslist_name);
3407
0
  XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3408
0
}
3409
3410
static void vty_save_cwd(void)
3411
4
{
3412
4
  char *c;
3413
3414
4
  c = getcwd(vty_cwd, sizeof(vty_cwd));
3415
3416
4
  if (!c) {
3417
    /*
3418
     * At this point if these go wrong, more than likely
3419
     * the whole world is coming down around us
3420
     * Hence not worrying about it too much.
3421
     */
3422
0
    if (chdir(SYSCONFDIR)) {
3423
0
      flog_err_sys(EC_LIB_SYSTEM_CALL,
3424
0
             "Failure to chdir to %s, errno: %d",
3425
0
             SYSCONFDIR, errno);
3426
0
      exit(-1);
3427
0
    }
3428
0
    if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) {
3429
0
      flog_err_sys(EC_LIB_SYSTEM_CALL,
3430
0
             "Failure to getcwd, errno: %d", errno);
3431
0
      exit(-1);
3432
0
    }
3433
0
  }
3434
4
}
3435
3436
char *vty_get_cwd(void)
3437
0
{
3438
0
  return vty_cwd;
3439
0
}
3440
3441
int vty_shell(struct vty *vty)
3442
0
{
3443
0
  return vty->type == VTY_SHELL ? 1 : 0;
3444
0
}
3445
3446
int vty_shell_serv(struct vty *vty)
3447
0
{
3448
0
  return vty->type == VTY_SHELL_SERV ? 1 : 0;
3449
0
}
3450
3451
void vty_init_vtysh(void)
3452
0
{
3453
  /* currently nothing to do, but likely to have future use */
3454
0
}
3455
3456
3457
/*
3458
 * These functions allow for CLI handling to be placed inside daemons; however,
3459
 * currently they are only used by mgmtd, with mgmtd having each daemons CLI
3460
 * functionality linked into it. This design choice was taken for efficiency.
3461
 */
3462
3463
static void vty_mgmt_server_connected(struct mgmt_fe_client *client,
3464
              uintptr_t usr_data, bool connected)
3465
0
{
3466
0
  MGMTD_FE_CLIENT_DBG("Got %sconnected %s MGMTD Frontend Server",
3467
0
          !connected ? "dis: " : "",
3468
0
          !connected ? "from" : "to");
3469
3470
  /*
3471
   * We should not have any sessions for connecting or disconnecting case.
3472
   * The  fe client library will delete all session on disconnect before
3473
   * calling us.
3474
   */
3475
0
  assert(mgmt_fe_client_session_count(client) == 0);
3476
3477
0
  mgmt_fe_connected = connected;
3478
3479
  /* Start or stop listening for vty connections */
3480
0
  if (connected)
3481
0
    frr_vty_serv_start();
3482
0
  else
3483
0
    frr_vty_serv_stop();
3484
0
}
3485
3486
/*
3487
 * A session has successfully been created for a vty.
3488
 */
3489
static void vty_mgmt_session_notify(struct mgmt_fe_client *client,
3490
            uintptr_t usr_data, uint64_t client_id,
3491
            bool create, bool success,
3492
            uintptr_t session_id, uintptr_t session_ctx)
3493
0
{
3494
0
  struct vty *vty;
3495
3496
0
  vty = (struct vty *)session_ctx;
3497
3498
0
  if (!success) {
3499
0
    zlog_err("%s session for client %" PRIu64 " failed!",
3500
0
       create ? "Creating" : "Destroying", client_id);
3501
0
    return;
3502
0
  }
3503
3504
0
  MGMTD_FE_CLIENT_DBG("%s session for client %" PRIu64 " successfully",
3505
0
          create ? "Created" : "Destroyed", client_id);
3506
3507
0
  if (create) {
3508
0
    assert(session_id != 0);
3509
0
    vty->mgmt_session_id = session_id;
3510
0
  } else {
3511
0
    vty->mgmt_session_id = 0;
3512
    /* We may come here by way of vty_close() and short-circuits */
3513
0
    if (vty->status != VTY_CLOSE)
3514
0
      vty_close(vty);
3515
0
  }
3516
0
}
3517
3518
static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,
3519
              uintptr_t usr_data, uint64_t client_id,
3520
              uintptr_t session_id,
3521
              uintptr_t session_ctx, uint64_t req_id,
3522
              bool lock_ds, bool success,
3523
              Mgmtd__DatastoreId ds_id,
3524
              char *errmsg_if_any)
3525
0
{
3526
0
  struct vty *vty;
3527
0
  bool is_short_circuit = mgmt_fe_client_current_msg_short_circuit(client);
3528
3529
0
  vty = (struct vty *)session_ctx;
3530
3531
0
  assert(ds_id == MGMTD_DS_CANDIDATE || ds_id == MGMTD_DS_RUNNING);
3532
0
  if (!success)
3533
0
    zlog_err("%socking for DS %u failed, Err: '%s' vty %p",
3534
0
       lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty);
3535
0
  else {
3536
0
    MGMTD_FE_CLIENT_DBG("%socked DS %u successfully",
3537
0
            lock_ds ? "L" : "Unl", ds_id);
3538
0
    if (ds_id == MGMTD_DS_CANDIDATE)
3539
0
      vty->mgmt_locked_candidate_ds = lock_ds;
3540
0
    else
3541
0
      vty->mgmt_locked_running_ds = lock_ds;
3542
0
  }
3543
3544
0
  if (!is_short_circuit && vty->mgmt_req_pending_cmd) {
3545
0
    assert(!strcmp(vty->mgmt_req_pending_cmd, "MESSAGE_LOCKDS_REQ"));
3546
0
    vty_mgmt_resume_response(vty, success);
3547
0
  }
3548
0
}
3549
3550
static void vty_mgmt_set_config_result_notified(
3551
  struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
3552
  uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
3553
  bool success, Mgmtd__DatastoreId ds_id, bool implicit_commit,
3554
  char *errmsg_if_any)
3555
0
{
3556
0
  struct vty *vty;
3557
3558
0
  vty = (struct vty *)session_ctx;
3559
3560
0
  if (!success) {
3561
0
    zlog_err("SET_CONFIG request for client 0x%" PRIx64
3562
0
       " failed, Error: '%s'",
3563
0
       client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
3564
0
    vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n",
3565
0
      errmsg_if_any ? errmsg_if_any : "Unknown");
3566
0
  } else {
3567
0
    MGMTD_FE_CLIENT_DBG("SET_CONFIG request for client 0x%" PRIx64
3568
0
            " req-id %" PRIu64 " was successfull",
3569
0
            client_id, req_id);
3570
0
  }
3571
3572
0
  if (implicit_commit) {
3573
    /* In this case the changes have been applied, we are done */
3574
0
    vty_mgmt_unlock_candidate_inline(vty);
3575
0
    vty_mgmt_unlock_running_inline(vty);
3576
0
  }
3577
3578
0
  vty_mgmt_resume_response(vty, success);
3579
0
}
3580
3581
static void vty_mgmt_commit_config_result_notified(
3582
  struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
3583
  uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
3584
  bool success, Mgmtd__DatastoreId src_ds_id,
3585
  Mgmtd__DatastoreId dst_ds_id, bool validate_only, char *errmsg_if_any)
3586
0
{
3587
0
  struct vty *vty;
3588
3589
0
  vty = (struct vty *)session_ctx;
3590
3591
0
  if (!success) {
3592
0
    zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64
3593
0
       " failed, Error: '%s'",
3594
0
       client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
3595
0
    vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n",
3596
0
      errmsg_if_any ? errmsg_if_any : "Unknown");
3597
0
  } else {
3598
0
    MGMTD_FE_CLIENT_DBG(
3599
0
      "COMMIT_CONFIG request for client 0x%" PRIx64
3600
0
      " req-id %" PRIu64 " was successfull",
3601
0
      client_id, req_id);
3602
0
    if (errmsg_if_any)
3603
0
      vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
3604
0
  }
3605
3606
0
  vty_mgmt_resume_response(vty, success);
3607
0
}
3608
3609
static int vty_mgmt_get_data_result_notified(
3610
  struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
3611
  uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
3612
  bool success, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data,
3613
  size_t num_data, int next_key, char *errmsg_if_any)
3614
0
{
3615
0
  struct vty *vty;
3616
0
  size_t indx;
3617
3618
0
  vty = (struct vty *)session_ctx;
3619
3620
0
  if (!success) {
3621
0
    zlog_err("GET_DATA request for client 0x%" PRIx64
3622
0
       " failed, Error: '%s'",
3623
0
       client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
3624
0
    vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
3625
0
      errmsg_if_any ? errmsg_if_any : "Unknown");
3626
0
    vty_mgmt_resume_response(vty, success);
3627
0
    return -1;
3628
0
  }
3629
3630
0
  MGMTD_FE_CLIENT_DBG("GET_DATA request succeeded, client 0x%" PRIx64
3631
0
          " req-id %" PRIu64,
3632
0
          client_id, req_id);
3633
3634
0
  if (req_id != mgmt_last_req_id) {
3635
0
    mgmt_last_req_id = req_id;
3636
0
    vty_out(vty, "[\n");
3637
0
  }
3638
3639
0
  for (indx = 0; indx < num_data; indx++) {
3640
0
    vty_out(vty, "  \"%s\": \"%s\"\n", yang_data[indx]->xpath,
3641
0
      yang_data[indx]->value->encoded_str_val);
3642
0
  }
3643
0
  if (next_key < 0) {
3644
0
    vty_out(vty, "]\n");
3645
0
    vty_mgmt_resume_response(vty, success);
3646
0
  }
3647
3648
0
  return 0;
3649
0
}
3650
3651
static struct mgmt_fe_client_cbs mgmt_cbs = {
3652
  .client_connect_notify = vty_mgmt_server_connected,
3653
  .client_session_notify = vty_mgmt_session_notify,
3654
  .lock_ds_notify = vty_mgmt_ds_lock_notified,
3655
  .set_config_notify = vty_mgmt_set_config_result_notified,
3656
  .commit_config_notify = vty_mgmt_commit_config_result_notified,
3657
  .get_data_notify = vty_mgmt_get_data_result_notified,
3658
};
3659
3660
void vty_init_mgmt_fe(void)
3661
0
{
3662
0
  char name[40];
3663
3664
0
  assert(vty_master);
3665
0
  assert(!mgmt_fe_client);
3666
0
  snprintf(name, sizeof(name), "vty-%s-%ld", frr_get_progname(),
3667
0
     (long)getpid());
3668
0
  mgmt_fe_client = mgmt_fe_client_create(name, &mgmt_cbs, 0, vty_master);
3669
0
  assert(mgmt_fe_client);
3670
0
}
3671
3672
bool vty_mgmt_fe_enabled(void)
3673
0
{
3674
0
  return mgmt_fe_client && mgmt_fe_connected;
3675
0
}
3676
3677
bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty)
3678
0
{
3679
0
  return vty->type != VTY_FILE && vty_mgmt_fe_enabled();
3680
0
}
3681
3682
int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
3683
           bool lock, bool scok)
3684
0
{
3685
0
  assert(mgmt_fe_client);
3686
0
  assert(vty->mgmt_session_id);
3687
3688
0
  vty->mgmt_req_id++;
3689
0
  if (mgmt_fe_send_lockds_req(mgmt_fe_client, vty->mgmt_session_id,
3690
0
            vty->mgmt_req_id, ds_id, lock, scok)) {
3691
0
    zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64,
3692
0
       lock ? "" : "UN", vty->mgmt_req_id);
3693
0
    vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
3694
0
      lock ? "" : "UN");
3695
0
    return -1;
3696
0
  }
3697
3698
0
  if (!scok)
3699
0
    vty->mgmt_req_pending_cmd = "MESSAGE_LOCKDS_REQ";
3700
3701
0
  return 0;
3702
0
}
3703
3704
int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)
3705
0
{
3706
0
  Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES];
3707
0
  Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES];
3708
0
  Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];
3709
0
  Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};
3710
0
  size_t indx;
3711
3712
0
  if (vty->type == VTY_FILE) {
3713
    /*
3714
     * if this is a config file read we will not send any of the
3715
     * changes until we are done reading the file and have modified
3716
     * the local candidate DS.
3717
     */
3718
    /* no-one else should be sending data right now */
3719
0
    assert(!vty->mgmt_num_pending_setcfg);
3720
0
    return 0;
3721
0
  }
3722
3723
  /* If we are FE client and we have a vty then we have a session */
3724
0
  assert(mgmt_fe_client && vty->mgmt_client_id && vty->mgmt_session_id);
3725
3726
0
  if (!vty->num_cfg_changes)
3727
0
    return 0;
3728
3729
  /* grab the candidate and running lock prior to sending implicit commit
3730
   * command
3731
   */
3732
0
  if (implicit_commit) {
3733
0
    if (vty_mgmt_lock_candidate_inline(vty)) {
3734
0
      vty_out(vty,
3735
0
        "%% command failed, could not lock candidate DS\n");
3736
0
      return -1;
3737
0
    } else if (vty_mgmt_lock_running_inline(vty)) {
3738
0
      vty_out(vty,
3739
0
        "%% command failed, could not lock running DS\n");
3740
0
      vty_mgmt_unlock_candidate_inline(vty);
3741
0
      return -1;
3742
0
    }
3743
0
  }
3744
3745
0
  for (indx = 0; indx < vty->num_cfg_changes; indx++) {
3746
0
    mgmt_yang_data_init(&cfg_data[indx]);
3747
3748
0
    if (vty->cfg_changes[indx].value) {
3749
0
      mgmt_yang_data_value_init(&value[indx]);
3750
0
      value[indx].encoded_str_val =
3751
0
        (char *)vty->cfg_changes[indx].value;
3752
0
      value[indx].value_case =
3753
0
        MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
3754
0
      cfg_data[indx].value = &value[indx];
3755
0
    }
3756
3757
0
    cfg_data[indx].xpath = vty->cfg_changes[indx].xpath;
3758
3759
0
    mgmt_yang_cfg_data_req_init(&cfg_req[indx]);
3760
0
    cfg_req[indx].data = &cfg_data[indx];
3761
0
    switch (vty->cfg_changes[indx].operation) {
3762
0
    case NB_OP_DESTROY:
3763
0
      cfg_req[indx].req_type =
3764
0
        MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
3765
0
      break;
3766
3767
0
    case NB_OP_CREATE:
3768
0
    case NB_OP_MODIFY:
3769
0
    case NB_OP_MOVE:
3770
0
    case NB_OP_PRE_VALIDATE:
3771
0
    case NB_OP_APPLY_FINISH:
3772
0
      cfg_req[indx].req_type =
3773
0
        MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
3774
0
      break;
3775
0
    case NB_OP_GET_ELEM:
3776
0
    case NB_OP_GET_NEXT:
3777
0
    case NB_OP_GET_KEYS:
3778
0
    case NB_OP_LOOKUP_ENTRY:
3779
0
    case NB_OP_RPC:
3780
0
    default:
3781
0
      assertf(false,
3782
0
        "Invalid operation type for send config: %d",
3783
0
        vty->cfg_changes[indx].operation);
3784
      /*NOTREACHED*/
3785
0
      abort();
3786
0
    }
3787
3788
0
    cfgreq[indx] = &cfg_req[indx];
3789
0
  }
3790
0
  if (!indx)
3791
0
    return 0;
3792
3793
0
  vty->mgmt_req_id++;
3794
0
  if (mgmt_fe_send_setcfg_req(mgmt_fe_client, vty->mgmt_session_id,
3795
0
            vty->mgmt_req_id, MGMTD_DS_CANDIDATE,
3796
0
            cfgreq, indx, implicit_commit,
3797
0
            MGMTD_DS_RUNNING)) {
3798
0
    zlog_err("Failed to send %zu config xpaths to mgmtd", indx);
3799
0
    vty_out(vty, "%% Failed to send commands to mgmtd\n");
3800
0
    return -1;
3801
0
  }
3802
3803
0
  vty->mgmt_req_pending_cmd = "MESSAGE_SETCFG_REQ";
3804
3805
0
  return 0;
3806
0
}
3807
3808
int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort)
3809
0
{
3810
0
  if (mgmt_fe_client && vty->mgmt_session_id) {
3811
0
    vty->mgmt_req_id++;
3812
0
    if (mgmt_fe_send_commitcfg_req(
3813
0
          mgmt_fe_client, vty->mgmt_session_id,
3814
0
          vty->mgmt_req_id, MGMTD_DS_CANDIDATE,
3815
0
          MGMTD_DS_RUNNING, validate_only, abort)) {
3816
0
      zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64,
3817
0
         vty->mgmt_req_id);
3818
0
      vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n");
3819
0
      return -1;
3820
0
    }
3821
3822
0
    vty->mgmt_req_pending_cmd = "MESSAGE_COMMCFG_REQ";
3823
0
    vty->mgmt_num_pending_setcfg = 0;
3824
0
  }
3825
3826
0
  return 0;
3827
0
}
3828
3829
int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
3830
        Mgmtd__DatastoreId datastore, const char **xpath_list,
3831
        int num_req)
3832
0
{
3833
0
  Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
3834
0
  Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
3835
0
  Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
3836
0
  int i;
3837
3838
0
  vty->mgmt_req_id++;
3839
3840
0
  for (i = 0; i < num_req; i++) {
3841
0
    mgmt_yang_get_data_req_init(&get_req[i]);
3842
0
    mgmt_yang_data_init(&yang_data[i]);
3843
3844
0
    yang_data->xpath = (char *)xpath_list[i];
3845
3846
0
    get_req[i].data = &yang_data[i];
3847
0
    getreq[i] = &get_req[i];
3848
0
  }
3849
0
  if (mgmt_fe_send_get_req(mgmt_fe_client, vty->mgmt_session_id,
3850
0
         vty->mgmt_req_id, is_config, datastore, getreq,
3851
0
         num_req)) {
3852
0
    zlog_err("Failed to send GET- to MGMTD for req-id %" PRIu64 ".",
3853
0
       vty->mgmt_req_id);
3854
0
    vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n");
3855
0
    return -1;
3856
0
  }
3857
3858
0
  vty->mgmt_req_pending_cmd = "MESSAGE_GETCFG_REQ";
3859
3860
0
  return 0;
3861
0
}
3862
3863
/* Install vty's own commands like `who' command. */
3864
void vty_init(struct event_loop *master_thread, bool do_command_logging)
3865
4
{
3866
  /* For further configuration read, preserve current directory. */
3867
4
  vty_save_cwd();
3868
3869
4
  vty_master = master_thread;
3870
3871
4
  atexit(vty_stdio_atexit);
3872
3873
  /* Install bgp top node. */
3874
4
  install_node(&vty_node);
3875
3876
4
  install_element(VIEW_NODE, &config_who_cmd);
3877
4
  install_element(VIEW_NODE, &show_history_cmd);
3878
4
  install_element(CONFIG_NODE, &line_vty_cmd);
3879
4
  install_element(CONFIG_NODE, &service_advanced_vty_cmd);
3880
4
  install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
3881
4
  install_element(CONFIG_NODE, &show_history_cmd);
3882
4
  install_element(CONFIG_NODE, &log_commands_cmd);
3883
3884
4
  if (do_command_logging) {
3885
0
    vty_log_commands = true;
3886
0
    vty_log_commands_perm = true;
3887
0
  }
3888
3889
4
  install_element(ENABLE_NODE, &terminal_monitor_cmd);
3890
4
  install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
3891
4
  install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
3892
3893
4
  install_default(VTY_NODE);
3894
4
  install_element(VTY_NODE, &exec_timeout_min_cmd);
3895
4
  install_element(VTY_NODE, &exec_timeout_sec_cmd);
3896
4
  install_element(VTY_NODE, &no_exec_timeout_cmd);
3897
4
  install_element(VTY_NODE, &vty_access_class_cmd);
3898
4
  install_element(VTY_NODE, &no_vty_access_class_cmd);
3899
4
  install_element(VTY_NODE, &vty_login_cmd);
3900
4
  install_element(VTY_NODE, &no_vty_login_cmd);
3901
4
  install_element(VTY_NODE, &vty_ipv6_access_class_cmd);
3902
4
  install_element(VTY_NODE, &no_vty_ipv6_access_class_cmd);
3903
4
}
3904
3905
void vty_terminate(void)
3906
0
{
3907
0
  struct vty *vty;
3908
3909
0
  if (mgmt_fe_client) {
3910
0
    mgmt_fe_client_destroy(mgmt_fe_client);
3911
0
    mgmt_fe_client = 0;
3912
0
  }
3913
3914
0
  memset(vty_cwd, 0x00, sizeof(vty_cwd));
3915
3916
0
  vty_reset();
3917
3918
  /* default state of vty_sessions is initialized & empty. */
3919
0
  vtys_fini(vty_sessions);
3920
0
  vtys_init(vty_sessions);
3921
3922
  /* vty_reset() doesn't close vtysh sessions */
3923
0
  frr_each_safe (vtys, vtysh_sessions, vty) {
3924
0
    buffer_reset(vty->lbuf);
3925
0
    buffer_reset(vty->obuf);
3926
0
    vty->status = VTY_CLOSE;
3927
0
    vty_close(vty);
3928
0
  }
3929
3930
0
  vtys_fini(vtysh_sessions);
3931
0
  vtys_init(vtysh_sessions);
3932
3933
0
  vty_serv_stop();
3934
0
}