aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/vty
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-06-10 05:40:52 +0800
committerHarald Welte <laforge@gnumonks.org>2009-06-10 05:40:52 +0800
commit13e10daa330ea2b699c9aa9d14b3adbd01111fd6 (patch)
treebf9144f9cf625baab472492b3047970cab14ef83 /openbsc/src/vty
parentf7c43524cfc6e30a0223d3aaff89fe955d6e5146 (diff)
move openbsc into its own subdirectory
Diffstat (limited to 'openbsc/src/vty')
-rw-r--r--openbsc/src/vty/buffer.c460
-rw-r--r--openbsc/src/vty/cardshell.h5
-rw-r--r--openbsc/src/vty/command.c3416
-rw-r--r--openbsc/src/vty/vector.c186
-rw-r--r--openbsc/src/vty/vty.c1634
5 files changed, 5701 insertions, 0 deletions
diff --git a/openbsc/src/vty/buffer.c b/openbsc/src/vty/buffer.c
new file mode 100644
index 00000000..63661004
--- /dev/null
+++ b/openbsc/src/vty/buffer.c
@@ -0,0 +1,460 @@
+/*
+ * Buffering of output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/uio.h>
+
+#include <vty/buffer.h>
+#include <vty/vty.h>
+
+/* Buffer master. */
+struct buffer {
+ /* Data list. */
+ struct buffer_data *head;
+ struct buffer_data *tail;
+
+ /* Size of each buffer_data chunk. */
+ size_t size;
+};
+
+/* Data container. */
+struct buffer_data {
+ struct buffer_data *next;
+
+ /* Location to add new data. */
+ size_t cp;
+
+ /* Pointer to data not yet flushed. */
+ size_t sp;
+
+ /* Actual data stream (variable length). */
+ unsigned char data[0]; /* real dimension is buffer->size */
+};
+
+/* It should always be true that: 0 <= sp <= cp <= size */
+
+/* Default buffer size (used if none specified). It is rounded up to the
+ next page boundery. */
+#define BUFFER_SIZE_DEFAULT 4096
+
+#define BUFFER_DATA_FREE(D) free((D))
+
+/* Make new buffer. */
+struct buffer *buffer_new(size_t size)
+{
+ struct buffer *b;
+
+ b = calloc(1, sizeof(struct buffer));
+
+ if (size)
+ b->size = size;
+ else {
+ static size_t default_size;
+ if (!default_size) {
+ long pgsz = sysconf(_SC_PAGESIZE);
+ default_size =
+ ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
+ }
+ b->size = default_size;
+ }
+
+ return b;
+}
+
+/* Free buffer. */
+void buffer_free(struct buffer *b)
+{
+ buffer_reset(b);
+ free(b);
+}
+
+/* Make string clone. */
+char *buffer_getstr(struct buffer *b)
+{
+ size_t totlen = 0;
+ struct buffer_data *data;
+ char *s;
+ char *p;
+
+ for (data = b->head; data; data = data->next)
+ totlen += data->cp - data->sp;
+ if (!(s = malloc(totlen + 1)))
+ return NULL;
+ p = s;
+ for (data = b->head; data; data = data->next) {
+ memcpy(p, data->data + data->sp, data->cp - data->sp);
+ p += data->cp - data->sp;
+ }
+ *p = '\0';
+ return s;
+}
+
+/* Return 1 if buffer is empty. */
+int buffer_empty(struct buffer *b)
+{
+ return (b->head == NULL);
+}
+
+/* Clear and free all allocated data. */
+void buffer_reset(struct buffer *b)
+{
+ struct buffer_data *data;
+ struct buffer_data *next;
+
+ for (data = b->head; data; data = next) {
+ next = data->next;
+ BUFFER_DATA_FREE(data);
+ }
+ b->head = b->tail = NULL;
+}
+
+/* Add buffer_data to the end of buffer. */
+static struct buffer_data *buffer_add(struct buffer *b)
+{
+ struct buffer_data *d;
+
+ d = malloc(offsetof(struct buffer_data, data[b->size]));
+ if (!d)
+ return NULL;
+ d->cp = d->sp = 0;
+ d->next = NULL;
+
+ if (b->tail)
+ b->tail->next = d;
+ else
+ b->head = d;
+ b->tail = d;
+
+ return d;
+}
+
+/* Write data to buffer. */
+void buffer_put(struct buffer *b, const void *p, size_t size)
+{
+ struct buffer_data *data = b->tail;
+ const char *ptr = p;
+
+ /* We use even last one byte of data buffer. */
+ while (size) {
+ size_t chunk;
+
+ /* If there is no data buffer add it. */
+ if (data == NULL || data->cp == b->size)
+ data = buffer_add(b);
+
+ chunk =
+ ((size <=
+ (b->size - data->cp)) ? size : (b->size - data->cp));
+ memcpy((data->data + data->cp), ptr, chunk);
+ size -= chunk;
+ ptr += chunk;
+ data->cp += chunk;
+ }
+}
+
+/* Insert character into the buffer. */
+void buffer_putc(struct buffer *b, u_char c)
+{
+ buffer_put(b, &c, 1);
+}
+
+/* Put string to the buffer. */
+void buffer_putstr(struct buffer *b, const char *c)
+{
+ buffer_put(b, c, strlen(c));
+}
+
+/* Keep flushing data to the fd until the buffer is empty or an error is
+ encountered or the operation would block. */
+buffer_status_t buffer_flush_all(struct buffer *b, int fd)
+{
+ buffer_status_t ret;
+ struct buffer_data *head;
+ size_t head_sp;
+
+ if (!b->head)
+ return BUFFER_EMPTY;
+ head_sp = (head = b->head)->sp;
+ /* Flush all data. */
+ while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
+ if ((b->head == head) && (head_sp == head->sp)
+ && (errno != EINTR))
+ /* No data was flushed, so kernel buffer must be full. */
+ return ret;
+ head_sp = (head = b->head)->sp;
+ }
+
+ return ret;
+}
+
+#if 0
+/* Flush enough data to fill a terminal window of the given scene (used only
+ by vty telnet interface). */
+buffer_status_t
+buffer_flush_window(struct buffer * b, int fd, int width, int height,
+ int erase_flag, int no_more_flag)
+{
+ int nbytes;
+ int iov_alloc;
+ int iov_index;
+ struct iovec *iov;
+ struct iovec small_iov[3];
+ char more[] = " --More-- ";
+ char erase[] =
+ { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ };
+ struct buffer_data *data;
+ int column;
+
+ if (!b->head)
+ return BUFFER_EMPTY;
+
+ if (height < 1) {
+ zlog_warn
+ ("%s called with non-positive window height %d, forcing to 1",
+ __func__, height);
+ height = 1;
+ } else if (height >= 2)
+ height--;
+ if (width < 1) {
+ zlog_warn
+ ("%s called with non-positive window width %d, forcing to 1",
+ __func__, width);
+ width = 1;
+ }
+
+ /* For erase and more data add two to b's buffer_data count. */
+ if (b->head->next == NULL) {
+ iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
+ iov = small_iov;
+ } else {
+ iov_alloc = ((height * (width + 2)) / b->size) + 10;
+ iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
+ }
+ iov_index = 0;
+
+ /* Previously print out is performed. */
+ if (erase_flag) {
+ iov[iov_index].iov_base = erase;
+ iov[iov_index].iov_len = sizeof erase;
+ iov_index++;
+ }
+
+ /* Output data. */
+ column = 1; /* Column position of next character displayed. */
+ for (data = b->head; data && (height > 0); data = data->next) {
+ size_t cp;
+
+ cp = data->sp;
+ while ((cp < data->cp) && (height > 0)) {
+ /* Calculate lines remaining and column position after displaying
+ this character. */
+ if (data->data[cp] == '\r')
+ column = 1;
+ else if ((data->data[cp] == '\n') || (column == width)) {
+ column = 1;
+ height--;
+ } else
+ column++;
+ cp++;
+ }
+ iov[iov_index].iov_base = (char *)(data->data + data->sp);
+ iov[iov_index++].iov_len = cp - data->sp;
+ data->sp = cp;
+
+ if (iov_index == iov_alloc)
+ /* This should not ordinarily happen. */
+ {
+ iov_alloc *= 2;
+ if (iov != small_iov) {
+ zlog_warn("%s: growing iov array to %d; "
+ "width %d, height %d, size %lu",
+ __func__, iov_alloc, width, height,
+ (u_long) b->size);
+ iov =
+ XREALLOC(MTYPE_TMP, iov,
+ iov_alloc * sizeof(*iov));
+ } else {
+ /* This should absolutely never occur. */
+ zlog_err
+ ("%s: corruption detected: iov_small overflowed; "
+ "head %p, tail %p, head->next %p",
+ __func__, b->head, b->tail, b->head->next);
+ iov =
+ XMALLOC(MTYPE_TMP,
+ iov_alloc * sizeof(*iov));
+ memcpy(iov, small_iov, sizeof(small_iov));
+ }
+ }
+ }
+
+ /* In case of `more' display need. */
+ if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
+ iov[iov_index].iov_base = more;
+ iov[iov_index].iov_len = sizeof more;
+ iov_index++;
+ }
+#ifdef IOV_MAX
+ /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
+ example: Solaris2.6 are defined IOV_MAX size at 16. */
+ {
+ struct iovec *c_iov = iov;
+ nbytes = 0; /* Make sure it's initialized. */
+
+ while (iov_index > 0) {
+ int iov_size;
+
+ iov_size =
+ ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
+ if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+ zlog_warn("%s: writev to fd %d failed: %s",
+ __func__, fd, safe_strerror(errno));
+ break;
+ }
+
+ /* move pointer io-vector */
+ c_iov += iov_size;
+ iov_index -= iov_size;
+ }
+ }
+#else /* IOV_MAX */
+ if ((nbytes = writev(fd, iov, iov_index)) < 0)
+ zlog_warn("%s: writev to fd %d failed: %s",
+ __func__, fd, safe_strerror(errno));
+#endif /* IOV_MAX */
+
+ /* Free printed buffer data. */
+ while (b->head && (b->head->sp == b->head->cp)) {
+ struct buffer_data *del;
+ if (!(b->head = (del = b->head)->next))
+ b->tail = NULL;
+ BUFFER_DATA_FREE(del);
+ }
+
+ if (iov != small_iov)
+ XFREE(MTYPE_TMP, iov);
+
+ return (nbytes < 0) ? BUFFER_ERROR :
+ (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
+}
+#endif
+
+/* This function (unlike other buffer_flush* functions above) is designed
+to work with non-blocking sockets. It does not attempt to write out
+all of the queued data, just a "big" chunk. It returns 0 if it was
+able to empty out the buffers completely, 1 if more flushing is
+required later, or -1 on a fatal write error. */
+buffer_status_t buffer_flush_available(struct buffer * b, int fd)
+{
+
+/* These are just reasonable values to make sure a significant amount of
+data is written. There's no need to go crazy and try to write it all
+in one shot. */
+#ifdef IOV_MAX
+#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
+#else
+#define MAX_CHUNKS 16
+#endif
+#define MAX_FLUSH 131072
+
+ struct buffer_data *d;
+ size_t written;
+ struct iovec iov[MAX_CHUNKS];
+ size_t iovcnt = 0;
+ size_t nbyte = 0;
+
+ for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
+ d = d->next, iovcnt++) {
+ iov[iovcnt].iov_base = d->data + d->sp;
+ nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
+ }
+
+ if (!nbyte)
+ /* No data to flush: should we issue a warning message? */
+ return BUFFER_EMPTY;
+
+ /* only place where written should be sign compared */
+ if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ /* Calling code should try again later. */
+ return BUFFER_PENDING;
+ return BUFFER_ERROR;
+ }
+
+ /* Free printed buffer data. */
+ while (written > 0) {
+ struct buffer_data *d;
+ if (!(d = b->head))
+ break;
+ if (written < d->cp - d->sp) {
+ d->sp += written;
+ return BUFFER_PENDING;
+ }
+
+ written -= (d->cp - d->sp);
+ if (!(b->head = d->next))
+ b->tail = NULL;
+ BUFFER_DATA_FREE(d);
+ }
+
+ return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+
+#undef MAX_CHUNKS
+#undef MAX_FLUSH
+}
+
+buffer_status_t
+buffer_write(struct buffer * b, int fd, const void *p, size_t size)
+{
+ ssize_t nbytes;
+
+#if 0
+ /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
+
+ if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
+ return BUFFER_ERROR;
+#endif
+ if (b->head)
+ /* Buffer is not empty, so do not attempt to write the new data. */
+ nbytes = 0;
+ else if ((nbytes = write(fd, p, size)) < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ nbytes = 0;
+ else
+ return BUFFER_ERROR;
+ }
+ /* Add any remaining data to the buffer. */
+ {
+ size_t written = nbytes;
+ if (written < size)
+ buffer_put(b, ((const char *)p) + written,
+ size - written);
+ }
+ return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+}
diff --git a/openbsc/src/vty/cardshell.h b/openbsc/src/vty/cardshell.h
new file mode 100644
index 00000000..d963a381
--- /dev/null
+++ b/openbsc/src/vty/cardshell.h
@@ -0,0 +1,5 @@
+#define QUAGGA_PROGNAME "OpenBSC"
+#define QUAGGA_VERSION "0.01"
+#define QUAGGA_COPYRIGHT "Harald Welte <laforge@gnumonks.org>"
+#define CONFIGFILE_MASK 022
+#define SYSCONFDIR "/usr/local/etc"
diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c
new file mode 100644
index 00000000..94a5c2af
--- /dev/null
+++ b/openbsc/src/vty/command.c
@@ -0,0 +1,3416 @@
+/*
+ $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 "cardshell.h"
+
+#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 "memory.h"
+//#include "log.h"
+//#include <lib/version.h>
+//#include "thread.h"
+#include <vty/vector.h>
+#include <vty/vty.h>
+#include <vty/command.h>
+//#include "workqueue.h"
+
+#include <openbsc/gsm_data.h>
+
+/* 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 = "\r\n\
+Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
+" QUAGGA_COPYRIGHT "\r\n\
+\r\n";
+
+#if 0
+static struct facility_map {
+ int facility;
+ const char *name;
+ size_t match;
+} syslog_facilities[] = {
+ {
+ LOG_KERN, "kern", 1}, {
+ LOG_USER, "user", 2}, {
+ LOG_MAIL, "mail", 1}, {
+ LOG_DAEMON, "daemon", 1}, {
+ LOG_AUTH, "auth", 1}, {
+ LOG_SYSLOG, "syslog", 1}, {
+ LOG_LPR, "lpr", 2}, {
+ LOG_NEWS, "news", 1}, {
+ LOG_UUCP, "uucp", 2}, {
+ LOG_CRON, "cron", 1},
+#ifdef LOG_FTP
+ {
+ LOG_FTP, "ftp", 1},
+#endif
+ {
+ LOG_LOCAL0, "local0", 6}, {
+ LOG_LOCAL1, "local1", 6}, {
+ LOG_LOCAL2, "local2", 6}, {
+ LOG_LOCAL3, "local3", 6}, {
+ LOG_LOCAL4, "local4", 6}, {
+ LOG_LOCAL5, "local5", 6}, {
+ LOG_LOCAL6, "local6", 6}, {
+ LOG_LOCAL7, "local7", 6}, {
+0, NULL, 0},};
+
+static const char *facility_name(int facility)
+{
+ struct facility_map *fm;
+
+ for (fm = syslog_facilities; fm->name; fm++)
+ if (fm->facility == facility)
+ return fm->name;
+ return "";
+}
+
+static int facility_match(const char *str)
+{
+ struct facility_map *fm;
+
+ for (fm = syslog_facilities; fm->name; fm++)
+ if (!strncmp(str, fm->name, fm->match))
+ return fm->facility;
+ return -1;
+}
+
+static int level_match(const char *s)
+{
+ int level;
+
+ for (level = 0; zlog_priority[level] != NULL; level++)
+ if (!strncmp(s, zlog_priority[level], 2))
+ return level;
+ return ZLOG_DISABLED;
+}
+#endif
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void print_version(const char *progname)
+{
+ printf("%s version %s\n", progname, QUAGGA_VERSION);
+ printf("%s\n", QUAGGA_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 = malloc(len);
+ for (i = shift; i < argc; i++) {
+ size_t arglen;
+ memcpy(p, argv[i], (arglen = strlen(argv[i])));
+ p += arglen;
+ *p++ = ' ';
+ }
+ *(p - 1) = '\0';
+ return str;
+}
+
+/* Install top node of command vector. */
+void install_node(struct cmd_node *node, int (*func) (struct vty *))
+{
+ vector_set_index(cmdvec, node->node, node);
+ node->func = func;
+ node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
+}
+
+/* Compare two command's string. Used in sort_node (). */
+static int cmp_node(const void *p, const void *q)
+{
+ struct cmd_element *a = *(struct cmd_element **)p;
+ struct cmd_element *b = *(struct cmd_element **)q;
+
+ return strcmp(a->string, b->string);
+}
+
+static int cmp_desc(const void *p, const void *q)
+{
+ struct desc *a = *(struct desc **)p;
+ struct desc *b = *(struct desc **)q;
+
+ return strcmp(a->cmd, b->cmd);
+}
+
+/* Sort each node's command element according to command string. */
+void sort_node()
+{
+ unsigned int i, j;
+ struct cmd_node *cnode;
+ vector descvec;
+ struct cmd_element *cmd_element;
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((cnode = vector_slot(cmdvec, i)) != NULL) {
+ vector cmd_vector = cnode->cmd_vector;
+ qsort(cmd_vector->index, vector_active(cmd_vector),
+ sizeof(void *), cmp_node);
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ && vector_active(cmd_element->strvec)) {
+ descvec =
+ vector_slot(cmd_element->strvec,
+ vector_active
+ (cmd_element->strvec) -
+ 1);
+ qsort(descvec->index,
+ vector_active(descvec),
+ sizeof(void *), cmp_desc);
+ }
+ }
+}
+
+/* Breaking up string into each command piece. I assume given
+ character is separated by a space character. Return value is a
+ vector which includes char ** data element. */
+vector cmd_make_strvec(const char *string)
+{
+ const char *cp, *start;
+ char *token;
+ int strlen;
+ vector strvec;
+
+ if (string == NULL)
+ return NULL;
+
+ cp = string;
+
+ /* Skip white spaces. */
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ /* Return if there is only white spaces */
+ if (*cp == '\0')
+ return NULL;
+
+ if (*cp == '!' || *cp == '#')
+ return NULL;
+
+ /* Prepare return vector. */
+ strvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Copy each command piece and set into vector. */
+ while (1) {
+ start = cp;
+ while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
+ *cp != '\0')
+ cp++;
+ strlen = cp - start;
+ token = malloc(strlen + 1);
+ 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)
+ 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 = malloc(strlen + 1);
+ 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 = malloc(len + 1);
+ memcpy(token, sp, len);
+ *(token + len) = '\0';
+
+ desc = calloc(1, sizeof(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);
+}
+
+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);
+}
+
+/* This function write configuration of this host. */
+static int config_write_host(struct vty *vty)
+{
+#if 0
+ 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 (zlog_default->default_lvl != LOG_DEBUG) {
+ vty_out(vty, "! N.B. The 'log trap' command is deprecated.%s",
+ VTY_NEWLINE);
+ vty_out(vty, "log trap %s%s",
+ zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
+ }
+
+ if (host.logfile
+ && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) {
+ vty_out(vty, "log file %s", host.logfile);
+ if (zlog_default->maxlvl[ZLOG_DEST_FILE] !=
+ zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->
+ maxlvl[ZLOG_DEST_FILE]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
+ vty_out(vty, "log stdout");
+ if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] !=
+ zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->
+ maxlvl[ZLOG_DEST_STDOUT]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ vty_out(vty, "no log monitor%s", VTY_NEWLINE);
+ else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] !=
+ zlog_default->default_lvl)
+ vty_out(vty, "log monitor %s%s",
+ zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],
+ VTY_NEWLINE);
+
+ if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
+ vty_out(vty, "log syslog");
+ if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] !=
+ zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->
+ maxlvl[ZLOG_DEST_SYSLOG]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (zlog_default->facility != LOG_DAEMON)
+ vty_out(vty, "log facility %s%s",
+ facility_name(zlog_default->facility), VTY_NEWLINE);
+
+ if (zlog_default->record_priority == 1)
+ vty_out(vty, "log record-priority%s", 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);
+
+#endif
+ 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;
+}
+
+#if 0
+/* Filter command vector by symbol. This function is not actually used;
+ * should it be deleted? */
+static int cmd_filter_by_symbol(char *command, char *symbol)
+{
+ int i, lim;
+
+ if (strcmp(symbol, "IPV4_ADDRESS") == 0) {
+ i = 0;
+ lim = strlen(command);
+ while (i < lim) {
+ if (!
+ (isdigit((int)command[i]) || command[i] == '.'
+ || command[i] == '/'))
+ return 1;
+ i++;
+ }
+ return 0;
+ }
+ if (strcmp(symbol, "STRING") == 0) {
+ i = 0;
+ lim = strlen(command);
+ while (i < lim) {
+ if (!
+ (isalpha((int)command[i]) || command[i] == '_'
+ || command[i] == '-'))
+ return 1;
+ i++;
+ }
+ return 0;
+ }
+ if (strcmp(symbol, "IFNAME") == 0) {
+ i = 0;
+ lim = strlen(command);
+ while (i < lim) {
+ if (!isalnum((int)command[i]))
+ return 1;
+ i++;
+ }
+ return 0;
+ }
+ return 0;
+}
+#endif
+
+/* Completion match types. */
+enum match_type {
+ no_match,
+ extend_match,
+ ipv4_prefix_match,
+ ipv4_match,
+ ipv6_prefix_match,
+ ipv6_match,
+ range_match,
+ vararg_match,
+ partly_match,
+ exact_match
+};
+
+static enum match_type cmd_ipv4_match(const char *str)
+{
+ const char *sp;
+ int dots = 0, nums = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;) {
+ memset(buf, 0, sizeof(buf));
+ sp = str;
+ while (*str != '\0') {
+ if (*str == '.') {
+ if (dots >= 3)
+ return no_match;
+
+ if (*(str + 1) == '.')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy(buf, sp, str - sp);
+ if (atoi(buf) > 255)
+ return no_match;
+
+ nums++;
+
+ if (*str == '\0')
+ break;
+
+ str++;
+ }
+
+ if (nums < 4)
+ return partly_match;
+
+ return exact_match;
+}
+
+static enum match_type cmd_ipv4_prefix_match(const char *str)
+{
+ const char *sp;
+ int dots = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;) {
+ memset(buf, 0, sizeof(buf));
+ sp = str;
+ while (*str != '\0' && *str != '/') {
+ if (*str == '.') {
+ if (dots == 3)
+ return no_match;
+
+ if (*(str + 1) == '.' || *(str + 1) == '/')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy(buf, sp, str - sp);
+ if (atoi(buf) > 255)
+ return no_match;
+
+ if (dots == 3) {
+ if (*str == '/') {
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ str++;
+ break;
+ } else if (*str == '\0')
+ return partly_match;
+ }
+
+ if (*str == '\0')
+ return partly_match;
+
+ str++;
+ }
+
+ sp = str;
+ while (*str != '\0') {
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (atoi(sp) > 32)
+ return no_match;
+
+ return exact_match;
+}
+
+#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
+#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
+#define STATE_START 1
+#define STATE_COLON 2
+#define STATE_DOUBLE 3
+#define STATE_ADDR 4
+#define STATE_DOT 5
+#define STATE_SLASH 6
+#define STATE_MASK 7
+
+#ifdef HAVE_IPV6
+
+static enum match_type cmd_ipv6_match(const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ const char *sp = NULL;
+ struct sockaddr_in6 sin6_dummy;
+ int ret;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn(str, IPV6_ADDR_STR) != strlen(str))
+ return no_match;
+
+ /* use inet_pton that has a better support,
+ * for example inet_pton can support the automatic addresses:
+ * ::1.2.3.4
+ */
+ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+
+ if (ret == 1)
+ return exact_match;
+
+ while (*str != '\0') {
+ switch (state) {
+ case STATE_START:
+ if (*str == ':') {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ } else {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else {
+ if (*(str + 1) != '\0')
+ colons++;
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums++;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '\0') {
+ if (str - sp > 3)
+ return no_match;
+
+ nums++;
+ state = STATE_COLON;
+ }
+ if (*(str + 1) == '.')
+ state = STATE_DOT;
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 8)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+#if 0
+ if (nums < 11)
+ return partly_match;
+#endif /* 0 */
+
+ return exact_match;
+}
+
+static enum match_type cmd_ipv6_prefix_match(const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ int mask;
+ const char *sp = NULL;
+ char *endptr = NULL;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
+ return no_match;
+
+ while (*str != '\0' && state != STATE_MASK) {
+ switch (state) {
+ case STATE_START:
+ if (*str == ':') {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ } else {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == '/')
+ return no_match;
+ else if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else {
+ if (*(str + 1) != '\0' && *(str + 1) != '/')
+ colons++;
+ sp = str + 1;
+
+ if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ else
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums += 1;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '.'
+ || *(str + 1) == '\0' || *(str + 1) == '/') {
+ if (str - sp > 3)
+ return no_match;
+
+ for (; sp <= str; sp++)
+ if (*sp == '/')
+ return no_match;
+
+ nums++;
+
+ if (*(str + 1) == ':')
+ state = STATE_COLON;
+ else if (*(str + 1) == '.')
+ state = STATE_DOT;
+ else if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ }
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ case STATE_SLASH:
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ state = STATE_MASK;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 11)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+ if (state < STATE_MASK)
+ return partly_match;
+
+ mask = strtol(str, &endptr, 10);
+ if (*endptr != '\0')
+ return no_match;
+
+ if (mask < 0 || mask > 128)
+ return no_match;
+
+/* I don't know why mask < 13 makes command match partly.
+ Forgive me to make this comments. I Want to set static default route
+ because of lack of function to originate default in ospf6d; sorry
+ yasu
+ if (mask < 13)
+ return partly_match;
+*/
+
+ return exact_match;
+}
+
+#endif /* HAVE_IPV6 */
+
+#define DECIMAL_STRLEN_MAX 10
+
+static int cmd_range_match(const char *range, const char *str)
+{
+ char *p;
+ char buf[DECIMAL_STRLEN_MAX + 1];
+ char *endptr = NULL;
+ unsigned long min, max, val;
+
+ if (str == NULL)
+ return 1;
+
+ val = strtoul(str, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range++;
+ p = strchr(range, '-');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ min = strtoul(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range = p + 1;
+ p = strchr(range, '>');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ max = strtoul(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ if (val < min || val > max)
+ return 0;
+
+ return 1;
+}
+
+/* Make completion match and return match type flag. */
+static enum match_type
+cmd_filter_by_completion(char *command, vector v, unsigned int index)
+{
+ unsigned int i;
+ const char *str;
+ struct cmd_element *cmd_element;
+ enum match_type match_type;
+ vector descvec;
+ struct desc *desc;
+
+ match_type = no_match;
+
+ /* If command and cmd_element string does not match set NULL to vector */
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ if (index >= vector_active(cmd_element->strvec))
+ vector_slot(v, i) = NULL;
+ else {
+ unsigned int j;
+ int matched = 0;
+
+ descvec =
+ vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ str = desc->cmd;
+
+ if (CMD_VARARG(str)) {
+ if (match_type <
+ vararg_match)
+ match_type =
+ vararg_match;
+ matched++;
+ } else if (CMD_RANGE(str)) {
+ if (cmd_range_match
+ (str, command)) {
+ if (match_type <
+ range_match)
+ match_type
+ =
+ range_match;
+
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str)) {
+ if (cmd_ipv6_match
+ (command)) {
+ if (match_type <
+ ipv6_match)
+ match_type
+ =
+ ipv6_match;
+
+ matched++;
+ }
+ } else if (CMD_IPV6_PREFIX(str)) {
+ if (cmd_ipv6_prefix_match(command)) {
+ if (match_type <
+ ipv6_prefix_match)
+ match_type
+ =
+ ipv6_prefix_match;
+
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str)) {
+ if (cmd_ipv4_match
+ (command)) {
+ if (match_type <
+ ipv4_match)
+ match_type
+ =
+ ipv4_match;
+
+ matched++;
+ }
+ } else if (CMD_IPV4_PREFIX(str)) {
+ if (cmd_ipv4_prefix_match(command)) {
+ if (match_type <
+ ipv4_prefix_match)
+ match_type
+ =
+ ipv4_prefix_match;
+ matched++;
+ }
+ } else
+ /* Check is this point's argument optional ? */
+ if (CMD_OPTION(str)
+ ||
+ CMD_VARIABLE(str)) {
+ if (match_type <
+ extend_match)
+ match_type =
+ extend_match;
+ matched++;
+ } else
+ if (strncmp
+ (command, str,
+ strlen(command)) ==
+ 0) {
+ if (strcmp(command, str)
+ == 0)
+ match_type =
+ exact_match;
+ else {
+ if (match_type <
+ partly_match)
+ match_type
+ =
+ partly_match;
+ }
+ matched++;
+ }
+ }
+ if (!matched)
+ vector_slot(v, i) = NULL;
+ }
+ }
+ return match_type;
+}
+
+/* Filter vector by command character with index. */
+static enum match_type
+cmd_filter_by_string(char *command, vector v, unsigned int index)
+{
+ unsigned int i;
+ const char *str;
+ struct cmd_element *cmd_element;
+ enum match_type match_type;
+ vector descvec;
+ struct desc *desc;
+
+ match_type = no_match;
+
+ /* If command and cmd_element string does not match set NULL to vector */
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ /* If given index is bigger than max string vector of command,
+ set NULL */
+ if (index >= vector_active(cmd_element->strvec))
+ vector_slot(v, i) = NULL;
+ else {
+ unsigned int j;
+ int matched = 0;
+
+ descvec =
+ vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ str = desc->cmd;
+
+ if (CMD_VARARG(str)) {
+ if (match_type <
+ vararg_match)
+ match_type =
+ vararg_match;
+ matched++;
+ } else if (CMD_RANGE(str)) {
+ if (cmd_range_match
+ (str, command)) {
+ if (match_type <
+ range_match)
+ match_type
+ =
+ range_match;
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str)) {
+ if (cmd_ipv6_match
+ (command) ==
+ exact_match) {
+ if (match_type <
+ ipv6_match)
+ match_type
+ =
+ ipv6_match;
+ matched++;
+ }
+ } else if (CMD_IPV6_PREFIX(str)) {
+ if (cmd_ipv6_prefix_match(command) == exact_match) {
+ if (match_type <
+ ipv6_prefix_match)
+ match_type
+ =
+ ipv6_prefix_match;
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str)) {
+ if (cmd_ipv4_match
+ (command) ==
+ exact_match) {
+ if (match_type <
+ ipv4_match)
+ match_type
+ =
+ ipv4_match;
+ matched++;
+ }
+ } else if (CMD_IPV4_PREFIX(str)) {
+ if (cmd_ipv4_prefix_match(command) == exact_match) {
+ if (match_type <
+ ipv4_prefix_match)
+ match_type
+ =
+ ipv4_prefix_match;
+ matched++;
+ }
+ } else if (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+ {
+ if (match_type <
+ extend_match)
+ match_type =
+ extend_match;
+ matched++;
+ } else {
+ if (strcmp(command, str)
+ == 0) {
+ match_type =
+ exact_match;
+ matched++;
+ }
+ }
+ }
+ if (!matched)
+ vector_slot(v, i) = NULL;
+ }
+ }
+ return match_type;
+}
+
+/* Check ambiguous match */
+static int
+is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
+{
+ unsigned int i;
+ unsigned int j;
+ const char *str = NULL;
+ struct cmd_element *cmd_element;
+ const char *matched = NULL;
+ vector descvec;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ int match = 0;
+
+ descvec = vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ enum match_type ret;
+
+ str = desc->cmd;
+
+ switch (type) {
+ case exact_match:
+ if (!
+ (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+&& strcmp(command, str) == 0)
+ match++;
+ break;
+ case partly_match:
+ if (!
+ (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+&& strncmp(command, str, strlen(command)) == 0) {
+ if (matched
+ && strcmp(matched,
+ str) != 0)
+ return 1; /* There is ambiguous match. */
+ else
+ matched = str;
+ match++;
+ }
+ break;
+ case range_match:
+ if (cmd_range_match
+ (str, command)) {
+ if (matched
+ && strcmp(matched,
+ str) != 0)
+ return 1;
+ else
+ matched = str;
+ match++;
+ }
+ break;
+#ifdef HAVE_IPV6
+ case ipv6_match:
+ if (CMD_IPV6(str))
+ match++;
+ break;
+ case ipv6_prefix_match:
+ if ((ret =
+ cmd_ipv6_prefix_match
+ (command)) != no_match) {
+ if (ret == partly_match)
+ return 2; /* There is incomplete match. */
+
+ match++;
+ }
+ break;
+#endif /* HAVE_IPV6 */
+ case ipv4_match:
+ if (CMD_IPV4(str))
+ match++;
+ break;
+ case ipv4_prefix_match:
+ if ((ret =
+ cmd_ipv4_prefix_match
+ (command)) != no_match) {
+ if (ret == partly_match)
+ return 2; /* There is incomplete match. */
+
+ match++;
+ }
+ break;
+ case extend_match:
+ if (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+ match++;
+ break;
+ case no_match:
+ default:
+ break;
+ }
+ }
+ if (!match)
+ vector_slot(v, i) = NULL;
+ }
+ return 0;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+static const char *cmd_entry_function(const char *src, const char *dst)
+{
+ /* Skip variable arguments. */
+ if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
+ CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
+ return NULL;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ /* Matched with input string. */
+ if (strncmp(src, dst, strlen(src)) == 0)
+ return dst;
+
+ return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+ CMD_VARIABLE for '?' key processing */
+static const char *cmd_entry_function_desc(const char *src, const char *dst)
+{
+ if (CMD_VARARG(dst))
+ return dst;
+
+ if (CMD_RANGE(dst)) {
+ if (cmd_range_match(dst, src))
+ return dst;
+ else
+ return NULL;
+ }
+#ifdef HAVE_IPV6
+ if (CMD_IPV6(dst)) {
+ if (cmd_ipv6_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV6_PREFIX(dst)) {
+ if (cmd_ipv6_prefix_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+#endif /* HAVE_IPV6 */
+
+ if (CMD_IPV4(dst)) {
+ if (cmd_ipv4_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV4_PREFIX(dst)) {
+ if (cmd_ipv4_prefix_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ /* Optional or variable commands always match on '?' */
+ if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
+ return dst;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ if (strncmp(src, dst, strlen(src)) == 0)
+ return dst;
+ else
+ return NULL;
+}
+
+/* Check same string element existence. If it isn't there return
+ 1. */
+static int cmd_unique_string(vector v, const char *str)
+{
+ unsigned int i;
+ char *match;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((match = vector_slot(v, i)) != NULL)
+ if (strcmp(match, str) == 0)
+ return 0;
+ return 1;
+}
+
+/* Compare string to description vector. If there is same string
+ return 1 else return 0. */
+static int desc_unique_string(vector v, const char *str)
+{
+ unsigned int i;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((desc = vector_slot(v, i)) != NULL)
+ if (strcmp(desc->cmd, str) == 0)
+ return 1;
+ return 0;
+}
+
+static int cmd_try_do_shortcut(enum node_type node, char *first_word)
+{
+ if (first_word != NULL &&
+ node != AUTH_NODE &&
+ node != VIEW_NODE &&
+ node != AUTH_ENABLE_NODE &&
+ node != ENABLE_NODE && 0 == strcmp("do", first_word))
+ return 1;
+ return 0;
+}
+
+/* '?' describe command support. */
+static vector
+cmd_describe_command_real(vector vline, struct vty *vty, int *status)
+{
+ unsigned int i;
+ vector cmd_vector;
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ int ret;
+ enum match_type match;
+ char *command;
+ static struct desc desc_cr = { "<cr>", "" };
+
+ /* Set index. */
+ if (vector_active(vline) == 0) {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ } else
+ index = vector_active(vline) - 1;
+
+ /* Make copy vector of current node's command vector. */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ /* Prepare match vector */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+ /* Filter commands. */
+ /* Only words precedes current word will be checked in this loop. */
+ for (i = 0; i < index; i++)
+ if ((command = vector_slot(vline, i))) {
+ match =
+ cmd_filter_by_completion(command, cmd_vector, i);
+
+ if (match == vararg_match) {
+ struct cmd_element *cmd_element;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ &&
+ (vector_active
+ (cmd_element->strvec))) {
+ descvec =
+ vector_slot(cmd_element->
+ strvec,
+ vector_active
+ (cmd_element->
+ strvec) - 1);
+ for (k = 0;
+ k < vector_active(descvec);
+ k++) {
+ struct desc *desc =
+ vector_slot(descvec,
+ k);
+ vector_set(matchvec,
+ desc);
+ }
+ }
+
+ vector_set(matchvec, &desc_cr);
+ vector_free(cmd_vector);
+
+ return matchvec;
+ }
+
+ if ((ret =
+ is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ } else if (ret == 2) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ }
+
+ /* Prepare match vector */
+ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
+
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_slot(vline, index);
+ if (command)
+ match = cmd_filter_by_completion(command, cmd_vector, index);
+
+ /* Make description vector. */
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) {
+ const char *string = NULL;
+ vector strvec = cmd_element->strvec;
+
+ /* if command is NULL, index may be equal to vector_active */
+ if (command && index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ /* Check if command is completed. */
+ if (command == NULL
+ && index == vector_active(strvec)) {
+ string = "<cr>";
+ if (!desc_unique_string
+ (matchvec, string))
+ vector_set(matchvec, &desc_cr);
+ } else {
+ unsigned int j;
+ vector descvec =
+ vector_slot(strvec, index);
+ struct desc *desc;
+
+ for (j = 0; j < vector_active(descvec);
+ j++)
+ if ((desc =
+ vector_slot(descvec, j))) {
+ string =
+ cmd_entry_function_desc
+ (command,
+ desc->cmd);
+ if (string) {
+ /* Uniqueness check */
+ if (!desc_unique_string(matchvec, string))
+ vector_set
+ (matchvec,
+ desc);
+ }
+ }
+ }
+ }
+ }
+ vector_free(cmd_vector);
+
+ if (vector_slot(matchvec, 0) == NULL) {
+ vector_free(matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ } else
+ *status = CMD_SUCCESS;
+
+ return matchvec;
+}
+
+vector cmd_describe_command(vector vline, struct vty * vty, int *status)
+{
+ vector ret;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ enum node_type onode;
+ vector shifted_vline;
+ unsigned int index;
+
+ onode = vty->node;
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_describe_command_real(shifted_vline, vty, status);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ return cmd_describe_command_real(vline, vty, status);
+}
+
+/* Check LCD of matched command. */
+static int cmd_lcd(char **matched)
+{
+ int i;
+ int j;
+ int lcd = -1;
+ char *s1, *s2;
+ char c1, c2;
+
+ if (matched[0] == NULL || matched[1] == NULL)
+ return 0;
+
+ for (i = 1; matched[i] != NULL; i++) {
+ s1 = matched[i - 1];
+ s2 = matched[i];
+
+ for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
+ if (c1 != c2)
+ break;
+
+ if (lcd < 0)
+ lcd = j;
+ else {
+ if (lcd > j)
+ lcd = j;
+ }
+ }
+ return lcd;
+}
+
+/* Command line completion support. */
+static char **cmd_complete_command_real(vector vline, struct vty *vty,
+ int *status)
+{
+ unsigned int i;
+ vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ char **match_str;
+ struct desc *desc;
+ vector descvec;
+ char *command;
+ int lcd;
+
+ if (vector_active(vline) == 0) {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ } else
+ index = vector_active(vline) - 1;
+
+ /* First, filter by preceeding command string */
+ for (i = 0; i < index; i++)
+ if ((command = vector_slot(vline, i))) {
+ enum match_type match;
+ int ret;
+
+ /* First try completion match, if there is exactly match return 1 */
+ match =
+ cmd_filter_by_completion(command, cmd_vector, i);
+
+ /* If there is exact match then filter ambiguous match else check
+ ambiguousness. */
+ if ((ret =
+ is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+ /*
+ else if (ret == 2)
+ {
+ vector_free (cmd_vector);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ */
+ }
+
+ /* Prepare match vector. */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+ /* Now we got into completion */
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i))) {
+ const char *string;
+ vector strvec = cmd_element->strvec;
+
+ /* Check field length */
+ if (index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ unsigned int j;
+
+ descvec = vector_slot(strvec, index);
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ if ((string =
+ cmd_entry_function
+ (vector_slot(vline, index),
+ desc->cmd)))
+ if (cmd_unique_string
+ (matchvec, string))
+ vector_set
+ (matchvec,
+ strdup
+ (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 = malloc(lcd + 1);
+ 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))
+ 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 node_parent(enum node_type node)
+{
+ enum node_type ret;
+
+ assert(node > CONFIG_NODE);
+
+ switch (node) {
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ ret = BGP_NODE;
+ break;
+ case KEYCHAIN_KEY_NODE:
+ ret = KEYCHAIN_NODE;
+ break;
+ default:
+ ret = CONFIG_NODE;
+ }
+
+ return ret;
+}
+
+/* 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, try_node;
+
+ onode = try_node = vty->node;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ vector shifted_vline;
+ unsigned int index;
+
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_execute_command_real(shifted_vline, vty, cmd);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ saved_ret = ret = cmd_execute_command_real(vline, vty, cmd);
+
+ if (vtysh)
+ return saved_ret;
+
+ /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && vty->node > CONFIG_NODE) {
+ try_node = node_parent(try_node);
+ vty->node = try_node;
+ 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;
+ 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);
+}
+
+#if 0
+/* Configration make from file. */
+int config_from_file(struct vty *vty, FILE * fp)
+{
+ int ret;
+ vector vline;
+
+ while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
+ vline = cmd_make_strvec(vty->buf);
+
+ /* In case of comment line */
+ if (vline == NULL)
+ continue;
+ /* Execute configuration command : this is strict match */
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+
+ /* Try again with setting node to CONFIG_NODE */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_NOTHING_TODO
+ && vty->node != CONFIG_NODE) {
+ vty->node = node_parent(vty->node);
+ 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;
+}
+#endif
+
+/* 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. */
+DEFUN(config_exit,
+ config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
+{
+ switch (vty->node) {
+ case BTS_NODE:
+ vty->node = VIEW_NODE;
+ vty->index = NULL;
+ break;
+ case TRX_NODE:
+ vty->node = BTS_NODE;
+ {
+ /* set vty->index correctly ! */
+ struct gsm_bts_trx *trx = vty->index;
+ vty->index = trx->bts;
+ }
+ break;
+ case TS_NODE:
+ vty->node = TRX_NODE;
+ {
+ /* set vty->index correctly ! */
+ struct gsm_bts_trx_ts *ts = vty->index;
+ vty->index = ts->trx;
+ }
+ break;
+ case SUBSCR_NODE:
+ vty->node = VIEW_NODE;
+ subscr_put(vty->index);
+ vty->index = NULL;
+ break;
+ 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 INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case BGP_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case MASC_NODE:
+ case RMAP_NODE:
+ case VTY_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ vty->node = BGP_NODE;
+ break;
+ case KEYCHAIN_KEY_NODE:
+ vty->node = KEYCHAIN_NODE;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* quit is alias of exit. */
+ALIAS(config_exit,
+ config_quit_cmd, "quit", "Exit current mode and down to previous mode\n")
+
+/* End of configuration. */
+ DEFUN(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 INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_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", QUAGGA_PROGNAME, QUAGGA_VERSION,
+ host.name ? host.name : "", VTY_NEWLINE);
+ vty_out(vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+DEFUN(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. */
+DEFUN(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;
+}
+
+#if 0
+/* 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 =
+ malloc(strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1);
+ strcpy(config_file_sav, config_file);
+ strcat(config_file_sav, CONF_BACKUP_EXT);
+
+ config_file_tmp = malloc(strlen(config_file) + 8);
+ 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);
+ free(config_file_tmp);
+ 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! Zebra configuration saved from vty\n! ");
+ //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);
+ free(config_file_sav);
+ 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);
+ free(config_file_sav);
+ 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);
+ free(config_file_sav);
+ 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);
+ free(config_file_sav);
+ free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ unlink(config_file_tmp);
+ sync();
+
+ free(config_file_sav);
+ free(config_file_tmp);
+
+ if (chmod(config_file, CONFIGFILE_MASK) != 0) {
+ vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
+ config_file, safe_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;
+}
+#endif
+
+/* 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)
+ free(host.name);
+
+ host.name = strdup(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)
+ 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)
+ free(host.password);
+ host.password = NULL;
+ if (host.password_encrypt)
+ free(host.password_encrypt);
+ host.password_encrypt = strdup(strdup(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)
+ free(host.password);
+ host.password = NULL;
+
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt) {
+ if (host.password_encrypt)
+ free(host.password_encrypt);
+ host.password_encrypt = strdup(zencrypt(argv[0]));
+ } else
+#endif
+ host.password = strdup(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)
+ free(host.enable);
+ host.enable = NULL;
+
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = strdup(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)
+ free(host.enable);
+ host.enable = NULL;
+
+ /* Plain password input. */
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt) {
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = strdup(zencrypt(argv[0]));
+ } else
+#endif
+ host.enable = strdup(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)
+ free(host.enable);
+ host.enable = NULL;
+
+ if (host.enable_encrypt)
+ 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)
+ free(host.password_encrypt);
+ host.password_encrypt = strdup(zencrypt(host.password));
+ }
+ if (host.enable) {
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = strdup(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)
+ free(host.password_encrypt);
+ host.password_encrypt = NULL;
+
+ if (host.enable_encrypt)
+ 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)
+ 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)
+ 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 = malloc(strlen(cwd) + strlen(fname) + 2))
+ == 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)
+ free(p);
+
+ if (!ret) {
+ vty_out(vty, "can't open logfile %s\n", fname);
+ return CMD_WARNING;
+ }
+
+ if (host.logfile)
+ free(host.logfile);
+
+ host.logfile = strdup(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)
+ 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)
+ free(host.motdfile);
+ host.motdfile = strdup(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)
+ free(host.motdfile);
+ host.motdfile = NULL;
+ return CMD_SUCCESS;
+}
+
+/* Set config filename. Called from vty.c */
+void host_config_set(char *filename)
+{
+ host.config = strdup(filename);
+}
+
+void install_default(enum node_type node)
+{
+ install_element(node, &config_exit_cmd);
+ install_element(node, &config_quit_cmd);
+ install_element(node, &config_end_cmd);
+ install_element(node, &config_help_cmd);
+ install_element(node, &config_list_cmd);
+
+#if 0
+ 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);
+#endif
+}
+
+/* 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.password = "foo";
+ 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_quit_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_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, &hostname_cmd);
+ install_element(CONFIG_NODE, &no_hostname_cmd);
+
+ if (terminal) {
+ install_element(CONFIG_NODE, &password_cmd);
+ install_element(CONFIG_NODE, &password_text_cmd);
+ install_element(CONFIG_NODE, &enable_password_cmd);
+ install_element(CONFIG_NODE, &enable_password_text_cmd);
+ install_element(CONFIG_NODE, &no_enable_password_cmd);
+
+#ifdef VTY_CRYPT_PW
+ install_element(CONFIG_NODE, &service_password_encrypt_cmd);
+ install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
+#endif
+ install_element(CONFIG_NODE, &banner_motd_default_cmd);
+ install_element(CONFIG_NODE, &banner_motd_file_cmd);
+ install_element(CONFIG_NODE, &no_banner_motd_cmd);
+ install_element(CONFIG_NODE, &service_terminal_length_cmd);
+ install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
+
+ }
+ srand(time(NULL));
+}
diff --git a/openbsc/src/vty/vector.c b/openbsc/src/vty/vector.c
new file mode 100644
index 00000000..76870105
--- /dev/null
+++ b/openbsc/src/vty/vector.c
@@ -0,0 +1,186 @@
+/* Generic vector interface routine
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <vty/vector.h>
+#include <memory.h>
+
+/* Initialize vector : allocate memory and return vector. */
+vector vector_init(unsigned int size)
+{
+ vector v = calloc(1, sizeof(struct _vector));
+ if (!v)
+ return NULL;
+
+ /* allocate at least one slot */
+ if (size == 0)
+ size = 1;
+
+ v->alloced = size;
+ v->active = 0;
+ v->index = calloc(1, sizeof(void *) * size);
+ if (!v->index) {
+ free(v);
+ return NULL;
+ }
+ return v;
+}
+
+void vector_only_wrapper_free(vector v)
+{
+ free(v);
+}
+
+void vector_only_index_free(void *index)
+{
+ free(index);
+}
+
+void vector_free(vector v)
+{
+ free(v->index);
+ free(v);
+}
+
+vector vector_copy(vector v)
+{
+ unsigned int size;
+ vector new = calloc(1, sizeof(struct _vector));
+ if (!new)
+ return NULL;
+
+ new->active = v->active;
+ new->alloced = v->alloced;
+
+ size = sizeof(void *) * (v->alloced);
+ new->index = calloc(1, size);
+ if (!new->index) {
+ free(new);
+ return NULL;
+ }
+ memcpy(new->index, v->index, size);
+
+ return new;
+}
+
+/* Check assigned index, and if it runs short double index pointer */
+void vector_ensure(vector v, unsigned int num)
+{
+ if (v->alloced > num)
+ return;
+
+ v->index = realloc(v->index, sizeof(void *) * (v->alloced * 2));
+ memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
+ v->alloced *= 2;
+
+ if (v->alloced <= num)
+ vector_ensure(v, num);
+}
+
+/* This function only returns next empty slot index. It dose not mean
+ the slot's index memory is assigned, please call vector_ensure()
+ after calling this function. */
+int vector_empty_slot(vector v)
+{
+ unsigned int i;
+
+ if (v->active == 0)
+ return 0;
+
+ for (i = 0; i < v->active; i++)
+ if (v->index[i] == 0)
+ return i;
+
+ return i;
+}
+
+/* Set value to the smallest empty slot. */
+int vector_set(vector v, void *val)
+{
+ unsigned int i;
+
+ i = vector_empty_slot(v);
+ vector_ensure(v, i);
+
+ v->index[i] = val;
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return i;
+}
+
+/* Set value to specified index slot. */
+int vector_set_index(vector v, unsigned int i, void *val)
+{
+ vector_ensure(v, i);
+
+ v->index[i] = val;
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return i;
+}
+
+/* Look up vector. */
+void *vector_lookup(vector v, unsigned int i)
+{
+ if (i >= v->active)
+ return NULL;
+ return v->index[i];
+}
+
+/* Lookup vector, ensure it. */
+void *vector_lookup_ensure(vector v, unsigned int i)
+{
+ vector_ensure(v, i);
+ return v->index[i];
+}
+
+/* Unset value at specified index slot. */
+void vector_unset(vector v, unsigned int i)
+{
+ if (i >= v->alloced)
+ return;
+
+ v->index[i] = NULL;
+
+ if (i + 1 == v->active) {
+ v->active--;
+ while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */
+ }
+}
+
+/* Count the number of not emplty slot. */
+unsigned int vector_count(vector v)
+{
+ unsigned int i;
+ unsigned count = 0;
+
+ for (i = 0; i < v->active; i++)
+ if (v->index[i] != NULL)
+ count++;
+
+ return count;
+}
diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c
new file mode 100644
index 00000000..ca6fff73
--- /dev/null
+++ b/openbsc/src/vty/vty.c
@@ -0,0 +1,1634 @@
+
+#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 "cardshell.h"
+#include <vty/vty.h>
+#include <vty/command.h>
+#include <vty/buffer.h>
+
+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;
+
+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 = malloc(sizeof(struct vty));
+
+ if (!new)
+ goto out;
+
+ new->obuf = buffer_new(0); /* Use default buffer size. */
+ if (!new->obuf)
+ goto out_new;
+ new->buf = calloc(1, VTY_BUFSIZ);
+ if (!new->buf)
+ goto out_obuf;
+
+ new->max = VTY_BUFSIZ;
+
+ return new;
+
+out_obuf:
+ free(new->obuf);
+out_new:
+ 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;
+
+ /* Flush buffer. */
+ buffer_flush_all(vty->obuf, vty->fd);
+
+ /* Free input buffer. */
+ buffer_free(vty->obuf);
+
+ /* Free command history. */
+ for (i = 0; i < VTY_MAXHIST; i++)
+ if (vty->hist[i])
+ free(vty->hist[i]);
+
+ /* Unset vector. */
+ vector_unset(vtyvec, vty->fd);
+
+ /* Close socket. */
+ if (vty->fd > 0)
+ close(vty->fd);
+
+ if (vty->buf)
+ free(vty->buf);
+
+ /* Check configure. */
+ vty_config_unlock(vty);
+
+ /* OK free vty. */
+ 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 = realloc(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)
+ free(p);
+ }
+
+ return len;
+}
+
+int vty_out_newline(struct vty *vty)
+{
+ char *p = vty_newline(vty);
+ buffer_put(vty->obuf, p, strlen(p));
+ return 0;
+}
+
+int vty_config_lock(struct vty *vty)
+{
+ if (vty_config == 0) {
+ vty->config = 1;
+ vty_config = 1;
+ }
+ return vty->config;
+}
+
+int vty_config_unlock(struct vty *vty)
+{
+ if (vty_config == 1 && vty->config == 1) {
+ vty->config = 0;
+ vty_config = 0;
+ }
+ return vty->config;
+}
+
+/* Say hello to vty interface. */
+void vty_hello(struct vty *vty)
+{
+ if (host.motdfile) {
+ FILE *f;
+ char buf[4096];
+
+ f = fopen(host.motdfile, "r");
+ if (f) {
+ while (fgets(buf, sizeof(buf), f)) {
+ char *s;
+ /* work backwards to ignore trailling isspace() */
+ for (s = buf + strlen(buf);
+ (s > buf) && isspace(*(s - 1)); s--) ;
+ *s = '\0';
+ vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+ }
+ fclose(f);
+ } else
+ vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
+ } else if (host.motd)
+ vty_out(vty, 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 = realloc(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])
+ free(vty->hist[vty->hindex]);
+ vty->hist[vty->hindex] = strdup(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);
+ (*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);
+
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_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, ' ');
+ 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]);
+ free(matched[0]);
+ vector_only_index_free(matched);
+ return;
+ 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]);
+ 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 = calloc(1, strlen(desc->str) + 1);
+ 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);
+
+ 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 INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_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;
+}
+
+/* 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) {
+ chdir(SYSCONFDIR);
+ getcwd(cwd, MAXPATHLEN);
+ }
+
+ vty_cwd = malloc(strlen(cwd) + 1);
+ 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);
+}
+
+/* Install vty's own commands like `who' command. */
+void vty_init()
+{
+ /* 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(VIEW_NODE, &config_who_cmd);
+ install_element(VIEW_NODE, &show_history_cmd);
+ install_element(ENABLE_NODE, &config_who_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_element(ENABLE_NODE, &show_history_cmd);
+
+ install_default(VTY_NODE);
+#if 0
+ install_element(VTY_NODE, &vty_login_cmd);
+ install_element(VTY_NODE, &no_vty_login_cmd);
+#endif
+}