Merge commit '816e24cb4296d6b7110da4a89661bbac8dc7af21' into libosmocore
authorHarald Welte <laforge@gnumonks.org>
Fri, 25 Jun 2010 00:55:59 +0000 (02:55 +0200)
committerHarald Welte <laforge@gnumonks.org>
Fri, 25 Jun 2010 00:58:15 +0000 (02:58 +0200)
Conflicts:
src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h

36 files changed:
src/shared/libosmocore/Makefile.am
src/shared/libosmocore/configure.in
src/shared/libosmocore/include/Makefile.am
src/shared/libosmocore/include/osmocom/Makefile.am [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/Makefile.am [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/buffer.h [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/command.h [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/logging.h [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/telnet_interface.h [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/vector.h [new file with mode: 0644]
src/shared/libosmocore/include/osmocom/vty/vty.h [new file with mode: 0644]
src/shared/libosmocore/include/osmocore/Makefile.am
src/shared/libosmocore/include/osmocore/gsm0808.h
src/shared/libosmocore/include/osmocore/gsm48.h
src/shared/libosmocore/include/osmocore/gsm_utils.h
src/shared/libosmocore/include/osmocore/logging.h
src/shared/libosmocore/include/osmocore/msgb.h
src/shared/libosmocore/include/osmocore/protocol/Makefile.am
src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
src/shared/libosmocore/include/osmocore/rate_ctr.h [new file with mode: 0644]
src/shared/libosmocore/libosmovty.pc.in [new file with mode: 0644]
src/shared/libosmocore/src/Makefile.am
src/shared/libosmocore/src/gsm0808.c
src/shared/libosmocore/src/gsm48.c
src/shared/libosmocore/src/gsm_utils.c
src/shared/libosmocore/src/logging.c
src/shared/libosmocore/src/msgb.c
src/shared/libosmocore/src/rate_ctr.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/Makefile.am [new file with mode: 0644]
src/shared/libosmocore/src/vty/buffer.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/command.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/logging_vty.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/telnet_interface.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/utils.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/vector.c [new file with mode: 0644]
src/shared/libosmocore/src/vty/vty.c [new file with mode: 0644]

index 2adf502..81da629 100644 (file)
@@ -5,7 +5,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
 SUBDIRS = include src tests
 
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libosmocore.pc
+pkgconfig_DATA = libosmocore.pc libosmovty.pc
 
 BUILT_SOURCES = $(top_srcdir)/.version
 $(top_srcdir)/.version:
index 9879ebc..cc8fdf6 100644 (file)
@@ -44,12 +44,22 @@ AC_ARG_ENABLE(tests,
        [enable_tests=0], [enable_tests=1])
 AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1")
 
+AC_ARG_ENABLE(vtyc,
+       [  --disable-vty Disable building VTY telnet interface ],
+       [enable_vty=0], [enable_vty=1])
+AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1")
+
+
 AC_OUTPUT(
        libosmocore.pc
+       libosmovty.pc
+       include/osmocom/Makefile
+       include/osmocom/vty/Makefile
        include/osmocore/Makefile
        include/osmocore/protocol/Makefile
        include/Makefile
        src/Makefile
+       src/vty/Makefile
        tests/Makefile
        tests/timer/Makefile
        tests/sms/Makefile
diff --git a/src/shared/libosmocore/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am
new file mode 100644 (file)
index 0000000..71293d1
--- /dev/null
@@ -0,0 +1,3 @@
+if ENABLE_VTY
+SUBDIRS = vty
+endif
diff --git a/src/shared/libosmocore/include/osmocom/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am
new file mode 100644 (file)
index 0000000..d2f0616
--- /dev/null
@@ -0,0 +1,4 @@
+osmovty_HEADERS = buffer.h command.h vector.h vty.h \
+       telnet_interface.h logging.h
+
+osmovtydir = $(includedir)/osmocom/vty
diff --git a/src/shared/libosmocore/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h
new file mode 100644 (file)
index 0000000..c9467a9
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Buffering to output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_BUFFER_H
+#define _ZEBRA_BUFFER_H
+
+#include <sys/types.h>
+
+/* Create a new buffer.  Memory will be allocated in chunks of the given
+   size.  If the argument is 0, the library will supply a reasonable
+   default size suitable for buffering socket I/O. */
+struct buffer *buffer_new(void *ctx, size_t);
+
+/* Free all data in the buffer. */
+void buffer_reset(struct buffer *);
+
+/* This function first calls buffer_reset to release all buffered data.
+   Then it frees the struct buffer itself. */
+void buffer_free(struct buffer *);
+
+/* Add the given data to the end of the buffer. */
+extern void buffer_put(struct buffer *, const void *, size_t);
+/* Add a single character to the end of the buffer. */
+extern void buffer_putc(struct buffer *, u_char);
+/* Add a NUL-terminated string to the end of the buffer. */
+extern void buffer_putstr(struct buffer *, const char *);
+
+/* Combine all accumulated (and unflushed) data inside the buffer into a
+   single NUL-terminated string allocated using XMALLOC(MTYPE_TMP).  Note
+   that this function does not alter the state of the buffer, so the data
+   is still inside waiting to be flushed. */
+char *buffer_getstr(struct buffer *);
+
+/* Returns 1 if there is no pending data in the buffer.  Otherwise returns 0. */
+int buffer_empty(struct buffer *);
+
+typedef enum {
+       /* An I/O error occurred.  The buffer should be destroyed and the
+          file descriptor should be closed. */
+       BUFFER_ERROR = -1,
+
+       /* The data was written successfully, and the buffer is now empty
+          (there is no pending data waiting to be flushed). */
+       BUFFER_EMPTY = 0,
+
+       /* There is pending data in the buffer waiting to be flushed.  Please
+          try flushing the buffer when select indicates that the file descriptor
+          is writeable. */
+       BUFFER_PENDING = 1
+} buffer_status_t;
+
+/* Try to write this data to the file descriptor.  Any data that cannot
+   be written immediately is added to the buffer queue. */
+extern buffer_status_t buffer_write(struct buffer *, int fd,
+                                   const void *, size_t);
+
+/* This function attempts to flush some (but perhaps not all) of
+   the queued data to the given file descriptor. */
+extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
+
+/* The following 2 functions (buffer_flush_all and buffer_flush_window)
+   are for use in lib/vty.c only.  They should not be used elsewhere. */
+
+/* Call buffer_flush_available repeatedly until either all data has been
+   flushed, or an I/O error has been encountered, or the operation would
+   block. */
+extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
+
+/* Attempt to write enough data to the given fd to fill a window of the
+   given width and height (and remove the data written from the buffer).
+
+   If !no_more, then a message saying " --More-- " is appended.
+   If erase is true, then first overwrite the previous " --More-- " message
+   with spaces.
+
+   Any write error (including EAGAIN or EINTR) will cause this function
+   to return -1 (because the logic for handling the erase and more features
+   is too complicated to retry the write later).
+*/
+extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
+                                          int height, int erase, int no_more);
+
+#endif                         /* _ZEBRA_BUFFER_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h
new file mode 100644 (file)
index 0000000..69e9e77
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Zebra configuration command interface routine
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_COMMAND_H
+#define _ZEBRA_COMMAND_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "vector.h"
+#include "vty.h"
+
+/* Host configuration variable */
+struct host {
+       /* Host name of this router. */
+       char *name;
+
+       /* Password for vty interface. */
+       char *password;
+       char *password_encrypt;
+
+       /* Enable password */
+       char *enable;
+       char *enable_encrypt;
+
+       /* System wide terminal lines. */
+       int lines;
+
+       /* Log filename. */
+       char *logfile;
+
+       /* config file name of this host */
+       char *config;
+
+       /* Flags for services */
+       int advanced;
+       int encrypt;
+
+       /* Banner configuration. */
+       const char *motd;
+       char *motdfile;
+
+       const struct vty_app_info *app_info;
+};
+
+/* There are some command levels which called from command node. */
+enum node_type {
+       AUTH_NODE,              /* Authentication mode of vty interface. */
+       VIEW_NODE,              /* View node. Default mode of vty interface. */
+       AUTH_ENABLE_NODE,       /* Authentication mode for change enable. */
+       ENABLE_NODE,            /* Enable node. */
+       CONFIG_NODE,            /* Config node. Default mode of config file. */
+       SERVICE_NODE,           /* Service node. */
+       DEBUG_NODE,             /* Debug node. */
+
+       VTY_NODE,               /* Vty node. */
+
+       _LAST_OSMOVTY_NODE
+};
+
+/* Node which has some commands and prompt string and configuration
+   function pointer . */
+struct cmd_node {
+       /* Node index. */
+       enum node_type node;
+
+       /* Prompt character at vty interface. */
+       const char *prompt;
+
+       /* Is this node's configuration goes to vtysh ? */
+       int vtysh;
+
+       /* Node's configuration write function */
+       int (*func) (struct vty *);
+
+       /* Vector of this node's command list. */
+       vector cmd_vector;
+};
+
+enum {
+       CMD_ATTR_DEPRECATED = 1,
+       CMD_ATTR_HIDDEN,
+};
+
+/* Structure of command element. */
+struct cmd_element {
+       const char *string;     /* Command specification by string. */
+       int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
+       const char *doc;        /* Documentation of this command. */
+       int daemon;             /* Daemon to which this command belong. */
+       vector strvec;          /* Pointing out each description vector. */
+       unsigned int cmdsize;   /* Command index count. */
+       char *config;           /* Configuration string */
+       vector subconfig;       /* Sub configuration string */
+       u_char attr;            /* Command attributes */
+};
+
+/* Command description structure. */
+struct desc {
+       const char *cmd;        /* Command string. */
+       const char *str;        /* Command's description. */
+};
+
+/* Return value of the commands. */
+#define CMD_SUCCESS              0
+#define CMD_WARNING              1
+#define CMD_ERR_NO_MATCH         2
+#define CMD_ERR_AMBIGUOUS        3
+#define CMD_ERR_INCOMPLETE       4
+#define CMD_ERR_EXEED_ARGC_MAX   5
+#define CMD_ERR_NOTHING_TODO     6
+#define CMD_COMPLETE_FULL_MATCH  7
+#define CMD_COMPLETE_MATCH       8
+#define CMD_COMPLETE_LIST_MATCH  9
+#define CMD_SUCCESS_DAEMON      10
+
+/* Argc max counts. */
+#define CMD_ARGC_MAX   25
+
+/* Turn off these macros when uisng cpp with extract.pl */
+#ifndef VTYSH_EXTRACT_PL
+
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+  static struct cmd_element cmdname = \
+  { \
+    .string = cmdstr, \
+    .func = funcname, \
+    .doc = helpstr, \
+    .attr = attrs, \
+    .daemon = dnum, \
+  };
+
+/* global (non static) cmd_element */
+#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+  struct cmd_element cmdname = \
+  { \
+    .string = cmdstr, \
+    .func = funcname, \
+    .doc = helpstr, \
+    .attr = attrs, \
+    .daemon = dnum, \
+  };
+
+#define DEFUN_CMD_FUNC_DECL(funcname) \
+  static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \
+
+#define DEFUN_CMD_FUNC_TEXT(funcname) \
+  static int funcname \
+    (struct cmd_element *self, struct vty *vty, int argc, const char *argv[])
+
+/* DEFUN for vty command interafce. Little bit hacky ;-). */
+#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* global (non static) cmd_element */
+#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+
+/* DEFUN_NOSH for commands that vtysh should ignore */
+#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN(funcname, cmdname, cmdstr, helpstr)
+
+/* DEFSH for vtysh. */
+#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+
+/* DEFUN + DEFSH */
+#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* DEFUN + DEFSH with attributes */
+#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
+  DEFUN_CMD_FUNC_DECL(funcname) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+  DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+/* ALIAS macro which define existing command's alias. */
+#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+/* global (non static) cmd_element */
+#define gALIAS(funcname, cmdname, cmdstr, helpstr) \
+  gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+
+#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+
+#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+
+#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+
+#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+  DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+
+#endif                         /* VTYSH_EXTRACT_PL */
+
+/* Some macroes */
+#define CMD_OPTION(S)   ((S[0]) == '[')
+#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
+#define CMD_VARARG(S)   ((S[0]) == '.')
+#define CMD_RANGE(S)   ((S[0] == '<'))
+
+#define CMD_IPV4(S)       ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S)        ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+
+/* Common descriptions. */
+#define SHOW_STR "Show running system information\n"
+#define IP_STR "IP information\n"
+#define IPV6_STR "IPv6 information\n"
+#define NO_STR "Negate a command or set its defaults\n"
+#define CLEAR_STR "Reset functions\n"
+#define RIP_STR "RIP information\n"
+#define BGP_STR "BGP information\n"
+#define OSPF_STR "OSPF information\n"
+#define NEIGHBOR_STR "Specify neighbor router\n"
+#define DEBUG_STR "Debugging functions (see also 'undebug')\n"
+#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
+#define ROUTER_STR "Enable a routing process\n"
+#define AS_STR "AS number\n"
+#define MBGP_STR "MBGP information\n"
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+#define OUT_STR "Filter outgoing routing updates\n"
+#define IN_STR  "Filter incoming routing updates\n"
+#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n"
+#define OSPF6_NUMBER_STR "Specify by number\n"
+#define INTERFACE_STR "Interface infomation\n"
+#define IFNAME_STR "Interface name(e.g. ep0)\n"
+#define IP6_STR "IPv6 Information\n"
+#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"
+#define OSPF6_ROUTER_STR "Enable a routing process\n"
+#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n"
+#define SECONDS_STR "<1-65535> Seconds\n"
+#define ROUTE_STR "Routing Table\n"
+#define PREFIX_LIST_STR "Build a prefix list\n"
+#define OSPF6_DUMP_TYPE_LIST \
+"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
+#define ISIS_STR "IS-IS information\n"
+#define AREA_TAG_STR "[area tag]\n"
+
+#define CONF_BACKUP_EXT ".sav"
+
+/* IPv4 only machine should not accept IPv6 address for peer's IP
+   address.  So we replace VTY command string like below. */
+#ifdef HAVE_IPV6
+#define NEIGHBOR_CMD       "neighbor (A.B.C.D|X:X::X:X) "
+#define NO_NEIGHBOR_CMD    "no neighbor (A.B.C.D|X:X::X:X) "
+#define NEIGHBOR_ADDR_STR  "Neighbor address\nIPv6 address\n"
+#define NEIGHBOR_CMD2      "neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NO_NEIGHBOR_CMD2   "no neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n"
+#else
+#define NEIGHBOR_CMD       "neighbor A.B.C.D "
+#define NO_NEIGHBOR_CMD    "no neighbor A.B.C.D "
+#define NEIGHBOR_ADDR_STR  "Neighbor address\n"
+#define NEIGHBOR_CMD2      "neighbor (A.B.C.D|WORD) "
+#define NO_NEIGHBOR_CMD2   "no neighbor (A.B.C.D|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"
+#endif                         /* HAVE_IPV6 */
+
+/* Prototypes. */
+void install_node(struct cmd_node *, int (*)(struct vty *));
+void install_default(enum node_type);
+void install_element(enum node_type, struct cmd_element *);
+void install_element_ve(struct cmd_element *cmd);
+void sort_node();
+
+/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
+   string with a space between each element (allocated using
+   XMALLOC(MTYPE_TMP)).  Returns NULL if shift >= argc. */
+char *argv_concat(const char **argv, int argc, int shift);
+
+vector cmd_make_strvec(const char *);
+void cmd_free_strvec(vector);
+vector cmd_describe_command();
+char **cmd_complete_command();
+const char *cmd_prompt(enum node_type);
+int config_from_file(struct vty *, FILE *);
+enum node_type node_parent(enum node_type);
+int cmd_execute_command(vector, struct vty *, struct cmd_element **, int);
+int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **);
+void config_replace_string(struct cmd_element *, char *, ...);
+void cmd_init(int);
+
+/* Export typical functions. */
+extern struct cmd_element config_exit_cmd;
+extern struct cmd_element config_help_cmd;
+extern struct cmd_element config_list_cmd;
+char *host_config_file();
+void host_config_set(const char *);
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void print_version(int print_copyright);
+
+extern void *tall_vty_cmd_ctx;
+
+#endif                         /* _ZEBRA_COMMAND_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h
new file mode 100644 (file)
index 0000000..f8ffbc3
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _VTY_LOGGING_H
+#define _VTY_LOGGING_H
+
+#define LOGGING_STR    "Configure log message to this terminal\n"
+#define FILTER_STR     "Filter log messages\n"
+
+#endif /* _VTY_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h
new file mode 100644 (file)
index 0000000..444e649
--- /dev/null
@@ -0,0 +1,40 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef TELNET_INTERFACE_H
+#define TELNET_INTERFACE_H
+
+#include <osmocore/logging.h>
+#include <osmocore/select.h>
+
+#include <osmocom/vty/vty.h>
+
+struct telnet_connection {
+       struct llist_head entry;
+       void *priv;
+       struct bsc_fd fd;
+       struct vty *vty;
+       struct log_target *dbg;
+};
+
+
+int telnet_init(void *tall_ctx, void *priv, int port);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h
new file mode 100644 (file)
index 0000000..22a184d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Generic vector interface header.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VECTOR_H
+#define _ZEBRA_VECTOR_H
+
+/* struct for vector */
+struct _vector {
+       unsigned int active;    /* number of active slots */
+       unsigned int alloced;   /* number of allocated slot */
+       void **index;           /* index to data */
+};
+typedef struct _vector *vector;
+
+#define VECTOR_MIN_SIZE 1
+
+/* (Sometimes) usefull macros.  This macro convert index expression to
+ array expression. */
+/* Reference slot at given index, caller must ensure slot is active */
+#define vector_slot(V,I)  ((V)->index[(I)])
+/* Number of active slots.
+ * Note that this differs from vector_count() as it the count returned
+ * will include any empty slots
+ */
+#define vector_active(V) ((V)->active)
+
+/* Prototypes. */
+vector vector_init(unsigned int size);
+void vector_ensure(vector v, unsigned int num);
+int vector_empty_slot(vector v);
+int vector_set(vector v, void *val);
+int vector_set_index(vector v, unsigned int i, void *val);
+void vector_unset(vector v, unsigned int i);
+unsigned int vector_count(vector v);
+void vector_only_wrapper_free(vector v);
+void vector_only_index_free(void *index);
+void vector_free(vector v);
+vector vector_copy(vector v);
+
+void *vector_lookup(vector, unsigned int);
+void *vector_lookup_ensure(vector, unsigned int);
+
+extern void *tall_vty_vec_ctx;
+
+#endif                         /* _ZEBRA_VECTOR_H */
diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h
new file mode 100644 (file)
index 0000000..e7399ba
--- /dev/null
@@ -0,0 +1,159 @@
+#ifndef _VTY_H
+#define _VTY_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* GCC have printf type attribute check.  */
+#ifdef __GNUC__
+#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define VTY_PRINTF_ATTRIBUTE(a,b)
+#endif                         /* __GNUC__ */
+
+/* Does the I/O error indicate that the operation should be retried later? */
+#define ERRNO_IO_RETRY(EN) \
+       (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+/* Vty read buffer size. */
+#define VTY_READ_BUFSIZ 512
+
+#define VTY_BUFSIZ 512
+#define VTY_MAXHIST 20
+
+/* Vty events */
+enum event {
+       VTY_SERV,
+       VTY_READ,
+       VTY_WRITE,
+       VTY_CLOSED,
+       VTY_TIMEOUT_RESET,
+#ifdef VTYSH
+       VTYSH_SERV,
+       VTYSH_READ,
+       VTYSH_WRITE
+#endif                         /* VTYSH */
+};
+
+struct vty {
+       FILE *file;
+
+       /* private data, specified by creator */
+       void *priv;
+
+       /* File descripter of this vty. */
+       int fd;
+
+       /* Is this vty connect to file or not */
+       enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type;
+
+       /* Node status of this vty */
+       int node;
+
+       /* Failure count */
+       int fail;
+
+       /* Output buffer. */
+       struct buffer *obuf;
+
+       /* Command input buffer */
+       char *buf;
+
+       /* Command cursor point */
+       int cp;
+
+       /* Command length */
+       int length;
+
+       /* Command max length. */
+       int max;
+
+       /* Histry of command */
+       char *hist[VTY_MAXHIST];
+
+       /* History lookup current point */
+       int hp;
+
+       /* History insert end point */
+       int hindex;
+
+       /* For current referencing point of interface, route-map,
+          access-list etc... */
+       void *index;
+
+       /* For multiple level index treatment such as key chain and key. */
+       void *index_sub;
+
+       /* For escape character. */
+       unsigned char escape;
+
+       /* Current vty status. */
+       enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
+
+       /* IAC handling: was the last character received the IAC
+        * (interpret-as-command) escape character (and therefore the next
+        * character will be the command code)?  Refer to Telnet RFC 854. */
+       unsigned char iac;
+
+       /* IAC SB (option subnegotiation) handling */
+       unsigned char iac_sb_in_progress;
+       /* At the moment, we care only about the NAWS (window size) negotiation,
+        * and that requires just a 5-character buffer (RFC 1073):
+        * <NAWS char> <16-bit width> <16-bit height> */
+#define TELNET_NAWS_SB_LEN 5
+       unsigned char sb_buf[TELNET_NAWS_SB_LEN];
+       /* How many subnegotiation characters have we received?  We just drop
+        * those that do not fit in the buffer. */
+       size_t sb_len;
+
+       /* Window width/height. */
+       int width;
+       int height;
+
+       /* Configure lines. */
+       int lines;
+
+       int monitor;
+
+       /* In configure mode. */
+       int config;
+};
+
+/* Small macro to determine newline is newline only or linefeed needed. */
+#define VTY_NEWLINE  ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+
+static inline char *vty_newline(struct vty *vty)
+{
+       return VTY_NEWLINE;
+}
+
+struct vty_app_info {
+       const char *name;
+       const char *version;
+       const char *copyright;
+       void *tall_ctx;
+       enum node_type (*go_parent_cb)(struct vty *vty);
+};
+
+/* Prototypes. */
+void vty_init(struct vty_app_info *app_info);
+int vty_read_config_file(const char *file_name, void *priv);
+void vty_init_vtysh (void);
+void vty_reset (void);
+struct vty *vty_new (void);
+struct vty *vty_create (int vty_sock, void *priv);
+int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3);
+int vty_out_newline(struct vty *);
+int vty_read(struct vty *vty);
+//void vty_time_print (struct vty *, int);
+void vty_close (struct vty *);
+char *vty_get_cwd (void);
+void vty_log (const char *level, const char *proto, const char *fmt, va_list);
+int vty_config_lock (struct vty *);
+int vty_config_unlock (struct vty *);
+int vty_shell (struct vty *);
+int vty_shell_serv (struct vty *);
+void vty_hello (struct vty *);
+
+void *tall_vty_ctx;
+#endif
index 56f926b..fde0102 100644 (file)
@@ -1,7 +1,7 @@
 osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
                   tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
                   gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
-                  gsm48_ie.h logging.h gsm0808.h
+                  gsm48_ie.h logging.h gsm0808.h rate_ctr.h
 
 if ENABLE_TALLOC
 osmocore_HEADERS += talloc.h
index a40713f..9166e54 100644 (file)
@@ -36,6 +36,8 @@ struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_
                                                 uint8_t speech_mode);
 struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause);
 
+void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id);
+
 const struct tlv_definition *gsm0808_att_tlvdef();
 
 #endif
index 22749a8..ffe0399 100644 (file)
@@ -1,9 +1,18 @@
 #ifndef _OSMOCORE_GSM48_H
+#define _OSMOCORE_GSM48_H
 
 #include <osmocore/tlv.h>
 #include <osmocore/protocol/gsm_04_08.h>
 #include <osmocore/gsm48_ie.h>
 
+/* A parsed GPRS routing area */
+struct gprs_ra_id {
+       uint16_t        mnc;
+       uint16_t        mcc;
+       uint16_t        lac;
+       uint8_t         rac;
+};
+
 extern const struct tlv_definition gsm48_att_tlvdef;
 extern const struct tlv_definition gsm48_rr_att_tlvdef;
 extern const struct tlv_definition gsm48_mm_att_tlvdef;
@@ -20,4 +29,8 @@ int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
 int gsm48_mi_to_string(char *string, const int str_len,
                        const uint8_t *mi, const int mi_len);
 
+/* Parse Routeing Area Identifier */
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid);
+
 #endif
index 195e865..7dc2388 100644 (file)
@@ -66,6 +66,18 @@ int ms_pwr_dbm(enum gsm_band band, uint8_t lvl);
 int rxlev2dbm(uint8_t rxlev);
 uint8_t dbm2rxlev(int dbm);
 
+/* According to GSM 04.08 Chapter 10.5.1.6 */
+static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) {
+       switch (n) {
+               case 0: return 1;
+               case 1: return (cm2[0] & (1<<3)) ? 0 : 1;
+               case 2: return (cm2[2] & (1<<0)) ? 1 : 0;
+               case 3: return (cm2[2] & (1<<1)) ? 1 : 0;
+               default:
+                       return 0;
+       }
+}
+
 /* According to GSM 04.08 Chapter 10.5.2.29 */
 static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
 static inline int rach_max_trans_raw2val(int raw) {
@@ -87,5 +99,19 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
 /* Convert from GSM time to frame number */
 uint32_t gsm_gsmtime2fn(struct gsm_time *time);
 
+/* GSM TS 03.03 Chapter 2.6 */
+enum gprs_tlli_type {
+       TLLI_LOCAL,
+       TLLI_FOREIGN,
+       TLLI_RANDOM,
+       TLLI_AUXILIARY,
+       TLLI_RESERVED,
+};
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli);
+
+uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type);
+
 void generate_backtrace();
 #endif
