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