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