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