Merge commit 'af5ee34c353ea2868a4b04b227bc1b511e1ac42b'
authorSylvain Munaut <tnt@246tNt.com>
Fri, 17 Sep 2010 12:40:56 +0000 (14:40 +0200)
committerSylvain Munaut <tnt@246tNt.com>
Fri, 17 Sep 2010 12:40:56 +0000 (14:40 +0200)
32 files changed:
1  2 
src/shared/libosmocore/configure.in
src/shared/libosmocore/debian/changelog
src/shared/libosmocore/debian/compat
src/shared/libosmocore/debian/control
src/shared/libosmocore/debian/copyright
src/shared/libosmocore/debian/docs
src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log
src/shared/libosmocore/debian/libosmocore-dbg.dirs
src/shared/libosmocore/debian/libosmocore-dbg.install
src/shared/libosmocore/debian/libosmocore-dev.dirs
src/shared/libosmocore/debian/libosmocore-dev.install
src/shared/libosmocore/debian/libosmocore.dirs
src/shared/libosmocore/debian/libosmocore.install
src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1
src/shared/libosmocore/debian/patches/series
src/shared/libosmocore/debian/rules
src/shared/libosmocore/debian/source/format
src/shared/libosmocore/include/osmocom/vty/vty.h
src/shared/libosmocore/include/osmocore/Makefile.am
src/shared/libosmocore/include/osmocore/logging.h
src/shared/libosmocore/include/osmocore/process.h
src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
src/shared/libosmocore/src/Makefile.am
src/shared/libosmocore/src/gsm0808.c
src/shared/libosmocore/src/logging.c
src/shared/libosmocore/src/plugin.c
src/shared/libosmocore/src/process.c
src/shared/libosmocore/src/select.c
src/shared/libosmocore/src/vty/Makefile.am
src/shared/libosmocore/src/vty/command.c
src/shared/libosmocore/src/vty/telnet_interface.c
src/shared/libosmocore/src/vty/vty.c

index c3e0061,0000000..1684aaa
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,106 @@@
 +AC_INIT([libosmocore],
 +      m4_esyscmd([./git-version-gen .tarball-version]),
 +      [openbsc-devel@lists.openbsc.org])
 +
 +AM_INIT_AUTOMAKE([dist-bzip2])
 +
 +dnl kernel style compile messages
 +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 +
 +dnl checks for programs
 +AC_PROG_MAKE_SET
 +AC_PROG_CC
 +AC_PROG_INSTALL
 +LT_INIT
 +AC_PROG_LIBTOOL
 +
 +AC_CONFIG_MACRO_DIR([m4])
 +
 +dnl checks for header files
 +AC_HEADER_STDC
 +AC_CHECK_HEADERS(execinfo.h sys/select.h)
 +
 +# The following test is taken from WebKit's webkit.m4
 +saved_CFLAGS="$CFLAGS"
 +CFLAGS="$CFLAGS -fvisibility=hidden "
 +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
 +AC_COMPILE_IFELSE([char foo;],
 +      [ AC_MSG_RESULT([yes])
 +        SYMBOL_VISIBILITY="-fvisibility=hidden"],
 +        AC_MSG_RESULT([no]))
 +CFLAGS="$saved_CFLAGS"
 +AC_SUBST(SYMBOL_VISIBILITY)
 +
 +dnl Generate the output
 +AM_CONFIG_HEADER(config.h)
 +
 +AC_ARG_ENABLE(talloc,
 +      [AS_HELP_STRING(
 +              [--disable-talloc],
 +              [Disable building talloc memory allocator]
 +      )],
 +      [enable_talloc=0], [enable_talloc=1])
 +AM_CONDITIONAL(ENABLE_TALLOC, test "x$enable_talloc" = "x1")
 +
 +AC_ARG_ENABLE(plugin,
 +      [AS_HELP_STRING(
 +              [--disable-plugin],
 +              [Disable support for dlopen plugins],
 +      )],
 +      [enable_plugin=0], [enable_plugin=1])
 +AM_CONDITIONAL(ENABLE_PLUGIN, test "x$enable_plugin" = "x1")
 +
 +AC_ARG_ENABLE(tests,
 +      [AS_HELP_STRING(
 +              [--disable-tests],
 +              [Disable building test programs]
 +      )],
 +      [enable_tests=0], [enable_tests=1])
 +AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1")
 +
 +AC_ARG_ENABLE(vty,
 +      [AS_HELP_STRING(
 +              [--disable-vty],
 +              [Disable building VTY telnet interface]
 +      )],
 +      [enable_vty=0], [enable_vty=1])
 +AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1")
 +
 +AC_ARG_ENABLE(panic_infloop,
 +      [AS_HELP_STRING(
 +              [--enable-panic-infloop],
 +              [Trigger infinite loop on panic rather than fprintf/abort]
 +      )],
 +      [panic_infloop=1], [panic_infloop=0])
 +if test "x$panic_infloop" = "x1"
 +then
 +      AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort])
 +fi
 +
++AC_ARG_ENABLE(bsc_fd_check,
++      [AS_HELP_STRING(
++              [--enable-bsc-fd-check],
++              [Instrument bsc_register_fd to check that the fd is registered]
++      )],
++      [fd_check=1], [fd_check=0])
++if test "x$fd_check" = "x1"
++then
++      AC_DEFINE([BSC_FD_CHECK],[1],[Instrument the bsc_register_fd])
++fi
++
 +
 +AC_OUTPUT(
 +      libosmocore.pc
 +      libosmovty.pc
 +      include/osmocom/Makefile
 +      include/osmocom/vty/Makefile
 +      include/osmocom/crypt/Makefile
 +      include/osmocore/Makefile
 +      include/osmocore/protocol/Makefile
 +      include/Makefile
 +      src/Makefile
 +      src/vty/Makefile
 +      tests/Makefile
 +      tests/timer/Makefile
 +      tests/sms/Makefile
 +      Makefile)
index 0000000,0000000..5783127
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++libosmocore (0.1.17-1) unstable; urgency=low
++
++  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
++
++ -- Harald Welte <laforge@gnumonks.org>  Tue, 24 Aug 2010 10:55:04 +0200
index 0000000,0000000..7f8f011
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++7
index 0000000,0000000..1348ec3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++Source: libosmocore
++Section: libs
++Priority: optional
++Maintainer: Harald Welte <laforge@gnumonks.org>
++Build-Depends: debhelper (>= 7.0.50~), autotools-dev
++Standards-Version: 3.8.4
++Homepage: http://bb.osmocom.org/trac/wiki/libosmocore
++Vcs-Git: git://git.osmocom.org/libosmocore.git
++Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmocore.git;a=summary
++
++Package: libosmocore
++Section: libs
++Architecture: any
++Depends: ${shlibs:Depends}, ${misc:Depends}
++Description: Open Source MObile COMmunications CORE library
++
++Package: libosmocore-dev
++Section: libdevel
++Architecture: any
++Depends: ${shlibs:Depends}, ${misc:Depends}
++Description: Development headers for Open Source MObile COMmunications CORE library
++
++#Package: libosmocore-dbg
++#Section: libdevel
++#Architecture: any
++#Depends: ${shlibs:Depends}, ${misc:Depends}
++#Description: Debug symbols for Open Source MObile COMmunications CORE library
index 0000000,0000000..c450be5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++This work was packaged for Debian by:
++
++    Harald Welte <laforge@gnumonks.org> on Tue, 24 Aug 2010 10:55:04 +0200
++
++It was downloaded from:
++
++    git://git.osmocom.org/libosmocore.git
++
++Upstream Author(s):
++
++    Harald Welte <laforge@gnumonks.org>
++    Holger Hans Peter Freyther <zecke@selfish.org>
++    Sylvain Munaut <tnt@246tNt.com>
++    Daniel Willmann <daniel@totalueberwachung.de>
++    Golde <nico@ngolde.de>
++      For src/talloc.c and include/osmocore/talloc.h:
++    Andrew Tridgell
++    Stefan Metzmacher
++      For src/vty/* and include/osmocom/vty/*
++    Kunihiro Ishiguro
++
++Copyright:
++
++    Copyright (C) 2008-2010 Harald Welte <laforge@gnumonks.org>
++    Copyright (C) 2008-2010 Holger Hans Peter Freyther <zecke@selfish.org>
++    Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
++    Copyright (C) 2009-2010 On-Waves
++    Copyright (C) 2008 Daniel Willmann <daniel@totalueberwachung.de>
++    Copyright (C) 2010 Nico Golde <nico@ngolde.de>
++      For src/talloc.c and include/osmocore/talloc.h:
++    Copyright (C) 2004 Andrew Tridgell
++    Copyright (C) 2006 Stefan Metzmacher
++      For src/vty/* and include/osmocom/vty/*
++    Copyright (C) 1998 Kunihiro Ishiguro
++
++License:
++
++    GNU General Public License, Version 2 or later
++
++The Debian packaging is:
++
++    Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
++
++# Please chose a license for your packaging work. If the program you package
++# uses a mainstream license, using the same license is the safest choice.
++# Please avoid to pick license terms that are more restrictive than the
++# packaged work, as it may make Debian's contributions unacceptable upstream.
++# If you just want it to be GPL version 3, leave the following lines in.
++
++and is licensed under the GPL version 3,
++see "/usr/share/common-licenses/GPL-3".
++
++# Please also look if there are files or directories which have a
++# different copyright/license attached and list them here.
index 0000000,0000000..e69de29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000,0000000..2742cb4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++dh_auto_configure
++dh_auto_build
++dh_auto_test
++dh_prep
++dh_installdirs
++dh_auto_install
index 0000000,0000000..af59b0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++usr/lib/debug/lib
index 0000000,0000000..7ce0212
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++usr/lib/debug/lib/*
index 0000000,0000000..e168dc6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++usr/lib
++usr/include
++usr/include/osmocore
++usr/include/osmocom
++usr/include/osmocom/vty
index 0000000,0000000..eec0e15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++usr/include/*
++usr/lib/lib*.a
++usr/lib/lib*.so
++usr/lib/lib*.la
++usr/lib/pkgconfig/*
index 0000000,0000000..e168dc6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++usr/lib
++usr/include
++usr/include/osmocore
++usr/include/osmocom
++usr/include/osmocom/vty
index 0000000,0000000..d0dbfd1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++usr/lib/lib*.so.*
index 0000000,0000000..c0a54bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++Description: Upstream changes introduced in version 0.1.17-1
++ This patch has been created by dpkg-source during the package build.
++ Here's the last changelog entry, hopefully it gives details on why
++ those changes were made:
++ .
++ libosmocore (0.1.17-1) unstable; urgency=low
++ .
++   * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
++ .
++ The person named in the Author field signed this changelog entry.
++Author: Harald Welte <laforge@gnumonks.org>
++
++---
++The information above should follow the Patch Tagging Guidelines, please
++checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
++are templates for supplementary fields that you might want to add:
++
++Origin: <vendor|upstream|other>, <url of original patch>
++Bug: <url in upstream bugtracker>
++Bug-Debian: http://bugs.debian.org/<bugnumber>
++Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
++Forwarded: <no|not-needed|url proving that it has been forwarded>
++Reviewed-By: <name and email of someone who approved the patch>
++Last-Update: <YYYY-MM-DD>
++
++--- /dev/null
+++++ libosmocore-0.1.17/.version
++@@ -0,0 +1 @@
+++0.1.17
++--- /dev/null
+++++ libosmocore-0.1.17/copyright
++@@ -0,0 +1,14 @@
+++Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
+++Name: libosmocore
+++Maintainer: Harald Welte <laforge@gnumonks.org>
+++Source: git://git.osmocom.org/libosmocore.git
+++
+++Copyright: 2008-2010 Harald Welte <laforge@gnumonks.org>
+++License: GPL-2+
+++
+++Files: src/talloc.c include/osmocore/talloc.h
+++Copyright: 2004 Andrew Tridgell
+++License: LGPL-3+
+++
+++Files: include/osmocore/linuxlist.h
+++License: GPL-2
index 0000000,0000000..0ca407b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++debian-changes-0.1.17-1
index 0000000,0000000..a81850a
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++#!/usr/bin/make -f
++# -*- makefile -*-
++# Sample debian/rules that uses debhelper.
++# This file was originally written by Joey Hess and Craig Small.
++# As a special exception, when this file is copied by dh-make into a
++# dh-make output file, you may use that output file without restriction.
++# This special exception was added by Craig Small in version 0.37 of dh-make.
++
++# Uncomment this to turn on verbose mode.
++#export DH_VERBOSE=1
++
++CFLAGS = -Wall -g
++
++%:
++      dh $@ 
++
++#override_dh_strip:
++#     dh_strip --dbg-package=libosmocore-dbg
++
index 0000000,0000000..163aaf8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++3.0 (quilt)
index e7399ba,0000000..8035585
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,162 @@@
- void *tall_vty_ctx;
 +#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);
++      int (*is_config_node)(struct vty *vty, int node);
 +};
 +
 +/* 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 *vty_current_index(struct vty *);
++int vty_current_node(struct vty *vty);
 +
++extern void *tall_vty_ctx;
 +#endif
index ecdc65c,0000000..c8b614f
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
-                  plugin.h crc16.h panic.h
 +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 rate_ctr.h gsmtap_util.h \
++                 plugin.h crc16.h panic.h process.h
 +
 +if ENABLE_TALLOC
 +osmocore_HEADERS += talloc.h
 +endif
 +
 +osmocoredir = $(includedir)/osmocore
 +
 +SUBDIRS = protocol
index 7bf2440,0000000..7f33155
mode 100644,000000..100644
--- /dev/null
@@@ -1,134 -1,0 +1,139 @@@
-               } tgt_stdout;
 +#ifndef _OSMOCORE_LOGGING_H
 +#define _OSMOCORE_LOGGING_H
 +
 +#include <stdio.h>
 +#include <stdint.h>
 +#include <osmocore/linuxlist.h>
 +
 +#define LOG_MAX_CATEGORY      32
 +#define LOG_MAX_CTX           8
 +#define LOG_MAX_FILTERS       8
 +
 +#define DEBUG
 +
 +#ifdef DEBUG
 +#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
 +#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
 +#else
 +#define DEBUGP(xss, fmt, args...)
 +#define DEBUGPC(ss, fmt, args...)
 +#endif
 +
 +#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
 +
 +void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
 +
 +/* new logging interface */
 +#define LOGP(ss, level, fmt, args...) \
 +      logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
 +#define LOGPC(ss, level, fmt, args...) \
 +      logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
 +
 +/* different levels */
 +#define LOGL_DEBUG    1       /* debugging information */
 +#define LOGL_INFO     3
 +#define LOGL_NOTICE   5       /* abnormal/unexpected condition */
 +#define LOGL_ERROR    7       /* error condition, requires user action */
 +#define LOGL_FATAL    8       /* fatal, program aborted */
 +
 +#define LOG_FILTER_ALL        0x0001
 +
 +struct log_category {
 +      uint8_t loglevel;
 +      uint8_t enabled;
 +};
 +
 +struct log_info_cat {
 +      const char *name;
 +      const char *color;
 +      const char *description;
 +      uint8_t loglevel;
 +      uint8_t enabled;
 +};
 +
 +/* log context information, passed to filter */
 +struct log_context {
 +      void *ctx[LOG_MAX_CTX+1];
 +};
 +
 +struct log_target;
 +
 +typedef int log_filter(const struct log_context *ctx,
 +                     struct log_target *target);
 +
 +struct log_info {
 +      /* filter callback function */
 +      log_filter *filter_fn;
 +
 +      /* per-category information */
 +      const struct log_info_cat *cat;
 +      unsigned int num_cat;
 +};
 +
 +struct log_target {
 +        struct llist_head entry;
 +
 +      int filter_map;
 +      void *filter_data[LOG_MAX_FILTERS+1];
 +
 +      struct log_category categories[LOG_MAX_CATEGORY+1];
 +      uint8_t loglevel;
 +      int use_color:1;
 +      int print_timestamp:1;
 +
 +      union {
 +              struct {
 +                      FILE *out;
++                      const char *fname;
++              } tgt_file;
 +
 +              struct {
 +                      int priority;
 +              } tgt_syslog;
 +
 +              struct {
 +                      void *vty;
 +              } tgt_vty;
 +      };
 +
 +        void (*output) (struct log_target *target, const char *string);
 +};
 +
 +/* use the above macros */
 +void logp2(unsigned int subsys, unsigned int level, char *file,
 +         int line, int cont, const char *format, ...)
 +                              __attribute__ ((format (printf, 6, 7)));
 +void log_init(const struct log_info *cat);
 +
 +/* context management */
 +void log_reset_context(void);
 +int log_set_context(uint8_t ctx, void *value);
 +
 +/* filter on the targets */
 +void log_set_all_filter(struct log_target *target, int);
 +
 +void log_set_use_color(struct log_target *target, int);
 +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);
 +
 +/* management of the targets */
 +struct log_target *log_target_create(void);
