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