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