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