Merge commit '4a4f96d1f67441e2902a0806f1d7c4b29a072ca3'
authorSteve Markgraf <steve@steve-m.de>
Wed, 5 Jan 2011 22:48:06 +0000 (23:48 +0100)
committerSteve Markgraf <steve@steve-m.de>
Wed, 5 Jan 2011 22:48:06 +0000 (23:48 +0100)
1  2 
src/shared/libosmocore/.gitignore
src/shared/libosmocore/configure.in
src/shared/libosmocore/include/osmocore/protocol/gsm_03_41.h
src/shared/libosmocore/src/rate_ctr.c
src/shared/libosmocore/src/vty/telnet_interface.c
src/shared/libosmocore/src/vty/vty.c
src/shared/libosmocore/tests/Makefile.am
src/shared/libosmocore/tests/smscb/Makefile.am
src/shared/libosmocore/tests/smscb/smscb_test.c

index 455440f,0000000..d39ae4a
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,32 @@@
 +Makefile
 +Makefile.in
 +.deps
 +.libs
 +*.o
 +*.lo
 +*.la
 +*.pc
 +aclocal.m4
 +m4/*.m4
 +autom4te.cache
 +config.h*
 +config.sub
 +config.log
 +config.status
 +config.guess
 +configure
 +depcomp
 +missing
 +ltmain.sh
 +install-sh
 +stamp-h1
 +libtool
 +
 +.tarball-version
 +.version
 +
 +tests/sms/sms_test
 +tests/timer/timer_test
 +tests/msgfile/msgfile_test
 +tests/ussd/ussd_test
++tests/smscb/smscb_test
index 825152d,0000000..309aa03
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,120 @@@
 +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_ARG_ENABLE(msgfile,
 +      [AS_HELP_STRING(
 +              [--disable-msgfile],
 +              [Disable support for the msgfile],
 +      )],
 +      [enable_msgfile=0], [enable_msgfile=1])
 +AM_CONDITIONAL(ENABLE_MSGFILE, test "x$enable_msgfile" = "x1")
 +
 +
 +AC_OUTPUT(
 +      libosmocore.pc
 +      libosmocodec.pc
 +      libosmovty.pc
 +      include/osmocom/Makefile
 +      include/osmocom/vty/Makefile
 +      include/osmocom/codec/Makefile
 +      include/osmocom/crypt/Makefile
 +      include/osmocore/Makefile
 +      include/osmocore/protocol/Makefile
 +      include/Makefile
 +      src/Makefile
 +      src/vty/Makefile
 +      src/codec/Makefile
 +      tests/Makefile
 +      tests/timer/Makefile
 +      tests/sms/Makefile
 +      tests/msgfile/Makefile
 +      tests/ussd/Makefile
++      tests/smscb/Makefile
 +      Makefile)
index 250d667,0000000..3b1b7c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,51 @@@
- /* GSM TS 03.41 definitions */
 +#ifndef PROTO_GSM_03_41_H
 +#define PROTO_GSM_03_41_H
 +
 +#include <stdint.h>
 +
-               uint8_t update:2;
-               uint8_t code_lo:6;
++/* GSM TS 03.41 definitions also TS 23.041*/
 +
 +/* Chapter 9.3.2 */
 +struct gsm341_ms_message {
 +      struct {
 +              uint8_t code_hi:6;
 +              uint8_t gs:2;
++              uint8_t update:4;
++              uint8_t code_lo:4;
 +      } serial;
 +      uint16_t msg_id;
 +      struct {
 +              uint8_t language:4;
 +              uint8_t group:4;
 +      } dcs;
 +      struct {
 +              uint8_t total:4;
 +              uint8_t current:4;
 +      } page;
 +      uint8_t data[0];
 +} __attribute__((packed));
 +
++/* Chapter 9.4.1.3 */
++struct gsm341_etws_message {
++      struct {
++              uint8_t code_hi:4;
++              uint8_t popup:1;
++              uint8_t alert:1;
++              uint8_t gs:2;
++              uint8_t update:4;
++              uint8_t code_lo:4;
++      } serial;
++      uint16_t msg_id;
++      uint16_t warning_type;
++      uint8_t data[0];
++} __attribute__((packed));
++
++#define GSM341_MSG_CODE(ms) (ms->serial.code_lo | (msg->serial.code_hi << 4))
++
 +/* Section 9.3.2.1 - Geographical Scope */
 +#define GSM341_GS_CELL_WIDE_IMMED     0
 +#define GSM341_GS_PLMN_WIDE           1
 +#define GSM341_GS_LA_WIDE             2
 +#define GSM341_GS_CELL_WIDE           3
 +
 +#endif /* PROTO_GSM_03_41_H */
