1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Virtual terminal [aka TeletYpe] interface routine.
4 * Copyright (C) 1997, 98 Kunihiro Ishiguro
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>
21 #endif /* HAVE_LIBPCRE2_POSIX */
28 #include "sockunion.h"
38 #include "lib_errors.h"
39 #include "northbound_cli.h"
43 #include <arpa/telnet.h>
46 #include "lib/vty_clippy.c"
48 DEFINE_MTYPE_STATIC(LIB
, VTY
, "VTY");
49 DEFINE_MTYPE_STATIC(LIB
, VTY_SERV
, "VTY server");
50 DEFINE_MTYPE_STATIC(LIB
, VTY_OUT_BUF
, "VTY output buffer");
51 DEFINE_MTYPE_STATIC(LIB
, VTY_HIST
, "VTY history");
53 DECLARE_DLIST(vtys
, struct vty
, itm
);
68 struct nb_config
*vty_mgmt_candidate_config
;
70 static uintptr_t mgmt_lib_hndl
;
71 static bool mgmt_fe_connected
;
72 static bool mgmt_candidate_ds_wr_locked
;
73 static uint64_t mgmt_client_id_next
;
74 static uint64_t mgmt_last_req_id
= UINT64_MAX
;
76 PREDECL_DLIST(vtyservs
);
79 struct vtyservs_item itm
;
84 struct event
*t_accept
;
87 DECLARE_DLIST(vtyservs
, struct vty_serv
, itm
);
89 static void vty_event_serv(enum vty_event event
, struct vty_serv
*);
90 static void vty_event(enum vty_event
, struct vty
*);
91 static int vtysh_flush(struct vty
*vty
);
93 /* Extern host structure from command.c */
94 extern struct host host
;
96 /* active listeners */
97 static struct vtyservs_head vty_servs
[1] = {INIT_DLIST(vty_servs
[0])};
99 /* active connections */
100 static struct vtys_head vty_sessions
[1] = {INIT_DLIST(vty_sessions
[0])};
101 static struct vtys_head vtysh_sessions
[1] = {INIT_DLIST(vtysh_sessions
[0])};
103 /* Vty timeout value. */
104 static unsigned long vty_timeout_val
= VTY_TIMEOUT_DEFAULT
;
106 /* Vty access-class command */
107 static char *vty_accesslist_name
= NULL
;
109 /* Vty access-calss for IPv6. */
110 static char *vty_ipv6_accesslist_name
= NULL
;
112 /* Current directory. */
113 static char vty_cwd
[MAXPATHLEN
];
115 /* Login password check. */
116 static int no_password_check
= 0;
118 /* Integrated configuration file path */
119 static char integrate_default
[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG
;
121 static bool do_log_commands
;
122 static bool do_log_commands_perm
;
124 void vty_mgmt_resume_response(struct vty
*vty
, bool success
)
126 uint8_t header
[4] = {0, 0, 0, 0};
127 int ret
= CMD_SUCCESS
;
129 if (!vty
->mgmt_req_pending
) {
131 "vty response called without setting mgmt_req_pending");
136 ret
= CMD_WARNING_CONFIG_FAILED
;
138 vty
->mgmt_req_pending
= false;
140 buffer_put(vty
->obuf
, header
, 4);
142 if (!vty
->t_write
&& (vtysh_flush(vty
) < 0))
143 /* Try to flush results; exit if a write
148 if (vty
->status
== VTY_CLOSE
)
151 vty_event(VTYSH_READ
, vty
);
154 void vty_frame(struct vty
*vty
, const char *format
, ...)
158 va_start(args
, format
);
159 vsnprintfrr(vty
->frame
+ vty
->frame_pos
,
160 sizeof(vty
->frame
) - vty
->frame_pos
, format
, args
);
161 vty
->frame_pos
= strlen(vty
->frame
);
165 void vty_endframe(struct vty
*vty
, const char *endtext
)
167 if (vty
->frame_pos
== 0 && endtext
)
168 vty_out(vty
, "%s", endtext
);
172 bool vty_set_include(struct vty
*vty
, const char *regexp
)
180 regfree(&vty
->include
);
186 errcode
= regcomp(&vty
->include
, regexp
,
187 REG_EXTENDED
| REG_NEWLINE
| REG_NOSUB
);
190 regerror(errcode
, &vty
->include
, errbuf
, sizeof(errbuf
));
191 vty_out(vty
, "%% Regex compilation error: %s\n", errbuf
);
199 /* VTY standard output function. */
200 int vty_out(struct vty
*vty
, const char *format
, ...)
207 /* format string may contain %m, keep errno intact for printfrr */
208 int saved_errno
= errno
;
210 if (vty
->frame_pos
) {
212 vty_out(vty
, "%s", vty
->frame
);
215 va_start(args
, format
);
217 p
= vasnprintfrr(MTYPE_VTY_OUT_BUF
, buf
, sizeof(buf
), format
, args
);
224 vector lines
= frrstr_split_vec(p
, "\n");
226 /* Place first value in the cache */
227 char *firstline
= vector_slot(lines
, 0);
228 buffer_put(vty
->lbuf
, (uint8_t *) firstline
, strlen(firstline
));
230 /* If our split returned more than one entry, time to filter */
231 if (vector_active(lines
) > 1) {
233 * returned string is MTYPE_TMP so it matches the MTYPE
234 * of everything else in the vector
236 char *bstr
= buffer_getstr(vty
->lbuf
);
237 buffer_reset(vty
->lbuf
);
238 XFREE(MTYPE_TMP
, lines
->index
[0]);
239 vector_set_index(lines
, 0, bstr
);
240 frrstr_filter_vec(lines
, &vty
->include
);
241 vector_compact(lines
);
243 * Consider the string "foo\n". If the regex is an empty string
244 * and the line ended with a newline, then the vector will look
250 * If the regex isn't empty, the vector will look like:
254 * In this case we'd like to preserve the newline, so we add
255 * the empty string [1] as in the first example.
257 if (p
[strlen(p
) - 1] == '\n' && vector_active(lines
) > 0
258 && strlen(vector_slot(lines
, vector_active(lines
) - 1)))
259 vector_set(lines
, XSTRDUP(MTYPE_TMP
, ""));
261 filtered
= frrstr_join_vec(lines
, "\n");
267 frrstr_strvec_free(lines
);
278 /* print with crlf replacement */
279 buffer_put_crlf(vty
->obuf
, (uint8_t *)filtered
,
284 fprintf(vty
->of
, "%s", filtered
);
286 } else if (vty
->of_saved
) {
287 fprintf(vty
->of_saved
, "%s", filtered
);
288 fflush(vty
->of_saved
);
294 /* print without crlf replacement */
295 buffer_put(vty
->obuf
, (uint8_t *)filtered
, strlen(filtered
));
301 if (vty
->filter
&& filtered
)
302 XFREE(MTYPE_TMP
, filtered
);
304 /* If p is not different with buf, it is allocated buffer. */
306 XFREE(MTYPE_VTY_OUT_BUF
, p
);
311 static int vty_json_helper(struct vty
*vty
, struct json_object
*json
,
319 text
= json_object_to_json_string_ext(
321 vty_out(vty
, "%s\n", text
);
322 json_object_free(json
);
327 int vty_json(struct vty
*vty
, struct json_object
*json
)
329 return vty_json_helper(vty
, json
,
330 JSON_C_TO_STRING_PRETTY
|
331 JSON_C_TO_STRING_NOSLASHESCAPE
);
334 int vty_json_no_pretty(struct vty
*vty
, struct json_object
*json
)
336 return vty_json_helper(vty
, json
, JSON_C_TO_STRING_NOSLASHESCAPE
);
339 void vty_json_empty(struct vty
*vty
)
341 json_object
*json
= json_object_new_object();
346 /* Output current time to the vty. */
347 void vty_time_print(struct vty
*vty
, int cr
)
349 char buf
[FRR_TIMESTAMP_LEN
];
351 if (frr_timestamp(0, buf
, sizeof(buf
)) == 0) {
352 zlog_info("frr_timestamp error");
356 vty_out(vty
, "%s\n", buf
);
358 vty_out(vty
, "%s ", buf
);
363 /* Say hello to vty interface. */
364 void vty_hello(struct vty
*vty
)
370 f
= fopen(host
.motdfile
, "r");
372 while (fgets(buf
, sizeof(buf
), f
)) {
374 /* work backwards to ignore trailling isspace()
376 for (s
= buf
+ strlen(buf
);
377 (s
> buf
) && isspace((unsigned char)s
[-1]);
381 vty_out(vty
, "%s\n", buf
);
385 vty_out(vty
, "MOTD file not found\n");
386 } else if (host
.motd
)
387 vty_out(vty
, "%s", host
.motd
);
390 #pragma GCC diagnostic push
391 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
392 /* prompt formatting has a %s in the cmd_node prompt string.
394 * Also for some reason GCC emits the warning on the end of the function
395 * (optimization maybe?) rather than on the vty_out line, so this pragma
396 * wraps the entire function rather than just the vty_out line.
399 /* Put out prompt and wait input from user. */
400 static void vty_prompt(struct vty
*vty
)
402 if (vty
->type
== VTY_TERM
) {
403 vty_out(vty
, cmd_prompt(vty
->node
), cmd_hostname_get());
406 #pragma GCC diagnostic pop
408 /* Send WILL TELOPT_ECHO to remote server. */
409 static void vty_will_echo(struct vty
*vty
)
411 unsigned char cmd
[] = {IAC
, WILL
, TELOPT_ECHO
, '\0'};
412 vty_out(vty
, "%s", cmd
);
415 /* Make suppress Go-Ahead telnet option. */
416 static void vty_will_suppress_go_ahead(struct vty
*vty
)
418 unsigned char cmd
[] = {IAC
, WILL
, TELOPT_SGA
, '\0'};
419 vty_out(vty
, "%s", cmd
);
422 /* Make don't use linemode over telnet. */
423 static void vty_dont_linemode(struct vty
*vty
)
425 unsigned char cmd
[] = {IAC
, DONT
, TELOPT_LINEMODE
, '\0'};
426 vty_out(vty
, "%s", cmd
);
429 /* Use window size. */
430 static void vty_do_window_size(struct vty
*vty
)
432 unsigned char cmd
[] = {IAC
, DO
, TELOPT_NAWS
, '\0'};
433 vty_out(vty
, "%s", cmd
);
436 /* Authentication of vty */
437 static void vty_auth(struct vty
*vty
, char *buf
)
440 enum node_type next_node
= 0;
446 passwd
= host
.password_encrypt
;
448 passwd
= host
.password
;
450 next_node
= host
.enable
? VIEW_NODE
: ENABLE_NODE
;
452 next_node
= VIEW_NODE
;
454 case AUTH_ENABLE_NODE
:
456 passwd
= host
.enable_encrypt
;
458 passwd
= host
.enable
;
459 next_node
= ENABLE_NODE
;
465 fail
= strcmp(crypt(buf
, passwd
), passwd
);
467 fail
= strcmp(buf
, passwd
);
473 vty
->node
= next_node
; /* Success ! */
476 if (vty
->fail
>= 3) {
477 if (vty
->node
== AUTH_NODE
) {
479 "%% Bad passwords, too many failures!\n");
480 vty
->status
= VTY_CLOSE
;
482 /* AUTH_ENABLE_NODE */
485 "%% Bad enable passwords, too many failures!\n");
486 vty
->status
= VTY_CLOSE
;
492 /* Command execution over the vty interface. */
493 static int vty_command(struct vty
*vty
, char *buf
)
496 const char *protocolname
;
502 * Log non empty command lines
504 if (do_log_commands
&&
505 strncmp(buf
, "echo PING", strlen("echo PING")) != 0)
508 /* Skip white spaces. */
509 while (isspace((unsigned char)*cp
) && *cp
!= '\0')
512 if (cp
!= NULL
&& *cp
!= '\0') {
513 char vty_str
[VTY_BUFSIZ
];
514 char prompt_str
[VTY_BUFSIZ
];
516 /* format the base vty info */
517 snprintf(vty_str
, sizeof(vty_str
), "vty[%d]@%s", vty
->fd
,
520 /* format the prompt */
521 #pragma GCC diagnostic push
522 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
523 /* prompt formatting has a %s in the cmd_node prompt string */
524 snprintf(prompt_str
, sizeof(prompt_str
), cmd_prompt(vty
->node
),
526 #pragma GCC diagnostic pop
528 /* now log the command */
529 zlog_notice("%s%s", prompt_str
, buf
);
534 unsigned long walltime
, cputime
;
536 /* cmd_execute() may change cputime_enabled if we're executing the
537 * "service cputime-stats" command, which can result in nonsensical
538 * and very confusing warnings
540 bool cputime_enabled_here
= cputime_enabled
;
544 ret
= cmd_execute(vty
, buf
, NULL
, 0);
548 walltime
= event_consumed_time(&after
, &before
, &cputime
);
550 if (cputime_enabled_here
&& cputime_enabled
&& cputime_threshold
551 && cputime
> cputime_threshold
)
552 /* Warn about CPU hog that must be fixed. */
553 flog_warn(EC_LIB_SLOW_THREAD_CPU
,
554 "CPU HOG: command took %lums (cpu time %lums): %s",
555 walltime
/ 1000, cputime
/ 1000, buf
);
556 else if (walltime_threshold
&& walltime
> walltime_threshold
)
557 flog_warn(EC_LIB_SLOW_THREAD_WALL
,
558 "STARVATION: command took %lums (cpu time %lums): %s",
559 walltime
/ 1000, cputime
/ 1000, buf
);
561 /* Get the name of the protocol if any */
562 protocolname
= frr_protoname
;
564 if (ret
!= CMD_SUCCESS
)
567 if (vty
->type
== VTY_FILE
)
568 vty_out(vty
, "Warning...\n");
570 case CMD_ERR_AMBIGUOUS
:
571 vty_out(vty
, "%% Ambiguous command.\n");
573 case CMD_ERR_NO_MATCH
:
574 vty_out(vty
, "%% [%s] Unknown command: %s\n",
577 case CMD_ERR_INCOMPLETE
:
578 vty_out(vty
, "%% Command incomplete.\n");
585 static const char telnet_backward_char
= 0x08;
586 static const char telnet_space_char
= ' ';
588 /* Basic function to write buffer to vty. */
589 static void vty_write(struct vty
*vty
, const char *buf
, size_t nbytes
)
591 if ((vty
->node
== AUTH_NODE
) || (vty
->node
== AUTH_ENABLE_NODE
))
594 /* Should we do buffering here ? And make vty_flush (vty) ? */
595 buffer_put(vty
->obuf
, buf
, nbytes
);
598 /* Basic function to insert character into vty. */
599 static void vty_self_insert(struct vty
*vty
, char c
)
604 if (vty
->length
+ 1 >= VTY_BUFSIZ
)
607 length
= vty
->length
- vty
->cp
;
608 memmove(&vty
->buf
[vty
->cp
+ 1], &vty
->buf
[vty
->cp
], length
);
609 vty
->buf
[vty
->cp
] = c
;
611 vty_write(vty
, &vty
->buf
[vty
->cp
], length
+ 1);
612 for (i
= 0; i
< length
; i
++)
613 vty_write(vty
, &telnet_backward_char
, 1);
618 vty
->buf
[vty
->length
] = '\0';
621 /* Self insert character 'c' in overwrite mode. */
622 static void vty_self_insert_overwrite(struct vty
*vty
, char c
)
624 if (vty
->cp
== vty
->length
) {
625 vty_self_insert(vty
, c
);
629 vty
->buf
[vty
->cp
++] = c
;
630 vty_write(vty
, &c
, 1);
634 * Insert a string into vty->buf at the current cursor position.
636 * If the resultant string would be larger than VTY_BUFSIZ it is
639 static void vty_insert_word_overwrite(struct vty
*vty
, char *str
)
641 if (vty
->cp
== VTY_BUFSIZ
)
644 size_t nwrite
= MIN((int)strlen(str
), VTY_BUFSIZ
- vty
->cp
- 1);
645 memcpy(&vty
->buf
[vty
->cp
], str
, nwrite
);
647 vty
->length
= MAX(vty
->cp
, vty
->length
);
648 vty
->buf
[vty
->length
] = '\0';
649 vty_write(vty
, str
, nwrite
);
652 /* Forward character. */
653 static void vty_forward_char(struct vty
*vty
)
655 if (vty
->cp
< vty
->length
) {
656 vty_write(vty
, &vty
->buf
[vty
->cp
], 1);
661 /* Backward character. */
662 static void vty_backward_char(struct vty
*vty
)
666 vty_write(vty
, &telnet_backward_char
, 1);
670 /* Move to the beginning of the line. */
671 static void vty_beginning_of_line(struct vty
*vty
)
674 vty_backward_char(vty
);
677 /* Move to the end of the line. */
678 static void vty_end_of_line(struct vty
*vty
)
680 while (vty
->cp
< vty
->length
)
681 vty_forward_char(vty
);
684 static void vty_kill_line_from_beginning(struct vty
*);
685 static void vty_redraw_line(struct vty
*);
687 /* Print command line history. This function is called from
688 vty_next_line and vty_previous_line. */
689 static void vty_history_print(struct vty
*vty
)
693 vty_kill_line_from_beginning(vty
);
695 /* Get previous line from history buffer */
696 length
= strlen(vty
->hist
[vty
->hp
]);
697 memcpy(vty
->buf
, vty
->hist
[vty
->hp
], length
);
698 vty
->cp
= vty
->length
= length
;
699 vty
->buf
[vty
->length
] = '\0';
701 /* Redraw current line */
702 vty_redraw_line(vty
);
705 /* Show next command line history. */
706 static void vty_next_line(struct vty
*vty
)
710 if (vty
->hp
== vty
->hindex
)
713 /* Try is there history exist or not. */
715 if (try_index
== (VTY_MAXHIST
- 1))
720 /* If there is not history return. */
721 if (vty
->hist
[try_index
] == NULL
)
726 vty_history_print(vty
);
729 /* Show previous command line history. */
730 static void vty_previous_line(struct vty
*vty
)
736 try_index
= VTY_MAXHIST
- 1;
740 if (vty
->hist
[try_index
] == NULL
)
745 vty_history_print(vty
);
748 /* This function redraw all of the command line character. */
749 static void vty_redraw_line(struct vty
*vty
)
751 vty_write(vty
, vty
->buf
, vty
->length
);
752 vty
->cp
= vty
->length
;
756 static void vty_forward_word(struct vty
*vty
)
758 while (vty
->cp
!= vty
->length
&& vty
->buf
[vty
->cp
] != ' ')
759 vty_forward_char(vty
);
761 while (vty
->cp
!= vty
->length
&& vty
->buf
[vty
->cp
] == ' ')
762 vty_forward_char(vty
);
765 /* Backward word without skipping training space. */
766 static void vty_backward_pure_word(struct vty
*vty
)
768 while (vty
->cp
> 0 && vty
->buf
[vty
->cp
- 1] != ' ')
769 vty_backward_char(vty
);
773 static void vty_backward_word(struct vty
*vty
)
775 while (vty
->cp
> 0 && vty
->buf
[vty
->cp
- 1] == ' ')
776 vty_backward_char(vty
);
778 while (vty
->cp
> 0 && vty
->buf
[vty
->cp
- 1] != ' ')
779 vty_backward_char(vty
);
782 /* When '^D' is typed at the beginning of the line we move to the down
784 static void vty_down_level(struct vty
*vty
)
792 /* When '^Z' is received from vty, move down to the enable mode. */
793 static void vty_end_config(struct vty
*vty
)
798 vty_config_exit(vty
);
799 vty
->node
= ENABLE_NODE
;
806 /* Delete a character at the current point. */
807 static void vty_delete_char(struct vty
*vty
)
812 if (vty
->length
== 0) {
817 if (vty
->cp
== vty
->length
)
818 return; /* completion need here? */
820 size
= vty
->length
- vty
->cp
;
823 memmove(&vty
->buf
[vty
->cp
], &vty
->buf
[vty
->cp
+ 1], size
- 1);
824 vty
->buf
[vty
->length
] = '\0';
826 if (vty
->node
== AUTH_NODE
|| vty
->node
== AUTH_ENABLE_NODE
)
829 vty_write(vty
, &vty
->buf
[vty
->cp
], size
- 1);
830 vty_write(vty
, &telnet_space_char
, 1);
832 for (i
= 0; i
< size
; i
++)
833 vty_write(vty
, &telnet_backward_char
, 1);
836 /* Delete a character before the point. */
837 static void vty_delete_backward_char(struct vty
*vty
)
842 vty_backward_char(vty
);
843 vty_delete_char(vty
);
846 /* Kill rest of line from current point. */
847 static void vty_kill_line(struct vty
*vty
)
852 size
= vty
->length
- vty
->cp
;
857 for (i
= 0; i
< size
; i
++)
858 vty_write(vty
, &telnet_space_char
, 1);
859 for (i
= 0; i
< size
; i
++)
860 vty_write(vty
, &telnet_backward_char
, 1);
862 memset(&vty
->buf
[vty
->cp
], 0, size
);
863 vty
->length
= vty
->cp
;
866 /* Kill line from the beginning. */
867 static void vty_kill_line_from_beginning(struct vty
*vty
)
869 vty_beginning_of_line(vty
);
873 /* Delete a word before the point. */
874 static void vty_forward_kill_word(struct vty
*vty
)
876 while (vty
->cp
!= vty
->length
&& vty
->buf
[vty
->cp
] == ' ')
877 vty_delete_char(vty
);
878 while (vty
->cp
!= vty
->length
&& vty
->buf
[vty
->cp
] != ' ')
879 vty_delete_char(vty
);
882 /* Delete a word before the point. */
883 static void vty_backward_kill_word(struct vty
*vty
)
885 while (vty
->cp
> 0 && vty
->buf
[vty
->cp
- 1] == ' ')
886 vty_delete_backward_char(vty
);
887 while (vty
->cp
> 0 && vty
->buf
[vty
->cp
- 1] != ' ')
888 vty_delete_backward_char(vty
);
891 /* Transpose chars before or at the point. */
892 static void vty_transpose_chars(struct vty
*vty
)
896 /* If length is short or point is near by the beginning of line then
898 if (vty
->length
< 2 || vty
->cp
< 1)
901 /* In case of point is located at the end of the line. */
902 if (vty
->cp
== vty
->length
) {
903 c1
= vty
->buf
[vty
->cp
- 1];
904 c2
= vty
->buf
[vty
->cp
- 2];
906 vty_backward_char(vty
);
907 vty_backward_char(vty
);
908 vty_self_insert_overwrite(vty
, c1
);
909 vty_self_insert_overwrite(vty
, c2
);
911 c1
= vty
->buf
[vty
->cp
];
912 c2
= vty
->buf
[vty
->cp
- 1];
914 vty_backward_char(vty
);
915 vty_self_insert_overwrite(vty
, c1
);
916 vty_self_insert_overwrite(vty
, c2
);
920 /* Do completion at vty interface. */
921 static void vty_complete_command(struct vty
*vty
)
925 char **matched
= NULL
;
928 if (vty
->node
== AUTH_NODE
|| vty
->node
== AUTH_ENABLE_NODE
)
931 vline
= cmd_make_strvec(vty
->buf
);
935 /* In case of 'help \t'. */
936 if (isspace((unsigned char)vty
->buf
[vty
->length
- 1]))
937 vector_set(vline
, NULL
);
939 matched
= cmd_complete_command(vline
, vty
, &ret
);
941 cmd_free_strvec(vline
);
945 case CMD_ERR_AMBIGUOUS
:
946 vty_out(vty
, "%% Ambiguous command.\n");
948 vty_redraw_line(vty
);
950 case CMD_ERR_NO_MATCH
:
951 /* vty_out (vty, "%% There is no matched command.\n"); */
953 vty_redraw_line(vty
);
955 case CMD_COMPLETE_FULL_MATCH
:
957 /* 2016-11-28 equinox -- need to debug, SEGV here */
958 vty_out(vty
, "%% CLI BUG: FULL_MATCH with NULL str\n");
960 vty_redraw_line(vty
);
964 vty_redraw_line(vty
);
965 vty_backward_pure_word(vty
);
966 vty_insert_word_overwrite(vty
, matched
[0]);
967 vty_self_insert(vty
, ' ');
968 XFREE(MTYPE_COMPLETION
, matched
[0]);
970 case CMD_COMPLETE_MATCH
:
972 vty_redraw_line(vty
);
973 vty_backward_pure_word(vty
);
974 vty_insert_word_overwrite(vty
, matched
[0]);
975 XFREE(MTYPE_COMPLETION
, matched
[0]);
977 case CMD_COMPLETE_LIST_MATCH
:
978 for (i
= 0; matched
[i
] != NULL
; i
++) {
979 if (i
!= 0 && ((i
% 6) == 0))
981 vty_out(vty
, "%-10s ", matched
[i
]);
982 XFREE(MTYPE_COMPLETION
, matched
[i
]);
987 vty_redraw_line(vty
);
989 case CMD_ERR_NOTHING_TODO
:
991 vty_redraw_line(vty
);
996 XFREE(MTYPE_TMP
, matched
);
999 static void vty_describe_fold(struct vty
*vty
, int cmd_width
,
1000 unsigned int desc_width
, struct cmd_token
*token
)
1003 const char *cmd
, *p
;
1008 if (desc_width
<= 0) {
1009 vty_out(vty
, " %-*s %s\n", cmd_width
, cmd
, token
->desc
);
1013 buf
= XCALLOC(MTYPE_TMP
, strlen(token
->desc
) + 1);
1015 for (p
= token
->desc
; strlen(p
) > desc_width
; p
+= pos
+ 1) {
1016 for (pos
= desc_width
; pos
> 0; pos
--)
1017 if (*(p
+ pos
) == ' ')
1023 memcpy(buf
, p
, pos
);
1025 vty_out(vty
, " %-*s %s\n", cmd_width
, cmd
, buf
);
1030 vty_out(vty
, " %-*s %s\n", cmd_width
, cmd
, p
);
1032 XFREE(MTYPE_TMP
, buf
);
1035 /* Describe matched command function. */
1036 static void vty_describe_command(struct vty
*vty
)
1041 unsigned int i
, width
, desc_width
;
1042 struct cmd_token
*token
, *token_cr
= NULL
;
1044 vline
= cmd_make_strvec(vty
->buf
);
1046 /* In case of '> ?'. */
1047 if (vline
== NULL
) {
1048 vline
= vector_init(1);
1049 vector_set(vline
, NULL
);
1050 } else if (isspace((unsigned char)vty
->buf
[vty
->length
- 1]))
1051 vector_set(vline
, NULL
);
1053 describe
= cmd_describe_command(vline
, vty
, &ret
);
1057 /* Ambiguous error. */
1059 case CMD_ERR_AMBIGUOUS
:
1060 vty_out(vty
, "%% Ambiguous command.\n");
1063 case CMD_ERR_NO_MATCH
:
1064 vty_out(vty
, "%% There is no matched command.\n");
1069 /* Get width of command string. */
1071 for (i
= 0; i
< vector_active(describe
); i
++)
1072 if ((token
= vector_slot(describe
, i
)) != NULL
) {
1075 if (token
->text
[0] == '\0')
1078 len
= strlen(token
->text
);
1084 /* Get width of description string. */
1085 desc_width
= vty
->width
- (width
+ 6);
1087 /* Print out description. */
1088 for (i
= 0; i
< vector_active(describe
); i
++)
1089 if ((token
= vector_slot(describe
, i
)) != NULL
) {
1090 if (token
->text
[0] == '\0')
1093 if (strcmp(token
->text
, CMD_CR_TEXT
) == 0) {
1099 vty_out(vty
, " %-s\n", token
->text
);
1100 else if (desc_width
>= strlen(token
->desc
))
1101 vty_out(vty
, " %-*s %s\n", width
, token
->text
,
1104 vty_describe_fold(vty
, width
, desc_width
,
1107 if (IS_VARYING_TOKEN(token
->type
)) {
1108 const char *ref
= vector_slot(
1109 vline
, vector_active(vline
) - 1);
1111 vector varcomps
= vector_init(VECTOR_MIN_SIZE
);
1112 cmd_variable_complete(token
, ref
, varcomps
);
1114 if (vector_active(varcomps
) > 0) {
1115 char *ac
= cmd_variable_comp2str(
1116 varcomps
, vty
->width
);
1117 vty_out(vty
, "%s\n", ac
);
1118 XFREE(MTYPE_TMP
, ac
);
1121 vector_free(varcomps
);
1125 if ((token
= token_cr
)) {
1127 vty_out(vty
, " %-s\n", token
->text
);
1128 else if (desc_width
>= strlen(token
->desc
))
1129 vty_out(vty
, " %-*s %s\n", width
, token
->text
,
1132 vty_describe_fold(vty
, width
, desc_width
, token
);
1136 cmd_free_strvec(vline
);
1138 vector_free(describe
);
1141 vty_redraw_line(vty
);
1144 static void vty_clear_buf(struct vty
*vty
)
1146 memset(vty
->buf
, 0, vty
->max
);
1149 /* ^C stop current input and do not add command line to the history. */
1150 static void vty_stop_input(struct vty
*vty
)
1152 vty
->cp
= vty
->length
= 0;
1157 vty_config_exit(vty
);
1158 vty
->node
= ENABLE_NODE
;
1163 /* Set history pointer to the latest one. */
1164 vty
->hp
= vty
->hindex
;
1167 /* Add current command line to the history buffer. */
1168 static void vty_hist_add(struct vty
*vty
)
1172 if (vty
->length
== 0)
1175 index
= vty
->hindex
? vty
->hindex
- 1 : VTY_MAXHIST
- 1;
1177 /* Ignore the same string as previous one. */
1178 if (vty
->hist
[index
])
1179 if (strcmp(vty
->buf
, vty
->hist
[index
]) == 0) {
1180 vty
->hp
= vty
->hindex
;
1184 /* Insert history entry. */
1185 XFREE(MTYPE_VTY_HIST
, vty
->hist
[vty
->hindex
]);
1186 vty
->hist
[vty
->hindex
] = XSTRDUP(MTYPE_VTY_HIST
, vty
->buf
);
1188 /* History index rotation. */
1190 if (vty
->hindex
== VTY_MAXHIST
)
1193 vty
->hp
= vty
->hindex
;
1196 /* #define TELNET_OPTION_DEBUG */
1198 /* Get telnet window size. */
1199 static int vty_telnet_option(struct vty
*vty
, unsigned char *buf
, int nbytes
)
1201 #ifdef TELNET_OPTION_DEBUG
1204 for (i
= 0; i
< nbytes
; i
++) {
1207 vty_out(vty
, "IAC ");
1210 vty_out(vty
, "WILL ");
1213 vty_out(vty
, "WONT ");
1216 vty_out(vty
, "DO ");
1219 vty_out(vty
, "DONT ");
1222 vty_out(vty
, "SB ");
1225 vty_out(vty
, "SE ");
1228 vty_out(vty
, "TELOPT_ECHO \n");
1231 vty_out(vty
, "TELOPT_SGA \n");
1234 vty_out(vty
, "TELOPT_NAWS \n");
1237 vty_out(vty
, "%x ", buf
[i
]);
1243 #endif /* TELNET_OPTION_DEBUG */
1248 vty
->iac_sb_in_progress
= 1;
1251 if (!vty
->iac_sb_in_progress
)
1254 if ((vty
->sb_len
== 0) || (vty
->sb_buf
[0] == '\0')) {
1255 vty
->iac_sb_in_progress
= 0;
1258 switch (vty
->sb_buf
[0]) {
1260 if (vty
->sb_len
!= TELNET_NAWS_SB_LEN
)
1263 "RFC 1073 violation detected: telnet NAWS option should send %d characters, but we received %lu",
1265 (unsigned long)vty
->sb_len
);
1266 else if (sizeof(vty
->sb_buf
) < TELNET_NAWS_SB_LEN
)
1269 "Bug detected: sizeof(vty->sb_buf) %lu < %d, too small to handle the telnet NAWS option",
1270 (unsigned long)sizeof(vty
->sb_buf
),
1271 TELNET_NAWS_SB_LEN
);
1273 vty
->width
= ((vty
->sb_buf
[1] << 8)
1275 vty
->height
= ((vty
->sb_buf
[3] << 8)
1277 #ifdef TELNET_OPTION_DEBUG
1279 "TELNET NAWS window size negotiation completed: width %d, height %d\n",
1280 vty
->width
, vty
->height
);
1285 vty
->iac_sb_in_progress
= 0;
1294 /* Execute current command line. */
1295 static int vty_execute(struct vty
*vty
)
1301 switch (vty
->node
) {
1303 case AUTH_ENABLE_NODE
:
1304 vty_auth(vty
, vty
->buf
);
1307 ret
= vty_command(vty
, vty
->buf
);
1308 if (vty
->type
== VTY_TERM
)
1313 /* Clear command line buffer. */
1314 vty
->cp
= vty
->length
= 0;
1317 if (vty
->status
!= VTY_CLOSE
)
1323 #define CONTROL(X) ((X) - '@')
1324 #define VTY_NORMAL 0
1325 #define VTY_PRE_ESCAPE 1
1326 #define VTY_ESCAPE 2
1329 /* Escape character command map. */
1330 static void vty_escape_map(unsigned char c
, struct vty
*vty
)
1334 vty_previous_line(vty
);
1340 vty_forward_char(vty
);
1343 vty_backward_char(vty
);
1349 /* Go back to normal mode. */
1350 vty
->escape
= VTY_NORMAL
;
1353 /* Quit print out to the buffer. */
1354 static void vty_buffer_reset(struct vty
*vty
)
1356 buffer_reset(vty
->obuf
);
1357 buffer_reset(vty
->lbuf
);
1359 vty_redraw_line(vty
);
1362 /* Read data via vty socket. */
1363 static void vty_read(struct event
*thread
)
1367 unsigned char buf
[VTY_READ_BUFSIZ
];
1369 struct vty
*vty
= EVENT_ARG(thread
);
1371 /* Read raw data from socket */
1372 if ((nbytes
= read(vty
->fd
, buf
, VTY_READ_BUFSIZ
)) <= 0) {
1374 if (ERRNO_IO_RETRY(errno
)) {
1375 vty_event(VTY_READ
, vty
);
1380 "%s: read error on vty client fd %d, closing: %s",
1381 __func__
, vty
->fd
, safe_strerror(errno
));
1382 buffer_reset(vty
->obuf
);
1383 buffer_reset(vty
->lbuf
);
1385 vty
->status
= VTY_CLOSE
;
1388 for (i
= 0; i
< nbytes
; i
++) {
1389 if (buf
[i
] == IAC
) {
1398 if (vty
->iac_sb_in_progress
&& !vty
->iac
) {
1399 if (vty
->sb_len
< sizeof(vty
->sb_buf
))
1400 vty
->sb_buf
[vty
->sb_len
] = buf
[i
];
1406 /* In case of telnet command */
1408 ret
= vty_telnet_option(vty
, buf
+ i
, nbytes
- i
);
1415 if (vty
->status
== VTY_MORE
) {
1420 vty_buffer_reset(vty
);
1428 /* Escape character. */
1429 if (vty
->escape
== VTY_ESCAPE
) {
1430 vty_escape_map(buf
[i
], vty
);
1434 /* Pre-escape status. */
1435 if (vty
->escape
== VTY_PRE_ESCAPE
) {
1438 vty
->escape
= VTY_ESCAPE
;
1441 vty_backward_word(vty
);
1442 vty
->escape
= VTY_NORMAL
;
1445 vty_forward_word(vty
);
1446 vty
->escape
= VTY_NORMAL
;
1449 vty_forward_kill_word(vty
);
1450 vty
->escape
= VTY_NORMAL
;
1454 vty_backward_kill_word(vty
);
1455 vty
->escape
= VTY_NORMAL
;
1458 vty
->escape
= VTY_NORMAL
;
1464 if (vty
->escape
== VTY_CR
) {
1465 /* if we get CR+NL, the NL results in an extra empty
1466 * prompt line being printed without this; just drop
1467 * the NL if it immediately follows CR.
1469 vty
->escape
= VTY_NORMAL
;
1477 vty_beginning_of_line(vty
);
1480 vty_backward_char(vty
);
1483 vty_stop_input(vty
);
1486 vty_delete_char(vty
);
1489 vty_end_of_line(vty
);
1492 vty_forward_char(vty
);
1496 vty_delete_backward_char(vty
);
1505 vty_previous_line(vty
);
1508 vty_transpose_chars(vty
);
1511 vty_kill_line_from_beginning(vty
);
1514 vty_backward_kill_word(vty
);
1517 vty_end_config(vty
);
1520 vty
->escape
= VTY_CR
;
1524 buffer_flush_available(vty
->obuf
, vty
->wfd
);
1527 if (vty
->pass_fd
!= -1) {
1528 close(vty
->pass_fd
);
1533 vty_complete_command(vty
);
1536 if (vty
->node
== AUTH_NODE
1537 || vty
->node
== AUTH_ENABLE_NODE
)
1538 vty_self_insert(vty
, buf
[i
]);
1540 vty_describe_command(vty
);
1543 if (i
+ 1 < nbytes
&& buf
[i
+ 1] == '[') {
1544 vty
->escape
= VTY_ESCAPE
;
1547 vty
->escape
= VTY_PRE_ESCAPE
;
1550 if (buf
[i
] > 31 && buf
[i
] < 127)
1551 vty_self_insert(vty
, buf
[i
]);
1557 if (vty
->status
== VTY_CLOSE
)
1560 vty_event(VTY_WRITE
, vty
);
1561 vty_event(VTY_READ
, vty
);
1565 /* Flush buffer to the vty. */
1566 static void vty_flush(struct event
*thread
)
1569 buffer_status_t flushrc
;
1570 struct vty
*vty
= EVENT_ARG(thread
);
1572 /* Tempolary disable read thread. */
1573 if (vty
->lines
== 0)
1574 EVENT_OFF(vty
->t_read
);
1576 /* Function execution continue. */
1577 erase
= ((vty
->status
== VTY_MORE
|| vty
->status
== VTY_MORELINE
));
1579 /* N.B. if width is 0, that means we don't know the window size. */
1580 if ((vty
->lines
== 0) || (vty
->width
== 0) || (vty
->height
== 0))
1581 flushrc
= buffer_flush_available(vty
->obuf
, vty
->wfd
);
1582 else if (vty
->status
== VTY_MORELINE
)
1583 flushrc
= buffer_flush_window(vty
->obuf
, vty
->wfd
, vty
->width
,
1586 flushrc
= buffer_flush_window(
1587 vty
->obuf
, vty
->wfd
, vty
->width
,
1588 vty
->lines
>= 0 ? vty
->lines
: vty
->height
, erase
, 0);
1591 zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
1593 buffer_reset(vty
->lbuf
);
1594 buffer_reset(vty
->obuf
);
1598 if (vty
->status
== VTY_CLOSE
)
1601 vty
->status
= VTY_NORMAL
;
1602 if (vty
->lines
== 0)
1603 vty_event(VTY_READ
, vty
);
1606 case BUFFER_PENDING
:
1607 /* There is more data waiting to be written. */
1608 vty
->status
= VTY_MORE
;
1609 if (vty
->lines
== 0)
1610 vty_event(VTY_WRITE
, vty
);
1615 /* Allocate new vty struct. */
1616 struct vty
*vty_new(void)
1618 struct vty
*new = XCALLOC(MTYPE_VTY
, sizeof(struct vty
));
1620 new->fd
= new->wfd
= -1;
1622 new->lbuf
= buffer_new(0);
1623 new->obuf
= buffer_new(0); /* Use default buffer size. */
1624 new->buf
= XCALLOC(MTYPE_VTY
, VTY_BUFSIZ
);
1625 new->max
= VTY_BUFSIZ
;
1628 if (mgmt_lib_hndl
) {
1629 new->mgmt_client_id
= mgmt_client_id_next
++;
1630 if (mgmt_fe_create_client_session(
1631 mgmt_lib_hndl
, new->mgmt_client_id
,
1632 (uintptr_t) new) != MGMTD_SUCCESS
)
1634 "Failed to open a MGMTD Frontend session for VTY session %p!!",
1642 /* allocate and initialise vty */
1643 static struct vty
*vty_new_init(int vty_sock
)
1649 vty
->wfd
= vty_sock
;
1650 vty
->type
= VTY_TERM
;
1651 vty
->node
= AUTH_NODE
;
1656 memset(vty
->hist
, 0, sizeof(vty
->hist
));
1659 vty
->xpath_index
= 0;
1660 memset(vty
->xpath
, 0, sizeof(vty
->xpath
));
1661 vty
->private_config
= false;
1662 vty
->candidate_config
= vty_shared_candidate_config
;
1663 vty
->status
= VTY_NORMAL
;
1666 vty
->iac_sb_in_progress
= 0;
1669 vtys_add_tail(vty_sessions
, vty
);
1674 /* Create new vty structure. */
1675 static struct vty
*vty_create(int vty_sock
, union sockunion
*su
)
1677 char buf
[SU_ADDRSTRLEN
];
1680 sockunion2str(su
, buf
, SU_ADDRSTRLEN
);
1682 /* Allocate new vty structure and set up default values. */
1683 vty
= vty_new_init(vty_sock
);
1685 /* configurable parameters not part of basic init */
1686 vty
->v_timeout
= vty_timeout_val
;
1687 strlcpy(vty
->address
, buf
, sizeof(vty
->address
));
1688 if (no_password_check
) {
1690 vty
->node
= ENABLE_NODE
;
1692 vty
->node
= VIEW_NODE
;
1694 if (host
.lines
>= 0)
1695 vty
->lines
= host
.lines
;
1697 if (!no_password_check
) {
1698 /* Vty is not available if password isn't set. */
1699 if (host
.password
== NULL
&& host
.password_encrypt
== NULL
) {
1700 vty_out(vty
, "Vty password is not set.\n");
1701 vty
->status
= VTY_CLOSE
;
1707 /* Say hello to the world. */
1709 if (!no_password_check
)
1710 vty_out(vty
, "\nUser Access Verification\n\n");
1712 /* Setting up terminal. */
1714 vty_will_suppress_go_ahead(vty
);
1716 vty_dont_linemode(vty
);
1717 vty_do_window_size(vty
);
1718 /* vty_dont_lflow_ahead (vty); */
1722 /* Add read/write thread. */
1723 vty_event(VTY_WRITE
, vty
);
1724 vty_event(VTY_READ
, vty
);
1729 /* create vty for stdio */
1730 static struct termios stdio_orig_termios
;
1731 static struct vty
*stdio_vty
= NULL
;
1732 static bool stdio_termios
= false;
1733 static void (*stdio_vty_atclose
)(int isexit
);
1735 static void vty_stdio_reset(int isexit
)
1739 tcsetattr(0, TCSANOW
, &stdio_orig_termios
);
1740 stdio_termios
= false;
1744 if (stdio_vty_atclose
)
1745 stdio_vty_atclose(isexit
);
1746 stdio_vty_atclose
= NULL
;
1750 static void vty_stdio_atexit(void)
1755 void vty_stdio_suspend(void)
1760 EVENT_OFF(stdio_vty
->t_write
);
1761 EVENT_OFF(stdio_vty
->t_read
);
1762 EVENT_OFF(stdio_vty
->t_timeout
);
1765 tcsetattr(0, TCSANOW
, &stdio_orig_termios
);
1766 stdio_termios
= false;
1769 void vty_stdio_resume(void)
1774 if (!tcgetattr(0, &stdio_orig_termios
)) {
1775 struct termios termios
;
1777 termios
= stdio_orig_termios
;
1778 termios
.c_iflag
&= ~(IGNBRK
| BRKINT
| PARMRK
| ISTRIP
| INLCR
1779 | IGNCR
| ICRNL
| IXON
);
1780 termios
.c_lflag
&= ~(ECHO
| ECHONL
| ICANON
| IEXTEN
);
1781 termios
.c_cflag
&= ~(CSIZE
| PARENB
);
1782 termios
.c_cflag
|= CS8
;
1783 tcsetattr(0, TCSANOW
, &termios
);
1784 stdio_termios
= true;
1787 vty_prompt(stdio_vty
);
1789 /* Add read/write thread. */
1790 vty_event(VTY_WRITE
, stdio_vty
);
1791 vty_event(VTY_READ
, stdio_vty
);
1794 void vty_stdio_close(void)
1798 vty_close(stdio_vty
);
1801 struct vty
*vty_stdio(void (*atclose
)(int isexit
))
1805 /* refuse creating two vtys on stdio */
1809 vty
= stdio_vty
= vty_new_init(0);
1810 stdio_vty_atclose
= atclose
;
1813 /* always have stdio vty in a known _unchangeable_ state, don't want
1815 * to have any effect here to make sure scripting this works as intended
1817 vty
->node
= ENABLE_NODE
;
1819 strlcpy(vty
->address
, "console", sizeof(vty
->address
));
1825 /* Accept connection from the network. */
1826 static void vty_accept(struct event
*thread
)
1828 struct vty_serv
*vtyserv
= EVENT_ARG(thread
);
1833 int accept_sock
= vtyserv
->sock
;
1835 struct access_list
*acl
= NULL
;
1837 /* We continue hearing vty socket. */
1838 vty_event_serv(VTY_SERV
, vtyserv
);
1840 memset(&su
, 0, sizeof(union sockunion
));
1842 /* We can handle IPv4 or IPv6 socket. */
1843 vty_sock
= sockunion_accept(accept_sock
, &su
);
1845 flog_err(EC_LIB_SOCKET
, "can't accept vty socket : %s",
1846 safe_strerror(errno
));
1849 set_nonblocking(vty_sock
);
1850 set_cloexec(vty_sock
);
1852 if (!sockunion2hostprefix(&su
, &p
)) {
1854 zlog_info("Vty unable to convert prefix from sockunion %pSU",
1859 /* VTY's accesslist apply. */
1860 if (p
.family
== AF_INET
&& vty_accesslist_name
) {
1861 if ((acl
= access_list_lookup(AFI_IP
, vty_accesslist_name
))
1862 && (access_list_apply(acl
, &p
) == FILTER_DENY
)) {
1863 zlog_info("Vty connection refused from %pSU", &su
);
1869 /* VTY's ipv6 accesslist apply. */
1870 if (p
.family
== AF_INET6
&& vty_ipv6_accesslist_name
) {
1871 if ((acl
= access_list_lookup(AFI_IP6
,
1872 vty_ipv6_accesslist_name
))
1873 && (access_list_apply(acl
, &p
) == FILTER_DENY
)) {
1874 zlog_info("Vty connection refused from %pSU", &su
);
1881 ret
= setsockopt(vty_sock
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&on
,
1884 zlog_info("can't set sockopt to vty_sock : %s",
1885 safe_strerror(errno
));
1887 zlog_info("Vty connection from %pSU", &su
);
1889 vty_create(vty_sock
, &su
);
1892 static void vty_serv_sock_addrinfo(const char *hostname
, unsigned short port
)
1895 struct addrinfo req
;
1896 struct addrinfo
*ainfo
;
1897 struct addrinfo
*ainfo_save
;
1899 char port_str
[BUFSIZ
];
1901 memset(&req
, 0, sizeof(req
));
1902 req
.ai_flags
= AI_PASSIVE
;
1903 req
.ai_family
= AF_UNSPEC
;
1904 req
.ai_socktype
= SOCK_STREAM
;
1905 snprintf(port_str
, sizeof(port_str
), "%d", port
);
1906 port_str
[sizeof(port_str
) - 1] = '\0';
1908 ret
= getaddrinfo(hostname
, port_str
, &req
, &ainfo
);
1911 flog_err_sys(EC_LIB_SYSTEM_CALL
, "getaddrinfo failed: %s",
1919 struct vty_serv
*vtyserv
;
1921 if (ainfo
->ai_family
!= AF_INET
&& ainfo
->ai_family
!= AF_INET6
)
1924 sock
= socket(ainfo
->ai_family
, ainfo
->ai_socktype
,
1925 ainfo
->ai_protocol
);
1929 sockopt_v6only(ainfo
->ai_family
, sock
);
1930 sockopt_reuseaddr(sock
);
1931 sockopt_reuseport(sock
);
1934 ret
= bind(sock
, ainfo
->ai_addr
, ainfo
->ai_addrlen
);
1936 close(sock
); /* Avoid sd leak. */
1940 ret
= listen(sock
, 3);
1942 close(sock
); /* Avoid sd leak. */
1946 vtyserv
= XCALLOC(MTYPE_VTY_SERV
, sizeof(*vtyserv
));
1947 vtyserv
->sock
= sock
;
1948 vtyservs_add_tail(vty_servs
, vtyserv
);
1950 vty_event_serv(VTY_SERV
, vtyserv
);
1951 } while ((ainfo
= ainfo
->ai_next
) != NULL
);
1953 freeaddrinfo(ainfo_save
);
1957 /* For sockaddr_un. */
1960 /* VTY shell UNIX domain socket. */
1961 static void vty_serv_un(const char *path
)
1963 struct vty_serv
*vtyserv
;
1966 struct sockaddr_un serv
;
1968 struct zprivs_ids_t ids
;
1970 /* First of all, unlink existing socket */
1974 old_mask
= umask(0007);
1976 /* Make UNIX domain socket. */
1977 sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
1979 flog_err_sys(EC_LIB_SOCKET
,
1980 "Cannot create unix stream socket: %s",
1981 safe_strerror(errno
));
1985 /* Make server socket. */
1986 memset(&serv
, 0, sizeof(serv
));
1987 serv
.sun_family
= AF_UNIX
;
1988 strlcpy(serv
.sun_path
, path
, sizeof(serv
.sun_path
));
1989 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
1990 len
= serv
.sun_len
= SUN_LEN(&serv
);
1992 len
= sizeof(serv
.sun_family
) + strlen(serv
.sun_path
);
1993 #endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
1997 ret
= bind(sock
, (struct sockaddr
*)&serv
, len
);
1999 flog_err_sys(EC_LIB_SOCKET
, "Cannot bind path %s: %s", path
,
2000 safe_strerror(errno
));
2001 close(sock
); /* Avoid sd leak. */
2005 ret
= listen(sock
, 5);
2007 flog_err_sys(EC_LIB_SOCKET
, "listen(fd %d) failed: %s", sock
,
2008 safe_strerror(errno
));
2009 close(sock
); /* Avoid sd leak. */
2015 zprivs_get_ids(&ids
);
2017 /* Hack: ids.gid_vty is actually a uint, but we stored -1 in it
2018 earlier for the case when we don't need to chown the file
2019 type casting it here to make a compare */
2020 if ((int)ids
.gid_vty
> 0) {
2021 /* set group of socket */
2022 if (chown(path
, -1, ids
.gid_vty
)) {
2023 flog_err_sys(EC_LIB_SYSTEM_CALL
,
2024 "vty_serv_un: could chown socket, %s",
2025 safe_strerror(errno
));
2029 vtyserv
= XCALLOC(MTYPE_VTY_SERV
, sizeof(*vtyserv
));
2030 vtyserv
->sock
= sock
;
2031 vtyserv
->vtysh
= true;
2032 vtyservs_add_tail(vty_servs
, vtyserv
);
2034 vty_event_serv(VTYSH_SERV
, vtyserv
);
2037 /* #define VTYSH_DEBUG 1 */
2039 static void vtysh_accept(struct event
*thread
)
2041 struct vty_serv
*vtyserv
= EVENT_ARG(thread
);
2042 int accept_sock
= vtyserv
->sock
;
2045 struct sockaddr_un client
;
2048 vty_event_serv(VTYSH_SERV
, vtyserv
);
2050 memset(&client
, 0, sizeof(client
));
2051 client_len
= sizeof(struct sockaddr_un
);
2053 sock
= accept(accept_sock
, (struct sockaddr
*)&client
,
2054 (socklen_t
*)&client_len
);
2057 flog_err(EC_LIB_SOCKET
, "can't accept vty socket : %s",
2058 safe_strerror(errno
));
2062 if (set_nonblocking(sock
) < 0) {
2065 "vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
2066 sock
, safe_strerror(errno
));
2073 printf("VTY shell accept\n");
2074 #endif /* VTYSH_DEBUG */
2079 vty
->type
= VTY_SHELL_SERV
;
2080 vty
->node
= VIEW_NODE
;
2081 vtys_add_tail(vtysh_sessions
, vty
);
2083 vty_event(VTYSH_READ
, vty
);
2086 static int vtysh_do_pass_fd(struct vty
*vty
)
2088 struct iovec iov
[1] = {
2090 .iov_base
= vty
->pass_fd_status
,
2091 .iov_len
= sizeof(vty
->pass_fd_status
),
2095 uint8_t buf
[CMSG_SPACE(sizeof(int))];
2096 struct cmsghdr align
;
2098 struct msghdr mh
= {
2100 .msg_iovlen
= array_size(iov
),
2101 .msg_control
= u
.buf
,
2102 .msg_controllen
= sizeof(u
.buf
),
2104 struct cmsghdr
*cmh
= CMSG_FIRSTHDR(&mh
);
2107 memset(&u
.buf
, 0, sizeof(u
.buf
));
2108 cmh
->cmsg_level
= SOL_SOCKET
;
2109 cmh
->cmsg_type
= SCM_RIGHTS
;
2110 cmh
->cmsg_len
= CMSG_LEN(sizeof(int));
2111 memcpy(CMSG_DATA(cmh
), &vty
->pass_fd
, sizeof(int));
2113 ret
= sendmsg(vty
->wfd
, &mh
, 0);
2114 if (ret
< 0 && ERRNO_IO_RETRY(errno
))
2115 return BUFFER_PENDING
;
2117 close(vty
->pass_fd
);
2119 vty
->status
= VTY_NORMAL
;
2122 return BUFFER_ERROR
;
2124 /* resume accepting commands (suspended in vtysh_read) */
2125 vty_event(VTYSH_READ
, vty
);
2127 if ((size_t)ret
< sizeof(vty
->pass_fd_status
)) {
2128 size_t remains
= sizeof(vty
->pass_fd_status
) - ret
;
2130 buffer_put(vty
->obuf
, vty
->pass_fd_status
+ ret
, remains
);
2131 return BUFFER_PENDING
;
2133 return BUFFER_EMPTY
;
2136 static int vtysh_flush(struct vty
*vty
)
2140 ret
= buffer_flush_available(vty
->obuf
, vty
->wfd
);
2141 if (ret
== BUFFER_EMPTY
&& vty
->status
== VTY_PASSFD
)
2142 ret
= vtysh_do_pass_fd(vty
);
2145 case BUFFER_PENDING
:
2146 vty_event(VTYSH_WRITE
, vty
);
2149 flog_err(EC_LIB_SOCKET
, "%s: write error to fd %d, closing",
2151 buffer_reset(vty
->lbuf
);
2152 buffer_reset(vty
->obuf
);
2161 void vty_pass_fd(struct vty
*vty
, int fd
)
2163 if (vty
->pass_fd
!= -1)
2164 close(vty
->pass_fd
);
2169 static void vtysh_read(struct event
*thread
)
2175 unsigned char buf
[VTY_READ_BUFSIZ
];
2177 uint8_t header
[4] = {0, 0, 0, 0};
2179 sock
= EVENT_FD(thread
);
2180 vty
= EVENT_ARG(thread
);
2182 if ((nbytes
= read(sock
, buf
, VTY_READ_BUFSIZ
)) <= 0) {
2184 if (ERRNO_IO_RETRY(errno
)) {
2185 vty_event(VTYSH_READ
, vty
);
2190 "%s: read failed on vtysh client fd %d, closing: %s",
2191 __func__
, sock
, safe_strerror(errno
));
2193 buffer_reset(vty
->lbuf
);
2194 buffer_reset(vty
->obuf
);
2197 printf("close vtysh\n");
2198 #endif /* VTYSH_DEBUG */
2203 printf("line: %.*s\n", nbytes
, buf
);
2204 #endif /* VTYSH_DEBUG */
2206 if (vty
->length
+ nbytes
>= VTY_BUFSIZ
) {
2207 /* Clear command line buffer. */
2208 vty
->cp
= vty
->length
= 0;
2210 vty_out(vty
, "%% Command is too long.\n");
2212 for (p
= buf
; p
< buf
+ nbytes
; p
++) {
2213 vty
->buf
[vty
->length
++] = *p
;
2215 /* Pass this line to parser. */
2216 ret
= vty_execute(vty
);
2217 /* Note that vty_execute clears the command buffer and resets
2218 vty->length to 0. */
2220 /* Return result. */
2222 printf("result: %d\n", ret
);
2223 printf("vtysh node: %d\n", vty
->node
);
2224 #endif /* VTYSH_DEBUG */
2226 if (vty
->pass_fd
!= -1) {
2227 memset(vty
->pass_fd_status
, 0, 4);
2228 vty
->pass_fd_status
[3] = ret
;
2229 vty
->status
= VTY_PASSFD
;
2232 vty_event(VTYSH_WRITE
, vty
);
2234 /* this introduces a "sequence point"
2235 * command output is written normally,
2236 * read processing is suspended until
2238 * then retcode + FD is written
2239 * then normal processing resumes
2241 * => skip vty_event(VTYSH_READ, vty)!
2246 /* hack for asynchronous "write integrated"
2247 * - other commands in "buf" will be ditched
2248 * - input during pending config-write is
2250 if (ret
== CMD_SUSPEND
)
2253 /* with new infra we need to stop response till
2254 * we get response through callback.
2256 if (vty
->mgmt_req_pending
)
2259 /* warning: watchfrr hardcodes this result write
2262 buffer_put(vty
->obuf
, header
, 4);
2264 if (!vty
->t_write
&& (vtysh_flush(vty
) < 0))
2265 /* Try to flush results; exit if a write
2272 if (vty
->status
== VTY_CLOSE
)
2275 vty_event(VTYSH_READ
, vty
);
2278 static void vtysh_write(struct event
*thread
)
2280 struct vty
*vty
= EVENT_ARG(thread
);
2287 /* Determine address family to bind. */
2288 void vty_serv_sock(const char *addr
, unsigned short port
, const char *path
)
2290 /* If port is set to 0, do not listen on TCP/IP at all! */
2292 vty_serv_sock_addrinfo(addr
, port
);
2299 static void vty_error_delete(void *arg
)
2301 struct vty_error
*ve
= arg
;
2303 XFREE(MTYPE_TMP
, ve
);
2306 /* Close vty interface. Warning: call this only from functions that
2307 will be careful not to access the vty afterwards (since it has
2308 now been freed). This is safest from top-level functions (called
2309 directly by the thread dispatcher). */
2310 void vty_close(struct vty
*vty
)
2313 bool was_stdio
= false;
2315 if (mgmt_lib_hndl
) {
2316 mgmt_fe_destroy_client_session(mgmt_lib_hndl
,
2317 vty
->mgmt_client_id
);
2318 vty
->mgmt_session_id
= 0;
2321 /* Drop out of configure / transaction if needed. */
2322 vty_config_exit(vty
);
2324 /* Cancel threads.*/
2325 EVENT_OFF(vty
->t_read
);
2326 EVENT_OFF(vty
->t_write
);
2327 EVENT_OFF(vty
->t_timeout
);
2329 if (vty
->pass_fd
!= -1) {
2330 close(vty
->pass_fd
);
2333 zlog_live_close(&vty
->live_log
);
2336 buffer_flush_all(vty
->obuf
, vty
->wfd
);
2338 /* Free input buffer. */
2339 buffer_free(vty
->obuf
);
2340 buffer_free(vty
->lbuf
);
2342 /* Free command history. */
2343 for (i
= 0; i
< VTY_MAXHIST
; i
++) {
2344 XFREE(MTYPE_VTY_HIST
, vty
->hist
[i
]);
2348 if (vty
->fd
!= -1) {
2349 if (vty
->type
== VTY_SHELL_SERV
)
2350 vtys_del(vtysh_sessions
, vty
);
2352 vtys_del(vty_sessions
, vty
);
2355 if (vty
->wfd
> 0 && vty
->type
== VTY_FILE
)
2359 * note check is for fd > STDERR_FILENO, not fd != -1.
2360 * We never close stdin/stdout/stderr here, because we may be
2361 * running in foreground mode with logging to stdout. Also,
2362 * additionally, we'd need to replace these fds with /dev/null. */
2363 if (vty
->wfd
> STDERR_FILENO
&& vty
->wfd
!= vty
->fd
)
2365 if (vty
->fd
> STDERR_FILENO
)
2367 if (vty
->fd
== STDIN_FILENO
)
2370 XFREE(MTYPE_VTY
, vty
->buf
);
2373 vty
->error
->del
= vty_error_delete
;
2374 list_delete(&vty
->error
);
2378 XFREE(MTYPE_VTY
, vty
);
2384 /* When time out occur output message then close connection. */
2385 static void vty_timeout(struct event
*thread
)
2389 vty
= EVENT_ARG(thread
);
2393 buffer_reset(vty
->lbuf
);
2394 buffer_reset(vty
->obuf
);
2395 vty_out(vty
, "\nVty connection is timed out.\n");
2397 /* Close connection. */
2398 vty
->status
= VTY_CLOSE
;
2402 /* Read up configuration file from file_name. */
2403 void vty_read_file(struct nb_config
*config
, FILE *confp
)
2407 struct vty_error
*ve
;
2408 struct listnode
*node
;
2409 unsigned int line_num
= 0;
2412 /* vty_close won't close stderr; if some config command prints
2413 * something it'll end up there. (not ideal; it'd be better if output
2414 * from a file-load went to logging instead. Also note that if this
2415 * function is called after daemonizing, stderr will be /dev/null.)
2417 * vty->fd will be -1 from vty_new()
2419 vty
->wfd
= STDERR_FILENO
;
2420 vty
->type
= VTY_FILE
;
2421 vty
->node
= CONFIG_NODE
;
2424 vty
->candidate_config
= config
;
2426 vty
->private_config
= true;
2427 vty
->candidate_config
= nb_config_new(NULL
);
2430 /* Execute configuration file */
2431 ret
= config_from_file(vty
, confp
, &line_num
);
2433 /* Flush any previous errors before printing messages below */
2434 buffer_flush_all(vty
->obuf
, vty
->wfd
);
2436 if (!((ret
== CMD_SUCCESS
) || (ret
== CMD_ERR_NOTHING_TODO
))) {
2437 const char *message
= NULL
;
2441 case CMD_ERR_AMBIGUOUS
:
2442 message
= "Ambiguous command";
2444 case CMD_ERR_NO_MATCH
:
2445 message
= "No such command";
2448 message
= "Command returned Warning";
2450 case CMD_WARNING_CONFIG_FAILED
:
2451 message
= "Command returned Warning Config Failed";
2453 case CMD_ERR_INCOMPLETE
:
2454 message
= "Command returned Incomplete";
2456 case CMD_ERR_EXEED_ARGC_MAX
:
2458 "Command exceeded maximum number of Arguments";
2461 message
= "Command returned unhandled error message";
2465 for (ALL_LIST_ELEMENTS_RO(vty
->error
, node
, ve
)) {
2466 nl
= strchr(ve
->error_buf
, '\n');
2469 flog_err(EC_LIB_VTY
, "%s on config line %u: %s",
2470 message
, ve
->line_num
, ve
->error_buf
);
2475 * Automatically commit the candidate configuration after
2476 * reading the configuration file.
2478 if (config
== NULL
) {
2479 struct nb_context context
= {};
2480 char errmsg
[BUFSIZ
] = {0};
2482 context
.client
= NB_CLIENT_CLI
;
2484 ret
= nb_candidate_commit(context
, vty
->candidate_config
, true,
2485 "Read configuration file", NULL
,
2486 errmsg
, sizeof(errmsg
));
2487 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
)
2489 "%s: failed to read configuration file: %s (%s)",
2490 __func__
, nb_err_name(ret
), errmsg
);
2496 static FILE *vty_use_backup_config(const char *fullpath
)
2498 char *fullpath_sav
, *fullpath_tmp
;
2504 size_t fullpath_sav_sz
= strlen(fullpath
) + strlen(CONF_BACKUP_EXT
) + 1;
2505 fullpath_sav
= malloc(fullpath_sav_sz
);
2506 strlcpy(fullpath_sav
, fullpath
, fullpath_sav_sz
);
2507 strlcat(fullpath_sav
, CONF_BACKUP_EXT
, fullpath_sav_sz
);
2509 sav
= open(fullpath_sav
, O_RDONLY
);
2515 fullpath_tmp
= malloc(strlen(fullpath
) + 8);
2516 snprintf(fullpath_tmp
, strlen(fullpath
) + 8, "%s.XXXXXX", fullpath
);
2518 /* Open file to configuration write. */
2519 tmp
= mkstemp(fullpath_tmp
);
2523 if (fchmod(tmp
, CONFIGFILE_MASK
) != 0)
2526 while ((c
= read(sav
, buffer
, 512)) > 0) {
2527 if (write(tmp
, buffer
, c
) <= 0)
2533 if (rename(fullpath_tmp
, fullpath
) == 0)
2534 ret
= fopen(fullpath
, "r");
2536 unlink(fullpath_tmp
);
2541 unlink(fullpath_tmp
);
2551 /* Read up configuration file from file_name. */
2552 bool vty_read_config(struct nb_config
*config
, const char *config_file
,
2553 char *config_default_dir
)
2555 char cwd
[MAXPATHLEN
];
2557 const char *fullpath
;
2559 bool read_success
= false;
2561 /* If -f flag specified. */
2562 if (config_file
!= NULL
) {
2563 if (!IS_DIRECTORY_SEP(config_file
[0])) {
2564 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
2567 "%s: failure to determine Current Working Directory %d!",
2569 goto tmp_free_and_out
;
2571 size_t tmp_len
= strlen(cwd
) + strlen(config_file
) + 2;
2572 tmp
= XMALLOC(MTYPE_TMP
, tmp_len
);
2573 snprintf(tmp
, tmp_len
, "%s/%s", cwd
, config_file
);
2576 fullpath
= config_file
;
2578 confp
= fopen(fullpath
, "r");
2580 if (confp
== NULL
) {
2582 EC_LIB_BACKUP_CONFIG
,
2583 "%s: failed to open configuration file %s: %s, checking backup",
2584 __func__
, fullpath
, safe_strerror(errno
));
2586 confp
= vty_use_backup_config(fullpath
);
2588 flog_warn(EC_LIB_BACKUP_CONFIG
,
2589 "using backup configuration file!");
2593 "%s: can't open configuration file [%s]",
2594 __func__
, config_file
);
2595 goto tmp_free_and_out
;
2600 host_config_set(config_default_dir
);
2604 struct stat conf_stat
;
2606 /* !!!!PLEASE LEAVE!!!!
2607 * This is NEEDED for use with vtysh -b, or else you can get
2608 * a real configuration food fight with a lot garbage in the
2609 * merged configuration file it creates coming from the per
2610 * daemon configuration files. This also allows the daemons
2611 * to start if there default configuration file is not
2612 * present or ignore them, as needed when using vtysh -b to
2613 * configure the daemons at boot - MAG
2616 /* Stat for vtysh Zebra.conf, if found startup and wait for
2617 * boot configuration
2620 if (strstr(config_default_dir
, "vtysh") == NULL
) {
2621 ret
= stat(integrate_default
, &conf_stat
);
2623 read_success
= true;
2624 goto tmp_free_and_out
;
2628 confp
= fopen(config_default_dir
, "r");
2629 if (confp
== NULL
) {
2632 "%s: failed to open configuration file %s: %s, checking backup",
2633 __func__
, config_default_dir
,
2634 safe_strerror(errno
));
2636 confp
= vty_use_backup_config(config_default_dir
);
2638 flog_warn(EC_LIB_BACKUP_CONFIG
,
2639 "using backup configuration file!");
2640 fullpath
= config_default_dir
;
2642 flog_err(EC_LIB_VTY
,
2643 "can't open configuration file [%s]",
2644 config_default_dir
);
2645 goto tmp_free_and_out
;
2648 fullpath
= config_default_dir
;
2651 vty_read_file(config
, confp
);
2652 read_success
= true;
2656 host_config_set(fullpath
);
2659 XFREE(MTYPE_TMP
, tmp
);
2661 return read_success
;
2664 static void update_xpath(struct vty
*vty
, const char *oldpath
,
2665 const char *newpath
)
2669 for (i
= 0; i
< vty
->xpath_index
; i
++) {
2670 if (!frrstr_startswith(vty
->xpath
[i
], oldpath
))
2673 char *tmp
= frrstr_replace(vty
->xpath
[i
], oldpath
, newpath
);
2674 strlcpy(vty
->xpath
[i
], tmp
, sizeof(vty
->xpath
[0]));
2675 XFREE(MTYPE_TMP
, tmp
);
2679 void vty_update_xpath(const char *oldpath
, const char *newpath
)
2683 frr_each (vtys
, vtysh_sessions
, vty
)
2684 update_xpath(vty
, oldpath
, newpath
);
2685 frr_each (vtys
, vty_sessions
, vty
)
2686 update_xpath(vty
, oldpath
, newpath
);
2689 int vty_config_enter(struct vty
*vty
, bool private_config
, bool exclusive
)
2691 if (exclusive
&& nb_running_lock(NB_CLIENT_CLI
, vty
)) {
2692 vty_out(vty
, "%% Configuration is locked by other client\n");
2696 if (vty_mgmt_fe_enabled()) {
2697 if (!mgmt_candidate_ds_wr_locked
) {
2698 if (vty_mgmt_send_lockds_req(vty
, MGMTD_DS_CANDIDATE
,
2700 vty_out(vty
, "Not able to lock candidate DS\n");
2705 "Candidate DS already locked by different session\n");
2709 vty
->mgmt_locked_candidate_ds
= true;
2710 mgmt_candidate_ds_wr_locked
= true;
2713 vty
->node
= CONFIG_NODE
;
2715 vty
->private_config
= private_config
;
2716 vty
->xpath_index
= 0;
2718 if (private_config
) {
2719 vty
->candidate_config
= nb_config_dup(running_config
);
2720 vty
->candidate_config_base
= nb_config_dup(running_config
);
2722 "Warning: uncommitted changes will be discarded on exit.\n\n");
2725 * NOTE: On the MGMTD daemon we point the VTY candidate DS to
2726 * the global MGMTD candidate DS. Else we point to the VTY
2727 * Shared Candidate Config.
2729 vty
->candidate_config
= vty_mgmt_candidate_config
2730 ? vty_mgmt_candidate_config
2731 : vty_shared_candidate_config
;
2732 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
)
2733 vty
->candidate_config_base
=
2734 nb_config_dup(running_config
);
2740 void vty_config_exit(struct vty
*vty
)
2742 enum node_type node
= vty
->node
;
2743 struct cmd_node
*cnode
;
2745 /* unlock and jump up to ENABLE_NODE if -and only if- we're
2746 * somewhere below CONFIG_NODE */
2747 while (node
&& node
!= CONFIG_NODE
) {
2748 cnode
= vector_lookup(cmdvec
, node
);
2749 node
= cnode
->parent_node
;
2751 if (node
!= CONFIG_NODE
)
2752 /* called outside config, e.g. vty_close() in ENABLE_NODE */
2755 while (vty
->node
!= ENABLE_NODE
)
2756 /* will call vty_config_node_exit() below */
2760 int vty_config_node_exit(struct vty
*vty
)
2762 vty
->xpath_index
= 0;
2764 if (vty_mgmt_fe_enabled() && mgmt_candidate_ds_wr_locked
&&
2765 vty
->mgmt_locked_candidate_ds
) {
2766 if (vty_mgmt_send_lockds_req(vty
, MGMTD_DS_CANDIDATE
, false) !=
2768 vty_out(vty
, "Not able to unlock candidate DS\n");
2772 vty
->mgmt_locked_candidate_ds
= false;
2773 mgmt_candidate_ds_wr_locked
= false;
2776 /* Perform any pending commits. */
2777 (void)nb_cli_pending_commit_check(vty
);
2779 /* Check if there's a pending confirmed commit. */
2780 if (vty
->t_confirmed_commit_timeout
) {
2782 "exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
2783 nb_cli_confirmed_commit_rollback(vty
);
2784 nb_cli_confirmed_commit_clean(vty
);
2787 (void)nb_running_unlock(NB_CLIENT_CLI
, vty
);
2789 if (vty
->candidate_config
) {
2790 if (vty
->private_config
)
2791 nb_config_free(vty
->candidate_config
);
2792 vty
->candidate_config
= NULL
;
2794 if (vty
->candidate_config_base
) {
2795 nb_config_free(vty
->candidate_config_base
);
2796 vty
->candidate_config_base
= NULL
;
2799 vty
->config
= false;
2803 /* Master of the threads. */
2804 static struct event_loop
*vty_master
;
2806 static void vty_event_serv(enum vty_event event
, struct vty_serv
*vty_serv
)
2810 event_add_read(vty_master
, vty_accept
, vty_serv
, vty_serv
->sock
,
2811 &vty_serv
->t_accept
);
2815 event_add_read(vty_master
, vtysh_accept
, vty_serv
,
2816 vty_serv
->sock
, &vty_serv
->t_accept
);
2821 case VTY_TIMEOUT_RESET
:
2824 assert(!"vty_event_serv() called incorrectly");
2828 static void vty_event(enum vty_event event
, struct vty
*vty
)
2833 event_add_read(vty_master
, vtysh_read
, vty
, vty
->fd
,
2837 event_add_write(vty_master
, vtysh_write
, vty
, vty
->wfd
,
2842 event_add_read(vty_master
, vty_read
, vty
, vty
->fd
,
2845 /* Time out treatment. */
2846 if (vty
->v_timeout
) {
2847 EVENT_OFF(vty
->t_timeout
);
2848 event_add_timer(vty_master
, vty_timeout
, vty
,
2849 vty
->v_timeout
, &vty
->t_timeout
);
2853 event_add_write(vty_master
, vty_flush
, vty
, vty
->wfd
,
2856 case VTY_TIMEOUT_RESET
:
2857 EVENT_OFF(vty
->t_timeout
);
2859 event_add_timer(vty_master
, vty_timeout
, vty
,
2860 vty
->v_timeout
, &vty
->t_timeout
);
2864 assert(!"vty_event() called incorrectly");
2868 DEFUN_NOSH (config_who
,
2871 "Display who is on vty\n")
2875 frr_each (vtys
, vty_sessions
, v
)
2876 vty_out(vty
, "%svty[%d] connected from %s%s.\n",
2877 v
->config
? "*" : " ", v
->fd
, v
->address
,
2878 zlog_live_is_null(&v
->live_log
) ? "" : ", live log");
2882 /* Move to vty configuration mode. */
2883 DEFUN_NOSH (line_vty
,
2886 "Configure a terminal line\n"
2887 "Virtual terminal\n")
2889 vty
->node
= VTY_NODE
;
2893 /* Set time out value. */
2894 static int exec_timeout(struct vty
*vty
, const char *min_str
,
2895 const char *sec_str
)
2897 unsigned long timeout
= 0;
2899 /* min_str and sec_str are already checked by parser. So it must be
2900 all digit string. */
2902 timeout
= strtol(min_str
, NULL
, 10);
2906 timeout
+= strtol(sec_str
, NULL
, 10);
2908 vty_timeout_val
= timeout
;
2909 vty
->v_timeout
= timeout
;
2910 vty_event(VTY_TIMEOUT_RESET
, vty
);
2916 DEFUN (exec_timeout_min
,
2917 exec_timeout_min_cmd
,
2918 "exec-timeout (0-35791)",
2919 "Set timeout value\n"
2920 "Timeout value in minutes\n")
2923 return exec_timeout(vty
, argv
[idx_number
]->arg
, NULL
);
2926 DEFUN (exec_timeout_sec
,
2927 exec_timeout_sec_cmd
,
2928 "exec-timeout (0-35791) (0-2147483)",
2929 "Set the EXEC timeout\n"
2930 "Timeout in minutes\n"
2931 "Timeout in seconds\n")
2934 int idx_number_2
= 2;
2935 return exec_timeout(vty
, argv
[idx_number
]->arg
,
2936 argv
[idx_number_2
]->arg
);
2939 DEFUN (no_exec_timeout
,
2940 no_exec_timeout_cmd
,
2943 "Set the EXEC timeout\n")
2945 return exec_timeout(vty
, NULL
, NULL
);
2948 /* Set vty access class. */
2949 DEFUN (vty_access_class
,
2950 vty_access_class_cmd
,
2951 "access-class WORD",
2952 "Filter connections based on an IP access list\n"
2956 if (vty_accesslist_name
)
2957 XFREE(MTYPE_VTY
, vty_accesslist_name
);
2959 vty_accesslist_name
= XSTRDUP(MTYPE_VTY
, argv
[idx_word
]->arg
);
2964 /* Clear vty access class. */
2965 DEFUN (no_vty_access_class
,
2966 no_vty_access_class_cmd
,
2967 "no access-class [WORD]",
2969 "Filter connections based on an IP access list\n"
2973 const char *accesslist
= (argc
== 3) ? argv
[idx_word
]->arg
: NULL
;
2974 if (!vty_accesslist_name
2975 || (argc
== 3 && strcmp(vty_accesslist_name
, accesslist
))) {
2976 vty_out(vty
, "Access-class is not currently applied to vty\n");
2977 return CMD_WARNING_CONFIG_FAILED
;
2980 XFREE(MTYPE_VTY
, vty_accesslist_name
);
2982 vty_accesslist_name
= NULL
;
2987 /* Set vty access class. */
2988 DEFUN (vty_ipv6_access_class
,
2989 vty_ipv6_access_class_cmd
,
2990 "ipv6 access-class WORD",
2992 "Filter connections based on an IP access list\n"
2993 "IPv6 access list\n")
2996 if (vty_ipv6_accesslist_name
)
2997 XFREE(MTYPE_VTY
, vty_ipv6_accesslist_name
);
2999 vty_ipv6_accesslist_name
= XSTRDUP(MTYPE_VTY
, argv
[idx_word
]->arg
);
3004 /* Clear vty access class. */
3005 DEFUN (no_vty_ipv6_access_class
,
3006 no_vty_ipv6_access_class_cmd
,
3007 "no ipv6 access-class [WORD]",
3010 "Filter connections based on an IP access list\n"
3011 "IPv6 access list\n")
3014 const char *accesslist
= (argc
== 4) ? argv
[idx_word
]->arg
: NULL
;
3016 if (!vty_ipv6_accesslist_name
3017 || (argc
== 4 && strcmp(vty_ipv6_accesslist_name
, accesslist
))) {
3019 "IPv6 access-class is not currently applied to vty\n");
3020 return CMD_WARNING_CONFIG_FAILED
;
3023 XFREE(MTYPE_VTY
, vty_ipv6_accesslist_name
);
3025 vty_ipv6_accesslist_name
= NULL
;
3034 "Enable password checking\n")
3036 no_password_check
= 0;
3040 DEFUN (no_vty_login
,
3044 "Enable password checking\n")
3046 no_password_check
= 1;
3050 DEFUN (service_advanced_vty
,
3051 service_advanced_vty_cmd
,
3052 "service advanced-vty",
3053 "Set up miscellaneous service\n"
3054 "Enable advanced mode vty interface\n")
3060 DEFUN (no_service_advanced_vty
,
3061 no_service_advanced_vty_cmd
,
3062 "no service advanced-vty",
3064 "Set up miscellaneous service\n"
3065 "Enable advanced mode vty interface\n")
3071 DEFUN_NOSH(terminal_monitor
,
3072 terminal_monitor_cmd
,
3073 "terminal monitor [detach]",
3074 "Set terminal line parameters\n"
3075 "Copy debug output to the current terminal line\n"
3076 "Keep logging feed open independent of VTY session\n")
3080 if (vty
->type
!= VTY_SHELL_SERV
) {
3081 vty_out(vty
, "%% not supported\n");
3086 struct zlog_live_cfg detach_log
= {};
3088 zlog_live_open(&detach_log
, LOG_DEBUG
, &fd_ret
);
3089 zlog_live_disown(&detach_log
);
3091 zlog_live_open(&vty
->live_log
, LOG_DEBUG
, &fd_ret
);
3094 vty_out(vty
, "%% error opening live log: %m\n");
3098 vty_pass_fd(vty
, fd_ret
);
3102 DEFUN_NOSH(no_terminal_monitor
,
3103 no_terminal_monitor_cmd
,
3104 "no terminal monitor",
3106 "Set terminal line parameters\n"
3107 "Copy debug output to the current terminal line\n")
3109 zlog_live_close(&vty
->live_log
);
3113 DEFUN_NOSH(terminal_no_monitor
,
3114 terminal_no_monitor_cmd
,
3115 "terminal no monitor",
3116 "Set terminal line parameters\n"
3118 "Copy debug output to the current terminal line\n")
3120 return no_terminal_monitor(self
, vty
, argc
, argv
);
3124 DEFUN_NOSH (show_history
,
3128 "Display the session command history\n")
3132 for (index
= vty
->hindex
+ 1; index
!= vty
->hindex
;) {
3133 if (index
== VTY_MAXHIST
) {
3138 if (vty
->hist
[index
] != NULL
)
3139 vty_out(vty
, " %s\n", vty
->hist
[index
]);
3148 DEFPY (log_commands
,
3150 "[no] log commands",
3153 "Log all commands\n")
3156 if (do_log_commands_perm
) {
3158 "Daemon started with permanent logging turned on for commands, ignoring\n");
3162 do_log_commands
= false;
3164 do_log_commands
= true;
3169 /* Display current configuration. */
3170 static int vty_config_write(struct vty
*vty
)
3172 vty_frame(vty
, "line vty\n");
3174 if (vty_accesslist_name
)
3175 vty_out(vty
, " access-class %s\n", vty_accesslist_name
);
3177 if (vty_ipv6_accesslist_name
)
3178 vty_out(vty
, " ipv6 access-class %s\n",
3179 vty_ipv6_accesslist_name
);
3182 if (vty_timeout_val
!= VTY_TIMEOUT_DEFAULT
)
3183 vty_out(vty
, " exec-timeout %ld %ld\n", vty_timeout_val
/ 60,
3184 vty_timeout_val
% 60);
3187 if (no_password_check
)
3188 vty_out(vty
, " no login\n");
3190 vty_endframe(vty
, "exit\n");
3192 if (do_log_commands
)
3193 vty_out(vty
, "log commands\n");
3195 vty_out(vty
, "!\n");
3200 static int vty_config_write(struct vty
*vty
);
3201 struct cmd_node vty_node
= {
3204 .parent_node
= CONFIG_NODE
,
3205 .prompt
= "%s(config-line)# ",
3206 .config_write
= vty_config_write
,
3209 /* Reset all VTY status. */
3210 void vty_reset(void)
3214 frr_each_safe (vtys
, vty_sessions
, vty
) {
3215 buffer_reset(vty
->lbuf
);
3216 buffer_reset(vty
->obuf
);
3217 vty
->status
= VTY_CLOSE
;
3221 vty_timeout_val
= VTY_TIMEOUT_DEFAULT
;
3223 XFREE(MTYPE_VTY
, vty_accesslist_name
);
3224 XFREE(MTYPE_VTY
, vty_ipv6_accesslist_name
);
3227 static void vty_save_cwd(void)
3231 c
= getcwd(vty_cwd
, sizeof(vty_cwd
));
3235 * At this point if these go wrong, more than likely
3236 * the whole world is coming down around us
3237 * Hence not worrying about it too much.
3239 if (chdir(SYSCONFDIR
)) {
3240 flog_err_sys(EC_LIB_SYSTEM_CALL
,
3241 "Failure to chdir to %s, errno: %d",
3245 if (getcwd(vty_cwd
, sizeof(vty_cwd
)) == NULL
) {
3246 flog_err_sys(EC_LIB_SYSTEM_CALL
,
3247 "Failure to getcwd, errno: %d", errno
);
3253 char *vty_get_cwd(void)
3258 int vty_shell(struct vty
*vty
)
3260 return vty
->type
== VTY_SHELL
? 1 : 0;
3263 int vty_shell_serv(struct vty
*vty
)
3265 return vty
->type
== VTY_SHELL_SERV
? 1 : 0;
3268 void vty_init_vtysh(void)
3270 /* currently nothing to do, but likely to have future use */
3273 static void vty_mgmt_server_connected(uintptr_t lib_hndl
, uintptr_t usr_data
,
3276 zlog_err("%sGot %sconnected %s MGMTD Frontend Server",
3277 !connected
? "ERROR: " : "", !connected
? "dis: " : "",
3278 !connected
? "from" : "to");
3280 mgmt_fe_connected
= connected
;
3283 * TODO: Setup or teardown front-end sessions for existing
3288 static void vty_mgmt_session_created(uintptr_t lib_hndl
, uintptr_t usr_data
,
3289 uint64_t client_id
, bool create
,
3290 bool success
, uintptr_t session_id
,
3291 uintptr_t session_ctx
)
3295 vty
= (struct vty
*)session_ctx
;
3298 zlog_err("%s session for client %llu failed!",
3299 create
? "Creating" : "Destroying",
3300 (unsigned long long)client_id
);
3304 zlog_err("%s session for client %llu successfully!",
3305 create
? "Created" : "Destroyed",
3306 (unsigned long long)client_id
);
3308 vty
->mgmt_session_id
= session_id
;
3311 static void vty_mgmt_ds_lock_notified(uintptr_t lib_hndl
, uintptr_t usr_data
,
3312 uint64_t client_id
, uintptr_t session_id
,
3313 uintptr_t session_ctx
, uint64_t req_id
,
3314 bool lock_ds
, bool success
,
3315 Mgmtd__DatastoreId ds_id
,
3316 char *errmsg_if_any
)
3320 vty
= (struct vty
*)session_ctx
;
3323 zlog_err("%socking for DS %u failed! Err: '%s'",
3324 lock_ds
? "L" : "Unl", ds_id
, errmsg_if_any
);
3325 vty_out(vty
, "ERROR: %socking for DS %u failed! Err: '%s'\n",
3326 lock_ds
? "L" : "Unl", ds_id
, errmsg_if_any
);
3328 zlog_err("%socked DS %u successfully!", lock_ds
? "L" : "Unl",
3332 vty_mgmt_resume_response(vty
, success
);
3335 static void vty_mgmt_set_config_result_notified(
3336 uintptr_t lib_hndl
, uintptr_t usr_data
, uint64_t client_id
,
3337 uintptr_t session_id
, uintptr_t session_ctx
, uint64_t req_id
,
3338 bool success
, Mgmtd__DatastoreId ds_id
, char *errmsg_if_any
)
3342 vty
= (struct vty
*)session_ctx
;
3346 "SET_CONFIG request for client 0x%llx failed! Error: '%s'",
3347 (unsigned long long)client_id
,
3348 errmsg_if_any
? errmsg_if_any
: "Unknown");
3349 vty_out(vty
, "ERROR: SET_CONFIG request failed! Error: %s\n",
3350 errmsg_if_any
? errmsg_if_any
: "Unknown");
3353 "SET_CONFIG request for client 0x%llx req-id %llu was successfull!",
3354 (unsigned long long)client_id
,
3355 (unsigned long long)req_id
);
3358 vty_mgmt_resume_response(vty
, success
);
3361 static void vty_mgmt_commit_config_result_notified(
3362 uintptr_t lib_hndl
, uintptr_t usr_data
, uint64_t client_id
,
3363 uintptr_t session_id
, uintptr_t session_ctx
, uint64_t req_id
,
3364 bool success
, Mgmtd__DatastoreId src_ds_id
,
3365 Mgmtd__DatastoreId dst_ds_id
, bool validate_only
, char *errmsg_if_any
)
3369 vty
= (struct vty
*)session_ctx
;
3373 "COMMIT_CONFIG request for client 0x%llx failed! Error: '%s'",
3374 (unsigned long long)client_id
,
3375 errmsg_if_any
? errmsg_if_any
: "Unknown");
3376 vty_out(vty
, "ERROR: COMMIT_CONFIG request failed! Error: %s\n",
3377 errmsg_if_any
? errmsg_if_any
: "Unknown");
3380 "COMMIT_CONFIG request for client 0x%llx req-id %llu was successfull!",
3381 (unsigned long long)client_id
,
3382 (unsigned long long)req_id
);
3384 vty_out(vty
, "MGMTD: %s\n", errmsg_if_any
);
3387 vty_mgmt_resume_response(vty
, success
);
3390 static enum mgmt_result
vty_mgmt_get_data_result_notified(
3391 uintptr_t lib_hndl
, uintptr_t usr_data
, uint64_t client_id
,
3392 uintptr_t session_id
, uintptr_t session_ctx
, uint64_t req_id
,
3393 bool success
, Mgmtd__DatastoreId ds_id
, Mgmtd__YangData
**yang_data
,
3394 size_t num_data
, int next_key
, char *errmsg_if_any
)
3399 vty
= (struct vty
*)session_ctx
;
3403 "GET_DATA request for client 0x%llx failed! Error: '%s'",
3404 (unsigned long long)client_id
,
3405 errmsg_if_any
? errmsg_if_any
: "Unknown");
3406 vty_out(vty
, "ERROR: GET_DATA request failed! Error: %s\n",
3407 errmsg_if_any
? errmsg_if_any
: "Unknown");
3408 vty_mgmt_resume_response(vty
, success
);
3409 return MGMTD_INTERNAL_ERROR
;
3413 "GET_DATA request for client 0x%llx req-id %llu was successfull!",
3414 (unsigned long long)client_id
, (unsigned long long)req_id
);
3416 if (req_id
!= mgmt_last_req_id
) {
3417 mgmt_last_req_id
= req_id
;
3418 vty_out(vty
, "[\n");
3421 for (indx
= 0; indx
< num_data
; indx
++) {
3422 vty_out(vty
, " \"%s\": \"%s\"\n", yang_data
[indx
]->xpath
,
3423 yang_data
[indx
]->value
->encoded_str_val
);
3426 vty_out(vty
, "]\n");
3427 vty_mgmt_resume_response(vty
, success
);
3430 return MGMTD_SUCCESS
;
3433 static struct mgmt_fe_client_params client_params
= {
3434 .client_connect_notify
= vty_mgmt_server_connected
,
3435 .client_session_notify
= vty_mgmt_session_created
,
3436 .lock_ds_notify
= vty_mgmt_ds_lock_notified
,
3437 .set_config_notify
= vty_mgmt_set_config_result_notified
,
3438 .commit_config_notify
= vty_mgmt_commit_config_result_notified
,
3439 .get_data_notify
= vty_mgmt_get_data_result_notified
,
3442 void vty_init_mgmt_fe(void)
3445 zlog_err("Always call vty_mgmt_init_fe() after vty_init()!!");
3449 assert(!mgmt_lib_hndl
);
3450 snprintf(client_params
.name
, sizeof(client_params
.name
), "%s-%lld",
3451 frr_get_progname(), (long long)getpid());
3452 mgmt_lib_hndl
= mgmt_fe_client_lib_init(&client_params
, vty_master
);
3453 assert(mgmt_lib_hndl
);
3456 bool vty_mgmt_fe_enabled(void)
3458 return mgmt_lib_hndl
&& mgmt_fe_connected
? true : false;
3461 int vty_mgmt_send_lockds_req(struct vty
*vty
, Mgmtd__DatastoreId ds_id
,
3464 enum mgmt_result ret
;
3466 if (mgmt_lib_hndl
&& vty
->mgmt_session_id
) {
3468 ret
= mgmt_fe_lock_ds(mgmt_lib_hndl
, vty
->mgmt_session_id
,
3469 vty
->mgmt_req_id
, ds_id
, lock
);
3470 if (ret
!= MGMTD_SUCCESS
) {
3472 "Failed to send %sLOCK-DS-REQ to MGMTD for req-id %llu.",
3474 (unsigned long long)vty
->mgmt_req_id
);
3475 vty_out(vty
, "Failed to send %sLOCK-DS-REQ to MGMTD!",
3480 vty
->mgmt_req_pending
= true;
3486 int vty_mgmt_send_config_data(struct vty
*vty
)
3488 Mgmtd__YangDataValue value
[VTY_MAXCFGCHANGES
];
3489 Mgmtd__YangData cfg_data
[VTY_MAXCFGCHANGES
];
3490 Mgmtd__YangCfgDataReq cfg_req
[VTY_MAXCFGCHANGES
];
3491 Mgmtd__YangCfgDataReq
*cfgreq
[VTY_MAXCFGCHANGES
] = {0};
3494 bool implicit_commit
= false;
3496 if (mgmt_lib_hndl
&& vty
->mgmt_session_id
) {
3498 for (indx
= 0; indx
< vty
->num_cfg_changes
; indx
++) {
3499 mgmt_yang_data_init(&cfg_data
[cnt
]);
3501 if (vty
->cfg_changes
[indx
].value
) {
3502 mgmt_yang_data_value_init(&value
[cnt
]);
3503 value
[cnt
].encoded_str_val
=
3504 (char *)vty
->cfg_changes
[indx
].value
;
3505 value
[cnt
].value_case
=
3506 MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL
;
3507 cfg_data
[cnt
].value
= &value
[cnt
];
3510 cfg_data
[cnt
].xpath
= vty
->cfg_changes
[indx
].xpath
;
3512 mgmt_yang_cfg_data_req_init(&cfg_req
[cnt
]);
3513 cfg_req
[cnt
].data
= &cfg_data
[cnt
];
3514 switch (vty
->cfg_changes
[indx
].operation
) {
3516 cfg_req
[cnt
].req_type
=
3517 MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA
;
3523 case NB_OP_PRE_VALIDATE
:
3524 case NB_OP_APPLY_FINISH
:
3525 cfg_req
[cnt
].req_type
=
3526 MGMTD__CFG_DATA_REQ_TYPE__SET_DATA
;
3528 case NB_OP_GET_ELEM
:
3529 case NB_OP_GET_NEXT
:
3530 case NB_OP_GET_KEYS
:
3531 case NB_OP_LOOKUP_ENTRY
:
3533 assert(!"Invalid type of operation");
3536 assert(!"non-enum value, invalid");
3539 cfgreq
[cnt
] = &cfg_req
[cnt
];
3544 implicit_commit
= vty_needs_implicit_commit(vty
);
3545 if (cnt
&& mgmt_fe_set_config_data(
3546 mgmt_lib_hndl
, vty
->mgmt_session_id
,
3547 vty
->mgmt_req_id
, MGMTD_DS_CANDIDATE
, cfgreq
,
3548 cnt
, implicit_commit
,
3549 MGMTD_DS_RUNNING
) != MGMTD_SUCCESS
) {
3550 zlog_err("Failed to send %d Config Xpaths to MGMTD!!",
3555 vty
->mgmt_req_pending
= true;
3561 int vty_mgmt_send_commit_config(struct vty
*vty
, bool validate_only
, bool abort
)
3563 enum mgmt_result ret
;
3565 if (mgmt_lib_hndl
&& vty
->mgmt_session_id
) {
3567 ret
= mgmt_fe_commit_config_data(
3568 mgmt_lib_hndl
, vty
->mgmt_session_id
, vty
->mgmt_req_id
,
3569 MGMTD_DS_CANDIDATE
, MGMTD_DS_RUNNING
, validate_only
,
3571 if (ret
!= MGMTD_SUCCESS
) {
3573 "Failed to send COMMIT-REQ to MGMTD for req-id %llu.",
3574 (unsigned long long)vty
->mgmt_req_id
);
3575 vty_out(vty
, "Failed to send COMMIT-REQ to MGMTD!");
3579 vty
->mgmt_req_pending
= true;
3580 vty
->mgmt_num_pending_setcfg
= 0;
3586 int vty_mgmt_send_get_config(struct vty
*vty
, Mgmtd__DatastoreId datastore
,
3587 const char **xpath_list
, int num_req
)
3589 enum mgmt_result ret
;
3590 Mgmtd__YangData yang_data
[VTY_MAXCFGCHANGES
];
3591 Mgmtd__YangGetDataReq get_req
[VTY_MAXCFGCHANGES
];
3592 Mgmtd__YangGetDataReq
*getreq
[VTY_MAXCFGCHANGES
];
3597 for (i
= 0; i
< num_req
; i
++) {
3598 mgmt_yang_get_data_req_init(&get_req
[i
]);
3599 mgmt_yang_data_init(&yang_data
[i
]);
3601 yang_data
->xpath
= (char *)xpath_list
[i
];
3603 get_req
[i
].data
= &yang_data
[i
];
3604 getreq
[i
] = &get_req
[i
];
3606 ret
= mgmt_fe_get_config_data(mgmt_lib_hndl
, vty
->mgmt_session_id
,
3607 vty
->mgmt_req_id
, datastore
, getreq
,
3610 if (ret
!= MGMTD_SUCCESS
) {
3611 zlog_err("Failed to send GET-CONFIG to MGMTD for req-id %llu.",
3612 (unsigned long long)vty
->mgmt_req_id
);
3613 vty_out(vty
, "Failed to send GET-CONFIG to MGMTD!");
3617 vty
->mgmt_req_pending
= true;
3622 int vty_mgmt_send_get_data(struct vty
*vty
, Mgmtd__DatastoreId datastore
,
3623 const char **xpath_list
, int num_req
)
3625 enum mgmt_result ret
;
3626 Mgmtd__YangData yang_data
[VTY_MAXCFGCHANGES
];
3627 Mgmtd__YangGetDataReq get_req
[VTY_MAXCFGCHANGES
];
3628 Mgmtd__YangGetDataReq
*getreq
[VTY_MAXCFGCHANGES
];
3633 for (i
= 0; i
< num_req
; i
++) {
3634 mgmt_yang_get_data_req_init(&get_req
[i
]);
3635 mgmt_yang_data_init(&yang_data
[i
]);
3637 yang_data
->xpath
= (char *)xpath_list
[i
];
3639 get_req
[i
].data
= &yang_data
[i
];
3640 getreq
[i
] = &get_req
[i
];
3642 ret
= mgmt_fe_get_data(mgmt_lib_hndl
, vty
->mgmt_session_id
,
3643 vty
->mgmt_req_id
, datastore
, getreq
, num_req
);
3645 if (ret
!= MGMTD_SUCCESS
) {
3646 zlog_err("Failed to send GET-DATA to MGMTD for req-id %llu.",
3647 (unsigned long long)vty
->mgmt_req_id
);
3648 vty_out(vty
, "Failed to send GET-DATA to MGMTD!");
3652 vty
->mgmt_req_pending
= true;
3657 /* Install vty's own commands like `who' command. */
3658 void vty_init(struct event_loop
*master_thread
, bool do_command_logging
)
3660 /* For further configuration read, preserve current directory. */
3663 vty_master
= master_thread
;
3665 atexit(vty_stdio_atexit
);
3667 /* Install bgp top node. */
3668 install_node(&vty_node
);
3670 install_element(VIEW_NODE
, &config_who_cmd
);
3671 install_element(VIEW_NODE
, &show_history_cmd
);
3672 install_element(CONFIG_NODE
, &line_vty_cmd
);
3673 install_element(CONFIG_NODE
, &service_advanced_vty_cmd
);
3674 install_element(CONFIG_NODE
, &no_service_advanced_vty_cmd
);
3675 install_element(CONFIG_NODE
, &show_history_cmd
);
3676 install_element(CONFIG_NODE
, &log_commands_cmd
);
3678 if (do_command_logging
) {
3679 do_log_commands
= true;
3680 do_log_commands_perm
= true;
3683 install_element(ENABLE_NODE
, &terminal_monitor_cmd
);
3684 install_element(ENABLE_NODE
, &terminal_no_monitor_cmd
);
3685 install_element(ENABLE_NODE
, &no_terminal_monitor_cmd
);
3687 install_default(VTY_NODE
);
3688 install_element(VTY_NODE
, &exec_timeout_min_cmd
);
3689 install_element(VTY_NODE
, &exec_timeout_sec_cmd
);
3690 install_element(VTY_NODE
, &no_exec_timeout_cmd
);
3691 install_element(VTY_NODE
, &vty_access_class_cmd
);
3692 install_element(VTY_NODE
, &no_vty_access_class_cmd
);
3693 install_element(VTY_NODE
, &vty_login_cmd
);
3694 install_element(VTY_NODE
, &no_vty_login_cmd
);
3695 install_element(VTY_NODE
, &vty_ipv6_access_class_cmd
);
3696 install_element(VTY_NODE
, &no_vty_ipv6_access_class_cmd
);
3699 void vty_terminate(void)
3702 struct vty_serv
*vtyserv
;
3704 if (mgmt_lib_hndl
) {
3705 mgmt_fe_client_lib_destroy(mgmt_lib_hndl
);
3709 memset(vty_cwd
, 0x00, sizeof(vty_cwd
));
3713 /* default state of vty_sessions is initialized & empty. */
3714 vtys_fini(vty_sessions
);
3715 vtys_init(vty_sessions
);
3717 /* vty_reset() doesn't close vtysh sessions */
3718 frr_each_safe (vtys
, vtysh_sessions
, vty
) {
3719 buffer_reset(vty
->lbuf
);
3720 buffer_reset(vty
->obuf
);
3721 vty
->status
= VTY_CLOSE
;
3725 vtys_fini(vtysh_sessions
);
3726 vtys_init(vtysh_sessions
);
3728 while ((vtyserv
= vtyservs_pop(vty_servs
))) {
3729 EVENT_OFF(vtyserv
->t_accept
);
3730 close(vtyserv
->sock
);
3731 XFREE(MTYPE_VTY_SERV
, vtyserv
);
3734 vtyservs_fini(vty_servs
);
3735 vtyservs_init(vty_servs
);