]> git.proxmox.com Git - mirror_frr.git/blob - lib/vty.c
*: style for | support
[mirror_frr.git] / lib / vty.c
1 /*
2 * Virtual terminal [aka TeletYpe] interface routine.
3 * Copyright (C) 1997, 98 Kunihiro Ishiguro
4 *
5 * This file is part of GNU Zebra.
6 *
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <zebra.h>
23
24 #include <lib/version.h>
25 #include <sys/types.h>
26 #include <regex.h>
27
28 #include "linklist.h"
29 #include "thread.h"
30 #include "buffer.h"
31 #include "command.h"
32 #include "sockunion.h"
33 #include "memory.h"
34 #include "log.h"
35 #include "prefix.h"
36 #include "filter.h"
37 #include "vty.h"
38 #include "privs.h"
39 #include "network.h"
40 #include "libfrr.h"
41 #include "frrstr.h"
42
43 #include <arpa/telnet.h>
44 #include <termios.h>
45
46 DEFINE_MTYPE_STATIC(LIB, VTY, "VTY")
47 DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer")
48 DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history")
49
50 /* Vty events */
51 enum event {
52 VTY_SERV,
53 VTY_READ,
54 VTY_WRITE,
55 VTY_TIMEOUT_RESET,
56 #ifdef VTYSH
57 VTYSH_SERV,
58 VTYSH_READ,
59 VTYSH_WRITE
60 #endif /* VTYSH */
61 };
62
63 static void vty_event(enum event, int, struct vty *);
64
65 /* Extern host structure from command.c */
66 extern struct host host;
67
68 /* Vector which store each vty structure. */
69 static vector vtyvec;
70
71 /* Vty timeout value. */
72 static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
73
74 /* Vty access-class command */
75 static char *vty_accesslist_name = NULL;
76
77 /* Vty access-calss for IPv6. */
78 static char *vty_ipv6_accesslist_name = NULL;
79
80 /* VTY server thread. */
81 static vector Vvty_serv_thread;
82
83 /* Current directory. */
84 char *vty_cwd = NULL;
85
86 /* Configure lock. */
87 static int vty_config;
88 static int vty_config_is_lockless = 0;
89
90 /* Login password check. */
91 static int no_password_check = 0;
92
93 /* Integrated configuration file path */
94 char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
95
96 static int do_log_commands = 0;
97
98 void vty_frame(struct vty *vty, const char *format, ...)
99 {
100 va_list args;
101
102 va_start(args, format);
103 vsnprintf(vty->frame + vty->frame_pos,
104 sizeof(vty->frame) - vty->frame_pos, format, args);
105 vty->frame_pos = strlen(vty->frame);
106 va_end(args);
107 }
108
109 void vty_endframe(struct vty *vty, const char *endtext)
110 {
111 if (vty->frame_pos == 0 && endtext)
112 vty_out(vty, "%s", endtext);
113 vty->frame_pos = 0;
114 }
115
116 bool vty_set_include(struct vty *vty, const char *regexp)
117 {
118 int errcode;
119 bool ret = true;
120 char errbuf[256];
121
122 if (!regexp) {
123 memset(&vty->include, 0x00, sizeof(vty->include));
124 vty->filter = false;
125 return true;
126 }
127
128 errcode = regcomp(&vty->include, regexp,
129 REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
130 if (errcode) {
131 ret = false;
132 regerror(ret, &vty->include, errbuf, sizeof(errbuf));
133 vty_out(vty, "%% Regex compilation error: %s", errbuf);
134 } else {
135 vty->filter = true;
136 }
137
138 return ret;
139 }
140
141 /* VTY standard output function. */
142 int vty_out(struct vty *vty, const char *format, ...)
143 {
144 va_list args;
145 int len = 0;
146 int size = 1024;
147 char buf[1024];
148 char *p = NULL;
149 char *filtered;
150
151 if (vty->frame_pos) {
152 vty->frame_pos = 0;
153 vty_out(vty, "%s", vty->frame);
154 }
155
156 /* Try to write to initial buffer. */
157 va_start(args, format);
158 len = vsnprintf(buf, sizeof(buf), format, args);
159 va_end(args);
160
161 /* Initial buffer is not enough. */
162 if (len < 0 || len >= size) {
163 while (1) {
164 if (len > -1)
165 size = len + 1;
166 else
167 size = size * 2;
168
169 p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size);
170 if (!p)
171 return -1;
172
173 va_start(args, format);
174 len = vsnprintf(p, size, format, args);
175 va_end(args);
176
177 if (len > -1 && len < size)
178 break;
179 }
180 }
181
182 /* When initial buffer is enough to store all output. */
183 if (!p)
184 p = buf;
185
186 /* filter buffer */
187 if (vty->filter) {
188 vector lines = frrstr_split_vec(buf, "\n");
189
190 frrstr_filter_vec(lines, &vty->include);
191 if (buf[strlen(buf) - 1] == '\n' && vector_active(lines) > 0)
192 vector_set(lines, XSTRDUP(MTYPE_TMP, ""));
193 filtered = frrstr_join_vec(lines, "\n");
194 frrstr_strvec_free(lines);
195 } else {
196 filtered = p;
197 }
198
199 switch (vty->type) {
200 case VTY_TERM:
201 /* print with crlf replacement */
202 buffer_put_crlf(vty->obuf, (uint8_t *)filtered,
203 strlen(filtered));
204 break;
205 case VTY_SHELL:
206 fprintf(vty->of, "%s", filtered);
207 fflush(vty->of);
208 break;
209 case VTY_SHELL_SERV:
210 case VTY_FILE:
211 default:
212 /* print without crlf replacement */
213 buffer_put(vty->obuf, (uint8_t *)filtered, strlen(filtered));
214 break;
215 }
216
217 if (vty->filter)
218 XFREE(MTYPE_TMP, filtered);
219
220 /* If p is not different with buf, it is allocated buffer. */
221 if (p != buf)
222 XFREE(MTYPE_VTY_OUT_BUF, p);
223
224 return len;
225 }
226
227 static int vty_log_out(struct vty *vty, const char *level,
228 const char *proto_str, const char *format,
229 struct timestamp_control *ctl, va_list va)
230 {
231 int ret;
232 int len;
233 char buf[1024];
234
235 if (!ctl->already_rendered) {
236 ctl->len = quagga_timestamp(ctl->precision, ctl->buf,
237 sizeof(ctl->buf));
238 ctl->already_rendered = 1;
239 }
240 if (ctl->len + 1 >= sizeof(buf))
241 return -1;
242 memcpy(buf, ctl->buf, len = ctl->len);
243 buf[len++] = ' ';
244 buf[len] = '\0';
245
246 if (level)
247 ret = snprintf(buf + len, sizeof(buf) - len, "%s: %s: ", level,
248 proto_str);
249 else
250 ret = snprintf(buf + len, sizeof(buf) - len, "%s: ", proto_str);
251 if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
252 return -1;
253
254 if (((ret = vsnprintf(buf + len, sizeof(buf) - len, format, va)) < 0)
255 || ((size_t)((len += ret) + 2) > sizeof(buf)))
256 return -1;
257
258 buf[len++] = '\r';
259 buf[len++] = '\n';
260
261 if (write(vty->wfd, buf, len) < 0) {
262 if (ERRNO_IO_RETRY(errno))
263 /* Kernel buffer is full, probably too much debugging
264 output, so just
265 drop the data and ignore. */
266 return -1;
267 /* Fatal I/O error. */
268 vty->monitor =
269 0; /* disable monitoring to avoid infinite recursion */
270 zlog_warn("%s: write failed to vty client fd %d, closing: %s",
271 __func__, vty->fd, safe_strerror(errno));
272 buffer_reset(vty->obuf);
273 /* cannot call vty_close, because a parent routine may still try
274 to access the vty struct */
275 vty->status = VTY_CLOSE;
276 shutdown(vty->fd, SHUT_RDWR);
277 return -1;
278 }
279 return 0;
280 }
281
282 /* Output current time to the vty. */
283 void vty_time_print(struct vty *vty, int cr)
284 {
285 char buf[QUAGGA_TIMESTAMP_LEN];
286
287 if (quagga_timestamp(0, buf, sizeof(buf)) == 0) {
288 zlog_info("quagga_timestamp error");
289 return;
290 }
291 if (cr)
292 vty_out(vty, "%s\n", buf);
293 else
294 vty_out(vty, "%s ", buf);
295
296 return;
297 }
298
299 /* Say hello to vty interface. */
300 void vty_hello(struct vty *vty)
301 {
302 if (host.motdfile) {
303 FILE *f;
304 char buf[4096];
305
306 f = fopen(host.motdfile, "r");
307 if (f) {
308 while (fgets(buf, sizeof(buf), f)) {
309 char *s;
310 /* work backwards to ignore trailling isspace()
311 */
312 for (s = buf + strlen(buf);
313 (s > buf) && isspace((int)*(s - 1)); s--)
314 ;
315 *s = '\0';
316 vty_out(vty, "%s\n", buf);
317 }
318 fclose(f);
319 } else
320 vty_out(vty, "MOTD file not found\n");
321 } else if (host.motd)
322 vty_out(vty, "%s", host.motd);
323 }
324
325 /* Put out prompt and wait input from user. */
326 static void vty_prompt(struct vty *vty)
327 {
328 if (vty->type == VTY_TERM) {
329 vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
330 }
331 }
332
333 /* Send WILL TELOPT_ECHO to remote server. */
334 static void vty_will_echo(struct vty *vty)
335 {
336 unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
337 vty_out(vty, "%s", cmd);
338 }
339
340 /* Make suppress Go-Ahead telnet option. */
341 static void vty_will_suppress_go_ahead(struct vty *vty)
342 {
343 unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
344 vty_out(vty, "%s", cmd);
345 }
346
347 /* Make don't use linemode over telnet. */
348 static void vty_dont_linemode(struct vty *vty)
349 {
350 unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
351 vty_out(vty, "%s", cmd);
352 }
353
354 /* Use window size. */
355 static void vty_do_window_size(struct vty *vty)
356 {
357 unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
358 vty_out(vty, "%s", cmd);
359 }
360
361 #if 0 /* Currently not used. */
362 /* Make don't use lflow vty interface. */
363 static void
364 vty_dont_lflow_ahead (struct vty *vty)
365 {
366 unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
367 vty_out (vty, "%s", cmd);
368 }
369 #endif /* 0 */
370
371 /* Authentication of vty */
372 static void vty_auth(struct vty *vty, char *buf)
373 {
374 char *passwd = NULL;
375 enum node_type next_node = 0;
376 int fail;
377 char *crypt(const char *, const char *);
378
379 switch (vty->node) {
380 case AUTH_NODE:
381 if (host.encrypt)
382 passwd = host.password_encrypt;
383 else
384 passwd = host.password;
385 if (host.advanced)
386 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
387 else
388 next_node = VIEW_NODE;
389 break;
390 case AUTH_ENABLE_NODE:
391 if (host.encrypt)
392 passwd = host.enable_encrypt;
393 else
394 passwd = host.enable;
395 next_node = ENABLE_NODE;
396 break;
397 }
398
399 if (passwd) {
400 if (host.encrypt)
401 fail = strcmp(crypt(buf, passwd), passwd);
402 else
403 fail = strcmp(buf, passwd);
404 } else
405 fail = 1;
406
407 if (!fail) {
408 vty->fail = 0;
409 vty->node = next_node; /* Success ! */
410 } else {
411 vty->fail++;
412 if (vty->fail >= 3) {
413 if (vty->node == AUTH_NODE) {
414 vty_out(vty,
415 "%% Bad passwords, too many failures!\n");
416 vty->status = VTY_CLOSE;
417 } else {
418 /* AUTH_ENABLE_NODE */
419 vty->fail = 0;
420 vty_out(vty,
421 "%% Bad enable passwords, too many failures!\n");
422 vty->status = VTY_CLOSE;
423 }
424 }
425 }
426 }
427
428 /* Command execution over the vty interface. */
429 static int vty_command(struct vty *vty, char *buf)
430 {
431 int ret;
432 const char *protocolname;
433 char *cp = NULL;
434
435 /*
436 * Log non empty command lines
437 */
438 if (do_log_commands)
439 cp = buf;
440 if (cp != NULL) {
441 /* Skip white spaces. */
442 while (isspace((int)*cp) && *cp != '\0')
443 cp++;
444 }
445 if (cp != NULL && *cp != '\0') {
446 unsigned i;
447 char vty_str[VTY_BUFSIZ];
448 char prompt_str[VTY_BUFSIZ];
449
450 /* format the base vty info */
451 snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
452 if (vty)
453 for (i = 0; i < vector_active(vtyvec); i++)
454 if (vty == vector_slot(vtyvec, i)) {
455 snprintf(vty_str, sizeof(vty_str),
456 "vty[%d]@%s", i, vty->address);
457 break;
458 }
459
460 /* format the prompt */
461 snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
462 vty_str);
463
464 /* now log the command */
465 zlog_err("%s%s", prompt_str, buf);
466 }
467
468 #ifdef CONSUMED_TIME_CHECK
469 {
470 RUSAGE_T before;
471 RUSAGE_T after;
472 unsigned long realtime, cputime;
473
474 GETRUSAGE(&before);
475 #endif /* CONSUMED_TIME_CHECK */
476
477 ret = cmd_execute(vty, buf, NULL, 0);
478
479 /* Get the name of the protocol if any */
480 protocolname = frr_protoname;
481
482 #ifdef CONSUMED_TIME_CHECK
483 GETRUSAGE(&after);
484 if ((realtime = thread_consumed_time(&after, &before, &cputime))
485 > CONSUMED_TIME_CHECK)
486 /* Warn about CPU hog that must be fixed. */
487 zlog_warn(
488 "SLOW COMMAND: command took %lums (cpu time %lums): %s",
489 realtime / 1000, cputime / 1000, buf);
490 }
491 #endif /* CONSUMED_TIME_CHECK */
492
493 if (ret != CMD_SUCCESS)
494 switch (ret) {
495 case CMD_WARNING:
496 if (vty->type == VTY_FILE)
497 vty_out(vty, "Warning...\n");
498 break;
499 case CMD_ERR_AMBIGUOUS:
500 vty_out(vty, "%% Ambiguous command.\n");
501 break;
502 case CMD_ERR_NO_MATCH:
503 vty_out(vty, "%% [%s] Unknown command: %s\n",
504 protocolname, buf);
505 break;
506 case CMD_ERR_INCOMPLETE:
507 vty_out(vty, "%% Command incomplete.\n");
508 break;
509 }
510
511 return ret;
512 }
513
514 static const char telnet_backward_char = 0x08;
515 static const char telnet_space_char = ' ';
516
517 /* Basic function to write buffer to vty. */
518 static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
519 {
520 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
521 return;
522
523 /* Should we do buffering here ? And make vty_flush (vty) ? */
524 buffer_put(vty->obuf, buf, nbytes);
525 }
526
527 /* Basic function to insert character into vty. */
528 static void vty_self_insert(struct vty *vty, char c)
529 {
530 int i;
531 int length;
532
533 if (vty->length + 1 >= VTY_BUFSIZ)
534 return;
535
536 length = vty->length - vty->cp;
537 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
538 vty->buf[vty->cp] = c;
539
540 vty_write(vty, &vty->buf[vty->cp], length + 1);
541 for (i = 0; i < length; i++)
542 vty_write(vty, &telnet_backward_char, 1);
543
544 vty->cp++;
545 vty->length++;
546
547 vty->buf[vty->length] = '\0';
548 }
549
550 /* Self insert character 'c' in overwrite mode. */
551 static void vty_self_insert_overwrite(struct vty *vty, char c)
552 {
553 if (vty->cp == vty->length) {
554 vty_self_insert(vty, c);
555 return;
556 }
557
558 vty->buf[vty->cp++] = c;
559 vty_write(vty, &c, 1);
560 }
561
562 /**
563 * Insert a string into vty->buf at the current cursor position.
564 *
565 * If the resultant string would be larger than VTY_BUFSIZ it is
566 * truncated to fit.
567 */
568 static void vty_insert_word_overwrite(struct vty *vty, char *str)
569 {
570 if (vty->cp == VTY_BUFSIZ)
571 return;
572
573 size_t nwrite = MIN((int)strlen(str), VTY_BUFSIZ - vty->cp - 1);
574 memcpy(&vty->buf[vty->cp], str, nwrite);
575 vty->cp += nwrite;
576 vty->length = MAX(vty->cp, vty->length);
577 vty->buf[vty->length] = '\0';
578 vty_write(vty, str, nwrite);
579 }
580
581 /* Forward character. */
582 static void vty_forward_char(struct vty *vty)
583 {
584 if (vty->cp < vty->length) {
585 vty_write(vty, &vty->buf[vty->cp], 1);
586 vty->cp++;
587 }
588 }
589
590 /* Backward character. */
591 static void vty_backward_char(struct vty *vty)
592 {
593 if (vty->cp > 0) {
594 vty->cp--;
595 vty_write(vty, &telnet_backward_char, 1);
596 }
597 }
598
599 /* Move to the beginning of the line. */
600 static void vty_beginning_of_line(struct vty *vty)
601 {
602 while (vty->cp)
603 vty_backward_char(vty);
604 }
605
606 /* Move to the end of the line. */
607 static void vty_end_of_line(struct vty *vty)
608 {
609 while (vty->cp < vty->length)
610 vty_forward_char(vty);
611 }
612
613 static void vty_kill_line_from_beginning(struct vty *);
614 static void vty_redraw_line(struct vty *);
615
616 /* Print command line history. This function is called from
617 vty_next_line and vty_previous_line. */
618 static void vty_history_print(struct vty *vty)
619 {
620 int length;
621
622 vty_kill_line_from_beginning(vty);
623
624 /* Get previous line from history buffer */
625 length = strlen(vty->hist[vty->hp]);
626 memcpy(vty->buf, vty->hist[vty->hp], length);
627 vty->cp = vty->length = length;
628 vty->buf[vty->length] = '\0';
629
630 /* Redraw current line */
631 vty_redraw_line(vty);
632 }
633
634 /* Show next command line history. */
635 static void vty_next_line(struct vty *vty)
636 {
637 int try_index;
638
639 if (vty->hp == vty->hindex)
640 return;
641
642 /* Try is there history exist or not. */
643 try_index = vty->hp;
644 if (try_index == (VTY_MAXHIST - 1))
645 try_index = 0;
646 else
647 try_index++;
648
649 /* If there is not history return. */
650 if (vty->hist[try_index] == NULL)
651 return;
652 else
653 vty->hp = try_index;
654
655 vty_history_print(vty);
656 }
657
658 /* Show previous command line history. */
659 static void vty_previous_line(struct vty *vty)
660 {
661 int try_index;
662
663 try_index = vty->hp;
664 if (try_index == 0)
665 try_index = VTY_MAXHIST - 1;
666 else
667 try_index--;
668
669 if (vty->hist[try_index] == NULL)
670 return;
671 else
672 vty->hp = try_index;
673
674 vty_history_print(vty);
675 }
676
677 /* This function redraw all of the command line character. */
678 static void vty_redraw_line(struct vty *vty)
679 {
680 vty_write(vty, vty->buf, vty->length);
681 vty->cp = vty->length;
682 }
683
684 /* Forward word. */
685 static void vty_forward_word(struct vty *vty)
686 {
687 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
688 vty_forward_char(vty);
689
690 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
691 vty_forward_char(vty);
692 }
693
694 /* Backward word without skipping training space. */
695 static void vty_backward_pure_word(struct vty *vty)
696 {
697 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
698 vty_backward_char(vty);
699 }
700
701 /* Backward word. */
702 static void vty_backward_word(struct vty *vty)
703 {
704 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
705 vty_backward_char(vty);
706
707 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
708 vty_backward_char(vty);
709 }
710
711 /* When '^D' is typed at the beginning of the line we move to the down
712 level. */
713 static void vty_down_level(struct vty *vty)
714 {
715 vty_out(vty, "\n");
716 cmd_exit(vty);
717 vty_prompt(vty);
718 vty->cp = 0;
719 }
720
721 /* When '^Z' is received from vty, move down to the enable mode. */
722 static void vty_end_config(struct vty *vty)
723 {
724 vty_out(vty, "\n");
725
726 switch (vty->node) {
727 case VIEW_NODE:
728 case ENABLE_NODE:
729 /* Nothing to do. */
730 break;
731 case CONFIG_NODE:
732 case INTERFACE_NODE:
733 case PW_NODE:
734 case ZEBRA_NODE:
735 case RIP_NODE:
736 case RIPNG_NODE:
737 case EIGRP_NODE:
738 case BGP_NODE:
739 case BGP_VPNV4_NODE:
740 case BGP_VPNV6_NODE:
741 case BGP_VRF_POLICY_NODE:
742 case BGP_VNC_DEFAULTS_NODE:
743 case BGP_VNC_NVE_GROUP_NODE:
744 case BGP_VNC_L2_GROUP_NODE:
745 case BGP_IPV4_NODE:
746 case BGP_IPV4M_NODE:
747 case BGP_IPV4L_NODE:
748 case BGP_IPV6_NODE:
749 case BGP_IPV6M_NODE:
750 case BGP_EVPN_NODE:
751 case BGP_IPV6L_NODE:
752 case RMAP_NODE:
753 case PBRMAP_NODE:
754 case OSPF_NODE:
755 case OSPF6_NODE:
756 case LDP_NODE:
757 case LDP_IPV4_NODE:
758 case LDP_IPV6_NODE:
759 case LDP_IPV4_IFACE_NODE:
760 case LDP_IPV6_IFACE_NODE:
761 case LDP_L2VPN_NODE:
762 case LDP_PSEUDOWIRE_NODE:
763 case ISIS_NODE:
764 case KEYCHAIN_NODE:
765 case KEYCHAIN_KEY_NODE:
766 case VTY_NODE:
767 case BGP_EVPN_VNI_NODE:
768 vty_config_unlock(vty);
769 vty->node = ENABLE_NODE;
770 break;
771 default:
772 /* Unknown node, we have to ignore it. */
773 break;
774 }
775
776 vty_prompt(vty);
777 vty->cp = 0;
778 }
779
780 /* Delete a charcter at the current point. */
781 static void vty_delete_char(struct vty *vty)
782 {
783 int i;
784 int size;
785
786 if (vty->length == 0) {
787 vty_down_level(vty);
788 return;
789 }
790
791 if (vty->cp == vty->length)
792 return; /* completion need here? */
793
794 size = vty->length - vty->cp;
795
796 vty->length--;
797 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
798 vty->buf[vty->length] = '\0';
799
800 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
801 return;
802
803 vty_write(vty, &vty->buf[vty->cp], size - 1);
804 vty_write(vty, &telnet_space_char, 1);
805
806 for (i = 0; i < size; i++)
807 vty_write(vty, &telnet_backward_char, 1);
808 }
809
810 /* Delete a character before the point. */
811 static void vty_delete_backward_char(struct vty *vty)
812 {
813 if (vty->cp == 0)
814 return;
815
816 vty_backward_char(vty);
817 vty_delete_char(vty);
818 }
819
820 /* Kill rest of line from current point. */
821 static void vty_kill_line(struct vty *vty)
822 {
823 int i;
824 int size;
825
826 size = vty->length - vty->cp;
827
828 if (size == 0)
829 return;
830
831 for (i = 0; i < size; i++)
832 vty_write(vty, &telnet_space_char, 1);
833 for (i = 0; i < size; i++)
834 vty_write(vty, &telnet_backward_char, 1);
835
836 memset(&vty->buf[vty->cp], 0, size);
837 vty->length = vty->cp;
838 }
839
840 /* Kill line from the beginning. */
841 static void vty_kill_line_from_beginning(struct vty *vty)
842 {
843 vty_beginning_of_line(vty);
844 vty_kill_line(vty);
845 }
846
847 /* Delete a word before the point. */
848 static void vty_forward_kill_word(struct vty *vty)
849 {
850 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
851 vty_delete_char(vty);
852 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
853 vty_delete_char(vty);
854 }
855
856 /* Delete a word before the point. */
857 static void vty_backward_kill_word(struct vty *vty)
858 {
859 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
860 vty_delete_backward_char(vty);
861 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
862 vty_delete_backward_char(vty);
863 }
864
865 /* Transpose chars before or at the point. */
866 static void vty_transpose_chars(struct vty *vty)
867 {
868 char c1, c2;
869
870 /* If length is short or point is near by the beginning of line then
871 return. */
872 if (vty->length < 2 || vty->cp < 1)
873 return;
874
875 /* In case of point is located at the end of the line. */
876 if (vty->cp == vty->length) {
877 c1 = vty->buf[vty->cp - 1];
878 c2 = vty->buf[vty->cp - 2];
879
880 vty_backward_char(vty);
881 vty_backward_char(vty);
882 vty_self_insert_overwrite(vty, c1);
883 vty_self_insert_overwrite(vty, c2);
884 } else {
885 c1 = vty->buf[vty->cp];
886 c2 = vty->buf[vty->cp - 1];
887
888 vty_backward_char(vty);
889 vty_self_insert_overwrite(vty, c1);
890 vty_self_insert_overwrite(vty, c2);
891 }
892 }
893
894 /* Do completion at vty interface. */
895 static void vty_complete_command(struct vty *vty)
896 {
897 int i;
898 int ret;
899 char **matched = NULL;
900 vector vline;
901
902 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
903 return;
904
905 vline = cmd_make_strvec(vty->buf);
906 if (vline == NULL)
907 return;
908
909 /* In case of 'help \t'. */
910 if (isspace((int)vty->buf[vty->length - 1]))
911 vector_set(vline, NULL);
912
913 matched = cmd_complete_command(vline, vty, &ret);
914
915 cmd_free_strvec(vline);
916
917 vty_out(vty, "\n");
918 switch (ret) {
919 case CMD_ERR_AMBIGUOUS:
920 vty_out(vty, "%% Ambiguous command.\n");
921 vty_prompt(vty);
922 vty_redraw_line(vty);
923 break;
924 case CMD_ERR_NO_MATCH:
925 /* vty_out (vty, "%% There is no matched command.\n"); */
926 vty_prompt(vty);
927 vty_redraw_line(vty);
928 break;
929 case CMD_COMPLETE_FULL_MATCH:
930 if (!matched[0]) {
931 /* 2016-11-28 equinox -- need to debug, SEGV here */
932 vty_out(vty, "%% CLI BUG: FULL_MATCH with NULL str\n");
933 vty_prompt(vty);
934 vty_redraw_line(vty);
935 break;
936 }
937 vty_prompt(vty);
938 vty_redraw_line(vty);
939 vty_backward_pure_word(vty);
940 vty_insert_word_overwrite(vty, matched[0]);
941 vty_self_insert(vty, ' ');
942 XFREE(MTYPE_COMPLETION, matched[0]);
943 break;
944 case CMD_COMPLETE_MATCH:
945 vty_prompt(vty);
946 vty_redraw_line(vty);
947 vty_backward_pure_word(vty);
948 vty_insert_word_overwrite(vty, matched[0]);
949 XFREE(MTYPE_COMPLETION, matched[0]);
950 break;
951 case CMD_COMPLETE_LIST_MATCH:
952 for (i = 0; matched[i] != NULL; i++) {
953 if (i != 0 && ((i % 6) == 0))
954 vty_out(vty, "\n");
955 vty_out(vty, "%-10s ", matched[i]);
956 XFREE(MTYPE_COMPLETION, matched[i]);
957 }
958 vty_out(vty, "\n");
959
960 vty_prompt(vty);
961 vty_redraw_line(vty);
962 break;
963 case CMD_ERR_NOTHING_TODO:
964 vty_prompt(vty);
965 vty_redraw_line(vty);
966 break;
967 default:
968 break;
969 }
970 if (matched)
971 XFREE(MTYPE_TMP, matched);
972 }
973
974 static void vty_describe_fold(struct vty *vty, int cmd_width,
975 unsigned int desc_width, struct cmd_token *token)
976 {
977 char *buf;
978 const char *cmd, *p;
979 int pos;
980
981 cmd = token->text;
982
983 if (desc_width <= 0) {
984 vty_out(vty, " %-*s %s\n", cmd_width, cmd, token->desc);
985 return;
986 }
987
988 buf = XCALLOC(MTYPE_TMP, strlen(token->desc) + 1);
989
990 for (p = token->desc; strlen(p) > desc_width; p += pos + 1) {
991 for (pos = desc_width; pos > 0; pos--)
992 if (*(p + pos) == ' ')
993 break;
994
995 if (pos == 0)
996 break;
997
998 strncpy(buf, p, pos);
999 buf[pos] = '\0';
1000 vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf);
1001
1002 cmd = "";
1003 }
1004
1005 vty_out(vty, " %-*s %s\n", cmd_width, cmd, p);
1006
1007 XFREE(MTYPE_TMP, buf);
1008 }
1009
1010 /* Describe matched command function. */
1011 static void vty_describe_command(struct vty *vty)
1012 {
1013 int ret;
1014 vector vline;
1015 vector describe;
1016 unsigned int i, width, desc_width;
1017 struct cmd_token *token, *token_cr = NULL;
1018
1019 vline = cmd_make_strvec(vty->buf);
1020
1021 /* In case of '> ?'. */
1022 if (vline == NULL) {
1023 vline = vector_init(1);
1024 vector_set(vline, NULL);
1025 } else if (isspace((int)vty->buf[vty->length - 1]))
1026 vector_set(vline, NULL);
1027
1028 describe = cmd_describe_command(vline, vty, &ret);
1029
1030 vty_out(vty, "\n");
1031
1032 /* Ambiguous error. */
1033 switch (ret) {
1034 case CMD_ERR_AMBIGUOUS:
1035 vty_out(vty, "%% Ambiguous command.\n");
1036 goto out;
1037 break;
1038 case CMD_ERR_NO_MATCH:
1039 vty_out(vty, "%% There is no matched command.\n");
1040 goto out;
1041 break;
1042 }
1043
1044 /* Get width of command string. */
1045 width = 0;
1046 for (i = 0; i < vector_active(describe); i++)
1047 if ((token = vector_slot(describe, i)) != NULL) {
1048 unsigned int len;
1049
1050 if (token->text[0] == '\0')
1051 continue;
1052
1053 len = strlen(token->text);
1054
1055 if (width < len)
1056 width = len;
1057 }
1058
1059 /* Get width of description string. */
1060 desc_width = vty->width - (width + 6);
1061
1062 /* Print out description. */
1063 for (i = 0; i < vector_active(describe); i++)
1064 if ((token = vector_slot(describe, i)) != NULL) {
1065 if (token->text[0] == '\0')
1066 continue;
1067
1068 if (strcmp(token->text, CMD_CR_TEXT) == 0) {
1069 token_cr = token;
1070 continue;
1071 }
1072
1073 if (!token->desc)
1074 vty_out(vty, " %-s\n", token->text);
1075 else if (desc_width >= strlen(token->desc))
1076 vty_out(vty, " %-*s %s\n", width, token->text,
1077 token->desc);
1078 else
1079 vty_describe_fold(vty, width, desc_width,
1080 token);
1081
1082 if (IS_VARYING_TOKEN(token->type)) {
1083 const char *ref = vector_slot(
1084 vline, vector_active(vline) - 1);
1085
1086 vector varcomps = vector_init(VECTOR_MIN_SIZE);
1087 cmd_variable_complete(token, ref, varcomps);
1088
1089 if (vector_active(varcomps) > 0) {
1090 char *ac = cmd_variable_comp2str(
1091 varcomps, vty->width);
1092 vty_out(vty, "%s\n", ac);
1093 XFREE(MTYPE_TMP, ac);
1094 }
1095
1096 vector_free(varcomps);
1097 }
1098 #if 0
1099 vty_out (vty, " %-*s %s\n", width
1100 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1101 desc->str ? desc->str : "");
1102 #endif /* 0 */
1103 }
1104
1105 if ((token = token_cr)) {
1106 if (!token->desc)
1107 vty_out(vty, " %-s\n", token->text);
1108 else if (desc_width >= strlen(token->desc))
1109 vty_out(vty, " %-*s %s\n", width, token->text,
1110 token->desc);
1111 else
1112 vty_describe_fold(vty, width, desc_width, token);
1113 }
1114
1115 out:
1116 cmd_free_strvec(vline);
1117 if (describe)
1118 vector_free(describe);
1119
1120 vty_prompt(vty);
1121 vty_redraw_line(vty);
1122 }
1123
1124 static void vty_clear_buf(struct vty *vty)
1125 {
1126 memset(vty->buf, 0, vty->max);
1127 }
1128
1129 /* ^C stop current input and do not add command line to the history. */
1130 static void vty_stop_input(struct vty *vty)
1131 {
1132 vty->cp = vty->length = 0;
1133 vty_clear_buf(vty);
1134 vty_out(vty, "\n");
1135
1136 switch (vty->node) {
1137 case VIEW_NODE:
1138 case ENABLE_NODE:
1139 /* Nothing to do. */
1140 break;
1141 case CONFIG_NODE:
1142 case INTERFACE_NODE:
1143 case PW_NODE:
1144 case ZEBRA_NODE:
1145 case RIP_NODE:
1146 case RIPNG_NODE:
1147 case EIGRP_NODE:
1148 case BGP_NODE:
1149 case RMAP_NODE:
1150 case PBRMAP_NODE:
1151 case OSPF_NODE:
1152 case OSPF6_NODE:
1153 case LDP_NODE:
1154 case LDP_IPV4_NODE:
1155 case LDP_IPV6_NODE:
1156 case LDP_IPV4_IFACE_NODE:
1157 case LDP_IPV6_IFACE_NODE:
1158 case LDP_L2VPN_NODE:
1159 case LDP_PSEUDOWIRE_NODE:
1160 case ISIS_NODE:
1161 case KEYCHAIN_NODE:
1162 case KEYCHAIN_KEY_NODE:
1163 case VTY_NODE:
1164 vty_config_unlock(vty);
1165 vty->node = ENABLE_NODE;
1166 break;
1167 default:
1168 /* Unknown node, we have to ignore it. */
1169 break;
1170 }
1171 vty_prompt(vty);
1172
1173 /* Set history pointer to the latest one. */
1174 vty->hp = vty->hindex;
1175 }
1176
1177 /* Add current command line to the history buffer. */
1178 static void vty_hist_add(struct vty *vty)
1179 {
1180 int index;
1181
1182 if (vty->length == 0)
1183 return;
1184
1185 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1186
1187 /* Ignore the same string as previous one. */
1188 if (vty->hist[index])
1189 if (strcmp(vty->buf, vty->hist[index]) == 0) {
1190 vty->hp = vty->hindex;
1191 return;
1192 }
1193
1194 /* Insert history entry. */
1195 if (vty->hist[vty->hindex])
1196 XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1197 vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
1198
1199 /* History index rotation. */
1200 vty->hindex++;
1201 if (vty->hindex == VTY_MAXHIST)
1202 vty->hindex = 0;
1203
1204 vty->hp = vty->hindex;
1205 }
1206
1207 /* #define TELNET_OPTION_DEBUG */
1208
1209 /* Get telnet window size. */
1210 static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
1211 {
1212 #ifdef TELNET_OPTION_DEBUG
1213 int i;
1214
1215 for (i = 0; i < nbytes; i++) {
1216 switch (buf[i]) {
1217 case IAC:
1218 vty_out(vty, "IAC ");
1219 break;
1220 case WILL:
1221 vty_out(vty, "WILL ");
1222 break;
1223 case WONT:
1224 vty_out(vty, "WONT ");
1225 break;
1226 case DO:
1227 vty_out(vty, "DO ");
1228 break;
1229 case DONT:
1230 vty_out(vty, "DONT ");
1231 break;
1232 case SB:
1233 vty_out(vty, "SB ");
1234 break;
1235 case SE:
1236 vty_out(vty, "SE ");
1237 break;
1238 case TELOPT_ECHO:
1239 vty_out(vty, "TELOPT_ECHO \n");
1240 break;
1241 case TELOPT_SGA:
1242 vty_out(vty, "TELOPT_SGA \n");
1243 break;
1244 case TELOPT_NAWS:
1245 vty_out(vty, "TELOPT_NAWS \n");
1246 break;
1247 default:
1248 vty_out(vty, "%x ", buf[i]);
1249 break;
1250 }
1251 }
1252 vty_out(vty, "\n");
1253
1254 #endif /* TELNET_OPTION_DEBUG */
1255
1256 switch (buf[0]) {
1257 case SB:
1258 vty->sb_len = 0;
1259 vty->iac_sb_in_progress = 1;
1260 return 0;
1261 break;
1262 case SE: {
1263 if (!vty->iac_sb_in_progress)
1264 return 0;
1265
1266 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) {
1267 vty->iac_sb_in_progress = 0;
1268 return 0;
1269 }
1270 switch (vty->sb_buf[0]) {
1271 case TELOPT_NAWS:
1272 if (vty->sb_len != TELNET_NAWS_SB_LEN)
1273 zlog_warn(
1274 "RFC 1073 violation detected: telnet NAWS option "
1275 "should send %d characters, but we received %lu",
1276 TELNET_NAWS_SB_LEN,
1277 (unsigned long)vty->sb_len);
1278 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1279 zlog_err(
1280 "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
1281 "too small to handle the telnet NAWS option",
1282 (unsigned long)sizeof(vty->sb_buf),
1283 TELNET_NAWS_SB_LEN);
1284 else {
1285 vty->width = ((vty->sb_buf[1] << 8)
1286 | vty->sb_buf[2]);
1287 vty->height = ((vty->sb_buf[3] << 8)
1288 | vty->sb_buf[4]);
1289 #ifdef TELNET_OPTION_DEBUG
1290 vty_out(vty,
1291 "TELNET NAWS window size negotiation completed: "
1292 "width %d, height %d\n",
1293 vty->width, vty->height);
1294 #endif
1295 }
1296 break;
1297 }
1298 vty->iac_sb_in_progress = 0;
1299 return 0;
1300 break;
1301 }
1302 default:
1303 break;
1304 }
1305 return 1;
1306 }
1307
1308 /* Execute current command line. */
1309 static int vty_execute(struct vty *vty)
1310 {
1311 int ret;
1312
1313 ret = CMD_SUCCESS;
1314
1315 switch (vty->node) {
1316 case AUTH_NODE:
1317 case AUTH_ENABLE_NODE:
1318 vty_auth(vty, vty->buf);
1319 break;
1320 default:
1321 ret = vty_command(vty, vty->buf);
1322 if (vty->type == VTY_TERM)
1323 vty_hist_add(vty);
1324 break;
1325 }
1326
1327 /* Clear command line buffer. */
1328 vty->cp = vty->length = 0;
1329 vty_clear_buf(vty);
1330
1331 if (vty->status != VTY_CLOSE)
1332 vty_prompt(vty);
1333
1334 return ret;
1335 }
1336
1337 #define CONTROL(X) ((X) - '@')
1338 #define VTY_NORMAL 0
1339 #define VTY_PRE_ESCAPE 1
1340 #define VTY_ESCAPE 2
1341
1342 /* Escape character command map. */
1343 static void vty_escape_map(unsigned char c, struct vty *vty)
1344 {
1345 switch (c) {
1346 case ('A'):
1347 vty_previous_line(vty);
1348 break;
1349 case ('B'):
1350 vty_next_line(vty);
1351 break;
1352 case ('C'):
1353 vty_forward_char(vty);
1354 break;
1355 case ('D'):
1356 vty_backward_char(vty);
1357 break;
1358 default:
1359 break;
1360 }
1361
1362 /* Go back to normal mode. */
1363 vty->escape = VTY_NORMAL;
1364 }
1365
1366 /* Quit print out to the buffer. */
1367 static void vty_buffer_reset(struct vty *vty)
1368 {
1369 buffer_reset(vty->obuf);
1370 vty_prompt(vty);
1371 vty_redraw_line(vty);
1372 }
1373
1374 /* Read data via vty socket. */
1375 static int vty_read(struct thread *thread)
1376 {
1377 int i;
1378 int nbytes;
1379 unsigned char buf[VTY_READ_BUFSIZ];
1380
1381 int vty_sock = THREAD_FD(thread);
1382 struct vty *vty = THREAD_ARG(thread);
1383 vty->t_read = NULL;
1384
1385 /* Read raw data from socket */
1386 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1387 if (nbytes < 0) {
1388 if (ERRNO_IO_RETRY(errno)) {
1389 vty_event(VTY_READ, vty_sock, vty);
1390 return 0;
1391 }
1392 vty->monitor = 0; /* disable monitoring to avoid
1393 infinite recursion */
1394 zlog_warn(
1395 "%s: read error on vty client fd %d, closing: %s",
1396 __func__, vty->fd, safe_strerror(errno));
1397 buffer_reset(vty->obuf);
1398 }
1399 vty->status = VTY_CLOSE;
1400 }
1401
1402 for (i = 0; i < nbytes; i++) {
1403 if (buf[i] == IAC) {
1404 if (!vty->iac) {
1405 vty->iac = 1;
1406 continue;
1407 } else {
1408 vty->iac = 0;
1409 }
1410 }
1411
1412 if (vty->iac_sb_in_progress && !vty->iac) {
1413 if (vty->sb_len < sizeof(vty->sb_buf))
1414 vty->sb_buf[vty->sb_len] = buf[i];
1415 vty->sb_len++;
1416 continue;
1417 }
1418
1419 if (vty->iac) {
1420 /* In case of telnet command */
1421 int ret = 0;
1422 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1423 vty->iac = 0;
1424 i += ret;
1425 continue;
1426 }
1427
1428
1429 if (vty->status == VTY_MORE) {
1430 switch (buf[i]) {
1431 case CONTROL('C'):
1432 case 'q':
1433 case 'Q':
1434 vty_buffer_reset(vty);
1435 break;
1436 #if 0 /* More line does not work for "show ip bgp". */
1437 case '\n':
1438 case '\r':
1439 vty->status = VTY_MORELINE;
1440 break;
1441 #endif
1442 default:
1443 break;
1444 }
1445 continue;
1446 }
1447
1448 /* Escape character. */
1449 if (vty->escape == VTY_ESCAPE) {
1450 vty_escape_map(buf[i], vty);
1451 continue;
1452 }
1453
1454 /* Pre-escape status. */
1455 if (vty->escape == VTY_PRE_ESCAPE) {
1456 switch (buf[i]) {
1457 case '[':
1458 vty->escape = VTY_ESCAPE;
1459 break;
1460 case 'b':
1461 vty_backward_word(vty);
1462 vty->escape = VTY_NORMAL;
1463 break;
1464 case 'f':
1465 vty_forward_word(vty);
1466 vty->escape = VTY_NORMAL;
1467 break;
1468 case 'd':
1469 vty_forward_kill_word(vty);
1470 vty->escape = VTY_NORMAL;
1471 break;
1472 case CONTROL('H'):
1473 case 0x7f:
1474 vty_backward_kill_word(vty);
1475 vty->escape = VTY_NORMAL;
1476 break;
1477 default:
1478 vty->escape = VTY_NORMAL;
1479 break;
1480 }
1481 continue;
1482 }
1483
1484 switch (buf[i]) {
1485 case CONTROL('A'):
1486 vty_beginning_of_line(vty);
1487 break;
1488 case CONTROL('B'):
1489 vty_backward_char(vty);
1490 break;
1491 case CONTROL('C'):
1492 vty_stop_input(vty);
1493 break;
1494 case CONTROL('D'):
1495 vty_delete_char(vty);
1496 break;
1497 case CONTROL('E'):
1498 vty_end_of_line(vty);
1499 break;
1500 case CONTROL('F'):
1501 vty_forward_char(vty);
1502 break;
1503 case CONTROL('H'):
1504 case 0x7f:
1505 vty_delete_backward_char(vty);
1506 break;
1507 case CONTROL('K'):
1508 vty_kill_line(vty);
1509 break;
1510 case CONTROL('N'):
1511 vty_next_line(vty);
1512 break;
1513 case CONTROL('P'):
1514 vty_previous_line(vty);
1515 break;
1516 case CONTROL('T'):
1517 vty_transpose_chars(vty);
1518 break;
1519 case CONTROL('U'):
1520 vty_kill_line_from_beginning(vty);
1521 break;
1522 case CONTROL('W'):
1523 vty_backward_kill_word(vty);
1524 break;
1525 case CONTROL('Z'):
1526 vty_end_config(vty);
1527 break;
1528 case '\n':
1529 case '\r':
1530 vty_out(vty, "\n");
1531 vty_execute(vty);
1532 break;
1533 case '\t':
1534 vty_complete_command(vty);
1535 break;
1536 case '?':
1537 if (vty->node == AUTH_NODE
1538 || vty->node == AUTH_ENABLE_NODE)
1539 vty_self_insert(vty, buf[i]);
1540 else
1541 vty_describe_command(vty);
1542 break;
1543 case '\033':
1544 if (i + 1 < nbytes && buf[i + 1] == '[') {
1545 vty->escape = VTY_ESCAPE;
1546 i++;
1547 } else
1548 vty->escape = VTY_PRE_ESCAPE;
1549 break;
1550 default:
1551 if (buf[i] > 31 && buf[i] < 127)
1552 vty_self_insert(vty, buf[i]);
1553 break;
1554 }
1555 }
1556
1557 /* Check status. */
1558 if (vty->status == VTY_CLOSE)
1559 vty_close(vty);
1560 else {
1561 vty_event(VTY_WRITE, vty->wfd, vty);
1562 vty_event(VTY_READ, vty_sock, vty);
1563 }
1564 return 0;
1565 }
1566
1567 /* Flush buffer to the vty. */
1568 static int vty_flush(struct thread *thread)
1569 {
1570 int erase;
1571 buffer_status_t flushrc;
1572 int vty_sock = THREAD_FD(thread);
1573 struct vty *vty = THREAD_ARG(thread);
1574
1575 vty->t_write = NULL;
1576
1577 /* Tempolary disable read thread. */
1578 if ((vty->lines == 0) && vty->t_read) {
1579 thread_cancel(vty->t_read);
1580 vty->t_read = NULL;
1581 }
1582
1583 /* Function execution continue. */
1584 erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
1585
1586 /* N.B. if width is 0, that means we don't know the window size. */
1587 if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
1588 flushrc = buffer_flush_available(vty->obuf, vty_sock);
1589 else if (vty->status == VTY_MORELINE)
1590 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
1591 1, erase, 0);
1592 else
1593 flushrc = buffer_flush_window(
1594 vty->obuf, vty_sock, vty->width,
1595 vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
1596 switch (flushrc) {
1597 case BUFFER_ERROR:
1598 vty->monitor =
1599 0; /* disable monitoring to avoid infinite recursion */
1600 zlog_warn("buffer_flush failed on vty client fd %d, closing",
1601 vty->fd);
1602 buffer_reset(vty->obuf);
1603 vty_close(vty);
1604 return 0;
1605 case BUFFER_EMPTY:
1606 if (vty->status == VTY_CLOSE)
1607 vty_close(vty);
1608 else {
1609 vty->status = VTY_NORMAL;
1610 if (vty->lines == 0)
1611 vty_event(VTY_READ, vty_sock, vty);
1612 }
1613 break;
1614 case BUFFER_PENDING:
1615 /* There is more data waiting to be written. */
1616 vty->status = VTY_MORE;
1617 if (vty->lines == 0)
1618 vty_event(VTY_WRITE, vty_sock, vty);
1619 break;
1620 }
1621
1622 return 0;
1623 }
1624
1625 /* Allocate new vty struct. */
1626 struct vty *vty_new()
1627 {
1628 struct vty *new = XCALLOC(MTYPE_VTY, sizeof(struct vty));
1629
1630 new->fd = new->wfd = -1;
1631 new->obuf = buffer_new(0); /* Use default buffer size. */
1632 new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
1633 new->error_buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
1634 new->max = VTY_BUFSIZ;
1635
1636 return new;
1637 }
1638
1639
1640 /* allocate and initialise vty */
1641 static struct vty *vty_new_init(int vty_sock)
1642 {
1643 struct vty *vty;
1644
1645 vty = vty_new();
1646 vty->fd = vty_sock;
1647 vty->wfd = vty_sock;
1648 vty->type = VTY_TERM;
1649 vty->node = AUTH_NODE;
1650 vty->fail = 0;
1651 vty->cp = 0;
1652 vty_clear_buf(vty);
1653 vty->length = 0;
1654 memset(vty->hist, 0, sizeof(vty->hist));
1655 vty->hp = 0;
1656 vty->hindex = 0;
1657 vector_set_index(vtyvec, vty_sock, vty);
1658 vty->status = VTY_NORMAL;
1659 vty->lines = -1;
1660 vty->iac = 0;
1661 vty->iac_sb_in_progress = 0;
1662 vty->sb_len = 0;
1663
1664 return vty;
1665 }
1666
1667 /* Create new vty structure. */
1668 static struct vty *vty_create(int vty_sock, union sockunion *su)
1669 {
1670 char buf[SU_ADDRSTRLEN];
1671 struct vty *vty;
1672
1673 sockunion2str(su, buf, SU_ADDRSTRLEN);
1674
1675 /* Allocate new vty structure and set up default values. */
1676 vty = vty_new_init(vty_sock);
1677
1678 /* configurable parameters not part of basic init */
1679 vty->v_timeout = vty_timeout_val;
1680 strcpy(vty->address, buf);
1681 if (no_password_check) {
1682 if (host.advanced)
1683 vty->node = ENABLE_NODE;
1684 else
1685 vty->node = VIEW_NODE;
1686 }
1687 if (host.lines >= 0)
1688 vty->lines = host.lines;
1689
1690 if (!no_password_check) {
1691 /* Vty is not available if password isn't set. */
1692 if (host.password == NULL && host.password_encrypt == NULL) {
1693 vty_out(vty, "Vty password is not set.\n");
1694 vty->status = VTY_CLOSE;
1695 vty_close(vty);
1696 return NULL;
1697 }
1698 }
1699
1700 /* Say hello to the world. */
1701 vty_hello(vty);
1702 if (!no_password_check)
1703 vty_out(vty, "\nUser Access Verification\n\n");
1704
1705 /* Setting up terminal. */
1706 vty_will_echo(vty);
1707 vty_will_suppress_go_ahead(vty);
1708
1709 vty_dont_linemode(vty);
1710 vty_do_window_size(vty);
1711 /* vty_dont_lflow_ahead (vty); */
1712
1713 vty_prompt(vty);
1714
1715 /* Add read/write thread. */
1716 vty_event(VTY_WRITE, vty_sock, vty);
1717 vty_event(VTY_READ, vty_sock, vty);
1718
1719 return vty;
1720 }
1721
1722 /* create vty for stdio */
1723 static struct termios stdio_orig_termios;
1724 static struct vty *stdio_vty = NULL;
1725 static bool stdio_termios = false;
1726 static void (*stdio_vty_atclose)(int isexit);
1727
1728 static void vty_stdio_reset(int isexit)
1729 {
1730 if (stdio_vty) {
1731 if (stdio_termios)
1732 tcsetattr(0, TCSANOW, &stdio_orig_termios);
1733 stdio_termios = false;
1734
1735 stdio_vty = NULL;
1736
1737 if (stdio_vty_atclose)
1738 stdio_vty_atclose(isexit);
1739 stdio_vty_atclose = NULL;
1740 }
1741 }
1742
1743 static void vty_stdio_atexit(void)
1744 {
1745 vty_stdio_reset(1);
1746 }
1747
1748 void vty_stdio_suspend(void)
1749 {
1750 if (!stdio_vty)
1751 return;
1752
1753 if (stdio_vty->t_write)
1754 thread_cancel(stdio_vty->t_write);
1755 if (stdio_vty->t_read)
1756 thread_cancel(stdio_vty->t_read);
1757 if (stdio_vty->t_timeout)
1758 thread_cancel(stdio_vty->t_timeout);
1759
1760 if (stdio_termios)
1761 tcsetattr(0, TCSANOW, &stdio_orig_termios);
1762 stdio_termios = false;
1763 }
1764
1765 void vty_stdio_resume(void)
1766 {
1767 if (!stdio_vty)
1768 return;
1769
1770 if (!tcgetattr(0, &stdio_orig_termios)) {
1771 struct termios termios;
1772
1773 termios = stdio_orig_termios;
1774 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
1775 | IGNCR | ICRNL | IXON);
1776 termios.c_oflag &= ~OPOST;
1777 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
1778 termios.c_cflag &= ~(CSIZE | PARENB);
1779 termios.c_cflag |= CS8;
1780 tcsetattr(0, TCSANOW, &termios);
1781 stdio_termios = true;
1782 }
1783
1784 vty_prompt(stdio_vty);
1785
1786 /* Add read/write thread. */
1787 vty_event(VTY_WRITE, 1, stdio_vty);
1788 vty_event(VTY_READ, 0, stdio_vty);
1789 }
1790
1791 void vty_stdio_close(void)
1792 {
1793 if (!stdio_vty)
1794 return;
1795 vty_close(stdio_vty);
1796 }
1797
1798 struct vty *vty_stdio(void (*atclose)(int isexit))
1799 {
1800 struct vty *vty;
1801
1802 /* refuse creating two vtys on stdio */
1803 if (stdio_vty)
1804 return NULL;
1805
1806 vty = stdio_vty = vty_new_init(0);
1807 stdio_vty_atclose = atclose;
1808 vty->wfd = 1;
1809
1810 /* always have stdio vty in a known _unchangeable_ state, don't want
1811 * config
1812 * to have any effect here to make sure scripting this works as intended
1813 */
1814 vty->node = ENABLE_NODE;
1815 vty->v_timeout = 0;
1816 strcpy(vty->address, "console");
1817
1818 vty_stdio_resume();
1819 return vty;
1820 }
1821
1822 /* Accept connection from the network. */
1823 static int vty_accept(struct thread *thread)
1824 {
1825 int vty_sock;
1826 union sockunion su;
1827 int ret;
1828 unsigned int on;
1829 int accept_sock;
1830 struct prefix p;
1831 struct access_list *acl = NULL;
1832 char buf[SU_ADDRSTRLEN];
1833
1834 accept_sock = THREAD_FD(thread);
1835
1836 /* We continue hearing vty socket. */
1837 vty_event(VTY_SERV, accept_sock, NULL);
1838
1839 memset(&su, 0, sizeof(union sockunion));
1840
1841 /* We can handle IPv4 or IPv6 socket. */
1842 vty_sock = sockunion_accept(accept_sock, &su);
1843 if (vty_sock < 0) {
1844 zlog_warn("can't accept vty socket : %s", safe_strerror(errno));
1845 return -1;
1846 }
1847 set_nonblocking(vty_sock);
1848 set_cloexec(vty_sock);
1849
1850 sockunion2hostprefix(&su, &p);
1851
1852 /* VTY's accesslist apply. */
1853 if (p.family == AF_INET && vty_accesslist_name) {
1854 if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
1855 && (access_list_apply(acl, &p) == FILTER_DENY)) {
1856 zlog_info("Vty connection refused from %s",
1857 sockunion2str(&su, buf, SU_ADDRSTRLEN));
1858 close(vty_sock);
1859
1860 /* continue accepting connections */
1861 vty_event(VTY_SERV, accept_sock, NULL);
1862
1863 return 0;
1864 }
1865 }
1866
1867 /* VTY's ipv6 accesslist apply. */
1868 if (p.family == AF_INET6 && vty_ipv6_accesslist_name) {
1869 if ((acl = access_list_lookup(AFI_IP6,
1870 vty_ipv6_accesslist_name))
1871 && (access_list_apply(acl, &p) == FILTER_DENY)) {
1872 zlog_info("Vty connection refused from %s",
1873 sockunion2str(&su, buf, SU_ADDRSTRLEN));
1874 close(vty_sock);
1875
1876 /* continue accepting connections */
1877 vty_event(VTY_SERV, accept_sock, NULL);
1878
1879 return 0;
1880 }
1881 }
1882
1883 on = 1;
1884 ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on,
1885 sizeof(on));
1886 if (ret < 0)
1887 zlog_info("can't set sockopt to vty_sock : %s",
1888 safe_strerror(errno));
1889
1890 zlog_info("Vty connection from %s",
1891 sockunion2str(&su, buf, SU_ADDRSTRLEN));
1892
1893 vty_create(vty_sock, &su);
1894
1895 return 0;
1896 }
1897
1898 static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
1899 {
1900 int ret;
1901 struct addrinfo req;
1902 struct addrinfo *ainfo;
1903 struct addrinfo *ainfo_save;
1904 int sock;
1905 char port_str[BUFSIZ];
1906
1907 memset(&req, 0, sizeof(struct addrinfo));
1908 req.ai_flags = AI_PASSIVE;
1909 req.ai_family = AF_UNSPEC;
1910 req.ai_socktype = SOCK_STREAM;
1911 sprintf(port_str, "%d", port);
1912 port_str[sizeof(port_str) - 1] = '\0';
1913
1914 ret = getaddrinfo(hostname, port_str, &req, &ainfo);
1915
1916 if (ret != 0) {
1917 zlog_err("getaddrinfo failed: %s", gai_strerror(ret));
1918 exit(1);
1919 }
1920
1921 ainfo_save = ainfo;
1922
1923 do {
1924 if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
1925 continue;
1926
1927 sock = socket(ainfo->ai_family, ainfo->ai_socktype,
1928 ainfo->ai_protocol);
1929 if (sock < 0)
1930 continue;
1931
1932 sockopt_v6only(ainfo->ai_family, sock);
1933 sockopt_reuseaddr(sock);
1934 sockopt_reuseport(sock);
1935 set_cloexec(sock);
1936
1937 ret = bind(sock, ainfo->ai_addr, ainfo->ai_addrlen);
1938 if (ret < 0) {
1939 close(sock); /* Avoid sd leak. */
1940 continue;
1941 }
1942
1943 ret = listen(sock, 3);
1944 if (ret < 0) {
1945 close(sock); /* Avoid sd leak. */
1946 continue;
1947 }
1948
1949 vty_event(VTY_SERV, sock, NULL);
1950 } while ((ainfo = ainfo->ai_next) != NULL);
1951
1952 freeaddrinfo(ainfo_save);
1953 }
1954
1955 #ifdef VTYSH
1956 /* For sockaddr_un. */
1957 #include <sys/un.h>
1958
1959 /* VTY shell UNIX domain socket. */
1960 static void vty_serv_un(const char *path)
1961 {
1962 int ret;
1963 int sock, len;
1964 struct sockaddr_un serv;
1965 mode_t old_mask;
1966 struct zprivs_ids_t ids;
1967
1968 /* First of all, unlink existing socket */
1969 unlink(path);
1970
1971 /* Set umask */
1972 old_mask = umask(0007);
1973
1974 /* Make UNIX domain socket. */
1975 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1976 if (sock < 0) {
1977 zlog_err("Cannot create unix stream socket: %s",
1978 safe_strerror(errno));
1979 return;
1980 }
1981
1982 /* Make server socket. */
1983 memset(&serv, 0, sizeof(struct sockaddr_un));
1984 serv.sun_family = AF_UNIX;
1985 strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
1986 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
1987 len = serv.sun_len = SUN_LEN(&serv);
1988 #else
1989 len = sizeof(serv.sun_family) + strlen(serv.sun_path);
1990 #endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
1991
1992 set_cloexec(sock);
1993
1994 ret = bind(sock, (struct sockaddr *)&serv, len);
1995 if (ret < 0) {
1996 zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
1997 close(sock); /* Avoid sd leak. */
1998 return;
1999 }
2000
2001 ret = listen(sock, 5);
2002 if (ret < 0) {
2003 zlog_err("listen(fd %d) failed: %s", sock,
2004 safe_strerror(errno));
2005 close(sock); /* Avoid sd leak. */
2006 return;
2007 }
2008
2009 umask(old_mask);
2010
2011 zprivs_get_ids(&ids);
2012
2013 /* Hack: ids.gid_vty is actually a uint, but we stored -1 in it
2014 earlier for the case when we don't need to chown the file
2015 type casting it here to make a compare */
2016 if ((int)ids.gid_vty > 0) {
2017 /* set group of socket */
2018 if (chown(path, -1, ids.gid_vty)) {
2019 zlog_err("vty_serv_un: could chown socket, %s",
2020 safe_strerror(errno));
2021 }
2022 }
2023
2024 vty_event(VTYSH_SERV, sock, NULL);
2025 }
2026
2027 /* #define VTYSH_DEBUG 1 */
2028
2029 static int vtysh_accept(struct thread *thread)
2030 {
2031 int accept_sock;
2032 int sock;
2033 int client_len;
2034 struct sockaddr_un client;
2035 struct vty *vty;
2036
2037 accept_sock = THREAD_FD(thread);
2038
2039 vty_event(VTYSH_SERV, accept_sock, NULL);
2040
2041 memset(&client, 0, sizeof(struct sockaddr_un));
2042 client_len = sizeof(struct sockaddr_un);
2043
2044 sock = accept(accept_sock, (struct sockaddr *)&client,
2045 (socklen_t *)&client_len);
2046
2047 if (sock < 0) {
2048 zlog_warn("can't accept vty socket : %s", safe_strerror(errno));
2049 return -1;
2050 }
2051
2052 if (set_nonblocking(sock) < 0) {
2053 zlog_warn(
2054 "vtysh_accept: could not set vty socket %d to non-blocking,"
2055 " %s, closing",
2056 sock, safe_strerror(errno));
2057 close(sock);
2058 return -1;
2059 }
2060 set_cloexec(sock);
2061
2062 #ifdef VTYSH_DEBUG
2063 printf("VTY shell accept\n");
2064 #endif /* VTYSH_DEBUG */
2065
2066 vty = vty_new();
2067 vty->fd = sock;
2068 vty->wfd = sock;
2069 vty->type = VTY_SHELL_SERV;
2070 vty->node = VIEW_NODE;
2071
2072 vty_event(VTYSH_READ, sock, vty);
2073
2074 return 0;
2075 }
2076
2077 static int vtysh_flush(struct vty *vty)
2078 {
2079 switch (buffer_flush_available(vty->obuf, vty->wfd)) {
2080 case BUFFER_PENDING:
2081 vty_event(VTYSH_WRITE, vty->wfd, vty);
2082 break;
2083 case BUFFER_ERROR:
2084 vty->monitor =
2085 0; /* disable monitoring to avoid infinite recursion */
2086 zlog_warn("%s: write error to fd %d, closing", __func__,
2087 vty->fd);
2088 buffer_reset(vty->obuf);
2089 vty_close(vty);
2090 return -1;
2091 break;
2092 case BUFFER_EMPTY:
2093 break;
2094 }
2095 return 0;
2096 }
2097
2098 static int vtysh_read(struct thread *thread)
2099 {
2100 int ret;
2101 int sock;
2102 int nbytes;
2103 struct vty *vty;
2104 unsigned char buf[VTY_READ_BUFSIZ];
2105 unsigned char *p;
2106 uint8_t header[4] = {0, 0, 0, 0};
2107
2108 sock = THREAD_FD(thread);
2109 vty = THREAD_ARG(thread);
2110 vty->t_read = NULL;
2111
2112 if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
2113 if (nbytes < 0) {
2114 if (ERRNO_IO_RETRY(errno)) {
2115 vty_event(VTYSH_READ, sock, vty);
2116 return 0;
2117 }
2118 vty->monitor = 0; /* disable monitoring to avoid
2119 infinite recursion */
2120 zlog_warn(
2121 "%s: read failed on vtysh client fd %d, closing: %s",
2122 __func__, sock, safe_strerror(errno));
2123 }
2124 buffer_reset(vty->obuf);
2125 vty_close(vty);
2126 #ifdef VTYSH_DEBUG
2127 printf("close vtysh\n");
2128 #endif /* VTYSH_DEBUG */
2129 return 0;
2130 }
2131
2132 #ifdef VTYSH_DEBUG
2133 printf("line: %.*s\n", nbytes, buf);
2134 #endif /* VTYSH_DEBUG */
2135
2136 if (vty->length + nbytes >= VTY_BUFSIZ) {
2137 /* Clear command line buffer. */
2138 vty->cp = vty->length = 0;
2139 vty_clear_buf(vty);
2140 vty_out(vty, "%% Command is too long.\n");
2141 } else {
2142 for (p = buf; p < buf + nbytes; p++) {
2143 vty->buf[vty->length++] = *p;
2144 if (*p == '\0') {
2145 /* Pass this line to parser. */
2146 ret = vty_execute(vty);
2147 /* Note that vty_execute clears the command buffer and resets
2148 vty->length to 0. */
2149
2150 /* Return result. */
2151 #ifdef VTYSH_DEBUG
2152 printf("result: %d\n", ret);
2153 printf("vtysh node: %d\n", vty->node);
2154 #endif /* VTYSH_DEBUG */
2155
2156 /* hack for asynchronous "write integrated"
2157 * - other commands in "buf" will be ditched
2158 * - input during pending config-write is
2159 * "unsupported" */
2160 if (ret == CMD_SUSPEND)
2161 break;
2162
2163 /* warning: watchfrr hardcodes this result write
2164 */
2165 header[3] = ret;
2166 buffer_put(vty->obuf, header, 4);
2167
2168 if (!vty->t_write && (vtysh_flush(vty) < 0))
2169 /* Try to flush results; exit if a write
2170 * error occurs. */
2171 return 0;
2172 }
2173 }
2174 }
2175
2176 if (vty->status == VTY_CLOSE)
2177 vty_close(vty);
2178 else
2179 vty_event(VTYSH_READ, sock, vty);
2180
2181 return 0;
2182 }
2183
2184 static int vtysh_write(struct thread *thread)
2185 {
2186 struct vty *vty = THREAD_ARG(thread);
2187
2188 vty->t_write = NULL;
2189 vtysh_flush(vty);
2190 return 0;
2191 }
2192
2193 #endif /* VTYSH */
2194
2195 /* Determine address family to bind. */
2196 void vty_serv_sock(const char *addr, unsigned short port, const char *path)
2197 {
2198 /* If port is set to 0, do not listen on TCP/IP at all! */
2199 if (port)
2200 vty_serv_sock_addrinfo(addr, port);
2201
2202 #ifdef VTYSH
2203 vty_serv_un(path);
2204 #endif /* VTYSH */
2205 }
2206
2207 /* Close vty interface. Warning: call this only from functions that
2208 will be careful not to access the vty afterwards (since it has
2209 now been freed). This is safest from top-level functions (called
2210 directly by the thread dispatcher). */
2211 void vty_close(struct vty *vty)
2212 {
2213 int i;
2214 bool was_stdio = false;
2215
2216 /* Cancel threads.*/
2217 if (vty->t_read)
2218 thread_cancel(vty->t_read);
2219 if (vty->t_write)
2220 thread_cancel(vty->t_write);
2221 if (vty->t_timeout)
2222 thread_cancel(vty->t_timeout);
2223
2224 /* Flush buffer. */
2225 buffer_flush_all(vty->obuf, vty->wfd);
2226
2227 /* Free input buffer. */
2228 buffer_free(vty->obuf);
2229
2230 /* Free command history. */
2231 for (i = 0; i < VTY_MAXHIST; i++)
2232 if (vty->hist[i])
2233 XFREE(MTYPE_VTY_HIST, vty->hist[i]);
2234
2235 /* Unset vector. */
2236 if (vty->fd != -1)
2237 vector_unset(vtyvec, vty->fd);
2238
2239 if (vty->wfd > 0 && vty->type == VTY_FILE)
2240 fsync(vty->wfd);
2241
2242 /* Close socket.
2243 * note check is for fd > STDERR_FILENO, not fd != -1.
2244 * We never close stdin/stdout/stderr here, because we may be
2245 * running in foreground mode with logging to stdout. Also,
2246 * additionally, we'd need to replace these fds with /dev/null. */
2247 if (vty->wfd > STDERR_FILENO && vty->wfd != vty->fd)
2248 close(vty->wfd);
2249 if (vty->fd > STDERR_FILENO) {
2250 close(vty->fd);
2251 } else
2252 was_stdio = true;
2253
2254 if (vty->buf)
2255 XFREE(MTYPE_VTY, vty->buf);
2256
2257 if (vty->error_buf)
2258 XFREE(MTYPE_VTY, vty->error_buf);
2259
2260 /* Check configure. */
2261 vty_config_unlock(vty);
2262
2263 /* OK free vty. */
2264 XFREE(MTYPE_VTY, vty);
2265
2266 if (was_stdio)
2267 vty_stdio_reset(0);
2268 }
2269
2270 /* When time out occur output message then close connection. */
2271 static int vty_timeout(struct thread *thread)
2272 {
2273 struct vty *vty;
2274
2275 vty = THREAD_ARG(thread);
2276 vty->t_timeout = NULL;
2277 vty->v_timeout = 0;
2278
2279 /* Clear buffer*/
2280 buffer_reset(vty->obuf);
2281 vty_out(vty, "\nVty connection is timed out.\n");
2282
2283 /* Close connection. */
2284 vty->status = VTY_CLOSE;
2285 vty_close(vty);
2286
2287 return 0;
2288 }
2289
2290 /* Read up configuration file from file_name. */
2291 static void vty_read_file(FILE *confp)
2292 {
2293 int ret;
2294 struct vty *vty;
2295 unsigned int line_num = 0;
2296
2297 vty = vty_new();
2298 /* vty_close won't close stderr; if some config command prints
2299 * something it'll end up there. (not ideal; it'd be beter if output
2300 * from a file-load went to logging instead. Also note that if this
2301 * function is called after daemonizing, stderr will be /dev/null.)
2302 *
2303 * vty->fd will be -1 from vty_new()
2304 */
2305 vty->wfd = STDERR_FILENO;
2306 vty->type = VTY_FILE;
2307 vty->node = CONFIG_NODE;
2308
2309 /* Execute configuration file */
2310 ret = config_from_file(vty, confp, &line_num);
2311
2312 /* Flush any previous errors before printing messages below */
2313 buffer_flush_all(vty->obuf, vty->wfd);
2314
2315 if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) {
2316 const char *message = NULL;
2317 char *nl;
2318
2319 switch (ret) {
2320 case CMD_ERR_AMBIGUOUS:
2321 message = "Ambiguous command";
2322 break;
2323 case CMD_ERR_NO_MATCH:
2324 message = "No such command";
2325 break;
2326 case CMD_WARNING:
2327 message = "Command returned Warning";
2328 break;
2329 case CMD_WARNING_CONFIG_FAILED:
2330 message = "Command returned Warning Config Failed";
2331 break;
2332 case CMD_ERR_INCOMPLETE:
2333 message = "Command returned Incomplete";
2334 break;
2335 case CMD_ERR_EXEED_ARGC_MAX:
2336 message =
2337 "Command exceeded maximum number of Arguments";
2338 break;
2339 default:
2340 message = "Command returned unhandled error message";
2341 break;
2342 }
2343
2344 nl = strchr(vty->error_buf, '\n');
2345 if (nl)
2346 *nl = '\0';
2347 zlog_err("ERROR: %s on config line %u: %s", message, line_num,
2348 vty->error_buf);
2349 }
2350
2351 vty_close(vty);
2352 }
2353
2354 static FILE *vty_use_backup_config(const char *fullpath)
2355 {
2356 char *fullpath_sav, *fullpath_tmp;
2357 FILE *ret = NULL;
2358 int tmp, sav;
2359 int c;
2360 char buffer[512];
2361
2362 fullpath_sav = malloc(strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1);
2363 strcpy(fullpath_sav, fullpath);
2364 strcat(fullpath_sav, CONF_BACKUP_EXT);
2365
2366 sav = open(fullpath_sav, O_RDONLY);
2367 if (sav < 0) {
2368 free(fullpath_sav);
2369 return NULL;
2370 }
2371
2372 fullpath_tmp = malloc(strlen(fullpath) + 8);
2373 sprintf(fullpath_tmp, "%s.XXXXXX", fullpath);
2374
2375 /* Open file to configuration write. */
2376 tmp = mkstemp(fullpath_tmp);
2377 if (tmp < 0)
2378 goto out_close_sav;
2379
2380 if (fchmod(tmp, CONFIGFILE_MASK) != 0)
2381 goto out_close;
2382
2383 while ((c = read(sav, buffer, 512)) > 0) {
2384 if (write(tmp, buffer, c) <= 0)
2385 goto out_close;
2386 }
2387 close(sav);
2388 close(tmp);
2389
2390 if (rename(fullpath_tmp, fullpath) == 0)
2391 ret = fopen(fullpath, "r");
2392 else
2393 unlink(fullpath_tmp);
2394
2395 if (0) {
2396 out_close:
2397 close(tmp);
2398 unlink(fullpath_tmp);
2399 out_close_sav:
2400 close(sav);
2401 }
2402
2403 free(fullpath_sav);
2404 free(fullpath_tmp);
2405 return ret;
2406 }
2407
2408 /* Read up configuration file from file_name. */
2409 void vty_read_config(const char *config_file, char *config_default_dir)
2410 {
2411 char cwd[MAXPATHLEN];
2412 FILE *confp = NULL;
2413 const char *fullpath;
2414 char *tmp = NULL;
2415
2416 /* If -f flag specified. */
2417 if (config_file != NULL) {
2418 if (!IS_DIRECTORY_SEP(config_file[0])) {
2419 if (getcwd(cwd, MAXPATHLEN) == NULL) {
2420 zlog_err(
2421 "Failure to determine Current Working Directory %d!",
2422 errno);
2423 exit(1);
2424 }
2425 tmp = XMALLOC(MTYPE_TMP,
2426 strlen(cwd) + strlen(config_file) + 2);
2427 sprintf(tmp, "%s/%s", cwd, config_file);
2428 fullpath = tmp;
2429 } else
2430 fullpath = config_file;
2431
2432 confp = fopen(fullpath, "r");
2433
2434 if (confp == NULL) {
2435 zlog_err("%s: failed to open configuration file %s: %s",
2436 __func__, fullpath, safe_strerror(errno));
2437
2438 confp = vty_use_backup_config(fullpath);
2439 if (confp)
2440 zlog_warn(
2441 "WARNING: using backup configuration file!");
2442 else {
2443 zlog_err("can't open configuration file [%s]",
2444 config_file);
2445 exit(1);
2446 }
2447 }
2448 } else {
2449
2450 host_config_set(config_default_dir);
2451
2452 #ifdef VTYSH
2453 int ret;
2454 struct stat conf_stat;
2455
2456 /* !!!!PLEASE LEAVE!!!!
2457 * This is NEEDED for use with vtysh -b, or else you can get
2458 * a real configuration food fight with a lot garbage in the
2459 * merged configuration file it creates coming from the per
2460 * daemon configuration files. This also allows the daemons
2461 * to start if there default configuration file is not
2462 * present or ignore them, as needed when using vtysh -b to
2463 * configure the daemons at boot - MAG
2464 */
2465
2466 /* Stat for vtysh Zebra.conf, if found startup and wait for
2467 * boot configuration
2468 */
2469
2470 if (strstr(config_default_dir, "vtysh") == NULL) {
2471 ret = stat(integrate_default, &conf_stat);
2472 if (ret >= 0)
2473 goto tmp_free_and_out;
2474 }
2475 #endif /* VTYSH */
2476 confp = fopen(config_default_dir, "r");
2477 if (confp == NULL) {
2478 zlog_err("%s: failed to open configuration file %s: %s",
2479 __func__, config_default_dir,
2480 safe_strerror(errno));
2481
2482 confp = vty_use_backup_config(config_default_dir);
2483 if (confp) {
2484 zlog_warn(
2485 "WARNING: using backup configuration file!");
2486 fullpath = config_default_dir;
2487 } else {
2488 zlog_err("can't open configuration file [%s]",
2489 config_default_dir);
2490 goto tmp_free_and_out;
2491 }
2492 } else
2493 fullpath = config_default_dir;
2494 }
2495
2496 vty_read_file(confp);
2497
2498 fclose(confp);
2499
2500 host_config_set(fullpath);
2501
2502 tmp_free_and_out:
2503 if (tmp)
2504 XFREE(MTYPE_TMP, tmp);
2505 }
2506
2507 /* Small utility function which output log to the VTY. */
2508 void vty_log(const char *level, const char *proto_str, const char *format,
2509 struct timestamp_control *ctl, va_list va)
2510 {
2511 unsigned int i;
2512 struct vty *vty;
2513
2514 if (!vtyvec)
2515 return;
2516
2517 for (i = 0; i < vector_active(vtyvec); i++)
2518 if ((vty = vector_slot(vtyvec, i)) != NULL)
2519 if (vty->monitor) {
2520 va_list ac;
2521 va_copy(ac, va);
2522 vty_log_out(vty, level, proto_str, format, ctl,
2523 ac);
2524 va_end(ac);
2525 }
2526 }
2527
2528 /* Async-signal-safe version of vty_log for fixed strings. */
2529 void vty_log_fixed(char *buf, size_t len)
2530 {
2531 unsigned int i;
2532 struct iovec iov[2];
2533 char crlf[4] = "\r\n";
2534
2535 /* vty may not have been initialised */
2536 if (!vtyvec)
2537 return;
2538
2539 iov[0].iov_base = buf;
2540 iov[0].iov_len = len;
2541 iov[1].iov_base = crlf;
2542 iov[1].iov_len = 2;
2543
2544 for (i = 0; i < vector_active(vtyvec); i++) {
2545 struct vty *vty;
2546 if (((vty = vector_slot(vtyvec, i)) != NULL) && vty->monitor)
2547 /* N.B. We don't care about the return code, since
2548 process is
2549 most likely just about to die anyway. */
2550 if (writev(vty->wfd, iov, 2) == -1) {
2551 fprintf(stderr, "Failure to writev: %d\n",
2552 errno);
2553 exit(-1);
2554 }
2555 }
2556 }
2557
2558 int vty_config_lock(struct vty *vty)
2559 {
2560 if (vty_config_is_lockless)
2561 return 1;
2562 if (vty_config == 0) {
2563 vty->config = 1;
2564 vty_config = 1;
2565 }
2566 return vty->config;
2567 }
2568
2569 int vty_config_unlock(struct vty *vty)
2570 {
2571 if (vty_config_is_lockless)
2572 return 0;
2573 if (vty_config == 1 && vty->config == 1) {
2574 vty->config = 0;
2575 vty_config = 0;
2576 }
2577 return vty->config;
2578 }
2579
2580 void vty_config_lockless(void)
2581 {
2582 vty_config_is_lockless = 1;
2583 }
2584
2585 /* Master of the threads. */
2586 static struct thread_master *vty_master;
2587
2588 static void vty_event(enum event event, int sock, struct vty *vty)
2589 {
2590 struct thread *vty_serv_thread = NULL;
2591
2592 switch (event) {
2593 case VTY_SERV:
2594 vty_serv_thread = thread_add_read(vty_master, vty_accept, vty,
2595 sock, NULL);
2596 vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
2597 break;
2598 #ifdef VTYSH
2599 case VTYSH_SERV:
2600 vty_serv_thread = thread_add_read(vty_master, vtysh_accept, vty,
2601 sock, NULL);
2602 vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
2603 break;
2604 case VTYSH_READ:
2605 vty->t_read = NULL;
2606 thread_add_read(vty_master, vtysh_read, vty, sock,
2607 &vty->t_read);
2608 break;
2609 case VTYSH_WRITE:
2610 vty->t_write = NULL;
2611 thread_add_write(vty_master, vtysh_write, vty, sock,
2612 &vty->t_write);
2613 break;
2614 #endif /* VTYSH */
2615 case VTY_READ:
2616 vty->t_read = NULL;
2617 thread_add_read(vty_master, vty_read, vty, sock, &vty->t_read);
2618
2619 /* Time out treatment. */
2620 if (vty->v_timeout) {
2621 if (vty->t_timeout)
2622 thread_cancel(vty->t_timeout);
2623 vty->t_timeout = NULL;
2624 thread_add_timer(vty_master, vty_timeout, vty,
2625 vty->v_timeout, &vty->t_timeout);
2626 }
2627 break;
2628 case VTY_WRITE:
2629 thread_add_write(vty_master, vty_flush, vty, sock,
2630 &vty->t_write);
2631 break;
2632 case VTY_TIMEOUT_RESET:
2633 if (vty->t_timeout) {
2634 thread_cancel(vty->t_timeout);
2635 vty->t_timeout = NULL;
2636 }
2637 if (vty->v_timeout) {
2638 vty->t_timeout = NULL;
2639 thread_add_timer(vty_master, vty_timeout, vty,
2640 vty->v_timeout, &vty->t_timeout);
2641 }
2642 break;
2643 }
2644 }
2645
2646 DEFUN_NOSH (config_who,
2647 config_who_cmd,
2648 "who",
2649 "Display who is on vty\n")
2650 {
2651 unsigned int i;
2652 struct vty *v;
2653
2654 for (i = 0; i < vector_active(vtyvec); i++)
2655 if ((v = vector_slot(vtyvec, i)) != NULL)
2656 vty_out(vty, "%svty[%d] connected from %s.\n",
2657 v->config ? "*" : " ", i, v->address);
2658 return CMD_SUCCESS;
2659 }
2660
2661 /* Move to vty configuration mode. */
2662 DEFUN_NOSH (line_vty,
2663 line_vty_cmd,
2664 "line vty",
2665 "Configure a terminal line\n"
2666 "Virtual terminal\n")
2667 {
2668 vty->node = VTY_NODE;
2669 return CMD_SUCCESS;
2670 }
2671
2672 /* Set time out value. */
2673 static int exec_timeout(struct vty *vty, const char *min_str,
2674 const char *sec_str)
2675 {
2676 unsigned long timeout = 0;
2677
2678 /* min_str and sec_str are already checked by parser. So it must be
2679 all digit string. */
2680 if (min_str) {
2681 timeout = strtol(min_str, NULL, 10);
2682 timeout *= 60;
2683 }
2684 if (sec_str)
2685 timeout += strtol(sec_str, NULL, 10);
2686
2687 vty_timeout_val = timeout;
2688 vty->v_timeout = timeout;
2689 vty_event(VTY_TIMEOUT_RESET, 0, vty);
2690
2691
2692 return CMD_SUCCESS;
2693 }
2694
2695 DEFUN (exec_timeout_min,
2696 exec_timeout_min_cmd,
2697 "exec-timeout (0-35791)",
2698 "Set timeout value\n"
2699 "Timeout value in minutes\n")
2700 {
2701 int idx_number = 1;
2702 return exec_timeout(vty, argv[idx_number]->arg, NULL);
2703 }
2704
2705 DEFUN (exec_timeout_sec,
2706 exec_timeout_sec_cmd,
2707 "exec-timeout (0-35791) (0-2147483)",
2708 "Set the EXEC timeout\n"
2709 "Timeout in minutes\n"
2710 "Timeout in seconds\n")
2711 {
2712 int idx_number = 1;
2713 int idx_number_2 = 2;
2714 return exec_timeout(vty, argv[idx_number]->arg,
2715 argv[idx_number_2]->arg);
2716 }
2717
2718 DEFUN (no_exec_timeout,
2719 no_exec_timeout_cmd,
2720 "no exec-timeout",
2721 NO_STR
2722 "Set the EXEC timeout\n")
2723 {
2724 return exec_timeout(vty, NULL, NULL);
2725 }
2726
2727 /* Set vty access class. */
2728 DEFUN (vty_access_class,
2729 vty_access_class_cmd,
2730 "access-class WORD",
2731 "Filter connections based on an IP access list\n"
2732 "IP access list\n")
2733 {
2734 int idx_word = 1;
2735 if (vty_accesslist_name)
2736 XFREE(MTYPE_VTY, vty_accesslist_name);
2737
2738 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
2739
2740 return CMD_SUCCESS;
2741 }
2742
2743 /* Clear vty access class. */
2744 DEFUN (no_vty_access_class,
2745 no_vty_access_class_cmd,
2746 "no access-class [WORD]",
2747 NO_STR
2748 "Filter connections based on an IP access list\n"
2749 "IP access list\n")
2750 {
2751 int idx_word = 2;
2752 const char *accesslist = (argc == 3) ? argv[idx_word]->arg : NULL;
2753 if (!vty_accesslist_name
2754 || (argc == 3 && strcmp(vty_accesslist_name, accesslist))) {
2755 vty_out(vty, "Access-class is not currently applied to vty\n");
2756 return CMD_WARNING_CONFIG_FAILED;
2757 }
2758
2759 XFREE(MTYPE_VTY, vty_accesslist_name);
2760
2761 vty_accesslist_name = NULL;
2762
2763 return CMD_SUCCESS;
2764 }
2765
2766 /* Set vty access class. */
2767 DEFUN (vty_ipv6_access_class,
2768 vty_ipv6_access_class_cmd,
2769 "ipv6 access-class WORD",
2770 IPV6_STR
2771 "Filter connections based on an IP access list\n"
2772 "IPv6 access list\n")
2773 {
2774 int idx_word = 2;
2775 if (vty_ipv6_accesslist_name)
2776 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2777
2778 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
2779
2780 return CMD_SUCCESS;
2781 }
2782
2783 /* Clear vty access class. */
2784 DEFUN (no_vty_ipv6_access_class,
2785 no_vty_ipv6_access_class_cmd,
2786 "no ipv6 access-class [WORD]",
2787 NO_STR
2788 IPV6_STR
2789 "Filter connections based on an IP access list\n"
2790 "IPv6 access list\n")
2791 {
2792 int idx_word = 3;
2793 const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL;
2794
2795 if (!vty_ipv6_accesslist_name
2796 || (argc == 4 && strcmp(vty_ipv6_accesslist_name, accesslist))) {
2797 vty_out(vty,
2798 "IPv6 access-class is not currently applied to vty\n");
2799 return CMD_WARNING_CONFIG_FAILED;
2800 }
2801
2802 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2803
2804 vty_ipv6_accesslist_name = NULL;
2805
2806 return CMD_SUCCESS;
2807 }
2808
2809 /* vty login. */
2810 DEFUN (vty_login,
2811 vty_login_cmd,
2812 "login",
2813 "Enable password checking\n")
2814 {
2815 no_password_check = 0;
2816 return CMD_SUCCESS;
2817 }
2818
2819 DEFUN (no_vty_login,
2820 no_vty_login_cmd,
2821 "no login",
2822 NO_STR
2823 "Enable password checking\n")
2824 {
2825 no_password_check = 1;
2826 return CMD_SUCCESS;
2827 }
2828
2829 DEFUN (service_advanced_vty,
2830 service_advanced_vty_cmd,
2831 "service advanced-vty",
2832 "Set up miscellaneous service\n"
2833 "Enable advanced mode vty interface\n")
2834 {
2835 host.advanced = 1;
2836 return CMD_SUCCESS;
2837 }
2838
2839 DEFUN (no_service_advanced_vty,
2840 no_service_advanced_vty_cmd,
2841 "no service advanced-vty",
2842 NO_STR
2843 "Set up miscellaneous service\n"
2844 "Enable advanced mode vty interface\n")
2845 {
2846 host.advanced = 0;
2847 return CMD_SUCCESS;
2848 }
2849
2850 DEFUN_NOSH (terminal_monitor,
2851 terminal_monitor_cmd,
2852 "terminal monitor",
2853 "Set terminal line parameters\n"
2854 "Copy debug output to the current terminal line\n")
2855 {
2856 vty->monitor = 1;
2857 return CMD_SUCCESS;
2858 }
2859
2860 DEFUN_NOSH (terminal_no_monitor,
2861 terminal_no_monitor_cmd,
2862 "terminal no monitor",
2863 "Set terminal line parameters\n"
2864 NO_STR
2865 "Copy debug output to the current terminal line\n")
2866 {
2867 vty->monitor = 0;
2868 return CMD_SUCCESS;
2869 }
2870
2871 DEFUN_NOSH (no_terminal_monitor,
2872 no_terminal_monitor_cmd,
2873 "no terminal monitor",
2874 NO_STR
2875 "Set terminal line parameters\n"
2876 "Copy debug output to the current terminal line\n")
2877 {
2878 return terminal_no_monitor(self, vty, argc, argv);
2879 }
2880
2881
2882 DEFUN_NOSH (show_history,
2883 show_history_cmd,
2884 "show history",
2885 SHOW_STR
2886 "Display the session command history\n")
2887 {
2888 int index;
2889
2890 for (index = vty->hindex + 1; index != vty->hindex;) {
2891 if (index == VTY_MAXHIST) {
2892 index = 0;
2893 continue;
2894 }
2895
2896 if (vty->hist[index] != NULL)
2897 vty_out(vty, " %s\n", vty->hist[index]);
2898
2899 index++;
2900 }
2901
2902 return CMD_SUCCESS;
2903 }
2904
2905 /* vty login. */
2906 DEFUN (log_commands,
2907 log_commands_cmd,
2908 "log commands",
2909 "Logging control\n"
2910 "Log all commands (can't be unset without restart)\n")
2911 {
2912 do_log_commands = 1;
2913 return CMD_SUCCESS;
2914 }
2915
2916 /* Display current configuration. */
2917 static int vty_config_write(struct vty *vty)
2918 {
2919 vty_out(vty, "line vty\n");
2920
2921 if (vty_accesslist_name)
2922 vty_out(vty, " access-class %s\n", vty_accesslist_name);
2923
2924 if (vty_ipv6_accesslist_name)
2925 vty_out(vty, " ipv6 access-class %s\n",
2926 vty_ipv6_accesslist_name);
2927
2928 /* exec-timeout */
2929 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
2930 vty_out(vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
2931 vty_timeout_val % 60);
2932
2933 /* login */
2934 if (no_password_check)
2935 vty_out(vty, " no login\n");
2936
2937 if (do_log_commands)
2938 vty_out(vty, "log commands\n");
2939
2940 vty_out(vty, "!\n");
2941
2942 return CMD_SUCCESS;
2943 }
2944
2945 struct cmd_node vty_node = {
2946 VTY_NODE, "%s(config-line)# ", 1,
2947 };
2948
2949 /* Reset all VTY status. */
2950 void vty_reset()
2951 {
2952 unsigned int i;
2953 struct vty *vty;
2954 struct thread *vty_serv_thread;
2955
2956 for (i = 0; i < vector_active(vtyvec); i++)
2957 if ((vty = vector_slot(vtyvec, i)) != NULL) {
2958 buffer_reset(vty->obuf);
2959 vty->status = VTY_CLOSE;
2960 vty_close(vty);
2961 }
2962
2963 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
2964 if ((vty_serv_thread = vector_slot(Vvty_serv_thread, i))
2965 != NULL) {
2966 thread_cancel(vty_serv_thread);
2967 vector_slot(Vvty_serv_thread, i) = NULL;
2968 close(i);
2969 }
2970
2971 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
2972
2973 if (vty_accesslist_name) {
2974 XFREE(MTYPE_VTY, vty_accesslist_name);
2975 vty_accesslist_name = NULL;
2976 }
2977
2978 if (vty_ipv6_accesslist_name) {
2979 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2980 vty_ipv6_accesslist_name = NULL;
2981 }
2982 }
2983
2984 static void vty_save_cwd(void)
2985 {
2986 char cwd[MAXPATHLEN];
2987 char *c;
2988
2989 c = getcwd(cwd, MAXPATHLEN);
2990
2991 if (!c) {
2992 /*
2993 * At this point if these go wrong, more than likely
2994 * the whole world is coming down around us
2995 * Hence not worrying about it too much.
2996 */
2997 if (!chdir(SYSCONFDIR)) {
2998 zlog_err("Failure to chdir to %s, errno: %d",
2999 SYSCONFDIR, errno);
3000 exit(-1);
3001 }
3002 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3003 zlog_err("Failure to getcwd, errno: %d", errno);
3004 exit(-1);
3005 }
3006 }
3007
3008 vty_cwd = XMALLOC(MTYPE_TMP, strlen(cwd) + 1);
3009 strcpy(vty_cwd, cwd);
3010 }
3011
3012 char *vty_get_cwd()
3013 {
3014 return vty_cwd;
3015 }
3016
3017 int vty_shell(struct vty *vty)
3018 {
3019 return vty->type == VTY_SHELL ? 1 : 0;
3020 }
3021
3022 int vty_shell_serv(struct vty *vty)
3023 {
3024 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3025 }
3026
3027 void vty_init_vtysh()
3028 {
3029 vtyvec = vector_init(VECTOR_MIN_SIZE);
3030 }
3031
3032 /* Install vty's own commands like `who' command. */
3033 void vty_init(struct thread_master *master_thread)
3034 {
3035 /* For further configuration read, preserve current directory. */
3036 vty_save_cwd();
3037
3038 vtyvec = vector_init(VECTOR_MIN_SIZE);
3039
3040 vty_master = master_thread;
3041
3042 atexit(vty_stdio_atexit);
3043
3044 /* Initilize server thread vector. */
3045 Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE);
3046
3047 /* Install bgp top node. */
3048 install_node(&vty_node, vty_config_write);
3049
3050 install_element(VIEW_NODE, &config_who_cmd);
3051 install_element(VIEW_NODE, &show_history_cmd);
3052 install_element(CONFIG_NODE, &line_vty_cmd);
3053 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
3054 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
3055 install_element(CONFIG_NODE, &show_history_cmd);
3056 install_element(CONFIG_NODE, &log_commands_cmd);
3057 install_element(ENABLE_NODE, &terminal_monitor_cmd);
3058 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
3059 install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
3060
3061 install_default(VTY_NODE);
3062 install_element(VTY_NODE, &exec_timeout_min_cmd);
3063 install_element(VTY_NODE, &exec_timeout_sec_cmd);
3064 install_element(VTY_NODE, &no_exec_timeout_cmd);
3065 install_element(VTY_NODE, &vty_access_class_cmd);
3066 install_element(VTY_NODE, &no_vty_access_class_cmd);
3067 install_element(VTY_NODE, &vty_login_cmd);
3068 install_element(VTY_NODE, &no_vty_login_cmd);
3069 install_element(VTY_NODE, &vty_ipv6_access_class_cmd);
3070 install_element(VTY_NODE, &no_vty_ipv6_access_class_cmd);
3071 }
3072
3073 void vty_terminate(void)
3074 {
3075 if (vty_cwd)
3076 XFREE(MTYPE_TMP, vty_cwd);
3077
3078 if (vtyvec && Vvty_serv_thread) {
3079 vty_reset();
3080 vector_free(vtyvec);
3081 vector_free(Vvty_serv_thread);
3082 vtyvec = NULL;
3083 Vvty_serv_thread = NULL;
3084 }
3085 }