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