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