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