nmcli: merge branch 'th/nmcli-gsm-fixes'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/827
This commit is contained in:
Thomas Haller 2021-05-03 10:39:53 +02:00
commit 14ca8962fd
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
9 changed files with 3811 additions and 2097 deletions

4
NEWS
View file

@ -10,7 +10,9 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* Add an 'accept-all-mac-addresses' property to the ethernet setting
to accept frames with any MAC address (also known as promiscuous
mode)
mode).
* nmcli: fix setting property aliases to empty value to reset the
default value.
=============================================
NetworkManager-1.30

View file

@ -1205,6 +1205,8 @@ typedef enum {
NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/
NM_UTILS_ERROR_NOT_READY, /*< nick=NotReady >*/
NM_UTILS_ERROR_AMBIGUOUS, /*< nick=Ambiguous >*/
/* the following codes have a special meaning and are exactly used for
* nm_device_check_connection_compatible() and nm_device_check_connection_available().
*

View file

@ -91,36 +91,39 @@ nmc_string_to_uint(const char * str,
gboolean
nmc_string_to_bool(const char *str, gboolean *val_bool, GError **error)
{
const char *s_true[] = {"true", "yes", "on", "1", NULL};
const char *s_false[] = {"false", "no", "off", "0", NULL};
gs_free char *str_to_free = NULL;
int i;
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
nm_assert(!error || !*error);
nm_assert(val_bool);
if (g_strcmp0(str, "o") == 0) {
g_set_error(error,
1,
0,
/* TRANSLATORS: the first %s is the partial value entered by
* the user, the second %s a list of compatible values.
*/
_("'%s' is ambiguous (%s)"),
str,
"on x off");
str = nm_strstrip_avoid_copy_a(300, str, &str_to_free);
if (nm_streq0(str, "o")) {
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
/* TRANSLATORS: the first %s is the partial value entered by
* the user, the second %s a list of compatible values.
*/
_("'%s' is ambiguous (%s)"),
str,
"on, off");
return FALSE;
}
if (nmc_string_is_valid(str, s_true, NULL))
if (nmc_string_is_valid(str, NM_MAKE_STRV("true", "yes", "on"), NULL))
*val_bool = TRUE;
else if (nmc_string_is_valid(str, s_false, NULL))
else if (nmc_string_is_valid(str, NM_MAKE_STRV("false", "no", "off"), NULL))
*val_bool = FALSE;
else {
g_set_error(error,
1,
0,
_("'%s' is not valid; use [%s] or [%s]"),
str,
"true, yes, on",
"false, no, off");
else if ((i = _nm_utils_ascii_str_to_int64(str, 0, 0, 1, -1)) >= 0) {
*val_bool = !!i;
} else {
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
_("'%s' is not valid; use [%s] or [%s]"),
str,
"true, yes, on",
"false, no, off");
return FALSE;
}
return TRUE;
@ -129,40 +132,42 @@ nmc_string_to_bool(const char *str, gboolean *val_bool, GError **error)
gboolean
nmc_string_to_ternary(const char *str, NMTernary *val, GError **error)
{
const char *s_true[] = {"true", "yes", "on", NULL};
const char *s_false[] = {"false", "no", "off", NULL};
const char *s_unknown[] = {"unknown", NULL};
gs_free char *str_to_free = NULL;
int i;
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
nm_assert(!error || !*error);
nm_assert(val);
if (g_strcmp0(str, "o") == 0) {
g_set_error(error,
1,
0,
/* TRANSLATORS: the first %s is the partial value entered by
* the user, the second %s a list of compatible values.
*/
_("'%s' is ambiguous (%s)"),
str,
"on x off");
str = nm_strstrip_avoid_copy_a(300, str, &str_to_free);
if (nm_streq0(str, "o")) {
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
/* TRANSLATORS: the first %s is the partial value entered by
* the user, the second %s a list of compatible values.
*/
_("'%s' is ambiguous (%s)"),
str,
"on, off");
return FALSE;
}
if (nmc_string_is_valid(str, s_true, NULL))
if (nmc_string_is_valid(str, NM_MAKE_STRV("true", "yes", "on"), NULL))
*val = NM_TERNARY_TRUE;
else if (nmc_string_is_valid(str, s_false, NULL))
else if (nmc_string_is_valid(str, NM_MAKE_STRV("false", "no", "off"), NULL))
*val = NM_TERNARY_FALSE;
else if (nmc_string_is_valid(str, s_unknown, NULL))
else if (nmc_string_is_valid(str, NM_MAKE_STRV("unknown", "default"), NULL))
*val = NM_TERNARY_DEFAULT;
else if ((i = _nm_utils_ascii_str_to_int64(str, 0, -1, 1, -2)) >= -1)
*val = (NMTernary) i;
else {
g_set_error(error,
1,
0,
_("'%s' is not valid; use [%s], [%s] or [%s]"),
str,
"true, yes, on",
"false, no, off",
"unknown");
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
_("'%s' is not valid; use [%s], [%s] or [%s]"),
str,
"true, yes, on",
"false, no, off",
"unknown");
return FALSE;
}
return TRUE;
@ -175,12 +180,12 @@ nmc_string_to_ternary(const char *str, NMTernary *val, GError **error)
* On failure: error->code : 0 - string not found; 1 - string is ambiguous
*/
const char *
nmc_string_is_valid(const char *input, const char **allowed, GError **error)
_nmc_string_is_valid(const char *input, const char *const *allowed, GError **error)
{
const char **p;
size_t input_ln, p_len;
const char * partial_match = NULL;
gboolean ambiguous = FALSE;
const char *const *p;
size_t input_ln, p_len;
const char * partial_match = NULL;
gboolean ambiguous = FALSE;
g_return_val_if_fail(!error || !*error, NULL);
@ -210,7 +215,11 @@ nmc_string_is_valid(const char *input, const char **allowed, GError **error)
g_string_append(candidates, *p);
}
}
g_set_error(error, 1, 1, _("'%s' is ambiguous: %s"), input, candidates->str);
nm_utils_error_set(error,
NM_UTILS_ERROR_AMBIGUOUS,
_("'%s' is ambiguous: %s"),
input,
candidates->str);
g_string_free(candidates, TRUE);
return NULL;
}
@ -219,9 +228,16 @@ finish:
char *valid_vals = g_strjoinv(", ", (char **) allowed);
if (!input || !*input)
g_set_error(error, 1, 0, _("missing name, try one of [%s]"), valid_vals);
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
_("missing name, try one of [%s]"),
valid_vals);
else
g_set_error(error, 1, 0, _("'%s' not among [%s]"), input, valid_vals);
nm_utils_error_set(error,
NM_UTILS_ERROR_UNKNOWN,
_("'%s' not among [%s]"),
input,
valid_vals);
g_free(valid_vals);
}

