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