| /* |
| * options.c - Wrapper for team generic netlink option-related communication |
| * 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 |
| */ |
| |
| /** |
| * @ingroup libteam |
| * @defgroup option Team options functions |
| * Wrapper for team generic netlink option-related communication |
| * |
| * @{ |
| * |
| * Header |
| * ------ |
| * ~~~~{.c} |
| * #include <team.h> |
| * ~~~~ |
| */ |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <netlink/netlink.h> |
| #include <netlink/genl/genl.h> |
| #include <netlink/genl/ctrl.h> |
| #include <netlink/cli/utils.h> |
| #include <netlink/cli/link.h> |
| #include <linux/if_team.h> |
| #include <linux/types.h> |
| #include <team.h> |
| #include <private/list.h> |
| #include <private/misc.h> |
| #include "team_private.h" |
| #include "nl_updates.h" |
| |
| /* \cond HIDDEN_SYMBOLS */ |
| |
| struct team_option_id { |
| char * name; |
| uint32_t port_ifindex; |
| bool port_ifindex_used; |
| uint32_t array_index; |
| bool array_index_used; |
| }; |
| |
| struct team_option { |
| struct list_item list; |
| bool initialized; |
| enum team_option_type type; |
| struct team_option_id id; |
| void * data; |
| int data_len; |
| bool changed; |
| bool changed_locally; |
| bool temporary; |
| }; |
| |
| static void destroy_option(struct team_option *option) |
| { |
| list_del(&option->list); |
| free(option->id.name); |
| free(option->data); |
| free(option); |
| } |
| |
| static void flush_option_list(struct team_handle *th) |
| { |
| struct team_option *option, *tmp; |
| |
| list_for_each_node_entry_safe(option, tmp, &th->option_list, list) |
| destroy_option(option); |
| } |
| |
| static void option_list_cleanup_last_state(struct team_handle *th) |
| { |
| struct team_option *option, *tmp; |
| |
| list_for_each_node_entry_safe(option, tmp, &th->option_list, list) { |
| option->changed = false; |
| if (option->temporary) |
| destroy_option(option); |
| } |
| } |
| |
| static struct team_option *do_find_option(struct team_handle *th, |
| struct team_option_id *opt_id) |
| { |
| struct team_option *option; |
| |
| list_for_each_node_entry(option, &th->option_list, list) { |
| if (strcmp(option->id.name, opt_id->name)) |
| continue; |
| if (option->id.port_ifindex_used != opt_id->port_ifindex_used) |
| continue; |
| if (option->id.port_ifindex_used && |
| option->id.port_ifindex != opt_id->port_ifindex) |
| continue; |
| if (option->id.array_index_used != opt_id->array_index_used) |
| continue; |
| if (option->id.array_index_used && |
| option->id.array_index != opt_id->array_index) |
| continue; |
| return option; |
| } |
| return NULL; |
| } |
| |
| static int get_option_data_size_by_type(int opt_type, const void *data, |
| int data_len) |
| { |
| switch (opt_type) { |
| case TEAM_OPTION_TYPE_U32: |
| return sizeof(__u32); |
| case TEAM_OPTION_TYPE_STRING: |
| return sizeof(char) * (strlen((char *) data) + 1); |
| case TEAM_OPTION_TYPE_BINARY: |
| return data_len; |
| case TEAM_OPTION_TYPE_BOOL: |
| return sizeof(bool); |
| case TEAM_OPTION_TYPE_S32: |
| return sizeof(__s32); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int create_option(struct team_handle *th, struct team_option **poption, |
| struct team_option_id *opt_id) |
| { |
| struct team_option *option; |
| int err; |
| |
| option = myzalloc(sizeof(struct team_option)); |
| if (!option) |
| return -ENOMEM; |
| |
| option->id.name = strdup(opt_id->name); |
| if (!option->id.name) { |
| err = -ENOMEM; |
| goto err_alloc_name; |
| } |
| option->id.port_ifindex = opt_id->port_ifindex; |
| option->id.port_ifindex_used = opt_id->port_ifindex_used; |
| option->id.array_index = opt_id->array_index; |
| option->id.array_index_used = opt_id->array_index_used; |
| |
| list_add(&th->option_list, &option->list); |
| |
| *poption = option; |
| return 0; |
| |
| err_alloc_name: |
| free(option); |
| |
| return err; |
| } |
| |
| static int do_update_option(struct team_handle *th, struct team_option *option, |
| int opt_type, const void *data, int data_len, |
| bool changed, bool changed_locally) |
| { |
| void *tmp_data; |
| int data_size; |
| |
| data_size = get_option_data_size_by_type(opt_type, data, data_len); |
| if (data_size < 0) |
| return data_size; |
| |
| if (option->initialized && option->type != opt_type) |
| dbg(th, "Updating option \"%s\" with different option type.", |
| option->id.name); |
| |
| tmp_data = malloc(data_size); |
| if (!tmp_data) |
| return -ENOMEM; |
| |
| memcpy(tmp_data, data, data_size); |
| free(option->data); |
| option->data = tmp_data; |
| option->data_len = data_size; |
| option->type = opt_type; |
| option->changed = changed; |
| option->changed_locally = changed_locally; |
| option->initialized = true; |
| |
| return 0; |
| } |
| |
| static int update_option(struct team_handle *th, struct team_option **poption, |
| struct team_option_id *opt_id, int opt_type, |
| const void *data, int data_len, |
| bool changed, bool changed_locally) |
| { |
| struct team_option *option; |
| bool option_created = false; |
| int err; |
| |
| option = do_find_option(th, opt_id); |
| if (!option) { |
| err = create_option(th, &option, opt_id); |
| if (err) |
| return err; |
| option_created = true; |
| } |
| err = do_update_option(th, option, opt_type, data, data_len, |
| changed, changed_locally); |
| if (err) { |
| if (option_created) |
| destroy_option(option); |
| return err; |
| } |
| *poption = option; |
| return 0; |
| } |
| |
| int get_options_handler(struct nl_msg *msg, void *arg) |
| { |
| struct nlmsghdr *nlh = nlmsg_hdr(msg); |
| struct team_handle *th = arg; |
| struct nlattr *attrs[TEAM_ATTR_MAX + 1]; |
| struct nlattr *nl_option; |
| struct nlattr *option_attrs[TEAM_ATTR_OPTION_MAX + 1]; |
| int i; |
| uint32_t team_ifindex = 0; |
| |
| genlmsg_parse(nlh, 0, attrs, TEAM_ATTR_MAX, NULL); |
| if (attrs[TEAM_ATTR_TEAM_IFINDEX]) |
| team_ifindex = nla_get_u32(attrs[TEAM_ATTR_TEAM_IFINDEX]); |
| |
| if (team_ifindex != th->ifindex) |
| return NL_SKIP; |
| |
| if (!attrs[TEAM_ATTR_LIST_OPTION]) |
| return NL_SKIP; |
| |
| if (!th->msg_recv_started) { |
| option_list_cleanup_last_state(th); |
| th->msg_recv_started = true; |
| } |
| nla_for_each_nested(nl_option, attrs[TEAM_ATTR_LIST_OPTION], i) { |
| struct team_option *option; |
| struct team_option_id opt_id; |
| bool changed; |
| int nla_type; |
| int opt_type; |
| void *data; |
| int data_len = 0; |
| int err; |
| struct nlattr *data_attr; |
| unsigned int tmp_u32; |
| bool tmp_bool; |
| int tmp_s32; |
| |
| if (nla_parse_nested(option_attrs, TEAM_ATTR_OPTION_MAX, |
| nl_option, NULL)) { |
| err(th, "Failed to parse nested attributes."); |
| return NL_SKIP; |
| } |
| |
| if (!option_attrs[TEAM_ATTR_OPTION_NAME] || |
| !option_attrs[TEAM_ATTR_OPTION_TYPE]) { |
| return NL_SKIP; |
| } |
| nla_type = nla_get_u8(option_attrs[TEAM_ATTR_OPTION_TYPE]); |
| data_attr = option_attrs[TEAM_ATTR_OPTION_DATA]; |
| if (nla_type != NLA_FLAG && !data_attr) |
| return NL_SKIP; |
| |
| memset(&opt_id, 0, sizeof(opt_id)); |
| opt_id.name = nla_get_string(option_attrs[TEAM_ATTR_OPTION_NAME]); |
| |
| if (option_attrs[TEAM_ATTR_OPTION_CHANGED]) |
| changed = true; |
| else |
| changed = false; |
| |
| if (option_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]) { |
| opt_id.port_ifindex = nla_get_u32(option_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]); |
| opt_id.port_ifindex_used = true; |
| } else { |
| opt_id.port_ifindex_used = false; |
| } |
| |
| if (option_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX]) { |
| opt_id.array_index = nla_get_u32(option_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX]); |
| opt_id.array_index_used = true; |
| } else { |
| opt_id.array_index_used = false; |
| } |
| |
| switch (nla_type) { |
| case NLA_U32: |
| tmp_u32 = nla_get_u32(data_attr); |
| data = &tmp_u32; |
| opt_type = TEAM_OPTION_TYPE_U32; |
| break; |
| case NLA_STRING: |
| data = nla_get_string(data_attr); |
| opt_type = TEAM_OPTION_TYPE_STRING; |
| break; |
| case NLA_BINARY: |
| data = nla_data(data_attr); |
| data_len = nla_len(data_attr); |
| opt_type = TEAM_OPTION_TYPE_BINARY; |
| break; |
| case NLA_FLAG: |
| tmp_bool = data_attr ? true : false; |
| data = &tmp_bool; |
| opt_type = TEAM_OPTION_TYPE_BOOL; |
| break; |
| case NLA_S32: |
| tmp_s32 = nla_get_s32(data_attr); |
| data = &tmp_s32; |
| opt_type = TEAM_OPTION_TYPE_S32; |
| break; |
| default: |
| err(th, "Unknown nla_type received."); |
| continue; |
| } |
| |
| err = update_option(th, &option, &opt_id, opt_type, |
| data, data_len, changed, false); |
| if (err) { |
| err(th, "Failed to update option: %s", strerror(-err)); |
| continue; |
| } |
| if (option_attrs[TEAM_ATTR_OPTION_REMOVED]) |
| destroy_option(option); |
| } |
| |
| set_call_change_handlers(th, TEAM_OPTION_CHANGE); |
| return NL_SKIP; |
| } |
| |
| static int get_options(struct team_handle *th) |
| { |
| struct nl_msg *msg; |
| int err; |
| |
| msg = nlmsg_alloc(); |
| if (!msg) |
| return -ENOMEM; |
| |
| genlmsg_put(msg, NL_AUTO_PID, th->nl_sock_seq, th->family, 0, 0, |
| TEAM_CMD_OPTIONS_GET, 0); |
| NLA_PUT_U32(msg, TEAM_ATTR_TEAM_IFINDEX, th->ifindex); |
| |
| th->msg_recv_started = false; |
| err = send_and_recv(th, msg, get_options_handler, th); |
| if (err) |
| return err; |
| |
| return check_call_change_handlers(th, TEAM_OPTION_CHANGE); |
| |
| nla_put_failure: |
| nlmsg_free(msg); |
| return -ENOBUFS; |
| } |
| |
| int option_list_alloc(struct team_handle *th) |
| { |
| list_init(&th->option_list); |
| |
| return 0; |
| } |
| |
| int option_list_init(struct team_handle *th) |
| { |
| int err; |
| |
| err = get_options(th); |
| if (err) { |
| err(th, "Failed to get options."); |
| return err; |
| } |
| return 0; |
| } |
| |
| void option_list_free(struct team_handle *th) |
| { |
| flush_option_list(th); |
| } |
| |
| static struct team_option *find_option(struct team_handle *th, |
| struct team_option_id *opt_id, |
| bool must_exist) |
| { |
| struct team_option *option; |
| int err; |
| |
| option = do_find_option(th, opt_id); |
| if (option) |
| return option; |
| if (must_exist) |
| return NULL; |
| /* |
| * In case option does not exist, create new uninitialized one |
| * which can be used for option tracking. |
| */ |
| err = create_option(th, &option, opt_id); |
| if (err) |
| return NULL; |
| option->temporary = true; |
| return option; |
| } |
| |
| /* \endcond */ |
| |
| /** |
| * @param th libteam library context |
| * @param fmt format string |
| * |
| * @details Get option structure referred by format string. |
| * |
| * @return Pointer to option structure or NULL in case of an error. |
| **/ |
| TEAM_EXPORT |
| struct team_option *team_get_option(struct team_handle *th, |
| const char *fmt, ...) |
| { |
| struct team_option_id opt_id = {}; |
| va_list ap; |
| bool must_exist = true; |
| |
| va_start(ap, fmt); |
| while (*fmt) { |
| switch (*fmt++) { |
| case 'n': /* name */ |
| opt_id.name = va_arg(ap, char *); |
| break; |
| case 'p': /* port_ifindex */ |
| opt_id.port_ifindex = va_arg(ap, uint32_t); |
| opt_id.port_ifindex_used = true; |
| break; |
| case 'a': /* array index */ |
| opt_id.array_index = va_arg(ap, uint32_t); |
| opt_id.array_index_used = true; |
| break; |
| case '!': /* option does not have to exist */ |
| must_exist = false; |
| break; |
| } |
| } |
| va_end(ap); |
| |
| if (!opt_id.name) |
| return NULL; |
| return find_option(th, &opt_id, must_exist); |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * |
| * @details Get next option in list. |
| * |
| * @return Option next to option passed. |
| **/ |
| TEAM_EXPORT |
| struct team_option *team_get_next_option(struct team_handle *th, |
| struct team_option *option) |
| { |
| struct team_option *next_option; |
| |
| next_option = list_get_next_node_entry(&th->option_list, option, list); |
| if (next_option && !next_option->initialized) |
| return team_get_next_option(th, next_option); |
| return next_option; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details See if option values are initialized. |
| * |
| * @return True if option is initialized. |
| **/ |
| TEAM_EXPORT |
| bool team_is_option_initialized(struct team_option *option) |
| { |
| return option->initialized; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option name. |
| * |
| * @return Pointer to string containing option name. |
| **/ |
| TEAM_EXPORT |
| char *team_get_option_name(struct team_option *option) |
| { |
| return option->id.name; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option port ifindex. |
| * |
| * @return Port interface index. |
| * to any port. |
| **/ |
| TEAM_EXPORT |
| uint32_t team_get_option_port_ifindex(struct team_option *option) |
| { |
| return option->id.port_ifindex; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details See if option is per-port. |
| * |
| * @return True if option is per-port. |
| **/ |
| TEAM_EXPORT |
| bool team_is_option_per_port(struct team_option *option) |
| { |
| return option->id.port_ifindex_used; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option array index. |
| * |
| * @return Array index. |
| **/ |
| TEAM_EXPORT |
| uint32_t team_get_option_array_index(struct team_option *option) |
| { |
| return option->id.array_index; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details See if option is array. |
| * |
| * @return True if option is array. |
| **/ |
| TEAM_EXPORT |
| bool team_is_option_array(struct team_option *option) |
| { |
| return option->id.array_index_used; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option type. |
| * |
| * @return Number identificating option type. |
| **/ |
| TEAM_EXPORT |
| enum team_option_type team_get_option_type(struct team_option *option) |
| { |
| return option->type; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details See if option values got changed. |
| * |
| * @return True if option got changed. |
| **/ |
| TEAM_EXPORT |
| bool team_is_option_changed(struct team_option *option) |
| { |
| return option->changed; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details See if option values got changed locally. |
| * |
| * @return True if option got changed locally. |
| **/ |
| TEAM_EXPORT |
| bool team_is_option_changed_locally(struct team_option *option) |
| { |
| return option->changed_locally; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option value length. |
| * |
| * @return Option value length. |
| **/ |
| TEAM_EXPORT |
| unsigned int team_get_option_value_len(struct team_option *option) |
| { |
| return option->data_len; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option value as unsigned 32-bit number. |
| * |
| * @return Number. |
| **/ |
| TEAM_EXPORT |
| uint32_t team_get_option_value_u32(struct team_option *option) |
| { |
| return *((__u32 *) option->data); |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option value as string. |
| * |
| * @return Pointer to string. |
| **/ |
| TEAM_EXPORT |
| char *team_get_option_value_string(struct team_option *option) |
| { |
| return option->data; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option value as void pointer. |
| * |
| * @return Pointer to data. |
| **/ |
| TEAM_EXPORT |
| void *team_get_option_value_binary(struct team_option *option) |
| { |
| return option->data; |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option value as bool. |
| * |
| * @return Bool. |
| **/ |
| TEAM_EXPORT |
| bool team_get_option_value_bool(struct team_option *option) |
| { |
| return *((bool *) option->data); |
| } |
| |
| /** |
| * @param option option structure |
| * |
| * @details Get option value as signed 32-bit number. |
| * |
| * @return Number. |
| **/ |
| TEAM_EXPORT |
| int32_t team_get_option_value_s32(struct team_option *option) |
| { |
| return *((__s32 *) option->data); |
| } |
| |
| static int local_set_option_value(struct team_handle *th, |
| struct team_option_id *opt_id, int opt_type, |
| const void *data, int data_len) |
| { |
| struct team_option *option; |
| int err; |
| |
| err = update_option(th, &option, opt_id, opt_type, |
| data, data_len, true, true); |
| if (err) |
| return err; |
| if (option->temporary) |
| destroy_option(option); |
| return 0; |
| } |
| |
| static int set_option_value(struct team_handle *th, struct team_option *option, |
| const void *data, int data_len, int opt_type) |
| { |
| struct nl_msg *msg; |
| struct nlattr *option_list; |
| struct nlattr *option_item; |
| int nla_type; |
| int err; |
| |
| if (option->initialized && option->type != opt_type) |
| return -EINVAL; |
| |
| switch (opt_type) { |
| case TEAM_OPTION_TYPE_U32: |
| nla_type = NLA_U32; |
| break; |
| case TEAM_OPTION_TYPE_STRING: |
| nla_type = NLA_STRING; |
| break; |
| case TEAM_OPTION_TYPE_BINARY: |
| nla_type = NLA_BINARY; |
| break; |
| case TEAM_OPTION_TYPE_BOOL: |
| nla_type = NLA_FLAG; |
| break; |
| case TEAM_OPTION_TYPE_S32: |
| nla_type = NLA_S32; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| msg = nlmsg_alloc(); |
| if (!msg) |
| return -ENOMEM; |
| |
| genlmsg_put(msg, NL_AUTO_PID, th->nl_sock_seq, th->family, 0, 0, |
| TEAM_CMD_OPTIONS_SET, 0); |
| NLA_PUT_U32(msg, TEAM_ATTR_TEAM_IFINDEX, th->ifindex); |
| option_list = nla_nest_start(msg, TEAM_ATTR_LIST_OPTION); |
| if (!option_list) |
| goto nla_put_failure; |
| option_item = nla_nest_start(msg, TEAM_ATTR_ITEM_OPTION); |
| if (!option_item) |
| goto nla_put_failure; |
| NLA_PUT_STRING(msg, TEAM_ATTR_OPTION_NAME, option->id.name); |
| if (option->id.port_ifindex_used) |
| NLA_PUT_U32(msg, TEAM_ATTR_OPTION_PORT_IFINDEX, |
| option->id.port_ifindex); |
| if (option->id.array_index_used) |
| NLA_PUT_U32(msg, TEAM_ATTR_OPTION_ARRAY_INDEX, |
| option->id.array_index); |
| NLA_PUT_U8(msg, TEAM_ATTR_OPTION_TYPE, nla_type); |
| switch (nla_type) { |
| case NLA_U32: |
| NLA_PUT_U32(msg, TEAM_ATTR_OPTION_DATA, *((__u32 *) data)); |
| break; |
| case NLA_STRING: |
| NLA_PUT_STRING(msg, TEAM_ATTR_OPTION_DATA, (char *) data); |
| break; |
| case NLA_BINARY: |
| NLA_PUT(msg, TEAM_ATTR_OPTION_DATA, data_len, (char *) data); |
| break; |
| case NLA_FLAG: |
| if (*((bool *) data)) |
| NLA_PUT_FLAG(msg, TEAM_ATTR_OPTION_DATA); |
| break; |
| case NLA_S32: |
| NLA_PUT_S32(msg, TEAM_ATTR_OPTION_DATA, *((__u32 *) data)); |
| break; |
| default: |
| goto nla_put_failure; |
| } |
| nla_nest_end(msg, option_item); |
| nla_nest_end(msg, option_list); |
| |
| err = send_and_recv(th, msg, NULL, NULL); |
| if (err) { |
| return err; |
| } |
| |
| err = local_set_option_value(th, &option->id, opt_type, |
| data, data_len); |
| return err; |
| |
| nla_put_failure: |
| nlmsg_free(msg); |
| return -ENOBUFS; |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param val value to be set |
| * |
| * @details Set 32-bit number type option. |
| * |
| * @return Zero on success or negative number in case of an error. |
| **/ |
| TEAM_EXPORT |
| int team_set_option_value_u32(struct team_handle *th, |
| struct team_option *option, uint32_t val) |
| { |
| return set_option_value(th, option, &val, 0, |
| TEAM_OPTION_TYPE_U32); |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param str string to be set |
| * |
| * @details Set string type option. |
| * |
| * @return Zero on success or negative number in case of an error. |
| **/ |
| TEAM_EXPORT |
| int team_set_option_value_string(struct team_handle *th, |
| struct team_option *option, const char *str) |
| { |
| return set_option_value(th, option, str, 0, TEAM_OPTION_TYPE_STRING); |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param data binary data to be set |
| * @param data_len binary data length |
| * |
| * @details Set binary type option. |
| * |
| * @return Zero on success or negative number in case of an error. |
| **/ |
| TEAM_EXPORT |
| int team_set_option_value_binary(struct team_handle *th, |
| struct team_option *option, |
| const void *data, unsigned int data_len) |
| { |
| return set_option_value(th, option, data, data_len, |
| TEAM_OPTION_TYPE_BINARY); |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param val value to be set |
| * |
| * @details Set bool type option. |
| * |
| * @return Zero on success or negative number in case of an error. |
| **/ |
| TEAM_EXPORT |
| int team_set_option_value_bool(struct team_handle *th, |
| struct team_option *option, bool val) |
| { |
| return set_option_value(th, option, &val, 0, TEAM_OPTION_TYPE_BOOL); |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param val value to be set |
| * |
| * @details Set 32-bit signed number type option. |
| * |
| * @return Zero on success or negative number in case of an error. |
| **/ |
| TEAM_EXPORT |
| int team_set_option_value_s32(struct team_handle *th, |
| struct team_option *option, int32_t val) |
| { |
| return set_option_value(th, option, &val, 0, |
| TEAM_OPTION_TYPE_S32); |
| } |
| |
| /** |
| * @} |
| */ |