View file

@ -12,7 +12,10 @@
const NMObject **nmc_objects_sort_by_path(const NMObject *const *objs, gssize len);
const char *nmc_string_is_valid(const char *input, const char **allowed, GError **error);
const char *_nmc_string_is_valid(const char *input, const char *const *allowed, GError **error);
#define nmc_string_is_valid(input, allowed, error) \
_nmc_string_is_valid((input), NM_CAST_STRV_CC(allowed), (error))
gboolean nmc_string_to_uint(const char * str,
gboolean range_check,

View file

@ -864,7 +864,7 @@ _get_fcn_gobject_impl(const NMMetaPropertyInfo *property_info,
* signal them differently. */
cstr = g_value_get_string(&val);
nm_assert((!!is_default) == (cstr == NULL));
RETURN_STR_EMPTYUNSET(get_type, is_default, NULL);
RETURN_STR_EMPTYUNSET(get_type, is_default, cstr);
}
}
@ -1258,7 +1258,12 @@ static gboolean _set_fcn_gobject_string(ARGS_SET_FCN)
return TRUE;
}
static gboolean _set_fcn_gobject_bool(ARGS_SET_FCN)
static gboolean
_set_fcn_gobject_bool_impl(const NMMetaPropertyInfo *property_info,
NMSetting * setting,
NMMetaAccessorModifier modifier,
const char * value,
GError ** error)
{
gboolean val_bool;
@ -1272,6 +1277,28 @@ static gboolean _set_fcn_gobject_bool(ARGS_SET_FCN)
return TRUE;
}
static gboolean _set_fcn_gobject_bool(ARGS_SET_FCN)
{
return _set_fcn_gobject_bool_impl(property_info, setting, modifier, value, error);
}
static gboolean _set_fcn_gobject_ternary(ARGS_SET_FCN)
{
NMTernary val;
nm_assert(_gobject_property_get_gtype(G_OBJECT(setting), property_info->property_name)
== NM_TYPE_TERNARY);
if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value))
return _gobject_property_reset_default(setting, property_info->property_name);
if (!nmc_string_to_ternary(value, &val, error))
return FALSE;
g_object_set(setting, property_info->property_name, (int) val, NULL);
return TRUE;
}
static gboolean _set_fcn_gobject_int(ARGS_SET_FCN)
{
int errsv;
@ -1695,6 +1722,28 @@ static const char *const *_complete_fcn_gobject_bool(ARGS_COMPLETE_FCN)
return v;
}
static const char *const *_complete_fcn_gobject_ternary(ARGS_COMPLETE_FCN)
{
static const char *const v[] = {
"on",
"off",
"1",
"0",
"-1",
"yes",
"no",
"unknown",
"true",
"false",
"default",
NULL,
};
if (!text || !text[0])
return &v[8];
return v;
}
static const char *const *_complete_fcn_gobject_devices(ARGS_COMPLETE_FCN)
{
NMDevice *const *devices = NULL;
@ -3052,6 +3101,28 @@ static gboolean _set_fcn_dcb_bool(ARGS_SET_FCN)
return TRUE;
}
static gboolean _set_fcn_gsm_auto_config(ARGS_SET_FCN)
{
if (!_set_fcn_gobject_bool_impl(property_info, setting, modifier, value, error))
return FALSE;
if (nm_setting_gsm_get_auto_config(NM_SETTING_GSM(setting))) {
/* the auto-config flag gets normalized to FALSE, if any of
* APN, username or password is set. Thus, setting auto-config
* needs us to reset those flags too. */
g_object_set(setting,
NM_SETTING_GSM_APN,
NULL,
NM_SETTING_GSM_USERNAME,
NULL,
NM_SETTING_GSM_PASSWORD,
NULL,
NULL);
}
return TRUE;
}
static gboolean _set_fcn_gsm_sim_operator_id(ARGS_SET_FCN)
{
const char *p = value;
@ -4416,6 +4487,12 @@ static const NMMetaPropertyType _pt_gobject_bool = {
.complete_fcn = _complete_fcn_gobject_bool,
};
static const NMMetaPropertyType _pt_gobject_ternary = {
.get_fcn = _get_fcn_gobject_enum,
.set_fcn = _set_fcn_gobject_ternary,
.complete_fcn = _complete_fcn_gobject_ternary,
};
static const NMMetaPropertyType _pt_gobject_int = {
.get_fcn = _get_fcn_gobject_int,
.set_fcn = _set_fcn_gobject_int,
@ -5585,7 +5662,11 @@ static const NMMetaPropertyInfo *const property_infos_ETHTOOL[] = {
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GSM
static const NMMetaPropertyInfo *const property_infos_GSM[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_AUTO_CONFIG,
.property_type = &_pt_gobject_bool,
.property_type = DEFINE_PROPERTY_TYPE (
.get_fcn = _get_fcn_gobject,
.set_fcn = _set_fcn_gsm_auto_config,
.complete_fcn = _complete_fcn_gobject_bool,
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_NUMBER,
.property_type = &_pt_gobject_string,
@ -5611,6 +5692,9 @@ static const NMMetaPropertyInfo *const property_infos_GSM[] = {
.property_alias = "apn",
.prompt = N_("APN"),
.property_type = &_pt_gobject_string,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_string,
.handle_emptyunset = TRUE,
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GSM_NETWORK_ID,
.property_type = &_pt_gobject_string,
@ -5653,13 +5737,13 @@ static const NMMetaPropertyInfo *const property_infos_HOSTNAME[] = {
.property_type = &_pt_gobject_int,
),
PROPERTY_INFO (NM_SETTING_HOSTNAME_FROM_DHCP, DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DHCP,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
PROPERTY_INFO (NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP, DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
PROPERTY_INFO (NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT, DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
NULL
};
@ -6728,7 +6812,7 @@ static const NMMetaPropertyInfo *const property_infos_SRIOV[] = {
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
NULL
};
@ -7456,7 +7540,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
.property_type = &_pt_gobject_mac,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_ACCEPT_ALL_MAC_ADDRESSES,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
NULL
};
@ -7487,10 +7571,10 @@ static const NMMetaPropertyInfo *const property_infos_WIREGUARD[] = {
.property_type = &_pt_gobject_mtu,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
NULL
};
@ -7624,7 +7708,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_AP_ISOLATION,
.property_type = &_pt_gobject_enum,
.property_type = &_pt_gobject_ternary,
),
NULL
};

View file

@ -3532,7 +3532,7 @@ check_valid_name(const char * val,
/* Check string validity */
str = nmc_string_is_valid(val, (const char **) tmp_arr->pdata, &tmp_err);
if (!str) {
if (tmp_err->code == 1)
if (nm_g_error_matches(tmp_err, NM_UTILS_ERROR, NM_UTILS_ERROR_AMBIGUOUS))
g_propagate_error(error, tmp_err);
else {
/* We want to handle aliases, so construct own error message */
@ -3590,7 +3590,7 @@ check_valid_name_toplevel(const char *val, const char **slave_type, GError **err
/* Check string validity */
str = nmc_string_is_valid(val, (const char **) tmp_arr->pdata, &tmp_err);
if (!str) {
if (tmp_err->code == 1)
if (nm_g_error_matches(tmp_err, NM_UTILS_ERROR, NM_UTILS_ERROR_AMBIGUOUS))
g_propagate_error(error, g_steal_pointer(&tmp_err));
else {
/* We want to handle aliases, so construct own error message */
@ -4182,23 +4182,17 @@ set_option(NmCli * nmc,
NULL);
if (option && option->check_and_set) {
return option->check_and_set(nmc, connection, option, value, error);
} else if (value) {
return set_property(nmc->client,
connection,
setting_name,
property_name,
value,
inf_flags & NM_META_PROPERTY_INF_FLAG_MULTI
? NM_META_ACCESSOR_MODIFIER_ADD
: NM_META_ACCESSOR_MODIFIER_SET,
error);
} else if (inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) {
g_set_error(error,
NMCLI_ERROR,
NMC_RESULT_ERROR_USER_INPUT,
_("Error: '%s' is mandatory."),
option_name);
return FALSE;
} else {
set_property(nmc->client,
connection,
setting_name,
property_name,
value,
!value ? NM_META_ACCESSOR_MODIFIER_DEL
: (inf_flags & NM_META_PROPERTY_INF_FLAG_MULTI
? NM_META_ACCESSOR_MODIFIER_ADD
: NM_META_ACCESSOR_MODIFIER_SET),
error);
}
return TRUE;

View file

@ -33,23 +33,22 @@ void nmc_terminal_show_progress(const char *str);
pid_t nmc_terminal_spawn_pager(const NmcConfig *nmc_config);
char * nmc_colorize(const NmcConfig *nmc_config, NMMetaColor color, const char *fmt, ...)
_nm_printf(3, 4);
void nmc_filter_out_colors_inplace(char *str);
char * nmc_filter_out_colors(const char *str);
char * nmc_get_user_input(const char *ask_str);
int nmc_string_to_arg_array(const char *line,
const char *delim,
gboolean unquote,
char *** argv,
int * argc);
const char *nmc_string_is_valid(const char *input, const char **allowed, GError **error);
char * nmc_util_strv_for_display(const char *const *strv, gboolean brackets);
int nmc_string_screen_width(const char *start, const char *end);
void set_val_str(NmcOutputField fields_array[], guint32 index, char *value);
void set_val_strc(NmcOutputField fields_array[], guint32 index, const char *value);
void set_val_arr(NmcOutputField fields_array[], guint32 index, char **value);
void set_val_arrc(NmcOutputField fields_array[], guint32 index, const char **value);
void set_val_color_all(NmcOutputField fields_array[], NMMetaColor color);
void nmc_free_output_field_values(NmcOutputField fields_array[]);
void nmc_filter_out_colors_inplace(char *str);
char *nmc_filter_out_colors(const char *str);
char *nmc_get_user_input(const char *ask_str);
int nmc_string_to_arg_array(const char *line,
const char *delim,
gboolean unquote,
char *** argv,
int * argc);
char *nmc_util_strv_for_display(const char *const *strv, gboolean brackets);
int nmc_string_screen_width(const char *start, const char *end);
void set_val_str(NmcOutputField fields_array[], guint32 index, char *value);
void set_val_strc(NmcOutputField fields_array[], guint32 index, const char *value);
void set_val_arr(NmcOutputField fields_array[], guint32 index, char **value);
void set_val_arrc(NmcOutputField fields_array[], guint32 index, const char **value);
void set_val_color_all(NmcOutputField fields_array[], NMMetaColor color);
void nmc_free_output_field_values(NmcOutputField fields_array[]);
GArray * parse_output_fields(const char * fields_str,
const NMMetaAbstractInfo *const *fields_array,

File diff suppressed because it is too large Load diff

View file

@ -1213,6 +1213,12 @@ class TestNmcli(NmTestBase):
@nm_test
def test_003(self):
con_gsm_list = [
("con-gsm1", "xyz.con-gsm1"),
("con-gsm2", ""),
("con-gsm3", " "),
]
self.init_001()
replace_uuids = []
@ -1231,38 +1237,40 @@ class TestNmcli(NmTestBase):
self.call_nmcli_l(["c", "s"], replace_stdout=replace_uuids)
replace_uuids.append(
(
Util.memoize_nullary(lambda: self.srv.findConnectionUuid("con-gsm1")),
"UUID-con-gsm1-REPLACED-REPLACED-REPL",
)
)
for con_name, apn in con_gsm_list:
self.call_nmcli(
[
"connection",
"add",
"type",
"gsm",
"autoconnect",
"no",
"con-name",
"con-gsm1",
"ifname",
"*",
"apn",
"xyz.con-gsm1",
"serial.baud",
"5",
"serial.send-delay",
"100",
"serial.pari",
"1",
"ipv4.dns-options",
" ",
],
replace_stdout=replace_uuids,
)
replace_uuids.append(
(
Util.memoize_nullary(lambda: self.srv.findConnectionUuid(con_name)),
"UUID-" + con_name + "-REPLACED-REPLACED-REPL",
)
)
self.call_nmcli(
[
"connection",
"add",
"type",
"gsm",
"autoconnect",
"no",
"con-name",
con_name,
"ifname",
"*",
"apn",
apn,
"serial.baud",
"5",
"serial.send-delay",
"100",
"serial.pari",
"1",
"ipv4.dns-options",
" ",
],
replace_stdout=replace_uuids,
)
replace_uuids.append(
(
@ -1286,7 +1294,11 @@ class TestNmcli(NmTestBase):
sort_lines_stdout=True,
)
self.call_nmcli_l(["con", "s", "con-gsm1"], replace_stdout=replace_uuids)
for con_name, apn in con_gsm_list:
self.call_nmcli_l(["con", "s", con_name], replace_stdout=replace_uuids)
self.call_nmcli_l(
["-g", "all", "con", "s", con_name], replace_stdout=replace_uuids
)
# activate the same profile on multiple devices. Our stub-implmentation
# is fine with that... although NetworkManager service would reject