blob: e5b5b48f061516fbf155c5e05df62c49f3d61b36 [file] [log] [blame]
Harald Welte955049f2009-03-10 12:16:51 +00001
2#include <stdio.h>
3#include <stdarg.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7#include <errno.h>
8#include <ctype.h>
9#include <termios.h>
10
11#include <sys/utsname.h>
12#include <sys/param.h>
13
14#include <arpa/telnet.h>
15
Harald Welte955049f2009-03-10 12:16:51 +000016#include <vty/vty.h>
17#include <vty/command.h>
18#include <vty/buffer.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010019#include <osmocore/talloc.h>
Harald Welte955049f2009-03-10 12:16:51 +000020
Harald Welte1353f962010-05-16 19:20:24 +020021#define SYSCONFDIR "/usr/local/etc"
22
Holger Hans Peter Freyther6d30a352009-08-10 08:10:26 +020023/* our callback, located in telnet_interface.c */
24void vty_event(enum event event, int sock, struct vty *vty);
25
Harald Welte955049f2009-03-10 12:16:51 +000026extern struct host host;
27
28/* Vector which store each vty structure. */
29static vector vtyvec;
30
31vector Vvty_serv_thread;
32
33char *vty_cwd = NULL;
34
35/* Configure lock. */
36static int vty_config;
37
38static int no_password_check = 1;
39
Harald Welte0224e4d2009-08-07 13:25:41 +020040void *tall_vty_ctx;
Harald Welte2477d932009-08-07 00:32:41 +020041
Harald Welte955049f2009-03-10 12:16:51 +000042static void vty_clear_buf(struct vty *vty)
43{
44 memset(vty->buf, 0, vty->max);
45}
46
47/* Allocate new vty struct. */
48struct vty *vty_new()
49{
Harald Welte2477d932009-08-07 00:32:41 +020050 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
Harald Welte955049f2009-03-10 12:16:51 +000051
52 if (!new)
53 goto out;
54
Holger Hans Peter Freyther1700c932010-04-30 12:26:52 +080055 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
Harald Welte955049f2009-03-10 12:16:51 +000056 if (!new->obuf)
57 goto out_new;
Holger Hans Peter Freyther9e282de2010-04-30 12:18:32 +080058 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
Harald Welte955049f2009-03-10 12:16:51 +000059 if (!new->buf)
60 goto out_obuf;
61
62 new->max = VTY_BUFSIZ;
63
64 return new;
65
66out_obuf:
Harald Welte2477d932009-08-07 00:32:41 +020067 buffer_free(new->obuf);
Harald Welte955049f2009-03-10 12:16:51 +000068out_new:
Harald Welte2477d932009-08-07 00:32:41 +020069 talloc_free(new);
Harald Welte955049f2009-03-10 12:16:51 +000070 new = NULL;
71out:
72 return new;
73}
74
75/* Authentication of vty */
76static void vty_auth(struct vty *vty, char *buf)
77{
78 char *passwd = NULL;
79 enum node_type next_node = 0;
80 int fail;
81 char *crypt(const char *, const char *);
82
83 switch (vty->node) {
84 case AUTH_NODE:
Harald Welted6cab812009-05-21 07:31:48 +000085#ifdef VTY_CRYPT_PW
Harald Welte955049f2009-03-10 12:16:51 +000086 if (host.encrypt)
87 passwd = host.password_encrypt;
88 else
Harald Welted6cab812009-05-21 07:31:48 +000089#endif
Harald Welte955049f2009-03-10 12:16:51 +000090 passwd = host.password;
91 if (host.advanced)
92 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
93 else
94 next_node = VIEW_NODE;
95 break;
96 case AUTH_ENABLE_NODE:
Harald Welted6cab812009-05-21 07:31:48 +000097#ifdef VTY_CRYPT_PW
Harald Welte955049f2009-03-10 12:16:51 +000098 if (host.encrypt)
99 passwd = host.enable_encrypt;
100 else
Harald Welted6cab812009-05-21 07:31:48 +0000101#endif
Harald Welte955049f2009-03-10 12:16:51 +0000102 passwd = host.enable;
103 next_node = ENABLE_NODE;
104 break;
105 }
106
107 if (passwd) {
Harald Welted6cab812009-05-21 07:31:48 +0000108#ifdef VTY_CRYPT_PW
Harald Welte955049f2009-03-10 12:16:51 +0000109 if (host.encrypt)
110 fail = strcmp(crypt(buf, passwd), passwd);
111 else
Harald Welted6cab812009-05-21 07:31:48 +0000112#endif
Harald Welte955049f2009-03-10 12:16:51 +0000113 fail = strcmp(buf, passwd);
114 } else
115 fail = 1;
116
117 if (!fail) {
118 vty->fail = 0;
119 vty->node = next_node; /* Success ! */
120 } else {
121 vty->fail++;
122 if (vty->fail >= 3) {
123 if (vty->node == AUTH_NODE) {
124 vty_out(vty,
125 "%% Bad passwords, too many failures!%s",
126 VTY_NEWLINE);
127 vty->status = VTY_CLOSE;
128 } else {
129 /* AUTH_ENABLE_NODE */
130 vty->fail = 0;
131 vty_out(vty,
132 "%% Bad enable passwords, too many failures!%s",
133 VTY_NEWLINE);
134 vty->node = VIEW_NODE;
135 }
136 }
137 }
138}
139
140/* Close vty interface. */
141void vty_close(struct vty *vty)
142{
143 int i;
144
Harald Welte2477d932009-08-07 00:32:41 +0200145 if (vty->obuf) {
146 /* Flush buffer. */
147 buffer_flush_all(vty->obuf, vty->fd);
Harald Welte955049f2009-03-10 12:16:51 +0000148
Harald Welte2477d932009-08-07 00:32:41 +0200149 /* Free input buffer. */
150 buffer_free(vty->obuf);
151 vty->obuf = NULL;
152 }
Harald Welte955049f2009-03-10 12:16:51 +0000153
154 /* Free command history. */
155 for (i = 0; i < VTY_MAXHIST; i++)
156 if (vty->hist[i])
Harald Welte2477d932009-08-07 00:32:41 +0200157 talloc_free(vty->hist[i]);
Harald Welte955049f2009-03-10 12:16:51 +0000158
159 /* Unset vector. */
160 vector_unset(vtyvec, vty->fd);
161
162 /* Close socket. */
163 if (vty->fd > 0)
164 close(vty->fd);
165
Harald Welte2477d932009-08-07 00:32:41 +0200166 if (vty->buf) {
167 talloc_free(vty->buf);
168 vty->buf = NULL;
169 }
Harald Welte955049f2009-03-10 12:16:51 +0000170
171 /* Check configure. */
172 vty_config_unlock(vty);
173
Holger Hans Peter Freyther22ca95c2010-04-30 11:33:08 +0800174 /* VTY_CLOSED is handled by the telnet_interface */
Harald Welte2477d932009-08-07 00:32:41 +0200175 vty_event(VTY_CLOSED, vty->fd, vty);
176
177 /* OK free vty. */
178 talloc_free(vty);
Harald Welte955049f2009-03-10 12:16:51 +0000179}
180
181int vty_shell(struct vty *vty)
182{
183 return vty->type == VTY_SHELL ? 1 : 0;
184}
185
186
187/* VTY standard output function. */
188int vty_out(struct vty *vty, const char *format, ...)
189{
190 va_list args;
191 int len = 0;
192 int size = 1024;
193 char buf[1024];
194 char *p = NULL;
195
196 if (vty_shell(vty)) {
197 va_start(args, format);
198 vprintf(format, args);
199 va_end(args);
200 } else {
201 /* Try to write to initial buffer. */
202 va_start(args, format);
203 len = vsnprintf(buf, sizeof buf, format, args);
204 va_end(args);
205
206 /* Initial buffer is not enough. */
207 if (len < 0 || len >= size) {
208 while (1) {
209 if (len > -1)
210 size = len + 1;
211 else
212 size = size * 2;
213
Holger Hans Peter Freyther9e282de2010-04-30 12:18:32 +0800214 p = talloc_realloc_size(vty, p, size);
Harald Welte955049f2009-03-10 12:16:51 +0000215 if (!p)
216 return -1;
217
218 va_start(args, format);
219 len = vsnprintf(p, size, format, args);
220 va_end(args);
221
222 if (len > -1 && len < size)
223 break;
224 }
225 }
226
227 /* When initial buffer is enough to store all output. */
228 if (!p)
229 p = buf;
230
231 /* Pointer p must point out buffer. */
232 buffer_put(vty->obuf, (u_char *) p, len);
233
234 /* If p is not different with buf, it is allocated buffer. */
235 if (p != buf)
Harald Welte2477d932009-08-07 00:32:41 +0200236 talloc_free(p);
Harald Welte955049f2009-03-10 12:16:51 +0000237 }
238
Harald Welte7cb7a732009-12-17 21:32:19 +0100239 vty_event(VTY_WRITE, vty->fd, vty);
240
Harald Welte955049f2009-03-10 12:16:51 +0000241 return len;
242}
243
244int vty_out_newline(struct vty *vty)
245{
246 char *p = vty_newline(vty);
247 buffer_put(vty->obuf, p, strlen(p));
Harald Weltec63e51d2009-03-10 19:46:16 +0000248 return 0;
Harald Welte955049f2009-03-10 12:16:51 +0000249}
250
251int vty_config_lock(struct vty *vty)
252{
253 if (vty_config == 0) {
254 vty->config = 1;
255 vty_config = 1;
256 }
257 return vty->config;
258}
259
260int vty_config_unlock(struct vty *vty)
261{
262 if (vty_config == 1 && vty->config == 1) {
263 vty->config = 0;
264 vty_config = 0;
265 }
266 return vty->config;
267}
268
Harald Welte955049f2009-03-10 12:16:51 +0000269/* Say hello to vty interface. */
270void vty_hello(struct vty *vty)
271{
272 if (host.motdfile) {
273 FILE *f;
274 char buf[4096];
275
276 f = fopen(host.motdfile, "r");
277 if (f) {
278 while (fgets(buf, sizeof(buf), f)) {
279 char *s;
280 /* work backwards to ignore trailling isspace() */
281 for (s = buf + strlen(buf);
282 (s > buf) && isspace(*(s - 1)); s--) ;
283 *s = '\0';
284 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
285 }
286 fclose(f);
287 } else
288 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
289 } else if (host.motd)
Holger Hans Peter Freytherc423a122009-08-10 08:10:11 +0200290 vty_out(vty, "%s", host.motd);
Harald Welte955049f2009-03-10 12:16:51 +0000291}
292
293/* Put out prompt and wait input from user. */
294static void vty_prompt(struct vty *vty)
295{
296 struct utsname names;
297 const char *hostname;
298
299 if (vty->type == VTY_TERM) {
300 hostname = host.name;
301 if (!hostname) {
302 uname(&names);
303 hostname = names.nodename;
304 }
305 vty_out(vty, cmd_prompt(vty->node), hostname);
306 }
307}
308
309/* Command execution over the vty interface. */
310static int vty_command(struct vty *vty, char *buf)
311{
312 int ret;
313 vector vline;
314
315 /* Split readline string up into the vector */
316 vline = cmd_make_strvec(buf);
317
318 if (vline == NULL)
319 return CMD_SUCCESS;
320
321 ret = cmd_execute_command(vline, vty, NULL, 0);
322 if (ret != CMD_SUCCESS)
323 switch (ret) {
324 case CMD_WARNING:
325 if (vty->type == VTY_FILE)
326 vty_out(vty, "Warning...%s", VTY_NEWLINE);
327 break;
328 case CMD_ERR_AMBIGUOUS:
329 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
330 break;
331 case CMD_ERR_NO_MATCH:
332 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
333 break;
334 case CMD_ERR_INCOMPLETE:
335 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
336 break;
337 }
338 cmd_free_strvec(vline);
339
340 return ret;
341}
342
343static const char telnet_backward_char = 0x08;
344static const char telnet_space_char = ' ';
345
346/* Basic function to write buffer to vty. */
347static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
348{
349 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
350 return;
351
352 /* Should we do buffering here ? And make vty_flush (vty) ? */
353 buffer_put(vty->obuf, buf, nbytes);
354}
355
356/* Ensure length of input buffer. Is buffer is short, double it. */
357static void vty_ensure(struct vty *vty, int length)
358{
359 if (vty->max <= length) {
360 vty->max *= 2;
Holger Hans Peter Freyther9e282de2010-04-30 12:18:32 +0800361 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
Harald Welte955049f2009-03-10 12:16:51 +0000362 // FIXME: check return
363 }
364}
365
366/* Basic function to insert character into vty. */
367static void vty_self_insert(struct vty *vty, char c)
368{
369 int i;
370 int length;
371
372 vty_ensure(vty, vty->length + 1);
373 length = vty->length - vty->cp;
374 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
375 vty->buf[vty->cp] = c;
376
377 vty_write(vty, &vty->buf[vty->cp], length + 1);
378 for (i = 0; i < length; i++)
379 vty_write(vty, &telnet_backward_char, 1);
380
381 vty->cp++;
382 vty->length++;
383}
384
385/* Self insert character 'c' in overwrite mode. */
386static void vty_self_insert_overwrite(struct vty *vty, char c)
387{
388 vty_ensure(vty, vty->length + 1);
389 vty->buf[vty->cp++] = c;
390
391 if (vty->cp > vty->length)
392 vty->length++;
393
394 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
395 return;
396
397 vty_write(vty, &c, 1);
398}
399
400/* Insert a word into vty interface with overwrite mode. */
401static void vty_insert_word_overwrite(struct vty *vty, char *str)
402{
403 int len = strlen(str);
404 vty_write(vty, str, len);
405 strcpy(&vty->buf[vty->cp], str);
406 vty->cp += len;
407 vty->length = vty->cp;
408}
409
410/* Forward character. */
411static void vty_forward_char(struct vty *vty)
412{
413 if (vty->cp < vty->length) {
414 vty_write(vty, &vty->buf[vty->cp], 1);
415 vty->cp++;
416 }
417}
418
419/* Backward character. */
420static void vty_backward_char(struct vty *vty)
421{
422 if (vty->cp > 0) {
423 vty->cp--;
424 vty_write(vty, &telnet_backward_char, 1);
425 }
426}
427
428/* Move to the beginning of the line. */
429static void vty_beginning_of_line(struct vty *vty)
430{
431 while (vty->cp)
432 vty_backward_char(vty);
433}
434
435/* Move to the end of the line. */
436static void vty_end_of_line(struct vty *vty)
437{
438 while (vty->cp < vty->length)
439 vty_forward_char(vty);
440}
441
442/* Add current command line to the history buffer. */
443static void vty_hist_add(struct vty *vty)
444{
445 int index;
446
447 if (vty->length == 0)
448 return;
449
450 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
451
452 /* Ignore the same string as previous one. */
453 if (vty->hist[index])
454 if (strcmp(vty->buf, vty->hist[index]) == 0) {
455 vty->hp = vty->hindex;
456 return;
457 }
458
459 /* Insert history entry. */
460 if (vty->hist[vty->hindex])
Harald Welte2477d932009-08-07 00:32:41 +0200461 talloc_free(vty->hist[vty->hindex]);
Holger Hans Peter Freyther9e282de2010-04-30 12:18:32 +0800462 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
Harald Welte955049f2009-03-10 12:16:51 +0000463
464 /* History index rotation. */
465 vty->hindex++;
466 if (vty->hindex == VTY_MAXHIST)
467 vty->hindex = 0;
468
469 vty->hp = vty->hindex;
470}
471
Harald Welte1353f962010-05-16 19:20:24 +0200472#define TELNET_OPTION_DEBUG
Harald Welte955049f2009-03-10 12:16:51 +0000473/* Get telnet window size. */
474static int
475vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
476{
477#ifdef TELNET_OPTION_DEBUG
478 int i;
479
480 for (i = 0; i < nbytes; i++)
481 {
482 switch (buf[i])
483 {
484 case IAC:
485 vty_out (vty, "IAC ");
486 break;
487 case WILL:
488 vty_out (vty, "WILL ");
489 break;
490 case WONT:
491 vty_out (vty, "WONT ");
492 break;
493 case DO:
494 vty_out (vty, "DO ");
495 break;
496 case DONT:
497 vty_out (vty, "DONT ");
498 break;
499 case SB:
500 vty_out (vty, "SB ");
501 break;
502 case SE:
503 vty_out (vty, "SE ");
504 break;
505 case TELOPT_ECHO:
506 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
507 break;
508 case TELOPT_SGA:
509 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
510 break;
511 case TELOPT_NAWS:
512 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
513 break;
514 default:
515 vty_out (vty, "%x ", buf[i]);
516 break;
517 }
518 }
519 vty_out (vty, "%s", VTY_NEWLINE);
520
521#endif /* TELNET_OPTION_DEBUG */
522
523 switch (buf[0])
524 {
525 case SB:
526 vty->sb_len = 0;
527 vty->iac_sb_in_progress = 1;
528 return 0;
529 break;
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200530 case SE:
Harald Welte955049f2009-03-10 12:16:51 +0000531 {
532 if (!vty->iac_sb_in_progress)
533 return 0;
534
535 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
536 {
537 vty->iac_sb_in_progress = 0;
538 return 0;
539 }
540 switch (vty->sb_buf[0])
541 {
542 case TELOPT_NAWS:
543 if (vty->sb_len != TELNET_NAWS_SB_LEN)
544 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
545 "should send %d characters, but we received %lu",
546 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
547 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
548 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
549 "too small to handle the telnet NAWS option",
550 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
551 else
552 {
553 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
554 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
555#ifdef TELNET_OPTION_DEBUG
556 vty_out(vty, "TELNET NAWS window size negotiation completed: "
557 "width %d, height %d%s",
558 vty->width, vty->height, VTY_NEWLINE);
559#endif
560 }
561 break;
562 }
563 vty->iac_sb_in_progress = 0;
564 return 0;
565 break;
566 }
567 default:
568 break;
569 }
570 return 1;
571}
572
573/* Execute current command line. */
574static int vty_execute(struct vty *vty)
575{
576 int ret;
577
578 ret = CMD_SUCCESS;
579
580 switch (vty->node) {
581 case AUTH_NODE:
582 case AUTH_ENABLE_NODE:
583 vty_auth(vty, vty->buf);
584 break;
585 default:
586 ret = vty_command(vty, vty->buf);
587 if (vty->type == VTY_TERM)
588 vty_hist_add(vty);
589 break;
590 }
591
592 /* Clear command line buffer. */
593 vty->cp = vty->length = 0;
594 vty_clear_buf(vty);
595
596 if (vty->status != VTY_CLOSE)
597 vty_prompt(vty);
598
599 return ret;
600}
601
602/* Send WILL TELOPT_ECHO to remote server. */
603static void
604vty_will_echo (struct vty *vty)
605{
606 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
607 vty_out (vty, "%s", cmd);
608}
609
610/* Make suppress Go-Ahead telnet option. */
611static void
612vty_will_suppress_go_ahead (struct vty *vty)
613{
614 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
615 vty_out (vty, "%s", cmd);
616}
617
618/* Make don't use linemode over telnet. */
619static void
620vty_dont_linemode (struct vty *vty)
621{
622 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
623 vty_out (vty, "%s", cmd);
624}
625
626/* Use window size. */
627static void
628vty_do_window_size (struct vty *vty)
629{
630 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
631 vty_out (vty, "%s", cmd);
632}
633
634static void vty_kill_line_from_beginning(struct vty *);
635static void vty_redraw_line(struct vty *);
636
637/* Print command line history. This function is called from
638 vty_next_line and vty_previous_line. */
639static void vty_history_print(struct vty *vty)
640{
641 int length;
642
643 vty_kill_line_from_beginning(vty);
644
645 /* Get previous line from history buffer */
646 length = strlen(vty->hist[vty->hp]);
647 memcpy(vty->buf, vty->hist[vty->hp], length);
648 vty->cp = vty->length = length;
649
650 /* Redraw current line */
651 vty_redraw_line(vty);
652}
653
654/* Show next command line history. */
655static void vty_next_line(struct vty *vty)
656{
657 int try_index;
658
659 if (vty->hp == vty->hindex)
660 return;
661
662 /* Try is there history exist or not. */
663 try_index = vty->hp;
664 if (try_index == (VTY_MAXHIST - 1))
665 try_index = 0;
666 else
667 try_index++;
668
669 /* If there is not history return. */
670 if (vty->hist[try_index] == NULL)
671 return;
672 else
673 vty->hp = try_index;
674
675 vty_history_print(vty);
676}
677
678/* Show previous command line history. */
679static void vty_previous_line(struct vty *vty)
680{
681 int try_index;
682
683 try_index = vty->hp;
684 if (try_index == 0)
685 try_index = VTY_MAXHIST - 1;
686 else
687 try_index--;
688
689 if (vty->hist[try_index] == NULL)
690 return;
691 else
692 vty->hp = try_index;
693
694 vty_history_print(vty);
695}
696
697/* This function redraw all of the command line character. */
698static void vty_redraw_line(struct vty *vty)
699{
700 vty_write(vty, vty->buf, vty->length);
701 vty->cp = vty->length;
702}
703
704/* Forward word. */
705static void vty_forward_word(struct vty *vty)
706{
707 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
708 vty_forward_char(vty);
709
710 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
711 vty_forward_char(vty);
712}
713
714/* Backward word without skipping training space. */
715static void vty_backward_pure_word(struct vty *vty)
716{
717 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
718 vty_backward_char(vty);
719}
720
721/* Backward word. */
722static void vty_backward_word(struct vty *vty)
723{
724 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
725 vty_backward_char(vty);
726
727 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
728 vty_backward_char(vty);
729}
730
731/* When '^D' is typed at the beginning of the line we move to the down
732 level. */
733static void vty_down_level(struct vty *vty)
734{
735 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte62ab20c2010-05-14 18:59:17 +0200736 /* FIXME: we need to call the exit function of the specific node
737 * in question, not this generic one that doesn't know all nodes */
Harald Welte955049f2009-03-10 12:16:51 +0000738 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
739 vty_prompt(vty);
740 vty->cp = 0;
741}
742
743/* When '^Z' is received from vty, move down to the enable mode. */
744static void vty_end_config(struct vty *vty)
745{
746 vty_out(vty, "%s", VTY_NEWLINE);
747
Harald Welte62ab20c2010-05-14 18:59:17 +0200748 /* FIXME: we need to call the exit function of the specific node
749 * in question, not this generic one that doesn't know all nodes */
Harald Welte955049f2009-03-10 12:16:51 +0000750 switch (vty->node) {
751 case VIEW_NODE:
752 case ENABLE_NODE:
753 /* Nothing to do. */
754 break;
755 case CONFIG_NODE:
Harald Welte955049f2009-03-10 12:16:51 +0000756 case VTY_NODE:
757 vty_config_unlock(vty);
758 vty->node = ENABLE_NODE;
759 break;
760 default:
761 /* Unknown node, we have to ignore it. */
762 break;
763 }
764
765 vty_prompt(vty);
766 vty->cp = 0;
767}
768
769/* Delete a charcter at the current point. */
770static void vty_delete_char(struct vty *vty)
771{
772 int i;
773 int size;
774
775 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
776 return;
777
778 if (vty->length == 0) {
779 vty_down_level(vty);
780 return;
781 }
782
783 if (vty->cp == vty->length)
784 return; /* completion need here? */
785
786 size = vty->length - vty->cp;
787
788 vty->length--;
789 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
790 vty->buf[vty->length] = '\0';
791
792 vty_write(vty, &vty->buf[vty->cp], size - 1);
793 vty_write(vty, &telnet_space_char, 1);
794
795 for (i = 0; i < size; i++)
796 vty_write(vty, &telnet_backward_char, 1);
797}
798
799/* Delete a character before the point. */
800static void vty_delete_backward_char(struct vty *vty)
801{
802 if (vty->cp == 0)
803 return;
804
805 vty_backward_char(vty);
806 vty_delete_char(vty);
807}
808
809/* Kill rest of line from current point. */
810static void vty_kill_line(struct vty *vty)
811{
812 int i;
813 int size;
814
815 size = vty->length - vty->cp;
816
817 if (size == 0)
818 return;
819
820 for (i = 0; i < size; i++)
821 vty_write(vty, &telnet_space_char, 1);
822 for (i = 0; i < size; i++)
823 vty_write(vty, &telnet_backward_char, 1);
824
825 memset(&vty->buf[vty->cp], 0, size);
826 vty->length = vty->cp;
827}
828
829/* Kill line from the beginning. */
830static void vty_kill_line_from_beginning(struct vty *vty)
831{
832 vty_beginning_of_line(vty);
833 vty_kill_line(vty);
834}
835
836/* Delete a word before the point. */
837static void vty_forward_kill_word(struct vty *vty)
838{
839 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
840 vty_delete_char(vty);
841 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
842 vty_delete_char(vty);
843}
844
845/* Delete a word before the point. */
846static void vty_backward_kill_word(struct vty *vty)
847{
848 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
849 vty_delete_backward_char(vty);
850 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
851 vty_delete_backward_char(vty);
852}
853
854/* Transpose chars before or at the point. */
855static void vty_transpose_chars(struct vty *vty)
856{
857 char c1, c2;
858
859 /* If length is short or point is near by the beginning of line then
860 return. */
861 if (vty->length < 2 || vty->cp < 1)
862 return;
863
864 /* In case of point is located at the end of the line. */
865 if (vty->cp == vty->length) {
866 c1 = vty->buf[vty->cp - 1];
867 c2 = vty->buf[vty->cp - 2];
868
869 vty_backward_char(vty);
870 vty_backward_char(vty);
871 vty_self_insert_overwrite(vty, c1);
872 vty_self_insert_overwrite(vty, c2);
873 } else {
874 c1 = vty->buf[vty->cp];
875 c2 = vty->buf[vty->cp - 1];
876
877 vty_backward_char(vty);
878 vty_self_insert_overwrite(vty, c1);
879 vty_self_insert_overwrite(vty, c2);
880 }
881}
882
883/* Do completion at vty interface. */
884static void vty_complete_command(struct vty *vty)
885{
886 int i;
887 int ret;
888 char **matched = NULL;
889 vector vline;
890
891 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
892 return;
893
894 vline = cmd_make_strvec(vty->buf);
895 if (vline == NULL)
896 return;
897
898 /* In case of 'help \t'. */
899 if (isspace((int)vty->buf[vty->length - 1]))
900 vector_set(vline, '\0');
901
902 matched = cmd_complete_command(vline, vty, &ret);
903
904 cmd_free_strvec(vline);
905
906 vty_out(vty, "%s", VTY_NEWLINE);
907 switch (ret) {
908 case CMD_ERR_AMBIGUOUS:
909 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
910 vty_prompt(vty);
911 vty_redraw_line(vty);
912 break;
913 case CMD_ERR_NO_MATCH:
914 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
915 vty_prompt(vty);
916 vty_redraw_line(vty);
917 break;
918 case CMD_COMPLETE_FULL_MATCH:
919 vty_prompt(vty);
920 vty_redraw_line(vty);
921 vty_backward_pure_word(vty);
922 vty_insert_word_overwrite(vty, matched[0]);
923 vty_self_insert(vty, ' ');
Holger Hans Peter Freytherf1af3062010-04-30 13:18:05 +0800924 talloc_free(matched[0]);
Harald Welte955049f2009-03-10 12:16:51 +0000925 break;
926 case CMD_COMPLETE_MATCH:
927 vty_prompt(vty);
928 vty_redraw_line(vty);
929 vty_backward_pure_word(vty);
930 vty_insert_word_overwrite(vty, matched[0]);
Harald Welte2477d932009-08-07 00:32:41 +0200931 talloc_free(matched[0]);
Harald Welte955049f2009-03-10 12:16:51 +0000932 break;
933 case CMD_COMPLETE_LIST_MATCH:
934 for (i = 0; matched[i] != NULL; i++) {
935 if (i != 0 && ((i % 6) == 0))
936 vty_out(vty, "%s", VTY_NEWLINE);
937 vty_out(vty, "%-10s ", matched[i]);
Harald Welte2477d932009-08-07 00:32:41 +0200938 talloc_free(matched[i]);
Harald Welte955049f2009-03-10 12:16:51 +0000939 }
940 vty_out(vty, "%s", VTY_NEWLINE);
941
942 vty_prompt(vty);
943 vty_redraw_line(vty);
944 break;
945 case CMD_ERR_NOTHING_TODO:
946 vty_prompt(vty);
947 vty_redraw_line(vty);
948 break;
949 default:
950 break;
951 }
952 if (matched)
953 vector_only_index_free(matched);
954}
955
956static void
957vty_describe_fold(struct vty *vty, int cmd_width,
958 unsigned int desc_width, struct desc *desc)
959{
960 char *buf;
961 const char *cmd, *p;
962 int pos;
963
964 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
965
966 if (desc_width <= 0) {
967 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
968 VTY_NEWLINE);
969 return;
970 }
971
Holger Hans Peter Freyther9e282de2010-04-30 12:18:32 +0800972 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
Harald Welte955049f2009-03-10 12:16:51 +0000973 if (!buf)
974 return;
975
976 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
977 for (pos = desc_width; pos > 0; pos--)
978 if (*(p + pos) == ' ')
979 break;
980
981 if (pos == 0)
982 break;
983
984 strncpy(buf, p, pos);
985 buf[pos] = '\0';
986 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
987
988 cmd = "";
989 }
990
991 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
992
Harald Welte2477d932009-08-07 00:32:41 +0200993 talloc_free(buf);
Harald Welte955049f2009-03-10 12:16:51 +0000994}
995
996/* Describe matched command function. */
997static void vty_describe_command(struct vty *vty)
998{
999 int ret;
1000 vector vline;
1001 vector describe;
1002 unsigned int i, width, desc_width;
1003 struct desc *desc, *desc_cr = NULL;
1004
1005 vline = cmd_make_strvec(vty->buf);
1006
1007 /* In case of '> ?'. */
1008 if (vline == NULL) {
1009 vline = vector_init(1);
1010 vector_set(vline, '\0');
1011 } else if (isspace((int)vty->buf[vty->length - 1]))
1012 vector_set(vline, '\0');
1013
1014 describe = cmd_describe_command(vline, vty, &ret);
1015
1016 vty_out(vty, "%s", VTY_NEWLINE);
1017
1018 /* Ambiguous error. */
1019 switch (ret) {
1020 case CMD_ERR_AMBIGUOUS:
1021 cmd_free_strvec(vline);
1022 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1023 vty_prompt(vty);
1024 vty_redraw_line(vty);
1025 return;
1026 break;
1027 case CMD_ERR_NO_MATCH:
1028 cmd_free_strvec(vline);
1029 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1030 vty_prompt(vty);
1031 vty_redraw_line(vty);
1032 return;
1033 break;
1034 }
1035
1036 /* Get width of command string. */
1037 width = 0;
1038 for (i = 0; i < vector_active(describe); i++)
1039 if ((desc = vector_slot(describe, i)) != NULL) {
1040 unsigned int len;
1041
1042 if (desc->cmd[0] == '\0')
1043 continue;
1044
1045 len = strlen(desc->cmd);
1046 if (desc->cmd[0] == '.')
1047 len--;
1048
1049 if (width < len)
1050 width = len;
1051 }
1052
1053 /* Get width of description string. */
1054 desc_width = vty->width - (width + 6);
1055
1056 /* Print out description. */
1057 for (i = 0; i < vector_active(describe); i++)
1058 if ((desc = vector_slot(describe, i)) != NULL) {
1059 if (desc->cmd[0] == '\0')
1060 continue;
1061
1062 if (strcmp(desc->cmd, "<cr>") == 0) {
1063 desc_cr = desc;
1064 continue;
1065 }
1066
1067 if (!desc->str)
1068 vty_out(vty, " %-s%s",
1069 desc->cmd[0] ==
1070 '.' ? desc->cmd + 1 : desc->cmd,
1071 VTY_NEWLINE);
1072 else if (desc_width >= strlen(desc->str))
1073 vty_out(vty, " %-*s %s%s", width,
1074 desc->cmd[0] ==
1075 '.' ? desc->cmd + 1 : desc->cmd,
1076 desc->str, VTY_NEWLINE);
1077 else
1078 vty_describe_fold(vty, width, desc_width, desc);
1079
1080#if 0
1081 vty_out(vty, " %-*s %s%s", width
1082 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1083 desc->str ? desc->str : "", VTY_NEWLINE);
1084#endif /* 0 */
1085 }
1086
1087 if ((desc = desc_cr)) {
1088 if (!desc->str)
1089 vty_out(vty, " %-s%s",
1090 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1091 VTY_NEWLINE);
1092 else if (desc_width >= strlen(desc->str))
1093 vty_out(vty, " %-*s %s%s", width,
1094 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1095 desc->str, VTY_NEWLINE);
1096 else
1097 vty_describe_fold(vty, width, desc_width, desc);
1098 }
1099
1100 cmd_free_strvec(vline);
1101 vector_free(describe);
1102
1103 vty_prompt(vty);
1104 vty_redraw_line(vty);
1105}
1106
1107/* ^C stop current input and do not add command line to the history. */
1108static void vty_stop_input(struct vty *vty)
1109{
1110 vty->cp = vty->length = 0;
1111 vty_clear_buf(vty);
1112 vty_out(vty, "%s", VTY_NEWLINE);
1113
1114 switch (vty->node) {
1115 case VIEW_NODE:
1116 case ENABLE_NODE:
1117 /* Nothing to do. */
1118 break;
1119 case CONFIG_NODE:
Harald Welte955049f2009-03-10 12:16:51 +00001120 case VTY_NODE:
1121 vty_config_unlock(vty);
1122 vty->node = ENABLE_NODE;
1123 break;
1124 default:
1125 /* Unknown node, we have to ignore it. */
1126 break;
1127 }
1128 vty_prompt(vty);
1129
1130 /* Set history pointer to the latest one. */
1131 vty->hp = vty->hindex;
1132}
1133
1134#define CONTROL(X) ((X) - '@')
1135#define VTY_NORMAL 0
1136#define VTY_PRE_ESCAPE 1
1137#define VTY_ESCAPE 2
1138
1139/* Escape character command map. */
1140static void vty_escape_map(unsigned char c, struct vty *vty)
1141{
1142 switch (c) {
1143 case ('A'):
1144 vty_previous_line(vty);
1145 break;
1146 case ('B'):
1147 vty_next_line(vty);
1148 break;
1149 case ('C'):
1150 vty_forward_char(vty);
1151 break;
1152 case ('D'):
1153 vty_backward_char(vty);
1154 break;
1155 default:
1156 break;
1157 }
1158
1159 /* Go back to normal mode. */
1160 vty->escape = VTY_NORMAL;
1161}
1162
1163/* Quit print out to the buffer. */
1164static void vty_buffer_reset(struct vty *vty)
1165{
1166 buffer_reset(vty->obuf);
1167 vty_prompt(vty);
1168 vty_redraw_line(vty);
1169}
1170
1171/* Read data via vty socket. */
1172int vty_read(struct vty *vty)
1173{
1174 int i;
1175 int nbytes;
1176 unsigned char buf[VTY_READ_BUFSIZ];
1177
1178 int vty_sock = vty->fd;
1179
1180 /* Read raw data from socket */
1181 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1182 if (nbytes < 0) {
1183 if (ERRNO_IO_RETRY(errno)) {
1184 vty_event(VTY_READ, vty_sock, vty);
1185 return 0;
1186 }
1187 }
1188 buffer_reset(vty->obuf);
1189 vty->status = VTY_CLOSE;
1190 }
1191
1192 for (i = 0; i < nbytes; i++) {
1193 if (buf[i] == IAC) {
1194 if (!vty->iac) {
1195 vty->iac = 1;
1196 continue;
1197 } else {
1198 vty->iac = 0;
1199 }
1200 }
1201
1202 if (vty->iac_sb_in_progress && !vty->iac) {
1203 if (vty->sb_len < sizeof(vty->sb_buf))
1204 vty->sb_buf[vty->sb_len] = buf[i];
1205 vty->sb_len++;
1206 continue;
1207 }
1208
1209 if (vty->iac) {
1210 /* In case of telnet command */
1211 int ret = 0;
1212 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1213 vty->iac = 0;
1214 i += ret;
1215 continue;
1216 }
1217
1218 if (vty->status == VTY_MORE) {
1219 switch (buf[i]) {
1220 case CONTROL('C'):
1221 case 'q':
1222 case 'Q':
1223 vty_buffer_reset(vty);
1224 break;
1225#if 0 /* More line does not work for "show ip bgp". */
1226 case '\n':
1227 case '\r':
1228 vty->status = VTY_MORELINE;
1229 break;
1230#endif
1231 default:
1232 break;
1233 }
1234 continue;
1235 }
1236
1237 /* Escape character. */
1238 if (vty->escape == VTY_ESCAPE) {
1239 vty_escape_map(buf[i], vty);
1240 continue;
1241 }
1242
1243 /* Pre-escape status. */
1244 if (vty->escape == VTY_PRE_ESCAPE) {
1245 switch (buf[i]) {
1246 case '[':
1247 vty->escape = VTY_ESCAPE;
1248 break;
1249 case 'b':
1250 vty_backward_word(vty);
1251 vty->escape = VTY_NORMAL;
1252 break;
1253 case 'f':
1254 vty_forward_word(vty);
1255 vty->escape = VTY_NORMAL;
1256 break;
1257 case 'd':
1258 vty_forward_kill_word(vty);
1259 vty->escape = VTY_NORMAL;
1260 break;
1261 case CONTROL('H'):
1262 case 0x7f:
1263 vty_backward_kill_word(vty);
1264 vty->escape = VTY_NORMAL;
1265 break;
1266 default:
1267 vty->escape = VTY_NORMAL;
1268 break;
1269 }
1270 continue;
1271 }
1272
1273 switch (buf[i]) {
1274 case CONTROL('A'):
1275 vty_beginning_of_line(vty);
1276 break;
1277 case CONTROL('B'):
1278 vty_backward_char(vty);
1279 break;
1280 case CONTROL('C'):
1281 vty_stop_input(vty);
1282 break;
1283 case CONTROL('D'):
1284 vty_delete_char(vty);
1285 break;
1286 case CONTROL('E'):
1287 vty_end_of_line(vty);
1288 break;
1289 case CONTROL('F'):
1290 vty_forward_char(vty);
1291 break;
1292 case CONTROL('H'):
1293 case 0x7f:
1294 vty_delete_backward_char(vty);
1295 break;
1296 case CONTROL('K'):
1297 vty_kill_line(vty);
1298 break;
1299 case CONTROL('N'):
1300 vty_next_line(vty);
1301 break;
1302 case CONTROL('P'):
1303 vty_previous_line(vty);
1304 break;
1305 case CONTROL('T'):
1306 vty_transpose_chars(vty);
1307 break;
1308 case CONTROL('U'):
1309 vty_kill_line_from_beginning(vty);
1310 break;
1311 case CONTROL('W'):
1312 vty_backward_kill_word(vty);
1313 break;
1314 case CONTROL('Z'):
1315 vty_end_config(vty);
1316 break;
1317 case '\n':
1318 case '\r':
1319 vty_out(vty, "%s", VTY_NEWLINE);
1320 vty_execute(vty);
1321 break;
1322 case '\t':
1323 vty_complete_command(vty);
1324 break;
1325 case '?':
1326 if (vty->node == AUTH_NODE
1327 || vty->node == AUTH_ENABLE_NODE)
1328 vty_self_insert(vty, buf[i]);
1329 else
1330 vty_describe_command(vty);
1331 break;
1332 case '\033':
1333 if (i + 1 < nbytes && buf[i + 1] == '[') {
1334 vty->escape = VTY_ESCAPE;
1335 i++;
1336 } else
1337 vty->escape = VTY_PRE_ESCAPE;
1338 break;
1339 default:
1340 if (buf[i] > 31 && buf[i] < 127)
1341 vty_self_insert(vty, buf[i]);
1342 break;
1343 }
1344 }
1345
1346 /* Check status. */
1347 if (vty->status == VTY_CLOSE)
1348 vty_close(vty);
1349 else {
1350 vty_event(VTY_WRITE, vty_sock, vty);
1351 vty_event(VTY_READ, vty_sock, vty);
1352 }
1353 return 0;
1354}
1355
Harald Weltec7c19822009-08-07 13:28:08 +02001356/* Read up configuration file */
1357static int
1358vty_read_file(FILE *confp)
1359{
1360 int ret;
1361 struct vty *vty;
1362
1363 vty = vty_new();
1364 vty->fd = 0;
1365 vty->type = VTY_FILE;
1366 vty->node = CONFIG_NODE;
1367
1368 ret = config_from_file(vty, confp);
1369
1370 if (ret != CMD_SUCCESS) {
1371 switch (ret) {
1372 case CMD_ERR_AMBIGUOUS:
1373 fprintf(stderr, "Ambiguous command.\n");
1374 break;
1375 case CMD_ERR_NO_MATCH:
Harald Welte42581822009-08-08 16:12:58 +02001376 fprintf(stderr, "There is no such command.\n");
Harald Weltec7c19822009-08-07 13:28:08 +02001377 break;
1378 }
1379 fprintf(stderr, "Error occurred during reading below "
1380 "line:\n%s\n", vty->buf);
1381 vty_close(vty);
1382 return -EINVAL;
1383 }
1384
1385 vty_close(vty);
1386 return 0;
1387}
1388
Harald Welte955049f2009-03-10 12:16:51 +00001389/* Create new vty structure. */
1390struct vty *
Harald Weltec63e51d2009-03-10 19:46:16 +00001391vty_create (int vty_sock, void *priv)
Harald Welte955049f2009-03-10 12:16:51 +00001392{
1393 struct vty *vty;
1394
1395 struct termios t;
1396
1397 tcgetattr(vty_sock, &t);
1398 cfmakeraw(&t);
1399 tcsetattr(vty_sock, TCSANOW, &t);
1400
1401 /* Allocate new vty structure and set up default values. */
1402 vty = vty_new ();
1403 vty->fd = vty_sock;
Harald Weltec63e51d2009-03-10 19:46:16 +00001404 vty->priv = priv;
Harald Welte955049f2009-03-10 12:16:51 +00001405 vty->type = VTY_TERM;
1406 if (no_password_check)
1407 {
1408 if (host.advanced)
1409 vty->node = ENABLE_NODE;
1410 else
1411 vty->node = VIEW_NODE;
1412 }
1413 else
1414 vty->node = AUTH_NODE;
1415 vty->fail = 0;
1416 vty->cp = 0;
1417 vty_clear_buf (vty);
1418 vty->length = 0;
1419 memset (vty->hist, 0, sizeof (vty->hist));
1420 vty->hp = 0;
1421 vty->hindex = 0;
1422 vector_set_index (vtyvec, vty_sock, vty);
1423 vty->status = VTY_NORMAL;
1424 if (host.lines >= 0)
1425 vty->lines = host.lines;
1426 else
1427 vty->lines = -1;
1428
1429 if (! no_password_check)
1430 {
1431 /* Vty is not available if password isn't set. */
1432 if (host.password == NULL && host.password_encrypt == NULL)
1433 {
1434 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1435 vty->status = VTY_CLOSE;
1436 vty_close (vty);
1437 return NULL;
1438 }
1439 }
1440
1441 /* Say hello to the world. */
1442 vty_hello (vty);
1443 if (! no_password_check)
1444 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1445
1446 /* Setting up terminal. */
1447 vty_will_echo (vty);
1448 vty_will_suppress_go_ahead (vty);
1449
1450 vty_dont_linemode (vty);
1451 vty_do_window_size (vty);
1452 /* vty_dont_lflow_ahead (vty); */
1453
1454 vty_prompt (vty);
1455
1456 /* Add read/write thread. */
1457 vty_event (VTY_WRITE, vty_sock, vty);
1458 vty_event (VTY_READ, vty_sock, vty);
1459
1460 return vty;
1461}
1462
1463DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1464{
1465 unsigned int i;
1466 struct vty *v;
1467
1468 for (i = 0; i < vector_active(vtyvec); i++)
1469 if ((v = vector_slot(vtyvec, i)) != NULL)
1470 vty_out(vty, "%svty[%d] %s",
1471 v->config ? "*" : " ", i, VTY_NEWLINE);
1472 return CMD_SUCCESS;
1473}
1474
1475/* Move to vty configuration mode. */
1476DEFUN(line_vty,
1477 line_vty_cmd,
1478 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1479{
1480 vty->node = VTY_NODE;
1481 return CMD_SUCCESS;
1482}
1483
1484/* vty login. */
1485DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1486{
1487 no_password_check = 0;
1488 return CMD_SUCCESS;
1489}
1490
1491DEFUN(no_vty_login,
1492 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1493{
1494 no_password_check = 1;
1495 return CMD_SUCCESS;
1496}
1497
1498DEFUN(service_advanced_vty,
1499 service_advanced_vty_cmd,
1500 "service advanced-vty",
1501 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1502{
1503 host.advanced = 1;
1504 return CMD_SUCCESS;
1505}
1506
1507DEFUN(no_service_advanced_vty,
1508 no_service_advanced_vty_cmd,
1509 "no service advanced-vty",
1510 NO_STR
1511 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1512{
1513 host.advanced = 0;
1514 return CMD_SUCCESS;
1515}
1516
1517DEFUN(terminal_monitor,
1518 terminal_monitor_cmd,
1519 "terminal monitor",
1520 "Set terminal line parameters\n"
1521 "Copy debug output to the current terminal line\n")
1522{
1523 vty->monitor = 1;
1524 return CMD_SUCCESS;
1525}
1526
1527DEFUN(terminal_no_monitor,
1528 terminal_no_monitor_cmd,
1529 "terminal no monitor",
1530 "Set terminal line parameters\n"
1531 NO_STR "Copy debug output to the current terminal line\n")
1532{
1533 vty->monitor = 0;
1534 return CMD_SUCCESS;
1535}
1536
1537DEFUN(show_history,
1538 show_history_cmd,
1539 "show history", SHOW_STR "Display the session command history\n")
1540{
1541 int index;
1542
1543 for (index = vty->hindex + 1; index != vty->hindex;) {
1544 if (index == VTY_MAXHIST) {
1545 index = 0;
1546 continue;
1547 }
1548
1549 if (vty->hist[index] != NULL)
1550 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1551
1552 index++;
1553 }
1554
1555 return CMD_SUCCESS;
1556}
1557
1558/* Display current configuration. */
1559static int vty_config_write(struct vty *vty)
1560{
1561 vty_out(vty, "line vty%s", VTY_NEWLINE);
1562
1563 /* login */
1564 if (no_password_check)
1565 vty_out(vty, " no login%s", VTY_NEWLINE);
1566
1567 vty_out(vty, "!%s", VTY_NEWLINE);
1568
1569 return CMD_SUCCESS;
1570}
1571
1572struct cmd_node vty_node = {
1573 VTY_NODE,
1574 "%s(config-line)# ",
1575 1,
1576};
1577
1578/* Reset all VTY status. */
1579void vty_reset()
1580{
1581 unsigned int i;
1582 struct vty *vty;
1583 struct thread *vty_serv_thread;
1584
1585 for (i = 0; i < vector_active(vtyvec); i++)
1586 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1587 buffer_reset(vty->obuf);
1588 vty->status = VTY_CLOSE;
1589 vty_close(vty);
1590 }
1591
1592 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1593 if ((vty_serv_thread =
1594 vector_slot(Vvty_serv_thread, i)) != NULL) {
1595 //thread_cancel (vty_serv_thread);
1596 vector_slot(Vvty_serv_thread, i) = NULL;
1597 close(i);
1598 }
1599}
1600
1601static void vty_save_cwd(void)
1602{
1603 char cwd[MAXPATHLEN];
Holger Hans Peter Freyther6d30a352009-08-10 08:10:26 +02001604 char *c ;
Harald Welte955049f2009-03-10 12:16:51 +00001605
1606 c = getcwd(cwd, MAXPATHLEN);
1607
1608 if (!c) {
Holger Hans Peter Freyther6d30a352009-08-10 08:10:26 +02001609 if (chdir(SYSCONFDIR) != 0)
1610 perror("chdir failed");
1611 if (getcwd(cwd, MAXPATHLEN) == NULL)
1612 perror("getcwd failed");
Harald Welte955049f2009-03-10 12:16:51 +00001613 }
1614
Harald Welte2477d932009-08-07 00:32:41 +02001615 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
Harald Welte955049f2009-03-10 12:16:51 +00001616 strcpy(vty_cwd, cwd);
1617}
1618
1619char *vty_get_cwd()
1620{
1621 return vty_cwd;
1622}
1623
1624int vty_shell_serv(struct vty *vty)
1625{
1626 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1627}
1628
1629void vty_init_vtysh()
1630{
1631 vtyvec = vector_init(VECTOR_MIN_SIZE);
1632}
1633
Harald Welte0224e4d2009-08-07 13:25:41 +02001634extern void *tall_bsc_ctx;
Harald Welte955049f2009-03-10 12:16:51 +00001635/* Install vty's own commands like `who' command. */
Harald Welte1353f962010-05-16 19:20:24 +02001636void vty_init(const char *name, const char *version, const char *copyright)
Harald Welte955049f2009-03-10 12:16:51 +00001637{
Harald Welte1353f962010-05-16 19:20:24 +02001638 host.prog_name = name;
1639 host.prog_version = version;
1640 host.prog_copyright = copyright;
1641
Harald Welte (local)d19e58b2009-08-15 02:30:58 +02001642 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3cefa9a2009-12-24 10:51:56 +01001643 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1644 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
Harald Welte0224e4d2009-08-07 13:25:41 +02001645
Harald Welte955049f2009-03-10 12:16:51 +00001646 /* For further configuration read, preserve current directory. */
1647 vty_save_cwd();
1648
1649 vtyvec = vector_init(VECTOR_MIN_SIZE);
1650
1651 /* Install bgp top node. */
1652 install_node(&vty_node, vty_config_write);
1653
Harald Welte00fd8b02010-05-16 17:08:27 +02001654 install_element_ve(&config_who_cmd);
1655 install_element_ve(&show_history_cmd);
Harald Welte955049f2009-03-10 12:16:51 +00001656 install_element(CONFIG_NODE, &line_vty_cmd);
1657 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1658 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1659 install_element(CONFIG_NODE, &show_history_cmd);
1660 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1661 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
Harald Welte955049f2009-03-10 12:16:51 +00001662
1663 install_default(VTY_NODE);
Harald Welte955049f2009-03-10 12:16:51 +00001664 install_element(VTY_NODE, &vty_login_cmd);
1665 install_element(VTY_NODE, &no_vty_login_cmd);
Harald Welte955049f2009-03-10 12:16:51 +00001666}
Harald Welte2477d932009-08-07 00:32:41 +02001667
Harald Weltec7c19822009-08-07 13:28:08 +02001668int vty_read_config_file(const char *file_name)
Harald Welte2477d932009-08-07 00:32:41 +02001669{
Harald Weltec7c19822009-08-07 13:28:08 +02001670 FILE *cfile;
1671 int rc;
1672
1673 cfile = fopen(file_name, "r");
1674 if (!cfile)
1675 return -ENOENT;
1676
1677 rc = vty_read_file(cfile);
1678 fclose(cfile);
1679
1680 host_config_set(file_name);
1681
1682 return rc;
Harald Welte2477d932009-08-07 00:32:41 +02001683}