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