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