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