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