blob: 1ad84f53a3c82efb8192509aa66981b84684b37a [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
Eric11a58a12021-09-09 15:42:32 +0200210void vty_flush(struct vty *vty)
211{
212 if (vty->obuf)
213 buffer_flush_all(vty->obuf, vty->fd);
214}
215
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200216/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200217void vty_close(struct vty *vty)
218{
219 int i;
220
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200221 /* VTY_CLOSED is handled by the telnet_interface */
222 vty_event(VTY_CLOSED, vty->fd, vty);
223
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200224 if (vty->obuf) {
225 /* Flush buffer. */
226 buffer_flush_all(vty->obuf, vty->fd);
227
228 /* Free input buffer. */
229 buffer_free(vty->obuf);
230 vty->obuf = NULL;
231 }
232
233 /* Free command history. */
234 for (i = 0; i < VTY_MAXHIST; i++)
235 if (vty->hist[i])
236 talloc_free(vty->hist[i]);
237
238 /* Unset vector. */
239 vector_unset(vtyvec, vty->fd);
240
Vadim Yanitskiy7a35b782019-11-21 21:51:23 +0700241 /* Close socket (ignore standard I/O streams). */
242 if (vty->fd > 2) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200243 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200244 vty->fd = -1;
245 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200246
247 if (vty->buf) {
248 talloc_free(vty->buf);
249 vty->buf = NULL;
250 }
251
252 /* Check configure. */
253 vty_config_unlock(vty);
254
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255 /* OK free vty. */
256 talloc_free(vty);
257}
258
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200259/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200260int vty_shell(struct vty *vty)
261{
262 return vty->type == VTY_SHELL ? 1 : 0;
263}
264
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100265int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200266{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200267 int len = 0;
268 int size = 1024;
269 char buf[1024];
270 char *p = NULL;
271
272 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100273 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200274 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100275 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200276 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100277 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200278 len = vsnprintf(buf, sizeof buf, format, args);
279 va_end(args);
280
281 /* Initial buffer is not enough. */
282 if (len < 0 || len >= size) {
283 while (1) {
284 if (len > -1)
285 size = len + 1;
286 else
287 size = size * 2;
288
289 p = talloc_realloc_size(vty, p, size);
290 if (!p)
291 return -1;
292
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100293 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200294 len = vsnprintf(p, size, format, args);
295 va_end(args);
296
297 if (len > -1 && len < size)
298 break;
299 }
300 }
301
302 /* When initial buffer is enough to store all output. */
303 if (!p)
304 p = buf;
305
306 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100307 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200308
309 /* If p is not different with buf, it is allocated buffer. */
310 if (p != buf)
311 talloc_free(p);
312 }
313
314 vty_event(VTY_WRITE, vty->fd, vty);
315
316 return len;
317}
318
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100319/*! VTY standard output function
320 * \param[in] vty VTY to which we should print
321 * \param[in] format variable-length format string
322 */
323int vty_out(struct vty *vty, const char *format, ...)
324{
325 va_list args;
326 int rc;
327 va_start(args, format);
328 rc = vty_out_va(vty, format, args);
329 va_end(args);
330 return rc;
331}
332
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200333/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200334int vty_out_newline(struct vty *vty)
335{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200336 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200337 buffer_put(vty->obuf, p, strlen(p));
338 return 0;
339}
340
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200341/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800342void *vty_current_index(struct vty *vty)
343{
344 return vty->index;
345}
Harald Welte7acb30c2011-08-17 17:13:48 +0200346
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200347/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800348int vty_current_node(struct vty *vty)
349{
350 return vty->node;
351}
352
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200353/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200354 * \param[in] vty VTY to which the config shall be locked
355 * \returns 1 on success, 0 on error
356 *
357 * This shall be used to make sure only one VTY at a given time has
358 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200359int vty_config_lock(struct vty *vty)
360{
361 if (vty_config == 0) {
362 vty->config = 1;
363 vty_config = 1;
364 }
365 return vty->config;
366}
367
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200368/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200369 * \param[in] vty VTY from which the configuration shall be unlocked
370 * \returns 0 in case of success
371 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200372int vty_config_unlock(struct vty *vty)
373{
374 if (vty_config == 1 && vty->config == 1) {
375 vty->config = 0;
376 vty_config = 0;
377 }
378 return vty->config;
379}
380
381/* Say hello to vty interface. */
382void vty_hello(struct vty *vty)
383{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200384 const char *app_name = "<unnamed>";
385
386 if (host.app_info->name)
387 app_name = host.app_info->name;
388
Harald Weltea2501a22018-03-23 19:33:27 +0100389 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800390 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200391
392 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200393 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200394
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200395 if (host.motdfile) {
396 FILE *f;
397 char buf[4096];
398
399 f = fopen(host.motdfile, "r");
400 if (f) {
401 while (fgets(buf, sizeof(buf), f)) {
402 char *s;
403 /* work backwards to ignore trailling isspace() */
404 for (s = buf + strlen(buf);
405 (s > buf) && isspace(*(s - 1)); s--) ;
406 *s = '\0';
407 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
408 }
409 fclose(f);
410 } else
411 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
412 } else if (host.motd)
413 vty_out(vty, "%s", host.motd);
414}
415
416/* Put out prompt and wait input from user. */
417static void vty_prompt(struct vty *vty)
418{
419 struct utsname names;
420 const char *hostname;
421
422 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100423 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200424 if (!hostname) {
425 uname(&names);
426 hostname = names.nodename;
427 }
428 vty_out(vty, cmd_prompt(vty->node), hostname);
429 }
430}
431
432/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700433static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200434{
435 int ret;
436 vector vline;
437
438 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700439 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200440
441 if (vline == NULL)
442 return CMD_SUCCESS;
443
444 ret = cmd_execute_command(vline, vty, NULL, 0);
445 if (ret != CMD_SUCCESS)
446 switch (ret) {
447 case CMD_WARNING:
448 if (vty->type == VTY_FILE)
449 vty_out(vty, "Warning...%s", VTY_NEWLINE);
450 break;
451 case CMD_ERR_AMBIGUOUS:
452 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
453 break;
454 case CMD_ERR_NO_MATCH:
455 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
456 break;
457 case CMD_ERR_INCOMPLETE:
458 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
459 break;
460 }
461 cmd_free_strvec(vline);
462
463 return ret;
464}
465
466static const char telnet_backward_char = 0x08;
467static const char telnet_space_char = ' ';
Oliver Smith053ad962021-07-22 20:24:00 +0200468static const char telnet_escape_char = 0x1B;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200469
470/* Basic function to write buffer to vty. */
471static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
472{
473 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
474 return;
475
476 /* Should we do buffering here ? And make vty_flush (vty) ? */
477 buffer_put(vty->obuf, buf, nbytes);
478}
479
480/* Ensure length of input buffer. Is buffer is short, double it. */
481static void vty_ensure(struct vty *vty, int length)
482{
483 if (vty->max <= length) {
484 vty->max *= 2;
485 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
486 // FIXME: check return
487 }
488}
489
490/* Basic function to insert character into vty. */
491static void vty_self_insert(struct vty *vty, char c)
492{
493 int i;
494 int length;
495
496 vty_ensure(vty, vty->length + 1);
497 length = vty->length - vty->cp;
498 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
499 vty->buf[vty->cp] = c;
500
501 vty_write(vty, &vty->buf[vty->cp], length + 1);
502 for (i = 0; i < length; i++)
503 vty_write(vty, &telnet_backward_char, 1);
504
505 vty->cp++;
506 vty->length++;
507}
508
509/* Self insert character 'c' in overwrite mode. */
510static void vty_self_insert_overwrite(struct vty *vty, char c)
511{
512 vty_ensure(vty, vty->length + 1);
513 vty->buf[vty->cp++] = c;
514
515 if (vty->cp > vty->length)
516 vty->length++;
517
518 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
519 return;
520
521 vty_write(vty, &c, 1);
522}
523
524/* Insert a word into vty interface with overwrite mode. */
525static void vty_insert_word_overwrite(struct vty *vty, char *str)
526{
527 int len = strlen(str);
528 vty_write(vty, str, len);
529 strcpy(&vty->buf[vty->cp], str);
530 vty->cp += len;
531 vty->length = vty->cp;
532}
533
534/* Forward character. */
535static void vty_forward_char(struct vty *vty)
536{
537 if (vty->cp < vty->length) {
538 vty_write(vty, &vty->buf[vty->cp], 1);
539 vty->cp++;
540 }
541}
542
543/* Backward character. */
544static void vty_backward_char(struct vty *vty)
545{
546 if (vty->cp > 0) {
547 vty->cp--;
548 vty_write(vty, &telnet_backward_char, 1);
549 }
550}
551
552/* Move to the beginning of the line. */
553static void vty_beginning_of_line(struct vty *vty)
554{
555 while (vty->cp)
556 vty_backward_char(vty);
557}
558
559/* Move to the end of the line. */
560static void vty_end_of_line(struct vty *vty)
561{
562 while (vty->cp < vty->length)
563 vty_forward_char(vty);
564}
565
566/* Add current command line to the history buffer. */
567static void vty_hist_add(struct vty *vty)
568{
569 int index;
570
571 if (vty->length == 0)
572 return;
573
574 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
575
576 /* Ignore the same string as previous one. */
577 if (vty->hist[index])
578 if (strcmp(vty->buf, vty->hist[index]) == 0) {
579 vty->hp = vty->hindex;
580 return;
581 }
582
583 /* Insert history entry. */
584 if (vty->hist[vty->hindex])
585 talloc_free(vty->hist[vty->hindex]);
586 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
587
588 /* History index rotation. */
589 vty->hindex++;
590 if (vty->hindex == VTY_MAXHIST)
591 vty->hindex = 0;
592
593 vty->hp = vty->hindex;
594}
595
596/* Get telnet window size. */
597static int
598vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
599{
600#ifdef TELNET_OPTION_DEBUG
601 int i;
602
603 for (i = 0; i < nbytes; i++)
604 {
605 switch (buf[i])
606 {
607 case IAC:
608 vty_out (vty, "IAC ");
609 break;
610 case WILL:
611 vty_out (vty, "WILL ");
612 break;
613 case WONT:
614 vty_out (vty, "WONT ");
615 break;
616 case DO:
617 vty_out (vty, "DO ");
618 break;
619 case DONT:
620 vty_out (vty, "DONT ");
621 break;
622 case SB:
623 vty_out (vty, "SB ");
624 break;
625 case SE:
626 vty_out (vty, "SE ");
627 break;
628 case TELOPT_ECHO:
629 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
630 break;
631 case TELOPT_SGA:
632 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
633 break;
634 case TELOPT_NAWS:
635 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
636 break;
637 default:
638 vty_out (vty, "%x ", buf[i]);
639 break;
640 }
641 }
642 vty_out (vty, "%s", VTY_NEWLINE);
643
644#endif /* TELNET_OPTION_DEBUG */
645
646 switch (buf[0])
647 {
648 case SB:
649 vty->sb_len = 0;
650 vty->iac_sb_in_progress = 1;
651 return 0;
652 break;
653 case SE:
654 {
655 if (!vty->iac_sb_in_progress)
656 return 0;
657
658 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
659 {
660 vty->iac_sb_in_progress = 0;
661 return 0;
662 }
663 switch (vty->sb_buf[0])
664 {
665 case TELOPT_NAWS:
666 if (vty->sb_len != TELNET_NAWS_SB_LEN)
667 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
668 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100669 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200670 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
671 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
672 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100673 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200674 else
675 {
676 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
677 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
678#ifdef TELNET_OPTION_DEBUG
679 vty_out(vty, "TELNET NAWS window size negotiation completed: "
680 "width %d, height %d%s",
681 vty->width, vty->height, VTY_NEWLINE);
682#endif
683 }
684 break;
685 }
686 vty->iac_sb_in_progress = 0;
687 return 0;
688 break;
689 }
690 default:
691 break;
692 }
693 return 1;
694}
695
696/* Execute current command line. */
697static int vty_execute(struct vty *vty)
698{
699 int ret;
700
701 ret = CMD_SUCCESS;
702
703 switch (vty->node) {
704 case AUTH_NODE:
705 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700706 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200707 break;
708 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700709 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200710 if (vty->type == VTY_TERM)
711 vty_hist_add(vty);
712 break;
713 }
714
715 /* Clear command line buffer. */
716 vty->cp = vty->length = 0;
717 vty_clear_buf(vty);
718
719 if (vty->status != VTY_CLOSE)
720 vty_prompt(vty);
721
722 return ret;
723}
724
725/* Send WILL TELOPT_ECHO to remote server. */
726static void
727vty_will_echo (struct vty *vty)
728{
729 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
730 vty_out (vty, "%s", cmd);
731}
732
733/* Make suppress Go-Ahead telnet option. */
734static void
735vty_will_suppress_go_ahead (struct vty *vty)
736{
737 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
738 vty_out (vty, "%s", cmd);
739}
740
741/* Make don't use linemode over telnet. */
742static void
743vty_dont_linemode (struct vty *vty)
744{
745 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
746 vty_out (vty, "%s", cmd);
747}
748
749/* Use window size. */
750static void
751vty_do_window_size (struct vty *vty)
752{
753 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
754 vty_out (vty, "%s", cmd);
755}
756
757static void vty_kill_line_from_beginning(struct vty *);
758static void vty_redraw_line(struct vty *);
759
760/* Print command line history. This function is called from
761 vty_next_line and vty_previous_line. */
762static void vty_history_print(struct vty *vty)
763{
764 int length;
765
766 vty_kill_line_from_beginning(vty);
767
768 /* Get previous line from history buffer */
769 length = strlen(vty->hist[vty->hp]);
770 memcpy(vty->buf, vty->hist[vty->hp], length);
771 vty->cp = vty->length = length;
772
773 /* Redraw current line */
774 vty_redraw_line(vty);
775}
776
777/* Show next command line history. */
778static void vty_next_line(struct vty *vty)
779{
780 int try_index;
781
782 if (vty->hp == vty->hindex)
783 return;
784
785 /* Try is there history exist or not. */
786 try_index = vty->hp;
787 if (try_index == (VTY_MAXHIST - 1))
788 try_index = 0;
789 else
790 try_index++;
791
792 /* If there is not history return. */
793 if (vty->hist[try_index] == NULL)
794 return;
795 else
796 vty->hp = try_index;
797
798 vty_history_print(vty);
799}
800
801/* Show previous command line history. */
802static void vty_previous_line(struct vty *vty)
803{
804 int try_index;
805
806 try_index = vty->hp;
807 if (try_index == 0)
808 try_index = VTY_MAXHIST - 1;
809 else
810 try_index--;
811
812 if (vty->hist[try_index] == NULL)
813 return;
814 else
815 vty->hp = try_index;
816
817 vty_history_print(vty);
818}
819
820/* This function redraw all of the command line character. */
821static void vty_redraw_line(struct vty *vty)
822{
823 vty_write(vty, vty->buf, vty->length);
824 vty->cp = vty->length;
825}
826
827/* Forward word. */
828static void vty_forward_word(struct vty *vty)
829{
830 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
831 vty_forward_char(vty);
832
833 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
834 vty_forward_char(vty);
835}
836
837/* Backward word without skipping training space. */
838static void vty_backward_pure_word(struct vty *vty)
839{
840 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
841 vty_backward_char(vty);
842}
843
844/* Backward word. */
845static void vty_backward_word(struct vty *vty)
846{
847 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
848 vty_backward_char(vty);
849
850 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
851 vty_backward_char(vty);
852}
853
854/* When '^D' is typed at the beginning of the line we move to the down
855 level. */
856static void vty_down_level(struct vty *vty)
857{
858 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100859 /* call the exit function of the specific node */
860 if (vty->node > CONFIG_NODE)
861 vty_go_parent(vty);
862 else
863 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200864 vty_prompt(vty);
865 vty->cp = 0;
866}
867
Oliver Smith053ad962021-07-22 20:24:00 +0200868/* When '^L' is typed, clear all lines above the current one. */
869static void vty_clear_screen(struct vty *vty)
870{
871 vty_out(vty, "%c%s%c%s",
872 telnet_escape_char,
873 "[2J", /* Erase Screen */
874 telnet_escape_char,
875 "[H" /* Cursor Home */
876 );
877 vty_prompt(vty);
878 vty_redraw_line(vty);
879}
880
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200881/* When '^Z' is received from vty, move down to the enable mode. */
882static void vty_end_config(struct vty *vty)
883{
884 vty_out(vty, "%s", VTY_NEWLINE);
885
886 /* FIXME: we need to call the exit function of the specific node
887 * in question, not this generic one that doesn't know all nodes */
888 switch (vty->node) {
889 case VIEW_NODE:
890 case ENABLE_NODE:
891 /* Nothing to do. */
892 break;
893 case CONFIG_NODE:
894 case VTY_NODE:
895 vty_config_unlock(vty);
896 vty->node = ENABLE_NODE;
897 break;
Harald Welte28222962011-02-18 20:37:04 +0100898 case CFG_LOG_NODE:
899 vty->node = CONFIG_NODE;
900 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200901 default:
902 /* Unknown node, we have to ignore it. */
903 break;
904 }
905
906 vty_prompt(vty);
907 vty->cp = 0;
908}
909
910/* Delete a charcter at the current point. */
911static void vty_delete_char(struct vty *vty)
912{
913 int i;
914 int size;
915
916 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
917 return;
918
919 if (vty->length == 0) {
920 vty_down_level(vty);
921 return;
922 }
923
924 if (vty->cp == vty->length)
925 return; /* completion need here? */
926
927 size = vty->length - vty->cp;
928
929 vty->length--;
930 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
931 vty->buf[vty->length] = '\0';
932
933 vty_write(vty, &vty->buf[vty->cp], size - 1);
934 vty_write(vty, &telnet_space_char, 1);
935
936 for (i = 0; i < size; i++)
937 vty_write(vty, &telnet_backward_char, 1);
938}
939
940/* Delete a character before the point. */
941static void vty_delete_backward_char(struct vty *vty)
942{
943 if (vty->cp == 0)
944 return;
945
946 vty_backward_char(vty);
947 vty_delete_char(vty);
948}
949
950/* Kill rest of line from current point. */
951static void vty_kill_line(struct vty *vty)
952{
953 int i;
954 int size;
955
956 size = vty->length - vty->cp;
957
958 if (size == 0)
959 return;
960
961 for (i = 0; i < size; i++)
962 vty_write(vty, &telnet_space_char, 1);
963 for (i = 0; i < size; i++)
964 vty_write(vty, &telnet_backward_char, 1);
965
966 memset(&vty->buf[vty->cp], 0, size);
967 vty->length = vty->cp;
968}
969
970/* Kill line from the beginning. */
971static void vty_kill_line_from_beginning(struct vty *vty)
972{
973 vty_beginning_of_line(vty);
974 vty_kill_line(vty);
975}
976
977/* Delete a word before the point. */
978static void vty_forward_kill_word(struct vty *vty)
979{
980 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
981 vty_delete_char(vty);
982 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
983 vty_delete_char(vty);
984}
985
986/* Delete a word before the point. */
987static void vty_backward_kill_word(struct vty *vty)
988{
989 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
990 vty_delete_backward_char(vty);
991 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
992 vty_delete_backward_char(vty);
993}
994
995/* Transpose chars before or at the point. */
996static void vty_transpose_chars(struct vty *vty)
997{
998 char c1, c2;
999
1000 /* If length is short or point is near by the beginning of line then
1001 return. */
1002 if (vty->length < 2 || vty->cp < 1)
1003 return;
1004
1005 /* In case of point is located at the end of the line. */
1006 if (vty->cp == vty->length) {
1007 c1 = vty->buf[vty->cp - 1];
1008 c2 = vty->buf[vty->cp - 2];
1009
1010 vty_backward_char(vty);
1011 vty_backward_char(vty);
1012 vty_self_insert_overwrite(vty, c1);
1013 vty_self_insert_overwrite(vty, c2);
1014 } else {
1015 c1 = vty->buf[vty->cp];
1016 c2 = vty->buf[vty->cp - 1];
1017
1018 vty_backward_char(vty);
1019 vty_self_insert_overwrite(vty, c1);
1020 vty_self_insert_overwrite(vty, c2);
1021 }
1022}
1023
1024/* Do completion at vty interface. */
1025static void vty_complete_command(struct vty *vty)
1026{
1027 int i;
1028 int ret;
1029 char **matched = NULL;
1030 vector vline;
1031
1032 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1033 return;
1034
1035 vline = cmd_make_strvec(vty->buf);
1036 if (vline == NULL)
1037 return;
1038
1039 /* In case of 'help \t'. */
1040 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001041 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001042
1043 matched = cmd_complete_command(vline, vty, &ret);
1044
1045 cmd_free_strvec(vline);
1046
1047 vty_out(vty, "%s", VTY_NEWLINE);
1048 switch (ret) {
1049 case CMD_ERR_AMBIGUOUS:
1050 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1051 vty_prompt(vty);
1052 vty_redraw_line(vty);
1053 break;
1054 case CMD_ERR_NO_MATCH:
1055 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1056 vty_prompt(vty);
1057 vty_redraw_line(vty);
1058 break;
1059 case CMD_COMPLETE_FULL_MATCH:
1060 vty_prompt(vty);
1061 vty_redraw_line(vty);
1062 vty_backward_pure_word(vty);
1063 vty_insert_word_overwrite(vty, matched[0]);
1064 vty_self_insert(vty, ' ');
1065 talloc_free(matched[0]);
1066 break;
1067 case CMD_COMPLETE_MATCH:
1068 vty_prompt(vty);
1069 vty_redraw_line(vty);
1070 vty_backward_pure_word(vty);
1071 vty_insert_word_overwrite(vty, matched[0]);
1072 talloc_free(matched[0]);
1073 break;
1074 case CMD_COMPLETE_LIST_MATCH:
1075 for (i = 0; matched[i] != NULL; i++) {
1076 if (i != 0 && ((i % 6) == 0))
1077 vty_out(vty, "%s", VTY_NEWLINE);
1078 vty_out(vty, "%-10s ", matched[i]);
1079 talloc_free(matched[i]);
1080 }
1081 vty_out(vty, "%s", VTY_NEWLINE);
1082
1083 vty_prompt(vty);
1084 vty_redraw_line(vty);
1085 break;
1086 case CMD_ERR_NOTHING_TODO:
1087 vty_prompt(vty);
1088 vty_redraw_line(vty);
1089 break;
1090 default:
1091 break;
1092 }
1093 if (matched)
1094 vector_only_index_free(matched);
1095}
1096
1097static void
1098vty_describe_fold(struct vty *vty, int cmd_width,
1099 unsigned int desc_width, struct desc *desc)
1100{
1101 char *buf;
1102 const char *cmd, *p;
1103 int pos;
1104
1105 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1106
1107 if (desc_width <= 0) {
1108 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1109 VTY_NEWLINE);
1110 return;
1111 }
1112
1113 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1114 if (!buf)
1115 return;
1116
1117 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1118 for (pos = desc_width; pos > 0; pos--)
1119 if (*(p + pos) == ' ')
1120 break;
1121
1122 if (pos == 0)
1123 break;
1124
1125 strncpy(buf, p, pos);
1126 buf[pos] = '\0';
1127 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1128
1129 cmd = "";
1130 }
1131
1132 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1133
1134 talloc_free(buf);
1135}
1136
1137/* Describe matched command function. */
1138static void vty_describe_command(struct vty *vty)
1139{
1140 int ret;
1141 vector vline;
1142 vector describe;
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001143 unsigned int i, cmd_width, desc_width;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001144 struct desc *desc, *desc_cr = NULL;
1145
1146 vline = cmd_make_strvec(vty->buf);
1147
1148 /* In case of '> ?'. */
1149 if (vline == NULL) {
1150 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001151 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001152 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001153 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001154
1155 describe = cmd_describe_command(vline, vty, &ret);
1156
1157 vty_out(vty, "%s", VTY_NEWLINE);
1158
1159 /* Ambiguous error. */
1160 switch (ret) {
1161 case CMD_ERR_AMBIGUOUS:
1162 cmd_free_strvec(vline);
1163 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1164 vty_prompt(vty);
1165 vty_redraw_line(vty);
1166 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001167 case CMD_ERR_NO_MATCH:
1168 cmd_free_strvec(vline);
1169 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1170 vty_prompt(vty);
1171 vty_redraw_line(vty);
1172 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001173 }
1174
1175 /* Get width of command string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001176 cmd_width = 0;
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001177 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001178 if ((desc = vector_slot(describe, i)) != NULL) {
1179 unsigned int len;
1180
1181 if (desc->cmd[0] == '\0')
1182 continue;
1183
1184 len = strlen(desc->cmd);
1185 if (desc->cmd[0] == '.')
1186 len--;
1187
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001188 if (cmd_width < len)
1189 cmd_width = len;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001190 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001191 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001192
1193 /* Get width of description string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001194 desc_width = vty->width - (cmd_width + 6);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001195
1196 /* Print out description. */
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001197 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001198 if ((desc = vector_slot(describe, i)) != NULL) {
1199 if (desc->cmd[0] == '\0')
1200 continue;
1201
1202 if (strcmp(desc->cmd, "<cr>") == 0) {
1203 desc_cr = desc;
1204 continue;
1205 }
1206
1207 if (!desc->str)
1208 vty_out(vty, " %-s%s",
1209 desc->cmd[0] ==
1210 '.' ? desc->cmd + 1 : desc->cmd,
1211 VTY_NEWLINE);
1212 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001213 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001214 desc->cmd[0] ==
1215 '.' ? desc->cmd + 1 : desc->cmd,
1216 desc->str, VTY_NEWLINE);
1217 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001218 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001219
1220#if 0
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001221 vty_out(vty, " %-*s %s%s", cmd_width
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001222 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1223 desc->str ? desc->str : "", VTY_NEWLINE);
1224#endif /* 0 */
1225 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001226 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001227
1228 if ((desc = desc_cr)) {
1229 if (!desc->str)
1230 vty_out(vty, " %-s%s",
1231 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1232 VTY_NEWLINE);
1233 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001234 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001235 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1236 desc->str, VTY_NEWLINE);
1237 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001238 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001239 }
1240
1241 cmd_free_strvec(vline);
1242 vector_free(describe);
1243
1244 vty_prompt(vty);
1245 vty_redraw_line(vty);
1246}
1247
1248/* ^C stop current input and do not add command line to the history. */
1249static void vty_stop_input(struct vty *vty)
1250{
1251 vty->cp = vty->length = 0;
1252 vty_clear_buf(vty);
1253 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001254 vty_prompt(vty);
1255
1256 /* Set history pointer to the latest one. */
1257 vty->hp = vty->hindex;
1258}
1259
1260#define CONTROL(X) ((X) - '@')
1261#define VTY_NORMAL 0
1262#define VTY_PRE_ESCAPE 1
1263#define VTY_ESCAPE 2
1264
1265/* Escape character command map. */
1266static void vty_escape_map(unsigned char c, struct vty *vty)
1267{
1268 switch (c) {
1269 case ('A'):
1270 vty_previous_line(vty);
1271 break;
1272 case ('B'):
1273 vty_next_line(vty);
1274 break;
1275 case ('C'):
1276 vty_forward_char(vty);
1277 break;
1278 case ('D'):
1279 vty_backward_char(vty);
1280 break;
1281 default:
1282 break;
1283 }
1284
1285 /* Go back to normal mode. */
1286 vty->escape = VTY_NORMAL;
1287}
1288
1289/* Quit print out to the buffer. */
1290static void vty_buffer_reset(struct vty *vty)
1291{
1292 buffer_reset(vty->obuf);
1293 vty_prompt(vty);
1294 vty_redraw_line(vty);
1295}
1296
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001297/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001298int vty_read(struct vty *vty)
1299{
1300 int i;
1301 int nbytes;
1302 unsigned char buf[VTY_READ_BUFSIZ];
1303
1304 int vty_sock = vty->fd;
1305
1306 /* Read raw data from socket */
1307 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1308 if (nbytes < 0) {
1309 if (ERRNO_IO_RETRY(errno)) {
1310 vty_event(VTY_READ, vty_sock, vty);
1311 return 0;
1312 }
1313 }
1314 buffer_reset(vty->obuf);
1315 vty->status = VTY_CLOSE;
1316 }
1317
1318 for (i = 0; i < nbytes; i++) {
1319 if (buf[i] == IAC) {
1320 if (!vty->iac) {
1321 vty->iac = 1;
1322 continue;
1323 } else {
1324 vty->iac = 0;
1325 }
1326 }
1327
1328 if (vty->iac_sb_in_progress && !vty->iac) {
1329 if (vty->sb_len < sizeof(vty->sb_buf))
1330 vty->sb_buf[vty->sb_len] = buf[i];
1331 vty->sb_len++;
1332 continue;
1333 }
1334
1335 if (vty->iac) {
1336 /* In case of telnet command */
1337 int ret = 0;
1338 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1339 vty->iac = 0;
1340 i += ret;
1341 continue;
1342 }
1343
1344 if (vty->status == VTY_MORE) {
1345 switch (buf[i]) {
1346 case CONTROL('C'):
1347 case 'q':
1348 case 'Q':
1349 vty_buffer_reset(vty);
1350 break;
1351#if 0 /* More line does not work for "show ip bgp". */
1352 case '\n':
1353 case '\r':
1354 vty->status = VTY_MORELINE;
1355 break;
1356#endif
1357 default:
1358 break;
1359 }
1360 continue;
1361 }
1362
1363 /* Escape character. */
1364 if (vty->escape == VTY_ESCAPE) {
1365 vty_escape_map(buf[i], vty);
1366 continue;
1367 }
1368
1369 /* Pre-escape status. */
1370 if (vty->escape == VTY_PRE_ESCAPE) {
1371 switch (buf[i]) {
1372 case '[':
1373 vty->escape = VTY_ESCAPE;
1374 break;
1375 case 'b':
1376 vty_backward_word(vty);
1377 vty->escape = VTY_NORMAL;
1378 break;
1379 case 'f':
1380 vty_forward_word(vty);
1381 vty->escape = VTY_NORMAL;
1382 break;
1383 case 'd':
1384 vty_forward_kill_word(vty);
1385 vty->escape = VTY_NORMAL;
1386 break;
1387 case CONTROL('H'):
1388 case 0x7f:
1389 vty_backward_kill_word(vty);
1390 vty->escape = VTY_NORMAL;
1391 break;
1392 default:
1393 vty->escape = VTY_NORMAL;
1394 break;
1395 }
1396 continue;
1397 }
1398
1399 switch (buf[i]) {
1400 case CONTROL('A'):
1401 vty_beginning_of_line(vty);
1402 break;
1403 case CONTROL('B'):
1404 vty_backward_char(vty);
1405 break;
1406 case CONTROL('C'):
1407 vty_stop_input(vty);
1408 break;
1409 case CONTROL('D'):
1410 vty_delete_char(vty);
1411 break;
1412 case CONTROL('E'):
1413 vty_end_of_line(vty);
1414 break;
1415 case CONTROL('F'):
1416 vty_forward_char(vty);
1417 break;
1418 case CONTROL('H'):
1419 case 0x7f:
1420 vty_delete_backward_char(vty);
1421 break;
1422 case CONTROL('K'):
1423 vty_kill_line(vty);
1424 break;
Oliver Smith053ad962021-07-22 20:24:00 +02001425 case CONTROL('L'):
1426 vty_clear_screen(vty);
1427 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428 case CONTROL('N'):
1429 vty_next_line(vty);
1430 break;
1431 case CONTROL('P'):
1432 vty_previous_line(vty);
1433 break;
1434 case CONTROL('T'):
1435 vty_transpose_chars(vty);
1436 break;
1437 case CONTROL('U'):
1438 vty_kill_line_from_beginning(vty);
1439 break;
1440 case CONTROL('W'):
1441 vty_backward_kill_word(vty);
1442 break;
1443 case CONTROL('Z'):
1444 vty_end_config(vty);
1445 break;
1446 case '\n':
1447 case '\r':
1448 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001449 /* '\0'-terminate the command buffer */
1450 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001451 vty_execute(vty);
1452 break;
1453 case '\t':
1454 vty_complete_command(vty);
1455 break;
1456 case '?':
1457 if (vty->node == AUTH_NODE
1458 || vty->node == AUTH_ENABLE_NODE)
1459 vty_self_insert(vty, buf[i]);
1460 else
1461 vty_describe_command(vty);
1462 break;
1463 case '\033':
1464 if (i + 1 < nbytes && buf[i + 1] == '[') {
1465 vty->escape = VTY_ESCAPE;
1466 i++;
1467 } else
1468 vty->escape = VTY_PRE_ESCAPE;
1469 break;
1470 default:
1471 if (buf[i] > 31 && buf[i] < 127)
1472 vty_self_insert(vty, buf[i]);
1473 break;
1474 }
1475 }
1476
1477 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001478 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001479 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001480 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001481 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001482 vty_event(VTY_WRITE, vty_sock, vty);
1483 vty_event(VTY_READ, vty_sock, vty);
1484 }
1485 return 0;
1486}
1487
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001488/* Read up configuration from a file stream */
1489/*! Read up VTY configuration from a file stream
1490 * \param[in] confp file pointer of the stream for the configuration file
1491 * \param[in] priv private data to be passed to \ref vty_read_file
1492 * \returns Zero on success, non-zero on error
1493 */
1494int vty_read_config_filep(FILE *confp, void *priv)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001495{
1496 int ret;
1497 struct vty *vty;
1498
1499 vty = vty_new();
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001500 vty->type = VTY_FILE;
1501 vty->node = CONFIG_NODE;
1502 vty->priv = priv;
1503
Vadim Yanitskiyb639b4d2019-11-21 02:20:11 +07001504 /* By default, write to stderr. Otherwise, during parsing of the logging
1505 * configuration, all invocations to vty_out() would make the process
1506 * write() to its own stdin (fd=0)! */
1507 vty->fd = fileno(stderr);
1508
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509 ret = config_from_file(vty, confp);
1510
1511 if (ret != CMD_SUCCESS) {
1512 switch (ret) {
1513 case CMD_ERR_AMBIGUOUS:
1514 fprintf(stderr, "Ambiguous command.\n");
1515 break;
1516 case CMD_ERR_NO_MATCH:
1517 fprintf(stderr, "There is no such command.\n");
1518 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001519 case CMD_ERR_INVALID_INDENT:
1520 fprintf(stderr,
1521 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1522 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1523 "allowed, but their sequence must not change within a child block.\n");
1524 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001525 }
Keith03516d62017-09-04 11:19:13 +02001526 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001527 "line:\n%s\n", vty->buf);
1528 vty_close(vty);
1529 return -EINVAL;
1530 }
1531
1532 vty_close(vty);
1533 return 0;
1534}
1535
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001536/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001537struct vty *
1538vty_create (int vty_sock, void *priv)
1539{
1540 struct vty *vty;
1541
Alexander Couzensab383e62018-07-17 19:02:34 +02001542 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001543
1544 tcgetattr(vty_sock, &t);
1545 cfmakeraw(&t);
1546 tcsetattr(vty_sock, TCSANOW, &t);
1547
1548 /* Allocate new vty structure and set up default values. */
1549 vty = vty_new ();
1550 vty->fd = vty_sock;
1551 vty->priv = priv;
1552 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001553 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001554 {
1555 if (host.advanced)
1556 vty->node = ENABLE_NODE;
1557 else
1558 vty->node = VIEW_NODE;
1559 }
1560 else
1561 vty->node = AUTH_NODE;
1562 vty->fail = 0;
1563 vty->cp = 0;
1564 vty_clear_buf (vty);
1565 vty->length = 0;
1566 memset (vty->hist, 0, sizeof (vty->hist));
1567 vty->hp = 0;
1568 vty->hindex = 0;
1569 vector_set_index (vtyvec, vty_sock, vty);
1570 vty->status = VTY_NORMAL;
1571 if (host.lines >= 0)
1572 vty->lines = host.lines;
1573 else
1574 vty->lines = -1;
1575
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001576 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001577 {
1578 /* Vty is not available if password isn't set. */
1579 if (host.password == NULL && host.password_encrypt == NULL)
1580 {
1581 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1582 vty->status = VTY_CLOSE;
1583 vty_close (vty);
1584 return NULL;
1585 }
1586 }
1587
1588 /* Say hello to the world. */
1589 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001590 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001591 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1592
1593 /* Setting up terminal. */
1594 vty_will_echo (vty);
1595 vty_will_suppress_go_ahead (vty);
1596
1597 vty_dont_linemode (vty);
1598 vty_do_window_size (vty);
1599 /* vty_dont_lflow_ahead (vty); */
1600
1601 vty_prompt (vty);
1602
1603 /* Add read/write thread. */
1604 vty_event (VTY_WRITE, vty_sock, vty);
1605 vty_event (VTY_READ, vty_sock, vty);
1606
1607 return vty;
1608}
1609
1610DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1611{
1612 unsigned int i;
1613 struct vty *v;
1614
1615 for (i = 0; i < vector_active(vtyvec); i++)
1616 if ((v = vector_slot(vtyvec, i)) != NULL)
1617 vty_out(vty, "%svty[%d] %s",
1618 v->config ? "*" : " ", i, VTY_NEWLINE);
1619 return CMD_SUCCESS;
1620}
1621
1622/* Move to vty configuration mode. */
1623DEFUN(line_vty,
1624 line_vty_cmd,
1625 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1626{
1627 vty->node = VTY_NODE;
1628 return CMD_SUCCESS;
1629}
1630
1631/* vty login. */
1632DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1633{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001634 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001635 return CMD_SUCCESS;
1636}
1637
1638DEFUN(no_vty_login,
1639 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1640{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001641 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001642 return CMD_SUCCESS;
1643}
1644
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001645/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001646DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001647 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001648 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1649 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001650{
1651 talloc_free((void*)vty_bind_addr);
1652 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001653 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001654 return CMD_SUCCESS;
1655}
1656
1657const char *vty_get_bind_addr(void)
1658{
1659 if (!vty_bind_addr)
1660 return VTY_BIND_ADDR_DEFAULT;
1661 return vty_bind_addr;
1662}
1663
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001664int vty_get_bind_port(int default_port)
1665{
1666 if (vty_bind_port >= 0)
1667 return vty_bind_port;
1668 return default_port;
1669}
1670
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001671DEFUN(service_advanced_vty,
1672 service_advanced_vty_cmd,
1673 "service advanced-vty",
1674 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1675{
1676 host.advanced = 1;
1677 return CMD_SUCCESS;
1678}
1679
1680DEFUN(no_service_advanced_vty,
1681 no_service_advanced_vty_cmd,
1682 "no service advanced-vty",
1683 NO_STR
1684 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1685{
1686 host.advanced = 0;
1687 return CMD_SUCCESS;
1688}
1689
1690DEFUN(terminal_monitor,
1691 terminal_monitor_cmd,
1692 "terminal monitor",
1693 "Set terminal line parameters\n"
1694 "Copy debug output to the current terminal line\n")
1695{
1696 vty->monitor = 1;
1697 return CMD_SUCCESS;
1698}
1699
1700DEFUN(terminal_no_monitor,
1701 terminal_no_monitor_cmd,
1702 "terminal no monitor",
1703 "Set terminal line parameters\n"
1704 NO_STR "Copy debug output to the current terminal line\n")
1705{
1706 vty->monitor = 0;
1707 return CMD_SUCCESS;
1708}
1709
1710DEFUN(show_history,
1711 show_history_cmd,
1712 "show history", SHOW_STR "Display the session command history\n")
1713{
1714 int index;
1715
1716 for (index = vty->hindex + 1; index != vty->hindex;) {
1717 if (index == VTY_MAXHIST) {
1718 index = 0;
1719 continue;
1720 }
1721
1722 if (vty->hist[index] != NULL)
1723 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1724
1725 index++;
1726 }
1727
1728 return CMD_SUCCESS;
1729}
1730
1731/* Display current configuration. */
1732static int vty_config_write(struct vty *vty)
1733{
1734 vty_out(vty, "line vty%s", VTY_NEWLINE);
1735
1736 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001737 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001738 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001739 else
1740 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001741
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001742 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001743 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1744 if (vty_bind_port >= 0) {
1745 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1746 vty_bind_port, VTY_NEWLINE);
1747 } else {
1748 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1749 }
1750 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001751
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001752 vty_out(vty, "!%s", VTY_NEWLINE);
1753
1754 return CMD_SUCCESS;
1755}
1756
1757struct cmd_node vty_node = {
1758 VTY_NODE,
1759 "%s(config-line)# ",
1760 1,
1761};
1762
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001763/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001764void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001765{
1766 unsigned int i;
1767 struct vty *vty;
1768 struct thread *vty_serv_thread;
1769
1770 for (i = 0; i < vector_active(vtyvec); i++)
1771 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1772 buffer_reset(vty->obuf);
1773 vty->status = VTY_CLOSE;
1774 vty_close(vty);
1775 }
1776
1777 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1778 if ((vty_serv_thread =
1779 vector_slot(Vvty_serv_thread, i)) != NULL) {
1780 //thread_cancel (vty_serv_thread);
1781 vector_slot(Vvty_serv_thread, i) = NULL;
1782 close(i);
1783 }
1784}
1785
1786static void vty_save_cwd(void)
1787{
1788 char cwd[MAXPATHLEN];
1789 char *c ;
1790
1791 c = getcwd(cwd, MAXPATHLEN);
1792
1793 if (!c) {
1794 if (chdir(SYSCONFDIR) != 0)
1795 perror("chdir failed");
1796 if (getcwd(cwd, MAXPATHLEN) == NULL)
1797 perror("getcwd failed");
1798 }
1799
1800 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1801 strcpy(vty_cwd, cwd);
1802}
1803
Harald Welte95b2b472011-07-16 11:58:09 +02001804char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001805{
1806 return vty_cwd;
1807}
1808
1809int vty_shell_serv(struct vty *vty)
1810{
1811 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1812}
1813
Harald Welte95b2b472011-07-16 11:58:09 +02001814void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001815{
1816 vtyvec = vector_init(VECTOR_MIN_SIZE);
1817}
1818
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001819/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001820 * \param[in] app_info application information
1821 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001822/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001823void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001824{
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001825 unsigned int i, j;
1826
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301827 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001828 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1829 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1830
1831 cmd_init(1);
1832
Harald Welte237f6242010-05-25 23:00:45 +02001833 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001834
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001835 /* Check for duplicate flags in application specific attributes (if any) */
1836 for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) {
1837 if (app_info->usr_attr_letters[i] == '\0')
1838 continue;
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001839
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07001840 /* Some flag characters are reserved for global attributes */
1841 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
1842 for (j = 0; j < ARRAY_SIZE(rafc); j++) {
1843 if (app_info->usr_attr_letters[i] != rafc[j])
1844 continue;
1845 fprintf(stderr, "Attribute flag character '%c' is reserved "
1846 "for globals! Please fix.\n", app_info->usr_attr_letters[i]);
1847 }
1848
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001849 /* Upper case flag letters are reserved for libraries */
1850 if (app_info->usr_attr_letters[i] >= 'A' &&
1851 app_info->usr_attr_letters[i] <= 'Z') {
1852 fprintf(stderr, "Attribute flag letter '%c' is reserved "
1853 "for libraries! Please fix.\n", app_info->usr_attr_letters[i]);
1854 }
1855
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001856 for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) {
1857 if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i])
1858 continue;
1859 fprintf(stderr, "Found duplicate flag letter '%c' in application "
1860 "specific attributes (index %u vs %u)! Please fix.\n",
1861 app_info->usr_attr_letters[i], i, j);
1862 }
1863 }
1864
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001865 /* For further configuration read, preserve current directory. */
1866 vty_save_cwd();
1867
1868 vtyvec = vector_init(VECTOR_MIN_SIZE);
1869
1870 /* Install bgp top node. */
1871 install_node(&vty_node, vty_config_write);
1872
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001873 install_lib_element_ve(&config_who_cmd);
1874 install_lib_element_ve(&show_history_cmd);
1875 install_lib_element(CONFIG_NODE, &line_vty_cmd);
1876 install_lib_element(CONFIG_NODE, &service_advanced_vty_cmd);
1877 install_lib_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1878 install_lib_element(CONFIG_NODE, &show_history_cmd);
1879 install_lib_element(ENABLE_NODE, &terminal_monitor_cmd);
1880 install_lib_element(ENABLE_NODE, &terminal_no_monitor_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001881
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001882 install_lib_element(VTY_NODE, &vty_login_cmd);
1883 install_lib_element(VTY_NODE, &no_vty_login_cmd);
1884 install_lib_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001885}
1886
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001887/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001888 * \param[in] file_name file name of the configuration file
1889 * \param[in] priv private data to be passed to \ref vty_read_file
1890 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001891int vty_read_config_file(const char *file_name, void *priv)
1892{
1893 FILE *cfile;
1894 int rc;
1895
1896 cfile = fopen(file_name, "r");
1897 if (!cfile)
1898 return -ENOENT;
1899
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001900 rc = vty_read_config_filep(cfile, priv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001901 fclose(cfile);
1902
1903 host_config_set(file_name);
1904
1905 return rc;
1906}
Harald Welte7acb30c2011-08-17 17:13:48 +02001907
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001908/*! @} */