blob: 9a0bff495f268a9b7b86c1f16f111b26f26c46c7 [file] [log] [blame]
/* Configuration generator.
Copyright (C) 2000 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 "command.h"
#include "linklist.h"
#include "memory.h"
#include "vtysh/vtysh.h"
vector configvec;
struct config
{
/* Configuration node name. */
char *name;
/* Configuration string line. */
struct list *line;
/* Configuration can be nest. */
struct config *config;
/* Index of this config. */
u_int32_t index;
};
struct list *config_top;
int
line_cmp (char *c1, char *c2)
{
return strcmp (c1, c2);
}
void
line_del (char *line)
{
XFREE (MTYPE_VTYSH_CONFIG_LINE, line);
}
struct config *
config_new ()
{
struct config *config;
config = XCALLOC (MTYPE_VTYSH_CONFIG, sizeof (struct config));
return config;
}
int
config_cmp (struct config *c1, struct config *c2)
{
return strcmp (c1->name, c2->name);
}
void
config_del (struct config* config)
{
list_delete (config->line);
if (config->name)
XFREE (MTYPE_VTYSH_CONFIG_LINE, config->name);
XFREE (MTYPE_VTYSH_CONFIG, config);
}
struct config *
config_get (int index, char *line)
{
struct config *config;
struct config *config_loop;
struct list *master;
struct listnode *nn;
config = config_loop = NULL;
master = vector_lookup_ensure (configvec, index);
if (! master)
{
master = list_new ();
master->del = (void (*) (void *))config_del;
master->cmp = (int (*)(void *, void *)) config_cmp;
vector_set_index (configvec, index, master);
}
LIST_LOOP (master, config_loop, nn)
{
if (strcmp (config_loop->name, line) == 0)
config = config_loop;
}
if (! config)
{
config = config_new ();
config->line = list_new ();
config->line->del = (void (*) (void *))line_del;
config->line->cmp = (int (*)(void *, void *)) line_cmp;
config->name = XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line);
config->index = index;
listnode_add (master, config);
}
return config;
}
void
config_add_line (struct list *config, char *line)
{
listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
void
config_add_line_uniq (struct list *config, char *line)
{
struct listnode *nn;
char *pnt;
LIST_LOOP (config, pnt, nn)
{
if (strcmp (pnt, line) == 0)
return;
}
listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
void
vtysh_config_parse_line (char *line)
{
char c;
static struct config *config = NULL;
if (! line)
return;
c = line[0];
if (c == '\0')
return;
/* printf ("[%s]\n", line); */
switch (c)
{
case '!':
case '#':
break;
case ' ':
/* Store line to current configuration. */
if (config)
{
if (strncmp (line, " address-family vpnv4", strlen (" address-family vpnv4")) == 0)
config = config_get (BGP_VPNV4_NODE, line);
else if (strncmp (line, " address-family ipv4 multicast", strlen (" address-family ipv4 multicast")) == 0)
config = config_get (BGP_IPV4M_NODE, line);
else if (strncmp (line, " address-family ipv6", strlen (" address-family ipv6")) == 0)
config = config_get (BGP_IPV6_NODE, line);
else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE )
config_add_line_uniq (config->line, line);
else
config_add_line (config->line, line);
}
else
config_add_line (config_top, line);
break;
default:
if (strncmp (line, "interface", strlen ("interface")) == 0)
config = config_get (INTERFACE_NODE, line);
else if (strncmp (line, "router rip", strlen ("router rip")) == 0)
config = config_get (RIP_NODE, line);
else if (strncmp (line, "router ripng", strlen ("router ripng")) == 0)
config = config_get (RIPNG_NODE, line);
else if (strncmp (line, "router ospf", strlen ("router ospf")) == 0)
config = config_get (OSPF_NODE, line);
else if (strncmp (line, "router ospf6", strlen ("router ospf6")) == 0)
config = config_get (OSPF6_NODE, line);
else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0)
config = config_get (BGP_NODE, line);
else if (strncmp (line, "router isis", strlen ("router isis")) == 0)
config = config_get (ISIS_NODE, line);
else if (strncmp (line, "router", strlen ("router")) == 0)
config = config_get (BGP_NODE, line);
else if (strncmp (line, "route-map", strlen ("route-map")) == 0)
config = config_get (RMAP_NODE, line);
else if (strncmp (line, "access-list", strlen ("access-list")) == 0)
config = config_get (ACCESS_NODE, line);
else if (strncmp (line, "ipv6 access-list", strlen ("ipv6 access-list")) == 0)
config = config_get (ACCESS_IPV6_NODE, line);
else if (strncmp (line, "ip prefix-list", strlen ("ip prefix-list")) == 0)
config = config_get (PREFIX_NODE, line);
else if (strncmp (line, "ipv6 prefix-list", strlen ("ipv6 prefix-list")) == 0)
config = config_get (PREFIX_IPV6_NODE, line);
else if (strncmp (line, "ip as-path access-list", strlen ("ip as-path access-list")) == 0)
config = config_get (AS_LIST_NODE, line);
else if (strncmp (line, "ip community-list", strlen ("ip community-list")) == 0)
config = config_get (COMMUNITY_LIST_NODE, line);
else if (strncmp (line, "ip route", strlen ("ip route")) == 0)
config = config_get (IP_NODE, line);
else if (strncmp (line, "ipv6 route", strlen ("ipv6 route")) == 0)
config = config_get (IP_NODE, line);
else if (strncmp (line, "key", strlen ("key")) == 0)
config = config_get (KEYCHAIN_NODE, line);
else
{
if (strncmp (line, "log", strlen ("log")) == 0
|| strncmp (line, "hostname", strlen ("hostname")) == 0
|| strncmp (line, "password", strlen ("hostname")) == 0)
config_add_line_uniq (config_top, line);
else
config_add_line (config_top, line);
config = NULL;
}
break;
}
}
void
vtysh_config_parse (char *line)
{
char *begin;
char *pnt;
begin = pnt = line;
while (*pnt != '\0')
{
if (*pnt == '\n')
{
*pnt++ = '\0';
vtysh_config_parse_line (begin);
begin = pnt;
}
else
{
pnt++;
}
}
}
/* Macro to check delimiter is needed between each configuration line
or not. */
#define NO_DELIMITER(I) \
((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
|| (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE || \
(I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE)
/* Display configuration to file pointer. */
void
vtysh_config_dump (FILE *fp)
{
struct listnode *nn;
struct listnode *nm;
struct config *config;
struct list *master;
char *line;
int i;
LIST_LOOP (config_top, line, nn)
{
fprintf (fp, "%s\n", line);
fflush (fp);
}
fprintf (fp, "!\n");
fflush (fp);
for (i = 0; i < vector_max (configvec); i++)
if ((master = vector_slot (configvec, i)) != NULL)
{
LIST_LOOP (master, config, nn)
{
fprintf (fp, "%s\n", config->name);
fflush (fp);
LIST_LOOP (config->line, line, nm)
{
fprintf (fp, "%s\n", line);
fflush (fp);
}
if (! NO_DELIMITER (i))
{
fprintf (fp, "!\n");
fflush (fp);
}
}
if (NO_DELIMITER (i))
{
fprintf (fp, "!\n");
fflush (fp);
}
}
for (i = 0; i < vector_max (configvec); i++)
if ((master = vector_slot (configvec, i)) != NULL)
{
list_delete (master);
vector_slot (configvec, i) = NULL;
}
list_delete_all_node (config_top);
}
/* Read up configuration file from file_name. */
static void
vtysh_read_file (FILE *confp)
{
int ret;
struct vty *vty;
vty = vty_new ();
vty->fd = 0; /* stdout */
vty->type = VTY_TERM;
vty->node = CONFIG_NODE;
vtysh_execute_no_pager ("enable");
vtysh_execute_no_pager ("configure terminal");
/* Execute configuration file */
ret = vtysh_config_from_file (vty, confp);
vtysh_execute_no_pager ("end");
vtysh_execute_no_pager ("disable");
vty_close (vty);
if (ret != CMD_SUCCESS)
{
switch (ret)
{
case CMD_ERR_AMBIGUOUS:
fprintf (stderr, "Ambiguous command.\n");
break;
case CMD_ERR_NO_MATCH:
fprintf (stderr, "There is no such command.\n");
break;
}
fprintf (stderr, "Error occured during reading below line.\n%s\n",
vty->buf);
exit (1);
}
}
/* Read up configuration file from file_name. */
void
vtysh_read_config (char *config_file,
char *config_current_dir,
char *config_default_dir)
{
char *cwd;
FILE *confp = NULL;
char *fullpath;
/* If -f flag specified. */
if (config_file != NULL)
{
if (! IS_DIRECTORY_SEP (config_file[0]))
{
cwd = getcwd (NULL, MAXPATHLEN);
fullpath = XMALLOC (MTYPE_TMP,
strlen (cwd) + strlen (config_file) + 2);
sprintf (fullpath, "%s/%s", cwd, config_file);
}
else
fullpath = config_file;
confp = fopen (fullpath, "r");
if (confp == NULL)
{
fprintf (stderr, "can't open configuration file [%s]\n",
config_file);
exit(1);
}
}
else
{
/* Relative path configuration file open. */
if (config_current_dir)
confp = fopen (config_current_dir, "r");
/* If there is no relative path exists, open system default file. */
if (confp == NULL)
{
confp = fopen (config_default_dir, "r");
if (confp == NULL)
{
fprintf (stderr, "can't open configuration file [%s]\n",
config_default_dir);
exit (1);
}
else
fullpath = config_default_dir;
}
else
{
/* Rleative path configuration file. */
cwd = getcwd (NULL, MAXPATHLEN);
fullpath = XMALLOC (MTYPE_TMP,
strlen (cwd) + strlen (config_current_dir) + 2);
sprintf (fullpath, "%s/%s", cwd, config_current_dir);
}
}
vtysh_read_file (confp);
fclose (confp);
host_config_set (fullpath);
}
void
vtysh_config_write (FILE *fp)
{
extern struct host host;
if (host.name)
fprintf (fp, "hostname %s\n", host.name);
fprintf (fp, "!\n");
}
void
vtysh_config_init ()
{
config_top = list_new ();
config_top->del = (void (*) (void *))line_del;
configvec = vector_init (1);
}