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