11 #include <sys/utsname.h>
12 #include <sys/param.h>
14 #include <arpa/telnet.h>
16 #include <osmocom/vty/vty.h>
17 #include <osmocom/vty/command.h>
18 #include <osmocom/vty/buffer.h>
19 #include <osmocore/talloc.h>
21 #define SYSCONFDIR "/usr/local/etc"
23 /* our callback, located in telnet_interface.c */
24 void vty_event(enum event event, int sock, struct vty *vty);
26 extern struct host host;
28 /* Vector which store each vty structure. */
31 vector Vvty_serv_thread;
36 static int vty_config;
38 static int no_password_check = 1;
42 static void vty_clear_buf(struct vty *vty)
44 memset(vty->buf, 0, vty->max);
47 /* Allocate new vty struct. */
50 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
55 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
58 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
62 new->max = VTY_BUFSIZ;
67 buffer_free(new->obuf);
75 /* Authentication of vty */
76 static void vty_auth(struct vty *vty, char *buf)
79 enum node_type next_node = 0;
81 char *crypt(const char *, const char *);
87 passwd = host.password_encrypt;
90 passwd = host.password;
92 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
94 next_node = VIEW_NODE;
96 case AUTH_ENABLE_NODE:
99 passwd = host.enable_encrypt;
102 passwd = host.enable;
103 next_node = ENABLE_NODE;
110 fail = strcmp(crypt(buf, passwd), passwd);
113 fail = strcmp(buf, passwd);
119 vty->node = next_node; /* Success ! */
122 if (vty->fail >= 3) {
123 if (vty->node == AUTH_NODE) {
125 "%% Bad passwords, too many failures!%s",
127 vty->status = VTY_CLOSE;
129 /* AUTH_ENABLE_NODE */
132 "%% Bad enable passwords, too many failures!%s",
134 vty->node = VIEW_NODE;
140 /* Close vty interface. */
141 void vty_close(struct vty *vty)
147 buffer_flush_all(vty->obuf, vty->fd);
149 /* Free input buffer. */
150 buffer_free(vty->obuf);
154 /* Free command history. */
155 for (i = 0; i < VTY_MAXHIST; i++)
157 talloc_free(vty->hist[i]);
160 vector_unset(vtyvec, vty->fd);
167 talloc_free(vty->buf);
171 /* Check configure. */
172 vty_config_unlock(vty);
174 /* VTY_CLOSED is handled by the telnet_interface */
175 vty_event(VTY_CLOSED, vty->fd, vty);
181 int vty_shell(struct vty *vty)
183 return vty->type == VTY_SHELL ? 1 : 0;
187 /* VTY standard output function. */
188 int vty_out(struct vty *vty, const char *format, ...)
196 if (vty_shell(vty)) {
197 va_start(args, format);
198 vprintf(format, args);
201 /* Try to write to initial buffer. */
202 va_start(args, format);
203 len = vsnprintf(buf, sizeof buf, format, args);
206 /* Initial buffer is not enough. */
207 if (len < 0 || len >= size) {
214 p = talloc_realloc_size(vty, p, size);
218 va_start(args, format);
219 len = vsnprintf(p, size, format, args);
222 if (len > -1 && len < size)
227 /* When initial buffer is enough to store all output. */
231 /* Pointer p must point out buffer. */
232 buffer_put(vty->obuf, (u_char *) p, len);
234 /* If p is not different with buf, it is allocated buffer. */
239 vty_event(VTY_WRITE, vty->fd, vty);
244 int vty_out_newline(struct vty *vty)
246 char *p = vty_newline(vty);
247 buffer_put(vty->obuf, p, strlen(p));
251 void *vty_current_index(struct vty *vty)
255 int vty_current_node(struct vty *vty)
260 int vty_config_lock(struct vty *vty)
262 if (vty_config == 0) {
269 int vty_config_unlock(struct vty *vty)
271 if (vty_config == 1 && vty->config == 1) {
278 /* Say hello to vty interface. */
279 void vty_hello(struct vty *vty)
285 f = fopen(host.motdfile, "r");
287 while (fgets(buf, sizeof(buf), f)) {
289 /* work backwards to ignore trailling isspace() */
290 for (s = buf + strlen(buf);
291 (s > buf) && isspace(*(s - 1)); s--) ;
293 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
297 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
298 } else if (host.motd)
299 vty_out(vty, "%s", host.motd);
302 /* Put out prompt and wait input from user. */
303 static void vty_prompt(struct vty *vty)
305 struct utsname names;
306 const char *hostname;
308 if (vty->type == VTY_TERM) {
309 hostname = host.name;
312 hostname = names.nodename;
314 vty_out(vty, cmd_prompt(vty->node), hostname);
318 /* Command execution over the vty interface. */
319 static int vty_command(struct vty *vty, char *buf)
324 /* Split readline string up into the vector */
325 vline = cmd_make_strvec(buf);
330 ret = cmd_execute_command(vline, vty, NULL, 0);
331 if (ret != CMD_SUCCESS)
334 if (vty->type == VTY_FILE)
335 vty_out(vty, "Warning...%s", VTY_NEWLINE);
337 case CMD_ERR_AMBIGUOUS:
338 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
340 case CMD_ERR_NO_MATCH:
341 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
343 case CMD_ERR_INCOMPLETE:
344 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
347 cmd_free_strvec(vline);
352 static const char telnet_backward_char = 0x08;
353 static const char telnet_space_char = ' ';
355 /* Basic function to write buffer to vty. */
356 static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
358 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
361 /* Should we do buffering here ? And make vty_flush (vty) ? */
362 buffer_put(vty->obuf, buf, nbytes);
365 /* Ensure length of input buffer. Is buffer is short, double it. */
366 static void vty_ensure(struct vty *vty, int length)
368 if (vty->max <= length) {
370 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
371 // FIXME: check return
375 /* Basic function to insert character into vty. */
376 static void vty_self_insert(struct vty *vty, char c)
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;
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);
394 /* Self insert character 'c' in overwrite mode. */
395 static void vty_self_insert_overwrite(struct vty *vty, char c)
397 vty_ensure(vty, vty->length + 1);
398 vty->buf[vty->cp++] = c;
400 if (vty->cp > vty->length)
403 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
406 vty_write(vty, &c, 1);
409 /* Insert a word into vty interface with overwrite mode. */
410 static void vty_insert_word_overwrite(struct vty *vty, char *str)
412 int len = strlen(str);
413 vty_write(vty, str, len);
414 strcpy(&vty->buf[vty->cp], str);
416 vty->length = vty->cp;
419 /* Forward character. */
420 static void vty_forward_char(struct vty *vty)
422 if (vty->cp < vty->length) {
423 vty_write(vty, &vty->buf[vty->cp], 1);
428 /* Backward character. */
429 static void vty_backward_char(struct vty *vty)
433 vty_write(vty, &telnet_backward_char, 1);
437 /* Move to the beginning of the line. */
438 static void vty_beginning_of_line(struct vty *vty)
441 vty_backward_char(vty);
444 /* Move to the end of the line. */
445 static void vty_end_of_line(struct vty *vty)
447 while (vty->cp < vty->length)
448 vty_forward_char(vty);
451 /* Add current command line to the history buffer. */
452 static void vty_hist_add(struct vty *vty)
456 if (vty->length == 0)
459 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
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;
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);
473 /* History index rotation. */
475 if (vty->hindex == VTY_MAXHIST)
478 vty->hp = vty->hindex;
481 /* Get telnet window size. */
483 vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
485 #ifdef TELNET_OPTION_DEBUG
488 for (i = 0; i < nbytes; i++)
493 vty_out (vty, "IAC ");
496 vty_out (vty, "WILL ");
499 vty_out (vty, "WONT ");
502 vty_out (vty, "DO ");
505 vty_out (vty, "DONT ");
508 vty_out (vty, "SB ");
511 vty_out (vty, "SE ");
514 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
517 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
520 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
523 vty_out (vty, "%x ", buf[i]);
527 vty_out (vty, "%s", VTY_NEWLINE);
529 #endif /* TELNET_OPTION_DEBUG */
535 vty->iac_sb_in_progress = 1;
540 if (!vty->iac_sb_in_progress)
543 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
545 vty->iac_sb_in_progress = 0;
548 switch (vty->sb_buf[0])
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);
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);
571 vty->iac_sb_in_progress = 0;
581 /* Execute current command line. */
582 static int vty_execute(struct vty *vty)
590 case AUTH_ENABLE_NODE:
591 vty_auth(vty, vty->buf);
594 ret = vty_command(vty, vty->buf);
595 if (vty->type == VTY_TERM)
600 /* Clear command line buffer. */
601 vty->cp = vty->length = 0;
604 if (vty->status != VTY_CLOSE)
610 /* Send WILL TELOPT_ECHO to remote server. */
612 vty_will_echo (struct vty *vty)
614 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
615 vty_out (vty, "%s", cmd);
618 /* Make suppress Go-Ahead telnet option. */
620 vty_will_suppress_go_ahead (struct vty *vty)
622 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
623 vty_out (vty, "%s", cmd);
626 /* Make don't use linemode over telnet. */
628 vty_dont_linemode (struct vty *vty)
630 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
631 vty_out (vty, "%s", cmd);
634 /* Use window size. */
636 vty_do_window_size (struct vty *vty)
638 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
639 vty_out (vty, "%s", cmd);
642 static void vty_kill_line_from_beginning(struct vty *);
643 static void vty_redraw_line(struct vty *);
645 /* Print command line history. This function is called from
646 vty_next_line and vty_previous_line. */
647 static void vty_history_print(struct vty *vty)
651 vty_kill_line_from_beginning(vty);
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;
658 /* Redraw current line */
659 vty_redraw_line(vty);
662 /* Show next command line history. */
663 static void vty_next_line(struct vty *vty)
667 if (vty->hp == vty->hindex)
670 /* Try is there history exist or not. */
672 if (try_index == (VTY_MAXHIST - 1))
677 /* If there is not history return. */
678 if (vty->hist[try_index] == NULL)
683 vty_history_print(vty);
686 /* Show previous command line history. */
687 static void vty_previous_line(struct vty *vty)
693 try_index = VTY_MAXHIST - 1;
697 if (vty->hist[try_index] == NULL)
702 vty_history_print(vty);
705 /* This function redraw all of the command line character. */
706 static void vty_redraw_line(struct vty *vty)
708 vty_write(vty, vty->buf, vty->length);
709 vty->cp = vty->length;
713 static void vty_forward_word(struct vty *vty)
715 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
716 vty_forward_char(vty);
718 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
719 vty_forward_char(vty);
722 /* Backward word without skipping training space. */
723 static void vty_backward_pure_word(struct vty *vty)
725 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
726 vty_backward_char(vty);
730 static void vty_backward_word(struct vty *vty)
732 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
733 vty_backward_char(vty);
735 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
736 vty_backward_char(vty);
739 /* When '^D' is typed at the beginning of the line we move to the down
741 static void vty_down_level(struct vty *vty)
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);
751 /* When '^Z' is received from vty, move down to the enable mode. */
752 static void vty_end_config(struct vty *vty)
754 vty_out(vty, "%s", VTY_NEWLINE);
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 */
765 vty_config_unlock(vty);
766 vty->node = ENABLE_NODE;
769 /* Unknown node, we have to ignore it. */
777 /* Delete a charcter at the current point. */
778 static void vty_delete_char(struct vty *vty)
783 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
786 if (vty->length == 0) {
791 if (vty->cp == vty->length)
792 return; /* completion need here? */
794 size = vty->length - vty->cp;
797 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
798 vty->buf[vty->length] = '\0';
800 vty_write(vty, &vty->buf[vty->cp], size - 1);
801 vty_write(vty, &telnet_space_char, 1);
803 for (i = 0; i < size; i++)
804 vty_write(vty, &telnet_backward_char, 1);
807 /* Delete a character before the point. */
808 static void vty_delete_backward_char(struct vty *vty)
813 vty_backward_char(vty);
814 vty_delete_char(vty);
817 /* Kill rest of line from current point. */
818 static void vty_kill_line(struct vty *vty)
823 size = vty->length - vty->cp;
828 for (i = 0; i < size; i++)
829 vty_write(vty, &telnet_space_char, 1);
830 for (i = 0; i < size; i++)
831 vty_write(vty, &telnet_backward_char, 1);
833 memset(&vty->buf[vty->cp], 0, size);
834 vty->length = vty->cp;
837 /* Kill line from the beginning. */
838 static void vty_kill_line_from_beginning(struct vty *vty)
840 vty_beginning_of_line(vty);
844 /* Delete a word before the point. */
845 static void vty_forward_kill_word(struct vty *vty)
847 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
848 vty_delete_char(vty);
849 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
850 vty_delete_char(vty);
853 /* Delete a word before the point. */
854 static void vty_backward_kill_word(struct vty *vty)
856 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
857 vty_delete_backward_char(vty);
858 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
859 vty_delete_backward_char(vty);
862 /* Transpose chars before or at the point. */
863 static void vty_transpose_chars(struct vty *vty)
867 /* If length is short or point is near by the beginning of line then
869 if (vty->length < 2 || vty->cp < 1)
872 /* In case of point is located at the end of the line. */
873 if (vty->cp == vty->length) {
874 c1 = vty->buf[vty->cp - 1];
875 c2 = vty->buf[vty->cp - 2];
877 vty_backward_char(vty);
878 vty_backward_char(vty);
879 vty_self_insert_overwrite(vty, c1);
880 vty_self_insert_overwrite(vty, c2);
882 c1 = vty->buf[vty->cp];
883 c2 = vty->buf[vty->cp - 1];
885 vty_backward_char(vty);
886 vty_self_insert_overwrite(vty, c1);
887 vty_self_insert_overwrite(vty, c2);
891 /* Do completion at vty interface. */
892 static void vty_complete_command(struct vty *vty)
896 char **matched = NULL;
899 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
902 vline = cmd_make_strvec(vty->buf);
906 /* In case of 'help \t'. */
907 if (isspace((int)vty->buf[vty->length - 1]))
908 vector_set(vline, '\0');
910 matched = cmd_complete_command(vline, vty, &ret);
912 cmd_free_strvec(vline);
914 vty_out(vty, "%s", VTY_NEWLINE);
916 case CMD_ERR_AMBIGUOUS:
917 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
919 vty_redraw_line(vty);
921 case CMD_ERR_NO_MATCH:
922 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
924 vty_redraw_line(vty);
926 case CMD_COMPLETE_FULL_MATCH:
928 vty_redraw_line(vty);
929 vty_backward_pure_word(vty);
930 vty_insert_word_overwrite(vty, matched[0]);
931 vty_self_insert(vty, ' ');
932 talloc_free(matched[0]);
934 case CMD_COMPLETE_MATCH:
936 vty_redraw_line(vty);
937 vty_backward_pure_word(vty);
938 vty_insert_word_overwrite(vty, matched[0]);
939 talloc_free(matched[0]);
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 talloc_free(matched[i]);
948 vty_out(vty, "%s", VTY_NEWLINE);
951 vty_redraw_line(vty);
953 case CMD_ERR_NOTHING_TODO:
955 vty_redraw_line(vty);
961 vector_only_index_free(matched);
965 vty_describe_fold(struct vty *vty, int cmd_width,
966 unsigned int desc_width, struct desc *desc)
972 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
974 if (desc_width <= 0) {
975 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
980 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
984 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
985 for (pos = desc_width; pos > 0; pos--)
986 if (*(p + pos) == ' ')
992 strncpy(buf, p, pos);
994 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
999 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1004 /* Describe matched command function. */
1005 static void vty_describe_command(struct vty *vty)
1010 unsigned int i, width, desc_width;
1011 struct desc *desc, *desc_cr = NULL;
1013 vline = cmd_make_strvec(vty->buf);
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');
1022 describe = cmd_describe_command(vline, vty, &ret);
1024 vty_out(vty, "%s", VTY_NEWLINE);
1026 /* Ambiguous error. */
1028 case CMD_ERR_AMBIGUOUS:
1029 cmd_free_strvec(vline);
1030 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1032 vty_redraw_line(vty);
1035 case CMD_ERR_NO_MATCH:
1036 cmd_free_strvec(vline);
1037 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1039 vty_redraw_line(vty);
1044 /* Get width of command string. */
1046 for (i = 0; i < vector_active(describe); i++)
1047 if ((desc = vector_slot(describe, i)) != NULL) {
1050 if (desc->cmd[0] == '\0')
1053 len = strlen(desc->cmd);
1054 if (desc->cmd[0] == '.')
1061 /* Get width of description string. */
1062 desc_width = vty->width - (width + 6);
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')
1070 if (strcmp(desc->cmd, "<cr>") == 0) {
1076 vty_out(vty, " %-s%s",
1078 '.' ? desc->cmd + 1 : desc->cmd,
1080 else if (desc_width >= strlen(desc->str))
1081 vty_out(vty, " %-*s %s%s", width,
1083 '.' ? desc->cmd + 1 : desc->cmd,
1084 desc->str, VTY_NEWLINE);
1086 vty_describe_fold(vty, width, desc_width, desc);
1089 vty_out(vty, " %-*s %s%s", width
1090 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1091 desc->str ? desc->str : "", VTY_NEWLINE);
1095 if ((desc = desc_cr)) {
1097 vty_out(vty, " %-s%s",
1098 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
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);
1105 vty_describe_fold(vty, width, desc_width, desc);
1108 cmd_free_strvec(vline);
1109 vector_free(describe);
1112 vty_redraw_line(vty);
1115 /* ^C stop current input and do not add command line to the history. */
1116 static void vty_stop_input(struct vty *vty)
1118 vty->cp = vty->length = 0;
1120 vty_out(vty, "%s", VTY_NEWLINE);
1122 switch (vty->node) {
1125 /* Nothing to do. */
1129 vty_config_unlock(vty);
1130 vty->node = ENABLE_NODE;
1133 /* Unknown node, we have to ignore it. */
1138 /* Set history pointer to the latest one. */
1139 vty->hp = vty->hindex;
1142 #define CONTROL(X) ((X) - '@')
1143 #define VTY_NORMAL 0
1144 #define VTY_PRE_ESCAPE 1
1145 #define VTY_ESCAPE 2
1147 /* Escape character command map. */
1148 static void vty_escape_map(unsigned char c, struct vty *vty)
1152 vty_previous_line(vty);
1158 vty_forward_char(vty);
1161 vty_backward_char(vty);
1167 /* Go back to normal mode. */
1168 vty->escape = VTY_NORMAL;
1171 /* Quit print out to the buffer. */
1172 static void vty_buffer_reset(struct vty *vty)
1174 buffer_reset(vty->obuf);
1176 vty_redraw_line(vty);
1179 /* Read data via vty socket. */
1180 int vty_read(struct vty *vty)
1184 unsigned char buf[VTY_READ_BUFSIZ];
1186 int vty_sock = vty->fd;
1188 /* Read raw data from socket */
1189 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1191 if (ERRNO_IO_RETRY(errno)) {
1192 vty_event(VTY_READ, vty_sock, vty);
1196 buffer_reset(vty->obuf);
1197 vty->status = VTY_CLOSE;
1200 for (i = 0; i < nbytes; i++) {
1201 if (buf[i] == IAC) {
1210 if (vty->iac_sb_in_progress && !vty->iac) {
1211 if (vty->sb_len < sizeof(vty->sb_buf))
1212 vty->sb_buf[vty->sb_len] = buf[i];
1218 /* In case of telnet command */
1220 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1226 if (vty->status == VTY_MORE) {
1231 vty_buffer_reset(vty);
1233 #if 0 /* More line does not work for "show ip bgp". */
1236 vty->status = VTY_MORELINE;
1245 /* Escape character. */
1246 if (vty->escape == VTY_ESCAPE) {
1247 vty_escape_map(buf[i], vty);
1251 /* Pre-escape status. */
1252 if (vty->escape == VTY_PRE_ESCAPE) {
1255 vty->escape = VTY_ESCAPE;
1258 vty_backward_word(vty);
1259 vty->escape = VTY_NORMAL;
1262 vty_forward_word(vty);
1263 vty->escape = VTY_NORMAL;
1266 vty_forward_kill_word(vty);
1267 vty->escape = VTY_NORMAL;
1271 vty_backward_kill_word(vty);
1272 vty->escape = VTY_NORMAL;
1275 vty->escape = VTY_NORMAL;
1283 vty_beginning_of_line(vty);
1286 vty_backward_char(vty);
1289 vty_stop_input(vty);
1292 vty_delete_char(vty);
1295 vty_end_of_line(vty);
1298 vty_forward_char(vty);
1302 vty_delete_backward_char(vty);
1311 vty_previous_line(vty);
1314 vty_transpose_chars(vty);
1317 vty_kill_line_from_beginning(vty);
1320 vty_backward_kill_word(vty);
1323 vty_end_config(vty);
1327 vty_out(vty, "%s", VTY_NEWLINE);
1331 vty_complete_command(vty);
1334 if (vty->node == AUTH_NODE
1335 || vty->node == AUTH_ENABLE_NODE)
1336 vty_self_insert(vty, buf[i]);
1338 vty_describe_command(vty);
1341 if (i + 1 < nbytes && buf[i + 1] == '[') {
1342 vty->escape = VTY_ESCAPE;
1345 vty->escape = VTY_PRE_ESCAPE;
1348 if (buf[i] > 31 && buf[i] < 127)
1349 vty_self_insert(vty, buf[i]);
1355 if (vty->status == VTY_CLOSE)
1358 vty_event(VTY_WRITE, vty_sock, vty);
1359 vty_event(VTY_READ, vty_sock, vty);
1364 /* Read up configuration file */
1366 vty_read_file(FILE *confp, void *priv)
1373 vty->type = VTY_FILE;
1374 vty->node = CONFIG_NODE;
1377 ret = config_from_file(vty, confp);
1379 if (ret != CMD_SUCCESS) {
1381 case CMD_ERR_AMBIGUOUS:
1382 fprintf(stderr, "Ambiguous command.\n");
1384 case CMD_ERR_NO_MATCH:
1385 fprintf(stderr, "There is no such command.\n");
1388 fprintf(stderr, "Error occurred during reading below "
1389 "line:\n%s\n", vty->buf);
1398 /* Create new vty structure. */
1400 vty_create (int vty_sock, void *priv)
1406 tcgetattr(vty_sock, &t);
1408 tcsetattr(vty_sock, TCSANOW, &t);
1410 /* Allocate new vty structure and set up default values. */
1414 vty->type = VTY_TERM;
1415 if (no_password_check)
1418 vty->node = ENABLE_NODE;
1420 vty->node = VIEW_NODE;
1423 vty->node = AUTH_NODE;
1426 vty_clear_buf (vty);
1428 memset (vty->hist, 0, sizeof (vty->hist));
1431 vector_set_index (vtyvec, vty_sock, vty);
1432 vty->status = VTY_NORMAL;
1433 if (host.lines >= 0)
1434 vty->lines = host.lines;
1438 if (! no_password_check)
1440 /* Vty is not available if password isn't set. */
1441 if (host.password == NULL && host.password_encrypt == NULL)
1443 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1444 vty->status = VTY_CLOSE;
1450 /* Say hello to the world. */
1452 if (! no_password_check)
1453 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1455 /* Setting up terminal. */
1456 vty_will_echo (vty);
1457 vty_will_suppress_go_ahead (vty);
1459 vty_dont_linemode (vty);
1460 vty_do_window_size (vty);
1461 /* vty_dont_lflow_ahead (vty); */
1465 /* Add read/write thread. */
1466 vty_event (VTY_WRITE, vty_sock, vty);
1467 vty_event (VTY_READ, vty_sock, vty);
1472 DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1477 for (i = 0; i < vector_active(vtyvec); i++)
1478 if ((v = vector_slot(vtyvec, i)) != NULL)
1479 vty_out(vty, "%svty[%d] %s",
1480 v->config ? "*" : " ", i, VTY_NEWLINE);
1484 /* Move to vty configuration mode. */
1487 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1489 vty->node = VTY_NODE;
1494 DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1496 no_password_check = 0;
1501 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1503 no_password_check = 1;
1507 DEFUN(service_advanced_vty,
1508 service_advanced_vty_cmd,
1509 "service advanced-vty",
1510 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1516 DEFUN(no_service_advanced_vty,
1517 no_service_advanced_vty_cmd,
1518 "no service advanced-vty",
1520 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1526 DEFUN(terminal_monitor,
1527 terminal_monitor_cmd,
1529 "Set terminal line parameters\n"
1530 "Copy debug output to the current terminal line\n")
1536 DEFUN(terminal_no_monitor,
1537 terminal_no_monitor_cmd,
1538 "terminal no monitor",
1539 "Set terminal line parameters\n"
1540 NO_STR "Copy debug output to the current terminal line\n")
1548 "show history", SHOW_STR "Display the session command history\n")
1552 for (index = vty->hindex + 1; index != vty->hindex;) {
1553 if (index == VTY_MAXHIST) {
1558 if (vty->hist[index] != NULL)
1559 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1567 /* Display current configuration. */
1568 static int vty_config_write(struct vty *vty)
1570 vty_out(vty, "line vty%s", VTY_NEWLINE);
1573 if (no_password_check)
1574 vty_out(vty, " no login%s", VTY_NEWLINE);
1576 vty_out(vty, "!%s", VTY_NEWLINE);
1581 struct cmd_node vty_node = {
1583 "%s(config-line)# ",
1587 /* Reset all VTY status. */
1592 struct thread *vty_serv_thread;
1594 for (i = 0; i < vector_active(vtyvec); i++)
1595 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1596 buffer_reset(vty->obuf);
1597 vty->status = VTY_CLOSE;
1601 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1602 if ((vty_serv_thread =
1603 vector_slot(Vvty_serv_thread, i)) != NULL) {
1604 //thread_cancel (vty_serv_thread);
1605 vector_slot(Vvty_serv_thread, i) = NULL;
1610 static void vty_save_cwd(void)
1612 char cwd[MAXPATHLEN];
1615 c = getcwd(cwd, MAXPATHLEN);
1618 if (chdir(SYSCONFDIR) != 0)
1619 perror("chdir failed");
1620 if (getcwd(cwd, MAXPATHLEN) == NULL)
1621 perror("getcwd failed");
1624 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1625 strcpy(vty_cwd, cwd);
1633 int vty_shell_serv(struct vty *vty)
1635 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1638 void vty_init_vtysh()
1640 vtyvec = vector_init(VECTOR_MIN_SIZE);
1643 extern void *tall_bsc_ctx;
1644 /* Install vty's own commands like `who' command. */
1645 void vty_init(struct vty_app_info *app_info)
1647 tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty");
1648 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1649 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1653 host.app_info = app_info;
1655 /* For further configuration read, preserve current directory. */
1658 vtyvec = vector_init(VECTOR_MIN_SIZE);
1660 /* Install bgp top node. */
1661 install_node(&vty_node, vty_config_write);
1663 install_element_ve(&config_who_cmd);
1664 install_element_ve(&show_history_cmd);
1665 install_element(CONFIG_NODE, &line_vty_cmd);
1666 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1667 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1668 install_element(CONFIG_NODE, &show_history_cmd);
1669 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1670 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1672 install_default(VTY_NODE);
1673 install_element(VTY_NODE, &vty_login_cmd);
1674 install_element(VTY_NODE, &no_vty_login_cmd);
1677 int vty_read_config_file(const char *file_name, void *priv)
1682 cfile = fopen(file_name, "r");
1686 rc = vty_read_file(cfile, priv);
1689 host_config_set(file_name);