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