blob: e4510f850513327804eaf281e6f7645ccafd48b4 [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
43/* Vty events */
44enum event
45{
46 VTY_SERV,
47 VTY_READ,
48 VTY_WRITE,
49 VTY_TIMEOUT_RESET,
50#ifdef VTYSH
51 VTYSH_SERV,
ajs49ff6d92004-11-04 19:26:16 +000052 VTYSH_READ,
53 VTYSH_WRITE
paul718e3742002-12-13 20:15:29 +000054#endif /* VTYSH */
55};
56
57static void vty_event (enum event, int, struct vty *);
58
59/* Extern host structure from command.c */
60extern struct host host;
David Lamparter6b0655a2014-06-04 06:53:35 +020061
paul718e3742002-12-13 20:15:29 +000062/* Vector which store each vty structure. */
63static vector vtyvec;
64
65/* Vty timeout value. */
66static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
67
68/* Vty access-class command */
69static char *vty_accesslist_name = NULL;
70
71/* Vty access-calss for IPv6. */
72static char *vty_ipv6_accesslist_name = NULL;
73
74/* VTY server thread. */
Christian Franke677bcbb2013-02-27 13:47:23 +000075static vector Vvty_serv_thread;
paul718e3742002-12-13 20:15:29 +000076
77/* Current directory. */
78char *vty_cwd = NULL;
79
80/* Configure lock. */
81static int vty_config;
82
83/* Login password check. */
84static int no_password_check = 0;
85
Paul Jakma62687ff2008-08-23 14:27:06 +010086/* Restrict unauthenticated logins? */
87static const u_char restricted_mode_default = 0;
88static u_char restricted_mode = 0;
89
paul718e3742002-12-13 20:15:29 +000090/* Integrated configuration file path */
91char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
92
David Lamparter6b0655a2014-06-04 06:53:35 +020093
paul718e3742002-12-13 20:15:29 +000094/* VTY standard output function. */
95int
96vty_out (struct vty *vty, const char *format, ...)
97{
98 va_list args;
99 int len = 0;
100 int size = 1024;
101 char buf[1024];
102 char *p = NULL;
paul718e3742002-12-13 20:15:29 +0000103
104 if (vty_shell (vty))
ajsd246bd92004-11-23 17:35:08 +0000105 {
106 va_start (args, format);
107 vprintf (format, args);
108 va_end (args);
109 }
paul718e3742002-12-13 20:15:29 +0000110 else
111 {
112 /* Try to write to initial buffer. */
ajsd246bd92004-11-23 17:35:08 +0000113 va_start (args, format);
Lou Bergerc7f7e492016-01-12 13:41:49 -0500114 len = vsnprintf (buf, sizeof(buf), format, args);
ajsd246bd92004-11-23 17:35:08 +0000115 va_end (args);
paul718e3742002-12-13 20:15:29 +0000116
117 /* Initial buffer is not enough. */
118 if (len < 0 || len >= size)
119 {
120 while (1)
121 {
122 if (len > -1)
123 size = len + 1;
124 else
125 size = size * 2;
126
127 p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
128 if (! p)
129 return -1;
130
ajsd246bd92004-11-23 17:35:08 +0000131 va_start (args, format);
paul718e3742002-12-13 20:15:29 +0000132 len = vsnprintf (p, size, format, args);
ajsd246bd92004-11-23 17:35:08 +0000133 va_end (args);
paul718e3742002-12-13 20:15:29 +0000134
135 if (len > -1 && len < size)
136 break;
137 }
138 }
139
140 /* When initial buffer is enough to store all output. */
141 if (! p)
142 p = buf;
143
144 /* Pointer p must point out buffer. */
ajs9fc7ebf2005-02-23 15:12:34 +0000145 buffer_put (vty->obuf, (u_char *) p, len);
paul718e3742002-12-13 20:15:29 +0000146
147 /* If p is not different with buf, it is allocated buffer. */
148 if (p != buf)
149 XFREE (MTYPE_VTY_OUT_BUF, p);
150 }
151
paul718e3742002-12-13 20:15:29 +0000152 return len;
153}
154
ajsd246bd92004-11-23 17:35:08 +0000155static int
ajs274a4a42004-12-07 15:39:31 +0000156vty_log_out (struct vty *vty, const char *level, const char *proto_str,
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000157 const char *format, struct timestamp_control *ctl, va_list va)
paul718e3742002-12-13 20:15:29 +0000158{
ajs9fc7ebf2005-02-23 15:12:34 +0000159 int ret;
paul718e3742002-12-13 20:15:29 +0000160 int len;
161 char buf[1024];
Andrew J. Schorr08942da2006-07-03 20:58:29 +0000162
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000163 if (!ctl->already_rendered)
164 {
165 ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
166 ctl->already_rendered = 1;
167 }
168 if (ctl->len+1 >= sizeof(buf))
169 return -1;
170 memcpy(buf, ctl->buf, len = ctl->len);
171 buf[len++] = ' ';
172 buf[len] = '\0';
paul718e3742002-12-13 20:15:29 +0000173
ajs274a4a42004-12-07 15:39:31 +0000174 if (level)
Andrew J. Schorr08942da2006-07-03 20:58:29 +0000175 ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
ajs274a4a42004-12-07 15:39:31 +0000176 else
Andrew J. Schorr08942da2006-07-03 20:58:29 +0000177 ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
178 if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
paul718e3742002-12-13 20:15:29 +0000179 return -1;
paul718e3742002-12-13 20:15:29 +0000180
ajs9fc7ebf2005-02-23 15:12:34 +0000181 if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
182 ((size_t)((len += ret)+2) > sizeof(buf)))
183 return -1;
paul718e3742002-12-13 20:15:29 +0000184
ajs9fc7ebf2005-02-23 15:12:34 +0000185 buf[len++] = '\r';
186 buf[len++] = '\n';
187
David Lamparter4715a532013-05-30 16:31:49 +0200188 if (write(vty->wfd, buf, len) < 0)
ajs9fc7ebf2005-02-23 15:12:34 +0000189 {
190 if (ERRNO_IO_RETRY(errno))
191 /* Kernel buffer is full, probably too much debugging output, so just
192 drop the data and ignore. */
193 return -1;
194 /* Fatal I/O error. */
Andrew J. Schorr74542d72006-07-10 18:09:42 +0000195 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +0000196 zlog_warn("%s: write failed to vty client fd %d, closing: %s",
197 __func__, vty->fd, safe_strerror(errno));
198 buffer_reset(vty->obuf);
Andrew J. Schorr9d0a3262006-07-11 00:06:49 +0000199 /* cannot call vty_close, because a parent routine may still try
200 to access the vty struct */
201 vty->status = VTY_CLOSE;
202 shutdown(vty->fd, SHUT_RDWR);
ajs9fc7ebf2005-02-23 15:12:34 +0000203 return -1;
204 }
205 return 0;
paul718e3742002-12-13 20:15:29 +0000206}
207
208/* Output current time to the vty. */
209void
210vty_time_print (struct vty *vty, int cr)
211{
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000212 char buf [25];
paul718e3742002-12-13 20:15:29 +0000213
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000214 if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
paul718e3742002-12-13 20:15:29 +0000215 {
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000216 zlog (NULL, LOG_INFO, "quagga_timestamp error");
paul718e3742002-12-13 20:15:29 +0000217 return;
218 }
219 if (cr)
220 vty_out (vty, "%s\n", buf);
221 else
222 vty_out (vty, "%s ", buf);
223
224 return;
225}
226
227/* Say hello to vty interface. */
228void
229vty_hello (struct vty *vty)
230{
paul3b0c5d92005-03-08 10:43:43 +0000231 if (host.motdfile)
232 {
233 FILE *f;
234 char buf[4096];
paul22085182005-03-08 16:00:12 +0000235
paul3b0c5d92005-03-08 10:43:43 +0000236 f = fopen (host.motdfile, "r");
237 if (f)
238 {
paulb45da6f2005-03-08 15:16:57 +0000239 while (fgets (buf, sizeof (buf), f))
paul3b0c5d92005-03-08 10:43:43 +0000240 {
paulb45da6f2005-03-08 15:16:57 +0000241 char *s;
paul22085182005-03-08 16:00:12 +0000242 /* work backwards to ignore trailling isspace() */
gdtf80a0162005-12-29 16:03:32 +0000243 for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
paul22085182005-03-08 16:00:12 +0000244 s--);
245 *s = '\0';
246 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
247 }
paul3b0c5d92005-03-08 10:43:43 +0000248 fclose (f);
249 }
250 else
paulb45da6f2005-03-08 15:16:57 +0000251 vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
paul3b0c5d92005-03-08 10:43:43 +0000252 }
253 else if (host.motd)
Nico Goldeb830c892010-08-01 15:24:35 +0200254 vty_out (vty, "%s", host.motd);
paul718e3742002-12-13 20:15:29 +0000255}
256
257/* Put out prompt and wait input from user. */
258static void
259vty_prompt (struct vty *vty)
260{
261 struct utsname names;
262 const char*hostname;
263
264 if (vty->type == VTY_TERM)
265 {
266 hostname = host.name;
267 if (!hostname)
268 {
269 uname (&names);
270 hostname = names.nodename;
271 }
272 vty_out (vty, cmd_prompt (vty->node), hostname);
273 }
274}
275
276/* Send WILL TELOPT_ECHO to remote server. */
ajs9fc7ebf2005-02-23 15:12:34 +0000277static void
paul718e3742002-12-13 20:15:29 +0000278vty_will_echo (struct vty *vty)
279{
paul02ff83c2004-06-11 11:27:03 +0000280 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
paul718e3742002-12-13 20:15:29 +0000281 vty_out (vty, "%s", cmd);
282}
283
284/* Make suppress Go-Ahead telnet option. */
285static void
286vty_will_suppress_go_ahead (struct vty *vty)
287{
paul02ff83c2004-06-11 11:27:03 +0000288 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
paul718e3742002-12-13 20:15:29 +0000289 vty_out (vty, "%s", cmd);
290}
291
292/* Make don't use linemode over telnet. */
293static void
294vty_dont_linemode (struct vty *vty)
295{
paul02ff83c2004-06-11 11:27:03 +0000296 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
paul718e3742002-12-13 20:15:29 +0000297 vty_out (vty, "%s", cmd);
298}
299
300/* Use window size. */
301static void
302vty_do_window_size (struct vty *vty)
303{
paul02ff83c2004-06-11 11:27:03 +0000304 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
paul718e3742002-12-13 20:15:29 +0000305 vty_out (vty, "%s", cmd);
306}
307
308#if 0 /* Currently not used. */
309/* Make don't use lflow vty interface. */
310static void
311vty_dont_lflow_ahead (struct vty *vty)
312{
paul02ff83c2004-06-11 11:27:03 +0000313 unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
paul718e3742002-12-13 20:15:29 +0000314 vty_out (vty, "%s", cmd);
315}
316#endif /* 0 */
317
318/* Allocate new vty struct. */
319struct vty *
320vty_new ()
321{
322 struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
323
ajs9fc7ebf2005-02-23 15:12:34 +0000324 new->obuf = buffer_new(0); /* Use default buffer size. */
paul718e3742002-12-13 20:15:29 +0000325 new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
326 new->max = VTY_BUFSIZ;
paul718e3742002-12-13 20:15:29 +0000327
328 return new;
329}
330
331/* Authentication of vty */
332static void
333vty_auth (struct vty *vty, char *buf)
334{
335 char *passwd = NULL;
336 enum node_type next_node = 0;
337 int fail;
338 char *crypt (const char *, const char *);
339
340 switch (vty->node)
341 {
342 case AUTH_NODE:
343 if (host.encrypt)
344 passwd = host.password_encrypt;
345 else
346 passwd = host.password;
347 if (host.advanced)
348 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
349 else
350 next_node = VIEW_NODE;
351 break;
352 case AUTH_ENABLE_NODE:
353 if (host.encrypt)
354 passwd = host.enable_encrypt;
355 else
356 passwd = host.enable;
357 next_node = ENABLE_NODE;
358 break;
359 }
360
361 if (passwd)
362 {
363 if (host.encrypt)
364 fail = strcmp (crypt(buf, passwd), passwd);
365 else
366 fail = strcmp (buf, passwd);
367 }
368 else
369 fail = 1;
370
371 if (! fail)
372 {
373 vty->fail = 0;
374 vty->node = next_node; /* Success ! */
375 }
376 else
377 {
378 vty->fail++;
379 if (vty->fail >= 3)
380 {
381 if (vty->node == AUTH_NODE)
382 {
383 vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
384 vty->status = VTY_CLOSE;
385 }
386 else
387 {
388 /* AUTH_ENABLE_NODE */
389 vty->fail = 0;
390 vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
Paul Jakma62687ff2008-08-23 14:27:06 +0100391 vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
paul718e3742002-12-13 20:15:29 +0000392 }
393 }
394 }
395}
396
397/* Command execution over the vty interface. */
ajs9fc7ebf2005-02-23 15:12:34 +0000398static int
paul718e3742002-12-13 20:15:29 +0000399vty_command (struct vty *vty, char *buf)
400{
401 int ret;
402 vector vline;
vincentfbf5d032005-09-29 11:25:50 +0000403 const char *protocolname;
Lou Bergerc7f7e492016-01-12 13:41:49 -0500404 char *cp;
paul718e3742002-12-13 20:15:29 +0000405
Lou Bergerc7f7e492016-01-12 13:41:49 -0500406 /*
407 * Log non empty command lines
408 */
409 cp = buf;
410 if (cp != NULL)
411 {
412 /* Skip white spaces. */
413 while (isspace ((int) *cp) && *cp != '\0')
414 cp++;
415 }
416 if (cp != NULL && *cp != '\0')
417 {
418 unsigned i;
419 char vty_str[VTY_BUFSIZ];
420 char prompt_str[VTY_BUFSIZ];
421
422 /* format the base vty info */
423 snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
424 if (vty)
425 for (i = 0; i < vector_active (vtyvec); i++)
426 if ((vty == vector_slot (vtyvec, i)))
427 {
428 snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s",
429 i, vty->address);
430 break;
431 }
432
433 /* format the prompt */
434 snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str);
435
436 /* now log the command */
437 zlog(NULL, LOG_NOTICE, "%s%s", prompt_str, buf);
438 }
paul718e3742002-12-13 20:15:29 +0000439 /* Split readline string up into the vector */
440 vline = cmd_make_strvec (buf);
441
442 if (vline == NULL)
443 return CMD_SUCCESS;
444
ajs924b9222005-04-16 17:11:24 +0000445#ifdef CONSUMED_TIME_CHECK
446 {
447 RUSAGE_T before;
448 RUSAGE_T after;
ajs8b70d0b2005-04-28 01:31:13 +0000449 unsigned long realtime, cputime;
ajs924b9222005-04-16 17:11:24 +0000450
451 GETRUSAGE(&before);
452#endif /* CONSUMED_TIME_CHECK */
453
hasso87d683b2005-01-16 23:31:54 +0000454 ret = cmd_execute_command (vline, vty, NULL, 0);
paul718e3742002-12-13 20:15:29 +0000455
vincentfbf5d032005-09-29 11:25:50 +0000456 /* Get the name of the protocol if any */
457 if (zlog_default)
458 protocolname = zlog_proto_names[zlog_default->protocol];
459 else
460 protocolname = zlog_proto_names[ZLOG_NONE];
461
ajs924b9222005-04-16 17:11:24 +0000462#ifdef CONSUMED_TIME_CHECK
463 GETRUSAGE(&after);
ajs8b70d0b2005-04-28 01:31:13 +0000464 if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
465 CONSUMED_TIME_CHECK)
ajs924b9222005-04-16 17:11:24 +0000466 /* Warn about CPU hog that must be fixed. */
ajs8b70d0b2005-04-28 01:31:13 +0000467 zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
468 realtime/1000, cputime/1000, buf);
ajs924b9222005-04-16 17:11:24 +0000469 }
470#endif /* CONSUMED_TIME_CHECK */
471
paul718e3742002-12-13 20:15:29 +0000472 if (ret != CMD_SUCCESS)
473 switch (ret)
474 {
475 case CMD_WARNING:
476 if (vty->type == VTY_FILE)
477 vty_out (vty, "Warning...%s", VTY_NEWLINE);
478 break;
479 case CMD_ERR_AMBIGUOUS:
480 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
481 break;
482 case CMD_ERR_NO_MATCH:
vincentfbf5d032005-09-29 11:25:50 +0000483 vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000484 break;
485 case CMD_ERR_INCOMPLETE:
486 vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
487 break;
488 }
489 cmd_free_strvec (vline);
490
491 return ret;
492}
David Lamparter6b0655a2014-06-04 06:53:35 +0200493
ajs9fc7ebf2005-02-23 15:12:34 +0000494static const char telnet_backward_char = 0x08;
495static const char telnet_space_char = ' ';
paul718e3742002-12-13 20:15:29 +0000496
497/* Basic function to write buffer to vty. */
498static void
ajs9fc7ebf2005-02-23 15:12:34 +0000499vty_write (struct vty *vty, const char *buf, size_t nbytes)
paul718e3742002-12-13 20:15:29 +0000500{
501 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
502 return;
503
504 /* Should we do buffering here ? And make vty_flush (vty) ? */
ajs9fc7ebf2005-02-23 15:12:34 +0000505 buffer_put (vty->obuf, buf, nbytes);
paul718e3742002-12-13 20:15:29 +0000506}
507
508/* Ensure length of input buffer. Is buffer is short, double it. */
509static void
510vty_ensure (struct vty *vty, int length)
511{
512 if (vty->max <= length)
513 {
514 vty->max *= 2;
515 vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max);
516 }
517}
518
519/* Basic function to insert character into vty. */
520static void
521vty_self_insert (struct vty *vty, char c)
522{
523 int i;
524 int length;
525
526 vty_ensure (vty, vty->length + 1);
527 length = vty->length - vty->cp;
528 memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
529 vty->buf[vty->cp] = c;
530
531 vty_write (vty, &vty->buf[vty->cp], length + 1);
532 for (i = 0; i < length; i++)
533 vty_write (vty, &telnet_backward_char, 1);
534
535 vty->cp++;
536 vty->length++;
537}
538
539/* Self insert character 'c' in overwrite mode. */
540static void
541vty_self_insert_overwrite (struct vty *vty, char c)
542{
543 vty_ensure (vty, vty->length + 1);
544 vty->buf[vty->cp++] = c;
545
546 if (vty->cp > vty->length)
547 vty->length++;
548
549 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
550 return;
551
552 vty_write (vty, &c, 1);
553}
554
555/* Insert a word into vty interface with overwrite mode. */
556static void
557vty_insert_word_overwrite (struct vty *vty, char *str)
558{
559 int len = strlen (str);
560 vty_write (vty, str, len);
561 strcpy (&vty->buf[vty->cp], str);
562 vty->cp += len;
563 vty->length = vty->cp;
564}
565
566/* Forward character. */
567static void
568vty_forward_char (struct vty *vty)
569{
570 if (vty->cp < vty->length)
571 {
572 vty_write (vty, &vty->buf[vty->cp], 1);
573 vty->cp++;
574 }
575}
576
577/* Backward character. */
578static void
579vty_backward_char (struct vty *vty)
580{
581 if (vty->cp > 0)
582 {
583 vty->cp--;
584 vty_write (vty, &telnet_backward_char, 1);
585 }
586}
587
588/* Move to the beginning of the line. */
589static void
590vty_beginning_of_line (struct vty *vty)
591{
592 while (vty->cp)
593 vty_backward_char (vty);
594}
595
596/* Move to the end of the line. */
597static void
598vty_end_of_line (struct vty *vty)
599{
600 while (vty->cp < vty->length)
601 vty_forward_char (vty);
602}
603
604static void vty_kill_line_from_beginning (struct vty *);
605static void vty_redraw_line (struct vty *);
606
607/* Print command line history. This function is called from
608 vty_next_line and vty_previous_line. */
609static void
610vty_history_print (struct vty *vty)
611{
612 int length;
613
614 vty_kill_line_from_beginning (vty);
615
616 /* Get previous line from history buffer */
617 length = strlen (vty->hist[vty->hp]);
618 memcpy (vty->buf, vty->hist[vty->hp], length);
619 vty->cp = vty->length = length;
620
621 /* Redraw current line */
622 vty_redraw_line (vty);
623}
624
625/* Show next command line history. */
ajs9fc7ebf2005-02-23 15:12:34 +0000626static void
paul718e3742002-12-13 20:15:29 +0000627vty_next_line (struct vty *vty)
628{
629 int try_index;
630
631 if (vty->hp == vty->hindex)
632 return;
633
634 /* Try is there history exist or not. */
635 try_index = vty->hp;
636 if (try_index == (VTY_MAXHIST - 1))
637 try_index = 0;
638 else
639 try_index++;
640
641 /* If there is not history return. */
642 if (vty->hist[try_index] == NULL)
643 return;
644 else
645 vty->hp = try_index;
646
647 vty_history_print (vty);
648}
649
650/* Show previous command line history. */
ajs9fc7ebf2005-02-23 15:12:34 +0000651static void
paul718e3742002-12-13 20:15:29 +0000652vty_previous_line (struct vty *vty)
653{
654 int try_index;
655
656 try_index = vty->hp;
657 if (try_index == 0)
658 try_index = VTY_MAXHIST - 1;
659 else
660 try_index--;
661
662 if (vty->hist[try_index] == NULL)
663 return;
664 else
665 vty->hp = try_index;
666
667 vty_history_print (vty);
668}
669
670/* This function redraw all of the command line character. */
671static void
672vty_redraw_line (struct vty *vty)
673{
674 vty_write (vty, vty->buf, vty->length);
675 vty->cp = vty->length;
676}
677
678/* Forward word. */
679static void
680vty_forward_word (struct vty *vty)
681{
682 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
683 vty_forward_char (vty);
684
685 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
686 vty_forward_char (vty);
687}
688
689/* Backward word without skipping training space. */
690static void
691vty_backward_pure_word (struct vty *vty)
692{
693 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
694 vty_backward_char (vty);
695}
696
697/* Backward word. */
698static void
699vty_backward_word (struct vty *vty)
700{
701 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
702 vty_backward_char (vty);
703
704 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
705 vty_backward_char (vty);
706}
707
708/* When '^D' is typed at the beginning of the line we move to the down
709 level. */
710static void
711vty_down_level (struct vty *vty)
712{
713 vty_out (vty, "%s", VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +0000714 (*config_exit_cmd.func)(NULL, vty, 0, NULL);
paul718e3742002-12-13 20:15:29 +0000715 vty_prompt (vty);
716 vty->cp = 0;
717}
718
719/* When '^Z' is received from vty, move down to the enable mode. */
ajs9fc7ebf2005-02-23 15:12:34 +0000720static void
paul718e3742002-12-13 20:15:29 +0000721vty_end_config (struct vty *vty)
722{
723 vty_out (vty, "%s", VTY_NEWLINE);
724
725 switch (vty->node)
726 {
727 case VIEW_NODE:
728 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +0100729 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +0000730 /* Nothing to do. */
731 break;
732 case CONFIG_NODE:
733 case INTERFACE_NODE:
734 case ZEBRA_NODE:
735 case RIP_NODE:
736 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +0100737 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +0000738 case BGP_NODE:
739 case BGP_VPNV4_NODE:
Lou Berger13c378d2016-01-12 13:41:56 -0500740 case BGP_VPNV6_NODE:
Lou Bergera3fda882016-01-12 13:42:04 -0500741 case BGP_ENCAP_NODE:
742 case BGP_ENCAPV6_NODE:
paul718e3742002-12-13 20:15:29 +0000743 case BGP_IPV4_NODE:
744 case BGP_IPV4M_NODE:
745 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +0000746 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +0000747 case RMAP_NODE:
748 case OSPF_NODE:
749 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +0000750 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +0000751 case KEYCHAIN_NODE:
752 case KEYCHAIN_KEY_NODE:
753 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -0200754 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +0000755 case VTY_NODE:
756 vty_config_unlock (vty);
757 vty->node = ENABLE_NODE;
758 break;
759 default:
760 /* Unknown node, we have to ignore it. */
761 break;
762 }
763
764 vty_prompt (vty);
765 vty->cp = 0;
766}
767
768/* Delete a charcter at the current point. */
769static void
770vty_delete_char (struct vty *vty)
771{
772 int i;
773 int size;
774
paul718e3742002-12-13 20:15:29 +0000775 if (vty->length == 0)
776 {
777 vty_down_level (vty);
778 return;
779 }
780
781 if (vty->cp == vty->length)
782 return; /* completion need here? */
783
784 size = vty->length - vty->cp;
785
786 vty->length--;
787 memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
788 vty->buf[vty->length] = '\0';
Roy7f794f22008-08-13 17:27:38 +0100789
790 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
791 return;
paul718e3742002-12-13 20:15:29 +0000792
793 vty_write (vty, &vty->buf[vty->cp], size - 1);
794 vty_write (vty, &telnet_space_char, 1);
795
796 for (i = 0; i < size; i++)
797 vty_write (vty, &telnet_backward_char, 1);
798}
799
800/* Delete a character before the point. */
801static void
802vty_delete_backward_char (struct vty *vty)
803{
804 if (vty->cp == 0)
805 return;
806
807 vty_backward_char (vty);
808 vty_delete_char (vty);
809}
810
811/* Kill rest of line from current point. */
812static void
813vty_kill_line (struct vty *vty)
814{
815 int i;
816 int size;
817
818 size = vty->length - vty->cp;
819
820 if (size == 0)
821 return;
822
823 for (i = 0; i < size; i++)
824 vty_write (vty, &telnet_space_char, 1);
825 for (i = 0; i < size; i++)
826 vty_write (vty, &telnet_backward_char, 1);
827
828 memset (&vty->buf[vty->cp], 0, size);
829 vty->length = vty->cp;
830}
831
832/* Kill line from the beginning. */
833static void
834vty_kill_line_from_beginning (struct vty *vty)
835{
836 vty_beginning_of_line (vty);
837 vty_kill_line (vty);
838}
839
840/* Delete a word before the point. */
841static void
842vty_forward_kill_word (struct vty *vty)
843{
844 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
845 vty_delete_char (vty);
846 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
847 vty_delete_char (vty);
848}
849
850/* Delete a word before the point. */
851static void
852vty_backward_kill_word (struct vty *vty)
853{
854 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
855 vty_delete_backward_char (vty);
856 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
857 vty_delete_backward_char (vty);
858}
859
860/* Transpose chars before or at the point. */
861static void
862vty_transpose_chars (struct vty *vty)
863{
864 char c1, c2;
865
866 /* If length is short or point is near by the beginning of line then
867 return. */
868 if (vty->length < 2 || vty->cp < 1)
869 return;
870
871 /* In case of point is located at the end of the line. */
872 if (vty->cp == vty->length)
873 {
874 c1 = vty->buf[vty->cp - 1];
875 c2 = vty->buf[vty->cp - 2];
876
877 vty_backward_char (vty);
878 vty_backward_char (vty);
879 vty_self_insert_overwrite (vty, c1);
880 vty_self_insert_overwrite (vty, c2);
881 }
882 else
883 {
884 c1 = vty->buf[vty->cp];
885 c2 = vty->buf[vty->cp - 1];
886
887 vty_backward_char (vty);
888 vty_self_insert_overwrite (vty, c1);
889 vty_self_insert_overwrite (vty, c2);
890 }
891}
892
893/* Do completion at vty interface. */
894static void
895vty_complete_command (struct vty *vty)
896{
897 int i;
898 int ret;
899 char **matched = NULL;
900 vector vline;
901
902 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
903 return;
904
905 vline = cmd_make_strvec (vty->buf);
906 if (vline == NULL)
907 return;
908
909 /* In case of 'help \t'. */
910 if (isspace ((int) vty->buf[vty->length - 1]))
David Lampartera91a3ba2015-03-03 09:06:51 +0100911 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +0000912
Lou Berger67290032016-01-12 13:41:46 -0500913 matched = cmd_complete_command_lib (vline, vty, &ret, 1);
paul718e3742002-12-13 20:15:29 +0000914
915 cmd_free_strvec (vline);
916
917 vty_out (vty, "%s", VTY_NEWLINE);
918 switch (ret)
919 {
920 case CMD_ERR_AMBIGUOUS:
921 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
922 vty_prompt (vty);
923 vty_redraw_line (vty);
924 break;
925 case CMD_ERR_NO_MATCH:
926 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
927 vty_prompt (vty);
928 vty_redraw_line (vty);
929 break;
930 case CMD_COMPLETE_FULL_MATCH:
931 vty_prompt (vty);
932 vty_redraw_line (vty);
933 vty_backward_pure_word (vty);
934 vty_insert_word_overwrite (vty, matched[0]);
935 vty_self_insert (vty, ' ');
936 XFREE (MTYPE_TMP, matched[0]);
937 break;
938 case CMD_COMPLETE_MATCH:
939 vty_prompt (vty);
940 vty_redraw_line (vty);
941 vty_backward_pure_word (vty);
942 vty_insert_word_overwrite (vty, matched[0]);
943 XFREE (MTYPE_TMP, matched[0]);
944 vector_only_index_free (matched);
945 return;
946 break;
947 case CMD_COMPLETE_LIST_MATCH:
948 for (i = 0; matched[i] != NULL; i++)
949 {
950 if (i != 0 && ((i % 6) == 0))
951 vty_out (vty, "%s", VTY_NEWLINE);
952 vty_out (vty, "%-10s ", matched[i]);
953 XFREE (MTYPE_TMP, matched[i]);
954 }
955 vty_out (vty, "%s", VTY_NEWLINE);
956
957 vty_prompt (vty);
958 vty_redraw_line (vty);
959 break;
960 case CMD_ERR_NOTHING_TODO:
961 vty_prompt (vty);
962 vty_redraw_line (vty);
963 break;
964 default:
965 break;
966 }
967 if (matched)
968 vector_only_index_free (matched);
969}
970
ajs9fc7ebf2005-02-23 15:12:34 +0000971static void
paul718e3742002-12-13 20:15:29 +0000972vty_describe_fold (struct vty *vty, int cmd_width,
Christian Frankecd40b322013-09-30 12:27:51 +0000973 unsigned int desc_width, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +0000974{
hasso8c328f12004-10-05 21:01:23 +0000975 char *buf;
976 const char *cmd, *p;
paul718e3742002-12-13 20:15:29 +0000977 int pos;
978
Christian Frankecd40b322013-09-30 12:27:51 +0000979 cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
paul718e3742002-12-13 20:15:29 +0000980
981 if (desc_width <= 0)
982 {
Christian Frankecd40b322013-09-30 12:27:51 +0000983 vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000984 return;
985 }
986
Christian Frankecd40b322013-09-30 12:27:51 +0000987 buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
paul718e3742002-12-13 20:15:29 +0000988
Christian Frankecd40b322013-09-30 12:27:51 +0000989 for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
paul718e3742002-12-13 20:15:29 +0000990 {
991 for (pos = desc_width; pos > 0; pos--)
992 if (*(p + pos) == ' ')
993 break;
994
995 if (pos == 0)
996 break;
997
998 strncpy (buf, p, pos);
999 buf[pos] = '\0';
1000 vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1001
1002 cmd = "";
1003 }
1004
1005 vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1006
1007 XFREE (MTYPE_TMP, buf);
1008}
1009
1010/* Describe matched command function. */
1011static void
1012vty_describe_command (struct vty *vty)
1013{
1014 int ret;
1015 vector vline;
1016 vector describe;
hasso8c328f12004-10-05 21:01:23 +00001017 unsigned int i, width, desc_width;
Christian Frankecd40b322013-09-30 12:27:51 +00001018 struct cmd_token *token, *token_cr = NULL;
paul718e3742002-12-13 20:15:29 +00001019
1020 vline = cmd_make_strvec (vty->buf);
1021
1022 /* In case of '> ?'. */
1023 if (vline == NULL)
1024 {
1025 vline = vector_init (1);
David Lampartera91a3ba2015-03-03 09:06:51 +01001026 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +00001027 }
1028 else
1029 if (isspace ((int) vty->buf[vty->length - 1]))
David Lampartera91a3ba2015-03-03 09:06:51 +01001030 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +00001031
1032 describe = cmd_describe_command (vline, vty, &ret);
1033
1034 vty_out (vty, "%s", VTY_NEWLINE);
1035
1036 /* Ambiguous error. */
1037 switch (ret)
1038 {
1039 case CMD_ERR_AMBIGUOUS:
paul718e3742002-12-13 20:15:29 +00001040 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001041 goto out;
paul718e3742002-12-13 20:15:29 +00001042 break;
1043 case CMD_ERR_NO_MATCH:
paul718e3742002-12-13 20:15:29 +00001044 vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001045 goto out;
paul718e3742002-12-13 20:15:29 +00001046 break;
1047 }
1048
1049 /* Get width of command string. */
1050 width = 0;
paul55468c82005-03-14 20:19:01 +00001051 for (i = 0; i < vector_active (describe); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00001052 if ((token = vector_slot (describe, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001053 {
hasso8c328f12004-10-05 21:01:23 +00001054 unsigned int len;
paul718e3742002-12-13 20:15:29 +00001055
Christian Frankecd40b322013-09-30 12:27:51 +00001056 if (token->cmd[0] == '\0')
paul718e3742002-12-13 20:15:29 +00001057 continue;
1058
Christian Frankecd40b322013-09-30 12:27:51 +00001059 len = strlen (token->cmd);
1060 if (token->cmd[0] == '.')
paul718e3742002-12-13 20:15:29 +00001061 len--;
1062
1063 if (width < len)
1064 width = len;
1065 }
1066
1067 /* Get width of description string. */
1068 desc_width = vty->width - (width + 6);
1069
1070 /* Print out description. */
paul55468c82005-03-14 20:19:01 +00001071 for (i = 0; i < vector_active (describe); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00001072 if ((token = vector_slot (describe, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001073 {
Christian Frankecd40b322013-09-30 12:27:51 +00001074 if (token->cmd[0] == '\0')
paul718e3742002-12-13 20:15:29 +00001075 continue;
1076
Christian Frankecd40b322013-09-30 12:27:51 +00001077 if (strcmp (token->cmd, command_cr) == 0)
paul718e3742002-12-13 20:15:29 +00001078 {
Christian Frankecd40b322013-09-30 12:27:51 +00001079 token_cr = token;
paul718e3742002-12-13 20:15:29 +00001080 continue;
1081 }
1082
Christian Frankecd40b322013-09-30 12:27:51 +00001083 if (!token->desc)
paul718e3742002-12-13 20:15:29 +00001084 vty_out (vty, " %-s%s",
Christian Frankecd40b322013-09-30 12:27:51 +00001085 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
paul718e3742002-12-13 20:15:29 +00001086 VTY_NEWLINE);
Christian Frankecd40b322013-09-30 12:27:51 +00001087 else if (desc_width >= strlen (token->desc))
paul718e3742002-12-13 20:15:29 +00001088 vty_out (vty, " %-*s %s%s", width,
Christian Frankecd40b322013-09-30 12:27:51 +00001089 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1090 token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001091 else
Christian Frankecd40b322013-09-30 12:27:51 +00001092 vty_describe_fold (vty, width, desc_width, token);
paul718e3742002-12-13 20:15:29 +00001093
1094#if 0
1095 vty_out (vty, " %-*s %s%s", width
1096 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1097 desc->str ? desc->str : "", VTY_NEWLINE);
1098#endif /* 0 */
1099 }
1100
Christian Frankecd40b322013-09-30 12:27:51 +00001101 if ((token = token_cr))
paul718e3742002-12-13 20:15:29 +00001102 {
Christian Frankecd40b322013-09-30 12:27:51 +00001103 if (!token->desc)
paul718e3742002-12-13 20:15:29 +00001104 vty_out (vty, " %-s%s",
Christian Frankecd40b322013-09-30 12:27:51 +00001105 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
paul718e3742002-12-13 20:15:29 +00001106 VTY_NEWLINE);
Christian Frankecd40b322013-09-30 12:27:51 +00001107 else if (desc_width >= strlen (token->desc))
paul718e3742002-12-13 20:15:29 +00001108 vty_out (vty, " %-*s %s%s", width,
Christian Frankecd40b322013-09-30 12:27:51 +00001109 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1110 token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001111 else
Christian Frankecd40b322013-09-30 12:27:51 +00001112 vty_describe_fold (vty, width, desc_width, token);
paul718e3742002-12-13 20:15:29 +00001113 }
1114
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001115out:
paul718e3742002-12-13 20:15:29 +00001116 cmd_free_strvec (vline);
Paul Jakmad16e0432006-05-15 10:56:46 +00001117 if (describe)
1118 vector_free (describe);
paul718e3742002-12-13 20:15:29 +00001119
1120 vty_prompt (vty);
1121 vty_redraw_line (vty);
1122}
1123
ajs9fc7ebf2005-02-23 15:12:34 +00001124static void
paul718e3742002-12-13 20:15:29 +00001125vty_clear_buf (struct vty *vty)
1126{
1127 memset (vty->buf, 0, vty->max);
1128}
1129
1130/* ^C stop current input and do not add command line to the history. */
1131static void
1132vty_stop_input (struct vty *vty)
1133{
1134 vty->cp = vty->length = 0;
1135 vty_clear_buf (vty);
1136 vty_out (vty, "%s", VTY_NEWLINE);
1137
1138 switch (vty->node)
1139 {
1140 case VIEW_NODE:
1141 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01001142 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00001143 /* Nothing to do. */
1144 break;
1145 case CONFIG_NODE:
1146 case INTERFACE_NODE:
1147 case ZEBRA_NODE:
1148 case RIP_NODE:
1149 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01001150 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00001151 case BGP_NODE:
1152 case RMAP_NODE:
1153 case OSPF_NODE:
1154 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00001155 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00001156 case KEYCHAIN_NODE:
1157 case KEYCHAIN_KEY_NODE:
1158 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02001159 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00001160 case VTY_NODE:
1161 vty_config_unlock (vty);
1162 vty->node = ENABLE_NODE;
1163 break;
1164 default:
1165 /* Unknown node, we have to ignore it. */
1166 break;
1167 }
1168 vty_prompt (vty);
1169
1170 /* Set history pointer to the latest one. */
1171 vty->hp = vty->hindex;
1172}
1173
1174/* Add current command line to the history buffer. */
1175static void
1176vty_hist_add (struct vty *vty)
1177{
1178 int index;
1179
1180 if (vty->length == 0)
1181 return;
1182
1183 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1184
1185 /* Ignore the same string as previous one. */
1186 if (vty->hist[index])
1187 if (strcmp (vty->buf, vty->hist[index]) == 0)
1188 {
1189 vty->hp = vty->hindex;
1190 return;
1191 }
1192
1193 /* Insert history entry. */
1194 if (vty->hist[vty->hindex])
1195 XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1196 vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
1197
1198 /* History index rotation. */
1199 vty->hindex++;
1200 if (vty->hindex == VTY_MAXHIST)
1201 vty->hindex = 0;
1202
1203 vty->hp = vty->hindex;
1204}
1205
1206/* #define TELNET_OPTION_DEBUG */
1207
1208/* Get telnet window size. */
1209static int
1210vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
1211{
1212#ifdef TELNET_OPTION_DEBUG
1213 int i;
1214
1215 for (i = 0; i < nbytes; i++)
1216 {
1217 switch (buf[i])
1218 {
1219 case IAC:
1220 vty_out (vty, "IAC ");
1221 break;
1222 case WILL:
1223 vty_out (vty, "WILL ");
1224 break;
1225 case WONT:
1226 vty_out (vty, "WONT ");
1227 break;
1228 case DO:
1229 vty_out (vty, "DO ");
1230 break;
1231 case DONT:
1232 vty_out (vty, "DONT ");
1233 break;
1234 case SB:
1235 vty_out (vty, "SB ");
1236 break;
1237 case SE:
1238 vty_out (vty, "SE ");
1239 break;
1240 case TELOPT_ECHO:
1241 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
1242 break;
1243 case TELOPT_SGA:
1244 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
1245 break;
1246 case TELOPT_NAWS:
1247 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
1248 break;
1249 default:
1250 vty_out (vty, "%x ", buf[i]);
1251 break;
1252 }
1253 }
1254 vty_out (vty, "%s", VTY_NEWLINE);
1255
1256#endif /* TELNET_OPTION_DEBUG */
1257
1258 switch (buf[0])
1259 {
1260 case SB:
ajs9fc7ebf2005-02-23 15:12:34 +00001261 vty->sb_len = 0;
paul718e3742002-12-13 20:15:29 +00001262 vty->iac_sb_in_progress = 1;
1263 return 0;
1264 break;
1265 case SE:
1266 {
paul718e3742002-12-13 20:15:29 +00001267 if (!vty->iac_sb_in_progress)
1268 return 0;
1269
ajs9fc7ebf2005-02-23 15:12:34 +00001270 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
paul718e3742002-12-13 20:15:29 +00001271 {
1272 vty->iac_sb_in_progress = 0;
1273 return 0;
1274 }
ajs9fc7ebf2005-02-23 15:12:34 +00001275 switch (vty->sb_buf[0])
paul718e3742002-12-13 20:15:29 +00001276 {
1277 case TELOPT_NAWS:
ajs9fc7ebf2005-02-23 15:12:34 +00001278 if (vty->sb_len != TELNET_NAWS_SB_LEN)
1279 zlog_warn("RFC 1073 violation detected: telnet NAWS option "
1280 "should send %d characters, but we received %lu",
1281 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
1282 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1283 zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
1284 "too small to handle the telnet NAWS option",
1285 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
1286 else
1287 {
1288 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
1289 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
1290#ifdef TELNET_OPTION_DEBUG
1291 vty_out(vty, "TELNET NAWS window size negotiation completed: "
1292 "width %d, height %d%s",
1293 vty->width, vty->height, VTY_NEWLINE);
1294#endif
1295 }
paul718e3742002-12-13 20:15:29 +00001296 break;
1297 }
1298 vty->iac_sb_in_progress = 0;
1299 return 0;
1300 break;
1301 }
1302 default:
1303 break;
1304 }
1305 return 1;
1306}
1307
1308/* Execute current command line. */
1309static int
1310vty_execute (struct vty *vty)
1311{
1312 int ret;
1313
1314 ret = CMD_SUCCESS;
1315
1316 switch (vty->node)
1317 {
1318 case AUTH_NODE:
1319 case AUTH_ENABLE_NODE:
1320 vty_auth (vty, vty->buf);
1321 break;
1322 default:
1323 ret = vty_command (vty, vty->buf);
1324 if (vty->type == VTY_TERM)
1325 vty_hist_add (vty);
1326 break;
1327 }
1328
1329 /* Clear command line buffer. */
1330 vty->cp = vty->length = 0;
1331 vty_clear_buf (vty);
1332
ajs5a646652004-11-05 01:25:55 +00001333 if (vty->status != VTY_CLOSE )
paul718e3742002-12-13 20:15:29 +00001334 vty_prompt (vty);
1335
1336 return ret;
1337}
1338
1339#define CONTROL(X) ((X) - '@')
1340#define VTY_NORMAL 0
1341#define VTY_PRE_ESCAPE 1
1342#define VTY_ESCAPE 2
1343
1344/* Escape character command map. */
1345static void
1346vty_escape_map (unsigned char c, struct vty *vty)
1347{
1348 switch (c)
1349 {
1350 case ('A'):
1351 vty_previous_line (vty);
1352 break;
1353 case ('B'):
1354 vty_next_line (vty);
1355 break;
1356 case ('C'):
1357 vty_forward_char (vty);
1358 break;
1359 case ('D'):
1360 vty_backward_char (vty);
1361 break;
1362 default:
1363 break;
1364 }
1365
1366 /* Go back to normal mode. */
1367 vty->escape = VTY_NORMAL;
1368}
1369
1370/* Quit print out to the buffer. */
1371static void
1372vty_buffer_reset (struct vty *vty)
1373{
1374 buffer_reset (vty->obuf);
1375 vty_prompt (vty);
1376 vty_redraw_line (vty);
1377}
1378
1379/* Read data via vty socket. */
1380static int
1381vty_read (struct thread *thread)
1382{
1383 int i;
paul718e3742002-12-13 20:15:29 +00001384 int nbytes;
1385 unsigned char buf[VTY_READ_BUFSIZ];
1386
1387 int vty_sock = THREAD_FD (thread);
1388 struct vty *vty = THREAD_ARG (thread);
1389 vty->t_read = NULL;
1390
1391 /* Read raw data from socket */
ajs9fc7ebf2005-02-23 15:12:34 +00001392 if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
1393 {
1394 if (nbytes < 0)
1395 {
1396 if (ERRNO_IO_RETRY(errno))
1397 {
1398 vty_event (VTY_READ, vty_sock, vty);
1399 return 0;
1400 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001401 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001402 zlog_warn("%s: read error on vty client fd %d, closing: %s",
1403 __func__, vty->fd, safe_strerror(errno));
David Lamparter90d31352015-05-14 14:24:06 +02001404 buffer_reset(vty->obuf);
ajs9fc7ebf2005-02-23 15:12:34 +00001405 }
ajs9fc7ebf2005-02-23 15:12:34 +00001406 vty->status = VTY_CLOSE;
1407 }
paul718e3742002-12-13 20:15:29 +00001408
1409 for (i = 0; i < nbytes; i++)
1410 {
1411 if (buf[i] == IAC)
1412 {
1413 if (!vty->iac)
1414 {
1415 vty->iac = 1;
1416 continue;
1417 }
1418 else
1419 {
1420 vty->iac = 0;
1421 }
1422 }
1423
1424 if (vty->iac_sb_in_progress && !vty->iac)
1425 {
ajs9fc7ebf2005-02-23 15:12:34 +00001426 if (vty->sb_len < sizeof(vty->sb_buf))
1427 vty->sb_buf[vty->sb_len] = buf[i];
1428 vty->sb_len++;
paul718e3742002-12-13 20:15:29 +00001429 continue;
1430 }
1431
1432 if (vty->iac)
1433 {
1434 /* In case of telnet command */
paul5b8c1b02003-10-15 23:08:55 +00001435 int ret = 0;
paule9372532003-10-26 21:36:07 +00001436 ret = vty_telnet_option (vty, buf + i, nbytes - i);
paul718e3742002-12-13 20:15:29 +00001437 vty->iac = 0;
1438 i += ret;
1439 continue;
1440 }
paul5b8c1b02003-10-15 23:08:55 +00001441
paul718e3742002-12-13 20:15:29 +00001442
1443 if (vty->status == VTY_MORE)
1444 {
1445 switch (buf[i])
1446 {
1447 case CONTROL('C'):
1448 case 'q':
1449 case 'Q':
paul718e3742002-12-13 20:15:29 +00001450 vty_buffer_reset (vty);
1451 break;
1452#if 0 /* More line does not work for "show ip bgp". */
1453 case '\n':
1454 case '\r':
1455 vty->status = VTY_MORELINE;
1456 break;
1457#endif
1458 default:
paul718e3742002-12-13 20:15:29 +00001459 break;
1460 }
1461 continue;
1462 }
1463
1464 /* Escape character. */
1465 if (vty->escape == VTY_ESCAPE)
1466 {
1467 vty_escape_map (buf[i], vty);
1468 continue;
1469 }
1470
1471 /* Pre-escape status. */
1472 if (vty->escape == VTY_PRE_ESCAPE)
1473 {
1474 switch (buf[i])
1475 {
1476 case '[':
1477 vty->escape = VTY_ESCAPE;
1478 break;
1479 case 'b':
1480 vty_backward_word (vty);
1481 vty->escape = VTY_NORMAL;
1482 break;
1483 case 'f':
1484 vty_forward_word (vty);
1485 vty->escape = VTY_NORMAL;
1486 break;
1487 case 'd':
1488 vty_forward_kill_word (vty);
1489 vty->escape = VTY_NORMAL;
1490 break;
1491 case CONTROL('H'):
1492 case 0x7f:
1493 vty_backward_kill_word (vty);
1494 vty->escape = VTY_NORMAL;
1495 break;
1496 default:
1497 vty->escape = VTY_NORMAL;
1498 break;
1499 }
1500 continue;
1501 }
1502
1503 switch (buf[i])
1504 {
1505 case CONTROL('A'):
1506 vty_beginning_of_line (vty);
1507 break;
1508 case CONTROL('B'):
1509 vty_backward_char (vty);
1510 break;
1511 case CONTROL('C'):
1512 vty_stop_input (vty);
1513 break;
1514 case CONTROL('D'):
1515 vty_delete_char (vty);
1516 break;
1517 case CONTROL('E'):
1518 vty_end_of_line (vty);
1519 break;
1520 case CONTROL('F'):
1521 vty_forward_char (vty);
1522 break;
1523 case CONTROL('H'):
1524 case 0x7f:
1525 vty_delete_backward_char (vty);
1526 break;
1527 case CONTROL('K'):
1528 vty_kill_line (vty);
1529 break;
1530 case CONTROL('N'):
1531 vty_next_line (vty);
1532 break;
1533 case CONTROL('P'):
1534 vty_previous_line (vty);
1535 break;
1536 case CONTROL('T'):
1537 vty_transpose_chars (vty);
1538 break;
1539 case CONTROL('U'):
1540 vty_kill_line_from_beginning (vty);
1541 break;
1542 case CONTROL('W'):
1543 vty_backward_kill_word (vty);
1544 break;
1545 case CONTROL('Z'):
1546 vty_end_config (vty);
1547 break;
1548 case '\n':
1549 case '\r':
1550 vty_out (vty, "%s", VTY_NEWLINE);
1551 vty_execute (vty);
1552 break;
1553 case '\t':
1554 vty_complete_command (vty);
1555 break;
1556 case '?':
1557 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1558 vty_self_insert (vty, buf[i]);
1559 else
1560 vty_describe_command (vty);
1561 break;
1562 case '\033':
1563 if (i + 1 < nbytes && buf[i + 1] == '[')
1564 {
1565 vty->escape = VTY_ESCAPE;
1566 i++;
1567 }
1568 else
1569 vty->escape = VTY_PRE_ESCAPE;
1570 break;
1571 default:
1572 if (buf[i] > 31 && buf[i] < 127)
1573 vty_self_insert (vty, buf[i]);
1574 break;
1575 }
1576 }
1577
1578 /* Check status. */
1579 if (vty->status == VTY_CLOSE)
1580 vty_close (vty);
1581 else
1582 {
David Lamparter4715a532013-05-30 16:31:49 +02001583 vty_event (VTY_WRITE, vty->wfd, vty);
paul718e3742002-12-13 20:15:29 +00001584 vty_event (VTY_READ, vty_sock, vty);
1585 }
1586 return 0;
1587}
1588
1589/* Flush buffer to the vty. */
1590static int
1591vty_flush (struct thread *thread)
1592{
1593 int erase;
ajs9fc7ebf2005-02-23 15:12:34 +00001594 buffer_status_t flushrc;
paul718e3742002-12-13 20:15:29 +00001595 int vty_sock = THREAD_FD (thread);
1596 struct vty *vty = THREAD_ARG (thread);
ajs9fc7ebf2005-02-23 15:12:34 +00001597
paul718e3742002-12-13 20:15:29 +00001598 vty->t_write = NULL;
1599
1600 /* Tempolary disable read thread. */
ajs9fc7ebf2005-02-23 15:12:34 +00001601 if ((vty->lines == 0) && vty->t_read)
1602 {
1603 thread_cancel (vty->t_read);
1604 vty->t_read = NULL;
1605 }
paul718e3742002-12-13 20:15:29 +00001606
1607 /* Function execution continue. */
ajs9fc7ebf2005-02-23 15:12:34 +00001608 erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
paul718e3742002-12-13 20:15:29 +00001609
ajs9fc7ebf2005-02-23 15:12:34 +00001610 /* N.B. if width is 0, that means we don't know the window size. */
Lou Bergerc7f7e492016-01-12 13:41:49 -05001611 if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
David Lamparter4715a532013-05-30 16:31:49 +02001612 flushrc = buffer_flush_available(vty->obuf, vty_sock);
ajs9fc7ebf2005-02-23 15:12:34 +00001613 else if (vty->status == VTY_MORELINE)
David Lamparter4715a532013-05-30 16:31:49 +02001614 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001615 1, erase, 0);
1616 else
David Lamparter4715a532013-05-30 16:31:49 +02001617 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001618 vty->lines >= 0 ? vty->lines :
1619 vty->height,
1620 erase, 0);
1621 switch (flushrc)
1622 {
1623 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001624 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001625 zlog_warn("buffer_flush failed on vty client fd %d, closing",
1626 vty->fd);
1627 buffer_reset(vty->obuf);
1628 vty_close(vty);
1629 return 0;
1630 case BUFFER_EMPTY:
1631 if (vty->status == VTY_CLOSE)
1632 vty_close (vty);
paul718e3742002-12-13 20:15:29 +00001633 else
1634 {
ajs9fc7ebf2005-02-23 15:12:34 +00001635 vty->status = VTY_NORMAL;
paul718e3742002-12-13 20:15:29 +00001636 if (vty->lines == 0)
ajs9fc7ebf2005-02-23 15:12:34 +00001637 vty_event (VTY_READ, vty_sock, vty);
paul718e3742002-12-13 20:15:29 +00001638 }
ajs9fc7ebf2005-02-23 15:12:34 +00001639 break;
1640 case BUFFER_PENDING:
1641 /* There is more data waiting to be written. */
1642 vty->status = VTY_MORE;
1643 if (vty->lines == 0)
1644 vty_event (VTY_WRITE, vty_sock, vty);
1645 break;
1646 }
paul718e3742002-12-13 20:15:29 +00001647
1648 return 0;
1649}
1650
David Lamparterba5dc5e2013-05-30 16:33:45 +02001651/* allocate and initialise vty */
1652static struct vty *
1653vty_new_init (int vty_sock)
1654{
1655 struct vty *vty;
1656
1657 vty = vty_new ();
1658 vty->fd = vty_sock;
1659 vty->wfd = vty_sock;
1660 vty->type = VTY_TERM;
1661 vty->node = AUTH_NODE;
1662 vty->fail = 0;
1663 vty->cp = 0;
1664 vty_clear_buf (vty);
1665 vty->length = 0;
1666 memset (vty->hist, 0, sizeof (vty->hist));
1667 vty->hp = 0;
1668 vty->hindex = 0;
1669 vector_set_index (vtyvec, vty_sock, vty);
1670 vty->status = VTY_NORMAL;
1671 vty->lines = -1;
1672 vty->iac = 0;
1673 vty->iac_sb_in_progress = 0;
1674 vty->sb_len = 0;
1675
1676 return vty;
1677}
1678
paul718e3742002-12-13 20:15:29 +00001679/* Create new vty structure. */
ajs9fc7ebf2005-02-23 15:12:34 +00001680static struct vty *
paul718e3742002-12-13 20:15:29 +00001681vty_create (int vty_sock, union sockunion *su)
1682{
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001683 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001684 struct vty *vty;
1685
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001686 sockunion2str(su, buf, SU_ADDRSTRLEN);
1687
paul718e3742002-12-13 20:15:29 +00001688 /* Allocate new vty structure and set up default values. */
David Lamparterba5dc5e2013-05-30 16:33:45 +02001689 vty = vty_new_init (vty_sock);
1690
1691 /* configurable parameters not part of basic init */
1692 vty->v_timeout = vty_timeout_val;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001693 strcpy (vty->address, buf);
paul718e3742002-12-13 20:15:29 +00001694 if (no_password_check)
1695 {
Paul Jakma62687ff2008-08-23 14:27:06 +01001696 if (restricted_mode)
1697 vty->node = RESTRICTED_NODE;
1698 else if (host.advanced)
paul718e3742002-12-13 20:15:29 +00001699 vty->node = ENABLE_NODE;
1700 else
1701 vty->node = VIEW_NODE;
1702 }
paul718e3742002-12-13 20:15:29 +00001703 if (host.lines >= 0)
1704 vty->lines = host.lines;
paul718e3742002-12-13 20:15:29 +00001705
1706 if (! no_password_check)
1707 {
1708 /* Vty is not available if password isn't set. */
1709 if (host.password == NULL && host.password_encrypt == NULL)
1710 {
1711 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1712 vty->status = VTY_CLOSE;
1713 vty_close (vty);
1714 return NULL;
1715 }
1716 }
1717
1718 /* Say hello to the world. */
1719 vty_hello (vty);
1720 if (! no_password_check)
1721 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1722
1723 /* Setting up terminal. */
1724 vty_will_echo (vty);
1725 vty_will_suppress_go_ahead (vty);
1726
1727 vty_dont_linemode (vty);
1728 vty_do_window_size (vty);
1729 /* vty_dont_lflow_ahead (vty); */
1730
1731 vty_prompt (vty);
1732
1733 /* Add read/write thread. */
1734 vty_event (VTY_WRITE, vty_sock, vty);
1735 vty_event (VTY_READ, vty_sock, vty);
1736
1737 return vty;
1738}
1739
David Lamparterba5dc5e2013-05-30 16:33:45 +02001740/* create vty for stdio */
David Lamparterba53a8f2015-05-05 11:04:46 +02001741static struct termios stdio_orig_termios;
1742static struct vty *stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001743static void (*stdio_vty_atclose)(void);
David Lamparterba53a8f2015-05-05 11:04:46 +02001744
1745static void
1746vty_stdio_reset (void)
1747{
1748 if (stdio_vty)
1749 {
1750 tcsetattr (0, TCSANOW, &stdio_orig_termios);
1751 stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001752
1753 if (stdio_vty_atclose)
1754 stdio_vty_atclose ();
1755 stdio_vty_atclose = NULL;
David Lamparterba53a8f2015-05-05 11:04:46 +02001756 }
1757}
1758
David Lamparterba5dc5e2013-05-30 16:33:45 +02001759struct vty *
David Lamparter464ccf32015-05-12 21:56:18 +02001760vty_stdio (void (*atclose)())
David Lamparterba5dc5e2013-05-30 16:33:45 +02001761{
1762 struct vty *vty;
David Lamparterba53a8f2015-05-05 11:04:46 +02001763 struct termios termios;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001764
David Lamparterba53a8f2015-05-05 11:04:46 +02001765 /* refuse creating two vtys on stdio */
1766 if (stdio_vty)
1767 return NULL;
1768
1769 vty = stdio_vty = vty_new_init (0);
David Lamparter464ccf32015-05-12 21:56:18 +02001770 stdio_vty_atclose = atclose;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001771 vty->wfd = 1;
1772
1773 /* always have stdio vty in a known _unchangeable_ state, don't want config
1774 * to have any effect here to make sure scripting this works as intended */
1775 vty->node = ENABLE_NODE;
1776 vty->v_timeout = 0;
1777 strcpy (vty->address, "console");
1778
David Lamparterba53a8f2015-05-05 11:04:46 +02001779 if (!tcgetattr (0, &stdio_orig_termios))
1780 {
1781 termios = stdio_orig_termios;
1782 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
1783 | INLCR | IGNCR | ICRNL | IXON);
1784 termios.c_oflag &= ~OPOST;
1785 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
1786 termios.c_cflag &= ~(CSIZE | PARENB);
1787 termios.c_cflag |= CS8;
1788 tcsetattr (0, TCSANOW, &termios);
1789 }
1790
David Lamparterba5dc5e2013-05-30 16:33:45 +02001791 vty_prompt (vty);
1792
1793 /* Add read/write thread. */
1794 vty_event (VTY_WRITE, 1, vty);
1795 vty_event (VTY_READ, 0, vty);
1796
1797 return vty;
1798}
1799
paul718e3742002-12-13 20:15:29 +00001800/* Accept connection from the network. */
1801static int
1802vty_accept (struct thread *thread)
1803{
1804 int vty_sock;
paul718e3742002-12-13 20:15:29 +00001805 union sockunion su;
1806 int ret;
1807 unsigned int on;
1808 int accept_sock;
Timo Teräsc1c69e42015-05-22 13:40:57 +03001809 struct prefix p;
paul718e3742002-12-13 20:15:29 +00001810 struct access_list *acl = NULL;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001811 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001812
1813 accept_sock = THREAD_FD (thread);
1814
1815 /* We continue hearing vty socket. */
1816 vty_event (VTY_SERV, accept_sock, NULL);
1817
1818 memset (&su, 0, sizeof (union sockunion));
1819
1820 /* We can handle IPv4 or IPv6 socket. */
1821 vty_sock = sockunion_accept (accept_sock, &su);
1822 if (vty_sock < 0)
1823 {
ajs6099b3b2004-11-20 02:06:59 +00001824 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001825 return -1;
1826 }
ajs9fc7ebf2005-02-23 15:12:34 +00001827 set_nonblocking(vty_sock);
paul718e3742002-12-13 20:15:29 +00001828
Timo Teräsc1c69e42015-05-22 13:40:57 +03001829 sockunion2hostprefix (&su, &p);
paul718e3742002-12-13 20:15:29 +00001830
1831 /* VTY's accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001832 if (p.family == AF_INET && vty_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001833 {
1834 if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001835 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001836 {
paul718e3742002-12-13 20:15:29 +00001837 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001838 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001839 close (vty_sock);
1840
1841 /* continue accepting connections */
1842 vty_event (VTY_SERV, accept_sock, NULL);
1843
paul718e3742002-12-13 20:15:29 +00001844 return 0;
1845 }
1846 }
1847
1848#ifdef HAVE_IPV6
1849 /* VTY's ipv6 accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001850 if (p.family == AF_INET6 && vty_ipv6_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001851 {
1852 if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001853 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001854 {
paul718e3742002-12-13 20:15:29 +00001855 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001856 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001857 close (vty_sock);
1858
1859 /* continue accepting connections */
1860 vty_event (VTY_SERV, accept_sock, NULL);
1861
paul718e3742002-12-13 20:15:29 +00001862 return 0;
1863 }
1864 }
1865#endif /* HAVE_IPV6 */
1866
paul718e3742002-12-13 20:15:29 +00001867 on = 1;
1868 ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
1869 (char *) &on, sizeof (on));
1870 if (ret < 0)
1871 zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
ajs6099b3b2004-11-20 02:06:59 +00001872 safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001873
heasley78e6cd92009-12-07 16:41:14 +03001874 zlog (NULL, LOG_INFO, "Vty connection from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001875 sockunion2str (&su, buf, SU_ADDRSTRLEN));
heasley78e6cd92009-12-07 16:41:14 +03001876
Stephen Hemminger9206f9e2011-12-18 19:43:40 +04001877 vty_create (vty_sock, &su);
paul718e3742002-12-13 20:15:29 +00001878
1879 return 0;
1880}
1881
David Lamparter6d6df302014-06-28 21:12:37 +02001882#ifdef HAVE_IPV6
ajs9fc7ebf2005-02-23 15:12:34 +00001883static void
paul718e3742002-12-13 20:15:29 +00001884vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
1885{
1886 int ret;
1887 struct addrinfo req;
1888 struct addrinfo *ainfo;
1889 struct addrinfo *ainfo_save;
1890 int sock;
1891 char port_str[BUFSIZ];
1892
1893 memset (&req, 0, sizeof (struct addrinfo));
1894 req.ai_flags = AI_PASSIVE;
1895 req.ai_family = AF_UNSPEC;
1896 req.ai_socktype = SOCK_STREAM;
1897 sprintf (port_str, "%d", port);
1898 port_str[sizeof (port_str) - 1] = '\0';
1899
1900 ret = getaddrinfo (hostname, port_str, &req, &ainfo);
1901
1902 if (ret != 0)
1903 {
1904 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
1905 exit (1);
1906 }
1907
1908 ainfo_save = ainfo;
1909
1910 do
1911 {
1912 if (ainfo->ai_family != AF_INET
1913#ifdef HAVE_IPV6
1914 && ainfo->ai_family != AF_INET6
1915#endif /* HAVE_IPV6 */
1916 )
1917 continue;
1918
1919 sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
1920 if (sock < 0)
1921 continue;
1922
David Lamparterca051262009-10-04 16:21:49 +02001923 sockopt_v6only (ainfo->ai_family, sock);
paul718e3742002-12-13 20:15:29 +00001924 sockopt_reuseaddr (sock);
1925 sockopt_reuseport (sock);
1926
1927 ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
1928 if (ret < 0)
1929 {
1930 close (sock); /* Avoid sd leak. */
1931 continue;
1932 }
1933
1934 ret = listen (sock, 3);
1935 if (ret < 0)
1936 {
1937 close (sock); /* Avoid sd leak. */
1938 continue;
1939 }
1940
1941 vty_event (VTY_SERV, sock, NULL);
1942 }
1943 while ((ainfo = ainfo->ai_next) != NULL);
1944
1945 freeaddrinfo (ainfo_save);
1946}
David Lamparter6d6df302014-06-28 21:12:37 +02001947#else /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00001948
1949/* Make vty server socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00001950static void
paul29db05b2003-05-08 20:10:22 +00001951vty_serv_sock_family (const char* addr, unsigned short port, int family)
paul718e3742002-12-13 20:15:29 +00001952{
1953 int ret;
1954 union sockunion su;
1955 int accept_sock;
paul29db05b2003-05-08 20:10:22 +00001956 void* naddr=NULL;
paul718e3742002-12-13 20:15:29 +00001957
1958 memset (&su, 0, sizeof (union sockunion));
1959 su.sa.sa_family = family;
paul29db05b2003-05-08 20:10:22 +00001960 if(addr)
1961 switch(family)
1962 {
1963 case AF_INET:
1964 naddr=&su.sin.sin_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00001965 break;
paul29db05b2003-05-08 20:10:22 +00001966#ifdef HAVE_IPV6
1967 case AF_INET6:
1968 naddr=&su.sin6.sin6_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00001969 break;
paul29db05b2003-05-08 20:10:22 +00001970#endif
1971 }
1972
1973 if(naddr)
1974 switch(inet_pton(family,addr,naddr))
1975 {
1976 case -1:
1977 zlog_err("bad address %s",addr);
1978 naddr=NULL;
1979 break;
1980 case 0:
ajs6099b3b2004-11-20 02:06:59 +00001981 zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
paul29db05b2003-05-08 20:10:22 +00001982 naddr=NULL;
1983 }
paul718e3742002-12-13 20:15:29 +00001984
1985 /* Make new socket. */
1986 accept_sock = sockunion_stream_socket (&su);
1987 if (accept_sock < 0)
1988 return;
1989
1990 /* This is server, so reuse address. */
1991 sockopt_reuseaddr (accept_sock);
1992 sockopt_reuseport (accept_sock);
1993
1994 /* Bind socket to universal address and given port. */
paul29db05b2003-05-08 20:10:22 +00001995 ret = sockunion_bind (accept_sock, &su, port, naddr);
paul718e3742002-12-13 20:15:29 +00001996 if (ret < 0)
1997 {
paul29db05b2003-05-08 20:10:22 +00001998 zlog_warn("can't bind socket");
paul718e3742002-12-13 20:15:29 +00001999 close (accept_sock); /* Avoid sd leak. */
2000 return;
2001 }
2002
2003 /* Listen socket under queue 3. */
2004 ret = listen (accept_sock, 3);
2005 if (ret < 0)
2006 {
2007 zlog (NULL, LOG_WARNING, "can't listen socket");
2008 close (accept_sock); /* Avoid sd leak. */
2009 return;
2010 }
2011
2012 /* Add vty server event. */
2013 vty_event (VTY_SERV, accept_sock, NULL);
2014}
David Lamparter6d6df302014-06-28 21:12:37 +02002015#endif /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00002016
2017#ifdef VTYSH
2018/* For sockaddr_un. */
2019#include <sys/un.h>
2020
2021/* VTY shell UNIX domain socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00002022static void
hasso6ad96ea2004-10-07 19:33:46 +00002023vty_serv_un (const char *path)
paul718e3742002-12-13 20:15:29 +00002024{
2025 int ret;
paul75e15fe2004-10-31 02:13:09 +00002026 int sock, len;
paul718e3742002-12-13 20:15:29 +00002027 struct sockaddr_un serv;
2028 mode_t old_mask;
pauledd7c242003-06-04 13:59:38 +00002029 struct zprivs_ids_t ids;
2030
paul718e3742002-12-13 20:15:29 +00002031 /* First of all, unlink existing socket */
2032 unlink (path);
2033
2034 /* Set umask */
paul1921e6f2003-05-23 08:12:36 +00002035 old_mask = umask (0007);
paul718e3742002-12-13 20:15:29 +00002036
2037 /* Make UNIX domain socket. */
2038 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2039 if (sock < 0)
2040 {
ajs6a52d0d2005-01-30 18:49:28 +00002041 zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002042 return;
2043 }
2044
2045 /* Make server socket. */
2046 memset (&serv, 0, sizeof (struct sockaddr_un));
2047 serv.sun_family = AF_UNIX;
2048 strncpy (serv.sun_path, path, strlen (path));
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002049#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
paul718e3742002-12-13 20:15:29 +00002050 len = serv.sun_len = SUN_LEN(&serv);
2051#else
2052 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002053#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
paul718e3742002-12-13 20:15:29 +00002054
2055 ret = bind (sock, (struct sockaddr *) &serv, len);
2056 if (ret < 0)
2057 {
ajs6a52d0d2005-01-30 18:49:28 +00002058 zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002059 close (sock); /* Avoid sd leak. */
2060 return;
2061 }
2062
2063 ret = listen (sock, 5);
2064 if (ret < 0)
2065 {
ajs6a52d0d2005-01-30 18:49:28 +00002066 zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002067 close (sock); /* Avoid sd leak. */
2068 return;
2069 }
2070
2071 umask (old_mask);
2072
pauledd7c242003-06-04 13:59:38 +00002073 zprivs_get_ids(&ids);
2074
2075 if (ids.gid_vty > 0)
2076 {
2077 /* set group of socket */
2078 if ( chown (path, -1, ids.gid_vty) )
2079 {
2080 zlog_err ("vty_serv_un: could chown socket, %s",
ajs6099b3b2004-11-20 02:06:59 +00002081 safe_strerror (errno) );
pauledd7c242003-06-04 13:59:38 +00002082 }
2083 }
2084
paul718e3742002-12-13 20:15:29 +00002085 vty_event (VTYSH_SERV, sock, NULL);
2086}
2087
2088/* #define VTYSH_DEBUG 1 */
2089
2090static int
2091vtysh_accept (struct thread *thread)
2092{
2093 int accept_sock;
2094 int sock;
2095 int client_len;
2096 struct sockaddr_un client;
2097 struct vty *vty;
2098
2099 accept_sock = THREAD_FD (thread);
2100
2101 vty_event (VTYSH_SERV, accept_sock, NULL);
2102
2103 memset (&client, 0, sizeof (struct sockaddr_un));
2104 client_len = sizeof (struct sockaddr_un);
2105
hassoe473b032004-09-26 16:08:11 +00002106 sock = accept (accept_sock, (struct sockaddr *) &client,
2107 (socklen_t *) &client_len);
paul718e3742002-12-13 20:15:29 +00002108
2109 if (sock < 0)
2110 {
ajs6099b3b2004-11-20 02:06:59 +00002111 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00002112 return -1;
2113 }
2114
ajs9fc7ebf2005-02-23 15:12:34 +00002115 if (set_nonblocking(sock) < 0)
paul75e15fe2004-10-31 02:13:09 +00002116 {
ajs9fc7ebf2005-02-23 15:12:34 +00002117 zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
2118 " %s, closing", sock, safe_strerror (errno));
paul75e15fe2004-10-31 02:13:09 +00002119 close (sock);
2120 return -1;
2121 }
pauldccfb192004-10-29 08:29:36 +00002122
paul718e3742002-12-13 20:15:29 +00002123#ifdef VTYSH_DEBUG
2124 printf ("VTY shell accept\n");
2125#endif /* VTYSH_DEBUG */
2126
2127 vty = vty_new ();
2128 vty->fd = sock;
David Lamparter4715a532013-05-30 16:31:49 +02002129 vty->wfd = sock;
paul718e3742002-12-13 20:15:29 +00002130 vty->type = VTY_SHELL_SERV;
2131 vty->node = VIEW_NODE;
2132
2133 vty_event (VTYSH_READ, sock, vty);
2134
2135 return 0;
2136}
2137
2138static int
ajs9fc7ebf2005-02-23 15:12:34 +00002139vtysh_flush(struct vty *vty)
2140{
David Lamparter4715a532013-05-30 16:31:49 +02002141 switch (buffer_flush_available(vty->obuf, vty->wfd))
ajs9fc7ebf2005-02-23 15:12:34 +00002142 {
2143 case BUFFER_PENDING:
David Lamparter4715a532013-05-30 16:31:49 +02002144 vty_event(VTYSH_WRITE, vty->wfd, vty);
ajs9fc7ebf2005-02-23 15:12:34 +00002145 break;
2146 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002147 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002148 zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
2149 buffer_reset(vty->obuf);
2150 vty_close(vty);
2151 return -1;
2152 break;
2153 case BUFFER_EMPTY:
2154 break;
2155 }
2156 return 0;
2157}
2158
2159static int
paul718e3742002-12-13 20:15:29 +00002160vtysh_read (struct thread *thread)
2161{
2162 int ret;
2163 int sock;
2164 int nbytes;
2165 struct vty *vty;
2166 unsigned char buf[VTY_READ_BUFSIZ];
ajs9fc7ebf2005-02-23 15:12:34 +00002167 unsigned char *p;
paul718e3742002-12-13 20:15:29 +00002168 u_char header[4] = {0, 0, 0, 0};
2169
2170 sock = THREAD_FD (thread);
2171 vty = THREAD_ARG (thread);
2172 vty->t_read = NULL;
2173
ajs9fc7ebf2005-02-23 15:12:34 +00002174 if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
paul718e3742002-12-13 20:15:29 +00002175 {
ajs9fc7ebf2005-02-23 15:12:34 +00002176 if (nbytes < 0)
2177 {
2178 if (ERRNO_IO_RETRY(errno))
2179 {
2180 vty_event (VTYSH_READ, sock, vty);
2181 return 0;
2182 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002183 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002184 zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
2185 __func__, sock, safe_strerror(errno));
2186 }
2187 buffer_reset(vty->obuf);
paul718e3742002-12-13 20:15:29 +00002188 vty_close (vty);
2189#ifdef VTYSH_DEBUG
2190 printf ("close vtysh\n");
2191#endif /* VTYSH_DEBUG */
2192 return 0;
2193 }
2194
2195#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002196 printf ("line: %.*s\n", nbytes, buf);
paul718e3742002-12-13 20:15:29 +00002197#endif /* VTYSH_DEBUG */
2198
ajs9fc7ebf2005-02-23 15:12:34 +00002199 for (p = buf; p < buf+nbytes; p++)
2200 {
2201 vty_ensure(vty, vty->length+1);
2202 vty->buf[vty->length++] = *p;
2203 if (*p == '\0')
2204 {
2205 /* Pass this line to parser. */
2206 ret = vty_execute (vty);
2207 /* Note that vty_execute clears the command buffer and resets
2208 vty->length to 0. */
paul718e3742002-12-13 20:15:29 +00002209
ajs9fc7ebf2005-02-23 15:12:34 +00002210 /* Return result. */
paul718e3742002-12-13 20:15:29 +00002211#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002212 printf ("result: %d\n", ret);
2213 printf ("vtysh node: %d\n", vty->node);
paul718e3742002-12-13 20:15:29 +00002214#endif /* VTYSH_DEBUG */
2215
ajs9fc7ebf2005-02-23 15:12:34 +00002216 header[3] = ret;
2217 buffer_put(vty->obuf, header, 4);
paul718e3742002-12-13 20:15:29 +00002218
ajs9fc7ebf2005-02-23 15:12:34 +00002219 if (!vty->t_write && (vtysh_flush(vty) < 0))
2220 /* Try to flush results; exit if a write error occurs. */
2221 return 0;
2222 }
2223 }
2224
paul718e3742002-12-13 20:15:29 +00002225 vty_event (VTYSH_READ, sock, vty);
2226
2227 return 0;
2228}
ajs49ff6d92004-11-04 19:26:16 +00002229
2230static int
2231vtysh_write (struct thread *thread)
2232{
2233 struct vty *vty = THREAD_ARG (thread);
2234
2235 vty->t_write = NULL;
ajs9fc7ebf2005-02-23 15:12:34 +00002236 vtysh_flush(vty);
ajs976d8c72004-11-10 15:40:09 +00002237 return 0;
ajs49ff6d92004-11-04 19:26:16 +00002238}
2239
paul718e3742002-12-13 20:15:29 +00002240#endif /* VTYSH */
2241
2242/* Determine address family to bind. */
2243void
hasso6ad96ea2004-10-07 19:33:46 +00002244vty_serv_sock (const char *addr, unsigned short port, const char *path)
paul718e3742002-12-13 20:15:29 +00002245{
2246 /* If port is set to 0, do not listen on TCP/IP at all! */
2247 if (port)
2248 {
2249
2250#ifdef HAVE_IPV6
paul29db05b2003-05-08 20:10:22 +00002251 vty_serv_sock_addrinfo (addr, port);
paul718e3742002-12-13 20:15:29 +00002252#else /* ! HAVE_IPV6 */
paul29db05b2003-05-08 20:10:22 +00002253 vty_serv_sock_family (addr,port, AF_INET);
paul718e3742002-12-13 20:15:29 +00002254#endif /* HAVE_IPV6 */
2255 }
2256
2257#ifdef VTYSH
2258 vty_serv_un (path);
2259#endif /* VTYSH */
2260}
2261
Andrew J. Schorr9d0a3262006-07-11 00:06:49 +00002262/* Close vty interface. Warning: call this only from functions that
2263 will be careful not to access the vty afterwards (since it has
2264 now been freed). This is safest from top-level functions (called
2265 directly by the thread dispatcher). */
paul718e3742002-12-13 20:15:29 +00002266void
2267vty_close (struct vty *vty)
2268{
2269 int i;
2270
2271 /* Cancel threads.*/
2272 if (vty->t_read)
2273 thread_cancel (vty->t_read);
2274 if (vty->t_write)
2275 thread_cancel (vty->t_write);
2276 if (vty->t_timeout)
2277 thread_cancel (vty->t_timeout);
paul718e3742002-12-13 20:15:29 +00002278
2279 /* Flush buffer. */
David Lamparter4715a532013-05-30 16:31:49 +02002280 buffer_flush_all (vty->obuf, vty->wfd);
paul718e3742002-12-13 20:15:29 +00002281
2282 /* Free input buffer. */
2283 buffer_free (vty->obuf);
2284
paul718e3742002-12-13 20:15:29 +00002285 /* Free command history. */
2286 for (i = 0; i < VTY_MAXHIST; i++)
2287 if (vty->hist[i])
2288 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2289
2290 /* Unset vector. */
2291 vector_unset (vtyvec, vty->fd);
2292
2293 /* Close socket. */
2294 if (vty->fd > 0)
2295 close (vty->fd);
David Lamparterba53a8f2015-05-05 11:04:46 +02002296 else
2297 vty_stdio_reset ();
paul718e3742002-12-13 20:15:29 +00002298
paul718e3742002-12-13 20:15:29 +00002299 if (vty->buf)
2300 XFREE (MTYPE_VTY, vty->buf);
2301
2302 /* Check configure. */
2303 vty_config_unlock (vty);
2304
2305 /* OK free vty. */
2306 XFREE (MTYPE_VTY, vty);
2307}
2308
2309/* When time out occur output message then close connection. */
2310static int
2311vty_timeout (struct thread *thread)
2312{
2313 struct vty *vty;
2314
2315 vty = THREAD_ARG (thread);
2316 vty->t_timeout = NULL;
2317 vty->v_timeout = 0;
2318
2319 /* Clear buffer*/
2320 buffer_reset (vty->obuf);
2321 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2322
2323 /* Close connection. */
2324 vty->status = VTY_CLOSE;
2325 vty_close (vty);
2326
2327 return 0;
2328}
2329
2330/* Read up configuration file from file_name. */
2331static void
2332vty_read_file (FILE *confp)
2333{
2334 int ret;
2335 struct vty *vty;
Steve Hillea555002009-07-28 16:36:14 -04002336 unsigned int line_num = 0;
paul718e3742002-12-13 20:15:29 +00002337
2338 vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02002339 vty->wfd = dup(STDERR_FILENO); /* vty_close() will close this */
2340 if (vty->wfd < 0)
Steve Hillea555002009-07-28 16:36:14 -04002341 {
2342 /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
David Lamparter4715a532013-05-30 16:31:49 +02002343 vty->wfd = STDOUT_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002344 }
David Lamparter4715a532013-05-30 16:31:49 +02002345 vty->fd = STDIN_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002346 vty->type = VTY_FILE;
paul718e3742002-12-13 20:15:29 +00002347 vty->node = CONFIG_NODE;
2348
2349 /* Execute configuration file */
Steve Hillea555002009-07-28 16:36:14 -04002350 ret = config_from_file (vty, confp, &line_num);
2351
2352 /* Flush any previous errors before printing messages below */
2353 buffer_flush_all (vty->obuf, vty->fd);
paul718e3742002-12-13 20:15:29 +00002354
paul7021c422003-07-15 12:52:22 +00002355 if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
paul718e3742002-12-13 20:15:29 +00002356 {
2357 switch (ret)
paul7021c422003-07-15 12:52:22 +00002358 {
2359 case CMD_ERR_AMBIGUOUS:
Steve Hillea555002009-07-28 16:36:14 -04002360 fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
paul7021c422003-07-15 12:52:22 +00002361 break;
2362 case CMD_ERR_NO_MATCH:
Steve Hillea555002009-07-28 16:36:14 -04002363 fprintf (stderr, "*** Error reading config: There is no such command.\n");
paul7021c422003-07-15 12:52:22 +00002364 break;
2365 }
Steve Hillea555002009-07-28 16:36:14 -04002366 fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
2367 line_num, vty->buf);
paul718e3742002-12-13 20:15:29 +00002368 vty_close (vty);
2369 exit (1);
2370 }
2371
2372 vty_close (vty);
2373}
2374
ajs9fc7ebf2005-02-23 15:12:34 +00002375static FILE *
paul718e3742002-12-13 20:15:29 +00002376vty_use_backup_config (char *fullpath)
2377{
2378 char *fullpath_sav, *fullpath_tmp;
2379 FILE *ret = NULL;
2380 struct stat buf;
2381 int tmp, sav;
2382 int c;
2383 char buffer[512];
2384
2385 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2386 strcpy (fullpath_sav, fullpath);
2387 strcat (fullpath_sav, CONF_BACKUP_EXT);
2388 if (stat (fullpath_sav, &buf) == -1)
2389 {
2390 free (fullpath_sav);
2391 return NULL;
2392 }
2393
2394 fullpath_tmp = malloc (strlen (fullpath) + 8);
2395 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2396
2397 /* Open file to configuration write. */
2398 tmp = mkstemp (fullpath_tmp);
2399 if (tmp < 0)
2400 {
2401 free (fullpath_sav);
2402 free (fullpath_tmp);
2403 return NULL;
2404 }
2405
2406 sav = open (fullpath_sav, O_RDONLY);
2407 if (sav < 0)
2408 {
gdt3dbf9962003-12-22 20:18:18 +00002409 unlink (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002410 free (fullpath_sav);
2411 free (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002412 return NULL;
2413 }
2414
2415 while((c = read (sav, buffer, 512)) > 0)
2416 write (tmp, buffer, c);
2417
2418 close (sav);
2419 close (tmp);
2420
gdtaa593d52003-12-22 20:15:53 +00002421 if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
2422 {
gdt3dbf9962003-12-22 20:18:18 +00002423 unlink (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002424 free (fullpath_sav);
2425 free (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002426 return NULL;
2427 }
2428
paul718e3742002-12-13 20:15:29 +00002429 if (link (fullpath_tmp, fullpath) == 0)
2430 ret = fopen (fullpath, "r");
2431
2432 unlink (fullpath_tmp);
2433
2434 free (fullpath_sav);
2435 free (fullpath_tmp);
hasso12f6ea22005-03-07 08:35:39 +00002436 return ret;
paul718e3742002-12-13 20:15:29 +00002437}
2438
2439/* Read up configuration file from file_name. */
2440void
2441vty_read_config (char *config_file,
hasso320ec102004-06-20 19:54:37 +00002442 char *config_default_dir)
paul718e3742002-12-13 20:15:29 +00002443{
paulccc92352003-10-22 02:49:38 +00002444 char cwd[MAXPATHLEN];
paul718e3742002-12-13 20:15:29 +00002445 FILE *confp = NULL;
2446 char *fullpath;
paul05865c92005-10-26 05:49:54 +00002447 char *tmp = NULL;
paul718e3742002-12-13 20:15:29 +00002448
2449 /* If -f flag specified. */
2450 if (config_file != NULL)
2451 {
2452 if (! IS_DIRECTORY_SEP (config_file[0]))
hasso320ec102004-06-20 19:54:37 +00002453 {
2454 getcwd (cwd, MAXPATHLEN);
paul05865c92005-10-26 05:49:54 +00002455 tmp = XMALLOC (MTYPE_TMP,
hasso320ec102004-06-20 19:54:37 +00002456 strlen (cwd) + strlen (config_file) + 2);
paul05865c92005-10-26 05:49:54 +00002457 sprintf (tmp, "%s/%s", cwd, config_file);
2458 fullpath = tmp;
hasso320ec102004-06-20 19:54:37 +00002459 }
paul718e3742002-12-13 20:15:29 +00002460 else
hasso320ec102004-06-20 19:54:37 +00002461 fullpath = config_file;
paul718e3742002-12-13 20:15:29 +00002462
2463 confp = fopen (fullpath, "r");
2464
2465 if (confp == NULL)
hasso320ec102004-06-20 19:54:37 +00002466 {
paul3d1dc852005-04-05 00:45:23 +00002467 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2468 __func__, fullpath, safe_strerror (errno));
2469
hasso320ec102004-06-20 19:54:37 +00002470 confp = vty_use_backup_config (fullpath);
2471 if (confp)
2472 fprintf (stderr, "WARNING: using backup configuration file!\n");
2473 else
2474 {
2475 fprintf (stderr, "can't open configuration file [%s]\n",
paul3d1dc852005-04-05 00:45:23 +00002476 config_file);
hasso320ec102004-06-20 19:54:37 +00002477 exit(1);
2478 }
2479 }
paul718e3742002-12-13 20:15:29 +00002480 }
2481 else
2482 {
paul718e3742002-12-13 20:15:29 +00002483#ifdef VTYSH
hasso320ec102004-06-20 19:54:37 +00002484 int ret;
2485 struct stat conf_stat;
paul718e3742002-12-13 20:15:29 +00002486
hasso320ec102004-06-20 19:54:37 +00002487 /* !!!!PLEASE LEAVE!!!!
2488 * This is NEEDED for use with vtysh -b, or else you can get
2489 * a real configuration food fight with a lot garbage in the
2490 * merged configuration file it creates coming from the per
2491 * daemon configuration files. This also allows the daemons
2492 * to start if there default configuration file is not
2493 * present or ignore them, as needed when using vtysh -b to
2494 * configure the daemons at boot - MAG
2495 */
paul718e3742002-12-13 20:15:29 +00002496
hasso320ec102004-06-20 19:54:37 +00002497 /* Stat for vtysh Zebra.conf, if found startup and wait for
2498 * boot configuration
2499 */
paul718e3742002-12-13 20:15:29 +00002500
hasso320ec102004-06-20 19:54:37 +00002501 if ( strstr(config_default_dir, "vtysh") == NULL)
2502 {
2503 ret = stat (integrate_default, &conf_stat);
2504 if (ret >= 0)
2505 return;
2506 }
paul718e3742002-12-13 20:15:29 +00002507#endif /* VTYSH */
2508
hasso320ec102004-06-20 19:54:37 +00002509 confp = fopen (config_default_dir, "r");
2510 if (confp == NULL)
2511 {
paul3d1dc852005-04-05 00:45:23 +00002512 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2513 __func__, config_default_dir, safe_strerror (errno));
2514
hasso320ec102004-06-20 19:54:37 +00002515 confp = vty_use_backup_config (config_default_dir);
2516 if (confp)
2517 {
2518 fprintf (stderr, "WARNING: using backup configuration file!\n");
2519 fullpath = config_default_dir;
2520 }
2521 else
2522 {
2523 fprintf (stderr, "can't open configuration file [%s]\n",
2524 config_default_dir);
2525 exit (1);
paul3d1dc852005-04-05 00:45:23 +00002526 }
hasso320ec102004-06-20 19:54:37 +00002527 }
paul718e3742002-12-13 20:15:29 +00002528 else
hasso320ec102004-06-20 19:54:37 +00002529 fullpath = config_default_dir;
2530 }
2531
paul718e3742002-12-13 20:15:29 +00002532 vty_read_file (confp);
2533
2534 fclose (confp);
2535
2536 host_config_set (fullpath);
paul05865c92005-10-26 05:49:54 +00002537
2538 if (tmp)
2539 XFREE (MTYPE_TMP, fullpath);
paul718e3742002-12-13 20:15:29 +00002540}
2541
2542/* Small utility function which output log to the VTY. */
2543void
ajs274a4a42004-12-07 15:39:31 +00002544vty_log (const char *level, const char *proto_str,
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002545 const char *format, struct timestamp_control *ctl, va_list va)
paul718e3742002-12-13 20:15:29 +00002546{
hasso8c328f12004-10-05 21:01:23 +00002547 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002548 struct vty *vty;
Paul Jakmaa4b30302006-05-28 08:18:38 +00002549
2550 if (!vtyvec)
2551 return;
paul718e3742002-12-13 20:15:29 +00002552
paul55468c82005-03-14 20:19:01 +00002553 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002554 if ((vty = vector_slot (vtyvec, i)) != NULL)
2555 if (vty->monitor)
ajsd246bd92004-11-23 17:35:08 +00002556 {
2557 va_list ac;
2558 va_copy(ac, va);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002559 vty_log_out (vty, level, proto_str, format, ctl, ac);
ajsd246bd92004-11-23 17:35:08 +00002560 va_end(ac);
2561 }
paul718e3742002-12-13 20:15:29 +00002562}
2563
ajs274a4a42004-12-07 15:39:31 +00002564/* Async-signal-safe version of vty_log for fixed strings. */
2565void
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002566vty_log_fixed (char *buf, size_t len)
ajs274a4a42004-12-07 15:39:31 +00002567{
2568 unsigned int i;
ajs9fc7ebf2005-02-23 15:12:34 +00002569 struct iovec iov[2];
2570
Paul Jakmaa4b30302006-05-28 08:18:38 +00002571 /* vty may not have been initialised */
2572 if (!vtyvec)
2573 return;
2574
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002575 iov[0].iov_base = buf;
ajs9fc7ebf2005-02-23 15:12:34 +00002576 iov[0].iov_len = len;
ajs926fe8f2005-04-08 18:50:40 +00002577 iov[1].iov_base = (void *)"\r\n";
ajs9fc7ebf2005-02-23 15:12:34 +00002578 iov[1].iov_len = 2;
ajs274a4a42004-12-07 15:39:31 +00002579
paul55468c82005-03-14 20:19:01 +00002580 for (i = 0; i < vector_active (vtyvec); i++)
ajs274a4a42004-12-07 15:39:31 +00002581 {
2582 struct vty *vty;
ajs9fc7ebf2005-02-23 15:12:34 +00002583 if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
2584 /* N.B. We don't care about the return code, since process is
2585 most likely just about to die anyway. */
David Lamparter4715a532013-05-30 16:31:49 +02002586 writev(vty->wfd, iov, 2);
ajs274a4a42004-12-07 15:39:31 +00002587 }
2588}
2589
paul718e3742002-12-13 20:15:29 +00002590int
2591vty_config_lock (struct vty *vty)
2592{
2593 if (vty_config == 0)
2594 {
2595 vty->config = 1;
2596 vty_config = 1;
2597 }
2598 return vty->config;
2599}
2600
2601int
2602vty_config_unlock (struct vty *vty)
2603{
2604 if (vty_config == 1 && vty->config == 1)
2605 {
2606 vty->config = 0;
2607 vty_config = 0;
2608 }
2609 return vty->config;
2610}
David Lamparter6b0655a2014-06-04 06:53:35 +02002611
paul718e3742002-12-13 20:15:29 +00002612/* Master of the threads. */
Donald Sharpeeef0db2015-10-14 08:50:38 -04002613static struct thread_master *vty_master;
paul718e3742002-12-13 20:15:29 +00002614
2615static void
2616vty_event (enum event event, int sock, struct vty *vty)
2617{
2618 struct thread *vty_serv_thread;
2619
2620 switch (event)
2621 {
2622 case VTY_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002623 vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
paul718e3742002-12-13 20:15:29 +00002624 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2625 break;
2626#ifdef VTYSH
2627 case VTYSH_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002628 vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
Christian Franke677bcbb2013-02-27 13:47:23 +00002629 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
paul718e3742002-12-13 20:15:29 +00002630 break;
2631 case VTYSH_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002632 vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
ajs49ff6d92004-11-04 19:26:16 +00002633 break;
2634 case VTYSH_WRITE:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002635 vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
paul718e3742002-12-13 20:15:29 +00002636 break;
2637#endif /* VTYSH */
2638 case VTY_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002639 vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
paul718e3742002-12-13 20:15:29 +00002640
2641 /* Time out treatment. */
2642 if (vty->v_timeout)
2643 {
2644 if (vty->t_timeout)
2645 thread_cancel (vty->t_timeout);
2646 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002647 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002648 }
2649 break;
2650 case VTY_WRITE:
2651 if (! vty->t_write)
Donald Sharpeeef0db2015-10-14 08:50:38 -04002652 vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
paul718e3742002-12-13 20:15:29 +00002653 break;
2654 case VTY_TIMEOUT_RESET:
2655 if (vty->t_timeout)
2656 {
2657 thread_cancel (vty->t_timeout);
2658 vty->t_timeout = NULL;
2659 }
2660 if (vty->v_timeout)
2661 {
2662 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002663 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002664 }
2665 break;
2666 }
2667}
David Lamparter6b0655a2014-06-04 06:53:35 +02002668
paul718e3742002-12-13 20:15:29 +00002669DEFUN (config_who,
2670 config_who_cmd,
2671 "who",
2672 "Display who is on vty\n")
2673{
hasso8c328f12004-10-05 21:01:23 +00002674 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002675 struct vty *v;
2676
paul55468c82005-03-14 20:19:01 +00002677 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002678 if ((v = vector_slot (vtyvec, i)) != NULL)
2679 vty_out (vty, "%svty[%d] connected from %s.%s",
2680 v->config ? "*" : " ",
2681 i, v->address, VTY_NEWLINE);
2682 return CMD_SUCCESS;
2683}
2684
2685/* Move to vty configuration mode. */
2686DEFUN (line_vty,
2687 line_vty_cmd,
2688 "line vty",
2689 "Configure a terminal line\n"
2690 "Virtual terminal\n")
2691{
2692 vty->node = VTY_NODE;
2693 return CMD_SUCCESS;
2694}
2695
2696/* Set time out value. */
ajs9fc7ebf2005-02-23 15:12:34 +00002697static int
paul9035efa2004-10-10 11:56:56 +00002698exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
paul718e3742002-12-13 20:15:29 +00002699{
2700 unsigned long timeout = 0;
2701
2702 /* min_str and sec_str are already checked by parser. So it must be
2703 all digit string. */
2704 if (min_str)
2705 {
2706 timeout = strtol (min_str, NULL, 10);
2707 timeout *= 60;
2708 }
2709 if (sec_str)
2710 timeout += strtol (sec_str, NULL, 10);
2711
2712 vty_timeout_val = timeout;
2713 vty->v_timeout = timeout;
2714 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2715
2716
2717 return CMD_SUCCESS;
2718}
2719
2720DEFUN (exec_timeout_min,
2721 exec_timeout_min_cmd,
2722 "exec-timeout <0-35791>",
2723 "Set timeout value\n"
2724 "Timeout value in minutes\n")
2725{
2726 return exec_timeout (vty, argv[0], NULL);
2727}
2728
2729DEFUN (exec_timeout_sec,
2730 exec_timeout_sec_cmd,
2731 "exec-timeout <0-35791> <0-2147483>",
2732 "Set the EXEC timeout\n"
2733 "Timeout in minutes\n"
2734 "Timeout in seconds\n")
2735{
2736 return exec_timeout (vty, argv[0], argv[1]);
2737}
2738
2739DEFUN (no_exec_timeout,
2740 no_exec_timeout_cmd,
2741 "no exec-timeout",
2742 NO_STR
2743 "Set the EXEC timeout\n")
2744{
2745 return exec_timeout (vty, NULL, NULL);
2746}
2747
2748/* Set vty access class. */
2749DEFUN (vty_access_class,
2750 vty_access_class_cmd,
2751 "access-class WORD",
2752 "Filter connections based on an IP access list\n"
2753 "IP access list\n")
2754{
2755 if (vty_accesslist_name)
2756 XFREE(MTYPE_VTY, vty_accesslist_name);
2757
2758 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2759
2760 return CMD_SUCCESS;
2761}
2762
2763/* Clear vty access class. */
2764DEFUN (no_vty_access_class,
2765 no_vty_access_class_cmd,
2766 "no access-class [WORD]",
2767 NO_STR
2768 "Filter connections based on an IP access list\n"
2769 "IP access list\n")
2770{
2771 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2772 {
2773 vty_out (vty, "Access-class is not currently applied to vty%s",
2774 VTY_NEWLINE);
2775 return CMD_WARNING;
2776 }
2777
2778 XFREE(MTYPE_VTY, vty_accesslist_name);
2779
2780 vty_accesslist_name = NULL;
2781
2782 return CMD_SUCCESS;
2783}
2784
2785#ifdef HAVE_IPV6
2786/* Set vty access class. */
2787DEFUN (vty_ipv6_access_class,
2788 vty_ipv6_access_class_cmd,
2789 "ipv6 access-class WORD",
2790 IPV6_STR
2791 "Filter connections based on an IP access list\n"
2792 "IPv6 access list\n")
2793{
2794 if (vty_ipv6_accesslist_name)
2795 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2796
2797 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2798
2799 return CMD_SUCCESS;
2800}
2801
2802/* Clear vty access class. */
2803DEFUN (no_vty_ipv6_access_class,
2804 no_vty_ipv6_access_class_cmd,
2805 "no ipv6 access-class [WORD]",
2806 NO_STR
2807 IPV6_STR
2808 "Filter connections based on an IP access list\n"
2809 "IPv6 access list\n")
2810{
2811 if (! vty_ipv6_accesslist_name ||
2812 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2813 {
2814 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2815 VTY_NEWLINE);
2816 return CMD_WARNING;
2817 }
2818
2819 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2820
2821 vty_ipv6_accesslist_name = NULL;
2822
2823 return CMD_SUCCESS;
2824}
2825#endif /* HAVE_IPV6 */
2826
2827/* vty login. */
2828DEFUN (vty_login,
2829 vty_login_cmd,
2830 "login",
2831 "Enable password checking\n")
2832{
2833 no_password_check = 0;
2834 return CMD_SUCCESS;
2835}
2836
2837DEFUN (no_vty_login,
2838 no_vty_login_cmd,
2839 "no login",
2840 NO_STR
2841 "Enable password checking\n")
2842{
2843 no_password_check = 1;
2844 return CMD_SUCCESS;
2845}
2846
Paul Jakma62687ff2008-08-23 14:27:06 +01002847/* initial mode. */
2848DEFUN (vty_restricted_mode,
2849 vty_restricted_mode_cmd,
2850 "anonymous restricted",
2851 "Restrict view commands available in anonymous, unauthenticated vty\n")
2852{
2853 restricted_mode = 1;
2854 return CMD_SUCCESS;
2855}
2856
2857DEFUN (vty_no_restricted_mode,
2858 vty_no_restricted_mode_cmd,
2859 "no anonymous restricted",
2860 NO_STR
2861 "Enable password checking\n")
2862{
2863 restricted_mode = 0;
2864 return CMD_SUCCESS;
2865}
2866
paul718e3742002-12-13 20:15:29 +00002867DEFUN (service_advanced_vty,
2868 service_advanced_vty_cmd,
2869 "service advanced-vty",
2870 "Set up miscellaneous service\n"
2871 "Enable advanced mode vty interface\n")
2872{
2873 host.advanced = 1;
2874 return CMD_SUCCESS;
2875}
2876
2877DEFUN (no_service_advanced_vty,
2878 no_service_advanced_vty_cmd,
2879 "no service advanced-vty",
2880 NO_STR
2881 "Set up miscellaneous service\n"
2882 "Enable advanced mode vty interface\n")
2883{
2884 host.advanced = 0;
2885 return CMD_SUCCESS;
2886}
2887
2888DEFUN (terminal_monitor,
2889 terminal_monitor_cmd,
2890 "terminal monitor",
2891 "Set terminal line parameters\n"
2892 "Copy debug output to the current terminal line\n")
2893{
2894 vty->monitor = 1;
2895 return CMD_SUCCESS;
2896}
2897
2898DEFUN (terminal_no_monitor,
2899 terminal_no_monitor_cmd,
2900 "terminal no monitor",
2901 "Set terminal line parameters\n"
2902 NO_STR
2903 "Copy debug output to the current terminal line\n")
2904{
2905 vty->monitor = 0;
2906 return CMD_SUCCESS;
2907}
2908
paul789f78a2006-01-17 17:42:03 +00002909ALIAS (terminal_no_monitor,
2910 no_terminal_monitor_cmd,
2911 "no terminal monitor",
2912 NO_STR
2913 "Set terminal line parameters\n"
2914 "Copy debug output to the current terminal line\n")
2915
paul718e3742002-12-13 20:15:29 +00002916DEFUN (show_history,
2917 show_history_cmd,
2918 "show history",
2919 SHOW_STR
2920 "Display the session command history\n")
2921{
2922 int index;
2923
2924 for (index = vty->hindex + 1; index != vty->hindex;)
2925 {
2926 if (index == VTY_MAXHIST)
2927 {
2928 index = 0;
2929 continue;
2930 }
2931
2932 if (vty->hist[index] != NULL)
2933 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
2934
2935 index++;
2936 }
2937
2938 return CMD_SUCCESS;
2939}
2940
2941/* Display current configuration. */
ajs9fc7ebf2005-02-23 15:12:34 +00002942static int
paul718e3742002-12-13 20:15:29 +00002943vty_config_write (struct vty *vty)
2944{
2945 vty_out (vty, "line vty%s", VTY_NEWLINE);
2946
2947 if (vty_accesslist_name)
2948 vty_out (vty, " access-class %s%s",
2949 vty_accesslist_name, VTY_NEWLINE);
2950
2951 if (vty_ipv6_accesslist_name)
2952 vty_out (vty, " ipv6 access-class %s%s",
2953 vty_ipv6_accesslist_name, VTY_NEWLINE);
2954
2955 /* exec-timeout */
2956 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
2957 vty_out (vty, " exec-timeout %ld %ld%s",
2958 vty_timeout_val / 60,
2959 vty_timeout_val % 60, VTY_NEWLINE);
2960
2961 /* login */
2962 if (no_password_check)
2963 vty_out (vty, " no login%s", VTY_NEWLINE);
Paul Jakma62687ff2008-08-23 14:27:06 +01002964
2965 if (restricted_mode != restricted_mode_default)
2966 {
2967 if (restricted_mode_default)
2968 vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
2969 else
2970 vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
2971 }
2972
paul718e3742002-12-13 20:15:29 +00002973 vty_out (vty, "!%s", VTY_NEWLINE);
2974
2975 return CMD_SUCCESS;
2976}
2977
2978struct cmd_node vty_node =
2979{
2980 VTY_NODE,
2981 "%s(config-line)# ",
hassoe7168df2004-10-03 20:11:32 +00002982 1,
paul718e3742002-12-13 20:15:29 +00002983};
2984
2985/* Reset all VTY status. */
2986void
2987vty_reset ()
2988{
hasso8c328f12004-10-05 21:01:23 +00002989 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002990 struct vty *vty;
2991 struct thread *vty_serv_thread;
2992
paul55468c82005-03-14 20:19:01 +00002993 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002994 if ((vty = vector_slot (vtyvec, i)) != NULL)
2995 {
2996 buffer_reset (vty->obuf);
2997 vty->status = VTY_CLOSE;
2998 vty_close (vty);
2999 }
3000
paul55468c82005-03-14 20:19:01 +00003001 for (i = 0; i < vector_active (Vvty_serv_thread); i++)
paul718e3742002-12-13 20:15:29 +00003002 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
3003 {
3004 thread_cancel (vty_serv_thread);
3005 vector_slot (Vvty_serv_thread, i) = NULL;
3006 close (i);
3007 }
3008
3009 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3010
3011 if (vty_accesslist_name)
3012 {
3013 XFREE(MTYPE_VTY, vty_accesslist_name);
3014 vty_accesslist_name = NULL;
3015 }
3016
3017 if (vty_ipv6_accesslist_name)
3018 {
3019 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3020 vty_ipv6_accesslist_name = NULL;
3021 }
3022}
3023
ajs9fc7ebf2005-02-23 15:12:34 +00003024static void
3025vty_save_cwd (void)
paul718e3742002-12-13 20:15:29 +00003026{
paul79ad2792003-10-15 22:09:28 +00003027 char cwd[MAXPATHLEN];
paulccc92352003-10-22 02:49:38 +00003028 char *c;
paul718e3742002-12-13 20:15:29 +00003029
paulccc92352003-10-22 02:49:38 +00003030 c = getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003031
paulccc92352003-10-22 02:49:38 +00003032 if (!c)
paul79ad2792003-10-15 22:09:28 +00003033 {
3034 chdir (SYSCONFDIR);
paulccc92352003-10-22 02:49:38 +00003035 getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003036 }
paul718e3742002-12-13 20:15:29 +00003037
3038 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
3039 strcpy (vty_cwd, cwd);
3040}
3041
3042char *
3043vty_get_cwd ()
3044{
3045 return vty_cwd;
3046}
3047
3048int
3049vty_shell (struct vty *vty)
3050{
3051 return vty->type == VTY_SHELL ? 1 : 0;
3052}
3053
3054int
3055vty_shell_serv (struct vty *vty)
3056{
3057 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3058}
3059
3060void
3061vty_init_vtysh ()
3062{
3063 vtyvec = vector_init (VECTOR_MIN_SIZE);
3064}
3065
3066/* Install vty's own commands like `who' command. */
3067void
paulb21b19c2003-06-15 01:28:29 +00003068vty_init (struct thread_master *master_thread)
paul718e3742002-12-13 20:15:29 +00003069{
3070 /* For further configuration read, preserve current directory. */
3071 vty_save_cwd ();
3072
3073 vtyvec = vector_init (VECTOR_MIN_SIZE);
3074
Donald Sharpeeef0db2015-10-14 08:50:38 -04003075 vty_master = master_thread;
paulb21b19c2003-06-15 01:28:29 +00003076
David Lamparterba53a8f2015-05-05 11:04:46 +02003077 atexit (vty_stdio_reset);
3078
paul718e3742002-12-13 20:15:29 +00003079 /* Initilize server thread vector. */
3080 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
3081
3082 /* Install bgp top node. */
3083 install_node (&vty_node, vty_config_write);
3084
Paul Jakma62687ff2008-08-23 14:27:06 +01003085 install_element (RESTRICTED_NODE, &config_who_cmd);
3086 install_element (RESTRICTED_NODE, &show_history_cmd);
paul718e3742002-12-13 20:15:29 +00003087 install_element (VIEW_NODE, &config_who_cmd);
3088 install_element (VIEW_NODE, &show_history_cmd);
3089 install_element (ENABLE_NODE, &config_who_cmd);
3090 install_element (CONFIG_NODE, &line_vty_cmd);
3091 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
3092 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
3093 install_element (CONFIG_NODE, &show_history_cmd);
3094 install_element (ENABLE_NODE, &terminal_monitor_cmd);
3095 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
paul789f78a2006-01-17 17:42:03 +00003096 install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00003097 install_element (ENABLE_NODE, &show_history_cmd);
3098
3099 install_default (VTY_NODE);
3100 install_element (VTY_NODE, &exec_timeout_min_cmd);
3101 install_element (VTY_NODE, &exec_timeout_sec_cmd);
3102 install_element (VTY_NODE, &no_exec_timeout_cmd);
3103 install_element (VTY_NODE, &vty_access_class_cmd);
3104 install_element (VTY_NODE, &no_vty_access_class_cmd);
3105 install_element (VTY_NODE, &vty_login_cmd);
3106 install_element (VTY_NODE, &no_vty_login_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01003107 install_element (VTY_NODE, &vty_restricted_mode_cmd);
3108 install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
paul718e3742002-12-13 20:15:29 +00003109#ifdef HAVE_IPV6
3110 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
3111 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
3112#endif /* HAVE_IPV6 */
3113}
Chris Caputo228da422009-07-18 05:44:03 +00003114
3115void
3116vty_terminate (void)
3117{
3118 if (vty_cwd)
3119 XFREE (MTYPE_TMP, vty_cwd);
3120
3121 if (vtyvec && Vvty_serv_thread)
3122 {
3123 vty_reset ();
3124 vector_free (vtyvec);
3125 vector_free (Vvty_serv_thread);
3126 }
3127}