index 93f18a0..2e82959 100644 (file)
@@ -117,6 +117,7 @@ void log_set_print_timestamp(struct log_target *target, int);
 void log_set_log_level(struct log_target *target, int log_level);
 void log_parse_category_mask(struct log_target *target, const char* mask);
 int log_parse_level(const char *lvl);
+const char *log_level_str(unsigned int lvl);
 int log_parse_category(const char *category);
 void log_set_category_filter(struct log_target *target, int category,
                               int enable, int level);
@@ -127,4 +128,8 @@ struct log_target *log_target_create_stderr(void);
 void log_add_target(struct log_target *target);
 void log_del_target(struct log_target *target);
 
+/* Gernerate command argument strings for VTY use */
+const char *log_vty_category_string(struct log_info *info);
+const char *log_vty_level_string(struct log_info *info);
+
 #endif /* _OSMOCORE_LOGGING_H */
index 31db719..2841dc5 100644 (file)
 #include <stdint.h>
 #include "linuxlist.h"
 
-struct bts_link;
-
 struct msgb {
        struct llist_head list;
 
-       /* ptr to the physical E1 link to the BTS(s) */
-       struct gsm_bts_link *bts_link;
-
        /* Part of which TRX logical channel we were received / transmitted */
+       /* FIXME: move them into the control buffer */
        struct gsm_bts_trx *trx;
        struct gsm_lchan *lchan;
 
@@ -41,17 +37,11 @@ struct msgb {
        unsigned char *l2h;
        /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
        unsigned char *l3h;
-
        /* the layer 4 header */
-       union {
-               unsigned char *smsh;
-               unsigned char *llch;
-               unsigned char *l4h;
-       };
+       unsigned char *l4h;
 
-       /* the layer 5 header, GPRS: GMM header */
-       unsigned char *gmmh;
-       uint32_t tlli;
+       /* the 'control buffer', large enough to contain 5 pointers */
+       unsigned long cb[5];
 
        uint16_t data_len;
        uint16_t len;
@@ -71,7 +61,7 @@ extern void msgb_reset(struct msgb *m);
 #define msgb_l1(m)     ((void *)(m->l1h))
 #define msgb_l2(m)     ((void *)(m->l2h))
 #define msgb_l3(m)     ((void *)(m->l3h))
-#define msgb_sms(m)    ((void *)(m->smsh))
+#define msgb_sms(m)    ((void *)(m->l4h))
 
 static inline unsigned int msgb_l1len(const struct msgb *msgb)
 {
index 6d8883e..557950e 100644 (file)
@@ -1,3 +1,3 @@
-osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h
+osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h
 
 osmocore_protodir = $(includedir)/osmocore/protocol
index 518aed4..995634c 100644 (file)
@@ -386,6 +386,15 @@ struct gsm48_imm_ass {
        uint8_t mob_alloc[0];
 } __attribute__ ((packed));
 
+/* Chapter 9.1.25 */
+struct gsm48_pag_resp {
+       uint8_t spare:4,
+                key_seq:4;
+       uint32_t classmark2;
+       uint8_t mi_len;
+       uint8_t mi[0];
+} __attribute__ ((packed));
+
 /* Chapter 10.5.1.3 */
 struct gsm48_loc_area_id {
        uint8_t digits[3];      /* BCD! */
@@ -399,6 +408,11 @@ struct gsm48_auth_req {
        uint8_t rand[16];
 } __attribute__ ((packed));
 
+/* Section 9.2.3 */
+struct gsm48_auth_resp {
+       uint8_t sres[4];
+} __attribute__ ((packed));
+
 /* Section 9.2.15 */
 struct gsm48_loc_upd_req {
        uint8_t type:4,
@@ -974,7 +988,17 @@ struct gsm48_rr_status {
 #define GSM48_IE_FOLLOW_ON_PROC        0xa1
 #define GSM48_IE_CTS_PERMISSION        0xa2
 
+#define GSM48_IE_CHANDESC_2    0x64
+#define GSM48_IE_MA_AFTER      0x72
+#define GSM48_IE_START_TIME    0x7c
+#define GSM48_IE_FREQ_L_BEFORE 0x19
+#define GSM48_IE_CH_DESC_1_BEFORE      0x1c
+#define GSM48_IE_CH_DESC_2_BEFORE      0x1d
+#define GSM48_IE_F_CH_SEQ_BEFORE       0x1e
+#define GSM48_IE_MA_BEFORE     0x21
+#define GSM48_IE_VGCS_T_MODE_I 0x01
 
+/* FIXME */
 
 /* Section 10.5.4.23 / Table 10.5.130 */
 enum gsm48_signal_val {
@@ -1205,10 +1229,17 @@ enum gsm48_bcap_rrq {
        GSM48_BCAP_RRQ_DUAL_FR  = 3,
 };
 
-
 #define GSM48_TMSI_LEN 5
 #define GSM48_MID_TMSI_LEN     (GSM48_TMSI_LEN + 2)
 #define GSM48_MI_SIZE 32
 
+/* Chapter 10.4.4.15 */
+struct gsm48_ra_id {
+       uint8_t digits[3];      /* MCC + MNC BCD digits */
+       uint16_t lac;           /* Location Area Code */
+       uint8_t rac;            /* Routing Area Code */
+} __attribute__ ((packed));
+
+
 
 #endif /* PROTO_GSM_04_08_H */
diff --git a/src/shared/libosmocore/include/osmocore/rate_ctr.h b/src/shared/libosmocore/include/osmocore/rate_ctr.h
new file mode 100644 (file)
index 0000000..f887d9a
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef _RATE_CTR_H
+#define _RATE_CTR_H
+
+#include <stdint.h>
+
+#include <osmocore/linuxlist.h>
+
+#define RATE_CTR_INTV_NUM      4
+
+enum rate_ctr_intv {
+       RATE_CTR_INTV_SEC,
+       RATE_CTR_INTV_MIN,
+       RATE_CTR_INTV_HOUR,
+       RATE_CTR_INTV_DAY,
+};
+
+/* for each of the intervals, we keep the following values */
+struct rate_ctr_per_intv {
+       uint64_t last;
+       uint64_t rate;
+};
+
+/* for each actual value, we keep the following data */
+struct rate_ctr {
+       uint64_t current;
+       struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM];
+};
+
+struct rate_ctr_desc {
+       const char *name;
+       const char *description;
+};
+
+/* Describe a counter group class */
+struct rate_ctr_group_desc {
+       /* The prefix to the name of all counters in this group */
+       const char *group_name_prefix;
+       /* The human-readable description of the group */
+       const char *group_description;
+       /* The number of counters in this group */
+       const unsigned int num_ctr;
+       /* Pointer to array of counter names */
+       const struct rate_ctr_desc *ctr_desc;
+};
+
+/* One instance of a counter group class */
+struct rate_ctr_group {
+       /* Linked list of all counter groups in the system */
+       struct llist_head list;
+       /* Pointer to the counter group class */
+       const struct rate_ctr_group_desc *desc;
+       /* The index of this ctr_group within its class */
+       unsigned int idx;
+       /* Actual counter structures below */
+       struct rate_ctr ctr[0];
+};
+
+/* Allocate a new group of counters according to description */
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+                                           const struct rate_ctr_group_desc *desc,
+                                           unsigned int idx);
+
+/* Free the memory for the specified group of counters */
+void rate_ctr_group_free(struct rate_ctr_group *grp);
+
+/* Add a number to the counter */
+void rate_ctr_add(struct rate_ctr *ctr, int inc);
+
+/* Increment the counter by 1 */
+static inline void rate_ctr_inc(struct rate_ctr *ctr)
+{
+       rate_ctr_add(ctr, 1);
+}
+
+/* Initialize the counter module */
+int rate_ctr_init(void *tall_ctx);
+
+struct vty;
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+                           struct rate_ctr_group *ctrg);
+#endif /* RATE_CTR_H */
diff --git a/src/shared/libosmocore/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in
new file mode 100644 (file)
index 0000000..2cc0b5f
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom VTY Interface Library
+Description: C Utility Library
+Version: @VERSION@
+Libs: -L${libdir} -losmovty
+Cflags: -I${includedir}/
+
index 20e99db..1a7d87f 100644 (file)
@@ -1,3 +1,5 @@
+SUBDIRS=vty
+
 # This is _NOT_ the library release version, it's an API version.
 # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
 LIBVERSION=0:0:0
@@ -10,7 +12,7 @@ lib_LTLIBRARIES = libosmocore.la
 libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
                         tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
                         write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
-                        logging.c gsm0808.c
+                        logging.c gsm0808.c rate_ctr.c
 
 if ENABLE_TALLOC
 libosmocore_la_SOURCES += talloc.c
index 7a7eb3a..1dc035b 100644 (file)
@@ -266,6 +266,14 @@ struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause)
        return msg;
 }
 
+void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id)
+{
+       uint8_t *hh = msgb_push(msg, 3);
+       hh[0] = BSSAP_MSG_DTAP;
+       hh[1] = link_id;
+       hh[2] = msg->len - 3;
+}
+
 static const struct tlv_definition bss_att_tlvdef = {
        .def = {
                [GSM0808_IE_IMSI]                   = { TLV_TYPE_TLV },
@@ -286,6 +294,9 @@ static const struct tlv_definition bss_att_tlvdef = {
                [GSM0808_IE_SERVICE_HANDOVER]       = { TLV_TYPE_TV},
                [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
                [GSM0808_IE_CIPHER_RESPONSE_MODE]   = { TLV_TYPE_TV },
+               [GSM0808_IE_CELL_IDENTIFIER]        = { TLV_TYPE_TLV },
+               [GSM0808_IE_CHOSEN_CHANNEL]         = { TLV_TYPE_TV },
+               [GSM0808_IE_LAYER_3_INFORMATION]    = { TLV_TYPE_TLV },
        },
 };
 
index 6a7c39e..1f0852f 100644 (file)
@@ -367,3 +367,49 @@ int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi,
 
        return str_cur - string;
 }
+
+void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
+{
+       raid->mcc = (buf[0] & 0xf) * 100;
+       raid->mcc += (buf[0] >> 4) * 10;
+       raid->mcc += (buf[1] & 0xf) * 1;
+
+       /* I wonder who came up with the stupidity of encoding the MNC
+        * differently depending on how many digits its decimal number has! */
+       if ((buf[1] >> 4) == 0xf) {
+               raid->mnc = (buf[2] & 0xf) * 10;
+               raid->mnc += (buf[2] >> 4) * 1;
+       } else {
+               raid->mnc = (buf[2] & 0xf) * 100;
+               raid->mnc += (buf[2] >> 4) * 10;
+               raid->mnc += (buf[1] >> 4) * 1;
+       }
+
+       raid->lac = ntohs(*(uint16_t *)(buf + 3));
+       raid->rac = buf[5];
+}
+
+int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
+{
+       uint16_t mcc = raid->mcc;
+       uint16_t mnc = raid->mnc;
+
+       buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
+       buf[1] = (mcc % 10);
+
+       /* I wonder who came up with the stupidity of encoding the MNC
+        * differently depending on how many digits its decimal number has! */
+       if (mnc < 100) {
+               buf[1] |= 0xf0;
+               buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
+       } else {
+               buf[1] |= (mnc % 10) << 4;
+               buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
+       }
+
+       *(uint16_t *)(buf+3) = htons(raid->lac);
+
+       buf[5] = raid->rac;
+
+       return 6;
+}
index 593dd5c..913946e 100644 (file)
@@ -359,3 +359,35 @@ uint32_t gsm_gsmtime2fn(struct gsm_time *time)
        /* TS 05.02 Chapter 4.3.3 TDMA frame number */
        return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
 }
+
+/* TS 03.03 Chapter 2.6 */
+int gprs_tlli_type(uint32_t tlli)
+{
+       if ((tlli & 0xc0000000) == 0xc0000000)
+               return TLLI_LOCAL;
+       else if ((tlli & 0xc0000000) == 0x80000000)
+               return TLLI_FOREIGN;
+       else if ((tlli & 0xf8000000) == 0x78000000)
+               return TLLI_RANDOM;
+       else if ((tlli & 0xf8000000) == 0x70000000)
+               return TLLI_AUXILIARY;
+
+       return TLLI_RESERVED;
+}
+
+uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type)
+{
+       uint32_t tlli;
+       switch (type) {
+       case TLLI_LOCAL:
+               tlli = p_tmsi | 0xc0000000;
+               break;
+       case TLLI_FOREIGN:
+               tlli = (p_tmsi & 0x3fffffff) | 0x80000000;
+               break;
+       default:
+               tlli = 0;
+               break;
+       }
+       return tlli;
+}
index 7c50877..1dc30db 100644 (file)
@@ -37,7 +37,7 @@
 #include <osmocore/utils.h>
 #include <osmocore/logging.h>
 
-static const struct log_info *log_info;
+const struct log_info *osmo_log_info;
 
 static struct log_context log_context;
 static void *tall_log_ctx = NULL;
@@ -58,12 +58,17 @@ int log_parse_level(const char *lvl)
        return get_string_value(loglevel_strs, lvl);
 }
 
