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