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