+const char *log_level_str(unsigned int lvl)
+{
+       return get_value_string(loglevel_strs, lvl);
+}
+
 int log_parse_category(const char *category)
 {
        int i;
 
-       for (i = 0; i < log_info->num_cat; ++i) {
-               if (!strcasecmp(log_info->cat[i].name+1, category))
+       for (i = 0; i < osmo_log_info->num_cat; ++i) {
+               if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
                        return i;
        }
 
@@ -87,15 +92,15 @@ void log_parse_category_mask(struct log_target* target, const char *_mask)
 
        category_token = strtok(mask, ":");
        do {
-               for (i = 0; i < log_info->num_cat; ++i) {
+               for (i = 0; i < osmo_log_info->num_cat; ++i) {
                        char* colon = strstr(category_token, ",");
                        int length = strlen(category_token);
 
                        if (colon)
                            length = colon - category_token;
 
-                       if (strncasecmp(log_info->cat[i].name, category_token,
-                                       length) == 0) {
+                       if (strncasecmp(osmo_log_info->cat[i].name,
+                                       category_token, length) == 0) {
                                int level = 0;
 
                                if (colon)
@@ -112,8 +117,8 @@ void log_parse_category_mask(struct log_target* target, const char *_mask)
 
 static const char* color(int subsys)
 {
-       if (subsys < log_info->num_cat)
-               return log_info->cat[subsys].color;
+       if (subsys < osmo_log_info->num_cat)
+               return osmo_log_info->cat[subsys].color;
 
        return NULL;
 }
@@ -193,8 +198,8 @@ static void _logp(unsigned int subsys, int level, char *file, int line,
                 * say stop, continue, output */
                if ((tar->filter_map & LOG_FILTER_ALL) != 0)
                        output = 1;
-               else if (log_info->filter_fn)
-                       output = log_info->filter_fn(&log_context,
+               else if (osmo_log_info->filter_fn)
+                       output = osmo_log_info->filter_fn(&log_context,
                                                       tar);
 
                if (output) {
@@ -301,7 +306,7 @@ void log_set_log_level(struct log_target *target, int log_level)
 void log_set_category_filter(struct log_target *target, int category,
                               int enable, int level)
 {
-       if (category >= log_info->num_cat)
+       if (category >= osmo_log_info->num_cat)
                return;
        target->categories[category].enabled = !!enable;
        target->categories[category].loglevel = level;
@@ -328,10 +333,10 @@ struct log_target *log_target_create(void)
        INIT_LLIST_HEAD(&target->entry);
 
        /* initialize the per-category enabled/loglevel from defaults */
-       for (i = 0; i < log_info->num_cat; i++) {
+       for (i = 0; i < osmo_log_info->num_cat; i++) {
                struct log_category *cat = &target->categories[i];
-               cat->enabled = log_info->cat[i].enabled;
-               cat->loglevel = log_info->cat[i].loglevel;
+               cat->enabled = osmo_log_info->cat[i].enabled;
+               cat->loglevel = osmo_log_info->cat[i].loglevel;
        }
 
        /* global settings */
@@ -361,8 +366,54 @@ struct log_target *log_target_create_stderr(void)
 #endif /* stderr */
 }
 
+const char *log_vty_level_string(struct log_info *info)
+{
+       const struct value_string *vs;
+       unsigned int len = 3; /* ()\0 */
+       char *str;
+
+       for (vs = loglevel_strs; vs->value || vs->str; vs++)
+               len += strlen(vs->str) + 1;
+
+       str = talloc_zero_size(NULL, len);
+       if (!str)
+               return NULL;
+
+       str[0] = '(';
+       for (vs = loglevel_strs; vs->value || vs->str; vs++) {
+               strcat(str, vs->str);
+               strcat(str, "|");
+       }
+       str[strlen(str)-1] = ')';
+
+       return str;
+}
+
+const char *log_vty_category_string(struct log_info *info)
+{
+       unsigned int len = 3;   /* "()\0" */
+       unsigned int i;
+       char *str;
+
+       for (i = 0; i < info->num_cat; i++)
+               len += strlen(info->cat[i].name) + 1;
+
+       str = talloc_zero_size(NULL, len);
+       if (!str)
+               return NULL;
+
+       str[0] = '(';
+       for (i = 0; i < info->num_cat; i++) {
+               strcat(str, info->cat[i].name+1);
+               strcat(str, "|");
+       }
+       str[strlen(str)-1] = ')';
+
+       return str;
+}
+
 void log_init(const struct log_info *cat)
 {
        tall_log_ctx = talloc_named_const(NULL, 1, "logging");
-       log_info = cat;
+       osmo_log_info = cat;
 }
index 60af373..a60e2ff 100644 (file)
@@ -80,10 +80,11 @@ void msgb_reset(struct msgb *msg)
        msg->head = msg->_data;
        msg->tail = msg->_data;
 
-       msg->bts_link = NULL;
        msg->trx = NULL;
        msg->lchan = NULL;
        msg->l2h = NULL;
        msg->l3h = NULL;
-       msg->smsh = NULL;
+       msg->l4h = NULL;
+
+       memset(&msg->cb, 0, sizeof(msg->cb));
 }
diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c
new file mode 100644 (file)
index 0000000..f58b5c4
--- /dev/null
@@ -0,0 +1,128 @@
+/* utility routines for keeping conters about events and the event rates */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <osmocore/utils.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/rate_ctr.h>
+
+static LLIST_HEAD(rate_ctr_groups);
+
+static void *tall_rate_ctr_ctx;
+
+struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
+                                           const struct rate_ctr_group_desc *desc,
+                                           unsigned int idx)
+{
+       unsigned int size;
+       struct rate_ctr_group *group;
+
+       size = sizeof(struct rate_ctr_group) +
+                       desc->num_ctr * sizeof(struct rate_ctr);
+
+       if (!ctx)
+               ctx = tall_rate_ctr_ctx;
+
+       group = talloc_zero_size(ctx, size);
+       if (!group)
+               return NULL;
+
+       group->desc = desc;
+       group->idx = idx;
+
+       llist_add(&group->list, &rate_ctr_groups);
+
+       return group;
+}
+
+void rate_ctr_group_free(struct rate_ctr_group *grp)
+{
+       llist_del(&grp->list);
+       talloc_free(grp);
+}
+
+void rate_ctr_add(struct rate_ctr *ctr, int inc)
+{
+       ctr->current += inc;
+}
+
+static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
+{
+       /* calculate rate over last interval */
+       ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
+       /* save current counter for next interval */
+       ctr->intv[intv].last = ctr->current;
+
+       /* update the rate of the next bigger interval.  This will
+        * be overwritten when that next larger interval expires */
+       if (intv + 1 < ARRAY_SIZE(ctr->intv))
+               ctr->intv[intv+1].rate += ctr->intv[intv].rate;
+}
+
+static struct timer_list rate_ctr_timer;
+static uint64_t timer_ticks;
+
+/* The one-second interval has expired */
+static void rate_ctr_group_intv(struct rate_ctr_group *grp)
+{
+       unsigned int i;
+
+       for (i = 0; i < grp->desc->num_ctr; i++) {
+               struct rate_ctr *ctr = &grp->ctr[i];
+
+               interval_expired(ctr, RATE_CTR_INTV_SEC);
+               if ((timer_ticks % 60) == 0)
+                       interval_expired(ctr, RATE_CTR_INTV_MIN);
+               if ((timer_ticks % (60*60)) == 0)
+                       interval_expired(ctr, RATE_CTR_INTV_HOUR);
+               if ((timer_ticks % (24*60*60)) == 0)
+                       interval_expired(ctr, RATE_CTR_INTV_DAY);
+       }
+}
+
+static void rate_ctr_timer_cb(void *data)
+{
+       struct rate_ctr_group *ctrg;
+
+       /* Increment number of ticks before we calculate intervals,
+        * as a counter value of 0 would already wrap all counters */
+       timer_ticks++;
+
+       llist_for_each_entry(ctrg, &rate_ctr_groups, list)
+               rate_ctr_group_intv(ctrg);
+
+       bsc_schedule_timer(&rate_ctr_timer, 1, 0);
+}
+
+int rate_ctr_init(void *tall_ctx)
+{
+       tall_rate_ctr_ctx = tall_ctx;
+       rate_ctr_timer.cb = rate_ctr_timer_cb;
+       bsc_schedule_timer(&rate_ctr_timer, 1, 0);
+
+       return 0;
+}
diff --git a/src/shared/libosmocore/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am
new file mode 100644 (file)
index 0000000..f2859cf
--- /dev/null
@@ -0,0 +1,13 @@
+# This is _NOT_ the library release version, it's an API version.
+# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
+LIBVERSION=0:0:0
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS = -fPIC -Wall
+
+if ENABLE_VTY
+lib_LTLIBRARIES = libosmovty.la
+
+libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
+                       telnet_interface.c logging_vty.c
+endif
diff --git a/src/shared/libosmocore/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c
new file mode 100644 (file)
index 0000000..a5655b9
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * Buffering of output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/uio.h>
+
+#include <osmocore/talloc.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+
+/* Buffer master. */
+struct buffer {
+       /* Data list. */
+       struct buffer_data *head;
+       struct buffer_data *tail;
+
+       /* Size of each buffer_data chunk. */
+       size_t size;
+};
+
+/* Data container. */
+struct buffer_data {
+       struct buffer_data *next;
+
+       /* Location to add new data. */
+       size_t cp;
+
+       /* Pointer to data not yet flushed. */
+       size_t sp;
+
+       /* Actual data stream (variable length). */
+       unsigned char data[0];  /* real dimension is buffer->size */
+};
+
+/* It should always be true that: 0 <= sp <= cp <= size */
+
+/* Default buffer size (used if none specified).  It is rounded up to the
+   next page boundery. */
+#define BUFFER_SIZE_DEFAULT            4096
+
+#define BUFFER_DATA_FREE(D) talloc_free((D))
+
+/* Make new buffer. */
+struct buffer *buffer_new(void *ctx, size_t size)
+{
+       struct buffer *b;
+
+       b = talloc_zero(ctx, struct buffer);
+
+       if (size)
+               b->size = size;
+       else {
+               static size_t default_size;
+               if (!default_size) {
+                       long pgsz = sysconf(_SC_PAGESIZE);
+                       default_size =
+                           ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
+               }
+               b->size = default_size;
+       }
+
+       return b;
+}
+
+/* Free buffer. */
+void buffer_free(struct buffer *b)
+{
+       buffer_reset(b);
+       talloc_free(b);
+}
+
+/* Make string clone. */
+char *buffer_getstr(struct buffer *b)
+{
+       size_t totlen = 0;
+       struct buffer_data *data;
+       char *s;
+       char *p;
+
+       for (data = b->head; data; data = data->next)
+               totlen += data->cp - data->sp;
+       if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr")))
+               return NULL;
+       p = s;
+       for (data = b->head; data; data = data->next) {
+               memcpy(p, data->data + data->sp, data->cp - data->sp);
+               p += data->cp - data->sp;
+       }
+       *p = '\0';
+       return s;
+}
+
+/* Return 1 if buffer is empty. */
+int buffer_empty(struct buffer *b)
+{
+       return (b->head == NULL);
+}
+
+/* Clear and free all allocated data. */
+void buffer_reset(struct buffer *b)
+{
+       struct buffer_data *data;
+       struct buffer_data *next;
+
+       for (data = b->head; data; data = next) {
+               next = data->next;
+               BUFFER_DATA_FREE(data);
+       }
+       b->head = b->tail = NULL;
+}
+
+/* Add buffer_data to the end of buffer. */
+static struct buffer_data *buffer_add(struct buffer *b)
+{
+       struct buffer_data *d;
+
+       d = _talloc_zero(b,
+                        offsetof(struct buffer_data, data[b->size]),
+                        "buffer_add");
+       if (!d)
+               return NULL;
+       d->cp = d->sp = 0;
+       d->next = NULL;
+
+       if (b->tail)
+               b->tail->next = d;
+       else
+               b->head = d;
+       b->tail = d;
+
+       return d;
+}
+
+/* Write data to buffer. */
+void buffer_put(struct buffer *b, const void *p, size_t size)
+{
+       struct buffer_data *data = b->tail;
+       const char *ptr = p;
+
+       /* We use even last one byte of data buffer. */
+       while (size) {
+               size_t chunk;
+
+               /* If there is no data buffer add it. */
+               if (data == NULL || data->cp == b->size)
+                       data = buffer_add(b);
+
+               chunk =
+                   ((size <=
+                     (b->size - data->cp)) ? size : (b->size - data->cp));
+               memcpy((data->data + data->cp), ptr, chunk);
+               size -= chunk;
+               ptr += chunk;
+               data->cp += chunk;
+       }
+}
+
+/* Insert character into the buffer. */
+void buffer_putc(struct buffer *b, u_char c)
+{
+       buffer_put(b, &c, 1);
+}
+
+/* Put string to the buffer. */
+void buffer_putstr(struct buffer *b, const char *c)
+{
+       buffer_put(b, c, strlen(c));
+}
+
+/* Keep flushing data to the fd until the buffer is empty or an error is
+   encountered or the operation would block. */
+buffer_status_t buffer_flush_all(struct buffer *b, int fd)
+{
+       buffer_status_t ret;
+       struct buffer_data *head;
+       size_t head_sp;
+
+       if (!b->head)
+               return BUFFER_EMPTY;
+       head_sp = (head = b->head)->sp;
+       /* Flush all data. */
+       while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
+               if ((b->head == head) && (head_sp == head->sp)
+                   && (errno != EINTR))
+                       /* No data was flushed, so kernel buffer must be full. */
+                       return ret;
+               head_sp = (head = b->head)->sp;
+       }
+
+       return ret;
+}
+
+#if 0
+/* Flush enough data to fill a terminal window of the given scene (used only
+   by vty telnet interface). */
+buffer_status_t
+buffer_flush_window(struct buffer * b, int fd, int width, int height,
+                   int erase_flag, int no_more_flag)
+{
+       int nbytes;
+       int iov_alloc;
+       int iov_index;
+       struct iovec *iov;
+       struct iovec small_iov[3];
+       char more[] = " --More-- ";
+       char erase[] =
+           { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+               ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+               0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+       };
+       struct buffer_data *data;
+       int column;
+
+       if (!b->head)
+               return BUFFER_EMPTY;
+
+       if (height < 1) {
+               zlog_warn
+                   ("%s called with non-positive window height %d, forcing to 1",
+                    __func__, height);
+               height = 1;
+       } else if (height >= 2)
+               height--;
+       if (width < 1) {
+               zlog_warn
+                   ("%s called with non-positive window width %d, forcing to 1",
+                    __func__, width);
+               width = 1;
+       }
+
+       /* For erase and more data add two to b's buffer_data count. */
+       if (b->head->next == NULL) {
+               iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
+               iov = small_iov;
+       } else {
+               iov_alloc = ((height * (width + 2)) / b->size) + 10;
+               iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
+       }
+       iov_index = 0;
+
+       /* Previously print out is performed. */
+       if (erase_flag) {
+               iov[iov_index].iov_base = erase;
+               iov[iov_index].iov_len = sizeof erase;
+               iov_index++;
+       }
+
+       /* Output data. */
+       column = 1;             /* Column position of next character displayed. */
+       for (data = b->head; data && (height > 0); data = data->next) {
+               size_t cp;
+
+               cp = data->sp;
+               while ((cp < data->cp) && (height > 0)) {
+                       /* Calculate lines remaining and column position after displaying
+                          this character. */
+                       if (data->data[cp] == '\r')
+                               column = 1;
+                       else if ((data->data[cp] == '\n') || (column == width)) {
+                               column = 1;
+                               height--;
+                       } else
+                               column++;
+                       cp++;
+               }
+               iov[iov_index].iov_base = (char *)(data->data + data->sp);
+               iov[iov_index++].iov_len = cp - data->sp;
+               data->sp = cp;
+
+               if (iov_index == iov_alloc)
+                       /* This should not ordinarily happen. */
+               {
+                       iov_alloc *= 2;
+                       if (iov != small_iov) {
+                               zlog_warn("%s: growing iov array to %d; "
+                                         "width %d, height %d, size %lu",
+                                         __func__, iov_alloc, width, height,
+                                         (u_long) b->size);
+                               iov =
+                                   XREALLOC(MTYPE_TMP, iov,
+                                            iov_alloc * sizeof(*iov));
+                       } else {
+                               /* This should absolutely never occur. */
+                               zlog_err
+                                   ("%s: corruption detected: iov_small overflowed; "
+                                    "head %p, tail %p, head->next %p",
+                                    __func__, b->head, b->tail, b->head->next);
+                               iov =
+                                   XMALLOC(MTYPE_TMP,
+                                           iov_alloc * sizeof(*iov));
+                               memcpy(iov, small_iov, sizeof(small_iov));
+                       }
+               }
+       }
+
+       /* In case of `more' display need. */
+       if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
+               iov[iov_index].iov_base = more;
+               iov[iov_index].iov_len = sizeof more;
+               iov_index++;
+       }
+#ifdef IOV_MAX
+       /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
+          example: Solaris2.6 are defined IOV_MAX size at 16.     */
+       {
+               struct iovec *c_iov = iov;
+               nbytes = 0;     /* Make sure it's initialized. */
+
+               while (iov_index > 0) {
+                       int iov_size;
+
+                       iov_size =
+                           ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
+                       if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+                               zlog_warn("%s: writev to fd %d failed: %s",
+                                         __func__, fd, safe_strerror(errno));
+                               break;
+                       }
+
+                       /* move pointer io-vector */
+                       c_iov += iov_size;
+                       iov_index -= iov_size;
+               }
+       }
+#else                          /* IOV_MAX */
+       if ((nbytes = writev(fd, iov, iov_index)) < 0)
+               zlog_warn("%s: writev to fd %d failed: %s",
+                         __func__, fd, safe_strerror(errno));
+#endif                         /* IOV_MAX */
+
+       /* Free printed buffer data. */
+       while (b->head && (b->head->sp == b->head->cp)) {
+               struct buffer_data *del;
+               if (!(b->head = (del = b->head)->next))
+                       b->tail = NULL;
+               BUFFER_DATA_FREE(del);
+       }
+
+       if (iov != small_iov)
+               XFREE(MTYPE_TMP, iov);
+
+       return (nbytes < 0) ? BUFFER_ERROR :
+           (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
+}
+#endif
+
+/* This function (unlike other buffer_flush* functions above) is designed
+to work with non-blocking sockets.  It does not attempt to write out
+all of the queued data, just a "big" chunk.  It returns 0 if it was
+able to empty out the buffers completely, 1 if more flushing is
+required later, or -1 on a fatal write error. */
+buffer_status_t buffer_flush_available(struct buffer * b, int fd)
+{
+
+/* These are just reasonable values to make sure a significant amount of
+data is written.  There's no need to go crazy and try to write it all
+in one shot. */
+#ifdef IOV_MAX
+#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
+#else
+#define MAX_CHUNKS 16
+#endif
+#define MAX_FLUSH 131072
+
+       struct buffer_data *d;
+       size_t written;
+       struct iovec iov[MAX_CHUNKS];
+       size_t iovcnt = 0;
+       size_t nbyte = 0;
+
+       for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
+            d = d->next, iovcnt++) {
+               iov[iovcnt].iov_base = d->data + d->sp;
+               nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
+       }
+
+       if (!nbyte)
+               /* No data to flush: should we issue a warning message? */
+               return BUFFER_EMPTY;
+
+       /* only place where written should be sign compared */
+       if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
+               if (ERRNO_IO_RETRY(errno))
+                       /* Calling code should try again later. */
+                       return BUFFER_PENDING;
+               return BUFFER_ERROR;
+       }
+
+       /* Free printed buffer data. */
+       while (written > 0) {
+               struct buffer_data *d;
+               if (!(d = b->head))
+                       break;
+               if (written < d->cp - d->sp) {
+                       d->sp += written;
+                       return BUFFER_PENDING;
+               }
+
+               written -= (d->cp - d->sp);
+               if (!(b->head = d->next))
+                       b->tail = NULL;
+               BUFFER_DATA_FREE(d);
+       }
+
+       return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+
+#undef MAX_CHUNKS
+#undef MAX_FLUSH
+}
+
+buffer_status_t
+buffer_write(struct buffer * b, int fd, const void *p, size_t size)
+{
+       ssize_t nbytes;
+
+#if 0
+       /* Should we attempt to drain any previously buffered data?  This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
+
+       if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
+               return BUFFER_ERROR;
+#endif
+       if (b->head)
+               /* Buffer is not empty, so do not attempt to write the new data. */
+               nbytes = 0;
+       else if ((nbytes = write(fd, p, size)) < 0) {
+               if (ERRNO_IO_RETRY(errno))
+                       nbytes = 0;
+               else
+                       return BUFFER_ERROR;
+       }
+       /* Add any remaining data to the buffer. */
+       {
+               size_t written = nbytes;
+               if (written < size)
+                       buffer_put(b, ((const char *)p) + written,
+                                  size - written);
+       }
+       return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+}
diff --git a/src/shared/libosmocore/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c
new file mode 100644 (file)
index 0000000..21afa5c
--- /dev/null
@@ -0,0 +1,3177 @@
+/*
+   $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
+
+   Command interpreter routine for virtual terminal [aka TeletYpe]
+   Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published
+by the Free Software Foundation; either version 2, or (at your
+option) any later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#define _XOPEN_SOURCE
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <osmocom/vty/vector.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+
+#include <osmocore/talloc.h>
+
+#define CONFIGFILE_MASK 022
+
+void *tall_vty_cmd_ctx;
+
+/* Command vector which includes some level of command lists. Normally
+   each daemon maintains each own cmdvec. */
+vector cmdvec;
+
+/* Host information structure. */
+struct host host;
+
+/* Standard command node structures. */
+struct cmd_node auth_node = {
+       AUTH_NODE,
+       "Password: ",
+};
+
+struct cmd_node view_node = {
+       VIEW_NODE,
+       "%s> ",
+};
+
+struct cmd_node auth_enable_node = {
+       AUTH_ENABLE_NODE,
+       "Password: ",
+};
+
+struct cmd_node enable_node = {
+       ENABLE_NODE,
+       "%s# ",
+};
+
+struct cmd_node config_node = {
+       CONFIG_NODE,
+       "%s(config)# ",
+       1
+};
+
+/* Default motd string. */
+const char *default_motd = "";
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void print_version(int print_copyright)
+{
+       printf("%s version %s\n", host.app_info->name, host.app_info->version);
+       if (print_copyright)
+               printf("\n%s\n", host.app_info->copyright);
+}
+
+/* Utility function to concatenate argv argument into a single string
+   with inserting ' ' character between each argument.  */
+char *argv_concat(const char **argv, int argc, int shift)
+{
+       int i;
+       size_t len;
+       char *str;
+       char *p;
+
+       len = 0;
+       for (i = shift; i < argc; i++)
+               len += strlen(argv[i]) + 1;
+       if (!len)
+               return NULL;
+       p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
+       for (i = shift; i < argc; i++) {
+               size_t arglen;
+               memcpy(p, argv[i], (arglen = strlen(argv[i])));
+               p += arglen;
+               *p++ = ' ';
+       }
+       *(p - 1) = '\0';
+       return str;
+}
+
+/* Install top node of command vector. */
+void install_node(struct cmd_node *node, int (*func) (struct vty *))
+{
+       vector_set_index(cmdvec, node->node, node);
+       node->func = func;
+       node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
+}
+
+/* Compare two command's string.  Used in sort_node (). */
+static int cmp_node(const void *p, const void *q)
+{
+       struct cmd_element *a = *(struct cmd_element **)p;
+       struct cmd_element *b = *(struct cmd_element **)q;
+
+       return strcmp(a->string, b->string);
+}
+
+static int cmp_desc(const void *p, const void *q)
+{
+       struct desc *a = *(struct desc **)p;
+       struct desc *b = *(struct desc **)q;
+
+       return strcmp(a->cmd, b->cmd);
+}
+
+/* Sort each node's command element according to command string. */
+void sort_node()
+{
+       unsigned int i, j;
+       struct cmd_node *cnode;
+       vector descvec;
+       struct cmd_element *cmd_element;
+
+       for (i = 0; i < vector_active(cmdvec); i++)
+               if ((cnode = vector_slot(cmdvec, i)) != NULL) {
+                       vector cmd_vector = cnode->cmd_vector;
+                       qsort(cmd_vector->index, vector_active(cmd_vector),
+                             sizeof(void *), cmp_node);
+
+                       for (j = 0; j < vector_active(cmd_vector); j++)
+                               if ((cmd_element =
+                                    vector_slot(cmd_vector, j)) != NULL
+                                   && vector_active(cmd_element->strvec)) {
+                                       descvec =
+                                           vector_slot(cmd_element->strvec,
+                                                       vector_active
+                                                       (cmd_element->strvec) -
+                                                       1);
+                                       qsort(descvec->index,
+                                             vector_active(descvec),
+                                             sizeof(void *), cmp_desc);
+                               }
+               }
+}
+
+/* Breaking up string into each command piece. I assume given
+   character is separated by a space character. Return value is a
+   vector which includes char ** data element. */
+vector cmd_make_strvec(const char *string)
+{
+       const char *cp, *start;
+       char *token;
+       int strlen;
+       vector strvec;
+
+       if (string == NULL)
+               return NULL;
+
+       cp = string;
+
+       /* Skip white spaces. */
+       while (isspace((int)*cp) && *cp != '\0')
+               cp++;
+
+       /* Return if there is only white spaces */
+       if (*cp == '\0')
+               return NULL;
+
+       if (*cp == '!' || *cp == '#')
+               return NULL;
+
+       /* Prepare return vector. */
+       strvec = vector_init(VECTOR_MIN_SIZE);
+
+       /* Copy each command piece and set into vector. */
+       while (1) {
+               start = cp;
+               while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
+                      *cp != '\0')
+                       cp++;
+               strlen = cp - start;
+               token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
+               memcpy(token, start, strlen);
+               *(token + strlen) = '\0';
+               vector_set(strvec, token);
+
+               while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
+                      *cp != '\0')
+                       cp++;
+
+               if (*cp == '\0')
+                       return strvec;
+       }
+}
+
+/* Free allocated string vector. */
+void cmd_free_strvec(vector v)
+{
+       unsigned int i;
+       char *cp;
+
+       if (!v)
+               return;
+
+       for (i = 0; i < vector_active(v); i++)
+               if ((cp = vector_slot(v, i)) != NULL)
+                       talloc_free(cp);
+
+       vector_free(v);
+}
+
+/* Fetch next description.  Used in cmd_make_descvec(). */
+static char *cmd_desc_str(const char **string)
+{
+       const char *cp, *start;
+       char *token;
+       int strlen;
+
+       cp = *string;
+
+       if (cp == NULL)
+               return NULL;
+
+       /* Skip white spaces. */
+       while (isspace((int)*cp) && *cp != '\0')
+               cp++;
+
+       /* Return if there is only white spaces */
+       if (*cp == '\0')
+               return NULL;
+
+       start = cp;
+
+       while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
+               cp++;
+
+       strlen = cp - start;
+       token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
+       memcpy(token, start, strlen);
+       *(token + strlen) = '\0';
+
+       *string = cp;
+
+       return token;
+}
+
+/* New string vector. */
+static vector cmd_make_descvec(const char *string, const char *descstr)
+{
+       int multiple = 0;
+       const char *sp;
+       char *token;
+       int len;
+       const char *cp;
+       const char *dp;
+       vector allvec;
+       vector strvec = NULL;
+       struct desc *desc;
+
+       cp = string;
+       dp = descstr;
+
+       if (cp == NULL)
+               return NULL;
+
+       allvec = vector_init(VECTOR_MIN_SIZE);
+
+       while (1) {
+               while (isspace((int)*cp) && *cp != '\0')
+                       cp++;
+
+               if (*cp == '(') {
+                       multiple = 1;
+                       cp++;
+               }
+               if (*cp == ')') {
+                       multiple = 0;
+                       cp++;
+               }
+               if (*cp == '|') {
+                       if (!multiple) {
+                               fprintf(stderr, "Command parse error!: %s\n",
+                                       string);
+                               exit(1);
+                       }
+                       cp++;
+               }
+
+               while (isspace((int)*cp) && *cp != '\0')
+                       cp++;
+
+               if (*cp == '(') {
+                       multiple = 1;
+                       cp++;
+               }
+
+               if (*cp == '\0')
+                       return allvec;
+
+               sp = cp;
+
+               while (!
+                      (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
+                       || *cp == ')' || *cp == '|') && *cp != '\0')
+                       cp++;
+
+               len = cp - sp;
+
+               token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
+               memcpy(token, sp, len);
+               *(token + len) = '\0';
+
+               desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
+               desc->cmd = token;
+               desc->str = cmd_desc_str(&dp);
+
+               if (multiple) {
+                       if (multiple == 1) {
+                               strvec = vector_init(VECTOR_MIN_SIZE);
+                               vector_set(allvec, strvec);
+                       }
+                       multiple++;
+               } else {
+                       strvec = vector_init(VECTOR_MIN_SIZE);
+                       vector_set(allvec, strvec);
+               }
+               vector_set(strvec, desc);
+       }
+}
+
+/* Count mandantory string vector size.  This is to determine inputed
+   command has enough command length. */
+static int cmd_cmdsize(vector strvec)
+{
+       unsigned int i;
+       int size = 0;
+       vector descvec;
+       struct desc *desc;
+
+       for (i = 0; i < vector_active(strvec); i++)
+               if ((descvec = vector_slot(strvec, i)) != NULL) {
+                       if ((vector_active(descvec)) == 1
+                           && (desc = vector_slot(descvec, 0)) != NULL) {
+                               if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
+                                       return size;
+                               else
+                                       size++;
+                       } else
+                               size++;
+               }
+       return size;
+}
+
+/* Return prompt character of specified node. */
+const char *cmd_prompt(enum node_type node)
+{
+       struct cmd_node *cnode;
+
+       cnode = vector_slot(cmdvec, node);
+       return cnode->prompt;
+}
+
+/* Install a command into a node. */
+void install_element(enum node_type ntype, struct cmd_element *cmd)
+{
+       struct cmd_node *cnode;
+
+       cnode = vector_slot(cmdvec, ntype);
+
+       if (cnode == NULL) {
+               fprintf(stderr,
+                       "Command node %d doesn't exist, please check it\n",
+                       ntype);
+               exit(1);
+       }
+
+       vector_set(cnode->cmd_vector, cmd);
+
+       cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
+       cmd->cmdsize = cmd_cmdsize(cmd->strvec);
+}
+
+/* Install a command into VIEW and ENABLE node */
+void install_element_ve(struct cmd_element *cmd)
+{
+       install_element(VIEW_NODE, cmd);
+       install_element(ENABLE_NODE, cmd);
+}
+
+#ifdef VTY_CRYPT_PW
+static unsigned char itoa64[] =
+    "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, long v, int n)
+{
+       while (--n >= 0) {
+               *s++ = itoa64[v & 0x3f];
+               v >>= 6;
+       }
+}
+
+static char *zencrypt(const char *passwd)
+{
+       char salt[6];
+       struct timeval tv;
+       char *crypt(const char *, const char *);
+
+       gettimeofday(&tv, 0);
+
+       to64(&salt[0], random(), 3);
+       to64(&salt[3], tv.tv_usec, 3);
+       salt[5] = '\0';
+
+       return crypt(passwd, salt);
+}
+#endif
+
+/* This function write configuration of this host. */
+static int config_write_host(struct vty *vty)
+{
+       if (host.name)
+               vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
+
+       if (host.encrypt) {
+               if (host.password_encrypt)
+                       vty_out(vty, "password 8 %s%s", host.password_encrypt,
+                               VTY_NEWLINE);
+               if (host.enable_encrypt)
+                       vty_out(vty, "enable password 8 %s%s",
+                               host.enable_encrypt, VTY_NEWLINE);
+       } else {
+               if (host.password)
+                       vty_out(vty, "password %s%s", host.password,
+                               VTY_NEWLINE);
+               if (host.enable)
+                       vty_out(vty, "enable password %s%s", host.enable,
+                               VTY_NEWLINE);
+       }
+
+       if (host.advanced)
+               vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
+
+       if (host.encrypt)
+               vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
+
+       if (host.lines >= 0)
+               vty_out(vty, "service terminal-length %d%s", host.lines,
+                       VTY_NEWLINE);
+
+       if (host.motdfile)
+               vty_out(vty, "banner motd file %s%s", host.motdfile,
+                       VTY_NEWLINE);
+       else if (!host.motd)
+               vty_out(vty, "no banner motd%s", VTY_NEWLINE);
+
+       return 1;
+}
+
+/* Utility function for getting command vector. */
+static vector cmd_node_vector(vector v, enum node_type ntype)
+{
+       struct cmd_node *cnode = vector_slot(v, ntype);
+       return cnode->cmd_vector;
+}
+
+/* Completion match types. */
+enum match_type {
+       no_match,
+       extend_match,
+       ipv4_prefix_match,
+       ipv4_match,
+       ipv6_prefix_match,
+       ipv6_match,
+       range_match,
+       vararg_match,
+       partly_match,
+       exact_match
+};
+
+static enum match_type cmd_ipv4_match(const char *str)
+{
+       const char *sp;
+       int dots = 0, nums = 0;
+       char buf[4];
+
+       if (str == NULL)
+               return partly_match;
+
+       for (;;) {
+               memset(buf, 0, sizeof(buf));
+               sp = str;
+               while (*str != '\0') {
+                       if (*str == '.') {
+                               if (dots >= 3)
+                                       return no_match;
+
+                               if (*(str + 1) == '.')
+                                       return no_match;
+
+                               if (*(str + 1) == '\0')
+                                       return partly_match;
+
+                               dots++;
+                               break;
+                       }
+                       if (!isdigit((int)*str))
+                               return no_match;
+
+                       str++;
+               }
+
+               if (str - sp > 3)
+                       return no_match;
+
+               strncpy(buf, sp, str - sp);
+               if (atoi(buf) > 255)
+                       return no_match;
+
+               nums++;
+
+               if (*str == '\0')
+                       break;
+
+               str++;
+       }
+
+       if (nums < 4)
+               return partly_match;
+
+       return exact_match;
+}
+
+static enum match_type cmd_ipv4_prefix_match(const char *str)
+{
+       const char *sp;
+       int dots = 0;
+       char buf[4];
+
+       if (str == NULL)
+               return partly_match;
+
+       for (;;) {
+               memset(buf, 0, sizeof(buf));
+               sp = str;
+               while (*str != '\0' && *str != '/') {
+                       if (*str == '.') {
+                               if (dots == 3)
+                                       return no_match;
+
+                               if (*(str + 1) == '.' || *(str + 1) == '/')
+                                       return no_match;
+
+                               if (*(str + 1) == '\0')
+                                       return partly_match;
+
+                               dots++;
+                               break;
+                       }
+
+                       if (!isdigit((int)*str))
+                               return no_match;
+
+                       str++;
+               }
+
+               if (str - sp > 3)
+                       return no_match;
+
+               strncpy(buf, sp, str - sp);
+               if (atoi(buf) > 255)
+                       return no_match;
+
+               if (dots == 3) {
+                       if (*str == '/') {
+                               if (*(str + 1) == '\0')
+                                       return partly_match;
+
+                               str++;
+                               break;
+                       } else if (*str == '\0')
+                               return partly_match;
+               }
+
+               if (*str == '\0')
+                       return partly_match;
+
+               str++;
+       }
+
+       sp = str;
+       while (*str != '\0') {
+               if (!isdigit((int)*str))
+                       return no_match;
+
+               str++;
+       }
+
+       if (atoi(sp) > 32)
+               return no_match;
+
+       return exact_match;
+}
+
+#define IPV6_ADDR_STR          "0123456789abcdefABCDEF:.%"
+#define IPV6_PREFIX_STR                "0123456789abcdefABCDEF:.%/"
+#define STATE_START            1
+#define STATE_COLON            2
+#define STATE_DOUBLE           3
+#define STATE_ADDR             4
+#define STATE_DOT               5
+#define STATE_SLASH            6
+#define STATE_MASK             7
+
+#ifdef HAVE_IPV6
+
+static enum match_type cmd_ipv6_match(const char *str)
+{
+       int state = STATE_START;
+       int colons = 0, nums = 0, double_colon = 0;
+       const char *sp = NULL;
+       struct sockaddr_in6 sin6_dummy;
+       int ret;
+
+       if (str == NULL)
+               return partly_match;
+
+       if (strspn(str, IPV6_ADDR_STR) != strlen(str))
+               return no_match;
+
+       /* use inet_pton that has a better support,
+        * for example inet_pton can support the automatic addresses:
+        *  ::1.2.3.4
+        */
+       ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+
+       if (ret == 1)
+               return exact_match;
+
+       while (*str != '\0') {
+               switch (state) {
+               case STATE_START:
+                       if (*str == ':') {
+                               if (*(str + 1) != ':' && *(str + 1) != '\0')
+                                       return no_match;
+                               colons--;
+                               state = STATE_COLON;
+                       } else {
+                               sp = str;
+                               state = STATE_ADDR;
+                       }
+
+                       continue;
+               case STATE_COLON:
+                       colons++;
+                       if (*(str + 1) == ':')
+                               state = STATE_DOUBLE;
+                       else {
+                               sp = str + 1;
+                               state = STATE_ADDR;
+                       }
+                       break;
+               case STATE_DOUBLE:
+                       if (double_colon)
+                               return no_match;
+
+                       if (*(str + 1) == ':')
+                               return no_match;
+                       else {
+                               if (*(str + 1) != '\0')
+                                       colons++;
+                               sp = str + 1;
+                               state = STATE_ADDR;
+                       }
+
+                       double_colon++;
+                       nums++;
+                       break;
+               case STATE_ADDR:
+                       if (*(str + 1) == ':' || *(str + 1) == '\0') {
+                               if (str - sp > 3)
+                                       return no_match;
+
+                               nums++;
+                               state = STATE_COLON;
+                       }
+                       if (*(str + 1) == '.')
+                               state = STATE_DOT;
+                       break;
+               case STATE_DOT:
+                       state = STATE_ADDR;
+                       break;
+               default:
+                       break;
+               }
+
+               if (nums > 8)
+                       return no_match;
+
+               if (colons > 7)
+                       return no_match;
+
+               str++;
+       }
+
+#if 0
+       if (nums < 11)
+               return partly_match;
+#endif                         /* 0 */
+
+       return exact_match;
+}
+
+static enum match_type cmd_ipv6_prefix_match(const char *str)
+{
+       int state = STATE_START;
+       int colons = 0, nums = 0, double_colon = 0;
+       int mask;
+       const char *sp = NULL;
+       char *endptr = NULL;
+
+       if (str == NULL)
+               return partly_match;
+
+       if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
+               return no_match;
+
+       while (*str != '\0' && state != STATE_MASK) {
+               switch (state) {
+               case STATE_START:
+                       if (*str == ':') {
+                               if (*(str + 1) != ':' && *(str + 1) != '\0')
+                                       return no_match;
+                               colons--;
+                               state = STATE_COLON;
+                       } else {
+                               sp = str;
+                               state = STATE_ADDR;
+                       }
+
+                       continue;
+               case STATE_COLON:
+                       colons++;
+                       if (*(str + 1) == '/')
+                               return no_match;
+                       else if (*(str + 1) == ':')
+                               state = STATE_DOUBLE;
+                       else {
+                               sp = str + 1;
+                               state = STATE_ADDR;
+                       }
+                       break;
+               case STATE_DOUBLE:
+                       if (double_colon)
+                               return no_match;
+
+                       if (*(str + 1) == ':')
+                               return no_match;
+                       else {
+                               if (*(str + 1) != '\0' && *(str + 1) != '/')
+                                       colons++;
+                               sp = str + 1;
+
+                               if (*(str + 1) == '/')
+                                       state = STATE_SLASH;
+                               else
+                                       state = STATE_ADDR;
+                       }
+
+                       double_colon++;
+                       nums += 1;
+                       break;
+               case STATE_ADDR:
+                       if (*(str + 1) == ':' || *(str + 1) == '.'
+                           || *(str + 1) == '\0' || *(str + 1) == '/') {
+                               if (str - sp > 3)
+                                       return no_match;
+
+                               for (; sp <= str; sp++)
+                                       if (*sp == '/')
+                                               return no_match;
+
+                               nums++;
+
+                               if (*(str + 1) == ':')
+                                       state = STATE_COLON;
+                               else if (*(str + 1) == '.')
+                                       state = STATE_DOT;
+                               else if (*(str + 1) == '/')
+                                       state = STATE_SLASH;
+                       }
+                       break;
+               case STATE_DOT:
+                       state = STATE_ADDR;
+                       break;
+               case STATE_SLASH:
+                       if (*(str + 1) == '\0')
+                               return partly_match;
+
+                       state = STATE_MASK;
+                       break;
+               default:
+                       break;
+               }
+
+               if (nums > 11)
+                       return no_match;
+
+               if (colons > 7)
+                       return no_match;
+
+               str++;
+       }
+
+       if (state < STATE_MASK)
+               return partly_match;
+
+       mask = strtol(str, &endptr, 10);
+       if (*endptr != '\0')
+               return no_match;
+
+       if (mask < 0 || mask > 128)
+               return no_match;
+
+/* I don't know why mask < 13 makes command match partly.
+   Forgive me to make this comments. I Want to set static default route
+   because of lack of function to originate default in ospf6d; sorry
+       yasu
+  if (mask < 13)
+    return partly_match;
+*/
+
+       return exact_match;
+}
+
+#endif                         /* HAVE_IPV6  */
+
+#define DECIMAL_STRLEN_MAX 10
+
+static int cmd_range_match(const char *range, const char *str)
+{
+       char *p;
+       char buf[DECIMAL_STRLEN_MAX + 1];
+       char *endptr = NULL;
+       unsigned long min, max, val;
+
+       if (str == NULL)
+               return 1;
+
+       val = strtoul(str, &endptr, 10);
+       if (*endptr != '\0')
+               return 0;
+
+       range++;
+       p = strchr(range, '-');
+       if (p == NULL)
+               return 0;
+       if (p - range > DECIMAL_STRLEN_MAX)
+               return 0;
+       strncpy(buf, range, p - range);
+       buf[p - range] = '\0';
+       min = strtoul(buf, &endptr, 10);
+       if (*endptr != '\0')
+               return 0;
+
+       range = p + 1;
+       p = strchr(range, '>');
+       if (p == NULL)
+               return 0;
+       if (p - range > DECIMAL_STRLEN_MAX)
+               return 0;
+       strncpy(buf, range, p - range);
+       buf[p - range] = '\0';
+       max = strtoul(buf, &endptr, 10);
+       if (*endptr != '\0')
+               return 0;
+
+       if (val < min || val > max)
+               return 0;
+
+       return 1;
+}
+
+/* Make completion match and return match type flag. */
+static enum match_type
+cmd_filter_by_completion(char *command, vector v, unsigned int index)
+{
+       unsigned int i;
+       const char *str;
+       struct cmd_element *cmd_element;
+       enum match_type match_type;
+       vector descvec;
+       struct desc *desc;
+
+       match_type = no_match;
+
+       /* If command and cmd_element string does not match set NULL to vector */
+       for (i = 0; i < vector_active(v); i++)
+               if ((cmd_element = vector_slot(v, i)) != NULL) {
+                       if (index >= vector_active(cmd_element->strvec))
+                               vector_slot(v, i) = NULL;
+                       else {
+                               unsigned int j;
+                               int matched = 0;
+
+                               descvec =
+                                   vector_slot(cmd_element->strvec, index);
+
+                               for (j = 0; j < vector_active(descvec); j++)
+                                       if ((desc = vector_slot(descvec, j))) {
+                                               str = desc->cmd;
+
+                                               if (CMD_VARARG(str)) {
+                                                       if (match_type <
+                                                           vararg_match)
+                                                               match_type =
+                                                                   vararg_match;
+                                                       matched++;
+                                               } else if (CMD_RANGE(str)) {
+                                                       if (cmd_range_match
+                                                           (str, command)) {
+                                                               if (match_type <
+                                                                   range_match)
+                                                                       match_type
+                                                                           =
+                                                                           range_match;
+
+                                                               matched++;
+                                                       }
+                                               }
+#ifdef HAVE_IPV6
+                                               else if (CMD_IPV6(str)) {
+                                                       if (cmd_ipv6_match
+                                                           (command)) {
+                                                               if (match_type <
+                                                                   ipv6_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv6_match;
+
+                                                               matched++;
+                                                       }
+                                               } else if (CMD_IPV6_PREFIX(str)) {
+                                                       if (cmd_ipv6_prefix_match(command)) {
+                                                               if (match_type <
+                                                                   ipv6_prefix_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv6_prefix_match;
+
+                                                               matched++;
+                                                       }
+                                               }
+#endif                         /* HAVE_IPV6  */
+                                               else if (CMD_IPV4(str)) {
+                                                       if (cmd_ipv4_match
+                                                           (command)) {
+                                                               if (match_type <
+                                                                   ipv4_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv4_match;
+
+                                                               matched++;
+                                                       }
+                                               } else if (CMD_IPV4_PREFIX(str)) {
+                                                       if (cmd_ipv4_prefix_match(command)) {
+                                                               if (match_type <
+                                                                   ipv4_prefix_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv4_prefix_match;
+                                                               matched++;
+                                                       }
+                                               } else
+                                                       /* Check is this point's argument optional ? */
+                                               if (CMD_OPTION(str)
+                                                           ||
+                                                           CMD_VARIABLE(str)) {
+                                                       if (match_type <
+                                                           extend_match)
+                                                               match_type =
+                                                                   extend_match;
+                                                       matched++;
+                                               } else
+                                                   if (strncmp
+                                                       (command, str,
+                                                        strlen(command)) ==
+                                                       0) {
+                                                       if (strcmp(command, str)
+                                                           == 0)
+                                                               match_type =
+                                                                   exact_match;
+                                                       else {
+                                                               if (match_type <
+                                                                   partly_match)
+                                                                       match_type
+                                                                           =
+                                                                           partly_match;
+                                                       }
+                                                       matched++;
+                                               }
+                                       }
+                               if (!matched)
+                                       vector_slot(v, i) = NULL;
+                       }
+               }
+       return match_type;
+}
+
+/* Filter vector by command character with index. */
+static enum match_type
+cmd_filter_by_string(char *command, vector v, unsigned int index)
+{
+       unsigned int i;
+       const char *str;
+       struct cmd_element *cmd_element;
+       enum match_type match_type;
+       vector descvec;
+       struct desc *desc;
+
+       match_type = no_match;
+
+       /* If command and cmd_element string does not match set NULL to vector */
+       for (i = 0; i < vector_active(v); i++)
+               if ((cmd_element = vector_slot(v, i)) != NULL) {
+                       /* If given index is bigger than max string vector of command,
+                          set NULL */
+                       if (index >= vector_active(cmd_element->strvec))
+                               vector_slot(v, i) = NULL;
+                       else {
+                               unsigned int j;
+                               int matched = 0;
+
+                               descvec =
+                                   vector_slot(cmd_element->strvec, index);
+
+                               for (j = 0; j < vector_active(descvec); j++)
+                                       if ((desc = vector_slot(descvec, j))) {
+                                               str = desc->cmd;
+
+                                               if (CMD_VARARG(str)) {
+                                                       if (match_type <
+                                                           vararg_match)
+                                                               match_type =
+                                                                   vararg_match;
+                                                       matched++;
+                                               } else if (CMD_RANGE(str)) {
+                                                       if (cmd_range_match
+                                                           (str, command)) {
+                                                               if (match_type <
+                                                                   range_match)
+                                                                       match_type
+                                                                           =
+                                                                           range_match;
+                                                               matched++;
+                                                       }
+                                               }
+#ifdef HAVE_IPV6
+                                               else if (CMD_IPV6(str)) {
+                                                       if (cmd_ipv6_match
+                                                           (command) ==
+                                                           exact_match) {
+                                                               if (match_type <
+                                                                   ipv6_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv6_match;
+                                                               matched++;
+                                                       }
+                                               } else if (CMD_IPV6_PREFIX(str)) {
+                                                       if (cmd_ipv6_prefix_match(command) == exact_match) {
+                                                               if (match_type <
+                                                                   ipv6_prefix_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv6_prefix_match;
+                                                               matched++;
+                                                       }
+                                               }
+#endif                         /* HAVE_IPV6  */
+                                               else if (CMD_IPV4(str)) {
+                                                       if (cmd_ipv4_match
+                                                           (command) ==
+                                                           exact_match) {
+                                                               if (match_type <
+                                                                   ipv4_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv4_match;
+                                                               matched++;
+                                                       }
+                                               } else if (CMD_IPV4_PREFIX(str)) {
+                                                       if (cmd_ipv4_prefix_match(command) == exact_match) {
+                                                               if (match_type <
+                                                                   ipv4_prefix_match)
+                                                                       match_type
+                                                                           =
+                                                                           ipv4_prefix_match;
+                                                               matched++;
+                                                       }
+                                               } else if (CMD_OPTION(str)
+                                                          || CMD_VARIABLE(str))
+                                               {
+                                                       if (match_type <
+                                                           extend_match)
+                                                               match_type =
+                                                                   extend_match;
+                                                       matched++;
+                                               } else {
+                                                       if (strcmp(command, str)
+                                                           == 0) {
+                                                               match_type =
+                                                                   exact_match;
+                                                               matched++;
+                                                       }
+                                               }
+                                       }
+                               if (!matched)
+                                       vector_slot(v, i) = NULL;
+                       }
+               }
+       return match_type;
+}
+
+/* Check ambiguous match */
+static int
+is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
+{
+       unsigned int i;
+       unsigned int j;
+       const char *str = NULL;
+       struct cmd_element *cmd_element;
+       const char *matched = NULL;
+       vector descvec;
+       struct desc *desc;
+
+       for (i = 0; i < vector_active(v); i++)
+               if ((cmd_element = vector_slot(v, i)) != NULL) {
+                       int match = 0;
+
+                       descvec = vector_slot(cmd_element->strvec, index);
+
+                       for (j = 0; j < vector_active(descvec); j++)
+                               if ((desc = vector_slot(descvec, j))) {
+                                       enum match_type ret;
+
+                                       str = desc->cmd;
+
+                                       switch (type) {
+                                       case exact_match:
+                                               if (!
+                                                   (CMD_OPTION(str)
+                                                    || CMD_VARIABLE(str))
+&& strcmp(command, str) == 0)
+                                                       match++;
+                                               break;
+                                       case partly_match:
+                                               if (!
+                                                   (CMD_OPTION(str)
+                                                    || CMD_VARIABLE(str))
+&& strncmp(command, str, strlen(command)) == 0) {
+                                                       if (matched
+                                                           && strcmp(matched,
+                                                                     str) != 0)
+                                                               return 1;       /* There is ambiguous match. */
+                                                       else
+                                                               matched = str;
+                                                       match++;
+                                               }
+                                               break;
+                                       case range_match:
+                                               if (cmd_range_match
+                                                   (str, command)) {
+                                                       if (matched
+                                                           && strcmp(matched,
+                                                                     str) != 0)
+                                                               return 1;
+                                                       else
+                                                               matched = str;
+                                                       match++;
+                                               }
+                                               break;
+#ifdef HAVE_IPV6
+                                       case ipv6_match:
+                                               if (CMD_IPV6(str))
+                                                       match++;
+                                               break;
+                                       case ipv6_prefix_match:
+                                               if ((ret =
+                                                    cmd_ipv6_prefix_match
+                                                    (command)) != no_match) {
+                                                       if (ret == partly_match)
+                                                               return 2;       /* There is incomplete match. */
+
+                                                       match++;
+                                               }
+                                               break;
+#endif                         /* HAVE_IPV6 */
+                                       case ipv4_match:
+                                               if (CMD_IPV4(str))
+                                                       match++;
+                                               break;
+                                       case ipv4_prefix_match:
+                                               if ((ret =
+                                                    cmd_ipv4_prefix_match
+                                                    (command)) != no_match) {
+                                                       if (ret == partly_match)
+                                                               return 2;       /* There is incomplete match. */
+
+                                                       match++;
+                                               }
+                                               break;
+                                       case extend_match:
+                                               if (CMD_OPTION(str)
+                                                   || CMD_VARIABLE(str))
+                                                       match++;
+                                               break;
+                                       case no_match:
+                                       default:
+                                               break;
+                                       }
+                               }
+                       if (!match)
+                               vector_slot(v, i) = NULL;
+               }
+       return 0;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+static const char *cmd_entry_function(const char *src, const char *dst)
+{
+       /* Skip variable arguments. */
+       if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
+           CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
+               return NULL;
+
+       /* In case of 'command \t', given src is NULL string. */
+       if (src == NULL)
+               return dst;
+
+       /* Matched with input string. */
+       if (strncmp(src, dst, strlen(src)) == 0)
+               return dst;
+
+       return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+   CMD_VARIABLE for '?' key processing */
+static const char *cmd_entry_function_desc(const char *src, const char *dst)
+{
+       if (CMD_VARARG(dst))
+               return dst;
+
+       if (CMD_RANGE(dst)) {
+               if (cmd_range_match(dst, src))
+                       return dst;
+               else
+                       return NULL;
+       }
+#ifdef HAVE_IPV6
+       if (CMD_IPV6(dst)) {
+               if (cmd_ipv6_match(src))
+                       return dst;
+               else
+                       return NULL;
+       }
+
+       if (CMD_IPV6_PREFIX(dst)) {
+               if (cmd_ipv6_prefix_match(src))
+                       return dst;
+               else
+                       return NULL;
+       }
+#endif                         /* HAVE_IPV6 */
+
+       if (CMD_IPV4(dst)) {
+               if (cmd_ipv4_match(src))
+                       return dst;
+               else
+                       return NULL;
+       }
+
+       if (CMD_IPV4_PREFIX(dst)) {
+               if (cmd_ipv4_prefix_match(src))
+                       return dst;
+               else
+                       return NULL;
+       }
+
+       /* Optional or variable commands always match on '?' */
+       if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
+               return dst;
+
+       /* In case of 'command \t', given src is NULL string. */
+       if (src == NULL)
+               return dst;
+
+       if (strncmp(src, dst, strlen(src)) == 0)
+               return dst;
+       else
+               return NULL;
+}
+
+/* Check same string element existence.  If it isn't there return
+    1. */
+static int cmd_unique_string(vector v, const char *str)
+{
+       unsigned int i;
+       char *match;
+
+       for (i = 0; i < vector_active(v); i++)
+               if ((match = vector_slot(v, i)) != NULL)
+                       if (strcmp(match, str) == 0)
+                               return 0;
+       return 1;
+}
+
+/* Compare string to description vector.  If there is same string
+   return 1 else return 0. */
+static int desc_unique_string(vector v, const char *str)
+{
+       unsigned int i;
+       struct desc *desc;
+
+       for (i = 0; i < vector_active(v); i++)
+               if ((desc = vector_slot(v, i)) != NULL)
+                       if (strcmp(desc->cmd, str) == 0)
+                               return 1;
+       return 0;
+}
+
+static int cmd_try_do_shortcut(enum node_type node, char *first_word)
+{
+       if (first_word != NULL &&
+           node != AUTH_NODE &&
+           node != VIEW_NODE &&
+           node != AUTH_ENABLE_NODE &&
+           node != ENABLE_NODE && 0 == strcmp("do", first_word))
+               return 1;
+       return 0;
+}
+
+/* '?' describe command support. */
+static vector
+cmd_describe_command_real(vector vline, struct vty *vty, int *status)
+{
+       unsigned int i;
+       vector cmd_vector;
+#define INIT_MATCHVEC_SIZE 10
+       vector matchvec;
+       struct cmd_element *cmd_element;
+       unsigned int index;
+       int ret;
+       enum match_type match;
+       char *command;
+       static struct desc desc_cr = { "<cr>", "" };
+
+       /* Set index. */
+       if (vector_active(vline) == 0) {
+               *status = CMD_ERR_NO_MATCH;
+               return NULL;
+       } else
+               index = vector_active(vline) - 1;
+
+       /* Make copy vector of current node's command vector. */
+       cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+       /* Prepare match vector */
+       matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+       /* Filter commands. */
+       /* Only words precedes current word will be checked in this loop. */
+       for (i = 0; i < index; i++)
+               if ((command = vector_slot(vline, i))) {
+                       match =
+                           cmd_filter_by_completion(command, cmd_vector, i);
+
+                       if (match == vararg_match) {
+                               struct cmd_element *cmd_element;
+                               vector descvec;
+                               unsigned int j, k;
+
+                               for (j = 0; j < vector_active(cmd_vector); j++)
+                                       if ((cmd_element =
+                                            vector_slot(cmd_vector, j)) != NULL
+                                           &&
+                                           (vector_active
+                                            (cmd_element->strvec))) {
+                                               descvec =
+                                                   vector_slot(cmd_element->
+                                                               strvec,
+                                                               vector_active
+                                                               (cmd_element->
+                                                                strvec) - 1);
+                                               for (k = 0;
+                                                    k < vector_active(descvec);
+                                                    k++) {
+                                                       struct desc *desc =
+                                                           vector_slot(descvec,
+                                                                       k);
+                                                       vector_set(matchvec,
+                                                                  desc);
+                                               }
+                                       }
+
+                               vector_set(matchvec, &desc_cr);
+                               vector_free(cmd_vector);
+
+                               return matchvec;
+                       }
+
+                       if ((ret =
+                            is_cmd_ambiguous(command, cmd_vector, i,
+                                             match)) == 1) {
+                               vector_free(cmd_vector);
+                               *status = CMD_ERR_AMBIGUOUS;
+                               return NULL;
+                       } else if (ret == 2) {
+                               vector_free(cmd_vector);
+                               *status = CMD_ERR_NO_MATCH;
+                               return NULL;
+                       }
+               }
+
+       /* Prepare match vector */
+       /*  matchvec = vector_init (INIT_MATCHVEC_SIZE); */
+
+       /* Make sure that cmd_vector is filtered based on current word */
+       command = vector_slot(vline, index);
+       if (command)
+               match = cmd_filter_by_completion(command, cmd_vector, index);
+
+       /* Make description vector. */
+       for (i = 0; i < vector_active(cmd_vector); i++)
+               if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) {
+                       const char *string = NULL;
+                       vector strvec = cmd_element->strvec;
+
+                       /* if command is NULL, index may be equal to vector_active */
+                       if (command && index >= vector_active(strvec))
+                               vector_slot(cmd_vector, i) = NULL;
+                       else {
+                               /* Check if command is completed. */
+                               if (command == NULL
+                                   && index == vector_active(strvec)) {
+                                       string = "<cr>";
+                                       if (!desc_unique_string
+                                           (matchvec, string))
+                                               vector_set(matchvec, &desc_cr);
+                               } else {
+                                       unsigned int j;
+                                       vector descvec =
+                                           vector_slot(strvec, index);
+                                       struct desc *desc;
+
+                                       for (j = 0; j < vector_active(descvec);
+                                            j++)
+                                               if ((desc =
+                                                    vector_slot(descvec, j))) {
+                                                       string =
+                                                           cmd_entry_function_desc
+                                                           (command,
+                                                            desc->cmd);
+                                                       if (string) {
+                                                               /* Uniqueness check */
+                                                               if (!desc_unique_string(matchvec, string))
+                                                                       vector_set
+                                                                           (matchvec,
+                                                                            desc);
+                                                       }
+                                               }
+                               }
+                       }
+               }
+       vector_free(cmd_vector);
+
+       if (vector_slot(matchvec, 0) == NULL) {
+               vector_free(matchvec);
+               *status = CMD_ERR_NO_MATCH;
+       } else
+               *status = CMD_SUCCESS;
+
+       return matchvec;
+}
+
+vector cmd_describe_command(vector vline, struct vty * vty, int *status)
+{
+       vector ret;
+
+       if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+               enum node_type onode;
+               vector shifted_vline;
+               unsigned int index;
+
+               onode = vty->node;
+               vty->node = ENABLE_NODE;
+               /* We can try it on enable node, cos' the vty is authenticated */
+
+               shifted_vline = vector_init(vector_count(vline));
+               /* use memcpy? */
+               for (index = 1; index < vector_active(vline); index++) {
+                       vector_set_index(shifted_vline, index - 1,
+                                        vector_lookup(vline, index));
+               }
+
+               ret = cmd_describe_command_real(shifted_vline, vty, status);
+
+               vector_free(shifted_vline);
+               vty->node = onode;
+               return ret;
+       }
+
+       return cmd_describe_command_real(vline, vty, status);
+}
+
+/* Check LCD of matched command. */
+static int cmd_lcd(char **matched)
+{
+       int i;
+       int j;
+       int lcd = -1;
+       char *s1, *s2;
+       char c1, c2;
+
+       if (matched[0] == NULL || matched[1] == NULL)
+               return 0;
+
+       for (i = 1; matched[i] != NULL; i++) {
+               s1 = matched[i - 1];
+               s2 = matched[i];
+
+               for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
+                       if (c1 != c2)
+                               break;
+
+               if (lcd < 0)
+                       lcd = j;
+               else {
+                       if (lcd > j)
+                               lcd = j;
+               }
+       }
+       return lcd;
+}
+
+/* Command line completion support. */
+static char **cmd_complete_command_real(vector vline, struct vty *vty,
+                                       int *status)
+{
+       unsigned int i;
+       vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+#define INIT_MATCHVEC_SIZE 10
+       vector matchvec;
+       struct cmd_element *cmd_element;
+       unsigned int index;
+       char **match_str;
+       struct desc *desc;
+       vector descvec;
+       char *command;
+       int lcd;
+
+       if (vector_active(vline) == 0) {
+               *status = CMD_ERR_NO_MATCH;
+               return NULL;
+       } else
+               index = vector_active(vline) - 1;
+
+       /* First, filter by preceeding command string */
+       for (i = 0; i < index; i++)
+               if ((command = vector_slot(vline, i))) {
+                       enum match_type match;
+                       int ret;
+
+                       /* First try completion match, if there is exactly match return 1 */
+                       match =
+                           cmd_filter_by_completion(command, cmd_vector, i);
+
+                       /* If there is exact match then filter ambiguous match else check
+                          ambiguousness. */
+                       if ((ret =
+                            is_cmd_ambiguous(command, cmd_vector, i,
+                                             match)) == 1) {
+                               vector_free(cmd_vector);
+                               *status = CMD_ERR_AMBIGUOUS;
+                               return NULL;
+                       }
+                       /*
+                          else if (ret == 2)
+                          {
+                          vector_free (cmd_vector);
+                          *status = CMD_ERR_NO_MATCH;
+                          return NULL;
+                          }
+                        */
+               }
+
+       /* Prepare match vector. */
+       matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+       /* Now we got into completion */
+       for (i = 0; i < vector_active(cmd_vector); i++)
+               if ((cmd_element = vector_slot(cmd_vector, i))) {
+                       const char *string;
+                       vector strvec = cmd_element->strvec;
+
+                       /* Check field length */
+                       if (index >= vector_active(strvec))
+                               vector_slot(cmd_vector, i) = NULL;
+                       else {
+                               unsigned int j;
+
+                               descvec = vector_slot(strvec, index);
+                               for (j = 0; j < vector_active(descvec); j++)
+                                       if ((desc = vector_slot(descvec, j))) {
+                                               if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
+                                                       if (cmd_unique_string (matchvec, string))
+                                                               vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
+                                       }
+                       }
+               }
+
+       /* We don't need cmd_vector any more. */
+       vector_free(cmd_vector);
+
+       /* No matched command */
+       if (vector_slot(matchvec, 0) == NULL) {
+               vector_free(matchvec);
+
+               /* In case of 'command \t' pattern.  Do you need '?' command at
+                  the end of the line. */
+               if (vector_slot(vline, index) == '\0')
+                       *status = CMD_ERR_NOTHING_TODO;
+               else
+                       *status = CMD_ERR_NO_MATCH;
+               return NULL;
+       }
+
+       /* Only one matched */
+       if (vector_slot(matchvec, 1) == NULL) {
+               match_str = (char **)matchvec->index;
+               vector_only_wrapper_free(matchvec);
+               *status = CMD_COMPLETE_FULL_MATCH;
+               return match_str;
+       }
+       /* Make it sure last element is NULL. */
+       vector_set(matchvec, NULL);
+
+       /* Check LCD of matched strings. */
+       if (vector_slot(vline, index) != NULL) {
+               lcd = cmd_lcd((char **)matchvec->index);
+
+               if (lcd) {
+                       int len = strlen(vector_slot(vline, index));
+
+                       if (len < lcd) {
+                               char *lcdstr;
+
+                               lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
+                                                     "complete-lcdstr");
+                               memcpy(lcdstr, matchvec->index[0], lcd);
+                               lcdstr[lcd] = '\0';
+
+                               /* match_str = (char **) &lcdstr; */
+
+                               /* Free matchvec. */
+                               for (i = 0; i < vector_active(matchvec); i++) {
+                                       if (vector_slot(matchvec, i))
+                                               talloc_free(vector_slot(matchvec, i));
+                               }
+                               vector_free(matchvec);
+
+                               /* Make new matchvec. */
+                               matchvec = vector_init(INIT_MATCHVEC_SIZE);
+                               vector_set(matchvec, lcdstr);
+                               match_str = (char **)matchvec->index;
+                               vector_only_wrapper_free(matchvec);
+
+                               *status = CMD_COMPLETE_MATCH;
+                               return match_str;
+                       }
+               }
+       }
+
+       match_str = (char **)matchvec->index;
+       vector_only_wrapper_free(matchvec);
+       *status = CMD_COMPLETE_LIST_MATCH;
+       return match_str;
+}
+
+char **cmd_complete_command(vector vline, struct vty *vty, int *status)
+{
+       char **ret;
+
+       if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+               enum node_type onode;
+               vector shifted_vline;
+               unsigned int index;
+
+               onode = vty->node;
+               vty->node = ENABLE_NODE;
+               /* We can try it on enable node, cos' the vty is authenticated */
+
+               shifted_vline = vector_init(vector_count(vline));
+               /* use memcpy? */
+               for (index = 1; index < vector_active(vline); index++) {
+                       vector_set_index(shifted_vline, index - 1,
+                                        vector_lookup(vline, index));
+               }
+
+               ret = cmd_complete_command_real(shifted_vline, vty, status);
+
+               vector_free(shifted_vline);
+               vty->node = onode;
+               return ret;
+       }
+
+       return cmd_complete_command_real(vline, vty, status);
+}
+
+/* return parent node */
+/* MUST eventually converge on CONFIG_NODE */
+enum node_type vty_go_parent(struct vty *vty)
+{
+       assert(vty->node > CONFIG_NODE);
+
+       if (host.app_info->go_parent_cb)
+               host.app_info->go_parent_cb(vty);
+       else
+               vty->node = CONFIG_NODE;
+
+       return vty->node;
+}
+
+/* Execute command by argument vline vector. */
+static int
+cmd_execute_command_real(vector vline, struct vty *vty,
+                        struct cmd_element **cmd)
+{
+       unsigned int i;
+       unsigned int index;
+       vector cmd_vector;
+       struct cmd_element *cmd_element;
+       struct cmd_element *matched_element;
+       unsigned int matched_count, incomplete_count;
+       int argc;
+       const char *argv[CMD_ARGC_MAX];
+       enum match_type match = 0;
+       int varflag;
+       char *command;
+
+       /* Make copy of command elements. */
+       cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+       for (index = 0; index < vector_active(vline); index++)
+               if ((command = vector_slot(vline, index))) {
+                       int ret;
+
+                       match =
+                           cmd_filter_by_completion(command, cmd_vector,
+                                                    index);
+
+                       if (match == vararg_match)
+                               break;
+
+                       ret =
+                           is_cmd_ambiguous(command, cmd_vector, index, match);
+
+                       if (ret == 1) {
+                               vector_free(cmd_vector);
+                               return CMD_ERR_AMBIGUOUS;
+                       } else if (ret == 2) {
+                               vector_free(cmd_vector);
+                               return CMD_ERR_NO_MATCH;
+                       }
+               }
+
+       /* Check matched count. */
+       matched_element = NULL;
+       matched_count = 0;
+       incomplete_count = 0;
+
+       for (i = 0; i < vector_active(cmd_vector); i++)
+               if ((cmd_element = vector_slot(cmd_vector, i))) {
+                       if (match == vararg_match
+                           || index >= cmd_element->cmdsize) {
+                               matched_element = cmd_element;
+#if 0
+                               printf("DEBUG: %s\n", cmd_element->string);
+#endif
+                               matched_count++;
+                       } else {
+                               incomplete_count++;
+                       }
+               }
+
+       /* Finish of using cmd_vector. */
+       vector_free(cmd_vector);
+
+       /* To execute command, matched_count must be 1. */
+       if (matched_count == 0) {
+               if (incomplete_count)
+                       return CMD_ERR_INCOMPLETE;
+               else
+                       return CMD_ERR_NO_MATCH;
+       }
+
+       if (matched_count > 1)
+               return CMD_ERR_AMBIGUOUS;
+
+       /* Argument treatment */
+       varflag = 0;
+       argc = 0;
+
+       for (i = 0; i < vector_active(vline); i++) {
+               if (varflag)
+                       argv[argc++] = vector_slot(vline, i);
+               else {
+                       vector descvec =
+                           vector_slot(matched_element->strvec, i);
+
+                       if (vector_active(descvec) == 1) {
+                               struct desc *desc = vector_slot(descvec, 0);
+
+                               if (CMD_VARARG(desc->cmd))
+                                       varflag = 1;
+
+                               if (varflag || CMD_VARIABLE(desc->cmd)
+                                   || CMD_OPTION(desc->cmd))
+                                       argv[argc++] = vector_slot(vline, i);
+                       } else
+                               argv[argc++] = vector_slot(vline, i);
+               }
+
+               if (argc >= CMD_ARGC_MAX)
+                       return CMD_ERR_EXEED_ARGC_MAX;
+       }
+
+       /* For vtysh execution. */
+       if (cmd)
+               *cmd = matched_element;
+
+       if (matched_element->daemon)
+               return CMD_SUCCESS_DAEMON;
+
+       /* Execute matched command. */
+       return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+int
+cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
+                   int vtysh)
+{
+       int ret, saved_ret, tried = 0;
+       enum node_type onode;
+       void *oindex;
+
+       onode = vty->node;
+       oindex = vty->index;
+
+       if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+               vector shifted_vline;
+               unsigned int index;
+
+               vty->node = ENABLE_NODE;
+               /* We can try it on enable node, cos' the vty is authenticated */
+
+               shifted_vline = vector_init(vector_count(vline));
+               /* use memcpy? */
+               for (index = 1; index < vector_active(vline); index++) {
+                       vector_set_index(shifted_vline, index - 1,
+                                        vector_lookup(vline, index));
+               }
+
+               ret = cmd_execute_command_real(shifted_vline, vty, cmd);
+
+               vector_free(shifted_vline);
+               vty->node = onode;
+               return ret;
+       }
+
+       saved_ret = ret = cmd_execute_command_real(vline, vty, cmd);
+
+       if (vtysh)
+               return saved_ret;
+
+       /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
+       while (ret != CMD_SUCCESS && ret != CMD_WARNING
+              && vty->node > CONFIG_NODE) {
+               vty_go_parent(vty);
+               ret = cmd_execute_command_real(vline, vty, cmd);
+               tried = 1;
+               if (ret == CMD_SUCCESS || ret == CMD_WARNING) {
+                       /* succesfull command, leave the node as is */
+                       return ret;
+               }
+       }
+       /* no command succeeded, reset the vty to the original node and
+          return the error for this node */
+       if (tried) {
+               vty->node = onode;
+               vty->index = oindex;
+       }
+       return saved_ret;
+}
+
+/* Execute command by argument readline. */
+int
+cmd_execute_command_strict(vector vline, struct vty *vty,
+                          struct cmd_element **cmd)
+{
+       unsigned int i;
+       unsigned int index;
+       vector cmd_vector;
+       struct cmd_element *cmd_element;
+       struct cmd_element *matched_element;
+       unsigned int matched_count, incomplete_count;
+       int argc;
+       const char *argv[CMD_ARGC_MAX];
+       int varflag;
+       enum match_type match = 0;
+       char *command;
+
+       /* Make copy of command element */
+       cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+       for (index = 0; index < vector_active(vline); index++)
+               if ((command = vector_slot(vline, index))) {
+                       int ret;
+
+                       match = cmd_filter_by_string(vector_slot(vline, index),
+                                                    cmd_vector, index);
+
+                       /* If command meets '.VARARG' then finish matching. */
+                       if (match == vararg_match)
+                               break;
+
+                       ret =
+                           is_cmd_ambiguous(command, cmd_vector, index, match);
+                       if (ret == 1) {
+                               vector_free(cmd_vector);
+                               return CMD_ERR_AMBIGUOUS;
+                       }
+                       if (ret == 2) {
+                               vector_free(cmd_vector);
+                               return CMD_ERR_NO_MATCH;
+                       }
+               }
+
+       /* Check matched count. */
+       matched_element = NULL;
+       matched_count = 0;
+       incomplete_count = 0;
+       for (i = 0; i < vector_active(cmd_vector); i++)
+               if (vector_slot(cmd_vector, i) != NULL) {
+                       cmd_element = vector_slot(cmd_vector, i);
+
+                       if (match == vararg_match
+                           || index >= cmd_element->cmdsize) {
+                               matched_element = cmd_element;
+                               matched_count++;
+                       } else
+                               incomplete_count++;
+               }
+
+       /* Finish of using cmd_vector. */
+       vector_free(cmd_vector);
+
+       /* To execute command, matched_count must be 1. */
+       if (matched_count == 0) {
+               if (incomplete_count)
+                       return CMD_ERR_INCOMPLETE;
+               else
+                       return CMD_ERR_NO_MATCH;
+       }
+
+       if (matched_count > 1)
+               return CMD_ERR_AMBIGUOUS;
+
+       /* Argument treatment */
+       varflag = 0;
+       argc = 0;
+
+       for (i = 0; i < vector_active(vline); i++) {
+               if (varflag)
+                       argv[argc++] = vector_slot(vline, i);
+               else {
+                       vector descvec =
+                           vector_slot(matched_element->strvec, i);
+
+                       if (vector_active(descvec) == 1) {
+                               struct desc *desc = vector_slot(descvec, 0);
+
+                               if (CMD_VARARG(desc->cmd))
+                                       varflag = 1;
+
+                               if (varflag || CMD_VARIABLE(desc->cmd)
+                                   || CMD_OPTION(desc->cmd))
+                                       argv[argc++] = vector_slot(vline, i);
+                       } else
+                               argv[argc++] = vector_slot(vline, i);
+               }
+
+               if (argc >= CMD_ARGC_MAX)
+                       return CMD_ERR_EXEED_ARGC_MAX;
+       }
+
+       /* For vtysh execution. */
+       if (cmd)
+               *cmd = matched_element;
+
+       if (matched_element->daemon)
+               return CMD_SUCCESS_DAEMON;
+
+       /* Now execute matched command */
+       return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+/* Configration make from file. */
+int config_from_file(struct vty *vty, FILE * fp)
+{
+       int ret;
+       vector vline;
+
+       while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
+               vline = cmd_make_strvec(vty->buf);
+
+               /* In case of comment line */
+               if (vline == NULL)
+                       continue;
+               /* Execute configuration command : this is strict match */
+               ret = cmd_execute_command_strict(vline, vty, NULL);
+
+               /* Try again with setting node to CONFIG_NODE */
+               while (ret != CMD_SUCCESS && ret != CMD_WARNING
+                      && ret != CMD_ERR_NOTHING_TODO
+                      && vty->node != CONFIG_NODE) {
+                       vty_go_parent(vty);
+                       ret = cmd_execute_command_strict(vline, vty, NULL);
+               }
+
+               cmd_free_strvec(vline);
+
+               if (ret != CMD_SUCCESS && ret != CMD_WARNING
+                   && ret != CMD_ERR_NOTHING_TODO)
+                       return ret;
+       }
+       return CMD_SUCCESS;
+}
+
+/* Configration from terminal */
+DEFUN(config_terminal,
+      config_terminal_cmd,
+      "configure terminal",
+      "Configuration from vty interface\n" "Configuration terminal\n")
+{
+       if (vty_config_lock(vty))
+               vty->node = CONFIG_NODE;
+       else {
+               vty_out(vty, "VTY configuration is locked by other VTY%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       return CMD_SUCCESS;
+}
+
+/* Enable command */
+DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
+{
+       /* If enable password is NULL, change to ENABLE_NODE */
+       if ((host.enable == NULL && host.enable_encrypt == NULL) ||
+           vty->type == VTY_SHELL_SERV)
+               vty->node = ENABLE_NODE;
+       else
+               vty->node = AUTH_ENABLE_NODE;
+
+       return CMD_SUCCESS;
+}
+
+/* Disable command */
+DEFUN(disable,
+      config_disable_cmd, "disable", "Turn off privileged mode command\n")
+{
+       if (vty->node == ENABLE_NODE)
+               vty->node = VIEW_NODE;
+       return CMD_SUCCESS;
+}
+
+/* Down vty node level. */
+gDEFUN(config_exit,
+      config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
+{
+       switch (vty->node) {
+       case VIEW_NODE:
+       case ENABLE_NODE:
+               if (0)          //vty_shell (vty))
+                       exit(0);
+               else
+                       vty->status = VTY_CLOSE;
+               break;
+       case CONFIG_NODE:
+               vty->node = ENABLE_NODE;
+               vty_config_unlock(vty);
+               break;
+       case VTY_NODE:
+               vty->node = CONFIG_NODE;
+               break;
+       default:
+               break;
+       }
+       return CMD_SUCCESS;
+}
+
+/* End of configuration. */
+    gDEFUN(config_end,
+      config_end_cmd, "end", "End current mode and change to enable mode.")
+{
+       switch (vty->node) {
+       case VIEW_NODE:
+       case ENABLE_NODE:
+               /* Nothing to do. */
+               break;
+       case CONFIG_NODE:
+       case VTY_NODE:
+               vty_config_unlock(vty);
+               vty->node = ENABLE_NODE;
+               break;
+       default:
+               break;
+       }
+       return CMD_SUCCESS;
+}
+
+/* Show version. */
+DEFUN(show_version,
+      show_version_cmd, "show version", SHOW_STR "Displays program version\n")
+{
+       vty_out(vty, "%s %s (%s).%s", host.app_info->name,
+               host.app_info->version,
+               host.app_info->name ? host.app_info->name : "", VTY_NEWLINE);
+       vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+gDEFUN(config_help,
+      config_help_cmd, "help", "Description of the interactive help system\n")
+{
+       vty_out(vty,
+               "This VTY provides advanced help features.  When you need help,%s\
+anytime at the command line please press '?'.%s\
+%s\
+If nothing matches, the help list will be empty and you must backup%s\
+ until entering a '?' shows the available options.%s\
+Two styles of help are provided:%s\
+1. Full help is available when you are ready to enter a%s\
+command argument (e.g. 'show ?') and describes each possible%s\
+argument.%s\
+2. Partial help is provided when an abbreviated argument is entered%s\
+   and you want to know what arguments match the input%s\
+   (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+               VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+       return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
+{
+       unsigned int i;
+       struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
+       struct cmd_element *cmd;
+
+       for (i = 0; i < vector_active(cnode->cmd_vector); i++)
+               if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
+                   && !(cmd->attr == CMD_ATTR_DEPRECATED
+                        || cmd->attr == CMD_ATTR_HIDDEN))
+                       vty_out(vty, "  %s%s", cmd->string, VTY_NEWLINE);
+       return CMD_SUCCESS;
+}
+
+/* Write current configuration into file. */
+DEFUN(config_write_file,
+      config_write_file_cmd,
+      "write file",
+      "Write running configuration to memory, network, or terminal\n"
+      "Write to configuration file\n")
+{
+       unsigned int i;
+       int fd;
+       struct cmd_node *node;
+       char *config_file;
+       char *config_file_tmp = NULL;
+       char *config_file_sav = NULL;
+       struct vty *file_vty;
+
+       /* Check and see if we are operating under vtysh configuration */
+       if (host.config == NULL) {
+               vty_out(vty, "Can't save to configuration file, using vtysh.%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       /* Get filename. */
+       config_file = host.config;
+
+       config_file_sav =
+           _talloc_zero(tall_vty_cmd_ctx,
+                        strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
+                        "config_file_sav");
+       strcpy(config_file_sav, config_file);
+       strcat(config_file_sav, CONF_BACKUP_EXT);
+
+       config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
+                                       "config_file_tmp");
+       sprintf(config_file_tmp, "%s.XXXXXX", config_file);
+
+       /* Open file to configuration write. */
+       fd = mkstemp(config_file_tmp);
+       if (fd < 0) {
+               vty_out(vty, "Can't open configuration file %s.%s",
+                       config_file_tmp, VTY_NEWLINE);
+               talloc_free(config_file_tmp);
+               talloc_free(config_file_sav);
+               return CMD_WARNING;
+       }
+
+       /* Make vty for configuration file. */
+       file_vty = vty_new();
+       file_vty->fd = fd;
+       file_vty->type = VTY_FILE;
+
+       /* Config file header print. */
+       vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!",
+               host.app_info->name, host.app_info->version);
+       //vty_time_print (file_vty, 1);
+       vty_out(file_vty, "!\n");
+
+       for (i = 0; i < vector_active(cmdvec); i++)
+               if ((node = vector_slot(cmdvec, i)) && node->func) {
+                       if ((*node->func) (file_vty))
+                               vty_out(file_vty, "!\n");
+               }
+       vty_close(file_vty);
+
+       if (unlink(config_file_sav) != 0)
+               if (errno != ENOENT) {
+                       vty_out(vty,
+                               "Can't unlink backup configuration file %s.%s",
+                               config_file_sav, VTY_NEWLINE);
+                       talloc_free(config_file_sav);
+                       talloc_free(config_file_tmp);
+                       unlink(config_file_tmp);
+                       return CMD_WARNING;
+               }
+       if (link(config_file, config_file_sav) != 0) {
+               vty_out(vty, "Can't backup old configuration file %s.%s",
+                       config_file_sav, VTY_NEWLINE);
+               talloc_free(config_file_sav);
+               talloc_free(config_file_tmp);
+               unlink(config_file_tmp);
+               return CMD_WARNING;
+       }
+       sync();
+       if (unlink(config_file) != 0) {
+               vty_out(vty, "Can't unlink configuration file %s.%s",
+                       config_file, VTY_NEWLINE);
+               talloc_free(config_file_sav);
+               talloc_free(config_file_tmp);
+               unlink(config_file_tmp);
+               return CMD_WARNING;
+       }
+       if (link(config_file_tmp, config_file) != 0) {
+               vty_out(vty, "Can't save configuration file %s.%s", config_file,
+                       VTY_NEWLINE);
+               talloc_free(config_file_sav);
+               talloc_free(config_file_tmp);
+               unlink(config_file_tmp);
+               return CMD_WARNING;
+       }
+       unlink(config_file_tmp);
+       sync();
+
+       talloc_free(config_file_sav);
+       talloc_free(config_file_tmp);
+
+       if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) {
+               vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
+                       config_file, strerror(errno), errno, VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE);
+       return CMD_SUCCESS;
+}
+
+ALIAS(config_write_file,
+      config_write_cmd,
+      "write", "Write running configuration to memory, network, or terminal\n")
+
+    ALIAS(config_write_file,
+      config_write_memory_cmd,
+      "write memory",
+      "Write running configuration to memory, network, or terminal\n"
+      "Write configuration to the file (same as write file)\n")
+
+    ALIAS(config_write_file,
+      copy_runningconfig_startupconfig_cmd,
+      "copy running-config startup-config",
+      "Copy configuration\n"
+      "Copy running config to... \n"
+      "Copy running config to startup config (same as write file)\n")
+
+/* Write current configuration into the terminal. */
+    DEFUN(config_write_terminal,
+      config_write_terminal_cmd,
+      "write terminal",
+      "Write running configuration to memory, network, or terminal\n"
+      "Write to terminal\n")
+{
+       unsigned int i;
+       struct cmd_node *node;
+
+       if (vty->type == VTY_SHELL_SERV) {
+               for (i = 0; i < vector_active(cmdvec); i++)
+                       if ((node = vector_slot(cmdvec, i)) && node->func
+                           && node->vtysh) {
+                               if ((*node->func) (vty))
+                                       vty_out(vty, "!%s", VTY_NEWLINE);
+                       }
+       } else {
+               vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
+                       VTY_NEWLINE);
+               vty_out(vty, "!%s", VTY_NEWLINE);
+
+               for (i = 0; i < vector_active(cmdvec); i++)
+                       if ((node = vector_slot(cmdvec, i)) && node->func) {
+                               if ((*node->func) (vty))
+                                       vty_out(vty, "!%s", VTY_NEWLINE);
+                       }
+               vty_out(vty, "end%s", VTY_NEWLINE);
+       }
+       return CMD_SUCCESS;
+}
+
+/* Write current configuration into the terminal. */
+ALIAS(config_write_terminal,
+      show_running_config_cmd,
+      "show running-config", SHOW_STR "running configuration\n")
+
+/* Write startup configuration into the terminal. */
+    DEFUN(show_startup_config,
+      show_startup_config_cmd,
+      "show startup-config", SHOW_STR "Contentes of startup configuration\n")
+{
+       char buf[BUFSIZ];
+       FILE *confp;
+
+       confp = fopen(host.config, "r");
+       if (confp == NULL) {
+               vty_out(vty, "Can't open configuration file [%s]%s",
+                       host.config, VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       while (fgets(buf, BUFSIZ, confp)) {
+               char *cp = buf;
+
+               while (*cp != '\r' && *cp != '\n' && *cp != '\0')
+                       cp++;
+               *cp = '\0';
+
+               vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+       }
+
+       fclose(confp);
+
+       return CMD_SUCCESS;
+}
+
+/* Hostname configuration */
+DEFUN(config_hostname,
+      hostname_cmd,
+      "hostname WORD",
+      "Set system's network name\n" "This system's network name\n")
+{
+       if (!isalpha((int)*argv[0])) {
+               vty_out(vty, "Please specify string starting with alphabet%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (host.name)
+               talloc_free(host.name);
+
+       host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_no_hostname,
+      no_hostname_cmd,
+      "no hostname [HOSTNAME]",
+      NO_STR "Reset system's network name\n" "Host name of this router\n")
+{
+       if (host.name)
+               talloc_free(host.name);
+       host.name = NULL;
+       return CMD_SUCCESS;
+}
+
+/* VTY interface password set. */
+DEFUN(config_password, password_cmd,
+      "password (8|) WORD",
+      "Assign the terminal connection password\n"
+      "Specifies a HIDDEN password will follow\n"
+      "dummy string \n" "The HIDDEN line password string\n")
+{
+       /* Argument check. */
+       if (argc == 0) {
+               vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (argc == 2) {
+               if (*argv[0] == '8') {
+                       if (host.password)
+                               talloc_free(host.password);
+                       host.password = NULL;
+                       if (host.password_encrypt)
+                               talloc_free(host.password_encrypt);
+                       host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
+                       return CMD_SUCCESS;
+               } else {
+                       vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
+
+       if (!isalnum((int)*argv[0])) {
+               vty_out(vty,
+                       "Please specify string starting with alphanumeric%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (host.password)
+               talloc_free(host.password);
+       host.password = NULL;
+
+#ifdef VTY_CRYPT_PW
+       if (host.encrypt) {
+               if (host.password_encrypt)
+                       talloc_free(host.password_encrypt);
+               host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
+       } else
+#endif
+               host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+
+       return CMD_SUCCESS;
+}
+
+ALIAS(config_password, password_text_cmd,
+      "password LINE",
+      "Assign the terminal connection password\n"
+      "The UNENCRYPTED (cleartext) line password\n")
+
+/* VTY enable password set. */
+    DEFUN(config_enable_password, enable_password_cmd,
+      "enable password (8|) WORD",
+      "Modify enable password parameters\n"
+      "Assign the privileged level password\n"
+      "Specifies a HIDDEN password will follow\n"
+      "dummy string \n" "The HIDDEN 'enable' password string\n")
+{
+       /* Argument check. */
+       if (argc == 0) {
+               vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       /* Crypt type is specified. */
+       if (argc == 2) {
+               if (*argv[0] == '8') {
+                       if (host.enable)
+                               talloc_free(host.enable);
+                       host.enable = NULL;
+
+                       if (host.enable_encrypt)
+                               talloc_free(host.enable_encrypt);
+                       host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
+
+                       return CMD_SUCCESS;
+               } else {
+                       vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
+                       return CMD_WARNING;
+               }
+       }
+
+       if (!isalnum((int)*argv[0])) {
+               vty_out(vty,
+                       "Please specify string starting with alphanumeric%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (host.enable)
+               talloc_free(host.enable);
+       host.enable = NULL;
+
+       /* Plain password input. */
+#ifdef VTY_CRYPT_PW
+       if (host.encrypt) {
+               if (host.enable_encrypt)
+                       talloc_free(host.enable_encrypt);
+               host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
+       } else
+#endif
+               host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+
+       return CMD_SUCCESS;
+}
+
+ALIAS(config_enable_password,
+      enable_password_text_cmd,
+      "enable password LINE",
+      "Modify enable password parameters\n"
+      "Assign the privileged level password\n"
+      "The UNENCRYPTED (cleartext) 'enable' password\n")
+
+/* VTY enable password delete. */
+    DEFUN(no_config_enable_password, no_enable_password_cmd,
+      "no enable password",
+      NO_STR
+      "Modify enable password parameters\n"
+      "Assign the privileged level password\n")
+{
+       if (host.enable)
+               talloc_free(host.enable);
+       host.enable = NULL;
+
+       if (host.enable_encrypt)
+               talloc_free(host.enable_encrypt);
+       host.enable_encrypt = NULL;
+
+       return CMD_SUCCESS;
+}
+
+#ifdef VTY_CRYPT_PW
+DEFUN(service_password_encrypt,
+      service_password_encrypt_cmd,
+      "service password-encryption",
+      "Set up miscellaneous service\n" "Enable encrypted passwords\n")
+{
+       if (host.encrypt)
+               return CMD_SUCCESS;
+
+       host.encrypt = 1;
+
+       if (host.password) {
+               if (host.password_encrypt)
+                       talloc_free(host.password_encrypt);
+               host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
+       }
+       if (host.enable) {
+               if (host.enable_encrypt)
+                       talloc_free(host.enable_encrypt);
+               host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_service_password_encrypt,
+      no_service_password_encrypt_cmd,
+      "no service password-encryption",
+      NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
+{
+       if (!host.encrypt)
+               return CMD_SUCCESS;
+
+       host.encrypt = 0;
+
+       if (host.password_encrypt)
+               talloc_free(host.password_encrypt);
+       host.password_encrypt = NULL;
+
+       if (host.enable_encrypt)
+               talloc_free(host.enable_encrypt);
+       host.enable_encrypt = NULL;
+
+       return CMD_SUCCESS;
+}
+#endif
+
+DEFUN(config_terminal_length, config_terminal_length_cmd,
+      "terminal length <0-512>",
+      "Set terminal line parameters\n"
+      "Set number of lines on a screen\n"
+      "Number of lines on screen (0 for no pausing)\n")
+{
+       int lines;
+       char *endptr = NULL;
+
+       lines = strtol(argv[0], &endptr, 10);
+       if (lines < 0 || lines > 512 || *endptr != '\0') {
+               vty_out(vty, "length is malformed%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       vty->lines = lines;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
+      "terminal no length",
+      "Set terminal line parameters\n"
+      NO_STR "Set number of lines on a screen\n")
+{
+       vty->lines = -1;
+       return CMD_SUCCESS;
+}
+
+DEFUN(service_terminal_length, service_terminal_length_cmd,
+      "service terminal-length <0-512>",
+      "Set up miscellaneous service\n"
+      "System wide terminal length configuration\n"
+      "Number of lines of VTY (0 means no line control)\n")
+{
+       int lines;
+       char *endptr = NULL;
+
+       lines = strtol(argv[0], &endptr, 10);
+       if (lines < 0 || lines > 512 || *endptr != '\0') {
+               vty_out(vty, "length is malformed%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       host.lines = lines;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
+      "no service terminal-length [<0-512>]",
+      NO_STR
+      "Set up miscellaneous service\n"
+      "System wide terminal length configuration\n"
+      "Number of lines of VTY (0 means no line control)\n")
+{
+       host.lines = -1;
+       return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(do_echo,
+            echo_cmd,
+            "echo .MESSAGE",
+            "Echo a message back to the vty\n" "The message to echo\n")
+{
+       char *message;
+
+       vty_out(vty, "%s%s",
+               ((message =
+                 argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
+       if (message)
+               talloc_free(message);
+       return CMD_SUCCESS;
+}
+
+#if 0
+DEFUN(config_logmsg,
+      config_logmsg_cmd,
+      "logmsg " LOG_LEVELS " .MESSAGE",
+      "Send a message to enabled logging destinations\n"
+      LOG_LEVEL_DESC "The message to send\n")
+{
+       int level;
+       char *message;
+
+       if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+               return CMD_ERR_NO_MATCH;
+
+       zlog(NULL, level,
+            ((message = argv_concat(argv, argc, 1)) ? message : ""));
+       if (message)
+               talloc_free(message);
+       return CMD_SUCCESS;
+}
+
+DEFUN(show_logging,
+      show_logging_cmd,
+      "show logging", SHOW_STR "Show current logging configuration\n")
+{
+       struct zlog *zl = zlog_default;
+
+       vty_out(vty, "Syslog logging: ");
+       if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
+               vty_out(vty, "disabled");
+       else
+               vty_out(vty, "level %s, facility %s, ident %s",
+                       zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
+                       facility_name(zl->facility), zl->ident);
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       vty_out(vty, "Stdout logging: ");
+       if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
+               vty_out(vty, "disabled");
+       else
+               vty_out(vty, "level %s",
+                       zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       vty_out(vty, "Monitor logging: ");
+       if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+               vty_out(vty, "disabled");
+       else
+               vty_out(vty, "level %s",
+                       zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       vty_out(vty, "File logging: ");
+       if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
+               vty_out(vty, "disabled");
+       else
+               vty_out(vty, "level %s, filename %s",
+                       zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
+                       zl->filename);
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       vty_out(vty, "Protocol name: %s%s",
+               zlog_proto_names[zl->protocol], VTY_NEWLINE);
+       vty_out(vty, "Record priority: %s%s",
+               (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_stdout,
+      config_log_stdout_cmd,
+      "log stdout", "Logging control\n" "Set stdout logging level\n")
+{
+       zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_stdout_level,
+      config_log_stdout_level_cmd,
+      "log stdout " LOG_LEVELS,
+      "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
+{
+       int level;
+
+       if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+               return CMD_ERR_NO_MATCH;
+       zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_stdout,
+      no_config_log_stdout_cmd,
+      "no log stdout [LEVEL]",
+      NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
+{
+       zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_monitor,
+      config_log_monitor_cmd,
+      "log monitor",
+      "Logging control\n" "Set terminal line (monitor) logging level\n")
+{
+       zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_monitor_level,
+      config_log_monitor_level_cmd,
+      "log monitor " LOG_LEVELS,
+      "Logging control\n"
+      "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
+{
+       int level;
+
+       if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+               return CMD_ERR_NO_MATCH;
+       zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_monitor,
+      no_config_log_monitor_cmd,
+      "no log monitor [LEVEL]",
+      NO_STR
+      "Logging control\n"
+      "Disable terminal line (monitor) logging\n" "Logging level\n")
+{
+       zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+       return CMD_SUCCESS;
+}
+
+static int set_log_file(struct vty *vty, const char *fname, int loglevel)
+{
+       int ret;
+       char *p = NULL;
+       const char *fullpath;
+
+       /* Path detection. */
+       if (!IS_DIRECTORY_SEP(*fname)) {
+               char cwd[MAXPATHLEN + 1];
+               cwd[MAXPATHLEN] = '\0';
+
+               if (getcwd(cwd, MAXPATHLEN) == NULL) {
+                       zlog_err("config_log_file: Unable to alloc mem!");
+                       return CMD_WARNING;
+               }
+
+               if ((p = _talloc_zero(tall_vcmd_ctx,
+                                     strlen(cwd) + strlen(fname) + 2),
+                                     "set_log_file")
+                   == NULL) {
+                       zlog_err("config_log_file: Unable to alloc mem!");
+                       return CMD_WARNING;
+               }
+               sprintf(p, "%s/%s", cwd, fname);
+               fullpath = p;
+       } else
+               fullpath = fname;
+
+       ret = zlog_set_file(NULL, fullpath, loglevel);
+
+       if (p)
+               talloc_free(p);
+
+       if (!ret) {
+               vty_out(vty, "can't open logfile %s\n", fname);
+               return CMD_WARNING;
+       }
+
+       if (host.logfile)
+               talloc_free(host.logfile);
+
+       host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_file,
+      config_log_file_cmd,
+      "log file FILENAME",
+      "Logging control\n" "Logging to file\n" "Logging filename\n")
+{
+       return set_log_file(vty, argv[0], zlog_default->default_lvl);
+}
+
+DEFUN(config_log_file_level,
+      config_log_file_level_cmd,
+      "log file FILENAME " LOG_LEVELS,
+      "Logging control\n"
+      "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
+{
+       int level;
+
+       if ((level = level_match(argv[1])) == ZLOG_DISABLED)
+               return CMD_ERR_NO_MATCH;
+       return set_log_file(vty, argv[0], level);
+}
+
+DEFUN(no_config_log_file,
+      no_config_log_file_cmd,
+      "no log file [FILENAME]",
+      NO_STR
+      "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
+{
+       zlog_reset_file(NULL);
+
+       if (host.logfile)
+               talloc_free(host.logfile);
+
+       host.logfile = NULL;
+
+       return CMD_SUCCESS;
+}
+
+ALIAS(no_config_log_file,
+      no_config_log_file_level_cmd,
+      "no log file FILENAME LEVEL",
+      NO_STR
+      "Logging control\n"
+      "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
+
+    DEFUN(config_log_syslog,
+      config_log_syslog_cmd,
+      "log syslog", "Logging control\n" "Set syslog logging level\n")
+{
+       zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_syslog_level,
+      config_log_syslog_level_cmd,
+      "log syslog " LOG_LEVELS,
+      "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
+{
+       int level;
+
+       if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+               return CMD_ERR_NO_MATCH;
+       zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
+       return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(config_log_syslog_facility,
+                config_log_syslog_facility_cmd,
+                "log syslog facility " LOG_FACILITIES,
+                "Logging control\n"
+                "Logging goes to syslog\n"
+                "(Deprecated) Facility parameter for syslog messages\n"
+                LOG_FACILITY_DESC)
+{
+       int facility;
+
+       if ((facility = facility_match(argv[0])) < 0)
+               return CMD_ERR_NO_MATCH;
+
+       zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+       zlog_default->facility = facility;
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_syslog,
+      no_config_log_syslog_cmd,
+      "no log syslog [LEVEL]",
+      NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
+{
+       zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+       return CMD_SUCCESS;
+}
+
+ALIAS(no_config_log_syslog,
+      no_config_log_syslog_facility_cmd,
+      "no log syslog facility " LOG_FACILITIES,
+      NO_STR
+      "Logging control\n"
+      "Logging goes to syslog\n"
+      "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
+
+    DEFUN(config_log_facility,
+      config_log_facility_cmd,
+      "log facility " LOG_FACILITIES,
+      "Logging control\n"
+      "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
+{
+       int facility;
+
+       if ((facility = facility_match(argv[0])) < 0)
+               return CMD_ERR_NO_MATCH;
+       zlog_default->facility = facility;
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_facility,
+      no_config_log_facility_cmd,
+      "no log facility [FACILITY]",
+      NO_STR
+      "Logging control\n"
+      "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
+{
+       zlog_default->facility = LOG_DAEMON;
+       return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(config_log_trap,
+                config_log_trap_cmd,
+                "log trap " LOG_LEVELS,
+                "Logging control\n"
+                "(Deprecated) Set logging level and default for all destinations\n"
+                LOG_LEVEL_DESC)
+{
+       int new_level;
+       int i;
+
+       if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
+               return CMD_ERR_NO_MATCH;
+
+       zlog_default->default_lvl = new_level;
+       for (i = 0; i < ZLOG_NUM_DESTS; i++)
+               if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
+                       zlog_default->maxlvl[i] = new_level;
+       return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(no_config_log_trap,
+                no_config_log_trap_cmd,
+                "no log trap [LEVEL]",
+                NO_STR
+                "Logging control\n"
+                "Permit all logging information\n" "Logging level\n")
+{
+       zlog_default->default_lvl = LOG_DEBUG;
+       return CMD_SUCCESS;
+}
+
+DEFUN(config_log_record_priority,
+      config_log_record_priority_cmd,
+      "log record-priority",
+      "Logging control\n"
+      "Log the priority of the message within the message\n")
+{
+       zlog_default->record_priority = 1;
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_record_priority,
+      no_config_log_record_priority_cmd,
+      "no log record-priority",
+      NO_STR
+      "Logging control\n"
+      "Do not log the priority of the message within the message\n")
+{
+       zlog_default->record_priority = 0;
+       return CMD_SUCCESS;
+}
+#endif
+
+DEFUN(banner_motd_file,
+      banner_motd_file_cmd,
+      "banner motd file [FILE]",
+      "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
+{
+       if (host.motdfile)
+               talloc_free(host.motdfile);
+       host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(banner_motd_default,
+      banner_motd_default_cmd,
+      "banner motd default",
+      "Set banner string\n" "Strings for motd\n" "Default string\n")
+{
+       host.motd = default_motd;
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_banner_motd,
+      no_banner_motd_cmd,
+      "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
+{
+       host.motd = NULL;
+       if (host.motdfile)
+               talloc_free(host.motdfile);
+       host.motdfile = NULL;
+       return CMD_SUCCESS;
+}
+
+/* Set config filename.  Called from vty.c */
+void host_config_set(const char *filename)
+{
+       host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
+}
+
+void install_default(enum node_type node)
+{
+       install_element(node, &config_help_cmd);
+       install_element(node, &config_list_cmd);
+
+       install_element(node, &config_write_terminal_cmd);
+       install_element(node, &config_write_file_cmd);
+       install_element(node, &config_write_memory_cmd);
+       install_element(node, &config_write_cmd);
+       install_element(node, &show_running_config_cmd);
+}
+
+/* Initialize command interface. Install basic nodes and commands. */
+void cmd_init(int terminal)
+{
+       /* Allocate initial top vector of commands. */
+       cmdvec = vector_init(VECTOR_MIN_SIZE);
+
+       /* Default host value settings. */
+       host.name = NULL;
+       host.password = NULL;
+       host.enable = NULL;
+       host.logfile = NULL;
+       host.config = NULL;
+       host.lines = -1;
+       host.motd = default_motd;
+       host.motdfile = NULL;
+
+       /* Install top nodes. */
+       install_node(&view_node, NULL);
+       install_node(&enable_node, NULL);
+       install_node(&auth_node, NULL);
+       install_node(&auth_enable_node, NULL);
+       install_node(&config_node, config_write_host);
+
+       /* Each node's basic commands. */
+       install_element(VIEW_NODE, &show_version_cmd);
+       if (terminal) {
+               install_element(VIEW_NODE, &config_list_cmd);
+               install_element(VIEW_NODE, &config_exit_cmd);
+               install_element(VIEW_NODE, &config_help_cmd);
+               install_element(VIEW_NODE, &config_enable_cmd);
+               install_element(VIEW_NODE, &config_terminal_length_cmd);
+               install_element(VIEW_NODE, &config_terminal_no_length_cmd);
+               install_element(VIEW_NODE, &echo_cmd);
+       }
+
+       if (terminal) {
+               install_element(ENABLE_NODE, &config_exit_cmd);
+               install_default(ENABLE_NODE);
+               install_element(ENABLE_NODE, &config_disable_cmd);
+               install_element(ENABLE_NODE, &config_terminal_cmd);
+               install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+       }
+       install_element (ENABLE_NODE, &show_startup_config_cmd);
+       install_element(ENABLE_NODE, &show_version_cmd);
+
+       if (terminal) {
+               install_element(ENABLE_NODE, &config_terminal_length_cmd);
+               install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
+               install_element(ENABLE_NODE, &echo_cmd);
+
+               install_default(CONFIG_NODE);
+               install_element(CONFIG_NODE, &config_exit_cmd);
+       }
+
+       install_element(CONFIG_NODE, &hostname_cmd);
+       install_element(CONFIG_NODE, &no_hostname_cmd);
+
+       if (terminal) {
+               install_element(CONFIG_NODE, &password_cmd);
+               install_element(CONFIG_NODE, &password_text_cmd);
+               install_element(CONFIG_NODE, &enable_password_cmd);
+               install_element(CONFIG_NODE, &enable_password_text_cmd);
+               install_element(CONFIG_NODE, &no_enable_password_cmd);
+
+#ifdef VTY_CRYPT_PW
+               install_element(CONFIG_NODE, &service_password_encrypt_cmd);
+               install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
+#endif
+               install_element(CONFIG_NODE, &banner_motd_default_cmd);
+               install_element(CONFIG_NODE, &banner_motd_file_cmd);
+               install_element(CONFIG_NODE, &no_banner_motd_cmd);
+               install_element(CONFIG_NODE, &service_terminal_length_cmd);
+               install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
+
+       }
+       srand(time(NULL));
+}
diff --git a/src/shared/libosmocore/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c
new file mode 100644 (file)
index 0000000..896d79a
--- /dev/null
@@ -0,0 +1,347 @@
+/* OpenBSC logging helper for the VTY */
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2010 by Holger Hans Peter Freyther
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/logging.h>
+
+//#include <openbsc/vty.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+
+extern const struct log_info *osmo_log_info;
+
+static void _vty_output(struct log_target *tgt, const char *line)
+{
+       struct vty *vty = tgt->tgt_vty.vty;
+       vty_out(vty, "%s", line);
+       /* This is an ugly hack, but there is no easy way... */
+       if (strchr(line, '\n'))
+               vty_out(vty, "\r");
+}
+
+struct log_target *log_target_create_vty(struct vty *vty)
+{
+       struct log_target *target;
+
+       target = log_target_create();
+       if (!target)
+               return NULL;
+
+       target->tgt_vty.vty = vty;
+       target->output = _vty_output;
+       return target;
+}
+
+DEFUN(enable_logging,
+      enable_logging_cmd,
+      "logging enable",
+       LOGGING_STR
+      "Enables logging to this vty\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (conn->dbg) {
+               vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       conn->dbg = log_target_create_vty(vty);
+       if (!conn->dbg)
+               return CMD_WARNING;
+
+       log_add_target(conn->dbg);
+       return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_all,
+      logging_fltr_all_cmd,
+      "logging filter all (0|1)",
+       LOGGING_STR FILTER_STR
+       "Do you want to log all messages?\n"
+       "Only print messages matched by other filters\n"
+       "Bypass filter and print all messages\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       log_set_all_filter(conn->dbg, atoi(argv[0]));
+       return CMD_SUCCESS;
+}
+
+DEFUN(logging_use_clr,
+      logging_use_clr_cmd,
+      "logging color (0|1)",
+       LOGGING_STR "Configure color-printing for log messages\n"
+      "Don't use color for printing messages\n"
+      "Use color for printing messages\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       log_set_use_color(conn->dbg, atoi(argv[0]));
+       return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_timestamp,
+      logging_prnt_timestamp_cmd,
+      "logging timestamp (0|1)",
+       LOGGING_STR "Configure log message timestamping\n"
+       "Don't prefix each log message\n"
+       "Prefix each log message with current timestamp\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       log_set_print_timestamp(conn->dbg, atoi(argv[0]));
+       return CMD_SUCCESS;
+}
+
+/* FIXME: those have to be kept in sync with the log levels and categories */
+#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs|ns|bssgp|llc|sndcp|all)"
+#define CATEGORIES_HELP        \
+       "A-bis Radio Link Layer (RLL)\n"                        \
+       "Layer3 Call Control (CC)\n"                            \
+       "Layer3 Mobility Management (MM)\n"                     \
+       "Layer3 Radio Resource (RR)\n"                          \
+       "A-bis Radio Signalling Link (RSL)\n"                   \
+       "A-bis Network Management / O&M (NM/OML)\n"             \
+       "Layer3 Short Messagaging Service (SMS)\n"              \
+       "Paging Subsystem\n"                                    \
+       "MNCC API for Call Control application\n"               \
+       "A-bis Input Subsystem\n"                               \
+       "A-bis Input Driver for Signalling\n"                   \
+       "A-bis Input Driver for B-Channel (voice data)\n"       \
+       "A-bis B-Channel / Sub-channel Multiplexer\n"           \
+       "Radio Measurements\n"                                  \
+       "SCCP\n"                                                \
+       "Mobile Switching Center\n"                             \
+       "Media Gateway Control Protocol\n"                      \
+       "Hand-over\n"                                           \
+       "Database Layer\n"                                      \
+       "Reference Counting\n"                                  \
+       "GPRS Core\n"                                           \
+       "GPRS Network Service (NS)\n"                           \
+       "GPRS BSS Gateway Protocol (BSSGP)\n"                   \
+       "GPRS Logical Link Control Protocol (LLC)\n"            \
+       "GPRS Sub-Network Dependent Control Protocol (SNDCP)\n" \
+       "Global setting for all subsytems\n"
+
+#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
+#define LEVELS_HELP    \
+       "Log simply everything\n"                               \
+       "Log debug messages and higher levels\n"                \
+       "Log informational messages and higher levels\n"        \
+       "Log noticable messages and higher levels\n"            \
+       "Log error messages and higher levels\n"                \
+       "Log only fatal messages\n"
+DEFUN(logging_level,
+      logging_level_cmd,
+      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
+      LOGGING_STR
+      "Set the log level for a specified category\n"
+      CATEGORIES_HELP
+      LEVELS_HELP)
+{
+       struct telnet_connection *conn;
+       int category = log_parse_category(argv[0]);
+       int level = log_parse_level(argv[1]);
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (level < 0) {
+               vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       /* Check for special case where we want to set global log level */
+       if (!strcmp(argv[0], "all")) {
+               log_set_log_level(conn->dbg, level);
+               return CMD_SUCCESS;
+       }
+
+       if (category < 0) {
+               vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       conn->dbg->categories[category].enabled = 1;
+       conn->dbg->categories[category].loglevel = level;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_category_mask,
+      logging_set_category_mask_cmd,
+      "logging set log mask MASK",
+       LOGGING_STR
+      "Decide which categories to output.\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       log_parse_category_mask(conn->dbg, argv[0]);
+       return CMD_SUCCESS;
+}
+
+DEFUN(diable_logging,
+      disable_logging_cmd,
+      "logging disable",
+       LOGGING_STR
+      "Disables logging to this vty\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       log_del_target(conn->dbg);
+       talloc_free(conn->dbg);
+       conn->dbg = NULL;
+       return CMD_SUCCESS;
+}
+
+static void vty_print_logtarget(struct vty *vty, const struct log_info *info,
+                               const struct log_target *tgt)
+{
+       unsigned int i;
+
+       vty_out(vty, " Global Loglevel: %s%s",
+               log_level_str(tgt->loglevel), VTY_NEWLINE);
+       vty_out(vty, " Use color: %s, Print Timestamp: %s%s",
+               tgt->use_color ? "On" : "Off",
+               tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE);
+
+       vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE);
+
+       for (i = 0; i < info->num_cat; i++) {
+               const struct log_category *cat = &tgt->categories[i];
+               vty_out(vty, "  %-10s %-10s %-8s %s%s",
+                       info->cat[i].name+1, log_level_str(cat->loglevel),
+                       cat->enabled ? "Enabled" : "Disabled",
+                       info->cat[i].description,
+                       VTY_NEWLINE);
+       }
+}
+
+#define SHOW_LOG_STR "Show current logging configuration\n"
+
+DEFUN(show_logging_vty,
+      show_logging_vty_cmd,
+      "show logging vty",
+       SHOW_STR SHOW_LOG_STR
+       "Show current logging configuration for this vty\n")
+{
+       struct telnet_connection *conn;
+
+       conn = (struct telnet_connection *) vty->priv;
+       if (!conn->dbg) {
+               vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       vty_print_logtarget(vty, osmo_log_info, conn->dbg);
+
+       return CMD_SUCCESS;
+}
+
+gDEFUN(cfg_description, cfg_description_cmd,
+       "description .TEXT",
+       "Save human-readable decription of the object\n")
+{
+       char **dptr = vty->index_sub;
+
+       if (!dptr) {
+               vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       *dptr = argv_concat(argv, argc, 0);
+       if (!dptr)
+               return CMD_WARNING;
+
+       return CMD_SUCCESS;
+}
+
+gDEFUN(cfg_no_description, cfg_no_description_cmd,
+       "no description",
+       NO_STR
+       "Remove description of the object\n")
+{
+       char **dptr = vty->index_sub;
+
+       if (!dptr) {
+               vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (*dptr) {
+               talloc_free(*dptr);
+               *dptr = NULL;
+       }
+
+       return CMD_SUCCESS;
+}
+
+void logging_vty_add_cmds()
+{
+       install_element_ve(&enable_logging_cmd);
+       install_element_ve(&disable_logging_cmd);
+       install_element_ve(&logging_fltr_all_cmd);
+       install_element_ve(&logging_use_clr_cmd);
+       install_element_ve(&logging_prnt_timestamp_cmd);
+       install_element_ve(&logging_set_category_mask_cmd);
+       install_element_ve(&logging_level_cmd);
+       install_element_ve(&show_logging_vty_cmd);
+}
diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c
new file mode 100644 (file)
index 0000000..9069096
--- /dev/null
@@ -0,0 +1,203 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/logging.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/buffer.h>
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+static void *tall_telnet_ctx;
+
+/* per network data */
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
+
+static struct bsc_fd server_socket = {
+       .when       = BSC_FD_READ,
+       .cb         = telnet_new_connection,
+       .priv_nr    = 0,
+};
+
+int telnet_init(void *tall_ctx, void *priv, int port)
+{
+       struct sockaddr_in sock_addr;
+       int fd, rc, on = 1;
+
+       tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
+                                            "telnet_connection");
+
+       fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+       if (fd < 0) {
+               LOGP(0, LOGL_ERROR, "Telnet interface socket creation failed\n");
+               return fd;
+       }
+
+       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+       memset(&sock_addr, 0, sizeof(sock_addr));
+       sock_addr.sin_family = AF_INET;
+       sock_addr.sin_port = htons(port);
+       sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+       rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
+       if (bind < 0) {
+               LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n");
+               close(fd);
+               return rc;
+       }
+
+       rc = listen(fd, 0);
+       if (rc < 0) {
+               LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n");
+               close(fd);
+               return rc;
+       }
+
+       server_socket.data = priv;
+       server_socket.fd = fd;
+       bsc_register_fd(&server_socket);
+
+       return 0;
+}
+
+extern const char *openbsc_copyright;
+
+static void print_welcome(int fd)
+{
+       int ret;
+       static char *msg =
+               "Welcome to the OpenBSC Control interface\n";
+
+       ret = write(fd, msg, strlen(msg));
+       ret = write(fd, openbsc_copyright, strlen(openbsc_copyright));
+}
+
+int telnet_close_client(struct bsc_fd *fd)
+{
+       struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+       close(fd->fd);
+       bsc_unregister_fd(fd);
+
+       if (conn->dbg) {
+               log_del_target(conn->dbg);
+               talloc_free(conn->dbg);
+       }
+
+       llist_del(&conn->entry);
+       talloc_free(conn);
+       return 0;
+}
+
+static int client_data(struct bsc_fd *fd, unsigned int what)
+{
+       struct telnet_connection *conn = fd->data;
+       int rc = 0;
+
+       if (what & BSC_FD_READ) {
+               conn->fd.when &= ~BSC_FD_READ;
+               rc = vty_read(conn->vty);
+       }
+
+       /* vty might have been closed from vithin vty_read() */
+       if (!conn->vty)
+               return rc;
+
+       if (what & BSC_FD_WRITE) {
+               rc = buffer_flush_all(conn->vty->obuf, fd->fd);
+               if (rc == BUFFER_EMPTY)
+                       conn->fd.when &= ~BSC_FD_WRITE;
+       }
+
+       return rc;
+}
+
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what)
+{
+       struct telnet_connection *connection;
+       struct sockaddr_in sockaddr;
+       socklen_t len = sizeof(sockaddr);
+       int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+
+       if (new_connection < 0) {
+               LOGP(0, LOGL_ERROR, "telnet accept failed\n");
+               return new_connection;
+       }
+
+       connection = talloc_zero(tall_telnet_ctx, struct telnet_connection);
+       connection->priv = fd->data;
+       connection->fd.data = connection;
+       connection->fd.fd = new_connection;
+       connection->fd.when = BSC_FD_READ;
+       connection->fd.cb = client_data;
+       bsc_register_fd(&connection->fd);
+       llist_add_tail(&connection->entry, &active_connections);
+
+       print_welcome(new_connection);
+
+       connection->vty = vty_create(new_connection, connection);
+       if (!connection->vty) {
+               LOGP(0, LOGL_ERROR, "couldn't create VTY\n");
+               close(new_connection);
+               talloc_free(connection);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* callback from VTY code */
+void vty_event(enum event event, int sock, struct vty *vty)
+{
+       struct telnet_connection *connection = vty->priv;
+       struct bsc_fd *bfd = &connection->fd;
+
+       if (vty->type != VTY_TERM)
+               return;
+
+       switch (event) {
+       case VTY_READ:
+               bfd->when |= BSC_FD_READ;
+               break;
+       case VTY_WRITE:
+               bfd->when |= BSC_FD_WRITE;
+               break;
+       case VTY_CLOSED:
+               /* vty layer is about to free() vty */
+               connection->vty = NULL;
+               telnet_close_client(bfd);
+               break;
+       default:
+               break;
+       }
+}
+
diff --git a/src/shared/libosmocore/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c
new file mode 100644 (file)
index 0000000..e163526
--- /dev/null
@@ -0,0 +1,50 @@
+/* utility routines for printing common objects in the Osmocom world */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/rate_ctr.h>
+
+#include <osmocom/vty/vty.h>
+
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+                           struct rate_ctr_group *ctrg)
+{
+       unsigned int i;
+
+       vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
+       for (i = 0; i < ctrg->desc->num_ctr; i++) {
+               struct rate_ctr *ctr = &ctrg->ctr[i];
+               vty_out(vty, " %s%s: %8" PRIu64 " "
+                       "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
+                       prefix, ctrg->desc->ctr_desc[i].description, ctr->current,
+                       ctr->intv[RATE_CTR_INTV_SEC].rate,
+                       ctr->intv[RATE_CTR_INTV_MIN].rate,
+                       ctr->intv[RATE_CTR_INTV_HOUR].rate,
+                       ctr->intv[RATE_CTR_INTV_DAY].rate,
+                       VTY_NEWLINE);
+       };
+}
diff --git a/src/shared/libosmocore/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c
new file mode 100644 (file)
index 0000000..0343163
--- /dev/null
@@ -0,0 +1,192 @@
+/* Generic vector interface routine
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <osmocom/vty/vector.h>
+#include <osmocom/vty/vty.h>
+#include <osmocore/talloc.h>
+#include <memory.h>
+
+void *tall_vty_vec_ctx;
+
+/* Initialize vector : allocate memory and return vector. */
+vector vector_init(unsigned int size)
+{
+       vector v = talloc_zero(tall_vty_vec_ctx, struct _vector);
+       if (!v)
+               return NULL;
+
+       /* allocate at least one slot */
+       if (size == 0)
+               size = 1;
+
+       v->alloced = size;
+       v->active = 0;
+       v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size,
+                               "vector_init:index");
+       if (!v->index) {
+               talloc_free(v);
+               return NULL;
+       }
+       return v;
+}
+
+void vector_only_wrapper_free(vector v)
+{
+       talloc_free(v);
+}
+
+void vector_only_index_free(void *index)
+{
+       talloc_free(index);
+}
+
+void vector_free(vector v)
+{
+       talloc_free(v->index);
+       talloc_free(v);
+}
+
+vector vector_copy(vector v)
+{
+       unsigned int size;
+       vector new = talloc_zero(tall_vty_vec_ctx, struct _vector);
+       if (!new)
+               return NULL;
+
+       new->active = v->active;
+       new->alloced = v->alloced;
+
+       size = sizeof(void *) * (v->alloced);
+       new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index");
+       if (!new->index) {
+               talloc_free(new);
+               return NULL;
+       }
+       memcpy(new->index, v->index, size);
+
+       return new;
+}
+
+/* Check assigned index, and if it runs short double index pointer */
+void vector_ensure(vector v, unsigned int num)
+{
+       if (v->alloced > num)
+               return;
+
+       v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index,
+                                      sizeof(void *) * (v->alloced * 2));
+       memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
+       v->alloced *= 2;
+
+       if (v->alloced <= num)
+               vector_ensure(v, num);
+}
+
+/* This function only returns next empty slot index.  It dose not mean
+   the slot's index memory is assigned, please call vector_ensure()
+   after calling this function. */
+int vector_empty_slot(vector v)
+{
+       unsigned int i;
+
+       if (v->active == 0)
+               return 0;
+
+       for (i = 0; i < v->active; i++)
+               if (v->index[i] == 0)
+                       return i;
+
+       return i;
+}
+
+/* Set value to the smallest empty slot. */
+int vector_set(vector v, void *val)
+{
+       unsigned int i;
+
+       i = vector_empty_slot(v);
+       vector_ensure(v, i);
+
+       v->index[i] = val;
+
+       if (v->active <= i)
+               v->active = i + 1;
+
+       return i;
+}
+
+/* Set value to specified index slot. */
+int vector_set_index(vector v, unsigned int i, void *val)
+{
+       vector_ensure(v, i);
+
+       v->index[i] = val;
+
+       if (v->active <= i)
+               v->active = i + 1;
+
+       return i;
+}
+
+/* Look up vector.  */
+void *vector_lookup(vector v, unsigned int i)
+{
+       if (i >= v->active)
+               return NULL;
+       return v->index[i];
+}
+
+/* Lookup vector, ensure it. */
+void *vector_lookup_ensure(vector v, unsigned int i)
+{
+       vector_ensure(v, i);
+       return v->index[i];
+}
+
+/* Unset value at specified index slot. */
+void vector_unset(vector v, unsigned int i)
+{
+       if (i >= v->alloced)
+               return;
+
+       v->index[i] = NULL;
+
+       if (i + 1 == v->active) {
+               v->active--;
+               while (i && v->index[--i] == NULL && v->active--) ;     /* Is this ugly ? */
+       }
+}
+
+/* Count the number of not emplty slot. */
+unsigned int vector_count(vector v)
+{
+       unsigned int i;
+       unsigned count = 0;
+
+       for (i = 0; i < v->active; i++)
+               if (v->index[i] != NULL)
+                       count++;
+
+       return count;
+}
diff --git a/src/shared/libosmocore/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c
new file mode 100644 (file)
index 0000000..ff17abf
--- /dev/null
@@ -0,0 +1,1683 @@
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <termios.h>
+
+#include <sys/utsname.h>
+#include <sys/param.h>
+
+#include <arpa/telnet.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocore/talloc.h>
+
+#define SYSCONFDIR "/usr/local/etc"
+
+/* our callback, located in telnet_interface.c */
+void vty_event(enum event event, int sock, struct vty *vty);
+
+extern struct host host;
+
+/* Vector which store each vty structure. */
+static vector vtyvec;
+
+vector Vvty_serv_thread;
+
+char *vty_cwd = NULL;
+
+/* Configure lock. */
+static int vty_config;
+
+static int no_password_check = 1;
+
+void *tall_vty_ctx;
+
+static void vty_clear_buf(struct vty *vty)
+{
+       memset(vty->buf, 0, vty->max);
+}
+
+/* Allocate new vty struct. */
+struct vty *vty_new()
+{
+       struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
+
+       if (!new)
+               goto out;
+
+       new->obuf = buffer_new(new, 0); /* Use default buffer size. */
+       if (!new->obuf)
+               goto out_new;
+       new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
+       if (!new->buf)
+               goto out_obuf;
+
+       new->max = VTY_BUFSIZ;
+
+       return new;
+
+out_obuf:
+       buffer_free(new->obuf);
+out_new:
+       talloc_free(new);
+       new = NULL;
+out:
+       return new;
+}
+
+/* Authentication of vty */
+static void vty_auth(struct vty *vty, char *buf)
+{
+       char *passwd = NULL;
+       enum node_type next_node = 0;
+       int fail;
+       char *crypt(const char *, const char *);
+
+       switch (vty->node) {
+       case AUTH_NODE:
+#ifdef VTY_CRYPT_PW
+               if (host.encrypt)
+                       passwd = host.password_encrypt;
+               else
+#endif
+                       passwd = host.password;
+               if (host.advanced)
+                       next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+               else
+                       next_node = VIEW_NODE;
+               break;
+       case AUTH_ENABLE_NODE:
+#ifdef VTY_CRYPT_PW
+               if (host.encrypt)
+                       passwd = host.enable_encrypt;
+               else
+#endif
+                       passwd = host.enable;
+               next_node = ENABLE_NODE;
+               break;
+       }
+
+       if (passwd) {
+#ifdef VTY_CRYPT_PW
+               if (host.encrypt)
+                       fail = strcmp(crypt(buf, passwd), passwd);
+               else
+#endif
+                       fail = strcmp(buf, passwd);
+       } else
+               fail = 1;
+
+       if (!fail) {
+               vty->fail = 0;
+               vty->node = next_node;  /* Success ! */
+       } else {
+               vty->fail++;
+               if (vty->fail >= 3) {
+                       if (vty->node == AUTH_NODE) {
+                               vty_out(vty,
+                                       "%% Bad passwords, too many failures!%s",
+                                       VTY_NEWLINE);
+                               vty->status = VTY_CLOSE;
+                       } else {
+                               /* AUTH_ENABLE_NODE */
+                               vty->fail = 0;
+                               vty_out(vty,
+                                       "%% Bad enable passwords, too many failures!%s",
+                                       VTY_NEWLINE);
+                               vty->node = VIEW_NODE;
+                       }
+               }
+       }
+}
+
+/* Close vty interface. */
+void vty_close(struct vty *vty)
+{
+       int i;
+
+       if (vty->obuf)  {
+               /* Flush buffer. */
+               buffer_flush_all(vty->obuf, vty->fd);
+
+               /* Free input buffer. */
+               buffer_free(vty->obuf);
+               vty->obuf = NULL;
+       }
+
+       /* Free command history. */
+       for (i = 0; i < VTY_MAXHIST; i++)
+               if (vty->hist[i])
+                       talloc_free(vty->hist[i]);
+
+       /* Unset vector. */
+       vector_unset(vtyvec, vty->fd);
+
+       /* Close socket. */
+       if (vty->fd > 0)
+               close(vty->fd);
+
+       if (vty->buf) {
+               talloc_free(vty->buf);
+               vty->buf = NULL;
+       }
+
+       /* Check configure. */
+       vty_config_unlock(vty);
+
+       /* VTY_CLOSED is handled by the telnet_interface */
+       vty_event(VTY_CLOSED, vty->fd, vty);
+
+       /* OK free vty. */
+       talloc_free(vty);
+}
+
+int vty_shell(struct vty *vty)
+{
+       return vty->type == VTY_SHELL ? 1 : 0;
+}
+
+
+/* VTY standard output function. */
+int vty_out(struct vty *vty, const char *format, ...)
+{
+       va_list args;
+       int len = 0;
+       int size = 1024;
+       char buf[1024];
+       char *p = NULL;
+
+       if (vty_shell(vty)) {
+               va_start(args, format);
+               vprintf(format, args);
+               va_end(args);
+       } else {
+               /* Try to write to initial buffer.  */
+               va_start(args, format);
+               len = vsnprintf(buf, sizeof buf, format, args);
+               va_end(args);
+
+               /* Initial buffer is not enough.  */
+               if (len < 0 || len >= size) {
+                       while (1) {
+                               if (len > -1)
+                                       size = len + 1;
+                               else
+                                       size = size * 2;
+
+                               p = talloc_realloc_size(vty, p, size);
+                               if (!p)
+                                       return -1;
+
+                               va_start(args, format);
+                               len = vsnprintf(p, size, format, args);
+                               va_end(args);
+
+                               if (len > -1 && len < size)
+                                       break;
+                       }
+               }
+
+               /* When initial buffer is enough to store all output.  */
+               if (!p)
+                       p = buf;
+
+               /* Pointer p must point out buffer. */
+               buffer_put(vty->obuf, (u_char *) p, len);
+
+               /* If p is not different with buf, it is allocated buffer.  */
+               if (p != buf)
+                       talloc_free(p);
+       }
+
+       vty_event(VTY_WRITE, vty->fd, vty);
+
+       return len;
+}
+
+int vty_out_newline(struct vty *vty)
+{
+       char *p = vty_newline(vty);
+       buffer_put(vty->obuf, p, strlen(p));
+       return 0;
+}
+
+int vty_config_lock(struct vty *vty)
+{
+       if (vty_config == 0) {
+               vty->config = 1;
+               vty_config = 1;
+       }
+       return vty->config;
+}
+
+int vty_config_unlock(struct vty *vty)
+{
+       if (vty_config == 1 && vty->config == 1) {
+               vty->config = 0;
+               vty_config = 0;
+       }
+       return vty->config;
+}
+
+/* Say hello to vty interface. */
+void vty_hello(struct vty *vty)
+{
+       if (host.motdfile) {
+               FILE *f;
+               char buf[4096];
+
+               f = fopen(host.motdfile, "r");
+               if (f) {
+                       while (fgets(buf, sizeof(buf), f)) {
+                               char *s;
+                               /* work backwards to ignore trailling isspace() */
+                               for (s = buf + strlen(buf);
+                                    (s > buf) && isspace(*(s - 1)); s--) ;
+                               *s = '\0';
+                               vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+                       }
+                       fclose(f);
+               } else
+                       vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
+       } else if (host.motd)
+               vty_out(vty, "%s", host.motd);
+}
+
+/* Put out prompt and wait input from user. */
+static void vty_prompt(struct vty *vty)
+{
+       struct utsname names;
+       const char *hostname;
+
+       if (vty->type == VTY_TERM) {
+               hostname = host.name;
+               if (!hostname) {
+                       uname(&names);
+                       hostname = names.nodename;
+               }
+               vty_out(vty, cmd_prompt(vty->node), hostname);
+       }
+}
+
+/* Command execution over the vty interface. */
+static int vty_command(struct vty *vty, char *buf)
+{
+       int ret;
+       vector vline;
+
+       /* Split readline string up into the vector */
+       vline = cmd_make_strvec(buf);
+
+       if (vline == NULL)
+               return CMD_SUCCESS;
+
+       ret = cmd_execute_command(vline, vty, NULL, 0);
+       if (ret != CMD_SUCCESS)
+               switch (ret) {
+               case CMD_WARNING:
+                       if (vty->type == VTY_FILE)
+                               vty_out(vty, "Warning...%s", VTY_NEWLINE);
+                       break;
+               case CMD_ERR_AMBIGUOUS:
+                       vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+                       break;
+               case CMD_ERR_NO_MATCH:
+                       vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
+                       break;
+               case CMD_ERR_INCOMPLETE:
+                       vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
+                       break;
+               }
+       cmd_free_strvec(vline);
+
+       return ret;
+}
+
+static const char telnet_backward_char = 0x08;
+static const char telnet_space_char = ' ';
+
+/* Basic function to write buffer to vty. */
+static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
+{
+       if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+               return;
+
+       /* Should we do buffering here ?  And make vty_flush (vty) ? */
+       buffer_put(vty->obuf, buf, nbytes);
+}
+
+/* Ensure length of input buffer.  Is buffer is short, double it. */
+static void vty_ensure(struct vty *vty, int length)
+{
+       if (vty->max <= length) {
+               vty->max *= 2;
+               vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
+               // FIXME: check return
+       }
+}
+
+/* Basic function to insert character into vty. */
+static void vty_self_insert(struct vty *vty, char c)
+{
+       int i;
+       int length;
+
+       vty_ensure(vty, vty->length + 1);
+       length = vty->length - vty->cp;
+       memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
+       vty->buf[vty->cp] = c;
+
+       vty_write(vty, &vty->buf[vty->cp], length + 1);
+       for (i = 0; i < length; i++)
+               vty_write(vty, &telnet_backward_char, 1);
+
+       vty->cp++;
+       vty->length++;
+}
+
+/* Self insert character 'c' in overwrite mode. */
+static void vty_self_insert_overwrite(struct vty *vty, char c)
+{
+       vty_ensure(vty, vty->length + 1);
+       vty->buf[vty->cp++] = c;
+
+       if (vty->cp > vty->length)
+               vty->length++;
+
+       if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+               return;
+
+       vty_write(vty, &c, 1);
+}
+
+/* Insert a word into vty interface with overwrite mode. */
+static void vty_insert_word_overwrite(struct vty *vty, char *str)
+{
+       int len = strlen(str);
+       vty_write(vty, str, len);
+       strcpy(&vty->buf[vty->cp], str);
+       vty->cp += len;
+       vty->length = vty->cp;
+}
+
+/* Forward character. */
+static void vty_forward_char(struct vty *vty)
+{
+       if (vty->cp < vty->length) {
+               vty_write(vty, &vty->buf[vty->cp], 1);
+               vty->cp++;
+       }
+}
+
+/* Backward character. */
+static void vty_backward_char(struct vty *vty)
+{
+       if (vty->cp > 0) {
+               vty->cp--;
+               vty_write(vty, &telnet_backward_char, 1);
+       }
+}
+
+/* Move to the beginning of the line. */
+static void vty_beginning_of_line(struct vty *vty)
+{
+       while (vty->cp)
+               vty_backward_char(vty);
+}
+
+/* Move to the end of the line. */
+static void vty_end_of_line(struct vty *vty)
+{
+       while (vty->cp < vty->length)
+               vty_forward_char(vty);
+}
+
+/* Add current command line to the history buffer. */
+static void vty_hist_add(struct vty *vty)
+{
+       int index;
+
+       if (vty->length == 0)
+               return;
+
+       index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
+
+       /* Ignore the same string as previous one. */
+       if (vty->hist[index])
+               if (strcmp(vty->buf, vty->hist[index]) == 0) {
+                       vty->hp = vty->hindex;
+                       return;
+               }
+
+       /* Insert history entry. */
+       if (vty->hist[vty->hindex])
+               talloc_free(vty->hist[vty->hindex]);
+       vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
+
+       /* History index rotation. */
+       vty->hindex++;
+       if (vty->hindex == VTY_MAXHIST)
+               vty->hindex = 0;
+
+       vty->hp = vty->hindex;
+}
+
+/* Get telnet window size. */
+static int
+vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
+{
+#ifdef TELNET_OPTION_DEBUG
+  int i;
+
+  for (i = 0; i < nbytes; i++)
+    {
+      switch (buf[i])
+       {
+       case IAC:
+         vty_out (vty, "IAC ");
+         break;
+       case WILL:
+         vty_out (vty, "WILL ");
+         break;
+       case WONT:
+         vty_out (vty, "WONT ");
+         break;
+       case DO:
+         vty_out (vty, "DO ");
+         break;
+       case DONT:
+         vty_out (vty, "DONT ");
+         break;
+       case SB:
+         vty_out (vty, "SB ");
+         break;
+       case SE:
+         vty_out (vty, "SE ");
+         break;
+       case TELOPT_ECHO:
+         vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
+         break;
+       case TELOPT_SGA:
+         vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
+         break;
+       case TELOPT_NAWS:
+         vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
+         break;
+       default:
+         vty_out (vty, "%x ", buf[i]);
+         break;
+       }
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+#endif /* TELNET_OPTION_DEBUG */
+
+  switch (buf[0])
+    {
+    case SB:
+      vty->sb_len = 0;
+      vty->iac_sb_in_progress = 1;
+      return 0;
+      break;
+    case SE:
+      {
+       if (!vty->iac_sb_in_progress)
+         return 0;
+
+       if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
+         {
+           vty->iac_sb_in_progress = 0;
+           return 0;
+         }
+       switch (vty->sb_buf[0])
+         {
+         case TELOPT_NAWS:
+           if (vty->sb_len != TELNET_NAWS_SB_LEN)
+             vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
+                       "should send %d characters, but we received %lu",
+                       TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
+           else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
+             vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
+                      "too small to handle the telnet NAWS option",
+                      (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
+           else
+             {
+               vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
+               vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
+#ifdef TELNET_OPTION_DEBUG
+               vty_out(vty, "TELNET NAWS window size negotiation completed: "
+                             "width %d, height %d%s",
+                       vty->width, vty->height, VTY_NEWLINE);
+#endif
+             }
+           break;
+         }
+       vty->iac_sb_in_progress = 0;
+       return 0;
+       break;
+      }
+    default:
+      break;
+    }
+  return 1;
+}
+
+/* Execute current command line. */
+static int vty_execute(struct vty *vty)
+{
+       int ret;
+
+       ret = CMD_SUCCESS;
+
+       switch (vty->node) {
+       case AUTH_NODE:
+       case AUTH_ENABLE_NODE:
+               vty_auth(vty, vty->buf);
+               break;
+       default:
+               ret = vty_command(vty, vty->buf);
+               if (vty->type == VTY_TERM)
+                       vty_hist_add(vty);
+               break;
+       }
+
+       /* Clear command line buffer. */
+       vty->cp = vty->length = 0;
+       vty_clear_buf(vty);
+
+       if (vty->status != VTY_CLOSE)
+               vty_prompt(vty);
+
+       return ret;
+}
+
+/* Send WILL TELOPT_ECHO to remote server. */
+static void
+vty_will_echo (struct vty *vty)
+{
+       unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+       vty_out (vty, "%s", cmd);
+}
+
+/* Make suppress Go-Ahead telnet option. */
+static void
+vty_will_suppress_go_ahead (struct vty *vty)
+{
+       unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
+       vty_out (vty, "%s", cmd);
+}
+
+/* Make don't use linemode over telnet. */
+static void
+vty_dont_linemode (struct vty *vty)
+{
+       unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
+       vty_out (vty, "%s", cmd);
+}
+
+/* Use window size. */
+static void
+vty_do_window_size (struct vty *vty)
+{
+       unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
+       vty_out (vty, "%s", cmd);
+}
+
+static void vty_kill_line_from_beginning(struct vty *);
+static void vty_redraw_line(struct vty *);
+
+/* Print command line history.  This function is called from
+   vty_next_line and vty_previous_line. */
+static void vty_history_print(struct vty *vty)
+{
+       int length;
+
+       vty_kill_line_from_beginning(vty);
+
+       /* Get previous line from history buffer */
+       length = strlen(vty->hist[vty->hp]);
+       memcpy(vty->buf, vty->hist[vty->hp], length);
+       vty->cp = vty->length = length;
+
+       /* Redraw current line */
+       vty_redraw_line(vty);
+}
+
+/* Show next command line history. */
+static void vty_next_line(struct vty *vty)
+{
+       int try_index;
+
+       if (vty->hp == vty->hindex)
+               return;
+
+       /* Try is there history exist or not. */
+       try_index = vty->hp;
+       if (try_index == (VTY_MAXHIST - 1))
+               try_index = 0;
+       else
+               try_index++;
+
+       /* If there is not history return. */
+       if (vty->hist[try_index] == NULL)
+               return;
+       else
+               vty->hp = try_index;
+
+       vty_history_print(vty);
+}
+
+/* Show previous command line history. */
+static void vty_previous_line(struct vty *vty)
+{
+       int try_index;
+
+       try_index = vty->hp;
+       if (try_index == 0)
+               try_index = VTY_MAXHIST - 1;
+       else
+               try_index--;
+
+       if (vty->hist[try_index] == NULL)
+               return;
+       else
+               vty->hp = try_index;
+
+       vty_history_print(vty);
+}
+
+/* This function redraw all of the command line character. */
+static void vty_redraw_line(struct vty *vty)
+{
+       vty_write(vty, vty->buf, vty->length);
+       vty->cp = vty->length;
+}
+
+/* Forward word. */
+static void vty_forward_word(struct vty *vty)
+{
+       while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+               vty_forward_char(vty);
+
+       while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+               vty_forward_char(vty);
+}
+
+/* Backward word without skipping training space. */
+static void vty_backward_pure_word(struct vty *vty)
+{
+       while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+               vty_backward_char(vty);
+}
+
+/* Backward word. */
+static void vty_backward_word(struct vty *vty)
+{
+       while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+               vty_backward_char(vty);
+
+       while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+               vty_backward_char(vty);
+}
+
+/* When '^D' is typed at the beginning of the line we move to the down
+   level. */
+static void vty_down_level(struct vty *vty)
+{
+       vty_out(vty, "%s", VTY_NEWLINE);
+       /* FIXME: we need to call the exit function of the specific node
+        * in question, not this generic one that doesn't know all nodes */
+       (*config_exit_cmd.func) (NULL, vty, 0, NULL);
+       vty_prompt(vty);
+       vty->cp = 0;
+}
+
+/* When '^Z' is received from vty, move down to the enable mode. */
+static void vty_end_config(struct vty *vty)
+{
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       /* FIXME: we need to call the exit function of the specific node
+        * in question, not this generic one that doesn't know all nodes */
+       switch (vty->node) {
+       case VIEW_NODE:
+       case ENABLE_NODE:
+               /* Nothing to do. */
+               break;
+       case CONFIG_NODE:
+       case VTY_NODE:
+               vty_config_unlock(vty);
+               vty->node = ENABLE_NODE;
+               break;
+       default:
+               /* Unknown node, we have to ignore it. */
+               break;
+       }
+
+       vty_prompt(vty);
+       vty->cp = 0;
+}
+
+/* Delete a charcter at the current point. */
+static void vty_delete_char(struct vty *vty)
+{
+       int i;
+       int size;
+
+       if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+               return;
+
+       if (vty->length == 0) {
+               vty_down_level(vty);
+               return;
+       }
+
+       if (vty->cp == vty->length)
+               return;         /* completion need here? */
+
+       size = vty->length - vty->cp;
+
+       vty->length--;
+       memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
+       vty->buf[vty->length] = '\0';
+
+       vty_write(vty, &vty->buf[vty->cp], size - 1);
+       vty_write(vty, &telnet_space_char, 1);
+
+       for (i = 0; i < size; i++)
+               vty_write(vty, &telnet_backward_char, 1);
+}
+
+/* Delete a character before the point. */
+static void vty_delete_backward_char(struct vty *vty)
+{
+       if (vty->cp == 0)
+               return;
+
+       vty_backward_char(vty);
+       vty_delete_char(vty);
+}
+
+/* Kill rest of line from current point. */
+static void vty_kill_line(struct vty *vty)
+{
+       int i;
+       int size;
+
+       size = vty->length - vty->cp;
+
+       if (size == 0)
+               return;
+
+       for (i = 0; i < size; i++)
+               vty_write(vty, &telnet_space_char, 1);
+       for (i = 0; i < size; i++)
+               vty_write(vty, &telnet_backward_char, 1);
+
+       memset(&vty->buf[vty->cp], 0, size);
+       vty->length = vty->cp;
+}
+
+/* Kill line from the beginning. */
+static void vty_kill_line_from_beginning(struct vty *vty)
+{
+       vty_beginning_of_line(vty);
+       vty_kill_line(vty);
+}
+
+/* Delete a word before the point. */
+static void vty_forward_kill_word(struct vty *vty)
+{
+       while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+               vty_delete_char(vty);
+       while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+               vty_delete_char(vty);
+}
+
+/* Delete a word before the point. */
+static void vty_backward_kill_word(struct vty *vty)
+{
+       while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+               vty_delete_backward_char(vty);
+       while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+               vty_delete_backward_char(vty);
+}
+
+/* Transpose chars before or at the point. */
+static void vty_transpose_chars(struct vty *vty)
+{
+       char c1, c2;
+
+       /* If length is short or point is near by the beginning of line then
+          return. */
+       if (vty->length < 2 || vty->cp < 1)
+               return;
+
+       /* In case of point is located at the end of the line. */
+       if (vty->cp == vty->length) {
+               c1 = vty->buf[vty->cp - 1];
+               c2 = vty->buf[vty->cp - 2];
+
+               vty_backward_char(vty);
+               vty_backward_char(vty);
+               vty_self_insert_overwrite(vty, c1);
+               vty_self_insert_overwrite(vty, c2);
+       } else {
+               c1 = vty->buf[vty->cp];
+               c2 = vty->buf[vty->cp - 1];
+
+               vty_backward_char(vty);
+               vty_self_insert_overwrite(vty, c1);
+               vty_self_insert_overwrite(vty, c2);
+       }
+}
+
+/* Do completion at vty interface. */
+static void vty_complete_command(struct vty *vty)
+{
+       int i;
+       int ret;
+       char **matched = NULL;
+       vector vline;
+
+       if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+               return;
+
+       vline = cmd_make_strvec(vty->buf);
+       if (vline == NULL)
+               return;
+
+       /* In case of 'help \t'. */
+       if (isspace((int)vty->buf[vty->length - 1]))
+               vector_set(vline, '\0');
+
+       matched = cmd_complete_command(vline, vty, &ret);
+
+       cmd_free_strvec(vline);
+
+       vty_out(vty, "%s", VTY_NEWLINE);
+       switch (ret) {
+       case CMD_ERR_AMBIGUOUS:
+               vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               break;
+       case CMD_ERR_NO_MATCH:
+               /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               break;
+       case CMD_COMPLETE_FULL_MATCH:
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               vty_backward_pure_word(vty);
+               vty_insert_word_overwrite(vty, matched[0]);
+               vty_self_insert(vty, ' ');
+               talloc_free(matched[0]);
+               break;
+       case CMD_COMPLETE_MATCH:
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               vty_backward_pure_word(vty);
+               vty_insert_word_overwrite(vty, matched[0]);
+               talloc_free(matched[0]);
+               break;
+       case CMD_COMPLETE_LIST_MATCH:
+               for (i = 0; matched[i] != NULL; i++) {
+                       if (i != 0 && ((i % 6) == 0))
+                               vty_out(vty, "%s", VTY_NEWLINE);
+                       vty_out(vty, "%-10s ", matched[i]);
+                       talloc_free(matched[i]);
+               }
+               vty_out(vty, "%s", VTY_NEWLINE);
+
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               break;
+       case CMD_ERR_NOTHING_TODO:
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               break;
+       default:
+               break;
+       }
+       if (matched)
+               vector_only_index_free(matched);
+}
+
+static void
+vty_describe_fold(struct vty *vty, int cmd_width,
+                 unsigned int desc_width, struct desc *desc)
+{
+       char *buf;
+       const char *cmd, *p;
+       int pos;
+
+       cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+
+       if (desc_width <= 0) {
+               vty_out(vty, "  %-*s  %s%s", cmd_width, cmd, desc->str,
+                       VTY_NEWLINE);
+               return;
+       }
+
+       buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
+       if (!buf)
+               return;
+
+       for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
+               for (pos = desc_width; pos > 0; pos--)
+                       if (*(p + pos) == ' ')
+                               break;
+
+               if (pos == 0)
+                       break;
+
+               strncpy(buf, p, pos);
+               buf[pos] = '\0';
+               vty_out(vty, "  %-*s  %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
+
+               cmd = "";
+       }
+
+       vty_out(vty, "  %-*s  %s%s", cmd_width, cmd, p, VTY_NEWLINE);
+
+       talloc_free(buf);
+}
+
+/* Describe matched command function. */
+static void vty_describe_command(struct vty *vty)
+{
+       int ret;
+       vector vline;
+       vector describe;
+       unsigned int i, width, desc_width;
+       struct desc *desc, *desc_cr = NULL;
+
+       vline = cmd_make_strvec(vty->buf);
+
+       /* In case of '> ?'. */
+       if (vline == NULL) {
+               vline = vector_init(1);
+               vector_set(vline, '\0');
+       } else if (isspace((int)vty->buf[vty->length - 1]))
+               vector_set(vline, '\0');
+
+       describe = cmd_describe_command(vline, vty, &ret);
+
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       /* Ambiguous error. */
+       switch (ret) {
+       case CMD_ERR_AMBIGUOUS:
+               cmd_free_strvec(vline);
+               vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               return;
+               break;
+       case CMD_ERR_NO_MATCH:
+               cmd_free_strvec(vline);
+               vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
+               vty_prompt(vty);
+               vty_redraw_line(vty);
+               return;
+               break;
+       }
+
+       /* Get width of command string. */
+       width = 0;
+       for (i = 0; i < vector_active(describe); i++)
+               if ((desc = vector_slot(describe, i)) != NULL) {
+                       unsigned int len;
+
+                       if (desc->cmd[0] == '\0')
+                               continue;
+
+                       len = strlen(desc->cmd);
+                       if (desc->cmd[0] == '.')
+                               len--;
+
+                       if (width < len)
+                               width = len;
+               }
+
+       /* Get width of description string. */
+       desc_width = vty->width - (width + 6);
+
+       /* Print out description. */
+       for (i = 0; i < vector_active(describe); i++)
+               if ((desc = vector_slot(describe, i)) != NULL) {
+                       if (desc->cmd[0] == '\0')
+                               continue;
+
+                       if (strcmp(desc->cmd, "<cr>") == 0) {
+                               desc_cr = desc;
+                               continue;
+                       }
+
+                       if (!desc->str)
+                               vty_out(vty, "  %-s%s",
+                                       desc->cmd[0] ==
+                                       '.' ? desc->cmd + 1 : desc->cmd,
+                                       VTY_NEWLINE);
+                       else if (desc_width >= strlen(desc->str))
+                               vty_out(vty, "  %-*s  %s%s", width,
+                                       desc->cmd[0] ==
+                                       '.' ? desc->cmd + 1 : desc->cmd,
+                                       desc->str, VTY_NEWLINE);
+                       else
+                               vty_describe_fold(vty, width, desc_width, desc);
+
+#if 0
+                       vty_out(vty, "  %-*s %s%s", width
+                               desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+                               desc->str ? desc->str : "", VTY_NEWLINE);
+#endif                         /* 0 */
+               }
+
+       if ((desc = desc_cr)) {
+               if (!desc->str)
+                       vty_out(vty, "  %-s%s",
+                               desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+                               VTY_NEWLINE);
+               else if (desc_width >= strlen(desc->str))
+                       vty_out(vty, "  %-*s  %s%s", width,
+                               desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+                               desc->str, VTY_NEWLINE);
+               else
+                       vty_describe_fold(vty, width, desc_width, desc);
+       }
+
+       cmd_free_strvec(vline);
+       vector_free(describe);
+
+       vty_prompt(vty);
+       vty_redraw_line(vty);
+}
+
+/* ^C stop current input and do not add command line to the history. */
+static void vty_stop_input(struct vty *vty)
+{
+       vty->cp = vty->length = 0;
+       vty_clear_buf(vty);
+       vty_out(vty, "%s", VTY_NEWLINE);
+
+       switch (vty->node) {
+       case VIEW_NODE:
+       case ENABLE_NODE:
+               /* Nothing to do. */
+               break;
+       case CONFIG_NODE:
+       case VTY_NODE:
+               vty_config_unlock(vty);
+               vty->node = ENABLE_NODE;
+               break;
+       default:
+               /* Unknown node, we have to ignore it. */
+               break;
+       }
+       vty_prompt(vty);
+
+       /* Set history pointer to the latest one. */
+       vty->hp = vty->hindex;
+}
+
+#define CONTROL(X)  ((X) - '@')
+#define VTY_NORMAL     0
+#define VTY_PRE_ESCAPE 1
+#define VTY_ESCAPE     2
+
+/* Escape character command map. */
+static void vty_escape_map(unsigned char c, struct vty *vty)
+{
+       switch (c) {
+       case ('A'):
+               vty_previous_line(vty);
+               break;
+       case ('B'):
+               vty_next_line(vty);
+               break;
+       case ('C'):
+               vty_forward_char(vty);
+               break;
+       case ('D'):
+               vty_backward_char(vty);
+               break;
+       default:
+               break;
+       }
+
+       /* Go back to normal mode. */
+       vty->escape = VTY_NORMAL;
+}
+
+/* Quit print out to the buffer. */
+static void vty_buffer_reset(struct vty *vty)
+{
+       buffer_reset(vty->obuf);
+       vty_prompt(vty);
+       vty_redraw_line(vty);
+}
+
+/* Read data via vty socket. */
+int vty_read(struct vty *vty)
+{
+       int i;
+       int nbytes;
+       unsigned char buf[VTY_READ_BUFSIZ];
+
+       int vty_sock = vty->fd;
+
+       /* Read raw data from socket */
+       if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
+               if (nbytes < 0) {
+                       if (ERRNO_IO_RETRY(errno)) {
+                               vty_event(VTY_READ, vty_sock, vty);
+                               return 0;
+                       }
+               }
+               buffer_reset(vty->obuf);
+               vty->status = VTY_CLOSE;
+       }
+
+       for (i = 0; i < nbytes; i++) {
+               if (buf[i] == IAC) {
+                       if (!vty->iac) {
+                               vty->iac = 1;
+                               continue;
+                       } else {
+                               vty->iac = 0;
+                       }
+               }
+
+               if (vty->iac_sb_in_progress && !vty->iac) {
+                       if (vty->sb_len < sizeof(vty->sb_buf))
+                               vty->sb_buf[vty->sb_len] = buf[i];
+                       vty->sb_len++;
+                       continue;
+               }
+
+               if (vty->iac) {
+                       /* In case of telnet command */
+                       int ret = 0;
+                       ret = vty_telnet_option(vty, buf + i, nbytes - i);
+                       vty->iac = 0;
+                       i += ret;
+                       continue;
+               }
+
+               if (vty->status == VTY_MORE) {
+                       switch (buf[i]) {
+                       case CONTROL('C'):
+                       case 'q':
+                       case 'Q':
+                               vty_buffer_reset(vty);
+                               break;
+#if 0                          /* More line does not work for "show ip bgp".  */
+                       case '\n':
+                       case '\r':
+                               vty->status = VTY_MORELINE;
+                               break;
+#endif
+                       default:
+                               break;
+                       }
+                       continue;
+               }
+
+               /* Escape character. */
+               if (vty->escape == VTY_ESCAPE) {
+                       vty_escape_map(buf[i], vty);
+                       continue;
+               }
+
+               /* Pre-escape status. */
+               if (vty->escape == VTY_PRE_ESCAPE) {
+                       switch (buf[i]) {
+                       case '[':
+                               vty->escape = VTY_ESCAPE;
+                               break;
+                       case 'b':
+                               vty_backward_word(vty);
+                               vty->escape = VTY_NORMAL;
+                               break;
+                       case 'f':
+                               vty_forward_word(vty);
+                               vty->escape = VTY_NORMAL;
+                               break;
+                       case 'd':
+                               vty_forward_kill_word(vty);
+                               vty->escape = VTY_NORMAL;
+                               break;
+                       case CONTROL('H'):
+                       case 0x7f:
+                               vty_backward_kill_word(vty);
+                               vty->escape = VTY_NORMAL;
+                               break;
+                       default:
+                               vty->escape = VTY_NORMAL;
+                               break;
+                       }
+                       continue;
+               }
+
+               switch (buf[i]) {
+               case CONTROL('A'):
+                       vty_beginning_of_line(vty);
+                       break;
+               case CONTROL('B'):
+                       vty_backward_char(vty);
+                       break;
+               case CONTROL('C'):
+                       vty_stop_input(vty);
+                       break;
+               case CONTROL('D'):
+                       vty_delete_char(vty);
+                       break;
+               case CONTROL('E'):
+                       vty_end_of_line(vty);
+                       break;
+               case CONTROL('F'):
+                       vty_forward_char(vty);
+                       break;
+               case CONTROL('H'):
+               case 0x7f:
+                       vty_delete_backward_char(vty);
+                       break;
+               case CONTROL('K'):
+                       vty_kill_line(vty);
+                       break;
+               case CONTROL('N'):
+                       vty_next_line(vty);
+                       break;
+               case CONTROL('P'):
+                       vty_previous_line(vty);
+                       break;
+               case CONTROL('T'):
+                       vty_transpose_chars(vty);
+                       break;
+               case CONTROL('U'):
+                       vty_kill_line_from_beginning(vty);
+                       break;
+               case CONTROL('W'):
+                       vty_backward_kill_word(vty);
+                       break;
+               case CONTROL('Z'):
+                       vty_end_config(vty);
+                       break;
+               case '\n':
+               case '\r':
+                       vty_out(vty, "%s", VTY_NEWLINE);
+                       vty_execute(vty);
+                       break;
+               case '\t':
+                       vty_complete_command(vty);
+                       break;
+               case '?':
+                       if (vty->node == AUTH_NODE
+                           || vty->node == AUTH_ENABLE_NODE)
+                               vty_self_insert(vty, buf[i]);
+                       else
+                               vty_describe_command(vty);
+                       break;
+               case '\033':
+                       if (i + 1 < nbytes && buf[i + 1] == '[') {
+                               vty->escape = VTY_ESCAPE;
+                               i++;
+                       } else
+                               vty->escape = VTY_PRE_ESCAPE;
+                       break;
+               default:
+                       if (buf[i] > 31 && buf[i] < 127)
+                               vty_self_insert(vty, buf[i]);
+                       break;
+               }
+       }
+
+       /* Check status. */
+       if (vty->status == VTY_CLOSE)
+               vty_close(vty);
+       else {
+               vty_event(VTY_WRITE, vty_sock, vty);
+               vty_event(VTY_READ, vty_sock, vty);
+       }
+       return 0;
+}
+
+/* Read up configuration file */
+static int
+vty_read_file(FILE *confp, void *priv)
+{
+       int ret;
+       struct vty *vty;
+
+       vty = vty_new();
+       vty->fd = 0;
+       vty->type = VTY_FILE;
+       vty->node = CONFIG_NODE;
+       vty->priv = priv;
+
+       ret = config_from_file(vty, confp);
+
+       if (ret != CMD_SUCCESS) {
+               switch (ret) {
+               case CMD_ERR_AMBIGUOUS:
+                       fprintf(stderr, "Ambiguous command.\n");
+                       break;
+               case CMD_ERR_NO_MATCH:
+                       fprintf(stderr, "There is no such command.\n");
+                       break;
+               }
+               fprintf(stderr, "Error occurred during reading below "
+                       "line:\n%s\n", vty->buf);
+               vty_close(vty);
+               return -EINVAL;
+       }
+
+       vty_close(vty);
+       return 0;
+}
+
+/* Create new vty structure. */
+struct vty *
+vty_create (int vty_sock, void *priv)
+{
+  struct vty *vty;
+
+       struct termios t;
+
+       tcgetattr(vty_sock, &t);
+       cfmakeraw(&t);
+       tcsetattr(vty_sock, TCSANOW, &t);
+
+  /* Allocate new vty structure and set up default values. */
+  vty = vty_new ();
+  vty->fd = vty_sock;
+  vty->priv = priv;
+  vty->type = VTY_TERM;
+  if (no_password_check)
+    {
+      if (host.advanced)
+       vty->node = ENABLE_NODE;
+      else
+       vty->node = VIEW_NODE;
+    }
+  else
+    vty->node = AUTH_NODE;
+  vty->fail = 0;
+  vty->cp = 0;
+  vty_clear_buf (vty);
+  vty->length = 0;
+  memset (vty->hist, 0, sizeof (vty->hist));
+  vty->hp = 0;
+  vty->hindex = 0;
+  vector_set_index (vtyvec, vty_sock, vty);
+  vty->status = VTY_NORMAL;
+  if (host.lines >= 0)
+    vty->lines = host.lines;
+  else
+    vty->lines = -1;
+
+  if (! no_password_check)
+    {
+      /* Vty is not available if password isn't set. */
+      if (host.password == NULL && host.password_encrypt == NULL)
+       {
+         vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
+         vty->status = VTY_CLOSE;
+         vty_close (vty);
+         return NULL;
+       }
+    }
+
+  /* Say hello to the world. */
+  vty_hello (vty);
+  if (! no_password_check)
+    vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+  /* Setting up terminal. */
+  vty_will_echo (vty);
+  vty_will_suppress_go_ahead (vty);
+
+  vty_dont_linemode (vty);
+  vty_do_window_size (vty);
+  /* vty_dont_lflow_ahead (vty); */
+
+  vty_prompt (vty);
+
+  /* Add read/write thread. */
+  vty_event (VTY_WRITE, vty_sock, vty);
+  vty_event (VTY_READ, vty_sock, vty);
+
+  return vty;
+}
+
+DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
+{
+       unsigned int i;
+       struct vty *v;
+
+       for (i = 0; i < vector_active(vtyvec); i++)
+               if ((v = vector_slot(vtyvec, i)) != NULL)
+                       vty_out(vty, "%svty[%d] %s",
+                               v->config ? "*" : " ", i, VTY_NEWLINE);
+       return CMD_SUCCESS;
+}
+
+/* Move to vty configuration mode. */
+DEFUN(line_vty,
+      line_vty_cmd,
+      "line vty", "Configure a terminal line\n" "Virtual terminal\n")
+{
+       vty->node = VTY_NODE;
+       return CMD_SUCCESS;
+}
+
+/* vty login. */
+DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
+{
+       no_password_check = 0;
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_vty_login,
+      no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
+{
+       no_password_check = 1;
+       return CMD_SUCCESS;
+}
+
+DEFUN(service_advanced_vty,
+      service_advanced_vty_cmd,
+      "service advanced-vty",
+      "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
+{
+       host.advanced = 1;
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_service_advanced_vty,
+      no_service_advanced_vty_cmd,
+      "no service advanced-vty",
+      NO_STR
+      "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
+{
+       host.advanced = 0;
+       return CMD_SUCCESS;
+}
+
+DEFUN(terminal_monitor,
+      terminal_monitor_cmd,
+      "terminal monitor",
+      "Set terminal line parameters\n"
+      "Copy debug output to the current terminal line\n")
+{
+       vty->monitor = 1;
+       return CMD_SUCCESS;
+}
+
+DEFUN(terminal_no_monitor,
+      terminal_no_monitor_cmd,
+      "terminal no monitor",
+      "Set terminal line parameters\n"
+      NO_STR "Copy debug output to the current terminal line\n")
+{
+       vty->monitor = 0;
+       return CMD_SUCCESS;
+}
+
+DEFUN(show_history,
+      show_history_cmd,
+      "show history", SHOW_STR "Display the session command history\n")
+{
+       int index;
+
+       for (index = vty->hindex + 1; index != vty->hindex;) {
+               if (index == VTY_MAXHIST) {
+                       index = 0;
+                       continue;
+               }
+
+               if (vty->hist[index] != NULL)
+                       vty_out(vty, "  %s%s", vty->hist[index], VTY_NEWLINE);
+
+               index++;
+       }
+
+       return CMD_SUCCESS;
+}
+
+/* Display current configuration. */
+static int vty_config_write(struct vty *vty)
+{
+       vty_out(vty, "line vty%s", VTY_NEWLINE);
+
+       /* login */
+       if (no_password_check)
+               vty_out(vty, " no login%s", VTY_NEWLINE);
+
+       vty_out(vty, "!%s", VTY_NEWLINE);
+
+       return CMD_SUCCESS;
+}
+
+struct cmd_node vty_node = {
+       VTY_NODE,
+       "%s(config-line)# ",
+       1,
+};
+
+/* Reset all VTY status. */
+void vty_reset()
+{
+       unsigned int i;
+       struct vty *vty;
+       struct thread *vty_serv_thread;
+
+       for (i = 0; i < vector_active(vtyvec); i++)
+               if ((vty = vector_slot(vtyvec, i)) != NULL) {
+                       buffer_reset(vty->obuf);
+                       vty->status = VTY_CLOSE;
+                       vty_close(vty);
+               }
+
+       for (i = 0; i < vector_active(Vvty_serv_thread); i++)
+               if ((vty_serv_thread =
+                    vector_slot(Vvty_serv_thread, i)) != NULL) {
+                       //thread_cancel (vty_serv_thread);
+                       vector_slot(Vvty_serv_thread, i) = NULL;
+                       close(i);
+               }
+}
+
+static void vty_save_cwd(void)
+{
+       char cwd[MAXPATHLEN];
+       char *c ;
+
+       c = getcwd(cwd, MAXPATHLEN);
+
+       if (!c) {
+               if (chdir(SYSCONFDIR) != 0)
+                   perror("chdir failed");
+               if (getcwd(cwd, MAXPATHLEN) == NULL)
+                   perror("getcwd failed");
+       }
+
+       vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
+       strcpy(vty_cwd, cwd);
+}
+
+char *vty_get_cwd()
+{
+       return vty_cwd;
+}
+
+int vty_shell_serv(struct vty *vty)
+{
+       return vty->type == VTY_SHELL_SERV ? 1 : 0;
+}
+
+void vty_init_vtysh()
+{
+       vtyvec = vector_init(VECTOR_MIN_SIZE);
+}
+
+extern void *tall_bsc_ctx;
+/* Install vty's own commands like `who' command. */
+void vty_init(struct vty_app_info *app_info)
+{
+       tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty");
+       tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
+       tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
+
+       cmd_init(1);
+
+       host.app_info = app_info;
+
+       /* For further configuration read, preserve current directory. */
+       vty_save_cwd();
+
+       vtyvec = vector_init(VECTOR_MIN_SIZE);
+
+       /* Install bgp top node. */
+       install_node(&vty_node, vty_config_write);
+
+       install_element_ve(&config_who_cmd);
+       install_element_ve(&show_history_cmd);
+       install_element(CONFIG_NODE, &line_vty_cmd);
+       install_element(CONFIG_NODE, &service_advanced_vty_cmd);
+       install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
+       install_element(CONFIG_NODE, &show_history_cmd);
+       install_element(ENABLE_NODE, &terminal_monitor_cmd);
+       install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
+
+       install_default(VTY_NODE);
+       install_element(VTY_NODE, &vty_login_cmd);
+       install_element(VTY_NODE, &no_vty_login_cmd);
+}
+
+int vty_read_config_file(const char *file_name, void *priv)
+{
+       FILE *cfile;
+       int rc;
+
+       cfile = fopen(file_name, "r");
+       if (!cfile)
+               return -ENOENT;
+
+       rc = vty_read_file(cfile, priv);
+       fclose(cfile);
+
+       host_config_set(file_name);
+
+       return rc;
+}