blob: 5f5e6a4ccc14642f50f5f47a89aedd7c5abad7fa [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);
805 /* FIXME: we need to call the exit function of the specific node
806 * in question, not this generic one that doesn't know all nodes */
807 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
808 vty_prompt(vty);
809 vty->cp = 0;
810}
811
812/* When '^Z' is received from vty, move down to the enable mode. */
813static void vty_end_config(struct vty *vty)
814{
815 vty_out(vty, "%s", VTY_NEWLINE);
816
817 /* FIXME: we need to call the exit function of the specific node
818 * in question, not this generic one that doesn't know all nodes */
819 switch (vty->node) {
820 case VIEW_NODE:
821 case ENABLE_NODE:
822 /* Nothing to do. */
823 break;
824 case CONFIG_NODE:
825 case VTY_NODE:
826 vty_config_unlock(vty);
827 vty->node = ENABLE_NODE;
828 break;
Harald Welte28222962011-02-18 20:37:04 +0100829 case CFG_LOG_NODE:
830 vty->node = CONFIG_NODE;
831 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200832 default:
833 /* Unknown node, we have to ignore it. */
834 break;
835 }
836
837 vty_prompt(vty);
838 vty->cp = 0;
839}
840
841/* Delete a charcter at the current point. */
842static void vty_delete_char(struct vty *vty)
843{
844 int i;
845 int size;
846
847 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
848 return;
849
850 if (vty->length == 0) {
851 vty_down_level(vty);
852 return;
853 }
854
855 if (vty->cp == vty->length)
856 return; /* completion need here? */
857
858 size = vty->length - vty->cp;
859
860 vty->length--;
861 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
862 vty->buf[vty->length] = '\0';
863
864 vty_write(vty, &vty->buf[vty->cp], size - 1);
865 vty_write(vty, &telnet_space_char, 1);
866
867 for (i = 0; i < size; i++)
868 vty_write(vty, &telnet_backward_char, 1);
869}
870
871/* Delete a character before the point. */
872static void vty_delete_backward_char(struct vty *vty)
873{
874 if (vty->cp == 0)
875 return;
876
877 vty_backward_char(vty);
878 vty_delete_char(vty);
879}
880
881/* Kill rest of line from current point. */
882static void vty_kill_line(struct vty *vty)
883{
884 int i;
885 int size;
886
887 size = vty->length - vty->cp;
888
889 if (size == 0)
890 return;
891
892 for (i = 0; i < size; i++)
893 vty_write(vty, &telnet_space_char, 1);
894 for (i = 0; i < size; i++)
895 vty_write(vty, &telnet_backward_char, 1);
896
897 memset(&vty->buf[vty->cp], 0, size);
898 vty->length = vty->cp;
899}
900
901/* Kill line from the beginning. */
902static void vty_kill_line_from_beginning(struct vty *vty)
903{
904 vty_beginning_of_line(vty);
905 vty_kill_line(vty);
906}
907
908/* Delete a word before the point. */
909static void vty_forward_kill_word(struct vty *vty)
910{
911 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
912 vty_delete_char(vty);
913 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
914 vty_delete_char(vty);
915}
916
917/* Delete a word before the point. */
918static void vty_backward_kill_word(struct vty *vty)
919{
920 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
921 vty_delete_backward_char(vty);
922 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
923 vty_delete_backward_char(vty);
924}
925
926/* Transpose chars before or at the point. */
927static void vty_transpose_chars(struct vty *vty)
928{
929 char c1, c2;
930
931 /* If length is short or point is near by the beginning of line then
932 return. */
933 if (vty->length < 2 || vty->cp < 1)
934 return;
935
936 /* In case of point is located at the end of the line. */
937 if (vty->cp == vty->length) {
938 c1 = vty->buf[vty->cp - 1];
939 c2 = vty->buf[vty->cp - 2];
940
941 vty_backward_char(vty);
942 vty_backward_char(vty);
943 vty_self_insert_overwrite(vty, c1);
944 vty_self_insert_overwrite(vty, c2);
945 } else {
946 c1 = vty->buf[vty->cp];
947 c2 = vty->buf[vty->cp - 1];
948
949 vty_backward_char(vty);
950 vty_self_insert_overwrite(vty, c1);
951 vty_self_insert_overwrite(vty, c2);
952 }
953}
954
955/* Do completion at vty interface. */
956static void vty_complete_command(struct vty *vty)
957{
958 int i;
959 int ret;
960 char **matched = NULL;
961 vector vline;
962
963 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
964 return;
965
966 vline = cmd_make_strvec(vty->buf);
967 if (vline == NULL)
968 return;
969
970 /* In case of 'help \t'. */
971 if (isspace((int)vty->buf[vty->length - 1]))
972 vector_set(vline, '\0');
973
974 matched = cmd_complete_command(vline, vty, &ret);
975
976 cmd_free_strvec(vline);
977
978 vty_out(vty, "%s", VTY_NEWLINE);
979 switch (ret) {
980 case CMD_ERR_AMBIGUOUS:
981 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
982 vty_prompt(vty);
983 vty_redraw_line(vty);
984 break;
985 case CMD_ERR_NO_MATCH:
986 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
987 vty_prompt(vty);
988 vty_redraw_line(vty);
989 break;
990 case CMD_COMPLETE_FULL_MATCH:
991 vty_prompt(vty);
992 vty_redraw_line(vty);
993 vty_backward_pure_word(vty);
994 vty_insert_word_overwrite(vty, matched[0]);
995 vty_self_insert(vty, ' ');
996 talloc_free(matched[0]);
997 break;
998 case CMD_COMPLETE_MATCH:
999 vty_prompt(vty);
1000 vty_redraw_line(vty);
1001 vty_backward_pure_word(vty);
1002 vty_insert_word_overwrite(vty, matched[0]);
1003 talloc_free(matched[0]);
1004 break;
1005 case CMD_COMPLETE_LIST_MATCH:
1006 for (i = 0; matched[i] != NULL; i++) {
1007 if (i != 0 && ((i % 6) == 0))
1008 vty_out(vty, "%s", VTY_NEWLINE);
1009 vty_out(vty, "%-10s ", matched[i]);
1010 talloc_free(matched[i]);
1011 }
1012 vty_out(vty, "%s", VTY_NEWLINE);
1013
1014 vty_prompt(vty);
1015 vty_redraw_line(vty);
1016 break;
1017 case CMD_ERR_NOTHING_TODO:
1018 vty_prompt(vty);
1019 vty_redraw_line(vty);
1020 break;
1021 default:
1022 break;
1023 }
1024 if (matched)
1025 vector_only_index_free(matched);
1026}
1027
1028static void
1029vty_describe_fold(struct vty *vty, int cmd_width,
1030 unsigned int desc_width, struct desc *desc)
1031{
1032 char *buf;
1033 const char *cmd, *p;
1034 int pos;
1035
1036 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1037
1038 if (desc_width <= 0) {
1039 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1040 VTY_NEWLINE);
1041 return;
1042 }
1043
1044 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1045 if (!buf)
1046 return;
1047
1048 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1049 for (pos = desc_width; pos > 0; pos--)
1050 if (*(p + pos) == ' ')
1051 break;
1052
1053 if (pos == 0)
1054 break;
1055
1056 strncpy(buf, p, pos);
1057 buf[pos] = '\0';
1058 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1059
1060 cmd = "";
1061 }
1062
1063 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1064
1065 talloc_free(buf);
1066}
1067
1068/* Describe matched command function. */
1069static void vty_describe_command(struct vty *vty)
1070{
1071 int ret;
1072 vector vline;
1073 vector describe;
1074 unsigned int i, width, desc_width;
1075 struct desc *desc, *desc_cr = NULL;
1076
1077 vline = cmd_make_strvec(vty->buf);
1078
1079 /* In case of '> ?'. */
1080 if (vline == NULL) {
1081 vline = vector_init(1);
1082 vector_set(vline, '\0');
1083 } else if (isspace((int)vty->buf[vty->length - 1]))
1084 vector_set(vline, '\0');
1085
1086 describe = cmd_describe_command(vline, vty, &ret);
1087
1088 vty_out(vty, "%s", VTY_NEWLINE);
1089
1090 /* Ambiguous error. */
1091 switch (ret) {
1092 case CMD_ERR_AMBIGUOUS:
1093 cmd_free_strvec(vline);
1094 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1095 vty_prompt(vty);
1096 vty_redraw_line(vty);
1097 return;
1098 break;
1099 case CMD_ERR_NO_MATCH:
1100 cmd_free_strvec(vline);
1101 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1102 vty_prompt(vty);
1103 vty_redraw_line(vty);
1104 return;
1105 break;
1106 }
1107
1108 /* Get width of command string. */
1109 width = 0;
1110 for (i = 0; i < vector_active(describe); i++)
1111 if ((desc = vector_slot(describe, i)) != NULL) {
1112 unsigned int len;
1113
1114 if (desc->cmd[0] == '\0')
1115 continue;
1116
1117 len = strlen(desc->cmd);
1118 if (desc->cmd[0] == '.')
1119 len--;
1120
1121 if (width < len)
1122 width = len;
1123 }
1124
1125 /* Get width of description string. */
1126 desc_width = vty->width - (width + 6);
1127
1128 /* Print out description. */
1129 for (i = 0; i < vector_active(describe); i++)
1130 if ((desc = vector_slot(describe, i)) != NULL) {
1131 if (desc->cmd[0] == '\0')
1132 continue;
1133
1134 if (strcmp(desc->cmd, "<cr>") == 0) {
1135 desc_cr = desc;
1136 continue;
1137 }
1138
1139 if (!desc->str)
1140 vty_out(vty, " %-s%s",
1141 desc->cmd[0] ==
1142 '.' ? desc->cmd + 1 : desc->cmd,
1143 VTY_NEWLINE);
1144 else if (desc_width >= strlen(desc->str))
1145 vty_out(vty, " %-*s %s%s", width,
1146 desc->cmd[0] ==
1147 '.' ? desc->cmd + 1 : desc->cmd,
1148 desc->str, VTY_NEWLINE);
1149 else
1150 vty_describe_fold(vty, width, desc_width, desc);
1151
1152#if 0
1153 vty_out(vty, " %-*s %s%s", width
1154 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1155 desc->str ? desc->str : "", VTY_NEWLINE);
1156#endif /* 0 */
1157 }
1158
1159 if ((desc = desc_cr)) {
1160 if (!desc->str)
1161 vty_out(vty, " %-s%s",
1162 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1163 VTY_NEWLINE);
1164 else if (desc_width >= strlen(desc->str))
1165 vty_out(vty, " %-*s %s%s", width,
1166 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1167 desc->str, VTY_NEWLINE);
1168 else
1169 vty_describe_fold(vty, width, desc_width, desc);
1170 }
1171
1172 cmd_free_strvec(vline);
1173 vector_free(describe);
1174
1175 vty_prompt(vty);
1176 vty_redraw_line(vty);
1177}
1178
1179/* ^C stop current input and do not add command line to the history. */
1180static void vty_stop_input(struct vty *vty)
1181{
1182 vty->cp = vty->length = 0;
1183 vty_clear_buf(vty);
1184 vty_out(vty, "%s", VTY_NEWLINE);
1185
1186 switch (vty->node) {
1187 case VIEW_NODE:
1188 case ENABLE_NODE:
1189 /* Nothing to do. */
1190 break;
1191 case CONFIG_NODE:
1192 case VTY_NODE:
1193 vty_config_unlock(vty);
1194 vty->node = ENABLE_NODE;
1195 break;
Harald Welte28222962011-02-18 20:37:04 +01001196 case CFG_LOG_NODE:
1197 vty->node = CONFIG_NODE;
1198 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001199 default:
1200 /* Unknown node, we have to ignore it. */
1201 break;
1202 }
1203 vty_prompt(vty);
1204
1205 /* Set history pointer to the latest one. */
1206 vty->hp = vty->hindex;
1207}
1208
1209#define CONTROL(X) ((X) - '@')
1210#define VTY_NORMAL 0
1211#define VTY_PRE_ESCAPE 1
1212#define VTY_ESCAPE 2
1213
1214/* Escape character command map. */
1215static void vty_escape_map(unsigned char c, struct vty *vty)
1216{
1217 switch (c) {
1218 case ('A'):
1219 vty_previous_line(vty);
1220 break;
1221 case ('B'):
1222 vty_next_line(vty);
1223 break;
1224 case ('C'):
1225 vty_forward_char(vty);
1226 break;
1227 case ('D'):
1228 vty_backward_char(vty);
1229 break;
1230 default:
1231 break;
1232 }
1233
1234 /* Go back to normal mode. */
1235 vty->escape = VTY_NORMAL;
1236}
1237
1238/* Quit print out to the buffer. */
1239static void vty_buffer_reset(struct vty *vty)
1240{
1241 buffer_reset(vty->obuf);
1242 vty_prompt(vty);
1243 vty_redraw_line(vty);
1244}
1245
Harald Welte7acb30c2011-08-17 17:13:48 +02001246/*! \brief Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001247int vty_read(struct vty *vty)
1248{
1249 int i;
1250 int nbytes;
1251 unsigned char buf[VTY_READ_BUFSIZ];
1252
1253 int vty_sock = vty->fd;
1254
1255 /* Read raw data from socket */
1256 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1257 if (nbytes < 0) {
1258 if (ERRNO_IO_RETRY(errno)) {
1259 vty_event(VTY_READ, vty_sock, vty);
1260 return 0;
1261 }
1262 }
1263 buffer_reset(vty->obuf);
1264 vty->status = VTY_CLOSE;
1265 }
1266
1267 for (i = 0; i < nbytes; i++) {
1268 if (buf[i] == IAC) {
1269 if (!vty->iac) {
1270 vty->iac = 1;
1271 continue;
1272 } else {
1273 vty->iac = 0;
1274 }
1275 }
1276
1277 if (vty->iac_sb_in_progress && !vty->iac) {
1278 if (vty->sb_len < sizeof(vty->sb_buf))
1279 vty->sb_buf[vty->sb_len] = buf[i];
1280 vty->sb_len++;
1281 continue;
1282 }
1283
1284 if (vty->iac) {
1285 /* In case of telnet command */
1286 int ret = 0;
1287 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1288 vty->iac = 0;
1289 i += ret;
1290 continue;
1291 }
1292
1293 if (vty->status == VTY_MORE) {
1294 switch (buf[i]) {
1295 case CONTROL('C'):
1296 case 'q':
1297 case 'Q':
1298 vty_buffer_reset(vty);
1299 break;
1300#if 0 /* More line does not work for "show ip bgp". */
1301 case '\n':
1302 case '\r':
1303 vty->status = VTY_MORELINE;
1304 break;
1305#endif
1306 default:
1307 break;
1308 }
1309 continue;
1310 }
1311
1312 /* Escape character. */
1313 if (vty->escape == VTY_ESCAPE) {
1314 vty_escape_map(buf[i], vty);
1315 continue;
1316 }
1317
1318 /* Pre-escape status. */
1319 if (vty->escape == VTY_PRE_ESCAPE) {
1320 switch (buf[i]) {
1321 case '[':
1322 vty->escape = VTY_ESCAPE;
1323 break;
1324 case 'b':
1325 vty_backward_word(vty);
1326 vty->escape = VTY_NORMAL;
1327 break;
1328 case 'f':
1329 vty_forward_word(vty);
1330 vty->escape = VTY_NORMAL;
1331 break;
1332 case 'd':
1333 vty_forward_kill_word(vty);
1334 vty->escape = VTY_NORMAL;
1335 break;
1336 case CONTROL('H'):
1337 case 0x7f:
1338 vty_backward_kill_word(vty);
1339 vty->escape = VTY_NORMAL;
1340 break;
1341 default:
1342 vty->escape = VTY_NORMAL;
1343 break;
1344 }
1345 continue;
1346 }
1347
1348 switch (buf[i]) {
1349 case CONTROL('A'):
1350 vty_beginning_of_line(vty);
1351 break;
1352 case CONTROL('B'):
1353 vty_backward_char(vty);
1354 break;
1355 case CONTROL('C'):
1356 vty_stop_input(vty);
1357 break;
1358 case CONTROL('D'):
1359 vty_delete_char(vty);
1360 break;
1361 case CONTROL('E'):
1362 vty_end_of_line(vty);
1363 break;
1364 case CONTROL('F'):
1365 vty_forward_char(vty);
1366 break;
1367 case CONTROL('H'):
1368 case 0x7f:
1369 vty_delete_backward_char(vty);
1370 break;
1371 case CONTROL('K'):
1372 vty_kill_line(vty);
1373 break;
1374 case CONTROL('N'):
1375 vty_next_line(vty);
1376 break;
1377 case CONTROL('P'):
1378 vty_previous_line(vty);
1379 break;
1380 case CONTROL('T'):
1381 vty_transpose_chars(vty);
1382 break;
1383 case CONTROL('U'):
1384 vty_kill_line_from_beginning(vty);
1385 break;
1386 case CONTROL('W'):
1387 vty_backward_kill_word(vty);
1388 break;
1389 case CONTROL('Z'):
1390 vty_end_config(vty);
1391 break;
1392 case '\n':
1393 case '\r':
1394 vty_out(vty, "%s", VTY_NEWLINE);
1395 vty_execute(vty);
1396 break;
1397 case '\t':
1398 vty_complete_command(vty);
1399 break;
1400 case '?':
1401 if (vty->node == AUTH_NODE
1402 || vty->node == AUTH_ENABLE_NODE)
1403 vty_self_insert(vty, buf[i]);
1404 else
1405 vty_describe_command(vty);
1406 break;
1407 case '\033':
1408 if (i + 1 < nbytes && buf[i + 1] == '[') {
1409 vty->escape = VTY_ESCAPE;
1410 i++;
1411 } else
1412 vty->escape = VTY_PRE_ESCAPE;
1413 break;
1414 default:
1415 if (buf[i] > 31 && buf[i] < 127)
1416 vty_self_insert(vty, buf[i]);
1417 break;
1418 }
1419 }
1420
1421 /* Check status. */
1422 if (vty->status == VTY_CLOSE)
1423 vty_close(vty);
1424 else {
1425 vty_event(VTY_WRITE, vty_sock, vty);
1426 vty_event(VTY_READ, vty_sock, vty);
1427 }
1428 return 0;
1429}
1430
1431/* Read up configuration file */
1432static int
1433vty_read_file(FILE *confp, void *priv)
1434{
1435 int ret;
1436 struct vty *vty;
1437
1438 vty = vty_new();
1439 vty->fd = 0;
1440 vty->type = VTY_FILE;
1441 vty->node = CONFIG_NODE;
1442 vty->priv = priv;
1443
1444 ret = config_from_file(vty, confp);
1445
1446 if (ret != CMD_SUCCESS) {
1447 switch (ret) {
1448 case CMD_ERR_AMBIGUOUS:
1449 fprintf(stderr, "Ambiguous command.\n");
1450 break;
1451 case CMD_ERR_NO_MATCH:
1452 fprintf(stderr, "There is no such command.\n");
1453 break;
1454 }
1455 fprintf(stderr, "Error occurred during reading below "
1456 "line:\n%s\n", vty->buf);
1457 vty_close(vty);
1458 return -EINVAL;
1459 }
1460
1461 vty_close(vty);
1462 return 0;
1463}
1464
Harald Welte7acb30c2011-08-17 17:13:48 +02001465/*! \brief Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001466struct vty *
1467vty_create (int vty_sock, void *priv)
1468{
1469 struct vty *vty;
1470
1471 struct termios t;
1472
1473 tcgetattr(vty_sock, &t);
1474 cfmakeraw(&t);
1475 tcsetattr(vty_sock, TCSANOW, &t);
1476
1477 /* Allocate new vty structure and set up default values. */
1478 vty = vty_new ();
1479 vty->fd = vty_sock;
1480 vty->priv = priv;
1481 vty->type = VTY_TERM;
1482 if (no_password_check)
1483 {
1484 if (host.advanced)
1485 vty->node = ENABLE_NODE;
1486 else
1487 vty->node = VIEW_NODE;
1488 }
1489 else
1490 vty->node = AUTH_NODE;
1491 vty->fail = 0;
1492 vty->cp = 0;
1493 vty_clear_buf (vty);
1494 vty->length = 0;
1495 memset (vty->hist, 0, sizeof (vty->hist));
1496 vty->hp = 0;
1497 vty->hindex = 0;
1498 vector_set_index (vtyvec, vty_sock, vty);
1499 vty->status = VTY_NORMAL;
1500 if (host.lines >= 0)
1501 vty->lines = host.lines;
1502 else
1503 vty->lines = -1;
1504
1505 if (! no_password_check)
1506 {
1507 /* Vty is not available if password isn't set. */
1508 if (host.password == NULL && host.password_encrypt == NULL)
1509 {
1510 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1511 vty->status = VTY_CLOSE;
1512 vty_close (vty);
1513 return NULL;
1514 }
1515 }
1516
1517 /* Say hello to the world. */
1518 vty_hello (vty);
1519 if (! no_password_check)
1520 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1521
1522 /* Setting up terminal. */
1523 vty_will_echo (vty);
1524 vty_will_suppress_go_ahead (vty);
1525
1526 vty_dont_linemode (vty);
1527 vty_do_window_size (vty);
1528 /* vty_dont_lflow_ahead (vty); */
1529
1530 vty_prompt (vty);
1531
1532 /* Add read/write thread. */
1533 vty_event (VTY_WRITE, vty_sock, vty);
1534 vty_event (VTY_READ, vty_sock, vty);
1535
1536 return vty;
1537}
1538
1539DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1540{
1541 unsigned int i;
1542 struct vty *v;
1543
1544 for (i = 0; i < vector_active(vtyvec); i++)
1545 if ((v = vector_slot(vtyvec, i)) != NULL)
1546 vty_out(vty, "%svty[%d] %s",
1547 v->config ? "*" : " ", i, VTY_NEWLINE);
1548 return CMD_SUCCESS;
1549}
1550
1551/* Move to vty configuration mode. */
1552DEFUN(line_vty,
1553 line_vty_cmd,
1554 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1555{
1556 vty->node = VTY_NODE;
1557 return CMD_SUCCESS;
1558}
1559
1560/* vty login. */
1561DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1562{
1563 no_password_check = 0;
1564 return CMD_SUCCESS;
1565}
1566
1567DEFUN(no_vty_login,
1568 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1569{
1570 no_password_check = 1;
1571 return CMD_SUCCESS;
1572}
1573
1574DEFUN(service_advanced_vty,
1575 service_advanced_vty_cmd,
1576 "service advanced-vty",
1577 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1578{
1579 host.advanced = 1;
1580 return CMD_SUCCESS;
1581}
1582
1583DEFUN(no_service_advanced_vty,
1584 no_service_advanced_vty_cmd,
1585 "no service advanced-vty",
1586 NO_STR
1587 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1588{
1589 host.advanced = 0;
1590 return CMD_SUCCESS;
1591}
1592
1593DEFUN(terminal_monitor,
1594 terminal_monitor_cmd,
1595 "terminal monitor",
1596 "Set terminal line parameters\n"
1597 "Copy debug output to the current terminal line\n")
1598{
1599 vty->monitor = 1;
1600 return CMD_SUCCESS;
1601}
1602
1603DEFUN(terminal_no_monitor,
1604 terminal_no_monitor_cmd,
1605 "terminal no monitor",
1606 "Set terminal line parameters\n"
1607 NO_STR "Copy debug output to the current terminal line\n")
1608{
1609 vty->monitor = 0;
1610 return CMD_SUCCESS;
1611}
1612
1613DEFUN(show_history,
1614 show_history_cmd,
1615 "show history", SHOW_STR "Display the session command history\n")
1616{
1617 int index;
1618
1619 for (index = vty->hindex + 1; index != vty->hindex;) {
1620 if (index == VTY_MAXHIST) {
1621 index = 0;
1622 continue;
1623 }
1624
1625 if (vty->hist[index] != NULL)
1626 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1627
1628 index++;
1629 }
1630
1631 return CMD_SUCCESS;
1632}
1633
1634/* Display current configuration. */
1635static int vty_config_write(struct vty *vty)
1636{
1637 vty_out(vty, "line vty%s", VTY_NEWLINE);
1638
1639 /* login */
1640 if (no_password_check)
1641 vty_out(vty, " no login%s", VTY_NEWLINE);
1642
1643 vty_out(vty, "!%s", VTY_NEWLINE);
1644
1645 return CMD_SUCCESS;
1646}
1647
1648struct cmd_node vty_node = {
1649 VTY_NODE,
1650 "%s(config-line)# ",
1651 1,
1652};
1653
Harald Welte7acb30c2011-08-17 17:13:48 +02001654/*! \brief Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001655void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001656{
1657 unsigned int i;
1658 struct vty *vty;
1659 struct thread *vty_serv_thread;
1660
1661 for (i = 0; i < vector_active(vtyvec); i++)
1662 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1663 buffer_reset(vty->obuf);
1664 vty->status = VTY_CLOSE;
1665 vty_close(vty);
1666 }
1667
1668 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1669 if ((vty_serv_thread =
1670 vector_slot(Vvty_serv_thread, i)) != NULL) {
1671 //thread_cancel (vty_serv_thread);
1672 vector_slot(Vvty_serv_thread, i) = NULL;
1673 close(i);
1674 }
1675}
1676
1677static void vty_save_cwd(void)
1678{
1679 char cwd[MAXPATHLEN];
1680 char *c ;
1681
1682 c = getcwd(cwd, MAXPATHLEN);
1683
1684 if (!c) {
1685 if (chdir(SYSCONFDIR) != 0)
1686 perror("chdir failed");
1687 if (getcwd(cwd, MAXPATHLEN) == NULL)
1688 perror("getcwd failed");
1689 }
1690
1691 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1692 strcpy(vty_cwd, cwd);
1693}
1694
Harald Welte95b2b472011-07-16 11:58:09 +02001695char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001696{
1697 return vty_cwd;
1698}
1699
1700int vty_shell_serv(struct vty *vty)
1701{
1702 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1703}
1704
Harald Welte95b2b472011-07-16 11:58:09 +02001705void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001706{
1707 vtyvec = vector_init(VECTOR_MIN_SIZE);
1708}
1709
1710extern void *tall_bsc_ctx;
Harald Welte7acb30c2011-08-17 17:13:48 +02001711
1712/*! \brief Initialize VTY layer
1713 * \param[in] app_info application information
1714 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001715/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001716void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001717{
Harald Welte237f6242010-05-25 23:00:45 +02001718 tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001719 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1720 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1721
1722 cmd_init(1);
1723
Harald Welte237f6242010-05-25 23:00:45 +02001724 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001725
1726 /* For further configuration read, preserve current directory. */
1727 vty_save_cwd();
1728
1729 vtyvec = vector_init(VECTOR_MIN_SIZE);
1730
1731 /* Install bgp top node. */
1732 install_node(&vty_node, vty_config_write);
1733
1734 install_element_ve(&config_who_cmd);
1735 install_element_ve(&show_history_cmd);
1736 install_element(CONFIG_NODE, &line_vty_cmd);
1737 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1738 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1739 install_element(CONFIG_NODE, &show_history_cmd);
1740 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1741 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1742
1743 install_default(VTY_NODE);
1744 install_element(VTY_NODE, &vty_login_cmd);
1745 install_element(VTY_NODE, &no_vty_login_cmd);
1746}
1747
Harald Welte7acb30c2011-08-17 17:13:48 +02001748/*! \brief Read the configuration file using the VTY code
1749 * \param[in] file_name file name of the configuration file
1750 * \param[in] priv private data to be passed to \ref vty_read_file
1751 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001752int vty_read_config_file(const char *file_name, void *priv)
1753{
1754 FILE *cfile;
1755 int rc;
1756
1757 cfile = fopen(file_name, "r");
1758 if (!cfile)
1759 return -ENOENT;
1760
1761 rc = vty_read_file(cfile, priv);
1762 fclose(cfile);
1763
1764 host_config_set(file_name);
1765
1766 return rc;
1767}
Harald Welte7acb30c2011-08-17 17:13:48 +02001768
1769/*! }@ */