blob: ad53537198c268219b9f00fedc0b766da89bb5f1 [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. */
224 if (vty->fd > 0)
225 close(vty->fd);
226
227 if (vty->buf) {
228 talloc_free(vty->buf);
229 vty->buf = NULL;
230 }
231
232 /* Check configure. */
233 vty_config_unlock(vty);
234
235 /* VTY_CLOSED is handled by the telnet_interface */
236 vty_event(VTY_CLOSED, vty->fd, vty);
237
238 /* OK free vty. */
239 talloc_free(vty);
240}
241
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200242/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200243int vty_shell(struct vty *vty)
244{
245 return vty->type == VTY_SHELL ? 1 : 0;
246}
247
248
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200249/*! VTY standard output function
Harald Welte7acb30c2011-08-17 17:13:48 +0200250 * \param[in] vty VTY to which we should print
251 * \param[in] format variable-length format string
252 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200253int vty_out(struct vty *vty, const char *format, ...)
254{
255 va_list args;
256 int len = 0;
257 int size = 1024;
258 char buf[1024];
259 char *p = NULL;
260
261 if (vty_shell(vty)) {
262 va_start(args, format);
263 vprintf(format, args);
264 va_end(args);
265 } else {
266 /* Try to write to initial buffer. */
267 va_start(args, format);
268 len = vsnprintf(buf, sizeof buf, format, args);
269 va_end(args);
270
271 /* Initial buffer is not enough. */
272 if (len < 0 || len >= size) {
273 while (1) {
274 if (len > -1)
275 size = len + 1;
276 else
277 size = size * 2;
278
279 p = talloc_realloc_size(vty, p, size);
280 if (!p)
281 return -1;
282
283 va_start(args, format);
284 len = vsnprintf(p, size, format, args);
285 va_end(args);
286
287 if (len > -1 && len < size)
288 break;
289 }
290 }
291
292 /* When initial buffer is enough to store all output. */
293 if (!p)
294 p = buf;
295
296 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100297 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200298
299 /* If p is not different with buf, it is allocated buffer. */
300 if (p != buf)
301 talloc_free(p);
302 }
303
304 vty_event(VTY_WRITE, vty->fd, vty);
305
306 return len;
307}
308
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200309/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200310int vty_out_newline(struct vty *vty)
311{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200312 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200313 buffer_put(vty->obuf, p, strlen(p));
314 return 0;
315}
316
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200317/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800318void *vty_current_index(struct vty *vty)
319{
320 return vty->index;
321}
Harald Welte7acb30c2011-08-17 17:13:48 +0200322
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200323/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800324int vty_current_node(struct vty *vty)
325{
326 return vty->node;
327}
328
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200329/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200330 * \param[in] vty VTY to which the config shall be locked
331 * \returns 1 on success, 0 on error
332 *
333 * This shall be used to make sure only one VTY at a given time has
334 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200335int vty_config_lock(struct vty *vty)
336{
337 if (vty_config == 0) {
338 vty->config = 1;
339 vty_config = 1;
340 }
341 return vty->config;
342}
343
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200344/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200345 * \param[in] vty VTY from which the configuration shall be unlocked
346 * \returns 0 in case of success
347 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200348int vty_config_unlock(struct vty *vty)
349{
350 if (vty_config == 1 && vty->config == 1) {
351 vty->config = 0;
352 vty_config = 0;
353 }
354 return vty->config;
355}
356
357/* Say hello to vty interface. */
358void vty_hello(struct vty *vty)
359{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200360 const char *app_name = "<unnamed>";
361
362 if (host.app_info->name)
363 app_name = host.app_info->name;
364
Harald Weltea2501a22018-03-23 19:33:27 +0100365 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800366 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200367
368 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200369 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200370
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200371 if (host.motdfile) {
372 FILE *f;
373 char buf[4096];
374
375 f = fopen(host.motdfile, "r");
376 if (f) {
377 while (fgets(buf, sizeof(buf), f)) {
378 char *s;
379 /* work backwards to ignore trailling isspace() */
380 for (s = buf + strlen(buf);
381 (s > buf) && isspace(*(s - 1)); s--) ;
382 *s = '\0';
383 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
384 }
385 fclose(f);
386 } else
387 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
388 } else if (host.motd)
389 vty_out(vty, "%s", host.motd);
390}
391
392/* Put out prompt and wait input from user. */
393static void vty_prompt(struct vty *vty)
394{
395 struct utsname names;
396 const char *hostname;
397
398 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100399 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200400 if (!hostname) {
401 uname(&names);
402 hostname = names.nodename;
403 }
404 vty_out(vty, cmd_prompt(vty->node), hostname);
405 }
406}
407
408/* Command execution over the vty interface. */
409static int vty_command(struct vty *vty, char *buf)
410{
411 int ret;
412 vector vline;
413
414 /* Split readline string up into the vector */
415 vline = cmd_make_strvec(buf);
416
417 if (vline == NULL)
418 return CMD_SUCCESS;
419
420 ret = cmd_execute_command(vline, vty, NULL, 0);
421 if (ret != CMD_SUCCESS)
422 switch (ret) {
423 case CMD_WARNING:
424 if (vty->type == VTY_FILE)
425 vty_out(vty, "Warning...%s", VTY_NEWLINE);
426 break;
427 case CMD_ERR_AMBIGUOUS:
428 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
429 break;
430 case CMD_ERR_NO_MATCH:
431 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
432 break;
433 case CMD_ERR_INCOMPLETE:
434 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
435 break;
436 }
437 cmd_free_strvec(vline);
438
439 return ret;
440}
441
442static const char telnet_backward_char = 0x08;
443static const char telnet_space_char = ' ';
444
445/* Basic function to write buffer to vty. */
446static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
447{
448 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
449 return;
450
451 /* Should we do buffering here ? And make vty_flush (vty) ? */
452 buffer_put(vty->obuf, buf, nbytes);
453}
454
455/* Ensure length of input buffer. Is buffer is short, double it. */
456static void vty_ensure(struct vty *vty, int length)
457{
458 if (vty->max <= length) {
459 vty->max *= 2;
460 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
461 // FIXME: check return
462 }
463}
464
465/* Basic function to insert character into vty. */
466static void vty_self_insert(struct vty *vty, char c)
467{
468 int i;
469 int length;
470
471 vty_ensure(vty, vty->length + 1);
472 length = vty->length - vty->cp;
473 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
474 vty->buf[vty->cp] = c;
475
476 vty_write(vty, &vty->buf[vty->cp], length + 1);
477 for (i = 0; i < length; i++)
478 vty_write(vty, &telnet_backward_char, 1);
479
480 vty->cp++;
481 vty->length++;
482}
483
484/* Self insert character 'c' in overwrite mode. */
485static void vty_self_insert_overwrite(struct vty *vty, char c)
486{
487 vty_ensure(vty, vty->length + 1);
488 vty->buf[vty->cp++] = c;
489
490 if (vty->cp > vty->length)
491 vty->length++;
492
493 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
494 return;
495
496 vty_write(vty, &c, 1);
497}
498
499/* Insert a word into vty interface with overwrite mode. */
500static void vty_insert_word_overwrite(struct vty *vty, char *str)
501{
502 int len = strlen(str);
503 vty_write(vty, str, len);
504 strcpy(&vty->buf[vty->cp], str);
505 vty->cp += len;
506 vty->length = vty->cp;
507}
508
509/* Forward character. */
510static void vty_forward_char(struct vty *vty)
511{
512 if (vty->cp < vty->length) {
513 vty_write(vty, &vty->buf[vty->cp], 1);
514 vty->cp++;
515 }
516}
517
518/* Backward character. */
519static void vty_backward_char(struct vty *vty)
520{
521 if (vty->cp > 0) {
522 vty->cp--;
523 vty_write(vty, &telnet_backward_char, 1);
524 }
525}
526
527/* Move to the beginning of the line. */
528static void vty_beginning_of_line(struct vty *vty)
529{
530 while (vty->cp)
531 vty_backward_char(vty);
532}
533
534/* Move to the end of the line. */
535static void vty_end_of_line(struct vty *vty)
536{
537 while (vty->cp < vty->length)
538 vty_forward_char(vty);
539}
540
541/* Add current command line to the history buffer. */
542static void vty_hist_add(struct vty *vty)
543{
544 int index;
545
546 if (vty->length == 0)
547 return;
548
549 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
550
551 /* Ignore the same string as previous one. */
552 if (vty->hist[index])
553 if (strcmp(vty->buf, vty->hist[index]) == 0) {
554 vty->hp = vty->hindex;
555 return;
556 }
557
558 /* Insert history entry. */
559 if (vty->hist[vty->hindex])
560 talloc_free(vty->hist[vty->hindex]);
561 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
562
563 /* History index rotation. */
564 vty->hindex++;
565 if (vty->hindex == VTY_MAXHIST)
566 vty->hindex = 0;
567
568 vty->hp = vty->hindex;
569}
570
571/* Get telnet window size. */
572static int
573vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
574{
575#ifdef TELNET_OPTION_DEBUG
576 int i;
577
578 for (i = 0; i < nbytes; i++)
579 {
580 switch (buf[i])
581 {
582 case IAC:
583 vty_out (vty, "IAC ");
584 break;
585 case WILL:
586 vty_out (vty, "WILL ");
587 break;
588 case WONT:
589 vty_out (vty, "WONT ");
590 break;
591 case DO:
592 vty_out (vty, "DO ");
593 break;
594 case DONT:
595 vty_out (vty, "DONT ");
596 break;
597 case SB:
598 vty_out (vty, "SB ");
599 break;
600 case SE:
601 vty_out (vty, "SE ");
602 break;
603 case TELOPT_ECHO:
604 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
605 break;
606 case TELOPT_SGA:
607 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
608 break;
609 case TELOPT_NAWS:
610 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
611 break;
612 default:
613 vty_out (vty, "%x ", buf[i]);
614 break;
615 }
616 }
617 vty_out (vty, "%s", VTY_NEWLINE);
618
619#endif /* TELNET_OPTION_DEBUG */
620
621 switch (buf[0])
622 {
623 case SB:
624 vty->sb_len = 0;
625 vty->iac_sb_in_progress = 1;
626 return 0;
627 break;
628 case SE:
629 {
630 if (!vty->iac_sb_in_progress)
631 return 0;
632
633 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
634 {
635 vty->iac_sb_in_progress = 0;
636 return 0;
637 }
638 switch (vty->sb_buf[0])
639 {
640 case TELOPT_NAWS:
641 if (vty->sb_len != TELNET_NAWS_SB_LEN)
642 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
643 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100644 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200645 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
646 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
647 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100648 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200649 else
650 {
651 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
652 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
653#ifdef TELNET_OPTION_DEBUG
654 vty_out(vty, "TELNET NAWS window size negotiation completed: "
655 "width %d, height %d%s",
656 vty->width, vty->height, VTY_NEWLINE);
657#endif
658 }
659 break;
660 }
661 vty->iac_sb_in_progress = 0;
662 return 0;
663 break;
664 }
665 default:
666 break;
667 }
668 return 1;
669}
670
671/* Execute current command line. */
672static int vty_execute(struct vty *vty)
673{
674 int ret;
675
676 ret = CMD_SUCCESS;
677
678 switch (vty->node) {
679 case AUTH_NODE:
680 case AUTH_ENABLE_NODE:
681 vty_auth(vty, vty->buf);
682 break;
683 default:
684 ret = vty_command(vty, vty->buf);
685 if (vty->type == VTY_TERM)
686 vty_hist_add(vty);
687 break;
688 }
689
690 /* Clear command line buffer. */
691 vty->cp = vty->length = 0;
692 vty_clear_buf(vty);
693
694 if (vty->status != VTY_CLOSE)
695 vty_prompt(vty);
696
697 return ret;
698}
699
700/* Send WILL TELOPT_ECHO to remote server. */
701static void
702vty_will_echo (struct vty *vty)
703{
704 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
705 vty_out (vty, "%s", cmd);
706}
707
708/* Make suppress Go-Ahead telnet option. */
709static void
710vty_will_suppress_go_ahead (struct vty *vty)
711{
712 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
713 vty_out (vty, "%s", cmd);
714}
715
716/* Make don't use linemode over telnet. */
717static void
718vty_dont_linemode (struct vty *vty)
719{
720 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
721 vty_out (vty, "%s", cmd);
722}
723
724/* Use window size. */
725static void
726vty_do_window_size (struct vty *vty)
727{
728 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
729 vty_out (vty, "%s", cmd);
730}
731
732static void vty_kill_line_from_beginning(struct vty *);
733static void vty_redraw_line(struct vty *);
734
735/* Print command line history. This function is called from
736 vty_next_line and vty_previous_line. */
737static void vty_history_print(struct vty *vty)
738{
739 int length;
740
741 vty_kill_line_from_beginning(vty);
742
743 /* Get previous line from history buffer */
744 length = strlen(vty->hist[vty->hp]);
745 memcpy(vty->buf, vty->hist[vty->hp], length);
746 vty->cp = vty->length = length;
747
748 /* Redraw current line */
749 vty_redraw_line(vty);
750}
751
752/* Show next command line history. */
753static void vty_next_line(struct vty *vty)
754{
755 int try_index;
756
757 if (vty->hp == vty->hindex)
758 return;
759
760 /* Try is there history exist or not. */
761 try_index = vty->hp;
762 if (try_index == (VTY_MAXHIST - 1))
763 try_index = 0;
764 else
765 try_index++;
766
767 /* If there is not history return. */
768 if (vty->hist[try_index] == NULL)
769 return;
770 else
771 vty->hp = try_index;
772
773 vty_history_print(vty);
774}
775
776/* Show previous command line history. */
777static void vty_previous_line(struct vty *vty)
778{
779 int try_index;
780
781 try_index = vty->hp;
782 if (try_index == 0)
783 try_index = VTY_MAXHIST - 1;
784 else
785 try_index--;
786
787 if (vty->hist[try_index] == NULL)
788 return;
789 else
790 vty->hp = try_index;
791
792 vty_history_print(vty);
793}
794
795/* This function redraw all of the command line character. */
796static void vty_redraw_line(struct vty *vty)
797{
798 vty_write(vty, vty->buf, vty->length);
799 vty->cp = vty->length;
800}
801
802/* Forward word. */
803static void vty_forward_word(struct vty *vty)
804{
805 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
806 vty_forward_char(vty);
807
808 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
809 vty_forward_char(vty);
810}
811
812/* Backward word without skipping training space. */
813static void vty_backward_pure_word(struct vty *vty)
814{
815 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
816 vty_backward_char(vty);
817}
818
819/* Backward word. */
820static void vty_backward_word(struct vty *vty)
821{
822 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
823 vty_backward_char(vty);
824
825 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
826 vty_backward_char(vty);
827}
828
829/* When '^D' is typed at the beginning of the line we move to the down
830 level. */
831static void vty_down_level(struct vty *vty)
832{
833 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100834 /* call the exit function of the specific node */
835 if (vty->node > CONFIG_NODE)
836 vty_go_parent(vty);
837 else
838 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200839 vty_prompt(vty);
840 vty->cp = 0;
841}
842
843/* When '^Z' is received from vty, move down to the enable mode. */
844static void vty_end_config(struct vty *vty)
845{
846 vty_out(vty, "%s", VTY_NEWLINE);
847
848 /* FIXME: we need to call the exit function of the specific node
849 * in question, not this generic one that doesn't know all nodes */
850 switch (vty->node) {
851 case VIEW_NODE:
852 case ENABLE_NODE:
853 /* Nothing to do. */
854 break;
855 case CONFIG_NODE:
856 case VTY_NODE:
857 vty_config_unlock(vty);
858 vty->node = ENABLE_NODE;
859 break;
Harald Welte28222962011-02-18 20:37:04 +0100860 case CFG_LOG_NODE:
861 vty->node = CONFIG_NODE;
862 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200863 default:
864 /* Unknown node, we have to ignore it. */
865 break;
866 }
867
868 vty_prompt(vty);
869 vty->cp = 0;
870}
871
872/* Delete a charcter at the current point. */
873static void vty_delete_char(struct vty *vty)
874{
875 int i;
876 int size;
877
878 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
879 return;
880
881 if (vty->length == 0) {
882 vty_down_level(vty);
883 return;
884 }
885
886 if (vty->cp == vty->length)
887 return; /* completion need here? */
888
889 size = vty->length - vty->cp;
890
891 vty->length--;
892 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
893 vty->buf[vty->length] = '\0';
894
895 vty_write(vty, &vty->buf[vty->cp], size - 1);
896 vty_write(vty, &telnet_space_char, 1);
897
898 for (i = 0; i < size; i++)
899 vty_write(vty, &telnet_backward_char, 1);
900}
901
902/* Delete a character before the point. */
903static void vty_delete_backward_char(struct vty *vty)
904{
905 if (vty->cp == 0)
906 return;
907
908 vty_backward_char(vty);
909 vty_delete_char(vty);
910}
911
912/* Kill rest of line from current point. */
913static void vty_kill_line(struct vty *vty)
914{
915 int i;
916 int size;
917
918 size = vty->length - vty->cp;
919
920 if (size == 0)
921 return;
922
923 for (i = 0; i < size; i++)
924 vty_write(vty, &telnet_space_char, 1);
925 for (i = 0; i < size; i++)
926 vty_write(vty, &telnet_backward_char, 1);
927
928 memset(&vty->buf[vty->cp], 0, size);
929 vty->length = vty->cp;
930}
931
932/* Kill line from the beginning. */
933static void vty_kill_line_from_beginning(struct vty *vty)
934{
935 vty_beginning_of_line(vty);
936 vty_kill_line(vty);
937}
938
939/* Delete a word before the point. */
940static void vty_forward_kill_word(struct vty *vty)
941{
942 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
943 vty_delete_char(vty);
944 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
945 vty_delete_char(vty);
946}
947
948/* Delete a word before the point. */
949static void vty_backward_kill_word(struct vty *vty)
950{
951 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
952 vty_delete_backward_char(vty);
953 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
954 vty_delete_backward_char(vty);
955}
956
957/* Transpose chars before or at the point. */
958static void vty_transpose_chars(struct vty *vty)
959{
960 char c1, c2;
961
962 /* If length is short or point is near by the beginning of line then
963 return. */
964 if (vty->length < 2 || vty->cp < 1)
965 return;
966
967 /* In case of point is located at the end of the line. */
968 if (vty->cp == vty->length) {
969 c1 = vty->buf[vty->cp - 1];
970 c2 = vty->buf[vty->cp - 2];
971
972 vty_backward_char(vty);
973 vty_backward_char(vty);
974 vty_self_insert_overwrite(vty, c1);
975 vty_self_insert_overwrite(vty, c2);
976 } else {
977 c1 = vty->buf[vty->cp];
978 c2 = vty->buf[vty->cp - 1];
979
980 vty_backward_char(vty);
981 vty_self_insert_overwrite(vty, c1);
982 vty_self_insert_overwrite(vty, c2);
983 }
984}
985
986/* Do completion at vty interface. */
987static void vty_complete_command(struct vty *vty)
988{
989 int i;
990 int ret;
991 char **matched = NULL;
992 vector vline;
993
994 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
995 return;
996
997 vline = cmd_make_strvec(vty->buf);
998 if (vline == NULL)
999 return;
1000
1001 /* In case of 'help \t'. */
1002 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001003 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001004
1005 matched = cmd_complete_command(vline, vty, &ret);
1006
1007 cmd_free_strvec(vline);
1008
1009 vty_out(vty, "%s", VTY_NEWLINE);
1010 switch (ret) {
1011 case CMD_ERR_AMBIGUOUS:
1012 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1013 vty_prompt(vty);
1014 vty_redraw_line(vty);
1015 break;
1016 case CMD_ERR_NO_MATCH:
1017 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1018 vty_prompt(vty);
1019 vty_redraw_line(vty);
1020 break;
1021 case CMD_COMPLETE_FULL_MATCH:
1022 vty_prompt(vty);
1023 vty_redraw_line(vty);
1024 vty_backward_pure_word(vty);
1025 vty_insert_word_overwrite(vty, matched[0]);
1026 vty_self_insert(vty, ' ');
1027 talloc_free(matched[0]);
1028 break;
1029 case CMD_COMPLETE_MATCH:
1030 vty_prompt(vty);
1031 vty_redraw_line(vty);
1032 vty_backward_pure_word(vty);
1033 vty_insert_word_overwrite(vty, matched[0]);
1034 talloc_free(matched[0]);
1035 break;
1036 case CMD_COMPLETE_LIST_MATCH:
1037 for (i = 0; matched[i] != NULL; i++) {
1038 if (i != 0 && ((i % 6) == 0))
1039 vty_out(vty, "%s", VTY_NEWLINE);
1040 vty_out(vty, "%-10s ", matched[i]);
1041 talloc_free(matched[i]);
1042 }
1043 vty_out(vty, "%s", VTY_NEWLINE);
1044
1045 vty_prompt(vty);
1046 vty_redraw_line(vty);
1047 break;
1048 case CMD_ERR_NOTHING_TODO:
1049 vty_prompt(vty);
1050 vty_redraw_line(vty);
1051 break;
1052 default:
1053 break;
1054 }
1055 if (matched)
1056 vector_only_index_free(matched);
1057}
1058
1059static void
1060vty_describe_fold(struct vty *vty, int cmd_width,
1061 unsigned int desc_width, struct desc *desc)
1062{
1063 char *buf;
1064 const char *cmd, *p;
1065 int pos;
1066
1067 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1068
1069 if (desc_width <= 0) {
1070 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1071 VTY_NEWLINE);
1072 return;
1073 }
1074
1075 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1076 if (!buf)
1077 return;
1078
1079 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1080 for (pos = desc_width; pos > 0; pos--)
1081 if (*(p + pos) == ' ')
1082 break;
1083
1084 if (pos == 0)
1085 break;
1086
1087 strncpy(buf, p, pos);
1088 buf[pos] = '\0';
1089 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1090
1091 cmd = "";
1092 }
1093
1094 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1095
1096 talloc_free(buf);
1097}
1098
1099/* Describe matched command function. */
1100static void vty_describe_command(struct vty *vty)
1101{
1102 int ret;
1103 vector vline;
1104 vector describe;
1105 unsigned int i, width, desc_width;
1106 struct desc *desc, *desc_cr = NULL;
1107
1108 vline = cmd_make_strvec(vty->buf);
1109
1110 /* In case of '> ?'. */
1111 if (vline == NULL) {
1112 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001113 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001114 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001115 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001116
1117 describe = cmd_describe_command(vline, vty, &ret);
1118
1119 vty_out(vty, "%s", VTY_NEWLINE);
1120
1121 /* Ambiguous error. */
1122 switch (ret) {
1123 case CMD_ERR_AMBIGUOUS:
1124 cmd_free_strvec(vline);
1125 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1126 vty_prompt(vty);
1127 vty_redraw_line(vty);
1128 return;
1129 break;
1130 case CMD_ERR_NO_MATCH:
1131 cmd_free_strvec(vline);
1132 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1133 vty_prompt(vty);
1134 vty_redraw_line(vty);
1135 return;
1136 break;
1137 }
1138
1139 /* Get width of command string. */
1140 width = 0;
1141 for (i = 0; i < vector_active(describe); i++)
1142 if ((desc = vector_slot(describe, i)) != NULL) {
1143 unsigned int len;
1144
1145 if (desc->cmd[0] == '\0')
1146 continue;
1147
1148 len = strlen(desc->cmd);
1149 if (desc->cmd[0] == '.')
1150 len--;
1151
1152 if (width < len)
1153 width = len;
1154 }
1155
1156 /* Get width of description string. */
1157 desc_width = vty->width - (width + 6);
1158
1159 /* Print out description. */
1160 for (i = 0; i < vector_active(describe); i++)
1161 if ((desc = vector_slot(describe, i)) != NULL) {
1162 if (desc->cmd[0] == '\0')
1163 continue;
1164
1165 if (strcmp(desc->cmd, "<cr>") == 0) {
1166 desc_cr = desc;
1167 continue;
1168 }
1169
1170 if (!desc->str)
1171 vty_out(vty, " %-s%s",
1172 desc->cmd[0] ==
1173 '.' ? desc->cmd + 1 : desc->cmd,
1174 VTY_NEWLINE);
1175 else if (desc_width >= strlen(desc->str))
1176 vty_out(vty, " %-*s %s%s", width,
1177 desc->cmd[0] ==
1178 '.' ? desc->cmd + 1 : desc->cmd,
1179 desc->str, VTY_NEWLINE);
1180 else
1181 vty_describe_fold(vty, width, desc_width, desc);
1182
1183#if 0
1184 vty_out(vty, " %-*s %s%s", width
1185 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1186 desc->str ? desc->str : "", VTY_NEWLINE);
1187#endif /* 0 */
1188 }
1189
1190 if ((desc = desc_cr)) {
1191 if (!desc->str)
1192 vty_out(vty, " %-s%s",
1193 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1194 VTY_NEWLINE);
1195 else if (desc_width >= strlen(desc->str))
1196 vty_out(vty, " %-*s %s%s", width,
1197 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1198 desc->str, VTY_NEWLINE);
1199 else
1200 vty_describe_fold(vty, width, desc_width, desc);
1201 }
1202
1203 cmd_free_strvec(vline);
1204 vector_free(describe);
1205
1206 vty_prompt(vty);
1207 vty_redraw_line(vty);
1208}
1209
1210/* ^C stop current input and do not add command line to the history. */
1211static void vty_stop_input(struct vty *vty)
1212{
1213 vty->cp = vty->length = 0;
1214 vty_clear_buf(vty);
1215 vty_out(vty, "%s", VTY_NEWLINE);
1216
1217 switch (vty->node) {
1218 case VIEW_NODE:
1219 case ENABLE_NODE:
1220 /* Nothing to do. */
1221 break;
1222 case CONFIG_NODE:
1223 case VTY_NODE:
1224 vty_config_unlock(vty);
1225 vty->node = ENABLE_NODE;
1226 break;
Harald Welte28222962011-02-18 20:37:04 +01001227 case CFG_LOG_NODE:
1228 vty->node = CONFIG_NODE;
1229 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001230 default:
1231 /* Unknown node, we have to ignore it. */
1232 break;
1233 }
1234 vty_prompt(vty);
1235
1236 /* Set history pointer to the latest one. */
1237 vty->hp = vty->hindex;
1238}
1239
1240#define CONTROL(X) ((X) - '@')
1241#define VTY_NORMAL 0
1242#define VTY_PRE_ESCAPE 1
1243#define VTY_ESCAPE 2
1244
1245/* Escape character command map. */
1246static void vty_escape_map(unsigned char c, struct vty *vty)
1247{
1248 switch (c) {
1249 case ('A'):
1250 vty_previous_line(vty);
1251 break;
1252 case ('B'):
1253 vty_next_line(vty);
1254 break;
1255 case ('C'):
1256 vty_forward_char(vty);
1257 break;
1258 case ('D'):
1259 vty_backward_char(vty);
1260 break;
1261 default:
1262 break;
1263 }
1264
1265 /* Go back to normal mode. */
1266 vty->escape = VTY_NORMAL;
1267}
1268
1269/* Quit print out to the buffer. */
1270static void vty_buffer_reset(struct vty *vty)
1271{
1272 buffer_reset(vty->obuf);
1273 vty_prompt(vty);
1274 vty_redraw_line(vty);
1275}
1276
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001277/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278int vty_read(struct vty *vty)
1279{
1280 int i;
1281 int nbytes;
1282 unsigned char buf[VTY_READ_BUFSIZ];
1283
1284 int vty_sock = vty->fd;
1285
1286 /* Read raw data from socket */
1287 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1288 if (nbytes < 0) {
1289 if (ERRNO_IO_RETRY(errno)) {
1290 vty_event(VTY_READ, vty_sock, vty);
1291 return 0;
1292 }
1293 }
1294 buffer_reset(vty->obuf);
1295 vty->status = VTY_CLOSE;
1296 }
1297
1298 for (i = 0; i < nbytes; i++) {
1299 if (buf[i] == IAC) {
1300 if (!vty->iac) {
1301 vty->iac = 1;
1302 continue;
1303 } else {
1304 vty->iac = 0;
1305 }
1306 }
1307
1308 if (vty->iac_sb_in_progress && !vty->iac) {
1309 if (vty->sb_len < sizeof(vty->sb_buf))
1310 vty->sb_buf[vty->sb_len] = buf[i];
1311 vty->sb_len++;
1312 continue;
1313 }
1314
1315 if (vty->iac) {
1316 /* In case of telnet command */
1317 int ret = 0;
1318 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1319 vty->iac = 0;
1320 i += ret;
1321 continue;
1322 }
1323
1324 if (vty->status == VTY_MORE) {
1325 switch (buf[i]) {
1326 case CONTROL('C'):
1327 case 'q':
1328 case 'Q':
1329 vty_buffer_reset(vty);
1330 break;
1331#if 0 /* More line does not work for "show ip bgp". */
1332 case '\n':
1333 case '\r':
1334 vty->status = VTY_MORELINE;
1335 break;
1336#endif
1337 default:
1338 break;
1339 }
1340 continue;
1341 }
1342
1343 /* Escape character. */
1344 if (vty->escape == VTY_ESCAPE) {
1345 vty_escape_map(buf[i], vty);
1346 continue;
1347 }
1348
1349 /* Pre-escape status. */
1350 if (vty->escape == VTY_PRE_ESCAPE) {
1351 switch (buf[i]) {
1352 case '[':
1353 vty->escape = VTY_ESCAPE;
1354 break;
1355 case 'b':
1356 vty_backward_word(vty);
1357 vty->escape = VTY_NORMAL;
1358 break;
1359 case 'f':
1360 vty_forward_word(vty);
1361 vty->escape = VTY_NORMAL;
1362 break;
1363 case 'd':
1364 vty_forward_kill_word(vty);
1365 vty->escape = VTY_NORMAL;
1366 break;
1367 case CONTROL('H'):
1368 case 0x7f:
1369 vty_backward_kill_word(vty);
1370 vty->escape = VTY_NORMAL;
1371 break;
1372 default:
1373 vty->escape = VTY_NORMAL;
1374 break;
1375 }
1376 continue;
1377 }
1378
1379 switch (buf[i]) {
1380 case CONTROL('A'):
1381 vty_beginning_of_line(vty);
1382 break;
1383 case CONTROL('B'):
1384 vty_backward_char(vty);
1385 break;
1386 case CONTROL('C'):
1387 vty_stop_input(vty);
1388 break;
1389 case CONTROL('D'):
1390 vty_delete_char(vty);
1391 break;
1392 case CONTROL('E'):
1393 vty_end_of_line(vty);
1394 break;
1395 case CONTROL('F'):
1396 vty_forward_char(vty);
1397 break;
1398 case CONTROL('H'):
1399 case 0x7f:
1400 vty_delete_backward_char(vty);
1401 break;
1402 case CONTROL('K'):
1403 vty_kill_line(vty);
1404 break;
1405 case CONTROL('N'):
1406 vty_next_line(vty);
1407 break;
1408 case CONTROL('P'):
1409 vty_previous_line(vty);
1410 break;
1411 case CONTROL('T'):
1412 vty_transpose_chars(vty);
1413 break;
1414 case CONTROL('U'):
1415 vty_kill_line_from_beginning(vty);
1416 break;
1417 case CONTROL('W'):
1418 vty_backward_kill_word(vty);
1419 break;
1420 case CONTROL('Z'):
1421 vty_end_config(vty);
1422 break;
1423 case '\n':
1424 case '\r':
1425 vty_out(vty, "%s", VTY_NEWLINE);
1426 vty_execute(vty);
1427 break;
1428 case '\t':
1429 vty_complete_command(vty);
1430 break;
1431 case '?':
1432 if (vty->node == AUTH_NODE
1433 || vty->node == AUTH_ENABLE_NODE)
1434 vty_self_insert(vty, buf[i]);
1435 else
1436 vty_describe_command(vty);
1437 break;
1438 case '\033':
1439 if (i + 1 < nbytes && buf[i + 1] == '[') {
1440 vty->escape = VTY_ESCAPE;
1441 i++;
1442 } else
1443 vty->escape = VTY_PRE_ESCAPE;
1444 break;
1445 default:
1446 if (buf[i] > 31 && buf[i] < 127)
1447 vty_self_insert(vty, buf[i]);
1448 break;
1449 }
1450 }
1451
1452 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001453 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001454 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001455 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001456 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001457 vty_event(VTY_WRITE, vty_sock, vty);
1458 vty_event(VTY_READ, vty_sock, vty);
1459 }
1460 return 0;
1461}
1462
1463/* Read up configuration file */
1464static int
1465vty_read_file(FILE *confp, void *priv)
1466{
1467 int ret;
1468 struct vty *vty;
1469
1470 vty = vty_new();
1471 vty->fd = 0;
1472 vty->type = VTY_FILE;
1473 vty->node = CONFIG_NODE;
1474 vty->priv = priv;
1475
1476 ret = config_from_file(vty, confp);
1477
1478 if (ret != CMD_SUCCESS) {
1479 switch (ret) {
1480 case CMD_ERR_AMBIGUOUS:
1481 fprintf(stderr, "Ambiguous command.\n");
1482 break;
1483 case CMD_ERR_NO_MATCH:
1484 fprintf(stderr, "There is no such command.\n");
1485 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001486 case CMD_ERR_INVALID_INDENT:
1487 fprintf(stderr,
1488 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1489 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1490 "allowed, but their sequence must not change within a child block.\n");
1491 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001492 }
Keith03516d62017-09-04 11:19:13 +02001493 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001494 "line:\n%s\n", vty->buf);
1495 vty_close(vty);
1496 return -EINVAL;
1497 }
1498
1499 vty_close(vty);
1500 return 0;
1501}
1502
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001503/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001504struct vty *
1505vty_create (int vty_sock, void *priv)
1506{
1507 struct vty *vty;
1508
Alexander Couzensab383e62018-07-17 19:02:34 +02001509 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001510
1511 tcgetattr(vty_sock, &t);
1512 cfmakeraw(&t);
1513 tcsetattr(vty_sock, TCSANOW, &t);
1514
1515 /* Allocate new vty structure and set up default values. */
1516 vty = vty_new ();
1517 vty->fd = vty_sock;
1518 vty->priv = priv;
1519 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001520 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001521 {
1522 if (host.advanced)
1523 vty->node = ENABLE_NODE;
1524 else
1525 vty->node = VIEW_NODE;
1526 }
1527 else
1528 vty->node = AUTH_NODE;
1529 vty->fail = 0;
1530 vty->cp = 0;
1531 vty_clear_buf (vty);
1532 vty->length = 0;
1533 memset (vty->hist, 0, sizeof (vty->hist));
1534 vty->hp = 0;
1535 vty->hindex = 0;
1536 vector_set_index (vtyvec, vty_sock, vty);
1537 vty->status = VTY_NORMAL;
1538 if (host.lines >= 0)
1539 vty->lines = host.lines;
1540 else
1541 vty->lines = -1;
1542
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001543 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001544 {
1545 /* Vty is not available if password isn't set. */
1546 if (host.password == NULL && host.password_encrypt == NULL)
1547 {
1548 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1549 vty->status = VTY_CLOSE;
1550 vty_close (vty);
1551 return NULL;
1552 }
1553 }
1554
1555 /* Say hello to the world. */
1556 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001557 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001558 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1559
1560 /* Setting up terminal. */
1561 vty_will_echo (vty);
1562 vty_will_suppress_go_ahead (vty);
1563
1564 vty_dont_linemode (vty);
1565 vty_do_window_size (vty);
1566 /* vty_dont_lflow_ahead (vty); */
1567
1568 vty_prompt (vty);
1569
1570 /* Add read/write thread. */
1571 vty_event (VTY_WRITE, vty_sock, vty);
1572 vty_event (VTY_READ, vty_sock, vty);
1573
1574 return vty;
1575}
1576
1577DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1578{
1579 unsigned int i;
1580 struct vty *v;
1581
1582 for (i = 0; i < vector_active(vtyvec); i++)
1583 if ((v = vector_slot(vtyvec, i)) != NULL)
1584 vty_out(vty, "%svty[%d] %s",
1585 v->config ? "*" : " ", i, VTY_NEWLINE);
1586 return CMD_SUCCESS;
1587}
1588
1589/* Move to vty configuration mode. */
1590DEFUN(line_vty,
1591 line_vty_cmd,
1592 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1593{
1594 vty->node = VTY_NODE;
1595 return CMD_SUCCESS;
1596}
1597
1598/* vty login. */
1599DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1600{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001601 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001602 return CMD_SUCCESS;
1603}
1604
1605DEFUN(no_vty_login,
1606 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1607{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001608 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001609 return CMD_SUCCESS;
1610}
1611
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001612/* vty bind */
1613DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D",
1614 "Accept VTY telnet connections on local interface\n"
1615 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n")
1616{
1617 talloc_free((void*)vty_bind_addr);
1618 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
1619 return CMD_SUCCESS;
1620}
1621
1622const char *vty_get_bind_addr(void)
1623{
1624 if (!vty_bind_addr)
1625 return VTY_BIND_ADDR_DEFAULT;
1626 return vty_bind_addr;
1627}
1628
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001629DEFUN(service_advanced_vty,
1630 service_advanced_vty_cmd,
1631 "service advanced-vty",
1632 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1633{
1634 host.advanced = 1;
1635 return CMD_SUCCESS;
1636}
1637
1638DEFUN(no_service_advanced_vty,
1639 no_service_advanced_vty_cmd,
1640 "no service advanced-vty",
1641 NO_STR
1642 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1643{
1644 host.advanced = 0;
1645 return CMD_SUCCESS;
1646}
1647
1648DEFUN(terminal_monitor,
1649 terminal_monitor_cmd,
1650 "terminal monitor",
1651 "Set terminal line parameters\n"
1652 "Copy debug output to the current terminal line\n")
1653{
1654 vty->monitor = 1;
1655 return CMD_SUCCESS;
1656}
1657
1658DEFUN(terminal_no_monitor,
1659 terminal_no_monitor_cmd,
1660 "terminal no monitor",
1661 "Set terminal line parameters\n"
1662 NO_STR "Copy debug output to the current terminal line\n")
1663{
1664 vty->monitor = 0;
1665 return CMD_SUCCESS;
1666}
1667
1668DEFUN(show_history,
1669 show_history_cmd,
1670 "show history", SHOW_STR "Display the session command history\n")
1671{
1672 int index;
1673
1674 for (index = vty->hindex + 1; index != vty->hindex;) {
1675 if (index == VTY_MAXHIST) {
1676 index = 0;
1677 continue;
1678 }
1679
1680 if (vty->hist[index] != NULL)
1681 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1682
1683 index++;
1684 }
1685
1686 return CMD_SUCCESS;
1687}
1688
1689/* Display current configuration. */
1690static int vty_config_write(struct vty *vty)
1691{
1692 vty_out(vty, "line vty%s", VTY_NEWLINE);
1693
1694 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001695 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001696 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001697 else
1698 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001699
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001700 /* bind */
Neels Hofmeyr898e1d82016-08-20 16:33:47 +02001701 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0))
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001702 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1703
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001704 vty_out(vty, "!%s", VTY_NEWLINE);
1705
1706 return CMD_SUCCESS;
1707}
1708
1709struct cmd_node vty_node = {
1710 VTY_NODE,
1711 "%s(config-line)# ",
1712 1,
1713};
1714
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001715/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001716void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001717{
1718 unsigned int i;
1719 struct vty *vty;
1720 struct thread *vty_serv_thread;
1721
1722 for (i = 0; i < vector_active(vtyvec); i++)
1723 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1724 buffer_reset(vty->obuf);
1725 vty->status = VTY_CLOSE;
1726 vty_close(vty);
1727 }
1728
1729 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1730 if ((vty_serv_thread =
1731 vector_slot(Vvty_serv_thread, i)) != NULL) {
1732 //thread_cancel (vty_serv_thread);
1733 vector_slot(Vvty_serv_thread, i) = NULL;
1734 close(i);
1735 }
1736}
1737
1738static void vty_save_cwd(void)
1739{
1740 char cwd[MAXPATHLEN];
1741 char *c ;
1742
1743 c = getcwd(cwd, MAXPATHLEN);
1744
1745 if (!c) {
1746 if (chdir(SYSCONFDIR) != 0)
1747 perror("chdir failed");
1748 if (getcwd(cwd, MAXPATHLEN) == NULL)
1749 perror("getcwd failed");
1750 }
1751
1752 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1753 strcpy(vty_cwd, cwd);
1754}
1755
Harald Welte95b2b472011-07-16 11:58:09 +02001756char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001757{
1758 return vty_cwd;
1759}
1760
1761int vty_shell_serv(struct vty *vty)
1762{
1763 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1764}
1765
Harald Welte95b2b472011-07-16 11:58:09 +02001766void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001767{
1768 vtyvec = vector_init(VECTOR_MIN_SIZE);
1769}
1770
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001771/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001772 * \param[in] app_info application information
1773 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001774/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001775void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001776{
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301777 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001778 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1779 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1780
1781 cmd_init(1);
1782
Harald Welte237f6242010-05-25 23:00:45 +02001783 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001784
1785 /* For further configuration read, preserve current directory. */
1786 vty_save_cwd();
1787
1788 vtyvec = vector_init(VECTOR_MIN_SIZE);
1789
1790 /* Install bgp top node. */
1791 install_node(&vty_node, vty_config_write);
1792
1793 install_element_ve(&config_who_cmd);
1794 install_element_ve(&show_history_cmd);
1795 install_element(CONFIG_NODE, &line_vty_cmd);
1796 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1797 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1798 install_element(CONFIG_NODE, &show_history_cmd);
1799 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1800 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1801
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001802 install_element(VTY_NODE, &vty_login_cmd);
1803 install_element(VTY_NODE, &no_vty_login_cmd);
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001804 install_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001805}
1806
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001807/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001808 * \param[in] file_name file name of the configuration file
1809 * \param[in] priv private data to be passed to \ref vty_read_file
1810 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001811int vty_read_config_file(const char *file_name, void *priv)
1812{
1813 FILE *cfile;
1814 int rc;
1815
1816 cfile = fopen(file_name, "r");
1817 if (!cfile)
1818 return -ENOENT;
1819
1820 rc = vty_read_file(cfile, priv);
1821 fclose(cfile);
1822
1823 host_config_set(file_name);
1824
1825 return rc;
1826}
Harald Welte7acb30c2011-08-17 17:13:48 +02001827
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001828/*! @} */