2867 lines
114 KiB
C
2867 lines
114 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2019 Red Hat, Inc.
|
|
*/
|
|
|
|
#define NM_VALUE_TYPE_DEFINE_FUNCTIONS
|
|
|
|
#include "libnm-core/nm-default-libnm-core.h"
|
|
|
|
#include "nm-team-utils.h"
|
|
|
|
#include "nm-errors.h"
|
|
#include "nm-utils-private.h"
|
|
#include "nm-glib-aux/nm-json-aux.h"
|
|
#include "nm-core-internal.h"
|
|
#include "nm-setting-team.h"
|
|
#include "nm-setting-team-port.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum {
|
|
SET_FIELD_MODE_UNSET = 0,
|
|
SET_FIELD_MODE_SET = 1,
|
|
|
|
/* Sets the field as set, unless the field is at the default.
|
|
* This is the case for API that is called from NMSettingTeam/NMSettingTeamPort.
|
|
* This means, using libnm API to reset the value of a NMSetting to the default,
|
|
* will mark the field as unset.
|
|
* This is different from initializing the field when parsing JSON/GVariant. In
|
|
* that case an explicitly set field (even set to the default value) will be remembered
|
|
* to be set. */
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT = 2,
|
|
} SetFieldModeEnum;
|
|
|
|
typedef enum {
|
|
RESET_JSON_NO = FALSE,
|
|
RESET_JSON_YES = TRUE,
|
|
} ResetJsonEnum;
|
|
|
|
/* we rely on "config" being the first. At various places we iterate over attribute types,
|
|
* starting after "config".*/
|
|
G_STATIC_ASSERT(_NM_TEAM_ATTRIBUTE_0 == 0);
|
|
G_STATIC_ASSERT(NM_TEAM_ATTRIBUTE_CONFIG == 1);
|
|
|
|
static const char *const _valid_names_runner[] = {
|
|
NM_SETTING_TEAM_RUNNER_BROADCAST,
|
|
NM_SETTING_TEAM_RUNNER_ROUNDROBIN,
|
|
NM_SETTING_TEAM_RUNNER_RANDOM,
|
|
NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP,
|
|
NM_SETTING_TEAM_RUNNER_LOADBALANCE,
|
|
NM_SETTING_TEAM_RUNNER_LACP,
|
|
NULL,
|
|
};
|
|
|
|
static const char *const _valid_names_runner_hwaddr_policy[] = {
|
|
NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL,
|
|
NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_BY_ACTIVE,
|
|
NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_ONLY_ACTIVE,
|
|
NULL,
|
|
};
|
|
|
|
static const char *const _valid_names_runner_tx_balancer[] = {
|
|
"basic",
|
|
NULL,
|
|
};
|
|
|
|
static const char *const _valid_names_runner_tx_hash[] = {
|
|
"eth",
|
|
"vlan",
|
|
"ipv4",
|
|
"ipv6",
|
|
"ip",
|
|
"l3",
|
|
"l4",
|
|
"tcp",
|
|
"udp",
|
|
"sctp",
|
|
NULL,
|
|
};
|
|
|
|
static const char *const _valid_names_runner_agg_select_policy[] = {
|
|
"lacp_prio",
|
|
"lacp_prio_stable",
|
|
"bandwidth",
|
|
"count",
|
|
"port_config",
|
|
NULL,
|
|
};
|
|
|
|
typedef struct {
|
|
NMTeamAttribute team_attr;
|
|
const char *const *valid_runners;
|
|
} RunnerCompatElem;
|
|
|
|
static const RunnerCompatElem _runner_compat_lst[] = {
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LOADBALANCE, NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LOADBALANCE, NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LOADBALANCE, NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
{
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY,
|
|
NM_MAKE_STRV(NM_SETTING_TEAM_RUNNER_LACP),
|
|
},
|
|
};
|
|
|
|
typedef struct {
|
|
const char *const *js_keys;
|
|
const char * property_name;
|
|
NMValueTypUnion default_val;
|
|
union {
|
|
struct {
|
|
gint32 min;
|
|
gint32 max;
|
|
} r_int32;
|
|
struct {
|
|
const char *const *valid_names;
|
|
} r_string;
|
|
} range;
|
|
NMTeamAttribute team_attr;
|
|
NMValueType value_type;
|
|
guint8 field_offset;
|
|
guint8 js_keys_len;
|
|
bool for_master : 1;
|
|
bool for_port : 1;
|
|
bool has_range : 1;
|
|
} TeamAttrData;
|
|
|
|
#define TEAM_ATTR_IDX(_is_port, _team_attr) \
|
|
(((!(_is_port) || (_team_attr) < _NM_TEAM_ATTRIBUTE_START) \
|
|
? (int) (_team_attr) \
|
|
: (((int) (_NM_TEAM_ATTRIBUTE_MASTER_NUM - _NM_TEAM_ATTRIBUTE_START)) \
|
|
+ ((int) (_team_attr)))) \
|
|
- 1)
|
|
|
|
#define TEAM_ATTR_IDX_CONFIG (TEAM_ATTR_IDX(FALSE, NM_TEAM_ATTRIBUTE_CONFIG))
|
|
|
|
static const TeamAttrData team_attr_datas[] = {
|
|
|
|
#define _JS_KEYS(...) .js_keys = NM_MAKE_STRV(__VA_ARGS__), .js_keys_len = NM_NARG(__VA_ARGS__)
|
|
|
|
#define _VAL_BOOL(_default) .default_val.v_bool = (_default)
|
|
|
|
#define _VAL_INT32(_default) .default_val.v_int32 = (_default)
|
|
|
|
#define _VAL_INT32_RANGE(_default, _min, _max) \
|
|
_VAL_INT32(_default), .has_range = TRUE, \
|
|
.range.r_int32 = { \
|
|
.min = _min, \
|
|
.max = _max, \
|
|
}
|
|
|
|
#define _VAL_STRING() .default_val.v_string = NULL
|
|
|
|
#define _VAL_STRING_RANGE(_valid_names) \
|
|
_VAL_STRING(), .has_range = TRUE, \
|
|
.range.r_string = { \
|
|
.valid_names = (_valid_names), \
|
|
}
|
|
|
|
#define _VAL_UNSPEC() .default_val.v_string = (NULL)
|
|
|
|
#define _INIT(_is_port, _team_attr, field, _value_type, _property_name, ...) \
|
|
[TEAM_ATTR_IDX(_is_port, _team_attr)] = { \
|
|
.for_master = (_team_attr) < _NM_TEAM_ATTRIBUTE_START || !(_is_port), \
|
|
.for_port = (_team_attr) < _NM_TEAM_ATTRIBUTE_START || (_is_port), \
|
|
.team_attr = (_team_attr), \
|
|
.field_offset = G_STRUCT_OFFSET(NMTeamSetting, _data_priv.field), \
|
|
.value_type = (_value_type), \
|
|
.property_name = ""_property_name \
|
|
"", \
|
|
__VA_ARGS__}
|
|
|
|
_INIT(0, NM_TEAM_ATTRIBUTE_CONFIG, _js_str, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_CONFIG, ),
|
|
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_LINK_WATCHERS,
|
|
link_watchers,
|
|
NM_VALUE_TYPE_UNSPEC,
|
|
NM_SETTING_TEAM_LINK_WATCHERS,
|
|
_JS_KEYS("link_watch"),
|
|
_VAL_UNSPEC(), ),
|
|
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT,
|
|
master.notify_peers_count,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_NOTIFY_PEERS_COUNT,
|
|
_JS_KEYS("notify_peers", "count"),
|
|
_VAL_INT32_RANGE(-1, 0, G_MAXINT32), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL,
|
|
master.notify_peers_interval,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL,
|
|
_JS_KEYS("notify_peers", "interval"),
|
|
_VAL_INT32_RANGE(-1, 0, G_MAXINT32), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT,
|
|
master.mcast_rejoin_count,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_MCAST_REJOIN_COUNT,
|
|
_JS_KEYS("mcast_rejoin", "count"),
|
|
_VAL_INT32_RANGE(-1, 0, G_MAXINT32), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL,
|
|
master.mcast_rejoin_interval,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL,
|
|
_JS_KEYS("mcast_rejoin", "interval"),
|
|
_VAL_INT32_RANGE(-1, 0, G_MAXINT32), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER,
|
|
master.runner,
|
|
NM_VALUE_TYPE_STRING,
|
|
NM_SETTING_TEAM_RUNNER,
|
|
_JS_KEYS("runner", "name"),
|
|
_VAL_STRING_RANGE(_valid_names_runner), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY,
|
|
master.runner_hwaddr_policy,
|
|
NM_VALUE_TYPE_STRING,
|
|
NM_SETTING_TEAM_RUNNER_HWADDR_POLICY,
|
|
_JS_KEYS("runner", "hwaddr_policy"),
|
|
_VAL_STRING_RANGE(_valid_names_runner_hwaddr_policy), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
|
|
master.runner_tx_hash,
|
|
NM_VALUE_TYPE_UNSPEC,
|
|
NM_SETTING_TEAM_RUNNER_TX_HASH,
|
|
_JS_KEYS("runner", "tx_hash"),
|
|
_VAL_UNSPEC(), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER,
|
|
master.runner_tx_balancer,
|
|
NM_VALUE_TYPE_STRING,
|
|
NM_SETTING_TEAM_RUNNER_TX_BALANCER,
|
|
_JS_KEYS("runner", "tx_balancer", "name"),
|
|
_VAL_STRING_RANGE(_valid_names_runner_tx_balancer), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL,
|
|
master.runner_tx_balancer_interval,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL,
|
|
_JS_KEYS("runner", "tx_balancer", "balancing_interval"),
|
|
_VAL_INT32_RANGE(-1, 0, G_MAXINT32), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE,
|
|
master.runner_active,
|
|
NM_VALUE_TYPE_BOOL,
|
|
NM_SETTING_TEAM_RUNNER_ACTIVE,
|
|
_JS_KEYS("runner", "active"),
|
|
_VAL_BOOL(TRUE), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE,
|
|
master.runner_fast_rate,
|
|
NM_VALUE_TYPE_BOOL,
|
|
NM_SETTING_TEAM_RUNNER_FAST_RATE,
|
|
_JS_KEYS("runner", "fast_rate"),
|
|
_VAL_BOOL(FALSE), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO,
|
|
master.runner_sys_prio,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_RUNNER_SYS_PRIO,
|
|
_JS_KEYS("runner", "sys_prio"),
|
|
_VAL_INT32_RANGE(-1, 0, USHRT_MAX + 1), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS,
|
|
master.runner_min_ports,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_RUNNER_MIN_PORTS,
|
|
_JS_KEYS("runner", "min_ports"),
|
|
_VAL_INT32_RANGE(-1, 1, UCHAR_MAX + 1), ),
|
|
_INIT(0,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY,
|
|
master.runner_agg_select_policy,
|
|
NM_VALUE_TYPE_STRING,
|
|
NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY,
|
|
_JS_KEYS("runner", "agg_select_policy"),
|
|
_VAL_STRING_RANGE(_valid_names_runner_agg_select_policy), ),
|
|
|
|
_INIT(1,
|
|
NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID,
|
|
port.queue_id,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_PORT_QUEUE_ID,
|
|
_JS_KEYS("queue_id"),
|
|
_VAL_INT32_RANGE(-1, 0, G_MAXINT32), ),
|
|
_INIT(1,
|
|
NM_TEAM_ATTRIBUTE_PORT_PRIO,
|
|
port.prio,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_PORT_PRIO,
|
|
_JS_KEYS("prio"),
|
|
_VAL_INT32(0), ),
|
|
_INIT(1,
|
|
NM_TEAM_ATTRIBUTE_PORT_STICKY,
|
|
port.sticky,
|
|
NM_VALUE_TYPE_BOOL,
|
|
NM_SETTING_TEAM_PORT_STICKY,
|
|
_JS_KEYS("sticky"),
|
|
_VAL_BOOL(FALSE), ),
|
|
_INIT(1,
|
|
NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO,
|
|
port.lacp_prio,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_PORT_LACP_PRIO,
|
|
_JS_KEYS("lacp_prio"),
|
|
_VAL_INT32_RANGE(-1, 0, USHRT_MAX + 1), ),
|
|
_INIT(1,
|
|
NM_TEAM_ATTRIBUTE_PORT_LACP_KEY,
|
|
port.lacp_key,
|
|
NM_VALUE_TYPE_INT32,
|
|
NM_SETTING_TEAM_PORT_LACP_KEY,
|
|
_JS_KEYS("lacp_key"),
|
|
_VAL_INT32_RANGE(-1, 0, USHRT_MAX + 1), ),
|
|
|
|
#undef _INIT
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef enum {
|
|
LINK_WATCHER_ATTRIBUTE_NAME,
|
|
LINK_WATCHER_ATTRIBUTE_DELAY_UP,
|
|
LINK_WATCHER_ATTRIBUTE_DELAY_DOWN,
|
|
LINK_WATCHER_ATTRIBUTE_INTERVAL,
|
|
LINK_WATCHER_ATTRIBUTE_INIT_WAIT,
|
|
LINK_WATCHER_ATTRIBUTE_MISSED_MAX,
|
|
LINK_WATCHER_ATTRIBUTE_SOURCE_HOST,
|
|
LINK_WATCHER_ATTRIBUTE_TARGET_HOST,
|
|
LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE,
|
|
LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE,
|
|
LINK_WATCHER_ATTRIBUTE_VLANID,
|
|
LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS,
|
|
} LinkWatcherAttribute;
|
|
|
|
#define _EXPECTED_LINK_WATCHER_ATTRIBUTES_ETHTOOL \
|
|
LINK_WATCHER_ATTRIBUTE_NAME, LINK_WATCHER_ATTRIBUTE_DELAY_UP, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN
|
|
#define _EXPECTED_LINK_WATCHER_ATTRIBUTES_NSNA_PING \
|
|
LINK_WATCHER_ATTRIBUTE_NAME, LINK_WATCHER_ATTRIBUTE_INTERVAL, \
|
|
LINK_WATCHER_ATTRIBUTE_INIT_WAIT, LINK_WATCHER_ATTRIBUTE_MISSED_MAX, \
|
|
LINK_WATCHER_ATTRIBUTE_TARGET_HOST
|
|
#define _EXPECTED_LINK_WATCHER_ATTRIBUTES_ARP_PING \
|
|
LINK_WATCHER_ATTRIBUTE_NAME, LINK_WATCHER_ATTRIBUTE_INTERVAL, \
|
|
LINK_WATCHER_ATTRIBUTE_INIT_WAIT, LINK_WATCHER_ATTRIBUTE_MISSED_MAX, \
|
|
LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, LINK_WATCHER_ATTRIBUTE_TARGET_HOST, \
|
|
LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, \
|
|
LINK_WATCHER_ATTRIBUTE_VLANID, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS
|
|
|
|
typedef struct {
|
|
const char * js_key;
|
|
const char * dbus_name;
|
|
NMValueTypUnion default_val;
|
|
LinkWatcherAttribute link_watcher_attr;
|
|
NMValueType value_type;
|
|
} LinkWatcherAttrData;
|
|
|
|
static const LinkWatcherAttrData link_watcher_attr_datas[] = {
|
|
#define _INIT(_link_watcher_attr, _js_key, _dbus_name, _value_type, ...) \
|
|
[_link_watcher_attr] = {.link_watcher_attr = (_link_watcher_attr), \
|
|
.value_type = (_value_type), \
|
|
.js_key = (""_js_key \
|
|
""), \
|
|
.dbus_name = (""_dbus_name \
|
|
""), \
|
|
__VA_ARGS__}
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_NAME, "name", "name", NM_VALUE_TYPE_STRING, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_DELAY_UP, "delay_up", "delay-up", NM_VALUE_TYPE_INT, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_DELAY_DOWN, "delay_down", "delay-down", NM_VALUE_TYPE_INT, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_INTERVAL, "interval", "interval", NM_VALUE_TYPE_INT, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_INIT_WAIT, "init_wait", "init-wait", NM_VALUE_TYPE_INT, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_MISSED_MAX,
|
|
"missed_max",
|
|
"missed-max",
|
|
NM_VALUE_TYPE_INT,
|
|
.default_val.v_int = 3, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, "source_host", "source-host", NM_VALUE_TYPE_STRING, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_TARGET_HOST, "target_host", "target-host", NM_VALUE_TYPE_STRING, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE,
|
|
"validate_active",
|
|
"validate-active",
|
|
NM_VALUE_TYPE_BOOL, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE,
|
|
"validate_inactive",
|
|
"validate-inactive",
|
|
NM_VALUE_TYPE_BOOL, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_VLANID,
|
|
"vlanid",
|
|
"vlanid",
|
|
NM_VALUE_TYPE_INT,
|
|
.default_val.v_int = -1, ),
|
|
_INIT(LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS, "send_always", "send-always", NM_VALUE_TYPE_BOOL, ),
|
|
#undef _INIT
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const TeamAttrData *_team_attr_data_get(gboolean is_port, NMTeamAttribute team_attr);
|
|
static gpointer _team_setting_get_field(const NMTeamSetting *self, const TeamAttrData *attr_data);
|
|
static void _link_watcher_to_json(const NMTeamLinkWatcher *link_watcher, GString *gstr);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_team_attr_data_ASSERT(const TeamAttrData *attr_data)
|
|
{
|
|
#if NM_MORE_ASSERTS > 5
|
|
nm_assert(attr_data);
|
|
if (attr_data->for_port)
|
|
nm_assert(attr_data == _team_attr_data_get(TRUE, attr_data->team_attr));
|
|
if (attr_data->for_master)
|
|
nm_assert(attr_data == _team_attr_data_get(FALSE, attr_data->team_attr));
|
|
nm_assert((attr_data - team_attr_datas)
|
|
== TEAM_ATTR_IDX(attr_data->for_port, attr_data->team_attr));
|
|
nm_assert(attr_data->value_type > 0);
|
|
nm_assert(attr_data->field_offset < sizeof(NMTeamSetting));
|
|
nm_assert(attr_data->js_keys_len == NM_PTRARRAY_LEN(attr_data->js_keys));
|
|
nm_assert(attr_data->property_name);
|
|
{
|
|
static int checked = 0;
|
|
|
|
if (checked == 0) {
|
|
checked = 1;
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++)
|
|
_team_attr_data_ASSERT(attr_data);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
_team_attr_data_is_relevant(const TeamAttrData *attr_data, gboolean is_port)
|
|
{
|
|
return is_port ? attr_data->for_port : attr_data->for_master;
|
|
}
|
|
|
|
static const TeamAttrData *
|
|
_team_attr_data_get(gboolean is_port, NMTeamAttribute team_attr)
|
|
{
|
|
const int idx = TEAM_ATTR_IDX(is_port, team_attr);
|
|
|
|
nm_assert(idx >= 0 && idx < G_N_ELEMENTS(team_attr_datas));
|
|
nm_assert(team_attr_datas[idx].team_attr == team_attr);
|
|
nm_assert(_team_attr_data_is_relevant(&team_attr_datas[idx], is_port));
|
|
|
|
return &team_attr_datas[idx];
|
|
}
|
|
|
|
static const TeamAttrData *
|
|
_team_attr_data_find_for_property_name(gboolean is_port, const char *property_name)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
|
|
for (attr_data = team_attr_datas; attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
if (_team_attr_data_is_relevant(attr_data, is_port)
|
|
&& nm_streq(property_name, attr_data->property_name))
|
|
return attr_data;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
_team_attr_data_cmp(const TeamAttrData *attr_data,
|
|
gboolean is_port,
|
|
gconstpointer val_a,
|
|
gconstpointer val_b)
|
|
{
|
|
const GPtrArray *v_ptrarray_a;
|
|
const GPtrArray *v_ptrarray_b;
|
|
guint len;
|
|
|
|
_team_attr_data_ASSERT(attr_data);
|
|
nm_assert(val_a);
|
|
nm_assert(val_b);
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
|
|
NM_CMP_RETURN(nm_value_type_cmp(attr_data->value_type, val_a, val_b));
|
|
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
|
|
v_ptrarray_a = *((const GPtrArray *const *) val_a);
|
|
v_ptrarray_b = *((const GPtrArray *const *) val_b);
|
|
len = v_ptrarray_a ? v_ptrarray_a->len : 0u;
|
|
NM_CMP_DIRECT(len, (v_ptrarray_b ? v_ptrarray_b->len : 0u));
|
|
if (len > 0) {
|
|
NM_CMP_RETURN(
|
|
nm_team_link_watchers_cmp((const NMTeamLinkWatcher *const *) v_ptrarray_a->pdata,
|
|
(const NMTeamLinkWatcher *const *) v_ptrarray_b->pdata,
|
|
len,
|
|
FALSE));
|
|
}
|
|
} else if (!is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
v_ptrarray_a = *((const GPtrArray *const *) val_a);
|
|
v_ptrarray_b = *((const GPtrArray *const *) val_b);
|
|
NM_CMP_RETURN(
|
|
nm_utils_strv_cmp_n(v_ptrarray_a ? (const char *const *) v_ptrarray_a->pdata : NULL,
|
|
v_ptrarray_a ? v_ptrarray_a->len : 0u,
|
|
v_ptrarray_b ? (const char *const *) v_ptrarray_b->pdata : NULL,
|
|
v_ptrarray_b ? v_ptrarray_b->len : 0u));
|
|
} else
|
|
nm_assert_not_reached();
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
_team_attr_data_equal(const TeamAttrData *attr_data,
|
|
gboolean is_port,
|
|
gconstpointer val_a,
|
|
gconstpointer val_b)
|
|
{
|
|
return _team_attr_data_cmp(attr_data, is_port, val_a, val_b) == 0;
|
|
}
|
|
|
|
static void
|
|
_team_attr_data_copy(const TeamAttrData *attr_data,
|
|
gboolean is_port,
|
|
gpointer dst,
|
|
gconstpointer src)
|
|
{
|
|
GPtrArray * v_ptrarray_dst;
|
|
const GPtrArray *v_ptrarray_src;
|
|
GPtrArray * dst_array;
|
|
guint i, len;
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
|
|
nm_value_type_copy(attr_data->value_type, dst, src);
|
|
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
|
|
v_ptrarray_src = *((const GPtrArray *const *) src);
|
|
v_ptrarray_dst = *((GPtrArray **) dst);
|
|
len = (v_ptrarray_src ? v_ptrarray_src->len : 0u);
|
|
|
|
if (len == 0) {
|
|
if (v_ptrarray_dst)
|
|
g_ptr_array_set_size(v_ptrarray_dst, 0);
|
|
} else {
|
|
dst_array = g_ptr_array_new_full(len, (GDestroyNotify) nm_team_link_watcher_unref);
|
|
for (i = 0; i < len; i++) {
|
|
if (v_ptrarray_src->pdata[i]) {
|
|
nm_team_link_watcher_ref(v_ptrarray_src->pdata[i]);
|
|
g_ptr_array_add(dst_array, v_ptrarray_src->pdata[i]);
|
|
}
|
|
}
|
|
if (v_ptrarray_dst)
|
|
g_ptr_array_unref(v_ptrarray_dst);
|
|
*((GPtrArray **) dst) = dst_array;
|
|
}
|
|
} else if (!is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
v_ptrarray_src = *((const GPtrArray *const *) src);
|
|
v_ptrarray_dst = *((GPtrArray **) dst);
|
|
len = (v_ptrarray_src ? v_ptrarray_src->len : 0u);
|
|
|
|
if (v_ptrarray_src && v_ptrarray_src->len > 0) {
|
|
dst_array = g_ptr_array_new_full(v_ptrarray_src->len, g_free);
|
|
for (i = 0; i < v_ptrarray_src->len; i++)
|
|
g_ptr_array_add(dst_array, g_strdup(v_ptrarray_src->pdata[i]));
|
|
} else
|
|
dst_array = NULL;
|
|
if (v_ptrarray_dst)
|
|
g_ptr_array_unref(v_ptrarray_dst);
|
|
*((GPtrArray **) dst) = dst_array;
|
|
} else
|
|
nm_assert_not_reached();
|
|
}
|
|
|
|
static void
|
|
_team_attr_data_to_json(const TeamAttrData *attr_data,
|
|
gboolean is_port,
|
|
GString * gstr,
|
|
gconstpointer p_field)
|
|
{
|
|
guint i;
|
|
|
|
_team_attr_data_ASSERT(attr_data);
|
|
nm_assert(p_field);
|
|
|
|
nm_json_gstr_append_obj_name(gstr, attr_data->js_keys[attr_data->js_keys_len - 1], '\0');
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) {
|
|
nm_value_type_to_json(attr_data->value_type, gstr, p_field);
|
|
return;
|
|
}
|
|
|
|
if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
|
|
const GPtrArray *v_ptrarray = *((const GPtrArray *const *) p_field);
|
|
|
|
if (!v_ptrarray)
|
|
g_string_append(gstr, "null");
|
|
else if (v_ptrarray->len == 0)
|
|
g_string_append(gstr, "[ ]");
|
|
else if (v_ptrarray->len == 1)
|
|
_link_watcher_to_json(v_ptrarray->pdata[0], gstr);
|
|
else {
|
|
g_string_append(gstr, "[ ");
|
|
for (i = 0; i < v_ptrarray->len; i++) {
|
|
if (i > 0)
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
_link_watcher_to_json(v_ptrarray->pdata[i], gstr);
|
|
}
|
|
g_string_append(gstr, " ]");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
const GPtrArray *v_ptrarray = *((const GPtrArray *const *) p_field);
|
|
|
|
if (!v_ptrarray)
|
|
g_string_append(gstr, "null");
|
|
else {
|
|
g_string_append(gstr, "[ ");
|
|
for (i = 0; i < v_ptrarray->len; i++) {
|
|
if (i > 0)
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
nm_json_gstr_append_string(gstr, v_ptrarray->pdata[i]);
|
|
}
|
|
g_string_append(gstr, i > 0 ? " ]" : "]");
|
|
}
|
|
return;
|
|
}
|
|
|
|
nm_assert_not_reached();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_team_setting_ASSERT(const NMTeamSetting *self)
|
|
{
|
|
nm_assert(self);
|
|
nm_assert(!self->d._js_str_need_synthetize || !self->d._js_str);
|
|
#if NM_MORE_ASSERTS > 2
|
|
if (!self->d.strict_validated) {
|
|
nm_assert(!self->d._js_str_need_synthetize);
|
|
nm_assert(self->d._js_str);
|
|
}
|
|
nm_assert(self->d.link_watchers);
|
|
nm_assert(self->d.is_port || !self->d.master.runner_tx_hash
|
|
|| self->d.master.runner_tx_hash->len > 0);
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
_team_setting_has_field(const NMTeamSetting *self, const TeamAttrData *attr_data)
|
|
{
|
|
_team_setting_ASSERT(self);
|
|
return NM_FLAGS_ALL(self->d.has_fields_mask, nm_team_attribute_to_flags(attr_data->team_attr));
|
|
}
|
|
|
|
static gboolean
|
|
_team_setting_has_fields_any_v(const NMTeamSetting * self,
|
|
const NMTeamAttribute *team_attrs,
|
|
gsize n_team_attrs)
|
|
{
|
|
gsize i;
|
|
|
|
for (i = 0; i < n_team_attrs; i++) {
|
|
const TeamAttrData *attr_data = _team_attr_data_get(self->d.is_port, team_attrs[i]);
|
|
|
|
if (_team_setting_has_field(self, attr_data))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define _team_setting_has_fields_any(self, ...) \
|
|
_team_setting_has_fields_any_v((self), \
|
|
((const NMTeamAttribute[]){__VA_ARGS__}), \
|
|
NM_NARG(__VA_ARGS__))
|
|
|
|
static void
|
|
_team_setting_has_field_set(NMTeamSetting * self,
|
|
const TeamAttrData *attr_data,
|
|
SetFieldModeEnum set_field_mode)
|
|
{
|
|
guint32 mask = nm_team_attribute_to_flags(attr_data->team_attr);
|
|
|
|
_team_setting_ASSERT(self);
|
|
|
|
switch (set_field_mode) {
|
|
case SET_FIELD_MODE_UNSET:
|
|
goto do_unset;
|
|
case SET_FIELD_MODE_SET:
|
|
goto do_set;
|
|
case SET_FIELD_MODE_SET_UNLESS_DEFAULT:
|
|
if (_team_attr_data_equal(attr_data,
|
|
self->d.is_port,
|
|
_team_setting_get_field(self, attr_data),
|
|
&attr_data->default_val))
|
|
goto do_unset;
|
|
goto do_set;
|
|
}
|
|
nm_assert_not_reached();
|
|
|
|
do_unset:
|
|
self->_data_priv.has_fields_mask &= ~mask;
|
|
return;
|
|
do_set:
|
|
self->_data_priv.has_fields_mask |= mask;
|
|
}
|
|
|
|
static gpointer
|
|
_team_setting_get_field(const NMTeamSetting *self, const TeamAttrData *attr_data)
|
|
{
|
|
_team_setting_ASSERT(self);
|
|
_team_attr_data_ASSERT(attr_data);
|
|
nm_assert(_team_attr_data_is_relevant(attr_data, self->d.is_port));
|
|
|
|
#if NM_MORE_ASSERTS > 5
|
|
if (attr_data->for_master && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO)
|
|
nm_assert((gpointer)(((char *) self) + attr_data->field_offset)
|
|
== &self->d.master.runner_sys_prio);
|
|
#endif
|
|
|
|
return (((char *) self) + attr_data->field_offset);
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_attribute_changed(NMTeamSetting * self,
|
|
const TeamAttrData *attr_data,
|
|
gboolean changed,
|
|
SetFieldModeEnum set_field_mode,
|
|
ResetJsonEnum reset_json)
|
|
{
|
|
guint32 changed_flags;
|
|
|
|
_team_setting_has_field_set(self, attr_data, set_field_mode);
|
|
|
|
if (!reset_json) {
|
|
return changed ? nm_team_attribute_to_flags(attr_data->team_attr) : 0u;
|
|
}
|
|
|
|
if (!changed) {
|
|
/* a regular attribute was set, but the value did not change.
|
|
*
|
|
* If we previously were in non-strict mode, then
|
|
*
|
|
* - switch to strict-mode. Clearly the user set a regular attribute
|
|
* and hence now we want to validate the setting.
|
|
*
|
|
* - clear the JSON string. We need to regenerate it.
|
|
*/
|
|
if (self->_data_priv.strict_validated)
|
|
return 0;
|
|
changed_flags = nm_team_attribute_to_flags(NM_TEAM_ATTRIBUTE_CONFIG);
|
|
} else {
|
|
changed_flags = nm_team_attribute_to_flags(attr_data->team_attr)
|
|
| nm_team_attribute_to_flags(NM_TEAM_ATTRIBUTE_CONFIG);
|
|
}
|
|
|
|
nm_clear_g_free((char **) &self->_data_priv._js_str);
|
|
self->_data_priv.strict_validated = TRUE;
|
|
self->_data_priv._js_str_need_synthetize = TRUE;
|
|
|
|
return changed_flags;
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_attribute_changed_attr(NMTeamSetting * self,
|
|
NMTeamAttribute team_attr,
|
|
gboolean changed,
|
|
SetFieldModeEnum set_field_mode,
|
|
ResetJsonEnum reset_json)
|
|
{
|
|
return _team_setting_attribute_changed(self,
|
|
_team_attr_data_get(self->d.is_port, team_attr),
|
|
changed,
|
|
set_field_mode,
|
|
reset_json);
|
|
}
|
|
|
|
static gboolean
|
|
_team_setting_field_to_json(const NMTeamSetting *self,
|
|
GString * gstr,
|
|
gboolean prepend_delimiter,
|
|
const TeamAttrData * attr_data)
|
|
{
|
|
if (!_team_setting_has_field(self, attr_data))
|
|
return FALSE;
|
|
|
|
if (prepend_delimiter)
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
_team_attr_data_to_json(attr_data,
|
|
self->d.is_port,
|
|
gstr,
|
|
_team_setting_get_field(self, attr_data));
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_team_setting_fields_to_json_maybe(const NMTeamSetting * self,
|
|
GString * gstr,
|
|
gboolean prepend_delimiter,
|
|
const NMTeamAttribute *team_attrs_lst,
|
|
gsize team_attrs_lst_len)
|
|
{
|
|
gsize i;
|
|
gboolean any_added = FALSE;
|
|
|
|
for (i = 0; i < team_attrs_lst_len; i++) {
|
|
if (_team_setting_field_to_json(self,
|
|
gstr,
|
|
prepend_delimiter,
|
|
_team_attr_data_get(self->d.is_port, team_attrs_lst[i]))) {
|
|
any_added = TRUE;
|
|
prepend_delimiter = TRUE;
|
|
}
|
|
}
|
|
return any_added;
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_set(NMTeamSetting * self,
|
|
gboolean modify,
|
|
const bool * has_lst,
|
|
const NMValueTypUnion *val_lst)
|
|
{
|
|
guint32 changed_flags = 0;
|
|
const TeamAttrData *attr_data;
|
|
|
|
nm_assert((!has_lst) == (!val_lst));
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
const NMValueTypUnion *p_val;
|
|
gconstpointer p_field;
|
|
gboolean has_field;
|
|
|
|
if (!_team_attr_data_is_relevant(attr_data, self->d.is_port))
|
|
continue;
|
|
|
|
has_field = (has_lst && has_lst[attr_data->team_attr]);
|
|
|
|
p_val = has_field ? &val_lst[attr_data->team_attr] : &attr_data->default_val;
|
|
|
|
p_field = _team_setting_get_field(self, attr_data);
|
|
|
|
if (!_team_attr_data_equal(attr_data, self->d.is_port, p_val, p_field)) {
|
|
if (modify) {
|
|
_team_attr_data_copy(attr_data, self->d.is_port, (gpointer) p_field, p_val);
|
|
}
|
|
changed_flags |= nm_team_attribute_to_flags(attr_data->team_attr);
|
|
}
|
|
|
|
if (modify) {
|
|
_team_setting_has_field_set(self,
|
|
attr_data,
|
|
has_field ? SET_FIELD_MODE_SET : SET_FIELD_MODE_UNSET);
|
|
}
|
|
}
|
|
|
|
return changed_flags;
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_check_default(const NMTeamSetting *self)
|
|
{
|
|
return _team_setting_set((NMTeamSetting *) self, FALSE, NULL, NULL);
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_set_default(NMTeamSetting *self)
|
|
{
|
|
return _team_setting_set(self, TRUE, NULL, NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gconstpointer
|
|
_nm_team_setting_value_get(const NMTeamSetting *self,
|
|
NMTeamAttribute team_attr,
|
|
NMValueType value_type)
|
|
{
|
|
const TeamAttrData *attr_data = _team_attr_data_get(self->d.is_port, team_attr);
|
|
|
|
nm_assert(value_type == attr_data->value_type);
|
|
|
|
nm_assert(_team_setting_has_field(self, attr_data)
|
|
|| _team_attr_data_equal(attr_data,
|
|
self->d.is_port,
|
|
_team_setting_get_field(self, attr_data),
|
|
&attr_data->default_val));
|
|
return _team_setting_get_field(self, attr_data);
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_value_set(NMTeamSetting * self,
|
|
const TeamAttrData *attr_data,
|
|
gconstpointer val,
|
|
SetFieldModeEnum set_field_mode,
|
|
ResetJsonEnum reset_json)
|
|
{
|
|
gpointer p_field;
|
|
gboolean changed;
|
|
|
|
nm_assert(self);
|
|
_team_attr_data_ASSERT(attr_data);
|
|
nm_assert(val);
|
|
|
|
p_field = _team_setting_get_field(self, attr_data);
|
|
|
|
changed = !_team_attr_data_equal(attr_data, self->d.is_port, p_field, val);
|
|
if (changed)
|
|
nm_value_type_copy(attr_data->value_type, p_field, val);
|
|
return _team_setting_attribute_changed(self, attr_data, changed, set_field_mode, reset_json);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_reset(NMTeamSetting * self,
|
|
NMTeamAttribute team_attr,
|
|
gboolean to_default /* or else unset */)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
|
|
nm_assert(self);
|
|
|
|
attr_data = _team_attr_data_get(self->d.is_port, team_attr);
|
|
|
|
return _team_setting_value_set(self,
|
|
attr_data,
|
|
&attr_data->default_val,
|
|
to_default ? SET_FIELD_MODE_SET : SET_FIELD_MODE_UNSET,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
guint32
|
|
_nm_team_setting_value_set(NMTeamSetting * self,
|
|
NMTeamAttribute team_attr,
|
|
NMValueType value_type,
|
|
gconstpointer val)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
|
|
nm_assert(self);
|
|
|
|
attr_data = _team_attr_data_get(self->d.is_port, team_attr);
|
|
|
|
nm_assert(value_type == attr_data->value_type);
|
|
|
|
return _team_setting_value_set(self,
|
|
attr_data,
|
|
val,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_link_watchers_add(NMTeamSetting *self, const NMTeamLinkWatcher *link_watcher)
|
|
{
|
|
guint i;
|
|
gboolean changed;
|
|
|
|
for (i = 0; i < self->d.link_watchers->len; i++) {
|
|
if (nm_team_link_watcher_equal(self->d.link_watchers->pdata[i], link_watcher)) {
|
|
changed = FALSE;
|
|
goto out;
|
|
}
|
|
}
|
|
changed = TRUE;
|
|
g_ptr_array_add((GPtrArray *) self->d.link_watchers,
|
|
_nm_team_link_watcher_ref((NMTeamLinkWatcher *) link_watcher));
|
|
out:
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_LINK_WATCHERS,
|
|
changed,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_link_watchers_remove_by_value(NMTeamSetting * self,
|
|
const NMTeamLinkWatcher *link_watcher)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < self->d.link_watchers->len; i++) {
|
|
if (nm_team_link_watcher_equal(self->d.link_watchers->pdata[i], link_watcher))
|
|
return nm_team_setting_value_link_watchers_remove(self, i);
|
|
}
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_LINK_WATCHERS,
|
|
FALSE,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_link_watchers_remove(NMTeamSetting *self, guint idx)
|
|
{
|
|
g_ptr_array_remove_index((GPtrArray *) self->d.link_watchers, idx);
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_LINK_WATCHERS,
|
|
TRUE,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_value_link_watchers_set_list(NMTeamSetting * self,
|
|
const NMTeamLinkWatcher *const *arr,
|
|
guint len,
|
|
SetFieldModeEnum set_field_mode,
|
|
ResetJsonEnum reset_json)
|
|
{
|
|
gboolean changed;
|
|
|
|
if (self->d.link_watchers->len == len
|
|
&& nm_team_link_watchers_cmp(
|
|
(const NMTeamLinkWatcher *const *) self->d.link_watchers->pdata,
|
|
arr,
|
|
len,
|
|
FALSE)
|
|
== 0) {
|
|
changed = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
changed = TRUE;
|
|
if (len == 0)
|
|
g_ptr_array_set_size((GPtrArray *) self->d.link_watchers, 0);
|
|
else {
|
|
_nm_unused gs_unref_ptrarray GPtrArray *old_val_destroy = NULL;
|
|
guint i;
|
|
|
|
old_val_destroy = (GPtrArray *) g_steal_pointer(&self->_data_priv.link_watchers);
|
|
|
|
self->_data_priv.link_watchers =
|
|
g_ptr_array_new_with_free_func((GDestroyNotify) nm_team_link_watcher_unref);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (arr[i]) {
|
|
g_ptr_array_add((GPtrArray *) self->d.link_watchers,
|
|
_nm_team_link_watcher_ref((NMTeamLinkWatcher *) arr[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_LINK_WATCHERS,
|
|
changed,
|
|
set_field_mode,
|
|
reset_json);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_link_watchers_set_list(NMTeamSetting * self,
|
|
const NMTeamLinkWatcher *const *arr,
|
|
guint len)
|
|
{
|
|
return _team_setting_value_link_watchers_set_list(self,
|
|
arr,
|
|
len,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
guint32
|
|
nm_team_setting_value_master_runner_tx_hash_add(NMTeamSetting *self, const char *txhash)
|
|
{
|
|
gboolean changed;
|
|
guint i;
|
|
|
|
if (!self->d.master.runner_tx_hash)
|
|
self->_data_priv.master.runner_tx_hash = g_ptr_array_new_with_free_func(g_free);
|
|
else {
|
|
for (i = 0; i < self->d.master.runner_tx_hash->len; i++) {
|
|
if (nm_streq(txhash, self->d.master.runner_tx_hash->pdata[i])) {
|
|
changed = FALSE;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
changed = TRUE;
|
|
g_ptr_array_add((GPtrArray *) self->d.master.runner_tx_hash, g_strdup(txhash));
|
|
out:
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
|
|
changed,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_master_runner_tx_hash_remove(NMTeamSetting *self, guint idx)
|
|
{
|
|
g_ptr_array_remove_index((GPtrArray *) self->d.master.runner_tx_hash, idx);
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
|
|
TRUE,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
static guint32
|
|
_team_setting_value_master_runner_tx_hash_set_list(NMTeamSetting * self,
|
|
const char *const *arr,
|
|
guint len,
|
|
SetFieldModeEnum set_field_mode,
|
|
ResetJsonEnum reset_json)
|
|
{
|
|
_nm_unused gs_unref_ptrarray GPtrArray *old_val_destroy = NULL;
|
|
gboolean changed;
|
|
guint i;
|
|
|
|
if (nm_utils_strv_cmp_n(self->d.master.runner_tx_hash
|
|
? (const char *const *) self->d.master.runner_tx_hash->pdata
|
|
: NULL,
|
|
self->d.master.runner_tx_hash ? self->d.master.runner_tx_hash->len : 0u,
|
|
arr,
|
|
len)
|
|
== 0) {
|
|
changed = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
changed = TRUE;
|
|
|
|
old_val_destroy = (GPtrArray *) g_steal_pointer(&self->_data_priv.master.runner_tx_hash);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (!arr[i])
|
|
continue;
|
|
if (!self->d.master.runner_tx_hash)
|
|
self->_data_priv.master.runner_tx_hash = g_ptr_array_new_with_free_func(g_free);
|
|
g_ptr_array_add((GPtrArray *) self->d.master.runner_tx_hash, g_strdup(arr[i]));
|
|
}
|
|
|
|
out:
|
|
return _team_setting_attribute_changed_attr(self,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
|
|
changed,
|
|
set_field_mode,
|
|
reset_json);
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_value_master_runner_tx_hash_set_list(NMTeamSetting * self,
|
|
const char *const *arr,
|
|
guint len)
|
|
{
|
|
return _team_setting_value_master_runner_tx_hash_set_list(self,
|
|
arr,
|
|
len,
|
|
SET_FIELD_MODE_SET_UNLESS_DEFAULT,
|
|
RESET_JSON_YES);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define _LINK_WATCHER_ATTR_GET(args, link_watcher_attribute, _value_type) \
|
|
({ \
|
|
const NMValueTypUnioMaybe *const _args = (args); \
|
|
\
|
|
nm_assert(link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \
|
|
\
|
|
_args[(link_watcher_attribute)].has \
|
|
? &_args[(link_watcher_attribute)].val \
|
|
: &link_watcher_attr_datas[(link_watcher_attribute)].default_val; \
|
|
})
|
|
#define _LINK_WATCHER_ATTR_GET_BOOL(args, link_watcher_attribute) \
|
|
(_LINK_WATCHER_ATTR_GET(args, link_watcher_attribute, NM_VALUE_TYPE_BOOL)->v_bool)
|
|
#define _LINK_WATCHER_ATTR_GET_INT(args, link_watcher_attribute) \
|
|
(_LINK_WATCHER_ATTR_GET(args, link_watcher_attribute, NM_VALUE_TYPE_INT)->v_int)
|
|
#define _LINK_WATCHER_ATTR_GET_STRING(args, link_watcher_attribute) \
|
|
(_LINK_WATCHER_ATTR_GET(args, link_watcher_attribute, NM_VALUE_TYPE_STRING)->v_string)
|
|
|
|
#define _LINK_WATCHER_ATTR_SET(args, link_watcher_attribute, _value_type, c_type, val) \
|
|
({ \
|
|
nm_assert(link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \
|
|
\
|
|
NM_VALUE_TYP_UNIO_MAYBE_SET(&(args)[(link_watcher_attribute)], c_type, (val)); \
|
|
})
|
|
#define _LINK_WATCHER_ATTR_SET_BOOL(args, link_watcher_attribute, val) \
|
|
_LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_BOOL, v_bool, (val))
|
|
#define _LINK_WATCHER_ATTR_SET_INT(args, link_watcher_attribute, val) \
|
|
_LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_INT, v_int, (val))
|
|
#define _LINK_WATCHER_ATTR_SET_STRING(args, link_watcher_attribute, val) \
|
|
_LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_STRING, v_string, (val))
|
|
|
|
static void
|
|
_link_watcher_unpack(const NMTeamLinkWatcher *link_watcher,
|
|
NMValueTypUnioMaybe args[static G_N_ELEMENTS(link_watcher_attr_datas)])
|
|
{
|
|
const char * v_name = nm_team_link_watcher_get_name(link_watcher);
|
|
NMTeamLinkWatcherArpPingFlags v_arp_ping_flags;
|
|
|
|
memset(args, 0, sizeof(args[0]) * G_N_ELEMENTS(link_watcher_attr_datas));
|
|
|
|
_LINK_WATCHER_ATTR_SET_STRING(args, LINK_WATCHER_ATTRIBUTE_NAME, v_name);
|
|
|
|
if (nm_streq(v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
|
|
_LINK_WATCHER_ATTR_SET_INT(args,
|
|
LINK_WATCHER_ATTRIBUTE_DELAY_UP,
|
|
nm_team_link_watcher_get_delay_up(link_watcher));
|
|
_LINK_WATCHER_ATTR_SET_INT(args,
|
|
LINK_WATCHER_ATTRIBUTE_DELAY_DOWN,
|
|
nm_team_link_watcher_get_delay_down(link_watcher));
|
|
} else if (NM_IN_STRSET(v_name,
|
|
NM_TEAM_LINK_WATCHER_NSNA_PING,
|
|
NM_TEAM_LINK_WATCHER_ARP_PING)) {
|
|
_LINK_WATCHER_ATTR_SET_INT(args,
|
|
LINK_WATCHER_ATTRIBUTE_INIT_WAIT,
|
|
nm_team_link_watcher_get_init_wait(link_watcher));
|
|
_LINK_WATCHER_ATTR_SET_INT(args,
|
|
LINK_WATCHER_ATTRIBUTE_INTERVAL,
|
|
nm_team_link_watcher_get_interval(link_watcher));
|
|
_LINK_WATCHER_ATTR_SET_INT(args,
|
|
LINK_WATCHER_ATTRIBUTE_MISSED_MAX,
|
|
nm_team_link_watcher_get_missed_max(link_watcher));
|
|
_LINK_WATCHER_ATTR_SET_STRING(args,
|
|
LINK_WATCHER_ATTRIBUTE_TARGET_HOST,
|
|
nm_team_link_watcher_get_target_host(link_watcher));
|
|
if (nm_streq(v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
|
|
v_arp_ping_flags = nm_team_link_watcher_get_flags(link_watcher);
|
|
_LINK_WATCHER_ATTR_SET_INT(args,
|
|
LINK_WATCHER_ATTRIBUTE_VLANID,
|
|
nm_team_link_watcher_get_vlanid(link_watcher));
|
|
_LINK_WATCHER_ATTR_SET_STRING(args,
|
|
LINK_WATCHER_ATTRIBUTE_SOURCE_HOST,
|
|
nm_team_link_watcher_get_source_host(link_watcher));
|
|
_LINK_WATCHER_ATTR_SET_BOOL(
|
|
args,
|
|
LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE,
|
|
NM_FLAGS_HAS(v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE));
|
|
_LINK_WATCHER_ATTR_SET_BOOL(
|
|
args,
|
|
LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE,
|
|
NM_FLAGS_HAS(v_arp_ping_flags,
|
|
NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE));
|
|
_LINK_WATCHER_ATTR_SET_BOOL(
|
|
args,
|
|
LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS,
|
|
NM_FLAGS_HAS(v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_link_watcher_to_json(const NMTeamLinkWatcher *link_watcher, GString *gstr)
|
|
{
|
|
NMValueTypUnioMaybe args[G_N_ELEMENTS(link_watcher_attr_datas)];
|
|
int i;
|
|
gboolean is_first = TRUE;
|
|
|
|
if (!link_watcher) {
|
|
g_string_append(gstr, "null");
|
|
return;
|
|
}
|
|
|
|
_link_watcher_unpack(link_watcher, args);
|
|
|
|
g_string_append(gstr, "{ ");
|
|
|
|
for (i = 0; i < (int) G_N_ELEMENTS(link_watcher_attr_datas); i++) {
|
|
const NMValueTypUnioMaybe *p_val = &args[i];
|
|
const LinkWatcherAttrData *attr_data = &link_watcher_attr_datas[i];
|
|
|
|
if (!p_val->has)
|
|
continue;
|
|
if (nm_value_type_equal(attr_data->value_type, &attr_data->default_val, &p_val->val))
|
|
continue;
|
|
|
|
if (is_first)
|
|
is_first = FALSE;
|
|
else
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
nm_json_gstr_append_obj_name(gstr, attr_data->js_key, '\0');
|
|
nm_value_type_to_json(attr_data->value_type, gstr, &p_val->val);
|
|
}
|
|
|
|
g_string_append(gstr, " }");
|
|
}
|
|
|
|
static NMTeamLinkWatcher *
|
|
_link_watcher_from_json(const NMJsonVt * vt,
|
|
const nm_json_t *root_js_obj,
|
|
gboolean * out_unrecognized_content)
|
|
{
|
|
NMValueTypUnioMaybe args[G_N_ELEMENTS(link_watcher_attr_datas)] = {};
|
|
const char * j_key;
|
|
nm_json_t * j_val;
|
|
const char * v_name;
|
|
NMTeamLinkWatcher * result = NULL;
|
|
|
|
if (!nm_json_is_object(root_js_obj))
|
|
goto fail;
|
|
|
|
nm_json_object_foreach (vt, (nm_json_t *) root_js_obj, j_key, j_val) {
|
|
const LinkWatcherAttrData *attr_data = NULL;
|
|
NMValueTypUnioMaybe * parse_result;
|
|
|
|
if (j_key) {
|
|
int i;
|
|
|
|
for (i = 0; i < (int) G_N_ELEMENTS(link_watcher_attr_datas); i++) {
|
|
if (nm_streq(link_watcher_attr_datas[i].js_key, j_key)) {
|
|
attr_data = &link_watcher_attr_datas[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!attr_data) {
|
|
*out_unrecognized_content = TRUE;
|
|
continue;
|
|
}
|
|
|
|
parse_result = &args[attr_data->link_watcher_attr];
|
|
|
|
if (parse_result->has)
|
|
*out_unrecognized_content = TRUE;
|
|
|
|
if (!nm_value_type_from_json(vt, attr_data->value_type, j_val, &parse_result->val))
|
|
*out_unrecognized_content = TRUE;
|
|
else
|
|
parse_result->has = TRUE;
|
|
}
|
|
|
|
#define _PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES(_parse_results, ...) \
|
|
({ \
|
|
int _i; \
|
|
\
|
|
for (_i = 0; _i < (int) G_N_ELEMENTS((_parse_results)); _i++) { \
|
|
if ((_parse_results)[_i].has && !NM_IN_SET((LinkWatcherAttribute) _i, __VA_ARGS__)) \
|
|
break; \
|
|
} \
|
|
\
|
|
(_i == (int) G_N_ELEMENTS((_parse_results))); \
|
|
})
|
|
|
|
v_name = _LINK_WATCHER_ATTR_GET_STRING(args, LINK_WATCHER_ATTRIBUTE_NAME);
|
|
|
|
if (nm_streq0(v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
|
|
if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES(args,
|
|
_EXPECTED_LINK_WATCHER_ATTRIBUTES_ETHTOOL))
|
|
*out_unrecognized_content = TRUE;
|
|
result = nm_team_link_watcher_new_ethtool(
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_DELAY_UP),
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN),
|
|
NULL);
|
|
} else if (nm_streq0(v_name, NM_TEAM_LINK_WATCHER_NSNA_PING)) {
|
|
if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES(args,
|
|
_EXPECTED_LINK_WATCHER_ATTRIBUTES_NSNA_PING))
|
|
*out_unrecognized_content = TRUE;
|
|
result = nm_team_link_watcher_new_nsna_ping(
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_INTERVAL),
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
|
|
_LINK_WATCHER_ATTR_GET_STRING(args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
|
|
NULL);
|
|
} else if (nm_streq0(v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
|
|
NMTeamLinkWatcherArpPingFlags v_flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE;
|
|
|
|
if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES(args,
|
|
_EXPECTED_LINK_WATCHER_ATTRIBUTES_ARP_PING))
|
|
*out_unrecognized_content = TRUE;
|
|
|
|
if (_LINK_WATCHER_ATTR_GET_BOOL(args, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE))
|
|
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
|
|
if (_LINK_WATCHER_ATTR_GET_BOOL(args, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE))
|
|
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE;
|
|
if (_LINK_WATCHER_ATTR_GET_BOOL(args, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS))
|
|
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
|
|
|
|
result = nm_team_link_watcher_new_arp_ping2(
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_INTERVAL),
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
|
|
_LINK_WATCHER_ATTR_GET_INT(args, LINK_WATCHER_ATTRIBUTE_VLANID),
|
|
_LINK_WATCHER_ATTR_GET_STRING(args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
|
|
_LINK_WATCHER_ATTR_GET_STRING(args, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST),
|
|
v_flags,
|
|
NULL);
|
|
}
|
|
|
|
if (result)
|
|
return result;
|
|
fail:
|
|
*out_unrecognized_content = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static GVariant *
|
|
_link_watcher_to_variant(const NMTeamLinkWatcher *link_watcher)
|
|
{
|
|
NMValueTypUnioMaybe args[G_N_ELEMENTS(link_watcher_attr_datas)];
|
|
GVariantBuilder builder;
|
|
int i;
|
|
|
|
if (!link_watcher)
|
|
return NULL;
|
|
|
|
_link_watcher_unpack(link_watcher, args);
|
|
|
|
if (!args[LINK_WATCHER_ATTRIBUTE_NAME].has)
|
|
return NULL;
|
|
|
|
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
|
|
|
|
for (i = 0; i < (int) G_N_ELEMENTS(link_watcher_attr_datas); i++) {
|
|
const NMValueTypUnioMaybe *p_val = &args[i];
|
|
const LinkWatcherAttrData *attr_data = &link_watcher_attr_datas[i];
|
|
GVariant * v;
|
|
|
|
if (!p_val->has)
|
|
continue;
|
|
if (nm_value_type_equal(attr_data->value_type, &attr_data->default_val, &p_val->val))
|
|
continue;
|
|
|
|
if (attr_data->value_type == NM_VALUE_TYPE_INT)
|
|
v = g_variant_new_int32(p_val->val.v_int);
|
|
else {
|
|
v = nm_value_type_to_variant(attr_data->value_type, &p_val->val);
|
|
}
|
|
if (!v)
|
|
continue;
|
|
|
|
nm_assert(g_variant_is_floating(v));
|
|
g_variant_builder_add(&builder, "{sv}", attr_data->dbus_name, v);
|
|
}
|
|
|
|
return g_variant_builder_end(&builder);
|
|
}
|
|
|
|
#define _LINK_WATCHER_ATTR_VARGET(variants, link_watcher_attribute, _value_type, c_type, _cmd) \
|
|
({ \
|
|
GVariant *const *_variants = (variants); \
|
|
GVariant * _cc; \
|
|
\
|
|
nm_assert(link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \
|
|
\
|
|
(_cc = _variants[(link_watcher_attribute)]) \
|
|
? (_cmd) \
|
|
: link_watcher_attr_datas[(link_watcher_attribute)].default_val.c_type; \
|
|
})
|
|
#define _LINK_WATCHER_ATTR_VARGET_BOOL(variants, link_watcher_attribute) \
|
|
(_LINK_WATCHER_ATTR_VARGET(variants, \
|
|
link_watcher_attribute, \
|
|
NM_VALUE_TYPE_BOOL, \
|
|
v_bool, \
|
|
g_variant_get_boolean(_cc)))
|
|
#define _LINK_WATCHER_ATTR_VARGET_INT(variants, link_watcher_attribute) \
|
|
(_LINK_WATCHER_ATTR_VARGET(variants, \
|
|
link_watcher_attribute, \
|
|
NM_VALUE_TYPE_INT, \
|
|
v_int, \
|
|
g_variant_get_int32(_cc)))
|
|
#define _LINK_WATCHER_ATTR_VARGET_STRING(variants, link_watcher_attribute) \
|
|
(_LINK_WATCHER_ATTR_VARGET(variants, \
|
|
link_watcher_attribute, \
|
|
NM_VALUE_TYPE_STRING, \
|
|
v_string, \
|
|
g_variant_get_string(_cc, NULL)))
|
|
|
|
static void
|
|
_variants_list_link_watcher_unref_auto(GVariant *(*p_variants)[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < (int) G_N_ELEMENTS(link_watcher_attr_datas); i++)
|
|
nm_g_variant_unref((*p_variants)[i]);
|
|
}
|
|
|
|
static NMTeamLinkWatcher *
|
|
_link_watcher_from_variant(GVariant *watcher_var, gboolean strict_parsing, GError **error)
|
|
{
|
|
nm_auto(_variants_list_link_watcher_unref_auto)
|
|
GVariant *variants[G_N_ELEMENTS(link_watcher_attr_datas)] = {
|
|
NULL,
|
|
};
|
|
const char * v_key;
|
|
GVariant * v_val;
|
|
const char * v_name;
|
|
GVariantIter iter;
|
|
|
|
g_return_val_if_fail(g_variant_is_of_type(watcher_var, G_VARIANT_TYPE("a{sv}")), NULL);
|
|
|
|
g_variant_iter_init(&iter, watcher_var);
|
|
while (g_variant_iter_next(&iter, "{&sv}", &v_key, &v_val)) {
|
|
_nm_unused gs_unref_variant GVariant *v_val_free = v_val;
|
|
const LinkWatcherAttrData * attr_data = NULL;
|
|
const GVariantType * variant_type;
|
|
int i;
|
|
|
|
for (i = 0; i < (int) G_N_ELEMENTS(link_watcher_attr_datas); i++) {
|
|
if (nm_streq(link_watcher_attr_datas[i].dbus_name, v_key)) {
|
|
attr_data = &link_watcher_attr_datas[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!attr_data) {
|
|
if (strict_parsing) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("invalid D-Bus property \"%s\""),
|
|
v_key);
|
|
return NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (attr_data->value_type == NM_VALUE_TYPE_INT)
|
|
variant_type = G_VARIANT_TYPE_INT32;
|
|
else
|
|
variant_type = nm_value_type_get_variant_type(attr_data->value_type);
|
|
|
|
if (!g_variant_is_of_type(v_val, variant_type)) {
|
|
if (strict_parsing) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("invalid D-Bus property \"%s\""),
|
|
v_key);
|
|
return NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (variants[attr_data->link_watcher_attr]) {
|
|
if (strict_parsing) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("duplicate D-Bus property \"%s\""),
|
|
v_key);
|
|
return NULL;
|
|
}
|
|
g_variant_unref(variants[attr_data->link_watcher_attr]);
|
|
}
|
|
variants[attr_data->link_watcher_attr] = g_steal_pointer(&v_val_free);
|
|
}
|
|
|
|
#define _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES(_type, _variants, _error, ...) \
|
|
({ \
|
|
int _i; \
|
|
gboolean _has_error = FALSE; \
|
|
\
|
|
for (_i = 0; _i < (int) G_N_ELEMENTS((_variants)); _i++) { \
|
|
if ((_variants)[_i] && !NM_IN_SET((LinkWatcherAttribute) _i, __VA_ARGS__)) { \
|
|
_has_error = TRUE; \
|
|
g_set_error(_error, \
|
|
NM_CONNECTION_ERROR, \
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY, \
|
|
_("invalid D-Bus property \"%s\" for \"%s\""), \
|
|
link_watcher_attr_datas[_i].dbus_name, \
|
|
_type); \
|
|
break; \
|
|
} \
|
|
} \
|
|
\
|
|
_has_error; \
|
|
})
|
|
|
|
v_name = _LINK_WATCHER_ATTR_VARGET_STRING(variants, LINK_WATCHER_ATTRIBUTE_NAME);
|
|
|
|
if (nm_streq0(v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
|
|
if (strict_parsing
|
|
&& _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES(v_name,
|
|
variants,
|
|
error,
|
|
_EXPECTED_LINK_WATCHER_ATTRIBUTES_ETHTOOL))
|
|
return NULL;
|
|
return nm_team_link_watcher_new_ethtool(
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_DELAY_UP),
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN),
|
|
strict_parsing ? error : NULL);
|
|
}
|
|
|
|
if (nm_streq0(v_name, NM_TEAM_LINK_WATCHER_NSNA_PING)) {
|
|
if (strict_parsing
|
|
&& _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES(v_name,
|
|
variants,
|
|
error,
|
|
_EXPECTED_LINK_WATCHER_ATTRIBUTES_NSNA_PING))
|
|
return NULL;
|
|
return nm_team_link_watcher_new_nsna_ping(
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_INTERVAL),
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
|
|
_LINK_WATCHER_ATTR_VARGET_STRING(variants, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
|
|
strict_parsing ? error : NULL);
|
|
}
|
|
|
|
if (nm_streq0(v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
|
|
NMTeamLinkWatcherArpPingFlags v_flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE;
|
|
|
|
if (strict_parsing
|
|
&& _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES(v_name,
|
|
variants,
|
|
error,
|
|
_EXPECTED_LINK_WATCHER_ATTRIBUTES_ARP_PING))
|
|
return NULL;
|
|
|
|
if (_LINK_WATCHER_ATTR_VARGET_BOOL(variants, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE))
|
|
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
|
|
if (_LINK_WATCHER_ATTR_VARGET_BOOL(variants, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE))
|
|
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE;
|
|
if (_LINK_WATCHER_ATTR_VARGET_BOOL(variants, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS))
|
|
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
|
|
|
|
return nm_team_link_watcher_new_arp_ping2(
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_INTERVAL),
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
|
|
_LINK_WATCHER_ATTR_VARGET_INT(variants, LINK_WATCHER_ATTRIBUTE_VLANID),
|
|
_LINK_WATCHER_ATTR_VARGET_STRING(variants, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
|
|
_LINK_WATCHER_ATTR_VARGET_STRING(variants, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST),
|
|
v_flags,
|
|
strict_parsing ? error : NULL);
|
|
}
|
|
|
|
if (strict_parsing) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("unknown link-watcher name \"%s\""),
|
|
v_name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* _nm_utils_team_link_watchers_to_variant:
|
|
* @link_watchers: (element-type NMTeamLinkWatcher): array of #NMTeamLinkWatcher
|
|
*
|
|
* Utility function to convert a #GPtrArray of #NMTeamLinkWatcher objects
|
|
* representing link watcher configuration for team devices into a #GVariant
|
|
* of type 'aa{sv}' representing an array of link watchers.
|
|
*
|
|
* Returns: (transfer full): a new floating #GVariant representing link watchers.
|
|
**/
|
|
GVariant *
|
|
_nm_utils_team_link_watchers_to_variant(const GPtrArray *link_watchers)
|
|
{
|
|
GVariantBuilder builder;
|
|
guint i;
|
|
|
|
g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
|
|
if (link_watchers) {
|
|
for (i = 0; i < link_watchers->len; i++) {
|
|
g_variant_builder_add(&builder,
|
|
"@a{sv}",
|
|
_link_watcher_to_variant(link_watchers->pdata[i]));
|
|
}
|
|
}
|
|
return g_variant_builder_end(&builder);
|
|
}
|
|
|
|
/**
|
|
* _nm_utils_team_link_watchers_from_variant:
|
|
* @value: a #GVariant of type 'aa{sv}'
|
|
* @strict_parsing: whether to parse strictly or ignore everything invalid.
|
|
* @error: error reason.
|
|
*
|
|
* Utility function to convert a #GVariant representing a list of team link
|
|
* watchers int a #GPtrArray of #NMTeamLinkWatcher objects.
|
|
*
|
|
* Returns: (transfer full) (element-type NMTeamLinkWatcher): a newly allocated
|
|
* #GPtrArray of #NMTeamLinkWatcher objects.
|
|
*
|
|
* Note that if you provide an @error, then the function can only fail (and return %NULL)
|
|
* or succeed (and not return %NULL). If you don't provide an @error, then the function
|
|
* never returns %NULL.
|
|
**/
|
|
GPtrArray *
|
|
_nm_utils_team_link_watchers_from_variant(GVariant *value, gboolean strict_parsing, GError **error)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
|
|
GVariantIter iter;
|
|
GVariant * watcher_var;
|
|
|
|
g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), NULL);
|
|
|
|
link_watchers = g_ptr_array_new_with_free_func((GDestroyNotify) nm_team_link_watcher_unref);
|
|
|
|
g_variant_iter_init(&iter, value);
|
|
while (g_variant_iter_next(&iter, "@a{sv}", &watcher_var)) {
|
|
_nm_unused gs_unref_variant GVariant *watcher_var_free = watcher_var;
|
|
NMTeamLinkWatcher * watcher;
|
|
|
|
watcher = _link_watcher_from_variant(watcher_var, strict_parsing, error);
|
|
if (error && *error)
|
|
return NULL;
|
|
if (watcher)
|
|
g_ptr_array_add(link_watchers, watcher);
|
|
}
|
|
|
|
return g_steal_pointer(&link_watchers);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nm_team_setting_config_get(const NMTeamSetting *self)
|
|
{
|
|
char *js_str;
|
|
|
|
nm_assert(self);
|
|
|
|
if (G_LIKELY(!self->d._js_str_need_synthetize))
|
|
return self->d._js_str;
|
|
|
|
nm_assert(!self->d._js_str);
|
|
nm_assert(self->d.strict_validated);
|
|
|
|
if (_team_setting_check_default(self) == 0) {
|
|
/* the default is set. We signal this as a NULL JSON string.
|
|
* Nothing to do. */
|
|
js_str = NULL;
|
|
} else {
|
|
gboolean list_is_empty = TRUE;
|
|
GString *gstr;
|
|
|
|
gstr = g_string_new(NULL);
|
|
|
|
g_string_append(gstr, "{ ");
|
|
|
|
if (self->d.is_port) {
|
|
static const NMTeamAttribute attr_lst_port[] = {
|
|
NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID,
|
|
NM_TEAM_ATTRIBUTE_PORT_PRIO,
|
|
NM_TEAM_ATTRIBUTE_PORT_STICKY,
|
|
NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO,
|
|
NM_TEAM_ATTRIBUTE_PORT_LACP_KEY,
|
|
};
|
|
|
|
if (_team_setting_fields_to_json_maybe(self,
|
|
gstr,
|
|
!list_is_empty,
|
|
attr_lst_port,
|
|
G_N_ELEMENTS(attr_lst_port)))
|
|
list_is_empty = FALSE;
|
|
} else {
|
|
static const NMTeamAttribute attr_lst_runner_pt1[] = {
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
|
|
};
|
|
static const NMTeamAttribute attr_lst_runner_pt2[] = {
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL,
|
|
};
|
|
static const NMTeamAttribute attr_lst_runner_pt3[] = {
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS,
|
|
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY,
|
|
};
|
|
static const NMTeamAttribute attr_lst_notify_peers[] = {
|
|
NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT,
|
|
NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL,
|
|
};
|
|
static const NMTeamAttribute attr_lst_mcast_rejoin[] = {
|
|
NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT,
|
|
NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL,
|
|
};
|
|
|
|
if (_team_setting_has_fields_any_v(self,
|
|
attr_lst_runner_pt1,
|
|
G_N_ELEMENTS(attr_lst_runner_pt1))
|
|
|| _team_setting_has_fields_any_v(self,
|
|
attr_lst_runner_pt2,
|
|
G_N_ELEMENTS(attr_lst_runner_pt2))
|
|
|| _team_setting_has_fields_any_v(self,
|
|
attr_lst_runner_pt3,
|
|
G_N_ELEMENTS(attr_lst_runner_pt3))) {
|
|
gboolean list_is_empty2 = TRUE;
|
|
|
|
nm_assert(list_is_empty);
|
|
|
|
nm_json_gstr_append_obj_name(gstr, "runner", '{');
|
|
|
|
if (_team_setting_fields_to_json_maybe(self,
|
|
gstr,
|
|
!list_is_empty2,
|
|
attr_lst_runner_pt1,
|
|
G_N_ELEMENTS(attr_lst_runner_pt1)))
|
|
list_is_empty2 = FALSE;
|
|
|
|
if (_team_setting_has_fields_any_v(self,
|
|
attr_lst_runner_pt2,
|
|
G_N_ELEMENTS(attr_lst_runner_pt2))) {
|
|
if (!list_is_empty2)
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
nm_json_gstr_append_obj_name(gstr, "tx_balancer", '{');
|
|
if (!_team_setting_fields_to_json_maybe(self,
|
|
gstr,
|
|
FALSE,
|
|
attr_lst_runner_pt2,
|
|
G_N_ELEMENTS(attr_lst_runner_pt2)))
|
|
nm_assert_not_reached();
|
|
g_string_append(gstr, " }");
|
|
list_is_empty2 = FALSE;
|
|
}
|
|
|
|
if (_team_setting_fields_to_json_maybe(self,
|
|
gstr,
|
|
!list_is_empty2,
|
|
attr_lst_runner_pt3,
|
|
G_N_ELEMENTS(attr_lst_runner_pt3)))
|
|
list_is_empty2 = FALSE;
|
|
|
|
nm_assert(!list_is_empty2);
|
|
g_string_append(gstr, " }");
|
|
list_is_empty = FALSE;
|
|
}
|
|
|
|
if (_team_setting_has_fields_any_v(self,
|
|
attr_lst_notify_peers,
|
|
G_N_ELEMENTS(attr_lst_notify_peers))) {
|
|
if (!list_is_empty)
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
nm_json_gstr_append_obj_name(gstr, "notify_peers", '{');
|
|
if (!_team_setting_fields_to_json_maybe(self,
|
|
gstr,
|
|
FALSE,
|
|
attr_lst_notify_peers,
|
|
G_N_ELEMENTS(attr_lst_notify_peers)))
|
|
nm_assert_not_reached();
|
|
g_string_append(gstr, " }");
|
|
list_is_empty = FALSE;
|
|
}
|
|
|
|
if (_team_setting_has_fields_any_v(self,
|
|
attr_lst_mcast_rejoin,
|
|
G_N_ELEMENTS(attr_lst_mcast_rejoin))) {
|
|
if (!list_is_empty)
|
|
nm_json_gstr_append_delimiter(gstr);
|
|
nm_json_gstr_append_obj_name(gstr, "mcast_rejoin", '{');
|
|
if (!_team_setting_fields_to_json_maybe(self,
|
|
gstr,
|
|
FALSE,
|
|
attr_lst_mcast_rejoin,
|
|
G_N_ELEMENTS(attr_lst_mcast_rejoin)))
|
|
nm_assert_not_reached();
|
|
g_string_append(gstr, " }");
|
|
list_is_empty = FALSE;
|
|
}
|
|
}
|
|
|
|
if (_team_setting_field_to_json(
|
|
self,
|
|
gstr,
|
|
!list_is_empty,
|
|
_team_attr_data_get(self->d.is_port, NM_TEAM_ATTRIBUTE_LINK_WATCHERS)))
|
|
list_is_empty = FALSE;
|
|
|
|
if (!list_is_empty)
|
|
g_string_append(gstr, " }");
|
|
|
|
js_str = g_string_free(gstr, list_is_empty);
|
|
}
|
|
|
|
/* mutate the constant object. In C++ speak, these fields are "mutable".
|
|
* That is because we construct the JSON string lazily/on-demand. */
|
|
*((char **) &self->_data_priv._js_str) = js_str;
|
|
*((bool *) &self->_data_priv._js_str_need_synthetize) = FALSE;
|
|
|
|
return self->d._js_str;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_attr_data_match_keys(const TeamAttrData *attr_data, const char *const *keys, guint8 n_keys)
|
|
{
|
|
guint8 i;
|
|
|
|
_team_attr_data_ASSERT(attr_data);
|
|
nm_assert(keys);
|
|
nm_assert(n_keys > 0);
|
|
nm_assert(({
|
|
gboolean all_non_null = TRUE;
|
|
|
|
for (i = 0; i < n_keys; i++)
|
|
all_non_null = all_non_null && keys[i] && keys[i][0] != '\0';
|
|
all_non_null;
|
|
}));
|
|
|
|
if (attr_data->js_keys_len < n_keys)
|
|
return FALSE;
|
|
for (i = 0; i < n_keys; i++) {
|
|
if (!nm_streq(keys[i], attr_data->js_keys[i]))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static const TeamAttrData *
|
|
_attr_data_find_by_json_key(gboolean is_port, const char *const *keys, guint8 n_keys)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
if (_team_attr_data_is_relevant(attr_data, is_port)
|
|
&& _attr_data_match_keys(attr_data, keys, n_keys))
|
|
return attr_data;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_js_parse_locate_keys(const NMJsonVt *vt,
|
|
NMTeamSetting * self,
|
|
nm_json_t * root_js_obj,
|
|
nm_json_t * found_keys[static _NM_TEAM_ATTRIBUTE_NUM],
|
|
gboolean * out_unrecognized_content)
|
|
{
|
|
const char *keys[3];
|
|
const char *cur_key1;
|
|
const char *cur_key2;
|
|
const char *cur_key3;
|
|
nm_json_t * cur_val1;
|
|
nm_json_t * cur_val2;
|
|
nm_json_t * cur_val3;
|
|
|
|
nm_assert(vt);
|
|
|
|
#define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content) \
|
|
({ \
|
|
const TeamAttrData *_attr_data; \
|
|
gboolean _handled = FALSE; \
|
|
\
|
|
(_keys)[(_level) -1] = (_cur_key); \
|
|
_attr_data = _attr_data_find_by_json_key((_self)->d.is_port, (_keys), (_level)); \
|
|
if (_attr_data && _attr_data->js_keys_len == (_level)) { \
|
|
if ((_found_keys)[_attr_data->team_attr]) \
|
|
*(_out_unrecognized_content) = TRUE; \
|
|
(_found_keys)[_attr_data->team_attr] = (_cur_val); \
|
|
_handled = TRUE; \
|
|
} else if (!_attr_data || !nm_json_is_object((_cur_val))) { \
|
|
*(_out_unrecognized_content) = TRUE; \
|
|
_handled = TRUE; \
|
|
} \
|
|
_handled; \
|
|
})
|
|
|
|
nm_json_object_foreach (vt, root_js_obj, cur_key1, cur_val1) {
|
|
if (!_handle(self, cur_key1, cur_val1, keys, 1, found_keys, out_unrecognized_content)) {
|
|
nm_json_object_foreach (vt, cur_val1, cur_key2, cur_val2) {
|
|
if (!_handle(self,
|
|
cur_key2,
|
|
cur_val2,
|
|
keys,
|
|
2,
|
|
found_keys,
|
|
out_unrecognized_content)) {
|
|
nm_json_object_foreach (vt, cur_val2, cur_key3, cur_val3) {
|
|
if (!_handle(self,
|
|
cur_key3,
|
|
cur_val3,
|
|
keys,
|
|
3,
|
|
found_keys,
|
|
out_unrecognized_content))
|
|
*out_unrecognized_content = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef _handle
|
|
}
|
|
|
|
static void
|
|
_js_parse_unpack(const NMJsonVt *vt,
|
|
gboolean is_port,
|
|
nm_json_t * found_keys[static _NM_TEAM_ATTRIBUTE_NUM],
|
|
bool out_has_lst[static _NM_TEAM_ATTRIBUTE_NUM],
|
|
NMValueTypUnion out_val_lst[static _NM_TEAM_ATTRIBUTE_NUM],
|
|
gboolean * out_unrecognized_content,
|
|
GPtrArray ** out_ptr_array_link_watchers_free,
|
|
GPtrArray ** out_ptr_array_master_runner_tx_hash_free)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
|
|
nm_assert(vt);
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
NMValueTypUnion *p_out_val;
|
|
gboolean valid = FALSE;
|
|
nm_json_t * arg_js_obj;
|
|
|
|
if (!_team_attr_data_is_relevant(attr_data, is_port))
|
|
continue;
|
|
|
|
nm_assert(!out_has_lst[attr_data->team_attr]);
|
|
|
|
arg_js_obj = found_keys[attr_data->team_attr];
|
|
if (!arg_js_obj)
|
|
continue;
|
|
|
|
p_out_val = &out_val_lst[attr_data->team_attr];
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
|
|
valid = nm_value_type_from_json(vt, attr_data->value_type, arg_js_obj, p_out_val);
|
|
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
|
|
GPtrArray * link_watchers = NULL;
|
|
NMTeamLinkWatcher *link_watcher;
|
|
|
|
nm_assert(out_ptr_array_link_watchers_free && !*out_ptr_array_link_watchers_free);
|
|
if (nm_json_is_array(arg_js_obj)) {
|
|
gsize i, len;
|
|
|
|
len = vt->nm_json_array_size(arg_js_obj);
|
|
link_watchers =
|
|
g_ptr_array_new_full(len, (GDestroyNotify) nm_team_link_watcher_unref);
|
|
for (i = 0; i < len; i++) {
|
|
link_watcher = _link_watcher_from_json(vt,
|
|
vt->nm_json_array_get(arg_js_obj, i),
|
|
out_unrecognized_content);
|
|
if (link_watcher)
|
|
g_ptr_array_add(link_watchers, link_watcher);
|
|
}
|
|
} else {
|
|
link_watcher = _link_watcher_from_json(vt, arg_js_obj, out_unrecognized_content);
|
|
if (link_watcher) {
|
|
link_watchers =
|
|
g_ptr_array_new_full(1, (GDestroyNotify) nm_team_link_watcher_unref);
|
|
g_ptr_array_add(link_watchers, link_watcher);
|
|
}
|
|
}
|
|
if (link_watchers) {
|
|
valid = TRUE;
|
|
p_out_val->v_ptrarray = link_watchers;
|
|
*out_ptr_array_link_watchers_free = link_watchers;
|
|
}
|
|
} else if (!is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
GPtrArray *strv = NULL;
|
|
|
|
nm_assert(out_ptr_array_master_runner_tx_hash_free
|
|
&& !*out_ptr_array_master_runner_tx_hash_free);
|
|
if (nm_json_is_array(arg_js_obj)) {
|
|
gsize i, len;
|
|
|
|
len = vt->nm_json_array_size(arg_js_obj);
|
|
if (len > 0) {
|
|
strv = g_ptr_array_sized_new(len);
|
|
for (i = 0; i < len; i++) {
|
|
const char *v_string;
|
|
|
|
if (nm_jansson_json_as_string(vt,
|
|
vt->nm_json_array_get(arg_js_obj, i),
|
|
&v_string)
|
|
<= 0
|
|
|| !v_string || v_string[0] == '\0') {
|
|
/* we remember that there was some invalid content, but parts of the
|
|
* list could still be parsed. */
|
|
*out_unrecognized_content = TRUE;
|
|
continue;
|
|
}
|
|
g_ptr_array_add(strv, (char *) v_string);
|
|
}
|
|
}
|
|
valid = TRUE;
|
|
*out_ptr_array_master_runner_tx_hash_free = strv;
|
|
}
|
|
p_out_val->v_ptrarray = strv;
|
|
} else
|
|
nm_assert_not_reached();
|
|
|
|
out_has_lst[attr_data->team_attr] = valid;
|
|
if (!valid)
|
|
*out_unrecognized_content = TRUE;
|
|
}
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_config_set(NMTeamSetting *self, const char *js_str)
|
|
{
|
|
const NMJsonVt *vt;
|
|
guint32 changed_flags = 0;
|
|
gboolean do_set_default = TRUE;
|
|
gboolean new_js_str_invalid = FALSE;
|
|
|
|
_team_setting_ASSERT(self);
|
|
|
|
if (!js_str || js_str[0] == '\0') {
|
|
changed_flags = _team_setting_set_default(self);
|
|
if (changed_flags != 0 || !nm_streq0(js_str, self->d._js_str))
|
|
changed_flags |= nm_team_attribute_to_flags(NM_TEAM_ATTRIBUTE_CONFIG);
|
|
nm_clear_g_free((char **) &self->_data_priv._js_str);
|
|
self->_data_priv._js_str = g_strdup(js_str);
|
|
self->_data_priv._js_str_need_synthetize = FALSE;
|
|
self->_data_priv.strict_validated = TRUE;
|
|
self->_data_priv.js_str_invalid = FALSE;
|
|
return changed_flags;
|
|
}
|
|
|
|
if (self->d._js_str && nm_streq(js_str, self->d._js_str)) {
|
|
if (!self->d.strict_validated) {
|
|
/* setting the same JSON string twice in a row has no effect. */
|
|
return 0;
|
|
}
|
|
} else
|
|
changed_flags |= nm_team_attribute_to_flags(NM_TEAM_ATTRIBUTE_CONFIG);
|
|
|
|
if ((vt = nm_json_vt())) {
|
|
nm_auto_decref_json nm_json_t *root_js_obj = NULL;
|
|
|
|
root_js_obj = vt->nm_json_loads(js_str, 0, NULL);
|
|
if (!root_js_obj || !nm_json_is_object(root_js_obj))
|
|
new_js_str_invalid = TRUE;
|
|
else {
|
|
gboolean unrecognized_content = FALSE;
|
|
bool has_lst[_NM_TEAM_ATTRIBUTE_NUM] = {
|
|
FALSE,
|
|
};
|
|
NMValueTypUnion val_lst[_NM_TEAM_ATTRIBUTE_NUM];
|
|
nm_json_t * found_keys[_NM_TEAM_ATTRIBUTE_NUM] = {
|
|
NULL,
|
|
};
|
|
gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL;
|
|
gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free = NULL;
|
|
|
|
_js_parse_locate_keys(vt, self, root_js_obj, found_keys, &unrecognized_content);
|
|
|
|
_js_parse_unpack(vt,
|
|
self->d.is_port,
|
|
found_keys,
|
|
has_lst,
|
|
val_lst,
|
|
&unrecognized_content,
|
|
&ptr_array_link_watchers_free,
|
|
&ptr_array_master_runner_tx_hash_free);
|
|
|
|
do_set_default = FALSE;
|
|
|
|
changed_flags |= _team_setting_set(self, TRUE, has_lst, val_lst);
|
|
}
|
|
}
|
|
|
|
if (do_set_default)
|
|
changed_flags |= _team_setting_set_default(self);
|
|
|
|
self->_data_priv.strict_validated = FALSE;
|
|
self->_data_priv._js_str_need_synthetize = FALSE;
|
|
self->_data_priv.js_str_invalid = new_js_str_invalid;
|
|
g_free((char *) self->_data_priv._js_str);
|
|
self->_data_priv._js_str = g_strdup(js_str);
|
|
|
|
return changed_flags;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_team_setting_prefix_error_plain(gboolean is_port, const char *property_name, GError **error)
|
|
{
|
|
g_prefix_error(error,
|
|
"%s.%s: ",
|
|
is_port ? NM_SETTING_TEAM_PORT_SETTING_NAME : NM_SETTING_TEAM_SETTING_NAME,
|
|
property_name);
|
|
}
|
|
|
|
static void
|
|
_team_setting_prefix_error(const NMTeamSetting *self,
|
|
const char * prop_name_master,
|
|
const char * prop_name_port,
|
|
GError ** error)
|
|
{
|
|
_team_setting_ASSERT(self);
|
|
nm_assert(self->d.is_port ? (!!prop_name_port) : (!!prop_name_master));
|
|
_team_setting_prefix_error_plain(self->d.is_port,
|
|
self->d.is_port ? prop_name_port : prop_name_master,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
_team_setting_verify_properties(const NMTeamSetting *self, GError **error)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
guint i;
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
if (!_team_attr_data_is_relevant(attr_data, self->d.is_port))
|
|
continue;
|
|
if (!_team_setting_has_field(self, attr_data))
|
|
continue;
|
|
|
|
if (attr_data->has_range) {
|
|
gconstpointer p_field;
|
|
|
|
p_field = _team_setting_get_field(self, attr_data);
|
|
if (attr_data->value_type == NM_VALUE_TYPE_INT32) {
|
|
gint32 v = *((const gint32 *) p_field);
|
|
|
|
if (v < attr_data->range.r_int32.min || v > attr_data->range.r_int32.max) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("value out or range"));
|
|
_team_setting_prefix_error_plain(self->d.is_port,
|
|
attr_data->property_name,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
} else if (attr_data->value_type == NM_VALUE_TYPE_STRING) {
|
|
const char *v = *((const char *const *) p_field);
|
|
|
|
if (nm_utils_strv_find_first((char **) attr_data->range.r_string.valid_names, -1, v)
|
|
< 0) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("invalid value"));
|
|
_team_setting_prefix_error_plain(self->d.is_port,
|
|
attr_data->property_name,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
} else
|
|
nm_assert_not_reached();
|
|
}
|
|
|
|
if (!self->d.is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
if (self->d.master.runner_tx_hash) {
|
|
for (i = 0; i < self->d.master.runner_tx_hash->len; i++) {
|
|
const char *val = self->d.master.runner_tx_hash->pdata[i];
|
|
|
|
if (!val
|
|
|| (nm_utils_strv_find_first((char **) _valid_names_runner_tx_hash, -1, val)
|
|
< 0)) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("invalid runner-tx-hash"));
|
|
_team_setting_prefix_error_plain(self->d.is_port,
|
|
NM_SETTING_TEAM_RUNNER_TX_HASH,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!self->d.is_port) {
|
|
for (i = 0; i < G_N_ELEMENTS(_runner_compat_lst); i++) {
|
|
const RunnerCompatElem *e = &_runner_compat_lst[i];
|
|
|
|
nm_assert(NM_PTRARRAY_LEN(e->valid_runners) > 0);
|
|
|
|
attr_data = _team_attr_data_get(FALSE, e->team_attr);
|
|
|
|
if (!_team_setting_has_field(self, attr_data))
|
|
continue;
|
|
if (self->d.master.runner
|
|
&& (nm_utils_strv_find_first((char **) e->valid_runners, -1, self->d.master.runner)
|
|
>= 0))
|
|
continue;
|
|
if (e->valid_runners[1] == NULL) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("%s is only allowed for runner %s"),
|
|
attr_data->property_name,
|
|
e->valid_runners[0]);
|
|
} else {
|
|
gs_free char *s = NULL;
|
|
|
|
s = g_strjoinv(",", (char **) e->valid_runners);
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("%s is only allowed for runners %s"),
|
|
attr_data->property_name,
|
|
s);
|
|
}
|
|
_team_setting_prefix_error_plain(self->d.is_port, NM_SETTING_TEAM_RUNNER, error);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
gboolean has_lacp_attrs;
|
|
gboolean has_activebackup_attrs;
|
|
|
|
has_lacp_attrs = _team_setting_has_fields_any(self,
|
|
NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO,
|
|
NM_TEAM_ATTRIBUTE_PORT_LACP_KEY);
|
|
has_activebackup_attrs = _team_setting_has_fields_any(self,
|
|
NM_TEAM_ATTRIBUTE_PORT_PRIO,
|
|
NM_TEAM_ATTRIBUTE_PORT_STICKY);
|
|
if (has_lacp_attrs && has_activebackup_attrs) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("cannot set parameters for lacp and activebackup runners together"));
|
|
_team_setting_prefix_error(self,
|
|
NM_SETTING_TEAM_LINK_WATCHERS,
|
|
NM_SETTING_TEAM_PORT_LINK_WATCHERS,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < self->d.link_watchers->len; i++) {
|
|
if (!self->d.link_watchers->pdata[i]) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("missing link watcher"));
|
|
_team_setting_prefix_error(self,
|
|
NM_SETTING_TEAM_LINK_WATCHERS,
|
|
NM_SETTING_TEAM_PORT_LINK_WATCHERS,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_team_setting_verify_config(const NMTeamSetting *self, GError **error)
|
|
{
|
|
const char *js_str;
|
|
|
|
/* we always materialize the JSON string. That is because we want to validate the
|
|
* string length of the resulting JSON. */
|
|
js_str = nm_team_setting_config_get(self);
|
|
|
|
if (js_str) {
|
|
if (strlen(js_str) > 1 * 1024 * 1024) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("team config exceeds size limit"));
|
|
_team_setting_prefix_error(self,
|
|
NM_SETTING_TEAM_CONFIG,
|
|
NM_SETTING_TEAM_PORT_CONFIG,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
if (!g_utf8_validate(js_str, -1, NULL)) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("team config is not valid UTF-8"));
|
|
_team_setting_prefix_error(self,
|
|
NM_SETTING_TEAM_CONFIG,
|
|
NM_SETTING_TEAM_PORT_CONFIG,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
if (self->d.js_str_invalid) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("invalid json"));
|
|
_team_setting_prefix_error(self,
|
|
NM_SETTING_TEAM_CONFIG,
|
|
NM_SETTING_TEAM_PORT_CONFIG,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_team_setting_verify(const NMTeamSetting *self, GError **error)
|
|
{
|
|
if (self->d.strict_validated) {
|
|
if (!_team_setting_verify_properties(self, error))
|
|
return FALSE;
|
|
}
|
|
return _team_setting_verify_config(self, error);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
nm_team_setting_cmp(const NMTeamSetting *self_a,
|
|
const NMTeamSetting *self_b,
|
|
gboolean ignore_js_str)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
|
|
NM_CMP_SELF(self_a, self_b);
|
|
|
|
NM_CMP_FIELD_UNSAFE(self_a, self_b, d.is_port);
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
if (!_team_attr_data_is_relevant(attr_data, self_a->d.is_port))
|
|
continue;
|
|
|
|
NM_CMP_RETURN(_team_attr_data_cmp(attr_data,
|
|
self_a->d.is_port,
|
|
_team_setting_get_field(self_a, attr_data),
|
|
_team_setting_get_field(self_b, attr_data)));
|
|
}
|
|
|
|
if (!ignore_js_str) {
|
|
NM_CMP_DIRECT_STRCMP0(nm_team_setting_config_get(self_a),
|
|
nm_team_setting_config_get(self_b));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
guint32
|
|
nm_team_setting_reset(NMTeamSetting *self, const NMTeamSetting *src)
|
|
{
|
|
const TeamAttrData *attr_data;
|
|
guint32 changed_flags;
|
|
|
|
_team_setting_ASSERT(self);
|
|
_team_setting_ASSERT(src);
|
|
nm_assert(self->d.is_port == src->d.is_port);
|
|
|
|
if (self == src)
|
|
return 0;
|
|
|
|
changed_flags = 0;
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
if (!_team_attr_data_is_relevant(attr_data, self->d.is_port))
|
|
continue;
|
|
if (_team_attr_data_equal(attr_data,
|
|
self->d.is_port,
|
|
_team_setting_get_field(self, attr_data),
|
|
_team_setting_get_field(src, attr_data)))
|
|
continue;
|
|
_team_attr_data_copy(attr_data,
|
|
self->d.is_port,
|
|
_team_setting_get_field(self, attr_data),
|
|
_team_setting_get_field(src, attr_data));
|
|
changed_flags |= nm_team_attribute_to_flags(attr_data->team_attr);
|
|
}
|
|
|
|
self->_data_priv.has_fields_mask = src->d.has_fields_mask;
|
|
|
|
if (!nm_streq0(self->d._js_str, src->d._js_str)) {
|
|
g_free((char *) self->_data_priv._js_str);
|
|
self->_data_priv._js_str = g_strdup(src->d._js_str);
|
|
changed_flags |= nm_team_attribute_to_flags(NM_TEAM_ATTRIBUTE_CONFIG);
|
|
} else if (changed_flags != 0)
|
|
changed_flags |= nm_team_attribute_to_flags(NM_TEAM_ATTRIBUTE_CONFIG);
|
|
|
|
self->_data_priv._js_str_need_synthetize = src->d._js_str_need_synthetize;
|
|
self->_data_priv.strict_validated = src->d.strict_validated;
|
|
self->_data_priv.js_str_invalid = src->d.js_str_invalid;
|
|
|
|
return changed_flags;
|
|
}
|
|
|
|
static void
|
|
_variants_list_team_unref_auto(GVariant *(*p_variants)[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < _NM_TEAM_ATTRIBUTE_NUM; i++)
|
|
nm_g_variant_unref((*p_variants)[i]);
|
|
}
|
|
|
|
gboolean
|
|
nm_team_setting_reset_from_dbus(NMTeamSetting * self,
|
|
GVariant * setting_dict,
|
|
GHashTable * keys,
|
|
guint32 * out_changed,
|
|
guint /* NMSettingParseFlags */ parse_flags,
|
|
GError ** error)
|
|
{
|
|
nm_auto(_variants_list_team_unref_auto) GVariant *variants[_NM_TEAM_ATTRIBUTE_NUM] = {
|
|
NULL,
|
|
};
|
|
gs_unref_ptrarray GPtrArray *v_link_watchers = NULL;
|
|
const TeamAttrData * attr_data;
|
|
GVariantIter iter;
|
|
const char * v_key;
|
|
GVariant * v_val;
|
|
const NMJsonVt * vt;
|
|
|
|
*out_changed = 0;
|
|
|
|
g_variant_iter_init(&iter, setting_dict);
|
|
while (g_variant_iter_next(&iter, "{&sv}", &v_key, &v_val)) {
|
|
_nm_unused gs_unref_variant GVariant *v_val_free = v_val;
|
|
const GVariantType * variant_type = NULL;
|
|
|
|
attr_data = _team_attr_data_find_for_property_name(self->d.is_port, v_key);
|
|
if (!attr_data) {
|
|
/* _nm_setting_new_from_dbus() already checks for unknown keys. Don't
|
|
* do that here. */
|
|
continue;
|
|
}
|
|
|
|
if (keys)
|
|
g_hash_table_remove(keys, v_key);
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
|
|
variant_type = nm_value_type_get_variant_type(attr_data->value_type);
|
|
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_CONFIG)
|
|
variant_type = G_VARIANT_TYPE_STRING;
|
|
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS)
|
|
variant_type = G_VARIANT_TYPE("aa{sv}");
|
|
else if (!self->d.is_port
|
|
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH)
|
|
variant_type = G_VARIANT_TYPE_STRING_ARRAY;
|
|
else
|
|
nm_assert_not_reached();
|
|
|
|
if (!g_variant_is_of_type(v_val, variant_type)) {
|
|
if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("invalid D-Bus type \"%s\""),
|
|
g_variant_get_type_string(v_val));
|
|
_team_setting_prefix_error_plain(self->d.is_port, attr_data->property_name, error);
|
|
return FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* _nm_setting_new_from_dbus() already checks for duplicate keys. Don't
|
|
* do that here. */
|
|
nm_g_variant_unref(variants[attr_data->team_attr]);
|
|
variants[attr_data->team_attr] = g_steal_pointer(&v_val_free);
|
|
}
|
|
|
|
vt = nm_json_vt();
|
|
|
|
if (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) {
|
|
if (variants[NM_TEAM_ATTRIBUTE_CONFIG] && vt
|
|
&& !NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
|
|
/* we don't require the content of the "link-watchers" and we also
|
|
* don't perform strict validation. No need to parse it. */
|
|
} else {
|
|
gs_free_error GError *local = NULL;
|
|
|
|
/* We might need the parsed v_link_watchers array below (because there is no JSON
|
|
* "config" present or because we don't have json support).
|
|
*
|
|
* Or we might run with NM_SETTING_PARSE_FLAGS_STRICT. In that mode, we may not necessarily
|
|
* require that the entire setting as a whole validates (if a JSON config is present and
|
|
* we are not "strict_validated") , but we require that we can at least parse the link watchers
|
|
* on their own. */
|
|
v_link_watchers = _nm_utils_team_link_watchers_from_variant(
|
|
variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS],
|
|
NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT),
|
|
&local);
|
|
if (local && NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("invalid link-watchers: %s"),
|
|
local->message);
|
|
_team_setting_prefix_error(self,
|
|
NM_SETTING_TEAM_LINK_WATCHERS,
|
|
NM_SETTING_TEAM_PORT_LINK_WATCHERS,
|
|
error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
*out_changed |= nm_team_setting_config_set(
|
|
self,
|
|
variants[NM_TEAM_ATTRIBUTE_CONFIG]
|
|
? g_variant_get_string(variants[NM_TEAM_ATTRIBUTE_CONFIG], NULL)
|
|
: NULL);
|
|
|
|
if (vt && variants[NM_TEAM_ATTRIBUTE_CONFIG]) {
|
|
/* for team settings, the JSON must be able to express all possible options. That means,
|
|
* if the GVariant contains both the JSON "config" and other options, then the other options
|
|
* are silently ignored. */
|
|
} else {
|
|
guint32 extra_changed = 0u;
|
|
|
|
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1];
|
|
attr_data < &team_attr_datas[G_N_ELEMENTS(team_attr_datas)];
|
|
attr_data++) {
|
|
NMValueTypUnion val;
|
|
guint32 changed_flags = 0u;
|
|
|
|
if (!_team_attr_data_is_relevant(attr_data, self->d.is_port))
|
|
continue;
|
|
if (!variants[attr_data->team_attr])
|
|
continue;
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) {
|
|
nm_value_type_get_from_variant(attr_data->value_type,
|
|
&val,
|
|
variants[attr_data->team_attr],
|
|
FALSE);
|
|
changed_flags = _team_setting_value_set(self,
|
|
attr_data,
|
|
&val,
|
|
SET_FIELD_MODE_SET,
|
|
RESET_JSON_NO);
|
|
} else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
|
|
changed_flags = _team_setting_value_link_watchers_set_list(
|
|
self,
|
|
v_link_watchers ? (const NMTeamLinkWatcher *const *) v_link_watchers->pdata
|
|
: NULL,
|
|
v_link_watchers ? v_link_watchers->len : 0u,
|
|
SET_FIELD_MODE_SET,
|
|
RESET_JSON_NO);
|
|
} else if (!self->d.is_port
|
|
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
gs_free const char **strv = NULL;
|
|
gsize len;
|
|
|
|
strv = g_variant_get_strv(variants[attr_data->team_attr], &len);
|
|
changed_flags = _team_setting_value_master_runner_tx_hash_set_list(
|
|
self,
|
|
strv,
|
|
NM_MIN(len, (gsize) G_MAXUINT),
|
|
SET_FIELD_MODE_SET,
|
|
RESET_JSON_NO);
|
|
} else
|
|
nm_assert_not_reached();
|
|
|
|
extra_changed |= changed_flags;
|
|
}
|
|
|
|
if (!variants[NM_TEAM_ATTRIBUTE_CONFIG]) {
|
|
/* clear the JSON string so it can be regenerated. But only if we didn't set
|
|
* it above. */
|
|
self->_data_priv.strict_validated = TRUE;
|
|
self->_data_priv._js_str_need_synthetize = TRUE;
|
|
}
|
|
|
|
*out_changed |= extra_changed;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_team_setting_maybe_changed(NMSetting * source,
|
|
const GParamSpec *const *obj_properties,
|
|
guint32 changed_flags)
|
|
{
|
|
NMTeamAttribute team_attr;
|
|
int count_flags;
|
|
guint32 ch;
|
|
|
|
if (changed_flags == 0u)
|
|
return FALSE;
|
|
|
|
count_flags = 0;
|
|
for (ch = changed_flags; ch != 0u; ch >>= 1) {
|
|
if (NM_FLAGS_HAS(ch, 0x1u))
|
|
count_flags++;
|
|
}
|
|
|
|
if (count_flags > 1)
|
|
g_object_freeze_notify(G_OBJECT(source));
|
|
|
|
ch = changed_flags;
|
|
for (team_attr = 0; team_attr < _NM_TEAM_ATTRIBUTE_NUM; team_attr++) {
|
|
if (!NM_FLAGS_ANY(ch, nm_team_attribute_to_flags(team_attr)))
|
|
continue;
|
|
g_object_notify_by_pspec(G_OBJECT(source), (GParamSpec *) obj_properties[team_attr]);
|
|
ch &= ~nm_team_attribute_to_flags(team_attr);
|
|
if (ch == 0)
|
|
break;
|
|
}
|
|
|
|
if (count_flags > 1)
|
|
g_object_thaw_notify(G_OBJECT(source));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMTeamSetting *
|
|
_nm_setting_get_team_setting(struct _NMSetting *setting)
|
|
{
|
|
if (NM_IS_SETTING_TEAM(setting))
|
|
return _nm_setting_team_get_team_setting(NM_SETTING_TEAM(setting));
|
|
return _nm_setting_team_port_get_team_setting(NM_SETTING_TEAM_PORT(setting));
|
|
}
|
|
|
|
static GVariant *
|
|
_nm_team_settings_property_to_dbus(const NMSettInfoSetting * sett_info,
|
|
guint property_idx,
|
|
NMConnection * connection,
|
|
NMSetting * setting,
|
|
NMConnectionSerializationFlags flags,
|
|
const NMConnectionSerializationOptions *options)
|
|
{
|
|
NMTeamSetting * self = _nm_setting_get_team_setting(setting);
|
|
const TeamAttrData *attr_data =
|
|
_team_attr_data_get(self->d.is_port,
|
|
sett_info->property_infos[property_idx].param_spec->param_id);
|
|
|
|
if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_CONFIG) {
|
|
const char *config;
|
|
|
|
if (self->d.strict_validated && !_nm_utils_is_manager_process) {
|
|
/* if we are in strict validating mode on the client side, the JSON is generated
|
|
* artificially. In this case, don't send the config via D-Bus to the server.
|
|
*
|
|
* This also will cause NetworkManager to strictly validate the settings.
|
|
* If a JSON "config" is present, strict validation won't be performed. */
|
|
return NULL;
|
|
}
|
|
|
|
config = nm_team_setting_config_get(self);
|
|
return config ? g_variant_new_string(config) : NULL;
|
|
}
|
|
|
|
if (!_team_setting_has_field(self, attr_data))
|
|
return NULL;
|
|
|
|
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) {
|
|
return nm_value_type_to_variant(attr_data->value_type,
|
|
_team_setting_get_field(self, attr_data));
|
|
}
|
|
if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS)
|
|
return _nm_utils_team_link_watchers_to_variant(self->d.link_watchers);
|
|
if (!self->d.is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
|
|
return g_variant_new_strv(self->d.master.runner_tx_hash
|
|
? (const char *const *) self->d.master.runner_tx_hash->pdata
|
|
: NULL,
|
|
self->d.master.runner_tx_hash ? self->d.master.runner_tx_hash->len
|
|
: 0u);
|
|
}
|
|
|
|
nm_assert_not_reached();
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_nm_team_settings_property_from_dbus_link_watchers(GVariant *dbus_value, GValue *prop_value)
|
|
{
|
|
g_value_take_boxed(prop_value,
|
|
_nm_utils_team_link_watchers_from_variant(dbus_value, FALSE, NULL));
|
|
}
|
|
|
|
const NMSettInfoPropertType nm_sett_info_propert_type_team_b = {
|
|
.dbus_type = G_VARIANT_TYPE_BOOLEAN,
|
|
.to_dbus_fcn = _nm_team_settings_property_to_dbus,
|
|
};
|
|
|
|
const NMSettInfoPropertType nm_sett_info_propert_type_team_i = {
|
|
.dbus_type = G_VARIANT_TYPE_INT32,
|
|
.to_dbus_fcn = _nm_team_settings_property_to_dbus,
|
|
};
|
|
|
|
const NMSettInfoPropertType nm_sett_info_propert_type_team_s = {
|
|
.dbus_type = G_VARIANT_TYPE_STRING,
|
|
.to_dbus_fcn = _nm_team_settings_property_to_dbus,
|
|
};
|
|
|
|
const NMSettInfoPropertType nm_sett_info_propert_type_team_as = {
|
|
.dbus_type = NM_G_VARIANT_TYPE("as"),
|
|
.to_dbus_fcn = _nm_team_settings_property_to_dbus,
|
|
};
|
|
|
|
const NMSettInfoPropertType nm_sett_info_propert_type_team_link_watchers = {
|
|
.dbus_type = NM_G_VARIANT_TYPE("aa{sv}"),
|
|
.to_dbus_fcn = _nm_team_settings_property_to_dbus,
|
|
.gprop_from_dbus_fcn = _nm_team_settings_property_from_dbus_link_watchers,
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMTeamSetting *
|
|
nm_team_setting_new(gboolean is_port, const char *js_str)
|
|
{
|
|
NMTeamSetting *self;
|
|
gsize l;
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(*self) == sizeof(self->_data_priv));
|
|
G_STATIC_ASSERT_EXPR(sizeof(*self)
|
|
== NM_CONST_MAX(nm_offsetofend(NMTeamSetting, d.master),
|
|
nm_offsetofend(NMTeamSetting, d.port)));
|
|
|
|
l = is_port ? nm_offsetofend(NMTeamSetting, d.port) : nm_offsetofend(NMTeamSetting, d.master);
|
|
|
|
self = g_malloc0(l);
|
|
|
|
self->_data_priv.is_port = is_port;
|
|
self->_data_priv.strict_validated = TRUE;
|
|
self->_data_priv._js_str_need_synthetize = FALSE;
|
|
self->_data_priv.link_watchers =
|
|
g_ptr_array_new_with_free_func((GDestroyNotify) nm_team_link_watcher_unref);
|
|
|
|
_team_setting_ASSERT(self);
|
|
|
|
nm_team_setting_config_set(self, js_str);
|
|
|
|
_team_setting_ASSERT(self);
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
nm_team_setting_free(NMTeamSetting *self)
|
|
{
|
|
if (!self)
|
|
return;
|
|
|
|
_team_setting_ASSERT(self);
|
|
|
|
if (!self->d.is_port) {
|
|
nm_clear_pointer(((GPtrArray **) &self->_data_priv.master.runner_tx_hash),
|
|
g_ptr_array_unref);
|
|
g_free((char *) self->_data_priv.master.runner);
|
|
g_free((char *) self->_data_priv.master.runner_hwaddr_policy);
|
|
g_free((char *) self->_data_priv.master.runner_tx_balancer);
|
|
g_free((char *) self->_data_priv.master.runner_agg_select_policy);
|
|
}
|
|
g_ptr_array_unref((GPtrArray *) self->_data_priv.link_watchers);
|
|
g_free((char *) self->_data_priv._js_str);
|
|
g_free(self);
|
|
}
|