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