++void log_target_destroy(struct log_target *target);
 +struct log_target *log_target_create_stderr(void);
++struct log_target *log_target_create_file(const char *fname);
++int log_target_file_reopen(struct log_target *tgt);
++
 +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 0000000,0000000..2d66382
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#ifndef _OSMO_PROCESS_H
++#define _OSMO_PROCESS_H
++
++int osmo_daemonize(void);
++
++#endif
index 80a455d,0000000..3ad7dfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,1243 -1,0 +1,1262 @@@
-                spare:3,
-                cr:1;
 +#ifndef PROTO_GSM_04_08_H
 +#define PROTO_GSM_04_08_H
 +
 +#include <stdint.h>
 +
 +/* GSM TS 04.08  definitions */
 +struct gsm_lchan;
 +
 +/* Chapter 10.5.1.5 */
 +struct gsm48_classmark1 {
 +      uint8_t pwr_lev:3,
 +               a5_1:1,
 +               es_ind:1,
 +               rev_lev:2,
 +               spare:1;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.1.6 */
 +struct gsm48_classmark2 {
 +      uint8_t pwr_lev:3,
 +               a5_1:1,
 +               es_ind:1,
 +               rev_lev:2,
 +               spare:1;
 +      uint8_t fc:1,
 +               vgcs:1,
 +               vbs:1,
 +               sm_cap:1,
 +               ss_scr:2,
 +               ps_cap:1,
 +               spare2:1;
 +      uint8_t a5_2:1,
 +               a5_3:1,
 +               cmsp:1,
 +               solsa:1,
 +               spare3:1,
 +               lcsva_cap:1,
 +               spare4:1,
 +               cm3:1;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.1b.3 */
 +struct gsm48_range_1024 {
 +      uint8_t w1_hi:2,
 +               f0:1,
 +               form_id:5;
 +      uint8_t w1_lo;
 +      uint8_t w2_hi;
 +      uint8_t w3_hi:7,
 +               w2_lo:1;
 +      uint8_t w4_hi:6,
 +               w3_lo:2;
 +      uint8_t w5_hi:6,
 +               w4_lo:2;
 +      uint8_t w6_hi:6,
 +               w5_lo:2;
 +      uint8_t w7_hi:6,
 +               w6_lo:2;
 +      uint8_t w8_hi:6,
 +               w7_lo:2;
 +      uint8_t w9:7,
 +               w8_lo:1;
 +      uint8_t w11_hi:1,
 +               w10:7;
 +      uint8_t w12_hi:2,
 +               w11_lo:6;
 +      uint8_t w13_hi:3,
 +               w12_lo:5;
 +      uint8_t w14_hi:4,
 +               w13_lo:4;
 +      uint8_t w15_hi:5,
 +               w14_lo:3;
 +      uint8_t w16:6,
 +               w15_lo:2;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.1b.4 */
 +struct gsm48_range_512 {
 +      uint8_t orig_arfcn_hi:1,
 +               form_id:7;
 +      uint8_t orig_arfcn_mid;
 +      uint8_t w1_hi:7,
 +               orig_arfcn_lo:1;
 +      uint8_t w2_hi:6,
 +               w1_lo:2;
 +      uint8_t w3_hi:6,
 +               w2_lo:2;
 +      uint8_t w4_hi:6,
 +               w3_lo:2;
 +      uint8_t w5:7,
 +               w4_lo:1;
 +      uint8_t w7_hi:1,
 +               w6:7;
 +      uint8_t w8_hi:2,
 +               w7_lo:6;
 +      uint8_t w9_hi:4,
 +               w8_lo:4;
 +      uint8_t w10:6,
 +               w9_lo:2;
 +      uint8_t w12_hi:2,
 +               w11:6;
 +      uint8_t w13_hi:4,
 +               w12_lo:4;
 +      uint8_t w14:6,
 +               w13_lo:2;
 +      uint8_t w16_hi:2,
 +               w15:6;
 +      uint8_t w17:5,
 +               w16_lo:3;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.1b.5 */
 +struct gsm48_range_256 {
 +      uint8_t orig_arfcn_hi:1,
 +               form_id:7;
 +      uint8_t orig_arfcn_mid;
 +      uint8_t w1_hi:7,
 +               orig_arfcn_lo:1;
 +      uint8_t w2:7,
 +               w1_lo:1;
 +      uint8_t w4_hi:1,
 +               w3:7;
 +      uint8_t w5_hi:3,
 +               w4_lo:5;
 +      uint8_t w6_hi:5,
 +               w5_lo:3;
 +      uint8_t w8_hi:1,
 +               w7:6,
 +               w6_lo:1;
 +      uint8_t w9_hi:4,
 +               w8_lo:4;
 +      uint8_t w11_hi:2,
 +               w10:5,
 +               w9_lo:1;
 +      uint8_t w12:5,
 +               w11_lo:3;
 +      uint8_t w14_hi:3,
 +               w13:5;
 +      uint8_t w16_hi:1,
 +               w15:5,
 +               w14_lo:2;
 +      uint8_t w18_hi:1,
 +               w17:4,
 +               w16_lo:3;
 +      uint8_t w20_hi:1,
 +               w19:4,
 +               w18_lo:3;
 +      uint8_t spare:1,
 +               w21:4,
 +               w20_lo:3;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.1b.6 */
 +struct gsm48_range_128 {
 +      uint8_t orig_arfcn_hi:1,
 +               form_id:7;
 +      uint8_t orig_arfcn_mid;
 +      uint8_t w1:7,
 +               orig_arfcn_lo:1;
 +      uint8_t w3_hi:2,
 +               w2:6;
 +      uint8_t w4_hi:4,
 +               w3_lo:4;
 +      uint8_t w6_hi:2,
 +               w5:5,
 +               w4_lo:1;
 +      uint8_t w7:5,
 +               w6_lo:3;
 +      uint8_t w9:4,
 +               w8:4;
 +      uint8_t w11:4,
 +               w10:4;
 +      uint8_t w13:4,
 +               w12:4;
 +      uint8_t w15:4,
 +               w14:4;
 +      uint8_t w18_hi:2,
 +               w17:3,
 +               w16:3;
 +      uint8_t w21_hi:1,
 +               w20:3,
 +               w19:3,
 +               w18_lo:1;
 +      uint8_t w23:3,
 +               w22:3,
 +               w21_lo:2;
 +      uint8_t w26_hi:2,
 +               w25:3,
 +               w24:3;
 +      uint8_t spare:1,
 +               w28:3,
 +               w27:3,
 +               w26_lo:1;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.1b.7 */
 +struct gsm48_var_bit {
 +      uint8_t orig_arfcn_hi:1,
 +               form_id:7;
 +      uint8_t orig_arfcn_mid;
 +      uint8_t rrfcn1_7:7,
 +               orig_arfcn_lo:1;
 +      uint8_t rrfcn8_111[13];
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.5 */
 +struct gsm48_chan_desc {
 +      uint8_t chan_nr;
 +      union {
 +              struct {
 +                      uint8_t maio_high:4,
 +                               h:1,
 +                               tsc:3;
 +                      uint8_t hsn:6,
 +                               maio_low:2;
 +              } h1;
 +              struct {
 +                      uint8_t arfcn_high:2,
 +                               spare:2,
 +                               h:1,
 +                               tsc:3;
 +                      uint8_t arfcn_low;
 +              } h0;
 +      };
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.20 */
 +struct gsm48_meas_res {
 +      uint8_t rxlev_full:6,
 +               dtx_used:1,
 +               ba_used:1;
 +      uint8_t rxlev_sub:6,
 +               meas_valid:1,
 +               spare:1;
 +      uint8_t no_nc_n_hi:1,
 +               rxqual_sub:3,
 +               rxqual_full:3,
 +               spare2:1;
 +      uint8_t rxlev_nc1:6,
 +               no_nc_n_lo:2;
 +      uint8_t bsic_nc1_hi:3,
 +               bcch_f_nc1:5;
 +      uint8_t rxlev_nc2_hi:5,
 +               bsic_nc1_lo:3;
 +      uint8_t bsic_nc2_hi:2,
 +               bcch_f_nc2:5,
 +               rxlev_nc2_lo:1;
 +      uint8_t rxlev_nc3_hi:4,
 +               bsic_nc2_lo:4;
 +      uint8_t bsic_nc3_hi:1,
 +               bcch_f_nc3:5,
 +               rxlev_nc3_lo:2;
 +      uint8_t rxlev_nc4_hi:3,
 +               bsic_nc3_lo:5;
 +      uint8_t bcch_f_nc4:5,
 +               rxlev_nc4_lo:3;
 +      uint8_t rxlev_nc5_hi:2,
 +               bsic_nc4:6;
 +      uint8_t bcch_f_nc5_hi:4,
 +               rxlev_nc5_lo:4;
 +      uint8_t rxlev_nc6_hi:1,
 +               bsic_nc5:6,
 +               bcch_f_nc5_lo:1;
 +      uint8_t bcch_f_nc6_hi:3,
 +               rxlev_nc6_lo:5;
 +      uint8_t bsic_nc6:6,
 +               bcch_f_nc6_lo:2;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.21aa */
 +struct gsm48_multi_rate_conf {
 +      uint8_t smod : 2,
 +               spare: 1,
 +               icmi : 1,
 +               nscb : 1,
 +               ver : 3;
 +      uint8_t m4_75 : 1,
 +               m5_15 : 1,
 +               m5_90 : 1,
 +               m6_70 : 1,
 +               m7_40 : 1,
 +               m7_95 : 1,
 +               m10_2 : 1,
 +               m12_2 : 1;
 +} __attribute__((packed));
 +
 +/* Chapter 10.5.2.28(a) */
 +struct gsm48_power_cmd {
 +      uint8_t power_level:5,
 +               spare:2,
 +               atc:1;
 +} __attribute__((packed));
 +
 +/* Chapter 10.5.2.29 */
 +struct gsm48_rach_control {
 +      uint8_t re :1,
 +               cell_bar :1,
 +               tx_integer :4,
 +               max_trans :2;
 +      uint8_t t2;
 +      uint8_t t3;
 +} __attribute__ ((packed));
 +
 +
 +/* Chapter 10.5.2.30 */
 +struct gsm48_req_ref {
 +      uint8_t ra;
 +      uint8_t t3_high:3,
 +               t1:5;
 +      uint8_t t2:5,
 +               t3_low:3;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.38 */
 +struct gsm48_start_time {
 +      uint8_t t3_high:3,
 +               t1:5;
 +      uint8_t t2:5,
 +               t3_low:3;
 +} __attribute__ ((packed));
 +
 +/* Chapter 10.5.2.39 */
 +struct gsm48_sync_ind {
 +      uint8_t si:2,
 +               rot:1,
 +               nci:1,
 +               sync_ie:4;
 +} __attribute__((packed));
 +
 +/*
 + * Chapter 9.1.5/9.1.6
 + *
 + * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a
 + */
 +struct gsm48_chan_mode_modify {
 +      struct gsm48_chan_desc chan_desc;
 +      uint8_t mode;
 +} __attribute__ ((packed));
 +
 +enum gsm48_chan_mode {
 +      GSM48_CMODE_SIGN        = 0x00,
 +      GSM48_CMODE_SPEECH_V1   = 0x01,
 +      GSM48_CMODE_SPEECH_EFR  = 0x21,
 +      GSM48_CMODE_SPEECH_AMR  = 0x41,
 +      GSM48_CMODE_DATA_14k5   = 0x0f,
 +      GSM48_CMODE_DATA_12k0   = 0x03,
 +      GSM48_CMODE_DATA_6k0    = 0x0b,
 +      GSM48_CMODE_DATA_3k6    = 0x23,
 +};
 +
 +/* Chapter 9.1.2 */
 +struct gsm48_ass_cmd {
 +      /* Semantic is from 10.5.2.5a */
 +      struct gsm48_chan_desc chan_desc;
 +      uint8_t power_command;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
++/* Chapter 9.1.13 */
++struct gsm48_frq_redef {
++      /* Semantic is from 10.5.2.5a */
++      struct gsm48_chan_desc chan_desc;
++      uint8_t mob_alloc_len;
++      uint8_t mob_alloc[0];
++} __attribute__((packed));
++
 +/* Chapter 10.5.2.2 */
 +struct gsm48_cell_desc {
 +      uint8_t bcc:3,
 +               ncc:3,
 +               arfcn_hi:2;
 +      uint8_t arfcn_lo;
 +} __attribute__((packed));
 +
 +/* Chapter 9.1.15 */
 +struct gsm48_ho_cmd {
 +      struct gsm48_cell_desc cell_desc;
 +      struct gsm48_chan_desc chan_desc;
 +      uint8_t ho_ref;
 +      uint8_t power_command;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Chapter 9.1.18 */
 +struct gsm48_imm_ass {
 +      uint8_t l2_plen;
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t page_mode;
 +      struct gsm48_chan_desc chan_desc;
 +      struct gsm48_req_ref req_ref;
 +      uint8_t timing_advance;
 +      uint8_t mob_alloc_len;
 +      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! */
 +      uint16_t lac;
 +} __attribute__ ((packed));
 +
 +/* Section 9.2.2 */
 +struct gsm48_auth_req {
 +      uint8_t key_seq:4,
 +               spare:4;
 +      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,
 +               key_seq:4;
 +      struct gsm48_loc_area_id lai;
 +      struct gsm48_classmark1 classmark1;
 +      uint8_t mi_len;
 +      uint8_t mi[0];
 +} __attribute__ ((packed));
 +
 +/* Section 10.1 */
 +struct gsm48_hdr {
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t data[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.3x System information Type header */
 +struct gsm48_system_information_type_header {
 +      uint8_t l2_plen;
 +      uint8_t rr_protocol_discriminator :4,
 +              skip_indicator:4; 
 +      uint8_t system_information;
 +} __attribute__ ((packed));
 +
 +/* Section 10.5.2.4 Cell Selection Parameters */
 +struct gsm48_cell_sel_par {
 +      uint8_t ms_txpwr_max_ccch:5,    /* GSM 05.08 MS-TXPWR-MAX-CCCH */
 +               cell_resel_hyst:3;     /* GSM 05.08 CELL-RESELECT-HYSTERESIS */
 +      uint8_t rxlev_acc_min:6,        /* GSM 05.08 RXLEV-ACCESS-MIN */
 +               neci:1,
 +               acs:1;
 +} __attribute__ ((packed));
 +
 +/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
 +struct gsm48_control_channel_descr {
 +      uint8_t ccch_conf :3,
 +              bs_ag_blks_res :3,
 +              att :1,
 +              spare1 :1;
 +      uint8_t bs_pa_mfrms : 3,
 +              spare2 :5;
 +      uint8_t t3212;
 +} __attribute__ ((packed));
 +
 +struct gsm48_cell_options {
 +      uint8_t radio_link_timeout:4,
 +               dtx:2,
 +               pwrc:1,
 +               spare:1;
 +} __attribute__ ((packed));
 +
 +/* Section 9.2.9 CM service request */
 +struct gsm48_service_request {
 +      uint8_t cm_service_type : 4,
 +               cipher_key_seq  : 4;
 +      /* length + 3 bytes */
 +      uint32_t classmark;
 +      uint8_t mi_len;
 +      uint8_t mi[0];
 +      /* optional priority level */
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.31 System information Type 1 */
 +struct gsm48_system_information_type_1 {
 +      struct gsm48_system_information_type_header header;
 +      uint8_t cell_channel_description[16];
 +      struct gsm48_rach_control rach_control;
 +      uint8_t rest_octets[0]; /* NCH position on the CCCH */
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.32 System information Type 2 */
 +struct gsm48_system_information_type_2 {
 +      struct gsm48_system_information_type_header header;
 +      uint8_t bcch_frequency_list[16];
 +      uint8_t ncc_permitted;
 +      struct gsm48_rach_control rach_control;
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.33 System information Type 2bis */
 +struct gsm48_system_information_type_2bis {
 +      struct gsm48_system_information_type_header header;
 +      uint8_t bcch_frequency_list[16];
 +      struct gsm48_rach_control rach_control;
 +      uint8_t rest_octets[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.34 System information Type 2ter */
 +struct gsm48_system_information_type_2ter {
 +      struct gsm48_system_information_type_header header;
 +      uint8_t ext_bcch_frequency_list[16];
 +      uint8_t rest_octets[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.35 System information Type 3 */
 +struct gsm48_system_information_type_3 {
 +      struct gsm48_system_information_type_header header;
 +      uint16_t cell_identity;
 +      struct gsm48_loc_area_id lai;
 +      struct gsm48_control_channel_descr control_channel_desc;
 +      struct gsm48_cell_options cell_options;
 +      struct gsm48_cell_sel_par cell_sel_par;
 +      struct gsm48_rach_control rach_control;
 +      uint8_t rest_octets[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.36 System information Type 4 */
 +struct gsm48_system_information_type_4 {
 +      struct gsm48_system_information_type_header header;
 +      struct gsm48_loc_area_id lai;
 +      struct gsm48_cell_sel_par cell_sel_par;
 +      struct gsm48_rach_control rach_control;
 +      /*      optional CBCH conditional CBCH... followed by
 +              mandantory SI 4 Reset Octets
 +       */
 +      uint8_t data[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.37 System information Type 5 */
 +struct gsm48_system_information_type_5 {
 +      uint8_t rr_protocol_discriminator :4,
 +              skip_indicator:4; 
 +      uint8_t system_information;
 +      uint8_t bcch_frequency_list[16];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.38 System information Type 5bis */
 +struct gsm48_system_information_type_5bis {
 +        uint8_t rr_protocol_discriminator :4,
 +               skip_indicator:4;
 +      uint8_t system_information;
 +      uint8_t bcch_frequency_list[16];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.39 System information Type 5ter */
 +struct gsm48_system_information_type_5ter {
 +        uint8_t rr_protocol_discriminator :4,
 +               skip_indicator:4;
 +      uint8_t system_information;
 +      uint8_t bcch_frequency_list[16];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.40 System information Type 6 */
 +struct gsm48_system_information_type_6 {
 +      uint8_t rr_protocol_discriminator :4,
 +              skip_indicator:4; 
 +      uint8_t system_information;
 +      uint16_t cell_identity;
 +      struct gsm48_loc_area_id lai;
 +      struct gsm48_cell_options cell_options;
 +      uint8_t ncc_permitted;
 +      uint8_t rest_octets[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.43a System Information type 13 */
 +struct gsm48_system_information_type_13 {
 +      struct gsm48_system_information_type_header header;
 +      uint8_t rest_octets[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.2.12 IMSI Detach Indication */
 +struct gsm48_imsi_detach_ind {
 +      struct gsm48_classmark1 classmark1;
 +      uint8_t mi_len;
 +      uint8_t mi[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.1 */
 +struct gsm48_add_ass {
 +      /* Semantic is from 10.5.2.5 */
 +      struct gsm48_chan_desc chan_desc;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.3 */
 +struct gsm48_ass_cpl {
 +      uint8_t rr_cause;
 +} __attribute__((packed));
 +
 +/* Section 9.1.4 */
 +struct gsm48_ass_fail {
 +      uint8_t rr_cause;
 +} __attribute__((packed));
 +
++/* Section 9.1.3 */
++struct gsm48_ho_cpl {
++      uint8_t rr_cause;
++      uint8_t data[0];
++} __attribute__((packed));
++
++/* Section 9.1.4 */
++struct gsm48_ho_fail {
++      uint8_t rr_cause;
++} __attribute__((packed));
++
 +/* Section 9.1.7 */
 +struct gsm48_chan_rel {
 +      uint8_t rr_cause;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.9 */
 +struct gsm48_cip_mode_cmd {
 +      uint8_t sc:1,
 +               alg_id:3,
++               cr:1,
++               spare:3;
 +} __attribute__((packed));
 +
 +/* Section 9.1.11 */
 +struct gsm48_cm_change {
 +      uint8_t cm2_len;
 +      struct gsm48_classmark2 cm2;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.19 */
 +struct gsm48_imm_ass_ext {
 +      uint8_t l2_plen;
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t page_mode;
 +      struct gsm48_chan_desc chan_desc1;
 +      struct gsm48_req_ref req_ref1;
 +      uint8_t timing_advance1;
 +      struct gsm48_chan_desc chan_desc2;
 +      struct gsm48_req_ref req_ref2;
 +      uint8_t timing_advance2;
 +      uint8_t mob_alloc_len;
 +      uint8_t mob_alloc[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.20 */
 +struct gsm48_imm_ass_rej {
 +      uint8_t l2_plen;
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t page_mode;
 +      struct gsm48_req_ref req_ref1;
 +      uint8_t wait_ind1;
 +      struct gsm48_req_ref req_ref2;
 +      uint8_t wait_ind2;
 +      struct gsm48_req_ref req_ref3;
 +      uint8_t wait_ind3;
 +      struct gsm48_req_ref req_ref4;
 +      uint8_t wait_ind4;
 +      uint8_t rest[0];
 +} __attribute__ ((packed));
 +
 +/* Section 9.1.22 */
 +struct gsm48_paging1 {
 +      uint8_t l2_plen;
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t pag_mode:2,
 +               spare:2,
 +               cneed1:2,
 +               cneed2:2;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.23 */
 +struct gsm48_paging2 {
 +      uint8_t l2_plen;
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t pag_mode:2,
 +               spare:2,
 +               cneed1:2,
 +               cneed2:2;
 +      uint32_t tmsi1;
 +      uint32_t tmsi2;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.24 */
 +struct gsm48_paging3 {
 +      uint8_t l2_plen;
 +      uint8_t proto_discr;
 +      uint8_t msg_type;
 +      uint8_t pag_mode:2,
 +               spare:2,
 +               cneed1:2,
 +               cneed2:2;
 +      uint32_t tmsi1;
 +      uint32_t tmsi2;
 +      uint32_t tmsi3;
 +      uint32_t tmsi4;
 +      uint8_t cneed3:2,
 +               cneed4:2,
 +               spare2:4;
 +      uint8_t rest[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.25 */
 +struct gsm48_pag_rsp {
 +      uint8_t key_seq:3,
 +               spare:5;
 +      uint8_t cm2_len;
 +      struct gsm48_classmark2 cm2;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
 +/* Section 9.1.29 */
 +struct gsm48_rr_status {
 +      uint8_t rr_cause;
 +} __attribute__((packed));
 +
 +/* Section 10.2 + GSM 04.07 12.2.3.1.1 */
 +#define GSM48_PDISC_GROUP_CC  0x00
 +#define GSM48_PDISC_BCAST_CC  0x01
 +#define GSM48_PDISC_PDSS1     0x02
 +#define GSM48_PDISC_CC                0x03
 +#define GSM48_PDISC_PDSS2     0x04
 +#define GSM48_PDISC_MM                0x05
 +#define GSM48_PDISC_RR                0x06
 +#define GSM48_PDISC_MM_GPRS   0x08
 +#define GSM48_PDISC_SMS               0x09
 +#define GSM48_PDISC_SM_GPRS   0x0a
 +#define GSM48_PDISC_NC_SS     0x0b
 +#define GSM48_PDISC_LOC               0x0c
 +#define GSM48_PDISC_MASK      0x0f
 +#define GSM48_PDISC_USSD      0x11
 +
 +/* Section 10.4 */
 +#define GSM48_MT_RR_INIT_REQ          0x3c
 +#define GSM48_MT_RR_ADD_ASS           0x3b
 +#define GSM48_MT_RR_IMM_ASS           0x3f
 +#define GSM48_MT_RR_IMM_ASS_EXT               0x39
 +#define GSM48_MT_RR_IMM_ASS_REJ               0x3a
 +
 +#define GSM48_MT_RR_CIPH_M_CMD                0x35
 +#define GSM48_MT_RR_CIPH_M_COMPL      0x32
 +
 +#define GSM48_MT_RR_CFG_CHG_CMD               0x30
 +#define GSM48_MT_RR_CFG_CHG_ACK               0x31
 +#define GSM48_MT_RR_CFG_CHG_REJ               0x33
 +
 +#define GSM48_MT_RR_ASS_CMD           0x2e
 +#define GSM48_MT_RR_ASS_COMPL         0x29
 +#define GSM48_MT_RR_ASS_FAIL          0x2f
 +#define GSM48_MT_RR_HANDO_CMD         0x2b
 +#define GSM48_MT_RR_HANDO_COMPL               0x2c
 +#define GSM48_MT_RR_HANDO_FAIL                0x28
 +#define GSM48_MT_RR_HANDO_INFO                0x2d
 +
 +#define GSM48_MT_RR_CELL_CHG_ORDER    0x08
 +#define GSM48_MT_RR_PDCH_ASS_CMD      0x23
 +
 +#define GSM48_MT_RR_CHAN_REL          0x0d
 +#define GSM48_MT_RR_PART_REL          0x0a
 +#define GSM48_MT_RR_PART_REL_COMP     0x0f
 +
 +#define GSM48_MT_RR_PAG_REQ_1         0x21
 +#define GSM48_MT_RR_PAG_REQ_2         0x22
 +#define GSM48_MT_RR_PAG_REQ_3         0x24
 +#define GSM48_MT_RR_PAG_RESP          0x27
 +#define GSM48_MT_RR_NOTIF_NCH         0x20
 +#define GSM48_MT_RR_NOTIF_FACCH               0x25
 +#define GSM48_MT_RR_NOTIF_RESP                0x26
 +
 +#define GSM48_MT_RR_SYSINFO_8         0x18
 +#define GSM48_MT_RR_SYSINFO_1         0x19
 +#define GSM48_MT_RR_SYSINFO_2         0x1a
 +#define GSM48_MT_RR_SYSINFO_3         0x1b
 +#define GSM48_MT_RR_SYSINFO_4         0x1c
 +#define GSM48_MT_RR_SYSINFO_5         0x1d
 +#define GSM48_MT_RR_SYSINFO_6         0x1e
 +#define GSM48_MT_RR_SYSINFO_7         0x1f
 +
 +#define GSM48_MT_RR_SYSINFO_2bis      0x02
 +#define GSM48_MT_RR_SYSINFO_2ter      0x03
 +#define GSM48_MT_RR_SYSINFO_5bis      0x05
 +#define GSM48_MT_RR_SYSINFO_5ter      0x06
 +#define GSM48_MT_RR_SYSINFO_9         0x04
 +#define GSM48_MT_RR_SYSINFO_13                0x00
 +
 +#define GSM48_MT_RR_SYSINFO_16                0x3d
 +#define GSM48_MT_RR_SYSINFO_17                0x3e
 +
 +#define GSM48_MT_RR_CHAN_MODE_MODIF   0x10
 +#define GSM48_MT_RR_STATUS            0x12
 +#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK       0x17
 +#define GSM48_MT_RR_FREQ_REDEF                0x14
 +#define GSM48_MT_RR_MEAS_REP          0x15
 +#define GSM48_MT_RR_CLSM_CHG          0x16
 +#define GSM48_MT_RR_CLSM_ENQ          0x13
 +#define GSM48_MT_RR_EXT_MEAS_REP      0x36
 +#define GSM48_MT_RR_EXT_MEAS_REP_ORD  0x37
 +#define GSM48_MT_RR_GPRS_SUSP_REQ     0x34
 +
 +#define GSM48_MT_RR_VGCS_UPL_GRANT    0x08
 +#define GSM48_MT_RR_UPLINK_RELEASE    0x0e
 +#define GSM48_MT_RR_UPLINK_FREE               0x0c
 +#define GSM48_MT_RR_UPLINK_BUSY               0x2a
 +#define GSM48_MT_RR_TALKER_IND                0x11
 +
 +#define GSM48_MT_RR_APP_INFO          0x38
 +
 +/* Table 10.2/3GPP TS 04.08 */
 +#define GSM48_MT_MM_IMSI_DETACH_IND   0x01
 +#define GSM48_MT_MM_LOC_UPD_ACCEPT    0x02
 +#define GSM48_MT_MM_LOC_UPD_REJECT    0x04
 +#define GSM48_MT_MM_LOC_UPD_REQUEST   0x08
 +
 +#define GSM48_MT_MM_AUTH_REJ          0x11
 +#define GSM48_MT_MM_AUTH_REQ          0x12
 +#define GSM48_MT_MM_AUTH_RESP         0x14
 +#define GSM48_MT_MM_ID_REQ            0x18
 +#define GSM48_MT_MM_ID_RESP           0x19
 +#define GSM48_MT_MM_TMSI_REALL_CMD    0x1a
 +#define GSM48_MT_MM_TMSI_REALL_COMPL  0x1b
 +
 +#define GSM48_MT_MM_CM_SERV_ACC               0x21
 +#define GSM48_MT_MM_CM_SERV_REJ               0x22
 +#define GSM48_MT_MM_CM_SERV_ABORT     0x23
 +#define GSM48_MT_MM_CM_SERV_REQ               0x24
 +#define GSM48_MT_MM_CM_SERV_PROMPT    0x25
 +#define GSM48_MT_MM_CM_REEST_REQ      0x28
 +#define GSM48_MT_MM_ABORT             0x29
 +
 +#define GSM48_MT_MM_NULL              0x30
 +#define GSM48_MT_MM_STATUS            0x31
 +#define GSM48_MT_MM_INFO              0x32
 +
 +/* Table 10.3/3GPP TS 04.08 */
 +#define GSM48_MT_CC_ALERTING          0x01
 +#define GSM48_MT_CC_CALL_CONF         0x08
 +#define GSM48_MT_CC_CALL_PROC         0x02
 +#define GSM48_MT_CC_CONNECT           0x07
 +#define GSM48_MT_CC_CONNECT_ACK               0x0f
 +#define GSM48_MT_CC_EMERG_SETUP               0x0e
 +#define GSM48_MT_CC_PROGRESS          0x03
 +#define GSM48_MT_CC_ESTAB             0x04
 +#define GSM48_MT_CC_ESTAB_CONF                0x06
 +#define GSM48_MT_CC_RECALL            0x0b
 +#define GSM48_MT_CC_START_CC          0x09
 +#define GSM48_MT_CC_SETUP             0x05
 +
 +#define GSM48_MT_CC_MODIFY            0x17
 +#define GSM48_MT_CC_MODIFY_COMPL      0x1f
 +#define GSM48_MT_CC_MODIFY_REJECT     0x13
 +#define GSM48_MT_CC_USER_INFO         0x10
 +#define GSM48_MT_CC_HOLD              0x18
 +#define GSM48_MT_CC_HOLD_ACK          0x19
 +#define GSM48_MT_CC_HOLD_REJ          0x1a
 +#define GSM48_MT_CC_RETR              0x1c
 +#define GSM48_MT_CC_RETR_ACK          0x1d
 +#define GSM48_MT_CC_RETR_REJ          0x1e
 +
 +#define GSM48_MT_CC_DISCONNECT                0x25
 +#define GSM48_MT_CC_RELEASE           0x2d
 +#define GSM48_MT_CC_RELEASE_COMPL     0x2a
 +
 +#define GSM48_MT_CC_CONG_CTRL         0x39
 +#define GSM48_MT_CC_NOTIFY            0x3e
 +#define GSM48_MT_CC_STATUS            0x3d
 +#define GSM48_MT_CC_STATUS_ENQ                0x34
 +#define GSM48_MT_CC_START_DTMF                0x35
 +#define GSM48_MT_CC_STOP_DTMF         0x31
 +#define GSM48_MT_CC_STOP_DTMF_ACK     0x32
 +#define GSM48_MT_CC_START_DTMF_ACK    0x36
 +#define GSM48_MT_CC_START_DTMF_REJ    0x37
 +#define GSM48_MT_CC_FACILITY          0x3a
 +
 +/* FIXME: Table 10.4 / 10.4a (GPRS) */
 +
 +/* Section 10.5.3.3 CM service type */
 +#define GSM48_CMSERV_MO_CALL_PACKET   1
 +#define GSM48_CMSERV_EMERGENCY                2
 +#define GSM48_CMSERV_SMS              4
 +#define GSM48_CMSERV_SUP_SERV         8
 +#define GSM48_CMSERV_VGCS             9
 +#define GSM48_CMSERV_VBS              10
 +#define GSM48_CMSERV_LOC_SERV         11
 +
 +/* Section 10.5.2.26, Table 10.5.64 */
 +#define GSM48_PM_MASK         0x03
 +#define GSM48_PM_NORMAL               0x00
 +#define GSM48_PM_EXTENDED     0x01
 +#define GSM48_PM_REORG                0x02
 +#define GSM48_PM_SAME         0x03
 +
 +/* Chapter 10.5.3.5 / Table 10.5.93 */
 +#define GSM48_LUPD_NORMAL     0x0
 +#define GSM48_LUPD_PERIODIC   0x1
 +#define GSM48_LUPD_IMSI_ATT   0x2
 +#define GSM48_LUPD_RESERVED   0x3
 +
 +/* Table 10.5.4 */
 +#define GSM_MI_TYPE_MASK      0x07
 +#define GSM_MI_TYPE_NONE      0x00
 +#define GSM_MI_TYPE_IMSI      0x01
 +#define GSM_MI_TYPE_IMEI      0x02
 +#define GSM_MI_TYPE_IMEISV    0x03
 +#define GSM_MI_TYPE_TMSI      0x04
 +#define GSM_MI_ODD            0x08
 +
 +#define GSM48_IE_MOBILE_ID    0x17    /* 10.5.1.4 */
 +#define GSM48_IE_NAME_LONG    0x43    /* 10.5.3.5a */
 +#define GSM48_IE_NAME_SHORT   0x45    /* 10.5.3.5a */
 +#define GSM48_IE_UTC          0x46    /* 10.5.3.8 */
 +#define GSM48_IE_NET_TIME_TZ  0x47    /* 10.5.3.9 */
 +#define GSM48_IE_LSA_IDENT    0x48    /* 10.5.3.11 */
 +
 +#define GSM48_IE_BEARER_CAP   0x04    /* 10.5.4.5 */
 +#define GSM48_IE_CAUSE                0x08    /* 10.5.4.11 */
 +#define GSM48_IE_CC_CAP               0x15    /* 10.5.4.5a */
 +#define GSM48_IE_ALERT                0x19    /* 10.5.4.26 */
 +#define GSM48_IE_FACILITY     0x1c    /* 10.5.4.15 */
 +#define GSM48_IE_PROGR_IND    0x1e    /* 10.5.4.21 */
 +#define GSM48_IE_AUX_STATUS   0x24    /* 10.5.4.4 */
 +#define GSM48_IE_NOTIFY               0x27    /* 10.5.4.20 */
 +#define GSM48_IE_KPD_FACILITY 0x2c    /* 10.5.4.17 */
 +#define GSM48_IE_SIGNAL               0x34    /* 10.5.4.23 */
 +#define GSM48_IE_CONN_BCD     0x4c    /* 10.5.4.13 */
 +#define GSM48_IE_CONN_SUB     0x4d    /* 10.5.4.14 */
 +#define GSM48_IE_CALLING_BCD  0x5c    /* 10.5.4.9 */
 +#define GSM48_IE_CALLING_SUB  0x5d    /* 10.5.4.10 */
 +#define GSM48_IE_CALLED_BCD   0x5e    /* 10.5.4.7 */
 +#define GSM48_IE_CALLED_SUB   0x6d    /* 10.5.4.8 */
 +#define GSM48_IE_REDIR_BCD    0x74    /* 10.5.4.21a */
 +#define GSM48_IE_REDIR_SUB    0x75    /* 10.5.4.21b */
 +#define GSM48_IE_LOWL_COMPAT  0x7c    /* 10.5.4.18 */
 +#define GSM48_IE_HIGHL_COMPAT 0x7d    /* 10.5.4.16 */
 +#define GSM48_IE_USER_USER    0x7e    /* 10.5.4.25 */
 +#define GSM48_IE_SS_VERS      0x7f    /* 10.5.4.24 */
 +#define GSM48_IE_MORE_DATA    0xa0    /* 10.5.4.19 */
 +#define GSM48_IE_CLIR_SUPP    0xa1    /* 10.5.4.11a */
 +#define GSM48_IE_CLIR_INVOC   0xa2    /* 10.5.4.11b */
 +#define GSM48_IE_REV_C_SETUP  0xa3    /* 10.5.4.22a */
 +#define GSM48_IE_REPEAT_CIR   0xd1    /* 10.5.4.22 */
 +#define GSM48_IE_REPEAT_SEQ   0xd3    /* 10.5.4.22 */
 +
 +/* Section 10.5.4.11 / Table 10.5.122 */
 +#define GSM48_CAUSE_CS_GSM    0x60
 +
 +/* Section 9.1.2 / Table 9.3 */
 +/* RR elements */
 +#define GSM48_IE_VGCS_TARGET  0x01
 +//#define GSM48_IE_VGCS_T_MODE_I      0x01
 +#define GSM48_IE_FRQSHORT_AFTER       0x02
 +#define GSM48_IE_MUL_RATE_CFG 0x03    /* 10.5.2.21aa */
 +#define GSM48_IE_FREQ_L_AFTER 0x05
 +#define GSM48_IE_MSLOT_DESC   0x10
 +#define GSM48_IE_CHANMODE_2   0x11
 +#define GSM48_IE_FRQSHORT_BEFORE 0x12
 +//#define GSM48_IE_FRQSHORT_BEFOR 0x12
 +#define GSM48_IE_CHANMODE_3   0x13
 +#define GSM48_IE_CHANMODE_4   0x14
 +#define GSM48_IE_CHANMODE_5   0x15
 +#define GSM48_IE_CHANMODE_6   0x16
 +#define GSM48_IE_CHANMODE_7   0x17
 +#define GSM48_IE_CHANMODE_8   0x18
 +#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_FRQLIST_BEFORE     0x19
 +#define GSM48_IE_CH_DESC_1_BEFORE     0x1c
 +//#define GSM48_IE_CHDES_1_BEFORE 0x1c
 +#define GSM48_IE_CH_DESC_2_BEFORE     0x1d
 +//#define GSM48_IE_CHDES_2_BEFORE     0x1d
 +#define GSM48_IE_F_CH_SEQ_BEFORE      0x1e
 +//#define GSM48_IE_FRQSEQ_BEFORE      0x1e
 +#define GSM48_IE_CLASSMARK3   0x20
 +#define GSM48_IE_MA_BEFORE    0x21
 +#define GSM48_IE_RR_PACKET_UL 0x22
 +#define GSM48_IE_RR_PACKET_DL 0x23
 +#define GSM48_IE_CELL_CH_DESC 0x62
 +#define GSM48_IE_CHANMODE_1   0x63
 +#define GSM48_IE_CHDES_2_AFTER        0x64
 +#define GSM48_IE_MODE_SEC_CH  0x66
 +#define GSM48_IE_F_CH_SEQ_AFTER       0x69
 +#define GSM48_IE_MA_AFTER     0x72
 +#define GSM48_IE_BA_RANGE     0x73
 +#define GSM48_IE_GROUP_CHDES  0x74
 +#define GSM48_IE_BA_LIST_PREF 0x75
 +#define GSM48_IE_MOB_OVSERV_DIF       0x77
 +#define GSM48_IE_REALTIME_DIFF        0x7b
 +#define GSM48_IE_START_TIME   0x7c
 +#define GSM48_IE_TIMING_ADVANCE       0x7d
 +#define GSM48_IE_GROUP_CIP_SEQ        0x80
 +#define GSM48_IE_CIP_MODE_SET 0x90
 +#define GSM48_IE_GPRS_RESUMPT 0xc0
 +#define GSM48_IE_SYNC_IND     0xd0
 +/* System Information 4 (types are equal IEs above) */
 +#define GSM48_IE_CBCH_CHAN_DESC       0x64
 +#define GSM48_IE_CBCH_MOB_AL  0x72
 +
 +/* Additional MM elements */
 +#define GSM48_IE_LOCATION_AREA        0x13
 +#define GSM48_IE_PRIORITY_LEV 0x80
 +#define GSM48_IE_FOLLOW_ON_PROC       0xa1
 +#define GSM48_IE_CTS_PERMISSION       0xa2
 +
 +/* Section 10.5.4.23 / Table 10.5.130 */
 +enum gsm48_signal_val {
 +      GSM48_SIGNAL_DIALTONE   = 0x00,
 +      GSM48_SIGNAL_RINGBACK   = 0x01,
 +      GSM48_SIGNAL_INTERCEPT  = 0x02,
 +      GSM48_SIGNAL_NET_CONG   = 0x03,
 +      GSM48_SIGNAL_BUSY       = 0x04,
 +      GSM48_SIGNAL_CONFIRM    = 0x05,
 +      GSM48_SIGNAL_ANSWER     = 0x06,
 +      GSM48_SIGNAL_CALL_WAIT  = 0x07,
 +      GSM48_SIGNAL_OFF_HOOK   = 0x08,
 +      GSM48_SIGNAL_OFF        = 0x3f,
 +      GSM48_SIGNAL_ALERT_OFF  = 0x4f,
 +};
 +
 +enum gsm48_cause_loc {
 +      GSM48_CAUSE_LOC_USER            = 0x00,
 +      GSM48_CAUSE_LOC_PRN_S_LU        = 0x01,
 +      GSM48_CAUSE_LOC_PUN_S_LU        = 0x02,
 +      GSM48_CAUSE_LOC_TRANS_NET       = 0x03,
 +      GSM48_CAUSE_LOC_PUN_S_RU        = 0x04,
 +      GSM48_CAUSE_LOC_PRN_S_RU        = 0x05,
 +      /* not defined */
 +      GSM48_CAUSE_LOC_INN_NET         = 0x07,
 +      GSM48_CAUSE_LOC_NET_BEYOND      = 0x0a,
 +};
 +
 +/* Section 10.5.2.31 RR Cause / Table 10.5.70 */
 +enum gsm48_rr_cause {
 +      GSM48_RR_CAUSE_NORMAL           = 0x00,
 +      GSM48_RR_CAUSE_ABNORMAL_UNSPEC  = 0x01,
 +      GSM48_RR_CAUSE_ABNORMAL_UNACCT  = 0x02,
 +      GSM48_RR_CAUSE_ABNORMAL_TIMER   = 0x03,
 +      GSM48_RR_CAUSE_ABNORMAL_NOACT   = 0x04,
 +      GSM48_RR_CAUSE_PREMPTIVE_REL    = 0x05,
 +      GSM48_RR_CAUSE_HNDOVER_IMP      = 0x06,
 +      GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07,
 +      GSM48_RR_CAUSE_FREQ_NOT_IMPL    = 0x08,
 +      GSM48_RR_CAUSE_CALL_CLEARED     = 0x41,
 +      GSM48_RR_CAUSE_SEMANT_INCORR    = 0x5f,
 +      GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
 +      GSM48_RR_CAUSE_MSG_TYPE_N       = 0x61,
 +      GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62,
 +      GSM48_RR_CAUSE_COND_IE_ERROR    = 0x64,
 +      GSM48_RR_CAUSE_NO_CELL_ALLOC_A  = 0x65,
 +      GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
 +};
 +
 +/* Section 10.5.4.11 CC Cause / Table 10.5.123 */
 +enum gsm48_cc_cause {
 +      GSM48_CC_CAUSE_UNASSIGNED_NR    = 1,
 +      GSM48_CC_CAUSE_NO_ROUTE         = 3,
 +      GSM48_CC_CAUSE_CHAN_UNACCEPT    = 6,
 +      GSM48_CC_CAUSE_OP_DET_BARRING   = 8,
 +      GSM48_CC_CAUSE_NORM_CALL_CLEAR  = 16,
 +      GSM48_CC_CAUSE_USER_BUSY        = 17,
 +      GSM48_CC_CAUSE_USER_NOTRESPOND  = 18,
 +      GSM48_CC_CAUSE_USER_ALERTING_NA = 19,
 +      GSM48_CC_CAUSE_CALL_REJECTED    = 21,
 +      GSM48_CC_CAUSE_NUMBER_CHANGED   = 22,
 +      GSM48_CC_CAUSE_PRE_EMPTION      = 25,
 +      GSM48_CC_CAUSE_NONSE_USER_CLR   = 26,
 +      GSM48_CC_CAUSE_DEST_OOO         = 27,
 +      GSM48_CC_CAUSE_INV_NR_FORMAT    = 28,
 +      GSM48_CC_CAUSE_FACILITY_REJ     = 29,
 +      GSM48_CC_CAUSE_RESP_STATUS_INQ  = 30,
 +      GSM48_CC_CAUSE_NORMAL_UNSPEC    = 31,
 +      GSM48_CC_CAUSE_NO_CIRCUIT_CHAN  = 34,
 +      GSM48_CC_CAUSE_NETWORK_OOO      = 38,
 +      GSM48_CC_CAUSE_TEMP_FAILURE     = 41,
 +      GSM48_CC_CAUSE_SWITCH_CONG      = 42,
 +      GSM48_CC_CAUSE_ACC_INF_DISCARD  = 43,
 +      GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44,
 +      GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47,
 +      GSM48_CC_CAUSE_QOS_UNAVAIL      = 49,
 +      GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50,
 +      GSM48_CC_CAUSE_INC_BARRED_CUG   = 55,
 +      GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57,
 +      GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58,
 +      GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63,
 +      GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65,
 +      GSM48_CC_CAUSE_ACM_GE_ACM_MAX   = 68,
 +      GSM48_CC_CAUSE_REQ_FAC_NOTIMPL  = 69,
 +      GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70,
 +      GSM48_CC_CAUSE_SERV_OPT_UNIMPL  = 79,
 +      GSM48_CC_CAUSE_INVAL_TRANS_ID   = 81,
 +      GSM48_CC_CAUSE_USER_NOT_IN_CUG  = 87,
 +      GSM48_CC_CAUSE_INCOMPAT_DEST    = 88,
 +      GSM48_CC_CAUSE_INVAL_TRANS_NET  = 91,
 +      GSM48_CC_CAUSE_SEMANTIC_INCORR  = 95,
 +      GSM48_CC_CAUSE_INVAL_MAND_INF   = 96,
 +      GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97,
 +      GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98,
 +      GSM48_CC_CAUSE_IE_NOTEXIST      = 99,
 +      GSM48_CC_CAUSE_COND_IE_ERR      = 100,
 +      GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101,
 +      GSM48_CC_CAUSE_RECOVERY_TIMER   = 102,
 +      GSM48_CC_CAUSE_PROTO_ERR        = 111,
 +      GSM48_CC_CAUSE_INTERWORKING     = 127,
 +};
 +
 +/* Annex G, GSM specific cause values for mobility management */
 +enum gsm48_reject_value {
 +      GSM48_REJECT_IMSI_UNKNOWN_IN_HLR        = 2,
 +      GSM48_REJECT_ILLEGAL_MS                 = 3,
 +      GSM48_REJECT_IMSI_UNKNOWN_IN_VLR        = 4,
 +      GSM48_REJECT_IMEI_NOT_ACCEPTED          = 5,
 +      GSM48_REJECT_ILLEGAL_ME                 = 6,
 +      GSM48_REJECT_PLMN_NOT_ALLOWED           = 11,
 +      GSM48_REJECT_LOC_NOT_ALLOWED            = 12,
 +      GSM48_REJECT_ROAMING_NOT_ALLOWED        = 13,
 +      GSM48_REJECT_NETWORK_FAILURE            = 17,
 +      GSM48_REJECT_CONGESTION                 = 22,
 +      GSM48_REJECT_SRV_OPT_NOT_SUPPORTED      = 32,
 +      GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED  = 33,
 +      GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER   = 34,
 +      GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38,
 +      GSM48_REJECT_INCORRECT_MESSAGE          = 95,
 +      GSM48_REJECT_INVALID_MANDANTORY_INF     = 96,
 +      GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED   = 97,
 +      GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE    = 98,
 +      GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED  = 99,
 +      GSM48_REJECT_CONDTIONAL_IE_ERROR        = 100,
 +      GSM48_REJECT_MSG_NOT_COMPATIBLE         = 101,
 +      GSM48_REJECT_PROTOCOL_ERROR             = 111,
 +
 +      /* according to G.6 Additional cause codes for GMM */
 +      GSM48_REJECT_GPRS_NOT_ALLOWED           = 7,
 +      GSM48_REJECT_SERVICES_NOT_ALLOWED       = 8,
 +      GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9,
 +      GSM48_REJECT_IMPLICITLY_DETACHED        = 10,
 +      GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN   = 14,
 +      GSM48_REJECT_MSC_TMP_NOT_REACHABLE      = 16,
 +};
 +
 +enum chreq_type {
 +      CHREQ_T_EMERG_CALL,
 +      CHREQ_T_CALL_REEST_TCH_F,
 +      CHREQ_T_CALL_REEST_TCH_H,
 +      CHREQ_T_CALL_REEST_TCH_H_DBL,
 +      CHREQ_T_SDCCH,
 +      CHREQ_T_TCH_F,
 +      CHREQ_T_VOICE_CALL_TCH_H,
 +      CHREQ_T_DATA_CALL_TCH_H,
 +      CHREQ_T_LOCATION_UPD,
 +      CHREQ_T_PAG_R_ANY_NECI0,
 +      CHREQ_T_PAG_R_ANY_NECI1,
 +      CHREQ_T_PAG_R_TCH_F,
 +      CHREQ_T_PAG_R_TCH_FH,
 +      CHREQ_T_LMU,
 +      CHREQ_T_RESERVED_SDCCH,
 +      CHREQ_T_RESERVED_IGNORE,
 +};
 +
 +/* Chapter 11.3 */
 +#define GSM48_T301    180, 0
 +#define GSM48_T303    30, 0
 +#define GSM48_T305    30, 0
 +#define GSM48_T306    30, 0
 +#define GSM48_T308    10, 0
 +#define GSM48_T310    180, 0
 +#define GSM48_T313    30, 0
 +#define GSM48_T323    30, 0
 +#define GSM48_T331    30, 0
 +#define GSM48_T333    30, 0
 +#define GSM48_T334    25, 0 /* min 15 */
 +#define GSM48_T338    30, 0
 +#define GSM48_T303_MS 30, 0
 +#define GSM48_T305_MS 30, 0
 +#define GSM48_T308_MS 30, 0
 +#define GSM48_T310_MS 30, 0
 +#define GSM48_T313_MS 30, 0
 +#define GSM48_T323_MS 30, 0
 +#define GSM48_T332_MS 30, 0
 +#define GSM48_T335_MS 30, 0
 +
 +/* Chapter 5.1.2.2 */
 +#define       GSM_CSTATE_NULL                 0
 +#define       GSM_CSTATE_INITIATED            1
 +#define       GSM_CSTATE_MM_CONNECTION_PEND   2 /* see 10.5.4.6 */
 +#define       GSM_CSTATE_MO_CALL_PROC         3
 +#define       GSM_CSTATE_CALL_DELIVERED       4
 +#define       GSM_CSTATE_CALL_PRESENT         6
 +#define       GSM_CSTATE_CALL_RECEIVED        7
 +#define       GSM_CSTATE_CONNECT_REQUEST      8
 +#define       GSM_CSTATE_MO_TERM_CALL_CONF    9
 +#define       GSM_CSTATE_ACTIVE               10
 +#define       GSM_CSTATE_DISCONNECT_REQ       12
 +#define       GSM_CSTATE_DISCONNECT_IND       12
 +#define       GSM_CSTATE_RELEASE_REQ          19
 +#define       GSM_CSTATE_MO_ORIG_MODIFY       26
 +#define       GSM_CSTATE_MO_TERM_MODIFY       27
 +#define       GSM_CSTATE_CONNECT_IND          28
 +
 +#define SBIT(a) (1 << a)
 +#define ALL_STATES 0xffffffff
 +
 +/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */
 +#define GSM_LAC_RESERVED_DETACHED       0x0
 +#define GSM_LAC_RESERVED_ALL_BTS        0xfffe
 +
 +/* GSM 04.08 Bearer Capability: Information Transfer Capability */
 +enum gsm48_bcap_itcap {
 +      GSM48_BCAP_ITCAP_SPEECH         = 0,
 +      GSM48_BCAP_ITCAP_UNR_DIG_INF    = 1,
 +      GSM48_BCAP_ITCAP_3k1_AUDIO      = 2,
 +      GSM48_BCAP_ITCAP_FAX_G3         = 3,
 +      GSM48_BCAP_ITCAP_OTHER          = 5,
 +      GSM48_BCAP_ITCAP_RESERVED       = 7,
 +};
 +
 +/* GSM 04.08 Bearer Capability: Transfer Mode */
 +enum gsm48_bcap_tmod {
 +      GSM48_BCAP_TMOD_CIRCUIT         = 0,
 +      GSM48_BCAP_TMOD_PACKET          = 1,
 +};
 +
 +/* GSM 04.08 Bearer Capability: Coding Standard */
 +enum gsm48_bcap_coding {
 +      GSM48_BCAP_CODING_GSM_STD       = 0,
 +};
 +
 +/* GSM 04.08 Bearer Capability: Radio Channel Requirements */
 +enum gsm48_bcap_rrq {
 +      GSM48_BCAP_RRQ_FR_ONLY  = 1,
 +      GSM48_BCAP_RRQ_DUAL_HR  = 2,
 +      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 */
index e197e17,0000000..1e97bb9
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,25 @@@
- SUBDIRS=vty
++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
 +
 +INCLUDES = $(all_includes) -I$(top_srcdir)/include
 +AM_CFLAGS = -fPIC -Wall
 +
 +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 rate_ctr.c gsmtap_util.c \
-                        gprs_cipher_core.c crc16.c panic.c
++                       gprs_cipher_core.c crc16.c panic.c process.c
 +
 +if ENABLE_PLUGIN
 +libosmocore_la_SOURCES += plugin.c
 +libosmocore_la_LDFLAGS = -ldl
 +endif
 +
 +if ENABLE_TALLOC
 +libosmocore_la_SOURCES += talloc.c
 +endif
index c8dc664,0000000..42a73b9
mode 100644,000000..100644
--- /dev/null
@@@ -1,311 -1,0 +1,313 @@@
 +/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
 + * (C) 2009,2010 by On-Waves
 + * 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 <osmocore/gsm0808.h>
 +#include <osmocore/protocol/gsm_08_08.h>
 +#include <osmocore/gsm48.h>
 +
 +#include <arpa/inet.h>
 +
 +#define BSSMAP_MSG_SIZE 512
 +#define BSSMAP_MSG_HEADROOM 128
 +
 +static void put_data_16(uint8_t *data, const uint16_t val)
 +{
 +      memcpy(data, &val, sizeof(val));
 +}
 +
 +struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, uint16_t _ci)
 +{
 +      uint8_t *data;
 +      uint8_t *ci;
 +      struct msgb* msg;
 +      struct gsm48_loc_area_id *lai;
 +
 +      msg  = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                 "bssmap cmpl l3");
 +      if (!msg)
 +              return NULL;
 +
 +      /* create the bssmap header */
 +      msg->l3h = msgb_put(msg, 2);
 +      msg->l3h[0] = 0x0;
 +
 +      /* create layer 3 header */
 +      data = msgb_put(msg, 1);
 +      data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3;
 +
 +      /* create the cell header */
 +      data = msgb_put(msg, 3);
 +      data[0] = GSM0808_IE_CELL_IDENTIFIER;
 +      data[1] = 1 + sizeof(*lai) + 2;
 +      data[2] = CELL_IDENT_WHOLE_GLOBAL;
 +
 +      lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
 +      gsm48_generate_lai(lai, cc, nc, lac);
 +
 +      ci = msgb_put(msg, 2);
 +      put_data_16(ci, htons(_ci));
 +
 +      /* copy the layer3 data */
 +      data = msgb_put(msg, msgb_l3len(msg_l3) + 2);
 +      data[0] = GSM0808_IE_LAYER_3_INFORMATION;
 +      data[1] = msgb_l3len(msg_l3);
 +      memcpy(&data[2], msg_l3->l3h, data[1]);
 +
 +      /* update the size */
 +      msg->l3h[1] = msgb_l3len(msg) - 2;
 +
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_reset(void)
 +{
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "bssmap: reset");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 6);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 0x04;
 +      msg->l3h[2] = 0x30;
 +      msg->l3h[3] = 0x04;
 +      msg->l3h[4] = 0x01;
 +      msg->l3h[5] = 0x20;
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_clear_complete(void)
 +{
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "bssmap: clear complete");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 3);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 1;
 +      msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE;
 +
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
 +{
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "cipher-complete");
 +      if (!msg)
 +              return NULL;
 +
 +        /* send response with BSS override for A5/1... cheating */
 +      msg->l3h = msgb_put(msg, 3);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 0xff;
 +      msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE;
 +
 +      /* include layer3 in case we have at least two octets */
 +      if (layer3 && msgb_l3len(layer3) > 2) {
 +              msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2);
 +              msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS;
 +              msg->l4h[1] = msgb_l3len(layer3);
 +              memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3));
 +      }
 +
 +      /* and the optional BSS message */
 +      msg->l4h = msgb_put(msg, 2);
 +      msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
 +      msg->l4h[1] = alg_id;
 +
 +      /* update the size */
 +      msg->l3h[1] = msgb_l3len(msg) - 2;
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_cipher_reject(uint8_t cause)
 +{
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "bssmap: clear complete");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 3);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 2;
 +      msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT;
 +      msg->l3h[3] = cause;
 +
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length)
 +{
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "classmark-update");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 3);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 0xff;
 +      msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE;
 +
 +      msg->l4h = msgb_put(msg, length);
 +      memcpy(msg->l4h, classmark_data, length);
 +
 +      /* update the size */
 +      msg->l3h[1] = msgb_l3len(msg) - 2;
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
 +{
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "bssmap: sapi 'n' reject");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 5);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 3;
 +      msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT;
 +      msg->l3h[3] = link_id;
 +      msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED;
 +
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause,
 +                                               uint8_t chosen_channel, uint8_t encr_alg_id,
 +                                               uint8_t speech_mode)
 +{
 +      uint8_t *data;
 +
 +      struct msgb *msg = msgb_alloc(35, "bssmap: ass compl");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 3);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 0xff;
 +      msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE;
 +
 +      /* write 3.2.2.22 */
 +      data = msgb_put(msg, 2);
 +      data[0] = GSM0808_IE_RR_CAUSE;
 +      data[1] = rr_cause;
 +
 +      /* write cirtcuit identity  code 3.2.2.2 */
 +      /* write cell identifier 3.2.2.17 */
 +      /* write chosen channel 3.2.2.33 when BTS picked it */
 +      data = msgb_put(msg, 2);
 +      data[0] = GSM0808_IE_CHOSEN_CHANNEL;
 +      data[1] = chosen_channel;
 +
 +      /* write chosen encryption algorithm 3.2.2.44 */
 +      data = msgb_put(msg, 2);
 +      data[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
 +      data[1] = encr_alg_id;
 +
 +      /* write circuit pool 3.2.2.45 */
 +      /* write speech version chosen: 3.2.2.51 when BTS picked it */
 +      if (speech_mode != 0) {
 +              data = msgb_put(msg, 2);
 +              data[0] = GSM0808_IE_SPEECH_VERSION;
 +              data[1] = speech_mode;
 +      }
 +
 +      /* write LSA identifier 3.2.2.15 */
 +
 +
 +      /* update the size */
 +      msg->l3h[1] = msgb_l3len(msg) - 2;
 +      return msg;
 +}
 +
 +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause)
 +{
 +      uint8_t *data;
 +      struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
 +                                             "bssmap: ass fail");
 +      if (!msg)
 +              return NULL;
 +
 +      msg->l3h = msgb_put(msg, 6);
 +      msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
 +      msg->l3h[1] = 0xff;
 +      msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE;
 +      msg->l3h[3] = GSM0808_IE_CAUSE;
 +      msg->l3h[4] = 1;
 +      msg->l3h[5] = cause;
 +
 +      /* RR cause 3.2.2.22 */
 +      if (rr_cause) {
 +              data = msgb_put(msg, 2);
 +              data[0] = GSM0808_IE_RR_CAUSE;
 +              data[1] = *rr_cause;
 +      }
 +
 +      /* Circuit pool 3.22.45 */
 +      /* Circuit pool list 3.2.2.46 */
 +
 +      /* update the size */
 +      msg->l3h[1] = msgb_l3len(msg) - 2;
 +      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 },
 +              [GSM0808_IE_TMSI]                   = { TLV_TYPE_TLV },
 +              [GSM0808_IE_CELL_IDENTIFIER_LIST]   = { TLV_TYPE_TLV },
 +              [GSM0808_IE_CHANNEL_NEEDED]         = { TLV_TYPE_TV },
 +              [GSM0808_IE_EMLPP_PRIORITY]         = { TLV_TYPE_TV },
 +              [GSM0808_IE_CHANNEL_TYPE]           = { TLV_TYPE_TLV },
 +              [GSM0808_IE_PRIORITY]               = { TLV_TYPE_TLV },
 +              [GSM0808_IE_CIRCUIT_IDENTITY_CODE]  = { TLV_TYPE_TV },
 +              [GSM0808_IE_DOWNLINK_DTX_FLAG]      = { TLV_TYPE_TV },
 +              [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
 +              [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
 +              [GSM0808_IE_GROUP_CALL_REFERENCE]   = { TLV_TYPE_TLV },
 +              [GSM0808_IE_TALKER_FLAG]            = { TLV_TYPE_T },
 +              [GSM0808_IE_CONFIG_EVO_INDI]        = { TLV_TYPE_TV },
 +              [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR]  = { TLV_TYPE_TV },
 +              [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 },
++              [GSM0808_IE_SPEECH_VERSION]         = { TLV_TYPE_TV },
++              [GSM0808_IE_CHOSEN_ENCR_ALG]        = { TLV_TYPE_TV },
 +      },
 +};
 +
 +const struct tlv_definition *gsm0808_att_tlvdef()
 +{
 +      return &bss_att_tlvdef;
 +}
index 30316a5,0000000..b3b5cb6
mode 100644,000000..100644
--- /dev/null
@@@ -1,401 -1,0 +1,452 @@@
- /* since C89/C99 says stderr is a macro, we can safely do this! */
- #ifdef stderr
- static void _stderr_output(struct log_target *target, const char *log)
 +/* Debugging/Logging support code */
 +
 +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
 + * (C) 2008 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 "../config.h"
 +
 +#include <stdarg.h>
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <string.h>
 +
 +#ifdef HAVE_STRINGS_H
 +#include <strings.h>
 +#endif
 +#include <time.h>
 +#include <errno.h>
 +
 +#include <osmocore/talloc.h>
 +#include <osmocore/utils.h>
 +#include <osmocore/logging.h>
 +
 +const struct log_info *osmo_log_info;
 +
 +static struct log_context log_context;
 +static void *tall_log_ctx = NULL;
 +static LLIST_HEAD(target_list);
 +
 +static const struct value_string loglevel_strs[] = {
 +      { 0,            "EVERYTHING" },
 +      { LOGL_DEBUG,   "DEBUG" },
 +      { LOGL_INFO,    "INFO" },
 +      { LOGL_NOTICE,  "NOTICE" },
 +      { LOGL_ERROR,   "ERROR" },
 +      { LOGL_FATAL,   "FATAL" },
 +      { 0, NULL },
 +};
 +
 +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 < osmo_log_info->num_cat; ++i) {
 +              if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
 +                      return i;
 +      }
 +
 +      return -EINVAL;
 +}
 +
 +/*
 + * Parse the category mask.
 + * The format can be this: category1:category2:category3
 + * or category1,2:category2,3:...
 + */
 +void log_parse_category_mask(struct log_target* target, const char *_mask)
 +{
 +      int i = 0;
 +      char *mask = strdup(_mask);
 +      char *category_token = NULL;
 +
 +      /* Disable everything to enable it afterwards */
 +      for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
 +              target->categories[i].enabled = 0;
 +
 +      category_token = strtok(mask, ":");
 +      do {
 +              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(osmo_log_info->cat[i].name,
 +                                      category_token, length) == 0) {
 +                              int level = 0;
 +
 +                              if (colon)
 +                                      level = atoi(colon+1);
 +
 +                              target->categories[i].enabled = 1;
 +                              target->categories[i].loglevel = level;
 +                      }
 +              }
 +      } while ((category_token = strtok(NULL, ":")));
 +
 +      free(mask);
 +}
 +
 +static const char* color(int subsys)
 +{
 +      if (subsys < osmo_log_info->num_cat)
 +              return osmo_log_info->cat[subsys].color;
 +
 +      return NULL;
 +}
 +
 +static void _output(struct log_target *target, unsigned int subsys,
 +                  char *file, int line, int cont, const char *format,
 +                  va_list ap)
 +{
 +      char col[30];
 +      char sub[30];
 +      char tim[30];
 +      char buf[4096];
 +      char final[4096];
 +
 +      /* prepare the data */
 +      col[0] = '\0';
 +      sub[0] = '\0';
 +      tim[0] = '\0';
 +      buf[0] = '\0';
 +
 +      /* are we using color */
 +      if (target->use_color) {
 +              const char *c = color(subsys);
 +              if (c) {
 +                      snprintf(col, sizeof(col), "%s", color(subsys));
 +                      col[sizeof(col)-1] = '\0';
 +              }
 +      }
 +      vsnprintf(buf, sizeof(buf), format, ap);
 +      buf[sizeof(buf)-1] = '\0';
 +
 +      if (!cont) {
 +              if (target->print_timestamp) {
 +                      char *timestr;
 +                      time_t tm;
 +                      tm = time(NULL);
 +                      timestr = ctime(&tm);
 +                      timestr[strlen(timestr)-1] = '\0';
 +                      snprintf(tim, sizeof(tim), "%s ", timestr);
 +                      tim[sizeof(tim)-1] = '\0';
 +              }
 +              snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
 +              sub[sizeof(sub)-1] = '\0';
 +      }
 +
 +      snprintf(final, sizeof(final), "%s%s%s%s%s", col, tim, sub, buf,
 +               target->use_color ? "\033[0;m" : "");
 +      final[sizeof(final)-1] = '\0';
 +      target->output(target, final);
 +}
 +
 +
 +static void _logp(unsigned int subsys, int level, char *file, int line,
 +                int cont, const char *format, va_list ap)
 +{
 +      struct log_target *tar;
 +
 +      llist_for_each_entry(tar, &target_list, entry) {
 +              struct log_category *category;
 +              int output = 0;
 +
 +              category = &tar->categories[subsys];
 +              /* subsystem is not supposed to be logged */
 +              if (!category->enabled)
 +                      continue;
 +
 +              /* Check the global log level */
 +              if (tar->loglevel != 0 && level < tar->loglevel)
 +                      continue;
 +
 +              /* Check the category log level */
 +              if (tar->loglevel == 0 && category->loglevel != 0 &&
 +                  level < category->loglevel)
 +                      continue;
 +
 +              /* Apply filters here... if that becomes messy we will
 +               * need to put filters in a list and each filter will
 +               * say stop, continue, output */
 +              if ((tar->filter_map & LOG_FILTER_ALL) != 0)
 +                      output = 1;
 +              else if (osmo_log_info->filter_fn)
 +                      output = osmo_log_info->filter_fn(&log_context,
 +                                                     tar);
 +
 +              if (output) {
 +                      /* FIXME: copying the va_list is an ugly
 +                       * workaround against a bug hidden somewhere in
 +                       * _output.  If we do not copy here, the first
 +                       * call to _output() will corrupt the va_list
 +                       * contents, and any further _output() calls
 +                       * with the same va_list will segfault */
 +                      va_list bp;
 +                      va_copy(bp, ap);
 +                      _output(tar, subsys, file, line, cont, format, bp);
 +                      va_end(bp);
 +              }
 +      }
 +}
 +
 +void logp(unsigned int subsys, char *file, int line, int cont,
 +        const char *format, ...)
 +{
 +      va_list ap;
 +
 +      va_start(ap, format);
 +      _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
 +      va_end(ap);
 +}
 +
 +void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
 +{
 +      va_list ap;
 +
 +      va_start(ap, format);
 +      _logp(subsys, level, file, line, cont, format, ap);
 +      va_end(ap);
 +}
 +
 +void log_add_target(struct log_target *target)
 +{
 +      llist_add_tail(&target->entry, &target_list);
 +}
 +
 +void log_del_target(struct log_target *target)
 +{
 +      llist_del(&target->entry);
 +}
 +
 +void log_reset_context(void)
 +{
 +      memset(&log_context, 0, sizeof(log_context));
 +}
 +
 +int log_set_context(uint8_t ctx_nr, void *value)
 +{
 +      if (ctx_nr > LOG_MAX_CTX)
 +              return -EINVAL;
 +
 +      log_context.ctx[ctx_nr] = value;
 +
 +      return 0;
 +}
 +
 +void log_set_all_filter(struct log_target *target, int all)
 +{
 +      if (all)
 +              target->filter_map |= LOG_FILTER_ALL;
 +      else
 +              target->filter_map &= ~LOG_FILTER_ALL;
 +}
 +
 +void log_set_use_color(struct log_target *target, int use_color)
 +{
 +      target->use_color = use_color;
 +}
 +
 +void log_set_print_timestamp(struct log_target *target, int print_timestamp)
 +{
 +      target->print_timestamp = print_timestamp;
 +}
 +
 +void log_set_log_level(struct log_target *target, int log_level)
 +{
 +      target->loglevel = log_level;
 +}
 +
 +void log_set_category_filter(struct log_target *target, int category,
 +                             int enable, int level)
 +{
 +      if (category >= osmo_log_info->num_cat)
 +              return;
 +      target->categories[category].enabled = !!enable;
 +      target->categories[category].loglevel = level;
 +}
 +
-       fprintf(target->tgt_stdout.out, "%s", log);
-       fflush(target->tgt_stdout.out);
++static void _file_output(struct log_target *target, const char *log)
 +{
- #endif
++      fprintf(target->tgt_file.out, "%s", log);
++      fflush(target->tgt_file.out);
 +}
-       target->tgt_stdout.out = stderr;
-       target->output = _stderr_output;
 +
 +struct log_target *log_target_create(void)
 +{
 +      struct log_target *target;
 +      unsigned int i;
 +
 +      target = talloc_zero(tall_log_ctx, struct log_target);
 +      if (!target)
 +              return NULL;
 +
 +      INIT_LLIST_HEAD(&target->entry);
 +
 +      /* initialize the per-category enabled/loglevel from defaults */
 +      for (i = 0; i < osmo_log_info->num_cat; i++) {
 +              struct log_category *cat = &target->categories[i];
 +              cat->enabled = osmo_log_info->cat[i].enabled;
 +              cat->loglevel = osmo_log_info->cat[i].loglevel;
 +      }
 +
 +      /* global settings */
 +      target->use_color = 1;
 +      target->print_timestamp = 0;
 +
 +      /* global log level */
 +      target->loglevel = 0;
 +      return target;
 +}
 +
 +struct log_target *log_target_create_stderr(void)
 +{
 +/* since C89/C99 says stderr is a macro, we can safely do this! */
 +#ifdef stderr
 +      struct log_target *target;
 +
 +      target = log_target_create();
 +      if (!target)
 +              return NULL;
 +
++      target->tgt_file.out = stderr;
++      target->output = _file_output;
 +      return target;
 +#else
 +      return NULL;
 +#endif /* stderr */
 +}
 +
++struct log_target *log_target_create_file(const char *fname)
++{
++      struct log_target *target;
++
++      target = log_target_create();
++      if (!target)
++              return NULL;
++
++      target->tgt_file.out = fopen(fname, "a");
++      if (!target->tgt_file.out)
++              return NULL;
++
++      target->output = _file_output;
++
++      target->tgt_file.fname = talloc_strdup(target, fname);
++
++      return target;
++}
++
++void log_target_destroy(struct log_target *target)
++{
++
++      /* just in case, to make sure we don't have any references */
++      log_del_target(target);
++
++      if (target->output == &_file_output) {
++/* since C89/C99 says stderr is a macro, we can safely do this! */
++#ifdef stderr
++              /* don't close stderr */
++              if (target->tgt_file.out != stderr)
++#endif
++              {
++                      fclose(target->tgt_file.out);
++                      target->tgt_file.out = NULL;
++              }
++      }
++
++      talloc_free(target);
++}
++
++/* close and re-open a log file (for log file rotation) */
++int log_target_file_reopen(struct log_target *target)
++{
++      fclose(target->tgt_file.out);
++
++      target->tgt_file.out = fopen(target->tgt_file.fname, "a");
++      if (!target->tgt_file.out)
++              return -errno;
++
++      /* we assume target->output already to be set */
++
++      return 0;
++}
++
 +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");
 +      osmo_log_info = cat;
 +}
index e953508,0000000..3ba2d43
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,63 @@@
 +/* plugin infrastructure */
 +
 +/* (C) 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 "../config.h"
 +
 +#if HAVE_DLFCN_H
 +
 +#include <sys/types.h>
 +#include <dirent.h>
 +#include <dlfcn.h>
 +#include <stdio.h>
 +#include <errno.h>
++#include <limits.h>
 +
 +#include <osmocore/plugin.h>
 +
 +int plugin_load_all(const char *directory)
 +{
 +      unsigned int num = 0;
 +      char fname[PATH_MAX];
 +      DIR *dir;
 +      struct dirent *entry;
 +
 +      dir = opendir(directory);
 +      if (!dir)
 +              return -errno;
 +
 +      while ((entry = readdir(dir))) {
 +              snprintf(fname, sizeof(fname), "%s/%s", directory,
 +                      entry->d_name);
 +              if (dlopen(fname, RTLD_NOW))
 +                      num++;
 +      }
 +
 +      closedir(dir);
 +
 +      return num;
 +}
 +#else
 +int plugin_load_all(const char *directory)
 +{
 +      return 0;
 +}
 +#endif /* HAVE_DLFCN_H */
index 0000000,0000000..180efa5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++/* Process handling support code */
++
++/* (C) 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 <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++
++int osmo_daemonize(void)
++{
++      int rc;
++      pid_t pid, sid;
++
++      /* Check if parent PID == init, in which case we are already a daemon */
++      if (getppid() == 1)
++              return -EEXIST;
++
++      /* Fork from the parent process */
++      pid = fork();
++      if (pid < 0) {
++              /* some error happened */
++              return pid;
++      }
++
++      if (pid > 0) {
++              /* if we have received a positive PID, then we are the parent
++               * and can exit */
++              exit(0);
++      }
++
++      /* FIXME: do we really want this? */
++      umask(0);
++
++      /* Create a new session and set process group ID */
++      sid = setsid();
++      if (sid < 0)
++              return sid;
++
++      /* Change to the /tmp directory, which prevents the CWD from being locked
++       * and unable to remove it */
++      rc = chdir("/tmp");
++      if (rc < 0)
++              return rc;
++
++      /* Redirect stdio to /dev/null */
++/* since C89/C99 says stderr is a macro, we can safely do this! */
++#ifdef stderr
++      freopen("/dev/null", "r", stdin);
++      freopen("/dev/null", "w", stdout);
++      freopen("/dev/null", "w", stderr);
++#endif
++
++      return 0;
++}
index 2f6afa7,0000000..f52b0a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,131 -1,0 +1,143 @@@
 +/* select filedescriptor handling, taken from:
 + * userspace logging daemon for the iptables ULOG target
 + * of the linux 2.4 netfilter subsystem.
 + *
 + * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU General Public License version 2 
 + *  as published by the Free Software Foundation
 + *
 + *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 + */
 +
 +#include <fcntl.h>
++#include <stdio.h>
++
 +#include <osmocore/select.h>
 +#include <osmocore/linuxlist.h>
 +#include <osmocore/timer.h>
 +
 +#include "../config.h"
 +
 +#ifdef HAVE_SYS_SELECT_H
 +
 +static int maxfd = 0;
 +static LLIST_HEAD(bsc_fds);
 +static int unregistered_count;
 +
 +int bsc_register_fd(struct bsc_fd *fd)
 +{
 +      int flags;
 +
 +      /* make FD nonblocking */
 +      flags = fcntl(fd->fd, F_GETFL);
 +      if (flags < 0)
 +              return flags;
 +      flags |= O_NONBLOCK;
 +      flags = fcntl(fd->fd, F_SETFL, flags);
 +      if (flags < 0)
 +              return flags;
 +
 +      /* Register FD */
 +      if (fd->fd > maxfd)
 +              maxfd = fd->fd;
 +
++#ifdef BSC_FD_CHECK
++      struct bsc_fd *entry;
++      llist_for_each_entry(entry, &bsc_fds, list) {
++              if (entry == fd) {
++                      fprintf(stderr, "Adding a bsc_fd that is already in the list.\n");
++                      return 0;
++              }
++      }
++#endif
++
 +      llist_add_tail(&fd->list, &bsc_fds);
 +
 +      return 0;
 +}
 +
 +void bsc_unregister_fd(struct bsc_fd *fd)
 +{
 +      unregistered_count++;
 +      llist_del(&fd->list);
 +}
 +
 +int bsc_select_main(int polling)
 +{
 +      struct bsc_fd *ufd, *tmp;
 +      fd_set readset, writeset, exceptset;
 +      int work = 0, rc;
 +      struct timeval no_time = {0, 0};
 +
 +      FD_ZERO(&readset);
 +      FD_ZERO(&writeset);
 +      FD_ZERO(&exceptset);
 +
 +      /* prepare read and write fdsets */
 +      llist_for_each_entry(ufd, &bsc_fds, list) {
 +              if (ufd->when & BSC_FD_READ)
 +                      FD_SET(ufd->fd, &readset);
 +
 +              if (ufd->when & BSC_FD_WRITE)
 +                      FD_SET(ufd->fd, &writeset);
 +
 +              if (ufd->when & BSC_FD_EXCEPT)
 +                      FD_SET(ufd->fd, &exceptset);
 +      }
 +
 +      bsc_timer_check();
 +
 +      if (!polling)
 +              bsc_prepare_timers();
 +      rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer());
 +      if (rc < 0)
 +              return 0;
 +
 +      /* fire timers */
 +      bsc_update_timers();
 +
 +      /* call registered callback functions */
 +restart:
 +      unregistered_count = 0;
 +      llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
 +              int flags = 0;
 +
 +              if (FD_ISSET(ufd->fd, &readset)) {
 +                      flags |= BSC_FD_READ;
 +                      FD_CLR(ufd->fd, &readset);
 +              }
 +
 +              if (FD_ISSET(ufd->fd, &writeset)) {
 +                      flags |= BSC_FD_WRITE;
 +                      FD_CLR(ufd->fd, &writeset);
 +              }
 +
 +              if (FD_ISSET(ufd->fd, &exceptset)) {
 +                      flags |= BSC_FD_EXCEPT;
 +                      FD_CLR(ufd->fd, &exceptset);
 +              }
 +
 +              if (flags) {
 +                      work = 1;
 +                      ufd->cb(ufd, flags);
 +              }
 +              /* ugly, ugly hack. If more than one filedescriptors were
 +               * unregistered, they might have been consecutive and
 +               * llist_for_each_entry_safe() is no longer safe */
 +              /* this seems to happen with the last element of the list as well */
 +              if (unregistered_count >= 1)
 +                      goto restart;
 +      }
 +      return work;
 +}
 +
 +#endif /* _HAVE_SYS_SELECT_H */
index f2859cf,0000000..7353ab8
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,14 @@@
 +# 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
++libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
 +endif
index 598e63c,0000000..7525df6
mode 100644,000000..100644
--- /dev/null
@@@ -1,3213 -1,0 +1,3225 @@@
-       /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
 +/*
 +   $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);
 +}
 +
++static int is_config(struct vty *vty)
++{
++      if (vty->node <= CONFIG_NODE)
++              return 0;
++      else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
++              return 1;
++      else if (host.app_info->is_config_node)
++              return host.app_info->is_config_node(vty, vty->node);
++      else
++              return vty->node > CONFIG_NODE;
++}
++
 +/* 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;
 +
 +      if (str == NULL)
 +              return 1;
 +
 +      if (range[1] == '-') {
 +              signed long min = 0, max = 0, val;
 +
 +              val = strtol(str, &endptr, 10);
 +              if (*endptr != '\0')
 +                      return 0;
 +
 +              range += 2;
 +              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 = -strtol(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 = strtol(buf, &endptr, 10);
 +              if (*endptr != '\0')
 +                      return 0;
 +
 +              if (val < min || val > max)
 +                      return 0;
 +      } else {
 +              unsigned long min, max, val;
 +
 +              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;
 +
-              && vty->node > CONFIG_NODE) {
++      /* Go to parent for config nodes to attempt to find the right command */
 +      while (ret != CMD_SUCCESS && ret != CMD_WARNING
-                      && vty->node != CONFIG_NODE) {
++             && is_config(vty)) {
 +              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 && is_config(vty)) {
 +                      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));
 +}
index 9069096,0000000..1523a89
mode 100644,000000..100644
--- /dev/null
@@@ -1,203 -1,0 +1,206 @@@
- extern const char *openbsc_copyright;
 +/* 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>
++#include <osmocom/vty/command.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;
 +}
 +
-               "Welcome to the OpenBSC Control interface\n";
++extern struct host host;
 +
 +static void print_welcome(int fd)
 +{
 +      int ret;
 +      static char *msg =
-       ret = write(fd, openbsc_copyright, strlen(openbsc_copyright));
++              "Welcome to the OpenBSC Control interface\r\n";
 +
 +      ret = write(fd, msg, strlen(msg));
++
++      if (host.app_info->copyright)
++              ret = write(fd, host.app_info->copyright, strlen(host.app_info->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;
 +      }
 +}
 +
index ff17abf,0000000..5c5a908
mode 100644,000000..100644
--- /dev/null
@@@ -1,1683 -1,0 +1,1692 @@@
 +
 +#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;
 +}
 +
++void *vty_current_index(struct vty *vty)
++{
++      return vty->index;
++}
++int vty_current_node(struct vty *vty)
++{
++      return vty->node;
++}
++
 +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;
 +}