blob: f695b72229601f85b76c25b91930bc913d5d9375 [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:
paul718e3742002-12-13 20:15:29 +0000741 case BGP_IPV4_NODE:
742 case BGP_IPV4M_NODE:
743 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +0000744 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +0000745 case RMAP_NODE:
746 case OSPF_NODE:
747 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +0000748 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +0000749 case KEYCHAIN_NODE:
750 case KEYCHAIN_KEY_NODE:
751 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -0200752 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +0000753 case VTY_NODE:
754 vty_config_unlock (vty);
755 vty->node = ENABLE_NODE;
756 break;
757 default:
758 /* Unknown node, we have to ignore it. */
759 break;
760 }
761
762 vty_prompt (vty);
763 vty->cp = 0;
764}
765
766/* Delete a charcter at the current point. */
767static void
768vty_delete_char (struct vty *vty)
769{
770 int i;
771 int size;
772
paul718e3742002-12-13 20:15:29 +0000773 if (vty->length == 0)
774 {
775 vty_down_level (vty);
776 return;
777 }
778
779 if (vty->cp == vty->length)
780 return; /* completion need here? */
781
782 size = vty->length - vty->cp;
783
784 vty->length--;
785 memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
786 vty->buf[vty->length] = '\0';
Roy7f794f22008-08-13 17:27:38 +0100787
788 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
789 return;
paul718e3742002-12-13 20:15:29 +0000790
791 vty_write (vty, &vty->buf[vty->cp], size - 1);
792 vty_write (vty, &telnet_space_char, 1);
793
794 for (i = 0; i < size; i++)
795 vty_write (vty, &telnet_backward_char, 1);
796}
797
798/* Delete a character before the point. */
799static void
800vty_delete_backward_char (struct vty *vty)
801{
802 if (vty->cp == 0)
803 return;
804
805 vty_backward_char (vty);
806 vty_delete_char (vty);
807}
808
809/* Kill rest of line from current point. */
810static void
811vty_kill_line (struct vty *vty)
812{
813 int i;
814 int size;
815
816 size = vty->length - vty->cp;
817
818 if (size == 0)
819 return;
820
821 for (i = 0; i < size; i++)
822 vty_write (vty, &telnet_space_char, 1);
823 for (i = 0; i < size; i++)
824 vty_write (vty, &telnet_backward_char, 1);
825
826 memset (&vty->buf[vty->cp], 0, size);
827 vty->length = vty->cp;
828}
829
830/* Kill line from the beginning. */
831static void
832vty_kill_line_from_beginning (struct vty *vty)
833{
834 vty_beginning_of_line (vty);
835 vty_kill_line (vty);
836}
837
838/* Delete a word before the point. */
839static void
840vty_forward_kill_word (struct vty *vty)
841{
842 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
843 vty_delete_char (vty);
844 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
845 vty_delete_char (vty);
846}
847
848/* Delete a word before the point. */
849static void
850vty_backward_kill_word (struct vty *vty)
851{
852 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
853 vty_delete_backward_char (vty);
854 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
855 vty_delete_backward_char (vty);
856}
857
858/* Transpose chars before or at the point. */
859static void
860vty_transpose_chars (struct vty *vty)
861{
862 char c1, c2;
863
864 /* If length is short or point is near by the beginning of line then
865 return. */
866 if (vty->length < 2 || vty->cp < 1)
867 return;
868
869 /* In case of point is located at the end of the line. */
870 if (vty->cp == vty->length)
871 {
872 c1 = vty->buf[vty->cp - 1];
873 c2 = vty->buf[vty->cp - 2];
874
875 vty_backward_char (vty);
876 vty_backward_char (vty);
877 vty_self_insert_overwrite (vty, c1);
878 vty_self_insert_overwrite (vty, c2);
879 }
880 else
881 {
882 c1 = vty->buf[vty->cp];
883 c2 = vty->buf[vty->cp - 1];
884
885 vty_backward_char (vty);
886 vty_self_insert_overwrite (vty, c1);
887 vty_self_insert_overwrite (vty, c2);
888 }
889}
890
891/* Do completion at vty interface. */
892static void
893vty_complete_command (struct vty *vty)
894{
895 int i;
896 int ret;
897 char **matched = NULL;
898 vector vline;
899
900 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
901 return;
902
903 vline = cmd_make_strvec (vty->buf);
904 if (vline == NULL)
905 return;
906
907 /* In case of 'help \t'. */
908 if (isspace ((int) vty->buf[vty->length - 1]))
David Lampartera91a3ba2015-03-03 09:06:51 +0100909 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +0000910
Lou Berger67290032016-01-12 13:41:46 -0500911 matched = cmd_complete_command_lib (vline, vty, &ret, 1);
paul718e3742002-12-13 20:15:29 +0000912
913 cmd_free_strvec (vline);
914
915 vty_out (vty, "%s", VTY_NEWLINE);
916 switch (ret)
917 {
918 case CMD_ERR_AMBIGUOUS:
919 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
920 vty_prompt (vty);
921 vty_redraw_line (vty);
922 break;
923 case CMD_ERR_NO_MATCH:
924 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
925 vty_prompt (vty);
926 vty_redraw_line (vty);
927 break;
928 case CMD_COMPLETE_FULL_MATCH:
929 vty_prompt (vty);
930 vty_redraw_line (vty);
931 vty_backward_pure_word (vty);
932 vty_insert_word_overwrite (vty, matched[0]);
933 vty_self_insert (vty, ' ');
934 XFREE (MTYPE_TMP, matched[0]);
935 break;
936 case CMD_COMPLETE_MATCH:
937 vty_prompt (vty);
938 vty_redraw_line (vty);
939 vty_backward_pure_word (vty);
940 vty_insert_word_overwrite (vty, matched[0]);
941 XFREE (MTYPE_TMP, matched[0]);
942 vector_only_index_free (matched);
943 return;
944 break;
945 case CMD_COMPLETE_LIST_MATCH:
946 for (i = 0; matched[i] != NULL; i++)
947 {
948 if (i != 0 && ((i % 6) == 0))
949 vty_out (vty, "%s", VTY_NEWLINE);
950 vty_out (vty, "%-10s ", matched[i]);
951 XFREE (MTYPE_TMP, matched[i]);
952 }
953 vty_out (vty, "%s", VTY_NEWLINE);
954
955 vty_prompt (vty);
956 vty_redraw_line (vty);
957 break;
958 case CMD_ERR_NOTHING_TODO:
959 vty_prompt (vty);
960 vty_redraw_line (vty);
961 break;
962 default:
963 break;
964 }
965 if (matched)
966 vector_only_index_free (matched);
967}
968
ajs9fc7ebf2005-02-23 15:12:34 +0000969static void
paul718e3742002-12-13 20:15:29 +0000970vty_describe_fold (struct vty *vty, int cmd_width,
Christian Frankecd40b322013-09-30 12:27:51 +0000971 unsigned int desc_width, struct cmd_token *token)
paul718e3742002-12-13 20:15:29 +0000972{
hasso8c328f12004-10-05 21:01:23 +0000973 char *buf;
974 const char *cmd, *p;
paul718e3742002-12-13 20:15:29 +0000975 int pos;
976
Christian Frankecd40b322013-09-30 12:27:51 +0000977 cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd;
paul718e3742002-12-13 20:15:29 +0000978
979 if (desc_width <= 0)
980 {
Christian Frankecd40b322013-09-30 12:27:51 +0000981 vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000982 return;
983 }
984
Christian Frankecd40b322013-09-30 12:27:51 +0000985 buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1);
paul718e3742002-12-13 20:15:29 +0000986
Christian Frankecd40b322013-09-30 12:27:51 +0000987 for (p = token->desc; strlen (p) > desc_width; p += pos + 1)
paul718e3742002-12-13 20:15:29 +0000988 {
989 for (pos = desc_width; pos > 0; pos--)
990 if (*(p + pos) == ' ')
991 break;
992
993 if (pos == 0)
994 break;
995
996 strncpy (buf, p, pos);
997 buf[pos] = '\0';
998 vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
999
1000 cmd = "";
1001 }
1002
1003 vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1004
1005 XFREE (MTYPE_TMP, buf);
1006}
1007
1008/* Describe matched command function. */
1009static void
1010vty_describe_command (struct vty *vty)
1011{
1012 int ret;
1013 vector vline;
1014 vector describe;
hasso8c328f12004-10-05 21:01:23 +00001015 unsigned int i, width, desc_width;
Christian Frankecd40b322013-09-30 12:27:51 +00001016 struct cmd_token *token, *token_cr = NULL;
paul718e3742002-12-13 20:15:29 +00001017
1018 vline = cmd_make_strvec (vty->buf);
1019
1020 /* In case of '> ?'. */
1021 if (vline == NULL)
1022 {
1023 vline = vector_init (1);
David Lampartera91a3ba2015-03-03 09:06:51 +01001024 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +00001025 }
1026 else
1027 if (isspace ((int) vty->buf[vty->length - 1]))
David Lampartera91a3ba2015-03-03 09:06:51 +01001028 vector_set (vline, NULL);
paul718e3742002-12-13 20:15:29 +00001029
1030 describe = cmd_describe_command (vline, vty, &ret);
1031
1032 vty_out (vty, "%s", VTY_NEWLINE);
1033
1034 /* Ambiguous error. */
1035 switch (ret)
1036 {
1037 case CMD_ERR_AMBIGUOUS:
paul718e3742002-12-13 20:15:29 +00001038 vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001039 goto out;
paul718e3742002-12-13 20:15:29 +00001040 break;
1041 case CMD_ERR_NO_MATCH:
paul718e3742002-12-13 20:15:29 +00001042 vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001043 goto out;
paul718e3742002-12-13 20:15:29 +00001044 break;
1045 }
1046
1047 /* Get width of command string. */
1048 width = 0;
paul55468c82005-03-14 20:19:01 +00001049 for (i = 0; i < vector_active (describe); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00001050 if ((token = vector_slot (describe, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001051 {
hasso8c328f12004-10-05 21:01:23 +00001052 unsigned int len;
paul718e3742002-12-13 20:15:29 +00001053
Christian Frankecd40b322013-09-30 12:27:51 +00001054 if (token->cmd[0] == '\0')
paul718e3742002-12-13 20:15:29 +00001055 continue;
1056
Christian Frankecd40b322013-09-30 12:27:51 +00001057 len = strlen (token->cmd);
1058 if (token->cmd[0] == '.')
paul718e3742002-12-13 20:15:29 +00001059 len--;
1060
1061 if (width < len)
1062 width = len;
1063 }
1064
1065 /* Get width of description string. */
1066 desc_width = vty->width - (width + 6);
1067
1068 /* Print out description. */
paul55468c82005-03-14 20:19:01 +00001069 for (i = 0; i < vector_active (describe); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00001070 if ((token = vector_slot (describe, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001071 {
Christian Frankecd40b322013-09-30 12:27:51 +00001072 if (token->cmd[0] == '\0')
paul718e3742002-12-13 20:15:29 +00001073 continue;
1074
Christian Frankecd40b322013-09-30 12:27:51 +00001075 if (strcmp (token->cmd, command_cr) == 0)
paul718e3742002-12-13 20:15:29 +00001076 {
Christian Frankecd40b322013-09-30 12:27:51 +00001077 token_cr = token;
paul718e3742002-12-13 20:15:29 +00001078 continue;
1079 }
1080
Christian Frankecd40b322013-09-30 12:27:51 +00001081 if (!token->desc)
paul718e3742002-12-13 20:15:29 +00001082 vty_out (vty, " %-s%s",
Christian Frankecd40b322013-09-30 12:27:51 +00001083 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
paul718e3742002-12-13 20:15:29 +00001084 VTY_NEWLINE);
Christian Frankecd40b322013-09-30 12:27:51 +00001085 else if (desc_width >= strlen (token->desc))
paul718e3742002-12-13 20:15:29 +00001086 vty_out (vty, " %-*s %s%s", width,
Christian Frankecd40b322013-09-30 12:27:51 +00001087 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1088 token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001089 else
Christian Frankecd40b322013-09-30 12:27:51 +00001090 vty_describe_fold (vty, width, desc_width, token);
paul718e3742002-12-13 20:15:29 +00001091
1092#if 0
1093 vty_out (vty, " %-*s %s%s", width
1094 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1095 desc->str ? desc->str : "", VTY_NEWLINE);
1096#endif /* 0 */
1097 }
1098
Christian Frankecd40b322013-09-30 12:27:51 +00001099 if ((token = token_cr))
paul718e3742002-12-13 20:15:29 +00001100 {
Christian Frankecd40b322013-09-30 12:27:51 +00001101 if (!token->desc)
paul718e3742002-12-13 20:15:29 +00001102 vty_out (vty, " %-s%s",
Christian Frankecd40b322013-09-30 12:27:51 +00001103 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
paul718e3742002-12-13 20:15:29 +00001104 VTY_NEWLINE);
Christian Frankecd40b322013-09-30 12:27:51 +00001105 else if (desc_width >= strlen (token->desc))
paul718e3742002-12-13 20:15:29 +00001106 vty_out (vty, " %-*s %s%s", width,
Christian Frankecd40b322013-09-30 12:27:51 +00001107 token->cmd[0] == '.' ? token->cmd + 1 : token->cmd,
1108 token->desc, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00001109 else
Christian Frankecd40b322013-09-30 12:27:51 +00001110 vty_describe_fold (vty, width, desc_width, token);
paul718e3742002-12-13 20:15:29 +00001111 }
1112
Paul Jakma2fe8aba2006-05-12 23:22:01 +00001113out:
paul718e3742002-12-13 20:15:29 +00001114 cmd_free_strvec (vline);
Paul Jakmad16e0432006-05-15 10:56:46 +00001115 if (describe)
1116 vector_free (describe);
paul718e3742002-12-13 20:15:29 +00001117
1118 vty_prompt (vty);
1119 vty_redraw_line (vty);
1120}
1121
ajs9fc7ebf2005-02-23 15:12:34 +00001122static void
paul718e3742002-12-13 20:15:29 +00001123vty_clear_buf (struct vty *vty)
1124{
1125 memset (vty->buf, 0, vty->max);
1126}
1127
1128/* ^C stop current input and do not add command line to the history. */
1129static void
1130vty_stop_input (struct vty *vty)
1131{
1132 vty->cp = vty->length = 0;
1133 vty_clear_buf (vty);
1134 vty_out (vty, "%s", VTY_NEWLINE);
1135
1136 switch (vty->node)
1137 {
1138 case VIEW_NODE:
1139 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01001140 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00001141 /* Nothing to do. */
1142 break;
1143 case CONFIG_NODE:
1144 case INTERFACE_NODE:
1145 case ZEBRA_NODE:
1146 case RIP_NODE:
1147 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01001148 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00001149 case BGP_NODE:
1150 case RMAP_NODE:
1151 case OSPF_NODE:
1152 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00001153 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00001154 case KEYCHAIN_NODE:
1155 case KEYCHAIN_KEY_NODE:
1156 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02001157 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00001158 case VTY_NODE:
1159 vty_config_unlock (vty);
1160 vty->node = ENABLE_NODE;
1161 break;
1162 default:
1163 /* Unknown node, we have to ignore it. */
1164 break;
1165 }
1166 vty_prompt (vty);
1167
1168 /* Set history pointer to the latest one. */
1169 vty->hp = vty->hindex;
1170}
1171
1172/* Add current command line to the history buffer. */
1173static void
1174vty_hist_add (struct vty *vty)
1175{
1176 int index;
1177
1178 if (vty->length == 0)
1179 return;
1180
1181 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
1182
1183 /* Ignore the same string as previous one. */
1184 if (vty->hist[index])
1185 if (strcmp (vty->buf, vty->hist[index]) == 0)
1186 {
1187 vty->hp = vty->hindex;
1188 return;
1189 }
1190
1191 /* Insert history entry. */
1192 if (vty->hist[vty->hindex])
1193 XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
1194 vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
1195
1196 /* History index rotation. */
1197 vty->hindex++;
1198 if (vty->hindex == VTY_MAXHIST)
1199 vty->hindex = 0;
1200
1201 vty->hp = vty->hindex;
1202}
1203
1204/* #define TELNET_OPTION_DEBUG */
1205
1206/* Get telnet window size. */
1207static int
1208vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
1209{
1210#ifdef TELNET_OPTION_DEBUG
1211 int i;
1212
1213 for (i = 0; i < nbytes; i++)
1214 {
1215 switch (buf[i])
1216 {
1217 case IAC:
1218 vty_out (vty, "IAC ");
1219 break;
1220 case WILL:
1221 vty_out (vty, "WILL ");
1222 break;
1223 case WONT:
1224 vty_out (vty, "WONT ");
1225 break;
1226 case DO:
1227 vty_out (vty, "DO ");
1228 break;
1229 case DONT:
1230 vty_out (vty, "DONT ");
1231 break;
1232 case SB:
1233 vty_out (vty, "SB ");
1234 break;
1235 case SE:
1236 vty_out (vty, "SE ");
1237 break;
1238 case TELOPT_ECHO:
1239 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
1240 break;
1241 case TELOPT_SGA:
1242 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
1243 break;
1244 case TELOPT_NAWS:
1245 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
1246 break;
1247 default:
1248 vty_out (vty, "%x ", buf[i]);
1249 break;
1250 }
1251 }
1252 vty_out (vty, "%s", VTY_NEWLINE);
1253
1254#endif /* TELNET_OPTION_DEBUG */
1255
1256 switch (buf[0])
1257 {
1258 case SB:
ajs9fc7ebf2005-02-23 15:12:34 +00001259 vty->sb_len = 0;
paul718e3742002-12-13 20:15:29 +00001260 vty->iac_sb_in_progress = 1;
1261 return 0;
1262 break;
1263 case SE:
1264 {
paul718e3742002-12-13 20:15:29 +00001265 if (!vty->iac_sb_in_progress)
1266 return 0;
1267
ajs9fc7ebf2005-02-23 15:12:34 +00001268 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
paul718e3742002-12-13 20:15:29 +00001269 {
1270 vty->iac_sb_in_progress = 0;
1271 return 0;
1272 }
ajs9fc7ebf2005-02-23 15:12:34 +00001273 switch (vty->sb_buf[0])
paul718e3742002-12-13 20:15:29 +00001274 {
1275 case TELOPT_NAWS:
ajs9fc7ebf2005-02-23 15:12:34 +00001276 if (vty->sb_len != TELNET_NAWS_SB_LEN)
1277 zlog_warn("RFC 1073 violation detected: telnet NAWS option "
1278 "should send %d characters, but we received %lu",
1279 TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
1280 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
1281 zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
1282 "too small to handle the telnet NAWS option",
1283 (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
1284 else
1285 {
1286 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
1287 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
1288#ifdef TELNET_OPTION_DEBUG
1289 vty_out(vty, "TELNET NAWS window size negotiation completed: "
1290 "width %d, height %d%s",
1291 vty->width, vty->height, VTY_NEWLINE);
1292#endif
1293 }
paul718e3742002-12-13 20:15:29 +00001294 break;
1295 }
1296 vty->iac_sb_in_progress = 0;
1297 return 0;
1298 break;
1299 }
1300 default:
1301 break;
1302 }
1303 return 1;
1304}
1305
1306/* Execute current command line. */
1307static int
1308vty_execute (struct vty *vty)
1309{
1310 int ret;
1311
1312 ret = CMD_SUCCESS;
1313
1314 switch (vty->node)
1315 {
1316 case AUTH_NODE:
1317 case AUTH_ENABLE_NODE:
1318 vty_auth (vty, vty->buf);
1319 break;
1320 default:
1321 ret = vty_command (vty, vty->buf);
1322 if (vty->type == VTY_TERM)
1323 vty_hist_add (vty);
1324 break;
1325 }
1326
1327 /* Clear command line buffer. */
1328 vty->cp = vty->length = 0;
1329 vty_clear_buf (vty);
1330
ajs5a646652004-11-05 01:25:55 +00001331 if (vty->status != VTY_CLOSE )
paul718e3742002-12-13 20:15:29 +00001332 vty_prompt (vty);
1333
1334 return ret;
1335}
1336
1337#define CONTROL(X) ((X) - '@')
1338#define VTY_NORMAL 0
1339#define VTY_PRE_ESCAPE 1
1340#define VTY_ESCAPE 2
1341
1342/* Escape character command map. */
1343static void
1344vty_escape_map (unsigned char c, struct vty *vty)
1345{
1346 switch (c)
1347 {
1348 case ('A'):
1349 vty_previous_line (vty);
1350 break;
1351 case ('B'):
1352 vty_next_line (vty);
1353 break;
1354 case ('C'):
1355 vty_forward_char (vty);
1356 break;
1357 case ('D'):
1358 vty_backward_char (vty);
1359 break;
1360 default:
1361 break;
1362 }
1363
1364 /* Go back to normal mode. */
1365 vty->escape = VTY_NORMAL;
1366}
1367
1368/* Quit print out to the buffer. */
1369static void
1370vty_buffer_reset (struct vty *vty)
1371{
1372 buffer_reset (vty->obuf);
1373 vty_prompt (vty);
1374 vty_redraw_line (vty);
1375}
1376
1377/* Read data via vty socket. */
1378static int
1379vty_read (struct thread *thread)
1380{
1381 int i;
paul718e3742002-12-13 20:15:29 +00001382 int nbytes;
1383 unsigned char buf[VTY_READ_BUFSIZ];
1384
1385 int vty_sock = THREAD_FD (thread);
1386 struct vty *vty = THREAD_ARG (thread);
1387 vty->t_read = NULL;
1388
1389 /* Read raw data from socket */
ajs9fc7ebf2005-02-23 15:12:34 +00001390 if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
1391 {
1392 if (nbytes < 0)
1393 {
1394 if (ERRNO_IO_RETRY(errno))
1395 {
1396 vty_event (VTY_READ, vty_sock, vty);
1397 return 0;
1398 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001399 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001400 zlog_warn("%s: read error on vty client fd %d, closing: %s",
1401 __func__, vty->fd, safe_strerror(errno));
David Lamparter90d31352015-05-14 14:24:06 +02001402 buffer_reset(vty->obuf);
ajs9fc7ebf2005-02-23 15:12:34 +00001403 }
ajs9fc7ebf2005-02-23 15:12:34 +00001404 vty->status = VTY_CLOSE;
1405 }
paul718e3742002-12-13 20:15:29 +00001406
1407 for (i = 0; i < nbytes; i++)
1408 {
1409 if (buf[i] == IAC)
1410 {
1411 if (!vty->iac)
1412 {
1413 vty->iac = 1;
1414 continue;
1415 }
1416 else
1417 {
1418 vty->iac = 0;
1419 }
1420 }
1421
1422 if (vty->iac_sb_in_progress && !vty->iac)
1423 {
ajs9fc7ebf2005-02-23 15:12:34 +00001424 if (vty->sb_len < sizeof(vty->sb_buf))
1425 vty->sb_buf[vty->sb_len] = buf[i];
1426 vty->sb_len++;
paul718e3742002-12-13 20:15:29 +00001427 continue;
1428 }
1429
1430 if (vty->iac)
1431 {
1432 /* In case of telnet command */
paul5b8c1b02003-10-15 23:08:55 +00001433 int ret = 0;
paule9372532003-10-26 21:36:07 +00001434 ret = vty_telnet_option (vty, buf + i, nbytes - i);
paul718e3742002-12-13 20:15:29 +00001435 vty->iac = 0;
1436 i += ret;
1437 continue;
1438 }
paul5b8c1b02003-10-15 23:08:55 +00001439
paul718e3742002-12-13 20:15:29 +00001440
1441 if (vty->status == VTY_MORE)
1442 {
1443 switch (buf[i])
1444 {
1445 case CONTROL('C'):
1446 case 'q':
1447 case 'Q':
paul718e3742002-12-13 20:15:29 +00001448 vty_buffer_reset (vty);
1449 break;
1450#if 0 /* More line does not work for "show ip bgp". */
1451 case '\n':
1452 case '\r':
1453 vty->status = VTY_MORELINE;
1454 break;
1455#endif
1456 default:
paul718e3742002-12-13 20:15:29 +00001457 break;
1458 }
1459 continue;
1460 }
1461
1462 /* Escape character. */
1463 if (vty->escape == VTY_ESCAPE)
1464 {
1465 vty_escape_map (buf[i], vty);
1466 continue;
1467 }
1468
1469 /* Pre-escape status. */
1470 if (vty->escape == VTY_PRE_ESCAPE)
1471 {
1472 switch (buf[i])
1473 {
1474 case '[':
1475 vty->escape = VTY_ESCAPE;
1476 break;
1477 case 'b':
1478 vty_backward_word (vty);
1479 vty->escape = VTY_NORMAL;
1480 break;
1481 case 'f':
1482 vty_forward_word (vty);
1483 vty->escape = VTY_NORMAL;
1484 break;
1485 case 'd':
1486 vty_forward_kill_word (vty);
1487 vty->escape = VTY_NORMAL;
1488 break;
1489 case CONTROL('H'):
1490 case 0x7f:
1491 vty_backward_kill_word (vty);
1492 vty->escape = VTY_NORMAL;
1493 break;
1494 default:
1495 vty->escape = VTY_NORMAL;
1496 break;
1497 }
1498 continue;
1499 }
1500
1501 switch (buf[i])
1502 {
1503 case CONTROL('A'):
1504 vty_beginning_of_line (vty);
1505 break;
1506 case CONTROL('B'):
1507 vty_backward_char (vty);
1508 break;
1509 case CONTROL('C'):
1510 vty_stop_input (vty);
1511 break;
1512 case CONTROL('D'):
1513 vty_delete_char (vty);
1514 break;
1515 case CONTROL('E'):
1516 vty_end_of_line (vty);
1517 break;
1518 case CONTROL('F'):
1519 vty_forward_char (vty);
1520 break;
1521 case CONTROL('H'):
1522 case 0x7f:
1523 vty_delete_backward_char (vty);
1524 break;
1525 case CONTROL('K'):
1526 vty_kill_line (vty);
1527 break;
1528 case CONTROL('N'):
1529 vty_next_line (vty);
1530 break;
1531 case CONTROL('P'):
1532 vty_previous_line (vty);
1533 break;
1534 case CONTROL('T'):
1535 vty_transpose_chars (vty);
1536 break;
1537 case CONTROL('U'):
1538 vty_kill_line_from_beginning (vty);
1539 break;
1540 case CONTROL('W'):
1541 vty_backward_kill_word (vty);
1542 break;
1543 case CONTROL('Z'):
1544 vty_end_config (vty);
1545 break;
1546 case '\n':
1547 case '\r':
1548 vty_out (vty, "%s", VTY_NEWLINE);
1549 vty_execute (vty);
1550 break;
1551 case '\t':
1552 vty_complete_command (vty);
1553 break;
1554 case '?':
1555 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1556 vty_self_insert (vty, buf[i]);
1557 else
1558 vty_describe_command (vty);
1559 break;
1560 case '\033':
1561 if (i + 1 < nbytes && buf[i + 1] == '[')
1562 {
1563 vty->escape = VTY_ESCAPE;
1564 i++;
1565 }
1566 else
1567 vty->escape = VTY_PRE_ESCAPE;
1568 break;
1569 default:
1570 if (buf[i] > 31 && buf[i] < 127)
1571 vty_self_insert (vty, buf[i]);
1572 break;
1573 }
1574 }
1575
1576 /* Check status. */
1577 if (vty->status == VTY_CLOSE)
1578 vty_close (vty);
1579 else
1580 {
David Lamparter4715a532013-05-30 16:31:49 +02001581 vty_event (VTY_WRITE, vty->wfd, vty);
paul718e3742002-12-13 20:15:29 +00001582 vty_event (VTY_READ, vty_sock, vty);
1583 }
1584 return 0;
1585}
1586
1587/* Flush buffer to the vty. */
1588static int
1589vty_flush (struct thread *thread)
1590{
1591 int erase;
ajs9fc7ebf2005-02-23 15:12:34 +00001592 buffer_status_t flushrc;
paul718e3742002-12-13 20:15:29 +00001593 int vty_sock = THREAD_FD (thread);
1594 struct vty *vty = THREAD_ARG (thread);
ajs9fc7ebf2005-02-23 15:12:34 +00001595
paul718e3742002-12-13 20:15:29 +00001596 vty->t_write = NULL;
1597
1598 /* Tempolary disable read thread. */
ajs9fc7ebf2005-02-23 15:12:34 +00001599 if ((vty->lines == 0) && vty->t_read)
1600 {
1601 thread_cancel (vty->t_read);
1602 vty->t_read = NULL;
1603 }
paul718e3742002-12-13 20:15:29 +00001604
1605 /* Function execution continue. */
ajs9fc7ebf2005-02-23 15:12:34 +00001606 erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
paul718e3742002-12-13 20:15:29 +00001607
ajs9fc7ebf2005-02-23 15:12:34 +00001608 /* N.B. if width is 0, that means we don't know the window size. */
Lou Bergerc7f7e492016-01-12 13:41:49 -05001609 if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
David Lamparter4715a532013-05-30 16:31:49 +02001610 flushrc = buffer_flush_available(vty->obuf, vty_sock);
ajs9fc7ebf2005-02-23 15:12:34 +00001611 else if (vty->status == VTY_MORELINE)
David Lamparter4715a532013-05-30 16:31:49 +02001612 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001613 1, erase, 0);
1614 else
David Lamparter4715a532013-05-30 16:31:49 +02001615 flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
ajs9fc7ebf2005-02-23 15:12:34 +00001616 vty->lines >= 0 ? vty->lines :
1617 vty->height,
1618 erase, 0);
1619 switch (flushrc)
1620 {
1621 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00001622 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00001623 zlog_warn("buffer_flush failed on vty client fd %d, closing",
1624 vty->fd);
1625 buffer_reset(vty->obuf);
1626 vty_close(vty);
1627 return 0;
1628 case BUFFER_EMPTY:
1629 if (vty->status == VTY_CLOSE)
1630 vty_close (vty);
paul718e3742002-12-13 20:15:29 +00001631 else
1632 {
ajs9fc7ebf2005-02-23 15:12:34 +00001633 vty->status = VTY_NORMAL;
paul718e3742002-12-13 20:15:29 +00001634 if (vty->lines == 0)
ajs9fc7ebf2005-02-23 15:12:34 +00001635 vty_event (VTY_READ, vty_sock, vty);
paul718e3742002-12-13 20:15:29 +00001636 }
ajs9fc7ebf2005-02-23 15:12:34 +00001637 break;
1638 case BUFFER_PENDING:
1639 /* There is more data waiting to be written. */
1640 vty->status = VTY_MORE;
1641 if (vty->lines == 0)
1642 vty_event (VTY_WRITE, vty_sock, vty);
1643 break;
1644 }
paul718e3742002-12-13 20:15:29 +00001645
1646 return 0;
1647}
1648
David Lamparterba5dc5e2013-05-30 16:33:45 +02001649/* allocate and initialise vty */
1650static struct vty *
1651vty_new_init (int vty_sock)
1652{
1653 struct vty *vty;
1654
1655 vty = vty_new ();
1656 vty->fd = vty_sock;
1657 vty->wfd = vty_sock;
1658 vty->type = VTY_TERM;
1659 vty->node = AUTH_NODE;
1660 vty->fail = 0;
1661 vty->cp = 0;
1662 vty_clear_buf (vty);
1663 vty->length = 0;
1664 memset (vty->hist, 0, sizeof (vty->hist));
1665 vty->hp = 0;
1666 vty->hindex = 0;
1667 vector_set_index (vtyvec, vty_sock, vty);
1668 vty->status = VTY_NORMAL;
1669 vty->lines = -1;
1670 vty->iac = 0;
1671 vty->iac_sb_in_progress = 0;
1672 vty->sb_len = 0;
1673
1674 return vty;
1675}
1676
paul718e3742002-12-13 20:15:29 +00001677/* Create new vty structure. */
ajs9fc7ebf2005-02-23 15:12:34 +00001678static struct vty *
paul718e3742002-12-13 20:15:29 +00001679vty_create (int vty_sock, union sockunion *su)
1680{
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001681 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001682 struct vty *vty;
1683
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001684 sockunion2str(su, buf, SU_ADDRSTRLEN);
1685
paul718e3742002-12-13 20:15:29 +00001686 /* Allocate new vty structure and set up default values. */
David Lamparterba5dc5e2013-05-30 16:33:45 +02001687 vty = vty_new_init (vty_sock);
1688
1689 /* configurable parameters not part of basic init */
1690 vty->v_timeout = vty_timeout_val;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001691 strcpy (vty->address, buf);
paul718e3742002-12-13 20:15:29 +00001692 if (no_password_check)
1693 {
Paul Jakma62687ff2008-08-23 14:27:06 +01001694 if (restricted_mode)
1695 vty->node = RESTRICTED_NODE;
1696 else if (host.advanced)
paul718e3742002-12-13 20:15:29 +00001697 vty->node = ENABLE_NODE;
1698 else
1699 vty->node = VIEW_NODE;
1700 }
paul718e3742002-12-13 20:15:29 +00001701 if (host.lines >= 0)
1702 vty->lines = host.lines;
paul718e3742002-12-13 20:15:29 +00001703
1704 if (! no_password_check)
1705 {
1706 /* Vty is not available if password isn't set. */
1707 if (host.password == NULL && host.password_encrypt == NULL)
1708 {
1709 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1710 vty->status = VTY_CLOSE;
1711 vty_close (vty);
1712 return NULL;
1713 }
1714 }
1715
1716 /* Say hello to the world. */
1717 vty_hello (vty);
1718 if (! no_password_check)
1719 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1720
1721 /* Setting up terminal. */
1722 vty_will_echo (vty);
1723 vty_will_suppress_go_ahead (vty);
1724
1725 vty_dont_linemode (vty);
1726 vty_do_window_size (vty);
1727 /* vty_dont_lflow_ahead (vty); */
1728
1729 vty_prompt (vty);
1730
1731 /* Add read/write thread. */
1732 vty_event (VTY_WRITE, vty_sock, vty);
1733 vty_event (VTY_READ, vty_sock, vty);
1734
1735 return vty;
1736}
1737
David Lamparterba5dc5e2013-05-30 16:33:45 +02001738/* create vty for stdio */
David Lamparterba53a8f2015-05-05 11:04:46 +02001739static struct termios stdio_orig_termios;
1740static struct vty *stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001741static void (*stdio_vty_atclose)(void);
David Lamparterba53a8f2015-05-05 11:04:46 +02001742
1743static void
1744vty_stdio_reset (void)
1745{
1746 if (stdio_vty)
1747 {
1748 tcsetattr (0, TCSANOW, &stdio_orig_termios);
1749 stdio_vty = NULL;
David Lamparter464ccf32015-05-12 21:56:18 +02001750
1751 if (stdio_vty_atclose)
1752 stdio_vty_atclose ();
1753 stdio_vty_atclose = NULL;
David Lamparterba53a8f2015-05-05 11:04:46 +02001754 }
1755}
1756
David Lamparterba5dc5e2013-05-30 16:33:45 +02001757struct vty *
David Lamparter464ccf32015-05-12 21:56:18 +02001758vty_stdio (void (*atclose)())
David Lamparterba5dc5e2013-05-30 16:33:45 +02001759{
1760 struct vty *vty;
David Lamparterba53a8f2015-05-05 11:04:46 +02001761 struct termios termios;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001762
David Lamparterba53a8f2015-05-05 11:04:46 +02001763 /* refuse creating two vtys on stdio */
1764 if (stdio_vty)
1765 return NULL;
1766
1767 vty = stdio_vty = vty_new_init (0);
David Lamparter464ccf32015-05-12 21:56:18 +02001768 stdio_vty_atclose = atclose;
David Lamparterba5dc5e2013-05-30 16:33:45 +02001769 vty->wfd = 1;
1770
1771 /* always have stdio vty in a known _unchangeable_ state, don't want config
1772 * to have any effect here to make sure scripting this works as intended */
1773 vty->node = ENABLE_NODE;
1774 vty->v_timeout = 0;
1775 strcpy (vty->address, "console");
1776
David Lamparterba53a8f2015-05-05 11:04:46 +02001777 if (!tcgetattr (0, &stdio_orig_termios))
1778 {
1779 termios = stdio_orig_termios;
1780 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
1781 | INLCR | IGNCR | ICRNL | IXON);
1782 termios.c_oflag &= ~OPOST;
1783 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
1784 termios.c_cflag &= ~(CSIZE | PARENB);
1785 termios.c_cflag |= CS8;
1786 tcsetattr (0, TCSANOW, &termios);
1787 }
1788
David Lamparterba5dc5e2013-05-30 16:33:45 +02001789 vty_prompt (vty);
1790
1791 /* Add read/write thread. */
1792 vty_event (VTY_WRITE, 1, vty);
1793 vty_event (VTY_READ, 0, vty);
1794
1795 return vty;
1796}
1797
paul718e3742002-12-13 20:15:29 +00001798/* Accept connection from the network. */
1799static int
1800vty_accept (struct thread *thread)
1801{
1802 int vty_sock;
paul718e3742002-12-13 20:15:29 +00001803 union sockunion su;
1804 int ret;
1805 unsigned int on;
1806 int accept_sock;
Timo Teräsc1c69e42015-05-22 13:40:57 +03001807 struct prefix p;
paul718e3742002-12-13 20:15:29 +00001808 struct access_list *acl = NULL;
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001809 char buf[SU_ADDRSTRLEN];
paul718e3742002-12-13 20:15:29 +00001810
1811 accept_sock = THREAD_FD (thread);
1812
1813 /* We continue hearing vty socket. */
1814 vty_event (VTY_SERV, accept_sock, NULL);
1815
1816 memset (&su, 0, sizeof (union sockunion));
1817
1818 /* We can handle IPv4 or IPv6 socket. */
1819 vty_sock = sockunion_accept (accept_sock, &su);
1820 if (vty_sock < 0)
1821 {
ajs6099b3b2004-11-20 02:06:59 +00001822 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001823 return -1;
1824 }
ajs9fc7ebf2005-02-23 15:12:34 +00001825 set_nonblocking(vty_sock);
paul718e3742002-12-13 20:15:29 +00001826
Timo Teräsc1c69e42015-05-22 13:40:57 +03001827 sockunion2hostprefix (&su, &p);
paul718e3742002-12-13 20:15:29 +00001828
1829 /* VTY's accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001830 if (p.family == AF_INET && vty_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001831 {
1832 if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001833 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001834 {
paul718e3742002-12-13 20:15:29 +00001835 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001836 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001837 close (vty_sock);
1838
1839 /* continue accepting connections */
1840 vty_event (VTY_SERV, accept_sock, NULL);
1841
paul718e3742002-12-13 20:15:29 +00001842 return 0;
1843 }
1844 }
1845
1846#ifdef HAVE_IPV6
1847 /* VTY's ipv6 accesslist apply. */
Timo Teräsc1c69e42015-05-22 13:40:57 +03001848 if (p.family == AF_INET6 && vty_ipv6_accesslist_name)
paul718e3742002-12-13 20:15:29 +00001849 {
1850 if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
Timo Teräsc1c69e42015-05-22 13:40:57 +03001851 (access_list_apply (acl, &p) == FILTER_DENY))
paul718e3742002-12-13 20:15:29 +00001852 {
paul718e3742002-12-13 20:15:29 +00001853 zlog (NULL, LOG_INFO, "Vty connection refused from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001854 sockunion2str (&su, buf, SU_ADDRSTRLEN));
paul718e3742002-12-13 20:15:29 +00001855 close (vty_sock);
1856
1857 /* continue accepting connections */
1858 vty_event (VTY_SERV, accept_sock, NULL);
1859
paul718e3742002-12-13 20:15:29 +00001860 return 0;
1861 }
1862 }
1863#endif /* HAVE_IPV6 */
1864
paul718e3742002-12-13 20:15:29 +00001865 on = 1;
1866 ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
1867 (char *) &on, sizeof (on));
1868 if (ret < 0)
1869 zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
ajs6099b3b2004-11-20 02:06:59 +00001870 safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00001871
heasley78e6cd92009-12-07 16:41:14 +03001872 zlog (NULL, LOG_INFO, "Vty connection from %s",
Jorge Boncompte [DTI2]d2276172012-04-10 16:57:23 +02001873 sockunion2str (&su, buf, SU_ADDRSTRLEN));
heasley78e6cd92009-12-07 16:41:14 +03001874
Stephen Hemminger9206f9e2011-12-18 19:43:40 +04001875 vty_create (vty_sock, &su);
paul718e3742002-12-13 20:15:29 +00001876
1877 return 0;
1878}
1879
David Lamparter6d6df302014-06-28 21:12:37 +02001880#ifdef HAVE_IPV6
ajs9fc7ebf2005-02-23 15:12:34 +00001881static void
paul718e3742002-12-13 20:15:29 +00001882vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
1883{
1884 int ret;
1885 struct addrinfo req;
1886 struct addrinfo *ainfo;
1887 struct addrinfo *ainfo_save;
1888 int sock;
1889 char port_str[BUFSIZ];
1890
1891 memset (&req, 0, sizeof (struct addrinfo));
1892 req.ai_flags = AI_PASSIVE;
1893 req.ai_family = AF_UNSPEC;
1894 req.ai_socktype = SOCK_STREAM;
1895 sprintf (port_str, "%d", port);
1896 port_str[sizeof (port_str) - 1] = '\0';
1897
1898 ret = getaddrinfo (hostname, port_str, &req, &ainfo);
1899
1900 if (ret != 0)
1901 {
1902 fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
1903 exit (1);
1904 }
1905
1906 ainfo_save = ainfo;
1907
1908 do
1909 {
1910 if (ainfo->ai_family != AF_INET
1911#ifdef HAVE_IPV6
1912 && ainfo->ai_family != AF_INET6
1913#endif /* HAVE_IPV6 */
1914 )
1915 continue;
1916
1917 sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
1918 if (sock < 0)
1919 continue;
1920
David Lamparterca051262009-10-04 16:21:49 +02001921 sockopt_v6only (ainfo->ai_family, sock);
paul718e3742002-12-13 20:15:29 +00001922 sockopt_reuseaddr (sock);
1923 sockopt_reuseport (sock);
1924
1925 ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
1926 if (ret < 0)
1927 {
1928 close (sock); /* Avoid sd leak. */
1929 continue;
1930 }
1931
1932 ret = listen (sock, 3);
1933 if (ret < 0)
1934 {
1935 close (sock); /* Avoid sd leak. */
1936 continue;
1937 }
1938
1939 vty_event (VTY_SERV, sock, NULL);
1940 }
1941 while ((ainfo = ainfo->ai_next) != NULL);
1942
1943 freeaddrinfo (ainfo_save);
1944}
David Lamparter6d6df302014-06-28 21:12:37 +02001945#else /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00001946
1947/* Make vty server socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00001948static void
paul29db05b2003-05-08 20:10:22 +00001949vty_serv_sock_family (const char* addr, unsigned short port, int family)
paul718e3742002-12-13 20:15:29 +00001950{
1951 int ret;
1952 union sockunion su;
1953 int accept_sock;
paul29db05b2003-05-08 20:10:22 +00001954 void* naddr=NULL;
paul718e3742002-12-13 20:15:29 +00001955
1956 memset (&su, 0, sizeof (union sockunion));
1957 su.sa.sa_family = family;
paul29db05b2003-05-08 20:10:22 +00001958 if(addr)
1959 switch(family)
1960 {
1961 case AF_INET:
1962 naddr=&su.sin.sin_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00001963 break;
paul29db05b2003-05-08 20:10:22 +00001964#ifdef HAVE_IPV6
1965 case AF_INET6:
1966 naddr=&su.sin6.sin6_addr;
Remi Gacognea11e0122013-09-08 13:48:34 +00001967 break;
paul29db05b2003-05-08 20:10:22 +00001968#endif
1969 }
1970
1971 if(naddr)
1972 switch(inet_pton(family,addr,naddr))
1973 {
1974 case -1:
1975 zlog_err("bad address %s",addr);
1976 naddr=NULL;
1977 break;
1978 case 0:
ajs6099b3b2004-11-20 02:06:59 +00001979 zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
paul29db05b2003-05-08 20:10:22 +00001980 naddr=NULL;
1981 }
paul718e3742002-12-13 20:15:29 +00001982
1983 /* Make new socket. */
1984 accept_sock = sockunion_stream_socket (&su);
1985 if (accept_sock < 0)
1986 return;
1987
1988 /* This is server, so reuse address. */
1989 sockopt_reuseaddr (accept_sock);
1990 sockopt_reuseport (accept_sock);
1991
1992 /* Bind socket to universal address and given port. */
paul29db05b2003-05-08 20:10:22 +00001993 ret = sockunion_bind (accept_sock, &su, port, naddr);
paul718e3742002-12-13 20:15:29 +00001994 if (ret < 0)
1995 {
paul29db05b2003-05-08 20:10:22 +00001996 zlog_warn("can't bind socket");
paul718e3742002-12-13 20:15:29 +00001997 close (accept_sock); /* Avoid sd leak. */
1998 return;
1999 }
2000
2001 /* Listen socket under queue 3. */
2002 ret = listen (accept_sock, 3);
2003 if (ret < 0)
2004 {
2005 zlog (NULL, LOG_WARNING, "can't listen socket");
2006 close (accept_sock); /* Avoid sd leak. */
2007 return;
2008 }
2009
2010 /* Add vty server event. */
2011 vty_event (VTY_SERV, accept_sock, NULL);
2012}
David Lamparter6d6df302014-06-28 21:12:37 +02002013#endif /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00002014
2015#ifdef VTYSH
2016/* For sockaddr_un. */
2017#include <sys/un.h>
2018
2019/* VTY shell UNIX domain socket. */
ajs9fc7ebf2005-02-23 15:12:34 +00002020static void
hasso6ad96ea2004-10-07 19:33:46 +00002021vty_serv_un (const char *path)
paul718e3742002-12-13 20:15:29 +00002022{
2023 int ret;
paul75e15fe2004-10-31 02:13:09 +00002024 int sock, len;
paul718e3742002-12-13 20:15:29 +00002025 struct sockaddr_un serv;
2026 mode_t old_mask;
pauledd7c242003-06-04 13:59:38 +00002027 struct zprivs_ids_t ids;
2028
paul718e3742002-12-13 20:15:29 +00002029 /* First of all, unlink existing socket */
2030 unlink (path);
2031
2032 /* Set umask */
paul1921e6f2003-05-23 08:12:36 +00002033 old_mask = umask (0007);
paul718e3742002-12-13 20:15:29 +00002034
2035 /* Make UNIX domain socket. */
2036 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2037 if (sock < 0)
2038 {
ajs6a52d0d2005-01-30 18:49:28 +00002039 zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002040 return;
2041 }
2042
2043 /* Make server socket. */
2044 memset (&serv, 0, sizeof (struct sockaddr_un));
2045 serv.sun_family = AF_UNIX;
2046 strncpy (serv.sun_path, path, strlen (path));
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002047#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
paul718e3742002-12-13 20:15:29 +00002048 len = serv.sun_len = SUN_LEN(&serv);
2049#else
2050 len = sizeof (serv.sun_family) + strlen (serv.sun_path);
Paul Jakma6f0e3f62007-05-10 02:38:51 +00002051#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
paul718e3742002-12-13 20:15:29 +00002052
2053 ret = bind (sock, (struct sockaddr *) &serv, len);
2054 if (ret < 0)
2055 {
ajs6a52d0d2005-01-30 18:49:28 +00002056 zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002057 close (sock); /* Avoid sd leak. */
2058 return;
2059 }
2060
2061 ret = listen (sock, 5);
2062 if (ret < 0)
2063 {
ajs6a52d0d2005-01-30 18:49:28 +00002064 zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +00002065 close (sock); /* Avoid sd leak. */
2066 return;
2067 }
2068
2069 umask (old_mask);
2070
pauledd7c242003-06-04 13:59:38 +00002071 zprivs_get_ids(&ids);
2072
2073 if (ids.gid_vty > 0)
2074 {
2075 /* set group of socket */
2076 if ( chown (path, -1, ids.gid_vty) )
2077 {
2078 zlog_err ("vty_serv_un: could chown socket, %s",
ajs6099b3b2004-11-20 02:06:59 +00002079 safe_strerror (errno) );
pauledd7c242003-06-04 13:59:38 +00002080 }
2081 }
2082
paul718e3742002-12-13 20:15:29 +00002083 vty_event (VTYSH_SERV, sock, NULL);
2084}
2085
2086/* #define VTYSH_DEBUG 1 */
2087
2088static int
2089vtysh_accept (struct thread *thread)
2090{
2091 int accept_sock;
2092 int sock;
2093 int client_len;
2094 struct sockaddr_un client;
2095 struct vty *vty;
2096
2097 accept_sock = THREAD_FD (thread);
2098
2099 vty_event (VTYSH_SERV, accept_sock, NULL);
2100
2101 memset (&client, 0, sizeof (struct sockaddr_un));
2102 client_len = sizeof (struct sockaddr_un);
2103
hassoe473b032004-09-26 16:08:11 +00002104 sock = accept (accept_sock, (struct sockaddr *) &client,
2105 (socklen_t *) &client_len);
paul718e3742002-12-13 20:15:29 +00002106
2107 if (sock < 0)
2108 {
ajs6099b3b2004-11-20 02:06:59 +00002109 zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +00002110 return -1;
2111 }
2112
ajs9fc7ebf2005-02-23 15:12:34 +00002113 if (set_nonblocking(sock) < 0)
paul75e15fe2004-10-31 02:13:09 +00002114 {
ajs9fc7ebf2005-02-23 15:12:34 +00002115 zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
2116 " %s, closing", sock, safe_strerror (errno));
paul75e15fe2004-10-31 02:13:09 +00002117 close (sock);
2118 return -1;
2119 }
pauldccfb192004-10-29 08:29:36 +00002120
paul718e3742002-12-13 20:15:29 +00002121#ifdef VTYSH_DEBUG
2122 printf ("VTY shell accept\n");
2123#endif /* VTYSH_DEBUG */
2124
2125 vty = vty_new ();
2126 vty->fd = sock;
David Lamparter4715a532013-05-30 16:31:49 +02002127 vty->wfd = sock;
paul718e3742002-12-13 20:15:29 +00002128 vty->type = VTY_SHELL_SERV;
2129 vty->node = VIEW_NODE;
2130
2131 vty_event (VTYSH_READ, sock, vty);
2132
2133 return 0;
2134}
2135
2136static int
ajs9fc7ebf2005-02-23 15:12:34 +00002137vtysh_flush(struct vty *vty)
2138{
David Lamparter4715a532013-05-30 16:31:49 +02002139 switch (buffer_flush_available(vty->obuf, vty->wfd))
ajs9fc7ebf2005-02-23 15:12:34 +00002140 {
2141 case BUFFER_PENDING:
David Lamparter4715a532013-05-30 16:31:49 +02002142 vty_event(VTYSH_WRITE, vty->wfd, vty);
ajs9fc7ebf2005-02-23 15:12:34 +00002143 break;
2144 case BUFFER_ERROR:
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002145 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002146 zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
2147 buffer_reset(vty->obuf);
2148 vty_close(vty);
2149 return -1;
2150 break;
2151 case BUFFER_EMPTY:
2152 break;
2153 }
2154 return 0;
2155}
2156
2157static int
paul718e3742002-12-13 20:15:29 +00002158vtysh_read (struct thread *thread)
2159{
2160 int ret;
2161 int sock;
2162 int nbytes;
2163 struct vty *vty;
2164 unsigned char buf[VTY_READ_BUFSIZ];
ajs9fc7ebf2005-02-23 15:12:34 +00002165 unsigned char *p;
paul718e3742002-12-13 20:15:29 +00002166 u_char header[4] = {0, 0, 0, 0};
2167
2168 sock = THREAD_FD (thread);
2169 vty = THREAD_ARG (thread);
2170 vty->t_read = NULL;
2171
ajs9fc7ebf2005-02-23 15:12:34 +00002172 if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
paul718e3742002-12-13 20:15:29 +00002173 {
ajs9fc7ebf2005-02-23 15:12:34 +00002174 if (nbytes < 0)
2175 {
2176 if (ERRNO_IO_RETRY(errno))
2177 {
2178 vty_event (VTYSH_READ, sock, vty);
2179 return 0;
2180 }
Andrew J. Schorr74542d72006-07-10 18:09:42 +00002181 vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
ajs9fc7ebf2005-02-23 15:12:34 +00002182 zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
2183 __func__, sock, safe_strerror(errno));
2184 }
2185 buffer_reset(vty->obuf);
paul718e3742002-12-13 20:15:29 +00002186 vty_close (vty);
2187#ifdef VTYSH_DEBUG
2188 printf ("close vtysh\n");
2189#endif /* VTYSH_DEBUG */
2190 return 0;
2191 }
2192
2193#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002194 printf ("line: %.*s\n", nbytes, buf);
paul718e3742002-12-13 20:15:29 +00002195#endif /* VTYSH_DEBUG */
2196
ajs9fc7ebf2005-02-23 15:12:34 +00002197 for (p = buf; p < buf+nbytes; p++)
2198 {
2199 vty_ensure(vty, vty->length+1);
2200 vty->buf[vty->length++] = *p;
2201 if (*p == '\0')
2202 {
2203 /* Pass this line to parser. */
2204 ret = vty_execute (vty);
2205 /* Note that vty_execute clears the command buffer and resets
2206 vty->length to 0. */
paul718e3742002-12-13 20:15:29 +00002207
ajs9fc7ebf2005-02-23 15:12:34 +00002208 /* Return result. */
paul718e3742002-12-13 20:15:29 +00002209#ifdef VTYSH_DEBUG
ajs9fc7ebf2005-02-23 15:12:34 +00002210 printf ("result: %d\n", ret);
2211 printf ("vtysh node: %d\n", vty->node);
paul718e3742002-12-13 20:15:29 +00002212#endif /* VTYSH_DEBUG */
2213
ajs9fc7ebf2005-02-23 15:12:34 +00002214 header[3] = ret;
2215 buffer_put(vty->obuf, header, 4);
paul718e3742002-12-13 20:15:29 +00002216
ajs9fc7ebf2005-02-23 15:12:34 +00002217 if (!vty->t_write && (vtysh_flush(vty) < 0))
2218 /* Try to flush results; exit if a write error occurs. */
2219 return 0;
2220 }
2221 }
2222
paul718e3742002-12-13 20:15:29 +00002223 vty_event (VTYSH_READ, sock, vty);
2224
2225 return 0;
2226}
ajs49ff6d92004-11-04 19:26:16 +00002227
2228static int
2229vtysh_write (struct thread *thread)
2230{
2231 struct vty *vty = THREAD_ARG (thread);
2232
2233 vty->t_write = NULL;
ajs9fc7ebf2005-02-23 15:12:34 +00002234 vtysh_flush(vty);
ajs976d8c72004-11-10 15:40:09 +00002235 return 0;
ajs49ff6d92004-11-04 19:26:16 +00002236}
2237
paul718e3742002-12-13 20:15:29 +00002238#endif /* VTYSH */
2239
2240/* Determine address family to bind. */
2241void
hasso6ad96ea2004-10-07 19:33:46 +00002242vty_serv_sock (const char *addr, unsigned short port, const char *path)
paul718e3742002-12-13 20:15:29 +00002243{
2244 /* If port is set to 0, do not listen on TCP/IP at all! */
2245 if (port)
2246 {
2247
2248#ifdef HAVE_IPV6
paul29db05b2003-05-08 20:10:22 +00002249 vty_serv_sock_addrinfo (addr, port);
paul718e3742002-12-13 20:15:29 +00002250#else /* ! HAVE_IPV6 */
paul29db05b2003-05-08 20:10:22 +00002251 vty_serv_sock_family (addr,port, AF_INET);
paul718e3742002-12-13 20:15:29 +00002252#endif /* HAVE_IPV6 */
2253 }
2254
2255#ifdef VTYSH
2256 vty_serv_un (path);
2257#endif /* VTYSH */
2258}
2259
Andrew J. Schorr9d0a3262006-07-11 00:06:49 +00002260/* Close vty interface. Warning: call this only from functions that
2261 will be careful not to access the vty afterwards (since it has
2262 now been freed). This is safest from top-level functions (called
2263 directly by the thread dispatcher). */
paul718e3742002-12-13 20:15:29 +00002264void
2265vty_close (struct vty *vty)
2266{
2267 int i;
2268
2269 /* Cancel threads.*/
2270 if (vty->t_read)
2271 thread_cancel (vty->t_read);
2272 if (vty->t_write)
2273 thread_cancel (vty->t_write);
2274 if (vty->t_timeout)
2275 thread_cancel (vty->t_timeout);
paul718e3742002-12-13 20:15:29 +00002276
2277 /* Flush buffer. */
David Lamparter4715a532013-05-30 16:31:49 +02002278 buffer_flush_all (vty->obuf, vty->wfd);
paul718e3742002-12-13 20:15:29 +00002279
2280 /* Free input buffer. */
2281 buffer_free (vty->obuf);
2282
paul718e3742002-12-13 20:15:29 +00002283 /* Free command history. */
2284 for (i = 0; i < VTY_MAXHIST; i++)
2285 if (vty->hist[i])
2286 XFREE (MTYPE_VTY_HIST, vty->hist[i]);
2287
2288 /* Unset vector. */
2289 vector_unset (vtyvec, vty->fd);
2290
2291 /* Close socket. */
2292 if (vty->fd > 0)
2293 close (vty->fd);
David Lamparterba53a8f2015-05-05 11:04:46 +02002294 else
2295 vty_stdio_reset ();
paul718e3742002-12-13 20:15:29 +00002296
paul718e3742002-12-13 20:15:29 +00002297 if (vty->buf)
2298 XFREE (MTYPE_VTY, vty->buf);
2299
2300 /* Check configure. */
2301 vty_config_unlock (vty);
2302
2303 /* OK free vty. */
2304 XFREE (MTYPE_VTY, vty);
2305}
2306
2307/* When time out occur output message then close connection. */
2308static int
2309vty_timeout (struct thread *thread)
2310{
2311 struct vty *vty;
2312
2313 vty = THREAD_ARG (thread);
2314 vty->t_timeout = NULL;
2315 vty->v_timeout = 0;
2316
2317 /* Clear buffer*/
2318 buffer_reset (vty->obuf);
2319 vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
2320
2321 /* Close connection. */
2322 vty->status = VTY_CLOSE;
2323 vty_close (vty);
2324
2325 return 0;
2326}
2327
2328/* Read up configuration file from file_name. */
2329static void
2330vty_read_file (FILE *confp)
2331{
2332 int ret;
2333 struct vty *vty;
Steve Hillea555002009-07-28 16:36:14 -04002334 unsigned int line_num = 0;
paul718e3742002-12-13 20:15:29 +00002335
2336 vty = vty_new ();
David Lamparter4715a532013-05-30 16:31:49 +02002337 vty->wfd = dup(STDERR_FILENO); /* vty_close() will close this */
2338 if (vty->wfd < 0)
Steve Hillea555002009-07-28 16:36:14 -04002339 {
2340 /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */
David Lamparter4715a532013-05-30 16:31:49 +02002341 vty->wfd = STDOUT_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002342 }
David Lamparter4715a532013-05-30 16:31:49 +02002343 vty->fd = STDIN_FILENO;
Steve Hillea555002009-07-28 16:36:14 -04002344 vty->type = VTY_FILE;
paul718e3742002-12-13 20:15:29 +00002345 vty->node = CONFIG_NODE;
2346
2347 /* Execute configuration file */
Steve Hillea555002009-07-28 16:36:14 -04002348 ret = config_from_file (vty, confp, &line_num);
2349
2350 /* Flush any previous errors before printing messages below */
2351 buffer_flush_all (vty->obuf, vty->fd);
paul718e3742002-12-13 20:15:29 +00002352
paul7021c422003-07-15 12:52:22 +00002353 if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
paul718e3742002-12-13 20:15:29 +00002354 {
2355 switch (ret)
paul7021c422003-07-15 12:52:22 +00002356 {
2357 case CMD_ERR_AMBIGUOUS:
Steve Hillea555002009-07-28 16:36:14 -04002358 fprintf (stderr, "*** Error reading config: Ambiguous command.\n");
paul7021c422003-07-15 12:52:22 +00002359 break;
2360 case CMD_ERR_NO_MATCH:
Steve Hillea555002009-07-28 16:36:14 -04002361 fprintf (stderr, "*** Error reading config: There is no such command.\n");
paul7021c422003-07-15 12:52:22 +00002362 break;
2363 }
Steve Hillea555002009-07-28 16:36:14 -04002364 fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n",
2365 line_num, vty->buf);
paul718e3742002-12-13 20:15:29 +00002366 vty_close (vty);
2367 exit (1);
2368 }
2369
2370 vty_close (vty);
2371}
2372
ajs9fc7ebf2005-02-23 15:12:34 +00002373static FILE *
paul718e3742002-12-13 20:15:29 +00002374vty_use_backup_config (char *fullpath)
2375{
2376 char *fullpath_sav, *fullpath_tmp;
2377 FILE *ret = NULL;
2378 struct stat buf;
2379 int tmp, sav;
2380 int c;
2381 char buffer[512];
2382
2383 fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
2384 strcpy (fullpath_sav, fullpath);
2385 strcat (fullpath_sav, CONF_BACKUP_EXT);
2386 if (stat (fullpath_sav, &buf) == -1)
2387 {
2388 free (fullpath_sav);
2389 return NULL;
2390 }
2391
2392 fullpath_tmp = malloc (strlen (fullpath) + 8);
2393 sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
2394
2395 /* Open file to configuration write. */
2396 tmp = mkstemp (fullpath_tmp);
2397 if (tmp < 0)
2398 {
2399 free (fullpath_sav);
2400 free (fullpath_tmp);
2401 return NULL;
2402 }
2403
2404 sav = open (fullpath_sav, O_RDONLY);
2405 if (sav < 0)
2406 {
gdt3dbf9962003-12-22 20:18:18 +00002407 unlink (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002408 free (fullpath_sav);
2409 free (fullpath_tmp);
paul718e3742002-12-13 20:15:29 +00002410 return NULL;
2411 }
2412
2413 while((c = read (sav, buffer, 512)) > 0)
2414 write (tmp, buffer, c);
2415
2416 close (sav);
2417 close (tmp);
2418
gdtaa593d52003-12-22 20:15:53 +00002419 if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
2420 {
gdt3dbf9962003-12-22 20:18:18 +00002421 unlink (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002422 free (fullpath_sav);
2423 free (fullpath_tmp);
gdtaa593d52003-12-22 20:15:53 +00002424 return NULL;
2425 }
2426
paul718e3742002-12-13 20:15:29 +00002427 if (link (fullpath_tmp, fullpath) == 0)
2428 ret = fopen (fullpath, "r");
2429
2430 unlink (fullpath_tmp);
2431
2432 free (fullpath_sav);
2433 free (fullpath_tmp);
hasso12f6ea22005-03-07 08:35:39 +00002434 return ret;
paul718e3742002-12-13 20:15:29 +00002435}
2436
2437/* Read up configuration file from file_name. */
2438void
2439vty_read_config (char *config_file,
hasso320ec102004-06-20 19:54:37 +00002440 char *config_default_dir)
paul718e3742002-12-13 20:15:29 +00002441{
paulccc92352003-10-22 02:49:38 +00002442 char cwd[MAXPATHLEN];
paul718e3742002-12-13 20:15:29 +00002443 FILE *confp = NULL;
2444 char *fullpath;
paul05865c92005-10-26 05:49:54 +00002445 char *tmp = NULL;
paul718e3742002-12-13 20:15:29 +00002446
2447 /* If -f flag specified. */
2448 if (config_file != NULL)
2449 {
2450 if (! IS_DIRECTORY_SEP (config_file[0]))
hasso320ec102004-06-20 19:54:37 +00002451 {
2452 getcwd (cwd, MAXPATHLEN);
paul05865c92005-10-26 05:49:54 +00002453 tmp = XMALLOC (MTYPE_TMP,
hasso320ec102004-06-20 19:54:37 +00002454 strlen (cwd) + strlen (config_file) + 2);
paul05865c92005-10-26 05:49:54 +00002455 sprintf (tmp, "%s/%s", cwd, config_file);
2456 fullpath = tmp;
hasso320ec102004-06-20 19:54:37 +00002457 }
paul718e3742002-12-13 20:15:29 +00002458 else
hasso320ec102004-06-20 19:54:37 +00002459 fullpath = config_file;
paul718e3742002-12-13 20:15:29 +00002460
2461 confp = fopen (fullpath, "r");
2462
2463 if (confp == NULL)
hasso320ec102004-06-20 19:54:37 +00002464 {
paul3d1dc852005-04-05 00:45:23 +00002465 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2466 __func__, fullpath, safe_strerror (errno));
2467
hasso320ec102004-06-20 19:54:37 +00002468 confp = vty_use_backup_config (fullpath);
2469 if (confp)
2470 fprintf (stderr, "WARNING: using backup configuration file!\n");
2471 else
2472 {
2473 fprintf (stderr, "can't open configuration file [%s]\n",
paul3d1dc852005-04-05 00:45:23 +00002474 config_file);
hasso320ec102004-06-20 19:54:37 +00002475 exit(1);
2476 }
2477 }
paul718e3742002-12-13 20:15:29 +00002478 }
2479 else
2480 {
paul718e3742002-12-13 20:15:29 +00002481#ifdef VTYSH
hasso320ec102004-06-20 19:54:37 +00002482 int ret;
2483 struct stat conf_stat;
paul718e3742002-12-13 20:15:29 +00002484
hasso320ec102004-06-20 19:54:37 +00002485 /* !!!!PLEASE LEAVE!!!!
2486 * This is NEEDED for use with vtysh -b, or else you can get
2487 * a real configuration food fight with a lot garbage in the
2488 * merged configuration file it creates coming from the per
2489 * daemon configuration files. This also allows the daemons
2490 * to start if there default configuration file is not
2491 * present or ignore them, as needed when using vtysh -b to
2492 * configure the daemons at boot - MAG
2493 */
paul718e3742002-12-13 20:15:29 +00002494
hasso320ec102004-06-20 19:54:37 +00002495 /* Stat for vtysh Zebra.conf, if found startup and wait for
2496 * boot configuration
2497 */
paul718e3742002-12-13 20:15:29 +00002498
hasso320ec102004-06-20 19:54:37 +00002499 if ( strstr(config_default_dir, "vtysh") == NULL)
2500 {
2501 ret = stat (integrate_default, &conf_stat);
2502 if (ret >= 0)
2503 return;
2504 }
paul718e3742002-12-13 20:15:29 +00002505#endif /* VTYSH */
2506
hasso320ec102004-06-20 19:54:37 +00002507 confp = fopen (config_default_dir, "r");
2508 if (confp == NULL)
2509 {
paul3d1dc852005-04-05 00:45:23 +00002510 fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
2511 __func__, config_default_dir, safe_strerror (errno));
2512
hasso320ec102004-06-20 19:54:37 +00002513 confp = vty_use_backup_config (config_default_dir);
2514 if (confp)
2515 {
2516 fprintf (stderr, "WARNING: using backup configuration file!\n");
2517 fullpath = config_default_dir;
2518 }
2519 else
2520 {
2521 fprintf (stderr, "can't open configuration file [%s]\n",
2522 config_default_dir);
2523 exit (1);
paul3d1dc852005-04-05 00:45:23 +00002524 }
hasso320ec102004-06-20 19:54:37 +00002525 }
paul718e3742002-12-13 20:15:29 +00002526 else
hasso320ec102004-06-20 19:54:37 +00002527 fullpath = config_default_dir;
2528 }
2529
paul718e3742002-12-13 20:15:29 +00002530 vty_read_file (confp);
2531
2532 fclose (confp);
2533
2534 host_config_set (fullpath);
paul05865c92005-10-26 05:49:54 +00002535
2536 if (tmp)
2537 XFREE (MTYPE_TMP, fullpath);
paul718e3742002-12-13 20:15:29 +00002538}
2539
2540/* Small utility function which output log to the VTY. */
2541void
ajs274a4a42004-12-07 15:39:31 +00002542vty_log (const char *level, const char *proto_str,
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002543 const char *format, struct timestamp_control *ctl, va_list va)
paul718e3742002-12-13 20:15:29 +00002544{
hasso8c328f12004-10-05 21:01:23 +00002545 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002546 struct vty *vty;
Paul Jakmaa4b30302006-05-28 08:18:38 +00002547
2548 if (!vtyvec)
2549 return;
paul718e3742002-12-13 20:15:29 +00002550
paul55468c82005-03-14 20:19:01 +00002551 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002552 if ((vty = vector_slot (vtyvec, i)) != NULL)
2553 if (vty->monitor)
ajsd246bd92004-11-23 17:35:08 +00002554 {
2555 va_list ac;
2556 va_copy(ac, va);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00002557 vty_log_out (vty, level, proto_str, format, ctl, ac);
ajsd246bd92004-11-23 17:35:08 +00002558 va_end(ac);
2559 }
paul718e3742002-12-13 20:15:29 +00002560}
2561
ajs274a4a42004-12-07 15:39:31 +00002562/* Async-signal-safe version of vty_log for fixed strings. */
2563void
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002564vty_log_fixed (char *buf, size_t len)
ajs274a4a42004-12-07 15:39:31 +00002565{
2566 unsigned int i;
ajs9fc7ebf2005-02-23 15:12:34 +00002567 struct iovec iov[2];
2568
Paul Jakmaa4b30302006-05-28 08:18:38 +00002569 /* vty may not have been initialised */
2570 if (!vtyvec)
2571 return;
2572
Paul Jakma7aa9dce2014-09-19 14:42:23 +01002573 iov[0].iov_base = buf;
ajs9fc7ebf2005-02-23 15:12:34 +00002574 iov[0].iov_len = len;
ajs926fe8f2005-04-08 18:50:40 +00002575 iov[1].iov_base = (void *)"\r\n";
ajs9fc7ebf2005-02-23 15:12:34 +00002576 iov[1].iov_len = 2;
ajs274a4a42004-12-07 15:39:31 +00002577
paul55468c82005-03-14 20:19:01 +00002578 for (i = 0; i < vector_active (vtyvec); i++)
ajs274a4a42004-12-07 15:39:31 +00002579 {
2580 struct vty *vty;
ajs9fc7ebf2005-02-23 15:12:34 +00002581 if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
2582 /* N.B. We don't care about the return code, since process is
2583 most likely just about to die anyway. */
David Lamparter4715a532013-05-30 16:31:49 +02002584 writev(vty->wfd, iov, 2);
ajs274a4a42004-12-07 15:39:31 +00002585 }
2586}
2587
paul718e3742002-12-13 20:15:29 +00002588int
2589vty_config_lock (struct vty *vty)
2590{
2591 if (vty_config == 0)
2592 {
2593 vty->config = 1;
2594 vty_config = 1;
2595 }
2596 return vty->config;
2597}
2598
2599int
2600vty_config_unlock (struct vty *vty)
2601{
2602 if (vty_config == 1 && vty->config == 1)
2603 {
2604 vty->config = 0;
2605 vty_config = 0;
2606 }
2607 return vty->config;
2608}
David Lamparter6b0655a2014-06-04 06:53:35 +02002609
paul718e3742002-12-13 20:15:29 +00002610/* Master of the threads. */
Donald Sharpeeef0db2015-10-14 08:50:38 -04002611static struct thread_master *vty_master;
paul718e3742002-12-13 20:15:29 +00002612
2613static void
2614vty_event (enum event event, int sock, struct vty *vty)
2615{
2616 struct thread *vty_serv_thread;
2617
2618 switch (event)
2619 {
2620 case VTY_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002621 vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock);
paul718e3742002-12-13 20:15:29 +00002622 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
2623 break;
2624#ifdef VTYSH
2625 case VTYSH_SERV:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002626 vty_serv_thread = thread_add_read (vty_master, vtysh_accept, vty, sock);
Christian Franke677bcbb2013-02-27 13:47:23 +00002627 vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
paul718e3742002-12-13 20:15:29 +00002628 break;
2629 case VTYSH_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002630 vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock);
ajs49ff6d92004-11-04 19:26:16 +00002631 break;
2632 case VTYSH_WRITE:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002633 vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock);
paul718e3742002-12-13 20:15:29 +00002634 break;
2635#endif /* VTYSH */
2636 case VTY_READ:
Donald Sharpeeef0db2015-10-14 08:50:38 -04002637 vty->t_read = thread_add_read (vty_master, vty_read, vty, sock);
paul718e3742002-12-13 20:15:29 +00002638
2639 /* Time out treatment. */
2640 if (vty->v_timeout)
2641 {
2642 if (vty->t_timeout)
2643 thread_cancel (vty->t_timeout);
2644 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002645 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002646 }
2647 break;
2648 case VTY_WRITE:
2649 if (! vty->t_write)
Donald Sharpeeef0db2015-10-14 08:50:38 -04002650 vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock);
paul718e3742002-12-13 20:15:29 +00002651 break;
2652 case VTY_TIMEOUT_RESET:
2653 if (vty->t_timeout)
2654 {
2655 thread_cancel (vty->t_timeout);
2656 vty->t_timeout = NULL;
2657 }
2658 if (vty->v_timeout)
2659 {
2660 vty->t_timeout =
Donald Sharpeeef0db2015-10-14 08:50:38 -04002661 thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout);
paul718e3742002-12-13 20:15:29 +00002662 }
2663 break;
2664 }
2665}
David Lamparter6b0655a2014-06-04 06:53:35 +02002666
paul718e3742002-12-13 20:15:29 +00002667DEFUN (config_who,
2668 config_who_cmd,
2669 "who",
2670 "Display who is on vty\n")
2671{
hasso8c328f12004-10-05 21:01:23 +00002672 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002673 struct vty *v;
2674
paul55468c82005-03-14 20:19:01 +00002675 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002676 if ((v = vector_slot (vtyvec, i)) != NULL)
2677 vty_out (vty, "%svty[%d] connected from %s.%s",
2678 v->config ? "*" : " ",
2679 i, v->address, VTY_NEWLINE);
2680 return CMD_SUCCESS;
2681}
2682
2683/* Move to vty configuration mode. */
2684DEFUN (line_vty,
2685 line_vty_cmd,
2686 "line vty",
2687 "Configure a terminal line\n"
2688 "Virtual terminal\n")
2689{
2690 vty->node = VTY_NODE;
2691 return CMD_SUCCESS;
2692}
2693
2694/* Set time out value. */
ajs9fc7ebf2005-02-23 15:12:34 +00002695static int
paul9035efa2004-10-10 11:56:56 +00002696exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
paul718e3742002-12-13 20:15:29 +00002697{
2698 unsigned long timeout = 0;
2699
2700 /* min_str and sec_str are already checked by parser. So it must be
2701 all digit string. */
2702 if (min_str)
2703 {
2704 timeout = strtol (min_str, NULL, 10);
2705 timeout *= 60;
2706 }
2707 if (sec_str)
2708 timeout += strtol (sec_str, NULL, 10);
2709
2710 vty_timeout_val = timeout;
2711 vty->v_timeout = timeout;
2712 vty_event (VTY_TIMEOUT_RESET, 0, vty);
2713
2714
2715 return CMD_SUCCESS;
2716}
2717
2718DEFUN (exec_timeout_min,
2719 exec_timeout_min_cmd,
2720 "exec-timeout <0-35791>",
2721 "Set timeout value\n"
2722 "Timeout value in minutes\n")
2723{
2724 return exec_timeout (vty, argv[0], NULL);
2725}
2726
2727DEFUN (exec_timeout_sec,
2728 exec_timeout_sec_cmd,
2729 "exec-timeout <0-35791> <0-2147483>",
2730 "Set the EXEC timeout\n"
2731 "Timeout in minutes\n"
2732 "Timeout in seconds\n")
2733{
2734 return exec_timeout (vty, argv[0], argv[1]);
2735}
2736
2737DEFUN (no_exec_timeout,
2738 no_exec_timeout_cmd,
2739 "no exec-timeout",
2740 NO_STR
2741 "Set the EXEC timeout\n")
2742{
2743 return exec_timeout (vty, NULL, NULL);
2744}
2745
2746/* Set vty access class. */
2747DEFUN (vty_access_class,
2748 vty_access_class_cmd,
2749 "access-class WORD",
2750 "Filter connections based on an IP access list\n"
2751 "IP access list\n")
2752{
2753 if (vty_accesslist_name)
2754 XFREE(MTYPE_VTY, vty_accesslist_name);
2755
2756 vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2757
2758 return CMD_SUCCESS;
2759}
2760
2761/* Clear vty access class. */
2762DEFUN (no_vty_access_class,
2763 no_vty_access_class_cmd,
2764 "no access-class [WORD]",
2765 NO_STR
2766 "Filter connections based on an IP access list\n"
2767 "IP access list\n")
2768{
2769 if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
2770 {
2771 vty_out (vty, "Access-class is not currently applied to vty%s",
2772 VTY_NEWLINE);
2773 return CMD_WARNING;
2774 }
2775
2776 XFREE(MTYPE_VTY, vty_accesslist_name);
2777
2778 vty_accesslist_name = NULL;
2779
2780 return CMD_SUCCESS;
2781}
2782
2783#ifdef HAVE_IPV6
2784/* Set vty access class. */
2785DEFUN (vty_ipv6_access_class,
2786 vty_ipv6_access_class_cmd,
2787 "ipv6 access-class WORD",
2788 IPV6_STR
2789 "Filter connections based on an IP access list\n"
2790 "IPv6 access list\n")
2791{
2792 if (vty_ipv6_accesslist_name)
2793 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2794
2795 vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
2796
2797 return CMD_SUCCESS;
2798}
2799
2800/* Clear vty access class. */
2801DEFUN (no_vty_ipv6_access_class,
2802 no_vty_ipv6_access_class_cmd,
2803 "no ipv6 access-class [WORD]",
2804 NO_STR
2805 IPV6_STR
2806 "Filter connections based on an IP access list\n"
2807 "IPv6 access list\n")
2808{
2809 if (! vty_ipv6_accesslist_name ||
2810 (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
2811 {
2812 vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
2813 VTY_NEWLINE);
2814 return CMD_WARNING;
2815 }
2816
2817 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2818
2819 vty_ipv6_accesslist_name = NULL;
2820
2821 return CMD_SUCCESS;
2822}
2823#endif /* HAVE_IPV6 */
2824
2825/* vty login. */
2826DEFUN (vty_login,
2827 vty_login_cmd,
2828 "login",
2829 "Enable password checking\n")
2830{
2831 no_password_check = 0;
2832 return CMD_SUCCESS;
2833}
2834
2835DEFUN (no_vty_login,
2836 no_vty_login_cmd,
2837 "no login",
2838 NO_STR
2839 "Enable password checking\n")
2840{
2841 no_password_check = 1;
2842 return CMD_SUCCESS;
2843}
2844
Paul Jakma62687ff2008-08-23 14:27:06 +01002845/* initial mode. */
2846DEFUN (vty_restricted_mode,
2847 vty_restricted_mode_cmd,
2848 "anonymous restricted",
2849 "Restrict view commands available in anonymous, unauthenticated vty\n")
2850{
2851 restricted_mode = 1;
2852 return CMD_SUCCESS;
2853}
2854
2855DEFUN (vty_no_restricted_mode,
2856 vty_no_restricted_mode_cmd,
2857 "no anonymous restricted",
2858 NO_STR
2859 "Enable password checking\n")
2860{
2861 restricted_mode = 0;
2862 return CMD_SUCCESS;
2863}
2864
paul718e3742002-12-13 20:15:29 +00002865DEFUN (service_advanced_vty,
2866 service_advanced_vty_cmd,
2867 "service advanced-vty",
2868 "Set up miscellaneous service\n"
2869 "Enable advanced mode vty interface\n")
2870{
2871 host.advanced = 1;
2872 return CMD_SUCCESS;
2873}
2874
2875DEFUN (no_service_advanced_vty,
2876 no_service_advanced_vty_cmd,
2877 "no service advanced-vty",
2878 NO_STR
2879 "Set up miscellaneous service\n"
2880 "Enable advanced mode vty interface\n")
2881{
2882 host.advanced = 0;
2883 return CMD_SUCCESS;
2884}
2885
2886DEFUN (terminal_monitor,
2887 terminal_monitor_cmd,
2888 "terminal monitor",
2889 "Set terminal line parameters\n"
2890 "Copy debug output to the current terminal line\n")
2891{
2892 vty->monitor = 1;
2893 return CMD_SUCCESS;
2894}
2895
2896DEFUN (terminal_no_monitor,
2897 terminal_no_monitor_cmd,
2898 "terminal no monitor",
2899 "Set terminal line parameters\n"
2900 NO_STR
2901 "Copy debug output to the current terminal line\n")
2902{
2903 vty->monitor = 0;
2904 return CMD_SUCCESS;
2905}
2906
paul789f78a2006-01-17 17:42:03 +00002907ALIAS (terminal_no_monitor,
2908 no_terminal_monitor_cmd,
2909 "no terminal monitor",
2910 NO_STR
2911 "Set terminal line parameters\n"
2912 "Copy debug output to the current terminal line\n")
2913
paul718e3742002-12-13 20:15:29 +00002914DEFUN (show_history,
2915 show_history_cmd,
2916 "show history",
2917 SHOW_STR
2918 "Display the session command history\n")
2919{
2920 int index;
2921
2922 for (index = vty->hindex + 1; index != vty->hindex;)
2923 {
2924 if (index == VTY_MAXHIST)
2925 {
2926 index = 0;
2927 continue;
2928 }
2929
2930 if (vty->hist[index] != NULL)
2931 vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
2932
2933 index++;
2934 }
2935
2936 return CMD_SUCCESS;
2937}
2938
2939/* Display current configuration. */
ajs9fc7ebf2005-02-23 15:12:34 +00002940static int
paul718e3742002-12-13 20:15:29 +00002941vty_config_write (struct vty *vty)
2942{
2943 vty_out (vty, "line vty%s", VTY_NEWLINE);
2944
2945 if (vty_accesslist_name)
2946 vty_out (vty, " access-class %s%s",
2947 vty_accesslist_name, VTY_NEWLINE);
2948
2949 if (vty_ipv6_accesslist_name)
2950 vty_out (vty, " ipv6 access-class %s%s",
2951 vty_ipv6_accesslist_name, VTY_NEWLINE);
2952
2953 /* exec-timeout */
2954 if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
2955 vty_out (vty, " exec-timeout %ld %ld%s",
2956 vty_timeout_val / 60,
2957 vty_timeout_val % 60, VTY_NEWLINE);
2958
2959 /* login */
2960 if (no_password_check)
2961 vty_out (vty, " no login%s", VTY_NEWLINE);
Paul Jakma62687ff2008-08-23 14:27:06 +01002962
2963 if (restricted_mode != restricted_mode_default)
2964 {
2965 if (restricted_mode_default)
2966 vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
2967 else
2968 vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
2969 }
2970
paul718e3742002-12-13 20:15:29 +00002971 vty_out (vty, "!%s", VTY_NEWLINE);
2972
2973 return CMD_SUCCESS;
2974}
2975
2976struct cmd_node vty_node =
2977{
2978 VTY_NODE,
2979 "%s(config-line)# ",
hassoe7168df2004-10-03 20:11:32 +00002980 1,
paul718e3742002-12-13 20:15:29 +00002981};
2982
2983/* Reset all VTY status. */
2984void
2985vty_reset ()
2986{
hasso8c328f12004-10-05 21:01:23 +00002987 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002988 struct vty *vty;
2989 struct thread *vty_serv_thread;
2990
paul55468c82005-03-14 20:19:01 +00002991 for (i = 0; i < vector_active (vtyvec); i++)
paul718e3742002-12-13 20:15:29 +00002992 if ((vty = vector_slot (vtyvec, i)) != NULL)
2993 {
2994 buffer_reset (vty->obuf);
2995 vty->status = VTY_CLOSE;
2996 vty_close (vty);
2997 }
2998
paul55468c82005-03-14 20:19:01 +00002999 for (i = 0; i < vector_active (Vvty_serv_thread); i++)
paul718e3742002-12-13 20:15:29 +00003000 if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
3001 {
3002 thread_cancel (vty_serv_thread);
3003 vector_slot (Vvty_serv_thread, i) = NULL;
3004 close (i);
3005 }
3006
3007 vty_timeout_val = VTY_TIMEOUT_DEFAULT;
3008
3009 if (vty_accesslist_name)
3010 {
3011 XFREE(MTYPE_VTY, vty_accesslist_name);
3012 vty_accesslist_name = NULL;
3013 }
3014
3015 if (vty_ipv6_accesslist_name)
3016 {
3017 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
3018 vty_ipv6_accesslist_name = NULL;
3019 }
3020}
3021
ajs9fc7ebf2005-02-23 15:12:34 +00003022static void
3023vty_save_cwd (void)
paul718e3742002-12-13 20:15:29 +00003024{
paul79ad2792003-10-15 22:09:28 +00003025 char cwd[MAXPATHLEN];
paulccc92352003-10-22 02:49:38 +00003026 char *c;
paul718e3742002-12-13 20:15:29 +00003027
paulccc92352003-10-22 02:49:38 +00003028 c = getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003029
paulccc92352003-10-22 02:49:38 +00003030 if (!c)
paul79ad2792003-10-15 22:09:28 +00003031 {
3032 chdir (SYSCONFDIR);
paulccc92352003-10-22 02:49:38 +00003033 getcwd (cwd, MAXPATHLEN);
paul79ad2792003-10-15 22:09:28 +00003034 }
paul718e3742002-12-13 20:15:29 +00003035
3036 vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
3037 strcpy (vty_cwd, cwd);
3038}
3039
3040char *
3041vty_get_cwd ()
3042{
3043 return vty_cwd;
3044}
3045
3046int
3047vty_shell (struct vty *vty)
3048{
3049 return vty->type == VTY_SHELL ? 1 : 0;
3050}
3051
3052int
3053vty_shell_serv (struct vty *vty)
3054{
3055 return vty->type == VTY_SHELL_SERV ? 1 : 0;
3056}
3057
3058void
3059vty_init_vtysh ()
3060{
3061 vtyvec = vector_init (VECTOR_MIN_SIZE);
3062}
3063
3064/* Install vty's own commands like `who' command. */
3065void
paulb21b19c2003-06-15 01:28:29 +00003066vty_init (struct thread_master *master_thread)
paul718e3742002-12-13 20:15:29 +00003067{
3068 /* For further configuration read, preserve current directory. */
3069 vty_save_cwd ();
3070
3071 vtyvec = vector_init (VECTOR_MIN_SIZE);
3072
Donald Sharpeeef0db2015-10-14 08:50:38 -04003073 vty_master = master_thread;
paulb21b19c2003-06-15 01:28:29 +00003074
David Lamparterba53a8f2015-05-05 11:04:46 +02003075 atexit (vty_stdio_reset);
3076
paul718e3742002-12-13 20:15:29 +00003077 /* Initilize server thread vector. */
3078 Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE);
3079
3080 /* Install bgp top node. */
3081 install_node (&vty_node, vty_config_write);
3082
Paul Jakma62687ff2008-08-23 14:27:06 +01003083 install_element (RESTRICTED_NODE, &config_who_cmd);
3084 install_element (RESTRICTED_NODE, &show_history_cmd);
paul718e3742002-12-13 20:15:29 +00003085 install_element (VIEW_NODE, &config_who_cmd);
3086 install_element (VIEW_NODE, &show_history_cmd);
3087 install_element (ENABLE_NODE, &config_who_cmd);
3088 install_element (CONFIG_NODE, &line_vty_cmd);
3089 install_element (CONFIG_NODE, &service_advanced_vty_cmd);
3090 install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
3091 install_element (CONFIG_NODE, &show_history_cmd);
3092 install_element (ENABLE_NODE, &terminal_monitor_cmd);
3093 install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
paul789f78a2006-01-17 17:42:03 +00003094 install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00003095 install_element (ENABLE_NODE, &show_history_cmd);
3096
3097 install_default (VTY_NODE);
3098 install_element (VTY_NODE, &exec_timeout_min_cmd);
3099 install_element (VTY_NODE, &exec_timeout_sec_cmd);
3100 install_element (VTY_NODE, &no_exec_timeout_cmd);
3101 install_element (VTY_NODE, &vty_access_class_cmd);
3102 install_element (VTY_NODE, &no_vty_access_class_cmd);
3103 install_element (VTY_NODE, &vty_login_cmd);
3104 install_element (VTY_NODE, &no_vty_login_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01003105 install_element (VTY_NODE, &vty_restricted_mode_cmd);
3106 install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
paul718e3742002-12-13 20:15:29 +00003107#ifdef HAVE_IPV6
3108 install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
3109 install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
3110#endif /* HAVE_IPV6 */
3111}
Chris Caputo228da422009-07-18 05:44:03 +00003112
3113void
3114vty_terminate (void)
3115{
3116 if (vty_cwd)
3117 XFREE (MTYPE_TMP, vty_cwd);
3118
3119 if (vtyvec && Vvty_serv_thread)
3120 {
3121 vty_reset ();
3122 vector_free (vtyvec);
3123 vector_free (Vvty_serv_thread);
3124 }
3125}