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