blob: babe0ef6dd83b1233c3461c01954dc9a2a10aad4 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001
Harald Welted38c8b82011-08-30 11:32:56 +02002/*! \mainpage libosmovty Documentation
3 *
4 * \section sec_intro Introduction
5 * This library is a collection of common code used in various
6 * GSM related sub-projects inside the Osmocom family of projects. It
7 * has been imported/derived from the GNU Zebra project.
8 * \n\n
9 * libosmovty implements the interactive command-line on the VTY
10 * (Virtual TTY) as well as configuration file parsing.
11 * \n\n
12 * Please note that C language projects inside Osmocom are typically
13 * single-threaded event-loop state machine designs. As such,
14 * routines in libosmovty are not thread-safe. If you must use them in
15 * a multi-threaded context, you have to add your own locking.
16 *
Harald Welte96e2a002017-06-12 21:44:18 +020017 * libosmovty is developed as part of the Osmocom (Open Source Mobile
Harald Welte71658802017-06-12 15:40:52 +020018 * Communications) project, a community-based, collaborative development
19 * project to create Free and Open Source implementations of mobile
20 * communications systems. For more information about Osmocom, please
21 * see https://osmocom.org/
22 *
Harald Welted38c8b82011-08-30 11:32:56 +020023 * \section sec_copyright Copyright and License
24 * Copyright © 1997-2007 - Kuninhiro Ishiguro\n
Harald Weltee08da972017-11-13 01:00:26 +090025 * Copyright © 2008-2012 - Harald Welte, Holger Freyther and contributors\n
Harald Welted38c8b82011-08-30 11:32:56 +020026 * All rights reserved. \n\n
27 * The source code of libosmovty is licensed under the terms of the GNU
28 * General Public License as published by the Free Software Foundation;
29 * either version 2 of the License, or (at your option) any later
30 * version.\n
31 * See <http://www.gnu.org/licenses/> or COPYING included in the source
32 * code package istelf.\n
33 * The information detailed here is provided AS IS with NO WARRANTY OF
34 * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
35 * FITNESS FOR A PARTICULAR PURPOSE.
36 * \n\n
37 *
Harald Welte71658802017-06-12 15:40:52 +020038 * \section sec_tracker Homepage + Issue Tracker
39 * libosmovty is distributed as part of libosmocore and shares its
40 * project page at http://osmocom.org/projects/libosmocore
41 *
Harald Welted38c8b82011-08-30 11:32:56 +020042 * \section sec_contact Contact and Support
43 * Community-based support is available at the OpenBSC mailing list
44 * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
45 * Commercial support options available upon request from
46 * <http://sysmocom.de/>
47 */
48
Harald Weltee08da972017-11-13 01:00:26 +090049/* SPDX-License-Identifier: GPL-2.0+ */
Harald Welted38c8b82011-08-30 11:32:56 +020050
Harald Welte3fb0b6f2010-05-19 19:02:52 +020051#include <stdio.h>
52#include <stdarg.h>
53#include <stdlib.h>
54#include <unistd.h>
55#include <string.h>
56#include <errno.h>
57#include <ctype.h>
58#include <termios.h>
59
60#include <sys/utsname.h>
61#include <sys/param.h>
62
63#include <arpa/telnet.h>
64
65#include <osmocom/vty/vty.h>
66#include <osmocom/vty/command.h>
67#include <osmocom/vty/buffer.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010068#include <osmocom/core/talloc.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020069
Ruben Undheim766f77c2018-11-18 13:02:47 +010070#ifndef MAXPATHLEN
71 #define MAXPATHLEN 4096
72#endif
73
74
Harald Welte7acb30c2011-08-17 17:13:48 +020075/* \addtogroup vty
76 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020077 * \file vty.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020078
Harald Welte3fb0b6f2010-05-19 19:02:52 +020079#define SYSCONFDIR "/usr/local/etc"
80
81/* our callback, located in telnet_interface.c */
82void vty_event(enum event event, int sock, struct vty *vty);
83
84extern struct host host;
85
86/* Vector which store each vty structure. */
87static vector vtyvec;
88
89vector Vvty_serv_thread;
90
91char *vty_cwd = NULL;
92
Neels Hofmeyr96172f02016-02-23 14:01:41 +010093/* IP address passed to the 'line vty'/'bind' command.
94 * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
95 * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
96static const char *vty_bind_addr = NULL;
97#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +000098/* Port the VTY should bind to. -1 means not configured */
99static int vty_bind_port = -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +0100100
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200101/* Configure lock. */
102static int vty_config;
103
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -0700104static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200105
106void *tall_vty_ctx;
107
108static void vty_clear_buf(struct vty *vty)
109{
110 memset(vty->buf, 0, vty->max);
111}
112
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200113/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200114struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200115{
116 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
117
118 if (!new)
119 goto out;
120
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200121 INIT_LLIST_HEAD(&new->parent_nodes);
122
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200123 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
124 if (!new->obuf)
125 goto out_new;
126 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
127 if (!new->buf)
128 goto out_obuf;
129
130 new->max = VTY_BUFSIZ;
131
132 return new;
133
134out_obuf:
135 buffer_free(new->obuf);
136out_new:
137 talloc_free(new);
138 new = NULL;
139out:
140 return new;
141}
142
143/* Authentication of vty */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700144static void vty_auth(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200145{
146 char *passwd = NULL;
147 enum node_type next_node = 0;
148 int fail;
149 char *crypt(const char *, const char *);
150
151 switch (vty->node) {
152 case AUTH_NODE:
153#ifdef VTY_CRYPT_PW
154 if (host.encrypt)
155 passwd = host.password_encrypt;
156 else
157#endif
158 passwd = host.password;
159 if (host.advanced)
160 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
161 else
162 next_node = VIEW_NODE;
163 break;
164 case AUTH_ENABLE_NODE:
165#ifdef VTY_CRYPT_PW
166 if (host.encrypt)
167 passwd = host.enable_encrypt;
168 else
169#endif
170 passwd = host.enable;
171 next_node = ENABLE_NODE;
172 break;
173 }
174
175 if (passwd) {
176#ifdef VTY_CRYPT_PW
177 if (host.encrypt)
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700178 fail = strcmp(crypt(vty->buf, passwd), passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200179 else
180#endif
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700181 fail = strcmp(vty->buf, passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200182 } else
183 fail = 1;
184
185 if (!fail) {
186 vty->fail = 0;
187 vty->node = next_node; /* Success ! */
188 } else {
189 vty->fail++;
190 if (vty->fail >= 3) {
191 if (vty->node == AUTH_NODE) {
192 vty_out(vty,
193 "%% Bad passwords, too many failures!%s",
194 VTY_NEWLINE);
195 vty->status = VTY_CLOSE;
196 } else {
197 /* AUTH_ENABLE_NODE */
198 vty->fail = 0;
199 vty_out(vty,
200 "%% Bad enable passwords, too many failures!%s",
201 VTY_NEWLINE);
202 vty->node = VIEW_NODE;
203 }
204 }
205 }
206}
207
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200208/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200209void vty_close(struct vty *vty)
210{
211 int i;
212
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200213 /* VTY_CLOSED is handled by the telnet_interface */
214 vty_event(VTY_CLOSED, vty->fd, vty);
215
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200216 if (vty->obuf) {
217 /* Flush buffer. */
218 buffer_flush_all(vty->obuf, vty->fd);
219
220 /* Free input buffer. */
221 buffer_free(vty->obuf);
222 vty->obuf = NULL;
223 }
224
225 /* Free command history. */
226 for (i = 0; i < VTY_MAXHIST; i++)
227 if (vty->hist[i])
228 talloc_free(vty->hist[i]);
229
230 /* Unset vector. */
231 vector_unset(vtyvec, vty->fd);
232
233 /* Close socket. */
Harald Welte2e0a9452018-10-21 13:22:52 +0200234 if (vty->fd > 0) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200235 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200236 vty->fd = -1;
237 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200238
239 if (vty->buf) {
240 talloc_free(vty->buf);
241 vty->buf = NULL;
242 }
243
244 /* Check configure. */
245 vty_config_unlock(vty);
246
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200247 /* OK free vty. */
248 talloc_free(vty);
249}
250
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200251/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200252int vty_shell(struct vty *vty)
253{
254 return vty->type == VTY_SHELL ? 1 : 0;
255}
256
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100257int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200258{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200259 int len = 0;
260 int size = 1024;
261 char buf[1024];
262 char *p = NULL;
263
264 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100265 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200266 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100267 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200268 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100269 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200270 len = vsnprintf(buf, sizeof buf, format, args);
271 va_end(args);
272
273 /* Initial buffer is not enough. */
274 if (len < 0 || len >= size) {
275 while (1) {
276 if (len > -1)
277 size = len + 1;
278 else
279 size = size * 2;
280
281 p = talloc_realloc_size(vty, p, size);
282 if (!p)
283 return -1;
284
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100285 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200286 len = vsnprintf(p, size, format, args);
287 va_end(args);
288
289 if (len > -1 && len < size)
290 break;
291 }
292 }
293
294 /* When initial buffer is enough to store all output. */
295 if (!p)
296 p = buf;
297
298 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100299 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200300
301 /* If p is not different with buf, it is allocated buffer. */
302 if (p != buf)
303 talloc_free(p);
304 }
305
306 vty_event(VTY_WRITE, vty->fd, vty);
307
308 return len;
309}
310
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100311/*! VTY standard output function
312 * \param[in] vty VTY to which we should print
313 * \param[in] format variable-length format string
314 */
315int vty_out(struct vty *vty, const char *format, ...)
316{
317 va_list args;
318 int rc;
319 va_start(args, format);
320 rc = vty_out_va(vty, format, args);
321 va_end(args);
322 return rc;
323}
324
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200325/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200326int vty_out_newline(struct vty *vty)
327{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200328 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200329 buffer_put(vty->obuf, p, strlen(p));
330 return 0;
331}
332
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200333/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800334void *vty_current_index(struct vty *vty)
335{
336 return vty->index;
337}
Harald Welte7acb30c2011-08-17 17:13:48 +0200338
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200339/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800340int vty_current_node(struct vty *vty)
341{
342 return vty->node;
343}
344
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200345/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200346 * \param[in] vty VTY to which the config shall be locked
347 * \returns 1 on success, 0 on error
348 *
349 * This shall be used to make sure only one VTY at a given time has
350 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200351int vty_config_lock(struct vty *vty)
352{
353 if (vty_config == 0) {
354 vty->config = 1;
355 vty_config = 1;
356 }
357 return vty->config;
358}
359
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200360/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200361 * \param[in] vty VTY from which the configuration shall be unlocked
362 * \returns 0 in case of success
363 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200364int vty_config_unlock(struct vty *vty)
365{
366 if (vty_config == 1 && vty->config == 1) {
367 vty->config = 0;
368 vty_config = 0;
369 }
370 return vty->config;
371}
372
373/* Say hello to vty interface. */
374void vty_hello(struct vty *vty)
375{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200376 const char *app_name = "<unnamed>";
377
378 if (host.app_info->name)
379 app_name = host.app_info->name;
380
Harald Weltea2501a22018-03-23 19:33:27 +0100381 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800382 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200383
384 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200385 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200386
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200387 if (host.motdfile) {
388 FILE *f;
389 char buf[4096];
390
391 f = fopen(host.motdfile, "r");
392 if (f) {
393 while (fgets(buf, sizeof(buf), f)) {
394 char *s;
395 /* work backwards to ignore trailling isspace() */
396 for (s = buf + strlen(buf);
397 (s > buf) && isspace(*(s - 1)); s--) ;
398 *s = '\0';
399 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
400 }
401 fclose(f);
402 } else
403 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
404 } else if (host.motd)
405 vty_out(vty, "%s", host.motd);
406}
407
408/* Put out prompt and wait input from user. */
409static void vty_prompt(struct vty *vty)
410{
411 struct utsname names;
412 const char *hostname;
413
414 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100415 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200416 if (!hostname) {
417 uname(&names);
418 hostname = names.nodename;
419 }
420 vty_out(vty, cmd_prompt(vty->node), hostname);
421 }
422}
423
424/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700425static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200426{
427 int ret;
428 vector vline;
429
430 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700431 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200432
433 if (vline == NULL)
434 return CMD_SUCCESS;
435
436 ret = cmd_execute_command(vline, vty, NULL, 0);
437 if (ret != CMD_SUCCESS)
438 switch (ret) {
439 case CMD_WARNING:
440 if (vty->type == VTY_FILE)
441 vty_out(vty, "Warning...%s", VTY_NEWLINE);
442 break;
443 case CMD_ERR_AMBIGUOUS:
444 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
445 break;
446 case CMD_ERR_NO_MATCH:
447 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
448 break;
449 case CMD_ERR_INCOMPLETE:
450 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
451 break;
452 }
453 cmd_free_strvec(vline);
454
455 return ret;
456}
457
458static const char telnet_backward_char = 0x08;
459static const char telnet_space_char = ' ';
460
461/* Basic function to write buffer to vty. */
462static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
463{
464 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
465 return;
466
467 /* Should we do buffering here ? And make vty_flush (vty) ? */
468 buffer_put(vty->obuf, buf, nbytes);
469}
470
471/* Ensure length of input buffer. Is buffer is short, double it. */
472static void vty_ensure(struct vty *vty, int length)
473{
474 if (vty->max <= length) {
475 vty->max *= 2;
476 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
477 // FIXME: check return
478 }
479}
480
481/* Basic function to insert character into vty. */
482static void vty_self_insert(struct vty *vty, char c)
483{
484 int i;
485 int length;
486
487 vty_ensure(vty, vty->length + 1);
488 length = vty->length - vty->cp;
489 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
490 vty->buf[vty->cp] = c;
491
492 vty_write(vty, &vty->buf[vty->cp], length + 1);
493 for (i = 0; i < length; i++)
494 vty_write(vty, &telnet_backward_char, 1);
495
496 vty->cp++;
497 vty->length++;
498}
499
500/* Self insert character 'c' in overwrite mode. */
501static void vty_self_insert_overwrite(struct vty *vty, char c)
502{
503 vty_ensure(vty, vty->length + 1);
504 vty->buf[vty->cp++] = c;
505
506 if (vty->cp > vty->length)
507 vty->length++;
508
509 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
510 return;
511
512 vty_write(vty, &c, 1);
513}
514
515/* Insert a word into vty interface with overwrite mode. */
516static void vty_insert_word_overwrite(struct vty *vty, char *str)
517{
518 int len = strlen(str);
519 vty_write(vty, str, len);
520 strcpy(&vty->buf[vty->cp], str);
521 vty->cp += len;
522 vty->length = vty->cp;
523}
524
525/* Forward character. */
526static void vty_forward_char(struct vty *vty)
527{
528 if (vty->cp < vty->length) {
529 vty_write(vty, &vty->buf[vty->cp], 1);
530 vty->cp++;
531 }
532}
533
534/* Backward character. */
535static void vty_backward_char(struct vty *vty)
536{
537 if (vty->cp > 0) {
538 vty->cp--;
539 vty_write(vty, &telnet_backward_char, 1);
540 }
541}
542
543/* Move to the beginning of the line. */
544static void vty_beginning_of_line(struct vty *vty)
545{
546 while (vty->cp)
547 vty_backward_char(vty);
548}
549
550/* Move to the end of the line. */
551static void vty_end_of_line(struct vty *vty)
552{
553 while (vty->cp < vty->length)
554 vty_forward_char(vty);
555}
556
557/* Add current command line to the history buffer. */
558static void vty_hist_add(struct vty *vty)
559{
560 int index;
561
562 if (vty->length == 0)
563 return;
564
565 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
566
567 /* Ignore the same string as previous one. */
568 if (vty->hist[index])
569 if (strcmp(vty->buf, vty->hist[index]) == 0) {
570 vty->hp = vty->hindex;
571 return;
572 }
573
574 /* Insert history entry. */
575 if (vty->hist[vty->hindex])
576 talloc_free(vty->hist[vty->hindex]);
577 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
578
579 /* History index rotation. */
580 vty->hindex++;
581 if (vty->hindex == VTY_MAXHIST)
582 vty->hindex = 0;
583
584 vty->hp = vty->hindex;
585}
586
587/* Get telnet window size. */
588static int
589vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
590{
591#ifdef TELNET_OPTION_DEBUG
592 int i;
593
594 for (i = 0; i < nbytes; i++)
595 {
596 switch (buf[i])
597 {
598 case IAC:
599 vty_out (vty, "IAC ");
600 break;
601 case WILL:
602 vty_out (vty, "WILL ");
603 break;
604 case WONT:
605 vty_out (vty, "WONT ");
606 break;
607 case DO:
608 vty_out (vty, "DO ");
609 break;
610 case DONT:
611 vty_out (vty, "DONT ");
612 break;
613 case SB:
614 vty_out (vty, "SB ");
615 break;
616 case SE:
617 vty_out (vty, "SE ");
618 break;
619 case TELOPT_ECHO:
620 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
621 break;
622 case TELOPT_SGA:
623 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
624 break;
625 case TELOPT_NAWS:
626 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
627 break;
628 default:
629 vty_out (vty, "%x ", buf[i]);
630 break;
631 }
632 }
633 vty_out (vty, "%s", VTY_NEWLINE);
634
635#endif /* TELNET_OPTION_DEBUG */
636
637 switch (buf[0])
638 {
639 case SB:
640 vty->sb_len = 0;
641 vty->iac_sb_in_progress = 1;
642 return 0;
643 break;
644 case SE:
645 {
646 if (!vty->iac_sb_in_progress)
647 return 0;
648
649 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
650 {
651 vty->iac_sb_in_progress = 0;
652 return 0;
653 }
654 switch (vty->sb_buf[0])
655 {
656 case TELOPT_NAWS:
657 if (vty->sb_len != TELNET_NAWS_SB_LEN)
658 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
659 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100660 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200661 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
662 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
663 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100664 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200665 else
666 {
667 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
668 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
669#ifdef TELNET_OPTION_DEBUG
670 vty_out(vty, "TELNET NAWS window size negotiation completed: "
671 "width %d, height %d%s",
672 vty->width, vty->height, VTY_NEWLINE);
673#endif
674 }
675 break;
676 }
677 vty->iac_sb_in_progress = 0;
678 return 0;
679 break;
680 }
681 default:
682 break;
683 }
684 return 1;
685}
686
687/* Execute current command line. */
688static int vty_execute(struct vty *vty)
689{
690 int ret;
691
692 ret = CMD_SUCCESS;
693
694 switch (vty->node) {
695 case AUTH_NODE:
696 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700697 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200698 break;
699 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700700 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200701 if (vty->type == VTY_TERM)
702 vty_hist_add(vty);
703 break;
704 }
705
706 /* Clear command line buffer. */
707 vty->cp = vty->length = 0;
708 vty_clear_buf(vty);
709
710 if (vty->status != VTY_CLOSE)
711 vty_prompt(vty);
712
713 return ret;
714}
715
716/* Send WILL TELOPT_ECHO to remote server. */
717static void
718vty_will_echo (struct vty *vty)
719{
720 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
721 vty_out (vty, "%s", cmd);
722}
723
724/* Make suppress Go-Ahead telnet option. */
725static void
726vty_will_suppress_go_ahead (struct vty *vty)
727{
728 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
729 vty_out (vty, "%s", cmd);
730}
731
732/* Make don't use linemode over telnet. */
733static void
734vty_dont_linemode (struct vty *vty)
735{
736 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
737 vty_out (vty, "%s", cmd);
738}
739
740/* Use window size. */
741static void
742vty_do_window_size (struct vty *vty)
743{
744 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
745 vty_out (vty, "%s", cmd);
746}
747
748static void vty_kill_line_from_beginning(struct vty *);
749static void vty_redraw_line(struct vty *);
750
751/* Print command line history. This function is called from
752 vty_next_line and vty_previous_line. */
753static void vty_history_print(struct vty *vty)
754{
755 int length;
756
757 vty_kill_line_from_beginning(vty);
758
759 /* Get previous line from history buffer */
760 length = strlen(vty->hist[vty->hp]);
761 memcpy(vty->buf, vty->hist[vty->hp], length);
762 vty->cp = vty->length = length;
763
764 /* Redraw current line */
765 vty_redraw_line(vty);
766}
767
768/* Show next command line history. */
769static void vty_next_line(struct vty *vty)
770{
771 int try_index;
772
773 if (vty->hp == vty->hindex)
774 return;
775
776 /* Try is there history exist or not. */
777 try_index = vty->hp;
778 if (try_index == (VTY_MAXHIST - 1))
779 try_index = 0;
780 else
781 try_index++;
782
783 /* If there is not history return. */
784 if (vty->hist[try_index] == NULL)
785 return;
786 else
787 vty->hp = try_index;
788
789 vty_history_print(vty);
790}
791
792/* Show previous command line history. */
793static void vty_previous_line(struct vty *vty)
794{
795 int try_index;
796
797 try_index = vty->hp;
798 if (try_index == 0)
799 try_index = VTY_MAXHIST - 1;
800 else
801 try_index--;
802
803 if (vty->hist[try_index] == NULL)
804 return;
805 else
806 vty->hp = try_index;
807
808 vty_history_print(vty);
809}
810
811/* This function redraw all of the command line character. */
812static void vty_redraw_line(struct vty *vty)
813{
814 vty_write(vty, vty->buf, vty->length);
815 vty->cp = vty->length;
816}
817
818/* Forward word. */
819static void vty_forward_word(struct vty *vty)
820{
821 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
822 vty_forward_char(vty);
823
824 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
825 vty_forward_char(vty);
826}
827
828/* Backward word without skipping training space. */
829static void vty_backward_pure_word(struct vty *vty)
830{
831 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
832 vty_backward_char(vty);
833}
834
835/* Backward word. */
836static void vty_backward_word(struct vty *vty)
837{
838 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
839 vty_backward_char(vty);
840
841 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
842 vty_backward_char(vty);
843}
844
845/* When '^D' is typed at the beginning of the line we move to the down
846 level. */
847static void vty_down_level(struct vty *vty)
848{
849 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100850 /* call the exit function of the specific node */
851 if (vty->node > CONFIG_NODE)
852 vty_go_parent(vty);
853 else
854 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200855 vty_prompt(vty);
856 vty->cp = 0;
857}
858
859/* When '^Z' is received from vty, move down to the enable mode. */
860static void vty_end_config(struct vty *vty)
861{
862 vty_out(vty, "%s", VTY_NEWLINE);
863
864 /* FIXME: we need to call the exit function of the specific node
865 * in question, not this generic one that doesn't know all nodes */
866 switch (vty->node) {
867 case VIEW_NODE:
868 case ENABLE_NODE:
869 /* Nothing to do. */
870 break;
871 case CONFIG_NODE:
872 case VTY_NODE:
873 vty_config_unlock(vty);
874 vty->node = ENABLE_NODE;
875 break;
Harald Welte28222962011-02-18 20:37:04 +0100876 case CFG_LOG_NODE:
877 vty->node = CONFIG_NODE;
878 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200879 default:
880 /* Unknown node, we have to ignore it. */
881 break;
882 }
883
884 vty_prompt(vty);
885 vty->cp = 0;
886}
887
888/* Delete a charcter at the current point. */
889static void vty_delete_char(struct vty *vty)
890{
891 int i;
892 int size;
893
894 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
895 return;
896
897 if (vty->length == 0) {
898 vty_down_level(vty);
899 return;
900 }
901
902 if (vty->cp == vty->length)
903 return; /* completion need here? */
904
905 size = vty->length - vty->cp;
906
907 vty->length--;
908 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
909 vty->buf[vty->length] = '\0';
910
911 vty_write(vty, &vty->buf[vty->cp], size - 1);
912 vty_write(vty, &telnet_space_char, 1);
913
914 for (i = 0; i < size; i++)
915 vty_write(vty, &telnet_backward_char, 1);
916}
917
918/* Delete a character before the point. */
919static void vty_delete_backward_char(struct vty *vty)
920{
921 if (vty->cp == 0)
922 return;
923
924 vty_backward_char(vty);
925 vty_delete_char(vty);
926}
927
928/* Kill rest of line from current point. */
929static void vty_kill_line(struct vty *vty)
930{
931 int i;
932 int size;
933
934 size = vty->length - vty->cp;
935
936 if (size == 0)
937 return;
938
939 for (i = 0; i < size; i++)
940 vty_write(vty, &telnet_space_char, 1);
941 for (i = 0; i < size; i++)
942 vty_write(vty, &telnet_backward_char, 1);
943
944 memset(&vty->buf[vty->cp], 0, size);
945 vty->length = vty->cp;
946}
947
948/* Kill line from the beginning. */
949static void vty_kill_line_from_beginning(struct vty *vty)
950{
951 vty_beginning_of_line(vty);
952 vty_kill_line(vty);
953}
954
955/* Delete a word before the point. */
956static void vty_forward_kill_word(struct vty *vty)
957{
958 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
959 vty_delete_char(vty);
960 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
961 vty_delete_char(vty);
962}
963
964/* Delete a word before the point. */
965static void vty_backward_kill_word(struct vty *vty)
966{
967 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
968 vty_delete_backward_char(vty);
969 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
970 vty_delete_backward_char(vty);
971}
972
973/* Transpose chars before or at the point. */
974static void vty_transpose_chars(struct vty *vty)
975{
976 char c1, c2;
977
978 /* If length is short or point is near by the beginning of line then
979 return. */
980 if (vty->length < 2 || vty->cp < 1)
981 return;
982
983 /* In case of point is located at the end of the line. */
984 if (vty->cp == vty->length) {
985 c1 = vty->buf[vty->cp - 1];
986 c2 = vty->buf[vty->cp - 2];
987
988 vty_backward_char(vty);
989 vty_backward_char(vty);
990 vty_self_insert_overwrite(vty, c1);
991 vty_self_insert_overwrite(vty, c2);
992 } else {
993 c1 = vty->buf[vty->cp];
994 c2 = vty->buf[vty->cp - 1];
995
996 vty_backward_char(vty);
997 vty_self_insert_overwrite(vty, c1);
998 vty_self_insert_overwrite(vty, c2);
999 }
1000}
1001
1002/* Do completion at vty interface. */
1003static void vty_complete_command(struct vty *vty)
1004{
1005 int i;
1006 int ret;
1007 char **matched = NULL;
1008 vector vline;
1009
1010 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1011 return;
1012
1013 vline = cmd_make_strvec(vty->buf);
1014 if (vline == NULL)
1015 return;
1016
1017 /* In case of 'help \t'. */
1018 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001019 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001020
1021 matched = cmd_complete_command(vline, vty, &ret);
1022
1023 cmd_free_strvec(vline);
1024
1025 vty_out(vty, "%s", VTY_NEWLINE);
1026 switch (ret) {
1027 case CMD_ERR_AMBIGUOUS:
1028 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1029 vty_prompt(vty);
1030 vty_redraw_line(vty);
1031 break;
1032 case CMD_ERR_NO_MATCH:
1033 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1034 vty_prompt(vty);
1035 vty_redraw_line(vty);
1036 break;
1037 case CMD_COMPLETE_FULL_MATCH:
1038 vty_prompt(vty);
1039 vty_redraw_line(vty);
1040 vty_backward_pure_word(vty);
1041 vty_insert_word_overwrite(vty, matched[0]);
1042 vty_self_insert(vty, ' ');
1043 talloc_free(matched[0]);
1044 break;
1045 case CMD_COMPLETE_MATCH:
1046 vty_prompt(vty);
1047 vty_redraw_line(vty);
1048 vty_backward_pure_word(vty);
1049 vty_insert_word_overwrite(vty, matched[0]);
1050 talloc_free(matched[0]);
1051 break;
1052 case CMD_COMPLETE_LIST_MATCH:
1053 for (i = 0; matched[i] != NULL; i++) {
1054 if (i != 0 && ((i % 6) == 0))
1055 vty_out(vty, "%s", VTY_NEWLINE);
1056 vty_out(vty, "%-10s ", matched[i]);
1057 talloc_free(matched[i]);
1058 }
1059 vty_out(vty, "%s", VTY_NEWLINE);
1060
1061 vty_prompt(vty);
1062 vty_redraw_line(vty);
1063 break;
1064 case CMD_ERR_NOTHING_TODO:
1065 vty_prompt(vty);
1066 vty_redraw_line(vty);
1067 break;
1068 default:
1069 break;
1070 }
1071 if (matched)
1072 vector_only_index_free(matched);
1073}
1074
1075static void
1076vty_describe_fold(struct vty *vty, int cmd_width,
1077 unsigned int desc_width, struct desc *desc)
1078{
1079 char *buf;
1080 const char *cmd, *p;
1081 int pos;
1082
1083 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1084
1085 if (desc_width <= 0) {
1086 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1087 VTY_NEWLINE);
1088 return;
1089 }
1090
1091 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1092 if (!buf)
1093 return;
1094
1095 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1096 for (pos = desc_width; pos > 0; pos--)
1097 if (*(p + pos) == ' ')
1098 break;
1099
1100 if (pos == 0)
1101 break;
1102
1103 strncpy(buf, p, pos);
1104 buf[pos] = '\0';
1105 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1106
1107 cmd = "";
1108 }
1109
1110 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1111
1112 talloc_free(buf);
1113}
1114
1115/* Describe matched command function. */
1116static void vty_describe_command(struct vty *vty)
1117{
1118 int ret;
1119 vector vline;
1120 vector describe;
1121 unsigned int i, width, desc_width;
1122 struct desc *desc, *desc_cr = NULL;
1123
1124 vline = cmd_make_strvec(vty->buf);
1125
1126 /* In case of '> ?'. */
1127 if (vline == NULL) {
1128 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001129 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001130 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001131 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132
1133 describe = cmd_describe_command(vline, vty, &ret);
1134
1135 vty_out(vty, "%s", VTY_NEWLINE);
1136
1137 /* Ambiguous error. */
1138 switch (ret) {
1139 case CMD_ERR_AMBIGUOUS:
1140 cmd_free_strvec(vline);
1141 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1142 vty_prompt(vty);
1143 vty_redraw_line(vty);
1144 return;
1145 break;
1146 case CMD_ERR_NO_MATCH:
1147 cmd_free_strvec(vline);
1148 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1149 vty_prompt(vty);
1150 vty_redraw_line(vty);
1151 return;
1152 break;
1153 }
1154
1155 /* Get width of command string. */
1156 width = 0;
1157 for (i = 0; i < vector_active(describe); i++)
1158 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
1168 if (width < len)
1169 width = len;
1170 }
1171
1172 /* Get width of description string. */
1173 desc_width = vty->width - (width + 6);
1174
1175 /* Print out description. */
1176 for (i = 0; i < vector_active(describe); i++)
1177 if ((desc = vector_slot(describe, i)) != NULL) {
1178 if (desc->cmd[0] == '\0')
1179 continue;
1180
1181 if (strcmp(desc->cmd, "<cr>") == 0) {
1182 desc_cr = desc;
1183 continue;
1184 }
1185
1186 if (!desc->str)
1187 vty_out(vty, " %-s%s",
1188 desc->cmd[0] ==
1189 '.' ? desc->cmd + 1 : desc->cmd,
1190 VTY_NEWLINE);
1191 else if (desc_width >= strlen(desc->str))
1192 vty_out(vty, " %-*s %s%s", width,
1193 desc->cmd[0] ==
1194 '.' ? desc->cmd + 1 : desc->cmd,
1195 desc->str, VTY_NEWLINE);
1196 else
1197 vty_describe_fold(vty, width, desc_width, desc);
1198
1199#if 0
1200 vty_out(vty, " %-*s %s%s", width
1201 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1202 desc->str ? desc->str : "", VTY_NEWLINE);
1203#endif /* 0 */
1204 }
1205
1206 if ((desc = desc_cr)) {
1207 if (!desc->str)
1208 vty_out(vty, " %-s%s",
1209 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1210 VTY_NEWLINE);
1211 else if (desc_width >= strlen(desc->str))
1212 vty_out(vty, " %-*s %s%s", width,
1213 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1214 desc->str, VTY_NEWLINE);
1215 else
1216 vty_describe_fold(vty, width, desc_width, desc);
1217 }
1218
1219 cmd_free_strvec(vline);
1220 vector_free(describe);
1221
1222 vty_prompt(vty);
1223 vty_redraw_line(vty);
1224}
1225
1226/* ^C stop current input and do not add command line to the history. */
1227static void vty_stop_input(struct vty *vty)
1228{
1229 vty->cp = vty->length = 0;
1230 vty_clear_buf(vty);
1231 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001232 vty_prompt(vty);
1233
1234 /* Set history pointer to the latest one. */
1235 vty->hp = vty->hindex;
1236}
1237
1238#define CONTROL(X) ((X) - '@')
1239#define VTY_NORMAL 0
1240#define VTY_PRE_ESCAPE 1
1241#define VTY_ESCAPE 2
1242
1243/* Escape character command map. */
1244static void vty_escape_map(unsigned char c, struct vty *vty)
1245{
1246 switch (c) {
1247 case ('A'):
1248 vty_previous_line(vty);
1249 break;
1250 case ('B'):
1251 vty_next_line(vty);
1252 break;
1253 case ('C'):
1254 vty_forward_char(vty);
1255 break;
1256 case ('D'):
1257 vty_backward_char(vty);
1258 break;
1259 default:
1260 break;
1261 }
1262
1263 /* Go back to normal mode. */
1264 vty->escape = VTY_NORMAL;
1265}
1266
1267/* Quit print out to the buffer. */
1268static void vty_buffer_reset(struct vty *vty)
1269{
1270 buffer_reset(vty->obuf);
1271 vty_prompt(vty);
1272 vty_redraw_line(vty);
1273}
1274
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001275/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001276int vty_read(struct vty *vty)
1277{
1278 int i;
1279 int nbytes;
1280 unsigned char buf[VTY_READ_BUFSIZ];
1281
1282 int vty_sock = vty->fd;
1283
1284 /* Read raw data from socket */
1285 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1286 if (nbytes < 0) {
1287 if (ERRNO_IO_RETRY(errno)) {
1288 vty_event(VTY_READ, vty_sock, vty);
1289 return 0;
1290 }
1291 }
1292 buffer_reset(vty->obuf);
1293 vty->status = VTY_CLOSE;
1294 }
1295
1296 for (i = 0; i < nbytes; i++) {
1297 if (buf[i] == IAC) {
1298 if (!vty->iac) {
1299 vty->iac = 1;
1300 continue;
1301 } else {
1302 vty->iac = 0;
1303 }
1304 }
1305
1306 if (vty->iac_sb_in_progress && !vty->iac) {
1307 if (vty->sb_len < sizeof(vty->sb_buf))
1308 vty->sb_buf[vty->sb_len] = buf[i];
1309 vty->sb_len++;
1310 continue;
1311 }
1312
1313 if (vty->iac) {
1314 /* In case of telnet command */
1315 int ret = 0;
1316 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1317 vty->iac = 0;
1318 i += ret;
1319 continue;
1320 }
1321
1322 if (vty->status == VTY_MORE) {
1323 switch (buf[i]) {
1324 case CONTROL('C'):
1325 case 'q':
1326 case 'Q':
1327 vty_buffer_reset(vty);
1328 break;
1329#if 0 /* More line does not work for "show ip bgp". */
1330 case '\n':
1331 case '\r':
1332 vty->status = VTY_MORELINE;
1333 break;
1334#endif
1335 default:
1336 break;
1337 }
1338 continue;
1339 }
1340
1341 /* Escape character. */
1342 if (vty->escape == VTY_ESCAPE) {
1343 vty_escape_map(buf[i], vty);
1344 continue;
1345 }
1346
1347 /* Pre-escape status. */
1348 if (vty->escape == VTY_PRE_ESCAPE) {
1349 switch (buf[i]) {
1350 case '[':
1351 vty->escape = VTY_ESCAPE;
1352 break;
1353 case 'b':
1354 vty_backward_word(vty);
1355 vty->escape = VTY_NORMAL;
1356 break;
1357 case 'f':
1358 vty_forward_word(vty);
1359 vty->escape = VTY_NORMAL;
1360 break;
1361 case 'd':
1362 vty_forward_kill_word(vty);
1363 vty->escape = VTY_NORMAL;
1364 break;
1365 case CONTROL('H'):
1366 case 0x7f:
1367 vty_backward_kill_word(vty);
1368 vty->escape = VTY_NORMAL;
1369 break;
1370 default:
1371 vty->escape = VTY_NORMAL;
1372 break;
1373 }
1374 continue;
1375 }
1376
1377 switch (buf[i]) {
1378 case CONTROL('A'):
1379 vty_beginning_of_line(vty);
1380 break;
1381 case CONTROL('B'):
1382 vty_backward_char(vty);
1383 break;
1384 case CONTROL('C'):
1385 vty_stop_input(vty);
1386 break;
1387 case CONTROL('D'):
1388 vty_delete_char(vty);
1389 break;
1390 case CONTROL('E'):
1391 vty_end_of_line(vty);
1392 break;
1393 case CONTROL('F'):
1394 vty_forward_char(vty);
1395 break;
1396 case CONTROL('H'):
1397 case 0x7f:
1398 vty_delete_backward_char(vty);
1399 break;
1400 case CONTROL('K'):
1401 vty_kill_line(vty);
1402 break;
1403 case CONTROL('N'):
1404 vty_next_line(vty);
1405 break;
1406 case CONTROL('P'):
1407 vty_previous_line(vty);
1408 break;
1409 case CONTROL('T'):
1410 vty_transpose_chars(vty);
1411 break;
1412 case CONTROL('U'):
1413 vty_kill_line_from_beginning(vty);
1414 break;
1415 case CONTROL('W'):
1416 vty_backward_kill_word(vty);
1417 break;
1418 case CONTROL('Z'):
1419 vty_end_config(vty);
1420 break;
1421 case '\n':
1422 case '\r':
1423 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001424 /* '\0'-terminate the command buffer */
1425 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001426 vty_execute(vty);
1427 break;
1428 case '\t':
1429 vty_complete_command(vty);
1430 break;
1431 case '?':
1432 if (vty->node == AUTH_NODE
1433 || vty->node == AUTH_ENABLE_NODE)
1434 vty_self_insert(vty, buf[i]);
1435 else
1436 vty_describe_command(vty);
1437 break;
1438 case '\033':
1439 if (i + 1 < nbytes && buf[i + 1] == '[') {
1440 vty->escape = VTY_ESCAPE;
1441 i++;
1442 } else
1443 vty->escape = VTY_PRE_ESCAPE;
1444 break;
1445 default:
1446 if (buf[i] > 31 && buf[i] < 127)
1447 vty_self_insert(vty, buf[i]);
1448 break;
1449 }
1450 }
1451
1452 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001453 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001454 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001455 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001456 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001457 vty_event(VTY_WRITE, vty_sock, vty);
1458 vty_event(VTY_READ, vty_sock, vty);
1459 }
1460 return 0;
1461}
1462
1463/* Read up configuration file */
1464static int
1465vty_read_file(FILE *confp, void *priv)
1466{
1467 int ret;
1468 struct vty *vty;
1469
1470 vty = vty_new();
1471 vty->fd = 0;
1472 vty->type = VTY_FILE;
1473 vty->node = CONFIG_NODE;
1474 vty->priv = priv;
1475
1476 ret = config_from_file(vty, confp);
1477
1478 if (ret != CMD_SUCCESS) {
1479 switch (ret) {
1480 case CMD_ERR_AMBIGUOUS:
1481 fprintf(stderr, "Ambiguous command.\n");
1482 break;
1483 case CMD_ERR_NO_MATCH:
1484 fprintf(stderr, "There is no such command.\n");
1485 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001486 case CMD_ERR_INVALID_INDENT:
1487 fprintf(stderr,
1488 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1489 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1490 "allowed, but their sequence must not change within a child block.\n");
1491 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001492 }
Keith03516d62017-09-04 11:19:13 +02001493 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001494 "line:\n%s\n", vty->buf);
1495 vty_close(vty);
1496 return -EINVAL;
1497 }
1498
1499 vty_close(vty);
1500 return 0;
1501}
1502
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001503/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001504struct vty *
1505vty_create (int vty_sock, void *priv)
1506{
1507 struct vty *vty;
1508
Alexander Couzensab383e62018-07-17 19:02:34 +02001509 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001510
1511 tcgetattr(vty_sock, &t);
1512 cfmakeraw(&t);
1513 tcsetattr(vty_sock, TCSANOW, &t);
1514
1515 /* Allocate new vty structure and set up default values. */
1516 vty = vty_new ();
1517 vty->fd = vty_sock;
1518 vty->priv = priv;
1519 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001520 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001521 {
1522 if (host.advanced)
1523 vty->node = ENABLE_NODE;
1524 else
1525 vty->node = VIEW_NODE;
1526 }
1527 else
1528 vty->node = AUTH_NODE;
1529 vty->fail = 0;
1530 vty->cp = 0;
1531 vty_clear_buf (vty);
1532 vty->length = 0;
1533 memset (vty->hist, 0, sizeof (vty->hist));
1534 vty->hp = 0;
1535 vty->hindex = 0;
1536 vector_set_index (vtyvec, vty_sock, vty);
1537 vty->status = VTY_NORMAL;
1538 if (host.lines >= 0)
1539 vty->lines = host.lines;
1540 else
1541 vty->lines = -1;
1542
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001543 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001544 {
1545 /* Vty is not available if password isn't set. */
1546 if (host.password == NULL && host.password_encrypt == NULL)
1547 {
1548 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1549 vty->status = VTY_CLOSE;
1550 vty_close (vty);
1551 return NULL;
1552 }
1553 }
1554
1555 /* Say hello to the world. */
1556 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001557 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001558 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1559
1560 /* Setting up terminal. */
1561 vty_will_echo (vty);
1562 vty_will_suppress_go_ahead (vty);
1563
1564 vty_dont_linemode (vty);
1565 vty_do_window_size (vty);
1566 /* vty_dont_lflow_ahead (vty); */
1567
1568 vty_prompt (vty);
1569
1570 /* Add read/write thread. */
1571 vty_event (VTY_WRITE, vty_sock, vty);
1572 vty_event (VTY_READ, vty_sock, vty);
1573
1574 return vty;
1575}
1576
1577DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1578{
1579 unsigned int i;
1580 struct vty *v;
1581
1582 for (i = 0; i < vector_active(vtyvec); i++)
1583 if ((v = vector_slot(vtyvec, i)) != NULL)
1584 vty_out(vty, "%svty[%d] %s",
1585 v->config ? "*" : " ", i, VTY_NEWLINE);
1586 return CMD_SUCCESS;
1587}
1588
1589/* Move to vty configuration mode. */
1590DEFUN(line_vty,
1591 line_vty_cmd,
1592 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1593{
1594 vty->node = VTY_NODE;
1595 return CMD_SUCCESS;
1596}
1597
1598/* vty login. */
1599DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1600{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001601 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001602 return CMD_SUCCESS;
1603}
1604
1605DEFUN(no_vty_login,
1606 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1607{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001608 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001609 return CMD_SUCCESS;
1610}
1611
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001612/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001613DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001614 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001615 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1616 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001617{
1618 talloc_free((void*)vty_bind_addr);
1619 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001620 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001621 return CMD_SUCCESS;
1622}
1623
1624const char *vty_get_bind_addr(void)
1625{
1626 if (!vty_bind_addr)
1627 return VTY_BIND_ADDR_DEFAULT;
1628 return vty_bind_addr;
1629}
1630
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001631int vty_get_bind_port(int default_port)
1632{
1633 if (vty_bind_port >= 0)
1634 return vty_bind_port;
1635 return default_port;
1636}
1637
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001638DEFUN(service_advanced_vty,
1639 service_advanced_vty_cmd,
1640 "service advanced-vty",
1641 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1642{
1643 host.advanced = 1;
1644 return CMD_SUCCESS;
1645}
1646
1647DEFUN(no_service_advanced_vty,
1648 no_service_advanced_vty_cmd,
1649 "no service advanced-vty",
1650 NO_STR
1651 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1652{
1653 host.advanced = 0;
1654 return CMD_SUCCESS;
1655}
1656
1657DEFUN(terminal_monitor,
1658 terminal_monitor_cmd,
1659 "terminal monitor",
1660 "Set terminal line parameters\n"
1661 "Copy debug output to the current terminal line\n")
1662{
1663 vty->monitor = 1;
1664 return CMD_SUCCESS;
1665}
1666
1667DEFUN(terminal_no_monitor,
1668 terminal_no_monitor_cmd,
1669 "terminal no monitor",
1670 "Set terminal line parameters\n"
1671 NO_STR "Copy debug output to the current terminal line\n")
1672{
1673 vty->monitor = 0;
1674 return CMD_SUCCESS;
1675}
1676
1677DEFUN(show_history,
1678 show_history_cmd,
1679 "show history", SHOW_STR "Display the session command history\n")
1680{
1681 int index;
1682
1683 for (index = vty->hindex + 1; index != vty->hindex;) {
1684 if (index == VTY_MAXHIST) {
1685 index = 0;
1686 continue;
1687 }
1688
1689 if (vty->hist[index] != NULL)
1690 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1691
1692 index++;
1693 }
1694
1695 return CMD_SUCCESS;
1696}
1697
1698/* Display current configuration. */
1699static int vty_config_write(struct vty *vty)
1700{
1701 vty_out(vty, "line vty%s", VTY_NEWLINE);
1702
1703 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001704 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001705 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001706 else
1707 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001708
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001709 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001710 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1711 if (vty_bind_port >= 0) {
1712 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1713 vty_bind_port, VTY_NEWLINE);
1714 } else {
1715 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1716 }
1717 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001718
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001719 vty_out(vty, "!%s", VTY_NEWLINE);
1720
1721 return CMD_SUCCESS;
1722}
1723
1724struct cmd_node vty_node = {
1725 VTY_NODE,
1726 "%s(config-line)# ",
1727 1,
1728};
1729
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001730/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001731void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001732{
1733 unsigned int i;
1734 struct vty *vty;
1735 struct thread *vty_serv_thread;
1736
1737 for (i = 0; i < vector_active(vtyvec); i++)
1738 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1739 buffer_reset(vty->obuf);
1740 vty->status = VTY_CLOSE;
1741 vty_close(vty);
1742 }
1743
1744 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1745 if ((vty_serv_thread =
1746 vector_slot(Vvty_serv_thread, i)) != NULL) {
1747 //thread_cancel (vty_serv_thread);
1748 vector_slot(Vvty_serv_thread, i) = NULL;
1749 close(i);
1750 }
1751}
1752
1753static void vty_save_cwd(void)
1754{
1755 char cwd[MAXPATHLEN];
1756 char *c ;
1757
1758 c = getcwd(cwd, MAXPATHLEN);
1759
1760 if (!c) {
1761 if (chdir(SYSCONFDIR) != 0)
1762 perror("chdir failed");
1763 if (getcwd(cwd, MAXPATHLEN) == NULL)
1764 perror("getcwd failed");
1765 }
1766
1767 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1768 strcpy(vty_cwd, cwd);
1769}
1770
Harald Welte95b2b472011-07-16 11:58:09 +02001771char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001772{
1773 return vty_cwd;
1774}
1775
1776int vty_shell_serv(struct vty *vty)
1777{
1778 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1779}
1780
Harald Welte95b2b472011-07-16 11:58:09 +02001781void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001782{
1783 vtyvec = vector_init(VECTOR_MIN_SIZE);
1784}
1785
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001786/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001787 * \param[in] app_info application information
1788 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001789/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001790void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001791{
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301792 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001793 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1794 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1795
1796 cmd_init(1);
1797
Harald Welte237f6242010-05-25 23:00:45 +02001798 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001799
1800 /* For further configuration read, preserve current directory. */
1801 vty_save_cwd();
1802
1803 vtyvec = vector_init(VECTOR_MIN_SIZE);
1804
1805 /* Install bgp top node. */
1806 install_node(&vty_node, vty_config_write);
1807
1808 install_element_ve(&config_who_cmd);
1809 install_element_ve(&show_history_cmd);
1810 install_element(CONFIG_NODE, &line_vty_cmd);
1811 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1812 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1813 install_element(CONFIG_NODE, &show_history_cmd);
1814 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1815 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1816
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001817 install_element(VTY_NODE, &vty_login_cmd);
1818 install_element(VTY_NODE, &no_vty_login_cmd);
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001819 install_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001820}
1821
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001822/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001823 * \param[in] file_name file name of the configuration file
1824 * \param[in] priv private data to be passed to \ref vty_read_file
1825 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001826int vty_read_config_file(const char *file_name, void *priv)
1827{
1828 FILE *cfile;
1829 int rc;
1830
1831 cfile = fopen(file_name, "r");
1832 if (!cfile)
1833 return -ENOENT;
1834
1835 rc = vty_read_file(cfile, priv);
1836 fclose(cfile);
1837
1838 host_config_set(file_name);
1839
1840 return rc;
1841}
Harald Welte7acb30c2011-08-17 17:13:48 +02001842
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001843/*! @} */