]> git.proxmox.com Git - mirror_frr.git/blame - lib/vty.c
watchquagga: add "write integrated"
[mirror_frr.git] / lib / vty.c
CommitLineData
718e3744 1/*
2 * Virtual terminal [aka TeletYpe] interface routine.
3 * Copyright (C) 1997, 98 Kunihiro Ishiguro
4 *
5 * This file is part of GNU Zebra.
6 *
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Zebra; see the file COPYING. If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23#include <zebra.h>
24
25#include "linklist.h"
b21b19c5 26#include "thread.h"
718e3744 27#include "buffer.h"
5e4fa164 28#include <lib/version.h>
718e3744 29#include "command.h"
30#include "sockunion.h"
718e3744 31#include "memory.h"
32#include "str.h"
33#include "log.h"
34#include "prefix.h"
35#include "filter.h"
b21b19c5 36#include "vty.h"
edd7c245 37#include "privs.h"
9fc7ebf1 38#include "network.h"
39
40#include <arpa/telnet.h>
b510a06e 41#include <termios.h>
718e3744 42
4a1ab8e4
DL
43DEFINE_MTYPE_STATIC(LIB, VTY, "VTY")
44DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer")
45DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history")
46
718e3744 47/* Vty events */
48enum event
49{
50 VTY_SERV,
51 VTY_READ,
52 VTY_WRITE,
53 VTY_TIMEOUT_RESET,
54#ifdef VTYSH
55 VTYSH_SERV,
49ff6d9d 56 VTYSH_READ,
57 VTYSH_WRITE
718e3744 58#endif /* VTYSH */
59};
60
61static void vty_event (enum event, int, struct vty *);
62
63/* Extern host structure from command.c */
64extern struct host host;
6b0655a2 65
718e3744 66/* Vector which store each vty structure. */
67static vector vtyvec;
68
69/* Vty timeout value. */
70static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
71
72/* Vty access-class command */
73static char *vty_accesslist_name = NULL;
74
75/* Vty access-calss for IPv6. */
76static char *vty_ipv6_accesslist_name = NULL;
77
78/* VTY server thread. */
677bcbbf 79static vector Vvty_serv_thread;
718e3744 80
81/* Current directory. */
82char *vty_cwd = NULL;
83
84/* Configure lock. */
85static int vty_config;
cc933ef9 86static int vty_config_is_lockless = 0;
718e3744 87
88/* Login password check. */
89static int no_password_check = 0;
90
91/* Integrated configuration file path */
92char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
93
da688ecd 94static int do_log_commands = 0;
6b0655a2 95
718e3744 96/* VTY standard output function. */
97int
98vty_out (struct vty *vty, const char *format, ...)
99{
100 va_list args;
101 int len = 0;
102 int size = 1024;
103 char buf[1024];
104 char *p = NULL;
718e3744 105
106 if (vty_shell (vty))
d246bd96 107 {
108 va_start (args, format);
109 vprintf (format, args);
110 va_end (args);
111 }
718e3744 112 else
113 {
114 /* Try to write to initial buffer. */
d246bd96 115 va_start (args, format);
1035065f 116 len = vsnprintf (buf, sizeof(buf), format, args);
d246bd96 117 va_end (args);
718e3744 118
119 /* Initial buffer is not enough. */
120 if (len < 0 || len >= size)
121 {
122 while (1)
123 {
124 if (len > -1)
125 size = len + 1;
126 else
127 size = size * 2;
128
129 p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
130 if (! p)
131 return -1;
132
d246bd96 133 va_start (args, format);
718e3744 134 len = vsnprintf (p, size, format, args);
d246bd96 135 va_end (args);
718e3744 136
137 if (len > -1 && len < size)
138 break;
139 }
140 }
141
142 /* When initial buffer is enough to store all output. */
143 if (! p)
144 p = buf;
145
146 /* Pointer p must point out buffer. */
9fc7ebf1 147 buffer_put (vty->obuf, (u_char *) p, len);
718e3744 148
149 /* If p is not different with buf, it is allocated buffer. */
150 if (p != buf)
151 XFREE (MTYPE_VTY_OUT_BUF, p);
152 }
153
718e3744 154 return len;
155}
156
d246bd96 157static int
274a4a44 158vty_log_out (struct vty *vty, const char *level, const char *proto_str,
1ed72e0b 159 const char *format, struct timestamp_control *ctl, va_list va)
718e3744 160{
9fc7ebf1 161 int ret;
718e3744 162 int len;
163 char buf[1024];
08942da5 164
1ed72e0b
AS
165 if (!ctl->already_rendered)
166 {
167 ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
168 ctl->already_rendered = 1;
169 }
170 if (ctl->len+1 >= sizeof(buf))
171 return -1;
172 memcpy(buf, ctl->buf, len = ctl->len);
173 buf[len++] = ' ';
174 buf[len] = '\0';
718e3744 175
274a4a44 176 if (level)
08942da5 177 ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
274a4a44 178 else
08942da5
AS
179 ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
180 if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
9fc7ebf1 181 return -1;
718e3744 182
9fc7ebf1 183 if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
184 ((size_t)((len += ret)+2) > sizeof(buf)))
718e3744 185 return -1;
718e3744 186
9fc7ebf1 187 buf[len++] = '\r';
188 buf[len++] = '\n';
718e3744 189
c5e69a02 190 if (write(vty->wfd, buf, len) < 0)
9fc7ebf1 191 {
192 if (ERRNO_IO_RETRY(errno))
193 /* Kernel buffer is full, probably too much debugging output, so just
194 drop the data and ignore. */
195 return -1;
196 /* Fatal I/O error. */
74542d73 197 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
9fc7ebf1 198 zlog_warn("%s: write failed to vty client fd %d, closing: %s",
199 __func__, vty->fd, safe_strerror(errno));
200 buffer_reset(vty->obuf);
9d0a3260
AS
201 /* cannot call vty_close, because a parent routine may still try
202 to access the vty struct */
203 vty->status = VTY_CLOSE;
204 shutdown(vty->fd, SHUT_RDWR);
9fc7ebf1 205 return -1;
206 }
207 return 0;
718e3744 208}
209
210/* Output current time to the vty. */
211void
212vty_time_print (struct vty *vty, int cr)
213{
ae616d60 214 char buf[QUAGGA_TIMESTAMP_LEN];
718e3744 215
1ed72e0b 216 if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
718e3744 217 {
1ed72e0b 218 zlog (NULL, LOG_INFO, "quagga_timestamp error");
718e3744 219 return;
220 }
221 if (cr)
222 vty_out (vty, "%s\n", buf);
223 else
224 vty_out (vty, "%s ", buf);
225
226 return;
227}
228
229/* Say hello to vty interface. */
230void
231vty_hello (struct vty *vty)
232{
3b0c5d9a 233 if (host.motdfile)
234 {
235 FILE *f;
236 char buf[4096];
22085181 237
3b0c5d9a 238 f = fopen (host.motdfile, "r");
239 if (f)
240 {
b45da6f0 241 while (fgets (buf, sizeof (buf), f))
3b0c5d9a 242 {
b45da6f0 243 char *s;
22085181 244 /* work backwards to ignore trailling isspace() */
f80a016f 245 for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
22085181 246 s--);
247 *s = '\0';
248 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
249 }
3b0c5d9a 250 fclose (f);
251 }
252 else
b45da6f0 253 vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
3b0c5d9a 254 }
255 else if (host.motd)
b830c89a 256 vty_out (vty, "%s", host.motd);
718e3744 257}
258
259/* Put out prompt and wait input from user. */
260static void
261vty_prompt (struct vty *vty)
262{
263 struct utsname names;
264 const char*hostname;
265
266 if (vty->type == VTY_TERM)
267 {
268 hostname = host.name;
269 if (!hostname)
270 {
271 uname (&names);
272 hostname = names.nodename;
273 }
274 vty_out (vty, cmd_prompt (vty->node), hostname);
275 }
276}
277
278/* Send WILL TELOPT_ECHO to remote server. */
9fc7ebf1 279static void
718e3744 280vty_will_echo (struct vty *vty)
281{
02ff83c5 282 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
718e3744 283 vty_out (vty, "%s", cmd);
284}
285
286/* Make suppress Go-Ahead telnet option. */
287static void
288vty_will_suppress_go_ahead (struct vty *vty)
289{
02ff83c5 290 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
718e3744 291 vty_out (vty, "%s", cmd);
292}
293
294/* Make don't use linemode over telnet. */
295static void
296vty_dont_linemode (struct vty *vty)
297{
02ff83c5 298 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
718e3744 299 vty_out (vty, "%s", cmd);
300}
301
302/* Use window size. */
303static void
304vty_do_window_size (struct vty *vty)
305{
02ff83c5 306 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
718e3744 307 vty_out (vty, "%s", cmd);
308}
309
310#if 0 /* Currently not used. */
311/* Make don't use lflow vty interface. */
312static void
313vty_dont_lflow_ahead (struct vty *vty)
314{
02ff83c5 315 unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
718e3744 316 vty_out (vty, "%s", cmd);
317}
318#endif /* 0 */
319
320/* Allocate new vty struct. */
321struct vty *
322vty_new ()
323{
324 struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
325
9fc7ebf1 326 new->obuf = buffer_new(0); /* Use default buffer size. */
718e3744 327 new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
5689fe5f 328 new->error_buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
718e3744 329 new->max = VTY_BUFSIZ;
718e3744 330
331 return new;
332}
333
334/* Authentication of vty */
335static void
336vty_auth (struct vty *vty, char *buf)
337{
338 char *passwd = NULL;
339 enum node_type next_node = 0;
340 int fail;
341 char *crypt (const char *, const char *);
342
343 switch (vty->node)
344 {
345 case AUTH_NODE:
346 if (host.encrypt)
347 passwd = host.password_encrypt;
348 else
349 passwd = host.password;
350 if (host.advanced)
351 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
352 else
353 next_node = VIEW_NODE;
354 break;
355 case AUTH_ENABLE_NODE:
356 if (host.encrypt)
357 passwd = host.enable_encrypt;
358 else
359 passwd = host.enable;
360 next_node = ENABLE_NODE;
361 break;
362 }
363
364 if (passwd)
365 {
366 if (host.encrypt)
367 fail = strcmp (crypt(buf, passwd), passwd);
368 else
369 fail = strcmp (buf, passwd);
370 }
371 else
372 fail = 1;
373
374 if (! fail)
375 {
376 vty->fail = 0;
377 vty->node = next_node; /* Success ! */
378 }
379 else
380 {
381 vty->fail++;
382 if (vty->fail >= 3)
383 {
384 if (vty->node == AUTH_NODE)
385 {
386 vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
387 vty->status = VTY_CLOSE;
388 }
389 else
390 {
391 /* AUTH_ENABLE_NODE */
392 vty->fail = 0;
393 vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
3c8ab49f 394 vty->status = VTY_CLOSE;
718e3744 395 }
396 }
397 }
398}
399
400/* Command execution over the vty interface. */
9fc7ebf1 401static int
718e3744 402vty_command (struct vty *vty, char *buf)
403{
404 int ret;
405 vector vline;
fbf5d033 406 const char *protocolname;
da688ecd 407 char *cp = NULL;
718e3744 408
1035065f
LB
409 /*
410 * Log non empty command lines
411 */
da688ecd
LB
412 if (do_log_commands)
413 cp = buf;
1035065f
LB
414 if (cp != NULL)
415 {
416 /* Skip white spaces. */
417 while (isspace ((int) *cp) && *cp != '\0')
418 cp++;
419 }
420 if (cp != NULL && *cp != '\0')
421 {
422 unsigned i;
423 char vty_str[VTY_BUFSIZ];
424 char prompt_str[VTY_BUFSIZ];
425
426 /* format the base vty info */
427 snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
428 if (vty)
429 for (i = 0; i < vector_active (vtyvec); i++)
430 if (vty == vector_slot (vtyvec, i))
431 {
432 snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s",
433 i, vty->address);
434 break;
435 }
436
437 /* format the prompt */
438 snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str);
439
440 /* now log the command */
da688ecd 441 zlog(NULL, LOG_ERR, "%s%s", prompt_str, buf);
1035065f 442 }
718e3744 443 /* Split readline string up into the vector */
444 vline = cmd_make_strvec (buf);
445
446 if (vline == NULL)
447 return CMD_SUCCESS;
448
924b9229 449#ifdef CONSUMED_TIME_CHECK
450 {
451 RUSAGE_T before;
452 RUSAGE_T after;
8b70d0b0 453 unsigned long realtime, cputime;
924b9229 454
455 GETRUSAGE(&before);
456#endif /* CONSUMED_TIME_CHECK */
457
87d683b0 458 ret = cmd_execute_command (vline, vty, NULL, 0);
718e3744 459
fbf5d033 460 /* Get the name of the protocol if any */
461 if (zlog_default)
462 protocolname = zlog_proto_names[zlog_default->protocol];
463 else
464 protocolname = zlog_proto_names[ZLOG_NONE];
465
924b9229 466#ifdef CONSUMED_TIME_CHECK
467 GETRUSAGE(&after);
8b70d0b0 468 if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
469 CONSUMED_TIME_CHECK)
924b9229 470 /* Warn about CPU hog that must be fixed. */
8b70d0b0 471 zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
472 realtime/1000, cputime/1000, buf);
924b9229 473 }
474#endif /* CONSUMED_TIME_CHECK */
475
718e3744 476 if (ret != CMD_SUCCESS)
477 switch (ret)
478 {
479 case CMD_WARNING:
480 if (vty->type == VTY_FILE)
481 vty_out (vty, "Warning...%s", VTY_NEWLINE);
482 break;
483 case CMD_ERR_AMBIGUOUS:
484 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
485 break;
486 case CMD_ERR_NO_MATCH:
fbf5d033 487 vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
718e3744 488 break;
489 case CMD_ERR_INCOMPLETE:
490 vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
491 break;
492 }
493 cmd_free_strvec (vline);
494
495 return ret;
496}
6b0655a2 497
9fc7ebf1 498static const char telnet_backward_char = 0x08;
499static const char telnet_space_char = ' ';
718e3744 500
501/* Basic function to write buffer to vty. */
502static void
9fc7ebf1 503vty_write (struct vty *vty, const char *buf, size_t nbytes)
718e3744 504{
505 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
506 return;
507
508 /* Should we do buffering here ? And make vty_flush (vty) ? */
9fc7ebf1 509 buffer_put (vty->obuf, buf, nbytes);
718e3744 510}
511
512/* Ensure length of input buffer. Is buffer is short, double it. */
513static void
514vty_ensure (struct vty *vty, int length)
515{
516 if (vty->max <= length)
517 {
518 vty->max *= 2;
519 vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max);
5689fe5f 520 vty->error_buf = XREALLOC (MTYPE_VTY, vty->error_buf, vty->max);
718e3744 521 }
522}
523
524/* Basic function to insert character into vty. */
525static void
526vty_self_insert (struct vty *vty, char c)
527{
528 int i;
529 int length;
530
531 vty_ensure (vty, vty->length + 1);
532 length = vty->length - vty->cp;
533 memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
534 vty->buf[vty->cp] = c;
535
536 vty_write (vty, &vty->buf[vty->cp], length + 1);
537 for (i = 0; i < length; i++)
538 vty_write (vty, &telnet_backward_char, 1);
539
540 vty->cp++;
541 vty->length++;
542}
543
544/* Self insert character 'c' in overwrite mode. */
545static void
546vty_self_insert_overwrite (struct vty *vty, char c)
547{
548 vty_ensure (vty, vty->length + 1);
549 vty->buf[vty->cp++] = c;
550
551 if (vty->cp > vty->length)
552 vty->length++;
553
554 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
555 return;
556
557 vty_write (vty, &c, 1);
558}
559
560/* Insert a word into vty interface with overwrite mode. */
561static void
562vty_insert_word_overwrite (struct vty *vty, char *str)
563{
564 int len = strlen (str);
565 vty_write (vty, str, len);
566 strcpy (&vty->buf[vty->cp], str);
567 vty->cp += len;
568 vty->length = vty->cp;
569}
570
571/* Forward character. */
572static void
573vty_forward_char (struct vty *vty)
574{
575 if (vty->cp < vty->length)
576 {
577 vty_write (vty, &vty->buf[vty->cp], 1);
578 vty->cp++;
579 }
580}
581
582/* Backward character. */
583static void
584vty_backward_char (struct vty *vty)
585{
586 if (vty->cp > 0)
587 {
588 vty->cp--;
589 vty_write (vty, &telnet_backward_char, 1);
590 }
591}
592
593/* Move to the beginning of the line. */
594static void
595vty_beginning_of_line (struct vty *vty)
596{
597 while (vty->cp)
598 vty_backward_char (vty);
599}
600
601/* Move to the end of the line. */
602static void
603vty_end_of_line (struct vty *vty)
604{
605 while (vty->cp < vty->length)
606 vty_forward_char (vty);
607}
608
609static void vty_kill_line_from_beginning (struct vty *);
610static void vty_redraw_line (struct vty *);
611
612/* Print command line history. This function is called from
613 vty_next_line and vty_previous_line. */
614static void
615vty_history_print (struct vty *vty)
616{
617 int length;
618
619 vty_kill_line_from_beginning (vty);
620
621 /* Get previous line from history buffer */
622 length = strlen (vty->hist[vty->hp]);
623 memcpy (vty->buf, vty->hist[vty->hp], length);
624 vty->cp = vty->length = length;
625
626 /* Redraw current line */
627 vty_redraw_line (vty);
628}
629
630/* Show next command line history. */
9fc7ebf1 631static void
718e3744 632vty_next_line (struct vty *vty)
633{
634 int try_index;
635
636 if (vty->hp == vty->hindex)
637 return;
638
639 /* Try is there history exist or not. */
640 try_index = vty->hp;
641 if (try_index == (VTY_MAXHIST - 1))
642 try_index = 0;
643 else
644 try_index++;
645
646 /* If there is not history return. */
647 if (vty->hist[try_index] == NULL)
648 return;
649 else
650 vty->hp = try_index;
651
652 vty_history_print (vty);
653}
654
655/* Show previous command line history. */
9fc7ebf1 656static void
718e3744 657vty_previous_line (struct vty *vty)
658{
659 int try_index;
660
661 try_index = vty->hp;
662 if (try_index == 0)
663 try_index = VTY_MAXHIST - 1;
664 else
665 try_index--;
666
667 if (vty->hist[try_index] == NULL)
668 return;
669 else
670 vty->hp = try_index;
671
672 vty_history_print (vty);
673}
674
675/* This function redraw all of the command line character. */
676static void
677vty_redraw_line (struct vty *vty)
678{
679 vty_write (vty, vty->buf, vty->length);
680 vty->cp = vty->length;
681}
682
683/* Forward word. */
684static void
685vty_forward_word (struct vty *vty)
686{
687 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
688 vty_forward_char (vty);
689
690 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
691 vty_forward_char (vty);
692}
693
694/* Backward word without skipping training space. */
695static void
696vty_backward_pure_word (struct vty *vty)
697{
698 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
699 vty_backward_char (vty);
700}
701
702/* Backward word. */
703static void
704vty_backward_word (struct vty *vty)
705{
706 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
707 vty_backward_char (vty);
708
709 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
710 vty_backward_char (vty);
711}
712
713/* When '^D' is typed at the beginning of the line we move to the down
714 level. */
715static void
716vty_down_level (struct vty *vty)
717{
718 vty_out (vty, "%s", VTY_NEWLINE);
274a4a44 719 (*config_exit_cmd.func)(NULL, vty, 0, NULL);
718e3744 720 vty_prompt (vty);
721 vty->cp = 0;
722}
723
724/* When '^Z' is received from vty, move down to the enable mode. */
9fc7ebf1 725static void
718e3744 726vty_end_config (struct vty *vty)
727{
728 vty_out (vty, "%s", VTY_NEWLINE);
729
730 switch (vty->node)
731 {
732 case VIEW_NODE:
733 case ENABLE_NODE:
734 /* Nothing to do. */
735 break;
736 case CONFIG_NODE:
737 case INTERFACE_NODE:
738 case ZEBRA_NODE:
739 case RIP_NODE:
740 case RIPNG_NODE:
741 case BGP_NODE:
742 case BGP_VPNV4_NODE:
8ecd3266 743 case BGP_VPNV6_NODE:
8b1fb8be
LB
744 case BGP_ENCAP_NODE:
745 case BGP_ENCAPV6_NODE:
65efcfce
LB
746 case BGP_VNC_DEFAULTS_NODE:
747 case BGP_VNC_NVE_GROUP_NODE:
748 case BGP_VNC_L2_GROUP_NODE:
718e3744 749 case BGP_IPV4_NODE:
750 case BGP_IPV4M_NODE:
751 case BGP_IPV6_NODE:
1e836590 752 case BGP_IPV6M_NODE:
718e3744 753 case RMAP_NODE:
754 case OSPF_NODE:
755 case OSPF6_NODE:
eac6e3f0
RW
756 case LDP_NODE:
757 case LDP_IPV4_NODE:
758 case LDP_IPV6_NODE:
759 case LDP_IPV4_IFACE_NODE:
760 case LDP_IPV6_IFACE_NODE:
761 case LDP_L2VPN_NODE:
762 case LDP_PSEUDOWIRE_NODE:
9e867fe6 763 case ISIS_NODE:
718e3744 764 case KEYCHAIN_NODE:
765 case KEYCHAIN_KEY_NODE:
766 case MASC_NODE:
12e41d03 767 case PIM_NODE:
718e3744 768 case VTY_NODE:
769 vty_config_unlock (vty);
770 vty->node = ENABLE_NODE;
771 break;
772 default:
773 /* Unknown node, we have to ignore it. */
774 break;
775 }
776
777 vty_prompt (vty);
778 vty->cp = 0;
779}
780
781/* Delete a charcter at the current point. */
782static void
783vty_delete_char (struct vty *vty)
784{
785 int i;
786 int size;
787
718e3744 788 if (vty->length == 0)
789 {
790 vty_down_level (vty);
791 return;
792 }
793
794 if (vty->cp == vty->length)
795 return; /* completion need here? */
796
797 size = vty->length - vty->cp;
798
799 vty->length--;
800 memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
801 vty->buf[vty->length] = '\0';
7f794f2b
R
802
803 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
804 return;
718e3744 805
806 vty_write (vty, &vty->buf[vty->cp], size - 1);
807 vty_write (vty, &telnet_space_char, 1);
808
809 for (i = 0; i < size; i++)
810 vty_write (vty, &telnet_backward_char, 1);
811}
812
813/* Delete a character before the point. */
814static void
815vty_delete_backward_char (struct vty *vty)
816{
817 if (vty->cp == 0)
818 return;
819
820 vty_backward_char (vty);
821 vty_delete_char (vty);
822}
823
824/* Kill rest of line from current point. */
825static void
826vty_kill_line (struct vty *vty)
827{
828 int i;
829 int size;
830
831 size = vty->length - vty->cp;
832
833 if (size == 0)
834 return;
835
836 for (i = 0; i < size; i++)
837 vty_write (vty, &telnet_space_char, 1);
838 for (i = 0; i < size; i++)
839 vty_write (vty, &telnet_backward_char, 1);
840
841 memset (&vty->buf[vty->cp], 0, size);
842 vty->length = vty->cp;
843}
844
845/* Kill line from the beginning. */
846static void
847vty_kill_line_from_beginning (struct vty *vty)
848{
849 vty_beginning_of_line (vty);
850 vty_kill_line (vty);
851}
852
853/* Delete a word before the point. */
854static void
855vty_forward_kill_word (struct vty *vty)
856{
857 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
858 vty_delete_char (vty);
859 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
860 vty_delete_char (vty);
861}
862
863/* Delete a word before the point. */
864static void
865vty_backward_kill_word (struct vty *vty)
866{
867 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
868 vty_delete_backward_char (vty);
869 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
870 vty_delete_backward_char (vty);
871}
872
873/* Transpose chars before or at the point. */
874static void
875vty_transpose_chars (struct vty *vty)
876{
877 char c1, c2;
878
879 /* If length is short or point is near by the beginning of line then
880 return. */
881 if (vty->length < 2 || vty->cp < 1)
882 return;
883
884 /* In case of point is located at the end of the line. */
885 if (vty->cp == vty->length)
886 {
887 c1 = vty->buf[vty->cp - 1];
888 c2 = vty->buf[vty->cp - 2];
889
890 vty_backward_char (vty);
891 vty_backward_char (vty);
892 vty_self_insert_overwrite (vty, c1);
893 vty_self_insert_overwrite (vty, c2);
894 }
895 else
896 {
897 c1 = vty->buf[vty->cp];
898 c2 = vty->buf[vty->cp - 1];
899
900 vty_backward_char (vty);
901 vty_self_insert_overwrite (vty, c1);
902 vty_self_insert_overwrite (vty, c2);
903 }
904}
905
906/* Do completion at vty interface. */
907static void
908vty_complete_command (struct vty *vty)
909{
910 int i;
911 int ret;
912 char **matched = NULL;
913 vector vline;
914
915 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
916 return;
917
918 vline = cmd_make_strvec (vty->buf);
919 if (vline == NULL)
920 return;
921
922 /* In case of 'help \t'. */
923 if (isspace ((int) vty->buf[vty->length - 1]))
7bf5d992 924 vector_set (vline, NULL);
718e3744 925
cde9f101 926 matched = cmd_complete_command_lib (vline, vty, &ret, 1);
718e3744 927
928 cmd_free_strvec (vline);
929
930 vty_out (vty, "%s", VTY_NEWLINE);
931 switch (ret)
932 {
933 case CMD_ERR_AMBIGUOUS:
934 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
935 vty_prompt (vty);
936 vty_redraw_line (vty);
937 break;
938 case CMD_ERR_NO_MATCH:
939 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
940 vty_prompt (vty);
941 vty_redraw_line (vty);
942 break;
943 case CMD_COMPLETE_FULL_MATCH:
944 vty_prompt (vty);
945 vty_redraw_line (vty);
946 vty_backward_pure_word (vty);
947 vty_insert_word_overwrite (vty, matched[0]);
948 vty_self_insert (vty, ' ');
949 XFREE (MTYPE_TMP, matched[0]);
950 break;
951 case CMD_COMPLETE_MATCH:
952 vty_prompt (vty);
953 vty_redraw_line (vty);
954 vty_backward_pure_word (vty);
955 vty_insert_word_overwrite (vty, matched[0]);
956 XFREE (MTYPE_TMP, matched[0]);
957 vector_only_index_free (matched);
958 return;
959 break;
960 case CMD_COMPLETE_LIST_MATCH:
961 for (i = 0; matched[i] != NULL; i++)
962 {
963 if (i != 0 && ((i % 6) == 0))
964 vty_out (vty, "%s", VTY_NEWLINE);
965 vty_out (vty, "%-10s ", matched[i]);
966 XFREE (MTYPE_TMP, matched[i]);
967 }
968 vty_out (vty, "%s", VTY_NEWLINE);
969
970 vty_prompt (vty);
971 vty_redraw_line (vty);
972 break;
973 case CMD_ERR_NOTHING_TODO:
974 vty_prompt (vty);
975 vty_redraw_line (vty);
976 break;
977 default:
978 break;
979 }
980 if (matched)
981 vector_only_index_free (matched);
982}
983
9fc7ebf1 984static void
718e3744 985vty_describe_fold (struct vty *vty, int cmd_width,
cd40b329 986 unsigned int desc_width, struct cmd_token *token)
718e3744 987{
8c328f11 988 char *buf;
989 const char *cmd, *p;
718e3744 990 int pos;
991
cd40b329 992 cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
718e3744 993
994 if (desc_width <= 0)
995 {
cd40b329 996 vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
718e3744 997 return;
998 }
999
cd40b329 1000 buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
718e3744 1001
cd40b329 1002 for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
718e3744 1003 {
1004 for (pos = desc_width; pos > 0; pos--)
1005 if (*(p + pos) == ' ')
1006 break;
1007
1008 if (pos == 0)
1009 break;
1010
1011 strncpy (buf, p, pos);
1012 buf[pos] = '\0';
1013 vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1014
1015 cmd = "";
1016 }
1017
1018 vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1019
1020 XFREE (MTYPE_TMP, buf);
1021}
1022
1023/* Describe matched command function. */
1024static void
1025vty_describe_command (struct vty *vty)
1026{
1027 int ret;
1028 vector vline;
1029 vector describe;
8c328f11 1030 unsigned int i, width, desc_width;
cd40b329 1031 struct cmd_token *token, *token_cr = NULL;
718e3744 1032
1033 vline = cmd_make_strvec (vty->buf);
1034
1035 /* In case of '> ?'. */
1036 if (vline == NULL)
1037 {
1038 vline = vector_init (1);
7bf5d992 1039 vector_set (vline, NULL);
718e3744 1040 }
1041 else
1042 if (isspace ((int) vty->buf[vty->length - 1]))
7bf5d992 1043 vector_set (vline, NULL);
718e3744 1044
1045 describe = cmd_describe_command (vline, vty, &ret);
1046
1047 vty_out (vty, "%s", VTY_NEWLINE);
1048
1049 /* Ambiguous error. */
1050 switch (ret)
1051 {
1052 case CMD_ERR_AMBIGUOUS:
718e3744 1053 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
2fe8aba3 1054 goto out;
718e3744 1055 break;
1056 case CMD_ERR_NO_MATCH:
718e3744 1057 vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
2fe8aba3 1058 goto out;
718e3744 1059 break;
1060 }
1061
1062 /* Get width of command string. */
1063 width = 0;
55468c86 1064 for (i = 0; i < vector_active (describe); i++)
cd40b329 1065 if ((token = vector_slot (describe, i)) != NULL)
718e3744 1066 {
8c328f11 1067 unsigned int len;
718e3744 1068
cd40b329 1069 if (token->cmd[0] == '\0')
718e3744 1070 continue;
1071
cd40b329
CF
1072 len = strlen (token->cmd);
1073 if (token->cmd[0] == '.')
718e3744 1074 len--;
1075
1076 if (width < len)
1077 width = len;
1078 }
1079
1080 /* Get width of description string. */
1081 desc_width = vty->width - (width + 6);
1082
1083 /* Print out description. */
55468c86 1084 for (i = 0; i < vector_active (describe); i++)
cd40b329 1085 if ((token = vector_slot (describe, i)) != NULL)
718e3744 1086 {
cd40b329 1087 if (token->cmd[0] == '\0')
718e3744 1088 continue;
1089
cd40b329 1090 if (strcmp (token->cmd, command_cr) == 0)
718e3744 1091 {
cd40b329 1092 token_cr = token;
718e3744 1093 continue;
1094 }
1095
cd40b329 1096 if (!token->desc)
718e3744 1097 vty_out (vty, " %-s%s",
cd40b329 1098 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
718e3744 1099 VTY_NEWLINE);
cd40b329 1100 else if (desc_width >= strlen (token->desc))
718e3744 1101 vty_out (vty, " %-*s %s%s", width,
cd40b329
CF
1102 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1103 token->desc, VTY_NEWLINE);
718e3744 1104 else
cd40b329 1105 vty_describe_fold (vty, width, desc_width, token);
718e3744 1106
1107#if 0
1108 vty_out (vty, " %-*s %s%s", width
1109 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1110 desc->str ? desc->str : "", VTY_NEWLINE);
1111#endif /* 0 */
1112 }
1113
cd40b329 1114 if ((token = token_cr))
718e3744 1115 {
cd40b329 1116 if (!token->desc)
718e3744 1117 vty_out (vty, " %-s%s",
cd40b329 1118 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
718e3744 1119 VTY_NEWLINE);
cd40b329 1120 else if (desc_width >= strlen (token->desc))
718e3744 1121 vty_out (vty, " %-*s %s%s", width,
cd40b329
CF
1122 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1123 token->desc, VTY_NEWLINE);
718e3744 1124 else
cd40b329 1125 vty_describe_fold (vty, width, desc_width, token);
718e3744 1126 }
1127
2fe8aba3 1128out:
718e3744 1129 cmd_free_strvec (vline);
d16e0433
PJ
1130 if (describe)
1131 vector_free (describe);
718e3744 1132
1133 vty_prompt (vty);
1134 vty_redraw_line (vty);
1135}
1136
9fc7ebf1 1137static void
718e3744 1138vty_clear_buf (struct vty *vty)
1139{
1140 memset (vty->buf, 0, vty->max);
1141}
1142
1143/* ^C stop current input and do not add command line to the history. */
1144static void
1145vty_stop_input (struct vty *vty)
1146{
1147 vty->cp = vty->length = 0;
1148 vty_clear_buf (vty);
1149 vty_out (vty, "%s", VTY_NEWLINE);
1150
1151 switch (vty->node)
1152 {
1153 case VIEW_NODE:
1154 case ENABLE_NODE:
1155 /* Nothing to do. */
1156 break;
1157 case CONFIG_NODE:
1158 case INTERFACE_NODE:
1159 case ZEBRA_NODE:
1160 case RIP_NODE:
1161 case RIPNG_NODE:
1162 case BGP_NODE:
1163 case RMAP_NODE:
1164 case OSPF_NODE:
1165 case OSPF6_NODE:
eac6e3f0
RW
1166 case LDP_NODE:
1167 case LDP_IPV4_NODE:
1168 case LDP_IPV6_NODE:
1169 case LDP_IPV4_IFACE_NODE:
1170 case LDP_IPV6_IFACE_NODE:
1171 case LDP_L2VPN_NODE:
1172 case LDP_PSEUDOWIRE_NODE:
9e867fe6 1173 case ISIS_NODE:
718e3744 1174 case KEYCHAIN_NODE:
1175 case KEYCHAIN_KEY_NODE:
1176 case MASC_NODE:
12e41d03 1177 case PIM_NODE:
718e3744 1178 case VTY_NODE:
1179 vty_config_unlock (vty);
1180 vty->node = ENABLE_NODE;
1181 break;
1182 default:
1183 /* Unknown node, we have to ignore it. */
1184 break;
1185 }
1186 vty_prompt (vty);
1187
1188 /* Set history pointer to the latest one. */
1189 vty->hp = vty->hindex;
1190}
1191
1192/* Add current command line to the history buffer. */
1193static void
1194vty_hist_add (struct vty *vty)
1195{
1196 int index;
1197
1198 if (vty->length == 0)
1199 return;
1200
1201 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1202
1203 /* Ignore the same string as previous one. */
1204 if (vty->hist[index])
1205 if (strcmp (vty->buf, vty->hist[index]) == 0)
1206 {
1207 vty->hp = vty->hindex;
1208 return;
1209 }
1210
1211 /* Insert history entry. */
1212 if (vty->hist[vty->hindex])
1213 XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1214 vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
1215
1216 /* History index rotation. */
1217 vty->hindex++;
1218 if (vty->hindex == VTY_MAXHIST)
1219 vty->hindex = 0;
1220
1221 vty->hp = vty->hindex;
1222}
1223
1224/* #define TELNET_OPTION_DEBUG */
1225
1226/* Get telnet window size. */
1227static int
1228vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
1229{
1230#ifdef TELNET_OPTION_DEBUG
1231 int i;
1232
1233 for (i = 0; i < nbytes; i++)
1234 {
1235 switch (buf[i])
1236 {
1237 case IAC:
1238 vty_out (vty, "IAC ");
1239 break;
1240 case WILL:
1241 vty_out (vty, "WILL ");
1242 break;
1243 case WONT:
1244 vty_out (vty, "WONT ");
1245 break;
1246 case DO:
1247 vty_out (vty, "DO ");
1248 break;
1249 case DONT:
1250 vty_out (vty, "DONT ");
1251 break;
1252 case SB:
1253 vty_out (vty, "SB ");
1254 break;
1255 case SE:
1256 vty_out (vty, "SE ");
1257 break;
1258 case TELOPT_ECHO:
1259 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
1260 break;
1261 case TELOPT_SGA:
1262 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
1263 break;
1264 case TELOPT_NAWS:
1265 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
1266 break;
1267 default:
1268 vty_out (vty, "%x ", buf[i]);
1269 break;
1270 }
1271 }
1272 vty_out (vty, "%s", VTY_NEWLINE);
1273
1274#endif /* TELNET_OPTION_DEBUG */
1275
1276 switch (buf[0])
1277 {
1278 case SB:
9fc7ebf1 1279 vty->sb_len = 0;
718e3744 1280 vty->iac_sb_in_progress = 1;
1281 return 0;
1282 break;
1283 case SE:
1284 {
5b8c1b0d 1285 if (!vty->iac_sb_in_progress)
718e3744 1286 return 0;
1287
9fc7ebf1 1288 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
718e3744 1289 {
1290 vty->iac_sb_in_progress = 0;
1291 return 0;
1292 }
9fc7ebf1 1293 switch (vty->sb_buf[0])
718e3744 1294 {
1295 case TELOPT_NAWS:
9fc7ebf1 1296 if (vty->sb_len != TELNET_NAWS_SB_LEN)
1297 zlog_warn("RFC 1073 violation detected: telnet NAWS option "
1298 "should send %d characters, but we received %lu",
1299 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
1300 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1301 zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
1302 "too small to handle the telnet NAWS option",
1303 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
1304 else
1305 {
1306 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
1307 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
1308#ifdef TELNET_OPTION_DEBUG
1309 vty_out(vty, "TELNET NAWS window size negotiation completed: "
1310 "width %d, height %d%s",
1311 vty->width, vty->height, VTY_NEWLINE);
1312#endif
1313 }
718e3744 1314 break;
1315 }
1316 vty->iac_sb_in_progress = 0;
1317 return 0;
1318 break;
1319 }
1320 default:
1321 break;
1322 }
1323 return 1;
1324}
1325
1326/* Execute current command line. */
1327static int
1328vty_execute (struct vty *vty)
1329{
1330 int ret;
1331
1332 ret = CMD_SUCCESS;
1333
1334 switch (vty->node)
1335 {
1336 case AUTH_NODE:
1337 case AUTH_ENABLE_NODE:
1338 vty_auth (vty, vty->buf);
1339 break;
1340 default:
1341 ret = vty_command (vty, vty->buf);
1342 if (vty->type == VTY_TERM)
1343 vty_hist_add (vty);
1344 break;
1345 }
1346
1347 /* Clear command line buffer. */
1348 vty->cp = vty->length = 0;
1349 vty_clear_buf (vty);
1350
5a646650 1351 if (vty->status != VTY_CLOSE )
718e3744 1352 vty_prompt (vty);
1353
1354 return ret;
1355}
1356
1357#define CONTROL(X) ((X) - '@')
1358#define VTY_NORMAL 0
1359#define VTY_PRE_ESCAPE 1
1360#define VTY_ESCAPE 2
1361
1362/* Escape character command map. */
1363static void
1364vty_escape_map (unsigned char c, struct vty *vty)
1365{
1366 switch (c)
1367 {
1368 case ('A'):
1369 vty_previous_line (vty);
1370 break;
1371 case ('B'):
1372 vty_next_line (vty);
1373 break;
1374 case ('C'):
1375 vty_forward_char (vty);
1376 break;
1377 case ('D'):
1378 vty_backward_char (vty);
1379 break;
1380 default:
1381 break;
1382 }
1383
1384 /* Go back to normal mode. */
1385 vty->escape = VTY_NORMAL;
1386}
1387
1388/* Quit print out to the buffer. */
1389static void
1390vty_buffer_reset (struct vty *vty)
1391{
1392 buffer_reset (vty->obuf);
1393 vty_prompt (vty);
1394 vty_redraw_line (vty);
1395}
1396
1397/* Read data via vty socket. */
1398static int
1399vty_read (struct thread *thread)
1400{
1401 int i;
718e3744 1402 int nbytes;
1403 unsigned char buf[VTY_READ_BUFSIZ];
1404
1405 int vty_sock = THREAD_FD (thread);
1406 struct vty *vty = THREAD_ARG (thread);
1407 vty->t_read = NULL;
1408
1409 /* Read raw data from socket */
9fc7ebf1 1410 if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
1411 {
1412 if (nbytes < 0)
1413 {
1414 if (ERRNO_IO_RETRY(errno))
1415 {
1416 vty_event (VTY_READ, vty_sock, vty);
1417 return 0;
1418 }
74542d73 1419 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
9fc7ebf1 1420 zlog_warn("%s: read error on vty client fd %d, closing: %s",
1421 __func__, vty->fd, safe_strerror(errno));
009a4a07 1422 buffer_reset(vty->obuf);
9fc7ebf1 1423 }
9fc7ebf1 1424 vty->status = VTY_CLOSE;
1425 }
718e3744 1426
1427 for (i = 0; i < nbytes; i++)
1428 {
1429 if (buf[i] == IAC)
1430 {
1431 if (!vty->iac)
1432 {
1433 vty->iac = 1;
1434 continue;
1435 }
1436 else
1437 {
1438 vty->iac = 0;
1439 }
1440 }
1441
1442 if (vty->iac_sb_in_progress && !vty->iac)
1443 {
9fc7ebf1 1444 if (vty->sb_len < sizeof(vty->sb_buf))
1445 vty->sb_buf[vty->sb_len] = buf[i];
1446 vty->sb_len++;
718e3744 1447 continue;
1448 }
1449
1450 if (vty->iac)
1451 {
1452 /* In case of telnet command */
5b8c1b0d 1453 int ret = 0;
e937253b 1454 ret = vty_telnet_option (vty, buf + i, nbytes - i);
718e3744 1455 vty->iac = 0;
1456 i += ret;
1457 continue;
1458 }
5b8c1b0d 1459
718e3744 1460
1461 if (vty->status == VTY_MORE)
1462 {
1463 switch (buf[i])
1464 {
1465 case CONTROL('C'):
1466 case 'q':
1467 case 'Q':
718e3744 1468 vty_buffer_reset (vty);
1469 break;
1470#if 0 /* More line does not work for "show ip bgp". */
1471 case '\n':
1472 case '\r':
1473 vty->status = VTY_MORELINE;
1474 break;
1475#endif
1476 default:
718e3744 1477 break;
1478 }
1479 continue;
1480 }
1481
1482 /* Escape character. */
1483 if (vty->escape == VTY_ESCAPE)
1484 {
1485 vty_escape_map (buf[i], vty);
1486 continue;
1487 }
1488
1489 /* Pre-escape status. */
1490 if (vty->escape == VTY_PRE_ESCAPE)
1491 {
1492 switch (buf[i])
1493 {
1494 case '[':
1495 vty->escape = VTY_ESCAPE;
1496 break;
1497 case 'b':
1498 vty_backward_word (vty);
1499 vty->escape = VTY_NORMAL;
1500 break;
1501 case 'f':
1502 vty_forward_word (vty);
1503 vty->escape = VTY_NORMAL;
1504 break;
1505 case 'd':
1506 vty_forward_kill_word (vty);
1507 vty->escape = VTY_NORMAL;
1508 break;
1509 case CONTROL('H'):
1510 case 0x7f:
1511 vty_backward_kill_word (vty);
1512 vty->escape = VTY_NORMAL;
1513 break;
1514 default:
1515 vty->escape = VTY_NORMAL;
1516 break;
1517 }
1518 continue;
1519 }
1520
1521 switch (buf[i])
1522 {
1523 case CONTROL('A'):
1524 vty_beginning_of_line (vty);
1525 break;
1526 case CONTROL('B'):
1527 vty_backward_char (vty);
1528 break;
1529 case CONTROL('C'):
1530 vty_stop_input (vty);
1531 break;
1532 case CONTROL('D'):
1533 vty_delete_char (vty);
1534 break;
1535 case CONTROL('E'):
1536 vty_end_of_line (vty);
1537 break;
1538 case CONTROL('F'):
1539 vty_forward_char (vty);
1540 break;
1541 case CONTROL('H'):
1542 case 0x7f:
1543 vty_delete_backward_char (vty);
1544 break;
1545 case CONTROL('K'):
1546 vty_kill_line (vty);
1547 break;
1548 case CONTROL('N'):
1549 vty_next_line (vty);
1550 break;
1551 case CONTROL('P'):
1552 vty_previous_line (vty);
1553 break;
1554 case CONTROL('T'):
1555 vty_transpose_chars (vty);
1556 break;
1557 case CONTROL('U'):
1558 vty_kill_line_from_beginning (vty);
1559 break;
1560 case CONTROL('W'):
1561 vty_backward_kill_word (vty);
1562 break;
1563 case CONTROL('Z'):
1564 vty_end_config (vty);
1565 break;
1566 case '\n':
1567 case '\r':
1568 vty_out (vty, "%s", VTY_NEWLINE);
1569 vty_execute (vty);
1570 break;
1571 case '\t':
1572 vty_complete_command (vty);
1573 break;
1574 case '?':
1575 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1576 vty_self_insert (vty, buf[i]);
1577 else
1578 vty_describe_command (vty);
1579 break;
1580 case '\033':
1581 if (i + 1 < nbytes && buf[i + 1] == '[')
1582 {
1583 vty->escape = VTY_ESCAPE;
1584 i++;
1585 }
1586 else
1587 vty->escape = VTY_PRE_ESCAPE;
1588 break;
1589 default:
1590 if (buf[i] > 31 && buf[i] < 127)
1591 vty_self_insert (vty, buf[i]);
1592 break;
1593 }
1594 }
1595
1596 /* Check status. */
1597 if (vty->status == VTY_CLOSE)
1598 vty_close (vty);
1599 else
1600 {
c5e69a02 1601 vty_event (VTY_WRITE, vty->wfd, vty);
718e3744 1602 vty_event (VTY_READ, vty_sock, vty);
1603 }
1604 return 0;
1605}
1606
1607/* Flush buffer to the vty. */
1608static int
1609vty_flush (struct thread *thread)
1610{
1611 int erase;
9fc7ebf1 1612 buffer_status_t flushrc;
718e3744 1613 int vty_sock = THREAD_FD (thread);
1614 struct vty *vty = THREAD_ARG (thread);
9fc7ebf1 1615
718e3744 1616 vty->t_write = NULL;
1617
1618 /* Tempolary disable read thread. */
9fc7ebf1 1619 if ((vty->lines == 0) && vty->t_read)
1620 {
1621 thread_cancel (vty->t_read);
1622 vty->t_read = NULL;
1623 }
718e3744 1624
1625 /* Function execution continue. */
9fc7ebf1 1626 erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
1627
1628 /* N.B. if width is 0, that means we don't know the window size. */
1035065f 1629 if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
c5e69a02 1630 flushrc = buffer_flush_available(vty->obuf, vty_sock);
9fc7ebf1 1631 else if (vty->status == VTY_MORELINE)
c5e69a02 1632 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
9fc7ebf1 1633 1, erase, 0);
1634 else
c5e69a02 1635 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
9fc7ebf1 1636 vty->lines >= 0 ? vty->lines :
1637 vty->height,
1638 erase, 0);
1639 switch (flushrc)
1640 {
1641 case BUFFER_ERROR:
74542d73 1642 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
9fc7ebf1 1643 zlog_warn("buffer_flush failed on vty client fd %d, closing",
1644 vty->fd);
1645 buffer_reset(vty->obuf);
1646 vty_close(vty);
1647 return 0;
1648 case BUFFER_EMPTY:
1649 if (vty->status == VTY_CLOSE)
1650 vty_close (vty);
718e3744 1651 else
1652 {
9fc7ebf1 1653 vty->status = VTY_NORMAL;
718e3744 1654 if (vty->lines == 0)
9fc7ebf1 1655 vty_event (VTY_READ, vty_sock, vty);
718e3744 1656 }
9fc7ebf1 1657 break;
1658 case BUFFER_PENDING:
1659 /* There is more data waiting to be written. */
1660 vty->status = VTY_MORE;
1661 if (vty->lines == 0)
1662 vty_event (VTY_WRITE, vty_sock, vty);
1663 break;
1664 }
718e3744 1665
1666 return 0;
1667}
1668
b7642925
DL
1669/* allocate and initialise vty */
1670static struct vty *
1671vty_new_init (int vty_sock)
1672{
1673 struct vty *vty;
1674
1675 vty = vty_new ();
1676 vty->fd = vty_sock;
1677 vty->wfd = vty_sock;
1678 vty->type = VTY_TERM;
1679 vty->node = AUTH_NODE;
1680 vty->fail = 0;
1681 vty->cp = 0;
1682 vty_clear_buf (vty);
1683 vty->length = 0;
1684 memset (vty->hist, 0, sizeof (vty->hist));
1685 vty->hp = 0;
1686 vty->hindex = 0;
1687 vector_set_index (vtyvec, vty_sock, vty);
1688 vty->status = VTY_NORMAL;
1689 vty->lines = -1;
1690 vty->iac = 0;
1691 vty->iac_sb_in_progress = 0;
1692 vty->sb_len = 0;
1693
1694 return vty;
1695}
1696
718e3744 1697/* Create new vty structure. */
9fc7ebf1 1698static struct vty *
718e3744 1699vty_create (int vty_sock, union sockunion *su)
1700{
d227617a 1701 char buf[SU_ADDRSTRLEN];
718e3744 1702 struct vty *vty;
1703
d227617a
JBD
1704 sockunion2str(su, buf, SU_ADDRSTRLEN);
1705
718e3744 1706 /* Allocate new vty structure and set up default values. */
b7642925
DL
1707 vty = vty_new_init (vty_sock);
1708
1709 /* configurable parameters not part of basic init */
1710 vty->v_timeout = vty_timeout_val;
d227617a 1711 strcpy (vty->address, buf);
718e3744 1712 if (no_password_check)
1713 {
3c8ab49f 1714 if (host.advanced)
718e3744 1715 vty->node = ENABLE_NODE;
1716 else
1717 vty->node = VIEW_NODE;
1718 }
718e3744 1719 if (host.lines >= 0)
1720 vty->lines = host.lines;
718e3744 1721
1722 if (! no_password_check)
1723 {
1724 /* Vty is not available if password isn't set. */
1725 if (host.password == NULL && host.password_encrypt == NULL)
1726 {
1727 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1728 vty->status = VTY_CLOSE;
1729 vty_close (vty);
1730 return NULL;
1731 }
1732 }
1733
1734 /* Say hello to the world. */
1735 vty_hello (vty);
1736 if (! no_password_check)
1737 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1738
1739 /* Setting up terminal. */
1740 vty_will_echo (vty);
1741 vty_will_suppress_go_ahead (vty);
1742
1743 vty_dont_linemode (vty);
1744 vty_do_window_size (vty);
1745 /* vty_dont_lflow_ahead (vty); */
1746
1747 vty_prompt (vty);
1748
1749 /* Add read/write thread. */
1750 vty_event (VTY_WRITE, vty_sock, vty);
1751 vty_event (VTY_READ, vty_sock, vty);
1752
1753 return vty;
1754}
1755
b7642925 1756/* create vty for stdio */
b510a06e
DL
1757static struct termios stdio_orig_termios;
1758static struct vty *stdio_vty = NULL;
dbf78092 1759static void (*stdio_vty_atclose)(void);
b510a06e
DL
1760
1761static void
1762vty_stdio_reset (void)
1763{
1764 if (stdio_vty)
1765 {
1766 tcsetattr (0, TCSANOW, &stdio_orig_termios);
1767 stdio_vty = NULL;
dbf78092
DL
1768
1769 if (stdio_vty_atclose)
1770 stdio_vty_atclose ();
1771 stdio_vty_atclose = NULL;
b510a06e
DL
1772 }
1773}
1774
b7642925 1775struct vty *
dbf78092 1776vty_stdio (void (*atclose)())
b7642925
DL
1777{
1778 struct vty *vty;
b510a06e 1779 struct termios termios;
b7642925 1780
b510a06e
DL
1781 /* refuse creating two vtys on stdio */
1782 if (stdio_vty)
1783 return NULL;
1784
1785 vty = stdio_vty = vty_new_init (0);
dbf78092 1786 stdio_vty_atclose = atclose;
b7642925
DL
1787 vty->wfd = 1;
1788
1789 /* always have stdio vty in a known _unchangeable_ state, don't want config
1790 * to have any effect here to make sure scripting this works as intended */
1791 vty->node = ENABLE_NODE;
1792 vty->v_timeout = 0;
1793 strcpy (vty->address, "console");
1794
b510a06e
DL
1795 if (!tcgetattr (0, &stdio_orig_termios))
1796 {
1797 termios = stdio_orig_termios;
1798 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
1799 | INLCR | IGNCR | ICRNL | IXON);
1800 termios.c_oflag &= ~OPOST;
1801 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
1802 termios.c_cflag &= ~(CSIZE | PARENB);
1803 termios.c_cflag |= CS8;
1804 tcsetattr (0, TCSANOW, &termios);
1805 }
1806
b7642925
DL
1807 vty_prompt (vty);
1808
1809 /* Add read/write thread. */
1810 vty_event (VTY_WRITE, 1, vty);
1811 vty_event (VTY_READ, 0, vty);
1812
1813 return vty;
1814}
1815
718e3744 1816/* Accept connection from the network. */
1817static int
1818vty_accept (struct thread *thread)
1819{
1820 int vty_sock;
718e3744 1821 union sockunion su;
1822 int ret;
1823 unsigned int on;
1824 int accept_sock;
40ee54a7 1825 struct prefix p;
718e3744 1826 struct access_list *acl = NULL;
d227617a 1827 char buf[SU_ADDRSTRLEN];
718e3744 1828
1829 accept_sock = THREAD_FD (thread);
1830
1831 /* We continue hearing vty socket. */
1832 vty_event (VTY_SERV, accept_sock, NULL);
1833
1834 memset (&su, 0, sizeof (union sockunion));
1835
1836 /* We can handle IPv4 or IPv6 socket. */
1837 vty_sock = sockunion_accept (accept_sock, &su);
1838 if (vty_sock < 0)
1839 {
6099b3b5 1840 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
718e3744 1841 return -1;
1842 }
9fc7ebf1 1843 set_nonblocking(vty_sock);
2da59394 1844 set_cloexec(vty_sock);
718e3744 1845
40ee54a7 1846 sockunion2hostprefix (&su, &p);
718e3744 1847
1848 /* VTY's accesslist apply. */
40ee54a7 1849 if (p.family == AF_INET && vty_accesslist_name)
718e3744 1850 {
1851 if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
40ee54a7 1852 (access_list_apply (acl, &p) == FILTER_DENY))
718e3744 1853 {
718e3744 1854 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
d227617a 1855 sockunion2str (&su, buf, SU_ADDRSTRLEN));
718e3744 1856 close (vty_sock);
1857
1858 /* continue accepting connections */
1859 vty_event (VTY_SERV, accept_sock, NULL);
1860
718e3744 1861 return 0;
1862 }
1863 }
1864
1865#ifdef HAVE_IPV6
1866 /* VTY's ipv6 accesslist apply. */
40ee54a7 1867 if (p.family == AF_INET6 && vty_ipv6_accesslist_name)
718e3744 1868 {
1869 if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
40ee54a7 1870 (access_list_apply (acl, &p) == FILTER_DENY))
718e3744 1871 {
718e3744 1872 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
d227617a 1873 sockunion2str (&su, buf, SU_ADDRSTRLEN));
718e3744 1874 close (vty_sock);
1875
1876 /* continue accepting connections */
1877 vty_event (VTY_SERV, accept_sock, NULL);
1878
718e3744 1879 return 0;
1880 }
1881 }
1882#endif /* HAVE_IPV6 */
1883
718e3744 1884 on = 1;
1885 ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
1886 (char *) &on, sizeof (on));
1887 if (ret < 0)
1888 zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
6099b3b5 1889 safe_strerror (errno));
718e3744 1890
78e6cd98 1891 zlog (NULL, LOG_INFO, "Vty connection from %s",
d227617a 1892 sockunion2str (&su, buf, SU_ADDRSTRLEN));
78e6cd98 1893
9206f9ec 1894 vty_create (vty_sock, &su);
718e3744 1895
1896 return 0;
1897}
1898
1cbb5dfc 1899#ifdef HAVE_IPV6
9fc7ebf1 1900static void
718e3744 1901vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
1902{
1903 int ret;
1904 struct addrinfo req;
1905 struct addrinfo *ainfo;
1906 struct addrinfo *ainfo_save;
1907 int sock;
1908 char port_str[BUFSIZ];
1909
1910 memset (&req, 0, sizeof (struct addrinfo));
1911 req.ai_flags = AI_PASSIVE;
1912 req.ai_family = AF_UNSPEC;
1913 req.ai_socktype = SOCK_STREAM;
1914 sprintf (port_str, "%d", port);
1915 port_str[sizeof (port_str) - 1] = '\0';
1916
1917 ret = getaddrinfo (hostname, port_str, &req, &ainfo);
1918
1919 if (ret != 0)
1920 {
1921 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
1922 exit (1);
1923 }
1924
1925 ainfo_save = ainfo;
1926
1927 do
1928 {
1929 if (ainfo->ai_family != AF_INET
1930#ifdef HAVE_IPV6
1931 && ainfo->ai_family != AF_INET6
1932#endif /* HAVE_IPV6 */
1933 )
1934 continue;
1935
1936 sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
1937 if (sock < 0)
1938 continue;
1939
ca051269 1940 sockopt_v6only (ainfo->ai_family, sock);
718e3744 1941 sockopt_reuseaddr (sock);
1942 sockopt_reuseport (sock);
2da59394 1943 set_cloexec (sock);
718e3744 1944
1945 ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
1946 if (ret < 0)
1947 {
1948 close (sock); /* Avoid sd leak. */
1949 continue;
1950 }
1951
1952 ret = listen (sock, 3);
1953 if (ret < 0)
1954 {
1955 close (sock); /* Avoid sd leak. */
1956 continue;
1957 }
1958
1959 vty_event (VTY_SERV, sock, NULL);
1960 }
1961 while ((ainfo = ainfo->ai_next) != NULL);
1962
1963 freeaddrinfo (ainfo_save);
1964}
1cbb5dfc 1965#else /* HAVE_IPV6 */
718e3744 1966
1967/* Make vty server socket. */
9fc7ebf1 1968static void
29db05b4 1969vty_serv_sock_family (const char* addr, unsigned short port, int family)
718e3744 1970{
1971 int ret;
1972 union sockunion su;
1973 int accept_sock;
29db05b4 1974 void* naddr=NULL;
718e3744 1975
1976 memset (&su, 0, sizeof (union sockunion));
1977 su.sa.sa_family = family;
29db05b4 1978 if(addr)
1979 switch(family)
1980 {
1981 case AF_INET:
1982 naddr=&su.sin.sin_addr;
a11e012e 1983 break;
29db05b4 1984#ifdef HAVE_IPV6
1985 case AF_INET6:
1986 naddr=&su.sin6.sin6_addr;
a11e012e 1987 break;
29db05b4 1988#endif
1989 }
1990
1991 if(naddr)
1992 switch(inet_pton(family,addr,naddr))
1993 {
1994 case -1:
1995 zlog_err("bad address %s",addr);
1996 naddr=NULL;
1997 break;
1998 case 0:
6099b3b5 1999 zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
29db05b4 2000 naddr=NULL;
2001 }
718e3744 2002
2003 /* Make new socket. */
2004 accept_sock = sockunion_stream_socket (&su);
2005 if (accept_sock < 0)
2006 return;
2007
2008 /* This is server, so reuse address. */
2009 sockopt_reuseaddr (accept_sock);
2010 sockopt_reuseport (accept_sock);
2da59394 2011 set_cloexec (accept_sock);
718e3744 2012
2013 /* Bind socket to universal address and given port. */
29db05b4 2014 ret = sockunion_bind (accept_sock, &su, port, naddr);
718e3744 2015 if (ret < 0)
2016 {
29db05b4 2017 zlog_warn("can't bind socket");
718e3744 2018 close (accept_sock); /* Avoid sd leak. */
2019 return;
2020 }
2021
2022 /* Listen socket under queue 3. */
2023 ret = listen (accept_sock, 3);
2024 if (ret < 0)
2025 {
2026 zlog (NULL, LOG_WARNING, "can't listen socket");
2027 close (accept_sock); /* Avoid sd leak. */
2028 return;
2029 }
2030
2031 /* Add vty server event. */
2032 vty_event (VTY_SERV, accept_sock, NULL);
2033}
1cbb5dfc 2034#endif /* HAVE_IPV6 */
718e3744 2035
2036#ifdef VTYSH
2037/* For sockaddr_un. */
2038#include <sys/un.h>
2039
2040/* VTY shell UNIX domain socket. */
9fc7ebf1 2041static void
6ad96ea1 2042vty_serv_un (const char *path)
718e3744 2043{
2044 int ret;
75e15fe4 2045 int sock, len;
718e3744 2046 struct sockaddr_un serv;
2047 mode_t old_mask;
edd7c245 2048 struct zprivs_ids_t ids;
2049
718e3744 2050 /* First of all, unlink existing socket */
2051 unlink (path);
2052
2053 /* Set umask */
1921e6f8 2054 old_mask = umask (0007);
718e3744 2055
2056 /* Make UNIX domain socket. */
2057 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2058 if (sock < 0)
2059 {
6a52d0d1 2060 zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
718e3744 2061 return;
2062 }
2063
2064 /* Make server socket. */
2065 memset (&serv, 0, sizeof (struct sockaddr_un));
2066 serv.sun_family = AF_UNIX;
2067 strncpy (serv.sun_path, path, strlen (path));
6f0e3f6e 2068#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
718e3744 2069 len = serv.sun_len = SUN_LEN(&serv);
2070#else
2071 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
6f0e3f6e 2072#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
718e3744 2073
2da59394
DL
2074 set_cloexec (sock);
2075
718e3744 2076 ret = bind (sock, (struct sockaddr *) &serv, len);
2077 if (ret < 0)
2078 {
6a52d0d1 2079 zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
718e3744 2080 close (sock); /* Avoid sd leak. */
2081 return;
2082 }
2083
2084 ret = listen (sock, 5);
2085 if (ret < 0)
2086 {
6a52d0d1 2087 zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
718e3744 2088 close (sock); /* Avoid sd leak. */
2089 return;
2090 }
2091
2092 umask (old_mask);
2093
edd7c245 2094 zprivs_get_ids(&ids);
2095
2096 if (ids.gid_vty > 0)
2097 {
2098 /* set group of socket */
2099 if ( chown (path, -1, ids.gid_vty) )
2100 {
2101 zlog_err ("vty_serv_un: could chown socket, %s",
6099b3b5 2102 safe_strerror (errno) );
edd7c245 2103 }
2104 }
2105
718e3744 2106 vty_event (VTYSH_SERV, sock, NULL);
2107}
2108
2109/* #define VTYSH_DEBUG 1 */
2110
2111static int
2112vtysh_accept (struct thread *thread)
2113{
2114 int accept_sock;
2115 int sock;
2116 int client_len;
2117 struct sockaddr_un client;
2118 struct vty *vty;
2119
2120 accept_sock = THREAD_FD (thread);
2121
2122 vty_event (VTYSH_SERV, accept_sock, NULL);
2123
2124 memset (&client, 0, sizeof (struct sockaddr_un));
2125 client_len = sizeof (struct sockaddr_un);
2126
e473b032 2127 sock = accept (accept_sock, (struct sockaddr *) &client,
2128 (socklen_t *) &client_len);
718e3744 2129
2130 if (sock < 0)
2131 {
6099b3b5 2132 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
718e3744 2133 return -1;
2134 }
2135
9fc7ebf1 2136 if (set_nonblocking(sock) < 0)
75e15fe4 2137 {
9fc7ebf1 2138 zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
2139 " %s, closing", sock, safe_strerror (errno));
75e15fe4 2140 close (sock);
2141 return -1;
2142 }
2da59394
DL
2143 set_cloexec(sock);
2144
718e3744 2145#ifdef VTYSH_DEBUG
2146 printf ("VTY shell accept\n");
2147#endif /* VTYSH_DEBUG */
2148
2149 vty = vty_new ();
2150 vty->fd = sock;
c5e69a02 2151 vty->wfd = sock;
718e3744 2152 vty->type = VTY_SHELL_SERV;
2153 vty->node = VIEW_NODE;
2154
2155 vty_event (VTYSH_READ, sock, vty);
2156
2157 return 0;
2158}
2159
9fc7ebf1 2160static int
2161vtysh_flush(struct vty *vty)
2162{
c5e69a02 2163 switch (buffer_flush_available(vty->obuf, vty->wfd))
9fc7ebf1 2164 {
2165 case BUFFER_PENDING:
c5e69a02 2166 vty_event(VTYSH_WRITE, vty->wfd, vty);
9fc7ebf1 2167 break;
2168 case BUFFER_ERROR:
74542d73 2169 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
9fc7ebf1 2170 zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
2171 buffer_reset(vty->obuf);
2172 vty_close(vty);
2173 return -1;
2174 break;
2175 case BUFFER_EMPTY:
2176 break;
2177 }
2178 return 0;
2179}
2180
718e3744 2181static int
2182vtysh_read (struct thread *thread)
2183{
2184 int ret;
2185 int sock;
2186 int nbytes;
2187 struct vty *vty;
2188 unsigned char buf[VTY_READ_BUFSIZ];
9fc7ebf1 2189 unsigned char *p;
718e3744 2190 u_char header[4] = {0, 0, 0, 0};
2191
2192 sock = THREAD_FD (thread);
2193 vty = THREAD_ARG (thread);
2194 vty->t_read = NULL;
2195
9fc7ebf1 2196 if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
718e3744 2197 {
9fc7ebf1 2198 if (nbytes < 0)
2199 {
2200 if (ERRNO_IO_RETRY(errno))
2201 {
2202 vty_event (VTYSH_READ, sock, vty);
2203 return 0;
2204 }
74542d73 2205 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
9fc7ebf1 2206 zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
2207 __func__, sock, safe_strerror(errno));
2208 }
2209 buffer_reset(vty->obuf);
718e3744 2210 vty_close (vty);
2211#ifdef VTYSH_DEBUG
2212 printf ("close vtysh\n");
2213#endif /* VTYSH_DEBUG */
2214 return 0;
2215 }
2216
2217#ifdef VTYSH_DEBUG
9fc7ebf1 2218 printf ("line: %.*s\n", nbytes, buf);
718e3744 2219#endif /* VTYSH_DEBUG */
2220
9fc7ebf1 2221 for (p = buf; p < buf+nbytes; p++)
2222 {
2223 vty_ensure(vty, vty->length+1);
2224 vty->buf[vty->length++] = *p;
2225 if (*p == '\0')
2226 {
2227 /* Pass this line to parser. */
2228 ret = vty_execute (vty);
2229 /* Note that vty_execute clears the command buffer and resets
2230 vty->length to 0. */
718e3744 2231
9fc7ebf1 2232 /* Return result. */
718e3744 2233#ifdef VTYSH_DEBUG
9fc7ebf1 2234 printf ("result: %d\n", ret);
2235 printf ("vtysh node: %d\n", vty->node);
718e3744 2236#endif /* VTYSH_DEBUG */
2237
95c4aff2
DL
2238 /* hack for asynchronous "write integrated"
2239 * - other commands in "buf" will be ditched
2240 * - input during pending config-write is "unsupported" */
2241 if (ret == CMD_SUSPEND)
2242 break;
2243
2244 /* warning: watchquagga hardcodes this result write */
9fc7ebf1 2245 header[3] = ret;
2246 buffer_put(vty->obuf, header, 4);
2247
2248 if (!vty->t_write && (vtysh_flush(vty) < 0))
2249 /* Try to flush results; exit if a write error occurs. */
2250 return 0;
2251 }
2252 }
718e3744 2253
2254 vty_event (VTYSH_READ, sock, vty);
2255
2256 return 0;
2257}
49ff6d9d 2258
2259static int
2260vtysh_write (struct thread *thread)
2261{
2262 struct vty *vty = THREAD_ARG (thread);
2263
2264 vty->t_write = NULL;
9fc7ebf1 2265 vtysh_flush(vty);
976d8c73 2266 return 0;
49ff6d9d 2267}
2268
718e3744 2269#endif /* VTYSH */
2270
2271/* Determine address family to bind. */
2272void
6ad96ea1 2273vty_serv_sock (const char *addr, unsigned short port, const char *path)
718e3744 2274{
2275 /* If port is set to 0, do not listen on TCP/IP at all! */
2276 if (port)
2277 {
2278
2279#ifdef HAVE_IPV6
29db05b4 2280 vty_serv_sock_addrinfo (addr, port);
718e3744 2281#else /* ! HAVE_IPV6 */
29db05b4 2282 vty_serv_sock_family (addr,port, AF_INET);
718e3744 2283#endif /* HAVE_IPV6 */
2284 }
2285
2286#ifdef VTYSH
2287 vty_serv_un (path);
2288#endif /* VTYSH */
2289}
2290
9d0a3260
AS
2291/* Close vty interface. Warning: call this only from functions that
2292 will be careful not to access the vty afterwards (since it has
2293 now been freed). This is safest from top-level functions (called
2294 directly by the thread dispatcher). */
718e3744 2295void
2296vty_close (struct vty *vty)
2297{
2298 int i;
2299
2300 /* Cancel threads.*/
2301 if (vty->t_read)
2302 thread_cancel (vty->t_read);
2303 if (vty->t_write)
2304 thread_cancel (vty->t_write);
2305 if (vty->t_timeout)
2306 thread_cancel (vty->t_timeout);
718e3744 2307
2308 /* Flush buffer. */
c5e69a02 2309 buffer_flush_all (vty->obuf, vty->wfd);
718e3744 2310
2311 /* Free input buffer. */
2312 buffer_free (vty->obuf);
2313
718e3744 2314 /* Free command history. */
2315 for (i = 0; i < VTY_MAXHIST; i++)
2316 if (vty->hist[i])
2317 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2318
2319 /* Unset vector. */
2320 vector_unset (vtyvec, vty->fd);
2321
2322 /* Close socket. */
2323 if (vty->fd > 0)
2324 close (vty->fd);
b510a06e
DL
2325 else
2326 vty_stdio_reset ();
718e3744 2327
718e3744 2328 if (vty->buf)
2329 XFREE (MTYPE_VTY, vty->buf);
2330
5689fe5f
DW
2331 if (vty->error_buf)
2332 XFREE (MTYPE_VTY, vty->error_buf);
2333
718e3744 2334 /* Check configure. */
2335 vty_config_unlock (vty);
2336
2337 /* OK free vty. */
2338 XFREE (MTYPE_VTY, vty);
2339}
2340
2341/* When time out occur output message then close connection. */
2342static int
2343vty_timeout (struct thread *thread)
2344{
2345 struct vty *vty;
2346
2347 vty = THREAD_ARG (thread);
2348 vty->t_timeout = NULL;
2349 vty->v_timeout = 0;
2350
2351 /* Clear buffer*/
2352 buffer_reset (vty->obuf);
2353 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2354
2355 /* Close connection. */
2356 vty->status = VTY_CLOSE;
2357 vty_close (vty);
2358
2359 return 0;
2360}
2361
2362/* Read up configuration file from file_name. */
2363static void
2364vty_read_file (FILE *confp)
2365{
2366 int ret;
2367 struct vty *vty;
13fbc82d 2368 unsigned int line_num = 0;
718e3744 2369
2370 vty = vty_new ();
c5e69a02
DL
2371 vty->wfd = dup(STDERR_FILENO); /* vty_close() will close this */
2372 if (vty->wfd < 0)
13fbc82d
SH
2373 {
2374 /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
c5e69a02 2375 vty->wfd = STDOUT_FILENO;
13fbc82d 2376 }
c5e69a02 2377 vty->fd = STDIN_FILENO;
13fbc82d 2378 vty->type = VTY_FILE;
718e3744 2379 vty->node = CONFIG_NODE;
2380
2381 /* Execute configuration file */
13fbc82d
SH
2382 ret = config_from_file (vty, confp, &line_num);
2383
2384 /* Flush any previous errors before printing messages below */
2385 buffer_flush_all (vty->obuf, vty->fd);
718e3744 2386
7021c425 2387 if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
718e3744 2388 {
2389 switch (ret)
7021c425 2390 {
2391 case CMD_ERR_AMBIGUOUS:
13fbc82d 2392 fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
7021c425 2393 break;
2394 case CMD_ERR_NO_MATCH:
13fbc82d 2395 fprintf (stderr, "*** Error reading config: There is no such command.\n");
7021c425 2396 break;
2397 }
13fbc82d
SH
2398 fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
2399 line_num, vty->error_buf);
718e3744 2400 }
2401
2402 vty_close (vty);
2403}
2404
9fc7ebf1 2405static FILE *
718e3744 2406vty_use_backup_config (char *fullpath)
2407{
2408 char *fullpath_sav, *fullpath_tmp;
2409 FILE *ret = NULL;
2410 struct stat buf;
2411 int tmp, sav;
2412 int c;
2413 char buffer[512];
2414
2415 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2416 strcpy (fullpath_sav, fullpath);
2417 strcat (fullpath_sav, CONF_BACKUP_EXT);
2418 if (stat (fullpath_sav, &buf) == -1)
2419 {
2420 free (fullpath_sav);
2421 return NULL;
2422 }
2423
2424 fullpath_tmp = malloc (strlen (fullpath) + 8);
2425 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2426
2427 /* Open file to configuration write. */
2428 tmp = mkstemp (fullpath_tmp);
2429 if (tmp < 0)
2430 {
2431 free (fullpath_sav);
2432 free (fullpath_tmp);
2433 return NULL;
2434 }
2435
2436 sav = open (fullpath_sav, O_RDONLY);
2437 if (sav < 0)
2438 {
3dbf9969 2439 unlink (fullpath_tmp);
718e3744 2440 free (fullpath_sav);
2441 free (fullpath_tmp);
718e3744 2442 return NULL;
2443 }
2444
2445 while((c = read (sav, buffer, 512)) > 0)
77f24551
DS
2446 {
2447 if (write (tmp, buffer, c) <= 0)
2448 {
2449 free (fullpath_sav);
2450 free (fullpath_tmp);
2451 close (sav);
2452 close (tmp);
2453 return NULL;
2454 }
2455 }
718e3744 2456 close (sav);
2457 close (tmp);
2458
aa593d5e 2459 if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
2460 {
3dbf9969 2461 unlink (fullpath_tmp);
aa593d5e 2462 free (fullpath_sav);
2463 free (fullpath_tmp);
aa593d5e 2464 return NULL;
2465 }
2466
718e3744 2467 if (link (fullpath_tmp, fullpath) == 0)
2468 ret = fopen (fullpath, "r");
2469
2470 unlink (fullpath_tmp);
2471
2472 free (fullpath_sav);
2473 free (fullpath_tmp);
12f6ea23 2474 return ret;
718e3744 2475}
2476
2477/* Read up configuration file from file_name. */
2478void
2479vty_read_config (char *config_file,
320ec10a 2480 char *config_default_dir)
718e3744 2481{
ccc9235e 2482 char cwd[MAXPATHLEN];
718e3744 2483 FILE *confp = NULL;
2484 char *fullpath;
05865c90 2485 char *tmp = NULL;
718e3744 2486
2487 /* If -f flag specified. */
2488 if (config_file != NULL)
2489 {
2490 if (! IS_DIRECTORY_SEP (config_file[0]))
320ec10a 2491 {
77f24551
DS
2492 if (getcwd (cwd, MAXPATHLEN) == NULL)
2493 {
2494 fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno);
2495 exit (1);
2496 }
05865c90 2497 tmp = XMALLOC (MTYPE_TMP,
320ec10a 2498 strlen (cwd) + strlen (config_file) + 2);
05865c90 2499 sprintf (tmp, "%s/%s", cwd, config_file);
2500 fullpath = tmp;
320ec10a 2501 }
718e3744 2502 else
320ec10a 2503 fullpath = config_file;
718e3744 2504
2505 confp = fopen (fullpath, "r");
2506
2507 if (confp == NULL)
320ec10a 2508 {
3d1dc857 2509 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2510 __func__, fullpath, safe_strerror (errno));
2511
320ec10a 2512 confp = vty_use_backup_config (fullpath);
2513 if (confp)
2514 fprintf (stderr, "WARNING: using backup configuration file!\n");
2515 else
2516 {
2517 fprintf (stderr, "can't open configuration file [%s]\n",
3d1dc857 2518 config_file);
320ec10a 2519 exit(1);
2520 }
2521 }
718e3744 2522 }
2523 else
2524 {
a7222276 2525
e4421165 2526 host_config_set (config_default_dir);
a7222276 2527
718e3744 2528#ifdef VTYSH
320ec10a 2529 int ret;
2530 struct stat conf_stat;
2531
2532 /* !!!!PLEASE LEAVE!!!!
2533 * This is NEEDED for use with vtysh -b, or else you can get
2534 * a real configuration food fight with a lot garbage in the
2535 * merged configuration file it creates coming from the per
2536 * daemon configuration files. This also allows the daemons
2537 * to start if there default configuration file is not
2538 * present or ignore them, as needed when using vtysh -b to
2539 * configure the daemons at boot - MAG
2540 */
2541
2542 /* Stat for vtysh Zebra.conf, if found startup and wait for
2543 * boot configuration
2544 */
2545
2546 if ( strstr(config_default_dir, "vtysh") == NULL)
2547 {
2548 ret = stat (integrate_default, &conf_stat);
2549 if (ret >= 0)
6eda6425 2550 goto tmp_free_and_out;
320ec10a 2551 }
a7222276 2552#endif /* VTYSH */
e4421165
DS
2553 confp = fopen (config_default_dir, "r");
2554 if (confp == NULL)
2555 {
2556 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2557 __func__, config_default_dir, safe_strerror (errno));
7ce2a26c 2558
e4421165
DS
2559 confp = vty_use_backup_config (config_default_dir);
2560 if (confp)
2561 {
2562 fprintf (stderr, "WARNING: using backup configuration file!\n");
2563 fullpath = config_default_dir;
2564 }
2565 else
2566 {
2567 fprintf (stderr, "can't open configuration file [%s]\n",
6eda6425
DS
2568 config_default_dir);
2569 goto tmp_free_and_out;
e4421165 2570 }
7ce2a26c 2571 }
e4421165
DS
2572 else
2573 fullpath = config_default_dir;
2574 }
2575
718e3744 2576 vty_read_file (confp);
2577
2578 fclose (confp);
2579
2580 host_config_set (fullpath);
6eda6425
DS
2581
2582tmp_free_and_out:
05865c90 2583 if (tmp)
2584 XFREE (MTYPE_TMP, fullpath);
718e3744 2585}
2586
2587/* Small utility function which output log to the VTY. */
2588void
274a4a44 2589vty_log (const char *level, const char *proto_str,
1ed72e0b 2590 const char *format, struct timestamp_control *ctl, va_list va)
718e3744 2591{
8c328f11 2592 unsigned int i;
718e3744 2593 struct vty *vty;
a4b30303
PJ
2594
2595 if (!vtyvec)
2596 return;
718e3744 2597
55468c86 2598 for (i = 0; i < vector_active (vtyvec); i++)
718e3744 2599 if ((vty = vector_slot (vtyvec, i)) != NULL)
2600 if (vty->monitor)
d246bd96 2601 {
2602 va_list ac;
2603 va_copy(ac, va);
1ed72e0b 2604 vty_log_out (vty, level, proto_str, format, ctl, ac);
d246bd96 2605 va_end(ac);
2606 }
718e3744 2607}
2608
274a4a44 2609/* Async-signal-safe version of vty_log for fixed strings. */
2610void
24873f0c 2611vty_log_fixed (char *buf, size_t len)
274a4a44 2612{
2613 unsigned int i;
9fc7ebf1 2614 struct iovec iov[2];
24873f0c 2615 char crlf[4] = "\r\n";
9fc7ebf1 2616
a4b30303
PJ
2617 /* vty may not have been initialised */
2618 if (!vtyvec)
2619 return;
2620
1f9a9fff 2621 iov[0].iov_base = buf;
9fc7ebf1 2622 iov[0].iov_len = len;
24873f0c 2623 iov[1].iov_base = crlf;
9fc7ebf1 2624 iov[1].iov_len = 2;
274a4a44 2625
55468c86 2626 for (i = 0; i < vector_active (vtyvec); i++)
274a4a44 2627 {
2628 struct vty *vty;
9fc7ebf1 2629 if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
2630 /* N.B. We don't care about the return code, since process is
2631 most likely just about to die anyway. */
c5e69a02 2632 if (writev(vty->wfd, iov, 2) == -1)
77f24551
DS
2633 {
2634 fprintf(stderr, "Failure to writev: %d\n", errno);
2635 exit(-1);
2636 }
274a4a44 2637 }
2638}
2639
718e3744 2640int
2641vty_config_lock (struct vty *vty)
2642{
cc933ef9
DL
2643 if (vty_config_is_lockless)
2644 return 1;
718e3744 2645 if (vty_config == 0)
2646 {
2647 vty->config = 1;
2648 vty_config = 1;
2649 }
2650 return vty->config;
2651}
2652
2653int
2654vty_config_unlock (struct vty *vty)
2655{
cc933ef9
DL
2656 if (vty_config_is_lockless)
2657 return 0;
718e3744 2658 if (vty_config == 1 && vty->config == 1)
2659 {
2660 vty->config = 0;
2661 vty_config = 0;
2662 }
2663 return vty->config;
2664}
6b0655a2 2665
cc933ef9
DL
2666void
2667vty_config_lockless (void)
2668{
2669 vty_config_is_lockless = 1;
2670}
2671
718e3744 2672/* Master of the threads. */
79159516 2673static struct thread_master *vty_master;
718e3744 2674
2675static void
2676vty_event (enum event event, int sock, struct vty *vty)
2677{
2678 struct thread *vty_serv_thread;
2679
2680 switch (event)
2681 {
2682 case VTY_SERV:
79159516 2683 vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
718e3744 2684 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2685 break;
2686#ifdef VTYSH
2687 case VTYSH_SERV:
79159516 2688 vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
677bcbbf 2689 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
718e3744 2690 break;
2691 case VTYSH_READ:
79159516 2692 vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
49ff6d9d 2693 break;
2694 case VTYSH_WRITE:
79159516 2695 vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
718e3744 2696 break;
2697#endif /* VTYSH */
2698 case VTY_READ:
79159516 2699 vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
718e3744 2700
2701 /* Time out treatment. */
2702 if (vty->v_timeout)
2703 {
2704 if (vty->t_timeout)
2705 thread_cancel (vty->t_timeout);
2706 vty->t_timeout =
79159516 2707 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
718e3744 2708 }
2709 break;
2710 case VTY_WRITE:
2711 if (! vty->t_write)
79159516 2712 vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
718e3744 2713 break;
2714 case VTY_TIMEOUT_RESET:
2715 if (vty->t_timeout)
2716 {
2717 thread_cancel (vty->t_timeout);
2718 vty->t_timeout = NULL;
2719 }
2720 if (vty->v_timeout)
2721 {
2722 vty->t_timeout =
79159516 2723 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
718e3744 2724 }
2725 break;
2726 }
2727}
6b0655a2 2728
718e3744 2729DEFUN (config_who,
2730 config_who_cmd,
2731 "who",
2732 "Display who is on vty\n")
2733{
8c328f11 2734 unsigned int i;
718e3744 2735 struct vty *v;
2736
55468c86 2737 for (i = 0; i < vector_active (vtyvec); i++)
718e3744 2738 if ((v = vector_slot (vtyvec, i)) != NULL)
2739 vty_out (vty, "%svty[%d] connected from %s.%s",
2740 v->config ? "*" : " ",
2741 i, v->address, VTY_NEWLINE);
2742 return CMD_SUCCESS;
2743}
2744
2745/* Move to vty configuration mode. */
2746DEFUN (line_vty,
2747 line_vty_cmd,
2748 "line vty",
2749 "Configure a terminal line\n"
2750 "Virtual terminal\n")
2751{
2752 vty->node = VTY_NODE;
2753 return CMD_SUCCESS;
2754}
2755
2756/* Set time out value. */
9fc7ebf1 2757static int
9035efaa 2758exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
718e3744 2759{
2760 unsigned long timeout = 0;
2761
2762 /* min_str and sec_str are already checked by parser. So it must be
2763 all digit string. */
2764 if (min_str)
2765 {
2766 timeout = strtol (min_str, NULL, 10);
2767 timeout *= 60;
2768 }
2769 if (sec_str)
2770 timeout += strtol (sec_str, NULL, 10);
2771
2772 vty_timeout_val = timeout;
2773 vty->v_timeout = timeout;
2774 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2775
2776
2777 return CMD_SUCCESS;
2778}
2779
2780DEFUN (exec_timeout_min,
2781 exec_timeout_min_cmd,
2782 "exec-timeout <0-35791>",
2783 "Set timeout value\n"
2784 "Timeout value in minutes\n")
2785{
2786 return exec_timeout (vty, argv[0], NULL);
2787}
2788
2789DEFUN (exec_timeout_sec,
2790 exec_timeout_sec_cmd,
2791 "exec-timeout <0-35791> <0-2147483>",
2792 "Set the EXEC timeout\n"
2793 "Timeout in minutes\n"
2794 "Timeout in seconds\n")
2795{
2796 return exec_timeout (vty, argv[0], argv[1]);
2797}
2798
2799DEFUN (no_exec_timeout,
2800 no_exec_timeout_cmd,
2801 "no exec-timeout",
2802 NO_STR
2803 "Set the EXEC timeout\n")
2804{
2805 return exec_timeout (vty, NULL, NULL);
2806}
2807
2808/* Set vty access class. */
2809DEFUN (vty_access_class,
2810 vty_access_class_cmd,
2811 "access-class WORD",
2812 "Filter connections based on an IP access list\n"
2813 "IP access list\n")
2814{
2815 if (vty_accesslist_name)
2816 XFREE(MTYPE_VTY, vty_accesslist_name);
2817
2818 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2819
2820 return CMD_SUCCESS;
2821}
2822
2823/* Clear vty access class. */
2824DEFUN (no_vty_access_class,
2825 no_vty_access_class_cmd,
2826 "no access-class [WORD]",
2827 NO_STR
2828 "Filter connections based on an IP access list\n"
2829 "IP access list\n")
2830{
2831 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2832 {
2833 vty_out (vty, "Access-class is not currently applied to vty%s",
2834 VTY_NEWLINE);
2835 return CMD_WARNING;
2836 }
2837
2838 XFREE(MTYPE_VTY, vty_accesslist_name);
2839
2840 vty_accesslist_name = NULL;
2841
2842 return CMD_SUCCESS;
2843}
2844
2845#ifdef HAVE_IPV6
2846/* Set vty access class. */
2847DEFUN (vty_ipv6_access_class,
2848 vty_ipv6_access_class_cmd,
2849 "ipv6 access-class WORD",
2850 IPV6_STR
2851 "Filter connections based on an IP access list\n"
2852 "IPv6 access list\n")
2853{
2854 if (vty_ipv6_accesslist_name)
2855 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2856
2857 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2858
2859 return CMD_SUCCESS;
2860}
2861
2862/* Clear vty access class. */
2863DEFUN (no_vty_ipv6_access_class,
2864 no_vty_ipv6_access_class_cmd,
2865 "no ipv6 access-class [WORD]",
2866 NO_STR
2867 IPV6_STR
2868 "Filter connections based on an IP access list\n"
2869 "IPv6 access list\n")
2870{
2871 if (! vty_ipv6_accesslist_name ||
2872 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2873 {
2874 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2875 VTY_NEWLINE);
2876 return CMD_WARNING;
2877 }
2878
2879 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2880
2881 vty_ipv6_accesslist_name = NULL;
2882
2883 return CMD_SUCCESS;
2884}
2885#endif /* HAVE_IPV6 */
2886
2887/* vty login. */
2888DEFUN (vty_login,
2889 vty_login_cmd,
2890 "login",
2891 "Enable password checking\n")
2892{
2893 no_password_check = 0;
2894 return CMD_SUCCESS;
2895}
2896
2897DEFUN (no_vty_login,
2898 no_vty_login_cmd,
2899 "no login",
2900 NO_STR
2901 "Enable password checking\n")
2902{
2903 no_password_check = 1;
2904 return CMD_SUCCESS;
2905}
2906
2907DEFUN (service_advanced_vty,
2908 service_advanced_vty_cmd,
2909 "service advanced-vty",
2910 "Set up miscellaneous service\n"
2911 "Enable advanced mode vty interface\n")
2912{
2913 host.advanced = 1;
2914 return CMD_SUCCESS;
2915}
2916
2917DEFUN (no_service_advanced_vty,
2918 no_service_advanced_vty_cmd,
2919 "no service advanced-vty",
2920 NO_STR
2921 "Set up miscellaneous service\n"
2922 "Enable advanced mode vty interface\n")
2923{
2924 host.advanced = 0;
2925 return CMD_SUCCESS;
2926}
2927
2928DEFUN (terminal_monitor,
2929 terminal_monitor_cmd,
2930 "terminal monitor",
2931 "Set terminal line parameters\n"
2932 "Copy debug output to the current terminal line\n")
2933{
2934 vty->monitor = 1;
2935 return CMD_SUCCESS;
2936}
2937
2938DEFUN (terminal_no_monitor,
2939 terminal_no_monitor_cmd,
2940 "terminal no monitor",
2941 "Set terminal line parameters\n"
2942 NO_STR
2943 "Copy debug output to the current terminal line\n")
2944{
2945 vty->monitor = 0;
2946 return CMD_SUCCESS;
2947}
2948
789f78ac 2949ALIAS (terminal_no_monitor,
2950 no_terminal_monitor_cmd,
2951 "no terminal monitor",
2952 NO_STR
2953 "Set terminal line parameters\n"
2954 "Copy debug output to the current terminal line\n")
2955
718e3744 2956DEFUN (show_history,
2957 show_history_cmd,
2958 "show history",
2959 SHOW_STR
2960 "Display the session command history\n")
2961{
2962 int index;
2963
2964 for (index = vty->hindex + 1; index != vty->hindex;)
2965 {
2966 if (index == VTY_MAXHIST)
2967 {
2968 index = 0;
2969 continue;
2970 }
2971
2972 if (vty->hist[index] != NULL)
2973 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
2974
2975 index++;
2976 }
2977
2978 return CMD_SUCCESS;
2979}
2980
da688ecd
LB
2981/* vty login. */
2982DEFUN (log_commands,
2983 log_commands_cmd,
2984 "log commands",
2985 "Logging control\n"
2986 "Log all commands (can't be unset without restart)\n")
2987{
2988 do_log_commands = 1;
2989 return CMD_SUCCESS;
2990}
2991
718e3744 2992/* Display current configuration. */
9fc7ebf1 2993static int
718e3744 2994vty_config_write (struct vty *vty)
2995{
2996 vty_out (vty, "line vty%s", VTY_NEWLINE);
2997
2998 if (vty_accesslist_name)
2999 vty_out (vty, " access-class %s%s",
3000 vty_accesslist_name, VTY_NEWLINE);
3001
3002 if (vty_ipv6_accesslist_name)
3003 vty_out (vty, " ipv6 access-class %s%s",
3004 vty_ipv6_accesslist_name, VTY_NEWLINE);
3005
3006 /* exec-timeout */
3007 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
3008 vty_out (vty, " exec-timeout %ld %ld%s",
3009 vty_timeout_val / 60,
3010 vty_timeout_val % 60, VTY_NEWLINE);
3011
3012 /* login */
3013 if (no_password_check)
3014 vty_out (vty, " no login%s", VTY_NEWLINE);
da688ecd
LB
3015
3016 if (do_log_commands)
3017 vty_out (vty, "log commands%s", VTY_NEWLINE);
3018
718e3744 3019 vty_out (vty, "!%s", VTY_NEWLINE);
3020
3021 return CMD_SUCCESS;
3022}
3023
3024struct cmd_node vty_node =
3025{
3026 VTY_NODE,
3027 "%s(config-line)# ",
e7168df4 3028 1,
718e3744 3029};
3030
3031/* Reset all VTY status. */
3032void
3033vty_reset ()
3034{
8c328f11 3035 unsigned int i;
718e3744 3036 struct vty *vty;
3037 struct thread *vty_serv_thread;
3038
55468c86 3039 for (i = 0; i < vector_active (vtyvec); i++)
718e3744 3040 if ((vty = vector_slot (vtyvec, i)) != NULL)
3041 {
3042 buffer_reset (vty->obuf);
3043 vty->status = VTY_CLOSE;
3044 vty_close (vty);
3045 }
3046
55468c86 3047 for (i = 0; i < vector_active (Vvty_serv_thread); i++)
718e3744 3048 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
3049 {
3050 thread_cancel (vty_serv_thread);
3051 vector_slot (Vvty_serv_thread, i) = NULL;
3052 close (i);
3053 }
3054
3055 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3056
3057 if (vty_accesslist_name)
3058 {
3059 XFREE(MTYPE_VTY, vty_accesslist_name);
3060 vty_accesslist_name = NULL;
3061 }
3062
3063 if (vty_ipv6_accesslist_name)
3064 {
3065 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3066 vty_ipv6_accesslist_name = NULL;
3067 }
3068}
3069
9fc7ebf1 3070static void
3071vty_save_cwd (void)
718e3744 3072{
79ad2798 3073 char cwd[MAXPATHLEN];
ccc9235e 3074 char *c;
79ad2798 3075
ccc9235e 3076 c = getcwd (cwd, MAXPATHLEN);
718e3744 3077
ccc9235e 3078 if (!c)
79ad2798 3079 {
77f24551
DS
3080 /*
3081 * At this point if these go wrong, more than likely
3082 * the whole world is coming down around us
3083 * Hence not worrying about it too much.
3084 */
3085 if (!chdir (SYSCONFDIR))
3086 {
3087 fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno);
3088 exit(-1);
3089 }
3090 if (getcwd (cwd, MAXPATHLEN) == NULL)
3091 {
3092 fprintf(stderr, "Failure to getcwd, errno: %d\n", errno);
3093 exit(-1);
3094 }
79ad2798 3095 }
718e3744 3096
3097 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
3098 strcpy (vty_cwd, cwd);
3099}
3100
3101char *
3102vty_get_cwd ()
3103{
3104 return vty_cwd;
3105}
3106
3107int
3108vty_shell (struct vty *vty)
3109{
3110 return vty->type == VTY_SHELL ? 1 : 0;
3111}
3112
3113int
3114vty_shell_serv (struct vty *vty)
3115{
3116 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3117}
3118
3119void
3120vty_init_vtysh ()
3121{
3122 vtyvec = vector_init (VECTOR_MIN_SIZE);
3123}
3124
3125/* Install vty's own commands like `who' command. */
3126void
b21b19c5 3127vty_init (struct thread_master *master_thread)
718e3744 3128{
3129 /* For further configuration read, preserve current directory. */
3130 vty_save_cwd ();
3131
3132 vtyvec = vector_init (VECTOR_MIN_SIZE);
3133
79159516 3134 vty_master = master_thread;
b21b19c5 3135
b510a06e
DL
3136 atexit (vty_stdio_reset);
3137
718e3744 3138 /* Initilize server thread vector. */
3139 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
3140
3141 /* Install bgp top node. */
3142 install_node (&vty_node, vty_config_write);
3143
3144 install_element (VIEW_NODE, &config_who_cmd);
3145 install_element (VIEW_NODE, &show_history_cmd);
718e3744 3146 install_element (CONFIG_NODE, &line_vty_cmd);
3147 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
3148 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
3149 install_element (CONFIG_NODE, &show_history_cmd);
da688ecd 3150 install_element (CONFIG_NODE, &log_commands_cmd);
718e3744 3151 install_element (ENABLE_NODE, &terminal_monitor_cmd);
3152 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
789f78ac 3153 install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
718e3744 3154
3155 install_default (VTY_NODE);
3156 install_element (VTY_NODE, &exec_timeout_min_cmd);
3157 install_element (VTY_NODE, &exec_timeout_sec_cmd);
3158 install_element (VTY_NODE, &no_exec_timeout_cmd);
3159 install_element (VTY_NODE, &vty_access_class_cmd);
3160 install_element (VTY_NODE, &no_vty_access_class_cmd);
3161 install_element (VTY_NODE, &vty_login_cmd);
3162 install_element (VTY_NODE, &no_vty_login_cmd);
3163#ifdef HAVE_IPV6
3164 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
3165 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
3166#endif /* HAVE_IPV6 */
3167}
228da428
CC
3168
3169void
3170vty_terminate (void)
3171{
3172 if (vty_cwd)
3173 XFREE (MTYPE_TMP, vty_cwd);
3174
3175 if (vtyvec && Vvty_serv_thread)
3176 {
3177 vty_reset ();
3178 vector_free (vtyvec);
3179 vector_free (Vvty_serv_thread);
3180 }
3181}
eac6e3f0
RW
3182
3183/* Utility functions to get arguments from commands generated
3184 by the xml2cli.pl script. */
3185const char *
3186vty_get_arg_value (struct vty_arg *args[], const char *arg)
3187{
3188 while (*args)
3189 {
3190 if (strcmp ((*args)->name, arg) == 0)
3191 return (*args)->value;
3192 args++;
3193 }
3194 return NULL;
3195}
3196
3197struct vty_arg *
3198vty_get_arg (struct vty_arg *args[], const char *arg)
3199{
3200 while (*args)
3201 {
3202 if (strcmp ((*args)->name, arg) == 0)
3203 return *args;
3204 args++;
3205 }
3206 return NULL;
3207}