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