index f58b5c4,0000000..80ef55b
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,127 @@@
- #include <inttypes.h>
 +/* utility routines for keeping conters about events and the event rates */
 +
 +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
 + *
 + * All Rights Reserved
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + */
 +
 +#include <stdint.h>
 +#include <string.h>
 +
 +#include <osmocore/utils.h>
 +#include <osmocore/linuxlist.h>
 +#include <osmocore/talloc.h>
 +#include <osmocore/timer.h>
 +#include <osmocore/rate_ctr.h>
 +
 +static LLIST_HEAD(rate_ctr_groups);
 +
 +static void *tall_rate_ctr_ctx;
 +
 +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
 +                                          const struct rate_ctr_group_desc *desc,
 +                                          unsigned int idx)
 +{
 +      unsigned int size;
 +      struct rate_ctr_group *group;
 +
 +      size = sizeof(struct rate_ctr_group) +
 +                      desc->num_ctr * sizeof(struct rate_ctr);
 +
 +      if (!ctx)
 +              ctx = tall_rate_ctr_ctx;
 +
 +      group = talloc_zero_size(ctx, size);
 +      if (!group)
 +              return NULL;
 +
 +      group->desc = desc;
 +      group->idx = idx;
 +
 +      llist_add(&group->list, &rate_ctr_groups);
 +
 +      return group;
 +}
 +
 +void rate_ctr_group_free(struct rate_ctr_group *grp)
 +{
 +      llist_del(&grp->list);
 +      talloc_free(grp);
 +}
 +
 +void rate_ctr_add(struct rate_ctr *ctr, int inc)
 +{
 +      ctr->current += inc;
 +}
 +
 +static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
 +{
 +      /* calculate rate over last interval */
 +      ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
 +      /* save current counter for next interval */
 +      ctr->intv[intv].last = ctr->current;
 +
 +      /* update the rate of the next bigger interval.  This will
 +       * be overwritten when that next larger interval expires */
 +      if (intv + 1 < ARRAY_SIZE(ctr->intv))
 +              ctr->intv[intv+1].rate += ctr->intv[intv].rate;
 +}
 +
 +static struct timer_list rate_ctr_timer;
 +static uint64_t timer_ticks;
 +
 +/* The one-second interval has expired */
 +static void rate_ctr_group_intv(struct rate_ctr_group *grp)
 +{
 +      unsigned int i;
 +
 +      for (i = 0; i < grp->desc->num_ctr; i++) {
 +              struct rate_ctr *ctr = &grp->ctr[i];
 +
 +              interval_expired(ctr, RATE_CTR_INTV_SEC);
 +              if ((timer_ticks % 60) == 0)
 +                      interval_expired(ctr, RATE_CTR_INTV_MIN);
 +              if ((timer_ticks % (60*60)) == 0)
 +                      interval_expired(ctr, RATE_CTR_INTV_HOUR);
 +              if ((timer_ticks % (24*60*60)) == 0)
 +                      interval_expired(ctr, RATE_CTR_INTV_DAY);
 +      }
 +}
 +
 +static void rate_ctr_timer_cb(void *data)
 +{
 +      struct rate_ctr_group *ctrg;
 +
 +      /* Increment number of ticks before we calculate intervals,
 +       * as a counter value of 0 would already wrap all counters */
 +      timer_ticks++;
 +
 +      llist_for_each_entry(ctrg, &rate_ctr_groups, list)
 +              rate_ctr_group_intv(ctrg);
 +
 +      bsc_schedule_timer(&rate_ctr_timer, 1, 0);
 +}
 +
 +int rate_ctr_init(void *tall_ctx)
 +{
 +      tall_rate_ctr_ctx = tall_ctx;
 +      rate_ctr_timer.cb = rate_ctr_timer_cb;
 +      bsc_schedule_timer(&rate_ctr_timer, 1, 0);
 +
 +      return 0;
 +}
index 1523a89,0000000..098fa2e
mode 100644,000000..100644
--- /dev/null
@@@ -1,206 -1,0 +1,206 @@@
-       if (bind < 0) {
 +/* 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 (rc < 0) {
 +              LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n");
 +              close(fd);
 +              return rc;
 +      }
 +
 +      rc = listen(fd, 0);
 +      if (rc < 0) {
 +              LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n");
 +              close(fd);
 +              return rc;
 +      }
 +
 +      server_socket.data = priv;
 +      server_socket.fd = fd;
 +      bsc_register_fd(&server_socket);
 +
 +      return 0;
 +}
 +
 +extern struct host host;
 +
 +static void print_welcome(int fd)
 +{
 +      int ret;
 +      static char *msg =
 +              "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 5c5a908,0000000..a5b16dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,1692 -1,0 +1,1692 @@@
-               hostname = host.name;
 +
 +#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.app_info->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;
 +}
index 0166b4f,0000000..2b4ac6e
mode 100644,000000..100644
--- /dev/null
@@@ -1,6 -1,0 +1,6 @@@
- SUBDIRS = timer sms ussd
 +if ENABLE_TESTS
++SUBDIRS = timer sms ussd smscb
 +if ENABLE_MSGFILE
 +SUBDIRS += msgfile
 +endif
 +endif
index 0000000,0000000..1d0e538
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++INCLUDES = $(all_includes) -I$(top_srcdir)/include
++noinst_PROGRAMS = smscb_test
++
++smscb_test_SOURCES = smscb_test.c
++smscb_test_LDADD = $(top_builddir)/src/libosmocore.la
index 0000000,0000000..627d5a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++/*
++ * (C) 2010 Holger Hans Peter Freyther
++ * All Rights Reserved
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ */
++
++#include <osmocore/protocol/gsm_03_41.h>
++
++#include <stdio.h>
++
++static uint8_t smscb_msg[] = { 0x40, 0x10, 0x05, 0x0d, 0x01, 0x11 };
++
++int main(int argc, char **argv)
++{
++      struct gsm341_ms_message *msg;
++
++      msg = (struct gsm341_ms_message *) smscb_msg;
++      printf("(srl) GS: %d MSG_CODE: %d UPDATE: %d\n",
++              msg->serial.gs, GSM341_MSG_CODE(msg), msg->serial.update);
++      printf("(msg) msg_id: %d\n", htons(msg->msg_id));
++      printf("(dcs) group: %d language: %d\n",
++              msg->dcs.language, msg->dcs.group);
++      printf("(pge) page total: %d current: %d\n",
++              msg->page.total, msg->page.current);
++
++      return 0;
++}