/
githubmirror
/
libteam
Обзор
Документация
Войти
/
githubmirror
/
libteam
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
teamd/teamd_state.c
775 строк
18 KB
Xin Long
teamd: tdport has to exist if item->per_port is set in __find_by_item_path
17 апр 2019, 10:38
17 апр 2019, 10:38
4dc3a7a
Код
Авторство
О чём код?
/* * teamd_state.c - Teamd state * 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 <stdbool.h> #include <stdlib.h> #include <limits.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <jansson.h> #include <team.h> #include <private/misc.h> #include "config.h" #include "teamd.h" #include "teamd_state.h" #include "teamd_json.h" struct teamd_state_val_item { struct list_item list; char *subpath; const struct teamd_state_val *val; void *priv; const struct teamd_state_val *parent_val; bool per_port; struct teamd_port *tdport; }; static struct teamd_state_val_item * __find_val_item(struct teamd_context *ctx, const struct teamd_state_val *val, void *priv, const struct teamd_state_val *parent_val) { struct teamd_state_val_item *item; list_for_each_node_entry(item, &ctx->state_val_list, list) { if (item->val == val && item->parent_val == parent_val && item->priv == priv) return item; } return NULL; } void __unreg_val(struct teamd_context *ctx, const struct teamd_state_val *val, void *priv, const struct teamd_state_val *parent_val) { struct teamd_state_val_item *item; if (val->type == TEAMD_STATE_ITEM_TYPE_NODE) { int i; TEAMD_BUG_ON(!val->vals); /* consistency check */ for (i = 0; i < val->vals_count; i++) __unreg_val(ctx, &val->vals[i], priv, val); } else { item = __find_val_item(ctx, val, priv, parent_val); if (!item) return; list_del(&item->list); free(item->subpath); free(item); } } int __reg_val(struct teamd_context *ctx, const struct teamd_state_val *val, void *priv, const char *parent_subpath, const char *val_subpath, bool per_port, struct teamd_port *tdport, const struct teamd_state_val *parent_val) { char *subpath; int ret; int err; if (val->type == TEAMD_STATE_ITEM_TYPE_NODE && !val_subpath) { subpath = strdup(parent_subpath); if (!subpath) return -ENOMEM; } else { ret = asprintf(&subpath, "%s.%s", parent_subpath, val_subpath); if (ret == -1) return -ENOMEM; } if (val->per_port) per_port = true; if (per_port && tdport) { err = -EINVAL; goto errout; } if (val->type == TEAMD_STATE_ITEM_TYPE_NODE) { int i; TEAMD_BUG_ON(!val->vals); /* consistency check */ for (i = 0; i < val->vals_count; i++) { const struct teamd_state_val *child_val = &val->vals[i]; err = __reg_val(ctx, child_val, priv, subpath, child_val->subpath, per_port, tdport, val); if (err) break; } /* rollback in case for did not finish */ if (i != val->vals_count) while (--i >= 0) __unreg_val(ctx, &val->vals[i], priv, val); free(subpath); } else { struct teamd_state_val_item *item; if (__find_val_item(ctx, val, priv, parent_val)) { err = -EEXIST; goto errout; } item = malloc(sizeof(*item)); if (!item) { err = -ENOMEM; goto errout; } item->subpath = subpath; item->val = val; item->parent_val = parent_val; item->priv = priv; item->per_port = per_port; item->tdport = tdport; list_add_tail(&ctx->state_val_list, &item->list); } return 0; errout: free(subpath); return err; } int teamd_state_val_register_ex(struct teamd_context *ctx, const struct teamd_state_val *val, void *priv, struct teamd_port *tdport, const char *fmt, ...) { va_list ap; char *val_subpath; int ret; int err; va_start(ap, fmt); ret = vasprintf(&val_subpath, fmt, ap); va_end(ap); if (ret == -1) return -ENOMEM; err = __reg_val(ctx, val, priv, "", val_subpath, false, tdport, NULL); free(val_subpath); return err; } int teamd_state_val_register(struct teamd_context *ctx, const struct teamd_state_val *val, void *priv) { return __reg_val(ctx, val, priv, "", val->subpath, false, NULL, NULL); } void teamd_state_val_unregister(struct teamd_context *ctx, const struct teamd_state_val *val, void *priv) { __unreg_val(ctx, val, priv, NULL); } #define TEAMD_STATE_PER_PORT_PREFIX "ports." static int teamd_state_build_val_json_subpath(json_t **p_vg_json_obj, json_t *root_json_obj, struct teamd_port *tdport, const char *subpath) { char *path; int ret; int err; char *dot; if (tdport) ret = asprintf(&path, "$." TEAMD_STATE_PER_PORT_PREFIX "\"%s\"%s", tdport->ifname, subpath); else ret = asprintf(&path, "$%s", subpath); if (ret == -1) return -ENOMEM; dot = strrchr(path, '.'); TEAMD_BUG_ON(!dot); *dot = '\0'; err = teamd_json_path_lite_build(p_vg_json_obj, root_json_obj, path); free(path); return err; } static int teamd_state_val_dump(struct teamd_context *ctx, json_t *root_json_obj, struct teamd_port *tdport, struct teamd_state_val_item *item) { const struct teamd_state_val *val = item->val; char *subpath = item->subpath; void *priv = item->priv; char *subpath_end; struct team_state_gsc gsc; json_t *val_json_obj = NULL; /* gcc needs this initialized */ json_t *vg_json_obj = NULL; /* gcc needs this initialized */ int err; int ret; subpath_end = strrchr(subpath, '.'); TEAMD_BUG_ON(!subpath_end); subpath_end++; err = teamd_state_build_val_json_subpath(&vg_json_obj, root_json_obj, tdport, subpath); if (err) return err; memset(&gsc, 0, sizeof(gsc)); gsc.info.tdport = tdport; err = val->getter(ctx, &gsc, priv); if (err) return err; switch (val->type) { case TEAMD_STATE_ITEM_TYPE_INT: val_json_obj = json_integer(gsc.data.int_val); break; case TEAMD_STATE_ITEM_TYPE_STRING: val_json_obj = json_string(gsc.data.str_val.ptr); if (gsc.data.str_val.free) free((void *) gsc.data.str_val.ptr); break; case TEAMD_STATE_ITEM_TYPE_BOOL: val_json_obj = gsc.data.bool_val ? json_true() : json_false(); break; case TEAMD_STATE_ITEM_TYPE_NODE: TEAMD_BUG(); } if (!val_json_obj) return -ENOMEM; ret = json_object_set_new(vg_json_obj, subpath_end, val_json_obj); if (ret) return -EINVAL; return 0; } static int teamd_state_vals_dump(struct teamd_context *ctx, json_t *root_json_obj) { struct teamd_state_val_item *item; int err; list_for_each_node_entry(item, &ctx->state_val_list, list) { if (item->per_port) { struct teamd_port *tdport; teamd_for_each_tdport(tdport, ctx) { err = teamd_state_val_dump(ctx, root_json_obj, tdport, item); if (err) return err; } } else { err = teamd_state_val_dump(ctx, root_json_obj, item->tdport, item); if (err) return err; } } return 0; } static int __find_by_item_path(struct teamd_state_val_item **p_item, struct teamd_port **p_tdport, struct teamd_context *ctx, const char *item_path) { struct teamd_state_val_item *item; char *subpath; struct teamd_port *tdport = NULL; if (!strncmp(item_path, TEAMD_STATE_PER_PORT_PREFIX, strlen(TEAMD_STATE_PER_PORT_PREFIX))) { char *ifname_start, *ifname_end; struct teamd_port *cur_tdport; size_t ifname_len; ifname_start = strchr(item_path, '.') + 1; if (*ifname_start == '\"') { ifname_start++; ifname_end = strrchr(ifname_start, '\"'); if (!ifname_end) return -EINVAL; ifname_len = ifname_end - ifname_start; ifname_end++; } else { ifname_end = strchr(ifname_start, '.'); if (!ifname_end) return -EINVAL; ifname_len = ifname_end - ifname_start; } subpath = ifname_end + 1; teamd_for_each_tdport(cur_tdport, ctx) { if (!strncmp(cur_tdport->ifname, ifname_start, ifname_len)) { tdport = cur_tdport; break; } } if (!tdport) return -ENOENT; } else { subpath = (char *) item_path; } list_for_each_node_entry(item, &ctx->state_val_list, list) { /* item->subpath[0] == '.' */ if (!strcmp(item->subpath + 1, subpath) && (!item->per_port || tdport) && (!item->tdport || item->tdport == tdport)) { *p_item = item; *p_tdport = tdport; return 0; } } return -ENOENT; } int teamd_state_item_value_get(struct teamd_context *ctx, const char *item_path, char **p_value) { struct teamd_state_val_item *item; const struct teamd_state_val *val; void *priv; struct team_state_gsc gsc; int err; int ret = 0; /* gcc needs this initialized */ memset(&gsc, 0, sizeof(gsc)); err = __find_by_item_path(&item, &gsc.info.tdport, ctx, item_path); if (err) return err; val = item->val; priv = item->priv; err = val->getter(ctx, &gsc, priv); if (err) return err; switch (val->type) { case TEAMD_STATE_ITEM_TYPE_INT: ret = asprintf(p_value, "%d", gsc.data.int_val); break; case TEAMD_STATE_ITEM_TYPE_STRING: ret = asprintf(p_value, "%s", gsc.data.str_val.ptr); if (gsc.data.str_val.free) free((void *) gsc.data.str_val.ptr); break; case TEAMD_STATE_ITEM_TYPE_BOOL: ret = asprintf(p_value, "%s", gsc.data.bool_val ? "true" : "false"); break; case TEAMD_STATE_ITEM_TYPE_NODE: TEAMD_BUG(); } if (ret == -1) return -ENOMEM; return 0; } int __set_int_val(struct team_state_gsc *gsc, const char *value) { long val; char *endptr; errno = 0; val = strtol(value, &endptr, 10); if (errno) return -errno; if (strlen(endptr) != 0) return -EINVAL; if (val < INT_MIN || val > INT_MAX) return -ERANGE; gsc->data.int_val = val; return 0; } int __set_bool_val(struct team_state_gsc *gsc, const char *value) { if (!strcasecmp("true", value)) gsc->data.bool_val = true; else if (!strcasecmp("false", value)) gsc->data.bool_val = false; else return -EINVAL; return 0; } int teamd_state_item_value_set(struct teamd_context *ctx, const char *item_path, const char *value) { struct teamd_state_val_item *item; const struct teamd_state_val *val; void *priv; struct team_state_gsc gsc; int err; err = __find_by_item_path(&item, &gsc.info.tdport, ctx, item_path); if (err) return err; val = item->val; priv = item->priv; if (!val->setter) return -EOPNOTSUPP; switch (val->type) { case TEAMD_STATE_ITEM_TYPE_INT: err = __set_int_val(&gsc, value); if (err) return err; break; case TEAMD_STATE_ITEM_TYPE_STRING: gsc.data.str_val.ptr = value; break; case TEAMD_STATE_ITEM_TYPE_BOOL: err = __set_bool_val(&gsc, value); if (err) return err; break; case TEAMD_STATE_ITEM_TYPE_NODE: TEAMD_BUG(); } return val->setter(ctx, &gsc, priv); } int teamd_state_init(struct teamd_context *ctx) { list_init(&ctx->state_val_list); return 0; } void teamd_state_fini(struct teamd_context *ctx) { } int teamd_state_dump(struct teamd_context *ctx, char **p_state_dump) { json_t *state_json; char *dump; int err; state_json = json_object(); if (!state_json) return -ENOMEM; err = teamd_state_vals_dump(ctx, state_json); if (err) goto errout; dump = json_dumps(state_json, TEAMD_JSON_DUMPS_FLAGS); json_decref(state_json); if (!dump) return -ENOMEM; *p_state_dump = dump; return 0; errout: json_decref(state_json); return err; } /* * state basics */ static struct team_ifinfo *__get_ifinfo(struct teamd_context *ctx, struct team_state_gsc *gsc) { if (gsc->info.tdport) return gsc->info.tdport->team_ifinfo; else return team_get_ifinfo(ctx->th); } static int ifinfo_state_ifindex_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.int_val = team_get_ifinfo_ifindex(__get_ifinfo(ctx, gsc)); return 0; } static int ifinfo_state_ifname_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.str_val.ptr = team_get_ifinfo_ifname(__get_ifinfo(ctx, gsc)); return 0; } static int ifinfo_state_dev_addr_len_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.int_val = team_get_ifinfo_hwaddr_len(__get_ifinfo(ctx, gsc)); return 0; } static int ifinfo_state_dev_addr_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { struct team_ifinfo *ifinfo = __get_ifinfo(ctx, gsc); char *addr_str; addr_str = a_hwaddr_str(team_get_ifinfo_hwaddr(ifinfo), team_get_ifinfo_hwaddr_len(ifinfo)); if (!addr_str) return -ENOMEM; gsc->data.str_val.ptr = addr_str; gsc->data.str_val.free = true; return 0; } static const struct teamd_state_val ifinfo_state_vals[] = { { .subpath = "ifindex", .type = TEAMD_STATE_ITEM_TYPE_INT, .getter = ifinfo_state_ifindex_get, }, { .subpath = "ifname", .type = TEAMD_STATE_ITEM_TYPE_STRING, .getter = ifinfo_state_ifname_get, }, { .subpath = "dev_addr", .type = TEAMD_STATE_ITEM_TYPE_STRING, .getter = ifinfo_state_dev_addr_get, }, { .subpath = "dev_addr_len", .type = TEAMD_STATE_ITEM_TYPE_INT, .getter = ifinfo_state_dev_addr_len_get, }, }; static int port_link_state_up_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.bool_val = team_is_port_link_up(gsc->info.tdport->team_port); return 0; } static int port_link_state_speed_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.int_val = team_get_port_speed(gsc->info.tdport->team_port); return 0; } static int port_link_state_duplex_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.str_val.ptr = team_get_port_duplex(gsc->info.tdport->team_port) ? "full" : "half"; return 0; } static const struct teamd_state_val port_link_state_vals[] = { { .subpath = "up", .type = TEAMD_STATE_ITEM_TYPE_BOOL, .getter = port_link_state_up_get, }, { .subpath = "speed", .type = TEAMD_STATE_ITEM_TYPE_INT, .getter = port_link_state_speed_get, }, { .subpath = "duplex", .type = TEAMD_STATE_ITEM_TYPE_STRING, .getter = port_link_state_duplex_get, }, }; static int setup_state_runner_name_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.str_val.ptr = ctx->runner->name; return 0; } static int setup_state_kernel_team_mode_name_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.str_val.ptr = ctx->runner->team_mode_name; return 0; } static int setup_state_dbus_enabled_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { #ifdef ENABLE_DBUS gsc->data.bool_val = ctx->dbus.enabled; #else gsc->data.bool_val = false; #endif return 0; } static int setup_state_zmq_enabled_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { #ifdef ENABLE_ZMQ gsc->data.bool_val = ctx->zmq.enabled; #else gsc->data.bool_val = false; #endif return 0; } static int setup_state_debug_level_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.int_val = ctx->debug; return 0; } static int setup_state_debug_level_set(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { if (gsc->data.int_val < 0) return -EINVAL; return teamd_change_debug_level(ctx, gsc->data.int_val); } static int setup_state_daemonized_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.bool_val = ctx->daemonize; return 0; } static int setup_state_pid_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.int_val = getpid(); return 0; } static int setup_state_pid_file_get(struct teamd_context *ctx, struct team_state_gsc *gsc, void *priv) { gsc->data.str_val.ptr = ctx->pid_file ? ctx->pid_file : ""; return 0; } static const struct teamd_state_val setup_state_vals[] = { { .subpath = "runner_name", .type = TEAMD_STATE_ITEM_TYPE_STRING, .getter = setup_state_runner_name_get, }, { .subpath = "kernel_team_mode_name", .type = TEAMD_STATE_ITEM_TYPE_STRING, .getter = setup_state_kernel_team_mode_name_get, }, { .subpath = "dbus_enabled", .type = TEAMD_STATE_ITEM_TYPE_BOOL, .getter = setup_state_dbus_enabled_get, }, { .subpath = "zmq_enabled", .type = TEAMD_STATE_ITEM_TYPE_BOOL, .getter = setup_state_zmq_enabled_get, }, { .subpath = "debug_level", .type = TEAMD_STATE_ITEM_TYPE_INT, .getter = setup_state_debug_level_get, .setter = setup_state_debug_level_set, }, { .subpath = "daemonized", .type = TEAMD_STATE_ITEM_TYPE_BOOL, .getter = setup_state_daemonized_get, }, { .subpath = "pid", .type = TEAMD_STATE_ITEM_TYPE_INT, .getter = setup_state_pid_get, }, { .subpath = "pid_file", .type = TEAMD_STATE_ITEM_TYPE_STRING, .getter = setup_state_pid_file_get, }, }; static const struct teamd_state_val state_vgs[] = { { .subpath = "team_device.ifinfo", .vals = ifinfo_state_vals, .vals_count = ARRAY_SIZE(ifinfo_state_vals), }, { .subpath = "ifinfo", .vals = ifinfo_state_vals, .vals_count = ARRAY_SIZE(ifinfo_state_vals), .per_port = true, }, { .subpath = "link", .vals = port_link_state_vals, .vals_count = ARRAY_SIZE(port_link_state_vals), .per_port = true, }, { .subpath = "setup", .vals = setup_state_vals, .vals_count = ARRAY_SIZE(setup_state_vals), }, }; static const struct teamd_state_val root_state_vg = { .vals = state_vgs, .vals_count = ARRAY_SIZE(state_vgs), }; int teamd_state_basics_init(struct teamd_context *ctx) { int err; err = teamd_state_val_register(ctx, &root_state_vg, ctx); if (err) return err; return 0; } void teamd_state_basics_fini(struct teamd_context *ctx) { teamd_state_val_unregister(ctx, &root_state_vg, ctx); }