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