/* * teamd_config.c - Teamd configuration frontend * 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 "teamd.h" #include "teamd_config.h" #include "teamd_json.h" #define TEAMD_IMPLICIT_CONFIG "{}" int teamd_config_load(struct teamd_context *ctx) { json_error_t jerror; size_t jflags = JSON_REJECT_DUPLICATES; if (!ctx->config_text && !ctx->config_file) { ctx->config_text = strdup(TEAMD_IMPLICIT_CONFIG); if (!ctx->config_text) return -ENOMEM; } if (ctx->config_text) { if (ctx->config_file) teamd_log_warn("Command line config string is present, ignoring given config file."); ctx->config_json = json_loads(ctx->config_text, jflags, &jerror); } else if (ctx->config_file) { ctx->config_json = json_load_file(ctx->config_file, jflags, &jerror); } if (!ctx->config_json) { teamd_log_err("Failed to parse config: %s on line %d, column %d", jerror.text, jerror.line, jerror.column); return -EIO; } return 0; } void teamd_config_free(struct teamd_context *ctx) { json_decref(ctx->config_json); } int teamd_config_dump(struct teamd_context *ctx, char **p_config_dump) { char *dump; dump = json_dumps(ctx->config_json, TEAMD_JSON_DUMPS_FLAGS); if (!dump) return -ENOMEM; *p_config_dump = dump; return 0; } static int get_port_obj(json_t **pport_obj, json_t *config_json, const char *port_name) { int err; json_t *ports_obj; json_t *port_obj; err = json_unpack(config_json, "{s:o}", "ports", &ports_obj); if (err) { ports_obj = json_object(); if (!ports_obj) return -ENOMEM; err = json_object_set_new(config_json, "ports", ports_obj); if (err) { json_decref(ports_obj); return -ENOMEM; } } err = json_unpack(ports_obj, "{s:o}", port_name, &port_obj); if (err) { port_obj = json_object(); if (!port_obj) return -ENOMEM; err = json_object_set_new(ports_obj, port_name, port_obj); if (err) { json_decref(port_obj); return -ENOMEM; } } if (pport_obj) *pport_obj = port_obj; return 0; } int teamd_config_actual_dump(struct teamd_context *ctx, char **p_config_dump) { json_t *actual_json; struct teamd_port *tdport; json_t *ports_obj; void *iter; char *dump; int err; actual_json = json_deep_copy(ctx->config_json); if (!actual_json) return -ENOMEM; /* * Create json objects for all present ports */ teamd_for_each_tdport(tdport, ctx) { err = get_port_obj(NULL, actual_json, tdport->ifname); if (err) goto errout; } /* * Get rid of json object of ports which are not present */ err = json_unpack(actual_json, "{s:o}", "ports", &ports_obj); if (!err) { iter = json_object_iter(ports_obj); while (iter) { const char *port_name = json_object_iter_key(iter); iter = json_object_iter_next(ports_obj, iter); if (!teamd_get_port_by_ifname(ctx, port_name)) json_object_del(ports_obj, port_name); } } dump = json_dumps(actual_json, TEAMD_JSON_DUMPS_FLAGS); json_decref(actual_json); if (!dump) return -ENOMEM; *p_config_dump = dump; return 0; errout: json_decref(actual_json); return err; } static int teamd_config_port_set(struct teamd_context *ctx, const char *port_name, json_t *port_obj) { struct teamd_port *tdport; json_t *config; int tmp, err; tdport = teamd_get_port_by_ifname(ctx, port_name); if (!tdport) return 0; config = json_object_get(port_obj, "prio"); if (!json_is_integer(config)) { teamd_log_err("%s: Failed to get integer for \"priority\".", tdport->ifname); return -ENOENT; } tmp = json_integer_value(config); err = team_set_port_priority(ctx->th, tdport->ifindex, tmp); if (err) teamd_log_err("%s: Failed to update \"priority\" to kernel", tdport->ifname); return err; } int teamd_config_port_update(struct teamd_context *ctx, const char *port_name, const char *json_port_cfg_str) { int err; json_t *port_obj; json_t *port_new_obj; json_error_t jerror; port_new_obj = json_loads(json_port_cfg_str, JSON_REJECT_DUPLICATES, &jerror); if (!port_new_obj) { teamd_log_err("%s: Failed to parse port config string: " "%s on line %d, column %d", port_name, jerror.text, jerror.line, jerror.column); return -EIO; } err = get_port_obj(&port_obj, ctx->config_json, port_name); if (err) { teamd_log_err("%s: Failed to obtain port config object", port_name); goto new_port_decref; } /* replace existing object content */ json_object_clear(port_obj); err = json_object_update(port_obj, port_new_obj); if (err) { teamd_log_err("%s: Failed to update existing config " "port object", port_name); goto new_port_decref; } err = teamd_config_port_set(ctx, port_name, port_new_obj); new_port_decref: json_decref(port_new_obj); return err; } int teamd_config_port_dump(struct teamd_context *ctx, const char *port_name, char **p_config_port_dump) { json_t *port_json; char *dump; int err; if (!teamd_get_port_by_ifname(ctx, port_name)) return -ENODEV; err = json_unpack(ctx->config_json, "{s:{s:o}}", "ports", port_name, &port_json); if (err) port_json = json_object(); else json_incref(port_json); if (!port_json) return -ENOMEM; dump = json_dumps(port_json, TEAMD_JSON_DUMPS_FLAGS); json_decref(port_json); if (!dump) return -ENOMEM; *p_config_port_dump = dump; return 0; } static int teamd_config_object_get(struct teamd_context *ctx, json_t **p_json_obj, const char *fmt, va_list ap) { int err; err = teamd_json_path_lite_va(p_json_obj, ctx->config_json, fmt, ap); if (err) { if (err == -EINVAL) teamd_log_err("Failed to get value from config: Wrong path format"); return err; } return 0; } static int teamd_config_object_build_type_get(struct teamd_context *ctx, json_t **p_json_obj, json_type obj_type, const char *fmt, va_list ap) { int err; err = teamd_json_path_lite_build_type_va(p_json_obj, ctx->config_json, obj_type, fmt, ap); if (err) { if (err == -EINVAL) teamd_log_err("Failed to get value from config: Wrong path format"); return err; } return 0; } struct teamd_config_path_cookie * teamd_config_path_cookie_get(struct teamd_context *ctx, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); if (err) return NULL; return (struct teamd_config_path_cookie *) json_obj; } bool teamd_config_path_exists(struct teamd_context *ctx, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); return err ? false : true; } bool teamd_config_path_is_arr(struct teamd_context *ctx, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); return !err && json_is_array(json_obj) ? true : false; } int teamd_config_string_get(struct teamd_context *ctx, const char **p_str_val, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); if (err) return err; if (!json_is_string(json_obj)) { teamd_log_err("Failed to get string from non-string object"); return -ENOENT; } *p_str_val = json_string_value(json_obj); return 0; } int teamd_config_string_set(struct teamd_context *ctx, const char *str_val, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; int ret; va_start(ap, fmt); err = teamd_config_object_build_type_get(ctx, &json_obj, JSON_STRING, fmt, ap); va_end(ap); if (err) return err; ret = json_string_set(json_obj, str_val); if (ret == -1) return -ENOMEM; return 0; } int teamd_config_int_get(struct teamd_context *ctx, int *p_int_val, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); if (err) return err; if (!json_is_integer(json_obj)) { teamd_log_err("Failed to get integer from non-integer object"); return -ENOENT; } *p_int_val = json_integer_value(json_obj); return 0; } int teamd_config_int_set(struct teamd_context *ctx, int int_val, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_build_type_get(ctx, &json_obj, JSON_INTEGER, fmt, ap); va_end(ap); if (err) return err; json_integer_set(json_obj, int_val); return 0; } int teamd_config_bool_get(struct teamd_context *ctx, bool *p_bool_val, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); if (err) return err; if (!json_is_boolean(json_obj)) { teamd_log_err("Failed to get boolean from non-boolean object"); return -ENOENT; } *p_bool_val = json_is_true(json_obj) ? true : false; return 0; } const char *teamd_config_next_key(struct teamd_context *ctx, const char *key, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ void *iter; int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); if (err) return NULL; if (key) { iter = json_object_key_to_iter(key); iter = json_object_iter_next(json_obj, iter); } else { iter = json_object_iter(json_obj); } return json_object_iter_key(iter); } size_t teamd_config_arr_size(struct teamd_context *ctx, const char *fmt, ...) { va_list ap; json_t *json_obj = NULL; /* gcc needs this initialized */ int err; va_start(ap, fmt); err = teamd_config_object_get(ctx, &json_obj, fmt, ap); va_end(ap); if (err) return 0; return json_array_size(json_obj); } size_t teamd_config_arr_string_append(struct teamd_context *ctx, const char *str_val, const char *fmt, ...) { va_list ap; json_t *json_arr = NULL; /* gcc needs this initialized */ json_t *json_str; int err; int ret; va_start(ap, fmt); err = teamd_config_object_build_type_get(ctx, &json_arr, JSON_ARRAY, fmt, ap); va_end(ap); if (err) return err; json_str = json_string(str_val); if (!json_str) return -ENOMEM; ret = json_array_append_new(json_arr, json_str); if (ret == -1) return -ENOMEM; return 0; }