]> git.proxmox.com Git - mirror_frr.git/blob - lib/vty.c
zebra: Unlock the route node when sending route notifications
[mirror_frr.git] / lib / vty.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Virtual terminal [aka TeletYpe] interface routine.
4 * Copyright (C) 1997, 98 Kunihiro Ishiguro
5 */
6
7 #include <zebra.h>
8
9 #include <lib/version.h>
10 #include <sys/types.h>
11 #include <sys/types.h>
12 #ifdef HAVE_LIBPCRE2_POSIX
13 #ifndef _FRR_PCRE2_POSIX
14 #define _FRR_PCRE2_POSIX
15 #include <pcre2posix.h>
16 #endif /* _FRR_PCRE2_POSIX */
17 #elif defined(HAVE_LIBPCREPOSIX)
18 #include <pcreposix.h>
19 #else
20 #include <regex.h>
21 #endif /* HAVE_LIBPCRE2_POSIX */
22 #include <stdio.h>
23
24 #include "debug.h"
25 #include "linklist.h"
26 #include "frrevent.h"
27 #include "buffer.h"
28 #include "command.h"
29 #include "sockunion.h"
30 #include "memory.h"
31 #include "log.h"
32 #include "prefix.h"
33 #include "filter.h"
34 #include "vty.h"
35 #include "privs.h"
36 #include "network.h"
37 #include "libfrr.h"
38 #include "frrstr.h"
39 #include "lib_errors.h"
40 #include "northbound_cli.h"
41 #include "printfrr.h"
42 #include "json.h"
43
44 #include <arpa/telnet.h>
45 #include <termios.h>
46
47 #include "lib/vty_clippy.c"
48
49 DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
50 DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server");
51 DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer");
52 DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history");
53
54 DECLARE_DLIST(vtys, struct vty, itm);
55
56 /* Vty events */
57 enum vty_event {
58 VTY_SERV,
59 VTY_READ,
60 VTY_WRITE,
61 VTY_TIMEOUT_RESET,
62 #ifdef VTYSH
63 VTYSH_SERV,
64 VTYSH_READ,
65 VTYSH_WRITE
66 #endif /* VTYSH */
67 };
68
69 struct nb_config *vty_mgmt_candidate_config;
70
71 static uintptr_t mgmt_lib_hndl;
72 static bool mgmt_fe_connected;
73 static bool mgmt_candidate_ds_wr_locked;
74 static uint64_t mgmt_client_id_next;
75 static uint64_t mgmt_last_req_id = UINT64_MAX;
76
77 PREDECL_DLIST(vtyservs);
78
79 struct vty_serv {
80 struct vtyservs_item itm;
81
82 int sock;
83 bool vtysh;
84
85 struct event *t_accept;
86 };
87
88 DECLARE_DLIST(vtyservs, struct vty_serv, itm);
89
90 static void vty_event_serv(enum vty_event event, struct vty_serv *);
91 static void vty_event(enum vty_event, struct vty *);
92 static int vtysh_flush(struct vty *vty);
93
94 /* Extern host structure from command.c */
95 extern struct host host;
96
97 /* active listeners */
98 static struct vtyservs_head vty_servs[1] = {INIT_DLIST(vty_servs[0])};
99
100 /* active connections */
101 static struct vtys_head vty_sessions[1] = {INIT_DLIST(vty_sessions[0])};
102 static struct vtys_head vtysh_sessions[1] = {INIT_DLIST(vtysh_sessions[0])};
103
104 /* Vty timeout value. */
105 static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
106
107 /* Vty access-class command */
108 static char *vty_accesslist_name = NULL;
109
110 /* Vty access-calss for IPv6. */
111 static char *vty_ipv6_accesslist_name = NULL;
112
113 /* Current directory. */
114 static char vty_cwd[MAXPATHLEN];
115
116 /* Login password check. */
117 static int no_password_check = 0;
118
119 /* Integrated configuration file path */
120 static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
121
122 bool vty_log_commands;
123 static bool vty_log_commands_perm;
124
125 char const *const mgmt_daemons[] = {
126 #ifdef HAVE_STATICD
127 "staticd",
128 #endif
129 };
130 uint mgmt_daemons_count = array_size(mgmt_daemons);
131
132 void vty_mgmt_resume_response(struct vty *vty, bool success)
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;
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 }
159
160 if (vty->status == VTY_CLOSE)
161 vty_close(vty);
162 else if (vty->type != VTY_FILE)
163 vty_event(VTYSH_READ, vty);
164 else
165 /* should we assert here? */
166 zlog_err("mgmtd: unexpected resume while reading config file");
167 }
168
169 void vty_frame(struct vty *vty, const char *format, ...)
170 {
171 va_list args;
172
173 va_start(args, format);
174 vsnprintfrr(vty->frame + vty->frame_pos,
175 sizeof(vty->frame) - vty->frame_pos, format, args);
176 vty->frame_pos = strlen(vty->frame);
177 va_end(args);
178 }
179
180 void 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
187 bool vty_set_include(struct vty *vty, const char *regexp)
188 {
189 int errcode;
190 bool ret = true;
191 char errbuf[256];
192
193 if (!regexp) {
194 if (vty->filter) {
195 regfree(&vty->include);
196 vty->filter = false;
197 }
198 return true;
199 }
200
201 errcode = regcomp(&vty->include, regexp,
202 REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
203 if (errcode) {
204 ret = false;
205 regerror(errcode, &vty->include, errbuf, sizeof(errbuf));
206 vty_out(vty, "%% Regex compilation error: %s\n", errbuf);
207 } else {
208 vty->filter = true;
209 }
210
211 return ret;
212 }
213
214 /* VTY standard output function. */
215 int vty_out(struct vty *vty, const char *format, ...)
216 {
217 va_list args;
218 ssize_t len;
219 char buf[1024];
220 char *p = NULL;
221 char *filtered;
222 /* format string may contain %m, keep errno intact for printfrr */
223 int saved_errno = errno;
224
225 if (vty->frame_pos) {
226 vty->frame_pos = 0;
227 vty_out(vty, "%s", vty->frame);
228 }
229
230 va_start(args, format);
231 errno = saved_errno;
232 p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args);
233 va_end(args);
234
235 len = strlen(p);
236
237 /* filter buffer */
238 if (vty->filter) {
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 }
281
282 frrstr_strvec_free(lines);
283
284 } else {
285 filtered = p;
286 }
287
288 if (!filtered)
289 goto done;
290
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:
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 }
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;
312 }
313
314 done:
315
316 if (vty->filter && filtered)
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
323 return len;
324 }
325
326 static int vty_json_helper(struct vty *vty, struct json_object *json,
327 uint32_t options)
328 {
329 const char *text;
330
331 if (!json)
332 return CMD_SUCCESS;
333
334 text = json_object_to_json_string_ext(
335 json, options);
336 vty_out(vty, "%s\n", text);
337 json_object_free(json);
338
339 return CMD_SUCCESS;
340 }
341
342 int 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
349 int 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
354 void vty_json_empty(struct vty *vty)
355 {
356 json_object *json = json_object_new_object();
357
358 vty_json(vty, json);
359 }
360
361 /* Output current time to the vty. */
362 void vty_time_print(struct vty *vty, int cr)
363 {
364 char buf[FRR_TIMESTAMP_LEN];
365
366 if (frr_timestamp(0, buf, sizeof(buf)) == 0) {
367 zlog_info("frr_timestamp error");
368 return;
369 }
370 if (cr)
371 vty_out(vty, "%s\n", buf);
372 else
373 vty_out(vty, "%s ", buf);
374
375 return;
376 }
377
378 /* Say hello to vty interface. */
379 void 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);
392 (s > buf) && isspace((unsigned char)s[-1]);
393 s--)
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);
403 }
404
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
414 /* Put out prompt and wait input from user. */
415 static void vty_prompt(struct vty *vty)
416 {
417 if (vty->type == VTY_TERM) {
418 vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
419 }
420 }
421 #pragma GCC diagnostic pop
422
423 /* Send WILL TELOPT_ECHO to remote server. */
424 static void vty_will_echo(struct vty *vty)
425 {
426 unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
427 vty_out(vty, "%s", cmd);
428 }
429
430 /* Make suppress Go-Ahead telnet option. */
431 static void vty_will_suppress_go_ahead(struct vty *vty)
432 {
433 unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
434 vty_out(vty, "%s", cmd);
435 }
436
437 /* Make don't use linemode over telnet. */
438 static void vty_dont_linemode(struct vty *vty)
439 {
440 unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
441 vty_out(vty, "%s", cmd);
442 }
443
444 /* Use window size. */
445 static void vty_do_window_size(struct vty *vty)
446 {
447 unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
448 vty_out(vty, "%s", cmd);
449 }
450
451 /* Authentication of vty */
452 static void vty_auth(struct vty *vty, char *buf)
453 {
454 char *passwd = NULL;
455 enum node_type next_node = 0;
456 int fail;
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 }
505 }
506
507 /* Command execution over the vty interface. */
508 static int vty_command(struct vty *vty, char *buf)
509 {
510 int ret;
511 const char *protocolname;
512 char *cp = NULL;
513
514 assert(vty);
515
516 /*
517 * Log non empty command lines
518 */
519 if (vty_log_commands &&
520 strncmp(buf, "echo PING", strlen("echo PING")) != 0)
521 cp = buf;
522 if (cp != NULL) {
523 /* Skip white spaces. */
524 while (isspace((unsigned char)*cp) && *cp != '\0')
525 cp++;
526 }
527 if (cp != NULL && *cp != '\0') {
528 char vty_str[VTY_BUFSIZ];
529 char prompt_str[VTY_BUFSIZ];
530
531 /* format the base vty info */
532 snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", vty->fd,
533 vty->address);
534
535 /* format the prompt */
536 #pragma GCC diagnostic push
537 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
538 /* prompt formatting has a %s in the cmd_node prompt string */
539 snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
540 vty_str);
541 #pragma GCC diagnostic pop
542
543 /* now log the command */
544 zlog_notice("%s%s", prompt_str, buf);
545 }
546
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
563 walltime = event_consumed_time(&after, &before, &cputime);
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;
578
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 }
596
597 return ret;
598 }
599
600 static const char telnet_backward_char = 0x08;
601 static const char telnet_space_char = ' ';
602
603 /* Basic function to write buffer to vty. */
604 static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
605 {
606 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
607 return;
608
609 /* Should we do buffering here ? And make vty_flush (vty) ? */
610 buffer_put(vty->obuf, buf, nbytes);
611 }
612
613 /* Basic function to insert character into vty. */
614 static void vty_self_insert(struct vty *vty, char c)
615 {
616 int i;
617 int length;
618
619 if (vty->length + 1 >= VTY_BUFSIZ)
620 return;
621
622 length = vty->length - vty->cp;
623 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
624 vty->buf[vty->cp] = c;
625
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);
629
630 vty->cp++;
631 vty->length++;
632
633 vty->buf[vty->length] = '\0';
634 }
635
636 /* Self insert character 'c' in overwrite mode. */
637 static void vty_self_insert_overwrite(struct vty *vty, char c)
638 {
639 if (vty->cp == vty->length) {
640 vty_self_insert(vty, c);
641 return;
642 }
643
644 vty->buf[vty->cp++] = c;
645 vty_write(vty, &c, 1);
646 }
647
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 */
654 static void vty_insert_word_overwrite(struct vty *vty, char *str)
655 {
656 if (vty->cp == VTY_BUFSIZ)
657 return;
658
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);
665 }
666
667 /* Forward character. */
668 static void vty_forward_char(struct vty *vty)
669 {
670 if (vty->cp < vty->length) {
671 vty_write(vty, &vty->buf[vty->cp], 1);
672 vty->cp++;
673 }
674 }
675
676 /* Backward character. */
677 static void vty_backward_char(struct vty *vty)
678 {
679 if (vty->cp > 0) {
680 vty->cp--;
681 vty_write(vty, &telnet_backward_char, 1);
682 }
683 }
684
685 /* Move to the beginning of the line. */
686 static void vty_beginning_of_line(struct vty *vty)
687 {
688 while (vty->cp)
689 vty_backward_char(vty);
690 }
691
692 /* Move to the end of the line. */
693 static void vty_end_of_line(struct vty *vty)
694 {
695 while (vty->cp < vty->length)
696 vty_forward_char(vty);
697 }
698
699 static void vty_kill_line_from_beginning(struct vty *);
700 static void vty_redraw_line(struct vty *);
701
702 /* Print command line history. This function is called from
703 vty_next_line and vty_previous_line. */
704 static void vty_history_print(struct vty *vty)
705 {
706 int length;
707
708 vty_kill_line_from_beginning(vty);
709
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';
715
716 /* Redraw current line */
717 vty_redraw_line(vty);
718 }
719
720 /* Show next command line history. */
721 static void vty_next_line(struct vty *vty)
722 {
723 int try_index;
724
725 if (vty->hp == vty->hindex)
726 return;
727
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++;
734
735 /* If there is not history return. */
736 if (vty->hist[try_index] == NULL)
737 return;
738 else
739 vty->hp = try_index;
740
741 vty_history_print(vty);
742 }
743
744 /* Show previous command line history. */
745 static void vty_previous_line(struct vty *vty)
746 {
747 int try_index;
748
749 try_index = vty->hp;
750 if (try_index == 0)
751 try_index = VTY_MAXHIST - 1;
752 else
753 try_index--;
754
755 if (vty->hist[try_index] == NULL)
756 return;
757 else
758 vty->hp = try_index;
759
760 vty_history_print(vty);
761 }
762
763 /* This function redraw all of the command line character. */
764 static void vty_redraw_line(struct vty *vty)
765 {
766 vty_write(vty, vty->buf, vty->length);
767 vty->cp = vty->length;
768 }
769
770 /* Forward word. */
771 static void vty_forward_word(struct vty *vty)
772 {
773 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
774 vty_forward_char(vty);
775
776 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
777 vty_forward_char(vty);
778 }
779
780 /* Backward word without skipping training space. */
781 static void vty_backward_pure_word(struct vty *vty)
782 {
783 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
784 vty_backward_char(vty);
785 }
786
787 /* Backward word. */
788 static void vty_backward_word(struct vty *vty)
789 {
790 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
791 vty_backward_char(vty);
792
793 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
794 vty_backward_char(vty);
795 }
796
797 /* When '^D' is typed at the beginning of the line we move to the down
798 level. */
799 static void vty_down_level(struct vty *vty)
800 {
801 vty_out(vty, "\n");
802 cmd_exit(vty);
803 vty_prompt(vty);
804 vty->cp = 0;
805 }
806
807 /* When '^Z' is received from vty, move down to the enable mode. */
808 static void vty_end_config(struct vty *vty)
809 {
810 vty_out(vty, "\n");
811
812 if (vty->config) {
813 vty_config_exit(vty);
814 vty->node = ENABLE_NODE;
815 }
816
817 vty_prompt(vty);
818 vty->cp = 0;
819 }
820
821 /* Delete a character at the current point. */
822 static void vty_delete_char(struct vty *vty)
823 {
824 int i;
825 int size;
826
827 if (vty->length == 0) {
828 vty_down_level(vty);
829 return;
830 }
831
832 if (vty->cp == vty->length)
833 return; /* completion need here? */
834
835 size = vty->length - vty->cp;
836
837 vty->length--;
838 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
839 vty->buf[vty->length] = '\0';
840
841 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
842 return;
843
844 vty_write(vty, &vty->buf[vty->cp], size - 1);
845 vty_write(vty, &telnet_space_char, 1);
846
847 for (i = 0; i < size; i++)
848 vty_write(vty, &telnet_backward_char, 1);
849 }
850
851 /* Delete a character before the point. */
852 static void vty_delete_backward_char(struct vty *vty)
853 {
854 if (vty->cp == 0)
855 return;
856
857 vty_backward_char(vty);
858 vty_delete_char(vty);
859 }
860
861 /* Kill rest of line from current point. */
862 static void vty_kill_line(struct vty *vty)
863 {
864 int i;
865 int size;
866
867 size = vty->length - vty->cp;
868
869 if (size == 0)
870 return;
871
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);
876
877 memset(&vty->buf[vty->cp], 0, size);
878 vty->length = vty->cp;
879 }
880
881 /* Kill line from the beginning. */
882 static void vty_kill_line_from_beginning(struct vty *vty)
883 {
884 vty_beginning_of_line(vty);
885 vty_kill_line(vty);
886 }
887
888 /* Delete a word before the point. */
889 static void vty_forward_kill_word(struct vty *vty)
890 {
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);
895 }
896
897 /* Delete a word before the point. */
898 static void vty_backward_kill_word(struct vty *vty)
899 {
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);
904 }
905
906 /* Transpose chars before or at the point. */
907 static void vty_transpose_chars(struct vty *vty)
908 {
909 char c1, c2;
910
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;
915
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];
920
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];
928
929 vty_backward_char(vty);
930 vty_self_insert_overwrite(vty, c1);
931 vty_self_insert_overwrite(vty, c2);
932 }
933 }
934
935 /* Do completion at vty interface. */
936 static 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'. */
951 if (isspace((unsigned char)vty->buf[vty->length - 1]))
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 }
1011 XFREE(MTYPE_TMP, matched);
1012 }
1013
1014 static 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
1038 memcpy(buf, p, pos);
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);
1048 }
1049
1050 /* Describe matched command function. */
1051 static 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);
1065 } else if (isspace((unsigned char)vty->buf[vty->length - 1]))
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 }
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 }
1149
1150 out:
1151 cmd_free_strvec(vline);
1152 if (describe)
1153 vector_free(describe);
1154
1155 vty_prompt(vty);
1156 vty_redraw_line(vty);
1157 }
1158
1159 static void vty_clear_buf(struct vty *vty)
1160 {
1161 memset(vty->buf, 0, vty->max);
1162 }
1163
1164 /* ^C stop current input and do not add command line to the history. */
1165 static 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
1171 if (vty->config) {
1172 vty_config_exit(vty);
1173 vty->node = ENABLE_NODE;
1174 }
1175
1176 vty_prompt(vty);
1177
1178 /* Set history pointer to the latest one. */
1179 vty->hp = vty->hindex;
1180 }
1181
1182 /* Add current command line to the history buffer. */
1183 static void vty_hist_add(struct vty *vty)
1184 {
1185 int index;
1186
1187 if (vty->length == 0)
1188 return;
1189
1190 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1191
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 }
1198
1199 /* Insert history entry. */
1200 XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1201 vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
1202
1203 /* History index rotation. */
1204 vty->hindex++;
1205 if (vty->hindex == VTY_MAXHIST)
1206 vty->hindex = 0;
1207
1208 vty->hp = vty->hindex;
1209 }
1210
1211 /* #define TELNET_OPTION_DEBUG */
1212
1213 /* Get telnet window size. */
1214 static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
1215 {
1216 #ifdef TELNET_OPTION_DEBUG
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");
1257
1258 #endif /* TELNET_OPTION_DEBUG */
1259
1260 switch (buf[0]) {
1261 case SB:
1262 vty->sb_len = 0;
1263 vty->iac_sb_in_progress = 1;
1264 return 0;
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)
1276 flog_err(
1277 EC_LIB_SYSTEM_CALL,
1278 "RFC 1073 violation detected: telnet NAWS option should send %d characters, but we received %lu",
1279 TELNET_NAWS_SB_LEN,
1280 (unsigned long)vty->sb_len);
1281 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1282 flog_err(
1283 EC_LIB_DEVELOPMENT,
1284 "Bug detected: sizeof(vty->sb_buf) %lu < %d, too small to handle the telnet NAWS option",
1285 (unsigned long)sizeof(vty->sb_buf),
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]);
1292 #ifdef TELNET_OPTION_DEBUG
1293 vty_out(vty,
1294 "TELNET NAWS window size negotiation completed: width %d, height %d\n",
1295 vty->width, vty->height);
1296 #endif
1297 }
1298 break;
1299 }
1300 vty->iac_sb_in_progress = 0;
1301 return 0;
1302 }
1303 default:
1304 break;
1305 }
1306 return 1;
1307 }
1308
1309 /* Execute current command line. */
1310 static int vty_execute(struct vty *vty)
1311 {
1312 int ret;
1313
1314 ret = CMD_SUCCESS;
1315
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 }
1327
1328 /* Clear command line buffer. */
1329 vty->cp = vty->length = 0;
1330 vty_clear_buf(vty);
1331
1332 if (vty->status != VTY_CLOSE)
1333 vty_prompt(vty);
1334
1335 return ret;
1336 }
1337
1338 #define CONTROL(X) ((X) - '@')
1339 #define VTY_NORMAL 0
1340 #define VTY_PRE_ESCAPE 1
1341 #define VTY_ESCAPE 2
1342 #define VTY_CR 3
1343
1344 /* Escape character command map. */
1345 static 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;
1366 }
1367
1368 /* Quit print out to the buffer. */
1369 static void vty_buffer_reset(struct vty *vty)
1370 {
1371 buffer_reset(vty->obuf);
1372 buffer_reset(vty->lbuf);
1373 vty_prompt(vty);
1374 vty_redraw_line(vty);
1375 }
1376
1377 /* Read data via vty socket. */
1378 static void vty_read(struct event *thread)
1379 {
1380 int i;
1381 int nbytes;
1382 unsigned char buf[VTY_READ_BUFSIZ];
1383
1384 struct vty *vty = EVENT_ARG(thread);
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)) {
1390 vty_event(VTY_READ, vty);
1391 return;
1392 }
1393 flog_err(
1394 EC_LIB_SOCKET,
1395 "%s: read error on vty client fd %d, closing: %s",
1396 __func__, vty->fd, safe_strerror(errno));
1397 buffer_reset(vty->obuf);
1398 buffer_reset(vty->lbuf);
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;
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
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
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;
1534 case '\r':
1535 vty->escape = VTY_CR;
1536 /* fallthru */
1537 case '\n':
1538 vty_out(vty, "\n");
1539 buffer_flush_available(vty->obuf, vty->wfd);
1540 vty_execute(vty);
1541
1542 if (vty->pass_fd != -1) {
1543 close(vty->pass_fd);
1544 vty->pass_fd = -1;
1545 }
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 {
1575 vty_event(VTY_WRITE, vty);
1576 vty_event(VTY_READ, vty);
1577 }
1578 }
1579
1580 /* Flush buffer to the vty. */
1581 static void vty_flush(struct event *thread)
1582 {
1583 int erase;
1584 buffer_status_t flushrc;
1585 struct vty *vty = EVENT_ARG(thread);
1586
1587 /* Tempolary disable read thread. */
1588 if (vty->lines == 0)
1589 EVENT_OFF(vty->t_read);
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))
1596 flushrc = buffer_flush_available(vty->obuf, vty->wfd);
1597 else if (vty->status == VTY_MORELINE)
1598 flushrc = buffer_flush_window(vty->obuf, vty->wfd, vty->width,
1599 1, erase, 0);
1600 else
1601 flushrc = buffer_flush_window(
1602 vty->obuf, vty->wfd, vty->width,
1603 vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
1604 switch (flushrc) {
1605 case BUFFER_ERROR:
1606 zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
1607 vty->fd, vty->wfd);
1608 buffer_reset(vty->lbuf);
1609 buffer_reset(vty->obuf);
1610 vty_close(vty);
1611 return;
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)
1618 vty_event(VTY_READ, vty);
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)
1625 vty_event(VTY_WRITE, vty);
1626 break;
1627 }
1628 }
1629
1630 /* Allocate new vty struct. */
1631 struct vty *vty_new(void)
1632 {
1633 struct vty *new = XCALLOC(MTYPE_VTY, sizeof(struct vty));
1634
1635 new->fd = new->wfd = -1;
1636 new->of = stdout;
1637 new->lbuf = buffer_new(0);
1638 new->obuf = buffer_new(0); /* Use default buffer size. */
1639 new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
1640 new->max = VTY_BUFSIZ;
1641 new->pass_fd = -1;
1642
1643 if (mgmt_lib_hndl) {
1644 if (!mgmt_client_id_next)
1645 mgmt_client_id_next++;
1646 new->mgmt_client_id = mgmt_client_id_next++;
1647 if (mgmt_fe_create_client_session(
1648 mgmt_lib_hndl, new->mgmt_client_id,
1649 (uintptr_t) new) != MGMTD_SUCCESS)
1650 zlog_err(
1651 "Failed to open a MGMTD Frontend session for VTY session %p!!",
1652 new);
1653 }
1654
1655 return new;
1656 }
1657
1658
1659 /* allocate and initialise vty */
1660 static 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;
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;
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
1686 vtys_add_tail(vty_sessions, vty);
1687
1688 return vty;
1689 }
1690
1691 /* Create new vty structure. */
1692 static 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;
1704 strlcpy(vty->address, buf, sizeof(vty->address));
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. */
1740 vty_event(VTY_WRITE, vty);
1741 vty_event(VTY_READ, vty);
1742
1743 return vty;
1744 }
1745
1746 /* create vty for stdio */
1747 static struct termios stdio_orig_termios;
1748 static struct vty *stdio_vty = NULL;
1749 static bool stdio_termios = false;
1750 static void (*stdio_vty_atclose)(int isexit);
1751
1752 static void vty_stdio_reset(int isexit)
1753 {
1754 if (stdio_vty) {
1755 if (stdio_termios)
1756 tcsetattr(0, TCSANOW, &stdio_orig_termios);
1757 stdio_termios = false;
1758
1759 stdio_vty = NULL;
1760
1761 if (stdio_vty_atclose)
1762 stdio_vty_atclose(isexit);
1763 stdio_vty_atclose = NULL;
1764 }
1765 }
1766
1767 static void vty_stdio_atexit(void)
1768 {
1769 vty_stdio_reset(1);
1770 }
1771
1772 void vty_stdio_suspend(void)
1773 {
1774 if (!stdio_vty)
1775 return;
1776
1777 EVENT_OFF(stdio_vty->t_write);
1778 EVENT_OFF(stdio_vty->t_read);
1779 EVENT_OFF(stdio_vty->t_timeout);
1780
1781 if (stdio_termios)
1782 tcsetattr(0, TCSANOW, &stdio_orig_termios);
1783 stdio_termios = false;
1784 }
1785
1786 void 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);
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. */
1807 vty_event(VTY_WRITE, stdio_vty);
1808 vty_event(VTY_READ, stdio_vty);
1809 }
1810
1811 void vty_stdio_close(void)
1812 {
1813 if (!stdio_vty)
1814 return;
1815 vty_close(stdio_vty);
1816 }
1817
1818 struct vty *vty_stdio(void (*atclose)(int isexit))
1819 {
1820 struct vty *vty;
1821
1822 /* refuse creating two vtys on stdio */
1823 if (stdio_vty)
1824 return NULL;
1825
1826 vty = stdio_vty = vty_new_init(0);
1827 stdio_vty_atclose = atclose;
1828 vty->wfd = 1;
1829
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;
1836 strlcpy(vty->address, "console", sizeof(vty->address));
1837
1838 vty_stdio_resume();
1839 return vty;
1840 }
1841
1842 /* Accept connection from the network. */
1843 static void vty_accept(struct event *thread)
1844 {
1845 struct vty_serv *vtyserv = EVENT_ARG(thread);
1846 int vty_sock;
1847 union sockunion su;
1848 int ret;
1849 unsigned int on;
1850 int accept_sock = vtyserv->sock;
1851 struct prefix p;
1852 struct access_list *acl = NULL;
1853
1854 /* We continue hearing vty socket. */
1855 vty_event_serv(VTY_SERV, vtyserv);
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) {
1862 flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
1863 safe_strerror(errno));
1864 return;
1865 }
1866 set_nonblocking(vty_sock);
1867 set_cloexec(vty_sock);
1868
1869 if (!sockunion2hostprefix(&su, &p)) {
1870 close(vty_sock);
1871 zlog_info("Vty unable to convert prefix from sockunion %pSU",
1872 &su);
1873 return;
1874 }
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)) {
1880 zlog_info("Vty connection refused from %pSU", &su);
1881 close(vty_sock);
1882 return;
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)) {
1891 zlog_info("Vty connection refused from %pSU", &su);
1892 close(vty_sock);
1893 return;
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
1904 zlog_info("Vty connection from %pSU", &su);
1905
1906 vty_create(vty_sock, &su);
1907 }
1908
1909 static 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
1918 memset(&req, 0, sizeof(req));
1919 req.ai_flags = AI_PASSIVE;
1920 req.ai_family = AF_UNSPEC;
1921 req.ai_socktype = SOCK_STREAM;
1922 snprintf(port_str, sizeof(port_str), "%d", port);
1923 port_str[sizeof(port_str) - 1] = '\0';
1924
1925 ret = getaddrinfo(hostname, port_str, &req, &ainfo);
1926
1927 if (ret != 0) {
1928 flog_err_sys(EC_LIB_SYSTEM_CALL, "getaddrinfo failed: %s",
1929 gai_strerror(ret));
1930 exit(1);
1931 }
1932
1933 ainfo_save = ainfo;
1934
1935 do {
1936 struct vty_serv *vtyserv;
1937
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;
1945
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
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);
1968 } while ((ainfo = ainfo->ai_next) != NULL);
1969
1970 freeaddrinfo(ainfo_save);
1971 }
1972
1973 #ifdef VTYSH
1974 /* For sockaddr_un. */
1975 #include <sys/un.h>
1976
1977 /* VTY shell UNIX domain socket. */
1978 static void vty_serv_un(const char *path)
1979 {
1980 struct vty_serv *vtyserv;
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) {
1996 flog_err_sys(EC_LIB_SOCKET,
1997 "Cannot create unix stream socket: %s",
1998 safe_strerror(errno));
1999 return;
2000 }
2001
2002 /* Make server socket. */
2003 memset(&serv, 0, sizeof(serv));
2004 serv.sun_family = AF_UNIX;
2005 strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
2006 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
2007 len = serv.sun_len = SUN_LEN(&serv);
2008 #else
2009 len = sizeof(serv.sun_family) + strlen(serv.sun_path);
2010 #endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
2011
2012 set_cloexec(sock);
2013
2014 ret = bind(sock, (struct sockaddr *)&serv, len);
2015 if (ret < 0) {
2016 flog_err_sys(EC_LIB_SOCKET, "Cannot bind path %s: %s", path,
2017 safe_strerror(errno));
2018 close(sock); /* Avoid sd leak. */
2019 return;
2020 }
2021
2022 ret = listen(sock, 5);
2023 if (ret < 0) {
2024 flog_err_sys(EC_LIB_SOCKET, "listen(fd %d) failed: %s", sock,
2025 safe_strerror(errno));
2026 close(sock); /* Avoid sd leak. */
2027 return;
2028 }
2029
2030 umask(old_mask);
2031
2032 zprivs_get_ids(&ids);
2033
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)) {
2040 flog_err_sys(EC_LIB_SYSTEM_CALL,
2041 "vty_serv_un: could chown socket, %s",
2042 safe_strerror(errno));
2043 }
2044 }
2045
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);
2052 }
2053
2054 /* #define VTYSH_DEBUG 1 */
2055
2056 static void vtysh_accept(struct event *thread)
2057 {
2058 struct vty_serv *vtyserv = EVENT_ARG(thread);
2059 int accept_sock = vtyserv->sock;
2060 int sock;
2061 int client_len;
2062 struct sockaddr_un client;
2063 struct vty *vty;
2064
2065 vty_event_serv(VTYSH_SERV, vtyserv);
2066
2067 memset(&client, 0, sizeof(client));
2068 client_len = sizeof(struct sockaddr_un);
2069
2070 sock = accept(accept_sock, (struct sockaddr *)&client,
2071 (socklen_t *)&client_len);
2072
2073 if (sock < 0) {
2074 flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
2075 safe_strerror(errno));
2076 return;
2077 }
2078
2079 if (set_nonblocking(sock) < 0) {
2080 flog_err(
2081 EC_LIB_SOCKET,
2082 "vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
2083 sock, safe_strerror(errno));
2084 close(sock);
2085 return;
2086 }
2087 set_cloexec(sock);
2088
2089 #ifdef VTYSH_DEBUG
2090 printf("VTY shell accept\n");
2091 #endif /* VTYSH_DEBUG */
2092
2093 vty = vty_new();
2094 vty->fd = sock;
2095 vty->wfd = sock;
2096 vty->type = VTY_SHELL_SERV;
2097 vty->node = VIEW_NODE;
2098 vtys_add_tail(vtysh_sessions, vty);
2099
2100 vty_event(VTYSH_READ, vty);
2101 }
2102
2103 static 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
2124 memset(&u.buf, 0, sizeof(u.buf));
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
2153 static int vtysh_flush(struct vty *vty)
2154 {
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) {
2162 case BUFFER_PENDING:
2163 vty_event(VTYSH_WRITE, vty);
2164 break;
2165 case BUFFER_ERROR:
2166 flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
2167 __func__, vty->fd);
2168 buffer_reset(vty->lbuf);
2169 buffer_reset(vty->obuf);
2170 vty_close(vty);
2171 return -1;
2172 case BUFFER_EMPTY:
2173 break;
2174 }
2175 return 0;
2176 }
2177
2178 void 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
2186 bool 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;
2193 uint index;
2194
2195 vty = vty_new();
2196 vty->wfd = STDERR_FILENO;
2197 vty->type = VTY_FILE;
2198 vty->node = CONFIG_NODE;
2199 vty->config = true;
2200 vty->pending_allowed = true;
2201 vty->candidate_config = vty_shared_candidate_config;
2202 vty->mgmt_locked_candidate_ds = true;
2203 mgmt_candidate_ds_wr_locked = true;
2204
2205
2206 for (index = 0; index < array_size(mgmt_daemons); index++) {
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
2214 zlog_info("mgmtd: reading config file: %s", path);
2215
2216 /* Execute configuration file */
2217 line_num = 0;
2218 (void)config_from_file(vty, confp, &line_num);
2219 count++;
2220 }
2221
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
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) {
2238 zlog_info("mgmtd: reading config file: %s", path);
2239
2240 line_num = 0;
2241 (void)config_from_file(vty, confp, &line_num);
2242 count++;
2243 }
2244
2245 vty->pending_allowed = false;
2246
2247 vty->mgmt_locked_candidate_ds = false;
2248 mgmt_candidate_ds_wr_locked = false;
2249
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
2257 return true;
2258 }
2259
2260 static void vtysh_read(struct event *thread)
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;
2268 uint8_t header[4] = {0, 0, 0, 0};
2269
2270 sock = EVENT_FD(thread);
2271 vty = EVENT_ARG(thread);
2272
2273 if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
2274 if (nbytes < 0) {
2275 if (ERRNO_IO_RETRY(errno)) {
2276 vty_event(VTYSH_READ, vty);
2277 return;
2278 }
2279 flog_err(
2280 EC_LIB_SOCKET,
2281 "%s: read failed on vtysh client fd %d, closing: %s",
2282 __func__, sock, safe_strerror(errno));
2283 }
2284 buffer_reset(vty->lbuf);
2285 buffer_reset(vty->obuf);
2286 vty_close(vty);
2287 #ifdef VTYSH_DEBUG
2288 printf("close vtysh\n");
2289 #endif /* VTYSH_DEBUG */
2290 return;
2291 }
2292
2293 #ifdef VTYSH_DEBUG
2294 printf("line: %.*s\n", nbytes, buf);
2295 #endif /* VTYSH_DEBUG */
2296
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. */
2312 #ifdef VTYSH_DEBUG
2313 printf("result: %d\n", ret);
2314 printf("vtysh node: %d\n", vty->node);
2315 #endif /* VTYSH_DEBUG */
2316
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
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;
2343
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
2350 /* warning: watchfrr hardcodes this result write
2351 */
2352 header[3] = ret;
2353 buffer_put(vty->obuf, header, 4);
2354
2355 if (!vty->t_write && (vtysh_flush(vty) < 0))
2356 /* Try to flush results; exit if a write
2357 * error occurs. */
2358 return;
2359 }
2360 }
2361 }
2362
2363 if (vty->status == VTY_CLOSE)
2364 vty_close(vty);
2365 else
2366 vty_event(VTYSH_READ, vty);
2367 }
2368
2369 static void vtysh_write(struct event *thread)
2370 {
2371 struct vty *vty = EVENT_ARG(thread);
2372
2373 vtysh_flush(vty);
2374 }
2375
2376 #endif /* VTYSH */
2377
2378 /* Determine address family to bind. */
2379 void vty_serv_start(const char *addr, unsigned short port, const char *path)
2380 {
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);
2384
2385 #ifdef VTYSH
2386 vty_serv_un(path);
2387 #endif /* VTYSH */
2388 }
2389
2390 void 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
2404 static void vty_error_delete(void *arg)
2405 {
2406 struct vty_error *ve = arg;
2407
2408 XFREE(MTYPE_TMP, ve);
2409 }
2410
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). */
2415 void vty_close(struct vty *vty)
2416 {
2417 int i;
2418 bool was_stdio = false;
2419
2420 vty->status = VTY_CLOSE;
2421
2422 if (mgmt_lib_hndl && vty->mgmt_session_id) {
2423 mgmt_fe_destroy_client_session(mgmt_lib_hndl,
2424 vty->mgmt_client_id);
2425 vty->mgmt_session_id = 0;
2426 }
2427
2428 /* Drop out of configure / transaction if needed. */
2429 vty_config_exit(vty);
2430
2431 /* Cancel threads.*/
2432 EVENT_OFF(vty->t_read);
2433 EVENT_OFF(vty->t_write);
2434 EVENT_OFF(vty->t_timeout);
2435
2436 if (vty->pass_fd != -1) {
2437 close(vty->pass_fd);
2438 vty->pass_fd = -1;
2439 }
2440 zlog_live_close(&vty->live_log);
2441
2442 /* Flush buffer. */
2443 buffer_flush_all(vty->obuf, vty->wfd);
2444
2445 /* Free input buffer. */
2446 buffer_free(vty->obuf);
2447 buffer_free(vty->lbuf);
2448
2449 /* Free command history. */
2450 for (i = 0; i < VTY_MAXHIST; i++) {
2451 XFREE(MTYPE_VTY_HIST, vty->hist[i]);
2452 }
2453
2454 /* Unset vector. */
2455 if (vty->fd != -1) {
2456 if (vty->type == VTY_SHELL_SERV)
2457 vtys_del(vtysh_sessions, vty);
2458 else if (vty->type == VTY_TERM)
2459 vtys_del(vty_sessions, vty);
2460 }
2461
2462 if (vty->wfd > 0 && vty->type == VTY_FILE)
2463 fsync(vty->wfd);
2464
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);
2472 if (vty->fd > STDERR_FILENO)
2473 close(vty->fd);
2474 if (vty->fd == STDIN_FILENO)
2475 was_stdio = true;
2476
2477 XFREE(MTYPE_TMP, vty->pending_cmds_buf);
2478 XFREE(MTYPE_VTY, vty->buf);
2479
2480 if (vty->error) {
2481 vty->error->del = vty_error_delete;
2482 list_delete(&vty->error);
2483 }
2484
2485 /* OK free vty. */
2486 XFREE(MTYPE_VTY, vty);
2487
2488 if (was_stdio)
2489 vty_stdio_reset(0);
2490 }
2491
2492 /* When time out occur output message then close connection. */
2493 static void vty_timeout(struct event *thread)
2494 {
2495 struct vty *vty;
2496
2497 vty = EVENT_ARG(thread);
2498 vty->v_timeout = 0;
2499
2500 /* Clear buffer*/
2501 buffer_reset(vty->lbuf);
2502 buffer_reset(vty->obuf);
2503 vty_out(vty, "\nVty connection is timed out.\n");
2504
2505 /* Close connection. */
2506 vty->status = VTY_CLOSE;
2507 vty_close(vty);
2508 }
2509
2510 /* Read up configuration file from file_name. */
2511 void vty_read_file(struct nb_config *config, FILE *confp)
2512 {
2513 struct vty *vty;
2514 unsigned int line_num = 0;
2515
2516 vty = vty_new();
2517 /* vty_close won't close stderr; if some config command prints
2518 * something it'll end up there. (not ideal; it'd be better if output
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;
2525 vty->type = VTY_FILE;
2526 vty->node = CONFIG_NODE;
2527 vty->config = true;
2528 if (config)
2529 vty->candidate_config = config;
2530 else {
2531 vty->private_config = true;
2532 vty->candidate_config = nb_config_new(NULL);
2533 }
2534
2535 /* Execute configuration file */
2536 (void)config_from_file(vty, confp, &line_num);
2537
2538 vty_read_file_finish(vty, config);
2539 }
2540
2541 void vty_read_file_finish(struct vty *vty, struct nb_config *config)
2542 {
2543 struct vty_error *ve;
2544 struct listnode *node;
2545
2546 /* Flush any previous errors before printing messages below */
2547 buffer_flush_all(vty->obuf, vty->wfd);
2548
2549 for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
2550 const char *message = NULL;
2551 char *nl;
2552
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;
2560 case CMD_ERR_AMBIGUOUS:
2561 message = "Ambiguous command";
2562 break;
2563 case CMD_ERR_NO_MATCH:
2564 message = "No such command";
2565 break;
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:
2576 message =
2577 "Command exceeded maximum number of Arguments";
2578 break;
2579 default:
2580 message = "Command returned unhandled error message";
2581 break;
2582 }
2583
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);
2589 }
2590
2591 /*
2592 * Automatically commit the candidate configuration after
2593 * reading the configuration file.
2594 */
2595 if (config == NULL) {
2596 struct nb_context context = {};
2597 char errmsg[BUFSIZ] = {0};
2598 int ret;
2599
2600 context.client = NB_CLIENT_CLI;
2601 context.user = vty;
2602 ret = nb_candidate_commit(context, vty->candidate_config, true,
2603 "Read configuration file", NULL,
2604 errmsg, sizeof(errmsg));
2605 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
2606 zlog_err(
2607 "%s: failed to read configuration file: %s (%s)",
2608 __func__, nb_err_name(ret), errmsg);
2609 }
2610
2611 vty_close(vty);
2612 }
2613
2614 static 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
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);
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);
2634 snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath);
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;
2667 }
2668
2669 FILE *vty_open_config(const char *config_file, char *config_default_dir)
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) {
2680 flog_err_sys(
2681 EC_LIB_SYSTEM_CALL,
2682 "%s: failure to determine Current Working Directory %d!",
2683 __func__, errno);
2684 goto tmp_free_and_out;
2685 }
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);
2689 fullpath = tmp;
2690 } else
2691 fullpath = config_file;
2692
2693 confp = fopen(fullpath, "r");
2694
2695 if (confp == NULL) {
2696 flog_warn(
2697 EC_LIB_BACKUP_CONFIG,
2698 "%s: failed to open configuration file %s: %s, checking backup",
2699 __func__, fullpath, safe_strerror(errno));
2700
2701 confp = vty_use_backup_config(fullpath);
2702 if (confp)
2703 flog_warn(EC_LIB_BACKUP_CONFIG,
2704 "using backup configuration file!");
2705 else {
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;
2711 }
2712 }
2713 } else {
2714
2715 host_config_set(config_default_dir);
2716
2717 #ifdef VTYSH
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);
2737 if (ret >= 0)
2738 goto tmp_free_and_out;
2739 }
2740 #endif /* VTYSH */
2741 confp = fopen(config_default_dir, "r");
2742 if (confp == NULL) {
2743 flog_err(
2744 EC_LIB_SYSTEM_CALL,
2745 "%s: failed to open configuration file %s: %s, checking backup",
2746 __func__, config_default_dir,
2747 safe_strerror(errno));
2748
2749 confp = vty_use_backup_config(config_default_dir);
2750 if (confp) {
2751 flog_warn(EC_LIB_BACKUP_CONFIG,
2752 "using backup configuration file!");
2753 fullpath = config_default_dir;
2754 } else {
2755 flog_err(EC_LIB_VTY,
2756 "can't open configuration file [%s]",
2757 config_default_dir);
2758 goto tmp_free_and_out;
2759 }
2760 } else
2761 fullpath = config_default_dir;
2762 }
2763
2764 host_config_set(fullpath);
2765
2766 tmp_free_and_out:
2767 XFREE(MTYPE_TMP, tmp);
2768
2769 return confp;
2770 }
2771
2772
2773 bool vty_read_config(struct nb_config *config, const char *config_file,
2774 char *config_default_dir)
2775 {
2776 FILE *confp;
2777
2778 confp = vty_open_config(config_file, config_default_dir);
2779 if (!confp)
2780 return false;
2781
2782 vty_read_file(config, confp);
2783
2784 fclose(confp);
2785
2786 return true;
2787 }
2788
2789 int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
2790 {
2791 if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) {
2792 vty_out(vty, "%% Configuration is locked by other client\n");
2793 return CMD_WARNING;
2794 }
2795
2796 if (vty_mgmt_fe_enabled()) {
2797 if (!mgmt_candidate_ds_wr_locked) {
2798 if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE,
2799 true) != 0) {
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
2813 vty->node = CONFIG_NODE;
2814 vty->config = true;
2815 vty->private_config = private_config;
2816 vty->xpath_index = 0;
2817
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 {
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;
2832 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
2833 vty->candidate_config_base =
2834 nb_config_dup(running_config);
2835 }
2836
2837 return CMD_SUCCESS;
2838 }
2839
2840 void vty_config_exit(struct vty *vty)
2841 {
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 }
2851 if (node != CONFIG_NODE)
2852 /* called outside config, e.g. vty_close() in ENABLE_NODE */
2853 return;
2854
2855 while (vty->node != ENABLE_NODE)
2856 /* will call vty_config_node_exit() below */
2857 cmd_exit(vty);
2858 }
2859
2860 int vty_config_node_exit(struct vty *vty)
2861 {
2862 vty->xpath_index = 0;
2863
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) {
2870 if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false) !=
2871 0) {
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
2880 /* Perform any pending commits. */
2881 (void)nb_cli_pending_commit_check(vty);
2882
2883 /* Check if there's a pending confirmed commit. */
2884 if (vty->t_confirmed_commit_timeout) {
2885 vty_out(vty,
2886 "exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
2887 nb_cli_confirmed_commit_rollback(vty);
2888 nb_cli_confirmed_commit_clean(vty);
2889 }
2890
2891 (void)nb_running_unlock(NB_CLIENT_CLI, vty);
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 }
2902
2903 vty->config = false;
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
2914 return 1;
2915 }
2916
2917 /* Master of the threads. */
2918 static struct event_loop *vty_master;
2919
2920 static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv)
2921 {
2922 switch (event) {
2923 case VTY_SERV:
2924 event_add_read(vty_master, vty_accept, vty_serv, vty_serv->sock,
2925 &vty_serv->t_accept);
2926 break;
2927 #ifdef VTYSH
2928 case VTYSH_SERV:
2929 event_add_read(vty_master, vtysh_accept, vty_serv,
2930 vty_serv->sock, &vty_serv->t_accept);
2931 break;
2932 #endif /* VTYSH */
2933 case VTY_READ:
2934 case VTY_WRITE:
2935 case VTY_TIMEOUT_RESET:
2936 case VTYSH_READ:
2937 case VTYSH_WRITE:
2938 assert(!"vty_event_serv() called incorrectly");
2939 }
2940 }
2941
2942 static void vty_event(enum vty_event event, struct vty *vty)
2943 {
2944 switch (event) {
2945 #ifdef VTYSH
2946 case VTYSH_READ:
2947 event_add_read(vty_master, vtysh_read, vty, vty->fd,
2948 &vty->t_read);
2949 break;
2950 case VTYSH_WRITE:
2951 event_add_write(vty_master, vtysh_write, vty, vty->wfd,
2952 &vty->t_write);
2953 break;
2954 #endif /* VTYSH */
2955 case VTY_READ:
2956 event_add_read(vty_master, vty_read, vty, vty->fd,
2957 &vty->t_read);
2958
2959 /* Time out treatment. */
2960 if (vty->v_timeout) {
2961 EVENT_OFF(vty->t_timeout);
2962 event_add_timer(vty_master, vty_timeout, vty,
2963 vty->v_timeout, &vty->t_timeout);
2964 }
2965 break;
2966 case VTY_WRITE:
2967 event_add_write(vty_master, vty_flush, vty, vty->wfd,
2968 &vty->t_write);
2969 break;
2970 case VTY_TIMEOUT_RESET:
2971 EVENT_OFF(vty->t_timeout);
2972 if (vty->v_timeout)
2973 event_add_timer(vty_master, vty_timeout, vty,
2974 vty->v_timeout, &vty->t_timeout);
2975 break;
2976 case VTY_SERV:
2977 case VTYSH_SERV:
2978 assert(!"vty_event() called incorrectly");
2979 }
2980 }
2981
2982 DEFUN_NOSH (config_who,
2983 config_who_cmd,
2984 "who",
2985 "Display who is on vty\n")
2986 {
2987 struct vty *v;
2988
2989 frr_each (vtys, vty_sessions, v)
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");
2993 return CMD_SUCCESS;
2994 }
2995
2996 /* Move to vty configuration mode. */
2997 DEFUN_NOSH (line_vty,
2998 line_vty_cmd,
2999 "line vty",
3000 "Configure a terminal line\n"
3001 "Virtual terminal\n")
3002 {
3003 vty->node = VTY_NODE;
3004 return CMD_SUCCESS;
3005 }
3006
3007 /* Set time out value. */
3008 static int exec_timeout(struct vty *vty, const char *min_str,
3009 const char *sec_str)
3010 {
3011 unsigned long timeout = 0;
3012
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);
3021
3022 vty_timeout_val = timeout;
3023 vty->v_timeout = timeout;
3024 vty_event(VTY_TIMEOUT_RESET, vty);
3025
3026
3027 return CMD_SUCCESS;
3028 }
3029
3030 DEFUN (exec_timeout_min,
3031 exec_timeout_min_cmd,
3032 "exec-timeout (0-35791)",
3033 "Set timeout value\n"
3034 "Timeout value in minutes\n")
3035 {
3036 int idx_number = 1;
3037 return exec_timeout(vty, argv[idx_number]->arg, NULL);
3038 }
3039
3040 DEFUN (exec_timeout_sec,
3041 exec_timeout_sec_cmd,
3042 "exec-timeout (0-35791) (0-2147483)",
3043 "Set the EXEC timeout\n"
3044 "Timeout in minutes\n"
3045 "Timeout in seconds\n")
3046 {
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);
3051 }
3052
3053 DEFUN (no_exec_timeout,
3054 no_exec_timeout_cmd,
3055 "no exec-timeout",
3056 NO_STR
3057 "Set the EXEC timeout\n")
3058 {
3059 return exec_timeout(vty, NULL, NULL);
3060 }
3061
3062 /* Set vty access class. */
3063 DEFUN (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 {
3069 int idx_word = 1;
3070 if (vty_accesslist_name)
3071 XFREE(MTYPE_VTY, vty_accesslist_name);
3072
3073 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
3074
3075 return CMD_SUCCESS;
3076 }
3077
3078 /* Clear vty access class. */
3079 DEFUN (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 {
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 }
3093
3094 XFREE(MTYPE_VTY, vty_accesslist_name);
3095
3096 vty_accesslist_name = NULL;
3097
3098 return CMD_SUCCESS;
3099 }
3100
3101 /* Set vty access class. */
3102 DEFUN (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 {
3109 int idx_word = 2;
3110 if (vty_ipv6_accesslist_name)
3111 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3112
3113 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
3114
3115 return CMD_SUCCESS;
3116 }
3117
3118 /* Clear vty access class. */
3119 DEFUN (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 {
3127 int idx_word = 3;
3128 const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL;
3129
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 }
3136
3137 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3138
3139 vty_ipv6_accesslist_name = NULL;
3140
3141 return CMD_SUCCESS;
3142 }
3143
3144 /* vty login. */
3145 DEFUN (vty_login,
3146 vty_login_cmd,
3147 "login",
3148 "Enable password checking\n")
3149 {
3150 no_password_check = 0;
3151 return CMD_SUCCESS;
3152 }
3153
3154 DEFUN (no_vty_login,
3155 no_vty_login_cmd,
3156 "no login",
3157 NO_STR
3158 "Enable password checking\n")
3159 {
3160 no_password_check = 1;
3161 return CMD_SUCCESS;
3162 }
3163
3164 DEFUN (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 {
3170 host.advanced = 1;
3171 return CMD_SUCCESS;
3172 }
3173
3174 DEFUN (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 {
3181 host.advanced = 0;
3182 return CMD_SUCCESS;
3183 }
3184
3185 DEFUN_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")
3191 {
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);
3213 return CMD_SUCCESS;
3214 }
3215
3216 DEFUN_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")
3222 {
3223 zlog_live_close(&vty->live_log);
3224 return CMD_SUCCESS;
3225 }
3226
3227 DEFUN_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")
3233 {
3234 return no_terminal_monitor(self, vty, argc, argv);
3235 }
3236
3237
3238 DEFUN_NOSH (show_history,
3239 show_history_cmd,
3240 "show history",
3241 SHOW_STR
3242 "Display the session command history\n")
3243 {
3244 int index;
3245
3246 for (index = vty->hindex + 1; index != vty->hindex;) {
3247 if (index == VTY_MAXHIST) {
3248 index = 0;
3249 continue;
3250 }
3251
3252 if (vty->hist[index] != NULL)
3253 vty_out(vty, " %s\n", vty->hist[index]);
3254
3255 index++;
3256 }
3257
3258 return CMD_SUCCESS;
3259 }
3260
3261 /* vty login. */
3262 DEFPY (log_commands,
3263 log_commands_cmd,
3264 "[no] log commands",
3265 NO_STR
3266 "Logging control\n"
3267 "Log all commands\n")
3268 {
3269 if (no) {
3270 if (vty_log_commands_perm) {
3271 vty_out(vty,
3272 "Daemon started with permanent logging turned on for commands, ignoring\n");
3273 return CMD_WARNING;
3274 }
3275
3276 vty_log_commands = false;
3277 } else
3278 vty_log_commands = true;
3279
3280 return CMD_SUCCESS;
3281 }
3282
3283 /* Display current configuration. */
3284 static int vty_config_write(struct vty *vty)
3285 {
3286 vty_frame(vty, "line vty\n");
3287
3288 if (vty_accesslist_name)
3289 vty_out(vty, " access-class %s\n", vty_accesslist_name);
3290
3291 if (vty_ipv6_accesslist_name)
3292 vty_out(vty, " ipv6 access-class %s\n",
3293 vty_ipv6_accesslist_name);
3294
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);
3299
3300 /* login */
3301 if (no_password_check)
3302 vty_out(vty, " no login\n");
3303
3304 vty_endframe(vty, "exit\n");
3305
3306 if (vty_log_commands)
3307 vty_out(vty, "log commands\n");
3308
3309 vty_out(vty, "!\n");
3310
3311 return CMD_SUCCESS;
3312 }
3313
3314 static int vty_config_write(struct vty *vty);
3315 struct cmd_node vty_node = {
3316 .name = "vty",
3317 .node = VTY_NODE,
3318 .parent_node = CONFIG_NODE,
3319 .prompt = "%s(config-line)# ",
3320 .config_write = vty_config_write,
3321 };
3322
3323 /* Reset all VTY status. */
3324 void vty_reset(void)
3325 {
3326 struct vty *vty;
3327
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 }
3334
3335 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3336
3337 XFREE(MTYPE_VTY, vty_accesslist_name);
3338 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3339 }
3340
3341 static void vty_save_cwd(void)
3342 {
3343 char *c;
3344
3345 c = getcwd(vty_cwd, sizeof(vty_cwd));
3346
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 */
3353 if (chdir(SYSCONFDIR)) {
3354 flog_err_sys(EC_LIB_SYSTEM_CALL,
3355 "Failure to chdir to %s, errno: %d",
3356 SYSCONFDIR, errno);
3357 exit(-1);
3358 }
3359 if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) {
3360 flog_err_sys(EC_LIB_SYSTEM_CALL,
3361 "Failure to getcwd, errno: %d", errno);
3362 exit(-1);
3363 }
3364 }
3365 }
3366
3367 char *vty_get_cwd(void)
3368 {
3369 return vty_cwd;
3370 }
3371
3372 int vty_shell(struct vty *vty)
3373 {
3374 return vty->type == VTY_SHELL ? 1 : 0;
3375 }
3376
3377 int vty_shell_serv(struct vty *vty)
3378 {
3379 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3380 }
3381
3382 void vty_init_vtysh(void)
3383 {
3384 /* currently nothing to do, but likely to have future use */
3385 }
3386
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
3394 static void vty_mgmt_server_connected(uintptr_t lib_hndl, uintptr_t usr_data,
3395 bool connected)
3396 {
3397 MGMTD_FE_CLIENT_DBG("Got %sconnected %s MGMTD Frontend Server",
3398 !connected ? "dis: " : "",
3399 !connected ? "from" : "to");
3400
3401 /*
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.
3405 */
3406 assert(mgmt_fe_client_session_count(lib_hndl) == 0);
3407
3408 mgmt_fe_connected = connected;
3409
3410 /* Start or stop listening for vty connections */
3411 if (connected)
3412 frr_vty_serv_start();
3413 else
3414 frr_vty_serv_stop();
3415 }
3416
3417 /*
3418 * A session has successfully been created for a vty.
3419 */
3420 static void vty_mgmt_session_notify(uintptr_t lib_hndl, uintptr_t usr_data,
3421 uint64_t client_id, bool create,
3422 bool success, uintptr_t session_id,
3423 uintptr_t session_ctx)
3424 {
3425 struct vty *vty;
3426
3427 vty = (struct vty *)session_ctx;
3428
3429 if (!success) {
3430 zlog_err("%s session for client %" PRIu64 " failed!",
3431 create ? "Creating" : "Destroying", client_id);
3432 return;
3433 }
3434
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);
3440 vty->mgmt_session_id = session_id;
3441 } else {
3442 vty->mgmt_session_id = 0;
3443 vty_close(vty);
3444 }
3445 }
3446
3447 static void vty_mgmt_ds_lock_notified(uintptr_t lib_hndl, uintptr_t usr_data,
3448 uint64_t client_id, uintptr_t session_id,
3449 uintptr_t session_ctx, uint64_t req_id,
3450 bool lock_ds, bool success,
3451 Mgmtd__DatastoreId ds_id,
3452 char *errmsg_if_any)
3453 {
3454 struct vty *vty;
3455
3456 vty = (struct vty *)session_ctx;
3457
3458 if (!success) {
3459 zlog_err("%socking for DS %u failed, Err: '%s'",
3460 lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
3461 vty_out(vty, "ERROR: %socking for DS %u failed, Err: '%s'\n",
3462 lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
3463 } else {
3464 MGMTD_FE_CLIENT_DBG("%socked DS %u successfully",
3465 lock_ds ? "L" : "Unl", ds_id);
3466 }
3467
3468 vty_mgmt_resume_response(vty, success);
3469 }
3470
3471 static void vty_mgmt_set_config_result_notified(
3472 uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
3473 uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
3474 bool success, Mgmtd__DatastoreId ds_id, char *errmsg_if_any)
3475 {
3476 struct vty *vty;
3477
3478 vty = (struct vty *)session_ctx;
3479
3480 if (!success) {
3481 zlog_err("SET_CONFIG request for client 0x%" PRIx64
3482 " failed, Error: '%s'",
3483 client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
3484 vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n",
3485 errmsg_if_any ? errmsg_if_any : "Unknown");
3486 } else {
3487 MGMTD_FE_CLIENT_DBG("SET_CONFIG request for client 0x%" PRIx64
3488 " req-id %" PRIu64 " was successfull",
3489 client_id, req_id);
3490 }
3491
3492 vty_mgmt_resume_response(vty, success);
3493 }
3494
3495 static void vty_mgmt_commit_config_result_notified(
3496 uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
3497 uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
3498 bool success, Mgmtd__DatastoreId src_ds_id,
3499 Mgmtd__DatastoreId dst_ds_id, bool validate_only, char *errmsg_if_any)
3500 {
3501 struct vty *vty;
3502
3503 vty = (struct vty *)session_ctx;
3504
3505 if (!success) {
3506 zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64
3507 " failed, Error: '%s'",
3508 client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
3509 vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n",
3510 errmsg_if_any ? errmsg_if_any : "Unknown");
3511 } else {
3512 MGMTD_FE_CLIENT_DBG(
3513 "COMMIT_CONFIG request for client 0x%" PRIx64
3514 " req-id %" PRIu64 " was successfull",
3515 client_id, req_id);
3516 if (errmsg_if_any)
3517 vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
3518 }
3519
3520 vty_mgmt_resume_response(vty, success);
3521 }
3522
3523 static enum mgmt_result vty_mgmt_get_data_result_notified(
3524 uintptr_t lib_hndl, uintptr_t usr_data, uint64_t client_id,
3525 uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
3526 bool success, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data,
3527 size_t num_data, int next_key, char *errmsg_if_any)
3528 {
3529 struct vty *vty;
3530 size_t indx;
3531
3532 vty = (struct vty *)session_ctx;
3533
3534 if (!success) {
3535 zlog_err("GET_DATA request for client 0x%" PRIx64
3536 " failed, Error: '%s'",
3537 client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
3538 vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
3539 errmsg_if_any ? errmsg_if_any : "Unknown");
3540 vty_mgmt_resume_response(vty, success);
3541 return MGMTD_INTERNAL_ERROR;
3542 }
3543
3544 MGMTD_FE_CLIENT_DBG("GET_DATA request succeeded, client 0x%" PRIx64
3545 " req-id %" PRIu64,
3546 client_id, req_id);
3547
3548 if (req_id != mgmt_last_req_id) {
3549 mgmt_last_req_id = req_id;
3550 vty_out(vty, "[\n");
3551 }
3552
3553 for (indx = 0; indx < num_data; indx++) {
3554 vty_out(vty, " \"%s\": \"%s\"\n", yang_data[indx]->xpath,
3555 yang_data[indx]->value->encoded_str_val);
3556 }
3557 if (next_key < 0) {
3558 vty_out(vty, "]\n");
3559 vty_mgmt_resume_response(vty, success);
3560 }
3561
3562 return MGMTD_SUCCESS;
3563 }
3564
3565 static struct mgmt_fe_client_params client_params = {
3566 .client_connect_notify = vty_mgmt_server_connected,
3567 .client_session_notify = vty_mgmt_session_notify,
3568 .lock_ds_notify = vty_mgmt_ds_lock_notified,
3569 .set_config_notify = vty_mgmt_set_config_result_notified,
3570 .commit_config_notify = vty_mgmt_commit_config_result_notified,
3571 .get_data_notify = vty_mgmt_get_data_result_notified,
3572 };
3573
3574 void vty_init_mgmt_fe(void)
3575 {
3576 if (!vty_master) {
3577 zlog_err("Always call vty_mgmt_init_fe() after vty_init()!!");
3578 return;
3579 }
3580
3581 assert(!mgmt_lib_hndl);
3582 snprintf(client_params.name, sizeof(client_params.name), "%s-%lld",
3583 frr_get_progname(), (long long)getpid());
3584 mgmt_lib_hndl = mgmt_fe_client_lib_init(&client_params, vty_master);
3585 assert(mgmt_lib_hndl);
3586 }
3587
3588 bool vty_mgmt_fe_enabled(void)
3589 {
3590 return mgmt_lib_hndl && mgmt_fe_connected;
3591 }
3592
3593 bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty)
3594 {
3595 return vty->type != VTY_FILE && vty_mgmt_fe_enabled();
3596 }
3597
3598 int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
3599 bool lock)
3600 {
3601 enum mgmt_result ret;
3602
3603 if (mgmt_lib_hndl && vty->mgmt_session_id) {
3604 vty->mgmt_req_id++;
3605 ret = mgmt_fe_lock_ds(mgmt_lib_hndl, vty->mgmt_session_id,
3606 vty->mgmt_req_id, ds_id, lock);
3607 if (ret != MGMTD_SUCCESS) {
3608 zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64,
3609 lock ? "" : "UN", vty->mgmt_req_id);
3610 vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
3611 lock ? "" : "UN");
3612 return -1;
3613 }
3614
3615 vty->mgmt_req_pending = true;
3616 }
3617
3618 return 0;
3619 }
3620
3621 int vty_mgmt_send_config_data(struct vty *vty)
3622 {
3623 Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES];
3624 Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES];
3625 Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];
3626 Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};
3627 size_t indx;
3628 int cnt;
3629 bool implicit_commit = false;
3630
3631 if (vty->type == VTY_FILE) {
3632 /*
3633 * if this is a config file read we will not send any of the
3634 * changes until we are done reading the file and have modified
3635 * the local candidate DS.
3636 */
3637 assert(vty->mgmt_locked_candidate_ds);
3638 /* no-one else should be sending data right now */
3639 assert(!vty->mgmt_num_pending_setcfg);
3640 return 0;
3641 }
3642
3643
3644 if (mgmt_lib_hndl && vty->mgmt_client_id && !vty->mgmt_session_id) {
3645 /*
3646 * We are connected to mgmtd but we do not yet have an
3647 * established session. this means we need to send any changes
3648 * made during this "down-time" to all backend clients when this
3649 * FE client finishes coming up.
3650 */
3651 MGMTD_FE_CLIENT_DBG("skipping as no session exists");
3652 return 0;
3653 }
3654
3655 if (mgmt_lib_hndl && vty->mgmt_session_id) {
3656 cnt = 0;
3657 for (indx = 0; indx < vty->num_cfg_changes; indx++) {
3658 mgmt_yang_data_init(&cfg_data[cnt]);
3659
3660 if (vty->cfg_changes[indx].value) {
3661 mgmt_yang_data_value_init(&value[cnt]);
3662 value[cnt].encoded_str_val =
3663 (char *)vty->cfg_changes[indx].value;
3664 value[cnt].value_case =
3665 MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
3666 cfg_data[cnt].value = &value[cnt];
3667 }
3668
3669 cfg_data[cnt].xpath = vty->cfg_changes[indx].xpath;
3670
3671 mgmt_yang_cfg_data_req_init(&cfg_req[cnt]);
3672 cfg_req[cnt].data = &cfg_data[cnt];
3673 switch (vty->cfg_changes[indx].operation) {
3674 case NB_OP_DESTROY:
3675 cfg_req[cnt].req_type =
3676 MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
3677 break;
3678
3679 case NB_OP_CREATE:
3680 case NB_OP_MODIFY:
3681 case NB_OP_MOVE:
3682 case NB_OP_PRE_VALIDATE:
3683 case NB_OP_APPLY_FINISH:
3684 cfg_req[cnt].req_type =
3685 MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
3686 break;
3687 case NB_OP_GET_ELEM:
3688 case NB_OP_GET_NEXT:
3689 case NB_OP_GET_KEYS:
3690 case NB_OP_LOOKUP_ENTRY:
3691 case NB_OP_RPC:
3692 assert(!"Invalid type of operation");
3693 break;
3694 default:
3695 assert(!"non-enum value, invalid");
3696 }
3697
3698 cfgreq[cnt] = &cfg_req[cnt];
3699 cnt++;
3700 }
3701
3702 vty->mgmt_req_id++;
3703 implicit_commit = vty_needs_implicit_commit(vty);
3704 if (cnt && mgmt_fe_set_config_data(
3705 mgmt_lib_hndl, vty->mgmt_session_id,
3706 vty->mgmt_req_id, MGMTD_DS_CANDIDATE, cfgreq,
3707 cnt, implicit_commit,
3708 MGMTD_DS_RUNNING) != MGMTD_SUCCESS) {
3709 zlog_err("Failed to send %d Config Xpaths to MGMTD!!",
3710 (int)indx);
3711 vty_out(vty, "Failed to send SETCFG-REQ to MGMTD!\n");
3712 return -1;
3713 }
3714
3715 vty->mgmt_req_pending = true;
3716 }
3717
3718 return 0;
3719 }
3720
3721 int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort)
3722 {
3723 enum mgmt_result ret;
3724
3725 if (mgmt_lib_hndl && vty->mgmt_session_id) {
3726 vty->mgmt_req_id++;
3727 ret = mgmt_fe_commit_config_data(
3728 mgmt_lib_hndl, vty->mgmt_session_id, vty->mgmt_req_id,
3729 MGMTD_DS_CANDIDATE, MGMTD_DS_RUNNING, validate_only,
3730 abort);
3731 if (ret != MGMTD_SUCCESS) {
3732 zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64,
3733 vty->mgmt_req_id);
3734 vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n");
3735 return -1;
3736 }
3737
3738 vty->mgmt_req_pending = true;
3739 vty->mgmt_num_pending_setcfg = 0;
3740 }
3741
3742 return 0;
3743 }
3744
3745 int vty_mgmt_send_get_config(struct vty *vty, Mgmtd__DatastoreId datastore,
3746 const char **xpath_list, int num_req)
3747 {
3748 enum mgmt_result ret;
3749 Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
3750 Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
3751 Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
3752 int i;
3753
3754 vty->mgmt_req_id++;
3755
3756 for (i = 0; i < num_req; i++) {
3757 mgmt_yang_get_data_req_init(&get_req[i]);
3758 mgmt_yang_data_init(&yang_data[i]);
3759
3760 yang_data->xpath = (char *)xpath_list[i];
3761
3762 get_req[i].data = &yang_data[i];
3763 getreq[i] = &get_req[i];
3764 }
3765 ret = mgmt_fe_get_config_data(mgmt_lib_hndl, vty->mgmt_session_id,
3766 vty->mgmt_req_id, datastore, getreq,
3767 num_req);
3768
3769 if (ret != MGMTD_SUCCESS) {
3770 zlog_err(
3771 "Failed to send GET-CONFIG to MGMTD for req-id %" PRIu64
3772 ".",
3773 vty->mgmt_req_id);
3774 vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n");
3775 return -1;
3776 }
3777
3778 vty->mgmt_req_pending = true;
3779
3780 return 0;
3781 }
3782
3783 int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
3784 const char **xpath_list, int num_req)
3785 {
3786 enum mgmt_result ret;
3787 Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
3788 Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
3789 Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
3790 int i;
3791
3792 vty->mgmt_req_id++;
3793
3794 for (i = 0; i < num_req; i++) {
3795 mgmt_yang_get_data_req_init(&get_req[i]);
3796 mgmt_yang_data_init(&yang_data[i]);
3797
3798 yang_data->xpath = (char *)xpath_list[i];
3799
3800 get_req[i].data = &yang_data[i];
3801 getreq[i] = &get_req[i];
3802 }
3803 ret = mgmt_fe_get_data(mgmt_lib_hndl, vty->mgmt_session_id,
3804 vty->mgmt_req_id, datastore, getreq, num_req);
3805
3806 if (ret != MGMTD_SUCCESS) {
3807 zlog_err("Failed to send GET-DATA to MGMTD for req-id %" PRIu64
3808 ".",
3809 vty->mgmt_req_id);
3810 vty_out(vty, "Failed to send GET-DATA to MGMTD!\n");
3811 return -1;
3812 }
3813
3814 vty->mgmt_req_pending = true;
3815
3816 return 0;
3817 }
3818
3819 /* Install vty's own commands like `who' command. */
3820 void vty_init(struct event_loop *master_thread, bool do_command_logging)
3821 {
3822 /* For further configuration read, preserve current directory. */
3823 vty_save_cwd();
3824
3825 vty_master = master_thread;
3826
3827 atexit(vty_stdio_atexit);
3828
3829 /* Install bgp top node. */
3830 install_node(&vty_node);
3831
3832 install_element(VIEW_NODE, &config_who_cmd);
3833 install_element(VIEW_NODE, &show_history_cmd);
3834 install_element(CONFIG_NODE, &line_vty_cmd);
3835 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
3836 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
3837 install_element(CONFIG_NODE, &show_history_cmd);
3838 install_element(CONFIG_NODE, &log_commands_cmd);
3839
3840 if (do_command_logging) {
3841 vty_log_commands = true;
3842 vty_log_commands_perm = true;
3843 }
3844
3845 install_element(ENABLE_NODE, &terminal_monitor_cmd);
3846 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
3847 install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
3848
3849 install_default(VTY_NODE);
3850 install_element(VTY_NODE, &exec_timeout_min_cmd);
3851 install_element(VTY_NODE, &exec_timeout_sec_cmd);
3852 install_element(VTY_NODE, &no_exec_timeout_cmd);
3853 install_element(VTY_NODE, &vty_access_class_cmd);
3854 install_element(VTY_NODE, &no_vty_access_class_cmd);
3855 install_element(VTY_NODE, &vty_login_cmd);
3856 install_element(VTY_NODE, &no_vty_login_cmd);
3857 install_element(VTY_NODE, &vty_ipv6_access_class_cmd);
3858 install_element(VTY_NODE, &no_vty_ipv6_access_class_cmd);
3859 }
3860
3861 void vty_terminate(void)
3862 {
3863 struct vty *vty;
3864
3865 if (mgmt_lib_hndl) {
3866 mgmt_fe_client_lib_destroy();
3867 mgmt_lib_hndl = 0;
3868 }
3869
3870 memset(vty_cwd, 0x00, sizeof(vty_cwd));
3871
3872 vty_reset();
3873
3874 /* default state of vty_sessions is initialized & empty. */
3875 vtys_fini(vty_sessions);
3876 vtys_init(vty_sessions);
3877
3878 /* vty_reset() doesn't close vtysh sessions */
3879 frr_each_safe (vtys, vtysh_sessions, vty) {
3880 buffer_reset(vty->lbuf);
3881 buffer_reset(vty->obuf);
3882 vty->status = VTY_CLOSE;
3883 vty_close(vty);
3884 }
3885
3886 vtys_fini(vtysh_sessions);
3887 vtys_init(vtysh_sessions);
3888
3889 vty_serv_stop();
3890 }