blob: 7ca8354367525ca2d8e14646ad862e659e7f51a8 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/*
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 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Zebra; see the file COPYING. If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23#include <zebra.h>
24
25#include "linklist.h"
paulb21b19c2003-06-15 01:28:29 +000026#include "thread.h"
paul718e3742002-12-13 20:15:29 +000027#include "buffer.h"
gdt5e4fa162004-03-16 14:38:36 +000028#include <lib/version.h>
paul718e3742002-12-13 20:15:29 +000029#include "command.h"
30#include "sockunion.h"
paul718e3742002-12-13 20:15:29 +000031#include "memory.h"
32#include "str.h"
33#include "log.h"
34#include "prefix.h"
35#include "filter.h"
paulb21b19c2003-06-15 01:28:29 +000036#include "vty.h"
pauledd7c242003-06-04 13:59:38 +000037#include "privs.h"
ajs9fc7ebf2005-02-23 15:12:34 +000038#include "network.h"
39
40#include <arpa/telnet.h>
David Lamparterba53a8f2015-05-05 11:04:46 +020041#include <termios.h>
paul718e3742002-12-13 20:15:29 +000042
Quentin Youngb7ceefe2017-01-10 23:33:50 +000043#define VTY_BUFSIZ 4096
44
paul718e3742002-12-13 20:15:29 +000045/* Vty events */
46enum event
47{
48 VTY_SERV,
49 VTY_READ,
50 VTY_WRITE,
51 VTY_TIMEOUT_RESET,
52#ifdef VTYSH
53 VTYSH_SERV,
ajs49ff6d92004-11-04 19:26:16 +000054 VTYSH_READ,
55 VTYSH_WRITE
paul718e3742002-12-13 20:15:29 +000056#endif /* VTYSH */
57};
58
59static void vty_event (enum event, int, struct vty *);
60
61/* Extern host structure from command.c */
62extern struct host host;
David Lamparter6b0655a2014-06-04 06:53:35 +020063
paul718e3742002-12-13 20:15:29 +000064/* 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. */
Christian Franke677bcbb2013-02-27 13:47:23 +000077static vector Vvty_serv_thread;
paul718e3742002-12-13 20:15:29 +000078
79/* Current directory. */
80char *vty_cwd = NULL;
81
82/* Configure lock. */
83static int vty_config;
84
85/* Login password check. */
86static int no_password_check = 0;
87
Paul Jakma62687ff2008-08-23 14:27:06 +010088/* Restrict unauthenticated logins? */
89static const u_char restricted_mode_default = 0;
90static u_char restricted_mode = 0;
91
paul718e3742002-12-13 20:15:29 +000092/* Integrated configuration file path */
93char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
94
Lou Berger86b2a0a2016-05-17 12:19:51 -040095static int do_log_commands = 0;
David Lamparter6b0655a2014-06-04 06:53:35 +020096
Paul Jakma7d662842017-01-19 10:39:23 +000097static void
98vty_buf_assert (struct vty *vty)
99{
100 assert (vty->cp <= vty->length);
101 assert (vty->length < vty->max);
102 assert (vty->buf[vty->length] == '\0');
103}
104
105/* Sanity/safety wrappers around access to vty->buf */
106static void
107vty_buf_put (struct vty *vty, char c)
108{
109 vty_buf_assert (vty);
110 vty->buf[vty->cp] = c;
111 vty->buf[vty->max - 1] = '\0';
112}
113
paul718e3742002-12-13 20:15:29 +0000114/* VTY standard output function. */
115int
116vty_out (struct vty *vty, const char *format, ...)
117{
118 va_list args;
119 int len = 0;
120 int size = 1024;
121 char buf[1024];
122 char *p = NULL;
paul718e3742002-12-13 20:15:29 +0000123
124 if (vty_shell (vty))
ajsd246bd92004-11-23 17:35:08 +0000125 {
126 va_start (args, format);
127 vprintf (format, args);
128 va_end (args);
129 }
paul718e3742002-12-13 20:15:29 +0000130 else
131 {
132 /* Try to write to initial buffer. */
ajsd246bd92004-11-23 17:35:08 +0000133 va_start (args, format);
Lou Bergerc7f7e492016-01-12 13:41:49 -0500134 len = vsnprintf (buf, sizeof(buf), format, args);
ajsd246bd92004-11-23 17:35:08 +0000135 va_end (args);
paul718e3742002-12-13 20:15:29 +0000136
137 /* Initial buffer is not enough. */
138 if (len < 0 || len >= size)
139 {
140 while (1)
141 {
142 if (len > -1)
143 size = len + 1;
144 else
145 size = size * 2;
146
147 p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
148 if (! p)
149 return -1;
150
ajsd246bd92004-11-23 17:35:08 +0000151 va_start (args, format);
paul718e3742002-12-13 20:15:29 +0000152 len = vsnprintf (p, size, format, args);
ajsd246bd92004-11-23 17:35:08 +0000153 va_end (args);
paul718e3742002-12-13 20:15:29 +0000154
155 if (len > -1 && len < size)
156 break;
157 }
158 }
159
160 /* When initial buffer is enough to store all output. */
161 if (! p)
162 p = buf;
163
164 /* Pointer p must point out buffer. */
ajs9fc7ebf2005-02-23 15:12:34 +0000165 buffer_put (vty->obuf, (u_char *) p, len);
paul718e3742002-12-13 20:15:29 +0000166
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
paul718e3742002-12-13 20:15:29 +0000172 return len;
173}
174
ajsd246bd92004-11-23 17:35:08 +0000175static int
ajs274a4a42004-12-07 15:39:31 +0000176vty_log_out (struct vty *vty, const char *level, const char *proto_str,
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000177 const char *format, struct timestamp_control *ctl, va_list va)
paul718e3742002-12-13 20:15:29 +0000178{
ajs9fc7ebf2005-02-23 15:12:34 +0000179 int ret;
paul718e3742002-12-13 20:15:29 +0000180 int len;
181 char buf[1024];
Andrew J. Schorr08942da2006-07-03 20:58:29 +0000182
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000183 if (!ctl->already_rendered)
184 {
185 ctl->len = quagga_timestamp(ctl->precision, ctl->buf, 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';
paul718e3742002-12-13 20:15:29 +0000193
ajs274a4a42004-12-07 15:39:31 +0000194 if (level)
Andrew J. Schorr08942da2006-07-03 20:58:29 +0000195 ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
ajs274a4a42004-12-07 15:39:31 +0000196 else
Andrew J. Schorr08942da2006-07-03 20:58:29 +0000197 ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
198 if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
paul718e3742002-12-13 20:15:29 +0000199 return -1;
paul718e3742002-12-13 20:15:29 +0000200
ajs9fc7ebf2005-02-23 15:12:34 +0000201 if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
202 ((size_t)((len += ret)+2) > sizeof(buf)))
203 return -1;
paul718e3742002-12-13 20:15:29 +0000204
ajs9fc7ebf2005-02-23 15:12:34 +0000205 buf[len++] = '\r';
206 buf[len++] = '\n';
207
David Lamparter4715a532013-05-30 16:31:49 +0200208 if (write(vty->wfd, buf, len) < 0)
ajs9fc7ebf2005-02-23 15:12:34 +0000209 {
210 if (ERRNO_IO_RETRY(errno))
211 /* Kernel buffer is full, probably too much debugging output, so just
212 drop the data and ignore. */
213 return -1;
214 /* Fatal I/O error. */
Andrew J. Schorr74542d72006-07-10 18:09:42 +0000215 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +0000216 zlog_warn("%s: write failed to vty client fd %d, closing: %s",
217 __func__, vty->fd, safe_strerror(errno));
218 buffer_reset(vty->obuf);
Andrew J. Schorr9d0a3262006-07-11 00:06:49 +0000219 /* cannot call vty_close, because a parent routine may still try
220 to access the vty struct */
221 vty->status = VTY_CLOSE;
222 shutdown(vty->fd, SHUT_RDWR);
ajs9fc7ebf2005-02-23 15:12:34 +0000223 return -1;
224 }
225 return 0;
paul718e3742002-12-13 20:15:29 +0000226}
227
228/* Output current time to the vty. */
229void
230vty_time_print (struct vty *vty, int cr)
231{
Christian Franke880e31c2016-05-03 19:59:40 +0200232 char buf[QUAGGA_TIMESTAMP_LEN];
paul718e3742002-12-13 20:15:29 +0000233
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000234 if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
paul718e3742002-12-13 20:15:29 +0000235 {
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000236 zlog (NULL, LOG_INFO, "quagga_timestamp error");
paul718e3742002-12-13 20:15:29 +0000237 return;
238 }
239 if (cr)
240 vty_out (vty, "%s\n", buf);
241 else
242 vty_out (vty, "%s ", buf);
243
244 return;
245}
246
247/* Say hello to vty interface. */
248void
249vty_hello (struct vty *vty)
250{
paul3b0c5d92005-03-08 10:43:43 +0000251 if (host.motdfile)
252 {
253 FILE *f;
254 char buf[4096];
paul22085182005-03-08 16:00:12 +0000255
paul3b0c5d92005-03-08 10:43:43 +0000256 f = fopen (host.motdfile, "r");
257 if (f)
258 {
paulb45da6f2005-03-08 15:16:57 +0000259 while (fgets (buf, sizeof (buf), f))
paul3b0c5d92005-03-08 10:43:43 +0000260 {
paulb45da6f2005-03-08 15:16:57 +0000261 char *s;
paul22085182005-03-08 16:00:12 +0000262 /* work backwards to ignore trailling isspace() */
gdtf80a0162005-12-29 16:03:32 +0000263 for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
paul22085182005-03-08 16:00:12 +0000264 s--);
265 *s = '\0';
266 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
267 }
paul3b0c5d92005-03-08 10:43:43 +0000268 fclose (f);
269 }
270 else
paulb45da6f2005-03-08 15:16:57 +0000271 vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
paul3b0c5d92005-03-08 10:43:43 +0000272 }
273 else if (host.motd)
Nico Goldeb830c892010-08-01 15:24:35 +0200274 vty_out (vty, "%s", host.motd);
paul718e3742002-12-13 20:15:29 +0000275}
276
277/* Put out prompt and wait input from user. */
278static void
279vty_prompt (struct vty *vty)
280{
281 struct utsname names;
282 const char*hostname;
283
284 if (vty->type == VTY_TERM)
285 {
286 hostname = host.name;
287 if (!hostname)
288 {
289 uname (&names);
290 hostname = names.nodename;
291 }
292 vty_out (vty, cmd_prompt (vty->node), hostname);
293 }
294}
295
296/* Send WILL TELOPT_ECHO to remote server. */
ajs9fc7ebf2005-02-23 15:12:34 +0000297static void
paul718e3742002-12-13 20:15:29 +0000298vty_will_echo (struct vty *vty)
299{
paul02ff83c2004-06-11 11:27:03 +0000300 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
paul718e3742002-12-13 20:15:29 +0000301 vty_out (vty, "%s", cmd);
302}
303
304/* Make suppress Go-Ahead telnet option. */
305static void
306vty_will_suppress_go_ahead (struct vty *vty)
307{
paul02ff83c2004-06-11 11:27:03 +0000308 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
paul718e3742002-12-13 20:15:29 +0000309 vty_out (vty, "%s", cmd);
310}
311
312/* Make don't use linemode over telnet. */
313static void
314vty_dont_linemode (struct vty *vty)
315{
paul02ff83c2004-06-11 11:27:03 +0000316 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
paul718e3742002-12-13 20:15:29 +0000317 vty_out (vty, "%s", cmd);
318}
319
320/* Use window size. */
321static void
322vty_do_window_size (struct vty *vty)
323{
paul02ff83c2004-06-11 11:27:03 +0000324 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
paul718e3742002-12-13 20:15:29 +0000325 vty_out (vty, "%s", cmd);
326}
327
328#if 0 /* Currently not used. */
329/* Make don't use lflow vty interface. */
330static void
331vty_dont_lflow_ahead (struct vty *vty)
332{
paul02ff83c2004-06-11 11:27:03 +0000333 unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
paul718e3742002-12-13 20:15:29 +0000334 vty_out (vty, "%s", cmd);
335}
336#endif /* 0 */
337
338/* Allocate new vty struct. */
339struct vty *
340vty_new ()
341{
342 struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
343
ajs9fc7ebf2005-02-23 15:12:34 +0000344 new->obuf = buffer_new(0); /* Use default buffer size. */
paul718e3742002-12-13 20:15:29 +0000345 new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
346 new->max = VTY_BUFSIZ;
paul718e3742002-12-13 20:15:29 +0000347
348 return new;
349}
350
351/* Authentication of vty */
352static void
353vty_auth (struct vty *vty, char *buf)
354{
355 char *passwd = NULL;
356 enum node_type next_node = 0;
357 int fail;
358 char *crypt (const char *, const char *);
359
360 switch (vty->node)
361 {
362 case AUTH_NODE:
363 if (host.encrypt)
364 passwd = host.password_encrypt;
365 else
366 passwd = host.password;
367 if (host.advanced)
368 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
369 else
370 next_node = VIEW_NODE;
371 break;
372 case AUTH_ENABLE_NODE:
373 if (host.encrypt)
374 passwd = host.enable_encrypt;
375 else
376 passwd = host.enable;
377 next_node = ENABLE_NODE;
378 break;
379 }
380
381 if (passwd)
382 {
383 if (host.encrypt)
384 fail = strcmp (crypt(buf, passwd), passwd);
385 else
386 fail = strcmp (buf, passwd);
387 }
388 else
389 fail = 1;
390
391 if (! fail)
392 {
393 vty->fail = 0;
394 vty->node = next_node; /* Success ! */
395 }
396 else
397 {
398 vty->fail++;
399 if (vty->fail >= 3)
400 {
401 if (vty->node == AUTH_NODE)
402 {
403 vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
404 vty->status = VTY_CLOSE;
405 }
406 else
407 {
408 /* AUTH_ENABLE_NODE */
409 vty->fail = 0;
410 vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
Paul Jakma62687ff2008-08-23 14:27:06 +0100411 vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
paul718e3742002-12-13 20:15:29 +0000412 }
413 }
414 }
415}
416
417/* Command execution over the vty interface. */
ajs9fc7ebf2005-02-23 15:12:34 +0000418static int
paul718e3742002-12-13 20:15:29 +0000419vty_command (struct vty *vty, char *buf)
420{
421 int ret;
422 vector vline;
vincentfbf5d032005-09-29 11:25:50 +0000423 const char *protocolname;
Lou Berger86b2a0a2016-05-17 12:19:51 -0400424 char *cp = NULL;
paul718e3742002-12-13 20:15:29 +0000425
Lou Bergerc7f7e492016-01-12 13:41:49 -0500426 /*
427 * Log non empty command lines
428 */
Lou Berger86b2a0a2016-05-17 12:19:51 -0400429 if (do_log_commands)
430 cp = buf;
Lou Bergerc7f7e492016-01-12 13:41:49 -0500431 if (cp != NULL)
432 {
433 /* Skip white spaces. */
434 while (isspace ((int) *cp) && *cp != '\0')
435 cp++;
436 }
437 if (cp != NULL && *cp != '\0')
438 {
439 unsigned i;
440 char vty_str[VTY_BUFSIZ];
441 char prompt_str[VTY_BUFSIZ];
442
443 /* format the base vty info */
444 snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
445 if (vty)
446 for (i = 0; i < vector_active (vtyvec); i++)
Donald Sharp811577e2016-03-10 20:16:48 -0500447 if (vty == vector_slot (vtyvec, i))
Lou Bergerc7f7e492016-01-12 13:41:49 -0500448 {
449 snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s",
450 i, vty->address);
451 break;
452 }
453
454 /* format the prompt */
455 snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str);
456
457 /* now log the command */
Lou Berger86b2a0a2016-05-17 12:19:51 -0400458 zlog(NULL, LOG_ERR, "%s%s", prompt_str, buf);
Lou Bergerc7f7e492016-01-12 13:41:49 -0500459 }
paul718e3742002-12-13 20:15:29 +0000460 /* Split readline string up into the vector */
461 vline = cmd_make_strvec (buf);
462
463 if (vline == NULL)
464 return CMD_SUCCESS;
465
ajs924b9222005-04-16 17:11:24 +0000466#ifdef CONSUMED_TIME_CHECK
467 {
468 RUSAGE_T before;
469 RUSAGE_T after;
ajs8b70d0b2005-04-28 01:31:13 +0000470 unsigned long realtime, cputime;
ajs924b9222005-04-16 17:11:24 +0000471
472 GETRUSAGE(&before);
473#endif /* CONSUMED_TIME_CHECK */
474
hasso87d683b2005-01-16 23:31:54 +0000475 ret = cmd_execute_command (vline, vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +0000476
vincentfbf5d032005-09-29 11:25:50 +0000477 /* Get the name of the protocol if any */
478 if (zlog_default)
479 protocolname = zlog_proto_names[zlog_default->protocol];
480 else
481 protocolname = zlog_proto_names[ZLOG_NONE];
482
ajs924b9222005-04-16 17:11:24 +0000483#ifdef CONSUMED_TIME_CHECK
484 GETRUSAGE(&after);
ajs8b70d0b2005-04-28 01:31:13 +0000485 if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
486 CONSUMED_TIME_CHECK)
ajs924b9222005-04-16 17:11:24 +0000487 /* Warn about CPU hog that must be fixed. */
ajs8b70d0b2005-04-28 01:31:13 +0000488 zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
489 realtime/1000, cputime/1000, buf);
ajs924b9222005-04-16 17:11:24 +0000490 }
491#endif /* CONSUMED_TIME_CHECK */
492
paul718e3742002-12-13 20:15:29 +0000493 if (ret != CMD_SUCCESS)
494 switch (ret)
495 {
496 case CMD_WARNING:
497 if (vty->type == VTY_FILE)
498 vty_out (vty, "Warning...%s", VTY_NEWLINE);
499 break;
500 case CMD_ERR_AMBIGUOUS:
501 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
502 break;
503 case CMD_ERR_NO_MATCH:
vincentfbf5d032005-09-29 11:25:50 +0000504 vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000505 break;
506 case CMD_ERR_INCOMPLETE:
507 vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
508 break;
509 }
510 cmd_free_strvec (vline);
511
512 return ret;
513}
David Lamparter6b0655a2014-06-04 06:53:35 +0200514
ajs9fc7ebf2005-02-23 15:12:34 +0000515static const char telnet_backward_char = 0x08;
516static const char telnet_space_char = ' ';
paul718e3742002-12-13 20:15:29 +0000517
518/* Basic function to write buffer to vty. */
519static void
ajs9fc7ebf2005-02-23 15:12:34 +0000520vty_write (struct vty *vty, const char *buf, size_t nbytes)
paul718e3742002-12-13 20:15:29 +0000521{
522 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
523 return;
524
525 /* Should we do buffering here ? And make vty_flush (vty) ? */
ajs9fc7ebf2005-02-23 15:12:34 +0000526 buffer_put (vty->obuf, buf, nbytes);
paul718e3742002-12-13 20:15:29 +0000527}
528
paul718e3742002-12-13 20:15:29 +0000529/* Basic function to insert character into vty. */
530static void
531vty_self_insert (struct vty *vty, char c)
532{
533 int i;
534 int length;
Paul Jakma7d662842017-01-19 10:39:23 +0000535
536 vty_buf_assert (vty);
537
538 /* length is sans nul, max is with */
539 if (vty->length + 1 >= vty->max)
Quentin Youngb7ceefe2017-01-10 23:33:50 +0000540 return;
541
paul718e3742002-12-13 20:15:29 +0000542 length = vty->length - vty->cp;
543 memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
Paul Jakma7d662842017-01-19 10:39:23 +0000544 vty->length++;
545 vty->buf[vty->length] = '\0';
546
547 vty_buf_put (vty, c);
paul718e3742002-12-13 20:15:29 +0000548
549 vty_write (vty, &vty->buf[vty->cp], length + 1);
550 for (i = 0; i < length; i++)
551 vty_write (vty, &telnet_backward_char, 1);
552
553 vty->cp++;
Paul Jakma7d662842017-01-19 10:39:23 +0000554
555 vty_buf_assert (vty);
paul718e3742002-12-13 20:15:29 +0000556}
557
558/* Self insert character 'c' in overwrite mode. */
559static void
560vty_self_insert_overwrite (struct vty *vty, char c)
561{
Paul Jakma7d662842017-01-19 10:39:23 +0000562 vty_buf_assert (vty);
563
Quentin Youngb7ceefe2017-01-10 23:33:50 +0000564 if (vty->cp == vty->length)
565 {
566 vty_self_insert (vty, c);
567 return;
568 }
569
Paul Jakma7d662842017-01-19 10:39:23 +0000570 vty_buf_put (vty, c);
571 vty->cp++;
572
573 vty_buf_assert (vty);
574
paul718e3742002-12-13 20:15:29 +0000575 vty_write (vty, &c, 1);
576}
577
Quentin Youngb7ceefe2017-01-10 23:33:50 +0000578/**
579 * Insert a string into vty->buf at the current cursor position.
580 *
581 * If the resultant string would be larger than VTY_BUFSIZ it is
582 * truncated to fit.
583 */
paul718e3742002-12-13 20:15:29 +0000584static void
585vty_insert_word_overwrite (struct vty *vty, char *str)
586{
Paul Jakma7d662842017-01-19 10:39:23 +0000587 vty_buf_assert (vty);
588
589 size_t nwrite = MIN ((int) strlen (str), vty->max - vty->cp - 1);
590 memcpy (&vty->buf[vty->cp], str, nwrite);
Quentin Youngb7ceefe2017-01-10 23:33:50 +0000591 vty->cp += nwrite;
paul718e3742002-12-13 20:15:29 +0000592 vty->length = vty->cp;
Paul Jakma7d662842017-01-19 10:39:23 +0000593 vty->buf[vty->length] = '\0';
594 vty_buf_assert (vty);
595
596 vty_write (vty, str, nwrite);
paul718e3742002-12-13 20:15:29 +0000597}
598
599/* Forward character. */
600static void
601vty_forward_char (struct vty *vty)
602{
Paul Jakma7d662842017-01-19 10:39:23 +0000603 vty_buf_assert (vty);
604
paul718e3742002-12-13 20:15:29 +0000605 if (vty->cp < vty->length)
606 {
607 vty_write (vty, &vty->buf[vty->cp], 1);
608 vty->cp++;
609 }
Paul Jakma7d662842017-01-19 10:39:23 +0000610
611 vty_buf_assert (vty);
paul718e3742002-12-13 20:15:29 +0000612}
613
614/* Backward character. */
615static void
616vty_backward_char (struct vty *vty)
617{
Paul Jakma7d662842017-01-19 10:39:23 +0000618 vty_buf_assert (vty);
619
paul718e3742002-12-13 20:15:29 +0000620 if (vty->cp > 0)
621 {
622 vty->cp--;
623 vty_write (vty, &telnet_backward_char, 1);
624 }
Paul Jakma7d662842017-01-19 10:39:23 +0000625
626 vty_buf_assert (vty);
paul718e3742002-12-13 20:15:29 +0000627}
628
629/* Move to the beginning of the line. */
630static void
631vty_beginning_of_line (struct vty *vty)
632{
633 while (vty->cp)
634 vty_backward_char (vty);
635}
636
637/* Move to the end of the line. */
638static void
639vty_end_of_line (struct vty *vty)
640{
641 while (vty->cp < vty->length)
642 vty_forward_char (vty);
643}
644
645static void vty_kill_line_from_beginning (struct vty *);
646static void vty_redraw_line (struct vty *);
647
648/* Print command line history. This function is called from
649 vty_next_line and vty_previous_line. */
650static void
651vty_history_print (struct vty *vty)
652{
653 int length;
654
655 vty_kill_line_from_beginning (vty);
656
657 /* Get previous line from history buffer */
658 length = strlen (vty->hist[vty->hp]);
659 memcpy (vty->buf, vty->hist[vty->hp], length);
660 vty->cp = vty->length = length;
Paul Jakma7d662842017-01-19 10:39:23 +0000661 vty->buf[vty->length] = '\0';
662 vty_buf_assert (vty);
663
paul718e3742002-12-13 20:15:29 +0000664 /* Redraw current line */
665 vty_redraw_line (vty);
666}
667
668/* Show next command line history. */
ajs9fc7ebf2005-02-23 15:12:34 +0000669static void
paul718e3742002-12-13 20:15:29 +0000670vty_next_line (struct vty *vty)
671{
672 int try_index;
673
674 if (vty->hp == vty->hindex)
675 return;
676
677 /* Try is there history exist or not. */
678 try_index = vty->hp;
679 if (try_index == (VTY_MAXHIST - 1))
680 try_index = 0;
681 else
682 try_index++;
683
684 /* If there is not history return. */
685 if (vty->hist[try_index] == NULL)
686 return;
687 else
688 vty->hp = try_index;
689
690 vty_history_print (vty);
691}
692
693/* Show previous command line history. */
ajs9fc7ebf2005-02-23 15:12:34 +0000694static void
paul718e3742002-12-13 20:15:29 +0000695vty_previous_line (struct vty *vty)
696{
697 int try_index;
698
699 try_index = vty->hp;
700 if (try_index == 0)
701 try_index = VTY_MAXHIST - 1;
702 else
703 try_index--;
704
705 if (vty->hist[try_index] == NULL)
706 return;
707 else
708 vty->hp = try_index;
709
710 vty_history_print (vty);
711}
712
713/* This function redraw all of the command line character. */
714static void
715vty_redraw_line (struct vty *vty)
716{
717 vty_write (vty, vty->buf, vty->length);
718 vty->cp = vty->length;
Paul Jakma7d662842017-01-19 10:39:23 +0000719
720 vty_buf_assert (vty);
paul718e3742002-12-13 20:15:29 +0000721}
722
723/* Forward word. */
724static void
725vty_forward_word (struct vty *vty)
726{
727 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
728 vty_forward_char (vty);
729
730 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
731 vty_forward_char (vty);
732}
733
734/* Backward word without skipping training space. */
735static void
736vty_backward_pure_word (struct vty *vty)
737{
738 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
739 vty_backward_char (vty);
740}
741
742/* Backward word. */
743static void
744vty_backward_word (struct vty *vty)
745{
746 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
747 vty_backward_char (vty);
748
749 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
750 vty_backward_char (vty);
751}
752
753/* When '^D' is typed at the beginning of the line we move to the down
754 level. */
755static void
756vty_down_level (struct vty *vty)
757{
758 vty_out (vty, "%s", VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +0000759 (*config_exit_cmd.func)(NULL, vty, 0, NULL);
paul718e3742002-12-13 20:15:29 +0000760 vty_prompt (vty);
761 vty->cp = 0;
762}
763
764/* When '^Z' is received from vty, move down to the enable mode. */
ajs9fc7ebf2005-02-23 15:12:34 +0000765static void
paul718e3742002-12-13 20:15:29 +0000766vty_end_config (struct vty *vty)
767{
768 vty_out (vty, "%s", VTY_NEWLINE);
769
770 switch (vty->node)
771 {
772 case VIEW_NODE:
773 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +0100774 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +0000775 /* Nothing to do. */
776 break;
777 case CONFIG_NODE:
778 case INTERFACE_NODE:
779 case ZEBRA_NODE:
780 case RIP_NODE:
781 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +0100782 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +0000783 case BGP_NODE:
784 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -0500785 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -0500786 case BGP_ENCAP_NODE:
787 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +0000788 case BGP_IPV4_NODE:
789 case BGP_IPV4M_NODE:
790 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +0000791 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +0000792 case RMAP_NODE:
793 case OSPF_NODE:
794 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +0000795 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +0000796 case KEYCHAIN_NODE:
797 case KEYCHAIN_KEY_NODE:
798 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -0200799 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +0000800 case VTY_NODE:
801 vty_config_unlock (vty);
802 vty->node = ENABLE_NODE;
803 break;
804 default:
805 /* Unknown node, we have to ignore it. */
806 break;
807 }
808
809 vty_prompt (vty);
810 vty->cp = 0;
811}
812
813/* Delete a charcter at the current point. */
814static void
815vty_delete_char (struct vty *vty)
816{
817 int i;
818 int size;
819
paul718e3742002-12-13 20:15:29 +0000820 if (vty->length == 0)
821 {
822 vty_down_level (vty);
823 return;
824 }
Paul Jakma7d662842017-01-19 10:39:23 +0000825
paul718e3742002-12-13 20:15:29 +0000826 if (vty->cp == vty->length)
827 return; /* completion need here? */
828
Paul Jakma7d662842017-01-19 10:39:23 +0000829 vty_buf_assert (vty);
830
paul718e3742002-12-13 20:15:29 +0000831 size = vty->length - vty->cp;
832
833 vty->length--;
834 memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
835 vty->buf[vty->length] = '\0';
Roy7f794f22008-08-13 17:27:38 +0100836
837 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
838 return;
paul718e3742002-12-13 20:15:29 +0000839
840 vty_write (vty, &vty->buf[vty->cp], size - 1);
841 vty_write (vty, &telnet_space_char, 1);
842
843 for (i = 0; i < size; i++)
844 vty_write (vty, &telnet_backward_char, 1);
845}
846
847/* Delete a character before the point. */
848static void
849vty_delete_backward_char (struct vty *vty)
850{
851 if (vty->cp == 0)
852 return;
853
854 vty_backward_char (vty);
855 vty_delete_char (vty);
856}
857
858/* Kill rest of line from current point. */
859static void
860vty_kill_line (struct vty *vty)
861{
862 int i;
863 int size;
864
865 size = vty->length - vty->cp;
866
867 if (size == 0)
868 return;
869
870 for (i = 0; i < size; i++)
871 vty_write (vty, &telnet_space_char, 1);
872 for (i = 0; i < size; i++)
873 vty_write (vty, &telnet_backward_char, 1);
874
875 memset (&vty->buf[vty->cp], 0, size);
876 vty->length = vty->cp;
Paul Jakma7d662842017-01-19 10:39:23 +0000877 vty_buf_assert (vty);
paul718e3742002-12-13 20:15:29 +0000878}
879
880/* Kill line from the beginning. */
881static void
882vty_kill_line_from_beginning (struct vty *vty)
883{
884 vty_beginning_of_line (vty);
885 vty_kill_line (vty);
886}
887
888/* Delete a word before the point. */
889static void
890vty_forward_kill_word (struct vty *vty)
891{
892 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
893 vty_delete_char (vty);
894 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
895 vty_delete_char (vty);
896}
897
898/* Delete a word before the point. */
899static void
900vty_backward_kill_word (struct vty *vty)
901{
902 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
903 vty_delete_backward_char (vty);
904 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
905 vty_delete_backward_char (vty);
906}
907
908/* Transpose chars before or at the point. */
909static void
910vty_transpose_chars (struct vty *vty)
911{
912 char c1, c2;
913
914 /* If length is short or point is near by the beginning of line then
915 return. */
916 if (vty->length < 2 || vty->cp < 1)
917 return;
918
919 /* In case of point is located at the end of the line. */
920 if (vty->cp == vty->length)
921 {
922 c1 = vty->buf[vty->cp - 1];
923 c2 = vty->buf[vty->cp - 2];
924
925 vty_backward_char (vty);
926 vty_backward_char (vty);
927 vty_self_insert_overwrite (vty, c1);
928 vty_self_insert_overwrite (vty, c2);
929 }
930 else
931 {
932 c1 = vty->buf[vty->cp];
933 c2 = vty->buf[vty->cp - 1];
934
935 vty_backward_char (vty);
936 vty_self_insert_overwrite (vty, c1);
937 vty_self_insert_overwrite (vty, c2);
938 }
939}
940
941/* Do completion at vty interface. */
942static void
943vty_complete_command (struct vty *vty)
944{
945 int i;
946 int ret;
947 char **matched = NULL;
948 vector vline;
949
950 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
951 return;
952
953 vline = cmd_make_strvec (vty->buf);
954 if (vline == NULL)
955 return;
956
957 /* In case of 'help \t'. */
958 if (isspace ((int) vty->buf[vty->length - 1]))
David Lampartera91a3ba2015-03-03 09:06:51 +0100959 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +0000960
Lou Berger67290032016-01-12 13:41:46 -0500961 matched = cmd_complete_command_lib (vline, vty, &ret, 1);
paul718e3742002-12-13 20:15:29 +0000962
963 cmd_free_strvec (vline);
964
965 vty_out (vty, "%s", VTY_NEWLINE);
966 switch (ret)
967 {
968 case CMD_ERR_AMBIGUOUS:
969 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
970 vty_prompt (vty);
971 vty_redraw_line (vty);
972 break;
973 case CMD_ERR_NO_MATCH:
974 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
975 vty_prompt (vty);
976 vty_redraw_line (vty);
977 break;
978 case CMD_COMPLETE_FULL_MATCH:
979 vty_prompt (vty);
980 vty_redraw_line (vty);
981 vty_backward_pure_word (vty);
982 vty_insert_word_overwrite (vty, matched[0]);
983 vty_self_insert (vty, ' ');
984 XFREE (MTYPE_TMP, matched[0]);
985 break;
986 case CMD_COMPLETE_MATCH:
987 vty_prompt (vty);
988 vty_redraw_line (vty);
989 vty_backward_pure_word (vty);
990 vty_insert_word_overwrite (vty, matched[0]);
991 XFREE (MTYPE_TMP, matched[0]);
992 vector_only_index_free (matched);
993 return;
994 break;
995 case CMD_COMPLETE_LIST_MATCH:
996 for (i = 0; matched[i] != NULL; i++)
997 {
998 if (i != 0 && ((i % 6) == 0))
999 vty_out (vty, "%s", VTY_NEWLINE);
1000 vty_out (vty, "%-10s ", matched[i]);
1001 XFREE (MTYPE_TMP, matched[i]);
1002 }
1003 vty_out (vty, "%s", VTY_NEWLINE);
1004
1005 vty_prompt (vty);
1006 vty_redraw_line (vty);
1007 break;
1008 case CMD_ERR_NOTHING_TODO:
1009 vty_prompt (vty);
1010 vty_redraw_line (vty);
1011 break;
1012 default:
1013 break;
1014 }
1015 if (matched)
1016 vector_only_index_free (matched);
1017}
1018
ajs9fc7ebf2005-02-23 15:12:34 +00001019static void
paul718e3742002-12-13 20:15:29 +00001020vty_describe_fold (struct vty *vty, int cmd_width,
Christian Frankecd40b322013-09-30 12:27:51 +00001021 unsigned int desc_width, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +00001022{
hasso8c328f12004-10-05 21:01:23 +00001023 char *buf;
1024 const char *cmd, *p;
paul718e3742002-12-13 20:15:29 +00001025 int pos;
1026
Christian Frankecd40b322013-09-30 12:27:51 +00001027 cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
paul718e3742002-12-13 20:15:29 +00001028
1029 if (desc_width <= 0)
1030 {
Christian Frankecd40b322013-09-30 12:27:51 +00001031 vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001032 return;
1033 }
1034
Christian Frankecd40b322013-09-30 12:27:51 +00001035 buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
paul718e3742002-12-13 20:15:29 +00001036
Christian Frankecd40b322013-09-30 12:27:51 +00001037 for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
paul718e3742002-12-13 20:15:29 +00001038 {
1039 for (pos = desc_width; pos > 0; pos--)
1040 if (*(p + pos) == ' ')
1041 break;
1042
1043 if (pos == 0)
1044 break;
1045
1046 strncpy (buf, p, pos);
1047 buf[pos] = '\0';
1048 vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1049
1050 cmd = "";
1051 }
1052
1053 vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1054
1055 XFREE (MTYPE_TMP, buf);
1056}
1057
1058/* Describe matched command function. */
1059static void
1060vty_describe_command (struct vty *vty)
1061{
1062 int ret;
1063 vector vline;
1064 vector describe;
hasso8c328f12004-10-05 21:01:23 +00001065 unsigned int i, width, desc_width;
Christian Frankecd40b322013-09-30 12:27:51 +00001066 struct cmd_token *token, *token_cr = NULL;
paul718e3742002-12-13 20:15:29 +00001067
1068 vline = cmd_make_strvec (vty->buf);
1069
1070 /* In case of '> ?'. */
1071 if (vline == NULL)
1072 {
1073 vline = vector_init (1);
David Lampartera91a3ba2015-03-03 09:06:51 +01001074 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +00001075 }
1076 else
1077 if (isspace ((int) vty->buf[vty->length - 1]))
David Lampartera91a3ba2015-03-03 09:06:51 +01001078 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +00001079
1080 describe = cmd_describe_command (vline, vty, &ret);
1081
1082 vty_out (vty, "%s", VTY_NEWLINE);
1083
1084 /* Ambiguous error. */
1085 switch (ret)
1086 {
1087 case CMD_ERR_AMBIGUOUS:
paul718e3742002-12-13 20:15:29 +00001088 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001089 goto out;
paul718e3742002-12-13 20:15:29 +00001090 break;
1091 case CMD_ERR_NO_MATCH:
paul718e3742002-12-13 20:15:29 +00001092 vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001093 goto out;
paul718e3742002-12-13 20:15:29 +00001094 break;
1095 }
1096
1097 /* Get width of command string. */
1098 width = 0;
paul55468c82005-03-14 20:19:01 +00001099 for (i = 0; i < vector_active (describe); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00001100 if ((token = vector_slot (describe, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001101 {
hasso8c328f12004-10-05 21:01:23 +00001102 unsigned int len;
paul718e3742002-12-13 20:15:29 +00001103
Christian Frankecd40b322013-09-30 12:27:51 +00001104 if (token->cmd[0] == '\0')
paul718e3742002-12-13 20:15:29 +00001105 continue;
1106
Christian Frankecd40b322013-09-30 12:27:51 +00001107 len = strlen (token->cmd);
1108 if (token->cmd[0] == '.')
paul718e3742002-12-13 20:15:29 +00001109 len--;
1110
1111 if (width < len)
1112 width = len;
1113 }
1114
1115 /* Get width of description string. */
1116 desc_width = vty->width - (width + 6);
1117
1118 /* Print out description. */
paul55468c82005-03-14 20:19:01 +00001119 for (i = 0; i < vector_active (describe); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00001120 if ((token = vector_slot (describe, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001121 {
Christian Frankecd40b322013-09-30 12:27:51 +00001122 if (token->cmd[0] == '\0')
paul718e3742002-12-13 20:15:29 +00001123 continue;
1124
Christian Frankecd40b322013-09-30 12:27:51 +00001125 if (strcmp (token->cmd, command_cr) == 0)
paul718e3742002-12-13 20:15:29 +00001126 {
Christian Frankecd40b322013-09-30 12:27:51 +00001127 token_cr = token;
paul718e3742002-12-13 20:15:29 +00001128 continue;
1129 }
1130
Christian Frankecd40b322013-09-30 12:27:51 +00001131 if (!token->desc)
paul718e3742002-12-13 20:15:29 +00001132 vty_out (vty, " %-s%s",
Christian Frankecd40b322013-09-30 12:27:51 +00001133 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
paul718e3742002-12-13 20:15:29 +00001134 VTY_NEWLINE);
Christian Frankecd40b322013-09-30 12:27:51 +00001135 else if (desc_width >= strlen (token->desc))
paul718e3742002-12-13 20:15:29 +00001136 vty_out (vty, " %-*s %s%s", width,
Christian Frankecd40b322013-09-30 12:27:51 +00001137 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1138 token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001139 else
Christian Frankecd40b322013-09-30 12:27:51 +00001140 vty_describe_fold (vty, width, desc_width, token);
paul718e3742002-12-13 20:15:29 +00001141
1142#if 0
1143 vty_out (vty, " %-*s %s%s", width
1144 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1145 desc->str ? desc->str : "", VTY_NEWLINE);
1146#endif /* 0 */
1147 }
1148
Christian Frankecd40b322013-09-30 12:27:51 +00001149 if ((token = token_cr))
paul718e3742002-12-13 20:15:29 +00001150 {
Christian Frankecd40b322013-09-30 12:27:51 +00001151 if (!token->desc)
paul718e3742002-12-13 20:15:29 +00001152 vty_out (vty, " %-s%s",
Christian Frankecd40b322013-09-30 12:27:51 +00001153 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
paul718e3742002-12-13 20:15:29 +00001154 VTY_NEWLINE);
Christian Frankecd40b322013-09-30 12:27:51 +00001155 else if (desc_width >= strlen (token->desc))
paul718e3742002-12-13 20:15:29 +00001156 vty_out (vty, " %-*s %s%s", width,
Christian Frankecd40b322013-09-30 12:27:51 +00001157 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1158 token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001159 else
Christian Frankecd40b322013-09-30 12:27:51 +00001160 vty_describe_fold (vty, width, desc_width, token);
paul718e3742002-12-13 20:15:29 +00001161 }
1162
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001163out:
paul718e3742002-12-13 20:15:29 +00001164 cmd_free_strvec (vline);
Paul Jakmad16e0432006-05-15 10:56:46 +00001165 if (describe)
1166 vector_free (describe);
paul718e3742002-12-13 20:15:29 +00001167
1168 vty_prompt (vty);
1169 vty_redraw_line (vty);
1170}
1171
ajs9fc7ebf2005-02-23 15:12:34 +00001172static void
paul718e3742002-12-13 20:15:29 +00001173vty_clear_buf (struct vty *vty)
1174{
1175 memset (vty->buf, 0, vty->max);
1176}
1177
1178/* ^C stop current input and do not add command line to the history. */
1179static void
1180vty_stop_input (struct vty *vty)
1181{
1182 vty->cp = vty->length = 0;
1183 vty_clear_buf (vty);
1184 vty_out (vty, "%s", VTY_NEWLINE);
1185
1186 switch (vty->node)
1187 {
1188 case VIEW_NODE:
1189 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01001190 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00001191 /* Nothing to do. */
1192 break;
1193 case CONFIG_NODE:
1194 case INTERFACE_NODE:
1195 case ZEBRA_NODE:
1196 case RIP_NODE:
1197 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01001198 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00001199 case BGP_NODE:
1200 case RMAP_NODE:
1201 case OSPF_NODE:
1202 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00001203 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00001204 case KEYCHAIN_NODE:
1205 case KEYCHAIN_KEY_NODE:
1206 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02001207 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00001208 case VTY_NODE:
1209 vty_config_unlock (vty);
1210 vty->node = ENABLE_NODE;
1211 break;
1212 default:
1213 /* Unknown node, we have to ignore it. */
1214 break;
1215 }
1216 vty_prompt (vty);
1217
1218 /* Set history pointer to the latest one. */
1219 vty->hp = vty->hindex;
1220}
1221
1222/* Add current command line to the history buffer. */
1223static void
1224vty_hist_add (struct vty *vty)
1225{
1226 int index;
1227
1228 if (vty->length == 0)
1229 return;
1230
1231 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1232
1233 /* Ignore the same string as previous one. */
1234 if (vty->hist[index])
1235 if (strcmp (vty->buf, vty->hist[index]) == 0)
1236 {
1237 vty->hp = vty->hindex;
1238 return;
1239 }
1240
1241 /* Insert history entry. */
1242 if (vty->hist[vty->hindex])
1243 XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1244 vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
1245
1246 /* History index rotation. */
1247 vty->hindex++;
1248 if (vty->hindex == VTY_MAXHIST)
1249 vty->hindex = 0;
1250
1251 vty->hp = vty->hindex;
1252}
1253
1254/* #define TELNET_OPTION_DEBUG */
1255
1256/* Get telnet window size. */
1257static int
1258vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
1259{
1260#ifdef TELNET_OPTION_DEBUG
1261 int i;
1262
1263 for (i = 0; i < nbytes; i++)
1264 {
1265 switch (buf[i])
1266 {
1267 case IAC:
1268 vty_out (vty, "IAC ");
1269 break;
1270 case WILL:
1271 vty_out (vty, "WILL ");
1272 break;
1273 case WONT:
1274 vty_out (vty, "WONT ");
1275 break;
1276 case DO:
1277 vty_out (vty, "DO ");
1278 break;
1279 case DONT:
1280 vty_out (vty, "DONT ");
1281 break;
1282 case SB:
1283 vty_out (vty, "SB ");
1284 break;
1285 case SE:
1286 vty_out (vty, "SE ");
1287 break;
1288 case TELOPT_ECHO:
1289 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
1290 break;
1291 case TELOPT_SGA:
1292 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
1293 break;
1294 case TELOPT_NAWS:
1295 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
1296 break;
1297 default:
1298 vty_out (vty, "%x ", buf[i]);
1299 break;
1300 }
1301 }
1302 vty_out (vty, "%s", VTY_NEWLINE);
1303
1304#endif /* TELNET_OPTION_DEBUG */
1305
1306 switch (buf[0])
1307 {
1308 case SB:
ajs9fc7ebf2005-02-23 15:12:34 +00001309 vty->sb_len = 0;
paul718e3742002-12-13 20:15:29 +00001310 vty->iac_sb_in_progress = 1;
1311 return 0;
1312 break;
1313 case SE:
1314 {
paul718e3742002-12-13 20:15:29 +00001315 if (!vty->iac_sb_in_progress)
1316 return 0;
1317
ajs9fc7ebf2005-02-23 15:12:34 +00001318 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
paul718e3742002-12-13 20:15:29 +00001319 {
1320 vty->iac_sb_in_progress = 0;
1321 return 0;
1322 }
ajs9fc7ebf2005-02-23 15:12:34 +00001323 switch (vty->sb_buf[0])
paul718e3742002-12-13 20:15:29 +00001324 {
1325 case TELOPT_NAWS:
ajs9fc7ebf2005-02-23 15:12:34 +00001326 if (vty->sb_len != TELNET_NAWS_SB_LEN)
1327 zlog_warn("RFC 1073 violation detected: telnet NAWS option "
1328 "should send %d characters, but we received %lu",
1329 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
1330 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1331 zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
1332 "too small to handle the telnet NAWS option",
1333 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
1334 else
1335 {
1336 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
1337 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
1338#ifdef TELNET_OPTION_DEBUG
1339 vty_out(vty, "TELNET NAWS window size negotiation completed: "
1340 "width %d, height %d%s",
1341 vty->width, vty->height, VTY_NEWLINE);
1342#endif
1343 }
paul718e3742002-12-13 20:15:29 +00001344 break;
1345 }
1346 vty->iac_sb_in_progress = 0;
1347 return 0;
1348 break;
1349 }
1350 default:
1351 break;
1352 }
1353 return 1;
1354}
1355
1356/* Execute current command line. */
1357static int
1358vty_execute (struct vty *vty)
1359{
1360 int ret;
1361
1362 ret = CMD_SUCCESS;
1363
1364 switch (vty->node)
1365 {
1366 case AUTH_NODE:
1367 case AUTH_ENABLE_NODE:
1368 vty_auth (vty, vty->buf);
1369 break;
1370 default:
1371 ret = vty_command (vty, vty->buf);
1372 if (vty->type == VTY_TERM)
1373 vty_hist_add (vty);
1374 break;
1375 }
1376
1377 /* Clear command line buffer. */
1378 vty->cp = vty->length = 0;
1379 vty_clear_buf (vty);
1380
ajs5a646652004-11-05 01:25:55 +00001381 if (vty->status != VTY_CLOSE )
paul718e3742002-12-13 20:15:29 +00001382 vty_prompt (vty);
1383
1384 return ret;
1385}
1386
1387#define CONTROL(X) ((X) - '@')
1388#define VTY_NORMAL 0
Paul Jakma126b0e72016-11-18 14:40:41 +00001389#define VTY_PRE_ESCAPE 1 /* Esc seen */
1390#define VTY_ESCAPE 2 /* ANSI terminal escape (Esc-[) seen */
1391#define VTY_LITERAL 3 /* Next char taken as literal */
paul718e3742002-12-13 20:15:29 +00001392
1393/* Escape character command map. */
1394static void
1395vty_escape_map (unsigned char c, struct vty *vty)
1396{
1397 switch (c)
1398 {
1399 case ('A'):
1400 vty_previous_line (vty);
1401 break;
1402 case ('B'):
1403 vty_next_line (vty);
1404 break;
1405 case ('C'):
1406 vty_forward_char (vty);
1407 break;
1408 case ('D'):
1409 vty_backward_char (vty);
1410 break;
1411 default:
1412 break;
1413 }
1414
1415 /* Go back to normal mode. */
1416 vty->escape = VTY_NORMAL;
1417}
1418
1419/* Quit print out to the buffer. */
1420static void
1421vty_buffer_reset (struct vty *vty)
1422{
1423 buffer_reset (vty->obuf);
1424 vty_prompt (vty);
1425 vty_redraw_line (vty);
1426}
1427
1428/* Read data via vty socket. */
1429static int
1430vty_read (struct thread *thread)
1431{
1432 int i;
paul718e3742002-12-13 20:15:29 +00001433 int nbytes;
1434 unsigned char buf[VTY_READ_BUFSIZ];
1435
1436 int vty_sock = THREAD_FD (thread);
1437 struct vty *vty = THREAD_ARG (thread);
1438 vty->t_read = NULL;
1439
1440 /* Read raw data from socket */
ajs9fc7ebf2005-02-23 15:12:34 +00001441 if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
1442 {
1443 if (nbytes < 0)
1444 {
1445 if (ERRNO_IO_RETRY(errno))
1446 {
1447 vty_event (VTY_READ, vty_sock, vty);
1448 return 0;
1449 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001450 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001451 zlog_warn("%s: read error on vty client fd %d, closing: %s",
1452 __func__, vty->fd, safe_strerror(errno));
David Lamparter90d31352015-05-14 14:24:06 +02001453 buffer_reset(vty->obuf);
ajs9fc7ebf2005-02-23 15:12:34 +00001454 }
ajs9fc7ebf2005-02-23 15:12:34 +00001455 vty->status = VTY_CLOSE;
1456 }
paul718e3742002-12-13 20:15:29 +00001457
1458 for (i = 0; i < nbytes; i++)
1459 {
1460 if (buf[i] == IAC)
1461 {
1462 if (!vty->iac)
1463 {
1464 vty->iac = 1;
1465 continue;
1466 }
1467 else
1468 {
1469 vty->iac = 0;
1470 }
1471 }
1472
1473 if (vty->iac_sb_in_progress && !vty->iac)
1474 {
ajs9fc7ebf2005-02-23 15:12:34 +00001475 if (vty->sb_len < sizeof(vty->sb_buf))
1476 vty->sb_buf[vty->sb_len] = buf[i];
1477 vty->sb_len++;
paul718e3742002-12-13 20:15:29 +00001478 continue;
1479 }
1480
1481 if (vty->iac)
1482 {
1483 /* In case of telnet command */
paul5b8c1b02003-10-15 23:08:55 +00001484 int ret = 0;
paule9372532003-10-26 21:36:07 +00001485 ret = vty_telnet_option (vty, buf + i, nbytes - i);
paul718e3742002-12-13 20:15:29 +00001486 vty->iac = 0;
1487 i += ret;
1488 continue;
1489 }
paul5b8c1b02003-10-15 23:08:55 +00001490
paul718e3742002-12-13 20:15:29 +00001491
1492 if (vty->status == VTY_MORE)
1493 {
1494 switch (buf[i])
1495 {
1496 case CONTROL('C'):
1497 case 'q':
1498 case 'Q':
paul718e3742002-12-13 20:15:29 +00001499 vty_buffer_reset (vty);
1500 break;
1501#if 0 /* More line does not work for "show ip bgp". */
1502 case '\n':
1503 case '\r':
1504 vty->status = VTY_MORELINE;
1505 break;
1506#endif
1507 default:
paul718e3742002-12-13 20:15:29 +00001508 break;
1509 }
1510 continue;
1511 }
1512
1513 /* Escape character. */
1514 if (vty->escape == VTY_ESCAPE)
1515 {
1516 vty_escape_map (buf[i], vty);
1517 continue;
1518 }
Paul Jakma126b0e72016-11-18 14:40:41 +00001519
1520 if (vty->escape == VTY_LITERAL)
1521 {
1522 vty_self_insert (vty, buf[i]);
1523 vty->escape = VTY_NORMAL;
1524 continue;
1525 }
1526
paul718e3742002-12-13 20:15:29 +00001527 /* Pre-escape status. */
1528 if (vty->escape == VTY_PRE_ESCAPE)
1529 {
1530 switch (buf[i])
1531 {
1532 case '[':
1533 vty->escape = VTY_ESCAPE;
1534 break;
1535 case 'b':
1536 vty_backward_word (vty);
1537 vty->escape = VTY_NORMAL;
1538 break;
1539 case 'f':
1540 vty_forward_word (vty);
1541 vty->escape = VTY_NORMAL;
1542 break;
1543 case 'd':
1544 vty_forward_kill_word (vty);
1545 vty->escape = VTY_NORMAL;
1546 break;
1547 case CONTROL('H'):
1548 case 0x7f:
1549 vty_backward_kill_word (vty);
1550 vty->escape = VTY_NORMAL;
1551 break;
1552 default:
1553 vty->escape = VTY_NORMAL;
1554 break;
1555 }
1556 continue;
1557 }
1558
1559 switch (buf[i])
1560 {
1561 case CONTROL('A'):
1562 vty_beginning_of_line (vty);
1563 break;
1564 case CONTROL('B'):
1565 vty_backward_char (vty);
1566 break;
1567 case CONTROL('C'):
1568 vty_stop_input (vty);
1569 break;
1570 case CONTROL('D'):
1571 vty_delete_char (vty);
1572 break;
1573 case CONTROL('E'):
1574 vty_end_of_line (vty);
1575 break;
1576 case CONTROL('F'):
1577 vty_forward_char (vty);
1578 break;
1579 case CONTROL('H'):
1580 case 0x7f:
1581 vty_delete_backward_char (vty);
1582 break;
1583 case CONTROL('K'):
1584 vty_kill_line (vty);
1585 break;
1586 case CONTROL('N'):
1587 vty_next_line (vty);
1588 break;
1589 case CONTROL('P'):
1590 vty_previous_line (vty);
1591 break;
1592 case CONTROL('T'):
1593 vty_transpose_chars (vty);
1594 break;
1595 case CONTROL('U'):
1596 vty_kill_line_from_beginning (vty);
1597 break;
Paul Jakma126b0e72016-11-18 14:40:41 +00001598 case CONTROL('V'):
1599 vty->escape = VTY_LITERAL;
1600 break;
paul718e3742002-12-13 20:15:29 +00001601 case CONTROL('W'):
1602 vty_backward_kill_word (vty);
1603 break;
1604 case CONTROL('Z'):
1605 vty_end_config (vty);
1606 break;
1607 case '\n':
1608 case '\r':
1609 vty_out (vty, "%s", VTY_NEWLINE);
1610 vty_execute (vty);
1611 break;
1612 case '\t':
1613 vty_complete_command (vty);
1614 break;
1615 case '?':
1616 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1617 vty_self_insert (vty, buf[i]);
1618 else
1619 vty_describe_command (vty);
1620 break;
1621 case '\033':
1622 if (i + 1 < nbytes && buf[i + 1] == '[')
1623 {
1624 vty->escape = VTY_ESCAPE;
1625 i++;
1626 }
1627 else
1628 vty->escape = VTY_PRE_ESCAPE;
1629 break;
1630 default:
1631 if (buf[i] > 31 && buf[i] < 127)
1632 vty_self_insert (vty, buf[i]);
1633 break;
1634 }
1635 }
1636
1637 /* Check status. */
1638 if (vty->status == VTY_CLOSE)
1639 vty_close (vty);
1640 else
1641 {
David Lamparter4715a532013-05-30 16:31:49 +02001642 vty_event (VTY_WRITE, vty->wfd, vty);
paul718e3742002-12-13 20:15:29 +00001643 vty_event (VTY_READ, vty_sock, vty);
1644 }
1645 return 0;
1646}
1647
1648/* Flush buffer to the vty. */
1649static int
1650vty_flush (struct thread *thread)
1651{
1652 int erase;
ajs9fc7ebf2005-02-23 15:12:34 +00001653 buffer_status_t flushrc;
paul718e3742002-12-13 20:15:29 +00001654 int vty_sock = THREAD_FD (thread);
1655 struct vty *vty = THREAD_ARG (thread);
ajs9fc7ebf2005-02-23 15:12:34 +00001656
paul718e3742002-12-13 20:15:29 +00001657 vty->t_write = NULL;
1658
1659 /* Tempolary disable read thread. */
ajs9fc7ebf2005-02-23 15:12:34 +00001660 if ((vty->lines == 0) && vty->t_read)
1661 {
1662 thread_cancel (vty->t_read);
1663 vty->t_read = NULL;
1664 }
paul718e3742002-12-13 20:15:29 +00001665
1666 /* Function execution continue. */
ajs9fc7ebf2005-02-23 15:12:34 +00001667 erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
paul718e3742002-12-13 20:15:29 +00001668
ajs9fc7ebf2005-02-23 15:12:34 +00001669 /* N.B. if width is 0, that means we don't know the window size. */
Lou Bergerc7f7e492016-01-12 13:41:49 -05001670 if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
David Lamparter4715a532013-05-30 16:31:49 +02001671 flushrc = buffer_flush_available(vty->obuf, vty_sock);
ajs9fc7ebf2005-02-23 15:12:34 +00001672 else if (vty->status == VTY_MORELINE)
David Lamparter4715a532013-05-30 16:31:49 +02001673 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001674 1, erase, 0);
1675 else
David Lamparter4715a532013-05-30 16:31:49 +02001676 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001677 vty->lines >= 0 ? vty->lines :
1678 vty->height,
1679 erase, 0);
1680 switch (flushrc)
1681 {
1682 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001683 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001684 zlog_warn("buffer_flush failed on vty client fd %d, closing",
1685 vty->fd);
1686 buffer_reset(vty->obuf);
1687 vty_close(vty);
1688 return 0;
1689 case BUFFER_EMPTY:
1690 if (vty->status == VTY_CLOSE)
1691 vty_close (vty);
paul718e3742002-12-13 20:15:29 +00001692 else
1693 {
ajs9fc7ebf2005-02-23 15:12:34 +00001694 vty->status = VTY_NORMAL;
paul718e3742002-12-13 20:15:29 +00001695 if (vty->lines == 0)
ajs9fc7ebf2005-02-23 15:12:34 +00001696 vty_event (VTY_READ, vty_sock, vty);
paul718e3742002-12-13 20:15:29 +00001697 }
ajs9fc7ebf2005-02-23 15:12:34 +00001698 break;
1699 case BUFFER_PENDING:
1700 /* There is more data waiting to be written. */
1701 vty->status = VTY_MORE;
1702 if (vty->lines == 0)
1703 vty_event (VTY_WRITE, vty_sock, vty);
1704 break;
1705 }
paul718e3742002-12-13 20:15:29 +00001706
1707 return 0;
1708}
1709
David Lamparterba5dc5e2013-05-30 16:33:45 +02001710/* allocate and initialise vty */
1711static struct vty *
1712vty_new_init (int vty_sock)
1713{
1714 struct vty *vty;
1715
1716 vty = vty_new ();
1717 vty->fd = vty_sock;
1718 vty->wfd = vty_sock;
1719 vty->type = VTY_TERM;
1720 vty->node = AUTH_NODE;
1721 vty->fail = 0;
1722 vty->cp = 0;
1723 vty_clear_buf (vty);
1724 vty->length = 0;
1725 memset (vty->hist, 0, sizeof (vty->hist));
1726 vty->hp = 0;
1727 vty->hindex = 0;
1728 vector_set_index (vtyvec, vty_sock, vty);
1729 vty->status = VTY_NORMAL;
1730 vty->lines = -1;
1731 vty->iac = 0;
1732 vty->iac_sb_in_progress = 0;
1733 vty->sb_len = 0;
1734
1735 return vty;
1736}
1737
paul718e3742002-12-13 20:15:29 +00001738/* Create new vty structure. */
ajs9fc7ebf2005-02-23 15:12:34 +00001739static struct vty *
paul718e3742002-12-13 20:15:29 +00001740vty_create (int vty_sock, union sockunion *su)
1741{
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001742 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001743 struct vty *vty;
1744
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001745 sockunion2str(su, buf, SU_ADDRSTRLEN);
1746
paul718e3742002-12-13 20:15:29 +00001747 /* Allocate new vty structure and set up default values. */
David Lamparterba5dc5e2013-05-30 16:33:45 +02001748 vty = vty_new_init (vty_sock);
1749
1750 /* configurable parameters not part of basic init */
1751 vty->v_timeout = vty_timeout_val;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001752 strcpy (vty->address, buf);
paul718e3742002-12-13 20:15:29 +00001753 if (no_password_check)
1754 {
Paul Jakma62687ff2008-08-23 14:27:06 +01001755 if (restricted_mode)
1756 vty->node = RESTRICTED_NODE;
1757 else if (host.advanced)
paul718e3742002-12-13 20:15:29 +00001758 vty->node = ENABLE_NODE;
1759 else
1760 vty->node = VIEW_NODE;
1761 }
paul718e3742002-12-13 20:15:29 +00001762 if (host.lines >= 0)
1763 vty->lines = host.lines;
paul718e3742002-12-13 20:15:29 +00001764
1765 if (! no_password_check)
1766 {
1767 /* Vty is not available if password isn't set. */
1768 if (host.password == NULL && host.password_encrypt == NULL)
1769 {
1770 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1771 vty->status = VTY_CLOSE;
1772 vty_close (vty);
1773 return NULL;
1774 }
1775 }
1776
1777 /* Say hello to the world. */
1778 vty_hello (vty);
1779 if (! no_password_check)
1780 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1781
1782 /* Setting up terminal. */
1783 vty_will_echo (vty);
1784 vty_will_suppress_go_ahead (vty);
1785
1786 vty_dont_linemode (vty);
1787 vty_do_window_size (vty);
1788 /* vty_dont_lflow_ahead (vty); */
1789
1790 vty_prompt (vty);
1791
1792 /* Add read/write thread. */
1793 vty_event (VTY_WRITE, vty_sock, vty);
1794 vty_event (VTY_READ, vty_sock, vty);
1795
1796 return vty;
1797}
1798
David Lamparterba5dc5e2013-05-30 16:33:45 +02001799/* create vty for stdio */
David Lamparterba53a8f2015-05-05 11:04:46 +02001800static struct termios stdio_orig_termios;
1801static struct vty *stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001802static void (*stdio_vty_atclose)(void);
David Lamparterba53a8f2015-05-05 11:04:46 +02001803
1804static void
1805vty_stdio_reset (void)
1806{
1807 if (stdio_vty)
1808 {
1809 tcsetattr (0, TCSANOW, &stdio_orig_termios);
1810 stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001811
1812 if (stdio_vty_atclose)
1813 stdio_vty_atclose ();
1814 stdio_vty_atclose = NULL;
David Lamparterba53a8f2015-05-05 11:04:46 +02001815 }
1816}
1817
David Lamparterba5dc5e2013-05-30 16:33:45 +02001818struct vty *
David Lamparter464ccf32015-05-12 21:56:18 +02001819vty_stdio (void (*atclose)())
David Lamparterba5dc5e2013-05-30 16:33:45 +02001820{
1821 struct vty *vty;
David Lamparterba53a8f2015-05-05 11:04:46 +02001822 struct termios termios;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001823
David Lamparterba53a8f2015-05-05 11:04:46 +02001824 /* refuse creating two vtys on stdio */
1825 if (stdio_vty)
1826 return NULL;
1827
1828 vty = stdio_vty = vty_new_init (0);
David Lamparter464ccf32015-05-12 21:56:18 +02001829 stdio_vty_atclose = atclose;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001830 vty->wfd = 1;
1831
1832 /* always have stdio vty in a known _unchangeable_ state, don't want config
1833 * to have any effect here to make sure scripting this works as intended */
1834 vty->node = ENABLE_NODE;
1835 vty->v_timeout = 0;
1836 strcpy (vty->address, "console");
1837
David Lamparterba53a8f2015-05-05 11:04:46 +02001838 if (!tcgetattr (0, &stdio_orig_termios))
1839 {
1840 termios = stdio_orig_termios;
1841 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
1842 | INLCR | IGNCR | ICRNL | IXON);
1843 termios.c_oflag &= ~OPOST;
1844 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
1845 termios.c_cflag &= ~(CSIZE | PARENB);
1846 termios.c_cflag |= CS8;
1847 tcsetattr (0, TCSANOW, &termios);
1848 }
1849
David Lamparterba5dc5e2013-05-30 16:33:45 +02001850 vty_prompt (vty);
1851
1852 /* Add read/write thread. */
1853 vty_event (VTY_WRITE, 1, vty);
1854 vty_event (VTY_READ, 0, vty);
1855
1856 return vty;
1857}
1858
paul718e3742002-12-13 20:15:29 +00001859/* Accept connection from the network. */
1860static int
1861vty_accept (struct thread *thread)
1862{
1863 int vty_sock;
paul718e3742002-12-13 20:15:29 +00001864 union sockunion su;
1865 int ret;
1866 unsigned int on;
1867 int accept_sock;
Timo Teräsc1c69e42015-05-22 13:40:57 +03001868 struct prefix p;
paul718e3742002-12-13 20:15:29 +00001869 struct access_list *acl = NULL;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001870 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001871
1872 accept_sock = THREAD_FD (thread);
1873
1874 /* We continue hearing vty socket. */
1875 vty_event (VTY_SERV, accept_sock, NULL);
1876
1877 memset (&su, 0, sizeof (union sockunion));
1878
1879 /* We can handle IPv4 or IPv6 socket. */
1880 vty_sock = sockunion_accept (accept_sock, &su);
1881 if (vty_sock < 0)
1882 {
ajs6099b3b2004-11-20 02:06:59 +00001883 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001884 return -1;
1885 }
ajs9fc7ebf2005-02-23 15:12:34 +00001886 set_nonblocking(vty_sock);
paul718e3742002-12-13 20:15:29 +00001887
Timo Teräsc1c69e42015-05-22 13:40:57 +03001888 sockunion2hostprefix (&su, &p);
paul718e3742002-12-13 20:15:29 +00001889
1890 /* VTY's accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001891 if (p.family == AF_INET && vty_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001892 {
1893 if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001894 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001895 {
paul718e3742002-12-13 20:15:29 +00001896 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001897 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001898 close (vty_sock);
1899
1900 /* continue accepting connections */
1901 vty_event (VTY_SERV, accept_sock, NULL);
1902
paul718e3742002-12-13 20:15:29 +00001903 return 0;
1904 }
1905 }
1906
1907#ifdef HAVE_IPV6
1908 /* VTY's ipv6 accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001909 if (p.family == AF_INET6 && vty_ipv6_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001910 {
1911 if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001912 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001913 {
paul718e3742002-12-13 20:15:29 +00001914 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001915 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001916 close (vty_sock);
1917
1918 /* continue accepting connections */
1919 vty_event (VTY_SERV, accept_sock, NULL);
1920
paul718e3742002-12-13 20:15:29 +00001921 return 0;
1922 }
1923 }
1924#endif /* HAVE_IPV6 */
1925
paul718e3742002-12-13 20:15:29 +00001926 on = 1;
1927 ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
1928 (char *) &on, sizeof (on));
1929 if (ret < 0)
1930 zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
ajs6099b3b2004-11-20 02:06:59 +00001931 safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001932
heasley78e6cd92009-12-07 16:41:14 +03001933 zlog (NULL, LOG_INFO, "Vty connection from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001934 sockunion2str (&su, buf, SU_ADDRSTRLEN));
heasley78e6cd92009-12-07 16:41:14 +03001935
Stephen Hemminger9206f9e2011-12-18 19:43:40 +04001936 vty_create (vty_sock, &su);
paul718e3742002-12-13 20:15:29 +00001937
1938 return 0;
1939}
1940
David Lamparter6d6df302014-06-28 21:12:37 +02001941#ifdef HAVE_IPV6
ajs9fc7ebf2005-02-23 15:12:34 +00001942static void
paul718e3742002-12-13 20:15:29 +00001943vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
1944{
1945 int ret;
1946 struct addrinfo req;
1947 struct addrinfo *ainfo;
1948 struct addrinfo *ainfo_save;
1949 int sock;
1950 char port_str[BUFSIZ];
1951
1952 memset (&req, 0, sizeof (struct addrinfo));
1953 req.ai_flags = AI_PASSIVE;
1954 req.ai_family = AF_UNSPEC;
1955 req.ai_socktype = SOCK_STREAM;
1956 sprintf (port_str, "%d", port);
1957 port_str[sizeof (port_str) - 1] = '\0';
1958
1959 ret = getaddrinfo (hostname, port_str, &req, &ainfo);
1960
1961 if (ret != 0)
1962 {
1963 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
1964 exit (1);
1965 }
1966
1967 ainfo_save = ainfo;
1968
1969 do
1970 {
1971 if (ainfo->ai_family != AF_INET
1972#ifdef HAVE_IPV6
1973 && ainfo->ai_family != AF_INET6
1974#endif /* HAVE_IPV6 */
1975 )
1976 continue;
1977
1978 sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
1979 if (sock < 0)
1980 continue;
1981
David Lamparterca051262009-10-04 16:21:49 +02001982 sockopt_v6only (ainfo->ai_family, sock);
paul718e3742002-12-13 20:15:29 +00001983 sockopt_reuseaddr (sock);
1984 sockopt_reuseport (sock);
1985
1986 ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
1987 if (ret < 0)
1988 {
1989 close (sock); /* Avoid sd leak. */
1990 continue;
1991 }
1992
1993 ret = listen (sock, 3);
1994 if (ret < 0)
1995 {
1996 close (sock); /* Avoid sd leak. */
1997 continue;
1998 }
1999
2000 vty_event (VTY_SERV, sock, NULL);
2001 }
2002 while ((ainfo = ainfo->ai_next) != NULL);
2003
2004 freeaddrinfo (ainfo_save);
2005}
David Lamparter6d6df302014-06-28 21:12:37 +02002006#else /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00002007
2008/* Make vty server socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00002009static void
paul29db05b2003-05-08 20:10:22 +00002010vty_serv_sock_family (const char* addr, unsigned short port, int family)
paul718e3742002-12-13 20:15:29 +00002011{
2012 int ret;
2013 union sockunion su;
2014 int accept_sock;
paul29db05b2003-05-08 20:10:22 +00002015 void* naddr=NULL;
paul718e3742002-12-13 20:15:29 +00002016
2017 memset (&su, 0, sizeof (union sockunion));
2018 su.sa.sa_family = family;
paul29db05b2003-05-08 20:10:22 +00002019 if(addr)
2020 switch(family)
2021 {
2022 case AF_INET:
2023 naddr=&su.sin.sin_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00002024 break;
paul29db05b2003-05-08 20:10:22 +00002025#ifdef HAVE_IPV6
2026 case AF_INET6:
2027 naddr=&su.sin6.sin6_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00002028 break;
paul29db05b2003-05-08 20:10:22 +00002029#endif
2030 }
2031
2032 if(naddr)
2033 switch(inet_pton(family,addr,naddr))
2034 {
2035 case -1:
2036 zlog_err("bad address %s",addr);
2037 naddr=NULL;
2038 break;
2039 case 0:
ajs6099b3b2004-11-20 02:06:59 +00002040 zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
paul29db05b2003-05-08 20:10:22 +00002041 naddr=NULL;
2042 }
paul718e3742002-12-13 20:15:29 +00002043
2044 /* Make new socket. */
2045 accept_sock = sockunion_stream_socket (&su);
2046 if (accept_sock < 0)
2047 return;
2048
2049 /* This is server, so reuse address. */
2050 sockopt_reuseaddr (accept_sock);
2051 sockopt_reuseport (accept_sock);
2052
2053 /* Bind socket to universal address and given port. */
paul29db05b2003-05-08 20:10:22 +00002054 ret = sockunion_bind (accept_sock, &su, port, naddr);
paul718e3742002-12-13 20:15:29 +00002055 if (ret < 0)
2056 {
paul29db05b2003-05-08 20:10:22 +00002057 zlog_warn("can't bind socket");
paul718e3742002-12-13 20:15:29 +00002058 close (accept_sock); /* Avoid sd leak. */
2059 return;
2060 }
2061
2062 /* Listen socket under queue 3. */
2063 ret = listen (accept_sock, 3);
2064 if (ret < 0)
2065 {
2066 zlog (NULL, LOG_WARNING, "can't listen socket");
2067 close (accept_sock); /* Avoid sd leak. */
2068 return;
2069 }
2070
2071 /* Add vty server event. */
2072 vty_event (VTY_SERV, accept_sock, NULL);
2073}
David Lamparter6d6df302014-06-28 21:12:37 +02002074#endif /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00002075
2076#ifdef VTYSH
2077/* For sockaddr_un. */
2078#include <sys/un.h>
2079
2080/* VTY shell UNIX domain socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00002081static void
hasso6ad96ea2004-10-07 19:33:46 +00002082vty_serv_un (const char *path)
paul718e3742002-12-13 20:15:29 +00002083{
2084 int ret;
paul75e15fe2004-10-31 02:13:09 +00002085 int sock, len;
paul718e3742002-12-13 20:15:29 +00002086 struct sockaddr_un serv;
2087 mode_t old_mask;
pauledd7c242003-06-04 13:59:38 +00002088 struct zprivs_ids_t ids;
2089
paul718e3742002-12-13 20:15:29 +00002090 /* First of all, unlink existing socket */
2091 unlink (path);
2092
2093 /* Set umask */
paul1921e6f2003-05-23 08:12:36 +00002094 old_mask = umask (0007);
paul718e3742002-12-13 20:15:29 +00002095
2096 /* Make UNIX domain socket. */
2097 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2098 if (sock < 0)
2099 {
ajs6a52d0d2005-01-30 18:49:28 +00002100 zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002101 return;
2102 }
2103
2104 /* Make server socket. */
2105 memset (&serv, 0, sizeof (struct sockaddr_un));
2106 serv.sun_family = AF_UNIX;
2107 strncpy (serv.sun_path, path, strlen (path));
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002108#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
paul718e3742002-12-13 20:15:29 +00002109 len = serv.sun_len = SUN_LEN(&serv);
2110#else
2111 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002112#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
paul718e3742002-12-13 20:15:29 +00002113
2114 ret = bind (sock, (struct sockaddr *) &serv, len);
2115 if (ret < 0)
2116 {
ajs6a52d0d2005-01-30 18:49:28 +00002117 zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002118 close (sock); /* Avoid sd leak. */
2119 return;
2120 }
2121
2122 ret = listen (sock, 5);
2123 if (ret < 0)
2124 {
ajs6a52d0d2005-01-30 18:49:28 +00002125 zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002126 close (sock); /* Avoid sd leak. */
2127 return;
2128 }
2129
2130 umask (old_mask);
2131
pauledd7c242003-06-04 13:59:38 +00002132 zprivs_get_ids(&ids);
2133
2134 if (ids.gid_vty > 0)
2135 {
2136 /* set group of socket */
2137 if ( chown (path, -1, ids.gid_vty) )
2138 {
2139 zlog_err ("vty_serv_un: could chown socket, %s",
ajs6099b3b2004-11-20 02:06:59 +00002140 safe_strerror (errno) );
pauledd7c242003-06-04 13:59:38 +00002141 }
2142 }
2143
paul718e3742002-12-13 20:15:29 +00002144 vty_event (VTYSH_SERV, sock, NULL);
2145}
2146
2147/* #define VTYSH_DEBUG 1 */
2148
2149static int
2150vtysh_accept (struct thread *thread)
2151{
2152 int accept_sock;
2153 int sock;
2154 int client_len;
2155 struct sockaddr_un client;
2156 struct vty *vty;
2157
2158 accept_sock = THREAD_FD (thread);
2159
2160 vty_event (VTYSH_SERV, accept_sock, NULL);
2161
2162 memset (&client, 0, sizeof (struct sockaddr_un));
2163 client_len = sizeof (struct sockaddr_un);
2164
hassoe473b032004-09-26 16:08:11 +00002165 sock = accept (accept_sock, (struct sockaddr *) &client,
2166 (socklen_t *) &client_len);
paul718e3742002-12-13 20:15:29 +00002167
2168 if (sock < 0)
2169 {
ajs6099b3b2004-11-20 02:06:59 +00002170 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00002171 return -1;
2172 }
2173
ajs9fc7ebf2005-02-23 15:12:34 +00002174 if (set_nonblocking(sock) < 0)
paul75e15fe2004-10-31 02:13:09 +00002175 {
ajs9fc7ebf2005-02-23 15:12:34 +00002176 zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
2177 " %s, closing", sock, safe_strerror (errno));
paul75e15fe2004-10-31 02:13:09 +00002178 close (sock);
2179 return -1;
2180 }
pauldccfb192004-10-29 08:29:36 +00002181
paul718e3742002-12-13 20:15:29 +00002182#ifdef VTYSH_DEBUG
2183 printf ("VTY shell accept\n");
2184#endif /* VTYSH_DEBUG */
2185
2186 vty = vty_new ();
2187 vty->fd = sock;
David Lamparter4715a532013-05-30 16:31:49 +02002188 vty->wfd = sock;
paul718e3742002-12-13 20:15:29 +00002189 vty->type = VTY_SHELL_SERV;
2190 vty->node = VIEW_NODE;
2191
2192 vty_event (VTYSH_READ, sock, vty);
2193
2194 return 0;
2195}
2196
2197static int
ajs9fc7ebf2005-02-23 15:12:34 +00002198vtysh_flush(struct vty *vty)
2199{
David Lamparter4715a532013-05-30 16:31:49 +02002200 switch (buffer_flush_available(vty->obuf, vty->wfd))
ajs9fc7ebf2005-02-23 15:12:34 +00002201 {
2202 case BUFFER_PENDING:
David Lamparter4715a532013-05-30 16:31:49 +02002203 vty_event(VTYSH_WRITE, vty->wfd, vty);
ajs9fc7ebf2005-02-23 15:12:34 +00002204 break;
2205 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002206 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002207 zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
2208 buffer_reset(vty->obuf);
2209 vty_close(vty);
2210 return -1;
2211 break;
2212 case BUFFER_EMPTY:
2213 break;
2214 }
2215 return 0;
2216}
2217
2218static int
paul718e3742002-12-13 20:15:29 +00002219vtysh_read (struct thread *thread)
2220{
2221 int ret;
2222 int sock;
2223 int nbytes;
2224 struct vty *vty;
2225 unsigned char buf[VTY_READ_BUFSIZ];
ajs9fc7ebf2005-02-23 15:12:34 +00002226 unsigned char *p;
paul718e3742002-12-13 20:15:29 +00002227 u_char header[4] = {0, 0, 0, 0};
2228
2229 sock = THREAD_FD (thread);
2230 vty = THREAD_ARG (thread);
2231 vty->t_read = NULL;
2232
ajs9fc7ebf2005-02-23 15:12:34 +00002233 if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
paul718e3742002-12-13 20:15:29 +00002234 {
ajs9fc7ebf2005-02-23 15:12:34 +00002235 if (nbytes < 0)
2236 {
2237 if (ERRNO_IO_RETRY(errno))
2238 {
2239 vty_event (VTYSH_READ, sock, vty);
2240 return 0;
2241 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002242 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002243 zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
2244 __func__, sock, safe_strerror(errno));
2245 }
2246 buffer_reset(vty->obuf);
paul718e3742002-12-13 20:15:29 +00002247 vty_close (vty);
2248#ifdef VTYSH_DEBUG
2249 printf ("close vtysh\n");
2250#endif /* VTYSH_DEBUG */
2251 return 0;
2252 }
2253
2254#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002255 printf ("line: %.*s\n", nbytes, buf);
paul718e3742002-12-13 20:15:29 +00002256#endif /* VTYSH_DEBUG */
2257
Paul Jakma7d662842017-01-19 10:39:23 +00002258 if (vty->length + nbytes >= vty->max)
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002259 {
2260 /* Clear command line buffer. */
2261 vty->cp = vty->length = 0;
2262 vty_clear_buf (vty);
2263 vty_out (vty, "%% Command is too long.%s", VTY_NEWLINE);
2264 goto out;
2265 }
2266
ajs9fc7ebf2005-02-23 15:12:34 +00002267 for (p = buf; p < buf+nbytes; p++)
2268 {
ajs9fc7ebf2005-02-23 15:12:34 +00002269 vty->buf[vty->length++] = *p;
2270 if (*p == '\0')
2271 {
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002272
ajs9fc7ebf2005-02-23 15:12:34 +00002273 /* Pass this line to parser. */
2274 ret = vty_execute (vty);
2275 /* Note that vty_execute clears the command buffer and resets
2276 vty->length to 0. */
paul718e3742002-12-13 20:15:29 +00002277
ajs9fc7ebf2005-02-23 15:12:34 +00002278 /* Return result. */
paul718e3742002-12-13 20:15:29 +00002279#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002280 printf ("result: %d\n", ret);
2281 printf ("vtysh node: %d\n", vty->node);
paul718e3742002-12-13 20:15:29 +00002282#endif /* VTYSH_DEBUG */
2283
ajs9fc7ebf2005-02-23 15:12:34 +00002284 header[3] = ret;
2285 buffer_put(vty->obuf, header, 4);
paul718e3742002-12-13 20:15:29 +00002286
ajs9fc7ebf2005-02-23 15:12:34 +00002287 if (!vty->t_write && (vtysh_flush(vty) < 0))
2288 /* Try to flush results; exit if a write error occurs. */
2289 return 0;
2290 }
2291 }
2292
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002293out:
paul718e3742002-12-13 20:15:29 +00002294 vty_event (VTYSH_READ, sock, vty);
2295
2296 return 0;
2297}
ajs49ff6d92004-11-04 19:26:16 +00002298
2299static int
2300vtysh_write (struct thread *thread)
2301{
2302 struct vty *vty = THREAD_ARG (thread);
2303
2304 vty->t_write = NULL;
ajs9fc7ebf2005-02-23 15:12:34 +00002305 vtysh_flush(vty);
ajs976d8c72004-11-10 15:40:09 +00002306 return 0;
ajs49ff6d92004-11-04 19:26:16 +00002307}
2308
paul718e3742002-12-13 20:15:29 +00002309#endif /* VTYSH */
2310
2311/* Determine address family to bind. */
2312void
hasso6ad96ea2004-10-07 19:33:46 +00002313vty_serv_sock (const char *addr, unsigned short port, const char *path)
paul718e3742002-12-13 20:15:29 +00002314{
2315 /* If port is set to 0, do not listen on TCP/IP at all! */
2316 if (port)
2317 {
2318
2319#ifdef HAVE_IPV6
paul29db05b2003-05-08 20:10:22 +00002320 vty_serv_sock_addrinfo (addr, port);
paul718e3742002-12-13 20:15:29 +00002321#else /* ! HAVE_IPV6 */
paul29db05b2003-05-08 20:10:22 +00002322 vty_serv_sock_family (addr,port, AF_INET);
paul718e3742002-12-13 20:15:29 +00002323#endif /* HAVE_IPV6 */
2324 }
2325
2326#ifdef VTYSH
2327 vty_serv_un (path);
2328#endif /* VTYSH */
2329}
2330
Andrew J. Schorr9d0a3262006-07-11 00:06:49 +00002331/* Close vty interface. Warning: call this only from functions that
2332 will be careful not to access the vty afterwards (since it has
2333 now been freed). This is safest from top-level functions (called
2334 directly by the thread dispatcher). */
paul718e3742002-12-13 20:15:29 +00002335void
2336vty_close (struct vty *vty)
2337{
2338 int i;
2339
2340 /* Cancel threads.*/
2341 if (vty->t_read)
2342 thread_cancel (vty->t_read);
2343 if (vty->t_write)
2344 thread_cancel (vty->t_write);
2345 if (vty->t_timeout)
2346 thread_cancel (vty->t_timeout);
paul718e3742002-12-13 20:15:29 +00002347
2348 /* Flush buffer. */
David Lamparter4715a532013-05-30 16:31:49 +02002349 buffer_flush_all (vty->obuf, vty->wfd);
paul718e3742002-12-13 20:15:29 +00002350
2351 /* Free input buffer. */
2352 buffer_free (vty->obuf);
2353
paul718e3742002-12-13 20:15:29 +00002354 /* Free command history. */
2355 for (i = 0; i < VTY_MAXHIST; i++)
2356 if (vty->hist[i])
2357 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2358
2359 /* Unset vector. */
2360 vector_unset (vtyvec, vty->fd);
2361
2362 /* Close socket. */
2363 if (vty->fd > 0)
2364 close (vty->fd);
David Lamparterba53a8f2015-05-05 11:04:46 +02002365 else
2366 vty_stdio_reset ();
paul718e3742002-12-13 20:15:29 +00002367
paul718e3742002-12-13 20:15:29 +00002368 if (vty->buf)
2369 XFREE (MTYPE_VTY, vty->buf);
2370
2371 /* Check configure. */
2372 vty_config_unlock (vty);
2373
2374 /* OK free vty. */
2375 XFREE (MTYPE_VTY, vty);
2376}
2377
2378/* When time out occur output message then close connection. */
2379static int
2380vty_timeout (struct thread *thread)
2381{
2382 struct vty *vty;
2383
2384 vty = THREAD_ARG (thread);
2385 vty->t_timeout = NULL;
2386 vty->v_timeout = 0;
2387
2388 /* Clear buffer*/
2389 buffer_reset (vty->obuf);
2390 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2391
2392 /* Close connection. */
2393 vty->status = VTY_CLOSE;
2394 vty_close (vty);
2395
2396 return 0;
2397}
2398
2399/* Read up configuration file from file_name. */
2400static void
2401vty_read_file (FILE *confp)
2402{
2403 int ret;
2404 struct vty *vty;
Steve Hillea555002009-07-28 16:36:14 -04002405 unsigned int line_num = 0;
paul718e3742002-12-13 20:15:29 +00002406
2407 vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02002408 vty->wfd = dup(STDERR_FILENO); /* vty_close() will close this */
2409 if (vty->wfd < 0)
Steve Hillea555002009-07-28 16:36:14 -04002410 {
2411 /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
David Lamparter4715a532013-05-30 16:31:49 +02002412 vty->wfd = STDOUT_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002413 }
David Lamparter4715a532013-05-30 16:31:49 +02002414 vty->fd = STDIN_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002415 vty->type = VTY_FILE;
paul718e3742002-12-13 20:15:29 +00002416 vty->node = CONFIG_NODE;
2417
2418 /* Execute configuration file */
Steve Hillea555002009-07-28 16:36:14 -04002419 ret = config_from_file (vty, confp, &line_num);
2420
2421 /* Flush any previous errors before printing messages below */
2422 buffer_flush_all (vty->obuf, vty->fd);
paul718e3742002-12-13 20:15:29 +00002423
paul7021c422003-07-15 12:52:22 +00002424 if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
paul718e3742002-12-13 20:15:29 +00002425 {
2426 switch (ret)
paul7021c422003-07-15 12:52:22 +00002427 {
2428 case CMD_ERR_AMBIGUOUS:
Steve Hillea555002009-07-28 16:36:14 -04002429 fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
paul7021c422003-07-15 12:52:22 +00002430 break;
2431 case CMD_ERR_NO_MATCH:
Steve Hillea555002009-07-28 16:36:14 -04002432 fprintf (stderr, "*** Error reading config: There is no such command.\n");
paul7021c422003-07-15 12:52:22 +00002433 break;
2434 }
Steve Hillea555002009-07-28 16:36:14 -04002435 fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
2436 line_num, vty->buf);
paul718e3742002-12-13 20:15:29 +00002437 vty_close (vty);
2438 exit (1);
2439 }
2440
2441 vty_close (vty);
2442}
2443
ajs9fc7ebf2005-02-23 15:12:34 +00002444static FILE *
paul718e3742002-12-13 20:15:29 +00002445vty_use_backup_config (char *fullpath)
2446{
2447 char *fullpath_sav, *fullpath_tmp;
2448 FILE *ret = NULL;
2449 struct stat buf;
2450 int tmp, sav;
2451 int c;
2452 char buffer[512];
2453
2454 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2455 strcpy (fullpath_sav, fullpath);
2456 strcat (fullpath_sav, CONF_BACKUP_EXT);
2457 if (stat (fullpath_sav, &buf) == -1)
2458 {
2459 free (fullpath_sav);
2460 return NULL;
2461 }
2462
2463 fullpath_tmp = malloc (strlen (fullpath) + 8);
2464 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2465
2466 /* Open file to configuration write. */
2467 tmp = mkstemp (fullpath_tmp);
2468 if (tmp < 0)
2469 {
2470 free (fullpath_sav);
2471 free (fullpath_tmp);
2472 return NULL;
2473 }
2474
2475 sav = open (fullpath_sav, O_RDONLY);
2476 if (sav < 0)
2477 {
gdt3dbf9962003-12-22 20:18:18 +00002478 unlink (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002479 free (fullpath_sav);
2480 free (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002481 return NULL;
2482 }
2483
2484 while((c = read (sav, buffer, 512)) > 0)
2485 write (tmp, buffer, c);
2486
2487 close (sav);
2488 close (tmp);
2489
gdtaa593d52003-12-22 20:15:53 +00002490 if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
2491 {
gdt3dbf9962003-12-22 20:18:18 +00002492 unlink (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002493 free (fullpath_sav);
2494 free (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002495 return NULL;
2496 }
2497
paul718e3742002-12-13 20:15:29 +00002498 if (link (fullpath_tmp, fullpath) == 0)
2499 ret = fopen (fullpath, "r");
2500
2501 unlink (fullpath_tmp);
2502
2503 free (fullpath_sav);
2504 free (fullpath_tmp);
hasso12f6ea22005-03-07 08:35:39 +00002505 return ret;
paul718e3742002-12-13 20:15:29 +00002506}
2507
2508/* Read up configuration file from file_name. */
2509void
2510vty_read_config (char *config_file,
hasso320ec102004-06-20 19:54:37 +00002511 char *config_default_dir)
paul718e3742002-12-13 20:15:29 +00002512{
paulccc92352003-10-22 02:49:38 +00002513 char cwd[MAXPATHLEN];
paul718e3742002-12-13 20:15:29 +00002514 FILE *confp = NULL;
2515 char *fullpath;
paul05865c92005-10-26 05:49:54 +00002516 char *tmp = NULL;
paul718e3742002-12-13 20:15:29 +00002517
2518 /* If -f flag specified. */
2519 if (config_file != NULL)
2520 {
2521 if (! IS_DIRECTORY_SEP (config_file[0]))
hasso320ec102004-06-20 19:54:37 +00002522 {
2523 getcwd (cwd, MAXPATHLEN);
paul05865c92005-10-26 05:49:54 +00002524 tmp = XMALLOC (MTYPE_TMP,
hasso320ec102004-06-20 19:54:37 +00002525 strlen (cwd) + strlen (config_file) + 2);
paul05865c92005-10-26 05:49:54 +00002526 sprintf (tmp, "%s/%s", cwd, config_file);
2527 fullpath = tmp;
hasso320ec102004-06-20 19:54:37 +00002528 }
paul718e3742002-12-13 20:15:29 +00002529 else
hasso320ec102004-06-20 19:54:37 +00002530 fullpath = config_file;
paul718e3742002-12-13 20:15:29 +00002531
2532 confp = fopen (fullpath, "r");
2533
2534 if (confp == NULL)
hasso320ec102004-06-20 19:54:37 +00002535 {
paul3d1dc852005-04-05 00:45:23 +00002536 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2537 __func__, fullpath, safe_strerror (errno));
2538
hasso320ec102004-06-20 19:54:37 +00002539 confp = vty_use_backup_config (fullpath);
2540 if (confp)
2541 fprintf (stderr, "WARNING: using backup configuration file!\n");
2542 else
2543 {
2544 fprintf (stderr, "can't open configuration file [%s]\n",
paul3d1dc852005-04-05 00:45:23 +00002545 config_file);
hasso320ec102004-06-20 19:54:37 +00002546 exit(1);
2547 }
2548 }
paul718e3742002-12-13 20:15:29 +00002549 }
2550 else
2551 {
paul718e3742002-12-13 20:15:29 +00002552#ifdef VTYSH
hasso320ec102004-06-20 19:54:37 +00002553 int ret;
2554 struct stat conf_stat;
paul718e3742002-12-13 20:15:29 +00002555
hasso320ec102004-06-20 19:54:37 +00002556 /* !!!!PLEASE LEAVE!!!!
2557 * This is NEEDED for use with vtysh -b, or else you can get
2558 * a real configuration food fight with a lot garbage in the
2559 * merged configuration file it creates coming from the per
2560 * daemon configuration files. This also allows the daemons
2561 * to start if there default configuration file is not
2562 * present or ignore them, as needed when using vtysh -b to
2563 * configure the daemons at boot - MAG
2564 */
paul718e3742002-12-13 20:15:29 +00002565
hasso320ec102004-06-20 19:54:37 +00002566 /* Stat for vtysh Zebra.conf, if found startup and wait for
2567 * boot configuration
2568 */
paul718e3742002-12-13 20:15:29 +00002569
hasso320ec102004-06-20 19:54:37 +00002570 if ( strstr(config_default_dir, "vtysh") == NULL)
2571 {
2572 ret = stat (integrate_default, &conf_stat);
2573 if (ret >= 0)
2574 return;
2575 }
paul718e3742002-12-13 20:15:29 +00002576#endif /* VTYSH */
2577
hasso320ec102004-06-20 19:54:37 +00002578 confp = fopen (config_default_dir, "r");
2579 if (confp == NULL)
2580 {
paul3d1dc852005-04-05 00:45:23 +00002581 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2582 __func__, config_default_dir, safe_strerror (errno));
2583
hasso320ec102004-06-20 19:54:37 +00002584 confp = vty_use_backup_config (config_default_dir);
2585 if (confp)
2586 {
2587 fprintf (stderr, "WARNING: using backup configuration file!\n");
2588 fullpath = config_default_dir;
2589 }
2590 else
2591 {
2592 fprintf (stderr, "can't open configuration file [%s]\n",
2593 config_default_dir);
2594 exit (1);
paul3d1dc852005-04-05 00:45:23 +00002595 }
hasso320ec102004-06-20 19:54:37 +00002596 }
paul718e3742002-12-13 20:15:29 +00002597 else
hasso320ec102004-06-20 19:54:37 +00002598 fullpath = config_default_dir;
2599 }
2600
paul718e3742002-12-13 20:15:29 +00002601 vty_read_file (confp);
2602
2603 fclose (confp);
2604
2605 host_config_set (fullpath);
paul05865c92005-10-26 05:49:54 +00002606
2607 if (tmp)
2608 XFREE (MTYPE_TMP, fullpath);
paul718e3742002-12-13 20:15:29 +00002609}
2610
2611/* Small utility function which output log to the VTY. */
2612void
ajs274a4a42004-12-07 15:39:31 +00002613vty_log (const char *level, const char *proto_str,
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002614 const char *format, struct timestamp_control *ctl, va_list va)
paul718e3742002-12-13 20:15:29 +00002615{
hasso8c328f12004-10-05 21:01:23 +00002616 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002617 struct vty *vty;
Paul Jakmaa4b30302006-05-28 08:18:38 +00002618
2619 if (!vtyvec)
2620 return;
paul718e3742002-12-13 20:15:29 +00002621
paul55468c82005-03-14 20:19:01 +00002622 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002623 if ((vty = vector_slot (vtyvec, i)) != NULL)
2624 if (vty->monitor)
ajsd246bd92004-11-23 17:35:08 +00002625 {
2626 va_list ac;
2627 va_copy(ac, va);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002628 vty_log_out (vty, level, proto_str, format, ctl, ac);
ajsd246bd92004-11-23 17:35:08 +00002629 va_end(ac);
2630 }
paul718e3742002-12-13 20:15:29 +00002631}
2632
ajs274a4a42004-12-07 15:39:31 +00002633/* Async-signal-safe version of vty_log for fixed strings. */
2634void
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002635vty_log_fixed (char *buf, size_t len)
ajs274a4a42004-12-07 15:39:31 +00002636{
2637 unsigned int i;
ajs9fc7ebf2005-02-23 15:12:34 +00002638 struct iovec iov[2];
2639
Paul Jakmaa4b30302006-05-28 08:18:38 +00002640 /* vty may not have been initialised */
2641 if (!vtyvec)
2642 return;
2643
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002644 iov[0].iov_base = buf;
ajs9fc7ebf2005-02-23 15:12:34 +00002645 iov[0].iov_len = len;
ajs926fe8f2005-04-08 18:50:40 +00002646 iov[1].iov_base = (void *)"\r\n";
ajs9fc7ebf2005-02-23 15:12:34 +00002647 iov[1].iov_len = 2;
ajs274a4a42004-12-07 15:39:31 +00002648
paul55468c82005-03-14 20:19:01 +00002649 for (i = 0; i < vector_active (vtyvec); i++)
ajs274a4a42004-12-07 15:39:31 +00002650 {
2651 struct vty *vty;
ajs9fc7ebf2005-02-23 15:12:34 +00002652 if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
2653 /* N.B. We don't care about the return code, since process is
2654 most likely just about to die anyway. */
David Lamparter4715a532013-05-30 16:31:49 +02002655 writev(vty->wfd, iov, 2);
ajs274a4a42004-12-07 15:39:31 +00002656 }
2657}
2658
paul718e3742002-12-13 20:15:29 +00002659int
2660vty_config_lock (struct vty *vty)
2661{
2662 if (vty_config == 0)
2663 {
2664 vty->config = 1;
2665 vty_config = 1;
2666 }
2667 return vty->config;
2668}
2669
2670int
2671vty_config_unlock (struct vty *vty)
2672{
2673 if (vty_config == 1 && vty->config == 1)
2674 {
2675 vty->config = 0;
2676 vty_config = 0;
2677 }
2678 return vty->config;
2679}
David Lamparter6b0655a2014-06-04 06:53:35 +02002680
paul718e3742002-12-13 20:15:29 +00002681/* Master of the threads. */
Donald Sharpeeef0db2015-10-14 08:50:38 -04002682static struct thread_master *vty_master;
paul718e3742002-12-13 20:15:29 +00002683
2684static void
2685vty_event (enum event event, int sock, struct vty *vty)
2686{
2687 struct thread *vty_serv_thread;
2688
2689 switch (event)
2690 {
2691 case VTY_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002692 vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
paul718e3742002-12-13 20:15:29 +00002693 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2694 break;
2695#ifdef VTYSH
2696 case VTYSH_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002697 vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
Christian Franke677bcbb2013-02-27 13:47:23 +00002698 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
paul718e3742002-12-13 20:15:29 +00002699 break;
2700 case VTYSH_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002701 vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
ajs49ff6d92004-11-04 19:26:16 +00002702 break;
2703 case VTYSH_WRITE:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002704 vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
paul718e3742002-12-13 20:15:29 +00002705 break;
2706#endif /* VTYSH */
2707 case VTY_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002708 vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
paul718e3742002-12-13 20:15:29 +00002709
2710 /* Time out treatment. */
2711 if (vty->v_timeout)
2712 {
2713 if (vty->t_timeout)
2714 thread_cancel (vty->t_timeout);
2715 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002716 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002717 }
2718 break;
2719 case VTY_WRITE:
2720 if (! vty->t_write)
Donald Sharpeeef0db2015-10-14 08:50:38 -04002721 vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
paul718e3742002-12-13 20:15:29 +00002722 break;
2723 case VTY_TIMEOUT_RESET:
2724 if (vty->t_timeout)
2725 {
2726 thread_cancel (vty->t_timeout);
2727 vty->t_timeout = NULL;
2728 }
2729 if (vty->v_timeout)
2730 {
2731 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002732 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002733 }
2734 break;
2735 }
2736}
David Lamparter6b0655a2014-06-04 06:53:35 +02002737
Paul Jakma0f2f7a32016-06-16 15:40:02 +01002738DEFUN (who,
2739 who_cmd,
paul718e3742002-12-13 20:15:29 +00002740 "who",
2741 "Display who is on vty\n")
2742{
hasso8c328f12004-10-05 21:01:23 +00002743 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002744 struct vty *v;
2745
paul55468c82005-03-14 20:19:01 +00002746 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002747 if ((v = vector_slot (vtyvec, i)) != NULL)
2748 vty_out (vty, "%svty[%d] connected from %s.%s",
2749 v->config ? "*" : " ",
2750 i, v->address, VTY_NEWLINE);
2751 return CMD_SUCCESS;
2752}
2753
2754/* Move to vty configuration mode. */
2755DEFUN (line_vty,
2756 line_vty_cmd,
2757 "line vty",
2758 "Configure a terminal line\n"
2759 "Virtual terminal\n")
2760{
2761 vty->node = VTY_NODE;
2762 return CMD_SUCCESS;
2763}
2764
2765/* Set time out value. */
ajs9fc7ebf2005-02-23 15:12:34 +00002766static int
paul9035efa2004-10-10 11:56:56 +00002767exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
paul718e3742002-12-13 20:15:29 +00002768{
2769 unsigned long timeout = 0;
2770
2771 /* min_str and sec_str are already checked by parser. So it must be
2772 all digit string. */
2773 if (min_str)
2774 {
2775 timeout = strtol (min_str, NULL, 10);
2776 timeout *= 60;
2777 }
2778 if (sec_str)
2779 timeout += strtol (sec_str, NULL, 10);
2780
2781 vty_timeout_val = timeout;
2782 vty->v_timeout = timeout;
2783 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2784
2785
2786 return CMD_SUCCESS;
2787}
2788
2789DEFUN (exec_timeout_min,
2790 exec_timeout_min_cmd,
2791 "exec-timeout <0-35791>",
2792 "Set timeout value\n"
2793 "Timeout value in minutes\n")
2794{
2795 return exec_timeout (vty, argv[0], NULL);
2796}
2797
2798DEFUN (exec_timeout_sec,
2799 exec_timeout_sec_cmd,
2800 "exec-timeout <0-35791> <0-2147483>",
2801 "Set the EXEC timeout\n"
2802 "Timeout in minutes\n"
2803 "Timeout in seconds\n")
2804{
2805 return exec_timeout (vty, argv[0], argv[1]);
2806}
2807
2808DEFUN (no_exec_timeout,
2809 no_exec_timeout_cmd,
2810 "no exec-timeout",
2811 NO_STR
2812 "Set the EXEC timeout\n")
2813{
2814 return exec_timeout (vty, NULL, NULL);
2815}
2816
2817/* Set vty access class. */
2818DEFUN (vty_access_class,
2819 vty_access_class_cmd,
2820 "access-class WORD",
2821 "Filter connections based on an IP access list\n"
2822 "IP access list\n")
2823{
2824 if (vty_accesslist_name)
2825 XFREE(MTYPE_VTY, vty_accesslist_name);
2826
2827 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2828
2829 return CMD_SUCCESS;
2830}
2831
2832/* Clear vty access class. */
2833DEFUN (no_vty_access_class,
2834 no_vty_access_class_cmd,
2835 "no access-class [WORD]",
2836 NO_STR
2837 "Filter connections based on an IP access list\n"
2838 "IP access list\n")
2839{
2840 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2841 {
2842 vty_out (vty, "Access-class is not currently applied to vty%s",
2843 VTY_NEWLINE);
2844 return CMD_WARNING;
2845 }
2846
2847 XFREE(MTYPE_VTY, vty_accesslist_name);
2848
2849 vty_accesslist_name = NULL;
2850
2851 return CMD_SUCCESS;
2852}
2853
2854#ifdef HAVE_IPV6
2855/* Set vty access class. */
2856DEFUN (vty_ipv6_access_class,
2857 vty_ipv6_access_class_cmd,
2858 "ipv6 access-class WORD",
2859 IPV6_STR
2860 "Filter connections based on an IP access list\n"
2861 "IPv6 access list\n")
2862{
2863 if (vty_ipv6_accesslist_name)
2864 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2865
2866 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2867
2868 return CMD_SUCCESS;
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{
2880 if (! vty_ipv6_accesslist_name ||
2881 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2882 {
2883 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2884 VTY_NEWLINE);
2885 return CMD_WARNING;
2886 }
2887
2888 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2889
2890 vty_ipv6_accesslist_name = NULL;
2891
2892 return CMD_SUCCESS;
2893}
2894#endif /* HAVE_IPV6 */
2895
2896/* vty login. */
2897DEFUN (vty_login,
2898 vty_login_cmd,
2899 "login",
2900 "Enable password checking\n")
2901{
2902 no_password_check = 0;
2903 return CMD_SUCCESS;
2904}
2905
2906DEFUN (no_vty_login,
2907 no_vty_login_cmd,
2908 "no login",
2909 NO_STR
2910 "Enable password checking\n")
2911{
2912 no_password_check = 1;
2913 return CMD_SUCCESS;
2914}
2915
Paul Jakma62687ff2008-08-23 14:27:06 +01002916/* initial mode. */
2917DEFUN (vty_restricted_mode,
2918 vty_restricted_mode_cmd,
2919 "anonymous restricted",
2920 "Restrict view commands available in anonymous, unauthenticated vty\n")
2921{
2922 restricted_mode = 1;
2923 return CMD_SUCCESS;
2924}
2925
2926DEFUN (vty_no_restricted_mode,
2927 vty_no_restricted_mode_cmd,
2928 "no anonymous restricted",
2929 NO_STR
2930 "Enable password checking\n")
2931{
2932 restricted_mode = 0;
2933 return CMD_SUCCESS;
2934}
2935
paul718e3742002-12-13 20:15:29 +00002936DEFUN (service_advanced_vty,
2937 service_advanced_vty_cmd,
2938 "service advanced-vty",
2939 "Set up miscellaneous service\n"
2940 "Enable advanced mode vty interface\n")
2941{
2942 host.advanced = 1;
2943 return CMD_SUCCESS;
2944}
2945
2946DEFUN (no_service_advanced_vty,
2947 no_service_advanced_vty_cmd,
2948 "no service advanced-vty",
2949 NO_STR
2950 "Set up miscellaneous service\n"
2951 "Enable advanced mode vty interface\n")
2952{
2953 host.advanced = 0;
2954 return CMD_SUCCESS;
2955}
2956
2957DEFUN (terminal_monitor,
2958 terminal_monitor_cmd,
2959 "terminal monitor",
2960 "Set terminal line parameters\n"
2961 "Copy debug output to the current terminal line\n")
2962{
2963 vty->monitor = 1;
2964 return CMD_SUCCESS;
2965}
2966
2967DEFUN (terminal_no_monitor,
2968 terminal_no_monitor_cmd,
2969 "terminal no monitor",
2970 "Set terminal line parameters\n"
2971 NO_STR
2972 "Copy debug output to the current terminal line\n")
2973{
2974 vty->monitor = 0;
2975 return CMD_SUCCESS;
2976}
2977
paul789f78a2006-01-17 17:42:03 +00002978ALIAS (terminal_no_monitor,
2979 no_terminal_monitor_cmd,
2980 "no terminal monitor",
2981 NO_STR
2982 "Set terminal line parameters\n"
2983 "Copy debug output to the current terminal line\n")
2984
paul718e3742002-12-13 20:15:29 +00002985DEFUN (show_history,
2986 show_history_cmd,
2987 "show history",
2988 SHOW_STR
2989 "Display the session command history\n")
2990{
2991 int index;
2992
2993 for (index = vty->hindex + 1; index != vty->hindex;)
2994 {
2995 if (index == VTY_MAXHIST)
2996 {
2997 index = 0;
2998 continue;
2999 }
3000
3001 if (vty->hist[index] != NULL)
3002 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
3003
3004 index++;
3005 }
3006
3007 return CMD_SUCCESS;
3008}
3009
Lou Berger86b2a0a2016-05-17 12:19:51 -04003010/* vty login. */
3011DEFUN (log_commands,
3012 log_commands_cmd,
3013 "log commands",
3014 "Logging control\n"
3015 "Log all commands (can't be unset without restart)\n")
3016{
3017 do_log_commands = 1;
3018 return CMD_SUCCESS;
3019}
3020
paul718e3742002-12-13 20:15:29 +00003021/* Display current configuration. */
ajs9fc7ebf2005-02-23 15:12:34 +00003022static int
paul718e3742002-12-13 20:15:29 +00003023vty_config_write (struct vty *vty)
3024{
3025 vty_out (vty, "line vty%s", VTY_NEWLINE);
3026
3027 if (vty_accesslist_name)
3028 vty_out (vty, " access-class %s%s",
3029 vty_accesslist_name, VTY_NEWLINE);
3030
3031 if (vty_ipv6_accesslist_name)
3032 vty_out (vty, " ipv6 access-class %s%s",
3033 vty_ipv6_accesslist_name, VTY_NEWLINE);
3034
3035 /* exec-timeout */
3036 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
3037 vty_out (vty, " exec-timeout %ld %ld%s",
3038 vty_timeout_val / 60,
3039 vty_timeout_val % 60, VTY_NEWLINE);
3040
3041 /* login */
3042 if (no_password_check)
3043 vty_out (vty, " no login%s", VTY_NEWLINE);
Paul Jakma62687ff2008-08-23 14:27:06 +01003044
3045 if (restricted_mode != restricted_mode_default)
3046 {
3047 if (restricted_mode_default)
3048 vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
3049 else
3050 vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
3051 }
3052
Lou Berger86b2a0a2016-05-17 12:19:51 -04003053 if (do_log_commands)
3054 vty_out (vty, "log commands%s", VTY_NEWLINE);
3055
paul718e3742002-12-13 20:15:29 +00003056 vty_out (vty, "!%s", VTY_NEWLINE);
3057
3058 return CMD_SUCCESS;
3059}
3060
3061struct cmd_node vty_node =
3062{
3063 VTY_NODE,
3064 "%s(config-line)# ",
hassoe7168df2004-10-03 20:11:32 +00003065 1,
paul718e3742002-12-13 20:15:29 +00003066};
3067
3068/* Reset all VTY status. */
3069void
3070vty_reset ()
3071{
hasso8c328f12004-10-05 21:01:23 +00003072 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003073 struct vty *vty;
3074 struct thread *vty_serv_thread;
3075
paul55468c82005-03-14 20:19:01 +00003076 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00003077 if ((vty = vector_slot (vtyvec, i)) != NULL)
3078 {
3079 buffer_reset (vty->obuf);
3080 vty->status = VTY_CLOSE;
3081 vty_close (vty);
3082 }
3083
paul55468c82005-03-14 20:19:01 +00003084 for (i = 0; i < vector_active (Vvty_serv_thread); i++)
paul718e3742002-12-13 20:15:29 +00003085 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
3086 {
3087 thread_cancel (vty_serv_thread);
3088 vector_slot (Vvty_serv_thread, i) = NULL;
3089 close (i);
3090 }
3091
3092 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3093
3094 if (vty_accesslist_name)
3095 {
3096 XFREE(MTYPE_VTY, vty_accesslist_name);
3097 vty_accesslist_name = NULL;
3098 }
3099
3100 if (vty_ipv6_accesslist_name)
3101 {
3102 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3103 vty_ipv6_accesslist_name = NULL;
3104 }
3105}
3106
ajs9fc7ebf2005-02-23 15:12:34 +00003107static void
3108vty_save_cwd (void)
paul718e3742002-12-13 20:15:29 +00003109{
paul79ad2792003-10-15 22:09:28 +00003110 char cwd[MAXPATHLEN];
paulccc92352003-10-22 02:49:38 +00003111 char *c;
paul718e3742002-12-13 20:15:29 +00003112
paulccc92352003-10-22 02:49:38 +00003113 c = getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003114
paulccc92352003-10-22 02:49:38 +00003115 if (!c)
paul79ad2792003-10-15 22:09:28 +00003116 {
3117 chdir (SYSCONFDIR);
paulccc92352003-10-22 02:49:38 +00003118 getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003119 }
paul718e3742002-12-13 20:15:29 +00003120
3121 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
3122 strcpy (vty_cwd, cwd);
3123}
3124
3125char *
3126vty_get_cwd ()
3127{
3128 return vty_cwd;
3129}
3130
3131int
3132vty_shell (struct vty *vty)
3133{
3134 return vty->type == VTY_SHELL ? 1 : 0;
3135}
3136
3137int
3138vty_shell_serv (struct vty *vty)
3139{
3140 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3141}
3142
3143void
3144vty_init_vtysh ()
3145{
3146 vtyvec = vector_init (VECTOR_MIN_SIZE);
3147}
3148
3149/* Install vty's own commands like `who' command. */
3150void
paulb21b19c2003-06-15 01:28:29 +00003151vty_init (struct thread_master *master_thread)
paul718e3742002-12-13 20:15:29 +00003152{
3153 /* For further configuration read, preserve current directory. */
3154 vty_save_cwd ();
3155
3156 vtyvec = vector_init (VECTOR_MIN_SIZE);
3157
Donald Sharpeeef0db2015-10-14 08:50:38 -04003158 vty_master = master_thread;
paulb21b19c2003-06-15 01:28:29 +00003159
David Lamparterba53a8f2015-05-05 11:04:46 +02003160 atexit (vty_stdio_reset);
3161
paul718e3742002-12-13 20:15:29 +00003162 /* Initilize server thread vector. */
3163 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
3164
3165 /* Install bgp top node. */
3166 install_node (&vty_node, vty_config_write);
3167
Paul Jakma0f2f7a32016-06-16 15:40:02 +01003168 install_element (RESTRICTED_NODE, &who_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01003169 install_element (RESTRICTED_NODE, &show_history_cmd);
Paul Jakma0f2f7a32016-06-16 15:40:02 +01003170 install_element (VIEW_NODE, &who_cmd);
paul718e3742002-12-13 20:15:29 +00003171 install_element (VIEW_NODE, &show_history_cmd);
paul718e3742002-12-13 20:15:29 +00003172 install_element (CONFIG_NODE, &line_vty_cmd);
3173 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
3174 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
3175 install_element (CONFIG_NODE, &show_history_cmd);
Lou Berger86b2a0a2016-05-17 12:19:51 -04003176 install_element (CONFIG_NODE, &log_commands_cmd);
paul718e3742002-12-13 20:15:29 +00003177 install_element (ENABLE_NODE, &terminal_monitor_cmd);
3178 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
paul789f78a2006-01-17 17:42:03 +00003179 install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00003180
3181 install_default (VTY_NODE);
3182 install_element (VTY_NODE, &exec_timeout_min_cmd);
3183 install_element (VTY_NODE, &exec_timeout_sec_cmd);
3184 install_element (VTY_NODE, &no_exec_timeout_cmd);
3185 install_element (VTY_NODE, &vty_access_class_cmd);
3186 install_element (VTY_NODE, &no_vty_access_class_cmd);
3187 install_element (VTY_NODE, &vty_login_cmd);
3188 install_element (VTY_NODE, &no_vty_login_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01003189 install_element (VTY_NODE, &vty_restricted_mode_cmd);
3190 install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
paul718e3742002-12-13 20:15:29 +00003191#ifdef HAVE_IPV6
3192 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
3193 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
3194#endif /* HAVE_IPV6 */
3195}
Chris Caputo228da422009-07-18 05:44:03 +00003196
3197void
3198vty_terminate (void)
3199{
3200 if (vty_cwd)
3201 XFREE (MTYPE_TMP, vty_cwd);
3202
3203 if (vtyvec && Vvty_serv_thread)
3204 {
3205 vty_reset ();
3206 vector_free (vtyvec);
3207 vector_free (Vvty_serv_thread);
3208 }
3209}