mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-29 17:36:56 +00:00

* bgp_main.c: (main) The 2nd argument to openzlog has been removed. * isis_main.c: (main) The 2nd argument to openzlog has been removed. * ospf6_main.c: (main) The 2nd argument to openzlog has been removed. Note that stdout logging will no longer be enabled by default when not running as a daemon. * ospf_main.c: (main) The 2nd argument to openzlog has been removed. * rip_main.c: (main) The 2nd argument to openzlog has been removed. * ripng_main.c: (main) The 2nd argument to openzlog has been removed. * main.c: (main) The 2nd argument to openzlog has been removed. So stdout logging will no longer be enabled by default. * irdp_main.c: (irdp_finish) Reduce severity of shutdown message from LOG_WARNING to LOG_INFO. * vtysh.c: Make several functions static instead of global. Added several commands to support destination-specific logging levels. (vtysh_completion) This function is unused, so comment it out. * basic.texi: Document new logging features. Separate basic config commands from basic VTY commands. * log.h: Replace struct zlog flags and maskpri fields with maxlvl array to support individual logging levels for each destination. Remove the 2nd argument to openzlog since the default logging config should be standardized inside the library. Replaced the zlog_set_flag and zlog_reset_flag functions with zlog_set_level. And zlog_set_file now requires an additional log_level argument. Declare zlog_proto_names for use inside command.c in the "show logging" command. Added defines useful for command construction. * log.c: (vzlog) Decide where to send the message based on the individual logging levels configured for each destination. Remove support for ZLOG_STDERR since it was never actually used. Support record-priority for terminal monitors. (zlog_signal,zlog_backtrace_sigsafe) Support destination-specific logging levels. Remove stderr support (was never used). Added support for terminal monitor logging. (_zlog_assert_failed) Increase message severity to LOG_EMERG. (openzlog) Remove 2nd argument since default config should be standardized in library. By default, terminal monitoring is set to debug, and all other logging is disabled. (zlog_set_flag,zlog_reset_flag) Removed. (zlog_set_level) New function to replace zlog_set_flag and zlog_reset_flag. Supports destination-specific logging levels. (zlog_set_file,zlog_reset_file) Support file-specific logging level. (zlog_rotate) Log an error message if fopen fails, and support new file-specific logging level. * command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that command functions will be static instead of global. Remove declarations for config_exit and config_help. Define new macros DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can have deprecated commands in vtysh. Similarly, for completeness, define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED. Also, fix bug in ALIAS_ATTR macro (didn't matter because it was never used). * command.c: Make many functions static instead of global. (facility_name,facility_match,level_match) New functions to support enhanced destination-specific logging levels. (config_write_host) Support new destination-specific logging levels. (config_logmsg) Added new "logmsg" command to help test logging system. (show_logging) Added "show logging" command to show the current configuration of the logging system. (config_log_stdout_level) Support explicit stdout logging level. (no_config_log_stdout) Now takes optional LEVEL arg. (config_log_monitor,config_log_monitor_level,no_config_log_monitor) New commands creating new "log monitor" commands to set terminal monitoring log level. (config_log_file_level) Support explicit file logging level. (config_log_syslog_level) Support explicit syslog logging level. (config_log_facility,no_config_log_facility) Implement new "log facility" command. (cmd_init) Add hooks for new commands: "show logging", "logmsg", "log stdout <level>", "log monitor", "log monitor <level>", "no log monitor", "log file <filename> <level>", "no log file <filename> <level>", "log syslog <level>", "log facility", and "no log facility". * vty.h: Added a "level" argument to vty_log so it can support "log record-priority". Declare new function vty_log_fixed for use in signal handlers. * vty.c: (vty_log,vty_log_out) Added a "level" argument to support "log record-priority" for vty terminal monitors. (vty_down_level) Use config_exit_cmd.func instead of calling config_exit directly (since command functions will now be static instead of global). (vty_log_fixed) New function to send terminal monitor messages from inside a signal handler.
3530 lines
79 KiB
C
3530 lines
79 KiB
C
/*
|
||
$Id: command.c,v 1.28 2004/12/07 15:39:32 ajs 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 <zebra.h>
|
||
|
||
|
||
#include "memory.h"
|
||
#include "log.h"
|
||
#include <lib/version.h>
|
||
#include "thread.h"
|
||
#include "vector.h"
|
||
#include "vty.h"
|
||
#include "command.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";
|
||
|
||
|
||
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;
|
||
}
|
||
|
||
void
|
||
print_version (const char *progname)
|
||
{
|
||
printf ("%s version %s (%s)\n", progname, QUAGGA_VERSION, host.name);
|
||
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;
|
||
int len;
|
||
int index;
|
||
char *str;
|
||
|
||
str = NULL;
|
||
index = 0;
|
||
|
||
for (i = shift; i < argc; i++)
|
||
{
|
||
len = strlen (argv[i]);
|
||
|
||
if (i == shift)
|
||
{
|
||
str = XSTRDUP (MTYPE_TMP, argv[i]);
|
||
index = len;
|
||
}
|
||
else
|
||
{
|
||
str = XREALLOC (MTYPE_TMP, str, (index + len + 2));
|
||
str[index++] = ' ';
|
||
memcpy (str + index, argv[i], len);
|
||
index += len;
|
||
str[index] = '\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_max (cmdvec); i++)
|
||
if ((cnode = vector_slot (cmdvec, i)) != NULL)
|
||
{
|
||
vector cmd_vector = cnode->cmd_vector;
|
||
qsort (cmd_vector->index, cmd_vector->max, sizeof (void *), cmp_node);
|
||
|
||
for (j = 0; j < vector_max (cmd_vector); j++)
|
||
if ((cmd_element = vector_slot (cmd_vector, j)) != NULL)
|
||
{
|
||
descvec = vector_slot (cmd_element->strvec,
|
||
vector_max (cmd_element->strvec) - 1);
|
||
qsort (descvec->index, descvec->max, 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 = XMALLOC (MTYPE_STRVEC, 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_max (v); i++)
|
||
if ((cp = vector_slot (v, i)) != NULL)
|
||
XFREE (MTYPE_STRVEC, 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 = XMALLOC (MTYPE_STRVEC, 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 = XMALLOC (MTYPE_STRVEC, len + 1);
|
||
memcpy (token, sp, len);
|
||
*(token + len) = '\0';
|
||
|
||
desc = XCALLOC (MTYPE_DESC, 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;
|
||
|
||
for (i = 0; i < vector_max (strvec); i++)
|
||
{
|
||
descvec = vector_slot (strvec, i);
|
||
|
||
if (vector_max (descvec) == 1)
|
||
{
|
||
struct desc *desc = vector_slot (descvec, 0);
|
||
|
||
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 (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, "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.motd)
|
||
vty_out (vty, "no banner motd%s", VTY_NEWLINE);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* Utility function for getting command vector. */
|
||
static vector
|
||
cmd_node_vector (vector v, enum node_type ntype)
|
||
{
|
||
struct cmd_node *cnode = vector_slot (v, ntype);
|
||
return cnode->cmd_vector;
|
||
}
|
||
|
||
#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_max (v); i++)
|
||
if ((cmd_element = vector_slot (v, i)) != NULL)
|
||
{
|
||
if (index >= vector_max (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_max (descvec); j++)
|
||
{
|
||
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_max (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_max (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_max (descvec); j++)
|
||
{
|
||
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_max (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_max (descvec); j++)
|
||
{
|
||
enum match_type ret;
|
||
|
||
desc = vector_slot (descvec, j);
|
||
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_max (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_max (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. */
|
||
index = vector_max (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++)
|
||
{
|
||
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_max (cmd_vector); j++)
|
||
if ((cmd_element = vector_slot (cmd_vector, j)) != NULL)
|
||
{
|
||
descvec = vector_slot (cmd_element->strvec,
|
||
vector_max (cmd_element->strvec) - 1);
|
||
for (k = 0; k < vector_max (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_max (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_max */
|
||
if (command && index >= vector_max (strvec))
|
||
vector_slot (cmd_vector, i) = NULL;
|
||
else
|
||
{
|
||
/* Check if command is completed. */
|
||
if (command == NULL && index == vector_max (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_max (descvec); j++)
|
||
{
|
||
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_max (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 = vector_max (vline) - 1;
|
||
char **match_str;
|
||
struct desc *desc;
|
||
vector descvec;
|
||
char *command;
|
||
int lcd;
|
||
|
||
/* First, filter by preceeding command string */
|
||
for (i = 0; i < index; i++)
|
||
{
|
||
enum match_type match;
|
||
int ret;
|
||
|
||
command = vector_slot (vline, i);
|
||
|
||
/* 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_max (cmd_vector); i++)
|
||
if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
|
||
{
|
||
const char *string;
|
||
vector strvec = cmd_element->strvec;
|
||
|
||
/* Check field length */
|
||
if (index >= vector_max (strvec))
|
||
vector_slot (cmd_vector, i) = NULL;
|
||
else
|
||
{
|
||
unsigned int j;
|
||
|
||
descvec = vector_slot (strvec, index);
|
||
for (j = 0; j < vector_max (descvec); j++)
|
||
{
|
||
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, XSTRDUP (MTYPE_TMP, 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 = XMALLOC (MTYPE_TMP, lcd + 1);
|
||
memcpy (lcdstr, matchvec->index[0], lcd);
|
||
lcdstr[lcd] = '\0';
|
||
|
||
/* match_str = (char **) &lcdstr; */
|
||
|
||
/* Free matchvec. */
|
||
for (i = 0; i < vector_max (matchvec); i++)
|
||
{
|
||
if (vector_slot (matchvec, i))
|
||
XFREE (MTYPE_TMP, 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_max (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 */
|
||
static 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_max (vline); index++)
|
||
{
|
||
int ret;
|
||
|
||
command = vector_slot (vline, index);
|
||
|
||
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_max (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;
|
||
#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_max (vline); i++)
|
||
{
|
||
if (varflag)
|
||
argv[argc++] = vector_slot (vline, i);
|
||
else
|
||
{
|
||
vector descvec = vector_slot (matched_element->strvec, i);
|
||
|
||
if (vector_max (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 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_max (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);
|
||
|
||
/* 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_max (vline); index++)
|
||
{
|
||
int ret;
|
||
|
||
command = vector_slot (vline, index);
|
||
|
||
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_max (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_max (vline); i++)
|
||
{
|
||
if (varflag)
|
||
argv[argc++] = vector_slot (vline, i);
|
||
else
|
||
{
|
||
vector descvec = vector_slot (matched_element->strvec, i);
|
||
|
||
if (vector_max (descvec) == 1)
|
||
{
|
||
struct desc *desc = vector_slot (descvec, 0);
|
||
|
||
if (CMD_VARARG (desc->cmd))
|
||
varflag = 1;
|
||
|
||
if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
|
||
argv[argc++] = vector_slot (vline, i);
|
||
}
|
||
else
|
||
argv[argc++] = vector_slot (vline, i);
|
||
}
|
||
|
||
if (argc >= CMD_ARGC_MAX)
|
||
return CMD_ERR_EXEED_ARGC_MAX;
|
||
}
|
||
|
||
/* For vtysh execution. */
|
||
if (cmd)
|
||
*cmd = matched_element;
|
||
|
||
if (matched_element->daemon)
|
||
return CMD_SUCCESS_DAEMON;
|
||
|
||
/* Now execute matched command */
|
||
return (*matched_element->func) (matched_element, vty, argc, argv);
|
||
}
|
||
|
||
/* Configration make from file. */
|
||
int
|
||
config_from_file (struct vty *vty, FILE *fp)
|
||
{
|
||
int ret;
|
||
vector vline;
|
||
|
||
while (fgets (vty->buf, VTY_BUFSIZ, fp))
|
||
{
|
||
vline = cmd_make_strvec (vty->buf);
|
||
|
||
/* In case of comment line */
|
||
if (vline == NULL)
|
||
continue;
|
||
/* Execute configuration command : this is strict match */
|
||
ret = cmd_execute_command_strict (vline, vty, NULL);
|
||
|
||
/* Try again with setting node to CONFIG_NODE */
|
||
while (ret != CMD_SUCCESS && ret != CMD_WARNING
|
||
&& ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE)
|
||
{
|
||
vty->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;
|
||
}
|
||
|
||
/* 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 VIEW_NODE:
|
||
case ENABLE_NODE:
|
||
if (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 zebra version\n")
|
||
{
|
||
vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, 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,
|
||
"Quagga VTY provides advanced help feature. 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_max (cnode->cmd_vector); i++)
|
||
if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL)
|
||
vty_out (vty, " %s%s", cmd->string,
|
||
VTY_NEWLINE);
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
/* Write current configuration into file. */
|
||
DEFUN (config_write_file,
|
||
config_write_file_cmd,
|
||
"write file",
|
||
"Write running configuration to memory, network, or terminal\n"
|
||
"Write to configuration file\n")
|
||
{
|
||
unsigned int i;
|
||
int fd;
|
||
struct cmd_node *node;
|
||
char *config_file;
|
||
char *config_file_tmp = NULL;
|
||
char *config_file_sav = NULL;
|
||
struct vty *file_vty;
|
||
|
||
/* Check and see if we are operating under vtysh configuration */
|
||
if (host.config == NULL)
|
||
{
|
||
vty_out (vty, "Can't save to configuration file, using vtysh.%s",
|
||
VTY_NEWLINE);
|
||
return CMD_WARNING;
|
||
}
|
||
|
||
/* Get filename. */
|
||
config_file = host.config;
|
||
|
||
config_file_sav = 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_max (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_max (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_max (cmdvec); i++)
|
||
if ((node = vector_slot (cmdvec, i)) && node->func)
|
||
{
|
||
if ((*node->func) (vty))
|
||
vty_out (vty, "!%s", VTY_NEWLINE);
|
||
}
|
||
vty_out (vty, "end%s",VTY_NEWLINE);
|
||
}
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
/* Write current configuration into the terminal. */
|
||
ALIAS (config_write_terminal,
|
||
show_running_config_cmd,
|
||
"show running-config",
|
||
SHOW_STR
|
||
"running configuration\n")
|
||
|
||
/* Write startup configuration into the terminal. */
|
||
DEFUN (show_startup_config,
|
||
show_startup_config_cmd,
|
||
"show startup-config",
|
||
SHOW_STR
|
||
"Contentes of startup configuration\n")
|
||
{
|
||
char buf[BUFSIZ];
|
||
FILE *confp;
|
||
|
||
confp = fopen (host.config, "r");
|
||
if (confp == NULL)
|
||
{
|
||
vty_out (vty, "Can't open configuration file [%s]%s",
|
||
host.config, VTY_NEWLINE);
|
||
return CMD_WARNING;
|
||
}
|
||
|
||
while (fgets (buf, BUFSIZ, confp))
|
||
{
|
||
char *cp = buf;
|
||
|
||
while (*cp != '\r' && *cp != '\n' && *cp != '\0')
|
||
cp++;
|
||
*cp = '\0';
|
||
|
||
vty_out (vty, "%s%s", buf, VTY_NEWLINE);
|
||
}
|
||
|
||
fclose (confp);
|
||
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
/* Hostname configuration */
|
||
DEFUN (config_hostname,
|
||
hostname_cmd,
|
||
"hostname WORD",
|
||
"Set system's network name\n"
|
||
"This system's network name\n")
|
||
{
|
||
if (!isalpha((int) *argv[0]))
|
||
{
|
||
vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
|
||
return CMD_WARNING;
|
||
}
|
||
|
||
if (host.name)
|
||
XFREE (0, 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)
|
||
XFREE (0, 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)
|
||
XFREE (0, host.password);
|
||
host.password = NULL;
|
||
if (host.password_encrypt)
|
||
XFREE (0, host.password_encrypt);
|
||
host.password_encrypt = XSTRDUP (0, 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)
|
||
XFREE (0, host.password);
|
||
host.password = NULL;
|
||
|
||
if (host.encrypt)
|
||
{
|
||
if (host.password_encrypt)
|
||
XFREE (0, host.password_encrypt);
|
||
host.password_encrypt = XSTRDUP (0, zencrypt (argv[0]));
|
||
}
|
||
else
|
||
host.password = XSTRDUP (0, 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)
|
||
XFREE (0, host.enable);
|
||
host.enable = NULL;
|
||
|
||
if (host.enable_encrypt)
|
||
XFREE (0, host.enable_encrypt);
|
||
host.enable_encrypt = XSTRDUP (0, 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)
|
||
XFREE (0, host.enable);
|
||
host.enable = NULL;
|
||
|
||
/* Plain password input. */
|
||
if (host.encrypt)
|
||
{
|
||
if (host.enable_encrypt)
|
||
XFREE (0, host.enable_encrypt);
|
||
host.enable_encrypt = XSTRDUP (0, zencrypt (argv[0]));
|
||
}
|
||
else
|
||
host.enable = XSTRDUP (0, 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)
|
||
XFREE (0, host.enable);
|
||
host.enable = NULL;
|
||
|
||
if (host.enable_encrypt)
|
||
XFREE (0, host.enable_encrypt);
|
||
host.enable_encrypt = NULL;
|
||
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
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)
|
||
XFREE (0, host.password_encrypt);
|
||
host.password_encrypt = XSTRDUP (0, zencrypt (host.password));
|
||
}
|
||
if (host.enable)
|
||
{
|
||
if (host.enable_encrypt)
|
||
XFREE (0, host.enable_encrypt);
|
||
host.enable_encrypt = XSTRDUP (0, 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)
|
||
XFREE (0, host.password_encrypt);
|
||
host.password_encrypt = NULL;
|
||
|
||
if (host.enable_encrypt)
|
||
XFREE (0, host.enable_encrypt);
|
||
host.enable_encrypt = NULL;
|
||
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
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 (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)));
|
||
XFREE(MTYPE_TMP, 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 = XMALLOC (MTYPE_TMP, 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)
|
||
XFREE (MTYPE_TMP, p);
|
||
|
||
if (!ret)
|
||
{
|
||
vty_out (vty, "can't open logfile %s\n", fname);
|
||
return CMD_WARNING;
|
||
}
|
||
|
||
if (host.logfile)
|
||
XFREE (MTYPE_TMP, host.logfile);
|
||
|
||
host.logfile = XSTRDUP (MTYPE_TMP, 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)
|
||
XFREE (MTYPE_TMP, 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;
|
||
}
|
||
|
||
|
||
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;
|
||
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);
|
||
|
||
install_element (node, &config_write_terminal_cmd);
|
||
install_element (node, &config_write_file_cmd);
|
||
install_element (node, &config_write_memory_cmd);
|
||
install_element (node, &config_write_cmd);
|
||
install_element (node, &show_running_config_cmd);
|
||
}
|
||
|
||
/* Initialize command interface. Install basic nodes and commands. */
|
||
void
|
||
cmd_init (int terminal)
|
||
{
|
||
/* Allocate initial top vector of commands. */
|
||
cmdvec = vector_init (VECTOR_MIN_SIZE);
|
||
|
||
/* Default host value settings. */
|
||
host.name = NULL;
|
||
host.password = NULL;
|
||
host.enable = NULL;
|
||
host.logfile = NULL;
|
||
host.config = NULL;
|
||
host.lines = -1;
|
||
host.motd = default_motd;
|
||
|
||
/* 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, &show_logging_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, ©_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, &show_logging_cmd);
|
||
install_element (ENABLE_NODE, &config_logmsg_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);
|
||
|
||
install_element (CONFIG_NODE, &config_log_stdout_cmd);
|
||
install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
|
||
install_element (CONFIG_NODE, &config_log_monitor_cmd);
|
||
install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
|
||
install_element (CONFIG_NODE, &config_log_file_cmd);
|
||
install_element (CONFIG_NODE, &config_log_file_level_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_file_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
|
||
install_element (CONFIG_NODE, &config_log_syslog_cmd);
|
||
install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
|
||
install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
|
||
install_element (CONFIG_NODE, &config_log_facility_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_facility_cmd);
|
||
install_element (CONFIG_NODE, &config_log_trap_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_trap_cmd);
|
||
install_element (CONFIG_NODE, &config_log_record_priority_cmd);
|
||
install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
|
||
install_element (CONFIG_NODE, &service_password_encrypt_cmd);
|
||
install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
|
||
install_element (CONFIG_NODE, &banner_motd_default_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);
|
||
|
||
install_element(VIEW_NODE, &show_thread_cpu_cmd);
|
||
install_element(ENABLE_NODE, &show_thread_cpu_cmd);
|
||
}
|
||
srand(time(NULL));
|
||
}
|