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