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