/* $Id$ 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 #include "memory.h" #include "log.h" #include #include "thread.h" #include "vector.h" #include "vty.h" #include "command.h" #include "workqueue.h" /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ vector cmdvec = NULL; struct desc desc_cr; char *command_cr = NULL; /* Host information structure. */ struct host host; /* Standard command node structures. */ static struct cmd_node auth_node = { AUTH_NODE, "Password: ", }; static struct cmd_node view_node = { VIEW_NODE, "%s> ", }; static struct cmd_node restricted_node = { RESTRICTED_NODE, "%s$ ", }; static struct cmd_node auth_enable_node = { AUTH_ENABLE_NODE, "Password: ", }; static struct cmd_node enable_node = { ENABLE_NODE, "%s# ", }; static struct cmd_node config_node = { CONFIG_NODE, "%s(config)# ", 1 }; /* Default motd string. */ static const char *default_motd = "\r\n\ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ " QUAGGA_COPYRIGHT "\r\n\ \r\n"; static const 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) { const 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) { const 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; } /* This is called from main when a daemon is invoked with -v or --version. */ void print_version (const char *progname) { printf ("%s version %s\n", progname, QUAGGA_VERSION); printf ("%s\n", QUAGGA_COPYRIGHT); } /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * argv_concat (const char **argv, int argc, int shift) { int i; size_t len; char *str; char *p; len = 0; for (i = shift; i < argc; i++) len += strlen(argv[i])+1; if (!len) return NULL; p = str = XMALLOC(MTYPE_TMP, len); for (i = shift; i < argc; i++) { size_t arglen; memcpy(p, argv[i], (arglen = strlen(argv[i]))); p += arglen; *p++ = ' '; } *(p-1) = '\0'; return str; } /* Install top node of command vector. */ void install_node (struct cmd_node *node, int (*func) (struct vty *)) { vector_set_index (cmdvec, node->node, node); node->func = func; node->cmd_vector = vector_init (VECTOR_MIN_SIZE); } /* Compare two command's string. Used in sort_node (). */ static int cmp_node (const void *p, const void *q) { const struct cmd_element *a = *(struct cmd_element * const *)p; const struct cmd_element *b = *(struct cmd_element * const *)q; return strcmp (a->string, b->string); } static int cmp_desc (const void *p, const void *q) { const struct desc *a = *(struct desc * const *)p; const struct desc *b = *(struct desc * const *)q; return strcmp (a->cmd, b->cmd); } /* Sort each node's command element according to command string. */ void sort_node () { unsigned int i, j; struct cmd_node *cnode; vector descvec; struct cmd_element *cmd_element; for (i = 0; i < vector_active (cmdvec); i++) if ((cnode = vector_slot (cmdvec, i)) != NULL) { vector cmd_vector = cnode->cmd_vector; qsort (cmd_vector->index, vector_active (cmd_vector), sizeof (void *), cmp_node); for (j = 0; j < vector_active (cmd_vector); j++) if ((cmd_element = vector_slot (cmd_vector, j)) != NULL && vector_active (cmd_element->strvec)) { descvec = vector_slot (cmd_element->strvec, vector_active (cmd_element->strvec) - 1); qsort (descvec->index, vector_active (descvec), sizeof (void *), cmp_desc); } } } /* Breaking up string into each command piece. I assume given character is separated by a space character. Return value is a vector which includes char ** data element. */ vector cmd_make_strvec (const char *string) { const char *cp, *start; char *token; int strlen; vector strvec; if (string == NULL) return NULL; cp = string; /* Skip white spaces. */ while (isspace ((int) *cp) && *cp != '\0') cp++; /* Return if there is only white spaces */ if (*cp == '\0') return NULL; if (*cp == '!' || *cp == '#') return NULL; /* Prepare return vector. */ strvec = vector_init (VECTOR_MIN_SIZE); /* Copy each command piece and set into vector. */ while (1) { start = cp; while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && *cp != '\0') cp++; strlen = cp - start; token = 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_active (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; struct desc *desc; for (i = 0; i < vector_active (strvec); i++) if ((descvec = vector_slot (strvec, i)) != NULL) { if ((vector_active (descvec)) == 1 && (desc = vector_slot (descvec, 0)) != NULL) { if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) return size; else size++; } else size++; } return size; } /* Return prompt character of specified node. */ const char * cmd_prompt (enum node_type node) { struct cmd_node *cnode; cnode = vector_slot (cmdvec, node); return cnode->prompt; } /* Install a command into a node. */ void install_element (enum node_type ntype, struct cmd_element *cmd) { struct cmd_node *cnode; /* cmd_init hasn't been called */ if (!cmdvec) return; 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); if (cmd->strvec == NULL) cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); cmd->cmdsize = cmd_cmdsize (cmd->strvec); } static const 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, "! N.B. The 'log trap' command is deprecated.%s", VTY_NEWLINE); vty_out (vty, "log trap %s%s", zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); } if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { vty_out (vty, "log file %s", host.logfile); if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) vty_out (vty, " %s", zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); vty_out (vty, "%s", VTY_NEWLINE); } if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) { vty_out (vty, "log stdout"); if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) vty_out (vty, " %s", zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); } if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) vty_out(vty,"no log monitor%s",VTY_NEWLINE); else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl) vty_out(vty,"log monitor %s%s", zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { vty_out (vty, "log syslog"); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) vty_out (vty, " %s", zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); vty_out (vty, "%s", VTY_NEWLINE); } if (zlog_default->facility != LOG_DAEMON) vty_out (vty, "log facility %s%s", facility_name(zlog_default->facility), VTY_NEWLINE); if (zlog_default->record_priority == 1) vty_out (vty, "log record-priority%s", VTY_NEWLINE); if (zlog_default->timestamp_precision > 0) vty_out (vty, "log timestamp precision %d%s", zlog_default->timestamp_precision, VTY_NEWLINE); if (host.advanced) vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); if (host.encrypt) vty_out (vty, "service password-encryption%s", VTY_NEWLINE); if (host.lines >= 0) vty_out (vty, "service terminal-length %d%s", host.lines, VTY_NEWLINE); if (host.motdfile) vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); else if (! host.motd) vty_out (vty, "no banner motd%s", VTY_NEWLINE); return 1; } /* Utility function for getting command vector. */ static vector cmd_node_vector (vector v, enum node_type ntype) { struct cmd_node *cnode = vector_slot (v, ntype); return cnode->cmd_vector; } #if 0 /* Filter command vector by symbol. This function is not actually used; * should it be deleted? */ static int cmd_filter_by_symbol (char *command, char *symbol) { int i, lim; if (strcmp (symbol, "IPV4_ADDRESS") == 0) { i = 0; lim = strlen (command); while (i < lim) { if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/')) return 1; i++; } return 0; } if (strcmp (symbol, "STRING") == 0) { i = 0; lim = strlen (command); while (i < lim) { if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-')) return 1; i++; } return 0; } if (strcmp (symbol, "IFNAME") == 0) { i = 0; lim = strlen (command); while (i < lim) { if (! isalnum ((int) command[i])) return 1; i++; } return 0; } return 0; } #endif /* Completion match types. */ enum match_type { no_match, extend_match, ipv4_prefix_match, ipv4_match, ipv6_prefix_match, ipv6_match, range_match, vararg_match, partly_match, exact_match }; static enum match_type cmd_ipv4_match (const char *str) { const char *sp; int dots = 0, nums = 0; char buf[4]; if (str == NULL) return partly_match; for (;;) { memset (buf, 0, sizeof (buf)); sp = str; while (*str != '\0') { if (*str == '.') { if (dots >= 3) return no_match; if (*(str + 1) == '.') return no_match; if (*(str + 1) == '\0') return partly_match; dots++; break; } if (!isdigit ((int) *str)) return no_match; str++; } if (str - sp > 3) return no_match; strncpy (buf, sp, str - sp); if (atoi (buf) > 255) return no_match; nums++; if (*str == '\0') break; str++; } if (nums < 4) return partly_match; return exact_match; } static enum match_type cmd_ipv4_prefix_match (const char *str) { const char *sp; int dots = 0; char buf[4]; if (str == NULL) return partly_match; for (;;) { memset (buf, 0, sizeof (buf)); sp = str; while (*str != '\0' && *str != '/') { if (*str == '.') { if (dots == 3) return no_match; if (*(str + 1) == '.' || *(str + 1) == '/') return no_match; if (*(str + 1) == '\0') return partly_match; dots++; break; } if (!isdigit ((int) *str)) return no_match; str++; } if (str - sp > 3) return no_match; strncpy (buf, sp, str - sp); if (atoi (buf) > 255) return no_match; if (dots == 3) { if (*str == '/') { if (*(str + 1) == '\0') return partly_match; str++; break; } else if (*str == '\0') return partly_match; } if (*str == '\0') return partly_match; str++; } sp = str; while (*str != '\0') { if (!isdigit ((int) *str)) return no_match; str++; } if (atoi (sp) > 32) return no_match; return exact_match; } #define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" #define STATE_START 1 #define STATE_COLON 2 #define STATE_DOUBLE 3 #define STATE_ADDR 4 #define STATE_DOT 5 #define STATE_SLASH 6 #define STATE_MASK 7 #ifdef HAVE_IPV6 static enum match_type cmd_ipv6_match (const char *str) { int state = STATE_START; int colons = 0, nums = 0, double_colon = 0; const char *sp = NULL; struct sockaddr_in6 sin6_dummy; int ret; if (str == NULL) return partly_match; if (strspn (str, IPV6_ADDR_STR) != strlen (str)) return no_match; /* use inet_pton that has a better support, * for example inet_pton can support the automatic addresses: * ::1.2.3.4 */ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); if (ret == 1) return exact_match; while (*str != '\0') { switch (state) { case STATE_START: if (*str == ':') { if (*(str + 1) != ':' && *(str + 1) != '\0') return no_match; colons--; state = STATE_COLON; } else { sp = str; state = STATE_ADDR; } continue; case STATE_COLON: colons++; if (*(str + 1) == ':') state = STATE_DOUBLE; else { sp = str + 1; state = STATE_ADDR; } break; case STATE_DOUBLE: if (double_colon) return no_match; if (*(str + 1) == ':') return no_match; else { if (*(str + 1) != '\0') colons++; sp = str + 1; state = STATE_ADDR; } double_colon++; nums++; break; case STATE_ADDR: if (*(str + 1) == ':' || *(str + 1) == '\0') { if (str - sp > 3) return no_match; nums++; state = STATE_COLON; } if (*(str + 1) == '.') state = STATE_DOT; break; case STATE_DOT: state = STATE_ADDR; break; default: break; } if (nums > 8) return no_match; if (colons > 7) return no_match; str++; } #if 0 if (nums < 11) return partly_match; #endif /* 0 */ return exact_match; } static enum match_type cmd_ipv6_prefix_match (const char *str) { int state = STATE_START; int colons = 0, nums = 0, double_colon = 0; int mask; const char *sp = NULL; char *endptr = NULL; if (str == NULL) return partly_match; if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) return no_match; while (*str != '\0' && state != STATE_MASK) { switch (state) { case STATE_START: if (*str == ':') { if (*(str + 1) != ':' && *(str + 1) != '\0') return no_match; colons--; state = STATE_COLON; } else { sp = str; state = STATE_ADDR; } continue; case STATE_COLON: colons++; if (*(str + 1) == '/') return no_match; else if (*(str + 1) == ':') state = STATE_DOUBLE; else { sp = str + 1; state = STATE_ADDR; } break; case STATE_DOUBLE: if (double_colon) return no_match; if (*(str + 1) == ':') return no_match; else { if (*(str + 1) != '\0' && *(str + 1) != '/') colons++; sp = str + 1; if (*(str + 1) == '/') state = STATE_SLASH; else state = STATE_ADDR; } double_colon++; nums += 1; break; case STATE_ADDR: if (*(str + 1) == ':' || *(str + 1) == '.' || *(str + 1) == '\0' || *(str + 1) == '/') { if (str - sp > 3) return no_match; for (; sp <= str; sp++) if (*sp == '/') return no_match; nums++; if (*(str + 1) == ':') state = STATE_COLON; else if (*(str + 1) == '.') state = STATE_DOT; else if (*(str + 1) == '/') state = STATE_SLASH; } break; case STATE_DOT: state = STATE_ADDR; break; case STATE_SLASH: if (*(str + 1) == '\0') return partly_match; state = STATE_MASK; break; default: break; } if (nums > 11) return no_match; if (colons > 7) return no_match; str++; } if (state < STATE_MASK) return partly_match; mask = strtol (str, &endptr, 10); if (*endptr != '\0') return no_match; if (mask < 0 || mask > 128) return no_match; /* I don't know why mask < 13 makes command match partly. Forgive me to make this comments. I Want to set static default route because of lack of function to originate default in ospf6d; sorry yasu if (mask < 13) return partly_match; */ return exact_match; } #endif /* HAVE_IPV6 */ #define DECIMAL_STRLEN_MAX 10 static int cmd_range_match (const char *range, const char *str) { char *p; char buf[DECIMAL_STRLEN_MAX + 1]; char *endptr = NULL; unsigned long min, max, val; if (str == NULL) return 1; val = strtoul (str, &endptr, 10); if (*endptr != '\0') return 0; range++; p = strchr (range, '-'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy (buf, range, p - range); buf[p - range] = '\0'; min = strtoul (buf, &endptr, 10); if (*endptr != '\0') return 0; range = p + 1; p = strchr (range, '>'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy (buf, range, p - range); buf[p - range] = '\0'; max = strtoul (buf, &endptr, 10); if (*endptr != '\0') return 0; if (val < min || val > max) return 0; return 1; } /* Make completion match and return match type flag. */ static enum match_type cmd_filter_by_completion (char *command, vector v, unsigned int index) { unsigned int i; const char *str; struct cmd_element *cmd_element; enum match_type match_type; vector descvec; struct desc *desc; match_type = no_match; /* If command and cmd_element string does not match set NULL to vector */ for (i = 0; i < vector_active (v); i++) if ((cmd_element = vector_slot (v, i)) != NULL) { if (index >= vector_active (cmd_element->strvec)) vector_slot (v, i) = NULL; else { unsigned int j; int matched = 0; descvec = vector_slot (cmd_element->strvec, index); for (j = 0; j < vector_active (descvec); j++) if ((desc = vector_slot (descvec, j))) { str = desc->cmd; if (CMD_VARARG (str)) { if (match_type < vararg_match) match_type = vararg_match; matched++; } else if (CMD_RANGE (str)) { if (cmd_range_match (str, command)) { if (match_type < range_match) match_type = range_match; matched++; } } #ifdef HAVE_IPV6 else if (CMD_IPV6 (str)) { if (cmd_ipv6_match (command)) { if (match_type < ipv6_match) match_type = ipv6_match; matched++; } } else if (CMD_IPV6_PREFIX (str)) { if (cmd_ipv6_prefix_match (command)) { if (match_type < ipv6_prefix_match) match_type = ipv6_prefix_match; matched++; } } #endif /* HAVE_IPV6 */ else if (CMD_IPV4 (str)) { if (cmd_ipv4_match (command)) { if (match_type < ipv4_match) match_type = ipv4_match; matched++; } } else if (CMD_IPV4_PREFIX (str)) { if (cmd_ipv4_prefix_match (command)) { if (match_type < ipv4_prefix_match) match_type = ipv4_prefix_match; matched++; } } else /* Check is this point's argument optional ? */ if (CMD_OPTION (str) || CMD_VARIABLE (str)) { if (match_type < extend_match) match_type = extend_match; matched++; } else if (strncmp (command, str, strlen (command)) == 0) { if (strcmp (command, str) == 0) match_type = exact_match; else { if (match_type < partly_match) match_type = partly_match; } matched++; } } if (!matched) vector_slot (v, i) = NULL; } } return match_type; } /* Filter vector by command character with index. */ static enum match_type cmd_filter_by_string (char *command, vector v, unsigned int index) { unsigned int i; const char *str; struct cmd_element *cmd_element; enum match_type match_type; vector descvec; struct desc *desc; match_type = no_match; /* If command and cmd_element string does not match set NULL to vector */ for (i = 0; i < vector_active (v); i++) if ((cmd_element = vector_slot (v, i)) != NULL) { /* If given index is bigger than max string vector of command, set NULL */ if (index >= vector_active (cmd_element->strvec)) vector_slot (v, i) = NULL; else { unsigned int j; int matched = 0; descvec = vector_slot (cmd_element->strvec, index); for (j = 0; j < vector_active (descvec); j++) if ((desc = vector_slot (descvec, j))) { str = desc->cmd; if (CMD_VARARG (str)) { if (match_type < vararg_match) match_type = vararg_match; matched++; } else if (CMD_RANGE (str)) { if (cmd_range_match (str, command)) { if (match_type < range_match) match_type = range_match; matched++; } } #ifdef HAVE_IPV6 else if (CMD_IPV6 (str)) { if (cmd_ipv6_match (command) == exact_match) { if (match_type < ipv6_match) match_type = ipv6_match; matched++; } } else if (CMD_IPV6_PREFIX (str)) { if (cmd_ipv6_prefix_match (command) == exact_match) { if (match_type < ipv6_prefix_match) match_type = ipv6_prefix_match; matched++; } } #endif /* HAVE_IPV6 */ else if (CMD_IPV4 (str)) { if (cmd_ipv4_match (command) == exact_match) { if (match_type < ipv4_match) match_type = ipv4_match; matched++; } } else if (CMD_IPV4_PREFIX (str)) { if (cmd_ipv4_prefix_match (command) == exact_match) { if (match_type < ipv4_prefix_match) match_type = ipv4_prefix_match; matched++; } } else if (CMD_OPTION (str) || CMD_VARIABLE (str)) { if (match_type < extend_match) match_type = extend_match; matched++; } else { if (strcmp (command, str) == 0) { match_type = exact_match; matched++; } } } if (!matched) vector_slot (v, i) = NULL; } } return match_type; } /* Check ambiguous match */ static int is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) { unsigned int i; unsigned int j; const char *str = NULL; struct cmd_element *cmd_element; const char *matched = NULL; vector descvec; struct desc *desc; for (i = 0; i < vector_active (v); i++) if ((cmd_element = vector_slot (v, i)) != NULL) { int match = 0; descvec = vector_slot (cmd_element->strvec, index); for (j = 0; j < vector_active (descvec); j++) if ((desc = vector_slot (descvec, j))) { enum match_type ret; str = desc->cmd; switch (type) { case exact_match: if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) && strcmp (command, str) == 0) match++; break; case partly_match: if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) && strncmp (command, str, strlen (command)) == 0) { if (matched && strcmp (matched, str) != 0) return 1; /* There is ambiguous match. */ else matched = str; match++; } break; case range_match: if (cmd_range_match (str, command)) { if (matched && strcmp (matched, str) != 0) return 1; else matched = str; match++; } break; #ifdef HAVE_IPV6 case ipv6_match: if (CMD_IPV6 (str)) match++; break; case ipv6_prefix_match: if ((ret = cmd_ipv6_prefix_match (command)) != no_match) { if (ret == partly_match) return 2; /* There is incomplete match. */ match++; } break; #endif /* HAVE_IPV6 */ case ipv4_match: if (CMD_IPV4 (str)) match++; break; case ipv4_prefix_match: if ((ret = cmd_ipv4_prefix_match (command)) != no_match) { if (ret == partly_match) return 2; /* There is incomplete match. */ match++; } break; case extend_match: if (CMD_OPTION (str) || CMD_VARIABLE (str)) match++; break; case no_match: default: break; } } if (!match) vector_slot (v, i) = NULL; } return 0; } /* If src matches dst return dst string, otherwise return NULL */ static const char * cmd_entry_function (const char *src, const char *dst) { /* Skip variable arguments. */ if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) return NULL; /* In case of 'command \t', given src is NULL string. */ if (src == NULL) return dst; /* Matched with input string. */ if (strncmp (src, dst, strlen (src)) == 0) return dst; return NULL; } /* If src matches dst return dst string, otherwise return NULL */ /* This version will return the dst string always if it is CMD_VARIABLE for '?' key processing */ static const char * cmd_entry_function_desc (const char *src, const char *dst) { if (CMD_VARARG (dst)) return dst; if (CMD_RANGE (dst)) { if (cmd_range_match (dst, src)) return dst; else return NULL; } #ifdef HAVE_IPV6 if (CMD_IPV6 (dst)) { if (cmd_ipv6_match (src)) return dst; else return NULL; } if (CMD_IPV6_PREFIX (dst)) { if (cmd_ipv6_prefix_match (src)) return dst; else return NULL; } #endif /* HAVE_IPV6 */ if (CMD_IPV4 (dst)) { if (cmd_ipv4_match (src)) return dst; else return NULL; } if (CMD_IPV4_PREFIX (dst)) { if (cmd_ipv4_prefix_match (src)) return dst; else return NULL; } /* Optional or variable commands always match on '?' */ if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) return dst; /* In case of 'command \t', given src is NULL string. */ if (src == NULL) return dst; if (strncmp (src, dst, strlen (src)) == 0) return dst; else return NULL; } /* Check same string element existence. If it isn't there return 1. */ static int cmd_unique_string (vector v, const char *str) { unsigned int i; char *match; for (i = 0; i < vector_active (v); i++) if ((match = vector_slot (v, i)) != NULL) if (strcmp (match, str) == 0) return 0; return 1; } /* Compare string to description vector. If there is same string return 1 else return 0. */ static int desc_unique_string (vector v, const char *str) { unsigned int i; struct desc *desc; for (i = 0; i < vector_active (v); i++) if ((desc = vector_slot (v, i)) != NULL) if (strcmp (desc->cmd, str) == 0) return 1; return 0; } static int cmd_try_do_shortcut (enum node_type node, char* first_word) { if ( first_word != NULL && node != AUTH_NODE && node != VIEW_NODE && node != AUTH_ENABLE_NODE && node != ENABLE_NODE && node != RESTRICTED_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; /* Set index. */ if (vector_active (vline) == 0) { *status = CMD_ERR_NO_MATCH; return NULL; } else index = vector_active (vline) - 1; /* Make copy vector of current node's command vector. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); /* Prepare match vector */ matchvec = vector_init (INIT_MATCHVEC_SIZE); /* Filter commands. */ /* Only words precedes current word will be checked in this loop. */ for (i = 0; i < index; i++) if ((command = vector_slot (vline, i))) { match = cmd_filter_by_completion (command, cmd_vector, i); if (match == vararg_match) { struct cmd_element *cmd_element; vector descvec; unsigned int j, k; for (j = 0; j < vector_active (cmd_vector); j++) if ((cmd_element = vector_slot (cmd_vector, j)) != NULL && (vector_active (cmd_element->strvec))) { descvec = vector_slot (cmd_element->strvec, vector_active (cmd_element->strvec) - 1); for (k = 0; k < vector_active (descvec); k++) { struct desc *desc = vector_slot (descvec, k); vector_set (matchvec, desc); } } vector_set (matchvec, &desc_cr); vector_free (cmd_vector); return matchvec; } if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) { vector_free (cmd_vector); vector_free (matchvec); *status = CMD_ERR_AMBIGUOUS; return NULL; } else if (ret == 2) { vector_free (cmd_vector); vector_free (matchvec); *status = CMD_ERR_NO_MATCH; return NULL; } } /* Prepare match vector */ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ /* Make sure that cmd_vector is filtered based on current word */ command = vector_slot (vline, index); if (command) match = cmd_filter_by_completion (command, cmd_vector, index); /* Make description vector. */ for (i = 0; i < vector_active (cmd_vector); i++) if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) { vector strvec = cmd_element->strvec; /* if command is NULL, index may be equal to vector_active */ if (command && index >= vector_active (strvec)) vector_slot (cmd_vector, i) = NULL; else { /* Check if command is completed. */ if (command == NULL && index == vector_active (strvec)) { if (!desc_unique_string (matchvec, command_cr)) vector_set (matchvec, &desc_cr); } else { unsigned int j; vector descvec = vector_slot (strvec, index); struct desc *desc; for (j = 0; j < vector_active (descvec); j++) if ((desc = vector_slot (descvec, j))) { const char *string; 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; return NULL; } *status = CMD_SUCCESS; return matchvec; } vector cmd_describe_command (vector vline, struct vty *vty, int *status) { vector ret; if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) { enum node_type onode; vector shifted_vline; unsigned int index; onode = vty->node; vty->node = ENABLE_NODE; /* We can try it on enable node, cos' the vty is authenticated */ shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ for (index = 1; index < vector_active (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } ret = cmd_describe_command_real (shifted_vline, vty, status); vector_free(shifted_vline); vty->node = onode; return ret; } return cmd_describe_command_real (vline, vty, status); } /* Check LCD of matched command. */ static int cmd_lcd (char **matched) { int i; int j; int lcd = -1; char *s1, *s2; char c1, c2; if (matched[0] == NULL || matched[1] == NULL) return 0; for (i = 1; matched[i] != NULL; i++) { s1 = matched[i - 1]; s2 = matched[i]; for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) if (c1 != c2) break; if (lcd < 0) lcd = j; else { if (lcd > j) lcd = j; } } return lcd; } /* Command line completion support. */ static char ** cmd_complete_command_real (vector vline, struct vty *vty, int *status) { unsigned int i; vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); #define INIT_MATCHVEC_SIZE 10 vector matchvec; struct cmd_element *cmd_element; unsigned int index; char **match_str; struct desc *desc; vector descvec; char *command; int lcd; if (vector_active (vline) == 0) { vector_free (cmd_vector); *status = CMD_ERR_NO_MATCH; return NULL; } else index = vector_active (vline) - 1; /* First, filter by preceeding command string */ for (i = 0; i < index; i++) if ((command = vector_slot (vline, i))) { enum match_type match; int ret; /* First try completion match, if there is exactly match return 1 */ match = cmd_filter_by_completion (command, cmd_vector, i); /* If there is exact match then filter ambiguous match else check ambiguousness. */ if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) { vector_free (cmd_vector); *status = CMD_ERR_AMBIGUOUS; return NULL; } /* else if (ret == 2) { vector_free (cmd_vector); *status = CMD_ERR_NO_MATCH; return NULL; } */ } /* Prepare match vector. */ matchvec = vector_init (INIT_MATCHVEC_SIZE); /* Now we got into completion */ for (i = 0; i < vector_active (cmd_vector); i++) if ((cmd_element = vector_slot (cmd_vector, i))) { const char *string; vector strvec = cmd_element->strvec; /* Check field length */ if (index >= vector_active (strvec)) vector_slot (cmd_vector, i) = NULL; else { unsigned int j; descvec = vector_slot (strvec, index); for (j = 0; j < vector_active (descvec); j++) if ((desc = vector_slot (descvec, j))) { if ((string = cmd_entry_function (vector_slot (vline, index), desc->cmd))) if (cmd_unique_string (matchvec, string)) vector_set (matchvec, 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_STRVEC, lcd + 1); memcpy (lcdstr, matchvec->index[0], lcd); lcdstr[lcd] = '\0'; /* match_str = (char **) &lcdstr; */ /* Free matchvec. */ for (i = 0; i < vector_active (matchvec); i++) { if (vector_slot (matchvec, i)) XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); } vector_free (matchvec); /* Make new matchvec. */ matchvec = vector_init (INIT_MATCHVEC_SIZE); vector_set (matchvec, lcdstr); match_str = (char **) matchvec->index; vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_MATCH; return match_str; } } } match_str = (char **) matchvec->index; vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_LIST_MATCH; return match_str; } char ** cmd_complete_command (vector vline, struct vty *vty, int *status) { char **ret; if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) { enum node_type onode; vector shifted_vline; unsigned int index; onode = vty->node; vty->node = ENABLE_NODE; /* We can try it on enable node, cos' the vty is authenticated */ shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ for (index = 1; index < vector_active (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } ret = cmd_complete_command_real (shifted_vline, vty, status); vector_free(shifted_vline); vty->node = onode; return ret; } return cmd_complete_command_real (vline, vty, status); } /* return parent node */ /* MUST eventually converge on CONFIG_NODE */ enum node_type node_parent ( enum node_type node ) { enum node_type ret; assert (node > CONFIG_NODE); switch (node) { case BGP_VPNV4_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: ret = BGP_NODE; break; case KEYCHAIN_KEY_NODE: ret = KEYCHAIN_NODE; break; default: ret = CONFIG_NODE; } return ret; } /* Execute command by argument vline vector. */ static int cmd_execute_command_real (vector vline, struct vty *vty, struct cmd_element **cmd) { unsigned int i; unsigned int index; vector cmd_vector; struct cmd_element *cmd_element; struct cmd_element *matched_element; unsigned int matched_count, incomplete_count; int argc; const char *argv[CMD_ARGC_MAX]; enum match_type match = 0; int varflag; char *command; /* Make copy of command elements. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); for (index = 0; index < vector_active (vline); index++) if ((command = vector_slot (vline, index))) { int ret; match = cmd_filter_by_completion (command, cmd_vector, index); if (match == vararg_match) break; ret = is_cmd_ambiguous (command, cmd_vector, index, match); if (ret == 1) { vector_free (cmd_vector); return CMD_ERR_AMBIGUOUS; } else if (ret == 2) { vector_free (cmd_vector); return CMD_ERR_NO_MATCH; } } /* Check matched count. */ matched_element = NULL; matched_count = 0; incomplete_count = 0; for (i = 0; i < vector_active (cmd_vector); i++) if ((cmd_element = vector_slot (cmd_vector, i))) { if (match == vararg_match || index >= cmd_element->cmdsize) { matched_element = cmd_element; #if 0 printf ("DEBUG: %s\n", cmd_element->string); #endif matched_count++; } else { incomplete_count++; } } /* Finish of using cmd_vector. */ vector_free (cmd_vector); /* To execute command, matched_count must be 1. */ if (matched_count == 0) { if (incomplete_count) return CMD_ERR_INCOMPLETE; else return CMD_ERR_NO_MATCH; } if (matched_count > 1) return CMD_ERR_AMBIGUOUS; /* Argument treatment */ varflag = 0; argc = 0; for (i = 0; i < vector_active (vline); i++) { if (varflag) argv[argc++] = vector_slot (vline, i); else { vector descvec = vector_slot (matched_element->strvec, i); if (vector_active (descvec) == 1) { struct desc *desc = vector_slot (descvec, 0); if (CMD_VARARG (desc->cmd)) varflag = 1; if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) argv[argc++] = vector_slot (vline, i); } else argv[argc++] = vector_slot (vline, i); } if (argc >= CMD_ARGC_MAX) return CMD_ERR_EXEED_ARGC_MAX; } /* For vtysh execution. */ if (cmd) *cmd = matched_element; if (matched_element->daemon) return CMD_SUCCESS_DAEMON; /* Execute matched command. */ return (*matched_element->func) (matched_element, vty, argc, argv); } int cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, int vtysh) { int ret, saved_ret, tried = 0; enum node_type onode, try_node; onode = try_node = vty->node; if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) { vector shifted_vline; unsigned int index; vty->node = ENABLE_NODE; /* We can try it on enable node, cos' the vty is authenticated */ shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ for (index = 1; index < vector_active (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } ret = cmd_execute_command_real (shifted_vline, vty, cmd); vector_free(shifted_vline); vty->node = onode; return ret; } saved_ret = ret = cmd_execute_command_real (vline, vty, cmd); if (vtysh) return saved_ret; /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ while ( ret != CMD_SUCCESS && ret != CMD_WARNING && vty->node > CONFIG_NODE ) { try_node = node_parent(try_node); vty->node = try_node; ret = cmd_execute_command_real (vline, vty, cmd); tried = 1; if (ret == CMD_SUCCESS || ret == CMD_WARNING) { /* succesfull command, leave the node as is */ return ret; } } /* no command succeeded, reset the vty to the original node and return the error for this node */ if ( tried ) vty->node = onode; return saved_ret; } /* Execute command by argument readline. */ int cmd_execute_command_strict (vector vline, struct vty *vty, struct cmd_element **cmd) { unsigned int i; unsigned int index; vector cmd_vector; struct cmd_element *cmd_element; struct cmd_element *matched_element; unsigned int matched_count, incomplete_count; int argc; const char *argv[CMD_ARGC_MAX]; int varflag; enum match_type match = 0; char *command; /* Make copy of command element */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); for (index = 0; index < vector_active (vline); index++) if ((command = vector_slot (vline, index))) { int ret; match = cmd_filter_by_string (vector_slot (vline, index), cmd_vector, index); /* If command meets '.VARARG' then finish matching. */ if (match == vararg_match) break; ret = is_cmd_ambiguous (command, cmd_vector, index, match); if (ret == 1) { vector_free (cmd_vector); return CMD_ERR_AMBIGUOUS; } if (ret == 2) { vector_free (cmd_vector); return CMD_ERR_NO_MATCH; } } /* Check matched count. */ matched_element = NULL; matched_count = 0; incomplete_count = 0; for (i = 0; i < vector_active (cmd_vector); i++) if (vector_slot (cmd_vector, i) != NULL) { cmd_element = vector_slot (cmd_vector, i); if (match == vararg_match || index >= cmd_element->cmdsize) { matched_element = cmd_element; matched_count++; } else incomplete_count++; } /* Finish of using cmd_vector. */ vector_free (cmd_vector); /* To execute command, matched_count must be 1. */ if (matched_count == 0) { if (incomplete_count) return CMD_ERR_INCOMPLETE; else return CMD_ERR_NO_MATCH; } if (matched_count > 1) return CMD_ERR_AMBIGUOUS; /* Argument treatment */ varflag = 0; argc = 0; for (i = 0; i < vector_active (vline); i++) { if (varflag) argv[argc++] = vector_slot (vline, i); else { vector descvec = vector_slot (matched_element->strvec, i); if (vector_active (descvec) == 1) { struct desc *desc = vector_slot (descvec, 0); if (CMD_VARARG (desc->cmd)) varflag = 1; if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) argv[argc++] = vector_slot (vline, i); } else argv[argc++] = vector_slot (vline, i); } if (argc >= CMD_ARGC_MAX) return CMD_ERR_EXEED_ARGC_MAX; } /* For vtysh execution. */ if (cmd) *cmd = matched_element; if (matched_element->daemon) return CMD_SUCCESS_DAEMON; /* Now execute matched command */ return (*matched_element->func) (matched_element, vty, argc, argv); } /* 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: case RESTRICTED_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: case BGP_IPV6M_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: case RESTRICTED_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 BGP_IPV6M_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?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_active (cnode->cmd_vector); i++) if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL && !(cmd->attr == CMD_ATTR_DEPRECATED || cmd->attr == CMD_ATTR_HIDDEN)) vty_out (vty, " %s%s", cmd->string, VTY_NEWLINE); return CMD_SUCCESS; } /* Write current configuration into file. */ DEFUN (config_write_file, config_write_file_cmd, "write file", "Write running configuration to memory, network, or terminal\n" "Write to configuration file\n") { unsigned int i; int fd; struct cmd_node *node; char *config_file; char *config_file_tmp = NULL; char *config_file_sav = NULL; int ret = CMD_WARNING; 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 = XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1); strcpy (config_file_sav, config_file); strcat (config_file_sav, CONF_BACKUP_EXT); config_file_tmp = XMALLOC (MTYPE_TMP, 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); goto finished; } /* Make vty for configuration file. */ file_vty = vty_new (); file_vty->fd = fd; file_vty->type = VTY_FILE; /* Config file header print. */ vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! "); vty_time_print (file_vty, 1); vty_out (file_vty, "!\n"); for (i = 0; i < vector_active (cmdvec); i++) if ((node = vector_slot (cmdvec, i)) && node->func) { if ((*node->func) (file_vty)) vty_out (file_vty, "!\n"); } vty_close (file_vty); if (unlink (config_file_sav) != 0) if (errno != ENOENT) { vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, VTY_NEWLINE); goto finished; } if (link (config_file, config_file_sav) != 0) { vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, VTY_NEWLINE); goto finished; } sync (); if (unlink (config_file) != 0) { vty_out (vty, "Can't unlink configuration file %s.%s", config_file, VTY_NEWLINE); goto finished; } if (link (config_file_tmp, config_file) != 0) { vty_out (vty, "Can't save configuration file %s.%s", config_file, VTY_NEWLINE); goto finished; } sync (); 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); goto finished; } vty_out (vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE); ret = CMD_SUCCESS; finished: unlink (config_file_tmp); XFREE (MTYPE_TMP, config_file_tmp); XFREE (MTYPE_TMP, config_file_sav); return ret; } ALIAS (config_write_file, config_write_cmd, "write", "Write running configuration to memory, network, or terminal\n") ALIAS (config_write_file, config_write_memory_cmd, "write memory", "Write running configuration to memory, network, or terminal\n" "Write configuration to the file (same as write file)\n") ALIAS (config_write_file, copy_runningconfig_startupconfig_cmd, "copy running-config startup-config", "Copy configuration\n" "Copy running config to... \n" "Copy running config to startup config (same as write file)\n") /* Write current configuration into the terminal. */ DEFUN (config_write_terminal, config_write_terminal_cmd, "write terminal", "Write running configuration to memory, network, or terminal\n" "Write to terminal\n") { unsigned int i; struct cmd_node *node; if (vty->type == VTY_SHELL_SERV) { for (i = 0; i < vector_active (cmdvec); i++) if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) { if ((*node->func) (vty)) vty_out (vty, "!%s", VTY_NEWLINE); } } else { vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, VTY_NEWLINE); vty_out (vty, "!%s", VTY_NEWLINE); for (i = 0; i < vector_active (cmdvec); i++) if ((node = vector_slot (cmdvec, i)) && node->func) { if ((*node->func) (vty)) vty_out (vty, "!%s", VTY_NEWLINE); } vty_out (vty, "end%s",VTY_NEWLINE); } return CMD_SUCCESS; } /* Write current configuration into the terminal. */ ALIAS (config_write_terminal, show_running_config_cmd, "show running-config", SHOW_STR "running configuration\n") /* Write startup configuration into the terminal. */ DEFUN (show_startup_config, show_startup_config_cmd, "show startup-config", SHOW_STR "Contentes of startup configuration\n") { char buf[BUFSIZ]; FILE *confp; confp = fopen (host.config, "r"); if (confp == NULL) { vty_out (vty, "Can't open configuration file [%s]%s", host.config, VTY_NEWLINE); return CMD_WARNING; } while (fgets (buf, BUFSIZ, confp)) { char *cp = buf; while (*cp != '\r' && *cp != '\n' && *cp != '\0') cp++; *cp = '\0'; vty_out (vty, "%s%s", buf, VTY_NEWLINE); } fclose (confp); return CMD_SUCCESS; } /* Hostname configuration */ DEFUN (config_hostname, hostname_cmd, "hostname WORD", "Set system's network name\n" "This system's network name\n") { if (!isalpha((int) *argv[0])) { vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); return CMD_WARNING; } if (host.name) XFREE (MTYPE_HOST, host.name); host.name = XSTRDUP (MTYPE_HOST, 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 (MTYPE_HOST, 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 (MTYPE_HOST, host.password); host.password = NULL; if (host.password_encrypt) XFREE (MTYPE_HOST, host.password_encrypt); host.password_encrypt = XSTRDUP (MTYPE_HOST, 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 (MTYPE_HOST, host.password); host.password = NULL; if (host.encrypt) { if (host.password_encrypt) XFREE (MTYPE_HOST, host.password_encrypt); host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); } else host.password = XSTRDUP (MTYPE_HOST, 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 (MTYPE_HOST, host.enable); host.enable = NULL; if (host.enable_encrypt) XFREE (MTYPE_HOST, host.enable_encrypt); host.enable_encrypt = XSTRDUP (MTYPE_HOST, 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 (MTYPE_HOST, host.enable); host.enable = NULL; /* Plain password input. */ if (host.encrypt) { if (host.enable_encrypt) XFREE (MTYPE_HOST, host.enable_encrypt); host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); } else host.enable = XSTRDUP (MTYPE_HOST, 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 (MTYPE_HOST, host.enable); host.enable = NULL; if (host.enable_encrypt) XFREE (MTYPE_HOST, 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 (MTYPE_HOST, host.password_encrypt); host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password)); } if (host.enable) { if (host.enable_encrypt) XFREE (MTYPE_HOST, host.enable_encrypt); host.enable_encrypt = XSTRDUP (MTYPE_HOST, 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 (MTYPE_HOST, host.password_encrypt); host.password_encrypt = NULL; if (host.enable_encrypt) XFREE (MTYPE_HOST, 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_HIDDEN (do_echo, echo_cmd, "echo .MESSAGE", "Echo a message back to the vty\n" "The message to echo\n") { char *message; vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE); if (message) XFREE(MTYPE_TMP, message); 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, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); if (message) 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); vty_out (vty, "Timestamp precision: %d%s", zl->timestamp_precision, 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_HOST, host.logfile); host.logfile = XSTRDUP (MTYPE_HOST, 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_HOST, 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 (config_log_timestamp_precision, config_log_timestamp_precision_cmd, "log timestamp precision <0-6>", "Logging control\n" "Timestamp configuration\n" "Set the timestamp precision\n" "Number of subsecond digits\n") { if (argc != 1) { vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); return CMD_WARNING; } VTY_GET_INTEGER_RANGE("Timestamp Precision", zlog_default->timestamp_precision, argv[0], 0, 6); return CMD_SUCCESS; } DEFUN (no_config_log_timestamp_precision, no_config_log_timestamp_precision_cmd, "no log timestamp precision", NO_STR "Logging control\n" "Timestamp configuration\n" "Reset the timestamp precision to the default value of 0\n") { zlog_default->timestamp_precision = 0 ; return CMD_SUCCESS; } DEFUN (banner_motd_file, banner_motd_file_cmd, "banner motd file [FILE]", "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n") { if (host.motdfile) XFREE (MTYPE_HOST, host.motdfile); host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]); return CMD_SUCCESS; } DEFUN (banner_motd_default, banner_motd_default_cmd, "banner motd default", "Set banner string\n" "Strings for motd\n" "Default string\n") { host.motd = default_motd; return CMD_SUCCESS; } DEFUN (no_banner_motd, no_banner_motd_cmd, "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n") { host.motd = NULL; if (host.motdfile) XFREE (MTYPE_HOST, host.motdfile); host.motdfile = NULL; return CMD_SUCCESS; } /* Set config filename. Called from vty.c */ void host_config_set (char *filename) { if (host.config) XFREE (MTYPE_HOST, host.config); host.config = XSTRDUP (MTYPE_HOST, 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) { command_cr = XSTRDUP(MTYPE_STRVEC, ""); desc_cr.cmd = command_cr; desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); /* Default host value settings. */ host.name = NULL; host.password = NULL; host.enable = NULL; host.logfile = NULL; host.config = NULL; host.lines = -1; host.motd = default_motd; host.motdfile = NULL; /* Install top nodes. */ install_node (&view_node, NULL); install_node (&enable_node, NULL); install_node (&auth_node, NULL); install_node (&auth_enable_node, NULL); install_node (&restricted_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); install_element (VIEW_NODE, &echo_cmd); install_element (RESTRICTED_NODE, &config_list_cmd); install_element (RESTRICTED_NODE, &config_exit_cmd); install_element (RESTRICTED_NODE, &config_quit_cmd); install_element (RESTRICTED_NODE, &config_help_cmd); install_element (RESTRICTED_NODE, &config_enable_cmd); install_element (RESTRICTED_NODE, &config_terminal_length_cmd); install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); install_element (RESTRICTED_NODE, &echo_cmd); } if (terminal) { install_default (ENABLE_NODE); install_element (ENABLE_NODE, &config_disable_cmd); install_element (ENABLE_NODE, &config_terminal_cmd); install_element (ENABLE_NODE, ©_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, &echo_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, &config_log_timestamp_precision_cmd); install_element (CONFIG_NODE, &no_config_log_timestamp_precision_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, &banner_motd_file_cmd); install_element (CONFIG_NODE, &no_banner_motd_cmd); install_element (CONFIG_NODE, &service_terminal_length_cmd); install_element (CONFIG_NODE, &no_service_terminal_length_cmd); install_element (VIEW_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &show_thread_cpu_cmd); install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &clear_thread_cpu_cmd); install_element (VIEW_NODE, &show_work_queues_cmd); install_element (ENABLE_NODE, &show_work_queues_cmd); } srand(time(NULL)); } void cmd_terminate () { unsigned int i, j, k, l; struct cmd_node *cmd_node; struct cmd_element *cmd_element; struct desc *desc; vector cmd_node_v, cmd_element_v, desc_v; if (cmdvec) { for (i = 0; i < vector_active (cmdvec); i++) if ((cmd_node = vector_slot (cmdvec, i)) != NULL) { cmd_node_v = cmd_node->cmd_vector; for (j = 0; j < vector_active (cmd_node_v); j++) if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && cmd_element->strvec != NULL) { cmd_element_v = cmd_element->strvec; for (k = 0; k < vector_active (cmd_element_v); k++) if ((desc_v = vector_slot (cmd_element_v, k)) != NULL) { for (l = 0; l < vector_active (desc_v); l++) if ((desc = vector_slot (desc_v, l)) != NULL) { if (desc->cmd) XFREE (MTYPE_STRVEC, desc->cmd); if (desc->str) XFREE (MTYPE_STRVEC, desc->str); XFREE (MTYPE_DESC, desc); } vector_free (desc_v); } cmd_element->strvec = NULL; vector_free (cmd_element_v); } vector_free (cmd_node_v); } vector_free (cmdvec); cmdvec = NULL; } if (command_cr) XFREE(MTYPE_STRVEC, command_cr); if (desc_cr.str) XFREE(MTYPE_STRVEC, desc_cr.str); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password) XFREE (MTYPE_HOST, host.password); if (host.password_encrypt) XFREE (MTYPE_HOST, host.password_encrypt); if (host.enable) XFREE (MTYPE_HOST, host.enable); if (host.enable_encrypt) XFREE (MTYPE_HOST, host.enable_encrypt); if (host.logfile) XFREE (MTYPE_HOST, host.logfile); if (host.motdfile) XFREE (MTYPE_HOST, host.motdfile); if (host.config) XFREE (MTYPE_HOST, host.config); }