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