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