blob: 3a549b4349df6e227991ac1f1b1b106d62e35a67 [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>
Alexander Couzens06929162021-09-05 23:08:59 +020069#include <osmocom/core/timer.h>
Vadim Yanitskiy024e1952020-10-02 18:23:38 +070070#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020071
Ruben Undheim766f77c2018-11-18 13:02:47 +010072#ifndef MAXPATHLEN
73 #define MAXPATHLEN 4096
74#endif
75
76
Harald Welte7acb30c2011-08-17 17:13:48 +020077/* \addtogroup vty
78 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020079 * \file vty.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020080
Harald Welte3fb0b6f2010-05-19 19:02:52 +020081#define SYSCONFDIR "/usr/local/etc"
82
83/* our callback, located in telnet_interface.c */
84void vty_event(enum event event, int sock, struct vty *vty);
85
86extern struct host host;
87
88/* Vector which store each vty structure. */
89static vector vtyvec;
90
91vector Vvty_serv_thread;
92
93char *vty_cwd = NULL;
94
Neels Hofmeyr96172f02016-02-23 14:01:41 +010095/* IP address passed to the 'line vty'/'bind' command.
96 * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
97 * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
98static const char *vty_bind_addr = NULL;
99#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +0000100/* Port the VTY should bind to. -1 means not configured */
101static int vty_bind_port = -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +0100102
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200103/* Configure lock. */
104static int vty_config;
105
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -0700106static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200107
108void *tall_vty_ctx;
109
110static void vty_clear_buf(struct vty *vty)
111{
112 memset(vty->buf, 0, vty->max);
113}
114
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200115/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200116struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200117{
118 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
119
120 if (!new)
121 goto out;
122
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200123 INIT_LLIST_HEAD(&new->parent_nodes);
124
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200125 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
126 if (!new->obuf)
127 goto out_new;
128 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
129 if (!new->buf)
130 goto out_obuf;
131
132 new->max = VTY_BUFSIZ;
Vadim Yanitskiy54df08e2019-11-21 21:44:30 +0700133 new->fd = -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200134
135 return new;
136
137out_obuf:
138 buffer_free(new->obuf);
139out_new:
140 talloc_free(new);
141 new = NULL;
142out:
143 return new;
144}
145
146/* Authentication of vty */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700147static void vty_auth(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200148{
149 char *passwd = NULL;
150 enum node_type next_node = 0;
151 int fail;
152 char *crypt(const char *, const char *);
153
154 switch (vty->node) {
155 case AUTH_NODE:
156#ifdef VTY_CRYPT_PW
157 if (host.encrypt)
158 passwd = host.password_encrypt;
159 else
160#endif
161 passwd = host.password;
162 if (host.advanced)
163 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
164 else
165 next_node = VIEW_NODE;
166 break;
167 case AUTH_ENABLE_NODE:
168#ifdef VTY_CRYPT_PW
169 if (host.encrypt)
170 passwd = host.enable_encrypt;
171 else
172#endif
173 passwd = host.enable;
174 next_node = ENABLE_NODE;
175 break;
176 }
177
178 if (passwd) {
179#ifdef VTY_CRYPT_PW
180 if (host.encrypt)
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700181 fail = strcmp(crypt(vty->buf, passwd), passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200182 else
183#endif
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700184 fail = strcmp(vty->buf, passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200185 } else
186 fail = 1;
187
188 if (!fail) {
189 vty->fail = 0;
190 vty->node = next_node; /* Success ! */
191 } else {
192 vty->fail++;
193 if (vty->fail >= 3) {
194 if (vty->node == AUTH_NODE) {
195 vty_out(vty,
196 "%% Bad passwords, too many failures!%s",
197 VTY_NEWLINE);
198 vty->status = VTY_CLOSE;
199 } else {
200 /* AUTH_ENABLE_NODE */
201 vty->fail = 0;
202 vty_out(vty,
203 "%% Bad enable passwords, too many failures!%s",
204 VTY_NEWLINE);
205 vty->node = VIEW_NODE;
206 }
207 }
208 }
209}
210
Eric11a58a12021-09-09 15:42:32 +0200211void vty_flush(struct vty *vty)
212{
213 if (vty->obuf)
214 buffer_flush_all(vty->obuf, vty->fd);
215}
216
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200217/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200218void vty_close(struct vty *vty)
219{
220 int i;
221
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200222 /* VTY_CLOSED is handled by the telnet_interface */
223 vty_event(VTY_CLOSED, vty->fd, vty);
224
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200225 if (vty->obuf) {
226 /* Flush buffer. */
227 buffer_flush_all(vty->obuf, vty->fd);
228
229 /* Free input buffer. */
230 buffer_free(vty->obuf);
231 vty->obuf = NULL;
232 }
233
234 /* Free command history. */
235 for (i = 0; i < VTY_MAXHIST; i++)
236 if (vty->hist[i])
237 talloc_free(vty->hist[i]);
238
239 /* Unset vector. */
240 vector_unset(vtyvec, vty->fd);
241
Vadim Yanitskiy7a35b782019-11-21 21:51:23 +0700242 /* Close socket (ignore standard I/O streams). */
243 if (vty->fd > 2) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200244 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200245 vty->fd = -1;
246 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200247
248 if (vty->buf) {
249 talloc_free(vty->buf);
250 vty->buf = NULL;
251 }
252
253 /* Check configure. */
254 vty_config_unlock(vty);
255
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200256 /* OK free vty. */
257 talloc_free(vty);
258}
259
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200260/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261int vty_shell(struct vty *vty)
262{
263 return vty->type == VTY_SHELL ? 1 : 0;
264}
265
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100266int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200267{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200268 int len = 0;
269 int size = 1024;
270 char buf[1024];
271 char *p = NULL;
272
273 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100274 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200275 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100276 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200277 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100278 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200279 len = vsnprintf(buf, sizeof buf, format, args);
280 va_end(args);
281
282 /* Initial buffer is not enough. */
283 if (len < 0 || len >= size) {
284 while (1) {
285 if (len > -1)
286 size = len + 1;
287 else
288 size = size * 2;
289
290 p = talloc_realloc_size(vty, p, size);
291 if (!p)
292 return -1;
293
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100294 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200295 len = vsnprintf(p, size, format, args);
296 va_end(args);
297
298 if (len > -1 && len < size)
299 break;
300 }
301 }
302
303 /* When initial buffer is enough to store all output. */
304 if (!p)
305 p = buf;
306
307 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100308 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200309
310 /* If p is not different with buf, it is allocated buffer. */
311 if (p != buf)
312 talloc_free(p);
313 }
314
315 vty_event(VTY_WRITE, vty->fd, vty);
316
317 return len;
318}
319
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100320/*! VTY standard output function
321 * \param[in] vty VTY to which we should print
322 * \param[in] format variable-length format string
323 */
324int vty_out(struct vty *vty, const char *format, ...)
325{
326 va_list args;
327 int rc;
328 va_start(args, format);
329 rc = vty_out_va(vty, format, args);
330 va_end(args);
331 return rc;
332}
333
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200334/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200335int vty_out_newline(struct vty *vty)
336{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200337 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200338 buffer_put(vty->obuf, p, strlen(p));
339 return 0;
340}
341
Alexander Couzens06929162021-09-05 23:08:59 +0200342/*! calculates the time difference of a give timespec to the current time
343 * and prints in a human readable format (days, hours, minutes, seconds).
344 */
345int vty_out_uptime(struct vty *vty, const struct timespec *starttime)
346{
347 struct timespec now;
348 struct timespec uptime;
349
350 osmo_clock_gettime(CLOCK_MONOTONIC, &now);
351 timespecsub(&now, starttime, &uptime);
352
353 int d = uptime.tv_sec / (3600 * 24);
354 int h = uptime.tv_sec / 3600 % 24;
355 int m = uptime.tv_sec / 60 % 60;
356 int s = uptime.tv_sec % 60;
357
358 return vty_out(vty, "%dd %dh %dm %ds", d, h, m, s);
359}
360
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200361/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800362void *vty_current_index(struct vty *vty)
363{
364 return vty->index;
365}
Harald Welte7acb30c2011-08-17 17:13:48 +0200366
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200367/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800368int vty_current_node(struct vty *vty)
369{
370 return vty->node;
371}
372
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200373/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200374 * \param[in] vty VTY to which the config shall be locked
375 * \returns 1 on success, 0 on error
376 *
377 * This shall be used to make sure only one VTY at a given time has
378 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200379int vty_config_lock(struct vty *vty)
380{
381 if (vty_config == 0) {
382 vty->config = 1;
383 vty_config = 1;
384 }
385 return vty->config;
386}
387
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200388/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200389 * \param[in] vty VTY from which the configuration shall be unlocked
390 * \returns 0 in case of success
391 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200392int vty_config_unlock(struct vty *vty)
393{
394 if (vty_config == 1 && vty->config == 1) {
395 vty->config = 0;
396 vty_config = 0;
397 }
398 return vty->config;
399}
400
401/* Say hello to vty interface. */
402void vty_hello(struct vty *vty)
403{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200404 const char *app_name = "<unnamed>";
405
406 if (host.app_info->name)
407 app_name = host.app_info->name;
408
Harald Weltea2501a22018-03-23 19:33:27 +0100409 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800410 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200411
412 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200413 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200414
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200415 if (host.motdfile) {
416 FILE *f;
417 char buf[4096];
418
419 f = fopen(host.motdfile, "r");
420 if (f) {
421 while (fgets(buf, sizeof(buf), f)) {
422 char *s;
423 /* work backwards to ignore trailling isspace() */
424 for (s = buf + strlen(buf);
425 (s > buf) && isspace(*(s - 1)); s--) ;
426 *s = '\0';
427 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
428 }
429 fclose(f);
430 } else
431 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
432 } else if (host.motd)
433 vty_out(vty, "%s", host.motd);
434}
435
436/* Put out prompt and wait input from user. */
437static void vty_prompt(struct vty *vty)
438{
439 struct utsname names;
440 const char *hostname;
441
442 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100443 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200444 if (!hostname) {
445 uname(&names);
446 hostname = names.nodename;
447 }
448 vty_out(vty, cmd_prompt(vty->node), hostname);
449 }
450}
451
452/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700453static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200454{
455 int ret;
456 vector vline;
457
458 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700459 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200460
461 if (vline == NULL)
462 return CMD_SUCCESS;
463
464 ret = cmd_execute_command(vline, vty, NULL, 0);
465 if (ret != CMD_SUCCESS)
466 switch (ret) {
467 case CMD_WARNING:
468 if (vty->type == VTY_FILE)
469 vty_out(vty, "Warning...%s", VTY_NEWLINE);
470 break;
471 case CMD_ERR_AMBIGUOUS:
472 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
473 break;
474 case CMD_ERR_NO_MATCH:
475 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
476 break;
477 case CMD_ERR_INCOMPLETE:
478 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
479 break;
480 }
481 cmd_free_strvec(vline);
482
483 return ret;
484}
485
486static const char telnet_backward_char = 0x08;
487static const char telnet_space_char = ' ';
Oliver Smith053ad962021-07-22 20:24:00 +0200488static const char telnet_escape_char = 0x1B;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200489
490/* Basic function to write buffer to vty. */
491static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
492{
493 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
494 return;
495
496 /* Should we do buffering here ? And make vty_flush (vty) ? */
497 buffer_put(vty->obuf, buf, nbytes);
498}
499
500/* Ensure length of input buffer. Is buffer is short, double it. */
501static void vty_ensure(struct vty *vty, int length)
502{
503 if (vty->max <= length) {
504 vty->max *= 2;
505 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
506 // FIXME: check return
507 }
508}
509
510/* Basic function to insert character into vty. */
511static void vty_self_insert(struct vty *vty, char c)
512{
513 int i;
514 int length;
515
516 vty_ensure(vty, vty->length + 1);
517 length = vty->length - vty->cp;
518 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
519 vty->buf[vty->cp] = c;
520
521 vty_write(vty, &vty->buf[vty->cp], length + 1);
522 for (i = 0; i < length; i++)
523 vty_write(vty, &telnet_backward_char, 1);
524
525 vty->cp++;
526 vty->length++;
527}
528
529/* Self insert character 'c' in overwrite mode. */
530static void vty_self_insert_overwrite(struct vty *vty, char c)
531{
532 vty_ensure(vty, vty->length + 1);
533 vty->buf[vty->cp++] = c;
534
535 if (vty->cp > vty->length)
536 vty->length++;
537
538 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
539 return;
540
541 vty_write(vty, &c, 1);
542}
543
544/* Insert a word into vty interface with overwrite mode. */
545static void vty_insert_word_overwrite(struct vty *vty, char *str)
546{
547 int len = strlen(str);
548 vty_write(vty, str, len);
549 strcpy(&vty->buf[vty->cp], str);
550 vty->cp += len;
551 vty->length = vty->cp;
552}
553
554/* Forward character. */
555static void vty_forward_char(struct vty *vty)
556{
557 if (vty->cp < vty->length) {
558 vty_write(vty, &vty->buf[vty->cp], 1);
559 vty->cp++;
560 }
561}
562
563/* Backward character. */
564static void vty_backward_char(struct vty *vty)
565{
566 if (vty->cp > 0) {
567 vty->cp--;
568 vty_write(vty, &telnet_backward_char, 1);
569 }
570}
571
572/* Move to the beginning of the line. */
573static void vty_beginning_of_line(struct vty *vty)
574{
575 while (vty->cp)
576 vty_backward_char(vty);
577}
578
579/* Move to the end of the line. */
580static void vty_end_of_line(struct vty *vty)
581{
582 while (vty->cp < vty->length)
583 vty_forward_char(vty);
584}
585
586/* Add current command line to the history buffer. */
587static void vty_hist_add(struct vty *vty)
588{
589 int index;
590
591 if (vty->length == 0)
592 return;
593
594 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
595
596 /* Ignore the same string as previous one. */
597 if (vty->hist[index])
598 if (strcmp(vty->buf, vty->hist[index]) == 0) {
599 vty->hp = vty->hindex;
600 return;
601 }
602
603 /* Insert history entry. */
604 if (vty->hist[vty->hindex])
605 talloc_free(vty->hist[vty->hindex]);
606 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
607
608 /* History index rotation. */
609 vty->hindex++;
610 if (vty->hindex == VTY_MAXHIST)
611 vty->hindex = 0;
612
613 vty->hp = vty->hindex;
614}
615
616/* Get telnet window size. */
617static int
618vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
619{
620#ifdef TELNET_OPTION_DEBUG
621 int i;
622
623 for (i = 0; i < nbytes; i++)
624 {
625 switch (buf[i])
626 {
627 case IAC:
628 vty_out (vty, "IAC ");
629 break;
630 case WILL:
631 vty_out (vty, "WILL ");
632 break;
633 case WONT:
634 vty_out (vty, "WONT ");
635 break;
636 case DO:
637 vty_out (vty, "DO ");
638 break;
639 case DONT:
640 vty_out (vty, "DONT ");
641 break;
642 case SB:
643 vty_out (vty, "SB ");
644 break;
645 case SE:
646 vty_out (vty, "SE ");
647 break;
648 case TELOPT_ECHO:
649 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
650 break;
651 case TELOPT_SGA:
652 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
653 break;
654 case TELOPT_NAWS:
655 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
656 break;
657 default:
658 vty_out (vty, "%x ", buf[i]);
659 break;
660 }
661 }
662 vty_out (vty, "%s", VTY_NEWLINE);
663
664#endif /* TELNET_OPTION_DEBUG */
665
666 switch (buf[0])
667 {
668 case SB:
669 vty->sb_len = 0;
670 vty->iac_sb_in_progress = 1;
671 return 0;
672 break;
673 case SE:
674 {
675 if (!vty->iac_sb_in_progress)
676 return 0;
677
678 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
679 {
680 vty->iac_sb_in_progress = 0;
681 return 0;
682 }
683 switch (vty->sb_buf[0])
684 {
685 case TELOPT_NAWS:
686 if (vty->sb_len != TELNET_NAWS_SB_LEN)
687 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
688 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100689 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200690 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
691 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
692 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100693 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200694 else
695 {
696 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
697 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
698#ifdef TELNET_OPTION_DEBUG
699 vty_out(vty, "TELNET NAWS window size negotiation completed: "
700 "width %d, height %d%s",
701 vty->width, vty->height, VTY_NEWLINE);
702#endif
703 }
704 break;
705 }
706 vty->iac_sb_in_progress = 0;
707 return 0;
708 break;
709 }
710 default:
711 break;
712 }
713 return 1;
714}
715
716/* Execute current command line. */
717static int vty_execute(struct vty *vty)
718{
719 int ret;
720
721 ret = CMD_SUCCESS;
722
723 switch (vty->node) {
724 case AUTH_NODE:
725 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700726 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200727 break;
728 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700729 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200730 if (vty->type == VTY_TERM)
731 vty_hist_add(vty);
732 break;
733 }
734
735 /* Clear command line buffer. */
736 vty->cp = vty->length = 0;
737 vty_clear_buf(vty);
738
739 if (vty->status != VTY_CLOSE)
740 vty_prompt(vty);
741
742 return ret;
743}
744
745/* Send WILL TELOPT_ECHO to remote server. */
746static void
747vty_will_echo (struct vty *vty)
748{
749 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
750 vty_out (vty, "%s", cmd);
751}
752
753/* Make suppress Go-Ahead telnet option. */
754static void
755vty_will_suppress_go_ahead (struct vty *vty)
756{
757 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
758 vty_out (vty, "%s", cmd);
759}
760
761/* Make don't use linemode over telnet. */
762static void
763vty_dont_linemode (struct vty *vty)
764{
765 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
766 vty_out (vty, "%s", cmd);
767}
768
769/* Use window size. */
770static void
771vty_do_window_size (struct vty *vty)
772{
773 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
774 vty_out (vty, "%s", cmd);
775}
776
777static void vty_kill_line_from_beginning(struct vty *);
778static void vty_redraw_line(struct vty *);
779
780/* Print command line history. This function is called from
781 vty_next_line and vty_previous_line. */
782static void vty_history_print(struct vty *vty)
783{
784 int length;
785
786 vty_kill_line_from_beginning(vty);
787
788 /* Get previous line from history buffer */
789 length = strlen(vty->hist[vty->hp]);
790 memcpy(vty->buf, vty->hist[vty->hp], length);
791 vty->cp = vty->length = length;
792
793 /* Redraw current line */
794 vty_redraw_line(vty);
795}
796
797/* Show next command line history. */
798static void vty_next_line(struct vty *vty)
799{
800 int try_index;
801
802 if (vty->hp == vty->hindex)
803 return;
804
805 /* Try is there history exist or not. */
806 try_index = vty->hp;
807 if (try_index == (VTY_MAXHIST - 1))
808 try_index = 0;
809 else
810 try_index++;
811
812 /* If there is not history return. */
813 if (vty->hist[try_index] == NULL)
814 return;
815 else
816 vty->hp = try_index;
817
818 vty_history_print(vty);
819}
820
821/* Show previous command line history. */
822static void vty_previous_line(struct vty *vty)
823{
824 int try_index;
825
826 try_index = vty->hp;
827 if (try_index == 0)
828 try_index = VTY_MAXHIST - 1;
829 else
830 try_index--;
831
832 if (vty->hist[try_index] == NULL)
833 return;
834 else
835 vty->hp = try_index;
836
837 vty_history_print(vty);
838}
839
840/* This function redraw all of the command line character. */
841static void vty_redraw_line(struct vty *vty)
842{
843 vty_write(vty, vty->buf, vty->length);
844 vty->cp = vty->length;
845}
846
847/* Forward word. */
848static void vty_forward_word(struct vty *vty)
849{
850 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
851 vty_forward_char(vty);
852
853 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
854 vty_forward_char(vty);
855}
856
857/* Backward word without skipping training space. */
858static void vty_backward_pure_word(struct vty *vty)
859{
860 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
861 vty_backward_char(vty);
862}
863
864/* Backward word. */
865static void vty_backward_word(struct vty *vty)
866{
867 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
868 vty_backward_char(vty);
869
870 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
871 vty_backward_char(vty);
872}
873
874/* When '^D' is typed at the beginning of the line we move to the down
875 level. */
876static void vty_down_level(struct vty *vty)
877{
878 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100879 /* call the exit function of the specific node */
880 if (vty->node > CONFIG_NODE)
881 vty_go_parent(vty);
882 else
883 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200884 vty_prompt(vty);
885 vty->cp = 0;
886}
887
Oliver Smith053ad962021-07-22 20:24:00 +0200888/* When '^L' is typed, clear all lines above the current one. */
889static void vty_clear_screen(struct vty *vty)
890{
891 vty_out(vty, "%c%s%c%s",
892 telnet_escape_char,
893 "[2J", /* Erase Screen */
894 telnet_escape_char,
895 "[H" /* Cursor Home */
896 );
897 vty_prompt(vty);
898 vty_redraw_line(vty);
899}
900
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200901/* When '^Z' is received from vty, move down to the enable mode. */
902static void vty_end_config(struct vty *vty)
903{
904 vty_out(vty, "%s", VTY_NEWLINE);
905
906 /* FIXME: we need to call the exit function of the specific node
907 * in question, not this generic one that doesn't know all nodes */
908 switch (vty->node) {
909 case VIEW_NODE:
910 case ENABLE_NODE:
911 /* Nothing to do. */
912 break;
913 case CONFIG_NODE:
914 case VTY_NODE:
915 vty_config_unlock(vty);
916 vty->node = ENABLE_NODE;
917 break;
Harald Welte28222962011-02-18 20:37:04 +0100918 case CFG_LOG_NODE:
919 vty->node = CONFIG_NODE;
920 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200921 default:
922 /* Unknown node, we have to ignore it. */
923 break;
924 }
925
926 vty_prompt(vty);
927 vty->cp = 0;
928}
929
930/* Delete a charcter at the current point. */
931static void vty_delete_char(struct vty *vty)
932{
933 int i;
934 int size;
935
936 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
937 return;
938
939 if (vty->length == 0) {
940 vty_down_level(vty);
941 return;
942 }
943
944 if (vty->cp == vty->length)
945 return; /* completion need here? */
946
947 size = vty->length - vty->cp;
948
949 vty->length--;
950 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
951 vty->buf[vty->length] = '\0';
952
953 vty_write(vty, &vty->buf[vty->cp], size - 1);
954 vty_write(vty, &telnet_space_char, 1);
955
956 for (i = 0; i < size; i++)
957 vty_write(vty, &telnet_backward_char, 1);
958}
959
960/* Delete a character before the point. */
961static void vty_delete_backward_char(struct vty *vty)
962{
963 if (vty->cp == 0)
964 return;
965
966 vty_backward_char(vty);
967 vty_delete_char(vty);
968}
969
970/* Kill rest of line from current point. */
971static void vty_kill_line(struct vty *vty)
972{
973 int i;
974 int size;
975
976 size = vty->length - vty->cp;
977
978 if (size == 0)
979 return;
980
981 for (i = 0; i < size; i++)
982 vty_write(vty, &telnet_space_char, 1);
983 for (i = 0; i < size; i++)
984 vty_write(vty, &telnet_backward_char, 1);
985
986 memset(&vty->buf[vty->cp], 0, size);
987 vty->length = vty->cp;
988}
989
990/* Kill line from the beginning. */
991static void vty_kill_line_from_beginning(struct vty *vty)
992{
993 vty_beginning_of_line(vty);
994 vty_kill_line(vty);
995}
996
997/* Delete a word before the point. */
998static void vty_forward_kill_word(struct vty *vty)
999{
1000 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
1001 vty_delete_char(vty);
1002 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
1003 vty_delete_char(vty);
1004}
1005
1006/* Delete a word before the point. */
1007static void vty_backward_kill_word(struct vty *vty)
1008{
1009 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
1010 vty_delete_backward_char(vty);
1011 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
1012 vty_delete_backward_char(vty);
1013}
1014
1015/* Transpose chars before or at the point. */
1016static void vty_transpose_chars(struct vty *vty)
1017{
1018 char c1, c2;
1019
1020 /* If length is short or point is near by the beginning of line then
1021 return. */
1022 if (vty->length < 2 || vty->cp < 1)
1023 return;
1024
1025 /* In case of point is located at the end of the line. */
1026 if (vty->cp == vty->length) {
1027 c1 = vty->buf[vty->cp - 1];
1028 c2 = vty->buf[vty->cp - 2];
1029
1030 vty_backward_char(vty);
1031 vty_backward_char(vty);
1032 vty_self_insert_overwrite(vty, c1);
1033 vty_self_insert_overwrite(vty, c2);
1034 } else {
1035 c1 = vty->buf[vty->cp];
1036 c2 = vty->buf[vty->cp - 1];
1037
1038 vty_backward_char(vty);
1039 vty_self_insert_overwrite(vty, c1);
1040 vty_self_insert_overwrite(vty, c2);
1041 }
1042}
1043
1044/* Do completion at vty interface. */
1045static void vty_complete_command(struct vty *vty)
1046{
1047 int i;
1048 int ret;
1049 char **matched = NULL;
1050 vector vline;
1051
1052 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1053 return;
1054
1055 vline = cmd_make_strvec(vty->buf);
1056 if (vline == NULL)
1057 return;
1058
1059 /* In case of 'help \t'. */
1060 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001061 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001062
1063 matched = cmd_complete_command(vline, vty, &ret);
1064
1065 cmd_free_strvec(vline);
1066
1067 vty_out(vty, "%s", VTY_NEWLINE);
1068 switch (ret) {
1069 case CMD_ERR_AMBIGUOUS:
1070 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1071 vty_prompt(vty);
1072 vty_redraw_line(vty);
1073 break;
1074 case CMD_ERR_NO_MATCH:
1075 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1076 vty_prompt(vty);
1077 vty_redraw_line(vty);
1078 break;
1079 case CMD_COMPLETE_FULL_MATCH:
1080 vty_prompt(vty);
1081 vty_redraw_line(vty);
1082 vty_backward_pure_word(vty);
1083 vty_insert_word_overwrite(vty, matched[0]);
1084 vty_self_insert(vty, ' ');
1085 talloc_free(matched[0]);
1086 break;
1087 case CMD_COMPLETE_MATCH:
1088 vty_prompt(vty);
1089 vty_redraw_line(vty);
1090 vty_backward_pure_word(vty);
1091 vty_insert_word_overwrite(vty, matched[0]);
1092 talloc_free(matched[0]);
1093 break;
1094 case CMD_COMPLETE_LIST_MATCH:
1095 for (i = 0; matched[i] != NULL; i++) {
1096 if (i != 0 && ((i % 6) == 0))
1097 vty_out(vty, "%s", VTY_NEWLINE);
1098 vty_out(vty, "%-10s ", matched[i]);
1099 talloc_free(matched[i]);
1100 }
1101 vty_out(vty, "%s", VTY_NEWLINE);
1102
1103 vty_prompt(vty);
1104 vty_redraw_line(vty);
1105 break;
1106 case CMD_ERR_NOTHING_TODO:
1107 vty_prompt(vty);
1108 vty_redraw_line(vty);
1109 break;
1110 default:
1111 break;
1112 }
1113 if (matched)
1114 vector_only_index_free(matched);
1115}
1116
1117static void
1118vty_describe_fold(struct vty *vty, int cmd_width,
1119 unsigned int desc_width, struct desc *desc)
1120{
1121 char *buf;
1122 const char *cmd, *p;
1123 int pos;
1124
1125 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1126
1127 if (desc_width <= 0) {
1128 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1129 VTY_NEWLINE);
1130 return;
1131 }
1132
1133 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1134 if (!buf)
1135 return;
1136
1137 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1138 for (pos = desc_width; pos > 0; pos--)
1139 if (*(p + pos) == ' ')
1140 break;
1141
1142 if (pos == 0)
1143 break;
1144
1145 strncpy(buf, p, pos);
1146 buf[pos] = '\0';
1147 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1148
1149 cmd = "";
1150 }
1151
1152 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1153
1154 talloc_free(buf);
1155}
1156
1157/* Describe matched command function. */
1158static void vty_describe_command(struct vty *vty)
1159{
1160 int ret;
1161 vector vline;
1162 vector describe;
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001163 unsigned int i, cmd_width, desc_width;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001164 struct desc *desc, *desc_cr = NULL;
1165
1166 vline = cmd_make_strvec(vty->buf);
1167
1168 /* In case of '> ?'. */
1169 if (vline == NULL) {
1170 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001171 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001172 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001173 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001174
1175 describe = cmd_describe_command(vline, vty, &ret);
1176
1177 vty_out(vty, "%s", VTY_NEWLINE);
1178
1179 /* Ambiguous error. */
1180 switch (ret) {
1181 case CMD_ERR_AMBIGUOUS:
1182 cmd_free_strvec(vline);
1183 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1184 vty_prompt(vty);
1185 vty_redraw_line(vty);
1186 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001187 case CMD_ERR_NO_MATCH:
1188 cmd_free_strvec(vline);
1189 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1190 vty_prompt(vty);
1191 vty_redraw_line(vty);
1192 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001193 }
1194
1195 /* Get width of command string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001196 cmd_width = 0;
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 unsigned int len;
1200
1201 if (desc->cmd[0] == '\0')
1202 continue;
1203
1204 len = strlen(desc->cmd);
1205 if (desc->cmd[0] == '.')
1206 len--;
1207
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001208 if (cmd_width < len)
1209 cmd_width = len;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001210 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001211 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001212
1213 /* Get width of description string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001214 desc_width = vty->width - (cmd_width + 6);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001215
1216 /* Print out description. */
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001217 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001218 if ((desc = vector_slot(describe, i)) != NULL) {
1219 if (desc->cmd[0] == '\0')
1220 continue;
1221
1222 if (strcmp(desc->cmd, "<cr>") == 0) {
1223 desc_cr = desc;
1224 continue;
1225 }
1226
1227 if (!desc->str)
1228 vty_out(vty, " %-s%s",
1229 desc->cmd[0] ==
1230 '.' ? desc->cmd + 1 : desc->cmd,
1231 VTY_NEWLINE);
1232 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001233 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001234 desc->cmd[0] ==
1235 '.' ? 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#if 0
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001241 vty_out(vty, " %-*s %s%s", cmd_width
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001242 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1243 desc->str ? desc->str : "", VTY_NEWLINE);
1244#endif /* 0 */
1245 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001246 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001247
1248 if ((desc = desc_cr)) {
1249 if (!desc->str)
1250 vty_out(vty, " %-s%s",
1251 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1252 VTY_NEWLINE);
1253 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001254 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001255 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1256 desc->str, VTY_NEWLINE);
1257 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001258 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001259 }
1260
1261 cmd_free_strvec(vline);
1262 vector_free(describe);
1263
1264 vty_prompt(vty);
1265 vty_redraw_line(vty);
1266}
1267
1268/* ^C stop current input and do not add command line to the history. */
1269static void vty_stop_input(struct vty *vty)
1270{
1271 vty->cp = vty->length = 0;
1272 vty_clear_buf(vty);
1273 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001274 vty_prompt(vty);
1275
1276 /* Set history pointer to the latest one. */
1277 vty->hp = vty->hindex;
1278}
1279
1280#define CONTROL(X) ((X) - '@')
1281#define VTY_NORMAL 0
1282#define VTY_PRE_ESCAPE 1
1283#define VTY_ESCAPE 2
1284
1285/* Escape character command map. */
1286static void vty_escape_map(unsigned char c, struct vty *vty)
1287{
1288 switch (c) {
1289 case ('A'):
1290 vty_previous_line(vty);
1291 break;
1292 case ('B'):
1293 vty_next_line(vty);
1294 break;
1295 case ('C'):
1296 vty_forward_char(vty);
1297 break;
1298 case ('D'):
1299 vty_backward_char(vty);
1300 break;
1301 default:
1302 break;
1303 }
1304
1305 /* Go back to normal mode. */
1306 vty->escape = VTY_NORMAL;
1307}
1308
1309/* Quit print out to the buffer. */
1310static void vty_buffer_reset(struct vty *vty)
1311{
1312 buffer_reset(vty->obuf);
1313 vty_prompt(vty);
1314 vty_redraw_line(vty);
1315}
1316
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001317/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001318int vty_read(struct vty *vty)
1319{
1320 int i;
1321 int nbytes;
1322 unsigned char buf[VTY_READ_BUFSIZ];
1323
1324 int vty_sock = vty->fd;
1325
1326 /* Read raw data from socket */
1327 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1328 if (nbytes < 0) {
1329 if (ERRNO_IO_RETRY(errno)) {
1330 vty_event(VTY_READ, vty_sock, vty);
1331 return 0;
1332 }
1333 }
1334 buffer_reset(vty->obuf);
1335 vty->status = VTY_CLOSE;
1336 }
1337
1338 for (i = 0; i < nbytes; i++) {
1339 if (buf[i] == IAC) {
1340 if (!vty->iac) {
1341 vty->iac = 1;
1342 continue;
1343 } else {
1344 vty->iac = 0;
1345 }
1346 }
1347
1348 if (vty->iac_sb_in_progress && !vty->iac) {
1349 if (vty->sb_len < sizeof(vty->sb_buf))
1350 vty->sb_buf[vty->sb_len] = buf[i];
1351 vty->sb_len++;
1352 continue;
1353 }
1354
1355 if (vty->iac) {
1356 /* In case of telnet command */
1357 int ret = 0;
1358 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1359 vty->iac = 0;
1360 i += ret;
1361 continue;
1362 }
1363
1364 if (vty->status == VTY_MORE) {
1365 switch (buf[i]) {
1366 case CONTROL('C'):
1367 case 'q':
1368 case 'Q':
1369 vty_buffer_reset(vty);
1370 break;
1371#if 0 /* More line does not work for "show ip bgp". */
1372 case '\n':
1373 case '\r':
1374 vty->status = VTY_MORELINE;
1375 break;
1376#endif
1377 default:
1378 break;
1379 }
1380 continue;
1381 }
1382
1383 /* Escape character. */
1384 if (vty->escape == VTY_ESCAPE) {
1385 vty_escape_map(buf[i], vty);
1386 continue;
1387 }
1388
1389 /* Pre-escape status. */
1390 if (vty->escape == VTY_PRE_ESCAPE) {
1391 switch (buf[i]) {
1392 case '[':
1393 vty->escape = VTY_ESCAPE;
1394 break;
1395 case 'b':
1396 vty_backward_word(vty);
1397 vty->escape = VTY_NORMAL;
1398 break;
1399 case 'f':
1400 vty_forward_word(vty);
1401 vty->escape = VTY_NORMAL;
1402 break;
1403 case 'd':
1404 vty_forward_kill_word(vty);
1405 vty->escape = VTY_NORMAL;
1406 break;
1407 case CONTROL('H'):
1408 case 0x7f:
1409 vty_backward_kill_word(vty);
1410 vty->escape = VTY_NORMAL;
1411 break;
1412 default:
1413 vty->escape = VTY_NORMAL;
1414 break;
1415 }
1416 continue;
1417 }
1418
1419 switch (buf[i]) {
1420 case CONTROL('A'):
1421 vty_beginning_of_line(vty);
1422 break;
1423 case CONTROL('B'):
1424 vty_backward_char(vty);
1425 break;
1426 case CONTROL('C'):
1427 vty_stop_input(vty);
1428 break;
1429 case CONTROL('D'):
1430 vty_delete_char(vty);
1431 break;
1432 case CONTROL('E'):
1433 vty_end_of_line(vty);
1434 break;
1435 case CONTROL('F'):
1436 vty_forward_char(vty);
1437 break;
1438 case CONTROL('H'):
1439 case 0x7f:
1440 vty_delete_backward_char(vty);
1441 break;
1442 case CONTROL('K'):
1443 vty_kill_line(vty);
1444 break;
Oliver Smith053ad962021-07-22 20:24:00 +02001445 case CONTROL('L'):
1446 vty_clear_screen(vty);
1447 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001448 case CONTROL('N'):
1449 vty_next_line(vty);
1450 break;
1451 case CONTROL('P'):
1452 vty_previous_line(vty);
1453 break;
1454 case CONTROL('T'):
1455 vty_transpose_chars(vty);
1456 break;
1457 case CONTROL('U'):
1458 vty_kill_line_from_beginning(vty);
1459 break;
1460 case CONTROL('W'):
1461 vty_backward_kill_word(vty);
1462 break;
1463 case CONTROL('Z'):
1464 vty_end_config(vty);
1465 break;
1466 case '\n':
1467 case '\r':
1468 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001469 /* '\0'-terminate the command buffer */
1470 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001471 vty_execute(vty);
1472 break;
1473 case '\t':
1474 vty_complete_command(vty);
1475 break;
1476 case '?':
1477 if (vty->node == AUTH_NODE
1478 || vty->node == AUTH_ENABLE_NODE)
1479 vty_self_insert(vty, buf[i]);
1480 else
1481 vty_describe_command(vty);
1482 break;
1483 case '\033':
1484 if (i + 1 < nbytes && buf[i + 1] == '[') {
1485 vty->escape = VTY_ESCAPE;
1486 i++;
1487 } else
1488 vty->escape = VTY_PRE_ESCAPE;
1489 break;
1490 default:
1491 if (buf[i] > 31 && buf[i] < 127)
1492 vty_self_insert(vty, buf[i]);
1493 break;
1494 }
1495 }
1496
1497 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001498 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001499 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001500 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001501 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001502 vty_event(VTY_WRITE, vty_sock, vty);
1503 vty_event(VTY_READ, vty_sock, vty);
1504 }
1505 return 0;
1506}
1507
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001508/* Read up configuration from a file stream */
1509/*! Read up VTY configuration from a file stream
1510 * \param[in] confp file pointer of the stream for the configuration file
1511 * \param[in] priv private data to be passed to \ref vty_read_file
1512 * \returns Zero on success, non-zero on error
1513 */
1514int vty_read_config_filep(FILE *confp, void *priv)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001515{
1516 int ret;
1517 struct vty *vty;
1518
1519 vty = vty_new();
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001520 vty->type = VTY_FILE;
1521 vty->node = CONFIG_NODE;
1522 vty->priv = priv;
1523
Vadim Yanitskiyb639b4d2019-11-21 02:20:11 +07001524 /* By default, write to stderr. Otherwise, during parsing of the logging
1525 * configuration, all invocations to vty_out() would make the process
1526 * write() to its own stdin (fd=0)! */
1527 vty->fd = fileno(stderr);
1528
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001529 ret = config_from_file(vty, confp);
1530
1531 if (ret != CMD_SUCCESS) {
1532 switch (ret) {
1533 case CMD_ERR_AMBIGUOUS:
1534 fprintf(stderr, "Ambiguous command.\n");
1535 break;
1536 case CMD_ERR_NO_MATCH:
1537 fprintf(stderr, "There is no such command.\n");
1538 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001539 case CMD_ERR_INVALID_INDENT:
1540 fprintf(stderr,
1541 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1542 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1543 "allowed, but their sequence must not change within a child block.\n");
1544 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001545 }
Keith03516d62017-09-04 11:19:13 +02001546 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001547 "line:\n%s\n", vty->buf);
1548 vty_close(vty);
1549 return -EINVAL;
1550 }
1551
1552 vty_close(vty);
1553 return 0;
1554}
1555
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001556/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001557struct vty *
1558vty_create (int vty_sock, void *priv)
1559{
1560 struct vty *vty;
1561
Alexander Couzensab383e62018-07-17 19:02:34 +02001562 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001563
1564 tcgetattr(vty_sock, &t);
1565 cfmakeraw(&t);
1566 tcsetattr(vty_sock, TCSANOW, &t);
1567
1568 /* Allocate new vty structure and set up default values. */
1569 vty = vty_new ();
1570 vty->fd = vty_sock;
1571 vty->priv = priv;
1572 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001573 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001574 {
1575 if (host.advanced)
1576 vty->node = ENABLE_NODE;
1577 else
1578 vty->node = VIEW_NODE;
1579 }
1580 else
1581 vty->node = AUTH_NODE;
1582 vty->fail = 0;
1583 vty->cp = 0;
1584 vty_clear_buf (vty);
1585 vty->length = 0;
1586 memset (vty->hist, 0, sizeof (vty->hist));
1587 vty->hp = 0;
1588 vty->hindex = 0;
1589 vector_set_index (vtyvec, vty_sock, vty);
1590 vty->status = VTY_NORMAL;
1591 if (host.lines >= 0)
1592 vty->lines = host.lines;
1593 else
1594 vty->lines = -1;
1595
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001596 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001597 {
1598 /* Vty is not available if password isn't set. */
1599 if (host.password == NULL && host.password_encrypt == NULL)
1600 {
1601 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1602 vty->status = VTY_CLOSE;
1603 vty_close (vty);
1604 return NULL;
1605 }
1606 }
1607
1608 /* Say hello to the world. */
1609 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001610 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001611 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1612
1613 /* Setting up terminal. */
1614 vty_will_echo (vty);
1615 vty_will_suppress_go_ahead (vty);
1616
1617 vty_dont_linemode (vty);
1618 vty_do_window_size (vty);
1619 /* vty_dont_lflow_ahead (vty); */
1620
1621 vty_prompt (vty);
1622
1623 /* Add read/write thread. */
1624 vty_event (VTY_WRITE, vty_sock, vty);
1625 vty_event (VTY_READ, vty_sock, vty);
1626
1627 return vty;
1628}
1629
1630DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1631{
1632 unsigned int i;
1633 struct vty *v;
1634
1635 for (i = 0; i < vector_active(vtyvec); i++)
1636 if ((v = vector_slot(vtyvec, i)) != NULL)
1637 vty_out(vty, "%svty[%d] %s",
1638 v->config ? "*" : " ", i, VTY_NEWLINE);
1639 return CMD_SUCCESS;
1640}
1641
1642/* Move to vty configuration mode. */
1643DEFUN(line_vty,
1644 line_vty_cmd,
1645 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1646{
1647 vty->node = VTY_NODE;
1648 return CMD_SUCCESS;
1649}
1650
1651/* vty login. */
1652DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1653{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001654 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001655 return CMD_SUCCESS;
1656}
1657
1658DEFUN(no_vty_login,
1659 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1660{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001661 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001662 return CMD_SUCCESS;
1663}
1664
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001665/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001666DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001667 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001668 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1669 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001670{
1671 talloc_free((void*)vty_bind_addr);
1672 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001673 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001674 return CMD_SUCCESS;
1675}
1676
1677const char *vty_get_bind_addr(void)
1678{
1679 if (!vty_bind_addr)
1680 return VTY_BIND_ADDR_DEFAULT;
1681 return vty_bind_addr;
1682}
1683
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001684int vty_get_bind_port(int default_port)
1685{
1686 if (vty_bind_port >= 0)
1687 return vty_bind_port;
1688 return default_port;
1689}
1690
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001691DEFUN(service_advanced_vty,
1692 service_advanced_vty_cmd,
1693 "service advanced-vty",
1694 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1695{
1696 host.advanced = 1;
1697 return CMD_SUCCESS;
1698}
1699
1700DEFUN(no_service_advanced_vty,
1701 no_service_advanced_vty_cmd,
1702 "no service advanced-vty",
1703 NO_STR
1704 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1705{
1706 host.advanced = 0;
1707 return CMD_SUCCESS;
1708}
1709
1710DEFUN(terminal_monitor,
1711 terminal_monitor_cmd,
1712 "terminal monitor",
1713 "Set terminal line parameters\n"
1714 "Copy debug output to the current terminal line\n")
1715{
1716 vty->monitor = 1;
1717 return CMD_SUCCESS;
1718}
1719
1720DEFUN(terminal_no_monitor,
1721 terminal_no_monitor_cmd,
1722 "terminal no monitor",
1723 "Set terminal line parameters\n"
1724 NO_STR "Copy debug output to the current terminal line\n")
1725{
1726 vty->monitor = 0;
1727 return CMD_SUCCESS;
1728}
1729
1730DEFUN(show_history,
1731 show_history_cmd,
1732 "show history", SHOW_STR "Display the session command history\n")
1733{
1734 int index;
1735
1736 for (index = vty->hindex + 1; index != vty->hindex;) {
1737 if (index == VTY_MAXHIST) {
1738 index = 0;
1739 continue;
1740 }
1741
1742 if (vty->hist[index] != NULL)
1743 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1744
1745 index++;
1746 }
1747
1748 return CMD_SUCCESS;
1749}
1750
1751/* Display current configuration. */
1752static int vty_config_write(struct vty *vty)
1753{
1754 vty_out(vty, "line vty%s", VTY_NEWLINE);
1755
1756 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001757 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001758 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001759 else
1760 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001761
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001762 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001763 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1764 if (vty_bind_port >= 0) {
1765 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1766 vty_bind_port, VTY_NEWLINE);
1767 } else {
1768 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1769 }
1770 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001771
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001772 vty_out(vty, "!%s", VTY_NEWLINE);
1773
1774 return CMD_SUCCESS;
1775}
1776
1777struct cmd_node vty_node = {
1778 VTY_NODE,
1779 "%s(config-line)# ",
1780 1,
1781};
1782
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001783/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001784void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001785{
1786 unsigned int i;
1787 struct vty *vty;
1788 struct thread *vty_serv_thread;
1789
1790 for (i = 0; i < vector_active(vtyvec); i++)
1791 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1792 buffer_reset(vty->obuf);
1793 vty->status = VTY_CLOSE;
1794 vty_close(vty);
1795 }
1796
1797 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1798 if ((vty_serv_thread =
1799 vector_slot(Vvty_serv_thread, i)) != NULL) {
1800 //thread_cancel (vty_serv_thread);
1801 vector_slot(Vvty_serv_thread, i) = NULL;
1802 close(i);
1803 }
1804}
1805
1806static void vty_save_cwd(void)
1807{
1808 char cwd[MAXPATHLEN];
1809 char *c ;
1810
1811 c = getcwd(cwd, MAXPATHLEN);
1812
1813 if (!c) {
1814 if (chdir(SYSCONFDIR) != 0)
1815 perror("chdir failed");
1816 if (getcwd(cwd, MAXPATHLEN) == NULL)
1817 perror("getcwd failed");
1818 }
1819
1820 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1821 strcpy(vty_cwd, cwd);
1822}
1823
Harald Welte95b2b472011-07-16 11:58:09 +02001824char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001825{
1826 return vty_cwd;
1827}
1828
1829int vty_shell_serv(struct vty *vty)
1830{
1831 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1832}
1833
Harald Welte95b2b472011-07-16 11:58:09 +02001834void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001835{
1836 vtyvec = vector_init(VECTOR_MIN_SIZE);
1837}
1838
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001839/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001840 * \param[in] app_info application information
1841 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001842/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001843void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001844{
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001845 unsigned int i, j;
1846
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301847 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001848 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1849 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1850
1851 cmd_init(1);
1852
Harald Welte237f6242010-05-25 23:00:45 +02001853 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001854
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001855 /* Check for duplicate flags in application specific attributes (if any) */
1856 for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) {
1857 if (app_info->usr_attr_letters[i] == '\0')
1858 continue;
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001859
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07001860 /* Some flag characters are reserved for global attributes */
1861 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
1862 for (j = 0; j < ARRAY_SIZE(rafc); j++) {
1863 if (app_info->usr_attr_letters[i] != rafc[j])
1864 continue;
1865 fprintf(stderr, "Attribute flag character '%c' is reserved "
1866 "for globals! Please fix.\n", app_info->usr_attr_letters[i]);
1867 }
1868
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001869 /* Upper case flag letters are reserved for libraries */
1870 if (app_info->usr_attr_letters[i] >= 'A' &&
1871 app_info->usr_attr_letters[i] <= 'Z') {
1872 fprintf(stderr, "Attribute flag letter '%c' is reserved "
1873 "for libraries! Please fix.\n", app_info->usr_attr_letters[i]);
1874 }
1875
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001876 for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) {
1877 if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i])
1878 continue;
1879 fprintf(stderr, "Found duplicate flag letter '%c' in application "
1880 "specific attributes (index %u vs %u)! Please fix.\n",
1881 app_info->usr_attr_letters[i], i, j);
1882 }
1883 }
1884
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001885 /* For further configuration read, preserve current directory. */
1886 vty_save_cwd();
1887
1888 vtyvec = vector_init(VECTOR_MIN_SIZE);
1889
1890 /* Install bgp top node. */
1891 install_node(&vty_node, vty_config_write);
1892
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001893 install_lib_element_ve(&config_who_cmd);
1894 install_lib_element_ve(&show_history_cmd);
1895 install_lib_element(CONFIG_NODE, &line_vty_cmd);
1896 install_lib_element(CONFIG_NODE, &service_advanced_vty_cmd);
1897 install_lib_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1898 install_lib_element(CONFIG_NODE, &show_history_cmd);
1899 install_lib_element(ENABLE_NODE, &terminal_monitor_cmd);
1900 install_lib_element(ENABLE_NODE, &terminal_no_monitor_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001901
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001902 install_lib_element(VTY_NODE, &vty_login_cmd);
1903 install_lib_element(VTY_NODE, &no_vty_login_cmd);
1904 install_lib_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001905}
1906
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001907/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001908 * \param[in] file_name file name of the configuration file
1909 * \param[in] priv private data to be passed to \ref vty_read_file
1910 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001911int vty_read_config_file(const char *file_name, void *priv)
1912{
1913 FILE *cfile;
1914 int rc;
1915
1916 cfile = fopen(file_name, "r");
1917 if (!cfile)
1918 return -ENOENT;
1919
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001920 rc = vty_read_config_filep(cfile, priv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001921 fclose(cfile);
1922
1923 host_config_set(file_name);
1924
1925 return rc;
1926}
Harald Welte7acb30c2011-08-17 17:13:48 +02001927
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001928/*! @} */