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