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