From: Dinesh Dutt <[email protected]>
To support applying only differences to the existing config, this patch
enables supplying the appropriate end markers to a provided file (or
stdin). By end markers, I mean, adding "end" and "exit-address-family"
at the appropriate places in the configuration to ease finding the
differences with the running configuration.
Signed-off-by: Dinesh Dutt <[email protected]>
---
lib/command.c | 2 +-
lib/command.h | 2 +-
vtysh/vtysh.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++-
vtysh/vtysh.h | 4 +-
vtysh/vtysh_config.c | 2 +-
vtysh/vtysh_main.c | 42 ++++++++++++--
6 files changed, 197 insertions(+), 10 deletions(-)
diff --git a/lib/command.c b/lib/command.c
index 37767e9..19d6fd1 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -4046,7 +4046,7 @@ DEFUN (no_banner_motd,
/* Set config filename. Called from vty.c */
void
-host_config_set (char *filename)
+host_config_set (const char *filename)
{
if (host.config)
XFREE (MTYPE_HOST, host.config);
diff --git a/lib/command.h b/lib/command.h
index f98a609..7d6cf5b 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -543,7 +543,7 @@ extern struct cmd_element config_quit_cmd;
extern struct cmd_element config_help_cmd;
extern struct cmd_element config_list_cmd;
extern char *host_config_file (void);
-extern void host_config_set (char *);
+extern void host_config_set (const char *);
extern void print_version (const char *);
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 00ce62c..c9e5fbb 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -446,6 +446,157 @@ vtysh_execute (const char *line)
return vtysh_execute_func (line, 1);
}
+int
+vtysh_mark_file (const char *filename)
+{
+ struct vty *vty;
+ FILE *confp = NULL;
+ int ret;
+ vector vline;
+ int tried = 0;
+ struct cmd_element *cmd;
+ int saved_ret, prev_node;
+ int lineno = 0;
+
+ if (strncmp("-", filename, 1) == 0)
+ confp = stdin;
+ else
+ confp = fopen (filename, "r");
+
+ if (confp == NULL)
+ return (1);
+
+ 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");
+
+ while (fgets (vty->buf, VTY_BUFSIZ, confp))
+ {
+ lineno++;
+ tried = 0;
+
+ if (vty->buf[0] == '!' || vty->buf[1] == '#')
+ {
+ fprintf(stdout, "%s", vty->buf);
+ continue;
+ }
+
+ /* Split readline string up into the vector. */
+ vline = cmd_make_strvec (vty->buf);
+
+ if (vline == NULL)
+ {
+ fprintf(stdout, "%s", vty->buf);
+ continue;
+ }
+
+ prev_node = vty->node;
+ saved_ret = ret = cmd_execute_command_strict (vline, vty, &cmd);
+
+ /* If command doesn't succeeded in current node, try to walk up in node
tree.
+ * Changing vty->node is enough to try it just out without actual walkup
in
+ * the vtysh. */
+ while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret !=
CMD_WARNING
+ && vty->node > CONFIG_NODE)
+ {
+ vty->node = node_parent(vty->node);
+ ret = cmd_execute_command_strict (vline, vty, &cmd);
+ tried++;
+ }
+
+ /* If command succeeded in any other node than current (tried > 0) we
have
+ * to move into node in the vtysh where it succeeded. */
+ if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret ==
CMD_WARNING)
+ {
+ if ((prev_node == BGP_VPNV4_NODE || prev_node == BGP_IPV4_NODE
+ || prev_node == BGP_IPV6_NODE || prev_node == BGP_IPV4M_NODE
+ || prev_node == BGP_IPV6M_NODE)
+ && (tried == 1))
+ {
+ fprintf(stdout, "exit-address-family\n");
+ }
+ else if ((prev_node == KEYCHAIN_KEY_NODE) && (tried == 1))
+ {
+ fprintf(stdout, "exit\n");
+ }
+ else if (tried)
+ {
+ fprintf(stdout, "end\n");
+ }
+ }
+ /* If command didn't succeed in any node, continue with return value from
+ * first try. */
+ else if (tried)
+ {
+ ret = saved_ret;
+ vty->node = prev_node;
+ }
+
+ cmd_free_strvec (vline);
+ switch (ret)
+ {
+ case CMD_WARNING:
+ if (vty->type == VTY_FILE)
+ fprintf (stderr,"line %d: Warning...: %s\n", lineno, vty->buf);
+ fclose(confp);
+ vty_close(vty);
+ return (1);
+ case CMD_ERR_AMBIGUOUS:
+ fprintf (stderr,"line %d: %% Ambiguous command: %s\n", lineno,
vty->buf);
+ fclose(confp);
+ vty_close(vty);
+ return(1);
+ case CMD_ERR_NO_MATCH:
+ fprintf (stderr,"line %d: %% Unknown command: %s\n", lineno,
vty->buf);
+ fclose(confp);
+ vty_close(vty);
+ return(1);
+ case CMD_ERR_INCOMPLETE:
+ fprintf (stderr,"line %d: %% Command incomplete: %s\n", lineno,
vty->buf);
+ fclose(confp);
+ vty_close(vty);
+ return(1);
+ case CMD_SUCCESS:
+ fprintf(stdout, "%s", vty->buf);
+ break;
+ case CMD_SUCCESS_DAEMON:
+ {
+ u_int i;
+ int cmd_stat = CMD_SUCCESS;
+
+ fprintf(stdout, "%s", vty->buf);
+ for (i = 0; i < array_size(vtysh_client); i++)
+ {
+ if (cmd->daemon & vtysh_client[i].flag)
+ {
+ cmd_stat = vtysh_client_execute (&vtysh_client[i],
+ vty->buf, stdout);
+ if (cmd_stat != CMD_SUCCESS)
+ break;
+ }
+ }
+ if (cmd_stat != CMD_SUCCESS)
+ break;
+
+ if (cmd->func)
+ (*cmd->func) (cmd, vty, 0, NULL);
+ }
+ }
+ }
+ /* This is the end */
+ fprintf(stdout, "end\n");
+ vty_close(vty);
+
+ if (confp != stdin)
+ fclose(confp);
+
+ return (0);
+}
+
/* Configration make from file. */
int
vtysh_config_from_file (struct vty *vty, FILE *fp)
@@ -464,13 +615,13 @@ vtysh_config_from_file (struct vty *vty, FILE *fp)
fprintf (stdout,"Warning...\n");
break;
case CMD_ERR_AMBIGUOUS:
- fprintf (stdout,"%% Ambiguous command.\n");
+ fprintf (stdout,"%% Ambiguous command: %s\n", vty->buf);
break;
case CMD_ERR_NO_MATCH:
fprintf (stdout,"%% Unknown command: %s", vty->buf);
break;
case CMD_ERR_INCOMPLETE:
- fprintf (stdout,"%% Command incomplete.\n");
+ fprintf (stdout,"%% Command incomplete: %s\n", vty->buf);
break;
case CMD_SUCCESS_DAEMON:
{
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index 1681a71..9f8fb23 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -53,7 +53,9 @@ void vtysh_config_write (void);
int vtysh_config_from_file (struct vty *, FILE *);
-int vtysh_read_config (char *);
+int vtysh_mark_file(const char *filename);
+
+int vtysh_read_config (const char *);
void vtysh_config_parse (char *);
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 94dc995..b5869dd 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -388,7 +388,7 @@ vtysh_read_file (FILE *confp)
/* Read up configuration file from config_default_dir. */
int
-vtysh_read_config (char *config_default_dir)
+vtysh_read_config (const char *config_default_dir)
{
FILE *confp = NULL;
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index aa7d021..8caf7a6 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -135,8 +135,10 @@ usage (int status)
"-b, --boot Execute boot startup configuration\n" \
"-c, --command Execute argument as command\n" \
"-d, --daemon Connect only to the specified daemon\n" \
+ "-f, --inputfile Execute commands from specific file and
exit\n" \
"-E, --echo Echo prompt and command in -c mode\n" \
"-C, --dryrun Check configuration for validity and
exit\n" \
+ "-m, --markfile Mark input file with context end\n"
"-h, --help Display this help and exit\n\n" \
"Note that multiple commands may be executed from the command\n" \
"line by passing multiple -c args, or by embedding linefeed\n" \
@@ -154,10 +156,12 @@ struct option longopts[] =
{ "eval", required_argument, NULL, 'e'},
{ "command", required_argument, NULL, 'c'},
{ "daemon", required_argument, NULL, 'd'},
+ { "inputfile", required_argument, NULL, 'f'},
{ "echo", no_argument, NULL, 'E'},
{ "dryrun", no_argument, NULL, 'C'},
{ "help", no_argument, NULL, 'h'},
{ "noerror", no_argument, NULL, 'n'},
+ { "mark", no_argument, NULL, 'm'},
{ 0 }
};
@@ -216,6 +220,7 @@ main (int argc, char **argv, char **env)
int dryrun = 0;
int boot_flag = 0;
const char *daemon_name = NULL;
+ const char *inputfile = NULL;
struct cmd_rec {
const char *line;
struct cmd_rec *next;
@@ -223,6 +228,7 @@ main (int argc, char **argv, char **env)
struct cmd_rec *tail = NULL;
int echo_command = 0;
int no_error = 0;
+ int markfile = 0;
/* Preserve name of myself. */
progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
@@ -234,7 +240,7 @@ main (int argc, char **argv, char **env)
/* Option handling. */
while (1)
{
- opt = getopt_long (argc, argv, "be:c:d:nEhC", longopts, 0);
+ opt = getopt_long (argc, argv, "be:c:d:nf:mEhC", longopts, 0);
if (opt == EOF)
break;
@@ -263,6 +269,12 @@ main (int argc, char **argv, char **env)
case 'd':
daemon_name = optarg;
break;
+ case 'f':
+ inputfile = optarg;
+ break;
+ case 'm':
+ markfile = 1;
+ break;
case 'n':
no_error = 1;
break;
@@ -297,12 +309,28 @@ main (int argc, char **argv, char **env)
vty_init_vtysh ();
/* Read vtysh configuration file before connecting to daemons. */
- vtysh_read_config (config_default);
+ vtysh_read_config(config_default);
+
+ if (markfile)
+ {
+ if (!inputfile)
+ {
+ fprintf(stderr, "-f option MUST be specified with -m option\n");
+ return(1);
+ }
+ return(vtysh_mark_file(inputfile));
+ }
/* Start execution only if not in dry-run mode */
if(dryrun)
- return(0);
-
+ {
+ if (inputfile)
+ {
+ vtysh_read_config(inputfile);
+ }
+ return(0);
+ }
+
/* Ignore error messages */
if (no_error)
freopen("/dev/null", "w", stdout);
@@ -317,6 +345,12 @@ main (int argc, char **argv, char **env)
exit(1);
}
+ if (inputfile)
+ {
+ vtysh_read_config(inputfile);
+ exit(0);
+ }
+
/* If eval mode. */
if (cmd)
{