blob: 7fcbfff05f91949b7b0dca7e9d50266544b04b26 [file] [log] [blame]
/*
* teamdctl.c - Network team device daemon control tool
* Copyright (C) 2013-2015 Jiri Pirko <jiri@resnulli.us>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <getopt.h>
#include <errno.h>
#include <jansson.h>
#include <private/misc.h>
#include <teamdctl.h>
#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;
}