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