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