blob: ca6fff73ce6d471cd4544fa3114ff11e26be479b [file] [log] [blame]
Harald Welte955049f2009-03-10 12:16:51 +00001
2#include <stdio.h>
3#include <stdarg.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7#include <errno.h>
8#include <ctype.h>
9#include <termios.h>
10
11#include <sys/utsname.h>
12#include <sys/param.h>
13
14#include <arpa/telnet.h>
15
16#include "cardshell.h"
17#include <vty/vty.h>
18#include <vty/command.h>
19#include <vty/buffer.h>
20
Harald Welte955049f2009-03-10 12:16:51 +000021extern struct host host;
22
23/* Vector which store each vty structure. */
24static vector vtyvec;
25
26vector Vvty_serv_thread;
27
28char *vty_cwd = NULL;
29
30/* Configure lock. */
31static int vty_config;
32
33static int no_password_check = 1;
34
35static void vty_clear_buf(struct vty *vty)
36{
37 memset(vty->buf, 0, vty->max);
38}
39
40/* Allocate new vty struct. */
41struct vty *vty_new()
42{
43 struct vty *new = malloc(sizeof(struct vty));
44
45 if (!new)
46 goto out;
47
48 new->obuf = buffer_new(0); /* Use default buffer size. */
49 if (!new->obuf)
50 goto out_new;
51 new->buf = calloc(1, VTY_BUFSIZ);
52 if (!new->buf)
53 goto out_obuf;
54
55 new->max = VTY_BUFSIZ;
56
57 return new;
58
59out_obuf:
60 free(new->obuf);
61out_new:
62 free(new);
63 new = NULL;
64out:
65 return new;
66}
67
68/* Authentication of vty */
69static void vty_auth(struct vty *vty, char *buf)
70{
71 char *passwd = NULL;
72 enum node_type next_node = 0;
73 int fail;
74 char *crypt(const char *, const char *);
75
76 switch (vty->node) {
77 case AUTH_NODE:
Harald Welted6cab812009-05-21 07:31:48 +000078#ifdef VTY_CRYPT_PW
Harald Welte955049f2009-03-10 12:16:51 +000079 if (host.encrypt)
80 passwd = host.password_encrypt;
81 else
Harald Welted6cab812009-05-21 07:31:48 +000082#endif
Harald Welte955049f2009-03-10 12:16:51 +000083 passwd = host.password;
84 if (host.advanced)
85 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
86 else
87 next_node = VIEW_NODE;
88 break;
89 case AUTH_ENABLE_NODE:
Harald Welted6cab812009-05-21 07:31:48 +000090#ifdef VTY_CRYPT_PW
Harald Welte955049f2009-03-10 12:16:51 +000091 if (host.encrypt)
92 passwd = host.enable_encrypt;
93 else
Harald Welted6cab812009-05-21 07:31:48 +000094#endif
Harald Welte955049f2009-03-10 12:16:51 +000095 passwd = host.enable;
96 next_node = ENABLE_NODE;
97 break;
98 }
99
100 if (passwd) {
Harald Welted6cab812009-05-21 07:31:48 +0000101#ifdef VTY_CRYPT_PW
Harald Welte955049f2009-03-10 12:16:51 +0000102 if (host.encrypt)
103 fail = strcmp(crypt(buf, passwd), passwd);
104 else
Harald Welted6cab812009-05-21 07:31:48 +0000105#endif
Harald Welte955049f2009-03-10 12:16:51 +0000106 fail = strcmp(buf, passwd);
107 } else
108 fail = 1;
109
110 if (!fail) {
111 vty->fail = 0;
112 vty->node = next_node; /* Success ! */
113 } else {
114 vty->fail++;
115 if (vty->fail >= 3) {
116 if (vty->node == AUTH_NODE) {
117 vty_out(vty,
118 "%% Bad passwords, too many failures!%s",
119 VTY_NEWLINE);
120 vty->status = VTY_CLOSE;
121 } else {
122 /* AUTH_ENABLE_NODE */
123 vty->fail = 0;
124 vty_out(vty,
125 "%% Bad enable passwords, too many failures!%s",
126 VTY_NEWLINE);
127 vty->node = VIEW_NODE;
128 }
129 }
130 }
131}
132
133/* Close vty interface. */
134void vty_close(struct vty *vty)
135{
136 int i;
137
138 /* Flush buffer. */
139 buffer_flush_all(vty->obuf, vty->fd);
140
141 /* Free input buffer. */
142 buffer_free(vty->obuf);
143
144 /* Free command history. */
145 for (i = 0; i < VTY_MAXHIST; i++)
146 if (vty->hist[i])
147 free(vty->hist[i]);
148
149 /* Unset vector. */
150 vector_unset(vtyvec, vty->fd);
151
152 /* Close socket. */
153 if (vty->fd > 0)
154 close(vty->fd);
155
156 if (vty->buf)
157 free(vty->buf);
158
159 /* Check configure. */
160 vty_config_unlock(vty);
161
162 /* OK free vty. */
163 free(vty);
164}
165
166int vty_shell(struct vty *vty)
167{
168 return vty->type == VTY_SHELL ? 1 : 0;
169}
170
171
172/* VTY standard output function. */
173int vty_out(struct vty *vty, const char *format, ...)
174{
175 va_list args;
176 int len = 0;
177 int size = 1024;
178 char buf[1024];
179 char *p = NULL;
180
181 if (vty_shell(vty)) {
182 va_start(args, format);
183 vprintf(format, args);
184 va_end(args);
185 } else {
186 /* Try to write to initial buffer. */
187 va_start(args, format);
188 len = vsnprintf(buf, sizeof buf, format, args);
189 va_end(args);
190
191 /* Initial buffer is not enough. */
192 if (len < 0 || len >= size) {
193 while (1) {
194 if (len > -1)
195 size = len + 1;
196 else
197 size = size * 2;
198
199 p = realloc(p, size);
200 if (!p)
201 return -1;
202
203 va_start(args, format);
204 len = vsnprintf(p, size, format, args);
205 va_end(args);
206
207 if (len > -1 && len < size)
208 break;
209 }
210 }
211
212 /* When initial buffer is enough to store all output. */
213 if (!p)
214 p = buf;
215
216 /* Pointer p must point out buffer. */
217 buffer_put(vty->obuf, (u_char *) p, len);
218
219 /* If p is not different with buf, it is allocated buffer. */
220 if (p != buf)
221 free(p);
222 }
223
224 return len;
225}
226
227int vty_out_newline(struct vty *vty)
228{
229 char *p = vty_newline(vty);
230 buffer_put(vty->obuf, p, strlen(p));
Harald Weltec63e51d2009-03-10 19:46:16 +0000231 return 0;
Harald Welte955049f2009-03-10 12:16:51 +0000232}
233
234int vty_config_lock(struct vty *vty)
235{
236 if (vty_config == 0) {
237 vty->config = 1;
238 vty_config = 1;
239 }
240 return vty->config;
241}
242
243int vty_config_unlock(struct vty *vty)
244{
245 if (vty_config == 1 && vty->config == 1) {
246 vty->config = 0;
247 vty_config = 0;
248 }
249 return vty->config;
250}
251
Harald Welte955049f2009-03-10 12:16:51 +0000252/* Say hello to vty interface. */
253void vty_hello(struct vty *vty)
254{
255 if (host.motdfile) {
256 FILE *f;
257 char buf[4096];
258
259 f = fopen(host.motdfile, "r");
260 if (f) {
261 while (fgets(buf, sizeof(buf), f)) {
262 char *s;
263 /* work backwards to ignore trailling isspace() */
264 for (s = buf + strlen(buf);
265 (s > buf) && isspace(*(s - 1)); s--) ;
266 *s = '\0';
267 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
268 }
269 fclose(f);
270 } else
271 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
272 } else if (host.motd)
273 vty_out(vty, host.motd);
274}
275
276/* Put out prompt and wait input from user. */
277static void vty_prompt(struct vty *vty)
278{
279 struct utsname names;
280 const char *hostname;
281
282 if (vty->type == VTY_TERM) {
283 hostname = host.name;
284 if (!hostname) {
285 uname(&names);
286 hostname = names.nodename;
287 }
288 vty_out(vty, cmd_prompt(vty->node), hostname);
289 }
290}
291
292/* Command execution over the vty interface. */
293static int vty_command(struct vty *vty, char *buf)
294{
295 int ret;
296 vector vline;
297
298 /* Split readline string up into the vector */
299 vline = cmd_make_strvec(buf);
300
301 if (vline == NULL)
302 return CMD_SUCCESS;
303
304 ret = cmd_execute_command(vline, vty, NULL, 0);
305 if (ret != CMD_SUCCESS)
306 switch (ret) {
307 case CMD_WARNING:
308 if (vty->type == VTY_FILE)
309 vty_out(vty, "Warning...%s", VTY_NEWLINE);
310 break;
311 case CMD_ERR_AMBIGUOUS:
312 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
313 break;
314 case CMD_ERR_NO_MATCH:
315 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
316 break;
317 case CMD_ERR_INCOMPLETE:
318 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
319 break;
320 }
321 cmd_free_strvec(vline);
322
323 return ret;
324}
325
326static const char telnet_backward_char = 0x08;
327static const char telnet_space_char = ' ';
328
329/* Basic function to write buffer to vty. */
330static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
331{
332 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
333 return;
334
335 /* Should we do buffering here ? And make vty_flush (vty) ? */
336 buffer_put(vty->obuf, buf, nbytes);
337}
338
339/* Ensure length of input buffer. Is buffer is short, double it. */
340static void vty_ensure(struct vty *vty, int length)
341{
342 if (vty->max <= length) {
343 vty->max *= 2;
344 vty->buf = realloc(vty->buf, vty->max);
345 // FIXME: check return
346 }
347}
348
349/* Basic function to insert character into vty. */
350static void vty_self_insert(struct vty *vty, char c)
351{
352 int i;
353 int length;
354
355 vty_ensure(vty, vty->length + 1);
356 length = vty->length - vty->cp;
357 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
358 vty->buf[vty->cp] = c;
359
360 vty_write(vty, &vty->buf[vty->cp], length + 1);
361 for (i = 0; i < length; i++)
362 vty_write(vty, &telnet_backward_char, 1);
363
364 vty->cp++;
365 vty->length++;
366}
367
368/* Self insert character 'c' in overwrite mode. */
369static void vty_self_insert_overwrite(struct vty *vty, char c)
370{
371 vty_ensure(vty, vty->length + 1);
372 vty->buf[vty->cp++] = c;
373
374 if (vty->cp > vty->length)
375 vty->length++;
376
377 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
378 return;
379
380 vty_write(vty, &c, 1);
381}
382
383/* Insert a word into vty interface with overwrite mode. */
384static void vty_insert_word_overwrite(struct vty *vty, char *str)
385{
386 int len = strlen(str);
387 vty_write(vty, str, len);
388 strcpy(&vty->buf[vty->cp], str);
389 vty->cp += len;
390 vty->length = vty->cp;
391}
392
393/* Forward character. */
394static void vty_forward_char(struct vty *vty)
395{
396 if (vty->cp < vty->length) {
397 vty_write(vty, &vty->buf[vty->cp], 1);
398 vty->cp++;
399 }
400}
401
402/* Backward character. */
403static void vty_backward_char(struct vty *vty)
404{
405 if (vty->cp > 0) {
406 vty->cp--;
407 vty_write(vty, &telnet_backward_char, 1);
408 }
409}
410
411/* Move to the beginning of the line. */
412static void vty_beginning_of_line(struct vty *vty)
413{
414 while (vty->cp)
415 vty_backward_char(vty);
416}
417
418/* Move to the end of the line. */
419static void vty_end_of_line(struct vty *vty)
420{
421 while (vty->cp < vty->length)
422 vty_forward_char(vty);
423}
424
425/* Add current command line to the history buffer. */
426static void vty_hist_add(struct vty *vty)
427{
428 int index;
429
430 if (vty->length == 0)
431 return;
432
433 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
434
435 /* Ignore the same string as previous one. */
436 if (vty->hist[index])
437 if (strcmp(vty->buf, vty->hist[index]) == 0) {
438 vty->hp = vty->hindex;
439 return;
440 }
441
442 /* Insert history entry. */
443 if (vty->hist[vty->hindex])
444 free(vty->hist[vty->hindex]);
445 vty->hist[vty->hindex] = strdup(vty->buf);
446
447 /* History index rotation. */
448 vty->hindex++;
449 if (vty->hindex == VTY_MAXHIST)
450 vty->hindex = 0;
451
452 vty->hp = vty->hindex;
453}
454
455/* Get telnet window size. */
456static int
457vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
458{
459#ifdef TELNET_OPTION_DEBUG
460 int i;
461
462 for (i = 0; i < nbytes; i++)
463 {
464 switch (buf[i])
465 {
466 case IAC:
467 vty_out (vty, "IAC ");
468 break;
469 case WILL:
470 vty_out (vty, "WILL ");
471 break;
472 case WONT:
473 vty_out (vty, "WONT ");
474 break;
475 case DO:
476 vty_out (vty, "DO ");
477 break;
478 case DONT:
479 vty_out (vty, "DONT ");
480 break;
481 case SB:
482 vty_out (vty, "SB ");
483 break;
484 case SE:
485 vty_out (vty, "SE ");
486 break;
487 case TELOPT_ECHO:
488 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
489 break;
490 case TELOPT_SGA:
491 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
492 break;
493 case TELOPT_NAWS:
494 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
495 break;
496 default:
497 vty_out (vty, "%x ", buf[i]);
498 break;
499 }
500 }
501 vty_out (vty, "%s", VTY_NEWLINE);
502
503#endif /* TELNET_OPTION_DEBUG */
504
505 switch (buf[0])
506 {
507 case SB:
508 vty->sb_len = 0;
509 vty->iac_sb_in_progress = 1;
510 return 0;
511 break;
512 case SE:
513 {
514 if (!vty->iac_sb_in_progress)
515 return 0;
516
517 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
518 {
519 vty->iac_sb_in_progress = 0;
520 return 0;
521 }
522 switch (vty->sb_buf[0])
523 {
524 case TELOPT_NAWS:
525 if (vty->sb_len != TELNET_NAWS_SB_LEN)
526 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
527 "should send %d characters, but we received %lu",
528 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
529 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
530 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
531 "too small to handle the telnet NAWS option",
532 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
533 else
534 {
535 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
536 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
537#ifdef TELNET_OPTION_DEBUG
538 vty_out(vty, "TELNET NAWS window size negotiation completed: "
539 "width %d, height %d%s",
540 vty->width, vty->height, VTY_NEWLINE);
541#endif
542 }
543 break;
544 }
545 vty->iac_sb_in_progress = 0;
546 return 0;
547 break;
548 }
549 default:
550 break;
551 }
552 return 1;
553}
554
555/* Execute current command line. */
556static int vty_execute(struct vty *vty)
557{
558 int ret;
559
560 ret = CMD_SUCCESS;
561
562 switch (vty->node) {
563 case AUTH_NODE:
564 case AUTH_ENABLE_NODE:
565 vty_auth(vty, vty->buf);
566 break;
567 default:
568 ret = vty_command(vty, vty->buf);
569 if (vty->type == VTY_TERM)
570 vty_hist_add(vty);
571 break;
572 }
573
574 /* Clear command line buffer. */
575 vty->cp = vty->length = 0;
576 vty_clear_buf(vty);
577
578 if (vty->status != VTY_CLOSE)
579 vty_prompt(vty);
580
581 return ret;
582}
583
584/* Send WILL TELOPT_ECHO to remote server. */
585static void
586vty_will_echo (struct vty *vty)
587{
588 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
589 vty_out (vty, "%s", cmd);
590}
591
592/* Make suppress Go-Ahead telnet option. */
593static void
594vty_will_suppress_go_ahead (struct vty *vty)
595{
596 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
597 vty_out (vty, "%s", cmd);
598}
599
600/* Make don't use linemode over telnet. */
601static void
602vty_dont_linemode (struct vty *vty)
603{
604 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
605 vty_out (vty, "%s", cmd);
606}
607
608/* Use window size. */
609static void
610vty_do_window_size (struct vty *vty)
611{
612 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
613 vty_out (vty, "%s", cmd);
614}
615
616static void vty_kill_line_from_beginning(struct vty *);
617static void vty_redraw_line(struct vty *);
618
619/* Print command line history. This function is called from
620 vty_next_line and vty_previous_line. */
621static void vty_history_print(struct vty *vty)
622{
623 int length;
624
625 vty_kill_line_from_beginning(vty);
626
627 /* Get previous line from history buffer */
628 length = strlen(vty->hist[vty->hp]);
629 memcpy(vty->buf, vty->hist[vty->hp], length);
630 vty->cp = vty->length = length;
631
632 /* Redraw current line */
633 vty_redraw_line(vty);
634}
635
636/* Show next command line history. */
637static void vty_next_line(struct vty *vty)
638{
639 int try_index;
640
641 if (vty->hp == vty->hindex)
642 return;
643
644 /* Try is there history exist or not. */
645 try_index = vty->hp;
646 if (try_index == (VTY_MAXHIST - 1))
647 try_index = 0;
648 else
649 try_index++;
650
651 /* If there is not history return. */
652 if (vty->hist[try_index] == NULL)
653 return;
654 else
655 vty->hp = try_index;
656
657 vty_history_print(vty);
658}
659
660/* Show previous command line history. */
661static void vty_previous_line(struct vty *vty)
662{
663 int try_index;
664
665 try_index = vty->hp;
666 if (try_index == 0)
667 try_index = VTY_MAXHIST - 1;
668 else
669 try_index--;
670
671 if (vty->hist[try_index] == NULL)
672 return;
673 else
674 vty->hp = try_index;
675
676 vty_history_print(vty);
677}
678
679/* This function redraw all of the command line character. */
680static void vty_redraw_line(struct vty *vty)
681{
682 vty_write(vty, vty->buf, vty->length);
683 vty->cp = vty->length;
684}
685
686/* Forward word. */
687static void vty_forward_word(struct vty *vty)
688{
689 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
690 vty_forward_char(vty);
691
692 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
693 vty_forward_char(vty);
694}
695
696/* Backward word without skipping training space. */
697static void vty_backward_pure_word(struct vty *vty)
698{
699 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
700 vty_backward_char(vty);
701}
702
703/* Backward word. */
704static void vty_backward_word(struct vty *vty)
705{
706 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
707 vty_backward_char(vty);
708
709 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
710 vty_backward_char(vty);
711}
712
713/* When '^D' is typed at the beginning of the line we move to the down
714 level. */
715static void vty_down_level(struct vty *vty)
716{
717 vty_out(vty, "%s", VTY_NEWLINE);
718 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
719 vty_prompt(vty);
720 vty->cp = 0;
721}
722
723/* When '^Z' is received from vty, move down to the enable mode. */
724static void vty_end_config(struct vty *vty)
725{
726 vty_out(vty, "%s", VTY_NEWLINE);
727
728 switch (vty->node) {
729 case VIEW_NODE:
730 case ENABLE_NODE:
731 /* Nothing to do. */
732 break;
733 case CONFIG_NODE:
734 case INTERFACE_NODE:
735 case ZEBRA_NODE:
736 case RIP_NODE:
737 case RIPNG_NODE:
738 case BGP_NODE:
739 case BGP_VPNV4_NODE:
740 case BGP_IPV4_NODE:
741 case BGP_IPV4M_NODE:
742 case BGP_IPV6_NODE:
743 case RMAP_NODE:
744 case OSPF_NODE:
745 case OSPF6_NODE:
746 case ISIS_NODE:
747 case KEYCHAIN_NODE:
748 case KEYCHAIN_KEY_NODE:
749 case MASC_NODE:
750 case VTY_NODE:
751 vty_config_unlock(vty);
752 vty->node = ENABLE_NODE;
753 break;
754 default:
755 /* Unknown node, we have to ignore it. */
756 break;
757 }
758
759 vty_prompt(vty);
760 vty->cp = 0;
761}
762
763/* Delete a charcter at the current point. */
764static void vty_delete_char(struct vty *vty)
765{
766 int i;
767 int size;
768
769 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
770 return;
771
772 if (vty->length == 0) {
773 vty_down_level(vty);
774 return;
775 }
776
777 if (vty->cp == vty->length)
778 return; /* completion need here? */
779
780 size = vty->length - vty->cp;
781
782 vty->length--;
783 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
784 vty->buf[vty->length] = '\0';
785
786 vty_write(vty, &vty->buf[vty->cp], size - 1);
787 vty_write(vty, &telnet_space_char, 1);
788
789 for (i = 0; i < size; i++)
790 vty_write(vty, &telnet_backward_char, 1);
791}
792
793/* Delete a character before the point. */
794static void vty_delete_backward_char(struct vty *vty)
795{
796 if (vty->cp == 0)
797 return;
798
799 vty_backward_char(vty);
800 vty_delete_char(vty);
801}
802
803/* Kill rest of line from current point. */
804static void vty_kill_line(struct vty *vty)
805{
806 int i;
807 int size;
808
809 size = vty->length - vty->cp;
810
811 if (size == 0)
812 return;
813
814 for (i = 0; i < size; i++)
815 vty_write(vty, &telnet_space_char, 1);
816 for (i = 0; i < size; i++)
817 vty_write(vty, &telnet_backward_char, 1);
818
819 memset(&vty->buf[vty->cp], 0, size);
820 vty->length = vty->cp;
821}
822
823/* Kill line from the beginning. */
824static void vty_kill_line_from_beginning(struct vty *vty)
825{
826 vty_beginning_of_line(vty);
827 vty_kill_line(vty);
828}
829
830/* Delete a word before the point. */
831static void vty_forward_kill_word(struct vty *vty)
832{
833 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
834 vty_delete_char(vty);
835 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
836 vty_delete_char(vty);
837}
838
839/* Delete a word before the point. */
840static void vty_backward_kill_word(struct vty *vty)
841{
842 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
843 vty_delete_backward_char(vty);
844 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
845 vty_delete_backward_char(vty);
846}
847
848/* Transpose chars before or at the point. */
849static void vty_transpose_chars(struct vty *vty)
850{
851 char c1, c2;
852
853 /* If length is short or point is near by the beginning of line then
854 return. */
855 if (vty->length < 2 || vty->cp < 1)
856 return;
857
858 /* In case of point is located at the end of the line. */
859 if (vty->cp == vty->length) {
860 c1 = vty->buf[vty->cp - 1];
861 c2 = vty->buf[vty->cp - 2];
862
863 vty_backward_char(vty);
864 vty_backward_char(vty);
865 vty_self_insert_overwrite(vty, c1);
866 vty_self_insert_overwrite(vty, c2);
867 } else {
868 c1 = vty->buf[vty->cp];
869 c2 = vty->buf[vty->cp - 1];
870
871 vty_backward_char(vty);
872 vty_self_insert_overwrite(vty, c1);
873 vty_self_insert_overwrite(vty, c2);
874 }
875}
876
877/* Do completion at vty interface. */
878static void vty_complete_command(struct vty *vty)
879{
880 int i;
881 int ret;
882 char **matched = NULL;
883 vector vline;
884
885 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
886 return;
887
888 vline = cmd_make_strvec(vty->buf);
889 if (vline == NULL)
890 return;
891
892 /* In case of 'help \t'. */
893 if (isspace((int)vty->buf[vty->length - 1]))
894 vector_set(vline, '\0');
895
896 matched = cmd_complete_command(vline, vty, &ret);
897
898 cmd_free_strvec(vline);
899
900 vty_out(vty, "%s", VTY_NEWLINE);
901 switch (ret) {
902 case CMD_ERR_AMBIGUOUS:
903 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
904 vty_prompt(vty);
905 vty_redraw_line(vty);
906 break;
907 case CMD_ERR_NO_MATCH:
908 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
909 vty_prompt(vty);
910 vty_redraw_line(vty);
911 break;
912 case CMD_COMPLETE_FULL_MATCH:
913 vty_prompt(vty);
914 vty_redraw_line(vty);
915 vty_backward_pure_word(vty);
916 vty_insert_word_overwrite(vty, matched[0]);
917 vty_self_insert(vty, ' ');
918 free(matched[0]);
919 break;
920 case CMD_COMPLETE_MATCH:
921 vty_prompt(vty);
922 vty_redraw_line(vty);
923 vty_backward_pure_word(vty);
924 vty_insert_word_overwrite(vty, matched[0]);
925 free(matched[0]);
926 vector_only_index_free(matched);
927 return;
928 break;
929 case CMD_COMPLETE_LIST_MATCH:
930 for (i = 0; matched[i] != NULL; i++) {
931 if (i != 0 && ((i % 6) == 0))
932 vty_out(vty, "%s", VTY_NEWLINE);
933 vty_out(vty, "%-10s ", matched[i]);
934 free(matched[i]);
935 }
936 vty_out(vty, "%s", VTY_NEWLINE);
937
938 vty_prompt(vty);
939 vty_redraw_line(vty);
940 break;
941 case CMD_ERR_NOTHING_TODO:
942 vty_prompt(vty);
943 vty_redraw_line(vty);
944 break;
945 default:
946 break;
947 }
948 if (matched)
949 vector_only_index_free(matched);
950}
951
952static void
953vty_describe_fold(struct vty *vty, int cmd_width,
954 unsigned int desc_width, struct desc *desc)
955{
956 char *buf;
957 const char *cmd, *p;
958 int pos;
959
960 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
961
962 if (desc_width <= 0) {
963 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
964 VTY_NEWLINE);
965 return;
966 }
967
968 buf = calloc(1, strlen(desc->str) + 1);
969 if (!buf)
970 return;
971
972 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
973 for (pos = desc_width; pos > 0; pos--)
974 if (*(p + pos) == ' ')
975 break;
976
977 if (pos == 0)
978 break;
979
980 strncpy(buf, p, pos);
981 buf[pos] = '\0';
982 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
983
984 cmd = "";
985 }
986
987 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
988
989 free(buf);
990}
991
992/* Describe matched command function. */
993static void vty_describe_command(struct vty *vty)
994{
995 int ret;
996 vector vline;
997 vector describe;
998 unsigned int i, width, desc_width;
999 struct desc *desc, *desc_cr = NULL;
1000
1001 vline = cmd_make_strvec(vty->buf);
1002
1003 /* In case of '> ?'. */
1004 if (vline == NULL) {
1005 vline = vector_init(1);
1006 vector_set(vline, '\0');
1007 } else if (isspace((int)vty->buf[vty->length - 1]))
1008 vector_set(vline, '\0');
1009
1010 describe = cmd_describe_command(vline, vty, &ret);
1011
1012 vty_out(vty, "%s", VTY_NEWLINE);
1013
1014 /* Ambiguous error. */
1015 switch (ret) {
1016 case CMD_ERR_AMBIGUOUS:
1017 cmd_free_strvec(vline);
1018 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1019 vty_prompt(vty);
1020 vty_redraw_line(vty);
1021 return;
1022 break;
1023 case CMD_ERR_NO_MATCH:
1024 cmd_free_strvec(vline);
1025 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1026 vty_prompt(vty);
1027 vty_redraw_line(vty);
1028 return;
1029 break;
1030 }
1031
1032 /* Get width of command string. */
1033 width = 0;
1034 for (i = 0; i < vector_active(describe); i++)
1035 if ((desc = vector_slot(describe, i)) != NULL) {
1036 unsigned int len;
1037
1038 if (desc->cmd[0] == '\0')
1039 continue;
1040
1041 len = strlen(desc->cmd);
1042 if (desc->cmd[0] == '.')
1043 len--;
1044
1045 if (width < len)
1046 width = len;
1047 }
1048
1049 /* Get width of description string. */
1050 desc_width = vty->width - (width + 6);
1051
1052 /* Print out description. */
1053 for (i = 0; i < vector_active(describe); i++)
1054 if ((desc = vector_slot(describe, i)) != NULL) {
1055 if (desc->cmd[0] == '\0')
1056 continue;
1057
1058 if (strcmp(desc->cmd, "<cr>") == 0) {
1059 desc_cr = desc;
1060 continue;
1061 }
1062
1063 if (!desc->str)
1064 vty_out(vty, " %-s%s",
1065 desc->cmd[0] ==
1066 '.' ? desc->cmd + 1 : desc->cmd,
1067 VTY_NEWLINE);
1068 else if (desc_width >= strlen(desc->str))
1069 vty_out(vty, " %-*s %s%s", width,
1070 desc->cmd[0] ==
1071 '.' ? desc->cmd + 1 : desc->cmd,
1072 desc->str, VTY_NEWLINE);
1073 else
1074 vty_describe_fold(vty, width, desc_width, desc);
1075
1076#if 0
1077 vty_out(vty, " %-*s %s%s", width
1078 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1079 desc->str ? desc->str : "", VTY_NEWLINE);
1080#endif /* 0 */
1081 }
1082
1083 if ((desc = desc_cr)) {
1084 if (!desc->str)
1085 vty_out(vty, " %-s%s",
1086 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1087 VTY_NEWLINE);
1088 else if (desc_width >= strlen(desc->str))
1089 vty_out(vty, " %-*s %s%s", width,
1090 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1091 desc->str, VTY_NEWLINE);
1092 else
1093 vty_describe_fold(vty, width, desc_width, desc);
1094 }
1095
1096 cmd_free_strvec(vline);
1097 vector_free(describe);
1098
1099 vty_prompt(vty);
1100 vty_redraw_line(vty);
1101}
1102
1103/* ^C stop current input and do not add command line to the history. */
1104static void vty_stop_input(struct vty *vty)
1105{
1106 vty->cp = vty->length = 0;
1107 vty_clear_buf(vty);
1108 vty_out(vty, "%s", VTY_NEWLINE);
1109
1110 switch (vty->node) {
1111 case VIEW_NODE:
1112 case ENABLE_NODE:
1113 /* Nothing to do. */
1114 break;
1115 case CONFIG_NODE:
1116 case INTERFACE_NODE:
1117 case ZEBRA_NODE:
1118 case RIP_NODE:
1119 case RIPNG_NODE:
1120 case BGP_NODE:
1121 case RMAP_NODE:
1122 case OSPF_NODE:
1123 case OSPF6_NODE:
1124 case ISIS_NODE:
1125 case KEYCHAIN_NODE:
1126 case KEYCHAIN_KEY_NODE:
1127 case MASC_NODE:
1128 case VTY_NODE:
1129 vty_config_unlock(vty);
1130 vty->node = ENABLE_NODE;
1131 break;
1132 default:
1133 /* Unknown node, we have to ignore it. */
1134 break;
1135 }
1136 vty_prompt(vty);
1137
1138 /* Set history pointer to the latest one. */
1139 vty->hp = vty->hindex;
1140}
1141
1142#define CONTROL(X) ((X) - '@')
1143#define VTY_NORMAL 0
1144#define VTY_PRE_ESCAPE 1
1145#define VTY_ESCAPE 2
1146
1147/* Escape character command map. */
1148static void vty_escape_map(unsigned char c, struct vty *vty)
1149{
1150 switch (c) {
1151 case ('A'):
1152 vty_previous_line(vty);
1153 break;
1154 case ('B'):
1155 vty_next_line(vty);
1156 break;
1157 case ('C'):
1158 vty_forward_char(vty);
1159 break;
1160 case ('D'):
1161 vty_backward_char(vty);
1162 break;
1163 default:
1164 break;
1165 }
1166
1167 /* Go back to normal mode. */
1168 vty->escape = VTY_NORMAL;
1169}
1170
1171/* Quit print out to the buffer. */
1172static void vty_buffer_reset(struct vty *vty)
1173{
1174 buffer_reset(vty->obuf);
1175 vty_prompt(vty);
1176 vty_redraw_line(vty);
1177}
1178
1179/* Read data via vty socket. */
1180int vty_read(struct vty *vty)
1181{
1182 int i;
1183 int nbytes;
1184 unsigned char buf[VTY_READ_BUFSIZ];
1185
1186 int vty_sock = vty->fd;
1187
1188 /* Read raw data from socket */
1189 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1190 if (nbytes < 0) {
1191 if (ERRNO_IO_RETRY(errno)) {
1192 vty_event(VTY_READ, vty_sock, vty);
1193 return 0;
1194 }
1195 }
1196 buffer_reset(vty->obuf);
1197 vty->status = VTY_CLOSE;
1198 }
1199
1200 for (i = 0; i < nbytes; i++) {
1201 if (buf[i] == IAC) {
1202 if (!vty->iac) {
1203 vty->iac = 1;
1204 continue;
1205 } else {
1206 vty->iac = 0;
1207 }
1208 }
1209
1210 if (vty->iac_sb_in_progress && !vty->iac) {
1211 if (vty->sb_len < sizeof(vty->sb_buf))
1212 vty->sb_buf[vty->sb_len] = buf[i];
1213 vty->sb_len++;
1214 continue;
1215 }
1216
1217 if (vty->iac) {
1218 /* In case of telnet command */
1219 int ret = 0;
1220 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1221 vty->iac = 0;
1222 i += ret;
1223 continue;
1224 }
1225
1226 if (vty->status == VTY_MORE) {
1227 switch (buf[i]) {
1228 case CONTROL('C'):
1229 case 'q':
1230 case 'Q':
1231 vty_buffer_reset(vty);
1232 break;
1233#if 0 /* More line does not work for "show ip bgp". */
1234 case '\n':
1235 case '\r':
1236 vty->status = VTY_MORELINE;
1237 break;
1238#endif
1239 default:
1240 break;
1241 }
1242 continue;
1243 }
1244
1245 /* Escape character. */
1246 if (vty->escape == VTY_ESCAPE) {
1247 vty_escape_map(buf[i], vty);
1248 continue;
1249 }
1250
1251 /* Pre-escape status. */
1252 if (vty->escape == VTY_PRE_ESCAPE) {
1253 switch (buf[i]) {
1254 case '[':
1255 vty->escape = VTY_ESCAPE;
1256 break;
1257 case 'b':
1258 vty_backward_word(vty);
1259 vty->escape = VTY_NORMAL;
1260 break;
1261 case 'f':
1262 vty_forward_word(vty);
1263 vty->escape = VTY_NORMAL;
1264 break;
1265 case 'd':
1266 vty_forward_kill_word(vty);
1267 vty->escape = VTY_NORMAL;
1268 break;
1269 case CONTROL('H'):
1270 case 0x7f:
1271 vty_backward_kill_word(vty);
1272 vty->escape = VTY_NORMAL;
1273 break;
1274 default:
1275 vty->escape = VTY_NORMAL;
1276 break;
1277 }
1278 continue;
1279 }
1280
1281 switch (buf[i]) {
1282 case CONTROL('A'):
1283 vty_beginning_of_line(vty);
1284 break;
1285 case CONTROL('B'):
1286 vty_backward_char(vty);
1287 break;
1288 case CONTROL('C'):
1289 vty_stop_input(vty);
1290 break;
1291 case CONTROL('D'):
1292 vty_delete_char(vty);
1293 break;
1294 case CONTROL('E'):
1295 vty_end_of_line(vty);
1296 break;
1297 case CONTROL('F'):
1298 vty_forward_char(vty);
1299 break;
1300 case CONTROL('H'):
1301 case 0x7f:
1302 vty_delete_backward_char(vty);
1303 break;
1304 case CONTROL('K'):
1305 vty_kill_line(vty);
1306 break;
1307 case CONTROL('N'):
1308 vty_next_line(vty);
1309 break;
1310 case CONTROL('P'):
1311 vty_previous_line(vty);
1312 break;
1313 case CONTROL('T'):
1314 vty_transpose_chars(vty);
1315 break;
1316 case CONTROL('U'):
1317 vty_kill_line_from_beginning(vty);
1318 break;
1319 case CONTROL('W'):
1320 vty_backward_kill_word(vty);
1321 break;
1322 case CONTROL('Z'):
1323 vty_end_config(vty);
1324 break;
1325 case '\n':
1326 case '\r':
1327 vty_out(vty, "%s", VTY_NEWLINE);
1328 vty_execute(vty);
1329 break;
1330 case '\t':
1331 vty_complete_command(vty);
1332 break;
1333 case '?':
1334 if (vty->node == AUTH_NODE
1335 || vty->node == AUTH_ENABLE_NODE)
1336 vty_self_insert(vty, buf[i]);
1337 else
1338 vty_describe_command(vty);
1339 break;
1340 case '\033':
1341 if (i + 1 < nbytes && buf[i + 1] == '[') {
1342 vty->escape = VTY_ESCAPE;
1343 i++;
1344 } else
1345 vty->escape = VTY_PRE_ESCAPE;
1346 break;
1347 default:
1348 if (buf[i] > 31 && buf[i] < 127)
1349 vty_self_insert(vty, buf[i]);
1350 break;
1351 }
1352 }
1353
1354 /* Check status. */
1355 if (vty->status == VTY_CLOSE)
1356 vty_close(vty);
1357 else {
1358 vty_event(VTY_WRITE, vty_sock, vty);
1359 vty_event(VTY_READ, vty_sock, vty);
1360 }
1361 return 0;
1362}
1363
1364/* Create new vty structure. */
1365struct vty *
Harald Weltec63e51d2009-03-10 19:46:16 +00001366vty_create (int vty_sock, void *priv)
Harald Welte955049f2009-03-10 12:16:51 +00001367{
1368 struct vty *vty;
1369
1370 struct termios t;
1371
1372 tcgetattr(vty_sock, &t);
1373 cfmakeraw(&t);
1374 tcsetattr(vty_sock, TCSANOW, &t);
1375
1376 /* Allocate new vty structure and set up default values. */
1377 vty = vty_new ();
1378 vty->fd = vty_sock;
Harald Weltec63e51d2009-03-10 19:46:16 +00001379 vty->priv = priv;
Harald Welte955049f2009-03-10 12:16:51 +00001380 vty->type = VTY_TERM;
1381 if (no_password_check)
1382 {
1383 if (host.advanced)
1384 vty->node = ENABLE_NODE;
1385 else
1386 vty->node = VIEW_NODE;
1387 }
1388 else
1389 vty->node = AUTH_NODE;
1390 vty->fail = 0;
1391 vty->cp = 0;
1392 vty_clear_buf (vty);
1393 vty->length = 0;
1394 memset (vty->hist, 0, sizeof (vty->hist));
1395 vty->hp = 0;
1396 vty->hindex = 0;
1397 vector_set_index (vtyvec, vty_sock, vty);
1398 vty->status = VTY_NORMAL;
1399 if (host.lines >= 0)
1400 vty->lines = host.lines;
1401 else
1402 vty->lines = -1;
1403
1404 if (! no_password_check)
1405 {
1406 /* Vty is not available if password isn't set. */
1407 if (host.password == NULL && host.password_encrypt == NULL)
1408 {
1409 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1410 vty->status = VTY_CLOSE;
1411 vty_close (vty);
1412 return NULL;
1413 }
1414 }
1415
1416 /* Say hello to the world. */
1417 vty_hello (vty);
1418 if (! no_password_check)
1419 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1420
1421 /* Setting up terminal. */
1422 vty_will_echo (vty);
1423 vty_will_suppress_go_ahead (vty);
1424
1425 vty_dont_linemode (vty);
1426 vty_do_window_size (vty);
1427 /* vty_dont_lflow_ahead (vty); */
1428
1429 vty_prompt (vty);
1430
1431 /* Add read/write thread. */
1432 vty_event (VTY_WRITE, vty_sock, vty);
1433 vty_event (VTY_READ, vty_sock, vty);
1434
1435 return vty;
1436}
1437
1438DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1439{
1440 unsigned int i;
1441 struct vty *v;
1442
1443 for (i = 0; i < vector_active(vtyvec); i++)
1444 if ((v = vector_slot(vtyvec, i)) != NULL)
1445 vty_out(vty, "%svty[%d] %s",
1446 v->config ? "*" : " ", i, VTY_NEWLINE);
1447 return CMD_SUCCESS;
1448}
1449
1450/* Move to vty configuration mode. */
1451DEFUN(line_vty,
1452 line_vty_cmd,
1453 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1454{
1455 vty->node = VTY_NODE;
1456 return CMD_SUCCESS;
1457}
1458
1459/* vty login. */
1460DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1461{
1462 no_password_check = 0;
1463 return CMD_SUCCESS;
1464}
1465
1466DEFUN(no_vty_login,
1467 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1468{
1469 no_password_check = 1;
1470 return CMD_SUCCESS;
1471}
1472
1473DEFUN(service_advanced_vty,
1474 service_advanced_vty_cmd,
1475 "service advanced-vty",
1476 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1477{
1478 host.advanced = 1;
1479 return CMD_SUCCESS;
1480}
1481
1482DEFUN(no_service_advanced_vty,
1483 no_service_advanced_vty_cmd,
1484 "no service advanced-vty",
1485 NO_STR
1486 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1487{
1488 host.advanced = 0;
1489 return CMD_SUCCESS;
1490}
1491
1492DEFUN(terminal_monitor,
1493 terminal_monitor_cmd,
1494 "terminal monitor",
1495 "Set terminal line parameters\n"
1496 "Copy debug output to the current terminal line\n")
1497{
1498 vty->monitor = 1;
1499 return CMD_SUCCESS;
1500}
1501
1502DEFUN(terminal_no_monitor,
1503 terminal_no_monitor_cmd,
1504 "terminal no monitor",
1505 "Set terminal line parameters\n"
1506 NO_STR "Copy debug output to the current terminal line\n")
1507{
1508 vty->monitor = 0;
1509 return CMD_SUCCESS;
1510}
1511
1512DEFUN(show_history,
1513 show_history_cmd,
1514 "show history", SHOW_STR "Display the session command history\n")
1515{
1516 int index;
1517
1518 for (index = vty->hindex + 1; index != vty->hindex;) {
1519 if (index == VTY_MAXHIST) {
1520 index = 0;
1521 continue;
1522 }
1523
1524 if (vty->hist[index] != NULL)
1525 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1526
1527 index++;
1528 }
1529
1530 return CMD_SUCCESS;
1531}
1532
1533/* Display current configuration. */
1534static int vty_config_write(struct vty *vty)
1535{
1536 vty_out(vty, "line vty%s", VTY_NEWLINE);
1537
1538 /* login */
1539 if (no_password_check)
1540 vty_out(vty, " no login%s", VTY_NEWLINE);
1541
1542 vty_out(vty, "!%s", VTY_NEWLINE);
1543
1544 return CMD_SUCCESS;
1545}
1546
1547struct cmd_node vty_node = {
1548 VTY_NODE,
1549 "%s(config-line)# ",
1550 1,
1551};
1552
1553/* Reset all VTY status. */
1554void vty_reset()
1555{
1556 unsigned int i;
1557 struct vty *vty;
1558 struct thread *vty_serv_thread;
1559
1560 for (i = 0; i < vector_active(vtyvec); i++)
1561 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1562 buffer_reset(vty->obuf);
1563 vty->status = VTY_CLOSE;
1564 vty_close(vty);
1565 }
1566
1567 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1568 if ((vty_serv_thread =
1569 vector_slot(Vvty_serv_thread, i)) != NULL) {
1570 //thread_cancel (vty_serv_thread);
1571 vector_slot(Vvty_serv_thread, i) = NULL;
1572 close(i);
1573 }
1574}
1575
1576static void vty_save_cwd(void)
1577{
1578 char cwd[MAXPATHLEN];
1579 char *c;
1580
1581 c = getcwd(cwd, MAXPATHLEN);
1582
1583 if (!c) {
1584 chdir(SYSCONFDIR);
1585 getcwd(cwd, MAXPATHLEN);
1586 }
1587
1588 vty_cwd = malloc(strlen(cwd) + 1);
1589 strcpy(vty_cwd, cwd);
1590}
1591
1592char *vty_get_cwd()
1593{
1594 return vty_cwd;
1595}
1596
1597int vty_shell_serv(struct vty *vty)
1598{
1599 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1600}
1601
1602void vty_init_vtysh()
1603{
1604 vtyvec = vector_init(VECTOR_MIN_SIZE);
1605}
1606
1607/* Install vty's own commands like `who' command. */
1608void vty_init()
1609{
1610 /* For further configuration read, preserve current directory. */
1611 vty_save_cwd();
1612
1613 vtyvec = vector_init(VECTOR_MIN_SIZE);
1614
1615 /* Install bgp top node. */
1616 install_node(&vty_node, vty_config_write);
1617
1618 install_element(VIEW_NODE, &config_who_cmd);
1619 install_element(VIEW_NODE, &show_history_cmd);
1620 install_element(ENABLE_NODE, &config_who_cmd);
1621 install_element(CONFIG_NODE, &line_vty_cmd);
1622 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1623 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1624 install_element(CONFIG_NODE, &show_history_cmd);
1625 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1626 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1627 install_element(ENABLE_NODE, &show_history_cmd);
1628
1629 install_default(VTY_NODE);
1630#if 0
1631 install_element(VTY_NODE, &vty_login_cmd);
1632 install_element(VTY_NODE, &no_vty_login_cmd);
1633#endif
1634}