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