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