blob: ebdf9fc9a79d08d1fe96825da4a3fd3348b86586 [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;
Vadim Yanitskiy54df08e2019-11-21 21:44:30 +0700131 new->fd = -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200132
133 return new;
134
135out_obuf:
136 buffer_free(new->obuf);
137out_new:
138 talloc_free(new);
139 new = NULL;
140out:
141 return new;
142}
143
144/* Authentication of vty */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700145static void vty_auth(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200146{
147 char *passwd = NULL;
148 enum node_type next_node = 0;
149 int fail;
150 char *crypt(const char *, const char *);
151
152 switch (vty->node) {
153 case AUTH_NODE:
154#ifdef VTY_CRYPT_PW
155 if (host.encrypt)
156 passwd = host.password_encrypt;
157 else
158#endif
159 passwd = host.password;
160 if (host.advanced)
161 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
162 else
163 next_node = VIEW_NODE;
164 break;
165 case AUTH_ENABLE_NODE:
166#ifdef VTY_CRYPT_PW
167 if (host.encrypt)
168 passwd = host.enable_encrypt;
169 else
170#endif
171 passwd = host.enable;
172 next_node = ENABLE_NODE;
173 break;
174 }
175
176 if (passwd) {
177#ifdef VTY_CRYPT_PW
178 if (host.encrypt)
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700179 fail = strcmp(crypt(vty->buf, passwd), passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200180 else
181#endif
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700182 fail = strcmp(vty->buf, passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200183 } else
184 fail = 1;
185
186 if (!fail) {
187 vty->fail = 0;
188 vty->node = next_node; /* Success ! */
189 } else {
190 vty->fail++;
191 if (vty->fail >= 3) {
192 if (vty->node == AUTH_NODE) {
193 vty_out(vty,
194 "%% Bad passwords, too many failures!%s",
195 VTY_NEWLINE);
196 vty->status = VTY_CLOSE;
197 } else {
198 /* AUTH_ENABLE_NODE */
199 vty->fail = 0;
200 vty_out(vty,
201 "%% Bad enable passwords, too many failures!%s",
202 VTY_NEWLINE);
203 vty->node = VIEW_NODE;
204 }
205 }
206 }
207}
208
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200209/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200210void vty_close(struct vty *vty)
211{
212 int i;
213
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200214 /* VTY_CLOSED is handled by the telnet_interface */
215 vty_event(VTY_CLOSED, vty->fd, vty);
216
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200217 if (vty->obuf) {
218 /* Flush buffer. */
219 buffer_flush_all(vty->obuf, vty->fd);
220
221 /* Free input buffer. */
222 buffer_free(vty->obuf);
223 vty->obuf = NULL;
224 }
225
226 /* Free command history. */
227 for (i = 0; i < VTY_MAXHIST; i++)
228 if (vty->hist[i])
229 talloc_free(vty->hist[i]);
230
231 /* Unset vector. */
232 vector_unset(vtyvec, vty->fd);
233
Vadim Yanitskiy7a35b782019-11-21 21:51:23 +0700234 /* Close socket (ignore standard I/O streams). */
235 if (vty->fd > 2) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200236 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200237 vty->fd = -1;
238 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200239
240 if (vty->buf) {
241 talloc_free(vty->buf);
242 vty->buf = NULL;
243 }
244
245 /* Check configure. */
246 vty_config_unlock(vty);
247
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200248 /* OK free vty. */
249 talloc_free(vty);
250}
251
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200252/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200253int vty_shell(struct vty *vty)
254{
255 return vty->type == VTY_SHELL ? 1 : 0;
256}
257
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100258int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200259{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200260 int len = 0;
261 int size = 1024;
262 char buf[1024];
263 char *p = NULL;
264
265 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100266 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200267 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100268 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200269 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100270 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200271 len = vsnprintf(buf, sizeof buf, format, args);
272 va_end(args);
273
274 /* Initial buffer is not enough. */
275 if (len < 0 || len >= size) {
276 while (1) {
277 if (len > -1)
278 size = len + 1;
279 else
280 size = size * 2;
281
282 p = talloc_realloc_size(vty, p, size);
283 if (!p)
284 return -1;
285
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100286 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200287 len = vsnprintf(p, size, format, args);
288 va_end(args);
289
290 if (len > -1 && len < size)
291 break;
292 }
293 }
294
295 /* When initial buffer is enough to store all output. */
296 if (!p)
297 p = buf;
298
299 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100300 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200301
302 /* If p is not different with buf, it is allocated buffer. */
303 if (p != buf)
304 talloc_free(p);
305 }
306
307 vty_event(VTY_WRITE, vty->fd, vty);
308
309 return len;
310}
311
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100312/*! VTY standard output function
313 * \param[in] vty VTY to which we should print
314 * \param[in] format variable-length format string
315 */
316int vty_out(struct vty *vty, const char *format, ...)
317{
318 va_list args;
319 int rc;
320 va_start(args, format);
321 rc = vty_out_va(vty, format, args);
322 va_end(args);
323 return rc;
324}
325
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200326/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200327int vty_out_newline(struct vty *vty)
328{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200329 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200330 buffer_put(vty->obuf, p, strlen(p));
331 return 0;
332}
333
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200334/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800335void *vty_current_index(struct vty *vty)
336{
337 return vty->index;
338}
Harald Welte7acb30c2011-08-17 17:13:48 +0200339
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200340/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800341int vty_current_node(struct vty *vty)
342{
343 return vty->node;
344}
345
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200346/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200347 * \param[in] vty VTY to which the config shall be locked
348 * \returns 1 on success, 0 on error
349 *
350 * This shall be used to make sure only one VTY at a given time has
351 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200352int vty_config_lock(struct vty *vty)
353{
354 if (vty_config == 0) {
355 vty->config = 1;
356 vty_config = 1;
357 }
358 return vty->config;
359}
360
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200361/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200362 * \param[in] vty VTY from which the configuration shall be unlocked
363 * \returns 0 in case of success
364 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200365int vty_config_unlock(struct vty *vty)
366{
367 if (vty_config == 1 && vty->config == 1) {
368 vty->config = 0;
369 vty_config = 0;
370 }
371 return vty->config;
372}
373
374/* Say hello to vty interface. */
375void vty_hello(struct vty *vty)
376{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200377 const char *app_name = "<unnamed>";
378
379 if (host.app_info->name)
380 app_name = host.app_info->name;
381
Harald Weltea2501a22018-03-23 19:33:27 +0100382 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800383 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200384
385 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200386 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200387
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200388 if (host.motdfile) {
389 FILE *f;
390 char buf[4096];
391
392 f = fopen(host.motdfile, "r");
393 if (f) {
394 while (fgets(buf, sizeof(buf), f)) {
395 char *s;
396 /* work backwards to ignore trailling isspace() */
397 for (s = buf + strlen(buf);
398 (s > buf) && isspace(*(s - 1)); s--) ;
399 *s = '\0';
400 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
401 }
402 fclose(f);
403 } else
404 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
405 } else if (host.motd)
406 vty_out(vty, "%s", host.motd);
407}
408
409/* Put out prompt and wait input from user. */
410static void vty_prompt(struct vty *vty)
411{
412 struct utsname names;
413 const char *hostname;
414
415 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100416 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200417 if (!hostname) {
418 uname(&names);
419 hostname = names.nodename;
420 }
421 vty_out(vty, cmd_prompt(vty->node), hostname);
422 }
423}
424
425/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700426static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200427{
428 int ret;
429 vector vline;
430
431 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700432 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200433
434 if (vline == NULL)
435 return CMD_SUCCESS;
436
437 ret = cmd_execute_command(vline, vty, NULL, 0);
438 if (ret != CMD_SUCCESS)
439 switch (ret) {
440 case CMD_WARNING:
441 if (vty->type == VTY_FILE)
442 vty_out(vty, "Warning...%s", VTY_NEWLINE);
443 break;
444 case CMD_ERR_AMBIGUOUS:
445 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
446 break;
447 case CMD_ERR_NO_MATCH:
448 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
449 break;
450 case CMD_ERR_INCOMPLETE:
451 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
452 break;
453 }
454 cmd_free_strvec(vline);
455
456 return ret;
457}
458
459static const char telnet_backward_char = 0x08;
460static const char telnet_space_char = ' ';
461
462/* Basic function to write buffer to vty. */
463static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
464{
465 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
466 return;
467
468 /* Should we do buffering here ? And make vty_flush (vty) ? */
469 buffer_put(vty->obuf, buf, nbytes);
470}
471
472/* Ensure length of input buffer. Is buffer is short, double it. */
473static void vty_ensure(struct vty *vty, int length)
474{
475 if (vty->max <= length) {
476 vty->max *= 2;
477 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
478 // FIXME: check return
479 }
480}
481
482/* Basic function to insert character into vty. */
483static void vty_self_insert(struct vty *vty, char c)
484{
485 int i;
486 int length;
487
488 vty_ensure(vty, vty->length + 1);
489 length = vty->length - vty->cp;
490 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
491 vty->buf[vty->cp] = c;
492
493 vty_write(vty, &vty->buf[vty->cp], length + 1);
494 for (i = 0; i < length; i++)
495 vty_write(vty, &telnet_backward_char, 1);
496
497 vty->cp++;
498 vty->length++;
499}
500
501/* Self insert character 'c' in overwrite mode. */
502static void vty_self_insert_overwrite(struct vty *vty, char c)
503{
504 vty_ensure(vty, vty->length + 1);
505 vty->buf[vty->cp++] = c;
506
507 if (vty->cp > vty->length)
508 vty->length++;
509
510 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
511 return;
512
513 vty_write(vty, &c, 1);
514}
515
516/* Insert a word into vty interface with overwrite mode. */
517static void vty_insert_word_overwrite(struct vty *vty, char *str)
518{
519 int len = strlen(str);
520 vty_write(vty, str, len);
521 strcpy(&vty->buf[vty->cp], str);
522 vty->cp += len;
523 vty->length = vty->cp;
524}
525
526/* Forward character. */
527static void vty_forward_char(struct vty *vty)
528{
529 if (vty->cp < vty->length) {
530 vty_write(vty, &vty->buf[vty->cp], 1);
531 vty->cp++;
532 }
533}
534
535/* Backward character. */
536static void vty_backward_char(struct vty *vty)
537{
538 if (vty->cp > 0) {
539 vty->cp--;
540 vty_write(vty, &telnet_backward_char, 1);
541 }
542}
543
544/* Move to the beginning of the line. */
545static void vty_beginning_of_line(struct vty *vty)
546{
547 while (vty->cp)
548 vty_backward_char(vty);
549}
550
551/* Move to the end of the line. */
552static void vty_end_of_line(struct vty *vty)
553{
554 while (vty->cp < vty->length)
555 vty_forward_char(vty);
556}
557
558/* Add current command line to the history buffer. */
559static void vty_hist_add(struct vty *vty)
560{
561 int index;
562
563 if (vty->length == 0)
564 return;
565
566 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
567
568 /* Ignore the same string as previous one. */
569 if (vty->hist[index])
570 if (strcmp(vty->buf, vty->hist[index]) == 0) {
571 vty->hp = vty->hindex;
572 return;
573 }
574
575 /* Insert history entry. */
576 if (vty->hist[vty->hindex])
577 talloc_free(vty->hist[vty->hindex]);
578 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
579
580 /* History index rotation. */
581 vty->hindex++;
582 if (vty->hindex == VTY_MAXHIST)
583 vty->hindex = 0;
584
585 vty->hp = vty->hindex;
586}
587
588/* Get telnet window size. */
589static int
590vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
591{
592#ifdef TELNET_OPTION_DEBUG
593 int i;
594
595 for (i = 0; i < nbytes; i++)
596 {
597 switch (buf[i])
598 {
599 case IAC:
600 vty_out (vty, "IAC ");
601 break;
602 case WILL:
603 vty_out (vty, "WILL ");
604 break;
605 case WONT:
606 vty_out (vty, "WONT ");
607 break;
608 case DO:
609 vty_out (vty, "DO ");
610 break;
611 case DONT:
612 vty_out (vty, "DONT ");
613 break;
614 case SB:
615 vty_out (vty, "SB ");
616 break;
617 case SE:
618 vty_out (vty, "SE ");
619 break;
620 case TELOPT_ECHO:
621 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
622 break;
623 case TELOPT_SGA:
624 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
625 break;
626 case TELOPT_NAWS:
627 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
628 break;
629 default:
630 vty_out (vty, "%x ", buf[i]);
631 break;
632 }
633 }
634 vty_out (vty, "%s", VTY_NEWLINE);
635
636#endif /* TELNET_OPTION_DEBUG */
637
638 switch (buf[0])
639 {
640 case SB:
641 vty->sb_len = 0;
642 vty->iac_sb_in_progress = 1;
643 return 0;
644 break;
645 case SE:
646 {
647 if (!vty->iac_sb_in_progress)
648 return 0;
649
650 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
651 {
652 vty->iac_sb_in_progress = 0;
653 return 0;
654 }
655 switch (vty->sb_buf[0])
656 {
657 case TELOPT_NAWS:
658 if (vty->sb_len != TELNET_NAWS_SB_LEN)
659 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
660 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100661 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200662 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
663 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
664 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100665 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200666 else
667 {
668 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
669 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
670#ifdef TELNET_OPTION_DEBUG
671 vty_out(vty, "TELNET NAWS window size negotiation completed: "
672 "width %d, height %d%s",
673 vty->width, vty->height, VTY_NEWLINE);
674#endif
675 }
676 break;
677 }
678 vty->iac_sb_in_progress = 0;
679 return 0;
680 break;
681 }
682 default:
683 break;
684 }
685 return 1;
686}
687
688/* Execute current command line. */
689static int vty_execute(struct vty *vty)
690{
691 int ret;
692
693 ret = CMD_SUCCESS;
694
695 switch (vty->node) {
696 case AUTH_NODE:
697 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700698 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200699 break;
700 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700701 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200702 if (vty->type == VTY_TERM)
703 vty_hist_add(vty);
704 break;
705 }
706
707 /* Clear command line buffer. */
708 vty->cp = vty->length = 0;
709 vty_clear_buf(vty);
710
711 if (vty->status != VTY_CLOSE)
712 vty_prompt(vty);
713
714 return ret;
715}
716
717/* Send WILL TELOPT_ECHO to remote server. */
718static void
719vty_will_echo (struct vty *vty)
720{
721 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
722 vty_out (vty, "%s", cmd);
723}
724
725/* Make suppress Go-Ahead telnet option. */
726static void
727vty_will_suppress_go_ahead (struct vty *vty)
728{
729 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
730 vty_out (vty, "%s", cmd);
731}
732
733/* Make don't use linemode over telnet. */
734static void
735vty_dont_linemode (struct vty *vty)
736{
737 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
738 vty_out (vty, "%s", cmd);
739}
740
741/* Use window size. */
742static void
743vty_do_window_size (struct vty *vty)
744{
745 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
746 vty_out (vty, "%s", cmd);
747}
748
749static void vty_kill_line_from_beginning(struct vty *);
750static void vty_redraw_line(struct vty *);
751
752/* Print command line history. This function is called from
753 vty_next_line and vty_previous_line. */
754static void vty_history_print(struct vty *vty)
755{
756 int length;
757
758 vty_kill_line_from_beginning(vty);
759
760 /* Get previous line from history buffer */
761 length = strlen(vty->hist[vty->hp]);
762 memcpy(vty->buf, vty->hist[vty->hp], length);
763 vty->cp = vty->length = length;
764
765 /* Redraw current line */
766 vty_redraw_line(vty);
767}
768
769/* Show next command line history. */
770static void vty_next_line(struct vty *vty)
771{
772 int try_index;
773
774 if (vty->hp == vty->hindex)
775 return;
776
777 /* Try is there history exist or not. */
778 try_index = vty->hp;
779 if (try_index == (VTY_MAXHIST - 1))
780 try_index = 0;
781 else
782 try_index++;
783
784 /* If there is not history return. */
785 if (vty->hist[try_index] == NULL)
786 return;
787 else
788 vty->hp = try_index;
789
790 vty_history_print(vty);
791}
792
793/* Show previous command line history. */
794static void vty_previous_line(struct vty *vty)
795{
796 int try_index;
797
798 try_index = vty->hp;
799 if (try_index == 0)
800 try_index = VTY_MAXHIST - 1;
801 else
802 try_index--;
803
804 if (vty->hist[try_index] == NULL)
805 return;
806 else
807 vty->hp = try_index;
808
809 vty_history_print(vty);
810}
811
812/* This function redraw all of the command line character. */
813static void vty_redraw_line(struct vty *vty)
814{
815 vty_write(vty, vty->buf, vty->length);
816 vty->cp = vty->length;
817}
818
819/* Forward word. */
820static void vty_forward_word(struct vty *vty)
821{
822 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
823 vty_forward_char(vty);
824
825 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
826 vty_forward_char(vty);
827}
828
829/* Backward word without skipping training space. */
830static void vty_backward_pure_word(struct vty *vty)
831{
832 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
833 vty_backward_char(vty);
834}
835
836/* Backward word. */
837static void vty_backward_word(struct vty *vty)
838{
839 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
840 vty_backward_char(vty);
841
842 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
843 vty_backward_char(vty);
844}
845
846/* When '^D' is typed at the beginning of the line we move to the down
847 level. */
848static void vty_down_level(struct vty *vty)
849{
850 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100851 /* call the exit function of the specific node */
852 if (vty->node > CONFIG_NODE)
853 vty_go_parent(vty);
854 else
855 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200856 vty_prompt(vty);
857 vty->cp = 0;
858}
859
860/* When '^Z' is received from vty, move down to the enable mode. */
861static void vty_end_config(struct vty *vty)
862{
863 vty_out(vty, "%s", VTY_NEWLINE);
864
865 /* FIXME: we need to call the exit function of the specific node
866 * in question, not this generic one that doesn't know all nodes */
867 switch (vty->node) {
868 case VIEW_NODE:
869 case ENABLE_NODE:
870 /* Nothing to do. */
871 break;
872 case CONFIG_NODE:
873 case VTY_NODE:
874 vty_config_unlock(vty);
875 vty->node = ENABLE_NODE;
876 break;
Harald Welte28222962011-02-18 20:37:04 +0100877 case CFG_LOG_NODE:
878 vty->node = CONFIG_NODE;
879 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200880 default:
881 /* Unknown node, we have to ignore it. */
882 break;
883 }
884
885 vty_prompt(vty);
886 vty->cp = 0;
887}
888
889/* Delete a charcter at the current point. */
890static void vty_delete_char(struct vty *vty)
891{
892 int i;
893 int size;
894
895 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
896 return;
897
898 if (vty->length == 0) {
899 vty_down_level(vty);
900 return;
901 }
902
903 if (vty->cp == vty->length)
904 return; /* completion need here? */
905
906 size = vty->length - vty->cp;
907
908 vty->length--;
909 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
910 vty->buf[vty->length] = '\0';
911
912 vty_write(vty, &vty->buf[vty->cp], size - 1);
913 vty_write(vty, &telnet_space_char, 1);
914
915 for (i = 0; i < size; i++)
916 vty_write(vty, &telnet_backward_char, 1);
917}
918
919/* Delete a character before the point. */
920static void vty_delete_backward_char(struct vty *vty)
921{
922 if (vty->cp == 0)
923 return;
924
925 vty_backward_char(vty);
926 vty_delete_char(vty);
927}
928
929/* Kill rest of line from current point. */
930static void vty_kill_line(struct vty *vty)
931{
932 int i;
933 int size;
934
935 size = vty->length - vty->cp;
936
937 if (size == 0)
938 return;
939
940 for (i = 0; i < size; i++)
941 vty_write(vty, &telnet_space_char, 1);
942 for (i = 0; i < size; i++)
943 vty_write(vty, &telnet_backward_char, 1);
944
945 memset(&vty->buf[vty->cp], 0, size);
946 vty->length = vty->cp;
947}
948
949/* Kill line from the beginning. */
950static void vty_kill_line_from_beginning(struct vty *vty)
951{
952 vty_beginning_of_line(vty);
953 vty_kill_line(vty);
954}
955
956/* Delete a word before the point. */
957static void vty_forward_kill_word(struct vty *vty)
958{
959 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
960 vty_delete_char(vty);
961 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
962 vty_delete_char(vty);
963}
964
965/* Delete a word before the point. */
966static void vty_backward_kill_word(struct vty *vty)
967{
968 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
969 vty_delete_backward_char(vty);
970 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
971 vty_delete_backward_char(vty);
972}
973
974/* Transpose chars before or at the point. */
975static void vty_transpose_chars(struct vty *vty)
976{
977 char c1, c2;
978
979 /* If length is short or point is near by the beginning of line then
980 return. */
981 if (vty->length < 2 || vty->cp < 1)
982 return;
983
984 /* In case of point is located at the end of the line. */
985 if (vty->cp == vty->length) {
986 c1 = vty->buf[vty->cp - 1];
987 c2 = vty->buf[vty->cp - 2];
988
989 vty_backward_char(vty);
990 vty_backward_char(vty);
991 vty_self_insert_overwrite(vty, c1);
992 vty_self_insert_overwrite(vty, c2);
993 } else {
994 c1 = vty->buf[vty->cp];
995 c2 = vty->buf[vty->cp - 1];
996
997 vty_backward_char(vty);
998 vty_self_insert_overwrite(vty, c1);
999 vty_self_insert_overwrite(vty, c2);
1000 }
1001}
1002
1003/* Do completion at vty interface. */
1004static void vty_complete_command(struct vty *vty)
1005{
1006 int i;
1007 int ret;
1008 char **matched = NULL;
1009 vector vline;
1010
1011 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1012 return;
1013
1014 vline = cmd_make_strvec(vty->buf);
1015 if (vline == NULL)
1016 return;
1017
1018 /* In case of 'help \t'. */
1019 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001020 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001021
1022 matched = cmd_complete_command(vline, vty, &ret);
1023
1024 cmd_free_strvec(vline);
1025
1026 vty_out(vty, "%s", VTY_NEWLINE);
1027 switch (ret) {
1028 case CMD_ERR_AMBIGUOUS:
1029 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1030 vty_prompt(vty);
1031 vty_redraw_line(vty);
1032 break;
1033 case CMD_ERR_NO_MATCH:
1034 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1035 vty_prompt(vty);
1036 vty_redraw_line(vty);
1037 break;
1038 case CMD_COMPLETE_FULL_MATCH:
1039 vty_prompt(vty);
1040 vty_redraw_line(vty);
1041 vty_backward_pure_word(vty);
1042 vty_insert_word_overwrite(vty, matched[0]);
1043 vty_self_insert(vty, ' ');
1044 talloc_free(matched[0]);
1045 break;
1046 case CMD_COMPLETE_MATCH:
1047 vty_prompt(vty);
1048 vty_redraw_line(vty);
1049 vty_backward_pure_word(vty);
1050 vty_insert_word_overwrite(vty, matched[0]);
1051 talloc_free(matched[0]);
1052 break;
1053 case CMD_COMPLETE_LIST_MATCH:
1054 for (i = 0; matched[i] != NULL; i++) {
1055 if (i != 0 && ((i % 6) == 0))
1056 vty_out(vty, "%s", VTY_NEWLINE);
1057 vty_out(vty, "%-10s ", matched[i]);
1058 talloc_free(matched[i]);
1059 }
1060 vty_out(vty, "%s", VTY_NEWLINE);
1061
1062 vty_prompt(vty);
1063 vty_redraw_line(vty);
1064 break;
1065 case CMD_ERR_NOTHING_TODO:
1066 vty_prompt(vty);
1067 vty_redraw_line(vty);
1068 break;
1069 default:
1070 break;
1071 }
1072 if (matched)
1073 vector_only_index_free(matched);
1074}
1075
1076static void
1077vty_describe_fold(struct vty *vty, int cmd_width,
1078 unsigned int desc_width, struct desc *desc)
1079{
1080 char *buf;
1081 const char *cmd, *p;
1082 int pos;
1083
1084 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1085
1086 if (desc_width <= 0) {
1087 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1088 VTY_NEWLINE);
1089 return;
1090 }
1091
1092 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1093 if (!buf)
1094 return;
1095
1096 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1097 for (pos = desc_width; pos > 0; pos--)
1098 if (*(p + pos) == ' ')
1099 break;
1100
1101 if (pos == 0)
1102 break;
1103
1104 strncpy(buf, p, pos);
1105 buf[pos] = '\0';
1106 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1107
1108 cmd = "";
1109 }
1110
1111 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1112
1113 talloc_free(buf);
1114}
1115
1116/* Describe matched command function. */
1117static void vty_describe_command(struct vty *vty)
1118{
1119 int ret;
1120 vector vline;
1121 vector describe;
1122 unsigned int i, width, desc_width;
1123 struct desc *desc, *desc_cr = NULL;
1124
1125 vline = cmd_make_strvec(vty->buf);
1126
1127 /* In case of '> ?'. */
1128 if (vline == NULL) {
1129 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001130 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001131 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001132 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001133
1134 describe = cmd_describe_command(vline, vty, &ret);
1135
1136 vty_out(vty, "%s", VTY_NEWLINE);
1137
1138 /* Ambiguous error. */
1139 switch (ret) {
1140 case CMD_ERR_AMBIGUOUS:
1141 cmd_free_strvec(vline);
1142 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1143 vty_prompt(vty);
1144 vty_redraw_line(vty);
1145 return;
1146 break;
1147 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;
1153 break;
1154 }
1155
1156 /* Get width of command string. */
1157 width = 0;
1158 for (i = 0; i < vector_active(describe); i++)
1159 if ((desc = vector_slot(describe, i)) != NULL) {
1160 unsigned int len;
1161
1162 if (desc->cmd[0] == '\0')
1163 continue;
1164
1165 len = strlen(desc->cmd);
1166 if (desc->cmd[0] == '.')
1167 len--;
1168
1169 if (width < len)
1170 width = len;
1171 }
1172
1173 /* Get width of description string. */
1174 desc_width = vty->width - (width + 6);
1175
1176 /* Print out description. */
1177 for (i = 0; i < vector_active(describe); i++)
1178 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))
1193 vty_out(vty, " %-*s %s%s", width,
1194 desc->cmd[0] ==
1195 '.' ? desc->cmd + 1 : desc->cmd,
1196 desc->str, VTY_NEWLINE);
1197 else
1198 vty_describe_fold(vty, width, desc_width, desc);
1199
1200#if 0
1201 vty_out(vty, " %-*s %s%s", width
1202 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1203 desc->str ? desc->str : "", VTY_NEWLINE);
1204#endif /* 0 */
1205 }
1206
1207 if ((desc = desc_cr)) {
1208 if (!desc->str)
1209 vty_out(vty, " %-s%s",
1210 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1211 VTY_NEWLINE);
1212 else if (desc_width >= strlen(desc->str))
1213 vty_out(vty, " %-*s %s%s", width,
1214 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1215 desc->str, VTY_NEWLINE);
1216 else
1217 vty_describe_fold(vty, width, desc_width, desc);
1218 }
1219
1220 cmd_free_strvec(vline);
1221 vector_free(describe);
1222
1223 vty_prompt(vty);
1224 vty_redraw_line(vty);
1225}
1226
1227/* ^C stop current input and do not add command line to the history. */
1228static void vty_stop_input(struct vty *vty)
1229{
1230 vty->cp = vty->length = 0;
1231 vty_clear_buf(vty);
1232 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001233 vty_prompt(vty);
1234
1235 /* Set history pointer to the latest one. */
1236 vty->hp = vty->hindex;
1237}
1238
1239#define CONTROL(X) ((X) - '@')
1240#define VTY_NORMAL 0
1241#define VTY_PRE_ESCAPE 1
1242#define VTY_ESCAPE 2
1243
1244/* Escape character command map. */
1245static void vty_escape_map(unsigned char c, struct vty *vty)
1246{
1247 switch (c) {
1248 case ('A'):
1249 vty_previous_line(vty);
1250 break;
1251 case ('B'):
1252 vty_next_line(vty);
1253 break;
1254 case ('C'):
1255 vty_forward_char(vty);
1256 break;
1257 case ('D'):
1258 vty_backward_char(vty);
1259 break;
1260 default:
1261 break;
1262 }
1263
1264 /* Go back to normal mode. */
1265 vty->escape = VTY_NORMAL;
1266}
1267
1268/* Quit print out to the buffer. */
1269static void vty_buffer_reset(struct vty *vty)
1270{
1271 buffer_reset(vty->obuf);
1272 vty_prompt(vty);
1273 vty_redraw_line(vty);
1274}
1275
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001276/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001277int vty_read(struct vty *vty)
1278{
1279 int i;
1280 int nbytes;
1281 unsigned char buf[VTY_READ_BUFSIZ];
1282
1283 int vty_sock = vty->fd;
1284
1285 /* Read raw data from socket */
1286 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1287 if (nbytes < 0) {
1288 if (ERRNO_IO_RETRY(errno)) {
1289 vty_event(VTY_READ, vty_sock, vty);
1290 return 0;
1291 }
1292 }
1293 buffer_reset(vty->obuf);
1294 vty->status = VTY_CLOSE;
1295 }
1296
1297 for (i = 0; i < nbytes; i++) {
1298 if (buf[i] == IAC) {
1299 if (!vty->iac) {
1300 vty->iac = 1;
1301 continue;
1302 } else {
1303 vty->iac = 0;
1304 }
1305 }
1306
1307 if (vty->iac_sb_in_progress && !vty->iac) {
1308 if (vty->sb_len < sizeof(vty->sb_buf))
1309 vty->sb_buf[vty->sb_len] = buf[i];
1310 vty->sb_len++;
1311 continue;
1312 }
1313
1314 if (vty->iac) {
1315 /* In case of telnet command */
1316 int ret = 0;
1317 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1318 vty->iac = 0;
1319 i += ret;
1320 continue;
1321 }
1322
1323 if (vty->status == VTY_MORE) {
1324 switch (buf[i]) {
1325 case CONTROL('C'):
1326 case 'q':
1327 case 'Q':
1328 vty_buffer_reset(vty);
1329 break;
1330#if 0 /* More line does not work for "show ip bgp". */
1331 case '\n':
1332 case '\r':
1333 vty->status = VTY_MORELINE;
1334 break;
1335#endif
1336 default:
1337 break;
1338 }
1339 continue;
1340 }
1341
1342 /* Escape character. */
1343 if (vty->escape == VTY_ESCAPE) {
1344 vty_escape_map(buf[i], vty);
1345 continue;
1346 }
1347
1348 /* Pre-escape status. */
1349 if (vty->escape == VTY_PRE_ESCAPE) {
1350 switch (buf[i]) {
1351 case '[':
1352 vty->escape = VTY_ESCAPE;
1353 break;
1354 case 'b':
1355 vty_backward_word(vty);
1356 vty->escape = VTY_NORMAL;
1357 break;
1358 case 'f':
1359 vty_forward_word(vty);
1360 vty->escape = VTY_NORMAL;
1361 break;
1362 case 'd':
1363 vty_forward_kill_word(vty);
1364 vty->escape = VTY_NORMAL;
1365 break;
1366 case CONTROL('H'):
1367 case 0x7f:
1368 vty_backward_kill_word(vty);
1369 vty->escape = VTY_NORMAL;
1370 break;
1371 default:
1372 vty->escape = VTY_NORMAL;
1373 break;
1374 }
1375 continue;
1376 }
1377
1378 switch (buf[i]) {
1379 case CONTROL('A'):
1380 vty_beginning_of_line(vty);
1381 break;
1382 case CONTROL('B'):
1383 vty_backward_char(vty);
1384 break;
1385 case CONTROL('C'):
1386 vty_stop_input(vty);
1387 break;
1388 case CONTROL('D'):
1389 vty_delete_char(vty);
1390 break;
1391 case CONTROL('E'):
1392 vty_end_of_line(vty);
1393 break;
1394 case CONTROL('F'):
1395 vty_forward_char(vty);
1396 break;
1397 case CONTROL('H'):
1398 case 0x7f:
1399 vty_delete_backward_char(vty);
1400 break;
1401 case CONTROL('K'):
1402 vty_kill_line(vty);
1403 break;
1404 case CONTROL('N'):
1405 vty_next_line(vty);
1406 break;
1407 case CONTROL('P'):
1408 vty_previous_line(vty);
1409 break;
1410 case CONTROL('T'):
1411 vty_transpose_chars(vty);
1412 break;
1413 case CONTROL('U'):
1414 vty_kill_line_from_beginning(vty);
1415 break;
1416 case CONTROL('W'):
1417 vty_backward_kill_word(vty);
1418 break;
1419 case CONTROL('Z'):
1420 vty_end_config(vty);
1421 break;
1422 case '\n':
1423 case '\r':
1424 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001425 /* '\0'-terminate the command buffer */
1426 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001427 vty_execute(vty);
1428 break;
1429 case '\t':
1430 vty_complete_command(vty);
1431 break;
1432 case '?':
1433 if (vty->node == AUTH_NODE
1434 || vty->node == AUTH_ENABLE_NODE)
1435 vty_self_insert(vty, buf[i]);
1436 else
1437 vty_describe_command(vty);
1438 break;
1439 case '\033':
1440 if (i + 1 < nbytes && buf[i + 1] == '[') {
1441 vty->escape = VTY_ESCAPE;
1442 i++;
1443 } else
1444 vty->escape = VTY_PRE_ESCAPE;
1445 break;
1446 default:
1447 if (buf[i] > 31 && buf[i] < 127)
1448 vty_self_insert(vty, buf[i]);
1449 break;
1450 }
1451 }
1452
1453 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001454 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001455 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001456 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001457 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001458 vty_event(VTY_WRITE, vty_sock, vty);
1459 vty_event(VTY_READ, vty_sock, vty);
1460 }
1461 return 0;
1462}
1463
1464/* Read up configuration file */
1465static int
1466vty_read_file(FILE *confp, void *priv)
1467{
1468 int ret;
1469 struct vty *vty;
1470
1471 vty = vty_new();
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001472 vty->type = VTY_FILE;
1473 vty->node = CONFIG_NODE;
1474 vty->priv = priv;
1475
Vadim Yanitskiyb639b4d2019-11-21 02:20:11 +07001476 /* By default, write to stderr. Otherwise, during parsing of the logging
1477 * configuration, all invocations to vty_out() would make the process
1478 * write() to its own stdin (fd=0)! */
1479 vty->fd = fileno(stderr);
1480
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001481 ret = config_from_file(vty, confp);
1482
1483 if (ret != CMD_SUCCESS) {
1484 switch (ret) {
1485 case CMD_ERR_AMBIGUOUS:
1486 fprintf(stderr, "Ambiguous command.\n");
1487 break;
1488 case CMD_ERR_NO_MATCH:
1489 fprintf(stderr, "There is no such command.\n");
1490 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001491 case CMD_ERR_INVALID_INDENT:
1492 fprintf(stderr,
1493 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1494 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1495 "allowed, but their sequence must not change within a child block.\n");
1496 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001497 }
Keith03516d62017-09-04 11:19:13 +02001498 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001499 "line:\n%s\n", vty->buf);
1500 vty_close(vty);
1501 return -EINVAL;
1502 }
1503
1504 vty_close(vty);
1505 return 0;
1506}
1507
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001508/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001509struct vty *
1510vty_create (int vty_sock, void *priv)
1511{
1512 struct vty *vty;
1513
Alexander Couzensab383e62018-07-17 19:02:34 +02001514 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001515
1516 tcgetattr(vty_sock, &t);
1517 cfmakeraw(&t);
1518 tcsetattr(vty_sock, TCSANOW, &t);
1519
1520 /* Allocate new vty structure and set up default values. */
1521 vty = vty_new ();
1522 vty->fd = vty_sock;
1523 vty->priv = priv;
1524 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001525 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001526 {
1527 if (host.advanced)
1528 vty->node = ENABLE_NODE;
1529 else
1530 vty->node = VIEW_NODE;
1531 }
1532 else
1533 vty->node = AUTH_NODE;
1534 vty->fail = 0;
1535 vty->cp = 0;
1536 vty_clear_buf (vty);
1537 vty->length = 0;
1538 memset (vty->hist, 0, sizeof (vty->hist));
1539 vty->hp = 0;
1540 vty->hindex = 0;
1541 vector_set_index (vtyvec, vty_sock, vty);
1542 vty->status = VTY_NORMAL;
1543 if (host.lines >= 0)
1544 vty->lines = host.lines;
1545 else
1546 vty->lines = -1;
1547
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001548 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001549 {
1550 /* Vty is not available if password isn't set. */
1551 if (host.password == NULL && host.password_encrypt == NULL)
1552 {
1553 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1554 vty->status = VTY_CLOSE;
1555 vty_close (vty);
1556 return NULL;
1557 }
1558 }
1559
1560 /* Say hello to the world. */
1561 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001562 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001563 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1564
1565 /* Setting up terminal. */
1566 vty_will_echo (vty);
1567 vty_will_suppress_go_ahead (vty);
1568
1569 vty_dont_linemode (vty);
1570 vty_do_window_size (vty);
1571 /* vty_dont_lflow_ahead (vty); */
1572
1573 vty_prompt (vty);
1574
1575 /* Add read/write thread. */
1576 vty_event (VTY_WRITE, vty_sock, vty);
1577 vty_event (VTY_READ, vty_sock, vty);
1578
1579 return vty;
1580}
1581
1582DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1583{
1584 unsigned int i;
1585 struct vty *v;
1586
1587 for (i = 0; i < vector_active(vtyvec); i++)
1588 if ((v = vector_slot(vtyvec, i)) != NULL)
1589 vty_out(vty, "%svty[%d] %s",
1590 v->config ? "*" : " ", i, VTY_NEWLINE);
1591 return CMD_SUCCESS;
1592}
1593
1594/* Move to vty configuration mode. */
1595DEFUN(line_vty,
1596 line_vty_cmd,
1597 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1598{
1599 vty->node = VTY_NODE;
1600 return CMD_SUCCESS;
1601}
1602
1603/* vty login. */
1604DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1605{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001606 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001607 return CMD_SUCCESS;
1608}
1609
1610DEFUN(no_vty_login,
1611 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1612{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001613 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001614 return CMD_SUCCESS;
1615}
1616
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001617/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001618DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001619 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001620 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1621 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001622{
1623 talloc_free((void*)vty_bind_addr);
1624 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001625 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001626 return CMD_SUCCESS;
1627}
1628
1629const char *vty_get_bind_addr(void)
1630{
1631 if (!vty_bind_addr)
1632 return VTY_BIND_ADDR_DEFAULT;
1633 return vty_bind_addr;
1634}
1635
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001636int vty_get_bind_port(int default_port)
1637{
1638 if (vty_bind_port >= 0)
1639 return vty_bind_port;
1640 return default_port;
1641}
1642
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001643DEFUN(service_advanced_vty,
1644 service_advanced_vty_cmd,
1645 "service advanced-vty",
1646 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1647{
1648 host.advanced = 1;
1649 return CMD_SUCCESS;
1650}
1651
1652DEFUN(no_service_advanced_vty,
1653 no_service_advanced_vty_cmd,
1654 "no service advanced-vty",
1655 NO_STR
1656 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1657{
1658 host.advanced = 0;
1659 return CMD_SUCCESS;
1660}
1661
1662DEFUN(terminal_monitor,
1663 terminal_monitor_cmd,
1664 "terminal monitor",
1665 "Set terminal line parameters\n"
1666 "Copy debug output to the current terminal line\n")
1667{
1668 vty->monitor = 1;
1669 return CMD_SUCCESS;
1670}
1671
1672DEFUN(terminal_no_monitor,
1673 terminal_no_monitor_cmd,
1674 "terminal no monitor",
1675 "Set terminal line parameters\n"
1676 NO_STR "Copy debug output to the current terminal line\n")
1677{
1678 vty->monitor = 0;
1679 return CMD_SUCCESS;
1680}
1681
1682DEFUN(show_history,
1683 show_history_cmd,
1684 "show history", SHOW_STR "Display the session command history\n")
1685{
1686 int index;
1687
1688 for (index = vty->hindex + 1; index != vty->hindex;) {
1689 if (index == VTY_MAXHIST) {
1690 index = 0;
1691 continue;
1692 }
1693
1694 if (vty->hist[index] != NULL)
1695 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1696
1697 index++;
1698 }
1699
1700 return CMD_SUCCESS;
1701}
1702
1703/* Display current configuration. */
1704static int vty_config_write(struct vty *vty)
1705{
1706 vty_out(vty, "line vty%s", VTY_NEWLINE);
1707
1708 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001709 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001710 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001711 else
1712 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001713
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001714 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001715 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1716 if (vty_bind_port >= 0) {
1717 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1718 vty_bind_port, VTY_NEWLINE);
1719 } else {
1720 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1721 }
1722 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001723
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001724 vty_out(vty, "!%s", VTY_NEWLINE);
1725
1726 return CMD_SUCCESS;
1727}
1728
1729struct cmd_node vty_node = {
1730 VTY_NODE,
1731 "%s(config-line)# ",
1732 1,
1733};
1734
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001735/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001736void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001737{
1738 unsigned int i;
1739 struct vty *vty;
1740 struct thread *vty_serv_thread;
1741
1742 for (i = 0; i < vector_active(vtyvec); i++)
1743 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1744 buffer_reset(vty->obuf);
1745 vty->status = VTY_CLOSE;
1746 vty_close(vty);
1747 }
1748
1749 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1750 if ((vty_serv_thread =
1751 vector_slot(Vvty_serv_thread, i)) != NULL) {
1752 //thread_cancel (vty_serv_thread);
1753 vector_slot(Vvty_serv_thread, i) = NULL;
1754 close(i);
1755 }
1756}
1757
1758static void vty_save_cwd(void)
1759{
1760 char cwd[MAXPATHLEN];
1761 char *c ;
1762
1763 c = getcwd(cwd, MAXPATHLEN);
1764
1765 if (!c) {
1766 if (chdir(SYSCONFDIR) != 0)
1767 perror("chdir failed");
1768 if (getcwd(cwd, MAXPATHLEN) == NULL)
1769 perror("getcwd failed");
1770 }
1771
1772 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1773 strcpy(vty_cwd, cwd);
1774}
1775
Harald Welte95b2b472011-07-16 11:58:09 +02001776char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001777{
1778 return vty_cwd;
1779}
1780
1781int vty_shell_serv(struct vty *vty)
1782{
1783 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1784}
1785
Harald Welte95b2b472011-07-16 11:58:09 +02001786void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001787{
1788 vtyvec = vector_init(VECTOR_MIN_SIZE);
1789}
1790
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001791/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001792 * \param[in] app_info application information
1793 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001794/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001795void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001796{
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301797 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001798 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1799 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1800
1801 cmd_init(1);
1802
Harald Welte237f6242010-05-25 23:00:45 +02001803 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001804
1805 /* For further configuration read, preserve current directory. */
1806 vty_save_cwd();
1807
1808 vtyvec = vector_init(VECTOR_MIN_SIZE);
1809
1810 /* Install bgp top node. */
1811 install_node(&vty_node, vty_config_write);
1812
1813 install_element_ve(&config_who_cmd);
1814 install_element_ve(&show_history_cmd);
1815 install_element(CONFIG_NODE, &line_vty_cmd);
1816 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1817 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1818 install_element(CONFIG_NODE, &show_history_cmd);
1819 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1820 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1821
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001822 install_element(VTY_NODE, &vty_login_cmd);
1823 install_element(VTY_NODE, &no_vty_login_cmd);
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001824 install_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001825}
1826
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001827/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001828 * \param[in] file_name file name of the configuration file
1829 * \param[in] priv private data to be passed to \ref vty_read_file
1830 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001831int vty_read_config_file(const char *file_name, void *priv)
1832{
1833 FILE *cfile;
1834 int rc;
1835
1836 cfile = fopen(file_name, "r");
1837 if (!cfile)
1838 return -ENOENT;
1839
1840 rc = vty_read_file(cfile, priv);
1841 fclose(cfile);
1842
1843 host_config_set(file_name);
1844
1845 return rc;
1846}
Harald Welte7acb30c2011-08-17 17:13:48 +02001847
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001848/*! @} */