blob: 8eaa76d13912183163be25385dcc6ae1ba2dae6f [file] [log] [blame]
/*
Command parsing taken from brctl utility.
Display code from stp_cli.c in rstplib.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <getopt.h>
#include <net/if.h>
/* For scanning through sysfs directories */
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "ctl_socket_client.h"
#include "ctl_functions.h"
#ifndef False
# define False 0
# define True 1
#endif
#define STP_IN_get_error_explanation CTL_error_explanation
static void print_bridge_id(UID_BRIDGE_ID_T * bridge_id, unsigned char cr)
{
printf("%04lX-%02x%02x%02x%02x%02x%02x",
(unsigned long)bridge_id->prio,
(unsigned char)bridge_id->addr[0],
(unsigned char)bridge_id->addr[1],
(unsigned char)bridge_id->addr[2],
(unsigned char)bridge_id->addr[3],
(unsigned char)bridge_id->addr[4],
(unsigned char)bridge_id->addr[5]);
if (cr)
printf("\n");
}
static char *stp_state2str(RSTP_PORT_STATE stp_port_state, int detail)
{
if (detail) {
switch (stp_port_state) {
case UID_PORT_DISABLED:
return "Disabled";
case UID_PORT_DISCARDING:
return "Discarding";
case UID_PORT_LEARNING:
return "Learning";
case UID_PORT_FORWARDING:
return "Forwarding";
case UID_PORT_NON_STP:
return "NoStp";
default:
return "Unknown";
}
}
switch (stp_port_state) {
case UID_PORT_DISABLED:
return "Dis";
case UID_PORT_DISCARDING:
return "Blk";
case UID_PORT_LEARNING:
return "Lrn";
case UID_PORT_FORWARDING:
return "Fwd";
case UID_PORT_NON_STP:
return "Non";
default:
return "Unk";
}
}
static void CLI_out_port_id(int port, unsigned char cr)
{
static char ifname[IFNAMSIZ];
if (if_indextoname(port, ifname))
printf("%s", ifname);
else
printf("Ifindex %02d", port);
if (cr)
printf("\n");
}
int get_index_die(const char *ifname, const char *doc, int die)
{
int r = if_nametoindex(ifname);
if (r == 0) {
fprintf(stderr,
"Can't find index for %s %s. Not a valid interface.\n",
doc, ifname);
if (die)
exit(1);
return -1;
}
return r;
}
int get_index(const char *ifname, const char *doc)
{
return get_index_die(ifname, doc, 1);
}
static int cmd_rstp(int argc, char *const *argv)
{
int stp, r;
int br_index = get_index(argv[1], "bridge");
if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes")
|| !strcmp(argv[2], "1"))
stp = 1;
else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no")
|| !strcmp(argv[2], "0"))
stp = 0;
else {
fprintf(stderr, "expect on/off for argument\n");
return 1;
}
r = CTL_enable_bridge_rstp(br_index, stp);
if (r) {
fprintf(stderr, "Failed to enable/disable RSTP: %s\n",
CTL_error_explanation(r));
return -1;
}
return 0;
}
static int do_showbridge(const char *br_name)
{
UID_STP_STATE_T uid_state;
UID_STP_CFG_T uid_cfg;
int br_index = get_index_die(br_name, "bridge", 0);
if (br_index < 0)
return -1;
int r = CTL_get_bridge_state(br_index, &uid_cfg, &uid_state);
if (r) {
fprintf(stderr, "Failed to get bridge state: %s\n",
CTL_error_explanation(r));
return -1;
}
#if 0
printf("Interface: %-7s (tag:%d) State: ",
uid_state.vlan_name, (int)uid_state.vlan_id);
#else
printf("Bridge: %-7s State:",
uid_state.vlan_name);
#endif
switch (uid_state.stp_enabled) {
case STP_ENABLED:
printf("enabled\n");
break;
case STP_DISABLED:
printf("disabled\n");
break;
default:
printf("unknown\n");
return 0;
}
printf("BridgeId: ");
print_bridge_id(&uid_state.bridge_id, 0);
printf(" Bridge Proirity: %lu (0x%lX)\n",
(unsigned long)uid_state.bridge_id.prio,
(unsigned long)uid_state.bridge_id.prio);
if (uid_cfg.force_version < 2)
printf("Force Version: stp\n");
printf("Designated Root: ");
print_bridge_id(&uid_state.designated_root, 1);
if (uid_state.root_port) {
printf("Root Port: %04lx",
(unsigned long)uid_state.root_port);
// CLI_out_port_id (uid_state.root_port & 0xfff, False);
// printf("not implemented"); // XXX
printf(", Root Cost: %-lu\n",
(unsigned long)uid_state.root_path_cost);
} else {
printf("Root Port: none\n");
}
if (uid_state.Topo_Change)
printf("Topology Change Count: %lu\n",
uid_state.Topo_Change_Count);
else
printf("Time Since Topology Change: %lu\n",
uid_state.timeSince_Topo_Change);
printf("Max Age: %2d Bridge Max Age: %-2d\n",
(int)uid_state.max_age, (int)uid_cfg.max_age);
printf("Hello Time: %2d Bridge Hello Time: %-2d\n",
(int)uid_state.hello_time, (int)uid_cfg.hello_time);
printf("Forward Delay: %2d Bridge Forward Delay: %-2d\n",
(int)uid_state.forward_delay, (int)uid_cfg.forward_delay);
printf("Hold Time: %2d\n", (int)uid_cfg.hold_time);
return 0;
}
#define SYSFS_PATH_MAX 256
#define SYSFS_CLASS_NET "/sys/class/net"
static int isbridge(const struct dirent *entry)
{
char path[SYSFS_PATH_MAX];
struct stat st;
snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge",
entry->d_name);
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
static int cmd_showbridge(int argc, char *const *argv)
{
int i, count = 0;
int r = 0;
struct dirent **namelist;
if (argc > 1) {
count = argc - 1;
} else {
count =
scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort);
if (count < 0) {
fprintf(stderr, "Error getting list of all bridges\n");
return -1;
}
}
for (i = 0; i < count; i++) {
const char *name;
if (argc > 1)
name = argv[i + 1];
else
name = namelist[i]->d_name;
int err = do_showbridge(name);
if (err)
r = err;
}
if (argc <= 1) {
for (i = 0; i < count; i++)
free(namelist[i]);
free(namelist);
}
return r;
}
int detail = 0;
static int do_showport(int br_index, const char *port_name,
UID_STP_STATE_T * uid_state)
{
UID_STP_PORT_STATE_T uid_port;
UID_STP_PORT_CFG_T uid_cfg;
int r = 0;
int port_index = get_index_die(port_name, "port", 0);
if (port_index < 0)
return -1;
memset(&uid_cfg, 0, sizeof(UID_STP_PORT_CFG_T));
r = CTL_get_port_state(br_index, port_index, &uid_cfg, &uid_port);
if (r) {
fprintf(stderr, "Failed to get port state for port %d: %s\n",
port_index, CTL_error_explanation(r));
return -1;
}
if (detail) {
printf("Stp Port ");
CLI_out_port_id(port_index, False);
#if 0
printf(": PortId: %04lx in vlan '%s' with tag %d:\n",
(unsigned long)uid_port.port_id, uid_state->vlan_name,
(int)uid_state->vlan_id);
#else
printf(": PortId: %04lx in Bridge '%s':\n",
(unsigned long)uid_port.port_id, uid_state->vlan_name);
#endif
printf("Priority: %-d\n",
(int)(uid_port.port_id >> 8));
printf("State: %-16s",
stp_state2str(uid_port.state, 1));
printf(" Uptime: %-9lu\n", uid_port.uptime);
printf("PortPathCost: admin: ");
if (ADMIN_PORT_PATH_COST_AUTO == uid_cfg.admin_port_path_cost)
printf("%-9s", "Auto");
else
printf("%-9lu", uid_cfg.admin_port_path_cost);
printf(" oper: %-9lu\n", uid_port.oper_port_path_cost);
printf("Point2Point: admin: ");
switch (uid_cfg.admin_point2point) {
case P2P_FORCE_TRUE:
printf("%-9s", "ForceYes");
break;
case P2P_FORCE_FALSE:
printf("%-9s", "ForceNo");
break;
case P2P_AUTO:
printf("%-9s", "Auto");
break;
}
printf(" oper: %-9s\n",
uid_port.oper_point2point ? "Yes" : "No");
printf("Edge: admin: %-9s oper: %-9s\n",
uid_cfg.admin_edge ? "Y" : "N",
uid_port.oper_edge ? "Y" : "N");
printf("Partner: oper: %-9s\n",
uid_port.oper_stp_neigb ? "Slow" : "Rapid");
if (' ' != uid_port.role) {
if ('-' != uid_port.role) {
printf("PathCost: %-lu\n",
(unsigned long)(uid_port.path_cost));
printf("Designated Root: ");
print_bridge_id(&uid_port.designated_root, 1);
printf("Designated Cost: %-ld\n",
(unsigned long)uid_port.designated_cost);
printf("Designated Bridge: ");
print_bridge_id(&uid_port.designated_bridge, 1);
printf("Designated Port: %-4lx\n\r",
(unsigned long)uid_port.designated_port);
}
printf("Role: ");
switch (uid_port.role) {
case 'A':
printf("Alternate\n");
break;
case 'B':
printf("Backup\n");
break;
case 'R':
printf("Root\n");
break;
case 'D':
printf("Designated\n");
break;
case '-':
printf("NonStp\n");
break;
default:
printf("Unknown(%c)\n", uid_port.role);
break;
}
if ('R' == uid_port.role || 'D' == uid_port.role) {
/* printf("Tc: %c ", uid_port.tc ? 'Y' : 'n'); */
printf("TcAck: %c ",
uid_port.top_change_ack ? 'Y' : 'N');
printf("TcWhile: %3d\n",
(int)uid_port.tcWhile);
}
}
if (UID_PORT_DISABLED == uid_port.state || '-' == uid_port.role) {
#if 0
printf("helloWhen: %3d ",
(int)uid_port.helloWhen);
printf("lnkWhile: %3d\n", (int)uid_port.lnkWhile);
printf("fdWhile: %3d\n", (int)uid_port.fdWhile);
#endif
} else if ('-' != uid_port.role) {
printf("fdWhile: %3d ", (int)uid_port.fdWhile);
printf("rcvdInfoWhile: %3d\n",
(int)uid_port.rcvdInfoWhile);
printf("rbWhile: %3d ", (int)uid_port.rbWhile);
printf("rrWhile: %3d\n", (int)uid_port.rrWhile);
#if 0
printf("mdelayWhile: %3d ",
(int)uid_port.mdelayWhile);
printf("lnkWhile: %3d\n", (int)uid_port.lnkWhile);
printf("helloWhen: %3d ",
(int)uid_port.helloWhen);
printf("txCount: %3d\n", (int)uid_port.txCount);
#endif
}
printf("RSTP BPDU rx: %lu\n",
(unsigned long)uid_port.rx_rstp_bpdu_cnt);
printf("CONFIG BPDU rx: %lu\n",
(unsigned long)uid_port.rx_cfg_bpdu_cnt);
printf("TCN BPDU rx: %lu\n",
(unsigned long)uid_port.rx_tcn_bpdu_cnt);
} else {
printf("%c%c%c ",
(uid_port.oper_point2point) ? ' ' : '*',
(uid_port.oper_edge) ? 'E' : ' ',
(uid_port.oper_stp_neigb) ? 's' : ' ');
CLI_out_port_id(port_index, False);
printf(" %04lx %3s ", (unsigned long)uid_port.port_id,
stp_state2str(uid_port.state, 0));
printf(" ");
print_bridge_id(&uid_port.designated_root, 0);
printf(" ");
print_bridge_id(&uid_port.designated_bridge, 0);
printf(" %4lx %c", (unsigned long)uid_port.designated_port,
uid_port.role);
printf("\n");
}
return 0;
}
static int not_dot_dotdot(const struct dirent *entry)
{
const char *n = entry->d_name;
return !(n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0)));
}
static int cmd_showport(int argc, char *const *argv)
{
UID_STP_STATE_T uid_state;
UID_STP_CFG_T uid_br_cfg;
int r = 0;
int br_index = get_index(argv[1], "bridge");
r = CTL_get_bridge_state(br_index, &uid_br_cfg, &uid_state);
if (r) {
fprintf(stderr, "Failed to get bridge state: %s\n",
CTL_error_explanation(r));
return -1;
}
int i, count = 0;
struct dirent **namelist;
if (argc > 2) {
count = argc - 2;
} else {
char buf[SYSFS_PATH_MAX];
snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%s/brif", argv[1]);
count = scandir(buf, &namelist, not_dot_dotdot, alphasort);
if (count < 0) {
fprintf(stderr,
"Error getting list of all ports of bridge %s\n",
argv[1]);
return -1;
}
}
for (i = 0; i < count; i++) {
const char *name;
if (argc > 2)
name = argv[i + 2];
else
name = namelist[i]->d_name;
int err = do_showport(br_index, name, &uid_state);
if (err)
r = err;
}
if (argc <= 2) {
for (i = 0; i < count; i++)
free(namelist[i]);
free(namelist);
}
return r;
}
static int cmd_showportdetail(int argc, char *const *argv)
{
detail = 1;
return cmd_showport(argc, argv);
}
unsigned int getuint(const char *s)
{
char *end;
long l;
l = strtoul(s, &end, 0);
if (*s == 0 || *end != 0 || l > INT_MAX) {
fprintf(stderr, "Invalid unsigned int arg %s\n", s);
exit(1);
}
return l;
}
int getenum(const char *s, const char *opt[])
{
int i;
for (i = 0; opt[i] != NULL; i++)
if (strcmp(s, opt[i]) == 0)
return i;
fprintf(stderr, "Invalid argument %s: expecting one of ", s);
for (i = 0; opt[i] != NULL; i++)
fprintf(stderr, "%s%s", opt[i], (opt[i + 1] ? ", " : "\n"));
exit(1);
}
int getyesno(const char *s, const char *yes, const char *no)
{
/* Reverse yes and no so error message looks more normal */
const char *opt[] = { yes, no, NULL };
return 1 - getenum(s, opt);
}
static int set_bridge_cfg_value(int br_index, unsigned long value,
unsigned long val_mask)
{
UID_STP_CFG_T uid_cfg;
char *val_name;
int rc;
uid_cfg.field_mask = val_mask;
switch (val_mask) {
case BR_CFG_STATE:
uid_cfg.stp_enabled = value;
val_name = "state";
break;
case BR_CFG_PRIO:
uid_cfg.bridge_priority = value;
val_name = "priority";
break;
case BR_CFG_AGE:
uid_cfg.max_age = value;
val_name = "max_age";
break;
case BR_CFG_HELLO:
uid_cfg.hello_time = value;
val_name = "hello_time";
break;
case BR_CFG_DELAY:
uid_cfg.forward_delay = value;
val_name = "forward_delay";
break;
case BR_CFG_FORCE_VER:
uid_cfg.force_version = value;
val_name = "force_version";
break;
case BR_CFG_AGE_MODE:
case BR_CFG_AGE_TIME:
default:
printf("Invalid value mask 0X%lx\n", val_mask);
return -1;
break;
}
rc = CTL_set_bridge_config(br_index, &uid_cfg);
if (0 != rc) {
printf("Can't change rstp bridge %s:%s\n", val_name,
STP_IN_get_error_explanation(rc));
return -1;
}
return 0;
}
static int cmd_setbridgestate(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
return set_bridge_cfg_value(br_index,
getyesno(argv[2], "on", "off"),
BR_CFG_STATE);
}
static int cmd_setbridgeprio(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_PRIO);
}
static int cmd_setbridgemaxage(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_AGE);
}
static int cmd_setbridgehello(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_HELLO);
}
static int cmd_setbridgefdelay(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_DELAY);
}
static int cmd_setbridgeforcevers(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
return set_bridge_cfg_value(br_index,
2 * getyesno(argv[2], "normal", "slow"),
BR_CFG_FORCE_VER);
}
static int
set_port_cfg_value(int br_index, int port_index,
unsigned long value, unsigned long val_mask)
{
UID_STP_PORT_CFG_T uid_cfg;
int rc;
char *val_name;
BitmapClear(&uid_cfg.port_bmp);
uid_cfg.field_mask = val_mask;
switch (val_mask) {
case PT_CFG_MCHECK:
val_name = "mcheck";
break;
case PT_CFG_COST:
uid_cfg.admin_port_path_cost = value;
val_name = "path cost";
break;
case PT_CFG_PRIO:
uid_cfg.port_priority = value;
val_name = "priority";
break;
case PT_CFG_P2P:
uid_cfg.admin_point2point = (ADMIN_P2P_T) value;
val_name = "p2p flag";
break;
case PT_CFG_EDGE:
uid_cfg.admin_edge = value;
val_name = "adminEdge";
break;
case PT_CFG_NON_STP:
uid_cfg.admin_non_stp = value;
val_name = "adminNonStp";
break;
#ifdef STP_DBG
case PT_CFG_DBG_SKIP_TX:
uid_cfg.skip_tx = value;
val_name = "skip tx";
break;
case PT_CFG_DBG_SKIP_RX:
uid_cfg.skip_rx = value;
val_name = "skip rx";
break;
#endif
case PT_CFG_STATE:
default:
printf("Invalid value mask 0X%lx\n", val_mask);
return -1;
}
rc = CTL_set_port_config(br_index, port_index, &uid_cfg);
if (0 != rc) {
printf("can't change rstp port[s] %s: %s\n",
val_name, STP_IN_get_error_explanation(rc));
return -1;
}
return 0;
}
static int cmd_setportprio(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
int port_index = get_index(argv[2], "port");
return set_port_cfg_value(br_index, port_index,
getuint(argv[3]), PT_CFG_PRIO);
}
static int cmd_setportpathcost(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
int port_index = get_index(argv[2], "port");
return set_port_cfg_value(br_index, port_index,
getuint(argv[3]), PT_CFG_COST);
}
static int cmd_setportedge(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
int port_index = get_index(argv[2], "port");
return set_port_cfg_value(br_index, port_index,
getyesno(argv[3], "yes", "no"), PT_CFG_EDGE);
}
static int cmd_setportnonstp(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
int port_index = get_index(argv[2], "port");
return set_port_cfg_value(br_index, port_index,
getyesno(argv[3], "yes", "no"),
PT_CFG_NON_STP);
}
static int cmd_setportp2p(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
int port_index = get_index(argv[2], "port");
const char *opts[] = { "yes", "no", "auto", NULL };
int vals[] = { P2P_FORCE_TRUE, P2P_FORCE_FALSE, P2P_AUTO };
return set_port_cfg_value(br_index, port_index,
vals[getenum(argv[3], opts)], PT_CFG_P2P);
}
static int cmd_portmcheck(int argc, char *const *argv)
{
int br_index = get_index(argv[1], "bridge");
int port_index = get_index(argv[2], "port");
return set_port_cfg_value(br_index, port_index, 0, PT_CFG_MCHECK);
}
static int cmd_debuglevel(int argc, char *const *argv)
{
return CTL_set_debug_level(getuint(argv[1]));
}
struct command {
int nargs;
int optargs;
const char *name;
int (*func) (int argc, char *const *argv);
const char *help;
};
static const struct command commands[] = {
{0, 32, "showbridge", cmd_showbridge,
"[<bridge> ... ]\t\tshow bridge state"},
{1, 32, "showport", cmd_showport,
"<bridge> [<port> ... ]\tshow port state"},
{1, 32, "showportdetail", cmd_showportdetail,
"<bridge> [<port> ... ]\tshow port state (detail)"},
{2, 0, "rstp", cmd_rstp,
"<bridge> {on|off}\tenable/disable rstpd control"},
{2, 0, "setbridgestate", cmd_setbridgestate,
"<bridge> {on|off}\tstart/stop rstp (when enabled)"},
{2, 0, "setbridgeprio", cmd_setbridgeprio,
"<bridge> <priority>\tset bridge priority (0-61440)"},
{2, 0, "sethello", cmd_setbridgehello,
"<bridge> <hellotime>\tset bridge hello time (1-10)"},
{2, 0, "setmaxage", cmd_setbridgemaxage,
"<bridge> <maxage>\tset bridge max age (6-40)"},
{2, 0, "setfdelay", cmd_setbridgefdelay,
"<bridge> <fwd_delay>\tset bridge forward delay (4-30)"},
{2, 0, "setforcevers", cmd_setbridgeforcevers,
"<bridge> {normal|slow}\tnormal RSTP or force to STP"},
{3, 0, "setportprio", cmd_setportprio,
"<bridge> <port> <priority>\tset port priority (0-240)"},
{3, 0, "setportpathcost", cmd_setportpathcost,
"<bridge> <port> <cost>\tset port path cost"},
{3, 0, "setportedge", cmd_setportedge,
"<bridge> <port> {yes|no}\tconfigure if it is an edge port"},
{3, 0, "setportnonstp", cmd_setportnonstp,
"<bridge> <port> {yes|no}\tdisable STP for the port"},
{3, 0, "setportp2p", cmd_setportp2p,
"<bridge> <port> {yes|no|auto}\tset whether p2p connection"},
{2, 0, "portmcheck", cmd_portmcheck,
"<bridge> <port>\ttry to get back from STP to RSTP mode"},
{1, 0, "debuglevel", cmd_debuglevel, "<level>\t\tLevel of verbosity"},
};
const struct command *command_lookup(const char *cmd)
{
int i;
for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
if (!strcmp(cmd, commands[i].name))
return &commands[i];
}
return NULL;
}
void command_helpall(void)
{
int i;
for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
printf("\t%-10s\t%s\n", commands[i].name, commands[i].help);
}
}
static void help()
{
printf("Usage: rstpctl [commands]\n");
printf("commands:\n");
command_helpall();
}
#define PACKAGE_VERSION2(v, b) "rstp, " #v "-" #b
#define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b)
int main(int argc, char *const *argv)
{
const struct command *cmd;
int f;
static const struct option options[] = {
{.name = "help",.val = 'h'},
{.name = "version",.val = 'V'},
{0}
};
while ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF)
switch (f) {
case 'h':
help();
return 0;
case 'V':
printf("%s\n", PACKAGE_VERSION(VERSION, BUILD));
return 0;
default:
fprintf(stderr, "Unknown option '%c'\n", f);
goto help;
}
if (argc == optind)
goto help;
if (ctl_client_init()) {
fprintf(stderr, "can't setup control connection\n");
return 1;
}
argc -= optind;
argv += optind;
if ((cmd = command_lookup(argv[0])) == NULL) {
fprintf(stderr, "never heard of command [%s]\n", argv[0]);
goto help;
}
if (argc < cmd->nargs + 1 || argc > cmd->nargs + cmd->optargs + 1) {
printf("Incorrect number of arguments for command\n");
printf("Usage: rstpctl %s %s\n", cmd->name, cmd->help);
return 1;
}
return cmd->func(argc, argv);
help:
help();
return 1;
}