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