blob: 5ef364a08097f8d5d4d845f1ae88db1ac8acbbab [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
paul29db05b2003-05-08 20:10:22 +00001779vty_serv_sock_family (const char* addr, unsigned short port, int family)
paul718e3742002-12-13 20:15:29 +00001780{
1781 int ret;
1782 union sockunion su;
1783 int accept_sock;
paul29db05b2003-05-08 20:10:22 +00001784 void* naddr=NULL;
paul718e3742002-12-13 20:15:29 +00001785
1786 memset (&su, 0, sizeof (union sockunion));
1787 su.sa.sa_family = family;
paul29db05b2003-05-08 20:10:22 +00001788 if(addr)
1789 switch(family)
1790 {
1791 case AF_INET:
1792 naddr=&su.sin.sin_addr;
1793#ifdef HAVE_IPV6
1794 case AF_INET6:
1795 naddr=&su.sin6.sin6_addr;
1796#endif
1797 }
1798
1799 if(naddr)
1800 switch(inet_pton(family,addr,naddr))
1801 {
1802 case -1:
1803 zlog_err("bad address %s",addr);
1804 naddr=NULL;
1805 break;
1806 case 0:
1807 zlog_err("error translating address %s: %s",addr,strerror(errno));
1808 naddr=NULL;
1809 }
paul718e3742002-12-13 20:15:29 +00001810
1811 /* Make new socket. */
1812 accept_sock = sockunion_stream_socket (&su);
1813 if (accept_sock < 0)
1814 return;
1815
1816 /* This is server, so reuse address. */
1817 sockopt_reuseaddr (accept_sock);
1818 sockopt_reuseport (accept_sock);
1819
1820 /* Bind socket to universal address and given port. */
paul29db05b2003-05-08 20:10:22 +00001821 ret = sockunion_bind (accept_sock, &su, port, naddr);
paul718e3742002-12-13 20:15:29 +00001822 if (ret < 0)
1823 {
paul29db05b2003-05-08 20:10:22 +00001824 zlog_warn("can't bind socket");
paul718e3742002-12-13 20:15:29 +00001825 close (accept_sock); /* Avoid sd leak. */
1826 return;
1827 }
1828
1829 /* Listen socket under queue 3. */
1830 ret = listen (accept_sock, 3);
1831 if (ret < 0)
1832 {
1833 zlog (NULL, LOG_WARNING, "can't listen socket");
1834 close (accept_sock); /* Avoid sd leak. */
1835 return;
1836 }
1837
1838 /* Add vty server event. */
1839 vty_event (VTY_SERV, accept_sock, NULL);
1840}
1841
1842#ifdef VTYSH
1843/* For sockaddr_un. */
1844#include <sys/un.h>
1845
1846/* VTY shell UNIX domain socket. */
1847void
1848vty_serv_un (char *path)
1849{
1850 int ret;
1851 int sock, len;
1852 struct sockaddr_un serv;
1853 mode_t old_mask;
1854
1855 /* First of all, unlink existing socket */
1856 unlink (path);
1857
1858 /* Set umask */
paul1921e6f2003-05-23 08:12:36 +00001859 old_mask = umask (0007);
paul718e3742002-12-13 20:15:29 +00001860
1861 /* Make UNIX domain socket. */
1862 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1863 if (sock < 0)
1864 {
1865 perror ("sock");
1866 return;
1867 }
1868
1869 /* Make server socket. */
1870 memset (&serv, 0, sizeof (struct sockaddr_un));
1871 serv.sun_family = AF_UNIX;
1872 strncpy (serv.sun_path, path, strlen (path));
1873#ifdef HAVE_SUN_LEN
1874 len = serv.sun_len = SUN_LEN(&serv);
1875#else
1876 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
1877#endif /* HAVE_SUN_LEN */
1878
1879 ret = bind (sock, (struct sockaddr *) &serv, len);
1880 if (ret < 0)
1881 {
1882 perror ("bind");
1883 close (sock); /* Avoid sd leak. */
1884 return;
1885 }
1886
1887 ret = listen (sock, 5);
1888 if (ret < 0)
1889 {
1890 perror ("listen");
1891 close (sock); /* Avoid sd leak. */
1892 return;
1893 }
1894
1895 umask (old_mask);
1896
1897 vty_event (VTYSH_SERV, sock, NULL);
1898}
1899
1900/* #define VTYSH_DEBUG 1 */
1901
1902static int
1903vtysh_accept (struct thread *thread)
1904{
1905 int accept_sock;
1906 int sock;
1907 int client_len;
1908 struct sockaddr_un client;
1909 struct vty *vty;
1910
1911 accept_sock = THREAD_FD (thread);
1912
1913 vty_event (VTYSH_SERV, accept_sock, NULL);
1914
1915 memset (&client, 0, sizeof (struct sockaddr_un));
1916 client_len = sizeof (struct sockaddr_un);
1917
1918 sock = accept (accept_sock, (struct sockaddr *) &client, &client_len);
1919
1920 if (sock < 0)
1921 {
1922 zlog_warn ("can't accept vty socket : %s", strerror (errno));
1923 return -1;
1924 }
1925
1926#ifdef VTYSH_DEBUG
1927 printf ("VTY shell accept\n");
1928#endif /* VTYSH_DEBUG */
1929
1930 vty = vty_new ();
1931 vty->fd = sock;
1932 vty->type = VTY_SHELL_SERV;
1933 vty->node = VIEW_NODE;
1934
1935 vty_event (VTYSH_READ, sock, vty);
1936
1937 return 0;
1938}
1939
1940static int
1941vtysh_read (struct thread *thread)
1942{
1943 int ret;
1944 int sock;
1945 int nbytes;
1946 struct vty *vty;
1947 unsigned char buf[VTY_READ_BUFSIZ];
1948 u_char header[4] = {0, 0, 0, 0};
1949
1950 sock = THREAD_FD (thread);
1951 vty = THREAD_ARG (thread);
1952 vty->t_read = NULL;
1953
1954 nbytes = read (sock, buf, VTY_READ_BUFSIZ);
1955 if (nbytes <= 0)
1956 {
1957 vty_close (vty);
1958#ifdef VTYSH_DEBUG
1959 printf ("close vtysh\n");
1960#endif /* VTYSH_DEBUG */
1961 return 0;
1962 }
1963
1964#ifdef VTYSH_DEBUG
1965 printf ("line: %s\n", buf);
1966#endif /* VTYSH_DEBUG */
1967
1968 vty_ensure (vty, nbytes);
1969 memcpy (vty->buf, buf, nbytes);
1970
1971 /* Pass this line to parser. */
1972 ret = vty_execute (vty);
1973
1974 vty_clear_buf (vty);
1975
1976 /* Return result. */
1977#ifdef VTYSH_DEBUG
1978 printf ("result: %d\n", ret);
1979 printf ("vtysh node: %d\n", vty->node);
1980#endif /* VTYSH_DEBUG */
1981
1982 header[3] = ret;
1983 write (vty->fd, header, 4);
1984
1985 vty_event (VTYSH_READ, sock, vty);
1986
1987 return 0;
1988}
1989#endif /* VTYSH */
1990
1991/* Determine address family to bind. */
1992void
paul29db05b2003-05-08 20:10:22 +00001993vty_serv_sock (const char *addr, unsigned short port, char *path)
paul718e3742002-12-13 20:15:29 +00001994{
1995 /* If port is set to 0, do not listen on TCP/IP at all! */
1996 if (port)
1997 {
1998
1999#ifdef HAVE_IPV6
2000#ifdef NRL
paul29db05b2003-05-08 20:10:22 +00002001 vty_serv_sock_family (addr, port, AF_INET);
2002 vty_serv_sock_family (addr, port, AF_INET6);
paul718e3742002-12-13 20:15:29 +00002003#else /* ! NRL */
paul29db05b2003-05-08 20:10:22 +00002004 vty_serv_sock_addrinfo (addr, port);
paul718e3742002-12-13 20:15:29 +00002005#endif /* NRL*/
2006#else /* ! HAVE_IPV6 */
paul29db05b2003-05-08 20:10:22 +00002007 vty_serv_sock_family (addr,port, AF_INET);
paul718e3742002-12-13 20:15:29 +00002008#endif /* HAVE_IPV6 */
2009 }
2010
2011#ifdef VTYSH
2012 vty_serv_un (path);
2013#endif /* VTYSH */
2014}
2015
2016/* Close vty interface. */
2017void
2018vty_close (struct vty *vty)
2019{
2020 int i;
2021
2022 /* Cancel threads.*/
2023 if (vty->t_read)
2024 thread_cancel (vty->t_read);
2025 if (vty->t_write)
2026 thread_cancel (vty->t_write);
2027 if (vty->t_timeout)
2028 thread_cancel (vty->t_timeout);
2029 if (vty->t_output)
2030 thread_cancel (vty->t_output);
2031
2032 /* Flush buffer. */
2033 if (! buffer_empty (vty->obuf))
2034 buffer_flush_all (vty->obuf, vty->fd);
2035
2036 /* Free input buffer. */
2037 buffer_free (vty->obuf);
2038
2039 /* Free SB buffer. */
2040 if (vty->sb_buffer)
2041 buffer_free (vty->sb_buffer);
2042
2043 /* Free command history. */
2044 for (i = 0; i < VTY_MAXHIST; i++)
2045 if (vty->hist[i])
2046 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2047
2048 /* Unset vector. */
2049 vector_unset (vtyvec, vty->fd);
2050
2051 /* Close socket. */
2052 if (vty->fd > 0)
2053 close (vty->fd);
2054
2055 if (vty->address)
2056 XFREE (0, vty->address);
2057 if (vty->buf)
2058 XFREE (MTYPE_VTY, vty->buf);
2059
2060 /* Check configure. */
2061 vty_config_unlock (vty);
2062
2063 /* OK free vty. */
2064 XFREE (MTYPE_VTY, vty);
2065}
2066
2067/* When time out occur output message then close connection. */
2068static int
2069vty_timeout (struct thread *thread)
2070{
2071 struct vty *vty;
2072
2073 vty = THREAD_ARG (thread);
2074 vty->t_timeout = NULL;
2075 vty->v_timeout = 0;
2076
2077 /* Clear buffer*/
2078 buffer_reset (vty->obuf);
2079 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2080
2081 /* Close connection. */
2082 vty->status = VTY_CLOSE;
2083 vty_close (vty);
2084
2085 return 0;
2086}
2087
2088/* Read up configuration file from file_name. */
2089static void
2090vty_read_file (FILE *confp)
2091{
2092 int ret;
2093 struct vty *vty;
2094
2095 vty = vty_new ();
2096 vty->fd = 0; /* stdout */
2097 vty->type = VTY_TERM;
2098 vty->node = CONFIG_NODE;
2099
2100 /* Execute configuration file */
2101 ret = config_from_file (vty, confp);
2102
2103 if (ret != CMD_SUCCESS)
2104 {
2105 switch (ret)
2106 {
2107 case CMD_ERR_AMBIGUOUS:
2108 fprintf (stderr, "Ambiguous command.\n");
2109 break;
2110 case CMD_ERR_NO_MATCH:
2111 fprintf (stderr, "There is no such command.\n");
2112 break;
2113 }
2114 fprintf (stderr, "Error occured during reading below line.\n%s\n",
2115 vty->buf);
2116 vty_close (vty);
2117 exit (1);
2118 }
2119
2120 vty_close (vty);
2121}
2122
2123FILE *
2124vty_use_backup_config (char *fullpath)
2125{
2126 char *fullpath_sav, *fullpath_tmp;
2127 FILE *ret = NULL;
2128 struct stat buf;
2129 int tmp, sav;
2130 int c;
2131 char buffer[512];
2132
2133 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2134 strcpy (fullpath_sav, fullpath);
2135 strcat (fullpath_sav, CONF_BACKUP_EXT);
2136 if (stat (fullpath_sav, &buf) == -1)
2137 {
2138 free (fullpath_sav);
2139 return NULL;
2140 }
2141
2142 fullpath_tmp = malloc (strlen (fullpath) + 8);
2143 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2144
2145 /* Open file to configuration write. */
2146 tmp = mkstemp (fullpath_tmp);
2147 if (tmp < 0)
2148 {
2149 free (fullpath_sav);
2150 free (fullpath_tmp);
2151 return NULL;
2152 }
2153
2154 sav = open (fullpath_sav, O_RDONLY);
2155 if (sav < 0)
2156 {
2157 free (fullpath_sav);
2158 free (fullpath_tmp);
2159 unlink (fullpath_tmp);
2160 return NULL;
2161 }
2162
2163 while((c = read (sav, buffer, 512)) > 0)
2164 write (tmp, buffer, c);
2165
2166 close (sav);
2167 close (tmp);
2168
2169 if (link (fullpath_tmp, fullpath) == 0)
2170 ret = fopen (fullpath, "r");
2171
2172 unlink (fullpath_tmp);
2173
2174 free (fullpath_sav);
2175 free (fullpath_tmp);
2176 return fopen (fullpath, "r");
2177}
2178
2179/* Read up configuration file from file_name. */
2180void
2181vty_read_config (char *config_file,
2182 char *config_current_dir,
2183 char *config_default_dir)
2184{
2185 char *cwd;
2186 FILE *confp = NULL;
2187 char *fullpath;
2188
2189 /* If -f flag specified. */
2190 if (config_file != NULL)
2191 {
2192 if (! IS_DIRECTORY_SEP (config_file[0]))
2193 {
2194 cwd = getcwd (NULL, MAXPATHLEN);
2195 fullpath = XMALLOC (MTYPE_TMP,
2196 strlen (cwd) + strlen (config_file) + 2);
2197 sprintf (fullpath, "%s/%s", cwd, config_file);
2198 }
2199 else
2200 fullpath = config_file;
2201
2202 confp = fopen (fullpath, "r");
2203
2204 if (confp == NULL)
2205 {
2206 confp = vty_use_backup_config (fullpath);
2207 if (confp)
2208 fprintf (stderr, "WARNING: using backup configuration file!\n");
2209 else
2210 {
2211 fprintf (stderr, "can't open configuration file [%s]\n",
2212 config_file);
2213 exit(1);
2214 }
2215 }
2216 }
2217 else
2218 {
2219 /* Relative path configuration file open. */
2220 if (config_current_dir)
2221 {
2222 confp = fopen (config_current_dir, "r");
2223 if (confp == NULL)
2224 {
2225 confp = vty_use_backup_config (config_current_dir);
2226 if (confp)
2227 fprintf (stderr, "WARNING: using backup configuration file!\n");
2228 }
2229 }
2230
2231 /* If there is no relative path exists, open system default file. */
2232 if (confp == NULL)
2233 {
2234#ifdef VTYSH
2235 int ret;
2236 struct stat conf_stat;
2237
2238 /* !!!!PLEASE LEAVE!!!!
2239 This is NEEDED for use with vtysh -b, or else you can get
2240 a real configuration food fight with a lot garbage in the
2241 merged configuration file it creates coming from the per
2242 daemon configuration files. This also allows the daemons
2243 to start if there default configuration file is not
2244 present or ignore them, as needed when using vtysh -b to
2245 configure the daemons at boot - MAG */
2246
2247 /* Stat for vtysh Zebra.conf, if found startup and wait for
2248 boot configuration */
2249
2250 if ( strstr(config_default_dir, "vtysh") == NULL)
2251 {
2252 ret = stat (integrate_default, &conf_stat);
2253 if (ret >= 0)
2254 {
2255 return;
2256 }
2257 }
2258#endif /* VTYSH */
2259
2260 confp = fopen (config_default_dir, "r");
2261 if (confp == NULL)
2262 {
2263 confp = vty_use_backup_config (config_default_dir);
2264 if (confp)
2265 {
2266 fprintf (stderr, "WARNING: using backup configuration file!\n");
2267 fullpath = config_default_dir;
2268 }
2269 else
2270 {
2271 fprintf (stderr, "can't open configuration file [%s]\n",
2272 config_default_dir);
2273 exit (1);
2274 }
2275 }
2276 else
2277 fullpath = config_default_dir;
2278 }
2279 else
2280 {
2281 /* Rleative path configuration file. */
2282 cwd = getcwd (NULL, MAXPATHLEN);
2283 fullpath = XMALLOC (MTYPE_TMP,
2284 strlen (cwd) + strlen (config_current_dir) + 2);
2285 sprintf (fullpath, "%s/%s", cwd, config_current_dir);
2286 }
2287 }
2288 vty_read_file (confp);
2289
2290 fclose (confp);
2291
2292 host_config_set (fullpath);
2293}
2294
2295/* Small utility function which output log to the VTY. */
2296void
2297vty_log (const char *proto_str, const char *format, va_list va)
2298{
2299 int i;
2300 struct vty *vty;
2301
2302 for (i = 0; i < vector_max (vtyvec); i++)
2303 if ((vty = vector_slot (vtyvec, i)) != NULL)
2304 if (vty->monitor)
2305 vty_log_out (vty, proto_str, format, va);
2306}
2307
2308int
2309vty_config_lock (struct vty *vty)
2310{
2311 if (vty_config == 0)
2312 {
2313 vty->config = 1;
2314 vty_config = 1;
2315 }
2316 return vty->config;
2317}
2318
2319int
2320vty_config_unlock (struct vty *vty)
2321{
2322 if (vty_config == 1 && vty->config == 1)
2323 {
2324 vty->config = 0;
2325 vty_config = 0;
2326 }
2327 return vty->config;
2328}
2329
2330/* Master of the threads. */
2331extern struct thread_master *master;
2332/* struct thread_master *master; */
2333
2334static void
2335vty_event (enum event event, int sock, struct vty *vty)
2336{
2337 struct thread *vty_serv_thread;
2338
2339 switch (event)
2340 {
2341 case VTY_SERV:
2342 vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);
2343 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2344 break;
2345#ifdef VTYSH
2346 case VTYSH_SERV:
2347 thread_add_read (master, vtysh_accept, vty, sock);
2348 break;
2349 case VTYSH_READ:
2350 thread_add_read (master, vtysh_read, vty, sock);
2351 break;
2352#endif /* VTYSH */
2353 case VTY_READ:
2354 vty->t_read = thread_add_read (master, vty_read, vty, sock);
2355
2356 /* Time out treatment. */
2357 if (vty->v_timeout)
2358 {
2359 if (vty->t_timeout)
2360 thread_cancel (vty->t_timeout);
2361 vty->t_timeout =
2362 thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
2363 }
2364 break;
2365 case VTY_WRITE:
2366 if (! vty->t_write)
2367 vty->t_write = thread_add_write (master, vty_flush, vty, sock);
2368 break;
2369 case VTY_TIMEOUT_RESET:
2370 if (vty->t_timeout)
2371 {
2372 thread_cancel (vty->t_timeout);
2373 vty->t_timeout = NULL;
2374 }
2375 if (vty->v_timeout)
2376 {
2377 vty->t_timeout =
2378 thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
2379 }
2380 break;
2381 }
2382}
2383
2384DEFUN (config_who,
2385 config_who_cmd,
2386 "who",
2387 "Display who is on vty\n")
2388{
2389 int i;
2390 struct vty *v;
2391
2392 for (i = 0; i < vector_max (vtyvec); i++)
2393 if ((v = vector_slot (vtyvec, i)) != NULL)
2394 vty_out (vty, "%svty[%d] connected from %s.%s",
2395 v->config ? "*" : " ",
2396 i, v->address, VTY_NEWLINE);
2397 return CMD_SUCCESS;
2398}
2399
2400/* Move to vty configuration mode. */
2401DEFUN (line_vty,
2402 line_vty_cmd,
2403 "line vty",
2404 "Configure a terminal line\n"
2405 "Virtual terminal\n")
2406{
2407 vty->node = VTY_NODE;
2408 return CMD_SUCCESS;
2409}
2410
2411/* Set time out value. */
2412int
2413exec_timeout (struct vty *vty, char *min_str, char *sec_str)
2414{
2415 unsigned long timeout = 0;
2416
2417 /* min_str and sec_str are already checked by parser. So it must be
2418 all digit string. */
2419 if (min_str)
2420 {
2421 timeout = strtol (min_str, NULL, 10);
2422 timeout *= 60;
2423 }
2424 if (sec_str)
2425 timeout += strtol (sec_str, NULL, 10);
2426
2427 vty_timeout_val = timeout;
2428 vty->v_timeout = timeout;
2429 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2430
2431
2432 return CMD_SUCCESS;
2433}
2434
2435DEFUN (exec_timeout_min,
2436 exec_timeout_min_cmd,
2437 "exec-timeout <0-35791>",
2438 "Set timeout value\n"
2439 "Timeout value in minutes\n")
2440{
2441 return exec_timeout (vty, argv[0], NULL);
2442}
2443
2444DEFUN (exec_timeout_sec,
2445 exec_timeout_sec_cmd,
2446 "exec-timeout <0-35791> <0-2147483>",
2447 "Set the EXEC timeout\n"
2448 "Timeout in minutes\n"
2449 "Timeout in seconds\n")
2450{
2451 return exec_timeout (vty, argv[0], argv[1]);
2452}
2453
2454DEFUN (no_exec_timeout,
2455 no_exec_timeout_cmd,
2456 "no exec-timeout",
2457 NO_STR
2458 "Set the EXEC timeout\n")
2459{
2460 return exec_timeout (vty, NULL, NULL);
2461}
2462
2463/* Set vty access class. */
2464DEFUN (vty_access_class,
2465 vty_access_class_cmd,
2466 "access-class WORD",
2467 "Filter connections based on an IP access list\n"
2468 "IP access list\n")
2469{
2470 if (vty_accesslist_name)
2471 XFREE(MTYPE_VTY, vty_accesslist_name);
2472
2473 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2474
2475 return CMD_SUCCESS;
2476}
2477
2478/* Clear vty access class. */
2479DEFUN (no_vty_access_class,
2480 no_vty_access_class_cmd,
2481 "no access-class [WORD]",
2482 NO_STR
2483 "Filter connections based on an IP access list\n"
2484 "IP access list\n")
2485{
2486 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2487 {
2488 vty_out (vty, "Access-class is not currently applied to vty%s",
2489 VTY_NEWLINE);
2490 return CMD_WARNING;
2491 }
2492
2493 XFREE(MTYPE_VTY, vty_accesslist_name);
2494
2495 vty_accesslist_name = NULL;
2496
2497 return CMD_SUCCESS;
2498}
2499
2500#ifdef HAVE_IPV6
2501/* Set vty access class. */
2502DEFUN (vty_ipv6_access_class,
2503 vty_ipv6_access_class_cmd,
2504 "ipv6 access-class WORD",
2505 IPV6_STR
2506 "Filter connections based on an IP access list\n"
2507 "IPv6 access list\n")
2508{
2509 if (vty_ipv6_accesslist_name)
2510 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2511
2512 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2513
2514 return CMD_SUCCESS;
2515}
2516
2517/* Clear vty access class. */
2518DEFUN (no_vty_ipv6_access_class,
2519 no_vty_ipv6_access_class_cmd,
2520 "no ipv6 access-class [WORD]",
2521 NO_STR
2522 IPV6_STR
2523 "Filter connections based on an IP access list\n"
2524 "IPv6 access list\n")
2525{
2526 if (! vty_ipv6_accesslist_name ||
2527 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2528 {
2529 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2530 VTY_NEWLINE);
2531 return CMD_WARNING;
2532 }
2533
2534 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2535
2536 vty_ipv6_accesslist_name = NULL;
2537
2538 return CMD_SUCCESS;
2539}
2540#endif /* HAVE_IPV6 */
2541
2542/* vty login. */
2543DEFUN (vty_login,
2544 vty_login_cmd,
2545 "login",
2546 "Enable password checking\n")
2547{
2548 no_password_check = 0;
2549 return CMD_SUCCESS;
2550}
2551
2552DEFUN (no_vty_login,
2553 no_vty_login_cmd,
2554 "no login",
2555 NO_STR
2556 "Enable password checking\n")
2557{
2558 no_password_check = 1;
2559 return CMD_SUCCESS;
2560}
2561
2562DEFUN (service_advanced_vty,
2563 service_advanced_vty_cmd,
2564 "service advanced-vty",
2565 "Set up miscellaneous service\n"
2566 "Enable advanced mode vty interface\n")
2567{
2568 host.advanced = 1;
2569 return CMD_SUCCESS;
2570}
2571
2572DEFUN (no_service_advanced_vty,
2573 no_service_advanced_vty_cmd,
2574 "no service advanced-vty",
2575 NO_STR
2576 "Set up miscellaneous service\n"
2577 "Enable advanced mode vty interface\n")
2578{
2579 host.advanced = 0;
2580 return CMD_SUCCESS;
2581}
2582
2583DEFUN (terminal_monitor,
2584 terminal_monitor_cmd,
2585 "terminal monitor",
2586 "Set terminal line parameters\n"
2587 "Copy debug output to the current terminal line\n")
2588{
2589 vty->monitor = 1;
2590 return CMD_SUCCESS;
2591}
2592
2593DEFUN (terminal_no_monitor,
2594 terminal_no_monitor_cmd,
2595 "terminal no monitor",
2596 "Set terminal line parameters\n"
2597 NO_STR
2598 "Copy debug output to the current terminal line\n")
2599{
2600 vty->monitor = 0;
2601 return CMD_SUCCESS;
2602}
2603
2604DEFUN (show_history,
2605 show_history_cmd,
2606 "show history",
2607 SHOW_STR
2608 "Display the session command history\n")
2609{
2610 int index;
2611
2612 for (index = vty->hindex + 1; index != vty->hindex;)
2613 {
2614 if (index == VTY_MAXHIST)
2615 {
2616 index = 0;
2617 continue;
2618 }
2619
2620 if (vty->hist[index] != NULL)
2621 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
2622
2623 index++;
2624 }
2625
2626 return CMD_SUCCESS;
2627}
2628
2629/* Display current configuration. */
2630int
2631vty_config_write (struct vty *vty)
2632{
2633 vty_out (vty, "line vty%s", VTY_NEWLINE);
2634
2635 if (vty_accesslist_name)
2636 vty_out (vty, " access-class %s%s",
2637 vty_accesslist_name, VTY_NEWLINE);
2638
2639 if (vty_ipv6_accesslist_name)
2640 vty_out (vty, " ipv6 access-class %s%s",
2641 vty_ipv6_accesslist_name, VTY_NEWLINE);
2642
2643 /* exec-timeout */
2644 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
2645 vty_out (vty, " exec-timeout %ld %ld%s",
2646 vty_timeout_val / 60,
2647 vty_timeout_val % 60, VTY_NEWLINE);
2648
2649 /* login */
2650 if (no_password_check)
2651 vty_out (vty, " no login%s", VTY_NEWLINE);
2652
2653 vty_out (vty, "!%s", VTY_NEWLINE);
2654
2655 return CMD_SUCCESS;
2656}
2657
2658struct cmd_node vty_node =
2659{
2660 VTY_NODE,
2661 "%s(config-line)# ",
2662};
2663
2664/* Reset all VTY status. */
2665void
2666vty_reset ()
2667{
2668 int i;
2669 struct vty *vty;
2670 struct thread *vty_serv_thread;
2671
2672 for (i = 0; i < vector_max (vtyvec); i++)
2673 if ((vty = vector_slot (vtyvec, i)) != NULL)
2674 {
2675 buffer_reset (vty->obuf);
2676 vty->status = VTY_CLOSE;
2677 vty_close (vty);
2678 }
2679
2680 for (i = 0; i < vector_max (Vvty_serv_thread); i++)
2681 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
2682 {
2683 thread_cancel (vty_serv_thread);
2684 vector_slot (Vvty_serv_thread, i) = NULL;
2685 close (i);
2686 }
2687
2688 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
2689
2690 if (vty_accesslist_name)
2691 {
2692 XFREE(MTYPE_VTY, vty_accesslist_name);
2693 vty_accesslist_name = NULL;
2694 }
2695
2696 if (vty_ipv6_accesslist_name)
2697 {
2698 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2699 vty_ipv6_accesslist_name = NULL;
2700 }
2701}
2702
2703/* for ospf6d easy temprary reload function */
2704/* vty_reset + close accept socket */
2705void
2706vty_finish ()
2707{
2708 int i;
2709 struct vty *vty;
2710 struct thread *vty_serv_thread;
2711
2712 for (i = 0; i < vector_max (vtyvec); i++)
2713 if ((vty = vector_slot (vtyvec, i)) != NULL)
2714 {
2715 buffer_reset (vty->obuf);
2716 vty->status = VTY_CLOSE;
2717 vty_close (vty);
2718 }
2719
2720 for (i = 0; i < vector_max (Vvty_serv_thread); i++)
2721 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
2722 {
2723 thread_cancel (vty_serv_thread);
2724 vector_slot (Vvty_serv_thread, i) = NULL;
2725 close (i);
2726 }
2727
2728 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
2729
2730 if (vty_accesslist_name)
2731 {
2732 XFREE(MTYPE_VTY, vty_accesslist_name);
2733 vty_accesslist_name = NULL;
2734 }
2735
2736 if (vty_ipv6_accesslist_name)
2737 {
2738 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2739 vty_ipv6_accesslist_name = NULL;
2740 }
2741}
2742
2743void
2744vty_save_cwd ()
2745{
2746 char *cwd;
2747
2748 cwd = getcwd (NULL, MAXPATHLEN);
2749
2750 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
2751 strcpy (vty_cwd, cwd);
2752}
2753
2754char *
2755vty_get_cwd ()
2756{
2757 return vty_cwd;
2758}
2759
2760int
2761vty_shell (struct vty *vty)
2762{
2763 return vty->type == VTY_SHELL ? 1 : 0;
2764}
2765
2766int
2767vty_shell_serv (struct vty *vty)
2768{
2769 return vty->type == VTY_SHELL_SERV ? 1 : 0;
2770}
2771
2772void
2773vty_init_vtysh ()
2774{
2775 vtyvec = vector_init (VECTOR_MIN_SIZE);
2776}
2777
2778/* Install vty's own commands like `who' command. */
2779void
2780vty_init ()
2781{
2782 /* For further configuration read, preserve current directory. */
2783 vty_save_cwd ();
2784
2785 vtyvec = vector_init (VECTOR_MIN_SIZE);
2786
2787 /* Initilize server thread vector. */
2788 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
2789
2790 /* Install bgp top node. */
2791 install_node (&vty_node, vty_config_write);
2792
2793 install_element (VIEW_NODE, &config_who_cmd);
2794 install_element (VIEW_NODE, &show_history_cmd);
2795 install_element (ENABLE_NODE, &config_who_cmd);
2796 install_element (CONFIG_NODE, &line_vty_cmd);
2797 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
2798 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
2799 install_element (CONFIG_NODE, &show_history_cmd);
2800 install_element (ENABLE_NODE, &terminal_monitor_cmd);
2801 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
2802 install_element (ENABLE_NODE, &show_history_cmd);
2803
2804 install_default (VTY_NODE);
2805 install_element (VTY_NODE, &exec_timeout_min_cmd);
2806 install_element (VTY_NODE, &exec_timeout_sec_cmd);
2807 install_element (VTY_NODE, &no_exec_timeout_cmd);
2808 install_element (VTY_NODE, &vty_access_class_cmd);
2809 install_element (VTY_NODE, &no_vty_access_class_cmd);
2810 install_element (VTY_NODE, &vty_login_cmd);
2811 install_element (VTY_NODE, &no_vty_login_cmd);
2812#ifdef HAVE_IPV6
2813 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
2814 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
2815#endif /* HAVE_IPV6 */
2816}