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