blob: a39f2680e5c29290b2fbbece01d0c3a4b4217ec0 [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>
Vadim Yanitskiy024e1952020-10-02 18:23:38 +070069#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020070
Ruben Undheim766f77c2018-11-18 13:02:47 +010071#ifndef MAXPATHLEN
72 #define MAXPATHLEN 4096
73#endif
74
75
Harald Welte7acb30c2011-08-17 17:13:48 +020076/* \addtogroup vty
77 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020078 * \file vty.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020079
Harald Welte3fb0b6f2010-05-19 19:02:52 +020080#define SYSCONFDIR "/usr/local/etc"
81
82/* our callback, located in telnet_interface.c */
83void vty_event(enum event event, int sock, struct vty *vty);
84
85extern struct host host;
86
87/* Vector which store each vty structure. */
88static vector vtyvec;
89
90vector Vvty_serv_thread;
91
92char *vty_cwd = NULL;
93
Neels Hofmeyr96172f02016-02-23 14:01:41 +010094/* IP address passed to the 'line vty'/'bind' command.
95 * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
96 * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
97static const char *vty_bind_addr = NULL;
98#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +000099/* Port the VTY should bind to. -1 means not configured */
100static int vty_bind_port = -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +0100101
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200102/* Configure lock. */
103static int vty_config;
104
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -0700105static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200106
107void *tall_vty_ctx;
108
109static void vty_clear_buf(struct vty *vty)
110{
111 memset(vty->buf, 0, vty->max);
112}
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200115struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200116{
117 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
118
119 if (!new)
120 goto out;
121
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200122 INIT_LLIST_HEAD(&new->parent_nodes);
123
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200124 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
125 if (!new->obuf)
126 goto out_new;
127 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
128 if (!new->buf)
129 goto out_obuf;
130
131 new->max = VTY_BUFSIZ;
Vadim Yanitskiy54df08e2019-11-21 21:44:30 +0700132 new->fd = -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200133
134 return new;
135
136out_obuf:
137 buffer_free(new->obuf);
138out_new:
139 talloc_free(new);
140 new = NULL;
141out:
142 return new;
143}
144
145/* Authentication of vty */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700146static void vty_auth(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200147{
148 char *passwd = NULL;
149 enum node_type next_node = 0;
150 int fail;
151 char *crypt(const char *, const char *);
152
153 switch (vty->node) {
154 case AUTH_NODE:
155#ifdef VTY_CRYPT_PW
156 if (host.encrypt)
157 passwd = host.password_encrypt;
158 else
159#endif
160 passwd = host.password;
161 if (host.advanced)
162 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
163 else
164 next_node = VIEW_NODE;
165 break;
166 case AUTH_ENABLE_NODE:
167#ifdef VTY_CRYPT_PW
168 if (host.encrypt)
169 passwd = host.enable_encrypt;
170 else
171#endif
172 passwd = host.enable;
173 next_node = ENABLE_NODE;
174 break;
175 }
176
177 if (passwd) {
178#ifdef VTY_CRYPT_PW
179 if (host.encrypt)
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700180 fail = strcmp(crypt(vty->buf, passwd), passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181 else
182#endif
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700183 fail = strcmp(vty->buf, passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200184 } else
185 fail = 1;
186
187 if (!fail) {
188 vty->fail = 0;
189 vty->node = next_node; /* Success ! */
190 } else {
191 vty->fail++;
192 if (vty->fail >= 3) {
193 if (vty->node == AUTH_NODE) {
194 vty_out(vty,
195 "%% Bad passwords, too many failures!%s",
196 VTY_NEWLINE);
197 vty->status = VTY_CLOSE;
198 } else {
199 /* AUTH_ENABLE_NODE */
200 vty->fail = 0;
201 vty_out(vty,
202 "%% Bad enable passwords, too many failures!%s",
203 VTY_NEWLINE);
204 vty->node = VIEW_NODE;
205 }
206 }
207 }
208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200211void vty_close(struct vty *vty)
212{
213 int i;
214
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200215 /* VTY_CLOSED is handled by the telnet_interface */
216 vty_event(VTY_CLOSED, vty->fd, vty);
217
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200218 if (vty->obuf) {
219 /* Flush buffer. */
220 buffer_flush_all(vty->obuf, vty->fd);
221
222 /* Free input buffer. */
223 buffer_free(vty->obuf);
224 vty->obuf = NULL;
225 }
226
227 /* Free command history. */
228 for (i = 0; i < VTY_MAXHIST; i++)
229 if (vty->hist[i])
230 talloc_free(vty->hist[i]);
231
232 /* Unset vector. */
233 vector_unset(vtyvec, vty->fd);
234
Vadim Yanitskiy7a35b782019-11-21 21:51:23 +0700235 /* Close socket (ignore standard I/O streams). */
236 if (vty->fd > 2) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200237 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200238 vty->fd = -1;
239 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200240
241 if (vty->buf) {
242 talloc_free(vty->buf);
243 vty->buf = NULL;
244 }
245
246 /* Check configure. */
247 vty_config_unlock(vty);
248
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200249 /* OK free vty. */
250 talloc_free(vty);
251}
252
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200253/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200254int vty_shell(struct vty *vty)
255{
256 return vty->type == VTY_SHELL ? 1 : 0;
257}
258
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100259int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200260{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 int len = 0;
262 int size = 1024;
263 char buf[1024];
264 char *p = NULL;
265
266 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100267 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200268 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100269 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200270 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100271 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 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
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100287 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288 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 Hofmeyrc1aa1782019-02-01 05:38:44 +0100313/*! VTY standard output function
314 * \param[in] vty VTY to which we should print
315 * \param[in] format variable-length format string
316 */
317int vty_out(struct vty *vty, const char *format, ...)
318{
319 va_list args;
320 int rc;
321 va_start(args, format);
322 rc = vty_out_va(vty, format, args);
323 va_end(args);
324 return rc;
325}
326
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200327/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200328int vty_out_newline(struct vty *vty)
329{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200330 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200331 buffer_put(vty->obuf, p, strlen(p));
332 return 0;
333}
334
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200335/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800336void *vty_current_index(struct vty *vty)
337{
338 return vty->index;
339}
Harald Welte7acb30c2011-08-17 17:13:48 +0200340
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200341/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800342int vty_current_node(struct vty *vty)
343{
344 return vty->node;
345}
346
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200347/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200348 * \param[in] vty VTY to which the config shall be locked
349 * \returns 1 on success, 0 on error
350 *
351 * This shall be used to make sure only one VTY at a given time has
352 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200353int vty_config_lock(struct vty *vty)
354{
355 if (vty_config == 0) {
356 vty->config = 1;
357 vty_config = 1;
358 }
359 return vty->config;
360}
361
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200362/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200363 * \param[in] vty VTY from which the configuration shall be unlocked
364 * \returns 0 in case of success
365 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200366int vty_config_unlock(struct vty *vty)
367{
368 if (vty_config == 1 && vty->config == 1) {
369 vty->config = 0;
370 vty_config = 0;
371 }
372 return vty->config;
373}
374
375/* Say hello to vty interface. */
376void vty_hello(struct vty *vty)
377{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200378 const char *app_name = "<unnamed>";
379
380 if (host.app_info->name)
381 app_name = host.app_info->name;
382
Harald Weltea2501a22018-03-23 19:33:27 +0100383 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800384 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200385
386 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200387 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200388
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200389 if (host.motdfile) {
390 FILE *f;
391 char buf[4096];
392
393 f = fopen(host.motdfile, "r");
394 if (f) {
395 while (fgets(buf, sizeof(buf), f)) {
396 char *s;
397 /* work backwards to ignore trailling isspace() */
398 for (s = buf + strlen(buf);
399 (s > buf) && isspace(*(s - 1)); s--) ;
400 *s = '\0';
401 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
402 }
403 fclose(f);
404 } else
405 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
406 } else if (host.motd)
407 vty_out(vty, "%s", host.motd);
408}
409
410/* Put out prompt and wait input from user. */
411static void vty_prompt(struct vty *vty)
412{
413 struct utsname names;
414 const char *hostname;
415
416 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100417 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 if (!hostname) {
419 uname(&names);
420 hostname = names.nodename;
421 }
422 vty_out(vty, cmd_prompt(vty->node), hostname);
423 }
424}
425
426/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700427static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200428{
429 int ret;
430 vector vline;
431
432 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700433 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200434
435 if (vline == NULL)
436 return CMD_SUCCESS;
437
438 ret = cmd_execute_command(vline, vty, NULL, 0);
439 if (ret != CMD_SUCCESS)
440 switch (ret) {
441 case CMD_WARNING:
442 if (vty->type == VTY_FILE)
443 vty_out(vty, "Warning...%s", VTY_NEWLINE);
444 break;
445 case CMD_ERR_AMBIGUOUS:
446 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
447 break;
448 case CMD_ERR_NO_MATCH:
449 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
450 break;
451 case CMD_ERR_INCOMPLETE:
452 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
453 break;
454 }
455 cmd_free_strvec(vline);
456
457 return ret;
458}
459
460static const char telnet_backward_char = 0x08;
461static const char telnet_space_char = ' ';
Oliver Smith053ad962021-07-22 20:24:00 +0200462static const char telnet_escape_char = 0x1B;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200463
464/* Basic function to write buffer to vty. */
465static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
466{
467 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
468 return;
469
470 /* Should we do buffering here ? And make vty_flush (vty) ? */
471 buffer_put(vty->obuf, buf, nbytes);
472}
473
474/* Ensure length of input buffer. Is buffer is short, double it. */
475static void vty_ensure(struct vty *vty, int length)
476{
477 if (vty->max <= length) {
478 vty->max *= 2;
479 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
480 // FIXME: check return
481 }
482}
483
484/* Basic function to insert character into vty. */
485static void vty_self_insert(struct vty *vty, char c)
486{
487 int i;
488 int length;
489
490 vty_ensure(vty, vty->length + 1);
491 length = vty->length - vty->cp;
492 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
493 vty->buf[vty->cp] = c;
494
495 vty_write(vty, &vty->buf[vty->cp], length + 1);
496 for (i = 0; i < length; i++)
497 vty_write(vty, &telnet_backward_char, 1);
498
499 vty->cp++;
500 vty->length++;
501}
502
503/* Self insert character 'c' in overwrite mode. */
504static void vty_self_insert_overwrite(struct vty *vty, char c)
505{
506 vty_ensure(vty, vty->length + 1);
507 vty->buf[vty->cp++] = c;
508
509 if (vty->cp > vty->length)
510 vty->length++;
511
512 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
513 return;
514
515 vty_write(vty, &c, 1);
516}
517
518/* Insert a word into vty interface with overwrite mode. */
519static void vty_insert_word_overwrite(struct vty *vty, char *str)
520{
521 int len = strlen(str);
522 vty_write(vty, str, len);
523 strcpy(&vty->buf[vty->cp], str);
524 vty->cp += len;
525 vty->length = vty->cp;
526}
527
528/* Forward character. */
529static void vty_forward_char(struct vty *vty)
530{
531 if (vty->cp < vty->length) {
532 vty_write(vty, &vty->buf[vty->cp], 1);
533 vty->cp++;
534 }
535}
536
537/* Backward character. */
538static void vty_backward_char(struct vty *vty)
539{
540 if (vty->cp > 0) {
541 vty->cp--;
542 vty_write(vty, &telnet_backward_char, 1);
543 }
544}
545
546/* Move to the beginning of the line. */
547static void vty_beginning_of_line(struct vty *vty)
548{
549 while (vty->cp)
550 vty_backward_char(vty);
551}
552
553/* Move to the end of the line. */
554static void vty_end_of_line(struct vty *vty)
555{
556 while (vty->cp < vty->length)
557 vty_forward_char(vty);
558}
559
560/* Add current command line to the history buffer. */
561static void vty_hist_add(struct vty *vty)
562{
563 int index;
564
565 if (vty->length == 0)
566 return;
567
568 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
569
570 /* Ignore the same string as previous one. */
571 if (vty->hist[index])
572 if (strcmp(vty->buf, vty->hist[index]) == 0) {
573 vty->hp = vty->hindex;
574 return;
575 }
576
577 /* Insert history entry. */
578 if (vty->hist[vty->hindex])
579 talloc_free(vty->hist[vty->hindex]);
580 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
581
582 /* History index rotation. */
583 vty->hindex++;
584 if (vty->hindex == VTY_MAXHIST)
585 vty->hindex = 0;
586
587 vty->hp = vty->hindex;
588}
589
590/* Get telnet window size. */
591static int
592vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
593{
594#ifdef TELNET_OPTION_DEBUG
595 int i;
596
597 for (i = 0; i < nbytes; i++)
598 {
599 switch (buf[i])
600 {
601 case IAC:
602 vty_out (vty, "IAC ");
603 break;
604 case WILL:
605 vty_out (vty, "WILL ");
606 break;
607 case WONT:
608 vty_out (vty, "WONT ");
609 break;
610 case DO:
611 vty_out (vty, "DO ");
612 break;
613 case DONT:
614 vty_out (vty, "DONT ");
615 break;
616 case SB:
617 vty_out (vty, "SB ");
618 break;
619 case SE:
620 vty_out (vty, "SE ");
621 break;
622 case TELOPT_ECHO:
623 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
624 break;
625 case TELOPT_SGA:
626 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
627 break;
628 case TELOPT_NAWS:
629 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
630 break;
631 default:
632 vty_out (vty, "%x ", buf[i]);
633 break;
634 }
635 }
636 vty_out (vty, "%s", VTY_NEWLINE);
637
638#endif /* TELNET_OPTION_DEBUG */
639
640 switch (buf[0])
641 {
642 case SB:
643 vty->sb_len = 0;
644 vty->iac_sb_in_progress = 1;
645 return 0;
646 break;
647 case SE:
648 {
649 if (!vty->iac_sb_in_progress)
650 return 0;
651
652 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
653 {
654 vty->iac_sb_in_progress = 0;
655 return 0;
656 }
657 switch (vty->sb_buf[0])
658 {
659 case TELOPT_NAWS:
660 if (vty->sb_len != TELNET_NAWS_SB_LEN)
661 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
662 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100663 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200664 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
665 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
666 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100667 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200668 else
669 {
670 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
671 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
672#ifdef TELNET_OPTION_DEBUG
673 vty_out(vty, "TELNET NAWS window size negotiation completed: "
674 "width %d, height %d%s",
675 vty->width, vty->height, VTY_NEWLINE);
676#endif
677 }
678 break;
679 }
680 vty->iac_sb_in_progress = 0;
681 return 0;
682 break;
683 }
684 default:
685 break;
686 }
687 return 1;
688}
689
690/* Execute current command line. */
691static int vty_execute(struct vty *vty)
692{
693 int ret;
694
695 ret = CMD_SUCCESS;
696
697 switch (vty->node) {
698 case AUTH_NODE:
699 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700700 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200701 break;
702 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700703 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200704 if (vty->type == VTY_TERM)
705 vty_hist_add(vty);
706 break;
707 }
708
709 /* Clear command line buffer. */
710 vty->cp = vty->length = 0;
711 vty_clear_buf(vty);
712
713 if (vty->status != VTY_CLOSE)
714 vty_prompt(vty);
715
716 return ret;
717}
718
719/* Send WILL TELOPT_ECHO to remote server. */
720static void
721vty_will_echo (struct vty *vty)
722{
723 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
724 vty_out (vty, "%s", cmd);
725}
726
727/* Make suppress Go-Ahead telnet option. */
728static void
729vty_will_suppress_go_ahead (struct vty *vty)
730{
731 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
732 vty_out (vty, "%s", cmd);
733}
734
735/* Make don't use linemode over telnet. */
736static void
737vty_dont_linemode (struct vty *vty)
738{
739 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
740 vty_out (vty, "%s", cmd);
741}
742
743/* Use window size. */
744static void
745vty_do_window_size (struct vty *vty)
746{
747 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
748 vty_out (vty, "%s", cmd);
749}
750
751static void vty_kill_line_from_beginning(struct vty *);
752static void vty_redraw_line(struct vty *);
753
754/* Print command line history. This function is called from
755 vty_next_line and vty_previous_line. */
756static void vty_history_print(struct vty *vty)
757{
758 int length;
759
760 vty_kill_line_from_beginning(vty);
761
762 /* Get previous line from history buffer */
763 length = strlen(vty->hist[vty->hp]);
764 memcpy(vty->buf, vty->hist[vty->hp], length);
765 vty->cp = vty->length = length;
766
767 /* Redraw current line */
768 vty_redraw_line(vty);
769}
770
771/* Show next command line history. */
772static void vty_next_line(struct vty *vty)
773{
774 int try_index;
775
776 if (vty->hp == vty->hindex)
777 return;
778
779 /* Try is there history exist or not. */
780 try_index = vty->hp;
781 if (try_index == (VTY_MAXHIST - 1))
782 try_index = 0;
783 else
784 try_index++;
785
786 /* If there is not history return. */
787 if (vty->hist[try_index] == NULL)
788 return;
789 else
790 vty->hp = try_index;
791
792 vty_history_print(vty);
793}
794
795/* Show previous command line history. */
796static void vty_previous_line(struct vty *vty)
797{
798 int try_index;
799
800 try_index = vty->hp;
801 if (try_index == 0)
802 try_index = VTY_MAXHIST - 1;
803 else
804 try_index--;
805
806 if (vty->hist[try_index] == NULL)
807 return;
808 else
809 vty->hp = try_index;
810
811 vty_history_print(vty);
812}
813
814/* This function redraw all of the command line character. */
815static void vty_redraw_line(struct vty *vty)
816{
817 vty_write(vty, vty->buf, vty->length);
818 vty->cp = vty->length;
819}
820
821/* Forward word. */
822static void vty_forward_word(struct vty *vty)
823{
824 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
825 vty_forward_char(vty);
826
827 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
828 vty_forward_char(vty);
829}
830
831/* Backward word without skipping training space. */
832static void vty_backward_pure_word(struct vty *vty)
833{
834 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
835 vty_backward_char(vty);
836}
837
838/* Backward word. */
839static void vty_backward_word(struct vty *vty)
840{
841 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
842 vty_backward_char(vty);
843
844 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
845 vty_backward_char(vty);
846}
847
848/* When '^D' is typed at the beginning of the line we move to the down
849 level. */
850static void vty_down_level(struct vty *vty)
851{
852 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100853 /* call the exit function of the specific node */
854 if (vty->node > CONFIG_NODE)
855 vty_go_parent(vty);
856 else
857 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200858 vty_prompt(vty);
859 vty->cp = 0;
860}
861
Oliver Smith053ad962021-07-22 20:24:00 +0200862/* When '^L' is typed, clear all lines above the current one. */
863static void vty_clear_screen(struct vty *vty)
864{
865 vty_out(vty, "%c%s%c%s",
866 telnet_escape_char,
867 "[2J", /* Erase Screen */
868 telnet_escape_char,
869 "[H" /* Cursor Home */
870 );
871 vty_prompt(vty);
872 vty_redraw_line(vty);
873}
874
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200875/* When '^Z' is received from vty, move down to the enable mode. */
876static void vty_end_config(struct vty *vty)
877{
878 vty_out(vty, "%s", VTY_NEWLINE);
879
880 /* FIXME: we need to call the exit function of the specific node
881 * in question, not this generic one that doesn't know all nodes */
882 switch (vty->node) {
883 case VIEW_NODE:
884 case ENABLE_NODE:
885 /* Nothing to do. */
886 break;
887 case CONFIG_NODE:
888 case VTY_NODE:
889 vty_config_unlock(vty);
890 vty->node = ENABLE_NODE;
891 break;
Harald Welte28222962011-02-18 20:37:04 +0100892 case CFG_LOG_NODE:
893 vty->node = CONFIG_NODE;
894 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200895 default:
896 /* Unknown node, we have to ignore it. */
897 break;
898 }
899
900 vty_prompt(vty);
901 vty->cp = 0;
902}
903
904/* Delete a charcter at the current point. */
905static void vty_delete_char(struct vty *vty)
906{
907 int i;
908 int size;
909
910 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
911 return;
912
913 if (vty->length == 0) {
914 vty_down_level(vty);
915 return;
916 }
917
918 if (vty->cp == vty->length)
919 return; /* completion need here? */
920
921 size = vty->length - vty->cp;
922
923 vty->length--;
924 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
925 vty->buf[vty->length] = '\0';
926
927 vty_write(vty, &vty->buf[vty->cp], size - 1);
928 vty_write(vty, &telnet_space_char, 1);
929
930 for (i = 0; i < size; i++)
931 vty_write(vty, &telnet_backward_char, 1);
932}
933
934/* Delete a character before the point. */
935static void vty_delete_backward_char(struct vty *vty)
936{
937 if (vty->cp == 0)
938 return;
939
940 vty_backward_char(vty);
941 vty_delete_char(vty);
942}
943
944/* Kill rest of line from current point. */
945static void vty_kill_line(struct vty *vty)
946{
947 int i;
948 int size;
949
950 size = vty->length - vty->cp;
951
952 if (size == 0)
953 return;
954
955 for (i = 0; i < size; i++)
956 vty_write(vty, &telnet_space_char, 1);
957 for (i = 0; i < size; i++)
958 vty_write(vty, &telnet_backward_char, 1);
959
960 memset(&vty->buf[vty->cp], 0, size);
961 vty->length = vty->cp;
962}
963
964/* Kill line from the beginning. */
965static void vty_kill_line_from_beginning(struct vty *vty)
966{
967 vty_beginning_of_line(vty);
968 vty_kill_line(vty);
969}
970
971/* Delete a word before the point. */
972static void vty_forward_kill_word(struct vty *vty)
973{
974 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
975 vty_delete_char(vty);
976 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
977 vty_delete_char(vty);
978}
979
980/* Delete a word before the point. */
981static void vty_backward_kill_word(struct vty *vty)
982{
983 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
984 vty_delete_backward_char(vty);
985 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
986 vty_delete_backward_char(vty);
987}
988
989/* Transpose chars before or at the point. */
990static void vty_transpose_chars(struct vty *vty)
991{
992 char c1, c2;
993
994 /* If length is short or point is near by the beginning of line then
995 return. */
996 if (vty->length < 2 || vty->cp < 1)
997 return;
998
999 /* In case of point is located at the end of the line. */
1000 if (vty->cp == vty->length) {
1001 c1 = vty->buf[vty->cp - 1];
1002 c2 = vty->buf[vty->cp - 2];
1003
1004 vty_backward_char(vty);
1005 vty_backward_char(vty);
1006 vty_self_insert_overwrite(vty, c1);
1007 vty_self_insert_overwrite(vty, c2);
1008 } else {
1009 c1 = vty->buf[vty->cp];
1010 c2 = vty->buf[vty->cp - 1];
1011
1012 vty_backward_char(vty);
1013 vty_self_insert_overwrite(vty, c1);
1014 vty_self_insert_overwrite(vty, c2);
1015 }
1016}
1017
1018/* Do completion at vty interface. */
1019static void vty_complete_command(struct vty *vty)
1020{
1021 int i;
1022 int ret;
1023 char **matched = NULL;
1024 vector vline;
1025
1026 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1027 return;
1028
1029 vline = cmd_make_strvec(vty->buf);
1030 if (vline == NULL)
1031 return;
1032
1033 /* In case of 'help \t'. */
1034 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001035 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001036
1037 matched = cmd_complete_command(vline, vty, &ret);
1038
1039 cmd_free_strvec(vline);
1040
1041 vty_out(vty, "%s", VTY_NEWLINE);
1042 switch (ret) {
1043 case CMD_ERR_AMBIGUOUS:
1044 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1045 vty_prompt(vty);
1046 vty_redraw_line(vty);
1047 break;
1048 case CMD_ERR_NO_MATCH:
1049 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1050 vty_prompt(vty);
1051 vty_redraw_line(vty);
1052 break;
1053 case CMD_COMPLETE_FULL_MATCH:
1054 vty_prompt(vty);
1055 vty_redraw_line(vty);
1056 vty_backward_pure_word(vty);
1057 vty_insert_word_overwrite(vty, matched[0]);
1058 vty_self_insert(vty, ' ');
1059 talloc_free(matched[0]);
1060 break;
1061 case CMD_COMPLETE_MATCH:
1062 vty_prompt(vty);
1063 vty_redraw_line(vty);
1064 vty_backward_pure_word(vty);
1065 vty_insert_word_overwrite(vty, matched[0]);
1066 talloc_free(matched[0]);
1067 break;
1068 case CMD_COMPLETE_LIST_MATCH:
1069 for (i = 0; matched[i] != NULL; i++) {
1070 if (i != 0 && ((i % 6) == 0))
1071 vty_out(vty, "%s", VTY_NEWLINE);
1072 vty_out(vty, "%-10s ", matched[i]);
1073 talloc_free(matched[i]);
1074 }
1075 vty_out(vty, "%s", VTY_NEWLINE);
1076
1077 vty_prompt(vty);
1078 vty_redraw_line(vty);
1079 break;
1080 case CMD_ERR_NOTHING_TODO:
1081 vty_prompt(vty);
1082 vty_redraw_line(vty);
1083 break;
1084 default:
1085 break;
1086 }
1087 if (matched)
1088 vector_only_index_free(matched);
1089}
1090
1091static void
1092vty_describe_fold(struct vty *vty, int cmd_width,
1093 unsigned int desc_width, struct desc *desc)
1094{
1095 char *buf;
1096 const char *cmd, *p;
1097 int pos;
1098
1099 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1100
1101 if (desc_width <= 0) {
1102 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1103 VTY_NEWLINE);
1104 return;
1105 }
1106
1107 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1108 if (!buf)
1109 return;
1110
1111 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1112 for (pos = desc_width; pos > 0; pos--)
1113 if (*(p + pos) == ' ')
1114 break;
1115
1116 if (pos == 0)
1117 break;
1118
1119 strncpy(buf, p, pos);
1120 buf[pos] = '\0';
1121 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1122
1123 cmd = "";
1124 }
1125
1126 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1127
1128 talloc_free(buf);
1129}
1130
1131/* Describe matched command function. */
1132static void vty_describe_command(struct vty *vty)
1133{
1134 int ret;
1135 vector vline;
1136 vector describe;
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001137 unsigned int i, cmd_width, desc_width;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001138 struct desc *desc, *desc_cr = NULL;
1139
1140 vline = cmd_make_strvec(vty->buf);
1141
1142 /* In case of '> ?'. */
1143 if (vline == NULL) {
1144 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001145 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001146 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001147 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001148
1149 describe = cmd_describe_command(vline, vty, &ret);
1150
1151 vty_out(vty, "%s", VTY_NEWLINE);
1152
1153 /* Ambiguous error. */
1154 switch (ret) {
1155 case CMD_ERR_AMBIGUOUS:
1156 cmd_free_strvec(vline);
1157 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1158 vty_prompt(vty);
1159 vty_redraw_line(vty);
1160 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001161 case CMD_ERR_NO_MATCH:
1162 cmd_free_strvec(vline);
1163 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1164 vty_prompt(vty);
1165 vty_redraw_line(vty);
1166 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167 }
1168
1169 /* Get width of command string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001170 cmd_width = 0;
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001171 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001172 if ((desc = vector_slot(describe, i)) != NULL) {
1173 unsigned int len;
1174
1175 if (desc->cmd[0] == '\0')
1176 continue;
1177
1178 len = strlen(desc->cmd);
1179 if (desc->cmd[0] == '.')
1180 len--;
1181
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001182 if (cmd_width < len)
1183 cmd_width = len;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001184 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001185 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001186
1187 /* Get width of description string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001188 desc_width = vty->width - (cmd_width + 6);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001189
1190 /* Print out description. */
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001191 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001192 if ((desc = vector_slot(describe, i)) != NULL) {
1193 if (desc->cmd[0] == '\0')
1194 continue;
1195
1196 if (strcmp(desc->cmd, "<cr>") == 0) {
1197 desc_cr = desc;
1198 continue;
1199 }
1200
1201 if (!desc->str)
1202 vty_out(vty, " %-s%s",
1203 desc->cmd[0] ==
1204 '.' ? desc->cmd + 1 : desc->cmd,
1205 VTY_NEWLINE);
1206 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001207 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001208 desc->cmd[0] ==
1209 '.' ? desc->cmd + 1 : desc->cmd,
1210 desc->str, VTY_NEWLINE);
1211 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001212 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001213
1214#if 0
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001215 vty_out(vty, " %-*s %s%s", cmd_width
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001216 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1217 desc->str ? desc->str : "", VTY_NEWLINE);
1218#endif /* 0 */
1219 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001220 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001221
1222 if ((desc = desc_cr)) {
1223 if (!desc->str)
1224 vty_out(vty, " %-s%s",
1225 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1226 VTY_NEWLINE);
1227 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001228 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001229 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1230 desc->str, VTY_NEWLINE);
1231 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001232 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233 }
1234
1235 cmd_free_strvec(vline);
1236 vector_free(describe);
1237
1238 vty_prompt(vty);
1239 vty_redraw_line(vty);
1240}
1241
1242/* ^C stop current input and do not add command line to the history. */
1243static void vty_stop_input(struct vty *vty)
1244{
1245 vty->cp = vty->length = 0;
1246 vty_clear_buf(vty);
1247 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001248 vty_prompt(vty);
1249
1250 /* Set history pointer to the latest one. */
1251 vty->hp = vty->hindex;
1252}
1253
1254#define CONTROL(X) ((X) - '@')
1255#define VTY_NORMAL 0
1256#define VTY_PRE_ESCAPE 1
1257#define VTY_ESCAPE 2
1258
1259/* Escape character command map. */
1260static void vty_escape_map(unsigned char c, struct vty *vty)
1261{
1262 switch (c) {
1263 case ('A'):
1264 vty_previous_line(vty);
1265 break;
1266 case ('B'):
1267 vty_next_line(vty);
1268 break;
1269 case ('C'):
1270 vty_forward_char(vty);
1271 break;
1272 case ('D'):
1273 vty_backward_char(vty);
1274 break;
1275 default:
1276 break;
1277 }
1278
1279 /* Go back to normal mode. */
1280 vty->escape = VTY_NORMAL;
1281}
1282
1283/* Quit print out to the buffer. */
1284static void vty_buffer_reset(struct vty *vty)
1285{
1286 buffer_reset(vty->obuf);
1287 vty_prompt(vty);
1288 vty_redraw_line(vty);
1289}
1290
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001291/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001292int vty_read(struct vty *vty)
1293{
1294 int i;
1295 int nbytes;
1296 unsigned char buf[VTY_READ_BUFSIZ];
1297
1298 int vty_sock = vty->fd;
1299
1300 /* Read raw data from socket */
1301 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1302 if (nbytes < 0) {
1303 if (ERRNO_IO_RETRY(errno)) {
1304 vty_event(VTY_READ, vty_sock, vty);
1305 return 0;
1306 }
1307 }
1308 buffer_reset(vty->obuf);
1309 vty->status = VTY_CLOSE;
1310 }
1311
1312 for (i = 0; i < nbytes; i++) {
1313 if (buf[i] == IAC) {
1314 if (!vty->iac) {
1315 vty->iac = 1;
1316 continue;
1317 } else {
1318 vty->iac = 0;
1319 }
1320 }
1321
1322 if (vty->iac_sb_in_progress && !vty->iac) {
1323 if (vty->sb_len < sizeof(vty->sb_buf))
1324 vty->sb_buf[vty->sb_len] = buf[i];
1325 vty->sb_len++;
1326 continue;
1327 }
1328
1329 if (vty->iac) {
1330 /* In case of telnet command */
1331 int ret = 0;
1332 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1333 vty->iac = 0;
1334 i += ret;
1335 continue;
1336 }
1337
1338 if (vty->status == VTY_MORE) {
1339 switch (buf[i]) {
1340 case CONTROL('C'):
1341 case 'q':
1342 case 'Q':
1343 vty_buffer_reset(vty);
1344 break;
1345#if 0 /* More line does not work for "show ip bgp". */
1346 case '\n':
1347 case '\r':
1348 vty->status = VTY_MORELINE;
1349 break;
1350#endif
1351 default:
1352 break;
1353 }
1354 continue;
1355 }
1356
1357 /* Escape character. */
1358 if (vty->escape == VTY_ESCAPE) {
1359 vty_escape_map(buf[i], vty);
1360 continue;
1361 }
1362
1363 /* Pre-escape status. */
1364 if (vty->escape == VTY_PRE_ESCAPE) {
1365 switch (buf[i]) {
1366 case '[':
1367 vty->escape = VTY_ESCAPE;
1368 break;
1369 case 'b':
1370 vty_backward_word(vty);
1371 vty->escape = VTY_NORMAL;
1372 break;
1373 case 'f':
1374 vty_forward_word(vty);
1375 vty->escape = VTY_NORMAL;
1376 break;
1377 case 'd':
1378 vty_forward_kill_word(vty);
1379 vty->escape = VTY_NORMAL;
1380 break;
1381 case CONTROL('H'):
1382 case 0x7f:
1383 vty_backward_kill_word(vty);
1384 vty->escape = VTY_NORMAL;
1385 break;
1386 default:
1387 vty->escape = VTY_NORMAL;
1388 break;
1389 }
1390 continue;
1391 }
1392
1393 switch (buf[i]) {
1394 case CONTROL('A'):
1395 vty_beginning_of_line(vty);
1396 break;
1397 case CONTROL('B'):
1398 vty_backward_char(vty);
1399 break;
1400 case CONTROL('C'):
1401 vty_stop_input(vty);
1402 break;
1403 case CONTROL('D'):
1404 vty_delete_char(vty);
1405 break;
1406 case CONTROL('E'):
1407 vty_end_of_line(vty);
1408 break;
1409 case CONTROL('F'):
1410 vty_forward_char(vty);
1411 break;
1412 case CONTROL('H'):
1413 case 0x7f:
1414 vty_delete_backward_char(vty);
1415 break;
1416 case CONTROL('K'):
1417 vty_kill_line(vty);
1418 break;
Oliver Smith053ad962021-07-22 20:24:00 +02001419 case CONTROL('L'):
1420 vty_clear_screen(vty);
1421 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001422 case CONTROL('N'):
1423 vty_next_line(vty);
1424 break;
1425 case CONTROL('P'):
1426 vty_previous_line(vty);
1427 break;
1428 case CONTROL('T'):
1429 vty_transpose_chars(vty);
1430 break;
1431 case CONTROL('U'):
1432 vty_kill_line_from_beginning(vty);
1433 break;
1434 case CONTROL('W'):
1435 vty_backward_kill_word(vty);
1436 break;
1437 case CONTROL('Z'):
1438 vty_end_config(vty);
1439 break;
1440 case '\n':
1441 case '\r':
1442 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001443 /* '\0'-terminate the command buffer */
1444 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001445 vty_execute(vty);
1446 break;
1447 case '\t':
1448 vty_complete_command(vty);
1449 break;
1450 case '?':
1451 if (vty->node == AUTH_NODE
1452 || vty->node == AUTH_ENABLE_NODE)
1453 vty_self_insert(vty, buf[i]);
1454 else
1455 vty_describe_command(vty);
1456 break;
1457 case '\033':
1458 if (i + 1 < nbytes && buf[i + 1] == '[') {
1459 vty->escape = VTY_ESCAPE;
1460 i++;
1461 } else
1462 vty->escape = VTY_PRE_ESCAPE;
1463 break;
1464 default:
1465 if (buf[i] > 31 && buf[i] < 127)
1466 vty_self_insert(vty, buf[i]);
1467 break;
1468 }
1469 }
1470
1471 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001472 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001473 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001474 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001475 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001476 vty_event(VTY_WRITE, vty_sock, vty);
1477 vty_event(VTY_READ, vty_sock, vty);
1478 }
1479 return 0;
1480}
1481
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001482/* Read up configuration from a file stream */
1483/*! Read up VTY configuration from a file stream
1484 * \param[in] confp file pointer of the stream for the configuration file
1485 * \param[in] priv private data to be passed to \ref vty_read_file
1486 * \returns Zero on success, non-zero on error
1487 */
1488int vty_read_config_filep(FILE *confp, void *priv)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001489{
1490 int ret;
1491 struct vty *vty;
1492
1493 vty = vty_new();
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001494 vty->type = VTY_FILE;
1495 vty->node = CONFIG_NODE;
1496 vty->priv = priv;
1497
Vadim Yanitskiyb639b4d2019-11-21 02:20:11 +07001498 /* By default, write to stderr. Otherwise, during parsing of the logging
1499 * configuration, all invocations to vty_out() would make the process
1500 * write() to its own stdin (fd=0)! */
1501 vty->fd = fileno(stderr);
1502
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001503 ret = config_from_file(vty, confp);
1504
1505 if (ret != CMD_SUCCESS) {
1506 switch (ret) {
1507 case CMD_ERR_AMBIGUOUS:
1508 fprintf(stderr, "Ambiguous command.\n");
1509 break;
1510 case CMD_ERR_NO_MATCH:
1511 fprintf(stderr, "There is no such command.\n");
1512 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001513 case CMD_ERR_INVALID_INDENT:
1514 fprintf(stderr,
1515 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1516 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1517 "allowed, but their sequence must not change within a child block.\n");
1518 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001519 }
Keith03516d62017-09-04 11:19:13 +02001520 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001521 "line:\n%s\n", vty->buf);
1522 vty_close(vty);
1523 return -EINVAL;
1524 }
1525
1526 vty_close(vty);
1527 return 0;
1528}
1529
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001530/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001531struct vty *
1532vty_create (int vty_sock, void *priv)
1533{
1534 struct vty *vty;
1535
Alexander Couzensab383e62018-07-17 19:02:34 +02001536 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001537
1538 tcgetattr(vty_sock, &t);
1539 cfmakeraw(&t);
1540 tcsetattr(vty_sock, TCSANOW, &t);
1541
1542 /* Allocate new vty structure and set up default values. */
1543 vty = vty_new ();
1544 vty->fd = vty_sock;
1545 vty->priv = priv;
1546 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001547 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001548 {
1549 if (host.advanced)
1550 vty->node = ENABLE_NODE;
1551 else
1552 vty->node = VIEW_NODE;
1553 }
1554 else
1555 vty->node = AUTH_NODE;
1556 vty->fail = 0;
1557 vty->cp = 0;
1558 vty_clear_buf (vty);
1559 vty->length = 0;
1560 memset (vty->hist, 0, sizeof (vty->hist));
1561 vty->hp = 0;
1562 vty->hindex = 0;
1563 vector_set_index (vtyvec, vty_sock, vty);
1564 vty->status = VTY_NORMAL;
1565 if (host.lines >= 0)
1566 vty->lines = host.lines;
1567 else
1568 vty->lines = -1;
1569
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001570 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001571 {
1572 /* Vty is not available if password isn't set. */
1573 if (host.password == NULL && host.password_encrypt == NULL)
1574 {
1575 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1576 vty->status = VTY_CLOSE;
1577 vty_close (vty);
1578 return NULL;
1579 }
1580 }
1581
1582 /* Say hello to the world. */
1583 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001584 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001585 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1586
1587 /* Setting up terminal. */
1588 vty_will_echo (vty);
1589 vty_will_suppress_go_ahead (vty);
1590
1591 vty_dont_linemode (vty);
1592 vty_do_window_size (vty);
1593 /* vty_dont_lflow_ahead (vty); */
1594
1595 vty_prompt (vty);
1596
1597 /* Add read/write thread. */
1598 vty_event (VTY_WRITE, vty_sock, vty);
1599 vty_event (VTY_READ, vty_sock, vty);
1600
1601 return vty;
1602}
1603
1604DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1605{
1606 unsigned int i;
1607 struct vty *v;
1608
1609 for (i = 0; i < vector_active(vtyvec); i++)
1610 if ((v = vector_slot(vtyvec, i)) != NULL)
1611 vty_out(vty, "%svty[%d] %s",
1612 v->config ? "*" : " ", i, VTY_NEWLINE);
1613 return CMD_SUCCESS;
1614}
1615
1616/* Move to vty configuration mode. */
1617DEFUN(line_vty,
1618 line_vty_cmd,
1619 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1620{
1621 vty->node = VTY_NODE;
1622 return CMD_SUCCESS;
1623}
1624
1625/* vty login. */
1626DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1627{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001628 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001629 return CMD_SUCCESS;
1630}
1631
1632DEFUN(no_vty_login,
1633 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1634{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001635 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001636 return CMD_SUCCESS;
1637}
1638
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001639/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001640DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001641 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001642 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1643 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001644{
1645 talloc_free((void*)vty_bind_addr);
1646 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001647 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001648 return CMD_SUCCESS;
1649}
1650
1651const char *vty_get_bind_addr(void)
1652{
1653 if (!vty_bind_addr)
1654 return VTY_BIND_ADDR_DEFAULT;
1655 return vty_bind_addr;
1656}
1657
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001658int vty_get_bind_port(int default_port)
1659{
1660 if (vty_bind_port >= 0)
1661 return vty_bind_port;
1662 return default_port;
1663}
1664
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001665DEFUN(service_advanced_vty,
1666 service_advanced_vty_cmd,
1667 "service advanced-vty",
1668 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1669{
1670 host.advanced = 1;
1671 return CMD_SUCCESS;
1672}
1673
1674DEFUN(no_service_advanced_vty,
1675 no_service_advanced_vty_cmd,
1676 "no service advanced-vty",
1677 NO_STR
1678 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1679{
1680 host.advanced = 0;
1681 return CMD_SUCCESS;
1682}
1683
1684DEFUN(terminal_monitor,
1685 terminal_monitor_cmd,
1686 "terminal monitor",
1687 "Set terminal line parameters\n"
1688 "Copy debug output to the current terminal line\n")
1689{
1690 vty->monitor = 1;
1691 return CMD_SUCCESS;
1692}
1693
1694DEFUN(terminal_no_monitor,
1695 terminal_no_monitor_cmd,
1696 "terminal no monitor",
1697 "Set terminal line parameters\n"
1698 NO_STR "Copy debug output to the current terminal line\n")
1699{
1700 vty->monitor = 0;
1701 return CMD_SUCCESS;
1702}
1703
1704DEFUN(show_history,
1705 show_history_cmd,
1706 "show history", SHOW_STR "Display the session command history\n")
1707{
1708 int index;
1709
1710 for (index = vty->hindex + 1; index != vty->hindex;) {
1711 if (index == VTY_MAXHIST) {
1712 index = 0;
1713 continue;
1714 }
1715
1716 if (vty->hist[index] != NULL)
1717 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1718
1719 index++;
1720 }
1721
1722 return CMD_SUCCESS;
1723}
1724
1725/* Display current configuration. */
1726static int vty_config_write(struct vty *vty)
1727{
1728 vty_out(vty, "line vty%s", VTY_NEWLINE);
1729
1730 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001731 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001732 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001733 else
1734 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001735
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001736 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001737 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1738 if (vty_bind_port >= 0) {
1739 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1740 vty_bind_port, VTY_NEWLINE);
1741 } else {
1742 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1743 }
1744 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001745
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001746 vty_out(vty, "!%s", VTY_NEWLINE);
1747
1748 return CMD_SUCCESS;
1749}
1750
1751struct cmd_node vty_node = {
1752 VTY_NODE,
1753 "%s(config-line)# ",
1754 1,
1755};
1756
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001757/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001758void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001759{
1760 unsigned int i;
1761 struct vty *vty;
1762 struct thread *vty_serv_thread;
1763
1764 for (i = 0; i < vector_active(vtyvec); i++)
1765 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1766 buffer_reset(vty->obuf);
1767 vty->status = VTY_CLOSE;
1768 vty_close(vty);
1769 }
1770
1771 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1772 if ((vty_serv_thread =
1773 vector_slot(Vvty_serv_thread, i)) != NULL) {
1774 //thread_cancel (vty_serv_thread);
1775 vector_slot(Vvty_serv_thread, i) = NULL;
1776 close(i);
1777 }
1778}
1779
1780static void vty_save_cwd(void)
1781{
1782 char cwd[MAXPATHLEN];
1783 char *c ;
1784
1785 c = getcwd(cwd, MAXPATHLEN);
1786
1787 if (!c) {
1788 if (chdir(SYSCONFDIR) != 0)
1789 perror("chdir failed");
1790 if (getcwd(cwd, MAXPATHLEN) == NULL)
1791 perror("getcwd failed");
1792 }
1793
1794 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1795 strcpy(vty_cwd, cwd);
1796}
1797
Harald Welte95b2b472011-07-16 11:58:09 +02001798char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001799{
1800 return vty_cwd;
1801}
1802
1803int vty_shell_serv(struct vty *vty)
1804{
1805 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1806}
1807
Harald Welte95b2b472011-07-16 11:58:09 +02001808void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001809{
1810 vtyvec = vector_init(VECTOR_MIN_SIZE);
1811}
1812
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001813/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001814 * \param[in] app_info application information
1815 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001816/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001817void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001818{
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001819 unsigned int i, j;
1820
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301821 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001822 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1823 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1824
1825 cmd_init(1);
1826
Harald Welte237f6242010-05-25 23:00:45 +02001827 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001828
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001829 /* Check for duplicate flags in application specific attributes (if any) */
1830 for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) {
1831 if (app_info->usr_attr_letters[i] == '\0')
1832 continue;
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001833
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07001834 /* Some flag characters are reserved for global attributes */
1835 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
1836 for (j = 0; j < ARRAY_SIZE(rafc); j++) {
1837 if (app_info->usr_attr_letters[i] != rafc[j])
1838 continue;
1839 fprintf(stderr, "Attribute flag character '%c' is reserved "
1840 "for globals! Please fix.\n", app_info->usr_attr_letters[i]);
1841 }
1842
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001843 /* Upper case flag letters are reserved for libraries */
1844 if (app_info->usr_attr_letters[i] >= 'A' &&
1845 app_info->usr_attr_letters[i] <= 'Z') {
1846 fprintf(stderr, "Attribute flag letter '%c' is reserved "
1847 "for libraries! Please fix.\n", app_info->usr_attr_letters[i]);
1848 }
1849
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001850 for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) {
1851 if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i])
1852 continue;
1853 fprintf(stderr, "Found duplicate flag letter '%c' in application "
1854 "specific attributes (index %u vs %u)! Please fix.\n",
1855 app_info->usr_attr_letters[i], i, j);
1856 }
1857 }
1858
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001859 /* For further configuration read, preserve current directory. */
1860 vty_save_cwd();
1861
1862 vtyvec = vector_init(VECTOR_MIN_SIZE);
1863
1864 /* Install bgp top node. */
1865 install_node(&vty_node, vty_config_write);
1866
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001867 install_lib_element_ve(&config_who_cmd);
1868 install_lib_element_ve(&show_history_cmd);
1869 install_lib_element(CONFIG_NODE, &line_vty_cmd);
1870 install_lib_element(CONFIG_NODE, &service_advanced_vty_cmd);
1871 install_lib_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1872 install_lib_element(CONFIG_NODE, &show_history_cmd);
1873 install_lib_element(ENABLE_NODE, &terminal_monitor_cmd);
1874 install_lib_element(ENABLE_NODE, &terminal_no_monitor_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001875
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001876 install_lib_element(VTY_NODE, &vty_login_cmd);
1877 install_lib_element(VTY_NODE, &no_vty_login_cmd);
1878 install_lib_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001879}
1880
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001881/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001882 * \param[in] file_name file name of the configuration file
1883 * \param[in] priv private data to be passed to \ref vty_read_file
1884 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001885int vty_read_config_file(const char *file_name, void *priv)
1886{
1887 FILE *cfile;
1888 int rc;
1889
1890 cfile = fopen(file_name, "r");
1891 if (!cfile)
1892 return -ENOENT;
1893
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001894 rc = vty_read_config_filep(cfile, priv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001895 fclose(cfile);
1896
1897 host_config_set(file_name);
1898
1899 return rc;
1900}
Harald Welte7acb30c2011-08-17 17:13:48 +02001901
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001902/*! @} */