blob: afaf2b06ed3fb9f562b7b36b09c28e702ab7b9ae [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
1389#define VTY_PRE_ESCAPE 1
1390#define VTY_ESCAPE 2
1391
1392/* Escape character command map. */
1393static void
1394vty_escape_map (unsigned char c, struct vty *vty)
1395{
1396 switch (c)
1397 {
1398 case ('A'):
1399 vty_previous_line (vty);
1400 break;
1401 case ('B'):
1402 vty_next_line (vty);
1403 break;
1404 case ('C'):
1405 vty_forward_char (vty);
1406 break;
1407 case ('D'):
1408 vty_backward_char (vty);
1409 break;
1410 default:
1411 break;
1412 }
1413
1414 /* Go back to normal mode. */
1415 vty->escape = VTY_NORMAL;
1416}
1417
1418/* Quit print out to the buffer. */
1419static void
1420vty_buffer_reset (struct vty *vty)
1421{
1422 buffer_reset (vty->obuf);
1423 vty_prompt (vty);
1424 vty_redraw_line (vty);
1425}
1426
1427/* Read data via vty socket. */
1428static int
1429vty_read (struct thread *thread)
1430{
1431 int i;
paul718e3742002-12-13 20:15:29 +00001432 int nbytes;
1433 unsigned char buf[VTY_READ_BUFSIZ];
1434
1435 int vty_sock = THREAD_FD (thread);
1436 struct vty *vty = THREAD_ARG (thread);
1437 vty->t_read = NULL;
1438
1439 /* Read raw data from socket */
ajs9fc7ebf2005-02-23 15:12:34 +00001440 if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
1441 {
1442 if (nbytes < 0)
1443 {
1444 if (ERRNO_IO_RETRY(errno))
1445 {
1446 vty_event (VTY_READ, vty_sock, vty);
1447 return 0;
1448 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001449 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001450 zlog_warn("%s: read error on vty client fd %d, closing: %s",
1451 __func__, vty->fd, safe_strerror(errno));
David Lamparter90d31352015-05-14 14:24:06 +02001452 buffer_reset(vty->obuf);
ajs9fc7ebf2005-02-23 15:12:34 +00001453 }
ajs9fc7ebf2005-02-23 15:12:34 +00001454 vty->status = VTY_CLOSE;
1455 }
paul718e3742002-12-13 20:15:29 +00001456
1457 for (i = 0; i < nbytes; i++)
1458 {
1459 if (buf[i] == IAC)
1460 {
1461 if (!vty->iac)
1462 {
1463 vty->iac = 1;
1464 continue;
1465 }
1466 else
1467 {
1468 vty->iac = 0;
1469 }
1470 }
1471
1472 if (vty->iac_sb_in_progress && !vty->iac)
1473 {
ajs9fc7ebf2005-02-23 15:12:34 +00001474 if (vty->sb_len < sizeof(vty->sb_buf))
1475 vty->sb_buf[vty->sb_len] = buf[i];
1476 vty->sb_len++;
paul718e3742002-12-13 20:15:29 +00001477 continue;
1478 }
1479
1480 if (vty->iac)
1481 {
1482 /* In case of telnet command */
paul5b8c1b02003-10-15 23:08:55 +00001483 int ret = 0;
paule9372532003-10-26 21:36:07 +00001484 ret = vty_telnet_option (vty, buf + i, nbytes - i);
paul718e3742002-12-13 20:15:29 +00001485 vty->iac = 0;
1486 i += ret;
1487 continue;
1488 }
paul5b8c1b02003-10-15 23:08:55 +00001489
paul718e3742002-12-13 20:15:29 +00001490
1491 if (vty->status == VTY_MORE)
1492 {
1493 switch (buf[i])
1494 {
1495 case CONTROL('C'):
1496 case 'q':
1497 case 'Q':
paul718e3742002-12-13 20:15:29 +00001498 vty_buffer_reset (vty);
1499 break;
1500#if 0 /* More line does not work for "show ip bgp". */
1501 case '\n':
1502 case '\r':
1503 vty->status = VTY_MORELINE;
1504 break;
1505#endif
1506 default:
paul718e3742002-12-13 20:15:29 +00001507 break;
1508 }
1509 continue;
1510 }
1511
1512 /* Escape character. */
1513 if (vty->escape == VTY_ESCAPE)
1514 {
1515 vty_escape_map (buf[i], vty);
1516 continue;
1517 }
1518
1519 /* Pre-escape status. */
1520 if (vty->escape == VTY_PRE_ESCAPE)
1521 {
1522 switch (buf[i])
1523 {
1524 case '[':
1525 vty->escape = VTY_ESCAPE;
1526 break;
1527 case 'b':
1528 vty_backward_word (vty);
1529 vty->escape = VTY_NORMAL;
1530 break;
1531 case 'f':
1532 vty_forward_word (vty);
1533 vty->escape = VTY_NORMAL;
1534 break;
1535 case 'd':
1536 vty_forward_kill_word (vty);
1537 vty->escape = VTY_NORMAL;
1538 break;
1539 case CONTROL('H'):
1540 case 0x7f:
1541 vty_backward_kill_word (vty);
1542 vty->escape = VTY_NORMAL;
1543 break;
1544 default:
1545 vty->escape = VTY_NORMAL;
1546 break;
1547 }
1548 continue;
1549 }
1550
1551 switch (buf[i])
1552 {
1553 case CONTROL('A'):
1554 vty_beginning_of_line (vty);
1555 break;
1556 case CONTROL('B'):
1557 vty_backward_char (vty);
1558 break;
1559 case CONTROL('C'):
1560 vty_stop_input (vty);
1561 break;
1562 case CONTROL('D'):
1563 vty_delete_char (vty);
1564 break;
1565 case CONTROL('E'):
1566 vty_end_of_line (vty);
1567 break;
1568 case CONTROL('F'):
1569 vty_forward_char (vty);
1570 break;
1571 case CONTROL('H'):
1572 case 0x7f:
1573 vty_delete_backward_char (vty);
1574 break;
1575 case CONTROL('K'):
1576 vty_kill_line (vty);
1577 break;
1578 case CONTROL('N'):
1579 vty_next_line (vty);
1580 break;
1581 case CONTROL('P'):
1582 vty_previous_line (vty);
1583 break;
1584 case CONTROL('T'):
1585 vty_transpose_chars (vty);
1586 break;
1587 case CONTROL('U'):
1588 vty_kill_line_from_beginning (vty);
1589 break;
1590 case CONTROL('W'):
1591 vty_backward_kill_word (vty);
1592 break;
1593 case CONTROL('Z'):
1594 vty_end_config (vty);
1595 break;
1596 case '\n':
1597 case '\r':
1598 vty_out (vty, "%s", VTY_NEWLINE);
1599 vty_execute (vty);
1600 break;
1601 case '\t':
1602 vty_complete_command (vty);
1603 break;
1604 case '?':
1605 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1606 vty_self_insert (vty, buf[i]);
1607 else
1608 vty_describe_command (vty);
1609 break;
1610 case '\033':
1611 if (i + 1 < nbytes && buf[i + 1] == '[')
1612 {
1613 vty->escape = VTY_ESCAPE;
1614 i++;
1615 }
1616 else
1617 vty->escape = VTY_PRE_ESCAPE;
1618 break;
1619 default:
1620 if (buf[i] > 31 && buf[i] < 127)
1621 vty_self_insert (vty, buf[i]);
1622 break;
1623 }
1624 }
1625
1626 /* Check status. */
1627 if (vty->status == VTY_CLOSE)
1628 vty_close (vty);
1629 else
1630 {
David Lamparter4715a532013-05-30 16:31:49 +02001631 vty_event (VTY_WRITE, vty->wfd, vty);
paul718e3742002-12-13 20:15:29 +00001632 vty_event (VTY_READ, vty_sock, vty);
1633 }
1634 return 0;
1635}
1636
1637/* Flush buffer to the vty. */
1638static int
1639vty_flush (struct thread *thread)
1640{
1641 int erase;
ajs9fc7ebf2005-02-23 15:12:34 +00001642 buffer_status_t flushrc;
paul718e3742002-12-13 20:15:29 +00001643 int vty_sock = THREAD_FD (thread);
1644 struct vty *vty = THREAD_ARG (thread);
ajs9fc7ebf2005-02-23 15:12:34 +00001645
paul718e3742002-12-13 20:15:29 +00001646 vty->t_write = NULL;
1647
1648 /* Tempolary disable read thread. */
ajs9fc7ebf2005-02-23 15:12:34 +00001649 if ((vty->lines == 0) && vty->t_read)
1650 {
1651 thread_cancel (vty->t_read);
1652 vty->t_read = NULL;
1653 }
paul718e3742002-12-13 20:15:29 +00001654
1655 /* Function execution continue. */
ajs9fc7ebf2005-02-23 15:12:34 +00001656 erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
paul718e3742002-12-13 20:15:29 +00001657
ajs9fc7ebf2005-02-23 15:12:34 +00001658 /* N.B. if width is 0, that means we don't know the window size. */
Lou Bergerc7f7e492016-01-12 13:41:49 -05001659 if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
David Lamparter4715a532013-05-30 16:31:49 +02001660 flushrc = buffer_flush_available(vty->obuf, vty_sock);
ajs9fc7ebf2005-02-23 15:12:34 +00001661 else if (vty->status == VTY_MORELINE)
David Lamparter4715a532013-05-30 16:31:49 +02001662 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001663 1, erase, 0);
1664 else
David Lamparter4715a532013-05-30 16:31:49 +02001665 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001666 vty->lines >= 0 ? vty->lines :
1667 vty->height,
1668 erase, 0);
1669 switch (flushrc)
1670 {
1671 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001672 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001673 zlog_warn("buffer_flush failed on vty client fd %d, closing",
1674 vty->fd);
1675 buffer_reset(vty->obuf);
1676 vty_close(vty);
1677 return 0;
1678 case BUFFER_EMPTY:
1679 if (vty->status == VTY_CLOSE)
1680 vty_close (vty);
paul718e3742002-12-13 20:15:29 +00001681 else
1682 {
ajs9fc7ebf2005-02-23 15:12:34 +00001683 vty->status = VTY_NORMAL;
paul718e3742002-12-13 20:15:29 +00001684 if (vty->lines == 0)
ajs9fc7ebf2005-02-23 15:12:34 +00001685 vty_event (VTY_READ, vty_sock, vty);
paul718e3742002-12-13 20:15:29 +00001686 }
ajs9fc7ebf2005-02-23 15:12:34 +00001687 break;
1688 case BUFFER_PENDING:
1689 /* There is more data waiting to be written. */
1690 vty->status = VTY_MORE;
1691 if (vty->lines == 0)
1692 vty_event (VTY_WRITE, vty_sock, vty);
1693 break;
1694 }
paul718e3742002-12-13 20:15:29 +00001695
1696 return 0;
1697}
1698
David Lamparterba5dc5e2013-05-30 16:33:45 +02001699/* allocate and initialise vty */
1700static struct vty *
1701vty_new_init (int vty_sock)
1702{
1703 struct vty *vty;
1704
1705 vty = vty_new ();
1706 vty->fd = vty_sock;
1707 vty->wfd = vty_sock;
1708 vty->type = VTY_TERM;
1709 vty->node = AUTH_NODE;
1710 vty->fail = 0;
1711 vty->cp = 0;
1712 vty_clear_buf (vty);
1713 vty->length = 0;
1714 memset (vty->hist, 0, sizeof (vty->hist));
1715 vty->hp = 0;
1716 vty->hindex = 0;
1717 vector_set_index (vtyvec, vty_sock, vty);
1718 vty->status = VTY_NORMAL;
1719 vty->lines = -1;
1720 vty->iac = 0;
1721 vty->iac_sb_in_progress = 0;
1722 vty->sb_len = 0;
1723
1724 return vty;
1725}
1726
paul718e3742002-12-13 20:15:29 +00001727/* Create new vty structure. */
ajs9fc7ebf2005-02-23 15:12:34 +00001728static struct vty *
paul718e3742002-12-13 20:15:29 +00001729vty_create (int vty_sock, union sockunion *su)
1730{
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001731 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001732 struct vty *vty;
1733
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001734 sockunion2str(su, buf, SU_ADDRSTRLEN);
1735
paul718e3742002-12-13 20:15:29 +00001736 /* Allocate new vty structure and set up default values. */
David Lamparterba5dc5e2013-05-30 16:33:45 +02001737 vty = vty_new_init (vty_sock);
1738
1739 /* configurable parameters not part of basic init */
1740 vty->v_timeout = vty_timeout_val;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001741 strcpy (vty->address, buf);
paul718e3742002-12-13 20:15:29 +00001742 if (no_password_check)
1743 {
Paul Jakma62687ff2008-08-23 14:27:06 +01001744 if (restricted_mode)
1745 vty->node = RESTRICTED_NODE;
1746 else if (host.advanced)
paul718e3742002-12-13 20:15:29 +00001747 vty->node = ENABLE_NODE;
1748 else
1749 vty->node = VIEW_NODE;
1750 }
paul718e3742002-12-13 20:15:29 +00001751 if (host.lines >= 0)
1752 vty->lines = host.lines;
paul718e3742002-12-13 20:15:29 +00001753
1754 if (! no_password_check)
1755 {
1756 /* Vty is not available if password isn't set. */
1757 if (host.password == NULL && host.password_encrypt == NULL)
1758 {
1759 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1760 vty->status = VTY_CLOSE;
1761 vty_close (vty);
1762 return NULL;
1763 }
1764 }
1765
1766 /* Say hello to the world. */
1767 vty_hello (vty);
1768 if (! no_password_check)
1769 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1770
1771 /* Setting up terminal. */
1772 vty_will_echo (vty);
1773 vty_will_suppress_go_ahead (vty);
1774
1775 vty_dont_linemode (vty);
1776 vty_do_window_size (vty);
1777 /* vty_dont_lflow_ahead (vty); */
1778
1779 vty_prompt (vty);
1780
1781 /* Add read/write thread. */
1782 vty_event (VTY_WRITE, vty_sock, vty);
1783 vty_event (VTY_READ, vty_sock, vty);
1784
1785 return vty;
1786}
1787
David Lamparterba5dc5e2013-05-30 16:33:45 +02001788/* create vty for stdio */
David Lamparterba53a8f2015-05-05 11:04:46 +02001789static struct termios stdio_orig_termios;
1790static struct vty *stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001791static void (*stdio_vty_atclose)(void);
David Lamparterba53a8f2015-05-05 11:04:46 +02001792
1793static void
1794vty_stdio_reset (void)
1795{
1796 if (stdio_vty)
1797 {
1798 tcsetattr (0, TCSANOW, &stdio_orig_termios);
1799 stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001800
1801 if (stdio_vty_atclose)
1802 stdio_vty_atclose ();
1803 stdio_vty_atclose = NULL;
David Lamparterba53a8f2015-05-05 11:04:46 +02001804 }
1805}
1806
David Lamparterba5dc5e2013-05-30 16:33:45 +02001807struct vty *
David Lamparter464ccf32015-05-12 21:56:18 +02001808vty_stdio (void (*atclose)())
David Lamparterba5dc5e2013-05-30 16:33:45 +02001809{
1810 struct vty *vty;
David Lamparterba53a8f2015-05-05 11:04:46 +02001811 struct termios termios;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001812
David Lamparterba53a8f2015-05-05 11:04:46 +02001813 /* refuse creating two vtys on stdio */
1814 if (stdio_vty)
1815 return NULL;
1816
1817 vty = stdio_vty = vty_new_init (0);
David Lamparter464ccf32015-05-12 21:56:18 +02001818 stdio_vty_atclose = atclose;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001819 vty->wfd = 1;
1820
1821 /* always have stdio vty in a known _unchangeable_ state, don't want config
1822 * to have any effect here to make sure scripting this works as intended */
1823 vty->node = ENABLE_NODE;
1824 vty->v_timeout = 0;
1825 strcpy (vty->address, "console");
1826
David Lamparterba53a8f2015-05-05 11:04:46 +02001827 if (!tcgetattr (0, &stdio_orig_termios))
1828 {
1829 termios = stdio_orig_termios;
1830 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
1831 | INLCR | IGNCR | ICRNL | IXON);
1832 termios.c_oflag &= ~OPOST;
1833 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
1834 termios.c_cflag &= ~(CSIZE | PARENB);
1835 termios.c_cflag |= CS8;
1836 tcsetattr (0, TCSANOW, &termios);
1837 }
1838
David Lamparterba5dc5e2013-05-30 16:33:45 +02001839 vty_prompt (vty);
1840
1841 /* Add read/write thread. */
1842 vty_event (VTY_WRITE, 1, vty);
1843 vty_event (VTY_READ, 0, vty);
1844
1845 return vty;
1846}
1847
paul718e3742002-12-13 20:15:29 +00001848/* Accept connection from the network. */
1849static int
1850vty_accept (struct thread *thread)
1851{
1852 int vty_sock;
paul718e3742002-12-13 20:15:29 +00001853 union sockunion su;
1854 int ret;
1855 unsigned int on;
1856 int accept_sock;
Timo Teräsc1c69e42015-05-22 13:40:57 +03001857 struct prefix p;
paul718e3742002-12-13 20:15:29 +00001858 struct access_list *acl = NULL;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001859 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001860
1861 accept_sock = THREAD_FD (thread);
1862
1863 /* We continue hearing vty socket. */
1864 vty_event (VTY_SERV, accept_sock, NULL);
1865
1866 memset (&su, 0, sizeof (union sockunion));
1867
1868 /* We can handle IPv4 or IPv6 socket. */
1869 vty_sock = sockunion_accept (accept_sock, &su);
1870 if (vty_sock < 0)
1871 {
ajs6099b3b2004-11-20 02:06:59 +00001872 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001873 return -1;
1874 }
ajs9fc7ebf2005-02-23 15:12:34 +00001875 set_nonblocking(vty_sock);
paul718e3742002-12-13 20:15:29 +00001876
Timo Teräsc1c69e42015-05-22 13:40:57 +03001877 sockunion2hostprefix (&su, &p);
paul718e3742002-12-13 20:15:29 +00001878
1879 /* VTY's accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001880 if (p.family == AF_INET && vty_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001881 {
1882 if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001883 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001884 {
paul718e3742002-12-13 20:15:29 +00001885 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001886 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001887 close (vty_sock);
1888
1889 /* continue accepting connections */
1890 vty_event (VTY_SERV, accept_sock, NULL);
1891
paul718e3742002-12-13 20:15:29 +00001892 return 0;
1893 }
1894 }
1895
1896#ifdef HAVE_IPV6
1897 /* VTY's ipv6 accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001898 if (p.family == AF_INET6 && vty_ipv6_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001899 {
1900 if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001901 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001902 {
paul718e3742002-12-13 20:15:29 +00001903 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001904 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001905 close (vty_sock);
1906
1907 /* continue accepting connections */
1908 vty_event (VTY_SERV, accept_sock, NULL);
1909
paul718e3742002-12-13 20:15:29 +00001910 return 0;
1911 }
1912 }
1913#endif /* HAVE_IPV6 */
1914
paul718e3742002-12-13 20:15:29 +00001915 on = 1;
1916 ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
1917 (char *) &on, sizeof (on));
1918 if (ret < 0)
1919 zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
ajs6099b3b2004-11-20 02:06:59 +00001920 safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001921
heasley78e6cd92009-12-07 16:41:14 +03001922 zlog (NULL, LOG_INFO, "Vty connection from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001923 sockunion2str (&su, buf, SU_ADDRSTRLEN));
heasley78e6cd92009-12-07 16:41:14 +03001924
Stephen Hemminger9206f9e2011-12-18 19:43:40 +04001925 vty_create (vty_sock, &su);
paul718e3742002-12-13 20:15:29 +00001926
1927 return 0;
1928}
1929
David Lamparter6d6df302014-06-28 21:12:37 +02001930#ifdef HAVE_IPV6
ajs9fc7ebf2005-02-23 15:12:34 +00001931static void
paul718e3742002-12-13 20:15:29 +00001932vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
1933{
1934 int ret;
1935 struct addrinfo req;
1936 struct addrinfo *ainfo;
1937 struct addrinfo *ainfo_save;
1938 int sock;
1939 char port_str[BUFSIZ];
1940
1941 memset (&req, 0, sizeof (struct addrinfo));
1942 req.ai_flags = AI_PASSIVE;
1943 req.ai_family = AF_UNSPEC;
1944 req.ai_socktype = SOCK_STREAM;
1945 sprintf (port_str, "%d", port);
1946 port_str[sizeof (port_str) - 1] = '\0';
1947
1948 ret = getaddrinfo (hostname, port_str, &req, &ainfo);
1949
1950 if (ret != 0)
1951 {
1952 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
1953 exit (1);
1954 }
1955
1956 ainfo_save = ainfo;
1957
1958 do
1959 {
1960 if (ainfo->ai_family != AF_INET
1961#ifdef HAVE_IPV6
1962 && ainfo->ai_family != AF_INET6
1963#endif /* HAVE_IPV6 */
1964 )
1965 continue;
1966
1967 sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
1968 if (sock < 0)
1969 continue;
1970
David Lamparterca051262009-10-04 16:21:49 +02001971 sockopt_v6only (ainfo->ai_family, sock);
paul718e3742002-12-13 20:15:29 +00001972 sockopt_reuseaddr (sock);
1973 sockopt_reuseport (sock);
1974
1975 ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
1976 if (ret < 0)
1977 {
1978 close (sock); /* Avoid sd leak. */
1979 continue;
1980 }
1981
1982 ret = listen (sock, 3);
1983 if (ret < 0)
1984 {
1985 close (sock); /* Avoid sd leak. */
1986 continue;
1987 }
1988
1989 vty_event (VTY_SERV, sock, NULL);
1990 }
1991 while ((ainfo = ainfo->ai_next) != NULL);
1992
1993 freeaddrinfo (ainfo_save);
1994}
David Lamparter6d6df302014-06-28 21:12:37 +02001995#else /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00001996
1997/* Make vty server socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00001998static void
paul29db05b2003-05-08 20:10:22 +00001999vty_serv_sock_family (const char* addr, unsigned short port, int family)
paul718e3742002-12-13 20:15:29 +00002000{
2001 int ret;
2002 union sockunion su;
2003 int accept_sock;
paul29db05b2003-05-08 20:10:22 +00002004 void* naddr=NULL;
paul718e3742002-12-13 20:15:29 +00002005
2006 memset (&su, 0, sizeof (union sockunion));
2007 su.sa.sa_family = family;
paul29db05b2003-05-08 20:10:22 +00002008 if(addr)
2009 switch(family)
2010 {
2011 case AF_INET:
2012 naddr=&su.sin.sin_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00002013 break;
paul29db05b2003-05-08 20:10:22 +00002014#ifdef HAVE_IPV6
2015 case AF_INET6:
2016 naddr=&su.sin6.sin6_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00002017 break;
paul29db05b2003-05-08 20:10:22 +00002018#endif
2019 }
2020
2021 if(naddr)
2022 switch(inet_pton(family,addr,naddr))
2023 {
2024 case -1:
2025 zlog_err("bad address %s",addr);
2026 naddr=NULL;
2027 break;
2028 case 0:
ajs6099b3b2004-11-20 02:06:59 +00002029 zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
paul29db05b2003-05-08 20:10:22 +00002030 naddr=NULL;
2031 }
paul718e3742002-12-13 20:15:29 +00002032
2033 /* Make new socket. */
2034 accept_sock = sockunion_stream_socket (&su);
2035 if (accept_sock < 0)
2036 return;
2037
2038 /* This is server, so reuse address. */
2039 sockopt_reuseaddr (accept_sock);
2040 sockopt_reuseport (accept_sock);
2041
2042 /* Bind socket to universal address and given port. */
paul29db05b2003-05-08 20:10:22 +00002043 ret = sockunion_bind (accept_sock, &su, port, naddr);
paul718e3742002-12-13 20:15:29 +00002044 if (ret < 0)
2045 {
paul29db05b2003-05-08 20:10:22 +00002046 zlog_warn("can't bind socket");
paul718e3742002-12-13 20:15:29 +00002047 close (accept_sock); /* Avoid sd leak. */
2048 return;
2049 }
2050
2051 /* Listen socket under queue 3. */
2052 ret = listen (accept_sock, 3);
2053 if (ret < 0)
2054 {
2055 zlog (NULL, LOG_WARNING, "can't listen socket");
2056 close (accept_sock); /* Avoid sd leak. */
2057 return;
2058 }
2059
2060 /* Add vty server event. */
2061 vty_event (VTY_SERV, accept_sock, NULL);
2062}
David Lamparter6d6df302014-06-28 21:12:37 +02002063#endif /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00002064
2065#ifdef VTYSH
2066/* For sockaddr_un. */
2067#include <sys/un.h>
2068
2069/* VTY shell UNIX domain socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00002070static void
hasso6ad96ea2004-10-07 19:33:46 +00002071vty_serv_un (const char *path)
paul718e3742002-12-13 20:15:29 +00002072{
2073 int ret;
paul75e15fe2004-10-31 02:13:09 +00002074 int sock, len;
paul718e3742002-12-13 20:15:29 +00002075 struct sockaddr_un serv;
2076 mode_t old_mask;
pauledd7c242003-06-04 13:59:38 +00002077 struct zprivs_ids_t ids;
2078
paul718e3742002-12-13 20:15:29 +00002079 /* First of all, unlink existing socket */
2080 unlink (path);
2081
2082 /* Set umask */
paul1921e6f2003-05-23 08:12:36 +00002083 old_mask = umask (0007);
paul718e3742002-12-13 20:15:29 +00002084
2085 /* Make UNIX domain socket. */
2086 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2087 if (sock < 0)
2088 {
ajs6a52d0d2005-01-30 18:49:28 +00002089 zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002090 return;
2091 }
2092
2093 /* Make server socket. */
2094 memset (&serv, 0, sizeof (struct sockaddr_un));
2095 serv.sun_family = AF_UNIX;
2096 strncpy (serv.sun_path, path, strlen (path));
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002097#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
paul718e3742002-12-13 20:15:29 +00002098 len = serv.sun_len = SUN_LEN(&serv);
2099#else
2100 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002101#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
paul718e3742002-12-13 20:15:29 +00002102
2103 ret = bind (sock, (struct sockaddr *) &serv, len);
2104 if (ret < 0)
2105 {
ajs6a52d0d2005-01-30 18:49:28 +00002106 zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002107 close (sock); /* Avoid sd leak. */
2108 return;
2109 }
2110
2111 ret = listen (sock, 5);
2112 if (ret < 0)
2113 {
ajs6a52d0d2005-01-30 18:49:28 +00002114 zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002115 close (sock); /* Avoid sd leak. */
2116 return;
2117 }
2118
2119 umask (old_mask);
2120
pauledd7c242003-06-04 13:59:38 +00002121 zprivs_get_ids(&ids);
2122
2123 if (ids.gid_vty > 0)
2124 {
2125 /* set group of socket */
2126 if ( chown (path, -1, ids.gid_vty) )
2127 {
2128 zlog_err ("vty_serv_un: could chown socket, %s",
ajs6099b3b2004-11-20 02:06:59 +00002129 safe_strerror (errno) );
pauledd7c242003-06-04 13:59:38 +00002130 }
2131 }
2132
paul718e3742002-12-13 20:15:29 +00002133 vty_event (VTYSH_SERV, sock, NULL);
2134}
2135
2136/* #define VTYSH_DEBUG 1 */
2137
2138static int
2139vtysh_accept (struct thread *thread)
2140{
2141 int accept_sock;
2142 int sock;
2143 int client_len;
2144 struct sockaddr_un client;
2145 struct vty *vty;
2146
2147 accept_sock = THREAD_FD (thread);
2148
2149 vty_event (VTYSH_SERV, accept_sock, NULL);
2150
2151 memset (&client, 0, sizeof (struct sockaddr_un));
2152 client_len = sizeof (struct sockaddr_un);
2153
hassoe473b032004-09-26 16:08:11 +00002154 sock = accept (accept_sock, (struct sockaddr *) &client,
2155 (socklen_t *) &client_len);
paul718e3742002-12-13 20:15:29 +00002156
2157 if (sock < 0)
2158 {
ajs6099b3b2004-11-20 02:06:59 +00002159 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00002160 return -1;
2161 }
2162
ajs9fc7ebf2005-02-23 15:12:34 +00002163 if (set_nonblocking(sock) < 0)
paul75e15fe2004-10-31 02:13:09 +00002164 {
ajs9fc7ebf2005-02-23 15:12:34 +00002165 zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
2166 " %s, closing", sock, safe_strerror (errno));
paul75e15fe2004-10-31 02:13:09 +00002167 close (sock);
2168 return -1;
2169 }
pauldccfb192004-10-29 08:29:36 +00002170
paul718e3742002-12-13 20:15:29 +00002171#ifdef VTYSH_DEBUG
2172 printf ("VTY shell accept\n");
2173#endif /* VTYSH_DEBUG */
2174
2175 vty = vty_new ();
2176 vty->fd = sock;
David Lamparter4715a532013-05-30 16:31:49 +02002177 vty->wfd = sock;
paul718e3742002-12-13 20:15:29 +00002178 vty->type = VTY_SHELL_SERV;
2179 vty->node = VIEW_NODE;
2180
2181 vty_event (VTYSH_READ, sock, vty);
2182
2183 return 0;
2184}
2185
2186static int
ajs9fc7ebf2005-02-23 15:12:34 +00002187vtysh_flush(struct vty *vty)
2188{
David Lamparter4715a532013-05-30 16:31:49 +02002189 switch (buffer_flush_available(vty->obuf, vty->wfd))
ajs9fc7ebf2005-02-23 15:12:34 +00002190 {
2191 case BUFFER_PENDING:
David Lamparter4715a532013-05-30 16:31:49 +02002192 vty_event(VTYSH_WRITE, vty->wfd, vty);
ajs9fc7ebf2005-02-23 15:12:34 +00002193 break;
2194 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002195 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002196 zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
2197 buffer_reset(vty->obuf);
2198 vty_close(vty);
2199 return -1;
2200 break;
2201 case BUFFER_EMPTY:
2202 break;
2203 }
2204 return 0;
2205}
2206
2207static int
paul718e3742002-12-13 20:15:29 +00002208vtysh_read (struct thread *thread)
2209{
2210 int ret;
2211 int sock;
2212 int nbytes;
2213 struct vty *vty;
2214 unsigned char buf[VTY_READ_BUFSIZ];
ajs9fc7ebf2005-02-23 15:12:34 +00002215 unsigned char *p;
paul718e3742002-12-13 20:15:29 +00002216 u_char header[4] = {0, 0, 0, 0};
2217
2218 sock = THREAD_FD (thread);
2219 vty = THREAD_ARG (thread);
2220 vty->t_read = NULL;
2221
ajs9fc7ebf2005-02-23 15:12:34 +00002222 if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
paul718e3742002-12-13 20:15:29 +00002223 {
ajs9fc7ebf2005-02-23 15:12:34 +00002224 if (nbytes < 0)
2225 {
2226 if (ERRNO_IO_RETRY(errno))
2227 {
2228 vty_event (VTYSH_READ, sock, vty);
2229 return 0;
2230 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002231 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002232 zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
2233 __func__, sock, safe_strerror(errno));
2234 }
2235 buffer_reset(vty->obuf);
paul718e3742002-12-13 20:15:29 +00002236 vty_close (vty);
2237#ifdef VTYSH_DEBUG
2238 printf ("close vtysh\n");
2239#endif /* VTYSH_DEBUG */
2240 return 0;
2241 }
2242
2243#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002244 printf ("line: %.*s\n", nbytes, buf);
paul718e3742002-12-13 20:15:29 +00002245#endif /* VTYSH_DEBUG */
2246
Paul Jakma7d662842017-01-19 10:39:23 +00002247 if (vty->length + nbytes >= vty->max)
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002248 {
2249 /* Clear command line buffer. */
2250 vty->cp = vty->length = 0;
2251 vty_clear_buf (vty);
2252 vty_out (vty, "%% Command is too long.%s", VTY_NEWLINE);
2253 goto out;
2254 }
2255
ajs9fc7ebf2005-02-23 15:12:34 +00002256 for (p = buf; p < buf+nbytes; p++)
2257 {
ajs9fc7ebf2005-02-23 15:12:34 +00002258 vty->buf[vty->length++] = *p;
2259 if (*p == '\0')
2260 {
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002261
ajs9fc7ebf2005-02-23 15:12:34 +00002262 /* Pass this line to parser. */
2263 ret = vty_execute (vty);
2264 /* Note that vty_execute clears the command buffer and resets
2265 vty->length to 0. */
paul718e3742002-12-13 20:15:29 +00002266
ajs9fc7ebf2005-02-23 15:12:34 +00002267 /* Return result. */
paul718e3742002-12-13 20:15:29 +00002268#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002269 printf ("result: %d\n", ret);
2270 printf ("vtysh node: %d\n", vty->node);
paul718e3742002-12-13 20:15:29 +00002271#endif /* VTYSH_DEBUG */
2272
ajs9fc7ebf2005-02-23 15:12:34 +00002273 header[3] = ret;
2274 buffer_put(vty->obuf, header, 4);
paul718e3742002-12-13 20:15:29 +00002275
ajs9fc7ebf2005-02-23 15:12:34 +00002276 if (!vty->t_write && (vtysh_flush(vty) < 0))
2277 /* Try to flush results; exit if a write error occurs. */
2278 return 0;
2279 }
2280 }
2281
Quentin Youngb7ceefe2017-01-10 23:33:50 +00002282out:
paul718e3742002-12-13 20:15:29 +00002283 vty_event (VTYSH_READ, sock, vty);
2284
2285 return 0;
2286}
ajs49ff6d92004-11-04 19:26:16 +00002287
2288static int
2289vtysh_write (struct thread *thread)
2290{
2291 struct vty *vty = THREAD_ARG (thread);
2292
2293 vty->t_write = NULL;
ajs9fc7ebf2005-02-23 15:12:34 +00002294 vtysh_flush(vty);
ajs976d8c72004-11-10 15:40:09 +00002295 return 0;
ajs49ff6d92004-11-04 19:26:16 +00002296}
2297
paul718e3742002-12-13 20:15:29 +00002298#endif /* VTYSH */
2299
2300/* Determine address family to bind. */
2301void
hasso6ad96ea2004-10-07 19:33:46 +00002302vty_serv_sock (const char *addr, unsigned short port, const char *path)
paul718e3742002-12-13 20:15:29 +00002303{
2304 /* If port is set to 0, do not listen on TCP/IP at all! */
2305 if (port)
2306 {
2307
2308#ifdef HAVE_IPV6
paul29db05b2003-05-08 20:10:22 +00002309 vty_serv_sock_addrinfo (addr, port);
paul718e3742002-12-13 20:15:29 +00002310#else /* ! HAVE_IPV6 */
paul29db05b2003-05-08 20:10:22 +00002311 vty_serv_sock_family (addr,port, AF_INET);
paul718e3742002-12-13 20:15:29 +00002312#endif /* HAVE_IPV6 */
2313 }
2314
2315#ifdef VTYSH
2316 vty_serv_un (path);
2317#endif /* VTYSH */
2318}
2319
Andrew J. Schorr9d0a3262006-07-11 00:06:49 +00002320/* Close vty interface. Warning: call this only from functions that
2321 will be careful not to access the vty afterwards (since it has
2322 now been freed). This is safest from top-level functions (called
2323 directly by the thread dispatcher). */
paul718e3742002-12-13 20:15:29 +00002324void
2325vty_close (struct vty *vty)
2326{
2327 int i;
2328
2329 /* Cancel threads.*/
2330 if (vty->t_read)
2331 thread_cancel (vty->t_read);
2332 if (vty->t_write)
2333 thread_cancel (vty->t_write);
2334 if (vty->t_timeout)
2335 thread_cancel (vty->t_timeout);
paul718e3742002-12-13 20:15:29 +00002336
2337 /* Flush buffer. */
David Lamparter4715a532013-05-30 16:31:49 +02002338 buffer_flush_all (vty->obuf, vty->wfd);
paul718e3742002-12-13 20:15:29 +00002339
2340 /* Free input buffer. */
2341 buffer_free (vty->obuf);
2342
paul718e3742002-12-13 20:15:29 +00002343 /* Free command history. */
2344 for (i = 0; i < VTY_MAXHIST; i++)
2345 if (vty->hist[i])
2346 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2347
2348 /* Unset vector. */
2349 vector_unset (vtyvec, vty->fd);
2350
2351 /* Close socket. */
2352 if (vty->fd > 0)
2353 close (vty->fd);
David Lamparterba53a8f2015-05-05 11:04:46 +02002354 else
2355 vty_stdio_reset ();
paul718e3742002-12-13 20:15:29 +00002356
paul718e3742002-12-13 20:15:29 +00002357 if (vty->buf)
2358 XFREE (MTYPE_VTY, vty->buf);
2359
2360 /* Check configure. */
2361 vty_config_unlock (vty);
2362
2363 /* OK free vty. */
2364 XFREE (MTYPE_VTY, vty);
2365}
2366
2367/* When time out occur output message then close connection. */
2368static int
2369vty_timeout (struct thread *thread)
2370{
2371 struct vty *vty;
2372
2373 vty = THREAD_ARG (thread);
2374 vty->t_timeout = NULL;
2375 vty->v_timeout = 0;
2376
2377 /* Clear buffer*/
2378 buffer_reset (vty->obuf);
2379 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2380
2381 /* Close connection. */
2382 vty->status = VTY_CLOSE;
2383 vty_close (vty);
2384
2385 return 0;
2386}
2387
2388/* Read up configuration file from file_name. */
2389static void
2390vty_read_file (FILE *confp)
2391{
2392 int ret;
2393 struct vty *vty;
Steve Hillea555002009-07-28 16:36:14 -04002394 unsigned int line_num = 0;
paul718e3742002-12-13 20:15:29 +00002395
2396 vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02002397 vty->wfd = dup(STDERR_FILENO); /* vty_close() will close this */
2398 if (vty->wfd < 0)
Steve Hillea555002009-07-28 16:36:14 -04002399 {
2400 /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
David Lamparter4715a532013-05-30 16:31:49 +02002401 vty->wfd = STDOUT_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002402 }
David Lamparter4715a532013-05-30 16:31:49 +02002403 vty->fd = STDIN_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002404 vty->type = VTY_FILE;
paul718e3742002-12-13 20:15:29 +00002405 vty->node = CONFIG_NODE;
2406
2407 /* Execute configuration file */
Steve Hillea555002009-07-28 16:36:14 -04002408 ret = config_from_file (vty, confp, &line_num);
2409
2410 /* Flush any previous errors before printing messages below */
2411 buffer_flush_all (vty->obuf, vty->fd);
paul718e3742002-12-13 20:15:29 +00002412
paul7021c422003-07-15 12:52:22 +00002413 if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
paul718e3742002-12-13 20:15:29 +00002414 {
2415 switch (ret)
paul7021c422003-07-15 12:52:22 +00002416 {
2417 case CMD_ERR_AMBIGUOUS:
Steve Hillea555002009-07-28 16:36:14 -04002418 fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
paul7021c422003-07-15 12:52:22 +00002419 break;
2420 case CMD_ERR_NO_MATCH:
Steve Hillea555002009-07-28 16:36:14 -04002421 fprintf (stderr, "*** Error reading config: There is no such command.\n");
paul7021c422003-07-15 12:52:22 +00002422 break;
2423 }
Steve Hillea555002009-07-28 16:36:14 -04002424 fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
2425 line_num, vty->buf);
paul718e3742002-12-13 20:15:29 +00002426 vty_close (vty);
2427 exit (1);
2428 }
2429
2430 vty_close (vty);
2431}
2432
ajs9fc7ebf2005-02-23 15:12:34 +00002433static FILE *
paul718e3742002-12-13 20:15:29 +00002434vty_use_backup_config (char *fullpath)
2435{
2436 char *fullpath_sav, *fullpath_tmp;
2437 FILE *ret = NULL;
2438 struct stat buf;
2439 int tmp, sav;
2440 int c;
2441 char buffer[512];
2442
2443 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2444 strcpy (fullpath_sav, fullpath);
2445 strcat (fullpath_sav, CONF_BACKUP_EXT);
2446 if (stat (fullpath_sav, &buf) == -1)
2447 {
2448 free (fullpath_sav);
2449 return NULL;
2450 }
2451
2452 fullpath_tmp = malloc (strlen (fullpath) + 8);
2453 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2454
2455 /* Open file to configuration write. */
2456 tmp = mkstemp (fullpath_tmp);
2457 if (tmp < 0)
2458 {
2459 free (fullpath_sav);
2460 free (fullpath_tmp);
2461 return NULL;
2462 }
2463
2464 sav = open (fullpath_sav, O_RDONLY);
2465 if (sav < 0)
2466 {
gdt3dbf9962003-12-22 20:18:18 +00002467 unlink (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002468 free (fullpath_sav);
2469 free (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002470 return NULL;
2471 }
2472
2473 while((c = read (sav, buffer, 512)) > 0)
2474 write (tmp, buffer, c);
2475
2476 close (sav);
2477 close (tmp);
2478
gdtaa593d52003-12-22 20:15:53 +00002479 if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
2480 {
gdt3dbf9962003-12-22 20:18:18 +00002481 unlink (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002482 free (fullpath_sav);
2483 free (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002484 return NULL;
2485 }
2486
paul718e3742002-12-13 20:15:29 +00002487 if (link (fullpath_tmp, fullpath) == 0)
2488 ret = fopen (fullpath, "r");
2489
2490 unlink (fullpath_tmp);
2491
2492 free (fullpath_sav);
2493 free (fullpath_tmp);
hasso12f6ea22005-03-07 08:35:39 +00002494 return ret;
paul718e3742002-12-13 20:15:29 +00002495}
2496
2497/* Read up configuration file from file_name. */
2498void
2499vty_read_config (char *config_file,
hasso320ec102004-06-20 19:54:37 +00002500 char *config_default_dir)
paul718e3742002-12-13 20:15:29 +00002501{
paulccc92352003-10-22 02:49:38 +00002502 char cwd[MAXPATHLEN];
paul718e3742002-12-13 20:15:29 +00002503 FILE *confp = NULL;
2504 char *fullpath;
paul05865c92005-10-26 05:49:54 +00002505 char *tmp = NULL;
paul718e3742002-12-13 20:15:29 +00002506
2507 /* If -f flag specified. */
2508 if (config_file != NULL)
2509 {
2510 if (! IS_DIRECTORY_SEP (config_file[0]))
hasso320ec102004-06-20 19:54:37 +00002511 {
2512 getcwd (cwd, MAXPATHLEN);
paul05865c92005-10-26 05:49:54 +00002513 tmp = XMALLOC (MTYPE_TMP,
hasso320ec102004-06-20 19:54:37 +00002514 strlen (cwd) + strlen (config_file) + 2);
paul05865c92005-10-26 05:49:54 +00002515 sprintf (tmp, "%s/%s", cwd, config_file);
2516 fullpath = tmp;
hasso320ec102004-06-20 19:54:37 +00002517 }
paul718e3742002-12-13 20:15:29 +00002518 else
hasso320ec102004-06-20 19:54:37 +00002519 fullpath = config_file;
paul718e3742002-12-13 20:15:29 +00002520
2521 confp = fopen (fullpath, "r");
2522
2523 if (confp == NULL)
hasso320ec102004-06-20 19:54:37 +00002524 {
paul3d1dc852005-04-05 00:45:23 +00002525 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2526 __func__, fullpath, safe_strerror (errno));
2527
hasso320ec102004-06-20 19:54:37 +00002528 confp = vty_use_backup_config (fullpath);
2529 if (confp)
2530 fprintf (stderr, "WARNING: using backup configuration file!\n");
2531 else
2532 {
2533 fprintf (stderr, "can't open configuration file [%s]\n",
paul3d1dc852005-04-05 00:45:23 +00002534 config_file);
hasso320ec102004-06-20 19:54:37 +00002535 exit(1);
2536 }
2537 }
paul718e3742002-12-13 20:15:29 +00002538 }
2539 else
2540 {
paul718e3742002-12-13 20:15:29 +00002541#ifdef VTYSH
hasso320ec102004-06-20 19:54:37 +00002542 int ret;
2543 struct stat conf_stat;
paul718e3742002-12-13 20:15:29 +00002544
hasso320ec102004-06-20 19:54:37 +00002545 /* !!!!PLEASE LEAVE!!!!
2546 * This is NEEDED for use with vtysh -b, or else you can get
2547 * a real configuration food fight with a lot garbage in the
2548 * merged configuration file it creates coming from the per
2549 * daemon configuration files. This also allows the daemons
2550 * to start if there default configuration file is not
2551 * present or ignore them, as needed when using vtysh -b to
2552 * configure the daemons at boot - MAG
2553 */
paul718e3742002-12-13 20:15:29 +00002554
hasso320ec102004-06-20 19:54:37 +00002555 /* Stat for vtysh Zebra.conf, if found startup and wait for
2556 * boot configuration
2557 */
paul718e3742002-12-13 20:15:29 +00002558
hasso320ec102004-06-20 19:54:37 +00002559 if ( strstr(config_default_dir, "vtysh") == NULL)
2560 {
2561 ret = stat (integrate_default, &conf_stat);
2562 if (ret >= 0)
2563 return;
2564 }
paul718e3742002-12-13 20:15:29 +00002565#endif /* VTYSH */
2566
hasso320ec102004-06-20 19:54:37 +00002567 confp = fopen (config_default_dir, "r");
2568 if (confp == NULL)
2569 {
paul3d1dc852005-04-05 00:45:23 +00002570 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2571 __func__, config_default_dir, safe_strerror (errno));
2572
hasso320ec102004-06-20 19:54:37 +00002573 confp = vty_use_backup_config (config_default_dir);
2574 if (confp)
2575 {
2576 fprintf (stderr, "WARNING: using backup configuration file!\n");
2577 fullpath = config_default_dir;
2578 }
2579 else
2580 {
2581 fprintf (stderr, "can't open configuration file [%s]\n",
2582 config_default_dir);
2583 exit (1);
paul3d1dc852005-04-05 00:45:23 +00002584 }
hasso320ec102004-06-20 19:54:37 +00002585 }
paul718e3742002-12-13 20:15:29 +00002586 else
hasso320ec102004-06-20 19:54:37 +00002587 fullpath = config_default_dir;
2588 }
2589
paul718e3742002-12-13 20:15:29 +00002590 vty_read_file (confp);
2591
2592 fclose (confp);
2593
2594 host_config_set (fullpath);
paul05865c92005-10-26 05:49:54 +00002595
2596 if (tmp)
2597 XFREE (MTYPE_TMP, fullpath);
paul718e3742002-12-13 20:15:29 +00002598}
2599
2600/* Small utility function which output log to the VTY. */
2601void
ajs274a4a42004-12-07 15:39:31 +00002602vty_log (const char *level, const char *proto_str,
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002603 const char *format, struct timestamp_control *ctl, va_list va)
paul718e3742002-12-13 20:15:29 +00002604{
hasso8c328f12004-10-05 21:01:23 +00002605 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002606 struct vty *vty;
Paul Jakmaa4b30302006-05-28 08:18:38 +00002607
2608 if (!vtyvec)
2609 return;
paul718e3742002-12-13 20:15:29 +00002610
paul55468c82005-03-14 20:19:01 +00002611 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002612 if ((vty = vector_slot (vtyvec, i)) != NULL)
2613 if (vty->monitor)
ajsd246bd92004-11-23 17:35:08 +00002614 {
2615 va_list ac;
2616 va_copy(ac, va);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002617 vty_log_out (vty, level, proto_str, format, ctl, ac);
ajsd246bd92004-11-23 17:35:08 +00002618 va_end(ac);
2619 }
paul718e3742002-12-13 20:15:29 +00002620}
2621
ajs274a4a42004-12-07 15:39:31 +00002622/* Async-signal-safe version of vty_log for fixed strings. */
2623void
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002624vty_log_fixed (char *buf, size_t len)
ajs274a4a42004-12-07 15:39:31 +00002625{
2626 unsigned int i;
ajs9fc7ebf2005-02-23 15:12:34 +00002627 struct iovec iov[2];
2628
Paul Jakmaa4b30302006-05-28 08:18:38 +00002629 /* vty may not have been initialised */
2630 if (!vtyvec)
2631 return;
2632
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002633 iov[0].iov_base = buf;
ajs9fc7ebf2005-02-23 15:12:34 +00002634 iov[0].iov_len = len;
ajs926fe8f2005-04-08 18:50:40 +00002635 iov[1].iov_base = (void *)"\r\n";
ajs9fc7ebf2005-02-23 15:12:34 +00002636 iov[1].iov_len = 2;
ajs274a4a42004-12-07 15:39:31 +00002637
paul55468c82005-03-14 20:19:01 +00002638 for (i = 0; i < vector_active (vtyvec); i++)
ajs274a4a42004-12-07 15:39:31 +00002639 {
2640 struct vty *vty;
ajs9fc7ebf2005-02-23 15:12:34 +00002641 if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
2642 /* N.B. We don't care about the return code, since process is
2643 most likely just about to die anyway. */
David Lamparter4715a532013-05-30 16:31:49 +02002644 writev(vty->wfd, iov, 2);
ajs274a4a42004-12-07 15:39:31 +00002645 }
2646}
2647
paul718e3742002-12-13 20:15:29 +00002648int
2649vty_config_lock (struct vty *vty)
2650{
2651 if (vty_config == 0)
2652 {
2653 vty->config = 1;
2654 vty_config = 1;
2655 }
2656 return vty->config;
2657}
2658
2659int
2660vty_config_unlock (struct vty *vty)
2661{
2662 if (vty_config == 1 && vty->config == 1)
2663 {
2664 vty->config = 0;
2665 vty_config = 0;
2666 }
2667 return vty->config;
2668}
David Lamparter6b0655a2014-06-04 06:53:35 +02002669
paul718e3742002-12-13 20:15:29 +00002670/* Master of the threads. */
Donald Sharpeeef0db2015-10-14 08:50:38 -04002671static struct thread_master *vty_master;
paul718e3742002-12-13 20:15:29 +00002672
2673static void
2674vty_event (enum event event, int sock, struct vty *vty)
2675{
2676 struct thread *vty_serv_thread;
2677
2678 switch (event)
2679 {
2680 case VTY_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002681 vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
paul718e3742002-12-13 20:15:29 +00002682 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2683 break;
2684#ifdef VTYSH
2685 case VTYSH_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002686 vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
Christian Franke677bcbb2013-02-27 13:47:23 +00002687 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
paul718e3742002-12-13 20:15:29 +00002688 break;
2689 case VTYSH_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002690 vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
ajs49ff6d92004-11-04 19:26:16 +00002691 break;
2692 case VTYSH_WRITE:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002693 vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
paul718e3742002-12-13 20:15:29 +00002694 break;
2695#endif /* VTYSH */
2696 case VTY_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002697 vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
paul718e3742002-12-13 20:15:29 +00002698
2699 /* Time out treatment. */
2700 if (vty->v_timeout)
2701 {
2702 if (vty->t_timeout)
2703 thread_cancel (vty->t_timeout);
2704 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002705 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002706 }
2707 break;
2708 case VTY_WRITE:
2709 if (! vty->t_write)
Donald Sharpeeef0db2015-10-14 08:50:38 -04002710 vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
paul718e3742002-12-13 20:15:29 +00002711 break;
2712 case VTY_TIMEOUT_RESET:
2713 if (vty->t_timeout)
2714 {
2715 thread_cancel (vty->t_timeout);
2716 vty->t_timeout = NULL;
2717 }
2718 if (vty->v_timeout)
2719 {
2720 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002721 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002722 }
2723 break;
2724 }
2725}
David Lamparter6b0655a2014-06-04 06:53:35 +02002726
Paul Jakma0f2f7a32016-06-16 15:40:02 +01002727DEFUN (who,
2728 who_cmd,
paul718e3742002-12-13 20:15:29 +00002729 "who",
2730 "Display who is on vty\n")
2731{
hasso8c328f12004-10-05 21:01:23 +00002732 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002733 struct vty *v;
2734
paul55468c82005-03-14 20:19:01 +00002735 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002736 if ((v = vector_slot (vtyvec, i)) != NULL)
2737 vty_out (vty, "%svty[%d] connected from %s.%s",
2738 v->config ? "*" : " ",
2739 i, v->address, VTY_NEWLINE);
2740 return CMD_SUCCESS;
2741}
2742
2743/* Move to vty configuration mode. */
2744DEFUN (line_vty,
2745 line_vty_cmd,
2746 "line vty",
2747 "Configure a terminal line\n"
2748 "Virtual terminal\n")
2749{
2750 vty->node = VTY_NODE;
2751 return CMD_SUCCESS;
2752}
2753
2754/* Set time out value. */
ajs9fc7ebf2005-02-23 15:12:34 +00002755static int
paul9035efa2004-10-10 11:56:56 +00002756exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
paul718e3742002-12-13 20:15:29 +00002757{
2758 unsigned long timeout = 0;
2759
2760 /* min_str and sec_str are already checked by parser. So it must be
2761 all digit string. */
2762 if (min_str)
2763 {
2764 timeout = strtol (min_str, NULL, 10);
2765 timeout *= 60;
2766 }
2767 if (sec_str)
2768 timeout += strtol (sec_str, NULL, 10);
2769
2770 vty_timeout_val = timeout;
2771 vty->v_timeout = timeout;
2772 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2773
2774
2775 return CMD_SUCCESS;
2776}
2777
2778DEFUN (exec_timeout_min,
2779 exec_timeout_min_cmd,
2780 "exec-timeout <0-35791>",
2781 "Set timeout value\n"
2782 "Timeout value in minutes\n")
2783{
2784 return exec_timeout (vty, argv[0], NULL);
2785}
2786
2787DEFUN (exec_timeout_sec,
2788 exec_timeout_sec_cmd,
2789 "exec-timeout <0-35791> <0-2147483>",
2790 "Set the EXEC timeout\n"
2791 "Timeout in minutes\n"
2792 "Timeout in seconds\n")
2793{
2794 return exec_timeout (vty, argv[0], argv[1]);
2795}
2796
2797DEFUN (no_exec_timeout,
2798 no_exec_timeout_cmd,
2799 "no exec-timeout",
2800 NO_STR
2801 "Set the EXEC timeout\n")
2802{
2803 return exec_timeout (vty, NULL, NULL);
2804}
2805
2806/* Set vty access class. */
2807DEFUN (vty_access_class,
2808 vty_access_class_cmd,
2809 "access-class WORD",
2810 "Filter connections based on an IP access list\n"
2811 "IP access list\n")
2812{
2813 if (vty_accesslist_name)
2814 XFREE(MTYPE_VTY, vty_accesslist_name);
2815
2816 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2817
2818 return CMD_SUCCESS;
2819}
2820
2821/* Clear vty access class. */
2822DEFUN (no_vty_access_class,
2823 no_vty_access_class_cmd,
2824 "no access-class [WORD]",
2825 NO_STR
2826 "Filter connections based on an IP access list\n"
2827 "IP access list\n")
2828{
2829 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2830 {
2831 vty_out (vty, "Access-class is not currently applied to vty%s",
2832 VTY_NEWLINE);
2833 return CMD_WARNING;
2834 }
2835
2836 XFREE(MTYPE_VTY, vty_accesslist_name);
2837
2838 vty_accesslist_name = NULL;
2839
2840 return CMD_SUCCESS;
2841}
2842
2843#ifdef HAVE_IPV6
2844/* Set vty access class. */
2845DEFUN (vty_ipv6_access_class,
2846 vty_ipv6_access_class_cmd,
2847 "ipv6 access-class WORD",
2848 IPV6_STR
2849 "Filter connections based on an IP access list\n"
2850 "IPv6 access list\n")
2851{
2852 if (vty_ipv6_accesslist_name)
2853 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2854
2855 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2856
2857 return CMD_SUCCESS;
2858}
2859
2860/* Clear vty access class. */
2861DEFUN (no_vty_ipv6_access_class,
2862 no_vty_ipv6_access_class_cmd,
2863 "no ipv6 access-class [WORD]",
2864 NO_STR
2865 IPV6_STR
2866 "Filter connections based on an IP access list\n"
2867 "IPv6 access list\n")
2868{
2869 if (! vty_ipv6_accesslist_name ||
2870 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2871 {
2872 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2873 VTY_NEWLINE);
2874 return CMD_WARNING;
2875 }
2876
2877 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2878
2879 vty_ipv6_accesslist_name = NULL;
2880
2881 return CMD_SUCCESS;
2882}
2883#endif /* HAVE_IPV6 */
2884
2885/* vty login. */
2886DEFUN (vty_login,
2887 vty_login_cmd,
2888 "login",
2889 "Enable password checking\n")
2890{
2891 no_password_check = 0;
2892 return CMD_SUCCESS;
2893}
2894
2895DEFUN (no_vty_login,
2896 no_vty_login_cmd,
2897 "no login",
2898 NO_STR
2899 "Enable password checking\n")
2900{
2901 no_password_check = 1;
2902 return CMD_SUCCESS;
2903}
2904
Paul Jakma62687ff2008-08-23 14:27:06 +01002905/* initial mode. */
2906DEFUN (vty_restricted_mode,
2907 vty_restricted_mode_cmd,
2908 "anonymous restricted",
2909 "Restrict view commands available in anonymous, unauthenticated vty\n")
2910{
2911 restricted_mode = 1;
2912 return CMD_SUCCESS;
2913}
2914
2915DEFUN (vty_no_restricted_mode,
2916 vty_no_restricted_mode_cmd,
2917 "no anonymous restricted",
2918 NO_STR
2919 "Enable password checking\n")
2920{
2921 restricted_mode = 0;
2922 return CMD_SUCCESS;
2923}
2924
paul718e3742002-12-13 20:15:29 +00002925DEFUN (service_advanced_vty,
2926 service_advanced_vty_cmd,
2927 "service advanced-vty",
2928 "Set up miscellaneous service\n"
2929 "Enable advanced mode vty interface\n")
2930{
2931 host.advanced = 1;
2932 return CMD_SUCCESS;
2933}
2934
2935DEFUN (no_service_advanced_vty,
2936 no_service_advanced_vty_cmd,
2937 "no service advanced-vty",
2938 NO_STR
2939 "Set up miscellaneous service\n"
2940 "Enable advanced mode vty interface\n")
2941{
2942 host.advanced = 0;
2943 return CMD_SUCCESS;
2944}
2945
2946DEFUN (terminal_monitor,
2947 terminal_monitor_cmd,
2948 "terminal monitor",
2949 "Set terminal line parameters\n"
2950 "Copy debug output to the current terminal line\n")
2951{
2952 vty->monitor = 1;
2953 return CMD_SUCCESS;
2954}
2955
2956DEFUN (terminal_no_monitor,
2957 terminal_no_monitor_cmd,
2958 "terminal no monitor",
2959 "Set terminal line parameters\n"
2960 NO_STR
2961 "Copy debug output to the current terminal line\n")
2962{
2963 vty->monitor = 0;
2964 return CMD_SUCCESS;
2965}
2966
paul789f78a2006-01-17 17:42:03 +00002967ALIAS (terminal_no_monitor,
2968 no_terminal_monitor_cmd,
2969 "no terminal monitor",
2970 NO_STR
2971 "Set terminal line parameters\n"
2972 "Copy debug output to the current terminal line\n")
2973
paul718e3742002-12-13 20:15:29 +00002974DEFUN (show_history,
2975 show_history_cmd,
2976 "show history",
2977 SHOW_STR
2978 "Display the session command history\n")
2979{
2980 int index;
2981
2982 for (index = vty->hindex + 1; index != vty->hindex;)
2983 {
2984 if (index == VTY_MAXHIST)
2985 {
2986 index = 0;
2987 continue;
2988 }
2989
2990 if (vty->hist[index] != NULL)
2991 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
2992
2993 index++;
2994 }
2995
2996 return CMD_SUCCESS;
2997}
2998
Lou Berger86b2a0a2016-05-17 12:19:51 -04002999/* vty login. */
3000DEFUN (log_commands,
3001 log_commands_cmd,
3002 "log commands",
3003 "Logging control\n"
3004 "Log all commands (can't be unset without restart)\n")
3005{
3006 do_log_commands = 1;
3007 return CMD_SUCCESS;
3008}
3009
paul718e3742002-12-13 20:15:29 +00003010/* Display current configuration. */
ajs9fc7ebf2005-02-23 15:12:34 +00003011static int
paul718e3742002-12-13 20:15:29 +00003012vty_config_write (struct vty *vty)
3013{
3014 vty_out (vty, "line vty%s", VTY_NEWLINE);
3015
3016 if (vty_accesslist_name)
3017 vty_out (vty, " access-class %s%s",
3018 vty_accesslist_name, VTY_NEWLINE);
3019
3020 if (vty_ipv6_accesslist_name)
3021 vty_out (vty, " ipv6 access-class %s%s",
3022 vty_ipv6_accesslist_name, VTY_NEWLINE);
3023
3024 /* exec-timeout */
3025 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
3026 vty_out (vty, " exec-timeout %ld %ld%s",
3027 vty_timeout_val / 60,
3028 vty_timeout_val % 60, VTY_NEWLINE);
3029
3030 /* login */
3031 if (no_password_check)
3032 vty_out (vty, " no login%s", VTY_NEWLINE);
Paul Jakma62687ff2008-08-23 14:27:06 +01003033
3034 if (restricted_mode != restricted_mode_default)
3035 {
3036 if (restricted_mode_default)
3037 vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
3038 else
3039 vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
3040 }
3041
Lou Berger86b2a0a2016-05-17 12:19:51 -04003042 if (do_log_commands)
3043 vty_out (vty, "log commands%s", VTY_NEWLINE);
3044
paul718e3742002-12-13 20:15:29 +00003045 vty_out (vty, "!%s", VTY_NEWLINE);
3046
3047 return CMD_SUCCESS;
3048}
3049
3050struct cmd_node vty_node =
3051{
3052 VTY_NODE,
3053 "%s(config-line)# ",
hassoe7168df2004-10-03 20:11:32 +00003054 1,
paul718e3742002-12-13 20:15:29 +00003055};
3056
3057/* Reset all VTY status. */
3058void
3059vty_reset ()
3060{
hasso8c328f12004-10-05 21:01:23 +00003061 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003062 struct vty *vty;
3063 struct thread *vty_serv_thread;
3064
paul55468c82005-03-14 20:19:01 +00003065 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00003066 if ((vty = vector_slot (vtyvec, i)) != NULL)
3067 {
3068 buffer_reset (vty->obuf);
3069 vty->status = VTY_CLOSE;
3070 vty_close (vty);
3071 }
3072
paul55468c82005-03-14 20:19:01 +00003073 for (i = 0; i < vector_active (Vvty_serv_thread); i++)
paul718e3742002-12-13 20:15:29 +00003074 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
3075 {
3076 thread_cancel (vty_serv_thread);
3077 vector_slot (Vvty_serv_thread, i) = NULL;
3078 close (i);
3079 }
3080
3081 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3082
3083 if (vty_accesslist_name)
3084 {
3085 XFREE(MTYPE_VTY, vty_accesslist_name);
3086 vty_accesslist_name = NULL;
3087 }
3088
3089 if (vty_ipv6_accesslist_name)
3090 {
3091 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3092 vty_ipv6_accesslist_name = NULL;
3093 }
3094}
3095
ajs9fc7ebf2005-02-23 15:12:34 +00003096static void
3097vty_save_cwd (void)
paul718e3742002-12-13 20:15:29 +00003098{
paul79ad2792003-10-15 22:09:28 +00003099 char cwd[MAXPATHLEN];
paulccc92352003-10-22 02:49:38 +00003100 char *c;
paul718e3742002-12-13 20:15:29 +00003101
paulccc92352003-10-22 02:49:38 +00003102 c = getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003103
paulccc92352003-10-22 02:49:38 +00003104 if (!c)
paul79ad2792003-10-15 22:09:28 +00003105 {
3106 chdir (SYSCONFDIR);
paulccc92352003-10-22 02:49:38 +00003107 getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003108 }
paul718e3742002-12-13 20:15:29 +00003109
3110 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
3111 strcpy (vty_cwd, cwd);
3112}
3113
3114char *
3115vty_get_cwd ()
3116{
3117 return vty_cwd;
3118}
3119
3120int
3121vty_shell (struct vty *vty)
3122{
3123 return vty->type == VTY_SHELL ? 1 : 0;
3124}
3125
3126int
3127vty_shell_serv (struct vty *vty)
3128{
3129 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3130}
3131
3132void
3133vty_init_vtysh ()
3134{
3135 vtyvec = vector_init (VECTOR_MIN_SIZE);
3136}
3137
3138/* Install vty's own commands like `who' command. */
3139void
paulb21b19c2003-06-15 01:28:29 +00003140vty_init (struct thread_master *master_thread)
paul718e3742002-12-13 20:15:29 +00003141{
3142 /* For further configuration read, preserve current directory. */
3143 vty_save_cwd ();
3144
3145 vtyvec = vector_init (VECTOR_MIN_SIZE);
3146
Donald Sharpeeef0db2015-10-14 08:50:38 -04003147 vty_master = master_thread;
paulb21b19c2003-06-15 01:28:29 +00003148
David Lamparterba53a8f2015-05-05 11:04:46 +02003149 atexit (vty_stdio_reset);
3150
paul718e3742002-12-13 20:15:29 +00003151 /* Initilize server thread vector. */
3152 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
3153
3154 /* Install bgp top node. */
3155 install_node (&vty_node, vty_config_write);
3156
Paul Jakma0f2f7a32016-06-16 15:40:02 +01003157 install_element (RESTRICTED_NODE, &who_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01003158 install_element (RESTRICTED_NODE, &show_history_cmd);
Paul Jakma0f2f7a32016-06-16 15:40:02 +01003159 install_element (VIEW_NODE, &who_cmd);
paul718e3742002-12-13 20:15:29 +00003160 install_element (VIEW_NODE, &show_history_cmd);
paul718e3742002-12-13 20:15:29 +00003161 install_element (CONFIG_NODE, &line_vty_cmd);
3162 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
3163 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
3164 install_element (CONFIG_NODE, &show_history_cmd);
Lou Berger86b2a0a2016-05-17 12:19:51 -04003165 install_element (CONFIG_NODE, &log_commands_cmd);
paul718e3742002-12-13 20:15:29 +00003166 install_element (ENABLE_NODE, &terminal_monitor_cmd);
3167 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
paul789f78a2006-01-17 17:42:03 +00003168 install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00003169
3170 install_default (VTY_NODE);
3171 install_element (VTY_NODE, &exec_timeout_min_cmd);
3172 install_element (VTY_NODE, &exec_timeout_sec_cmd);
3173 install_element (VTY_NODE, &no_exec_timeout_cmd);
3174 install_element (VTY_NODE, &vty_access_class_cmd);
3175 install_element (VTY_NODE, &no_vty_access_class_cmd);
3176 install_element (VTY_NODE, &vty_login_cmd);
3177 install_element (VTY_NODE, &no_vty_login_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01003178 install_element (VTY_NODE, &vty_restricted_mode_cmd);
3179 install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
paul718e3742002-12-13 20:15:29 +00003180#ifdef HAVE_IPV6
3181 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
3182 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
3183#endif /* HAVE_IPV6 */
3184}
Chris Caputo228da422009-07-18 05:44:03 +00003185
3186void
3187vty_terminate (void)
3188{
3189 if (vty_cwd)
3190 XFREE (MTYPE_TMP, vty_cwd);
3191
3192 if (vtyvec && Vvty_serv_thread)
3193 {
3194 vty_reset ();
3195 vector_free (vtyvec);
3196 vector_free (Vvty_serv_thread);
3197 }
3198}