| /* |
| * teamd_ctl.c - Teamd control subsustem |
| * Copyright (C) 2012-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 <stdbool.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <team.h> |
| #include <private/misc.h> |
| |
| #include "teamd.h" |
| #include "teamd_config.h" |
| #include "teamd_state.h" |
| #include "teamd_ctl.h" |
| |
| static int teamd_ctl_method_port_config_update(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| const char *port_devname; |
| const char *port_config; |
| uint32_t ifindex; |
| int err; |
| |
| err = ops->get_args(ops_priv, "ss", &port_devname, &port_config); |
| if (err) |
| return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments."); |
| teamd_log_dbgx(ctx, 2, "port_devname \"%s\", port_config \"%s\"", |
| port_devname, port_config); |
| |
| ifindex = team_ifname2ifindex(ctx->th, port_devname); |
| if (!ifindex) { |
| teamd_log_err("Device \"%s\" does not exist.", port_devname); |
| return ops->reply_err(ops_priv, "NoSuchDev", "No such device."); |
| } |
| err = teamd_config_port_update(ctx, port_devname, port_config); |
| if (err) { |
| teamd_log_err("Failed to update config for port \"%s\".", |
| port_devname); |
| return ops->reply_err(ops_priv, "ConfigUpdateFail", "Failed to update config."); |
| } |
| return ops->reply_succ(ops_priv, NULL); |
| } |
| |
| static int teamd_ctl_method_port_config_dump(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| const char *port_devname; |
| uint32_t ifindex; |
| char *cfg; |
| int err; |
| |
| err = ops->get_args(ops_priv, "s", &port_devname); |
| if (err) |
| return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments."); |
| teamd_log_dbgx(ctx, 2, "port_devname \"%s\"", port_devname); |
| |
| ifindex = team_ifname2ifindex(ctx->th, port_devname); |
| if (!ifindex) { |
| teamd_log_err("Device \"%s\" does not exist.", port_devname); |
| return ops->reply_err(ops_priv, "NoSuchDev", "No such device."); |
| } |
| err = teamd_config_port_dump(ctx, port_devname, &cfg); |
| if (err) { |
| teamd_log_err("Failed to dump config for port \"%s\".", |
| port_devname); |
| return ops->reply_err(ops_priv, "ConfigDumpFail", "Failed to dump config."); |
| } |
| err = ops->reply_succ(ops_priv, cfg); |
| free(cfg); |
| return err; |
| } |
| |
| static int teamd_ctl_method_port_add(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| const char *port_devname; |
| int err; |
| |
| err = ops->get_args(ops_priv, "s", &port_devname); |
| if (err) |
| return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments."); |
| teamd_log_dbgx(ctx, 2, "port_devname \"%s\"", port_devname); |
| |
| err = teamd_port_add_ifname(ctx, port_devname); |
| if (err) { |
| teamd_log_err("%s: Failed to add port (%s).", port_devname, |
| strerror(-err)); |
| } |
| |
| switch (err) { |
| case -ENODEV: |
| return ops->reply_err(ops_priv, "NoSuchDev", "No such device."); |
| case 0: |
| break; |
| default: |
| return ops->reply_err(ops_priv, "PortAddFail", "Failed to add port."); |
| } |
| return ops->reply_succ(ops_priv, NULL); |
| } |
| |
| static int teamd_ctl_method_port_remove(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| const char *port_devname; |
| int err; |
| |
| err = ops->get_args(ops_priv, "s", &port_devname); |
| if (err) |
| return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments."); |
| teamd_log_dbgx(ctx, 2, "port_devname \"%s\"", port_devname); |
| |
| err = teamd_port_remove_ifname(ctx, port_devname); |
| switch (err) { |
| case -ENODEV: |
| return ops->reply_err(ops_priv, "NoSuchDev", "No such device."); |
| case 0: |
| break; |
| default: |
| return ops->reply_err(ops_priv, "PortRemoveFail", "Failed to del port."); |
| } |
| return ops->reply_succ(ops_priv, NULL); |
| } |
| |
| static int teamd_ctl_method_config_dump(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| char *cfg; |
| int err; |
| |
| err = teamd_config_dump(ctx, &cfg); |
| if (err) { |
| teamd_log_err("Failed to dump config."); |
| return ops->reply_err(ops_priv, "ConfigDumpFail", "Failed to dump config."); |
| } |
| err = ops->reply_succ(ops_priv, cfg); |
| free(cfg); |
| return err; |
| } |
| |
| static int teamd_ctl_method_config_dump_actual(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| char *cfg; |
| int err; |
| |
| err = teamd_config_actual_dump(ctx, &cfg); |
| if (err) { |
| teamd_log_err("Failed to dump actual config."); |
| return ops->reply_err(ops_priv, "ConfigDumpActualFail", "Failed to dump actual config."); |
| } |
| err = ops->reply_succ(ops_priv, cfg); |
| free(cfg); |
| return err; |
| } |
| |
| static int teamd_ctl_method_state_dump(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| char *state; |
| int err; |
| |
| err = teamd_state_dump(ctx, &state); |
| if (err) { |
| teamd_log_err("Failed to dump state."); |
| return ops->reply_err(ops_priv, "StateDumpFail", "Failed to dump state."); |
| } |
| err = ops->reply_succ(ops_priv, state); |
| free(state); |
| return err; |
| } |
| |
| static int teamd_ctl_method_state_item_value_get(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| const char *item_path; |
| char *value; |
| int err; |
| |
| err = ops->get_args(ops_priv, "s", &item_path); |
| if (err) |
| return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments."); |
| teamd_log_dbgx(ctx, 2, "item_path \"%s\"", item_path); |
| |
| err = teamd_state_item_value_get(ctx, item_path, &value); |
| if (err == -ENOENT) { |
| teamd_log_err("Failed to get state item \"%s\".", item_path); |
| return ops->reply_err(ops_priv, "PathDoesNotExist", "Item path does not exist."); |
| } else if (err) { |
| teamd_log_err("Failed to get state item \"%s\".", item_path); |
| return ops->reply_err(ops_priv, "ItemValueGetFail", "Failed to get item value."); |
| } |
| err = ops->reply_succ(ops_priv, value); |
| free(value); |
| return err; |
| } |
| |
| static int teamd_ctl_method_state_item_value_set(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| const char *item_path; |
| const char *value; |
| int err; |
| |
| err = ops->get_args(ops_priv, "ss", &item_path, &value); |
| if (err) |
| return ops->reply_err(ops_priv, "InvalidArgs", "Did not receive correct message arguments."); |
| teamd_log_dbgx(ctx, 2, "item_path \"%s\", value \"%s\"", |
| item_path, value); |
| |
| err = teamd_state_item_value_set(ctx, item_path, value); |
| if (err == -ENOENT) { |
| teamd_log_err("Failed to set state item \"%s\".", item_path); |
| return ops->reply_err(ops_priv, "PathDoesNotExist", "Item path does not exist."); |
| } else if (err == -EOPNOTSUPP) { |
| teamd_log_err("Failed to set state item \"%s\".", item_path); |
| return ops->reply_err(ops_priv, "OpNotSupp", "Operation not supported."); |
| } else if (err) { |
| teamd_log_err("Failed to set state item \"%s\".", item_path); |
| return ops->reply_err(ops_priv, "ItemValueSetFail", "Failed to set item value."); |
| } |
| return ops->reply_succ(ops_priv, NULL); |
| } |
| |
| typedef int (*teamd_ctl_method_func_t)(struct teamd_context *ctx, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv); |
| struct teamd_ctl_method { |
| const char *name; |
| teamd_ctl_method_func_t func; |
| }; |
| |
| static const struct teamd_ctl_method teamd_ctl_method_list[] = { |
| { |
| .name = "PortConfigUpdate", |
| .func = teamd_ctl_method_port_config_update, |
| |
| }, |
| { |
| .name = "PortConfigDump", |
| .func = teamd_ctl_method_port_config_dump, |
| |
| }, |
| { |
| .name = "PortAdd", |
| .func = teamd_ctl_method_port_add, |
| |
| }, |
| { |
| .name = "PortRemove", |
| .func = teamd_ctl_method_port_remove, |
| |
| }, |
| { |
| .name = "ConfigDump", |
| .func = teamd_ctl_method_config_dump, |
| |
| }, |
| { |
| .name = "ConfigDumpActual", |
| .func = teamd_ctl_method_config_dump_actual, |
| |
| }, |
| { |
| .name = "StateDump", |
| .func = teamd_ctl_method_state_dump, |
| |
| }, |
| { |
| .name = "StateItemValueGet", |
| .func = teamd_ctl_method_state_item_value_get, |
| |
| }, |
| { |
| .name = "StateItemValueSet", |
| .func = teamd_ctl_method_state_item_value_set, |
| |
| }, |
| }; |
| |
| #define TEAMD_CTL_METHOD_LIST_SIZE ARRAY_SIZE(teamd_ctl_method_list) |
| |
| static teamd_ctl_method_func_t get_func_by_name(const char *method_name) |
| { |
| int i; |
| |
| for (i = 0; i < TEAMD_CTL_METHOD_LIST_SIZE; i++) { |
| const struct teamd_ctl_method *method; |
| |
| method = &teamd_ctl_method_list[i]; |
| if (!strcmp(method->name, method_name)) |
| return method->func; |
| } |
| return NULL; |
| } |
| |
| bool teamd_ctl_method_exists(const char *method_name) |
| { |
| return get_func_by_name(method_name); |
| } |
| |
| int teamd_ctl_method_call(struct teamd_context *ctx, const char *method_name, |
| const struct teamd_ctl_method_ops *ops, |
| void *ops_priv) |
| { |
| teamd_ctl_method_func_t func; |
| |
| func = get_func_by_name(method_name); |
| if (!func) { |
| teamd_log_err("Failed call non-existent method named \"%s\".", |
| method_name); |
| return -EINVAL; |
| } |
| return func(ctx, ops, ops_priv); |
| } |