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