blob: 831660d980d5c4a8d394d1887fefbfcc0cc7687d [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"
gdt5e4fa162004-03-16 14:38:36 +000028#include <lib/version.h>
paul718e3742002-12-13 20:15:29 +000029#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:
jardin9e867fe2003-12-23 08:56:18 +0000629 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +0000630 case KEYCHAIN_NODE:
631 case KEYCHAIN_KEY_NODE:
632 case MASC_NODE:
633 case VTY_NODE:
634 vty_config_unlock (vty);
635 vty->node = ENABLE_NODE;
636 break;
637 default:
638 /* Unknown node, we have to ignore it. */
639 break;
640 }
641
642 vty_prompt (vty);
643 vty->cp = 0;
644}
645
646/* Delete a charcter at the current point. */
647static void
648vty_delete_char (struct vty *vty)
649{
650 int i;
651 int size;
652
653 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
654 return;
655
656 if (vty->length == 0)
657 {
658 vty_down_level (vty);
659 return;
660 }
661
662 if (vty->cp == vty->length)
663 return; /* completion need here? */
664
665 size = vty->length - vty->cp;
666
667 vty->length--;
668 memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
669 vty->buf[vty->length] = '\0';
670
671 vty_write (vty, &vty->buf[vty->cp], size - 1);
672 vty_write (vty, &telnet_space_char, 1);
673
674 for (i = 0; i < size; i++)
675 vty_write (vty, &telnet_backward_char, 1);
676}
677
678/* Delete a character before the point. */
679static void
680vty_delete_backward_char (struct vty *vty)
681{
682 if (vty->cp == 0)
683 return;
684
685 vty_backward_char (vty);
686 vty_delete_char (vty);
687}
688
689/* Kill rest of line from current point. */
690static void
691vty_kill_line (struct vty *vty)
692{
693 int i;
694 int size;
695
696 size = vty->length - vty->cp;
697
698 if (size == 0)
699 return;
700
701 for (i = 0; i < size; i++)
702 vty_write (vty, &telnet_space_char, 1);
703 for (i = 0; i < size; i++)
704 vty_write (vty, &telnet_backward_char, 1);
705
706 memset (&vty->buf[vty->cp], 0, size);
707 vty->length = vty->cp;
708}
709
710/* Kill line from the beginning. */
711static void
712vty_kill_line_from_beginning (struct vty *vty)
713{
714 vty_beginning_of_line (vty);
715 vty_kill_line (vty);
716}
717
718/* Delete a word before the point. */
719static void
720vty_forward_kill_word (struct vty *vty)
721{
722 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
723 vty_delete_char (vty);
724 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
725 vty_delete_char (vty);
726}
727
728/* Delete a word before the point. */
729static void
730vty_backward_kill_word (struct vty *vty)
731{
732 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
733 vty_delete_backward_char (vty);
734 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
735 vty_delete_backward_char (vty);
736}
737
738/* Transpose chars before or at the point. */
739static void
740vty_transpose_chars (struct vty *vty)
741{
742 char c1, c2;
743
744 /* If length is short or point is near by the beginning of line then
745 return. */
746 if (vty->length < 2 || vty->cp < 1)
747 return;
748
749 /* In case of point is located at the end of the line. */
750 if (vty->cp == vty->length)
751 {
752 c1 = vty->buf[vty->cp - 1];
753 c2 = vty->buf[vty->cp - 2];
754
755 vty_backward_char (vty);
756 vty_backward_char (vty);
757 vty_self_insert_overwrite (vty, c1);
758 vty_self_insert_overwrite (vty, c2);
759 }
760 else
761 {
762 c1 = vty->buf[vty->cp];
763 c2 = vty->buf[vty->cp - 1];
764
765 vty_backward_char (vty);
766 vty_self_insert_overwrite (vty, c1);
767 vty_self_insert_overwrite (vty, c2);
768 }
769}
770
771/* Do completion at vty interface. */
772static void
773vty_complete_command (struct vty *vty)
774{
775 int i;
776 int ret;
777 char **matched = NULL;
778 vector vline;
779
780 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
781 return;
782
783 vline = cmd_make_strvec (vty->buf);
784 if (vline == NULL)
785 return;
786
787 /* In case of 'help \t'. */
788 if (isspace ((int) vty->buf[vty->length - 1]))
789 vector_set (vline, '\0');
790
791 matched = cmd_complete_command (vline, vty, &ret);
792
793 cmd_free_strvec (vline);
794
795 vty_out (vty, "%s", VTY_NEWLINE);
796 switch (ret)
797 {
798 case CMD_ERR_AMBIGUOUS:
799 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
800 vty_prompt (vty);
801 vty_redraw_line (vty);
802 break;
803 case CMD_ERR_NO_MATCH:
804 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
805 vty_prompt (vty);
806 vty_redraw_line (vty);
807 break;
808 case CMD_COMPLETE_FULL_MATCH:
809 vty_prompt (vty);
810 vty_redraw_line (vty);
811 vty_backward_pure_word (vty);
812 vty_insert_word_overwrite (vty, matched[0]);
813 vty_self_insert (vty, ' ');
814 XFREE (MTYPE_TMP, matched[0]);
815 break;
816 case CMD_COMPLETE_MATCH:
817 vty_prompt (vty);
818 vty_redraw_line (vty);
819 vty_backward_pure_word (vty);
820 vty_insert_word_overwrite (vty, matched[0]);
821 XFREE (MTYPE_TMP, matched[0]);
822 vector_only_index_free (matched);
823 return;
824 break;
825 case CMD_COMPLETE_LIST_MATCH:
826 for (i = 0; matched[i] != NULL; i++)
827 {
828 if (i != 0 && ((i % 6) == 0))
829 vty_out (vty, "%s", VTY_NEWLINE);
830 vty_out (vty, "%-10s ", matched[i]);
831 XFREE (MTYPE_TMP, matched[i]);
832 }
833 vty_out (vty, "%s", VTY_NEWLINE);
834
835 vty_prompt (vty);
836 vty_redraw_line (vty);
837 break;
838 case CMD_ERR_NOTHING_TODO:
839 vty_prompt (vty);
840 vty_redraw_line (vty);
841 break;
842 default:
843 break;
844 }
845 if (matched)
846 vector_only_index_free (matched);
847}
848
849void
850vty_describe_fold (struct vty *vty, int cmd_width,
851 int desc_width, struct desc *desc)
852{
853 char *buf, *cmd, *p;
854 int pos;
855
856 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
857
858 if (desc_width <= 0)
859 {
860 vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE);
861 return;
862 }
863
864 buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
865
866 for (p = desc->str; strlen (p) > desc_width; p += pos + 1)
867 {
868 for (pos = desc_width; pos > 0; pos--)
869 if (*(p + pos) == ' ')
870 break;
871
872 if (pos == 0)
873 break;
874
875 strncpy (buf, p, pos);
876 buf[pos] = '\0';
877 vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
878
879 cmd = "";
880 }
881
882 vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
883
884 XFREE (MTYPE_TMP, buf);
885}
886
887/* Describe matched command function. */
888static void
889vty_describe_command (struct vty *vty)
890{
891 int ret;
892 vector vline;
893 vector describe;
894 int i, width, desc_width;
895 struct desc *desc, *desc_cr = NULL;
896
897 vline = cmd_make_strvec (vty->buf);
898
899 /* In case of '> ?'. */
900 if (vline == NULL)
901 {
902 vline = vector_init (1);
903 vector_set (vline, '\0');
904 }
905 else
906 if (isspace ((int) vty->buf[vty->length - 1]))
907 vector_set (vline, '\0');
908
909 describe = cmd_describe_command (vline, vty, &ret);
910
911 vty_out (vty, "%s", VTY_NEWLINE);
912
913 /* Ambiguous error. */
914 switch (ret)
915 {
916 case CMD_ERR_AMBIGUOUS:
917 cmd_free_strvec (vline);
918 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
919 vty_prompt (vty);
920 vty_redraw_line (vty);
921 return;
922 break;
923 case CMD_ERR_NO_MATCH:
924 cmd_free_strvec (vline);
925 vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
926 vty_prompt (vty);
927 vty_redraw_line (vty);
928 return;
929 break;
930 }
931
932 /* Get width of command string. */
933 width = 0;
934 for (i = 0; i < vector_max (describe); i++)
935 if ((desc = vector_slot (describe, i)) != NULL)
936 {
937 int len;
938
939 if (desc->cmd[0] == '\0')
940 continue;
941
942 len = strlen (desc->cmd);
943 if (desc->cmd[0] == '.')
944 len--;
945
946 if (width < len)
947 width = len;
948 }
949
950 /* Get width of description string. */
951 desc_width = vty->width - (width + 6);
952
953 /* Print out description. */
954 for (i = 0; i < vector_max (describe); i++)
955 if ((desc = vector_slot (describe, i)) != NULL)
956 {
957 if (desc->cmd[0] == '\0')
958 continue;
959
960 if (strcmp (desc->cmd, "<cr>") == 0)
961 {
962 desc_cr = desc;
963 continue;
964 }
965
966 if (!desc->str)
967 vty_out (vty, " %-s%s",
968 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
969 VTY_NEWLINE);
970 else if (desc_width >= strlen (desc->str))
971 vty_out (vty, " %-*s %s%s", width,
972 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
973 desc->str, VTY_NEWLINE);
974 else
975 vty_describe_fold (vty, width, desc_width, desc);
976
977#if 0
978 vty_out (vty, " %-*s %s%s", width
979 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
980 desc->str ? desc->str : "", VTY_NEWLINE);
981#endif /* 0 */
982 }
983
984 if ((desc = desc_cr))
985 {
986 if (!desc->str)
987 vty_out (vty, " %-s%s",
988 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
989 VTY_NEWLINE);
990 else if (desc_width >= strlen (desc->str))
991 vty_out (vty, " %-*s %s%s", width,
992 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
993 desc->str, VTY_NEWLINE);
994 else
995 vty_describe_fold (vty, width, desc_width, desc);
996 }
997
998 cmd_free_strvec (vline);
999 vector_free (describe);
1000
1001 vty_prompt (vty);
1002 vty_redraw_line (vty);
1003}
1004
1005void
1006vty_clear_buf (struct vty *vty)
1007{
1008 memset (vty->buf, 0, vty->max);
1009}
1010
1011/* ^C stop current input and do not add command line to the history. */
1012static void
1013vty_stop_input (struct vty *vty)
1014{
1015 vty->cp = vty->length = 0;
1016 vty_clear_buf (vty);
1017 vty_out (vty, "%s", VTY_NEWLINE);
1018
1019 switch (vty->node)
1020 {
1021 case VIEW_NODE:
1022 case ENABLE_NODE:
1023 /* Nothing to do. */
1024 break;
1025 case CONFIG_NODE:
1026 case INTERFACE_NODE:
1027 case ZEBRA_NODE:
1028 case RIP_NODE:
1029 case RIPNG_NODE:
1030 case BGP_NODE:
1031 case RMAP_NODE:
1032 case OSPF_NODE:
1033 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00001034 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00001035 case KEYCHAIN_NODE:
1036 case KEYCHAIN_KEY_NODE:
1037 case MASC_NODE:
1038 case VTY_NODE:
1039 vty_config_unlock (vty);
1040 vty->node = ENABLE_NODE;
1041 break;
1042 default:
1043 /* Unknown node, we have to ignore it. */
1044 break;
1045 }
1046 vty_prompt (vty);
1047
1048 /* Set history pointer to the latest one. */
1049 vty->hp = vty->hindex;
1050}
1051
1052/* Add current command line to the history buffer. */
1053static void
1054vty_hist_add (struct vty *vty)
1055{
1056 int index;
1057
1058 if (vty->length == 0)
1059 return;
1060
1061 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1062
1063 /* Ignore the same string as previous one. */
1064 if (vty->hist[index])
1065 if (strcmp (vty->buf, vty->hist[index]) == 0)
1066 {
1067 vty->hp = vty->hindex;
1068 return;
1069 }
1070
1071 /* Insert history entry. */
1072 if (vty->hist[vty->hindex])
1073 XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1074 vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
1075
1076 /* History index rotation. */
1077 vty->hindex++;
1078 if (vty->hindex == VTY_MAXHIST)
1079 vty->hindex = 0;
1080
1081 vty->hp = vty->hindex;
1082}
1083
1084/* #define TELNET_OPTION_DEBUG */
1085
1086/* Get telnet window size. */
1087static int
1088vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
1089{
1090#ifdef TELNET_OPTION_DEBUG
1091 int i;
1092
1093 for (i = 0; i < nbytes; i++)
1094 {
1095 switch (buf[i])
1096 {
1097 case IAC:
1098 vty_out (vty, "IAC ");
1099 break;
1100 case WILL:
1101 vty_out (vty, "WILL ");
1102 break;
1103 case WONT:
1104 vty_out (vty, "WONT ");
1105 break;
1106 case DO:
1107 vty_out (vty, "DO ");
1108 break;
1109 case DONT:
1110 vty_out (vty, "DONT ");
1111 break;
1112 case SB:
1113 vty_out (vty, "SB ");
1114 break;
1115 case SE:
1116 vty_out (vty, "SE ");
1117 break;
1118 case TELOPT_ECHO:
1119 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
1120 break;
1121 case TELOPT_SGA:
1122 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
1123 break;
1124 case TELOPT_NAWS:
1125 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
1126 break;
1127 default:
1128 vty_out (vty, "%x ", buf[i]);
1129 break;
1130 }
1131 }
1132 vty_out (vty, "%s", VTY_NEWLINE);
1133
1134#endif /* TELNET_OPTION_DEBUG */
1135
1136 switch (buf[0])
1137 {
1138 case SB:
1139 buffer_reset(vty->sb_buffer);
1140 vty->iac_sb_in_progress = 1;
1141 return 0;
1142 break;
1143 case SE:
1144 {
paul5b8c1b02003-10-15 23:08:55 +00001145 char *buffer;
1146 int length;
paul718e3742002-12-13 20:15:29 +00001147
1148 if (!vty->iac_sb_in_progress)
1149 return 0;
1150
paul5b8c1b02003-10-15 23:08:55 +00001151 buffer = (char *)vty->sb_buffer->head->data;
1152 length = vty->sb_buffer->length;
1153
1154 if (buffer == NULL)
1155 return 0;
1156
paul718e3742002-12-13 20:15:29 +00001157 if (buffer[0] == '\0')
1158 {
1159 vty->iac_sb_in_progress = 0;
1160 return 0;
1161 }
1162 switch (buffer[0])
1163 {
1164 case TELOPT_NAWS:
1165 if (length < 5)
1166 break;
1167 vty->width = buffer[2];
1168 vty->height = vty->lines >= 0 ? vty->lines : buffer[4];
1169 break;
1170 }
1171 vty->iac_sb_in_progress = 0;
1172 return 0;
1173 break;
1174 }
1175 default:
1176 break;
1177 }
1178 return 1;
1179}
1180
1181/* Execute current command line. */
1182static int
1183vty_execute (struct vty *vty)
1184{
1185 int ret;
1186
1187 ret = CMD_SUCCESS;
1188
1189 switch (vty->node)
1190 {
1191 case AUTH_NODE:
1192 case AUTH_ENABLE_NODE:
1193 vty_auth (vty, vty->buf);
1194 break;
1195 default:
1196 ret = vty_command (vty, vty->buf);
1197 if (vty->type == VTY_TERM)
1198 vty_hist_add (vty);
1199 break;
1200 }
1201
1202 /* Clear command line buffer. */
1203 vty->cp = vty->length = 0;
1204 vty_clear_buf (vty);
1205
1206 if (vty->status != VTY_CLOSE
1207 && vty->status != VTY_START
1208 && vty->status != VTY_CONTINUE)
1209 vty_prompt (vty);
1210
1211 return ret;
1212}
1213
1214#define CONTROL(X) ((X) - '@')
1215#define VTY_NORMAL 0
1216#define VTY_PRE_ESCAPE 1
1217#define VTY_ESCAPE 2
1218
1219/* Escape character command map. */
1220static void
1221vty_escape_map (unsigned char c, struct vty *vty)
1222{
1223 switch (c)
1224 {
1225 case ('A'):
1226 vty_previous_line (vty);
1227 break;
1228 case ('B'):
1229 vty_next_line (vty);
1230 break;
1231 case ('C'):
1232 vty_forward_char (vty);
1233 break;
1234 case ('D'):
1235 vty_backward_char (vty);
1236 break;
1237 default:
1238 break;
1239 }
1240
1241 /* Go back to normal mode. */
1242 vty->escape = VTY_NORMAL;
1243}
1244
1245/* Quit print out to the buffer. */
1246static void
1247vty_buffer_reset (struct vty *vty)
1248{
1249 buffer_reset (vty->obuf);
1250 vty_prompt (vty);
1251 vty_redraw_line (vty);
1252}
1253
1254/* Read data via vty socket. */
1255static int
1256vty_read (struct thread *thread)
1257{
1258 int i;
paul718e3742002-12-13 20:15:29 +00001259 int nbytes;
1260 unsigned char buf[VTY_READ_BUFSIZ];
1261
1262 int vty_sock = THREAD_FD (thread);
1263 struct vty *vty = THREAD_ARG (thread);
1264 vty->t_read = NULL;
1265
1266 /* Read raw data from socket */
1267 nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ);
1268 if (nbytes <= 0)
1269 vty->status = VTY_CLOSE;
1270
1271 for (i = 0; i < nbytes; i++)
1272 {
1273 if (buf[i] == IAC)
1274 {
1275 if (!vty->iac)
1276 {
1277 vty->iac = 1;
1278 continue;
1279 }
1280 else
1281 {
1282 vty->iac = 0;
1283 }
1284 }
1285
1286 if (vty->iac_sb_in_progress && !vty->iac)
1287 {
1288 buffer_putc(vty->sb_buffer, buf[i]);
1289 continue;
1290 }
1291
1292 if (vty->iac)
1293 {
1294 /* In case of telnet command */
paul5b8c1b02003-10-15 23:08:55 +00001295 int ret = 0;
paule9372532003-10-26 21:36:07 +00001296 ret = vty_telnet_option (vty, buf + i, nbytes - i);
paul718e3742002-12-13 20:15:29 +00001297 vty->iac = 0;
1298 i += ret;
1299 continue;
1300 }
paul5b8c1b02003-10-15 23:08:55 +00001301
paul718e3742002-12-13 20:15:29 +00001302
1303 if (vty->status == VTY_MORE)
1304 {
1305 switch (buf[i])
1306 {
1307 case CONTROL('C'):
1308 case 'q':
1309 case 'Q':
1310 if (vty->output_func)
1311 (*vty->output_func) (vty, 1);
1312 vty_buffer_reset (vty);
1313 break;
1314#if 0 /* More line does not work for "show ip bgp". */
1315 case '\n':
1316 case '\r':
1317 vty->status = VTY_MORELINE;
1318 break;
1319#endif
1320 default:
1321 if (vty->output_func)
1322 (*vty->output_func) (vty, 0);
1323 break;
1324 }
1325 continue;
1326 }
1327
1328 /* Escape character. */
1329 if (vty->escape == VTY_ESCAPE)
1330 {
1331 vty_escape_map (buf[i], vty);
1332 continue;
1333 }
1334
1335 /* Pre-escape status. */
1336 if (vty->escape == VTY_PRE_ESCAPE)
1337 {
1338 switch (buf[i])
1339 {
1340 case '[':
1341 vty->escape = VTY_ESCAPE;
1342 break;
1343 case 'b':
1344 vty_backward_word (vty);
1345 vty->escape = VTY_NORMAL;
1346 break;
1347 case 'f':
1348 vty_forward_word (vty);
1349 vty->escape = VTY_NORMAL;
1350 break;
1351 case 'd':
1352 vty_forward_kill_word (vty);
1353 vty->escape = VTY_NORMAL;
1354 break;
1355 case CONTROL('H'):
1356 case 0x7f:
1357 vty_backward_kill_word (vty);
1358 vty->escape = VTY_NORMAL;
1359 break;
1360 default:
1361 vty->escape = VTY_NORMAL;
1362 break;
1363 }
1364 continue;
1365 }
1366
1367 switch (buf[i])
1368 {
1369 case CONTROL('A'):
1370 vty_beginning_of_line (vty);
1371 break;
1372 case CONTROL('B'):
1373 vty_backward_char (vty);
1374 break;
1375 case CONTROL('C'):
1376 vty_stop_input (vty);
1377 break;
1378 case CONTROL('D'):
1379 vty_delete_char (vty);
1380 break;
1381 case CONTROL('E'):
1382 vty_end_of_line (vty);
1383 break;
1384 case CONTROL('F'):
1385 vty_forward_char (vty);
1386 break;
1387 case CONTROL('H'):
1388 case 0x7f:
1389 vty_delete_backward_char (vty);
1390 break;
1391 case CONTROL('K'):
1392 vty_kill_line (vty);
1393 break;
1394 case CONTROL('N'):
1395 vty_next_line (vty);
1396 break;
1397 case CONTROL('P'):
1398 vty_previous_line (vty);
1399 break;
1400 case CONTROL('T'):
1401 vty_transpose_chars (vty);
1402 break;
1403 case CONTROL('U'):
1404 vty_kill_line_from_beginning (vty);
1405 break;
1406 case CONTROL('W'):
1407 vty_backward_kill_word (vty);
1408 break;
1409 case CONTROL('Z'):
1410 vty_end_config (vty);
1411 break;
1412 case '\n':
1413 case '\r':
1414 vty_out (vty, "%s", VTY_NEWLINE);
1415 vty_execute (vty);
1416 break;
1417 case '\t':
1418 vty_complete_command (vty);
1419 break;
1420 case '?':
1421 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1422 vty_self_insert (vty, buf[i]);
1423 else
1424 vty_describe_command (vty);
1425 break;
1426 case '\033':
1427 if (i + 1 < nbytes && buf[i + 1] == '[')
1428 {
1429 vty->escape = VTY_ESCAPE;
1430 i++;
1431 }
1432 else
1433 vty->escape = VTY_PRE_ESCAPE;
1434 break;
1435 default:
1436 if (buf[i] > 31 && buf[i] < 127)
1437 vty_self_insert (vty, buf[i]);
1438 break;
1439 }
1440 }
1441
1442 /* Check status. */
1443 if (vty->status == VTY_CLOSE)
1444 vty_close (vty);
1445 else
1446 {
1447 vty_event (VTY_WRITE, vty_sock, vty);
1448 vty_event (VTY_READ, vty_sock, vty);
1449 }
1450 return 0;
1451}
1452
1453/* Flush buffer to the vty. */
1454static int
1455vty_flush (struct thread *thread)
1456{
1457 int erase;
1458 int dont_more;
1459 int vty_sock = THREAD_FD (thread);
1460 struct vty *vty = THREAD_ARG (thread);
1461 vty->t_write = NULL;
1462
1463 /* Tempolary disable read thread. */
1464 if (vty->lines == 0)
1465 if (vty->t_read)
1466 {
1467 thread_cancel (vty->t_read);
1468 vty->t_read = NULL;
1469 }
1470
1471 /* Function execution continue. */
1472 if (vty->status == VTY_START || vty->status == VTY_CONTINUE)
1473 {
1474 if (vty->status == VTY_CONTINUE)
1475 erase = 1;
1476 else
1477 erase = 0;
1478
1479 if (vty->output_func == NULL)
1480 dont_more = 1;
1481 else
1482 dont_more = 0;
1483
1484 if (vty->lines == 0)
1485 {
1486 erase = 0;
1487 dont_more = 1;
1488 }
1489
1490 buffer_flush_vty_all (vty->obuf, vty->fd, erase, dont_more);
1491
1492 if (vty->status == VTY_CLOSE)
1493 {
1494 vty_close (vty);
1495 return 0;
1496 }
1497
1498 if (vty->output_func == NULL)
1499 {
1500 vty->status = VTY_NORMAL;
1501 vty_prompt (vty);
1502 vty_event (VTY_WRITE, vty_sock, vty);
1503 }
1504 else
1505 vty->status = VTY_MORE;
1506
1507 if (vty->lines == 0)
1508 {
1509 if (vty->output_func == NULL)
1510 vty_event (VTY_READ, vty_sock, vty);
1511 else
1512 {
1513 if (vty->output_func)
1514 (*vty->output_func) (vty, 0);
1515 vty_event (VTY_WRITE, vty_sock, vty);
1516 }
1517 }
1518 }
1519 else
1520 {
1521 if (vty->status == VTY_MORE || vty->status == VTY_MORELINE)
1522 erase = 1;
1523 else
1524 erase = 0;
1525
1526 if (vty->lines == 0)
1527 buffer_flush_window (vty->obuf, vty->fd, vty->width, 25, 0, 1);
1528 else if (vty->status == VTY_MORELINE)
1529 buffer_flush_window (vty->obuf, vty->fd, vty->width, 1, erase, 0);
1530 else
1531 buffer_flush_window (vty->obuf, vty->fd, vty->width,
1532 vty->lines >= 0 ? vty->lines : vty->height,
1533 erase, 0);
1534
1535 if (buffer_empty (vty->obuf))
1536 {
1537 if (vty->status == VTY_CLOSE)
1538 vty_close (vty);
1539 else
1540 {
1541 vty->status = VTY_NORMAL;
1542
1543 if (vty->lines == 0)
1544 vty_event (VTY_READ, vty_sock, vty);
1545 }
1546 }
1547 else
1548 {
1549 vty->status = VTY_MORE;
1550
1551 if (vty->lines == 0)
1552 vty_event (VTY_WRITE, vty_sock, vty);
1553 }
1554 }
1555
1556 return 0;
1557}
1558
1559/* Create new vty structure. */
1560struct vty *
1561vty_create (int vty_sock, union sockunion *su)
1562{
1563 struct vty *vty;
1564
1565 /* Allocate new vty structure and set up default values. */
1566 vty = vty_new ();
1567 vty->fd = vty_sock;
1568 vty->type = VTY_TERM;
1569 vty->address = sockunion_su2str (su);
1570 if (no_password_check)
1571 {
1572 if (host.advanced)
1573 vty->node = ENABLE_NODE;
1574 else
1575 vty->node = VIEW_NODE;
1576 }
1577 else
1578 vty->node = AUTH_NODE;
1579 vty->fail = 0;
1580 vty->cp = 0;
1581 vty_clear_buf (vty);
1582 vty->length = 0;
1583 memset (vty->hist, 0, sizeof (vty->hist));
1584 vty->hp = 0;
1585 vty->hindex = 0;
1586 vector_set_index (vtyvec, vty_sock, vty);
1587 vty->status = VTY_NORMAL;
1588 vty->v_timeout = vty_timeout_val;
1589 if (host.lines >= 0)
1590 vty->lines = host.lines;
1591 else
1592 vty->lines = -1;
1593 vty->iac = 0;
1594 vty->iac_sb_in_progress = 0;
1595 vty->sb_buffer = buffer_new (1024);
1596
1597 if (! no_password_check)
1598 {
1599 /* Vty is not available if password isn't set. */
1600 if (host.password == NULL && host.password_encrypt == NULL)
1601 {
1602 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1603 vty->status = VTY_CLOSE;
1604 vty_close (vty);
1605 return NULL;
1606 }
1607 }
1608
1609 /* Say hello to the world. */
1610 vty_hello (vty);
1611 if (! no_password_check)
1612 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1613
1614 /* Setting up terminal. */
1615 vty_will_echo (vty);
1616 vty_will_suppress_go_ahead (vty);
1617
1618 vty_dont_linemode (vty);
1619 vty_do_window_size (vty);
1620 /* vty_dont_lflow_ahead (vty); */
1621
1622 vty_prompt (vty);
1623
1624 /* Add read/write thread. */
1625 vty_event (VTY_WRITE, vty_sock, vty);
1626 vty_event (VTY_READ, vty_sock, vty);
1627
1628 return vty;
1629}
1630
1631/* Accept connection from the network. */
1632static int
1633vty_accept (struct thread *thread)
1634{
1635 int vty_sock;
1636 struct vty *vty;
1637 union sockunion su;
1638 int ret;
1639 unsigned int on;
1640 int accept_sock;
1641 struct prefix *p = NULL;
1642 struct access_list *acl = NULL;
1643
1644 accept_sock = THREAD_FD (thread);
1645
1646 /* We continue hearing vty socket. */
1647 vty_event (VTY_SERV, accept_sock, NULL);
1648
1649 memset (&su, 0, sizeof (union sockunion));
1650
1651 /* We can handle IPv4 or IPv6 socket. */
1652 vty_sock = sockunion_accept (accept_sock, &su);
1653 if (vty_sock < 0)
1654 {
1655 zlog_warn ("can't accept vty socket : %s", strerror (errno));
1656 return -1;
1657 }
1658
1659 p = sockunion2hostprefix (&su);
1660
1661 /* VTY's accesslist apply. */
1662 if (p->family == AF_INET && vty_accesslist_name)
1663 {
1664 if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
1665 (access_list_apply (acl, p) == FILTER_DENY))
1666 {
1667 char *buf;
1668 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
1669 (buf = sockunion_su2str (&su)));
1670 free (buf);
1671 close (vty_sock);
1672
1673 /* continue accepting connections */
1674 vty_event (VTY_SERV, accept_sock, NULL);
1675
1676 prefix_free (p);
1677
1678 return 0;
1679 }
1680 }
1681
1682#ifdef HAVE_IPV6
1683 /* VTY's ipv6 accesslist apply. */
1684 if (p->family == AF_INET6 && vty_ipv6_accesslist_name)
1685 {
1686 if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
1687 (access_list_apply (acl, p) == FILTER_DENY))
1688 {
1689 char *buf;
1690 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
1691 (buf = sockunion_su2str (&su)));
1692 free (buf);
1693 close (vty_sock);
1694
1695 /* continue accepting connections */
1696 vty_event (VTY_SERV, accept_sock, NULL);
1697
1698 prefix_free (p);
1699
1700 return 0;
1701 }
1702 }
1703#endif /* HAVE_IPV6 */
1704
1705 prefix_free (p);
1706
1707 on = 1;
1708 ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
1709 (char *) &on, sizeof (on));
1710 if (ret < 0)
1711 zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
1712 strerror (errno));
1713
1714 vty = vty_create (vty_sock, &su);
1715
1716 return 0;
1717}
1718
1719#if defined(HAVE_IPV6) && !defined(NRL)
1720void
1721vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
1722{
1723 int ret;
1724 struct addrinfo req;
1725 struct addrinfo *ainfo;
1726 struct addrinfo *ainfo_save;
1727 int sock;
1728 char port_str[BUFSIZ];
1729
1730 memset (&req, 0, sizeof (struct addrinfo));
1731 req.ai_flags = AI_PASSIVE;
1732 req.ai_family = AF_UNSPEC;
1733 req.ai_socktype = SOCK_STREAM;
1734 sprintf (port_str, "%d", port);
1735 port_str[sizeof (port_str) - 1] = '\0';
1736
1737 ret = getaddrinfo (hostname, port_str, &req, &ainfo);
1738
1739 if (ret != 0)
1740 {
1741 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
1742 exit (1);
1743 }
1744
1745 ainfo_save = ainfo;
1746
1747 do
1748 {
1749 if (ainfo->ai_family != AF_INET
1750#ifdef HAVE_IPV6
1751 && ainfo->ai_family != AF_INET6
1752#endif /* HAVE_IPV6 */
1753 )
1754 continue;
1755
1756 sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
1757 if (sock < 0)
1758 continue;
1759
1760 sockopt_reuseaddr (sock);
1761 sockopt_reuseport (sock);
1762
1763 ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
1764 if (ret < 0)
1765 {
1766 close (sock); /* Avoid sd leak. */
1767 continue;
1768 }
1769
1770 ret = listen (sock, 3);
1771 if (ret < 0)
1772 {
1773 close (sock); /* Avoid sd leak. */
1774 continue;
1775 }
1776
1777 vty_event (VTY_SERV, sock, NULL);
1778 }
1779 while ((ainfo = ainfo->ai_next) != NULL);
1780
1781 freeaddrinfo (ainfo_save);
1782}
1783#endif /* HAVE_IPV6 && ! NRL */
1784
1785/* Make vty server socket. */
1786void
paul29db05b2003-05-08 20:10:22 +00001787vty_serv_sock_family (const char* addr, unsigned short port, int family)
paul718e3742002-12-13 20:15:29 +00001788{
1789 int ret;
1790 union sockunion su;
1791 int accept_sock;
paul29db05b2003-05-08 20:10:22 +00001792 void* naddr=NULL;
paul718e3742002-12-13 20:15:29 +00001793
1794 memset (&su, 0, sizeof (union sockunion));
1795 su.sa.sa_family = family;
paul29db05b2003-05-08 20:10:22 +00001796 if(addr)
1797 switch(family)
1798 {
1799 case AF_INET:
1800 naddr=&su.sin.sin_addr;
1801#ifdef HAVE_IPV6
1802 case AF_INET6:
1803 naddr=&su.sin6.sin6_addr;
1804#endif
1805 }
1806
1807 if(naddr)
1808 switch(inet_pton(family,addr,naddr))
1809 {
1810 case -1:
1811 zlog_err("bad address %s",addr);
1812 naddr=NULL;
1813 break;
1814 case 0:
1815 zlog_err("error translating address %s: %s",addr,strerror(errno));
1816 naddr=NULL;
1817 }
paul718e3742002-12-13 20:15:29 +00001818
1819 /* Make new socket. */
1820 accept_sock = sockunion_stream_socket (&su);
1821 if (accept_sock < 0)
1822 return;
1823
1824 /* This is server, so reuse address. */
1825 sockopt_reuseaddr (accept_sock);
1826 sockopt_reuseport (accept_sock);
1827
1828 /* Bind socket to universal address and given port. */
paul29db05b2003-05-08 20:10:22 +00001829 ret = sockunion_bind (accept_sock, &su, port, naddr);
paul718e3742002-12-13 20:15:29 +00001830 if (ret < 0)
1831 {
paul29db05b2003-05-08 20:10:22 +00001832 zlog_warn("can't bind socket");
paul718e3742002-12-13 20:15:29 +00001833 close (accept_sock); /* Avoid sd leak. */
1834 return;
1835 }
1836
1837 /* Listen socket under queue 3. */
1838 ret = listen (accept_sock, 3);
1839 if (ret < 0)
1840 {
1841 zlog (NULL, LOG_WARNING, "can't listen socket");
1842 close (accept_sock); /* Avoid sd leak. */
1843 return;
1844 }
1845
1846 /* Add vty server event. */
1847 vty_event (VTY_SERV, accept_sock, NULL);
1848}
1849
1850#ifdef VTYSH
1851/* For sockaddr_un. */
1852#include <sys/un.h>
1853
1854/* VTY shell UNIX domain socket. */
1855void
1856vty_serv_un (char *path)
1857{
1858 int ret;
1859 int sock, len;
1860 struct sockaddr_un serv;
1861 mode_t old_mask;
pauledd7c242003-06-04 13:59:38 +00001862 struct zprivs_ids_t ids;
1863
paul718e3742002-12-13 20:15:29 +00001864 /* First of all, unlink existing socket */
1865 unlink (path);
1866
1867 /* Set umask */
paul1921e6f2003-05-23 08:12:36 +00001868 old_mask = umask (0007);
paul718e3742002-12-13 20:15:29 +00001869
1870 /* Make UNIX domain socket. */
1871 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1872 if (sock < 0)
1873 {
1874 perror ("sock");
1875 return;
1876 }
1877
1878 /* Make server socket. */
1879 memset (&serv, 0, sizeof (struct sockaddr_un));
1880 serv.sun_family = AF_UNIX;
1881 strncpy (serv.sun_path, path, strlen (path));
1882#ifdef HAVE_SUN_LEN
1883 len = serv.sun_len = SUN_LEN(&serv);
1884#else
1885 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
1886#endif /* HAVE_SUN_LEN */
1887
1888 ret = bind (sock, (struct sockaddr *) &serv, len);
1889 if (ret < 0)
1890 {
1891 perror ("bind");
1892 close (sock); /* Avoid sd leak. */
1893 return;
1894 }
1895
1896 ret = listen (sock, 5);
1897 if (ret < 0)
1898 {
1899 perror ("listen");
1900 close (sock); /* Avoid sd leak. */
1901 return;
1902 }
1903
1904 umask (old_mask);
1905
pauledd7c242003-06-04 13:59:38 +00001906 zprivs_get_ids(&ids);
1907
1908 if (ids.gid_vty > 0)
1909 {
1910 /* set group of socket */
1911 if ( chown (path, -1, ids.gid_vty) )
1912 {
1913 zlog_err ("vty_serv_un: could chown socket, %s",
1914 strerror (errno) );
1915 }
1916 }
1917
paul718e3742002-12-13 20:15:29 +00001918 vty_event (VTYSH_SERV, sock, NULL);
1919}
1920
1921/* #define VTYSH_DEBUG 1 */
1922
1923static int
1924vtysh_accept (struct thread *thread)
1925{
1926 int accept_sock;
1927 int sock;
1928 int client_len;
1929 struct sockaddr_un client;
1930 struct vty *vty;
1931
1932 accept_sock = THREAD_FD (thread);
1933
1934 vty_event (VTYSH_SERV, accept_sock, NULL);
1935
1936 memset (&client, 0, sizeof (struct sockaddr_un));
1937 client_len = sizeof (struct sockaddr_un);
1938
1939 sock = accept (accept_sock, (struct sockaddr *) &client, &client_len);
1940
1941 if (sock < 0)
1942 {
1943 zlog_warn ("can't accept vty socket : %s", strerror (errno));
1944 return -1;
1945 }
1946
1947#ifdef VTYSH_DEBUG
1948 printf ("VTY shell accept\n");
1949#endif /* VTYSH_DEBUG */
1950
1951 vty = vty_new ();
1952 vty->fd = sock;
1953 vty->type = VTY_SHELL_SERV;
1954 vty->node = VIEW_NODE;
1955
1956 vty_event (VTYSH_READ, sock, vty);
1957
1958 return 0;
1959}
1960
1961static int
1962vtysh_read (struct thread *thread)
1963{
1964 int ret;
1965 int sock;
1966 int nbytes;
1967 struct vty *vty;
1968 unsigned char buf[VTY_READ_BUFSIZ];
1969 u_char header[4] = {0, 0, 0, 0};
1970
1971 sock = THREAD_FD (thread);
1972 vty = THREAD_ARG (thread);
1973 vty->t_read = NULL;
1974
1975 nbytes = read (sock, buf, VTY_READ_BUFSIZ);
1976 if (nbytes <= 0)
1977 {
1978 vty_close (vty);
1979#ifdef VTYSH_DEBUG
1980 printf ("close vtysh\n");
1981#endif /* VTYSH_DEBUG */
1982 return 0;
1983 }
1984
1985#ifdef VTYSH_DEBUG
1986 printf ("line: %s\n", buf);
1987#endif /* VTYSH_DEBUG */
1988
1989 vty_ensure (vty, nbytes);
1990 memcpy (vty->buf, buf, nbytes);
1991
1992 /* Pass this line to parser. */
1993 ret = vty_execute (vty);
1994
1995 vty_clear_buf (vty);
1996
1997 /* Return result. */
1998#ifdef VTYSH_DEBUG
1999 printf ("result: %d\n", ret);
2000 printf ("vtysh node: %d\n", vty->node);
2001#endif /* VTYSH_DEBUG */
2002
2003 header[3] = ret;
2004 write (vty->fd, header, 4);
2005
2006 vty_event (VTYSH_READ, sock, vty);
2007
2008 return 0;
2009}
2010#endif /* VTYSH */
2011
2012/* Determine address family to bind. */
2013void
paul29db05b2003-05-08 20:10:22 +00002014vty_serv_sock (const char *addr, unsigned short port, char *path)
paul718e3742002-12-13 20:15:29 +00002015{
2016 /* If port is set to 0, do not listen on TCP/IP at all! */
2017 if (port)
2018 {
2019
2020#ifdef HAVE_IPV6
2021#ifdef NRL
paul29db05b2003-05-08 20:10:22 +00002022 vty_serv_sock_family (addr, port, AF_INET);
2023 vty_serv_sock_family (addr, port, AF_INET6);
paul718e3742002-12-13 20:15:29 +00002024#else /* ! NRL */
paul29db05b2003-05-08 20:10:22 +00002025 vty_serv_sock_addrinfo (addr, port);
paul718e3742002-12-13 20:15:29 +00002026#endif /* NRL*/
2027#else /* ! HAVE_IPV6 */
paul29db05b2003-05-08 20:10:22 +00002028 vty_serv_sock_family (addr,port, AF_INET);
paul718e3742002-12-13 20:15:29 +00002029#endif /* HAVE_IPV6 */
2030 }
2031
2032#ifdef VTYSH
2033 vty_serv_un (path);
2034#endif /* VTYSH */
2035}
2036
2037/* Close vty interface. */
2038void
2039vty_close (struct vty *vty)
2040{
2041 int i;
2042
2043 /* Cancel threads.*/
2044 if (vty->t_read)
2045 thread_cancel (vty->t_read);
2046 if (vty->t_write)
2047 thread_cancel (vty->t_write);
2048 if (vty->t_timeout)
2049 thread_cancel (vty->t_timeout);
2050 if (vty->t_output)
2051 thread_cancel (vty->t_output);
2052
2053 /* Flush buffer. */
2054 if (! buffer_empty (vty->obuf))
2055 buffer_flush_all (vty->obuf, vty->fd);
2056
2057 /* Free input buffer. */
2058 buffer_free (vty->obuf);
2059
2060 /* Free SB buffer. */
2061 if (vty->sb_buffer)
2062 buffer_free (vty->sb_buffer);
2063
2064 /* Free command history. */
2065 for (i = 0; i < VTY_MAXHIST; i++)
2066 if (vty->hist[i])
2067 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2068
2069 /* Unset vector. */
2070 vector_unset (vtyvec, vty->fd);
2071
2072 /* Close socket. */
2073 if (vty->fd > 0)
2074 close (vty->fd);
2075
2076 if (vty->address)
2077 XFREE (0, vty->address);
2078 if (vty->buf)
2079 XFREE (MTYPE_VTY, vty->buf);
2080
2081 /* Check configure. */
2082 vty_config_unlock (vty);
2083
2084 /* OK free vty. */
2085 XFREE (MTYPE_VTY, vty);
2086}
2087
2088/* When time out occur output message then close connection. */
2089static int
2090vty_timeout (struct thread *thread)
2091{
2092 struct vty *vty;
2093
2094 vty = THREAD_ARG (thread);
2095 vty->t_timeout = NULL;
2096 vty->v_timeout = 0;
2097
2098 /* Clear buffer*/
2099 buffer_reset (vty->obuf);
2100 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2101
2102 /* Close connection. */
2103 vty->status = VTY_CLOSE;
2104 vty_close (vty);
2105
2106 return 0;
2107}
2108
2109/* Read up configuration file from file_name. */
2110static void
2111vty_read_file (FILE *confp)
2112{
2113 int ret;
2114 struct vty *vty;
2115
2116 vty = vty_new ();
2117 vty->fd = 0; /* stdout */
2118 vty->type = VTY_TERM;
2119 vty->node = CONFIG_NODE;
2120
2121 /* Execute configuration file */
2122 ret = config_from_file (vty, confp);
2123
paul7021c422003-07-15 12:52:22 +00002124 if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
paul718e3742002-12-13 20:15:29 +00002125 {
2126 switch (ret)
paul7021c422003-07-15 12:52:22 +00002127 {
2128 case CMD_ERR_AMBIGUOUS:
2129 fprintf (stderr, "Ambiguous command.\n");
2130 break;
2131 case CMD_ERR_NO_MATCH:
2132 fprintf (stderr, "There is no such command.\n");
2133 break;
2134 }
paul718e3742002-12-13 20:15:29 +00002135 fprintf (stderr, "Error occured during reading below line.\n%s\n",
2136 vty->buf);
2137 vty_close (vty);
2138 exit (1);
2139 }
2140
2141 vty_close (vty);
2142}
2143
2144FILE *
2145vty_use_backup_config (char *fullpath)
2146{
2147 char *fullpath_sav, *fullpath_tmp;
2148 FILE *ret = NULL;
2149 struct stat buf;
2150 int tmp, sav;
2151 int c;
2152 char buffer[512];
2153
2154 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2155 strcpy (fullpath_sav, fullpath);
2156 strcat (fullpath_sav, CONF_BACKUP_EXT);
2157 if (stat (fullpath_sav, &buf) == -1)
2158 {
2159 free (fullpath_sav);
2160 return NULL;
2161 }
2162
2163 fullpath_tmp = malloc (strlen (fullpath) + 8);
2164 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2165
2166 /* Open file to configuration write. */
2167 tmp = mkstemp (fullpath_tmp);
2168 if (tmp < 0)
2169 {
2170 free (fullpath_sav);
2171 free (fullpath_tmp);
2172 return NULL;
2173 }
2174
2175 sav = open (fullpath_sav, O_RDONLY);
2176 if (sav < 0)
2177 {
gdt3dbf9962003-12-22 20:18:18 +00002178 unlink (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002179 free (fullpath_sav);
2180 free (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002181 return NULL;
2182 }
2183
2184 while((c = read (sav, buffer, 512)) > 0)
2185 write (tmp, buffer, c);
2186
2187 close (sav);
2188 close (tmp);
2189
gdtaa593d52003-12-22 20:15:53 +00002190 if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
2191 {
gdt3dbf9962003-12-22 20:18:18 +00002192 unlink (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002193 free (fullpath_sav);
2194 free (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002195 return NULL;
2196 }
2197
paul718e3742002-12-13 20:15:29 +00002198 if (link (fullpath_tmp, fullpath) == 0)
2199 ret = fopen (fullpath, "r");
2200
2201 unlink (fullpath_tmp);
2202
2203 free (fullpath_sav);
2204 free (fullpath_tmp);
2205 return fopen (fullpath, "r");
2206}
2207
2208/* Read up configuration file from file_name. */
2209void
2210vty_read_config (char *config_file,
2211 char *config_current_dir,
2212 char *config_default_dir)
2213{
paulccc92352003-10-22 02:49:38 +00002214 char cwd[MAXPATHLEN];
paul718e3742002-12-13 20:15:29 +00002215 FILE *confp = NULL;
2216 char *fullpath;
2217
2218 /* If -f flag specified. */
2219 if (config_file != NULL)
2220 {
2221 if (! IS_DIRECTORY_SEP (config_file[0]))
2222 {
paulccc92352003-10-22 02:49:38 +00002223 getcwd (cwd, MAXPATHLEN);
paul718e3742002-12-13 20:15:29 +00002224 fullpath = XMALLOC (MTYPE_TMP,
2225 strlen (cwd) + strlen (config_file) + 2);
2226 sprintf (fullpath, "%s/%s", cwd, config_file);
2227 }
2228 else
2229 fullpath = config_file;
2230
2231 confp = fopen (fullpath, "r");
2232
2233 if (confp == NULL)
2234 {
2235 confp = vty_use_backup_config (fullpath);
2236 if (confp)
2237 fprintf (stderr, "WARNING: using backup configuration file!\n");
2238 else
2239 {
2240 fprintf (stderr, "can't open configuration file [%s]\n",
2241 config_file);
2242 exit(1);
2243 }
2244 }
2245 }
2246 else
2247 {
2248 /* Relative path configuration file open. */
2249 if (config_current_dir)
2250 {
2251 confp = fopen (config_current_dir, "r");
2252 if (confp == NULL)
2253 {
2254 confp = vty_use_backup_config (config_current_dir);
2255 if (confp)
2256 fprintf (stderr, "WARNING: using backup configuration file!\n");
2257 }
2258 }
2259
2260 /* If there is no relative path exists, open system default file. */
2261 if (confp == NULL)
2262 {
2263#ifdef VTYSH
2264 int ret;
2265 struct stat conf_stat;
2266
2267 /* !!!!PLEASE LEAVE!!!!
2268 This is NEEDED for use with vtysh -b, or else you can get
2269 a real configuration food fight with a lot garbage in the
2270 merged configuration file it creates coming from the per
2271 daemon configuration files. This also allows the daemons
2272 to start if there default configuration file is not
2273 present or ignore them, as needed when using vtysh -b to
2274 configure the daemons at boot - MAG */
2275
2276 /* Stat for vtysh Zebra.conf, if found startup and wait for
2277 boot configuration */
2278
2279 if ( strstr(config_default_dir, "vtysh") == NULL)
2280 {
2281 ret = stat (integrate_default, &conf_stat);
2282 if (ret >= 0)
2283 {
2284 return;
2285 }
2286 }
2287#endif /* VTYSH */
2288
2289 confp = fopen (config_default_dir, "r");
2290 if (confp == NULL)
2291 {
2292 confp = vty_use_backup_config (config_default_dir);
2293 if (confp)
2294 {
2295 fprintf (stderr, "WARNING: using backup configuration file!\n");
2296 fullpath = config_default_dir;
2297 }
2298 else
2299 {
2300 fprintf (stderr, "can't open configuration file [%s]\n",
2301 config_default_dir);
2302 exit (1);
2303 }
2304 }
2305 else
2306 fullpath = config_default_dir;
2307 }
2308 else
2309 {
2310 /* Rleative path configuration file. */
paulccc92352003-10-22 02:49:38 +00002311 getcwd (cwd, MAXPATHLEN);
paul718e3742002-12-13 20:15:29 +00002312 fullpath = XMALLOC (MTYPE_TMP,
2313 strlen (cwd) + strlen (config_current_dir) + 2);
2314 sprintf (fullpath, "%s/%s", cwd, config_current_dir);
2315 }
2316 }
2317 vty_read_file (confp);
2318
2319 fclose (confp);
2320
2321 host_config_set (fullpath);
2322}
2323
2324/* Small utility function which output log to the VTY. */
2325void
2326vty_log (const char *proto_str, const char *format, va_list va)
2327{
2328 int i;
2329 struct vty *vty;
2330
2331 for (i = 0; i < vector_max (vtyvec); i++)
2332 if ((vty = vector_slot (vtyvec, i)) != NULL)
2333 if (vty->monitor)
2334 vty_log_out (vty, proto_str, format, va);
2335}
2336
2337int
2338vty_config_lock (struct vty *vty)
2339{
2340 if (vty_config == 0)
2341 {
2342 vty->config = 1;
2343 vty_config = 1;
2344 }
2345 return vty->config;
2346}
2347
2348int
2349vty_config_unlock (struct vty *vty)
2350{
2351 if (vty_config == 1 && vty->config == 1)
2352 {
2353 vty->config = 0;
2354 vty_config = 0;
2355 }
2356 return vty->config;
2357}
2358
2359/* Master of the threads. */
paulb21b19c2003-06-15 01:28:29 +00002360static struct thread_master *master;
paul718e3742002-12-13 20:15:29 +00002361
2362static void
2363vty_event (enum event event, int sock, struct vty *vty)
2364{
2365 struct thread *vty_serv_thread;
2366
2367 switch (event)
2368 {
2369 case VTY_SERV:
2370 vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);
2371 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2372 break;
2373#ifdef VTYSH
2374 case VTYSH_SERV:
2375 thread_add_read (master, vtysh_accept, vty, sock);
2376 break;
2377 case VTYSH_READ:
2378 thread_add_read (master, vtysh_read, vty, sock);
2379 break;
2380#endif /* VTYSH */
2381 case VTY_READ:
2382 vty->t_read = thread_add_read (master, vty_read, vty, sock);
2383
2384 /* Time out treatment. */
2385 if (vty->v_timeout)
2386 {
2387 if (vty->t_timeout)
2388 thread_cancel (vty->t_timeout);
2389 vty->t_timeout =
2390 thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
2391 }
2392 break;
2393 case VTY_WRITE:
2394 if (! vty->t_write)
2395 vty->t_write = thread_add_write (master, vty_flush, vty, sock);
2396 break;
2397 case VTY_TIMEOUT_RESET:
2398 if (vty->t_timeout)
2399 {
2400 thread_cancel (vty->t_timeout);
2401 vty->t_timeout = NULL;
2402 }
2403 if (vty->v_timeout)
2404 {
2405 vty->t_timeout =
2406 thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
2407 }
2408 break;
2409 }
2410}
2411
2412DEFUN (config_who,
2413 config_who_cmd,
2414 "who",
2415 "Display who is on vty\n")
2416{
2417 int i;
2418 struct vty *v;
2419
2420 for (i = 0; i < vector_max (vtyvec); i++)
2421 if ((v = vector_slot (vtyvec, i)) != NULL)
2422 vty_out (vty, "%svty[%d] connected from %s.%s",
2423 v->config ? "*" : " ",
2424 i, v->address, VTY_NEWLINE);
2425 return CMD_SUCCESS;
2426}
2427
2428/* Move to vty configuration mode. */
2429DEFUN (line_vty,
2430 line_vty_cmd,
2431 "line vty",
2432 "Configure a terminal line\n"
2433 "Virtual terminal\n")
2434{
2435 vty->node = VTY_NODE;
2436 return CMD_SUCCESS;
2437}
2438
2439/* Set time out value. */
2440int
2441exec_timeout (struct vty *vty, char *min_str, char *sec_str)
2442{
2443 unsigned long timeout = 0;
2444
2445 /* min_str and sec_str are already checked by parser. So it must be
2446 all digit string. */
2447 if (min_str)
2448 {
2449 timeout = strtol (min_str, NULL, 10);
2450 timeout *= 60;
2451 }
2452 if (sec_str)
2453 timeout += strtol (sec_str, NULL, 10);
2454
2455 vty_timeout_val = timeout;
2456 vty->v_timeout = timeout;
2457 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2458
2459
2460 return CMD_SUCCESS;
2461}
2462
2463DEFUN (exec_timeout_min,
2464 exec_timeout_min_cmd,
2465 "exec-timeout <0-35791>",
2466 "Set timeout value\n"
2467 "Timeout value in minutes\n")
2468{
2469 return exec_timeout (vty, argv[0], NULL);
2470}
2471
2472DEFUN (exec_timeout_sec,
2473 exec_timeout_sec_cmd,
2474 "exec-timeout <0-35791> <0-2147483>",
2475 "Set the EXEC timeout\n"
2476 "Timeout in minutes\n"
2477 "Timeout in seconds\n")
2478{
2479 return exec_timeout (vty, argv[0], argv[1]);
2480}
2481
2482DEFUN (no_exec_timeout,
2483 no_exec_timeout_cmd,
2484 "no exec-timeout",
2485 NO_STR
2486 "Set the EXEC timeout\n")
2487{
2488 return exec_timeout (vty, NULL, NULL);
2489}
2490
2491/* Set vty access class. */
2492DEFUN (vty_access_class,
2493 vty_access_class_cmd,
2494 "access-class WORD",
2495 "Filter connections based on an IP access list\n"
2496 "IP access list\n")
2497{
2498 if (vty_accesslist_name)
2499 XFREE(MTYPE_VTY, vty_accesslist_name);
2500
2501 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2502
2503 return CMD_SUCCESS;
2504}
2505
2506/* Clear vty access class. */
2507DEFUN (no_vty_access_class,
2508 no_vty_access_class_cmd,
2509 "no access-class [WORD]",
2510 NO_STR
2511 "Filter connections based on an IP access list\n"
2512 "IP access list\n")
2513{
2514 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2515 {
2516 vty_out (vty, "Access-class is not currently applied to vty%s",
2517 VTY_NEWLINE);
2518 return CMD_WARNING;
2519 }
2520
2521 XFREE(MTYPE_VTY, vty_accesslist_name);
2522
2523 vty_accesslist_name = NULL;
2524
2525 return CMD_SUCCESS;
2526}
2527
2528#ifdef HAVE_IPV6
2529/* Set vty access class. */
2530DEFUN (vty_ipv6_access_class,
2531 vty_ipv6_access_class_cmd,
2532 "ipv6 access-class WORD",
2533 IPV6_STR
2534 "Filter connections based on an IP access list\n"
2535 "IPv6 access list\n")
2536{
2537 if (vty_ipv6_accesslist_name)
2538 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2539
2540 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2541
2542 return CMD_SUCCESS;
2543}
2544
2545/* Clear vty access class. */
2546DEFUN (no_vty_ipv6_access_class,
2547 no_vty_ipv6_access_class_cmd,
2548 "no ipv6 access-class [WORD]",
2549 NO_STR
2550 IPV6_STR
2551 "Filter connections based on an IP access list\n"
2552 "IPv6 access list\n")
2553{
2554 if (! vty_ipv6_accesslist_name ||
2555 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2556 {
2557 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2558 VTY_NEWLINE);
2559 return CMD_WARNING;
2560 }
2561
2562 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2563
2564 vty_ipv6_accesslist_name = NULL;
2565
2566 return CMD_SUCCESS;
2567}
2568#endif /* HAVE_IPV6 */
2569
2570/* vty login. */
2571DEFUN (vty_login,
2572 vty_login_cmd,
2573 "login",
2574 "Enable password checking\n")
2575{
2576 no_password_check = 0;
2577 return CMD_SUCCESS;
2578}
2579
2580DEFUN (no_vty_login,
2581 no_vty_login_cmd,
2582 "no login",
2583 NO_STR
2584 "Enable password checking\n")
2585{
2586 no_password_check = 1;
2587 return CMD_SUCCESS;
2588}
2589
2590DEFUN (service_advanced_vty,
2591 service_advanced_vty_cmd,
2592 "service advanced-vty",
2593 "Set up miscellaneous service\n"
2594 "Enable advanced mode vty interface\n")
2595{
2596 host.advanced = 1;
2597 return CMD_SUCCESS;
2598}
2599
2600DEFUN (no_service_advanced_vty,
2601 no_service_advanced_vty_cmd,
2602 "no service advanced-vty",
2603 NO_STR
2604 "Set up miscellaneous service\n"
2605 "Enable advanced mode vty interface\n")
2606{
2607 host.advanced = 0;
2608 return CMD_SUCCESS;
2609}
2610
2611DEFUN (terminal_monitor,
2612 terminal_monitor_cmd,
2613 "terminal monitor",
2614 "Set terminal line parameters\n"
2615 "Copy debug output to the current terminal line\n")
2616{
2617 vty->monitor = 1;
2618 return CMD_SUCCESS;
2619}
2620
2621DEFUN (terminal_no_monitor,
2622 terminal_no_monitor_cmd,
2623 "terminal no monitor",
2624 "Set terminal line parameters\n"
2625 NO_STR
2626 "Copy debug output to the current terminal line\n")
2627{
2628 vty->monitor = 0;
2629 return CMD_SUCCESS;
2630}
2631
2632DEFUN (show_history,
2633 show_history_cmd,
2634 "show history",
2635 SHOW_STR
2636 "Display the session command history\n")
2637{
2638 int index;
2639
2640 for (index = vty->hindex + 1; index != vty->hindex;)
2641 {
2642 if (index == VTY_MAXHIST)
2643 {
2644 index = 0;
2645 continue;
2646 }
2647
2648 if (vty->hist[index] != NULL)
2649 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
2650
2651 index++;
2652 }
2653
2654 return CMD_SUCCESS;
2655}
2656
2657/* Display current configuration. */
2658int
2659vty_config_write (struct vty *vty)
2660{
2661 vty_out (vty, "line vty%s", VTY_NEWLINE);
2662
2663 if (vty_accesslist_name)
2664 vty_out (vty, " access-class %s%s",
2665 vty_accesslist_name, VTY_NEWLINE);
2666
2667 if (vty_ipv6_accesslist_name)
2668 vty_out (vty, " ipv6 access-class %s%s",
2669 vty_ipv6_accesslist_name, VTY_NEWLINE);
2670
2671 /* exec-timeout */
2672 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
2673 vty_out (vty, " exec-timeout %ld %ld%s",
2674 vty_timeout_val / 60,
2675 vty_timeout_val % 60, VTY_NEWLINE);
2676
2677 /* login */
2678 if (no_password_check)
2679 vty_out (vty, " no login%s", VTY_NEWLINE);
2680
2681 vty_out (vty, "!%s", VTY_NEWLINE);
2682
2683 return CMD_SUCCESS;
2684}
2685
2686struct cmd_node vty_node =
2687{
2688 VTY_NODE,
2689 "%s(config-line)# ",
2690};
2691
2692/* Reset all VTY status. */
2693void
2694vty_reset ()
2695{
2696 int i;
2697 struct vty *vty;
2698 struct thread *vty_serv_thread;
2699
2700 for (i = 0; i < vector_max (vtyvec); i++)
2701 if ((vty = vector_slot (vtyvec, i)) != NULL)
2702 {
2703 buffer_reset (vty->obuf);
2704 vty->status = VTY_CLOSE;
2705 vty_close (vty);
2706 }
2707
2708 for (i = 0; i < vector_max (Vvty_serv_thread); i++)
2709 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
2710 {
2711 thread_cancel (vty_serv_thread);
2712 vector_slot (Vvty_serv_thread, i) = NULL;
2713 close (i);
2714 }
2715
2716 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
2717
2718 if (vty_accesslist_name)
2719 {
2720 XFREE(MTYPE_VTY, vty_accesslist_name);
2721 vty_accesslist_name = NULL;
2722 }
2723
2724 if (vty_ipv6_accesslist_name)
2725 {
2726 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2727 vty_ipv6_accesslist_name = NULL;
2728 }
2729}
2730
2731/* for ospf6d easy temprary reload function */
2732/* vty_reset + close accept socket */
2733void
2734vty_finish ()
2735{
2736 int i;
2737 struct vty *vty;
2738 struct thread *vty_serv_thread;
2739
2740 for (i = 0; i < vector_max (vtyvec); i++)
2741 if ((vty = vector_slot (vtyvec, i)) != NULL)
2742 {
2743 buffer_reset (vty->obuf);
2744 vty->status = VTY_CLOSE;
2745 vty_close (vty);
2746 }
2747
2748 for (i = 0; i < vector_max (Vvty_serv_thread); i++)
2749 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
2750 {
2751 thread_cancel (vty_serv_thread);
2752 vector_slot (Vvty_serv_thread, i) = NULL;
2753 close (i);
2754 }
2755
2756 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
2757
2758 if (vty_accesslist_name)
2759 {
2760 XFREE(MTYPE_VTY, vty_accesslist_name);
2761 vty_accesslist_name = NULL;
2762 }
2763
2764 if (vty_ipv6_accesslist_name)
2765 {
2766 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2767 vty_ipv6_accesslist_name = NULL;
2768 }
2769}
2770
2771void
2772vty_save_cwd ()
2773{
paul79ad2792003-10-15 22:09:28 +00002774 char cwd[MAXPATHLEN];
paulccc92352003-10-22 02:49:38 +00002775 char *c;
paul718e3742002-12-13 20:15:29 +00002776
paulccc92352003-10-22 02:49:38 +00002777 c = getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00002778
paulccc92352003-10-22 02:49:38 +00002779 if (!c)
paul79ad2792003-10-15 22:09:28 +00002780 {
2781 chdir (SYSCONFDIR);
paulccc92352003-10-22 02:49:38 +00002782 getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00002783 }
paul718e3742002-12-13 20:15:29 +00002784
2785 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
2786 strcpy (vty_cwd, cwd);
2787}
2788
2789char *
2790vty_get_cwd ()
2791{
2792 return vty_cwd;
2793}
2794
2795int
2796vty_shell (struct vty *vty)
2797{
2798 return vty->type == VTY_SHELL ? 1 : 0;
2799}
2800
2801int
2802vty_shell_serv (struct vty *vty)
2803{
2804 return vty->type == VTY_SHELL_SERV ? 1 : 0;
2805}
2806
2807void
2808vty_init_vtysh ()
2809{
2810 vtyvec = vector_init (VECTOR_MIN_SIZE);
2811}
2812
2813/* Install vty's own commands like `who' command. */
2814void
paulb21b19c2003-06-15 01:28:29 +00002815vty_init (struct thread_master *master_thread)
paul718e3742002-12-13 20:15:29 +00002816{
2817 /* For further configuration read, preserve current directory. */
2818 vty_save_cwd ();
2819
2820 vtyvec = vector_init (VECTOR_MIN_SIZE);
2821
paulb21b19c2003-06-15 01:28:29 +00002822 master = master_thread;
2823
paul718e3742002-12-13 20:15:29 +00002824 /* Initilize server thread vector. */
2825 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
2826
2827 /* Install bgp top node. */
2828 install_node (&vty_node, vty_config_write);
2829
2830 install_element (VIEW_NODE, &config_who_cmd);
2831 install_element (VIEW_NODE, &show_history_cmd);
2832 install_element (ENABLE_NODE, &config_who_cmd);
2833 install_element (CONFIG_NODE, &line_vty_cmd);
2834 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
2835 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
2836 install_element (CONFIG_NODE, &show_history_cmd);
2837 install_element (ENABLE_NODE, &terminal_monitor_cmd);
2838 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
2839 install_element (ENABLE_NODE, &show_history_cmd);
2840
2841 install_default (VTY_NODE);
2842 install_element (VTY_NODE, &exec_timeout_min_cmd);
2843 install_element (VTY_NODE, &exec_timeout_sec_cmd);
2844 install_element (VTY_NODE, &no_exec_timeout_cmd);
2845 install_element (VTY_NODE, &vty_access_class_cmd);
2846 install_element (VTY_NODE, &no_vty_access_class_cmd);
2847 install_element (VTY_NODE, &vty_login_cmd);
2848 install_element (VTY_NODE, &no_vty_login_cmd);
2849#ifdef HAVE_IPV6
2850 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
2851 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
2852#endif /* HAVE_IPV6 */
2853}