| /* |
| * stringify.c - Helpers for conversion team objects to string |
| * 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 stringify String conversion functions |
| * Helpers for conversion team objects to string |
| * |
| * @{ |
| * |
| * Header |
| * ------ |
| * ~~~~{.c} |
| * #include <team.h> |
| * ~~~~ |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <errno.h> |
| #include <team.h> |
| #include <private/misc.h> |
| #include "team_private.h" |
| |
| static char *__get_port_ifname(struct team_handle *th, uint32_t port_ifindex) |
| { |
| struct team_port *port; |
| |
| team_for_each_port(port, th) { |
| struct team_ifinfo *ifinfo = team_get_port_ifinfo(port); |
| |
| if (port_ifindex == team_get_port_ifindex(port)) |
| return team_get_ifinfo_ifname(ifinfo); |
| } |
| return NULL; |
| } |
| |
| static bool __buf_append(char **pbuf, size_t *pbufsiz, const char *fmt, ...) |
| { |
| va_list ap; |
| size_t n; |
| |
| va_start(ap, fmt); |
| n = vsnprintf(*pbuf, *pbufsiz, fmt, ap); |
| va_end(ap); |
| if (n >= *pbufsiz) |
| return true; |
| *pbuf += n; |
| *pbufsiz -= n; |
| return false; |
| } |
| |
| static bool __team_option_value_str(struct team_option *option, |
| char **pbuf, size_t *pbufsiz, bool plain) |
| { |
| bool trunc; |
| |
| switch (team_get_option_type(option)) { |
| case TEAM_OPTION_TYPE_U32: |
| trunc = __buf_append(pbuf, pbufsiz, "%u", |
| team_get_option_value_u32(option)); |
| if (trunc) |
| return true; |
| break; |
| case TEAM_OPTION_TYPE_STRING: |
| trunc = __buf_append(pbuf, pbufsiz, "%s%s%s", |
| plain ? "" : "\"", |
| team_get_option_value_string(option), |
| plain ? "" : "\""); |
| if (trunc) |
| return true; |
| break; |
| case TEAM_OPTION_TYPE_BINARY: |
| { |
| unsigned int len = team_get_option_value_len(option); |
| char *data = team_get_option_value_binary(option); |
| int i; |
| unsigned char c; |
| |
| for (i = 0; i < len; i++) { |
| c = data[i]; |
| trunc = __buf_append(pbuf, pbufsiz, |
| "\\%02x", c); |
| if (trunc) |
| return true; |
| } |
| } |
| break; |
| case TEAM_OPTION_TYPE_BOOL: |
| trunc = __buf_append(pbuf, pbufsiz, "%s", |
| team_get_option_value_bool(option) ? |
| "true" : "false"); |
| if (trunc) |
| return true; |
| break; |
| case TEAM_OPTION_TYPE_S32: |
| trunc = __buf_append(pbuf, pbufsiz, "%d", |
| team_get_option_value_s32(option)); |
| if (trunc) |
| return true; |
| break; |
| default: |
| trunc = __buf_append(pbuf, pbufsiz, "<unknown>"); |
| if (trunc) |
| return true; |
| break; |
| } |
| return false; |
| } |
| |
| /** |
| * @param option option structure |
| * @param buf buffer where string will be stored |
| * @param bufsiz available buffer size |
| * |
| * @details Converts option value to string. |
| * |
| * @return True in case buffer is not big enough to contain whole string. |
| **/ |
| TEAM_EXPORT |
| bool team_option_value_str(struct team_option *option, char *buf, size_t bufsiz) |
| { |
| return __team_option_value_str(option, &buf, &bufsiz, true); |
| } |
| |
| static int __set_optval_from_str_u32(struct team_handle *th, |
| struct team_option *option, |
| const char *str) |
| { |
| uint32_t val; |
| unsigned long int tmp; |
| char *endptr; |
| |
| tmp = strtoul(str, &endptr, 10); |
| if (tmp == ULONG_MAX) |
| return -errno; |
| if (strlen(endptr) != 0) |
| return -EINVAL; |
| val = tmp; |
| if (tmp != val) |
| return -ERANGE; |
| return team_set_option_value_u32(th, option, val); |
| } |
| |
| static int __set_optval_from_str_s32(struct team_handle *th, |
| struct team_option *option, |
| const char *str) |
| { |
| int32_t val; |
| long int tmp; |
| char *endptr; |
| |
| tmp = strtol(str, &endptr, 10); |
| if (tmp == LONG_MIN || tmp == LONG_MAX) |
| return -errno; |
| if (strlen(endptr) != 0) |
| return -EINVAL; |
| val = tmp; |
| if (tmp != val) |
| return -ERANGE; |
| return team_set_option_value_s32(th, option, val); |
| } |
| |
| static int __one_char_from_str(char *pc, char *byte_str) |
| { |
| unsigned long int tmp; |
| char *endptr; |
| unsigned char c; |
| |
| if (byte_str[0] != '\\') |
| return -EINVAL; |
| |
| tmp = strtoul(byte_str + 1, &endptr, 16); |
| if (tmp == ULONG_MAX) |
| return -errno; |
| if (strlen(endptr) != 0) |
| return -EINVAL; |
| c = tmp; |
| if (tmp != c) |
| return -ERANGE; |
| *pc = c; |
| return 0; |
| } |
| |
| static int __set_optval_from_str_binary(struct team_handle *th, |
| struct team_option *option, |
| const char *str) |
| { |
| char byte_str[4]; |
| char *buf; |
| size_t i; |
| size_t numbytes; |
| int err; |
| |
| if (strlen(str) % 3) |
| return -EINVAL; |
| numbytes = strlen(str) / 3; |
| buf = malloc(numbytes); |
| if (!buf) |
| return -ENOMEM; |
| byte_str[3] = '\0'; |
| for (i = 0; i < numbytes; i++) { |
| memcpy(byte_str, str, 3); |
| err = __one_char_from_str(&buf[i], byte_str); |
| if (err) |
| goto errout; |
| str += 3; |
| } |
| |
| err = team_set_option_value_binary(th, option, buf, numbytes); |
| errout: |
| free(buf); |
| return err; |
| } |
| |
| static int __set_optval_from_str_bool(struct team_handle *th, |
| struct team_option *option, |
| const char *str) |
| { |
| bool val; |
| |
| if (!strcmp(str, "true")) |
| val = true; |
| else if (!strcmp(str, "false")) |
| val = false; |
| else |
| return -EINVAL; |
| return team_set_option_value_bool(th, option, val); |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param str string containing option value |
| * |
| * @details Convert option value from string and set it. |
| * |
| * @return Zero on success or negative number in case of an error. |
| **/ |
| TEAM_EXPORT |
| int team_set_option_value_from_string(struct team_handle *th, |
| struct team_option *option, |
| const char *str) |
| { |
| switch (team_get_option_type(option)) { |
| case TEAM_OPTION_TYPE_U32: |
| return __set_optval_from_str_u32(th, option, str); |
| case TEAM_OPTION_TYPE_STRING: |
| return team_set_option_value_string(th, option, str); |
| case TEAM_OPTION_TYPE_BINARY: |
| return __set_optval_from_str_binary(th, option, str); |
| case TEAM_OPTION_TYPE_BOOL: |
| return __set_optval_from_str_bool(th, option, str); |
| case TEAM_OPTION_TYPE_S32: |
| return __set_optval_from_str_s32(th, option, str); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static bool __team_option_str(struct team_handle *th, |
| struct team_option *option, |
| char **pbuf, size_t *pbufsiz) |
| { |
| char *name = team_get_option_name(option); |
| bool trunc; |
| |
| trunc = __buf_append(pbuf, pbufsiz, "%s%s ", |
| team_is_option_changed(option) ? "*" : " ", name); |
| if (trunc) |
| return true; |
| |
| if (team_is_option_per_port(option)) { |
| char *port_ifname; |
| uint32_t port_ifindex; |
| |
| port_ifindex = team_get_option_port_ifindex(option); |
| port_ifname = __get_port_ifname(th, port_ifindex); |
| if (!port_ifname) |
| port_ifname = ""; |
| trunc = __buf_append(pbuf, pbufsiz, "(port:%s) ", port_ifname); |
| if (trunc) |
| return true; |
| } |
| |
| if (team_is_option_array(option)) { |
| trunc = __buf_append(pbuf, pbufsiz, "(arridx:%u) ", |
| team_get_option_array_index(option)); |
| if (trunc) |
| return true; |
| } |
| |
| trunc = __team_option_value_str(option, pbuf, pbufsiz, true); |
| if (trunc) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * @param th libteam library context |
| * @param option option structure |
| * @param buf buffer where string will be stored |
| * @param bufsiz available buffer size |
| * |
| * @details Converts option structure to string. |
| * |
| * @return True in case buffer is not big enough to contain whole string. |
| **/ |
| TEAM_EXPORT |
| bool team_option_str(struct team_handle *th, struct team_option *option, |
| char *buf, size_t bufsiz) |
| { |
| return __team_option_str(th, option, &buf, &bufsiz); |
| } |
| |
| static bool __team_port_str(struct team_port *port, |
| char **pbuf, size_t *pbufsiz) |
| { |
| uint32_t ifindex = team_get_port_ifindex(port); |
| struct team_ifinfo *ifinfo = team_get_port_ifinfo(port); |
| |
| return __buf_append(pbuf, pbufsiz, "%s%d: %s: %s %uMbit %s", |
| team_is_port_removed(port) ? "-" : |
| team_is_port_changed(port) ? "*" : " ", |
| ifindex, |
| ifinfo ? team_get_ifinfo_ifname(ifinfo) : |
| "(removed)", |
| team_is_port_link_up(port) ? "up": "down", |
| team_get_port_speed(port), |
| team_get_port_duplex(port) ? "FD" : "HD"); |
| } |
| |
| /** |
| * @param port port structure |
| * @param buf buffer where string will be stored |
| * @param bufsiz available buffer size |
| * |
| * @details Converts port structure to string. |
| * |
| * @return True in case buffer is not big enough to contain whole string. |
| **/ |
| TEAM_EXPORT |
| bool team_port_str(struct team_port *port, char *buf, size_t bufsiz) |
| { |
| return __team_port_str(port, &buf, &bufsiz); |
| } |
| |
| static bool __team_ifinfo_str(struct team_ifinfo *ifinfo, |
| char **pbuf, size_t *pbufsiz) |
| { |
| uint32_t ifindex = team_get_ifinfo_ifindex(ifinfo); |
| size_t hwaddr_len = team_get_ifinfo_hwaddr_len(ifinfo); |
| char str[hwaddr_str_len(hwaddr_len)]; |
| |
| hwaddr_str(str, team_get_ifinfo_hwaddr(ifinfo), hwaddr_len); |
| return __buf_append(pbuf, pbufsiz, "%s%d: %s%s: %s%s: %s%d", |
| team_is_ifinfo_changed(ifinfo) ? "*" : " ", |
| ifindex, |
| team_is_ifinfo_ifname_changed(ifinfo) ? "*" : "", |
| team_get_ifinfo_ifname(ifinfo), |
| team_is_ifinfo_hwaddr_len_changed(ifinfo) || |
| team_is_ifinfo_hwaddr_changed(ifinfo) ? "*" : "", |
| str, |
| team_is_ifinfo_master_ifindex_changed(ifinfo) ? "*" : "", |
| team_get_ifinfo_master_ifindex(ifinfo)); |
| } |
| |
| /** |
| * @param ifinfo ifinfo structure |
| * @param buf buffer where string will be stored |
| * @param bufsiz available buffer size |
| * |
| * @details Converts ifinfo structure to string. |
| * |
| * @return True in case buffer is not big enough to contain whole string. |
| **/ |
| TEAM_EXPORT |
| bool team_ifinfo_str(struct team_ifinfo *ifinfo, char *buf, size_t bufsiz) |
| { |
| return __team_ifinfo_str(ifinfo, &buf, &bufsiz); |
| } |
| |
| /** |
| * @} |
| */ |