blob: 70f6811a2f513f8f8d7f3a4430903fbcccf96e5d [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"
93
Harald Welte3fb0b6f2010-05-19 19:02:52 +020094/* Configure lock. */
95static int vty_config;
96
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -070097static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020098
99void *tall_vty_ctx;
100
101static void vty_clear_buf(struct vty *vty)
102{
103 memset(vty->buf, 0, vty->max);
104}
105
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200106/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200107struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200108{
109 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
110
111 if (!new)
112 goto out;
113
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200114 INIT_LLIST_HEAD(&new->parent_nodes);
115
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200116 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
117 if (!new->obuf)
118 goto out_new;
119 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
120 if (!new->buf)
121 goto out_obuf;
122
123 new->max = VTY_BUFSIZ;
124
125 return new;
126
127out_obuf:
128 buffer_free(new->obuf);
129out_new:
130 talloc_free(new);
131 new = NULL;
132out:
133 return new;
134}
135
136/* Authentication of vty */
137static void vty_auth(struct vty *vty, char *buf)
138{
139 char *passwd = NULL;
140 enum node_type next_node = 0;
141 int fail;
142 char *crypt(const char *, const char *);
143
144 switch (vty->node) {
145 case AUTH_NODE:
146#ifdef VTY_CRYPT_PW
147 if (host.encrypt)
148 passwd = host.password_encrypt;
149 else
150#endif
151 passwd = host.password;
152 if (host.advanced)
153 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
154 else
155 next_node = VIEW_NODE;
156 break;
157 case AUTH_ENABLE_NODE:
158#ifdef VTY_CRYPT_PW
159 if (host.encrypt)
160 passwd = host.enable_encrypt;
161 else
162#endif
163 passwd = host.enable;
164 next_node = ENABLE_NODE;
165 break;
166 }
167
168 if (passwd) {
169#ifdef VTY_CRYPT_PW
170 if (host.encrypt)
171 fail = strcmp(crypt(buf, passwd), passwd);
172 else
173#endif
174 fail = strcmp(buf, passwd);
175 } else
176 fail = 1;
177
178 if (!fail) {
179 vty->fail = 0;
180 vty->node = next_node; /* Success ! */
181 } else {
182 vty->fail++;
183 if (vty->fail >= 3) {
184 if (vty->node == AUTH_NODE) {
185 vty_out(vty,
186 "%% Bad passwords, too many failures!%s",
187 VTY_NEWLINE);
188 vty->status = VTY_CLOSE;
189 } else {
190 /* AUTH_ENABLE_NODE */
191 vty->fail = 0;
192 vty_out(vty,
193 "%% Bad enable passwords, too many failures!%s",
194 VTY_NEWLINE);
195 vty->node = VIEW_NODE;
196 }
197 }
198 }
199}
200
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200201/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200202void vty_close(struct vty *vty)
203{
204 int i;
205
206 if (vty->obuf) {
207 /* Flush buffer. */
208 buffer_flush_all(vty->obuf, vty->fd);
209
210 /* Free input buffer. */
211 buffer_free(vty->obuf);
212 vty->obuf = NULL;
213 }
214
215 /* Free command history. */
216 for (i = 0; i < VTY_MAXHIST; i++)
217 if (vty->hist[i])
218 talloc_free(vty->hist[i]);
219
220 /* Unset vector. */
221 vector_unset(vtyvec, vty->fd);
222
223 /* Close socket. */
Harald Welte2e0a9452018-10-21 13:22:52 +0200224 if (vty->fd > 0) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200225 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200226 vty->fd = -1;
227 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200228
229 if (vty->buf) {
230 talloc_free(vty->buf);
231 vty->buf = NULL;
232 }
233
234 /* Check configure. */
235 vty_config_unlock(vty);
236
237 /* VTY_CLOSED is handled by the telnet_interface */
238 vty_event(VTY_CLOSED, vty->fd, vty);
239
240 /* OK free vty. */
241 talloc_free(vty);
242}
243
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200244/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200245int vty_shell(struct vty *vty)
246{
247 return vty->type == VTY_SHELL ? 1 : 0;
248}
249
250
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200251/*! VTY standard output function
Harald Welte7acb30c2011-08-17 17:13:48 +0200252 * \param[in] vty VTY to which we should print
253 * \param[in] format variable-length format string
254 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255int vty_out(struct vty *vty, const char *format, ...)
256{
257 va_list args;
258 int len = 0;
259 int size = 1024;
260 char buf[1024];
261 char *p = NULL;
262
263 if (vty_shell(vty)) {
264 va_start(args, format);
265 vprintf(format, args);
266 va_end(args);
267 } else {
268 /* Try to write to initial buffer. */
269 va_start(args, format);
270 len = vsnprintf(buf, sizeof buf, format, args);
271 va_end(args);
272
273 /* Initial buffer is not enough. */
274 if (len < 0 || len >= size) {
275 while (1) {
276 if (len > -1)
277 size = len + 1;
278 else
279 size = size * 2;
280
281 p = talloc_realloc_size(vty, p, size);
282 if (!p)
283 return -1;
284
285 va_start(args, format);
286 len = vsnprintf(p, size, format, args);
287 va_end(args);
288
289 if (len > -1 && len < size)
290 break;
291 }
292 }
293
294 /* When initial buffer is enough to store all output. */
295 if (!p)
296 p = buf;
297
298 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100299 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200300
301 /* If p is not different with buf, it is allocated buffer. */
302 if (p != buf)
303 talloc_free(p);
304 }
305
306 vty_event(VTY_WRITE, vty->fd, vty);
307
308 return len;
309}
310
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200311/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200312int vty_out_newline(struct vty *vty)
313{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200314 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200315 buffer_put(vty->obuf, p, strlen(p));
316 return 0;
317}
318
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200319/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800320void *vty_current_index(struct vty *vty)
321{
322 return vty->index;
323}
Harald Welte7acb30c2011-08-17 17:13:48 +0200324
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200325/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800326int vty_current_node(struct vty *vty)
327{
328 return vty->node;
329}
330
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200331/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200332 * \param[in] vty VTY to which the config shall be locked
333 * \returns 1 on success, 0 on error
334 *
335 * This shall be used to make sure only one VTY at a given time has
336 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200337int vty_config_lock(struct vty *vty)
338{
339 if (vty_config == 0) {
340 vty->config = 1;
341 vty_config = 1;
342 }
343 return vty->config;
344}
345
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200346/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200347 * \param[in] vty VTY from which the configuration shall be unlocked
348 * \returns 0 in case of success
349 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200350int vty_config_unlock(struct vty *vty)
351{
352 if (vty_config == 1 && vty->config == 1) {
353 vty->config = 0;
354 vty_config = 0;
355 }
356 return vty->config;
357}
358
359/* Say hello to vty interface. */
360void vty_hello(struct vty *vty)
361{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200362 const char *app_name = "<unnamed>";
363
364 if (host.app_info->name)
365 app_name = host.app_info->name;
366
Harald Weltea2501a22018-03-23 19:33:27 +0100367 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800368 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200369
370 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200371 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200372
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200373 if (host.motdfile) {
374 FILE *f;
375 char buf[4096];
376
377 f = fopen(host.motdfile, "r");
378 if (f) {
379 while (fgets(buf, sizeof(buf), f)) {
380 char *s;
381 /* work backwards to ignore trailling isspace() */
382 for (s = buf + strlen(buf);
383 (s > buf) && isspace(*(s - 1)); s--) ;
384 *s = '\0';
385 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
386 }
387 fclose(f);
388 } else
389 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
390 } else if (host.motd)
391 vty_out(vty, "%s", host.motd);
392}
393
394/* Put out prompt and wait input from user. */
395static void vty_prompt(struct vty *vty)
396{
397 struct utsname names;
398 const char *hostname;
399
400 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100401 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200402 if (!hostname) {
403 uname(&names);
404 hostname = names.nodename;
405 }
406 vty_out(vty, cmd_prompt(vty->node), hostname);
407 }
408}
409
410/* Command execution over the vty interface. */
411static int vty_command(struct vty *vty, char *buf)
412{
413 int ret;
414 vector vline;
415
416 /* Split readline string up into the vector */
417 vline = cmd_make_strvec(buf);
418
419 if (vline == NULL)
420 return CMD_SUCCESS;
421
422 ret = cmd_execute_command(vline, vty, NULL, 0);
423 if (ret != CMD_SUCCESS)
424 switch (ret) {
425 case CMD_WARNING:
426 if (vty->type == VTY_FILE)
427 vty_out(vty, "Warning...%s", VTY_NEWLINE);
428 break;
429 case CMD_ERR_AMBIGUOUS:
430 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
431 break;
432 case CMD_ERR_NO_MATCH:
433 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
434 break;
435 case CMD_ERR_INCOMPLETE:
436 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
437 break;
438 }
439 cmd_free_strvec(vline);
440
441 return ret;
442}
443
444static const char telnet_backward_char = 0x08;
445static const char telnet_space_char = ' ';
446
447/* Basic function to write buffer to vty. */
448static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
449{
450 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
451 return;
452
453 /* Should we do buffering here ? And make vty_flush (vty) ? */
454 buffer_put(vty->obuf, buf, nbytes);
455}
456
457/* Ensure length of input buffer. Is buffer is short, double it. */
458static void vty_ensure(struct vty *vty, int length)
459{
460 if (vty->max <= length) {
461 vty->max *= 2;
462 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
463 // FIXME: check return
464 }
465}
466
467/* Basic function to insert character into vty. */
468static void vty_self_insert(struct vty *vty, char c)
469{
470 int i;
471 int length;
472
473 vty_ensure(vty, vty->length + 1);
474 length = vty->length - vty->cp;
475 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
476 vty->buf[vty->cp] = c;
477
478 vty_write(vty, &vty->buf[vty->cp], length + 1);
479 for (i = 0; i < length; i++)
480 vty_write(vty, &telnet_backward_char, 1);
481
482 vty->cp++;
483 vty->length++;
484}
485
486/* Self insert character 'c' in overwrite mode. */
487static void vty_self_insert_overwrite(struct vty *vty, char c)
488{
489 vty_ensure(vty, vty->length + 1);
490 vty->buf[vty->cp++] = c;
491
492 if (vty->cp > vty->length)
493 vty->length++;
494
495 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
496 return;
497
498 vty_write(vty, &c, 1);
499}
500
501/* Insert a word into vty interface with overwrite mode. */
502static void vty_insert_word_overwrite(struct vty *vty, char *str)
503{
504 int len = strlen(str);
505 vty_write(vty, str, len);
506 strcpy(&vty->buf[vty->cp], str);
507 vty->cp += len;
508 vty->length = vty->cp;
509}
510
511/* Forward character. */
512static void vty_forward_char(struct vty *vty)
513{
514 if (vty->cp < vty->length) {
515 vty_write(vty, &vty->buf[vty->cp], 1);
516 vty->cp++;
517 }
518}
519
520/* Backward character. */
521static void vty_backward_char(struct vty *vty)
522{
523 if (vty->cp > 0) {
524 vty->cp--;
525 vty_write(vty, &telnet_backward_char, 1);
526 }
527}
528
529/* Move to the beginning of the line. */
530static void vty_beginning_of_line(struct vty *vty)
531{
532 while (vty->cp)
533 vty_backward_char(vty);
534}
535
536/* Move to the end of the line. */
537static void vty_end_of_line(struct vty *vty)
538{
539 while (vty->cp < vty->length)
540 vty_forward_char(vty);
541}
542
543/* Add current command line to the history buffer. */
544static void vty_hist_add(struct vty *vty)
545{
546 int index;
547
548 if (vty->length == 0)
549 return;
550
551 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
552
553 /* Ignore the same string as previous one. */
554 if (vty->hist[index])
555 if (strcmp(vty->buf, vty->hist[index]) == 0) {
556 vty->hp = vty->hindex;
557 return;
558 }
559
560 /* Insert history entry. */
561 if (vty->hist[vty->hindex])
562 talloc_free(vty->hist[vty->hindex]);
563 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
564
565 /* History index rotation. */
566 vty->hindex++;
567 if (vty->hindex == VTY_MAXHIST)
568 vty->hindex = 0;
569
570 vty->hp = vty->hindex;
571}
572
573/* Get telnet window size. */
574static int
575vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
576{
577#ifdef TELNET_OPTION_DEBUG
578 int i;
579
580 for (i = 0; i < nbytes; i++)
581 {
582 switch (buf[i])
583 {
584 case IAC:
585 vty_out (vty, "IAC ");
586 break;
587 case WILL:
588 vty_out (vty, "WILL ");
589 break;
590 case WONT:
591 vty_out (vty, "WONT ");
592 break;
593 case DO:
594 vty_out (vty, "DO ");
595 break;
596 case DONT:
597 vty_out (vty, "DONT ");
598 break;
599 case SB:
600 vty_out (vty, "SB ");
601 break;
602 case SE:
603 vty_out (vty, "SE ");
604 break;
605 case TELOPT_ECHO:
606 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
607 break;
608 case TELOPT_SGA:
609 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
610 break;
611 case TELOPT_NAWS:
612 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
613 break;
614 default:
615 vty_out (vty, "%x ", buf[i]);
616 break;
617 }
618 }
619 vty_out (vty, "%s", VTY_NEWLINE);
620
621#endif /* TELNET_OPTION_DEBUG */
622
623 switch (buf[0])
624 {
625 case SB:
626 vty->sb_len = 0;
627 vty->iac_sb_in_progress = 1;
628 return 0;
629 break;
630 case SE:
631 {
632 if (!vty->iac_sb_in_progress)
633 return 0;
634
635 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
636 {
637 vty->iac_sb_in_progress = 0;
638 return 0;
639 }
640 switch (vty->sb_buf[0])
641 {
642 case TELOPT_NAWS:
643 if (vty->sb_len != TELNET_NAWS_SB_LEN)
644 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
645 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100646 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200647 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
648 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
649 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100650 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200651 else
652 {
653 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
654 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
655#ifdef TELNET_OPTION_DEBUG
656 vty_out(vty, "TELNET NAWS window size negotiation completed: "
657 "width %d, height %d%s",
658 vty->width, vty->height, VTY_NEWLINE);
659#endif
660 }
661 break;
662 }
663 vty->iac_sb_in_progress = 0;
664 return 0;
665 break;
666 }
667 default:
668 break;
669 }
670 return 1;
671}
672
673/* Execute current command line. */
674static int vty_execute(struct vty *vty)
675{
676 int ret;
677
678 ret = CMD_SUCCESS;
679
680 switch (vty->node) {
681 case AUTH_NODE:
682 case AUTH_ENABLE_NODE:
683 vty_auth(vty, vty->buf);
684 break;
685 default:
686 ret = vty_command(vty, vty->buf);
687 if (vty->type == VTY_TERM)
688 vty_hist_add(vty);
689 break;
690 }
691
692 /* Clear command line buffer. */
693 vty->cp = vty->length = 0;
694 vty_clear_buf(vty);
695
696 if (vty->status != VTY_CLOSE)
697 vty_prompt(vty);
698
699 return ret;
700}
701
702/* Send WILL TELOPT_ECHO to remote server. */
703static void
704vty_will_echo (struct vty *vty)
705{
706 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
707 vty_out (vty, "%s", cmd);
708}
709
710/* Make suppress Go-Ahead telnet option. */
711static void
712vty_will_suppress_go_ahead (struct vty *vty)
713{
714 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
715 vty_out (vty, "%s", cmd);
716}
717
718/* Make don't use linemode over telnet. */
719static void
720vty_dont_linemode (struct vty *vty)
721{
722 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
723 vty_out (vty, "%s", cmd);
724}
725
726/* Use window size. */
727static void
728vty_do_window_size (struct vty *vty)
729{
730 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
731 vty_out (vty, "%s", cmd);
732}
733
734static void vty_kill_line_from_beginning(struct vty *);
735static void vty_redraw_line(struct vty *);
736
737/* Print command line history. This function is called from
738 vty_next_line and vty_previous_line. */
739static void vty_history_print(struct vty *vty)
740{
741 int length;
742
743 vty_kill_line_from_beginning(vty);
744
745 /* Get previous line from history buffer */
746 length = strlen(vty->hist[vty->hp]);
747 memcpy(vty->buf, vty->hist[vty->hp], length);
748 vty->cp = vty->length = length;
749
750 /* Redraw current line */
751 vty_redraw_line(vty);
752}
753
754/* Show next command line history. */
755static void vty_next_line(struct vty *vty)
756{
757 int try_index;
758
759 if (vty->hp == vty->hindex)
760 return;
761
762 /* Try is there history exist or not. */
763 try_index = vty->hp;
764 if (try_index == (VTY_MAXHIST - 1))
765 try_index = 0;
766 else
767 try_index++;
768
769 /* If there is not history return. */
770 if (vty->hist[try_index] == NULL)
771 return;
772 else
773 vty->hp = try_index;
774
775 vty_history_print(vty);
776}
777
778/* Show previous command line history. */
779static void vty_previous_line(struct vty *vty)
780{
781 int try_index;
782
783 try_index = vty->hp;
784 if (try_index == 0)
785 try_index = VTY_MAXHIST - 1;
786 else
787 try_index--;
788
789 if (vty->hist[try_index] == NULL)
790 return;
791 else
792 vty->hp = try_index;
793
794 vty_history_print(vty);
795}
796
797/* This function redraw all of the command line character. */
798static void vty_redraw_line(struct vty *vty)
799{
800 vty_write(vty, vty->buf, vty->length);
801 vty->cp = vty->length;
802}
803
804/* Forward word. */
805static void vty_forward_word(struct vty *vty)
806{
807 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
808 vty_forward_char(vty);
809
810 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
811 vty_forward_char(vty);
812}
813
814/* Backward word without skipping training space. */
815static void vty_backward_pure_word(struct vty *vty)
816{
817 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
818 vty_backward_char(vty);
819}
820
821/* Backward word. */
822static void vty_backward_word(struct vty *vty)
823{
824 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
825 vty_backward_char(vty);
826
827 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
828 vty_backward_char(vty);
829}
830
831/* When '^D' is typed at the beginning of the line we move to the down
832 level. */
833static void vty_down_level(struct vty *vty)
834{
835 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100836 /* call the exit function of the specific node */
837 if (vty->node > CONFIG_NODE)
838 vty_go_parent(vty);
839 else
840 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200841 vty_prompt(vty);
842 vty->cp = 0;
843}
844
845/* When '^Z' is received from vty, move down to the enable mode. */
846static void vty_end_config(struct vty *vty)
847{
848 vty_out(vty, "%s", VTY_NEWLINE);
849
850 /* FIXME: we need to call the exit function of the specific node
851 * in question, not this generic one that doesn't know all nodes */
852 switch (vty->node) {
853 case VIEW_NODE:
854 case ENABLE_NODE:
855 /* Nothing to do. */
856 break;
857 case CONFIG_NODE:
858 case VTY_NODE:
859 vty_config_unlock(vty);
860 vty->node = ENABLE_NODE;
861 break;
Harald Welte28222962011-02-18 20:37:04 +0100862 case CFG_LOG_NODE:
863 vty->node = CONFIG_NODE;
864 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200865 default:
866 /* Unknown node, we have to ignore it. */
867 break;
868 }
869
870 vty_prompt(vty);
871 vty->cp = 0;
872}
873
874/* Delete a charcter at the current point. */
875static void vty_delete_char(struct vty *vty)
876{
877 int i;
878 int size;
879
880 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
881 return;
882
883 if (vty->length == 0) {
884 vty_down_level(vty);
885 return;
886 }
887
888 if (vty->cp == vty->length)
889 return; /* completion need here? */
890
891 size = vty->length - vty->cp;
892
893 vty->length--;
894 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
895 vty->buf[vty->length] = '\0';
896
897 vty_write(vty, &vty->buf[vty->cp], size - 1);
898 vty_write(vty, &telnet_space_char, 1);
899
900 for (i = 0; i < size; i++)
901 vty_write(vty, &telnet_backward_char, 1);
902}
903
904/* Delete a character before the point. */
905static void vty_delete_backward_char(struct vty *vty)
906{
907 if (vty->cp == 0)
908 return;
909
910 vty_backward_char(vty);
911 vty_delete_char(vty);
912}
913
914/* Kill rest of line from current point. */
915static void vty_kill_line(struct vty *vty)
916{
917 int i;
918 int size;
919
920 size = vty->length - vty->cp;
921
922 if (size == 0)
923 return;
924
925 for (i = 0; i < size; i++)
926 vty_write(vty, &telnet_space_char, 1);
927 for (i = 0; i < size; i++)
928 vty_write(vty, &telnet_backward_char, 1);
929
930 memset(&vty->buf[vty->cp], 0, size);
931 vty->length = vty->cp;
932}
933
934/* Kill line from the beginning. */
935static void vty_kill_line_from_beginning(struct vty *vty)
936{
937 vty_beginning_of_line(vty);
938 vty_kill_line(vty);
939}
940
941/* Delete a word before the point. */
942static void vty_forward_kill_word(struct vty *vty)
943{
944 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
945 vty_delete_char(vty);
946 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
947 vty_delete_char(vty);
948}
949
950/* Delete a word before the point. */
951static void vty_backward_kill_word(struct vty *vty)
952{
953 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
954 vty_delete_backward_char(vty);
955 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
956 vty_delete_backward_char(vty);
957}
958
959/* Transpose chars before or at the point. */
960static void vty_transpose_chars(struct vty *vty)
961{
962 char c1, c2;
963
964 /* If length is short or point is near by the beginning of line then
965 return. */
966 if (vty->length < 2 || vty->cp < 1)
967 return;
968
969 /* In case of point is located at the end of the line. */
970 if (vty->cp == vty->length) {
971 c1 = vty->buf[vty->cp - 1];
972 c2 = vty->buf[vty->cp - 2];
973
974 vty_backward_char(vty);
975 vty_backward_char(vty);
976 vty_self_insert_overwrite(vty, c1);
977 vty_self_insert_overwrite(vty, c2);
978 } else {
979 c1 = vty->buf[vty->cp];
980 c2 = vty->buf[vty->cp - 1];
981
982 vty_backward_char(vty);
983 vty_self_insert_overwrite(vty, c1);
984 vty_self_insert_overwrite(vty, c2);
985 }
986}
987
988/* Do completion at vty interface. */
989static void vty_complete_command(struct vty *vty)
990{
991 int i;
992 int ret;
993 char **matched = NULL;
994 vector vline;
995
996 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
997 return;
998
999 vline = cmd_make_strvec(vty->buf);
1000 if (vline == NULL)
1001 return;
1002
1003 /* In case of 'help \t'. */
1004 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001005 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001006
1007 matched = cmd_complete_command(vline, vty, &ret);
1008
1009 cmd_free_strvec(vline);
1010
1011 vty_out(vty, "%s", VTY_NEWLINE);
1012 switch (ret) {
1013 case CMD_ERR_AMBIGUOUS:
1014 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1015 vty_prompt(vty);
1016 vty_redraw_line(vty);
1017 break;
1018 case CMD_ERR_NO_MATCH:
1019 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1020 vty_prompt(vty);
1021 vty_redraw_line(vty);
1022 break;
1023 case CMD_COMPLETE_FULL_MATCH:
1024 vty_prompt(vty);
1025 vty_redraw_line(vty);
1026 vty_backward_pure_word(vty);
1027 vty_insert_word_overwrite(vty, matched[0]);
1028 vty_self_insert(vty, ' ');
1029 talloc_free(matched[0]);
1030 break;
1031 case CMD_COMPLETE_MATCH:
1032 vty_prompt(vty);
1033 vty_redraw_line(vty);
1034 vty_backward_pure_word(vty);
1035 vty_insert_word_overwrite(vty, matched[0]);
1036 talloc_free(matched[0]);
1037 break;
1038 case CMD_COMPLETE_LIST_MATCH:
1039 for (i = 0; matched[i] != NULL; i++) {
1040 if (i != 0 && ((i % 6) == 0))
1041 vty_out(vty, "%s", VTY_NEWLINE);
1042 vty_out(vty, "%-10s ", matched[i]);
1043 talloc_free(matched[i]);
1044 }
1045 vty_out(vty, "%s", VTY_NEWLINE);
1046
1047 vty_prompt(vty);
1048 vty_redraw_line(vty);
1049 break;
1050 case CMD_ERR_NOTHING_TODO:
1051 vty_prompt(vty);
1052 vty_redraw_line(vty);
1053 break;
1054 default:
1055 break;
1056 }
1057 if (matched)
1058 vector_only_index_free(matched);
1059}
1060
1061static void
1062vty_describe_fold(struct vty *vty, int cmd_width,
1063 unsigned int desc_width, struct desc *desc)
1064{
1065 char *buf;
1066 const char *cmd, *p;
1067 int pos;
1068
1069 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1070
1071 if (desc_width <= 0) {
1072 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1073 VTY_NEWLINE);
1074 return;
1075 }
1076
1077 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1078 if (!buf)
1079 return;
1080
1081 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1082 for (pos = desc_width; pos > 0; pos--)
1083 if (*(p + pos) == ' ')
1084 break;
1085
1086 if (pos == 0)
1087 break;
1088
1089 strncpy(buf, p, pos);
1090 buf[pos] = '\0';
1091 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1092
1093 cmd = "";
1094 }
1095
1096 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1097
1098 talloc_free(buf);
1099}
1100
1101/* Describe matched command function. */
1102static void vty_describe_command(struct vty *vty)
1103{
1104 int ret;
1105 vector vline;
1106 vector describe;
1107 unsigned int i, width, desc_width;
1108 struct desc *desc, *desc_cr = NULL;
1109
1110 vline = cmd_make_strvec(vty->buf);
1111
1112 /* In case of '> ?'. */
1113 if (vline == NULL) {
1114 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001115 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001117 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001118
1119 describe = cmd_describe_command(vline, vty, &ret);
1120
1121 vty_out(vty, "%s", VTY_NEWLINE);
1122
1123 /* Ambiguous error. */
1124 switch (ret) {
1125 case CMD_ERR_AMBIGUOUS:
1126 cmd_free_strvec(vline);
1127 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1128 vty_prompt(vty);
1129 vty_redraw_line(vty);
1130 return;
1131 break;
1132 case CMD_ERR_NO_MATCH:
1133 cmd_free_strvec(vline);
1134 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1135 vty_prompt(vty);
1136 vty_redraw_line(vty);
1137 return;
1138 break;
1139 }
1140
1141 /* Get width of command string. */
1142 width = 0;
1143 for (i = 0; i < vector_active(describe); i++)
1144 if ((desc = vector_slot(describe, i)) != NULL) {
1145 unsigned int len;
1146
1147 if (desc->cmd[0] == '\0')
1148 continue;
1149
1150 len = strlen(desc->cmd);
1151 if (desc->cmd[0] == '.')
1152 len--;
1153
1154 if (width < len)
1155 width = len;
1156 }
1157
1158 /* Get width of description string. */
1159 desc_width = vty->width - (width + 6);
1160
1161 /* Print out description. */
1162 for (i = 0; i < vector_active(describe); i++)
1163 if ((desc = vector_slot(describe, i)) != NULL) {
1164 if (desc->cmd[0] == '\0')
1165 continue;
1166
1167 if (strcmp(desc->cmd, "<cr>") == 0) {
1168 desc_cr = desc;
1169 continue;
1170 }
1171
1172 if (!desc->str)
1173 vty_out(vty, " %-s%s",
1174 desc->cmd[0] ==
1175 '.' ? desc->cmd + 1 : desc->cmd,
1176 VTY_NEWLINE);
1177 else if (desc_width >= strlen(desc->str))
1178 vty_out(vty, " %-*s %s%s", width,
1179 desc->cmd[0] ==
1180 '.' ? desc->cmd + 1 : desc->cmd,
1181 desc->str, VTY_NEWLINE);
1182 else
1183 vty_describe_fold(vty, width, desc_width, desc);
1184
1185#if 0
1186 vty_out(vty, " %-*s %s%s", width
1187 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1188 desc->str ? desc->str : "", VTY_NEWLINE);
1189#endif /* 0 */
1190 }
1191
1192 if ((desc = desc_cr)) {
1193 if (!desc->str)
1194 vty_out(vty, " %-s%s",
1195 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1196 VTY_NEWLINE);
1197 else if (desc_width >= strlen(desc->str))
1198 vty_out(vty, " %-*s %s%s", width,
1199 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1200 desc->str, VTY_NEWLINE);
1201 else
1202 vty_describe_fold(vty, width, desc_width, desc);
1203 }
1204
1205 cmd_free_strvec(vline);
1206 vector_free(describe);
1207
1208 vty_prompt(vty);
1209 vty_redraw_line(vty);
1210}
1211
1212/* ^C stop current input and do not add command line to the history. */
1213static void vty_stop_input(struct vty *vty)
1214{
1215 vty->cp = vty->length = 0;
1216 vty_clear_buf(vty);
1217 vty_out(vty, "%s", VTY_NEWLINE);
1218
1219 switch (vty->node) {
1220 case VIEW_NODE:
1221 case ENABLE_NODE:
1222 /* Nothing to do. */
1223 break;
1224 case CONFIG_NODE:
1225 case VTY_NODE:
1226 vty_config_unlock(vty);
1227 vty->node = ENABLE_NODE;
1228 break;
Harald Welte28222962011-02-18 20:37:04 +01001229 case CFG_LOG_NODE:
1230 vty->node = CONFIG_NODE;
1231 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001232 default:
1233 /* Unknown node, we have to ignore it. */
1234 break;
1235 }
1236 vty_prompt(vty);
1237
1238 /* Set history pointer to the latest one. */
1239 vty->hp = vty->hindex;
1240}
1241
1242#define CONTROL(X) ((X) - '@')
1243#define VTY_NORMAL 0
1244#define VTY_PRE_ESCAPE 1
1245#define VTY_ESCAPE 2
1246
1247/* Escape character command map. */
1248static void vty_escape_map(unsigned char c, struct vty *vty)
1249{
1250 switch (c) {
1251 case ('A'):
1252 vty_previous_line(vty);
1253 break;
1254 case ('B'):
1255 vty_next_line(vty);
1256 break;
1257 case ('C'):
1258 vty_forward_char(vty);
1259 break;
1260 case ('D'):
1261 vty_backward_char(vty);
1262 break;
1263 default:
1264 break;
1265 }
1266
1267 /* Go back to normal mode. */
1268 vty->escape = VTY_NORMAL;
1269}
1270
1271/* Quit print out to the buffer. */
1272static void vty_buffer_reset(struct vty *vty)
1273{
1274 buffer_reset(vty->obuf);
1275 vty_prompt(vty);
1276 vty_redraw_line(vty);
1277}
1278
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001279/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001280int vty_read(struct vty *vty)
1281{
1282 int i;
1283 int nbytes;
1284 unsigned char buf[VTY_READ_BUFSIZ];
1285
1286 int vty_sock = vty->fd;
1287
1288 /* Read raw data from socket */
1289 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1290 if (nbytes < 0) {
1291 if (ERRNO_IO_RETRY(errno)) {
1292 vty_event(VTY_READ, vty_sock, vty);
1293 return 0;
1294 }
1295 }
1296 buffer_reset(vty->obuf);
1297 vty->status = VTY_CLOSE;
1298 }
1299
1300 for (i = 0; i < nbytes; i++) {
1301 if (buf[i] == IAC) {
1302 if (!vty->iac) {
1303 vty->iac = 1;
1304 continue;
1305 } else {
1306 vty->iac = 0;
1307 }
1308 }
1309
1310 if (vty->iac_sb_in_progress && !vty->iac) {
1311 if (vty->sb_len < sizeof(vty->sb_buf))
1312 vty->sb_buf[vty->sb_len] = buf[i];
1313 vty->sb_len++;
1314 continue;
1315 }
1316
1317 if (vty->iac) {
1318 /* In case of telnet command */
1319 int ret = 0;
1320 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1321 vty->iac = 0;
1322 i += ret;
1323 continue;
1324 }
1325
1326 if (vty->status == VTY_MORE) {
1327 switch (buf[i]) {
1328 case CONTROL('C'):
1329 case 'q':
1330 case 'Q':
1331 vty_buffer_reset(vty);
1332 break;
1333#if 0 /* More line does not work for "show ip bgp". */
1334 case '\n':
1335 case '\r':
1336 vty->status = VTY_MORELINE;
1337 break;
1338#endif
1339 default:
1340 break;
1341 }
1342 continue;
1343 }
1344
1345 /* Escape character. */
1346 if (vty->escape == VTY_ESCAPE) {
1347 vty_escape_map(buf[i], vty);
1348 continue;
1349 }
1350
1351 /* Pre-escape status. */
1352 if (vty->escape == VTY_PRE_ESCAPE) {
1353 switch (buf[i]) {
1354 case '[':
1355 vty->escape = VTY_ESCAPE;
1356 break;
1357 case 'b':
1358 vty_backward_word(vty);
1359 vty->escape = VTY_NORMAL;
1360 break;
1361 case 'f':
1362 vty_forward_word(vty);
1363 vty->escape = VTY_NORMAL;
1364 break;
1365 case 'd':
1366 vty_forward_kill_word(vty);
1367 vty->escape = VTY_NORMAL;
1368 break;
1369 case CONTROL('H'):
1370 case 0x7f:
1371 vty_backward_kill_word(vty);
1372 vty->escape = VTY_NORMAL;
1373 break;
1374 default:
1375 vty->escape = VTY_NORMAL;
1376 break;
1377 }
1378 continue;
1379 }
1380
1381 switch (buf[i]) {
1382 case CONTROL('A'):
1383 vty_beginning_of_line(vty);
1384 break;
1385 case CONTROL('B'):
1386 vty_backward_char(vty);
1387 break;
1388 case CONTROL('C'):
1389 vty_stop_input(vty);
1390 break;
1391 case CONTROL('D'):
1392 vty_delete_char(vty);
1393 break;
1394 case CONTROL('E'):
1395 vty_end_of_line(vty);
1396 break;
1397 case CONTROL('F'):
1398 vty_forward_char(vty);
1399 break;
1400 case CONTROL('H'):
1401 case 0x7f:
1402 vty_delete_backward_char(vty);
1403 break;
1404 case CONTROL('K'):
1405 vty_kill_line(vty);
1406 break;
1407 case CONTROL('N'):
1408 vty_next_line(vty);
1409 break;
1410 case CONTROL('P'):
1411 vty_previous_line(vty);
1412 break;
1413 case CONTROL('T'):
1414 vty_transpose_chars(vty);
1415 break;
1416 case CONTROL('U'):
1417 vty_kill_line_from_beginning(vty);
1418 break;
1419 case CONTROL('W'):
1420 vty_backward_kill_word(vty);
1421 break;
1422 case CONTROL('Z'):
1423 vty_end_config(vty);
1424 break;
1425 case '\n':
1426 case '\r':
1427 vty_out(vty, "%s", VTY_NEWLINE);
1428 vty_execute(vty);
1429 break;
1430 case '\t':
1431 vty_complete_command(vty);
1432 break;
1433 case '?':
1434 if (vty->node == AUTH_NODE
1435 || vty->node == AUTH_ENABLE_NODE)
1436 vty_self_insert(vty, buf[i]);
1437 else
1438 vty_describe_command(vty);
1439 break;
1440 case '\033':
1441 if (i + 1 < nbytes && buf[i + 1] == '[') {
1442 vty->escape = VTY_ESCAPE;
1443 i++;
1444 } else
1445 vty->escape = VTY_PRE_ESCAPE;
1446 break;
1447 default:
1448 if (buf[i] > 31 && buf[i] < 127)
1449 vty_self_insert(vty, buf[i]);
1450 break;
1451 }
1452 }
1453
1454 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001455 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001456 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001457 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001458 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459 vty_event(VTY_WRITE, vty_sock, vty);
1460 vty_event(VTY_READ, vty_sock, vty);
1461 }
1462 return 0;
1463}
1464
1465/* Read up configuration file */
1466static int
1467vty_read_file(FILE *confp, void *priv)
1468{
1469 int ret;
1470 struct vty *vty;
1471
1472 vty = vty_new();
1473 vty->fd = 0;
1474 vty->type = VTY_FILE;
1475 vty->node = CONFIG_NODE;
1476 vty->priv = priv;
1477
1478 ret = config_from_file(vty, confp);
1479
1480 if (ret != CMD_SUCCESS) {
1481 switch (ret) {
1482 case CMD_ERR_AMBIGUOUS:
1483 fprintf(stderr, "Ambiguous command.\n");
1484 break;
1485 case CMD_ERR_NO_MATCH:
1486 fprintf(stderr, "There is no such command.\n");
1487 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001488 case CMD_ERR_INVALID_INDENT:
1489 fprintf(stderr,
1490 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1491 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1492 "allowed, but their sequence must not change within a child block.\n");
1493 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001494 }
Keith03516d62017-09-04 11:19:13 +02001495 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001496 "line:\n%s\n", vty->buf);
1497 vty_close(vty);
1498 return -EINVAL;
1499 }
1500
1501 vty_close(vty);
1502 return 0;
1503}
1504
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001505/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001506struct vty *
1507vty_create (int vty_sock, void *priv)
1508{
1509 struct vty *vty;
1510
Alexander Couzensab383e62018-07-17 19:02:34 +02001511 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001512
1513 tcgetattr(vty_sock, &t);
1514 cfmakeraw(&t);
1515 tcsetattr(vty_sock, TCSANOW, &t);
1516
1517 /* Allocate new vty structure and set up default values. */
1518 vty = vty_new ();
1519 vty->fd = vty_sock;
1520 vty->priv = priv;
1521 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001522 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001523 {
1524 if (host.advanced)
1525 vty->node = ENABLE_NODE;
1526 else
1527 vty->node = VIEW_NODE;
1528 }
1529 else
1530 vty->node = AUTH_NODE;
1531 vty->fail = 0;
1532 vty->cp = 0;
1533 vty_clear_buf (vty);
1534 vty->length = 0;
1535 memset (vty->hist, 0, sizeof (vty->hist));
1536 vty->hp = 0;
1537 vty->hindex = 0;
1538 vector_set_index (vtyvec, vty_sock, vty);
1539 vty->status = VTY_NORMAL;
1540 if (host.lines >= 0)
1541 vty->lines = host.lines;
1542 else
1543 vty->lines = -1;
1544
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001545 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001546 {
1547 /* Vty is not available if password isn't set. */
1548 if (host.password == NULL && host.password_encrypt == NULL)
1549 {
1550 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1551 vty->status = VTY_CLOSE;
1552 vty_close (vty);
1553 return NULL;
1554 }
1555 }
1556
1557 /* Say hello to the world. */
1558 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001559 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001560 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1561
1562 /* Setting up terminal. */
1563 vty_will_echo (vty);
1564 vty_will_suppress_go_ahead (vty);
1565
1566 vty_dont_linemode (vty);
1567 vty_do_window_size (vty);
1568 /* vty_dont_lflow_ahead (vty); */
1569
1570 vty_prompt (vty);
1571
1572 /* Add read/write thread. */
1573 vty_event (VTY_WRITE, vty_sock, vty);
1574 vty_event (VTY_READ, vty_sock, vty);
1575
1576 return vty;
1577}
1578
1579DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1580{
1581 unsigned int i;
1582 struct vty *v;
1583
1584 for (i = 0; i < vector_active(vtyvec); i++)
1585 if ((v = vector_slot(vtyvec, i)) != NULL)
1586 vty_out(vty, "%svty[%d] %s",
1587 v->config ? "*" : " ", i, VTY_NEWLINE);
1588 return CMD_SUCCESS;
1589}
1590
1591/* Move to vty configuration mode. */
1592DEFUN(line_vty,
1593 line_vty_cmd,
1594 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1595{
1596 vty->node = VTY_NODE;
1597 return CMD_SUCCESS;
1598}
1599
1600/* vty login. */
1601DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1602{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001603 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001604 return CMD_SUCCESS;
1605}
1606
1607DEFUN(no_vty_login,
1608 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1609{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001610 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001611 return CMD_SUCCESS;
1612}
1613
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001614/* vty bind */
1615DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D",
1616 "Accept VTY telnet connections on local interface\n"
1617 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n")
1618{
1619 talloc_free((void*)vty_bind_addr);
1620 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
1621 return CMD_SUCCESS;
1622}
1623
1624const char *vty_get_bind_addr(void)
1625{
1626 if (!vty_bind_addr)
1627 return VTY_BIND_ADDR_DEFAULT;
1628 return vty_bind_addr;
1629}
1630
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 */
Neels Hofmeyr898e1d82016-08-20 16:33:47 +02001703 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0))
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001704 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1705
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001706 vty_out(vty, "!%s", VTY_NEWLINE);
1707
1708 return CMD_SUCCESS;
1709}
1710
1711struct cmd_node vty_node = {
1712 VTY_NODE,
1713 "%s(config-line)# ",
1714 1,
1715};
1716
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001717/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001718void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001719{
1720 unsigned int i;
1721 struct vty *vty;
1722 struct thread *vty_serv_thread;
1723
1724 for (i = 0; i < vector_active(vtyvec); i++)
1725 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1726 buffer_reset(vty->obuf);
1727 vty->status = VTY_CLOSE;
1728 vty_close(vty);
1729 }
1730
1731 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1732 if ((vty_serv_thread =
1733 vector_slot(Vvty_serv_thread, i)) != NULL) {
1734 //thread_cancel (vty_serv_thread);
1735 vector_slot(Vvty_serv_thread, i) = NULL;
1736 close(i);
1737 }
1738}
1739
1740static void vty_save_cwd(void)
1741{
1742 char cwd[MAXPATHLEN];
1743 char *c ;
1744
1745 c = getcwd(cwd, MAXPATHLEN);
1746
1747 if (!c) {
1748 if (chdir(SYSCONFDIR) != 0)
1749 perror("chdir failed");
1750 if (getcwd(cwd, MAXPATHLEN) == NULL)
1751 perror("getcwd failed");
1752 }
1753
1754 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1755 strcpy(vty_cwd, cwd);
1756}
1757
Harald Welte95b2b472011-07-16 11:58:09 +02001758char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001759{
1760 return vty_cwd;
1761}
1762
1763int vty_shell_serv(struct vty *vty)
1764{
1765 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1766}
1767
Harald Welte95b2b472011-07-16 11:58:09 +02001768void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001769{
1770 vtyvec = vector_init(VECTOR_MIN_SIZE);
1771}
1772
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001773/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001774 * \param[in] app_info application information
1775 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001776/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001777void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001778{
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301779 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001780 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1781 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1782
1783 cmd_init(1);
1784
Harald Welte237f6242010-05-25 23:00:45 +02001785 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001786
1787 /* For further configuration read, preserve current directory. */
1788 vty_save_cwd();
1789
1790 vtyvec = vector_init(VECTOR_MIN_SIZE);
1791
1792 /* Install bgp top node. */
1793 install_node(&vty_node, vty_config_write);
1794
1795 install_element_ve(&config_who_cmd);
1796 install_element_ve(&show_history_cmd);
1797 install_element(CONFIG_NODE, &line_vty_cmd);
1798 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1799 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1800 install_element(CONFIG_NODE, &show_history_cmd);
1801 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1802 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1803
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001804 install_element(VTY_NODE, &vty_login_cmd);
1805 install_element(VTY_NODE, &no_vty_login_cmd);
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001806 install_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001807}
1808
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001809/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001810 * \param[in] file_name file name of the configuration file
1811 * \param[in] priv private data to be passed to \ref vty_read_file
1812 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001813int vty_read_config_file(const char *file_name, void *priv)
1814{
1815 FILE *cfile;
1816 int rc;
1817
1818 cfile = fopen(file_name, "r");
1819 if (!cfile)
1820 return -ENOENT;
1821
1822 rc = vty_read_file(cfile, priv);
1823 fclose(cfile);
1824
1825 host_config_set(file_name);
1826
1827 return rc;
1828}
Harald Welte7acb30c2011-08-17 17:13:48 +02001829
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001830/*! @} */