/* * teamdctl.c - Network team device daemon control tool * Copyright (C) 2013-2015 Jiri Pirko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "config.h" enum verbosity_level { VERB1, VERB2, VERB3, VERB4, }; static bool g_oneline = false; #define DEFAULT_VERB VERB1 static int g_verbosity = DEFAULT_VERB; static int g_indent_level = 0; #define INDENT_STR_STEP 2 #define INDENT_STR_MAXLEN 32 static char g_indent_str[INDENT_STR_MAXLEN + 1] = ""; static void pr_out_indent_inc(void) { if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN) return; g_indent_level += INDENT_STR_STEP; memset(g_indent_str, ' ', sizeof(g_indent_str)); g_indent_str[g_indent_level] = '\0'; } static void pr_out_indent_dec(void) { if (g_indent_level - INDENT_STR_STEP < 0) return; g_indent_level -= INDENT_STR_STEP; g_indent_str[g_indent_level] = '\0'; } #define pr_err(args...) fprintf(stderr, ##args) #define pr_outx(verb_level, args...) \ if (verb_level <= g_verbosity) { \ fprintf(stdout, "%s", g_indent_str); \ fprintf(stdout, ##args); \ } #define pr_out(args...) pr_outx(DEFAULT_VERB, ##args) #define pr_out2(args...) pr_outx(VERB2, ##args) #define pr_out3(args...) pr_outx(VERB3, ##args) #define pr_out4(args...) pr_outx(VERB4, ##args) static int __jsonload(json_t **pjson, char *inputstrjson) { json_t *json; json_error_t jerror; json = json_loads(inputstrjson, JSON_REJECT_DUPLICATES, &jerror); if (!json) { pr_err("Failed to parse JSON dump.\n"); return -EINVAL; } *pjson = json; return 0; } static int __jsondump(json_t *json) { char *dump; int indent = g_oneline ? 0 : 4; dump = json_dumps(json, JSON_INDENT(indent) | JSON_ENSURE_ASCII | JSON_SORT_KEYS); if (!dump) { pr_err("Failed to get JSON dump.\n"); return -ENOMEM; } pr_out("%s\n", dump); free(dump); return 0; } static int __jsonloaddump(char *inputstrjson) { int err; json_t *json; err = __jsonload(&json, inputstrjson); if (err) return err; err = __jsondump(json); json_decref(json); return err; } static int jsonsimpledump_process_reply(char *reply) { return __jsonloaddump(reply); } static int noportsdump_json_process(char *dump) { int err; json_t *json; err = __jsonload(&json, dump); if (err) return err; json_object_del(json, "ports"); err = __jsondump(json); json_decref(json); return err; } static int jsonnoportsdump_process_reply(char *reply) { return noportsdump_json_process(reply); } #define boolyesno(val) (val ? "yes" : "no") #define boolupdown(val) (val ? "up" : "down") static int stateview_json_setup_process(char **prunner_name, json_t *dump_json) { int err; char *runner_name; char *kernel_team_mode_name; int dbus_enabled; int zmq_enabled; int debug_level; int daemonized; int pid; char *pid_file; pr_out("setup:\n"); err = json_unpack(dump_json, "{s:{s:s, s:s, s:b, s:b, s:i, s:b, s:i, s:s}}", "setup", "runner_name", &runner_name, "kernel_team_mode_name", &kernel_team_mode_name, "dbus_enabled", &dbus_enabled, "zmq_enabled", &zmq_enabled, "debug_level", &debug_level, "daemonized", &daemonized, "pid", &pid, "pid_file", &pid_file); if (err) { pr_err("Failed to parse JSON setup dump.\n"); return -EINVAL; } pr_out_indent_inc(); pr_out("runner: %s\n", runner_name); pr_out2("kernel team mode: %s\n", kernel_team_mode_name); pr_out2("D-BUS enabled: %s\n", boolyesno(dbus_enabled)); pr_out2("ZeroMQ enabled: %s\n", boolyesno(zmq_enabled)); pr_out2("debug level: %d\n", debug_level); pr_out2("daemonized: %s\n", boolyesno(daemonized)); pr_out2("PID: %d\n", pid); pr_out2("PID file: %s\n", pid_file); pr_out_indent_dec(); *prunner_name = runner_name; return 0; } static int stateview_json_link_watch_info_process(char *lw_name, json_t *lw_json) { int err; if (!strcmp(lw_name, "ethtool")) { int delay_up; int delay_down; err = json_unpack(lw_json, "{s:i, s:i}", "delay_up", &delay_up, "delay_down", &delay_down); if (err) { pr_err("Failed to parse JSON ethtool link watch dump.\n"); return -EINVAL; } pr_out2("link up delay: %d\n", delay_up); pr_out2("link down delay: %d\n", delay_down); } else if (!strcmp(lw_name, "arp_ping")) { char *source_host; char *target_host; int interval; int init_wait; int validate_active; int validate_inactive; int send_always; int missed_max; int missed; err = json_unpack(lw_json, "{s:s, s:s, s:i, s:i, s:b, s:b, s:b, s:i, s:i}", "source_host", &source_host, "target_host", &target_host, "interval", &interval, "init_wait", &init_wait, "validate_active", &validate_active, "validate_inactive", &validate_inactive, "send_always", &send_always, "missed_max", &missed_max, "missed", &missed); if (err) { pr_err("Failed to parse JSON arp_ping link watch dump.\n"); return -EINVAL; } pr_out2("source host: %s\n", source_host); pr_out2("target host: %s\n", target_host); pr_out2("interval: %d\n", interval); pr_out2("missed packets: %d/%d\n", missed, missed_max); pr_out2("validate_active: %s\n", boolyesno(validate_active)); pr_out2("validate_inactive: %s\n", boolyesno(validate_inactive)); pr_out2("send_always: %s\n", boolyesno(send_always)); pr_out2("initial wait: %d\n", init_wait); } else if (!strcmp(lw_name, "nsna_ping")) { char *target_host; int interval; int init_wait; int missed_max; int missed; err = json_unpack(lw_json, "{s:s, s:i, s:i, s:i, s:i}", "target_host", &target_host, "interval", &interval, "init_wait", &init_wait, "missed_max", &missed_max, "missed", &missed); if (err) { pr_err("Failed to parse JSON nsna_ping link watch dump.\n"); return -EINVAL; } pr_out2("target host: %s\n", target_host); pr_out2("interval: %d\n", interval); pr_out2("missed packets: %d/%d\n", missed, missed_max); pr_out2("initial wait: %d\n", init_wait); } else if (!strcmp(lw_name, "tipc")) { char *tipc_bearer; err = json_unpack(lw_json, "{s:s}", "tipc_bearer", &tipc_bearer); if (err) { pr_err("Failed to parse JSON tipc_bearer link watch dump\n"); return -EINVAL; } pr_out2("tipc bearer: %s\n", tipc_bearer); } else { pr_err("Failed to parse JSON unknown link watch info dump.\n"); return -EINVAL; } return 0; } static int stateview_json_port_link_watches_list_process(json_t *port_link_watches_json) { int err; int up; int down_count; json_t *lw_list_json; json_t *lw_json; char *lw_name; const char *key; err = json_unpack(port_link_watches_json, "{s:o}", "list", &lw_list_json); if (err) return 0; json_object_foreach(lw_list_json, key, lw_json) { err = json_unpack(lw_json, "{s:b, s:s, s:i}", "up", &up, "name", &lw_name, "down_count", &down_count); if (err) { pr_err("Failed to parse JSON port link watch dump.\n"); return -EINVAL; } pr_out("instance[%s]:\n", key); pr_out_indent_inc(); pr_out("name: %s\n", lw_name); pr_out("link: %s\n", boolupdown(up)); pr_out("down count: %d\n", down_count); err = stateview_json_link_watch_info_process(lw_name, lw_json); if (err) return err; pr_out_indent_dec(); } return 0; } static int stateview_json_port_link_watches_process(json_t *port_link_watches_json) { int err; int up; err = json_unpack(port_link_watches_json, "{s:b}", "up", &up); if (err) { pr_err("Failed to parse JSON port link watches dump.\n"); return -EINVAL; } pr_out("link watches:\n"); pr_out_indent_inc(); pr_out("link summary: %s\n", boolupdown(up)); err = stateview_json_port_link_watches_list_process(port_link_watches_json); if (err) return err; pr_out_indent_dec(); return 0; } static int stateview_json_lacpdu_process(json_t *lacpdu_json) { int err; int system_priority; char *system; int key; int port_priority; int port; int state; err = json_unpack(lacpdu_json, "{s:i, s:s, s:i, s:i, s:i, s:i}", "system_priority", &system_priority, "system", &system, "key", &key, "port_priority", &port_priority, "port", &port, "state", &state); if (err) { pr_err("Failed to parse JSON port runner lacpdu dump.\n"); return -EINVAL; } pr_out2("system priority: %d\n", system_priority); pr_out2("system: %s\n", system); pr_out2("key: %d\n", key); pr_out2("port_priority: %d\n", port_priority); pr_out2("port: %d\n", port); pr_out2("state: 0x%x\n", state); return 0; } static int stateview_json_port_runner_process(char *runner_name, json_t *port_json) { int err; if (!strcmp(runner_name, "lacp")) { int selected; int aggregator_id; int aggregator_selected; char *state; int key; int prio; json_t *actor_json; json_t *partner_json; pr_out("runner:\n"); err = json_unpack(port_json, "{s:{s:b, s:{s:i, s:b}, s:s, s:i, s:i, s:o, s:o}}", "runner", "selected", &selected, "aggregator", "id", &aggregator_id, "selected", &aggregator_selected, "state", &state, "key", &key, "prio", &prio, "actor_lacpdu_info", &actor_json, "partner_lacpdu_info", &partner_json); if (err) { pr_err("Failed to parse JSON port runner dump.\n"); return -EINVAL; } pr_out_indent_inc(); pr_out("aggregator ID: %d%s\n", aggregator_id, aggregator_selected ? ", Selected" : ""); pr_out("selected: %s\n", boolyesno(selected)); pr_out("state: %s\n", state); pr_out2("key: %d\n", key); pr_out2("priority: %d\n", prio); pr_out2("actor LACPDU info:\n"); pr_out_indent_inc(); err = stateview_json_lacpdu_process(actor_json); if (err) return err; pr_out_indent_dec(); pr_out2("partner LACPDU info:\n"); pr_out_indent_inc(); err = stateview_json_lacpdu_process(partner_json); if (err) return err; pr_out_indent_dec(); pr_out_indent_dec(); } return 0; } static int stateview_json_port_process(char *runner_name, const char *port_name, json_t *port_json) { int err; char *dev_addr; int dev_addr_len; int ifindex; char *ifname; char *duplex; int speed; int up; json_t *port_link_watches_json; err = json_unpack(port_json, "{s:{s:s, s:i, s:i, s:s}, s:{s:s, s:i, s:b}, s:o}", "ifinfo", "dev_addr", &dev_addr, "dev_addr_len", &dev_addr_len, "ifindex", &ifindex, "ifname", &ifname, "link", "duplex", &duplex, "speed", &speed, "up", &up, "link_watches", &port_link_watches_json); if (err) { pr_err("Failed to parse JSON port dump.\n"); return -EINVAL; } pr_out("%s\n", port_name); pr_out_indent_inc(); pr_out2("ifindex: %d\n", ifindex); pr_out2("addr: %s\n", dev_addr); pr_out2("ethtool link: %dmbit/%sduplex/%s\n", speed, duplex, boolupdown(up)); err = stateview_json_port_link_watches_process(port_link_watches_json); if (err) goto err_out; err = stateview_json_port_runner_process(runner_name, port_json); pr_out_indent_dec(); err_out: return err; } static int stateview_json_ports_process(char *runner_name, json_t *dump_json) { int err; json_t *ports_json; json_t *iter; err = json_unpack(dump_json, "{s:o}", "ports", &ports_json); if (err) return 0; pr_out("ports:\n"); for (iter = json_object_iter(ports_json); iter; iter = json_object_iter_next(ports_json, iter)) { const char *port_name = json_object_iter_key(iter); json_t *port_json = json_object_iter_value(iter); pr_out_indent_inc(); err = stateview_json_port_process(runner_name, port_name, port_json); if (err) return err; pr_out_indent_dec(); } return 0; } static int stateview_json_runner_process(char *runner_name, json_t *json) { int err; if (!strcmp(runner_name, "activebackup")) { char *active_port; pr_out("runner:\n"); err = json_unpack(json, "{s:{s:s}}", "runner", "active_port", &active_port); if (err) { pr_err("Failed to parse JSON runner dump.\n"); return -EINVAL; } pr_out_indent_inc(); pr_out("active port: %s\n", active_port); pr_out_indent_dec(); } else if (!strcmp(runner_name, "lacp")) { int active; int sys_prio; int fast_rate; pr_out("runner:\n"); err = json_unpack(json, "{s:{s:b, s:i, s:b}}", "runner", "active", &active, "sys_prio", &sys_prio, "fast_rate", &fast_rate); if (err) { pr_err("Failed to parse JSON runner dump.\n"); return -EINVAL; } pr_out_indent_inc(); pr_out("active: %s\n", boolyesno(active)); pr_out("fast rate: %s\n", boolyesno(fast_rate)); pr_out2("system priority: %d\n", sys_prio); pr_out_indent_dec(); } return 0; } static int stateview_json_process(char *dump) { int err; char *runner_name; json_t *dump_json; err = __jsonload(&dump_json, dump); if (err) return err; err = stateview_json_setup_process(&runner_name, dump_json); if (err) goto free_json; err = stateview_json_ports_process(runner_name, dump_json); if (err) goto free_json; err = stateview_json_runner_process(runner_name, dump_json); free_json: json_decref(dump_json); return err; } static int stateview_process_reply(char *reply) { return stateview_json_process(reply); } static int state_json_port_present(char *dump, const char *port_devname) { json_t *dump_json; json_t *port_json; int err; err = __jsonload(&dump_json, dump); if (err) return err; err = json_unpack(dump_json, "{s:{s:o}}", "ports", port_devname, &port_json); if (err) err = -ENODEV; json_decref(dump_json); return err; } static int call_method_config_jsonsimpledump(struct teamdctl *tdc, int argc, char **argv) { return jsonsimpledump_process_reply(teamdctl_config_get_raw(tdc)); } static int call_method_config_jsonnoportsdump(struct teamdctl *tdc, int argc, char **argv) { return jsonnoportsdump_process_reply(teamdctl_config_get_raw(tdc)); } static int call_method_config_actual_jsonsimpledump(struct teamdctl *tdc, int argc, char **argv) { return jsonsimpledump_process_reply(teamdctl_config_actual_get_raw(tdc)); } static int call_method_state_jsonsimpledump(struct teamdctl *tdc, int argc, char **argv) { return jsonsimpledump_process_reply(teamdctl_state_get_raw(tdc)); } static int call_method_state_stateview(struct teamdctl *tdc, int argc, char **argv) { return stateview_process_reply(teamdctl_state_get_raw(tdc)); } static int call_method_port_add(struct teamdctl *tdc, int argc, char **argv) { return teamdctl_port_add(tdc, argv[0]); } static int call_method_port_remove(struct teamdctl *tdc, int argc, char **argv) { return teamdctl_port_remove(tdc, argv[0]); } static int call_method_port_present(struct teamdctl *tdc, int argc, char **argv) { return state_json_port_present(teamdctl_state_get_raw(tdc), argv[0]); } static int call_method_port_config_update(struct teamdctl *tdc, int argc, char **argv) { return teamdctl_port_config_update_raw(tdc, argv[0], argv[1]); } static int call_method_port_config_dump(struct teamdctl *tdc, int argc, char **argv) { int err; char *cfg; err = teamdctl_port_config_get_raw_direct(tdc, argv[0], &cfg); if (err) return err; return jsonsimpledump_process_reply(cfg); } static int call_method_state_item_get(struct teamdctl *tdc, int argc, char **argv) { char *reply; int err; err = teamdctl_state_item_value_get(tdc, argv[0], &reply); if (err) return err; pr_out("%s\n", reply); free(reply); return 0; } static int call_method_state_item_set(struct teamdctl *tdc, int argc, char **argv) { return teamdctl_state_item_value_set(tdc, argv[0], argv[1]); } enum id_command_type { ID_CMDTYPE_NONE = 0, ID_CMDTYPE_C, ID_CMDTYPE_C_D, ID_CMDTYPE_C_D_N, ID_CMDTYPE_C_D_A, ID_CMDTYPE_S, ID_CMDTYPE_S_D, ID_CMDTYPE_S_V, ID_CMDTYPE_S_I, ID_CMDTYPE_S_I_G, ID_CMDTYPE_S_I_S, ID_CMDTYPE_P, ID_CMDTYPE_P_A, ID_CMDTYPE_P_R, ID_CMDTYPE_P_P, ID_CMDTYPE_P_C, ID_CMDTYPE_P_C_U, ID_CMDTYPE_P_C_D, }; typedef int (*process_reply_t)(int argc, char **argv, char *reply); typedef int (*call_method_t)(struct teamdctl *tdc, int argc, char **argv); #define COMMAND_PARAM_MAX_CNT 8 struct command_type { enum id_command_type id; enum id_command_type parent_id; char *name; char *params[COMMAND_PARAM_MAX_CNT]; call_method_t call_method; process_reply_t process_reply; size_t priv_size; }; static struct command_type command_types[] = { { .id = ID_CMDTYPE_C, .name = "config", }, { .id = ID_CMDTYPE_C_D, .parent_id = ID_CMDTYPE_C, .name = "dump", .call_method = call_method_config_jsonsimpledump, }, { .id = ID_CMDTYPE_C_D_N, .parent_id = ID_CMDTYPE_C_D, .name = "noports", .call_method = call_method_config_jsonnoportsdump, }, { .id = ID_CMDTYPE_C_D_A, .parent_id = ID_CMDTYPE_C_D, .name = "actual", .call_method = call_method_config_actual_jsonsimpledump, }, { .id = ID_CMDTYPE_S, .name = "state", .call_method = call_method_state_stateview, }, { .id = ID_CMDTYPE_S_D, .parent_id = ID_CMDTYPE_S, .name = "dump", .call_method = call_method_state_jsonsimpledump, }, { .id = ID_CMDTYPE_S_V, .parent_id = ID_CMDTYPE_S, .name = "view", .call_method = call_method_state_stateview, }, { .id = ID_CMDTYPE_S_I, .parent_id = ID_CMDTYPE_S, .name = "item", }, { .id = ID_CMDTYPE_S_I_G, .parent_id = ID_CMDTYPE_S_I, .name = "get", .call_method = call_method_state_item_get, .params = {"ITEMPATH"}, }, { .id = ID_CMDTYPE_S_I_S, .parent_id = ID_CMDTYPE_S_I, .name = "set", .call_method = call_method_state_item_set, .params = {"ITEMPATH", "VALUE"}, }, { .id = ID_CMDTYPE_P, .name = "port", }, { .id = ID_CMDTYPE_P_A, .parent_id = ID_CMDTYPE_P, .name = "add", .call_method = call_method_port_add, .params = {"PORTDEV"}, }, { .id = ID_CMDTYPE_P_R, .parent_id = ID_CMDTYPE_P, .name = "remove", .call_method = call_method_port_remove, .params = {"PORTDEV"}, }, { .id = ID_CMDTYPE_P_P, .parent_id = ID_CMDTYPE_P, .name = "present", .call_method = call_method_port_present, .params = {"PORTDEV"}, }, { .id = ID_CMDTYPE_P_C, .parent_id = ID_CMDTYPE_P, .name = "config", }, { .id = ID_CMDTYPE_P_C_U, .parent_id = ID_CMDTYPE_P_C, .name = "update", .call_method = call_method_port_config_update, .params = {"PORTDEV", "PORTCONFIG"}, }, { .id = ID_CMDTYPE_P_C_D, .parent_id = ID_CMDTYPE_P_C, .name = "dump", .call_method = call_method_port_config_dump, .params = {"PORTDEV"}, }, }; #define COMMAND_TYPE_COUNT ARRAY_SIZE(command_types) static bool __cmd_executable(struct command_type *command_type) { return command_type->call_method; } static int __cmd_param_cnt(struct command_type *command_type) { int i = 0; while (command_type->params[i]) i++; return i; } static struct command_type *__get_cmd_by_parent(char *cmd_name, enum id_command_type parent_id) { int i; for (i = 0; i < COMMAND_TYPE_COUNT; i++) { if (!strncmp(command_types[i].name, cmd_name, strlen(cmd_name)) && command_types[i].parent_id == parent_id) return &command_types[i]; } return NULL; } static struct command_type *__get_cmd_by_id(enum id_command_type id) { int i; for (i = 0; i < COMMAND_TYPE_COUNT; i++) { if (command_types[i].id == id) return &command_types[i]; } return NULL; } static int find_command(struct command_type **pcommand_type, int *argc, char ***argv) { char *cmd_name; enum id_command_type parent_id = ID_CMDTYPE_NONE; struct command_type *command_type; while (1) { if (!*argc) { pr_err("None or incomplete command\n"); return -EINVAL; } cmd_name = *argv[0]; (*argc)--; (*argv)++; command_type = __get_cmd_by_parent(cmd_name, parent_id); if (!command_type) { pr_err("Unknown command \"%s\".\n", cmd_name); return -EINVAL; } if (__cmd_executable(command_type) && __cmd_param_cnt(command_type) >= *argc) { *pcommand_type = command_type; return 0; } parent_id = command_type->id; } } static int check_command_params(struct command_type *command_type, int argc, char **argv) { int i = 0; while (command_type->params[i]) { if (i == argc) { pr_err("Command line parameter \"%s\" expected.\n", command_type->params[i]); return -EINVAL; } i++; } return 0; } static int check_team_devname(char *team_devname) { int err; uint32_t ifindex = 0; /* gcc needs this initialized */ err = ifname2ifindex(&ifindex, team_devname); if (err) { pr_err("Device \"%s\" - failed to get interface index (%s)\n", team_devname, strerror(-err)); return err; } if (!ifindex) { pr_err("Device \"%s\" does not exist\n", team_devname); return -ENODEV; } return 0; } static int check_teamd_team_devname(struct teamdctl *tdc, const char *team_devname) { json_t *dump_json; char *devname; int err; err = __jsonload(&dump_json, teamdctl_config_get_raw(tdc)); if (err) return err; err = json_unpack(dump_json, "{s:s}", "device", &devname); if (err) { pr_err("Failed to parse device name from config.\n"); err = -EINVAL; goto free_json; } if (strcmp(team_devname, devname)) { pr_err("Unable to access to %s through connected teamd daemon because it controls %s.\n", team_devname, devname); err = -EINVAL; goto free_json; } free_json: json_decref(dump_json); return err; } static int call_command(struct teamdctl *tdc, int argc, char **argv, struct command_type *command_type) { return command_type->call_method(tdc, argc, argv); } static void print_cmd(struct command_type *command_type) { if (command_type->parent_id != ID_CMDTYPE_NONE) { print_cmd(__get_cmd_by_id(command_type->parent_id)); pr_out(" "); } pr_out("%s", command_type->name); } static void print_help(const char *argv0) { int i, j; struct command_type *command_type; pr_out("%s [options] teamdevname command [command args]\n" " -h --help Show this help\n" " -v --verbose Increase output verbosity\n" " -o --oneline Force output to one line if possible\n" " -D --force-dbus Force to use D-Bus interface\n" " -Z --force-zmq=ADDRESS Force to use ZeroMQ interface [-Z[Address]]\n" " -U --force-usock Force to use UNIX domain socket interface\n", argv0); pr_out("Commands:\n"); for (i = 0; i < COMMAND_TYPE_COUNT; i++) { command_type = &command_types[i]; if (!__cmd_executable(command_type)) continue; pr_out(" "); print_cmd(command_type); for (j = 0; command_type->params[j]; j++) pr_out(" %s", command_type->params[j]); pr_out("\n"); } } int main(int argc, char **argv) { char *argv0 = argv[0]; char *team_devname; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "oneline", no_argument, NULL, 'o' }, { "force-dbus", no_argument, NULL, 'D' }, { "force-zmq", required_argument, NULL, 'Z' }, { "force-usock", no_argument, NULL, 'U' }, { NULL, 0, NULL, 0 } }; int opt; int err; struct command_type *command_type; struct teamdctl *tdc; int ret; char *addr = NULL; bool force_dbus = false; bool force_zmq = false; bool force_usock = false; while ((opt = getopt_long(argc, argv, "hvoDZ:U", long_options, NULL)) >= 0) { switch(opt) { case 'h': print_help(argv0); return EXIT_SUCCESS; case 'v': g_verbosity++; break; case 'o': g_oneline = true; break; case 'D': #ifndef ENABLE_DBUS fprintf(stderr, "D-Bus support is not compiled-in\n"); return EXIT_FAILURE; #else force_dbus = true; #endif break; case 'Z': #ifndef ENABLE_ZMQ fprintf(stderr, "ZeroMQ support is not compiled-in\n"); return EXIT_FAILURE; #else force_zmq = true; addr = optarg; #endif break; case 'U': force_usock = true; break; case '?': pr_err("unknown option.\n"); print_help(argv0); return EXIT_FAILURE; default: pr_err("unknown option \"%c\".\n", opt); print_help(argv0); return EXIT_FAILURE; } } if ((force_usock && force_dbus) || (force_usock && force_zmq) || (force_dbus && force_zmq)) { pr_err("Only one interface could be forced at a time (UNIX domain socket, D-Bus, ZMQ).\n"); print_help(argv0); return EXIT_FAILURE; } if (optind >= argc) { pr_err("No team device specified.\n"); print_help(argv0); return EXIT_FAILURE; } argv += optind; team_devname = *argv++; argc -= optind + 1; err = find_command(&command_type, &argc, &argv); if (err) { print_help(argv0); return EXIT_FAILURE; } err = check_command_params(command_type, argc, argv); if (err) { print_help(argv0); return EXIT_FAILURE; } err = check_team_devname(team_devname); if (err) return EXIT_FAILURE; tdc = teamdctl_alloc(); if (!tdc) { pr_err("teamdctl_alloc failed\n"); return EXIT_FAILURE; } err = teamdctl_connect(tdc, team_devname, addr, (force_usock ? "usock" : (force_dbus ? "dbus": (force_zmq ? "zmq" :NULL)))); if (err) { pr_err("teamdctl_connect failed (%s)\n", strerror(-err)); ret = EXIT_FAILURE; goto teamdctl_free; } err = check_teamd_team_devname(tdc, team_devname); if (err) { ret = EXIT_FAILURE; goto teamdctl_disconnect; } err = call_command(tdc, argc, argv, command_type); if (err) { pr_err("command call failed (%s)\n", strerror(-err)); ret = EXIT_FAILURE; goto teamdctl_disconnect; } ret = EXIT_SUCCESS; teamdctl_disconnect: teamdctl_disconnect(tdc); teamdctl_free: teamdctl_free(tdc); return ret; }