blob: 98b332d65b6c19b58204f2af0f5c45ac727d9fe0 [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
Harald Welte7acb30c2011-08-17 17:13:48 +020070/* \addtogroup vty
71 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020072 * \file vty.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020073
Harald Welte3fb0b6f2010-05-19 19:02:52 +020074#define SYSCONFDIR "/usr/local/etc"
75
76/* our callback, located in telnet_interface.c */
77void vty_event(enum event event, int sock, struct vty *vty);
78
79extern struct host host;
80
81/* Vector which store each vty structure. */
82static vector vtyvec;
83
84vector Vvty_serv_thread;
85
86char *vty_cwd = NULL;
87
Neels Hofmeyr96172f02016-02-23 14:01:41 +010088/* IP address passed to the 'line vty'/'bind' command.
89 * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
90 * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
91static const char *vty_bind_addr = NULL;
92#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +000093/* Port the VTY should bind to. -1 means not configured */
94static int vty_bind_port = -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +010095
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096/* Configure lock. */
97static int vty_config;
98
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -070099static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200100
101void *tall_vty_ctx;
102
103static void vty_clear_buf(struct vty *vty)
104{
105 memset(vty->buf, 0, vty->max);
106}
107
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200108/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200109struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200110{
111 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
112
113 if (!new)
114 goto out;
115
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200116 INIT_LLIST_HEAD(&new->parent_nodes);
117
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200118 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
119 if (!new->obuf)
120 goto out_new;
121 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
122 if (!new->buf)
123 goto out_obuf;
124
125 new->max = VTY_BUFSIZ;
126
127 return new;
128
129out_obuf:
130 buffer_free(new->obuf);
131out_new:
132 talloc_free(new);
133 new = NULL;
134out:
135 return new;
136}
137
138/* Authentication of vty */
139static void vty_auth(struct vty *vty, char *buf)
140{
141 char *passwd = NULL;
142 enum node_type next_node = 0;
143 int fail;
144 char *crypt(const char *, const char *);
145
146 switch (vty->node) {
147 case AUTH_NODE:
148#ifdef VTY_CRYPT_PW
149 if (host.encrypt)
150 passwd = host.password_encrypt;
151 else
152#endif
153 passwd = host.password;
154 if (host.advanced)
155 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
156 else
157 next_node = VIEW_NODE;
158 break;
159 case AUTH_ENABLE_NODE:
160#ifdef VTY_CRYPT_PW
161 if (host.encrypt)
162 passwd = host.enable_encrypt;
163 else
164#endif
165 passwd = host.enable;
166 next_node = ENABLE_NODE;
167 break;
168 }
169
170 if (passwd) {
171#ifdef VTY_CRYPT_PW
172 if (host.encrypt)
173 fail = strcmp(crypt(buf, passwd), passwd);
174 else
175#endif
176 fail = strcmp(buf, passwd);
177 } else
178 fail = 1;
179
180 if (!fail) {
181 vty->fail = 0;
182 vty->node = next_node; /* Success ! */
183 } else {
184 vty->fail++;
185 if (vty->fail >= 3) {
186 if (vty->node == AUTH_NODE) {
187 vty_out(vty,
188 "%% Bad passwords, too many failures!%s",
189 VTY_NEWLINE);
190 vty->status = VTY_CLOSE;
191 } else {
192 /* AUTH_ENABLE_NODE */
193 vty->fail = 0;
194 vty_out(vty,
195 "%% Bad enable passwords, too many failures!%s",
196 VTY_NEWLINE);
197 vty->node = VIEW_NODE;
198 }
199 }
200 }
201}
202
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200203/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200204void vty_close(struct vty *vty)
205{
206 int i;
207
208 if (vty->obuf) {
209 /* Flush buffer. */
210 buffer_flush_all(vty->obuf, vty->fd);
211
212 /* Free input buffer. */
213 buffer_free(vty->obuf);
214 vty->obuf = NULL;
215 }
216
217 /* Free command history. */
218 for (i = 0; i < VTY_MAXHIST; i++)
219 if (vty->hist[i])
220 talloc_free(vty->hist[i]);
221
222 /* Unset vector. */
223 vector_unset(vtyvec, vty->fd);
224
225 /* Close socket. */
Harald Welte2e0a9452018-10-21 13:22:52 +0200226 if (vty->fd > 0) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200227 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200228 vty->fd = -1;
229 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200230
231 if (vty->buf) {
232 talloc_free(vty->buf);
233 vty->buf = NULL;
234 }
235
236 /* Check configure. */
237 vty_config_unlock(vty);
238
239 /* VTY_CLOSED is handled by the telnet_interface */
240 vty_event(VTY_CLOSED, vty->fd, vty);
241
242 /* OK free vty. */
243 talloc_free(vty);
244}
245
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200246/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200247int vty_shell(struct vty *vty)
248{
249 return vty->type == VTY_SHELL ? 1 : 0;
250}
251
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100252int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200253{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200254 int len = 0;
255 int size = 1024;
256 char buf[1024];
257 char *p = NULL;
258
259 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100260 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100262 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200263 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100264 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200265 len = vsnprintf(buf, sizeof buf, format, args);
266 va_end(args);
267
268 /* Initial buffer is not enough. */
269 if (len < 0 || len >= size) {
270 while (1) {
271 if (len > -1)
272 size = len + 1;
273 else
274 size = size * 2;
275
276 p = talloc_realloc_size(vty, p, size);
277 if (!p)
278 return -1;
279
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100280 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200281 len = vsnprintf(p, size, format, args);
282 va_end(args);
283
284 if (len > -1 && len < size)
285 break;
286 }
287 }
288
289 /* When initial buffer is enough to store all output. */
290 if (!p)
291 p = buf;
292
293 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100294 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200295
296 /* If p is not different with buf, it is allocated buffer. */
297 if (p != buf)
298 talloc_free(p);
299 }
300
301 vty_event(VTY_WRITE, vty->fd, vty);
302
303 return len;
304}
305
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100306/*! VTY standard output function
307 * \param[in] vty VTY to which we should print
308 * \param[in] format variable-length format string
309 */
310int vty_out(struct vty *vty, const char *format, ...)
311{
312 va_list args;
313 int rc;
314 va_start(args, format);
315 rc = vty_out_va(vty, format, args);
316 va_end(args);
317 return rc;
318}
319
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200320/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200321int vty_out_newline(struct vty *vty)
322{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200323 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200324 buffer_put(vty->obuf, p, strlen(p));
325 return 0;
326}
327
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200328/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800329void *vty_current_index(struct vty *vty)
330{
331 return vty->index;
332}
Harald Welte7acb30c2011-08-17 17:13:48 +0200333
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200334/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800335int vty_current_node(struct vty *vty)
336{
337 return vty->node;
338}
339
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200340/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200341 * \param[in] vty VTY to which the config shall be locked
342 * \returns 1 on success, 0 on error
343 *
344 * This shall be used to make sure only one VTY at a given time has
345 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200346int vty_config_lock(struct vty *vty)
347{
348 if (vty_config == 0) {
349 vty->config = 1;
350 vty_config = 1;
351 }
352 return vty->config;
353}
354
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200355/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200356 * \param[in] vty VTY from which the configuration shall be unlocked
357 * \returns 0 in case of success
358 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200359int vty_config_unlock(struct vty *vty)
360{
361 if (vty_config == 1 && vty->config == 1) {
362 vty->config = 0;
363 vty_config = 0;
364 }
365 return vty->config;
366}
367
368/* Say hello to vty interface. */
369void vty_hello(struct vty *vty)
370{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200371 const char *app_name = "<unnamed>";
372
373 if (host.app_info->name)
374 app_name = host.app_info->name;
375
Harald Weltea2501a22018-03-23 19:33:27 +0100376 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800377 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200378
379 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200380 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200381
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200382 if (host.motdfile) {
383 FILE *f;
384 char buf[4096];
385
386 f = fopen(host.motdfile, "r");
387 if (f) {
388 while (fgets(buf, sizeof(buf), f)) {
389 char *s;
390 /* work backwards to ignore trailling isspace() */
391 for (s = buf + strlen(buf);
392 (s > buf) && isspace(*(s - 1)); s--) ;
393 *s = '\0';
394 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
395 }
396 fclose(f);
397 } else
398 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
399 } else if (host.motd)
400 vty_out(vty, "%s", host.motd);
401}
402
403/* Put out prompt and wait input from user. */
404static void vty_prompt(struct vty *vty)
405{
406 struct utsname names;
407 const char *hostname;
408
409 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100410 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200411 if (!hostname) {
412 uname(&names);
413 hostname = names.nodename;
414 }
415 vty_out(vty, cmd_prompt(vty->node), hostname);
416 }
417}
418
419/* Command execution over the vty interface. */
420static int vty_command(struct vty *vty, char *buf)
421{
422 int ret;
423 vector vline;
424
425 /* Split readline string up into the vector */
426 vline = cmd_make_strvec(buf);
427
428 if (vline == NULL)
429 return CMD_SUCCESS;
430
431 ret = cmd_execute_command(vline, vty, NULL, 0);
432 if (ret != CMD_SUCCESS)
433 switch (ret) {
434 case CMD_WARNING:
435 if (vty->type == VTY_FILE)
436 vty_out(vty, "Warning...%s", VTY_NEWLINE);
437 break;
438 case CMD_ERR_AMBIGUOUS:
439 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
440 break;
441 case CMD_ERR_NO_MATCH:
442 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
443 break;
444 case CMD_ERR_INCOMPLETE:
445 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
446 break;
447 }
448 cmd_free_strvec(vline);
449
450 return ret;
451}
452
453static const char telnet_backward_char = 0x08;
454static const char telnet_space_char = ' ';
455
456/* Basic function to write buffer to vty. */
457static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
458{
459 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
460 return;
461
462 /* Should we do buffering here ? And make vty_flush (vty) ? */
463 buffer_put(vty->obuf, buf, nbytes);
464}
465
466/* Ensure length of input buffer. Is buffer is short, double it. */
467static void vty_ensure(struct vty *vty, int length)
468{
469 if (vty->max <= length) {
470 vty->max *= 2;
471 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
472 // FIXME: check return
473 }
474}
475
476/* Basic function to insert character into vty. */
477static void vty_self_insert(struct vty *vty, char c)
478{
479 int i;
480 int length;
481
482 vty_ensure(vty, vty->length + 1);
483 length = vty->length - vty->cp;
484 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
485 vty->buf[vty->cp] = c;
486
487 vty_write(vty, &vty->buf[vty->cp], length + 1);
488 for (i = 0; i < length; i++)
489 vty_write(vty, &telnet_backward_char, 1);
490
491 vty->cp++;
492 vty->length++;
493}
494
495/* Self insert character 'c' in overwrite mode. */
496static void vty_self_insert_overwrite(struct vty *vty, char c)
497{
498 vty_ensure(vty, vty->length + 1);
499 vty->buf[vty->cp++] = c;
500
501 if (vty->cp > vty->length)
502 vty->length++;
503
504 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
505 return;
506
507 vty_write(vty, &c, 1);
508}
509
510/* Insert a word into vty interface with overwrite mode. */
511static void vty_insert_word_overwrite(struct vty *vty, char *str)
512{
513 int len = strlen(str);
514 vty_write(vty, str, len);
515 strcpy(&vty->buf[vty->cp], str);
516 vty->cp += len;
517 vty->length = vty->cp;
518}
519
520/* Forward character. */
521static void vty_forward_char(struct vty *vty)
522{
523 if (vty->cp < vty->length) {
524 vty_write(vty, &vty->buf[vty->cp], 1);
525 vty->cp++;
526 }
527}
528
529/* Backward character. */
530static void vty_backward_char(struct vty *vty)
531{
532 if (vty->cp > 0) {
533 vty->cp--;
534 vty_write(vty, &telnet_backward_char, 1);
535 }
536}
537
538/* Move to the beginning of the line. */
539static void vty_beginning_of_line(struct vty *vty)
540{
541 while (vty->cp)
542 vty_backward_char(vty);
543}
544
545/* Move to the end of the line. */
546static void vty_end_of_line(struct vty *vty)
547{
548 while (vty->cp < vty->length)
549 vty_forward_char(vty);
550}
551
552/* Add current command line to the history buffer. */
553static void vty_hist_add(struct vty *vty)
554{
555 int index;
556
557 if (vty->length == 0)
558 return;
559
560 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
561
562 /* Ignore the same string as previous one. */
563 if (vty->hist[index])
564 if (strcmp(vty->buf, vty->hist[index]) == 0) {
565 vty->hp = vty->hindex;
566 return;
567 }
568
569 /* Insert history entry. */
570 if (vty->hist[vty->hindex])
571 talloc_free(vty->hist[vty->hindex]);
572 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
573
574 /* History index rotation. */
575 vty->hindex++;
576 if (vty->hindex == VTY_MAXHIST)
577 vty->hindex = 0;
578
579 vty->hp = vty->hindex;
580}
581
582/* Get telnet window size. */
583static int
584vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
585{
586#ifdef TELNET_OPTION_DEBUG
587 int i;
588
589 for (i = 0; i < nbytes; i++)
590 {
591 switch (buf[i])
592 {
593 case IAC:
594 vty_out (vty, "IAC ");
595 break;
596 case WILL:
597 vty_out (vty, "WILL ");
598 break;
599 case WONT:
600 vty_out (vty, "WONT ");
601 break;
602 case DO:
603 vty_out (vty, "DO ");
604 break;
605 case DONT:
606 vty_out (vty, "DONT ");
607 break;
608 case SB:
609 vty_out (vty, "SB ");
610 break;
611 case SE:
612 vty_out (vty, "SE ");
613 break;
614 case TELOPT_ECHO:
615 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
616 break;
617 case TELOPT_SGA:
618 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
619 break;
620 case TELOPT_NAWS:
621 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
622 break;
623 default:
624 vty_out (vty, "%x ", buf[i]);
625 break;
626 }
627 }
628 vty_out (vty, "%s", VTY_NEWLINE);
629
630#endif /* TELNET_OPTION_DEBUG */
631
632 switch (buf[0])
633 {
634 case SB:
635 vty->sb_len = 0;
636 vty->iac_sb_in_progress = 1;
637 return 0;
638 break;
639 case SE:
640 {
641 if (!vty->iac_sb_in_progress)
642 return 0;
643
644 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
645 {
646 vty->iac_sb_in_progress = 0;
647 return 0;
648 }
649 switch (vty->sb_buf[0])
650 {
651 case TELOPT_NAWS:
652 if (vty->sb_len != TELNET_NAWS_SB_LEN)
653 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
654 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100655 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200656 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
657 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
658 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100659 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200660 else
661 {
662 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
663 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
664#ifdef TELNET_OPTION_DEBUG
665 vty_out(vty, "TELNET NAWS window size negotiation completed: "
666 "width %d, height %d%s",
667 vty->width, vty->height, VTY_NEWLINE);
668#endif
669 }
670 break;
671 }
672 vty->iac_sb_in_progress = 0;
673 return 0;
674 break;
675 }
676 default:
677 break;
678 }
679 return 1;
680}
681
682/* Execute current command line. */
683static int vty_execute(struct vty *vty)
684{
685 int ret;
686
687 ret = CMD_SUCCESS;
688
689 switch (vty->node) {
690 case AUTH_NODE:
691 case AUTH_ENABLE_NODE:
692 vty_auth(vty, vty->buf);
693 break;
694 default:
695 ret = vty_command(vty, vty->buf);
696 if (vty->type == VTY_TERM)
697 vty_hist_add(vty);
698 break;
699 }
700
701 /* Clear command line buffer. */
702 vty->cp = vty->length = 0;
703 vty_clear_buf(vty);
704
705 if (vty->status != VTY_CLOSE)
706 vty_prompt(vty);
707
708 return ret;
709}
710
711/* Send WILL TELOPT_ECHO to remote server. */
712static void
713vty_will_echo (struct vty *vty)
714{
715 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
716 vty_out (vty, "%s", cmd);
717}
718
719/* Make suppress Go-Ahead telnet option. */
720static void
721vty_will_suppress_go_ahead (struct vty *vty)
722{
723 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
724 vty_out (vty, "%s", cmd);
725}
726
727/* Make don't use linemode over telnet. */
728static void
729vty_dont_linemode (struct vty *vty)
730{
731 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
732 vty_out (vty, "%s", cmd);
733}
734
735/* Use window size. */
736static void
737vty_do_window_size (struct vty *vty)
738{
739 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
740 vty_out (vty, "%s", cmd);
741}
742
743static void vty_kill_line_from_beginning(struct vty *);
744static void vty_redraw_line(struct vty *);
745
746/* Print command line history. This function is called from
747 vty_next_line and vty_previous_line. */
748static void vty_history_print(struct vty *vty)
749{
750 int length;
751
752 vty_kill_line_from_beginning(vty);
753
754 /* Get previous line from history buffer */
755 length = strlen(vty->hist[vty->hp]);
756 memcpy(vty->buf, vty->hist[vty->hp], length);
757 vty->cp = vty->length = length;
758
759 /* Redraw current line */
760 vty_redraw_line(vty);
761}
762
763/* Show next command line history. */
764static void vty_next_line(struct vty *vty)
765{
766 int try_index;
767
768 if (vty->hp == vty->hindex)
769 return;
770
771 /* Try is there history exist or not. */
772 try_index = vty->hp;
773 if (try_index == (VTY_MAXHIST - 1))
774 try_index = 0;
775 else
776 try_index++;
777
778 /* If there is not history return. */
779 if (vty->hist[try_index] == NULL)
780 return;
781 else
782 vty->hp = try_index;
783
784 vty_history_print(vty);
785}
786
787/* Show previous command line history. */
788static void vty_previous_line(struct vty *vty)
789{
790 int try_index;
791
792 try_index = vty->hp;
793 if (try_index == 0)
794 try_index = VTY_MAXHIST - 1;
795 else
796 try_index--;
797
798 if (vty->hist[try_index] == NULL)
799 return;
800 else
801 vty->hp = try_index;
802
803 vty_history_print(vty);
804}
805
806/* This function redraw all of the command line character. */
807static void vty_redraw_line(struct vty *vty)
808{
809 vty_write(vty, vty->buf, vty->length);
810 vty->cp = vty->length;
811}
812
813/* Forward word. */
814static void vty_forward_word(struct vty *vty)
815{
816 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
817 vty_forward_char(vty);
818
819 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
820 vty_forward_char(vty);
821}
822
823/* Backward word without skipping training space. */
824static void vty_backward_pure_word(struct vty *vty)
825{
826 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
827 vty_backward_char(vty);
828}
829
830/* Backward word. */
831static void vty_backward_word(struct vty *vty)
832{
833 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
834 vty_backward_char(vty);
835
836 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
837 vty_backward_char(vty);
838}
839
840/* When '^D' is typed at the beginning of the line we move to the down
841 level. */
842static void vty_down_level(struct vty *vty)
843{
844 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100845 /* call the exit function of the specific node */
846 if (vty->node > CONFIG_NODE)
847 vty_go_parent(vty);
848 else
849 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200850 vty_prompt(vty);
851 vty->cp = 0;
852}
853
854/* When '^Z' is received from vty, move down to the enable mode. */
855static void vty_end_config(struct vty *vty)
856{
857 vty_out(vty, "%s", VTY_NEWLINE);
858
859 /* FIXME: we need to call the exit function of the specific node
860 * in question, not this generic one that doesn't know all nodes */
861 switch (vty->node) {
862 case VIEW_NODE:
863 case ENABLE_NODE:
864 /* Nothing to do. */
865 break;
866 case CONFIG_NODE:
867 case VTY_NODE:
868 vty_config_unlock(vty);
869 vty->node = ENABLE_NODE;
870 break;
Harald Welte28222962011-02-18 20:37:04 +0100871 case CFG_LOG_NODE:
872 vty->node = CONFIG_NODE;
873 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200874 default:
875 /* Unknown node, we have to ignore it. */
876 break;
877 }
878
879 vty_prompt(vty);
880 vty->cp = 0;
881}
882
883/* Delete a charcter at the current point. */
884static void vty_delete_char(struct vty *vty)
885{
886 int i;
887 int size;
888
889 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
890 return;
891
892 if (vty->length == 0) {
893 vty_down_level(vty);
894 return;
895 }
896
897 if (vty->cp == vty->length)
898 return; /* completion need here? */
899
900 size = vty->length - vty->cp;
901
902 vty->length--;
903 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
904 vty->buf[vty->length] = '\0';
905
906 vty_write(vty, &vty->buf[vty->cp], size - 1);
907 vty_write(vty, &telnet_space_char, 1);
908
909 for (i = 0; i < size; i++)
910 vty_write(vty, &telnet_backward_char, 1);
911}
912
913/* Delete a character before the point. */
914static void vty_delete_backward_char(struct vty *vty)
915{
916 if (vty->cp == 0)
917 return;
918
919 vty_backward_char(vty);
920 vty_delete_char(vty);
921}
922
923/* Kill rest of line from current point. */
924static void vty_kill_line(struct vty *vty)
925{
926 int i;
927 int size;
928
929 size = vty->length - vty->cp;
930
931 if (size == 0)
932 return;
933
934 for (i = 0; i < size; i++)
935 vty_write(vty, &telnet_space_char, 1);
936 for (i = 0; i < size; i++)
937 vty_write(vty, &telnet_backward_char, 1);
938
939 memset(&vty->buf[vty->cp], 0, size);
940 vty->length = vty->cp;
941}
942
943/* Kill line from the beginning. */
944static void vty_kill_line_from_beginning(struct vty *vty)
945{
946 vty_beginning_of_line(vty);
947 vty_kill_line(vty);
948}
949
950/* Delete a word before the point. */
951static void vty_forward_kill_word(struct vty *vty)
952{
953 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
954 vty_delete_char(vty);
955 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
956 vty_delete_char(vty);
957}
958
959/* Delete a word before the point. */
960static void vty_backward_kill_word(struct vty *vty)
961{
962 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
963 vty_delete_backward_char(vty);
964 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
965 vty_delete_backward_char(vty);
966}
967
968/* Transpose chars before or at the point. */
969static void vty_transpose_chars(struct vty *vty)
970{
971 char c1, c2;
972
973 /* If length is short or point is near by the beginning of line then
974 return. */
975 if (vty->length < 2 || vty->cp < 1)
976 return;
977
978 /* In case of point is located at the end of the line. */
979 if (vty->cp == vty->length) {
980 c1 = vty->buf[vty->cp - 1];
981 c2 = vty->buf[vty->cp - 2];
982
983 vty_backward_char(vty);
984 vty_backward_char(vty);
985 vty_self_insert_overwrite(vty, c1);
986 vty_self_insert_overwrite(vty, c2);
987 } else {
988 c1 = vty->buf[vty->cp];
989 c2 = vty->buf[vty->cp - 1];
990
991 vty_backward_char(vty);
992 vty_self_insert_overwrite(vty, c1);
993 vty_self_insert_overwrite(vty, c2);
994 }
995}
996
997/* Do completion at vty interface. */
998static void vty_complete_command(struct vty *vty)
999{
1000 int i;
1001 int ret;
1002 char **matched = NULL;
1003 vector vline;
1004
1005 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1006 return;
1007
1008 vline = cmd_make_strvec(vty->buf);
1009 if (vline == NULL)
1010 return;
1011
1012 /* In case of 'help \t'. */
1013 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001014 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001015
1016 matched = cmd_complete_command(vline, vty, &ret);
1017
1018 cmd_free_strvec(vline);
1019
1020 vty_out(vty, "%s", VTY_NEWLINE);
1021 switch (ret) {
1022 case CMD_ERR_AMBIGUOUS:
1023 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1024 vty_prompt(vty);
1025 vty_redraw_line(vty);
1026 break;
1027 case CMD_ERR_NO_MATCH:
1028 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1029 vty_prompt(vty);
1030 vty_redraw_line(vty);
1031 break;
1032 case CMD_COMPLETE_FULL_MATCH:
1033 vty_prompt(vty);
1034 vty_redraw_line(vty);
1035 vty_backward_pure_word(vty);
1036 vty_insert_word_overwrite(vty, matched[0]);
1037 vty_self_insert(vty, ' ');
1038 talloc_free(matched[0]);
1039 break;
1040 case CMD_COMPLETE_MATCH:
1041 vty_prompt(vty);
1042 vty_redraw_line(vty);
1043 vty_backward_pure_word(vty);
1044 vty_insert_word_overwrite(vty, matched[0]);
1045 talloc_free(matched[0]);
1046 break;
1047 case CMD_COMPLETE_LIST_MATCH:
1048 for (i = 0; matched[i] != NULL; i++) {
1049 if (i != 0 && ((i % 6) == 0))
1050 vty_out(vty, "%s", VTY_NEWLINE);
1051 vty_out(vty, "%-10s ", matched[i]);
1052 talloc_free(matched[i]);
1053 }
1054 vty_out(vty, "%s", VTY_NEWLINE);
1055
1056 vty_prompt(vty);
1057 vty_redraw_line(vty);
1058 break;
1059 case CMD_ERR_NOTHING_TODO:
1060 vty_prompt(vty);
1061 vty_redraw_line(vty);
1062 break;
1063 default:
1064 break;
1065 }
1066 if (matched)
1067 vector_only_index_free(matched);
1068}
1069
1070static void
1071vty_describe_fold(struct vty *vty, int cmd_width,
1072 unsigned int desc_width, struct desc *desc)
1073{
1074 char *buf;
1075 const char *cmd, *p;
1076 int pos;
1077
1078 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1079
1080 if (desc_width <= 0) {
1081 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1082 VTY_NEWLINE);
1083 return;
1084 }
1085
1086 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1087 if (!buf)
1088 return;
1089
1090 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1091 for (pos = desc_width; pos > 0; pos--)
1092 if (*(p + pos) == ' ')
1093 break;
1094
1095 if (pos == 0)
1096 break;
1097
1098 strncpy(buf, p, pos);
1099 buf[pos] = '\0';
1100 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1101
1102 cmd = "";
1103 }
1104
1105 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1106
1107 talloc_free(buf);
1108}
1109
1110/* Describe matched command function. */
1111static void vty_describe_command(struct vty *vty)
1112{
1113 int ret;
1114 vector vline;
1115 vector describe;
1116 unsigned int i, width, desc_width;
1117 struct desc *desc, *desc_cr = NULL;
1118
1119 vline = cmd_make_strvec(vty->buf);
1120
1121 /* In case of '> ?'. */
1122 if (vline == NULL) {
1123 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001124 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001125 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001126 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001127
1128 describe = cmd_describe_command(vline, vty, &ret);
1129
1130 vty_out(vty, "%s", VTY_NEWLINE);
1131
1132 /* Ambiguous error. */
1133 switch (ret) {
1134 case CMD_ERR_AMBIGUOUS:
1135 cmd_free_strvec(vline);
1136 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1137 vty_prompt(vty);
1138 vty_redraw_line(vty);
1139 return;
1140 break;
1141 case CMD_ERR_NO_MATCH:
1142 cmd_free_strvec(vline);
1143 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1144 vty_prompt(vty);
1145 vty_redraw_line(vty);
1146 return;
1147 break;
1148 }
1149
1150 /* Get width of command string. */
1151 width = 0;
1152 for (i = 0; i < vector_active(describe); i++)
1153 if ((desc = vector_slot(describe, i)) != NULL) {
1154 unsigned int len;
1155
1156 if (desc->cmd[0] == '\0')
1157 continue;
1158
1159 len = strlen(desc->cmd);
1160 if (desc->cmd[0] == '.')
1161 len--;
1162
1163 if (width < len)
1164 width = len;
1165 }
1166
1167 /* Get width of description string. */
1168 desc_width = vty->width - (width + 6);
1169
1170 /* Print out description. */
1171 for (i = 0; i < vector_active(describe); i++)
1172 if ((desc = vector_slot(describe, i)) != NULL) {
1173 if (desc->cmd[0] == '\0')
1174 continue;
1175
1176 if (strcmp(desc->cmd, "<cr>") == 0) {
1177 desc_cr = desc;
1178 continue;
1179 }
1180
1181 if (!desc->str)
1182 vty_out(vty, " %-s%s",
1183 desc->cmd[0] ==
1184 '.' ? desc->cmd + 1 : desc->cmd,
1185 VTY_NEWLINE);
1186 else if (desc_width >= strlen(desc->str))
1187 vty_out(vty, " %-*s %s%s", width,
1188 desc->cmd[0] ==
1189 '.' ? desc->cmd + 1 : desc->cmd,
1190 desc->str, VTY_NEWLINE);
1191 else
1192 vty_describe_fold(vty, width, desc_width, desc);
1193
1194#if 0
1195 vty_out(vty, " %-*s %s%s", width
1196 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1197 desc->str ? desc->str : "", VTY_NEWLINE);
1198#endif /* 0 */
1199 }
1200
1201 if ((desc = desc_cr)) {
1202 if (!desc->str)
1203 vty_out(vty, " %-s%s",
1204 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1205 VTY_NEWLINE);
1206 else if (desc_width >= strlen(desc->str))
1207 vty_out(vty, " %-*s %s%s", width,
1208 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1209 desc->str, VTY_NEWLINE);
1210 else
1211 vty_describe_fold(vty, width, desc_width, desc);
1212 }
1213
1214 cmd_free_strvec(vline);
1215 vector_free(describe);
1216
1217 vty_prompt(vty);
1218 vty_redraw_line(vty);
1219}
1220
1221/* ^C stop current input and do not add command line to the history. */
1222static void vty_stop_input(struct vty *vty)
1223{
1224 vty->cp = vty->length = 0;
1225 vty_clear_buf(vty);
1226 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001227 vty_prompt(vty);
1228
1229 /* Set history pointer to the latest one. */
1230 vty->hp = vty->hindex;
1231}
1232
1233#define CONTROL(X) ((X) - '@')
1234#define VTY_NORMAL 0
1235#define VTY_PRE_ESCAPE 1
1236#define VTY_ESCAPE 2
1237
1238/* Escape character command map. */
1239static void vty_escape_map(unsigned char c, struct vty *vty)
1240{
1241 switch (c) {
1242 case ('A'):
1243 vty_previous_line(vty);
1244 break;
1245 case ('B'):
1246 vty_next_line(vty);
1247 break;
1248 case ('C'):
1249 vty_forward_char(vty);
1250 break;
1251 case ('D'):
1252 vty_backward_char(vty);
1253 break;
1254 default:
1255 break;
1256 }
1257
1258 /* Go back to normal mode. */
1259 vty->escape = VTY_NORMAL;
1260}
1261
1262/* Quit print out to the buffer. */
1263static void vty_buffer_reset(struct vty *vty)
1264{
1265 buffer_reset(vty->obuf);
1266 vty_prompt(vty);
1267 vty_redraw_line(vty);
1268}
1269
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001270/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001271int vty_read(struct vty *vty)
1272{
1273 int i;
1274 int nbytes;
1275 unsigned char buf[VTY_READ_BUFSIZ];
1276
1277 int vty_sock = vty->fd;
1278
1279 /* Read raw data from socket */
1280 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1281 if (nbytes < 0) {
1282 if (ERRNO_IO_RETRY(errno)) {
1283 vty_event(VTY_READ, vty_sock, vty);
1284 return 0;
1285 }
1286 }
1287 buffer_reset(vty->obuf);
1288 vty->status = VTY_CLOSE;
1289 }
1290
1291 for (i = 0; i < nbytes; i++) {
1292 if (buf[i] == IAC) {
1293 if (!vty->iac) {
1294 vty->iac = 1;
1295 continue;
1296 } else {
1297 vty->iac = 0;
1298 }
1299 }
1300
1301 if (vty->iac_sb_in_progress && !vty->iac) {
1302 if (vty->sb_len < sizeof(vty->sb_buf))
1303 vty->sb_buf[vty->sb_len] = buf[i];
1304 vty->sb_len++;
1305 continue;
1306 }
1307
1308 if (vty->iac) {
1309 /* In case of telnet command */
1310 int ret = 0;
1311 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1312 vty->iac = 0;
1313 i += ret;
1314 continue;
1315 }
1316
1317 if (vty->status == VTY_MORE) {
1318 switch (buf[i]) {
1319 case CONTROL('C'):
1320 case 'q':
1321 case 'Q':
1322 vty_buffer_reset(vty);
1323 break;
1324#if 0 /* More line does not work for "show ip bgp". */
1325 case '\n':
1326 case '\r':
1327 vty->status = VTY_MORELINE;
1328 break;
1329#endif
1330 default:
1331 break;
1332 }
1333 continue;
1334 }
1335
1336 /* Escape character. */
1337 if (vty->escape == VTY_ESCAPE) {
1338 vty_escape_map(buf[i], vty);
1339 continue;
1340 }
1341
1342 /* Pre-escape status. */
1343 if (vty->escape == VTY_PRE_ESCAPE) {
1344 switch (buf[i]) {
1345 case '[':
1346 vty->escape = VTY_ESCAPE;
1347 break;
1348 case 'b':
1349 vty_backward_word(vty);
1350 vty->escape = VTY_NORMAL;
1351 break;
1352 case 'f':
1353 vty_forward_word(vty);
1354 vty->escape = VTY_NORMAL;
1355 break;
1356 case 'd':
1357 vty_forward_kill_word(vty);
1358 vty->escape = VTY_NORMAL;
1359 break;
1360 case CONTROL('H'):
1361 case 0x7f:
1362 vty_backward_kill_word(vty);
1363 vty->escape = VTY_NORMAL;
1364 break;
1365 default:
1366 vty->escape = VTY_NORMAL;
1367 break;
1368 }
1369 continue;
1370 }
1371
1372 switch (buf[i]) {
1373 case CONTROL('A'):
1374 vty_beginning_of_line(vty);
1375 break;
1376 case CONTROL('B'):
1377 vty_backward_char(vty);
1378 break;
1379 case CONTROL('C'):
1380 vty_stop_input(vty);
1381 break;
1382 case CONTROL('D'):
1383 vty_delete_char(vty);
1384 break;
1385 case CONTROL('E'):
1386 vty_end_of_line(vty);
1387 break;
1388 case CONTROL('F'):
1389 vty_forward_char(vty);
1390 break;
1391 case CONTROL('H'):
1392 case 0x7f:
1393 vty_delete_backward_char(vty);
1394 break;
1395 case CONTROL('K'):
1396 vty_kill_line(vty);
1397 break;
1398 case CONTROL('N'):
1399 vty_next_line(vty);
1400 break;
1401 case CONTROL('P'):
1402 vty_previous_line(vty);
1403 break;
1404 case CONTROL('T'):
1405 vty_transpose_chars(vty);
1406 break;
1407 case CONTROL('U'):
1408 vty_kill_line_from_beginning(vty);
1409 break;
1410 case CONTROL('W'):
1411 vty_backward_kill_word(vty);
1412 break;
1413 case CONTROL('Z'):
1414 vty_end_config(vty);
1415 break;
1416 case '\n':
1417 case '\r':
1418 vty_out(vty, "%s", VTY_NEWLINE);
1419 vty_execute(vty);
1420 break;
1421 case '\t':
1422 vty_complete_command(vty);
1423 break;
1424 case '?':
1425 if (vty->node == AUTH_NODE
1426 || vty->node == AUTH_ENABLE_NODE)
1427 vty_self_insert(vty, buf[i]);
1428 else
1429 vty_describe_command(vty);
1430 break;
1431 case '\033':
1432 if (i + 1 < nbytes && buf[i + 1] == '[') {
1433 vty->escape = VTY_ESCAPE;
1434 i++;
1435 } else
1436 vty->escape = VTY_PRE_ESCAPE;
1437 break;
1438 default:
1439 if (buf[i] > 31 && buf[i] < 127)
1440 vty_self_insert(vty, buf[i]);
1441 break;
1442 }
1443 }
1444
1445 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001446 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001447 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001448 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001449 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001450 vty_event(VTY_WRITE, vty_sock, vty);
1451 vty_event(VTY_READ, vty_sock, vty);
1452 }
1453 return 0;
1454}
1455
1456/* Read up configuration file */
1457static int
1458vty_read_file(FILE *confp, void *priv)
1459{
1460 int ret;
1461 struct vty *vty;
1462
1463 vty = vty_new();
1464 vty->fd = 0;
1465 vty->type = VTY_FILE;
1466 vty->node = CONFIG_NODE;
1467 vty->priv = priv;
1468
1469 ret = config_from_file(vty, confp);
1470
1471 if (ret != CMD_SUCCESS) {
1472 switch (ret) {
1473 case CMD_ERR_AMBIGUOUS:
1474 fprintf(stderr, "Ambiguous command.\n");
1475 break;
1476 case CMD_ERR_NO_MATCH:
1477 fprintf(stderr, "There is no such command.\n");
1478 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001479 case CMD_ERR_INVALID_INDENT:
1480 fprintf(stderr,
1481 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1482 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1483 "allowed, but their sequence must not change within a child block.\n");
1484 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001485 }
Keith03516d62017-09-04 11:19:13 +02001486 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001487 "line:\n%s\n", vty->buf);
1488 vty_close(vty);
1489 return -EINVAL;
1490 }
1491
1492 vty_close(vty);
1493 return 0;
1494}
1495
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001496/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001497struct vty *
1498vty_create (int vty_sock, void *priv)
1499{
1500 struct vty *vty;
1501
Alexander Couzensab383e62018-07-17 19:02:34 +02001502 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001503
1504 tcgetattr(vty_sock, &t);
1505 cfmakeraw(&t);
1506 tcsetattr(vty_sock, TCSANOW, &t);
1507
1508 /* Allocate new vty structure and set up default values. */
1509 vty = vty_new ();
1510 vty->fd = vty_sock;
1511 vty->priv = priv;
1512 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001513 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001514 {
1515 if (host.advanced)
1516 vty->node = ENABLE_NODE;
1517 else
1518 vty->node = VIEW_NODE;
1519 }
1520 else
1521 vty->node = AUTH_NODE;
1522 vty->fail = 0;
1523 vty->cp = 0;
1524 vty_clear_buf (vty);
1525 vty->length = 0;
1526 memset (vty->hist, 0, sizeof (vty->hist));
1527 vty->hp = 0;
1528 vty->hindex = 0;
1529 vector_set_index (vtyvec, vty_sock, vty);
1530 vty->status = VTY_NORMAL;
1531 if (host.lines >= 0)
1532 vty->lines = host.lines;
1533 else
1534 vty->lines = -1;
1535
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001536 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001537 {
1538 /* Vty is not available if password isn't set. */
1539 if (host.password == NULL && host.password_encrypt == NULL)
1540 {
1541 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1542 vty->status = VTY_CLOSE;
1543 vty_close (vty);
1544 return NULL;
1545 }
1546 }
1547
1548 /* Say hello to the world. */
1549 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001550 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001551 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1552
1553 /* Setting up terminal. */
1554 vty_will_echo (vty);
1555 vty_will_suppress_go_ahead (vty);
1556
1557 vty_dont_linemode (vty);
1558 vty_do_window_size (vty);
1559 /* vty_dont_lflow_ahead (vty); */
1560
1561 vty_prompt (vty);
1562
1563 /* Add read/write thread. */
1564 vty_event (VTY_WRITE, vty_sock, vty);
1565 vty_event (VTY_READ, vty_sock, vty);
1566
1567 return vty;
1568}
1569
1570DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1571{
1572 unsigned int i;
1573 struct vty *v;
1574
1575 for (i = 0; i < vector_active(vtyvec); i++)
1576 if ((v = vector_slot(vtyvec, i)) != NULL)
1577 vty_out(vty, "%svty[%d] %s",
1578 v->config ? "*" : " ", i, VTY_NEWLINE);
1579 return CMD_SUCCESS;
1580}
1581
1582/* Move to vty configuration mode. */
1583DEFUN(line_vty,
1584 line_vty_cmd,
1585 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1586{
1587 vty->node = VTY_NODE;
1588 return CMD_SUCCESS;
1589}
1590
1591/* vty login. */
1592DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1593{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001594 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001595 return CMD_SUCCESS;
1596}
1597
1598DEFUN(no_vty_login,
1599 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1600{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001601 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001602 return CMD_SUCCESS;
1603}
1604
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001605/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001606DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001607 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001608 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1609 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001610{
1611 talloc_free((void*)vty_bind_addr);
1612 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001613 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001614 return CMD_SUCCESS;
1615}
1616
1617const char *vty_get_bind_addr(void)
1618{
1619 if (!vty_bind_addr)
1620 return VTY_BIND_ADDR_DEFAULT;
1621 return vty_bind_addr;
1622}
1623
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001624int vty_get_bind_port(int default_port)
1625{
1626 if (vty_bind_port >= 0)
1627 return vty_bind_port;
1628 return default_port;
1629}
1630
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001631DEFUN(service_advanced_vty,
1632 service_advanced_vty_cmd,
1633 "service advanced-vty",
1634 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1635{
1636 host.advanced = 1;
1637 return CMD_SUCCESS;
1638}
1639
1640DEFUN(no_service_advanced_vty,
1641 no_service_advanced_vty_cmd,
1642 "no service advanced-vty",
1643 NO_STR
1644 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1645{
1646 host.advanced = 0;
1647 return CMD_SUCCESS;
1648}
1649
1650DEFUN(terminal_monitor,
1651 terminal_monitor_cmd,
1652 "terminal monitor",
1653 "Set terminal line parameters\n"
1654 "Copy debug output to the current terminal line\n")
1655{
1656 vty->monitor = 1;
1657 return CMD_SUCCESS;
1658}
1659
1660DEFUN(terminal_no_monitor,
1661 terminal_no_monitor_cmd,
1662 "terminal no monitor",
1663 "Set terminal line parameters\n"
1664 NO_STR "Copy debug output to the current terminal line\n")
1665{
1666 vty->monitor = 0;
1667 return CMD_SUCCESS;
1668}
1669
1670DEFUN(show_history,
1671 show_history_cmd,
1672 "show history", SHOW_STR "Display the session command history\n")
1673{
1674 int index;
1675
1676 for (index = vty->hindex + 1; index != vty->hindex;) {
1677 if (index == VTY_MAXHIST) {
1678 index = 0;
1679 continue;
1680 }
1681
1682 if (vty->hist[index] != NULL)
1683 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1684
1685 index++;
1686 }
1687
1688 return CMD_SUCCESS;
1689}
1690
1691/* Display current configuration. */
1692static int vty_config_write(struct vty *vty)
1693{
1694 vty_out(vty, "line vty%s", VTY_NEWLINE);
1695
1696 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001697 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001698 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001699 else
1700 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001701
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001702 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001703 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1704 if (vty_bind_port >= 0) {
1705 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1706 vty_bind_port, VTY_NEWLINE);
1707 } else {
1708 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1709 }
1710 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001711
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001712 vty_out(vty, "!%s", VTY_NEWLINE);
1713
1714 return CMD_SUCCESS;
1715}
1716
1717struct cmd_node vty_node = {
1718 VTY_NODE,
1719 "%s(config-line)# ",
1720 1,
1721};
1722
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001723/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001724void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001725{
1726 unsigned int i;
1727 struct vty *vty;
1728 struct thread *vty_serv_thread;
1729
1730 for (i = 0; i < vector_active(vtyvec); i++)
1731 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1732 buffer_reset(vty->obuf);
1733 vty->status = VTY_CLOSE;
1734 vty_close(vty);
1735 }
1736
1737 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1738 if ((vty_serv_thread =
1739 vector_slot(Vvty_serv_thread, i)) != NULL) {
1740 //thread_cancel (vty_serv_thread);
1741 vector_slot(Vvty_serv_thread, i) = NULL;
1742 close(i);
1743 }
1744}
1745
1746static void vty_save_cwd(void)
1747{
1748 char cwd[MAXPATHLEN];
1749 char *c ;
1750
1751 c = getcwd(cwd, MAXPATHLEN);
1752
1753 if (!c) {
1754 if (chdir(SYSCONFDIR) != 0)
1755 perror("chdir failed");
1756 if (getcwd(cwd, MAXPATHLEN) == NULL)
1757 perror("getcwd failed");
1758 }
1759
1760 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1761 strcpy(vty_cwd, cwd);
1762}
1763
Harald Welte95b2b472011-07-16 11:58:09 +02001764char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001765{
1766 return vty_cwd;
1767}
1768
1769int vty_shell_serv(struct vty *vty)
1770{
1771 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1772}
1773
Harald Welte95b2b472011-07-16 11:58:09 +02001774void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001775{
1776 vtyvec = vector_init(VECTOR_MIN_SIZE);
1777}
1778
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001779/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001780 * \param[in] app_info application information
1781 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001782/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001783void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001784{
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301785 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001786 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1787 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1788
1789 cmd_init(1);
1790
Harald Welte237f6242010-05-25 23:00:45 +02001791 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001792
1793 /* For further configuration read, preserve current directory. */
1794 vty_save_cwd();
1795
1796 vtyvec = vector_init(VECTOR_MIN_SIZE);
1797
1798 /* Install bgp top node. */
1799 install_node(&vty_node, vty_config_write);
1800
1801 install_element_ve(&config_who_cmd);
1802 install_element_ve(&show_history_cmd);
1803 install_element(CONFIG_NODE, &line_vty_cmd);
1804 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1805 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1806 install_element(CONFIG_NODE, &show_history_cmd);
1807 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1808 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1809
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001810 install_element(VTY_NODE, &vty_login_cmd);
1811 install_element(VTY_NODE, &no_vty_login_cmd);
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001812 install_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001813}
1814
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001815/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001816 * \param[in] file_name file name of the configuration file
1817 * \param[in] priv private data to be passed to \ref vty_read_file
1818 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001819int vty_read_config_file(const char *file_name, void *priv)
1820{
1821 FILE *cfile;
1822 int rc;
1823
1824 cfile = fopen(file_name, "r");
1825 if (!cfile)
1826 return -ENOENT;
1827
1828 rc = vty_read_file(cfile, priv);
1829 fclose(cfile);
1830
1831 host_config_set(file_name);
1832
1833 return rc;
1834}
Harald Welte7acb30c2011-08-17 17:13:48 +02001835
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001836/*! @} */