DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
+/* Destination for vtysh output */
+FILE *outputfile;
+
/* Struct VTY. */
struct vty *vty;
{.fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .next = NULL},
{.fd = -1, .name = "eigrpd", .flag = VTYSH_EIGRPD, .next = NULL},
{.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL},
- {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL},
+ {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL},
{.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL},
};
bufvalid += nread;
- end = memmem(buf, bufvalid - buf, terminator,
- sizeof(terminator));
- if (end + sizeof(terminator) + 1 > bufvalid)
+ if (bufvalid - buf >= 4)
+ end = memmem(bufvalid - 4, 4, terminator,
+ sizeof(terminator));
+
+ if (end && end + sizeof(terminator) + 1 > bufvalid)
/* found \0\0\0 but return code hasn't been read yet */
end = NULL;
if (end)
ret = end[sizeof(terminator)];
- while (bufvalid > buf && (end > buf || !end)) {
- size_t textlen = (end ? end : bufvalid) - buf;
+ /*
+ * calculate # bytes we have, up to & not including the
+ * terminator if present
+ */
+ size_t textlen = (end ? end : bufvalid) - buf;
+
+ /* feed line processing callback if present */
+ while (callback && bufvalid > buf && (end > buf || !end)) {
+ textlen = (end ? end : bufvalid) - buf;
char *eol = memchr(buf, '\n', textlen);
if (eol)
/* line break */
/* continue reading */
break;
- /* eol is at a line end now, either \n => \0 or \0\0\0
- */
+ /* eol is at line end now, either \n => \0 or \0\0\0 */
assert(eol && eol <= bufvalid);
if (fp) {
end -= eol - buf;
}
+ /* else if no callback, dump raw */
+ if (!callback) {
+ if (fp)
+ fwrite(buf, 1, textlen, fp);
+ memmove(buf, buf + textlen, bufvalid - buf - textlen);
+ bufvalid -= textlen;
+ }
+
if (bufvalid == buf + bufsz) {
char *new;
bufsz *= 2;
fprintf(stdout, "%% Command incomplete.\n");
break;
case CMD_SUCCESS_DAEMON: {
- /* FIXME: Don't open pager for exit commands. popen() causes
- * problems
- * if exited from vtysh at all. This hack shouldn't cause any
- * problem
- * but is really ugly. */
- if (pager && vtysh_pager_name
+ /*
+ * FIXME: Don't open pager for exit commands. popen() causes
+ * problems if exited from vtysh at all. This hack shouldn't
+ * cause any problem but is really ugly.
+ */
+ fp = outputfile;
+ if (pager && vtysh_pager_name && outputfile == stdout
&& (strncmp(line, "exit", 4) != 0)) {
fp = popen(vtysh_pager_name, "w");
if (fp == NULL) {
perror("popen failed for pager");
- fp = stdout;
+ fp = outputfile;
} else
closepager = 1;
- } else
- fp = stdout;
+ }
if (!strcmp(cmd->string, "configure terminal")) {
for (i = 0; i < array_size(vtysh_client); i++) {
if (vline == NULL) {
if (pager && vtysh_pager_name && fp
- && closepager) {
+ && fp != outputfile && closepager) {
if (pclose(fp) == -1) {
perror("pclose failed for pager");
}
(*cmd->func)(cmd, vty, 0, NULL);
}
}
- if (pager && vtysh_pager_name && fp && closepager) {
+ if (pager && vtysh_pager_name && fp && closepager && fp != outputfile) {
if (pclose(fp) == -1) {
perror("pclose failed for pager");
}
return s;
end = s + size - 1;
- while (end >= s && isspace(*end))
+ while (end >= s && isspace((int)*end))
end--;
*(end + 1) = '\0';
- while (*s && isspace(*s))
+ while (*s && isspace((int)*s))
s++;
return s;
}
vty = vty_new();
- vty->fd = 0; /* stdout */
+ vty->wfd = STDERR_FILENO;
vty->type = VTY_TERM;
vty->node = CONFIG_NODE;
switch (vty->node) {
case LDP_IPV4_IFACE_NODE:
if (strncmp(vty_buf_copy, " ", 3)) {
- fprintf(stdout, " end\n");
+ fprintf(outputfile, " end\n");
vty->node = LDP_IPV4_NODE;
}
break;
case LDP_IPV6_IFACE_NODE:
if (strncmp(vty_buf_copy, " ", 3)) {
- fprintf(stdout, " end\n");
+ fprintf(outputfile, " end\n");
vty->node = LDP_IPV6_NODE;
}
break;
case LDP_PSEUDOWIRE_NODE:
if (strncmp(vty_buf_copy, " ", 2)) {
- fprintf(stdout, " end\n");
+ fprintf(outputfile, " end\n");
vty->node = LDP_L2VPN_NODE;
}
break;
}
if (vty_buf_trimmed[0] == '!' || vty_buf_trimmed[0] == '#') {
- fprintf(stdout, "%s", vty->buf);
+ fprintf(outputfile, "%s", vty->buf);
continue;
}
vline = cmd_make_strvec(vty->buf);
if (vline == NULL) {
- fprintf(stdout, "%s", vty->buf);
+ fprintf(outputfile, "%s", vty->buf);
continue;
}
|| prev_node == BGP_IPV6M_NODE
|| prev_node == BGP_EVPN_NODE)
&& (tried == 1)) {
- fprintf(stdout, "exit-address-family\n");
+ fprintf(outputfile, "exit-address-family\n");
} else if ((prev_node == BGP_EVPN_VNI_NODE)
&& (tried == 1)) {
- fprintf(stdout, "exit-vni\n");
+ fprintf(outputfile, "exit-vni\n");
} else if ((prev_node == KEYCHAIN_KEY_NODE)
&& (tried == 1)) {
- fprintf(stdout, "exit\n");
+ fprintf(outputfile, "exit\n");
} else if (tried) {
- fprintf(stdout, "end\n");
+ fprintf(outputfile, "end\n");
}
}
/* If command didn't succeed in any node, continue with return
u_int i;
int cmd_stat = CMD_SUCCESS;
- fprintf(stdout, "%s", vty->buf);
+ fprintf(outputfile, "%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);
+ outputfile);
if (cmd_stat != CMD_SUCCESS)
break;
}
}
}
/* This is the end */
- fprintf(stdout, "\nend\n");
+ fprintf(outputfile, "\nend\n");
vty_close(vty);
XFREE(MTYPE_VTYSH_CMD, vty_buf_copy);
case CMD_ERR_NO_MATCH:
fprintf(stderr, "line %d: %% Unknown command[%d]: %s",
lineno, vty->node, vty->buf);
- retcode =
- CMD_ERR_NO_MATCH; /* once we have an error, we
- remember & return that */
+ retcode = CMD_ERR_NO_MATCH; /* once we have an error, we
+ remember & return that */
break;
case CMD_ERR_INCOMPLETE:
fprintf(stderr,
if (cmd->daemon & vtysh_client[i].flag) {
cmd_stat = vtysh_client_execute(
&vtysh_client[i], vty->buf,
- stdout);
+ outputfile);
/*
* CMD_WARNING - Can mean that the
* command was
PW_NODE, "%s(config-pw)# ",
};
-static struct cmd_node ns_node = {
- NS_NODE, "%s(config-logical-router)# ",
+static struct cmd_node logicalrouter_node = {
+ LOGICALROUTER_NODE, "%s(config-logical-router)# ",
};
static struct cmd_node vrf_node = {
DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD",
ROUTER_STR
"ISO IS-IS\n"
- "ISO Routing area tag")
+ "ISO Routing area tag\n")
{
vty->node = ISIS_NODE;
return CMD_SUCCESS;
break;
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
+ case NH_GROUP_NODE:
case ZEBRA_NODE:
case BGP_NODE:
case RIP_NODE:
"Delete a pseudo interface's configuration\n"
"Interface's name\n" VRF_CMD_HELP_STR)
-DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME",
+DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_cmd,
+ "logical-router (1-65535) ns NAME",
"Enable a logical-router\n"
"Specify the logical-router indentifier\n"
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
- vty->node = NS_NODE;
+ vty->node = LOGICALROUTER_NODE;
return CMD_SUCCESS;
}
+DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd,
+ "no logical-router (1-65535) ns NAME", NO_STR
+ "Enable a Logical-Router\n"
+ "Specify the Logical-Router identifier\n"
+ "The Name Space\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+
DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
"Select a VRF to configure\n"
"VRF's name\n")
"Delete a pseudo vrf's configuration\n"
"VRF's name\n")
-DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit",
+DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter,
+ vtysh_exit_logicalrouter_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
}
-DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit",
+DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter,
+ vtysh_quit_logicalrouter_cmd, "quit",
"Exit current mode and down to previous mode\n")
{
- return vtysh_exit_ns(self, vty, argc, argv);
+ return vtysh_exit_logicalrouter(self, vty, argc, argv);
}
DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit",
fprintf(stdout, "Thread statistics for %s:\n",
vtysh_client[i].name);
ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
+ outputfile);
fprintf(stdout, "\n");
}
return ret;
fprintf(stdout, "Work queue statistics for %s:\n",
vtysh_client[i].name);
ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
+ outputfile);
fprintf(stdout, "\n");
}
}
ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n",
- stdout);
+ outputfile);
return ret;
}
for (i = 0; i < array_size(vtysh_client); i++)
if (vtysh_client[i].fd >= 0) {
- fprintf(stdout, headline, vtysh_client[i].name);
+ fprintf(outputfile, headline, vtysh_client[i].name);
ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
+ outputfile);
fprintf(stdout, "\n");
}
return ret;
}
+DEFUNSH_HIDDEN (0x00,
+ vtysh_debug_all,
+ vtysh_debug_all_cmd,
+ "[no] debug all",
+ NO_STR
+ DEBUG_STR
+ "Toggle all debugs on or off\n")
+{
+ return CMD_SUCCESS;
+}
+
DEFUN (vtysh_show_debugging,
vtysh_show_debugging_cmd,
"show debugging",
SHOW_STR
DEBUG_STR)
{
- return show_per_daemon("do show debugging\n",
- "");
+ return show_per_daemon("do show debugging\n", "");
}
DEFUN (vtysh_show_debugging_hashtable,
SHOW_STR
"Memory statistics\n")
{
- return show_per_daemon("show memory\n",
- "Memory statistics for %s:\n");
+ return show_per_daemon("show memory\n", "Memory statistics for %s:\n");
}
DEFUN (vtysh_show_modules,
"log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
"Logging control\n"
"Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
-
{
return CMD_SUCCESS;
}
"Logging control\n"
"Reset syslog facility to default (daemon)\n"
"Syslog facility\n")
-
{
return CMD_SUCCESS;
}
"log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
"Logging control\n"
"(Deprecated) Set logging level and default for all destinations\n" LOG_LEVEL_DESC)
-
{
return CMD_SUCCESS;
}
{
u_int i;
char line[] = "do write terminal\n";
- FILE *fp = NULL;
+ FILE *fp = outputfile;
- if (vtysh_pager_name) {
+ if (fp == stdout && vtysh_pager_name) {
fp = popen(vtysh_pager_name, "w");
if (fp == NULL) {
perror("popen");
exit(1);
}
- } else
- fp = stdout;
+ }
- vty_out(vty, "Building configuration...\n");
- vty_out(vty, "\nCurrent configuration:\n");
- vty_out(vty, "!\n");
+ fprintf(outputfile, "Building configuration...\n");
+ fprintf(outputfile, "\nCurrent configuration:\n");
+ fprintf(outputfile, "!\n");
for (i = 0; i < array_size(vtysh_client); i++)
if ((argc < 3)
vtysh_config_dump(fp);
- if (vtysh_pager_name && fp) {
+ if (vtysh_pager_name && fp && fp != outputfile) {
fflush(fp);
if (pclose(fp) == -1) {
perror("pclose");
fp = NULL;
}
- vty_out(vty, "end\n");
+ fprintf(outputfile, "end\n");
return CMD_SUCCESS;
}
char line[] = "do write memory\n";
u_int i;
- fprintf(stdout,
+ fprintf(outputfile,
"Note: this version of vtysh never writes vtysh.conf\n");
/* If integrated frr.conf explicitely set. */
if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1)
ret = vtysh_client_execute(&vtysh_client[i],
"do write integrated",
- stdout);
+ outputfile);
if (ret != CMD_SUCCESS) {
printf("\nWarning: attempting direct configuration write without "
return ret;
}
- fprintf(stdout, "Building Configuration...\n");
+ fprintf(outputfile, "Building Configuration...\n");
for (i = 0; i < array_size(vtysh_client); i++)
- ret = vtysh_client_execute(&vtysh_client[i], line, stdout);
+ ret = vtysh_client_execute(&vtysh_client[i], line, outputfile);
return ret;
}
lines = strtol(argv[idx_number]->arg, &endptr, 10);
if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed\n");
+ fprintf(outputfile, "length is malformed\n");
return CMD_WARNING;
}
for (i = 0; i < array_size(vtysh_client); i++)
if (vtysh_client[i].fd >= 0)
- vty_out(vty, " %s", vtysh_client[i].name);
- vty_out(vty, "\n");
+ fprintf(outputfile, " %s", vtysh_client[i].name);
+ fprintf(outputfile, "\n");
return CMD_SUCCESS;
}
/* Execute command in child process. */
-static void execute_command(const char *command, int argc,
- const char *arg1, const char *arg2)
+static void execute_command(const char *command, int argc, const char *arg1,
+ const char *arg2)
{
pid_t pid;
int status;
"IP trace\n"
"Trace route to destination address or hostname\n")
+DEFUN (vtysh_mtrace,
+ vtysh_mtrace_cmd,
+ "mtrace WORD",
+ "Multicast trace route to multicast source\n"
+ "Multicast trace route to multicast source address\n")
+{
+ int idx = 1;
+
+ argv_find(argv, argc, "WORD", &idx);
+ execute_command("mtracebis", 1, argv[idx]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
DEFUN (vtysh_ping6,
vtysh_ping6_cmd,
"ping ipv6 WORD",
return cmd_list_cmds(vty, argc == 2);
}
+DEFUN (vtysh_output_file,
+ vtysh_output_file_cmd,
+ "output file FILE",
+ "Direct vtysh output to file\n"
+ "Direct vtysh output to file\n"
+ "Path to dump output to\n")
+{
+ const char *path = argv[argc - 1]->arg;
+ outputfile = fopen(path, "a");
+ if (!outputfile) {
+ fprintf(stdout, "Failed to open file '%s': %s\n", path,
+ safe_strerror(errno));
+ outputfile = stdout;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_vtysh_output_file,
+ no_vtysh_output_file_cmd,
+ "no output file [FILE]",
+ NO_STR
+ "Direct vtysh output to file\n"
+ "Direct vtysh output to file\n"
+ "Path to dump output to\n")
+{
+ if (outputfile != stdout) {
+ fclose(outputfile);
+ outputfile = stdout;
+ }
+ return CMD_SUCCESS;
+}
+
DEFUN(find,
find_cmd,
"find COMMAND...",
{
install_element(node, &config_list_cmd);
install_element(node, &find_cmd);
+ install_element(node, &vtysh_output_file_cmd);
+ install_element(node, &no_vtysh_output_file_cmd);
}
/* Making connection to protocol daemon. */
.completions = vtysh_autocomplete},
{.completions = NULL}};
+void vtysh_uninit()
+{
+ if (outputfile != stdout)
+ fclose(outputfile);
+}
+
void vtysh_init_vty(void)
{
/* Make vty structure. */
vty->type = VTY_SHELL;
vty->node = VIEW_NODE;
+ /* set default output */
+ outputfile = stdout;
+
/* Initialize commands. */
cmd_init(0);
cmd_variable_handler_register(vtysh_var_handler);
install_node(&interface_node, NULL);
install_node(&pw_node, NULL);
install_node(&link_params_node, NULL);
- install_node(&ns_node, NULL);
+ install_node(&logicalrouter_node, NULL);
install_node(&vrf_node, NULL);
install_node(&rmap_node, NULL);
install_node(&zebra_node, NULL);
install_element(PW_NODE, &vtysh_exit_interface_cmd);
install_element(PW_NODE, &vtysh_quit_interface_cmd);
- install_element(NS_NODE, &vtysh_end_all_cmd);
+ install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd);
- install_element(CONFIG_NODE, &vtysh_ns_cmd);
- install_element(NS_NODE, &vtysh_exit_ns_cmd);
- install_element(NS_NODE, &vtysh_quit_ns_cmd);
+ install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd);
+ install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd);
+ install_element(LOGICALROUTER_NODE, &vtysh_exit_logicalrouter_cmd);
+ install_element(LOGICALROUTER_NODE, &vtysh_quit_logicalrouter_cmd);
install_element(VRF_NODE, &vtysh_end_all_cmd);
install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
+ install_element(VIEW_NODE, &vtysh_mtrace_cmd);
install_element(VIEW_NODE, &vtysh_ping6_cmd);
install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
#if defined(HAVE_SHELL_ACCESS)
install_element(ENABLE_NODE, &vtysh_start_zsh_cmd);
#endif
+ /* debugging */
install_element(VIEW_NODE, &vtysh_show_debugging_cmd);
install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd);
+ install_element(VIEW_NODE, &vtysh_debug_all_cmd);
+ install_element(CONFIG_NODE, &vtysh_debug_all_cmd);
+
+ /* misc lib show commands */
install_element(VIEW_NODE, &vtysh_show_memory_cmd);
install_element(VIEW_NODE, &vtysh_show_modules_cmd);
-
install_element(VIEW_NODE, &vtysh_show_work_queues_cmd);
install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd);
-
install_element(VIEW_NODE, &vtysh_show_thread_cmd);
/* Logging */