blob: 76c6ef5585faf88d7b0fb32d7dcb7ff4e01014b7 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001
Harald Welted38c8b82011-08-30 11:32:56 +02002/*! \mainpage libosmovty Documentation
3 *
4 * \section sec_intro Introduction
5 * This library is a collection of common code used in various
6 * GSM related sub-projects inside the Osmocom family of projects. It
7 * has been imported/derived from the GNU Zebra project.
8 * \n\n
9 * libosmovty implements the interactive command-line on the VTY
10 * (Virtual TTY) as well as configuration file parsing.
11 * \n\n
12 * Please note that C language projects inside Osmocom are typically
13 * single-threaded event-loop state machine designs. As such,
14 * routines in libosmovty are not thread-safe. If you must use them in
15 * a multi-threaded context, you have to add your own locking.
16 *
Harald Welte96e2a002017-06-12 21:44:18 +020017 * libosmovty is developed as part of the Osmocom (Open Source Mobile
Harald Welte71658802017-06-12 15:40:52 +020018 * Communications) project, a community-based, collaborative development
19 * project to create Free and Open Source implementations of mobile
20 * communications systems. For more information about Osmocom, please
21 * see https://osmocom.org/
22 *
Harald Welted38c8b82011-08-30 11:32:56 +020023 * \section sec_copyright Copyright and License
24 * Copyright © 1997-2007 - Kuninhiro Ishiguro\n
Harald Weltee08da972017-11-13 01:00:26 +090025 * Copyright © 2008-2012 - Harald Welte, Holger Freyther and contributors\n
Harald Welted38c8b82011-08-30 11:32:56 +020026 * All rights reserved. \n\n
27 * The source code of libosmovty is licensed under the terms of the GNU
28 * General Public License as published by the Free Software Foundation;
29 * either version 2 of the License, or (at your option) any later
30 * version.\n
31 * See <http://www.gnu.org/licenses/> or COPYING included in the source
32 * code package istelf.\n
33 * The information detailed here is provided AS IS with NO WARRANTY OF
34 * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
35 * FITNESS FOR A PARTICULAR PURPOSE.
36 * \n\n
37 *
Harald Welte71658802017-06-12 15:40:52 +020038 * \section sec_tracker Homepage + Issue Tracker
39 * libosmovty is distributed as part of libosmocore and shares its
40 * project page at http://osmocom.org/projects/libosmocore
41 *
Harald Welted38c8b82011-08-30 11:32:56 +020042 * \section sec_contact Contact and Support
43 * Community-based support is available at the OpenBSC mailing list
44 * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
45 * Commercial support options available upon request from
46 * <http://sysmocom.de/>
47 */
48
Harald Weltee08da972017-11-13 01:00:26 +090049/* SPDX-License-Identifier: GPL-2.0+ */
Harald Welted38c8b82011-08-30 11:32:56 +020050
Harald Welte3fb0b6f2010-05-19 19:02:52 +020051#include <stdio.h>
52#include <stdarg.h>
53#include <stdlib.h>
54#include <unistd.h>
55#include <string.h>
56#include <errno.h>
57#include <ctype.h>
58#include <termios.h>
59
60#include <sys/utsname.h>
61#include <sys/param.h>
62
63#include <arpa/telnet.h>
64
65#include <osmocom/vty/vty.h>
66#include <osmocom/vty/command.h>
67#include <osmocom/vty/buffer.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010068#include <osmocom/core/talloc.h>
Vadim Yanitskiy024e1952020-10-02 18:23:38 +070069#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020070
Ruben Undheim766f77c2018-11-18 13:02:47 +010071#ifndef MAXPATHLEN
72 #define MAXPATHLEN 4096
73#endif
74
75
Harald Welte7acb30c2011-08-17 17:13:48 +020076/* \addtogroup vty
77 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020078 * \file vty.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020079
Harald Welte3fb0b6f2010-05-19 19:02:52 +020080#define SYSCONFDIR "/usr/local/etc"
81
82/* our callback, located in telnet_interface.c */
83void vty_event(enum event event, int sock, struct vty *vty);
84
85extern struct host host;
86
87/* Vector which store each vty structure. */
88static vector vtyvec;
89
90vector Vvty_serv_thread;
91
92char *vty_cwd = NULL;
93
Neels Hofmeyr96172f02016-02-23 14:01:41 +010094/* IP address passed to the 'line vty'/'bind' command.
95 * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
96 * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
97static const char *vty_bind_addr = NULL;
98#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +000099/* Port the VTY should bind to. -1 means not configured */
100static int vty_bind_port = -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +0100101
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200102/* Configure lock. */
103static int vty_config;
104
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -0700105static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200106
107void *tall_vty_ctx;
108
109static void vty_clear_buf(struct vty *vty)
110{
111 memset(vty->buf, 0, vty->max);
112}
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200115struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200116{
117 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
118
119 if (!new)
120 goto out;
121
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200122 INIT_LLIST_HEAD(&new->parent_nodes);
123
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200124 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
125 if (!new->obuf)
126 goto out_new;
127 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
128 if (!new->buf)
129 goto out_obuf;
130
131 new->max = VTY_BUFSIZ;
Vadim Yanitskiy54df08e2019-11-21 21:44:30 +0700132 new->fd = -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200133
134 return new;
135
136out_obuf:
137 buffer_free(new->obuf);
138out_new:
139 talloc_free(new);
140 new = NULL;
141out:
142 return new;
143}
144
145/* Authentication of vty */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700146static void vty_auth(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200147{
148 char *passwd = NULL;
149 enum node_type next_node = 0;
150 int fail;
151 char *crypt(const char *, const char *);
152
153 switch (vty->node) {
154 case AUTH_NODE:
155#ifdef VTY_CRYPT_PW
156 if (host.encrypt)
157 passwd = host.password_encrypt;
158 else
159#endif
160 passwd = host.password;
161 if (host.advanced)
162 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
163 else
164 next_node = VIEW_NODE;
165 break;
166 case AUTH_ENABLE_NODE:
167#ifdef VTY_CRYPT_PW
168 if (host.encrypt)
169 passwd = host.enable_encrypt;
170 else
171#endif
172 passwd = host.enable;
173 next_node = ENABLE_NODE;
174 break;
175 }
176
177 if (passwd) {
178#ifdef VTY_CRYPT_PW
179 if (host.encrypt)
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700180 fail = strcmp(crypt(vty->buf, passwd), passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181 else
182#endif
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700183 fail = strcmp(vty->buf, passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200184 } else
185 fail = 1;
186
187 if (!fail) {
188 vty->fail = 0;
189 vty->node = next_node; /* Success ! */
190 } else {
191 vty->fail++;
192 if (vty->fail >= 3) {
193 if (vty->node == AUTH_NODE) {
194 vty_out(vty,
195 "%% Bad passwords, too many failures!%s",
196 VTY_NEWLINE);
197 vty->status = VTY_CLOSE;
198 } else {
199 /* AUTH_ENABLE_NODE */
200 vty->fail = 0;
201 vty_out(vty,
202 "%% Bad enable passwords, too many failures!%s",
203 VTY_NEWLINE);
204 vty->node = VIEW_NODE;
205 }
206 }
207 }
208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200211void vty_close(struct vty *vty)
212{
213 int i;
214
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200215 /* VTY_CLOSED is handled by the telnet_interface */
216 vty_event(VTY_CLOSED, vty->fd, vty);
217
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200218 if (vty->obuf) {
219 /* Flush buffer. */
220 buffer_flush_all(vty->obuf, vty->fd);
221
222 /* Free input buffer. */
223 buffer_free(vty->obuf);
224 vty->obuf = NULL;
225 }
226
227 /* Free command history. */
228 for (i = 0; i < VTY_MAXHIST; i++)
229 if (vty->hist[i])
230 talloc_free(vty->hist[i]);
231
232 /* Unset vector. */
233 vector_unset(vtyvec, vty->fd);
234
Vadim Yanitskiy7a35b782019-11-21 21:51:23 +0700235 /* Close socket (ignore standard I/O streams). */
236 if (vty->fd > 2) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200237 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200238 vty->fd = -1;
239 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200240
241 if (vty->buf) {
242 talloc_free(vty->buf);
243 vty->buf = NULL;
244 }
245
246 /* Check configure. */
247 vty_config_unlock(vty);
248
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200249 /* OK free vty. */
250 talloc_free(vty);
251}
252
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200253/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200254int vty_shell(struct vty *vty)
255{
256 return vty->type == VTY_SHELL ? 1 : 0;
257}
258
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100259int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200260{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 int len = 0;
262 int size = 1024;
263 char buf[1024];
264 char *p = NULL;
265
266 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100267 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200268 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100269 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200270 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100271 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 len = vsnprintf(buf, sizeof buf, format, args);
273 va_end(args);
274
275 /* Initial buffer is not enough. */
276 if (len < 0 || len >= size) {
277 while (1) {
278 if (len > -1)
279 size = len + 1;
280 else
281 size = size * 2;
282
283 p = talloc_realloc_size(vty, p, size);
284 if (!p)
285 return -1;
286
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100287 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288 len = vsnprintf(p, size, format, args);
289 va_end(args);
290
291 if (len > -1 && len < size)
292 break;
293 }
294 }
295
296 /* When initial buffer is enough to store all output. */
297 if (!p)
298 p = buf;
299
300 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100301 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200302
303 /* If p is not different with buf, it is allocated buffer. */
304 if (p != buf)
305 talloc_free(p);
306 }
307
308 vty_event(VTY_WRITE, vty->fd, vty);
309
310 return len;
311}
312
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100313/*! VTY standard output function
314 * \param[in] vty VTY to which we should print
315 * \param[in] format variable-length format string
316 */
317int vty_out(struct vty *vty, const char *format, ...)
318{
319 va_list args;
320 int rc;
321 va_start(args, format);
322 rc = vty_out_va(vty, format, args);
323 va_end(args);
324 return rc;
325}
326
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200327/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200328int vty_out_newline(struct vty *vty)
329{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200330 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200331 buffer_put(vty->obuf, p, strlen(p));
332 return 0;
333}
334
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200335/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800336void *vty_current_index(struct vty *vty)
337{
338 return vty->index;
339}
Harald Welte7acb30c2011-08-17 17:13:48 +0200340
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200341/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800342int vty_current_node(struct vty *vty)
343{
344 return vty->node;
345}
346
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200347/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200348 * \param[in] vty VTY to which the config shall be locked
349 * \returns 1 on success, 0 on error
350 *
351 * This shall be used to make sure only one VTY at a given time has
352 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200353int vty_config_lock(struct vty *vty)
354{
355 if (vty_config == 0) {
356 vty->config = 1;
357 vty_config = 1;
358 }
359 return vty->config;
360}
361
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200362/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200363 * \param[in] vty VTY from which the configuration shall be unlocked
364 * \returns 0 in case of success
365 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200366int vty_config_unlock(struct vty *vty)
367{
368 if (vty_config == 1 && vty->config == 1) {
369 vty->config = 0;
370 vty_config = 0;
371 }
372 return vty->config;
373}
374
375/* Say hello to vty interface. */
376void vty_hello(struct vty *vty)
377{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200378 const char *app_name = "<unnamed>";
379
380 if (host.app_info->name)
381 app_name = host.app_info->name;
382
Harald Weltea2501a22018-03-23 19:33:27 +0100383 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800384 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200385
386 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200387 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200388
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200389 if (host.motdfile) {
390 FILE *f;
391 char buf[4096];
392
393 f = fopen(host.motdfile, "r");
394 if (f) {
395 while (fgets(buf, sizeof(buf), f)) {
396 char *s;
397 /* work backwards to ignore trailling isspace() */
398 for (s = buf + strlen(buf);
399 (s > buf) && isspace(*(s - 1)); s--) ;
400 *s = '\0';
401 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
402 }
403 fclose(f);
404 } else
405 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
406 } else if (host.motd)
407 vty_out(vty, "%s", host.motd);
408}
409
410/* Put out prompt and wait input from user. */
411static void vty_prompt(struct vty *vty)
412{
413 struct utsname names;
414 const char *hostname;
415
416 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100417 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 if (!hostname) {
419 uname(&names);
420 hostname = names.nodename;
421 }
422 vty_out(vty, cmd_prompt(vty->node), hostname);
423 }
424}
425
426/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700427static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200428{
429 int ret;
430 vector vline;
431
432 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700433 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200434
435 if (vline == NULL)
436 return CMD_SUCCESS;
437
438 ret = cmd_execute_command(vline, vty, NULL, 0);
439 if (ret != CMD_SUCCESS)
440 switch (ret) {
441 case CMD_WARNING:
442 if (vty->type == VTY_FILE)
443 vty_out(vty, "Warning...%s", VTY_NEWLINE);
444 break;
445 case CMD_ERR_AMBIGUOUS:
446 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
447 break;
448 case CMD_ERR_NO_MATCH:
449 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
450 break;
451 case CMD_ERR_INCOMPLETE:
452 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
453 break;
454 }
455 cmd_free_strvec(vline);
456
457 return ret;
458}
459
460static const char telnet_backward_char = 0x08;
461static const char telnet_space_char = ' ';
462
463/* Basic function to write buffer to vty. */
464static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
465{
466 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
467 return;
468
469 /* Should we do buffering here ? And make vty_flush (vty) ? */
470 buffer_put(vty->obuf, buf, nbytes);
471}
472
473/* Ensure length of input buffer. Is buffer is short, double it. */
474static void vty_ensure(struct vty *vty, int length)
475{
476 if (vty->max <= length) {
477 vty->max *= 2;
478 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
479 // FIXME: check return
480 }
481}
482
483/* Basic function to insert character into vty. */
484static void vty_self_insert(struct vty *vty, char c)
485{
486 int i;
487 int length;
488
489 vty_ensure(vty, vty->length + 1);
490 length = vty->length - vty->cp;
491 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
492 vty->buf[vty->cp] = c;
493
494 vty_write(vty, &vty->buf[vty->cp], length + 1);
495 for (i = 0; i < length; i++)
496 vty_write(vty, &telnet_backward_char, 1);
497
498 vty->cp++;
499 vty->length++;
500}
501
502/* Self insert character 'c' in overwrite mode. */
503static void vty_self_insert_overwrite(struct vty *vty, char c)
504{
505 vty_ensure(vty, vty->length + 1);
506 vty->buf[vty->cp++] = c;
507
508 if (vty->cp > vty->length)
509 vty->length++;
510
511 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
512 return;
513
514 vty_write(vty, &c, 1);
515}
516
517/* Insert a word into vty interface with overwrite mode. */
518static void vty_insert_word_overwrite(struct vty *vty, char *str)
519{
520 int len = strlen(str);
521 vty_write(vty, str, len);
522 strcpy(&vty->buf[vty->cp], str);
523 vty->cp += len;
524 vty->length = vty->cp;
525}
526
527/* Forward character. */
528static void vty_forward_char(struct vty *vty)
529{
530 if (vty->cp < vty->length) {
531 vty_write(vty, &vty->buf[vty->cp], 1);
532 vty->cp++;
533 }
534}
535
536/* Backward character. */
537static void vty_backward_char(struct vty *vty)
538{
539 if (vty->cp > 0) {
540 vty->cp--;
541 vty_write(vty, &telnet_backward_char, 1);
542 }
543}
544
545/* Move to the beginning of the line. */
546static void vty_beginning_of_line(struct vty *vty)
547{
548 while (vty->cp)
549 vty_backward_char(vty);
550}
551
552/* Move to the end of the line. */
553static void vty_end_of_line(struct vty *vty)
554{
555 while (vty->cp < vty->length)
556 vty_forward_char(vty);
557}
558
559/* Add current command line to the history buffer. */
560static void vty_hist_add(struct vty *vty)
561{
562 int index;
563
564 if (vty->length == 0)
565 return;
566
567 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
568
569 /* Ignore the same string as previous one. */
570 if (vty->hist[index])
571 if (strcmp(vty->buf, vty->hist[index]) == 0) {
572 vty->hp = vty->hindex;
573 return;
574 }
575
576 /* Insert history entry. */
577 if (vty->hist[vty->hindex])
578 talloc_free(vty->hist[vty->hindex]);
579 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
580
581 /* History index rotation. */
582 vty->hindex++;
583 if (vty->hindex == VTY_MAXHIST)
584 vty->hindex = 0;
585
586 vty->hp = vty->hindex;
587}
588
589/* Get telnet window size. */
590static int
591vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
592{
593#ifdef TELNET_OPTION_DEBUG
594 int i;
595
596 for (i = 0; i < nbytes; i++)
597 {
598 switch (buf[i])
599 {
600 case IAC:
601 vty_out (vty, "IAC ");
602 break;
603 case WILL:
604 vty_out (vty, "WILL ");
605 break;
606 case WONT:
607 vty_out (vty, "WONT ");
608 break;
609 case DO:
610 vty_out (vty, "DO ");
611 break;
612 case DONT:
613 vty_out (vty, "DONT ");
614 break;
615 case SB:
616 vty_out (vty, "SB ");
617 break;
618 case SE:
619 vty_out (vty, "SE ");
620 break;
621 case TELOPT_ECHO:
622 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
623 break;
624 case TELOPT_SGA:
625 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
626 break;
627 case TELOPT_NAWS:
628 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
629 break;
630 default:
631 vty_out (vty, "%x ", buf[i]);
632 break;
633 }
634 }
635 vty_out (vty, "%s", VTY_NEWLINE);
636
637#endif /* TELNET_OPTION_DEBUG */
638
639 switch (buf[0])
640 {
641 case SB:
642 vty->sb_len = 0;
643 vty->iac_sb_in_progress = 1;
644 return 0;
645 break;
646 case SE:
647 {
648 if (!vty->iac_sb_in_progress)
649 return 0;
650
651 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
652 {
653 vty->iac_sb_in_progress = 0;
654 return 0;
655 }
656 switch (vty->sb_buf[0])
657 {
658 case TELOPT_NAWS:
659 if (vty->sb_len != TELNET_NAWS_SB_LEN)
660 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
661 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100662 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200663 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
664 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
665 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100666 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200667 else
668 {
669 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
670 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
671#ifdef TELNET_OPTION_DEBUG
672 vty_out(vty, "TELNET NAWS window size negotiation completed: "
673 "width %d, height %d%s",
674 vty->width, vty->height, VTY_NEWLINE);
675#endif
676 }
677 break;
678 }
679 vty->iac_sb_in_progress = 0;
680 return 0;
681 break;
682 }
683 default:
684 break;
685 }
686 return 1;
687}
688
689/* Execute current command line. */
690static int vty_execute(struct vty *vty)
691{
692 int ret;
693
694 ret = CMD_SUCCESS;
695
696 switch (vty->node) {
697 case AUTH_NODE:
698 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700699 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200700 break;
701 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700702 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200703 if (vty->type == VTY_TERM)
704 vty_hist_add(vty);
705 break;
706 }
707
708 /* Clear command line buffer. */
709 vty->cp = vty->length = 0;
710 vty_clear_buf(vty);
711
712 if (vty->status != VTY_CLOSE)
713 vty_prompt(vty);
714
715 return ret;
716}
717
718/* Send WILL TELOPT_ECHO to remote server. */
719static void
720vty_will_echo (struct vty *vty)
721{
722 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
723 vty_out (vty, "%s", cmd);
724}
725
726/* Make suppress Go-Ahead telnet option. */
727static void
728vty_will_suppress_go_ahead (struct vty *vty)
729{
730 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
731 vty_out (vty, "%s", cmd);
732}
733
734/* Make don't use linemode over telnet. */
735static void
736vty_dont_linemode (struct vty *vty)
737{
738 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
739 vty_out (vty, "%s", cmd);
740}
741
742/* Use window size. */
743static void
744vty_do_window_size (struct vty *vty)
745{
746 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
747 vty_out (vty, "%s", cmd);
748}
749
750static void vty_kill_line_from_beginning(struct vty *);
751static void vty_redraw_line(struct vty *);
752
753/* Print command line history. This function is called from
754 vty_next_line and vty_previous_line. */
755static void vty_history_print(struct vty *vty)
756{
757 int length;
758
759 vty_kill_line_from_beginning(vty);
760
761 /* Get previous line from history buffer */
762 length = strlen(vty->hist[vty->hp]);
763 memcpy(vty->buf, vty->hist[vty->hp], length);
764 vty->cp = vty->length = length;
765
766 /* Redraw current line */
767 vty_redraw_line(vty);
768}
769
770/* Show next command line history. */
771static void vty_next_line(struct vty *vty)
772{
773 int try_index;
774
775 if (vty->hp == vty->hindex)
776 return;
777
778 /* Try is there history exist or not. */
779 try_index = vty->hp;
780 if (try_index == (VTY_MAXHIST - 1))
781 try_index = 0;
782 else
783 try_index++;
784
785 /* If there is not history return. */
786 if (vty->hist[try_index] == NULL)
787 return;
788 else
789 vty->hp = try_index;
790
791 vty_history_print(vty);
792}
793
794/* Show previous command line history. */
795static void vty_previous_line(struct vty *vty)
796{
797 int try_index;
798
799 try_index = vty->hp;
800 if (try_index == 0)
801 try_index = VTY_MAXHIST - 1;
802 else
803 try_index--;
804
805 if (vty->hist[try_index] == NULL)
806 return;
807 else
808 vty->hp = try_index;
809
810 vty_history_print(vty);
811}
812
813/* This function redraw all of the command line character. */
814static void vty_redraw_line(struct vty *vty)
815{
816 vty_write(vty, vty->buf, vty->length);
817 vty->cp = vty->length;
818}
819
820/* Forward word. */
821static void vty_forward_word(struct vty *vty)
822{
823 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
824 vty_forward_char(vty);
825
826 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
827 vty_forward_char(vty);
828}
829
830/* Backward word without skipping training space. */
831static void vty_backward_pure_word(struct vty *vty)
832{
833 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
834 vty_backward_char(vty);
835}
836
837/* Backward word. */
838static void vty_backward_word(struct vty *vty)
839{
840 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
841 vty_backward_char(vty);
842
843 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
844 vty_backward_char(vty);
845}
846
847/* When '^D' is typed at the beginning of the line we move to the down
848 level. */
849static void vty_down_level(struct vty *vty)
850{
851 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100852 /* call the exit function of the specific node */
853 if (vty->node > CONFIG_NODE)
854 vty_go_parent(vty);
855 else
856 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200857 vty_prompt(vty);
858 vty->cp = 0;
859}
860
861/* When '^Z' is received from vty, move down to the enable mode. */
862static void vty_end_config(struct vty *vty)
863{
864 vty_out(vty, "%s", VTY_NEWLINE);
865
866 /* FIXME: we need to call the exit function of the specific node
867 * in question, not this generic one that doesn't know all nodes */
868 switch (vty->node) {
869 case VIEW_NODE:
870 case ENABLE_NODE:
871 /* Nothing to do. */
872 break;
873 case CONFIG_NODE:
874 case VTY_NODE:
875 vty_config_unlock(vty);
876 vty->node = ENABLE_NODE;
877 break;
Harald Welte28222962011-02-18 20:37:04 +0100878 case CFG_LOG_NODE:
879 vty->node = CONFIG_NODE;
880 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200881 default:
882 /* Unknown node, we have to ignore it. */
883 break;
884 }
885
886 vty_prompt(vty);
887 vty->cp = 0;
888}
889
890/* Delete a charcter at the current point. */
891static void vty_delete_char(struct vty *vty)
892{
893 int i;
894 int size;
895
896 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
897 return;
898
899 if (vty->length == 0) {
900 vty_down_level(vty);
901 return;
902 }
903
904 if (vty->cp == vty->length)
905 return; /* completion need here? */
906
907 size = vty->length - vty->cp;
908
909 vty->length--;
910 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
911 vty->buf[vty->length] = '\0';
912
913 vty_write(vty, &vty->buf[vty->cp], size - 1);
914 vty_write(vty, &telnet_space_char, 1);
915
916 for (i = 0; i < size; i++)
917 vty_write(vty, &telnet_backward_char, 1);
918}
919
920/* Delete a character before the point. */
921static void vty_delete_backward_char(struct vty *vty)
922{
923 if (vty->cp == 0)
924 return;
925
926 vty_backward_char(vty);
927 vty_delete_char(vty);
928}
929
930/* Kill rest of line from current point. */
931static void vty_kill_line(struct vty *vty)
932{
933 int i;
934 int size;
935
936 size = vty->length - vty->cp;
937
938 if (size == 0)
939 return;
940
941 for (i = 0; i < size; i++)
942 vty_write(vty, &telnet_space_char, 1);
943 for (i = 0; i < size; i++)
944 vty_write(vty, &telnet_backward_char, 1);
945
946 memset(&vty->buf[vty->cp], 0, size);
947 vty->length = vty->cp;
948}
949
950/* Kill line from the beginning. */
951static void vty_kill_line_from_beginning(struct vty *vty)
952{
953 vty_beginning_of_line(vty);
954 vty_kill_line(vty);
955}
956
957/* Delete a word before the point. */
958static void vty_forward_kill_word(struct vty *vty)
959{
960 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
961 vty_delete_char(vty);
962 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
963 vty_delete_char(vty);
964}
965
966/* Delete a word before the point. */
967static void vty_backward_kill_word(struct vty *vty)
968{
969 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
970 vty_delete_backward_char(vty);
971 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
972 vty_delete_backward_char(vty);
973}
974
975/* Transpose chars before or at the point. */
976static void vty_transpose_chars(struct vty *vty)
977{
978 char c1, c2;
979
980 /* If length is short or point is near by the beginning of line then
981 return. */
982 if (vty->length < 2 || vty->cp < 1)
983 return;
984
985 /* In case of point is located at the end of the line. */
986 if (vty->cp == vty->length) {
987 c1 = vty->buf[vty->cp - 1];
988 c2 = vty->buf[vty->cp - 2];
989
990 vty_backward_char(vty);
991 vty_backward_char(vty);
992 vty_self_insert_overwrite(vty, c1);
993 vty_self_insert_overwrite(vty, c2);
994 } else {
995 c1 = vty->buf[vty->cp];
996 c2 = vty->buf[vty->cp - 1];
997
998 vty_backward_char(vty);
999 vty_self_insert_overwrite(vty, c1);
1000 vty_self_insert_overwrite(vty, c2);
1001 }
1002}
1003
1004/* Do completion at vty interface. */
1005static void vty_complete_command(struct vty *vty)
1006{
1007 int i;
1008 int ret;
1009 char **matched = NULL;
1010 vector vline;
1011
1012 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1013 return;
1014
1015 vline = cmd_make_strvec(vty->buf);
1016 if (vline == NULL)
1017 return;
1018
1019 /* In case of 'help \t'. */
1020 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001021 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022
1023 matched = cmd_complete_command(vline, vty, &ret);
1024
1025 cmd_free_strvec(vline);
1026
1027 vty_out(vty, "%s", VTY_NEWLINE);
1028 switch (ret) {
1029 case CMD_ERR_AMBIGUOUS:
1030 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1031 vty_prompt(vty);
1032 vty_redraw_line(vty);
1033 break;
1034 case CMD_ERR_NO_MATCH:
1035 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1036 vty_prompt(vty);
1037 vty_redraw_line(vty);
1038 break;
1039 case CMD_COMPLETE_FULL_MATCH:
1040 vty_prompt(vty);
1041 vty_redraw_line(vty);
1042 vty_backward_pure_word(vty);
1043 vty_insert_word_overwrite(vty, matched[0]);
1044 vty_self_insert(vty, ' ');
1045 talloc_free(matched[0]);
1046 break;
1047 case CMD_COMPLETE_MATCH:
1048 vty_prompt(vty);
1049 vty_redraw_line(vty);
1050 vty_backward_pure_word(vty);
1051 vty_insert_word_overwrite(vty, matched[0]);
1052 talloc_free(matched[0]);
1053 break;
1054 case CMD_COMPLETE_LIST_MATCH:
1055 for (i = 0; matched[i] != NULL; i++) {
1056 if (i != 0 && ((i % 6) == 0))
1057 vty_out(vty, "%s", VTY_NEWLINE);
1058 vty_out(vty, "%-10s ", matched[i]);
1059 talloc_free(matched[i]);
1060 }
1061 vty_out(vty, "%s", VTY_NEWLINE);
1062
1063 vty_prompt(vty);
1064 vty_redraw_line(vty);
1065 break;
1066 case CMD_ERR_NOTHING_TODO:
1067 vty_prompt(vty);
1068 vty_redraw_line(vty);
1069 break;
1070 default:
1071 break;
1072 }
1073 if (matched)
1074 vector_only_index_free(matched);
1075}
1076
1077static void
1078vty_describe_fold(struct vty *vty, int cmd_width,
1079 unsigned int desc_width, struct desc *desc)
1080{
1081 char *buf;
1082 const char *cmd, *p;
1083 int pos;
1084
1085 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1086
1087 if (desc_width <= 0) {
1088 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1089 VTY_NEWLINE);
1090 return;
1091 }
1092
1093 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1094 if (!buf)
1095 return;
1096
1097 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1098 for (pos = desc_width; pos > 0; pos--)
1099 if (*(p + pos) == ' ')
1100 break;
1101
1102 if (pos == 0)
1103 break;
1104
1105 strncpy(buf, p, pos);
1106 buf[pos] = '\0';
1107 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1108
1109 cmd = "";
1110 }
1111
1112 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1113
1114 talloc_free(buf);
1115}
1116
1117/* Describe matched command function. */
1118static void vty_describe_command(struct vty *vty)
1119{
1120 int ret;
1121 vector vline;
1122 vector describe;
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001123 unsigned int i, cmd_width, desc_width;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001124 struct desc *desc, *desc_cr = NULL;
1125
1126 vline = cmd_make_strvec(vty->buf);
1127
1128 /* In case of '> ?'. */
1129 if (vline == NULL) {
1130 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001131 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001133 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001134
1135 describe = cmd_describe_command(vline, vty, &ret);
1136
1137 vty_out(vty, "%s", VTY_NEWLINE);
1138
1139 /* Ambiguous error. */
1140 switch (ret) {
1141 case CMD_ERR_AMBIGUOUS:
1142 cmd_free_strvec(vline);
1143 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1144 vty_prompt(vty);
1145 vty_redraw_line(vty);
1146 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001147 case CMD_ERR_NO_MATCH:
1148 cmd_free_strvec(vline);
1149 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1150 vty_prompt(vty);
1151 vty_redraw_line(vty);
1152 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001153 }
1154
1155 /* Get width of command string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001156 cmd_width = 0;
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001157 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001158 if ((desc = vector_slot(describe, i)) != NULL) {
1159 unsigned int len;
1160
1161 if (desc->cmd[0] == '\0')
1162 continue;
1163
1164 len = strlen(desc->cmd);
1165 if (desc->cmd[0] == '.')
1166 len--;
1167
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001168 if (cmd_width < len)
1169 cmd_width = len;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001171 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001172
1173 /* Get width of description string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001174 desc_width = vty->width - (cmd_width + 6);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001175
1176 /* Print out description. */
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 if (desc->cmd[0] == '\0')
1180 continue;
1181
1182 if (strcmp(desc->cmd, "<cr>") == 0) {
1183 desc_cr = desc;
1184 continue;
1185 }
1186
1187 if (!desc->str)
1188 vty_out(vty, " %-s%s",
1189 desc->cmd[0] ==
1190 '.' ? desc->cmd + 1 : desc->cmd,
1191 VTY_NEWLINE);
1192 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001193 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001194 desc->cmd[0] ==
1195 '.' ? desc->cmd + 1 : desc->cmd,
1196 desc->str, VTY_NEWLINE);
1197 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001198 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001199
1200#if 0
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001201 vty_out(vty, " %-*s %s%s", cmd_width
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001202 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1203 desc->str ? desc->str : "", VTY_NEWLINE);
1204#endif /* 0 */
1205 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001206 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 if ((desc = desc_cr)) {
1209 if (!desc->str)
1210 vty_out(vty, " %-s%s",
1211 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1212 VTY_NEWLINE);
1213 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001214 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001215 desc->cmd[0] == '.' ? 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
1221 cmd_free_strvec(vline);
1222 vector_free(describe);
1223
1224 vty_prompt(vty);
1225 vty_redraw_line(vty);
1226}
1227
1228/* ^C stop current input and do not add command line to the history. */
1229static void vty_stop_input(struct vty *vty)
1230{
1231 vty->cp = vty->length = 0;
1232 vty_clear_buf(vty);
1233 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001234 vty_prompt(vty);
1235
1236 /* Set history pointer to the latest one. */
1237 vty->hp = vty->hindex;
1238}
1239
1240#define CONTROL(X) ((X) - '@')
1241#define VTY_NORMAL 0
1242#define VTY_PRE_ESCAPE 1
1243#define VTY_ESCAPE 2
1244
1245/* Escape character command map. */
1246static void vty_escape_map(unsigned char c, struct vty *vty)
1247{
1248 switch (c) {
1249 case ('A'):
1250 vty_previous_line(vty);
1251 break;
1252 case ('B'):
1253 vty_next_line(vty);
1254 break;
1255 case ('C'):
1256 vty_forward_char(vty);
1257 break;
1258 case ('D'):
1259 vty_backward_char(vty);
1260 break;
1261 default:
1262 break;
1263 }
1264
1265 /* Go back to normal mode. */
1266 vty->escape = VTY_NORMAL;
1267}
1268
1269/* Quit print out to the buffer. */
1270static void vty_buffer_reset(struct vty *vty)
1271{
1272 buffer_reset(vty->obuf);
1273 vty_prompt(vty);
1274 vty_redraw_line(vty);
1275}
1276
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001277/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278int vty_read(struct vty *vty)
1279{
1280 int i;
1281 int nbytes;
1282 unsigned char buf[VTY_READ_BUFSIZ];
1283
1284 int vty_sock = vty->fd;
1285
1286 /* Read raw data from socket */
1287 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1288 if (nbytes < 0) {
1289 if (ERRNO_IO_RETRY(errno)) {
1290 vty_event(VTY_READ, vty_sock, vty);
1291 return 0;
1292 }
1293 }
1294 buffer_reset(vty->obuf);
1295 vty->status = VTY_CLOSE;
1296 }
1297
1298 for (i = 0; i < nbytes; i++) {
1299 if (buf[i] == IAC) {
1300 if (!vty->iac) {
1301 vty->iac = 1;
1302 continue;
1303 } else {
1304 vty->iac = 0;
1305 }
1306 }
1307
1308 if (vty->iac_sb_in_progress && !vty->iac) {
1309 if (vty->sb_len < sizeof(vty->sb_buf))
1310 vty->sb_buf[vty->sb_len] = buf[i];
1311 vty->sb_len++;
1312 continue;
1313 }
1314
1315 if (vty->iac) {
1316 /* In case of telnet command */
1317 int ret = 0;
1318 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1319 vty->iac = 0;
1320 i += ret;
1321 continue;
1322 }
1323
1324 if (vty->status == VTY_MORE) {
1325 switch (buf[i]) {
1326 case CONTROL('C'):
1327 case 'q':
1328 case 'Q':
1329 vty_buffer_reset(vty);
1330 break;
1331#if 0 /* More line does not work for "show ip bgp". */
1332 case '\n':
1333 case '\r':
1334 vty->status = VTY_MORELINE;
1335 break;
1336#endif
1337 default:
1338 break;
1339 }
1340 continue;
1341 }
1342
1343 /* Escape character. */
1344 if (vty->escape == VTY_ESCAPE) {
1345 vty_escape_map(buf[i], vty);
1346 continue;
1347 }
1348
1349 /* Pre-escape status. */
1350 if (vty->escape == VTY_PRE_ESCAPE) {
1351 switch (buf[i]) {
1352 case '[':
1353 vty->escape = VTY_ESCAPE;
1354 break;
1355 case 'b':
1356 vty_backward_word(vty);
1357 vty->escape = VTY_NORMAL;
1358 break;
1359 case 'f':
1360 vty_forward_word(vty);
1361 vty->escape = VTY_NORMAL;
1362 break;
1363 case 'd':
1364 vty_forward_kill_word(vty);
1365 vty->escape = VTY_NORMAL;
1366 break;
1367 case CONTROL('H'):
1368 case 0x7f:
1369 vty_backward_kill_word(vty);
1370 vty->escape = VTY_NORMAL;
1371 break;
1372 default:
1373 vty->escape = VTY_NORMAL;
1374 break;
1375 }
1376 continue;
1377 }
1378
1379 switch (buf[i]) {
1380 case CONTROL('A'):
1381 vty_beginning_of_line(vty);
1382 break;
1383 case CONTROL('B'):
1384 vty_backward_char(vty);
1385 break;
1386 case CONTROL('C'):
1387 vty_stop_input(vty);
1388 break;
1389 case CONTROL('D'):
1390 vty_delete_char(vty);
1391 break;
1392 case CONTROL('E'):
1393 vty_end_of_line(vty);
1394 break;
1395 case CONTROL('F'):
1396 vty_forward_char(vty);
1397 break;
1398 case CONTROL('H'):
1399 case 0x7f:
1400 vty_delete_backward_char(vty);
1401 break;
1402 case CONTROL('K'):
1403 vty_kill_line(vty);
1404 break;
1405 case CONTROL('N'):
1406 vty_next_line(vty);
1407 break;
1408 case CONTROL('P'):
1409 vty_previous_line(vty);
1410 break;
1411 case CONTROL('T'):
1412 vty_transpose_chars(vty);
1413 break;
1414 case CONTROL('U'):
1415 vty_kill_line_from_beginning(vty);
1416 break;
1417 case CONTROL('W'):
1418 vty_backward_kill_word(vty);
1419 break;
1420 case CONTROL('Z'):
1421 vty_end_config(vty);
1422 break;
1423 case '\n':
1424 case '\r':
1425 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001426 /* '\0'-terminate the command buffer */
1427 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428 vty_execute(vty);
1429 break;
1430 case '\t':
1431 vty_complete_command(vty);
1432 break;
1433 case '?':
1434 if (vty->node == AUTH_NODE
1435 || vty->node == AUTH_ENABLE_NODE)
1436 vty_self_insert(vty, buf[i]);
1437 else
1438 vty_describe_command(vty);
1439 break;
1440 case '\033':
1441 if (i + 1 < nbytes && buf[i + 1] == '[') {
1442 vty->escape = VTY_ESCAPE;
1443 i++;
1444 } else
1445 vty->escape = VTY_PRE_ESCAPE;
1446 break;
1447 default:
1448 if (buf[i] > 31 && buf[i] < 127)
1449 vty_self_insert(vty, buf[i]);
1450 break;
1451 }
1452 }
1453
1454 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001455 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001456 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001457 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001458 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459 vty_event(VTY_WRITE, vty_sock, vty);
1460 vty_event(VTY_READ, vty_sock, vty);
1461 }
1462 return 0;
1463}
1464
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001465/* Read up configuration from a file stream */
1466/*! Read up VTY configuration from a file stream
1467 * \param[in] confp file pointer of the stream for the configuration file
1468 * \param[in] priv private data to be passed to \ref vty_read_file
1469 * \returns Zero on success, non-zero on error
1470 */
1471int vty_read_config_filep(FILE *confp, void *priv)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001472{
1473 int ret;
1474 struct vty *vty;
1475
1476 vty = vty_new();
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001477 vty->type = VTY_FILE;
1478 vty->node = CONFIG_NODE;
1479 vty->priv = priv;
1480
Vadim Yanitskiyb639b4d2019-11-21 02:20:11 +07001481 /* By default, write to stderr. Otherwise, during parsing of the logging
1482 * configuration, all invocations to vty_out() would make the process
1483 * write() to its own stdin (fd=0)! */
1484 vty->fd = fileno(stderr);
1485
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001486 ret = config_from_file(vty, confp);
1487
1488 if (ret != CMD_SUCCESS) {
1489 switch (ret) {
1490 case CMD_ERR_AMBIGUOUS:
1491 fprintf(stderr, "Ambiguous command.\n");
1492 break;
1493 case CMD_ERR_NO_MATCH:
1494 fprintf(stderr, "There is no such command.\n");
1495 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001496 case CMD_ERR_INVALID_INDENT:
1497 fprintf(stderr,
1498 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1499 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1500 "allowed, but their sequence must not change within a child block.\n");
1501 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001502 }
Keith03516d62017-09-04 11:19:13 +02001503 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001504 "line:\n%s\n", vty->buf);
1505 vty_close(vty);
1506 return -EINVAL;
1507 }
1508
1509 vty_close(vty);
1510 return 0;
1511}
1512
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001513/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001514struct vty *
1515vty_create (int vty_sock, void *priv)
1516{
1517 struct vty *vty;
1518
Alexander Couzensab383e62018-07-17 19:02:34 +02001519 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001520
1521 tcgetattr(vty_sock, &t);
1522 cfmakeraw(&t);
1523 tcsetattr(vty_sock, TCSANOW, &t);
1524
1525 /* Allocate new vty structure and set up default values. */
1526 vty = vty_new ();
1527 vty->fd = vty_sock;
1528 vty->priv = priv;
1529 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001530 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001531 {
1532 if (host.advanced)
1533 vty->node = ENABLE_NODE;
1534 else
1535 vty->node = VIEW_NODE;
1536 }
1537 else
1538 vty->node = AUTH_NODE;
1539 vty->fail = 0;
1540 vty->cp = 0;
1541 vty_clear_buf (vty);
1542 vty->length = 0;
1543 memset (vty->hist, 0, sizeof (vty->hist));
1544 vty->hp = 0;
1545 vty->hindex = 0;
1546 vector_set_index (vtyvec, vty_sock, vty);
1547 vty->status = VTY_NORMAL;
1548 if (host.lines >= 0)
1549 vty->lines = host.lines;
1550 else
1551 vty->lines = -1;
1552
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001553 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001554 {
1555 /* Vty is not available if password isn't set. */
1556 if (host.password == NULL && host.password_encrypt == NULL)
1557 {
1558 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1559 vty->status = VTY_CLOSE;
1560 vty_close (vty);
1561 return NULL;
1562 }
1563 }
1564
1565 /* Say hello to the world. */
1566 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001567 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001568 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1569
1570 /* Setting up terminal. */
1571 vty_will_echo (vty);
1572 vty_will_suppress_go_ahead (vty);
1573
1574 vty_dont_linemode (vty);
1575 vty_do_window_size (vty);
1576 /* vty_dont_lflow_ahead (vty); */
1577
1578 vty_prompt (vty);
1579
1580 /* Add read/write thread. */
1581 vty_event (VTY_WRITE, vty_sock, vty);
1582 vty_event (VTY_READ, vty_sock, vty);
1583
1584 return vty;
1585}
1586
1587DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1588{
1589 unsigned int i;
1590 struct vty *v;
1591
1592 for (i = 0; i < vector_active(vtyvec); i++)
1593 if ((v = vector_slot(vtyvec, i)) != NULL)
1594 vty_out(vty, "%svty[%d] %s",
1595 v->config ? "*" : " ", i, VTY_NEWLINE);
1596 return CMD_SUCCESS;
1597}
1598
1599/* Move to vty configuration mode. */
1600DEFUN(line_vty,
1601 line_vty_cmd,
1602 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1603{
1604 vty->node = VTY_NODE;
1605 return CMD_SUCCESS;
1606}
1607
1608/* vty login. */
1609DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1610{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001611 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001612 return CMD_SUCCESS;
1613}
1614
1615DEFUN(no_vty_login,
1616 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1617{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001618 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001619 return CMD_SUCCESS;
1620}
1621
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001622/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001623DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001624 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001625 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1626 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001627{
1628 talloc_free((void*)vty_bind_addr);
1629 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001630 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001631 return CMD_SUCCESS;
1632}
1633
1634const char *vty_get_bind_addr(void)
1635{
1636 if (!vty_bind_addr)
1637 return VTY_BIND_ADDR_DEFAULT;
1638 return vty_bind_addr;
1639}
1640
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001641int vty_get_bind_port(int default_port)
1642{
1643 if (vty_bind_port >= 0)
1644 return vty_bind_port;
1645 return default_port;
1646}
1647
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001648DEFUN(service_advanced_vty,
1649 service_advanced_vty_cmd,
1650 "service advanced-vty",
1651 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1652{
1653 host.advanced = 1;
1654 return CMD_SUCCESS;
1655}
1656
1657DEFUN(no_service_advanced_vty,
1658 no_service_advanced_vty_cmd,
1659 "no service advanced-vty",
1660 NO_STR
1661 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1662{
1663 host.advanced = 0;
1664 return CMD_SUCCESS;
1665}
1666
1667DEFUN(terminal_monitor,
1668 terminal_monitor_cmd,
1669 "terminal monitor",
1670 "Set terminal line parameters\n"
1671 "Copy debug output to the current terminal line\n")
1672{
1673 vty->monitor = 1;
1674 return CMD_SUCCESS;
1675}
1676
1677DEFUN(terminal_no_monitor,
1678 terminal_no_monitor_cmd,
1679 "terminal no monitor",
1680 "Set terminal line parameters\n"
1681 NO_STR "Copy debug output to the current terminal line\n")
1682{
1683 vty->monitor = 0;
1684 return CMD_SUCCESS;
1685}
1686
1687DEFUN(show_history,
1688 show_history_cmd,
1689 "show history", SHOW_STR "Display the session command history\n")
1690{
1691 int index;
1692
1693 for (index = vty->hindex + 1; index != vty->hindex;) {
1694 if (index == VTY_MAXHIST) {
1695 index = 0;
1696 continue;
1697 }
1698
1699 if (vty->hist[index] != NULL)
1700 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1701
1702 index++;
1703 }
1704
1705 return CMD_SUCCESS;
1706}
1707
1708/* Display current configuration. */
1709static int vty_config_write(struct vty *vty)
1710{
1711 vty_out(vty, "line vty%s", VTY_NEWLINE);
1712
1713 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001714 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001715 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001716 else
1717 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001718
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001719 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001720 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1721 if (vty_bind_port >= 0) {
1722 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1723 vty_bind_port, VTY_NEWLINE);
1724 } else {
1725 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1726 }
1727 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001728
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001729 vty_out(vty, "!%s", VTY_NEWLINE);
1730
1731 return CMD_SUCCESS;
1732}
1733
1734struct cmd_node vty_node = {
1735 VTY_NODE,
1736 "%s(config-line)# ",
1737 1,
1738};
1739
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001740/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001741void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001742{
1743 unsigned int i;
1744 struct vty *vty;
1745 struct thread *vty_serv_thread;
1746
1747 for (i = 0; i < vector_active(vtyvec); i++)
1748 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1749 buffer_reset(vty->obuf);
1750 vty->status = VTY_CLOSE;
1751 vty_close(vty);
1752 }
1753
1754 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1755 if ((vty_serv_thread =
1756 vector_slot(Vvty_serv_thread, i)) != NULL) {
1757 //thread_cancel (vty_serv_thread);
1758 vector_slot(Vvty_serv_thread, i) = NULL;
1759 close(i);
1760 }
1761}
1762
1763static void vty_save_cwd(void)
1764{
1765 char cwd[MAXPATHLEN];
1766 char *c ;
1767
1768 c = getcwd(cwd, MAXPATHLEN);
1769
1770 if (!c) {
1771 if (chdir(SYSCONFDIR) != 0)
1772 perror("chdir failed");
1773 if (getcwd(cwd, MAXPATHLEN) == NULL)
1774 perror("getcwd failed");
1775 }
1776
1777 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1778 strcpy(vty_cwd, cwd);
1779}
1780
Harald Welte95b2b472011-07-16 11:58:09 +02001781char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001782{
1783 return vty_cwd;
1784}
1785
1786int vty_shell_serv(struct vty *vty)
1787{
1788 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1789}
1790
Harald Welte95b2b472011-07-16 11:58:09 +02001791void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001792{
1793 vtyvec = vector_init(VECTOR_MIN_SIZE);
1794}
1795
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001796/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001797 * \param[in] app_info application information
1798 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001799/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001800void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001801{
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001802 unsigned int i, j;
1803
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301804 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001805 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1806 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1807
1808 cmd_init(1);
1809
Harald Welte237f6242010-05-25 23:00:45 +02001810 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001811
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001812 /* Check for duplicate flags in application specific attributes (if any) */
1813 for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) {
1814 if (app_info->usr_attr_letters[i] == '\0')
1815 continue;
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001816
Vadim Yanitskiyef4c5972020-10-07 13:44:31 +07001817 /* Some flag characters are reserved for global attributes */
1818 const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
1819 for (j = 0; j < ARRAY_SIZE(rafc); j++) {
1820 if (app_info->usr_attr_letters[i] != rafc[j])
1821 continue;
1822 fprintf(stderr, "Attribute flag character '%c' is reserved "
1823 "for globals! Please fix.\n", app_info->usr_attr_letters[i]);
1824 }
1825
Vadim Yanitskiyf94355d2020-10-03 17:43:37 +07001826 /* Upper case flag letters are reserved for libraries */
1827 if (app_info->usr_attr_letters[i] >= 'A' &&
1828 app_info->usr_attr_letters[i] <= 'Z') {
1829 fprintf(stderr, "Attribute flag letter '%c' is reserved "
1830 "for libraries! Please fix.\n", app_info->usr_attr_letters[i]);
1831 }
1832
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001833 for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) {
1834 if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i])
1835 continue;
1836 fprintf(stderr, "Found duplicate flag letter '%c' in application "
1837 "specific attributes (index %u vs %u)! Please fix.\n",
1838 app_info->usr_attr_letters[i], i, j);
1839 }
1840 }
1841
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001842 /* For further configuration read, preserve current directory. */
1843 vty_save_cwd();
1844
1845 vtyvec = vector_init(VECTOR_MIN_SIZE);
1846
1847 /* Install bgp top node. */
1848 install_node(&vty_node, vty_config_write);
1849
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001850 install_lib_element_ve(&config_who_cmd);
1851 install_lib_element_ve(&show_history_cmd);
1852 install_lib_element(CONFIG_NODE, &line_vty_cmd);
1853 install_lib_element(CONFIG_NODE, &service_advanced_vty_cmd);
1854 install_lib_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1855 install_lib_element(CONFIG_NODE, &show_history_cmd);
1856 install_lib_element(ENABLE_NODE, &terminal_monitor_cmd);
1857 install_lib_element(ENABLE_NODE, &terminal_no_monitor_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001858
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +07001859 install_lib_element(VTY_NODE, &vty_login_cmd);
1860 install_lib_element(VTY_NODE, &no_vty_login_cmd);
1861 install_lib_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001862}
1863
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001864/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001865 * \param[in] file_name file name of the configuration file
1866 * \param[in] priv private data to be passed to \ref vty_read_file
1867 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001868int vty_read_config_file(const char *file_name, void *priv)
1869{
1870 FILE *cfile;
1871 int rc;
1872
1873 cfile = fopen(file_name, "r");
1874 if (!cfile)
1875 return -ENOENT;
1876
Pau Espin Pedrol645aec82021-05-18 14:46:29 +02001877 rc = vty_read_config_filep(cfile, priv);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001878 fclose(cfile);
1879
1880 host_config_set(file_name);
1881
1882 return rc;
1883}
Harald Welte7acb30c2011-08-17 17:13:48 +02001884
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001885/*! @} */