blob: da035969fe6ed52b829549dfc37432cdd63c4bf9 [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
81static int no_password_check = 1;
82
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{
294 char *p = vty_newline(vty);
295 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{
342 if (host.motdfile) {
343 FILE *f;
344 char buf[4096];
345
346 f = fopen(host.motdfile, "r");
347 if (f) {
348 while (fgets(buf, sizeof(buf), f)) {
349 char *s;
350 /* work backwards to ignore trailling isspace() */
351 for (s = buf + strlen(buf);
352 (s > buf) && isspace(*(s - 1)); s--) ;
353 *s = '\0';
354 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
355 }
356 fclose(f);
357 } else
358 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
359 } else if (host.motd)
360 vty_out(vty, "%s", host.motd);
361}
362
363/* Put out prompt and wait input from user. */
364static void vty_prompt(struct vty *vty)
365{
366 struct utsname names;
367 const char *hostname;
368
369 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100370 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200371 if (!hostname) {
372 uname(&names);
373 hostname = names.nodename;
374 }
375 vty_out(vty, cmd_prompt(vty->node), hostname);
376 }
377}
378
379/* Command execution over the vty interface. */
380static int vty_command(struct vty *vty, char *buf)
381{
382 int ret;
383 vector vline;
384
385 /* Split readline string up into the vector */
386 vline = cmd_make_strvec(buf);
387
388 if (vline == NULL)
389 return CMD_SUCCESS;
390
391 ret = cmd_execute_command(vline, vty, NULL, 0);
392 if (ret != CMD_SUCCESS)
393 switch (ret) {
394 case CMD_WARNING:
395 if (vty->type == VTY_FILE)
396 vty_out(vty, "Warning...%s", VTY_NEWLINE);
397 break;
398 case CMD_ERR_AMBIGUOUS:
399 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
400 break;
401 case CMD_ERR_NO_MATCH:
402 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
403 break;
404 case CMD_ERR_INCOMPLETE:
405 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
406 break;
407 }
408 cmd_free_strvec(vline);
409
410 return ret;
411}
412
413static const char telnet_backward_char = 0x08;
414static const char telnet_space_char = ' ';
415
416/* Basic function to write buffer to vty. */
417static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
418{
419 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
420 return;
421
422 /* Should we do buffering here ? And make vty_flush (vty) ? */
423 buffer_put(vty->obuf, buf, nbytes);
424}
425
426/* Ensure length of input buffer. Is buffer is short, double it. */
427static void vty_ensure(struct vty *vty, int length)
428{
429 if (vty->max <= length) {
430 vty->max *= 2;
431 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
432 // FIXME: check return
433 }
434}
435
436/* Basic function to insert character into vty. */
437static void vty_self_insert(struct vty *vty, char c)
438{
439 int i;
440 int length;
441
442 vty_ensure(vty, vty->length + 1);
443 length = vty->length - vty->cp;
444 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
445 vty->buf[vty->cp] = c;
446
447 vty_write(vty, &vty->buf[vty->cp], length + 1);
448 for (i = 0; i < length; i++)
449 vty_write(vty, &telnet_backward_char, 1);
450
451 vty->cp++;
452 vty->length++;
453}
454
455/* Self insert character 'c' in overwrite mode. */
456static void vty_self_insert_overwrite(struct vty *vty, char c)
457{
458 vty_ensure(vty, vty->length + 1);
459 vty->buf[vty->cp++] = c;
460
461 if (vty->cp > vty->length)
462 vty->length++;
463
464 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
465 return;
466
467 vty_write(vty, &c, 1);
468}
469
470/* Insert a word into vty interface with overwrite mode. */
471static void vty_insert_word_overwrite(struct vty *vty, char *str)
472{
473 int len = strlen(str);
474 vty_write(vty, str, len);
475 strcpy(&vty->buf[vty->cp], str);
476 vty->cp += len;
477 vty->length = vty->cp;
478}
479
480/* Forward character. */
481static void vty_forward_char(struct vty *vty)
482{
483 if (vty->cp < vty->length) {
484 vty_write(vty, &vty->buf[vty->cp], 1);
485 vty->cp++;
486 }
487}
488
489/* Backward character. */
490static void vty_backward_char(struct vty *vty)
491{
492 if (vty->cp > 0) {
493 vty->cp--;
494 vty_write(vty, &telnet_backward_char, 1);
495 }
496}
497
498/* Move to the beginning of the line. */
499static void vty_beginning_of_line(struct vty *vty)
500{
501 while (vty->cp)
502 vty_backward_char(vty);
503}
504
505/* Move to the end of the line. */
506static void vty_end_of_line(struct vty *vty)
507{
508 while (vty->cp < vty->length)
509 vty_forward_char(vty);
510}
511
512/* Add current command line to the history buffer. */
513static void vty_hist_add(struct vty *vty)
514{
515 int index;
516
517 if (vty->length == 0)
518 return;
519
520 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
521
522 /* Ignore the same string as previous one. */
523 if (vty->hist[index])
524 if (strcmp(vty->buf, vty->hist[index]) == 0) {
525 vty->hp = vty->hindex;
526 return;
527 }
528
529 /* Insert history entry. */
530 if (vty->hist[vty->hindex])
531 talloc_free(vty->hist[vty->hindex]);
532 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
533
534 /* History index rotation. */
535 vty->hindex++;
536 if (vty->hindex == VTY_MAXHIST)
537 vty->hindex = 0;
538
539 vty->hp = vty->hindex;
540}
541
542/* Get telnet window size. */
543static int
544vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
545{
546#ifdef TELNET_OPTION_DEBUG
547 int i;
548
549 for (i = 0; i < nbytes; i++)
550 {
551 switch (buf[i])
552 {
553 case IAC:
554 vty_out (vty, "IAC ");
555 break;
556 case WILL:
557 vty_out (vty, "WILL ");
558 break;
559 case WONT:
560 vty_out (vty, "WONT ");
561 break;
562 case DO:
563 vty_out (vty, "DO ");
564 break;
565 case DONT:
566 vty_out (vty, "DONT ");
567 break;
568 case SB:
569 vty_out (vty, "SB ");
570 break;
571 case SE:
572 vty_out (vty, "SE ");
573 break;
574 case TELOPT_ECHO:
575 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
576 break;
577 case TELOPT_SGA:
578 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
579 break;
580 case TELOPT_NAWS:
581 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
582 break;
583 default:
584 vty_out (vty, "%x ", buf[i]);
585 break;
586 }
587 }
588 vty_out (vty, "%s", VTY_NEWLINE);
589
590#endif /* TELNET_OPTION_DEBUG */
591
592 switch (buf[0])
593 {
594 case SB:
595 vty->sb_len = 0;
596 vty->iac_sb_in_progress = 1;
597 return 0;
598 break;
599 case SE:
600 {
601 if (!vty->iac_sb_in_progress)
602 return 0;
603
604 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
605 {
606 vty->iac_sb_in_progress = 0;
607 return 0;
608 }
609 switch (vty->sb_buf[0])
610 {
611 case TELOPT_NAWS:
612 if (vty->sb_len != TELNET_NAWS_SB_LEN)
613 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
614 "should send %d characters, but we received %lu",
615 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
616 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
617 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
618 "too small to handle the telnet NAWS option",
619 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
620 else
621 {
622 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
623 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
624#ifdef TELNET_OPTION_DEBUG
625 vty_out(vty, "TELNET NAWS window size negotiation completed: "
626 "width %d, height %d%s",
627 vty->width, vty->height, VTY_NEWLINE);
628#endif
629 }
630 break;
631 }
632 vty->iac_sb_in_progress = 0;
633 return 0;
634 break;
635 }
636 default:
637 break;
638 }
639 return 1;
640}
641
642/* Execute current command line. */
643static int vty_execute(struct vty *vty)
644{
645 int ret;
646
647 ret = CMD_SUCCESS;
648
649 switch (vty->node) {
650 case AUTH_NODE:
651 case AUTH_ENABLE_NODE:
652 vty_auth(vty, vty->buf);
653 break;
654 default:
655 ret = vty_command(vty, vty->buf);
656 if (vty->type == VTY_TERM)
657 vty_hist_add(vty);
658 break;
659 }
660
661 /* Clear command line buffer. */
662 vty->cp = vty->length = 0;
663 vty_clear_buf(vty);
664
665 if (vty->status != VTY_CLOSE)
666 vty_prompt(vty);
667
668 return ret;
669}
670
671/* Send WILL TELOPT_ECHO to remote server. */
672static void
673vty_will_echo (struct vty *vty)
674{
675 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
676 vty_out (vty, "%s", cmd);
677}
678
679/* Make suppress Go-Ahead telnet option. */
680static void
681vty_will_suppress_go_ahead (struct vty *vty)
682{
683 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
684 vty_out (vty, "%s", cmd);
685}
686
687/* Make don't use linemode over telnet. */
688static void
689vty_dont_linemode (struct vty *vty)
690{
691 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
692 vty_out (vty, "%s", cmd);
693}
694
695/* Use window size. */
696static void
697vty_do_window_size (struct vty *vty)
698{
699 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
700 vty_out (vty, "%s", cmd);
701}
702
703static void vty_kill_line_from_beginning(struct vty *);
704static void vty_redraw_line(struct vty *);
705
706/* Print command line history. This function is called from
707 vty_next_line and vty_previous_line. */
708static void vty_history_print(struct vty *vty)
709{
710 int length;
711
712 vty_kill_line_from_beginning(vty);
713
714 /* Get previous line from history buffer */
715 length = strlen(vty->hist[vty->hp]);
716 memcpy(vty->buf, vty->hist[vty->hp], length);
717 vty->cp = vty->length = length;
718
719 /* Redraw current line */
720 vty_redraw_line(vty);
721}
722
723/* Show next command line history. */
724static void vty_next_line(struct vty *vty)
725{
726 int try_index;
727
728 if (vty->hp == vty->hindex)
729 return;
730
731 /* Try is there history exist or not. */
732 try_index = vty->hp;
733 if (try_index == (VTY_MAXHIST - 1))
734 try_index = 0;
735 else
736 try_index++;
737
738 /* If there is not history return. */
739 if (vty->hist[try_index] == NULL)
740 return;
741 else
742 vty->hp = try_index;
743
744 vty_history_print(vty);
745}
746
747/* Show previous command line history. */
748static void vty_previous_line(struct vty *vty)
749{
750 int try_index;
751
752 try_index = vty->hp;
753 if (try_index == 0)
754 try_index = VTY_MAXHIST - 1;
755 else
756 try_index--;
757
758 if (vty->hist[try_index] == NULL)
759 return;
760 else
761 vty->hp = try_index;
762
763 vty_history_print(vty);
764}
765
766/* This function redraw all of the command line character. */
767static void vty_redraw_line(struct vty *vty)
768{
769 vty_write(vty, vty->buf, vty->length);
770 vty->cp = vty->length;
771}
772
773/* Forward word. */
774static void vty_forward_word(struct vty *vty)
775{
776 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
777 vty_forward_char(vty);
778
779 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
780 vty_forward_char(vty);
781}
782
783/* Backward word without skipping training space. */
784static void vty_backward_pure_word(struct vty *vty)
785{
786 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
787 vty_backward_char(vty);
788}
789
790/* Backward word. */
791static void vty_backward_word(struct vty *vty)
792{
793 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
794 vty_backward_char(vty);
795
796 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
797 vty_backward_char(vty);
798}
799
800/* When '^D' is typed at the beginning of the line we move to the down
801 level. */
802static void vty_down_level(struct vty *vty)
803{
804 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100805 /* call the exit function of the specific node */
806 if (vty->node > CONFIG_NODE)
807 vty_go_parent(vty);
808 else
809 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200810 vty_prompt(vty);
811 vty->cp = 0;
812}
813
814/* When '^Z' is received from vty, move down to the enable mode. */
815static void vty_end_config(struct vty *vty)
816{
817 vty_out(vty, "%s", VTY_NEWLINE);
818
819 /* FIXME: we need to call the exit function of the specific node
820 * in question, not this generic one that doesn't know all nodes */
821 switch (vty->node) {
822 case VIEW_NODE:
823 case ENABLE_NODE:
824 /* Nothing to do. */
825 break;
826 case CONFIG_NODE:
827 case VTY_NODE:
828 vty_config_unlock(vty);
829 vty->node = ENABLE_NODE;
830 break;
Harald Welte28222962011-02-18 20:37:04 +0100831 case CFG_LOG_NODE:
832 vty->node = CONFIG_NODE;
833 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200834 default:
835 /* Unknown node, we have to ignore it. */
836 break;
837 }
838
839 vty_prompt(vty);
840 vty->cp = 0;
841}
842
843/* Delete a charcter at the current point. */
844static void vty_delete_char(struct vty *vty)
845{
846 int i;
847 int size;
848
849 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
850 return;
851
852 if (vty->length == 0) {
853 vty_down_level(vty);
854 return;
855 }
856
857 if (vty->cp == vty->length)
858 return; /* completion need here? */
859
860 size = vty->length - vty->cp;
861
862 vty->length--;
863 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
864 vty->buf[vty->length] = '\0';
865
866 vty_write(vty, &vty->buf[vty->cp], size - 1);
867 vty_write(vty, &telnet_space_char, 1);
868
869 for (i = 0; i < size; i++)
870 vty_write(vty, &telnet_backward_char, 1);
871}
872
873/* Delete a character before the point. */
874static void vty_delete_backward_char(struct vty *vty)
875{
876 if (vty->cp == 0)
877 return;
878
879 vty_backward_char(vty);
880 vty_delete_char(vty);
881}
882
883/* Kill rest of line from current point. */
884static void vty_kill_line(struct vty *vty)
885{
886 int i;
887 int size;
888
889 size = vty->length - vty->cp;
890
891 if (size == 0)
892 return;
893
894 for (i = 0; i < size; i++)
895 vty_write(vty, &telnet_space_char, 1);
896 for (i = 0; i < size; i++)
897 vty_write(vty, &telnet_backward_char, 1);
898
899 memset(&vty->buf[vty->cp], 0, size);
900 vty->length = vty->cp;
901}
902
903/* Kill line from the beginning. */
904static void vty_kill_line_from_beginning(struct vty *vty)
905{
906 vty_beginning_of_line(vty);
907 vty_kill_line(vty);
908}
909
910/* Delete a word before the point. */
911static void vty_forward_kill_word(struct vty *vty)
912{
913 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
914 vty_delete_char(vty);
915 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
916 vty_delete_char(vty);
917}
918
919/* Delete a word before the point. */
920static void vty_backward_kill_word(struct vty *vty)
921{
922 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
923 vty_delete_backward_char(vty);
924 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
925 vty_delete_backward_char(vty);
926}
927
928/* Transpose chars before or at the point. */
929static void vty_transpose_chars(struct vty *vty)
930{
931 char c1, c2;
932
933 /* If length is short or point is near by the beginning of line then
934 return. */
935 if (vty->length < 2 || vty->cp < 1)
936 return;
937
938 /* In case of point is located at the end of the line. */
939 if (vty->cp == vty->length) {
940 c1 = vty->buf[vty->cp - 1];
941 c2 = vty->buf[vty->cp - 2];
942
943 vty_backward_char(vty);
944 vty_backward_char(vty);
945 vty_self_insert_overwrite(vty, c1);
946 vty_self_insert_overwrite(vty, c2);
947 } else {
948 c1 = vty->buf[vty->cp];
949 c2 = vty->buf[vty->cp - 1];
950
951 vty_backward_char(vty);
952 vty_self_insert_overwrite(vty, c1);
953 vty_self_insert_overwrite(vty, c2);
954 }
955}
956
957/* Do completion at vty interface. */
958static void vty_complete_command(struct vty *vty)
959{
960 int i;
961 int ret;
962 char **matched = NULL;
963 vector vline;
964
965 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
966 return;
967
968 vline = cmd_make_strvec(vty->buf);
969 if (vline == NULL)
970 return;
971
972 /* In case of 'help \t'. */
973 if (isspace((int)vty->buf[vty->length - 1]))
974 vector_set(vline, '\0');
975
976 matched = cmd_complete_command(vline, vty, &ret);
977
978 cmd_free_strvec(vline);
979
980 vty_out(vty, "%s", VTY_NEWLINE);
981 switch (ret) {
982 case CMD_ERR_AMBIGUOUS:
983 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
984 vty_prompt(vty);
985 vty_redraw_line(vty);
986 break;
987 case CMD_ERR_NO_MATCH:
988 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
989 vty_prompt(vty);
990 vty_redraw_line(vty);
991 break;
992 case CMD_COMPLETE_FULL_MATCH:
993 vty_prompt(vty);
994 vty_redraw_line(vty);
995 vty_backward_pure_word(vty);
996 vty_insert_word_overwrite(vty, matched[0]);
997 vty_self_insert(vty, ' ');
998 talloc_free(matched[0]);
999 break;
1000 case CMD_COMPLETE_MATCH:
1001 vty_prompt(vty);
1002 vty_redraw_line(vty);
1003 vty_backward_pure_word(vty);
1004 vty_insert_word_overwrite(vty, matched[0]);
1005 talloc_free(matched[0]);
1006 break;
1007 case CMD_COMPLETE_LIST_MATCH:
1008 for (i = 0; matched[i] != NULL; i++) {
1009 if (i != 0 && ((i % 6) == 0))
1010 vty_out(vty, "%s", VTY_NEWLINE);
1011 vty_out(vty, "%-10s ", matched[i]);
1012 talloc_free(matched[i]);
1013 }
1014 vty_out(vty, "%s", VTY_NEWLINE);
1015
1016 vty_prompt(vty);
1017 vty_redraw_line(vty);
1018 break;
1019 case CMD_ERR_NOTHING_TODO:
1020 vty_prompt(vty);
1021 vty_redraw_line(vty);
1022 break;
1023 default:
1024 break;
1025 }
1026 if (matched)
1027 vector_only_index_free(matched);
1028}
1029
1030static void
1031vty_describe_fold(struct vty *vty, int cmd_width,
1032 unsigned int desc_width, struct desc *desc)
1033{
1034 char *buf;
1035 const char *cmd, *p;
1036 int pos;
1037
1038 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1039
1040 if (desc_width <= 0) {
1041 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1042 VTY_NEWLINE);
1043 return;
1044 }
1045
1046 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1047 if (!buf)
1048 return;
1049
1050 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1051 for (pos = desc_width; pos > 0; pos--)
1052 if (*(p + pos) == ' ')
1053 break;
1054
1055 if (pos == 0)
1056 break;
1057
1058 strncpy(buf, p, pos);
1059 buf[pos] = '\0';
1060 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1061
1062 cmd = "";
1063 }
1064
1065 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1066
1067 talloc_free(buf);
1068}
1069
1070/* Describe matched command function. */
1071static void vty_describe_command(struct vty *vty)
1072{
1073 int ret;
1074 vector vline;
1075 vector describe;
1076 unsigned int i, width, desc_width;
1077 struct desc *desc, *desc_cr = NULL;
1078
1079 vline = cmd_make_strvec(vty->buf);
1080
1081 /* In case of '> ?'. */
1082 if (vline == NULL) {
1083 vline = vector_init(1);
1084 vector_set(vline, '\0');
1085 } else if (isspace((int)vty->buf[vty->length - 1]))
1086 vector_set(vline, '\0');
1087
1088 describe = cmd_describe_command(vline, vty, &ret);
1089
1090 vty_out(vty, "%s", VTY_NEWLINE);
1091
1092 /* Ambiguous error. */
1093 switch (ret) {
1094 case CMD_ERR_AMBIGUOUS:
1095 cmd_free_strvec(vline);
1096 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1097 vty_prompt(vty);
1098 vty_redraw_line(vty);
1099 return;
1100 break;
1101 case CMD_ERR_NO_MATCH:
1102 cmd_free_strvec(vline);
1103 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1104 vty_prompt(vty);
1105 vty_redraw_line(vty);
1106 return;
1107 break;
1108 }
1109
1110 /* Get width of command string. */
1111 width = 0;
1112 for (i = 0; i < vector_active(describe); i++)
1113 if ((desc = vector_slot(describe, i)) != NULL) {
1114 unsigned int len;
1115
1116 if (desc->cmd[0] == '\0')
1117 continue;
1118
1119 len = strlen(desc->cmd);
1120 if (desc->cmd[0] == '.')
1121 len--;
1122
1123 if (width < len)
1124 width = len;
1125 }
1126
1127 /* Get width of description string. */
1128 desc_width = vty->width - (width + 6);
1129
1130 /* Print out description. */
1131 for (i = 0; i < vector_active(describe); i++)
1132 if ((desc = vector_slot(describe, i)) != NULL) {
1133 if (desc->cmd[0] == '\0')
1134 continue;
1135
1136 if (strcmp(desc->cmd, "<cr>") == 0) {
1137 desc_cr = desc;
1138 continue;
1139 }
1140
1141 if (!desc->str)
1142 vty_out(vty, " %-s%s",
1143 desc->cmd[0] ==
1144 '.' ? desc->cmd + 1 : desc->cmd,
1145 VTY_NEWLINE);
1146 else if (desc_width >= strlen(desc->str))
1147 vty_out(vty, " %-*s %s%s", width,
1148 desc->cmd[0] ==
1149 '.' ? desc->cmd + 1 : desc->cmd,
1150 desc->str, VTY_NEWLINE);
1151 else
1152 vty_describe_fold(vty, width, desc_width, desc);
1153
1154#if 0
1155 vty_out(vty, " %-*s %s%s", width
1156 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1157 desc->str ? desc->str : "", VTY_NEWLINE);
1158#endif /* 0 */
1159 }
1160
1161 if ((desc = desc_cr)) {
1162 if (!desc->str)
1163 vty_out(vty, " %-s%s",
1164 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1165 VTY_NEWLINE);
1166 else if (desc_width >= strlen(desc->str))
1167 vty_out(vty, " %-*s %s%s", width,
1168 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1169 desc->str, VTY_NEWLINE);
1170 else
1171 vty_describe_fold(vty, width, desc_width, desc);
1172 }
1173
1174 cmd_free_strvec(vline);
1175 vector_free(describe);
1176
1177 vty_prompt(vty);
1178 vty_redraw_line(vty);
1179}
1180
1181/* ^C stop current input and do not add command line to the history. */
1182static void vty_stop_input(struct vty *vty)
1183{
1184 vty->cp = vty->length = 0;
1185 vty_clear_buf(vty);
1186 vty_out(vty, "%s", VTY_NEWLINE);
1187
1188 switch (vty->node) {
1189 case VIEW_NODE:
1190 case ENABLE_NODE:
1191 /* Nothing to do. */
1192 break;
1193 case CONFIG_NODE:
1194 case VTY_NODE:
1195 vty_config_unlock(vty);
1196 vty->node = ENABLE_NODE;
1197 break;
Harald Welte28222962011-02-18 20:37:04 +01001198 case CFG_LOG_NODE:
1199 vty->node = CONFIG_NODE;
1200 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001201 default:
1202 /* Unknown node, we have to ignore it. */
1203 break;
1204 }
1205 vty_prompt(vty);
1206
1207 /* Set history pointer to the latest one. */
1208 vty->hp = vty->hindex;
1209}
1210
1211#define CONTROL(X) ((X) - '@')
1212#define VTY_NORMAL 0
1213#define VTY_PRE_ESCAPE 1
1214#define VTY_ESCAPE 2
1215
1216/* Escape character command map. */
1217static void vty_escape_map(unsigned char c, struct vty *vty)
1218{
1219 switch (c) {
1220 case ('A'):
1221 vty_previous_line(vty);
1222 break;
1223 case ('B'):
1224 vty_next_line(vty);
1225 break;
1226 case ('C'):
1227 vty_forward_char(vty);
1228 break;
1229 case ('D'):
1230 vty_backward_char(vty);
1231 break;
1232 default:
1233 break;
1234 }
1235
1236 /* Go back to normal mode. */
1237 vty->escape = VTY_NORMAL;
1238}
1239
1240/* Quit print out to the buffer. */
1241static void vty_buffer_reset(struct vty *vty)
1242{
1243 buffer_reset(vty->obuf);
1244 vty_prompt(vty);
1245 vty_redraw_line(vty);
1246}
1247
Harald Welte7acb30c2011-08-17 17:13:48 +02001248/*! \brief Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001249int vty_read(struct vty *vty)
1250{
1251 int i;
1252 int nbytes;
1253 unsigned char buf[VTY_READ_BUFSIZ];
1254
1255 int vty_sock = vty->fd;
1256
1257 /* Read raw data from socket */
1258 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1259 if (nbytes < 0) {
1260 if (ERRNO_IO_RETRY(errno)) {
1261 vty_event(VTY_READ, vty_sock, vty);
1262 return 0;
1263 }
1264 }
1265 buffer_reset(vty->obuf);
1266 vty->status = VTY_CLOSE;
1267 }
1268
1269 for (i = 0; i < nbytes; i++) {
1270 if (buf[i] == IAC) {
1271 if (!vty->iac) {
1272 vty->iac = 1;
1273 continue;
1274 } else {
1275 vty->iac = 0;
1276 }
1277 }
1278
1279 if (vty->iac_sb_in_progress && !vty->iac) {
1280 if (vty->sb_len < sizeof(vty->sb_buf))
1281 vty->sb_buf[vty->sb_len] = buf[i];
1282 vty->sb_len++;
1283 continue;
1284 }
1285
1286 if (vty->iac) {
1287 /* In case of telnet command */
1288 int ret = 0;
1289 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1290 vty->iac = 0;
1291 i += ret;
1292 continue;
1293 }
1294
1295 if (vty->status == VTY_MORE) {
1296 switch (buf[i]) {
1297 case CONTROL('C'):
1298 case 'q':
1299 case 'Q':
1300 vty_buffer_reset(vty);
1301 break;
1302#if 0 /* More line does not work for "show ip bgp". */
1303 case '\n':
1304 case '\r':
1305 vty->status = VTY_MORELINE;
1306 break;
1307#endif
1308 default:
1309 break;
1310 }
1311 continue;
1312 }
1313
1314 /* Escape character. */
1315 if (vty->escape == VTY_ESCAPE) {
1316 vty_escape_map(buf[i], vty);
1317 continue;
1318 }
1319
1320 /* Pre-escape status. */
1321 if (vty->escape == VTY_PRE_ESCAPE) {
1322 switch (buf[i]) {
1323 case '[':
1324 vty->escape = VTY_ESCAPE;
1325 break;
1326 case 'b':
1327 vty_backward_word(vty);
1328 vty->escape = VTY_NORMAL;
1329 break;
1330 case 'f':
1331 vty_forward_word(vty);
1332 vty->escape = VTY_NORMAL;
1333 break;
1334 case 'd':
1335 vty_forward_kill_word(vty);
1336 vty->escape = VTY_NORMAL;
1337 break;
1338 case CONTROL('H'):
1339 case 0x7f:
1340 vty_backward_kill_word(vty);
1341 vty->escape = VTY_NORMAL;
1342 break;
1343 default:
1344 vty->escape = VTY_NORMAL;
1345 break;
1346 }
1347 continue;
1348 }
1349
1350 switch (buf[i]) {
1351 case CONTROL('A'):
1352 vty_beginning_of_line(vty);
1353 break;
1354 case CONTROL('B'):
1355 vty_backward_char(vty);
1356 break;
1357 case CONTROL('C'):
1358 vty_stop_input(vty);
1359 break;
1360 case CONTROL('D'):
1361 vty_delete_char(vty);
1362 break;
1363 case CONTROL('E'):
1364 vty_end_of_line(vty);
1365 break;
1366 case CONTROL('F'):
1367 vty_forward_char(vty);
1368 break;
1369 case CONTROL('H'):
1370 case 0x7f:
1371 vty_delete_backward_char(vty);
1372 break;
1373 case CONTROL('K'):
1374 vty_kill_line(vty);
1375 break;
1376 case CONTROL('N'):
1377 vty_next_line(vty);
1378 break;
1379 case CONTROL('P'):
1380 vty_previous_line(vty);
1381 break;
1382 case CONTROL('T'):
1383 vty_transpose_chars(vty);
1384 break;
1385 case CONTROL('U'):
1386 vty_kill_line_from_beginning(vty);
1387 break;
1388 case CONTROL('W'):
1389 vty_backward_kill_word(vty);
1390 break;
1391 case CONTROL('Z'):
1392 vty_end_config(vty);
1393 break;
1394 case '\n':
1395 case '\r':
1396 vty_out(vty, "%s", VTY_NEWLINE);
1397 vty_execute(vty);
1398 break;
1399 case '\t':
1400 vty_complete_command(vty);
1401 break;
1402 case '?':
1403 if (vty->node == AUTH_NODE
1404 || vty->node == AUTH_ENABLE_NODE)
1405 vty_self_insert(vty, buf[i]);
1406 else
1407 vty_describe_command(vty);
1408 break;
1409 case '\033':
1410 if (i + 1 < nbytes && buf[i + 1] == '[') {
1411 vty->escape = VTY_ESCAPE;
1412 i++;
1413 } else
1414 vty->escape = VTY_PRE_ESCAPE;
1415 break;
1416 default:
1417 if (buf[i] > 31 && buf[i] < 127)
1418 vty_self_insert(vty, buf[i]);
1419 break;
1420 }
1421 }
1422
1423 /* Check status. */
1424 if (vty->status == VTY_CLOSE)
1425 vty_close(vty);
1426 else {
1427 vty_event(VTY_WRITE, vty_sock, vty);
1428 vty_event(VTY_READ, vty_sock, vty);
1429 }
1430 return 0;
1431}
1432
1433/* Read up configuration file */
1434static int
1435vty_read_file(FILE *confp, void *priv)
1436{
1437 int ret;
1438 struct vty *vty;
1439
1440 vty = vty_new();
1441 vty->fd = 0;
1442 vty->type = VTY_FILE;
1443 vty->node = CONFIG_NODE;
1444 vty->priv = priv;
1445
1446 ret = config_from_file(vty, confp);
1447
1448 if (ret != CMD_SUCCESS) {
1449 switch (ret) {
1450 case CMD_ERR_AMBIGUOUS:
1451 fprintf(stderr, "Ambiguous command.\n");
1452 break;
1453 case CMD_ERR_NO_MATCH:
1454 fprintf(stderr, "There is no such command.\n");
1455 break;
1456 }
1457 fprintf(stderr, "Error occurred during reading below "
1458 "line:\n%s\n", vty->buf);
1459 vty_close(vty);
1460 return -EINVAL;
1461 }
1462
1463 vty_close(vty);
1464 return 0;
1465}
1466
Harald Welte7acb30c2011-08-17 17:13:48 +02001467/*! \brief Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001468struct vty *
1469vty_create (int vty_sock, void *priv)
1470{
1471 struct vty *vty;
1472
1473 struct termios t;
1474
1475 tcgetattr(vty_sock, &t);
1476 cfmakeraw(&t);
1477 tcsetattr(vty_sock, TCSANOW, &t);
1478
1479 /* Allocate new vty structure and set up default values. */
1480 vty = vty_new ();
1481 vty->fd = vty_sock;
1482 vty->priv = priv;
1483 vty->type = VTY_TERM;
1484 if (no_password_check)
1485 {
1486 if (host.advanced)
1487 vty->node = ENABLE_NODE;
1488 else
1489 vty->node = VIEW_NODE;
1490 }
1491 else
1492 vty->node = AUTH_NODE;
1493 vty->fail = 0;
1494 vty->cp = 0;
1495 vty_clear_buf (vty);
1496 vty->length = 0;
1497 memset (vty->hist, 0, sizeof (vty->hist));
1498 vty->hp = 0;
1499 vty->hindex = 0;
1500 vector_set_index (vtyvec, vty_sock, vty);
1501 vty->status = VTY_NORMAL;
1502 if (host.lines >= 0)
1503 vty->lines = host.lines;
1504 else
1505 vty->lines = -1;
1506
1507 if (! no_password_check)
1508 {
1509 /* Vty is not available if password isn't set. */
1510 if (host.password == NULL && host.password_encrypt == NULL)
1511 {
1512 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1513 vty->status = VTY_CLOSE;
1514 vty_close (vty);
1515 return NULL;
1516 }
1517 }
1518
1519 /* Say hello to the world. */
1520 vty_hello (vty);
1521 if (! no_password_check)
1522 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1523
1524 /* Setting up terminal. */
1525 vty_will_echo (vty);
1526 vty_will_suppress_go_ahead (vty);
1527
1528 vty_dont_linemode (vty);
1529 vty_do_window_size (vty);
1530 /* vty_dont_lflow_ahead (vty); */
1531
1532 vty_prompt (vty);
1533
1534 /* Add read/write thread. */
1535 vty_event (VTY_WRITE, vty_sock, vty);
1536 vty_event (VTY_READ, vty_sock, vty);
1537
1538 return vty;
1539}
1540
1541DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1542{
1543 unsigned int i;
1544 struct vty *v;
1545
1546 for (i = 0; i < vector_active(vtyvec); i++)
1547 if ((v = vector_slot(vtyvec, i)) != NULL)
1548 vty_out(vty, "%svty[%d] %s",
1549 v->config ? "*" : " ", i, VTY_NEWLINE);
1550 return CMD_SUCCESS;
1551}
1552
1553/* Move to vty configuration mode. */
1554DEFUN(line_vty,
1555 line_vty_cmd,
1556 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1557{
1558 vty->node = VTY_NODE;
1559 return CMD_SUCCESS;
1560}
1561
1562/* vty login. */
1563DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1564{
1565 no_password_check = 0;
1566 return CMD_SUCCESS;
1567}
1568
1569DEFUN(no_vty_login,
1570 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1571{
1572 no_password_check = 1;
1573 return CMD_SUCCESS;
1574}
1575
1576DEFUN(service_advanced_vty,
1577 service_advanced_vty_cmd,
1578 "service advanced-vty",
1579 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1580{
1581 host.advanced = 1;
1582 return CMD_SUCCESS;
1583}
1584
1585DEFUN(no_service_advanced_vty,
1586 no_service_advanced_vty_cmd,
1587 "no service advanced-vty",
1588 NO_STR
1589 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1590{
1591 host.advanced = 0;
1592 return CMD_SUCCESS;
1593}
1594
1595DEFUN(terminal_monitor,
1596 terminal_monitor_cmd,
1597 "terminal monitor",
1598 "Set terminal line parameters\n"
1599 "Copy debug output to the current terminal line\n")
1600{
1601 vty->monitor = 1;
1602 return CMD_SUCCESS;
1603}
1604
1605DEFUN(terminal_no_monitor,
1606 terminal_no_monitor_cmd,
1607 "terminal no monitor",
1608 "Set terminal line parameters\n"
1609 NO_STR "Copy debug output to the current terminal line\n")
1610{
1611 vty->monitor = 0;
1612 return CMD_SUCCESS;
1613}
1614
1615DEFUN(show_history,
1616 show_history_cmd,
1617 "show history", SHOW_STR "Display the session command history\n")
1618{
1619 int index;
1620
1621 for (index = vty->hindex + 1; index != vty->hindex;) {
1622 if (index == VTY_MAXHIST) {
1623 index = 0;
1624 continue;
1625 }
1626
1627 if (vty->hist[index] != NULL)
1628 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1629
1630 index++;
1631 }
1632
1633 return CMD_SUCCESS;
1634}
1635
1636/* Display current configuration. */
1637static int vty_config_write(struct vty *vty)
1638{
1639 vty_out(vty, "line vty%s", VTY_NEWLINE);
1640
1641 /* login */
1642 if (no_password_check)
1643 vty_out(vty, " no login%s", VTY_NEWLINE);
1644
1645 vty_out(vty, "!%s", VTY_NEWLINE);
1646
1647 return CMD_SUCCESS;
1648}
1649
1650struct cmd_node vty_node = {
1651 VTY_NODE,
1652 "%s(config-line)# ",
1653 1,
1654};
1655
Harald Welte7acb30c2011-08-17 17:13:48 +02001656/*! \brief Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001657void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001658{
1659 unsigned int i;
1660 struct vty *vty;
1661 struct thread *vty_serv_thread;
1662
1663 for (i = 0; i < vector_active(vtyvec); i++)
1664 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1665 buffer_reset(vty->obuf);
1666 vty->status = VTY_CLOSE;
1667 vty_close(vty);
1668 }
1669
1670 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1671 if ((vty_serv_thread =
1672 vector_slot(Vvty_serv_thread, i)) != NULL) {
1673 //thread_cancel (vty_serv_thread);
1674 vector_slot(Vvty_serv_thread, i) = NULL;
1675 close(i);
1676 }
1677}
1678
1679static void vty_save_cwd(void)
1680{
1681 char cwd[MAXPATHLEN];
1682 char *c ;
1683
1684 c = getcwd(cwd, MAXPATHLEN);
1685
1686 if (!c) {
1687 if (chdir(SYSCONFDIR) != 0)
1688 perror("chdir failed");
1689 if (getcwd(cwd, MAXPATHLEN) == NULL)
1690 perror("getcwd failed");
1691 }
1692
1693 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1694 strcpy(vty_cwd, cwd);
1695}
1696
Harald Welte95b2b472011-07-16 11:58:09 +02001697char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001698{
1699 return vty_cwd;
1700}
1701
1702int vty_shell_serv(struct vty *vty)
1703{
1704 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1705}
1706
Harald Welte95b2b472011-07-16 11:58:09 +02001707void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001708{
1709 vtyvec = vector_init(VECTOR_MIN_SIZE);
1710}
1711
1712extern void *tall_bsc_ctx;
Harald Welte7acb30c2011-08-17 17:13:48 +02001713
1714/*! \brief Initialize VTY layer
1715 * \param[in] app_info application information
1716 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001717/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001718void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001719{
Harald Welte237f6242010-05-25 23:00:45 +02001720 tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001721 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1722 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1723
1724 cmd_init(1);
1725
Harald Welte237f6242010-05-25 23:00:45 +02001726 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001727
1728 /* For further configuration read, preserve current directory. */
1729 vty_save_cwd();
1730
1731 vtyvec = vector_init(VECTOR_MIN_SIZE);
1732
1733 /* Install bgp top node. */
1734 install_node(&vty_node, vty_config_write);
1735
1736 install_element_ve(&config_who_cmd);
1737 install_element_ve(&show_history_cmd);
1738 install_element(CONFIG_NODE, &line_vty_cmd);
1739 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1740 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1741 install_element(CONFIG_NODE, &show_history_cmd);
1742 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1743 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1744
1745 install_default(VTY_NODE);
1746 install_element(VTY_NODE, &vty_login_cmd);
1747 install_element(VTY_NODE, &no_vty_login_cmd);
1748}
1749
Harald Welte7acb30c2011-08-17 17:13:48 +02001750/*! \brief Read the configuration file using the VTY code
1751 * \param[in] file_name file name of the configuration file
1752 * \param[in] priv private data to be passed to \ref vty_read_file
1753 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001754int vty_read_config_file(const char *file_name, void *priv)
1755{
1756 FILE *cfile;
1757 int rc;
1758
1759 cfile = fopen(file_name, "r");
1760 if (!cfile)
1761 return -ENOENT;
1762
1763 rc = vty_read_file(cfile, priv);
1764 fclose(cfile);
1765
1766 host_config_set(file_name);
1767
1768 return rc;
1769}
Harald Welte7acb30c2011-08-17 17:13:48 +02001770
1771/*! }@ */