diff options
| author | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
| commit | e74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch) | |
| tree | 3469f17ea9af91f7ff169b890633bda68b0cf76e /src/nmcli/settings.c | |
| parent | bfe522304da217296e2a61040f58e35ec5d6f3f2 (diff) | |
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/nmcli/settings.c')
| -rw-r--r-- | src/nmcli/settings.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/src/nmcli/settings.c b/src/nmcli/settings.c new file mode 100644 index 00000000..6c93c021 --- /dev/null +++ b/src/nmcli/settings.c @@ -0,0 +1,762 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2015 Red Hat, Inc. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "settings.h" + +#include <stdlib.h> +#include <arpa/inet.h> + +#include "libnm-core-aux-intern/nm-common-macros.h" + +#include "libnmc-base/nm-client-utils.h" +#include "libnmc-base/nm-vpn-helpers.h" +#include "libnmc-setting/nm-meta-setting-access.h" + +#include "utils.h" +#include "common.h" + +/*****************************************************************************/ + +static gboolean +get_answer(const char *prop, const char *value) +{ + char * tmp_str; + char * question; + gboolean answer = FALSE; + + if (value) + question = g_strdup_printf(_("Do you also want to set '%s' to '%s'? [yes]: "), prop, value); + else + question = g_strdup_printf(_("Do you also want to clear '%s'? [yes]: "), prop); + tmp_str = nmc_get_user_input(question); + if (!tmp_str || matches(tmp_str, "yes")) + answer = TRUE; + g_free(tmp_str); + g_free(question); + return answer; +} + +static void ipv4_method_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data); +static void ipv6_method_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data); + +static void +ipv4_addresses_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func(object, G_CALLBACK(ipv4_method_changed_cb), NULL); + + /* If we have some IP addresses set method to 'manual'. + * Else if the method was 'manual', change it back to 'auto'. + */ + if (nm_setting_ip_config_get_num_addresses(NM_SETTING_IP_CONFIG(object))) { + if (g_strcmp0(nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(object)), + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (!answered) { + answered = TRUE; + answer = get_answer("ipv4.method", "manual"); + } + if (answer) + g_object_set(object, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + } + } else { + answered = FALSE; + if (!g_strcmp0(nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(object)), + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + g_object_set(object, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + } + + g_signal_handlers_unblock_by_func(object, G_CALLBACK(ipv4_method_changed_cb), NULL); +} + +static void +ipv4_method_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static GPtrArray *old_value = NULL; + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func(object, G_CALLBACK(ipv4_addresses_changed_cb), NULL); + + /* If method != manual, remove addresses (save them for restoring them later when method becomes 'manual' */ + if (g_strcmp0(nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(object)), + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + if (nm_setting_ip_config_get_num_addresses(NM_SETTING_IP_CONFIG(object))) { + if (!answered) { + answered = TRUE; + answer = get_answer("ipv4.addresses", NULL); + } + if (answer) { + nm_clear_pointer(&old_value, g_ptr_array_unref); + g_object_get(object, NM_SETTING_IP_CONFIG_ADDRESSES, &old_value, NULL); + g_object_set(object, NM_SETTING_IP_CONFIG_ADDRESSES, NULL, NULL); + } + } + } else { + answered = FALSE; + if (old_value) { + gs_unref_ptrarray GPtrArray *v = g_steal_pointer(&old_value); + + g_object_set(object, NM_SETTING_IP_CONFIG_ADDRESSES, v, NULL); + } + } + + g_signal_handlers_unblock_by_func(object, G_CALLBACK(ipv4_addresses_changed_cb), NULL); +} + +static void +ipv6_addresses_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func(object, G_CALLBACK(ipv6_method_changed_cb), NULL); + + /* If we have some IP addresses set method to 'manual'. + * Else if the method was 'manual', change it back to 'auto'. + */ + if (nm_setting_ip_config_get_num_addresses(NM_SETTING_IP_CONFIG(object))) { + if (g_strcmp0(nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(object)), + NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (!answered) { + answered = TRUE; + answer = get_answer("ipv6.method", "manual"); + } + if (answer) + g_object_set(object, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL); + } + } else { + answered = FALSE; + /* FIXME: editor_init_existing_connection() and registering handlers is not the + * right approach. + * + * This only happens to work because in nmcli's edit mode + * tends to append addresses -- instead of setting them. + * If we would change that (to behavior I'd expect), we'd get: + * + * nmcli> set ipv6.addresses fc01::1:5/68 + * Do you also want to set 'ipv6.method' to 'manual'? [yes]: y + * nmcli> set ipv6.addresses fc01::1:6/68 + * Do you also want to set 'ipv6.method' to 'manual'? [yes]: + * + * That's because nmc_setting_set_property() calls set_fcn(). With modifier '\0' + * (set), it would first clear all addresses before adding the address. Thereby + * emitting multiple property changed signals. + * + * That can be avoided by freezing/thawing the signals, but this solution + * here is ugly in general. + */ + if (!g_strcmp0(nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(object)), + NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + g_object_set(object, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + } + + g_signal_handlers_unblock_by_func(object, G_CALLBACK(ipv6_method_changed_cb), NULL); +} + +static void +ipv6_method_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + static GPtrArray *old_value = NULL; + static gboolean answered = FALSE; + static gboolean answer = FALSE; + + g_signal_handlers_block_by_func(object, G_CALLBACK(ipv6_addresses_changed_cb), NULL); + + /* If method != manual, remove addresses (save them for restoring them later when method becomes 'manual' */ + if (g_strcmp0(nm_setting_ip_config_get_method(NM_SETTING_IP_CONFIG(object)), + NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + if (nm_setting_ip_config_get_num_addresses(NM_SETTING_IP_CONFIG(object))) { + if (!answered) { + answered = TRUE; + answer = get_answer("ipv6.addresses", NULL); + } + if (answer) { + nm_clear_pointer(&old_value, g_ptr_array_unref); + g_object_get(object, NM_SETTING_IP_CONFIG_ADDRESSES, &old_value, NULL); + g_object_set(object, NM_SETTING_IP_CONFIG_ADDRESSES, NULL, NULL); + } + } + } else { + answered = FALSE; + if (old_value) { + gs_unref_ptrarray GPtrArray *v = g_steal_pointer(&old_value); + + g_object_set(object, NM_SETTING_IP_CONFIG_ADDRESSES, v, NULL); + } + } + + g_signal_handlers_unblock_by_func(object, G_CALLBACK(ipv6_addresses_changed_cb), NULL); +} + +static void +proxy_method_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSettingProxyMethod method; + + method = nm_setting_proxy_get_method(NM_SETTING_PROXY(object)); + + if (method == NM_SETTING_PROXY_METHOD_NONE) { + g_object_set(object, + NM_SETTING_PROXY_PAC_URL, + NULL, + NM_SETTING_PROXY_PAC_SCRIPT, + NULL, + NULL); + } +} + +static void +wireless_band_channel_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + const char * value = NULL, *mode; + char str[16]; + NMSettingWireless *s_wireless = NM_SETTING_WIRELESS(object); + + if (strcmp(g_param_spec_get_name(pspec), NM_SETTING_WIRELESS_BAND) == 0) { + value = nm_setting_wireless_get_band(s_wireless); + if (!value) + return; + } else { + guint32 channel = nm_setting_wireless_get_channel(s_wireless); + + if (channel == 0) + return; + + g_snprintf(str, sizeof(str), "%d", nm_setting_wireless_get_channel(s_wireless)); + value = str; + } + + mode = nm_setting_wireless_get_mode(NM_SETTING_WIRELESS(object)); + if (!mode || !*mode || strcmp(mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) { + g_print(_("Warning: %s.%s set to '%s', but it might be ignored in infrastructure mode\n"), + nm_setting_get_name(NM_SETTING(s_wireless)), + g_param_spec_get_name(pspec), + value); + } +} + +static void +connection_master_changed_cb(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + NMSettingConnection *s_con = NM_SETTING_CONNECTION(object); + NMConnection * connection = NM_CONNECTION(user_data); + NMSetting * s_ipv4, *s_ipv6; + const char * value, *tmp_str; + + value = nm_setting_connection_get_master(s_con); + if (value) { + s_ipv4 = nm_connection_get_setting_by_name(connection, NM_SETTING_IP4_CONFIG_SETTING_NAME); + s_ipv6 = nm_connection_get_setting_by_name(connection, NM_SETTING_IP6_CONFIG_SETTING_NAME); + if (s_ipv4 || s_ipv6) { + g_print(_("Warning: setting %s.%s requires removing ipv4 and ipv6 settings\n"), + nm_setting_get_name(NM_SETTING(s_con)), + g_param_spec_get_name(pspec)); + tmp_str = nmc_get_user_input(_("Do you want to remove them? [yes] ")); + if (!tmp_str || matches(tmp_str, "yes")) { + if (s_ipv4) + nm_connection_remove_setting(connection, G_OBJECT_TYPE(s_ipv4)); + if (s_ipv6) + nm_connection_remove_setting(connection, G_OBJECT_TYPE(s_ipv6)); + } + } + } +} + +void +nmc_setting_ip4_connect_handlers(NMSettingIPConfig *setting) +{ + g_return_if_fail(NM_IS_SETTING_IP4_CONFIG(setting)); + + g_signal_connect(setting, + "notify::" NM_SETTING_IP_CONFIG_ADDRESSES, + G_CALLBACK(ipv4_addresses_changed_cb), + NULL); + g_signal_connect(setting, + "notify::" NM_SETTING_IP_CONFIG_METHOD, + G_CALLBACK(ipv4_method_changed_cb), + NULL); +} + +void +nmc_setting_ip6_connect_handlers(NMSettingIPConfig *setting) +{ + g_return_if_fail(NM_IS_SETTING_IP6_CONFIG(setting)); + + g_signal_connect(setting, + "notify::" NM_SETTING_IP_CONFIG_ADDRESSES, + G_CALLBACK(ipv6_addresses_changed_cb), + NULL); + g_signal_connect(setting, + "notify::" NM_SETTING_IP_CONFIG_METHOD, + G_CALLBACK(ipv6_method_changed_cb), + NULL); +} + +void +nmc_setting_proxy_connect_handlers(NMSettingProxy *setting) +{ + g_return_if_fail(NM_IS_SETTING_PROXY(setting)); + + g_signal_connect(setting, + "notify::" NM_SETTING_PROXY_METHOD, + G_CALLBACK(proxy_method_changed_cb), + NULL); +} + +void +nmc_setting_wireless_connect_handlers(NMSettingWireless *setting) +{ + g_return_if_fail(NM_IS_SETTING_WIRELESS(setting)); + + g_signal_connect(setting, + "notify::" NM_SETTING_WIRELESS_BAND, + G_CALLBACK(wireless_band_channel_changed_cb), + NULL); + g_signal_connect(setting, + "notify::" NM_SETTING_WIRELESS_CHANNEL, + G_CALLBACK(wireless_band_channel_changed_cb), + NULL); +} + +void +nmc_setting_connection_connect_handlers(NMSettingConnection *setting, NMConnection *connection) +{ + g_return_if_fail(NM_IS_SETTING_CONNECTION(setting)); + + g_signal_connect(setting, + "notify::" NM_SETTING_CONNECTION_MASTER, + G_CALLBACK(connection_master_changed_cb), + connection); +} + +/*****************************************************************************/ + +static gboolean +_set_fcn_precheck_connection_secondaries(NMClient * client, + const char *value, + char ** value_coerced, + GError ** error) +{ + const GPtrArray * connections; + NMConnection * con; + gs_free const char **strv0 = NULL; + gs_strfreev char ** strv = NULL; + char ** iter; + gboolean modified = FALSE; + + strv0 = nm_utils_strsplit_set(value, " \t,"); + if (!strv0) + return TRUE; + + connections = nm_client_get_connections(client); + + strv = g_strdupv((char **) strv0); + for (iter = strv; *iter; iter++) { + if (nm_utils_is_uuid(*iter)) { + con = nmc_find_connection(connections, "uuid", *iter, NULL, FALSE); + if (!con) { + g_print(_("Warning: %s is not an UUID of any existing connection profile\n"), + *iter); + } else { + /* Currently, NM only supports VPN connections as secondaries */ + if (!nm_connection_is_type(con, NM_SETTING_VPN_SETTING_NAME)) { + g_set_error(error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + return FALSE; + } + } + } else { + con = nmc_find_connection(connections, "id", *iter, NULL, FALSE); + if (!con) { + g_set_error(error, 1, 0, _("'%s' is not a name of any exiting profile"), *iter); + return FALSE; + } + + /* Currently, NM only supports VPN connections as secondaries */ + if (!nm_connection_is_type(con, NM_SETTING_VPN_SETTING_NAME)) { + g_set_error(error, 1, 0, _("'%s' is not a VPN connection profile"), *iter); + return FALSE; + } + + /* translate id to uuid */ + g_free(*iter); + *iter = g_strdup(nm_connection_get_uuid(con)); + modified = TRUE; + } + } + + if (modified) + *value_coerced = g_strjoinv(" ", strv); + + return TRUE; +} + +/*****************************************************************************/ + +static void +_env_warn_fcn_handle( + const NMMetaEnvironment *environment, + gpointer environment_user_data, + NMMetaEnvWarnLevel warn_level, + const char * + fmt_l10n, /* the untranslated format string, but it is marked for translation using N_(). */ + va_list ap) +{ + NmCli * nmc = environment_user_data; + gs_free char *m = NULL; + + if (nmc->complete) + return; + + NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral") + m = g_strdup_vprintf(_(fmt_l10n), ap); + NM_PRAGMA_WARNING_REENABLE + + switch (warn_level) { + case NM_META_ENV_WARN_LEVEL_WARN: + g_print(_("Warning: %s\n"), m); + return; + case NM_META_ENV_WARN_LEVEL_INFO: + g_print(_("Info: %s\n"), m); + return; + } + g_print(_("Error: %s\n"), m); +} + +static NMDevice *const * +_env_get_nm_devices(const NMMetaEnvironment *environment, + gpointer environment_user_data, + guint * out_len) +{ + NmCli * nmc = environment_user_data; + const GPtrArray *devices; + + nm_assert(nmc); + + /* the returned list is *not* NULL terminated. Need to + * provide and honor the out_len argument. */ + nm_assert(out_len); + + devices = nm_client_get_devices(nmc->client); + if (!devices) { + *out_len = 0; + return NULL; + } + + *out_len = devices->len; + return (NMDevice *const *) devices->pdata; +} + +static NMRemoteConnection *const * +_env_get_nm_connections(const NMMetaEnvironment *environment, + gpointer environment_user_data, + guint * out_len) +{ + NmCli * nmc = environment_user_data; + const GPtrArray *values; + + nm_assert(nmc); + + /* the returned list is *not* NULL terminated. Need to + * provide and honor the out_len argument. */ + nm_assert(out_len); + + values = nm_client_get_connections(nmc->client); + if (!values) { + *out_len = 0; + return NULL; + } + + *out_len = values->len; + return (NMRemoteConnection *const *) values->pdata; +} + +/*****************************************************************************/ + +const NMMetaEnvironment *const nmc_meta_environment = &((NMMetaEnvironment){ + .warn_fcn = _env_warn_fcn_handle, + .get_nm_devices = _env_get_nm_devices, + .get_nm_connections = _env_get_nm_connections, +}); + +static char * +get_property_val(NMSetting * setting, + const char * prop, + NMMetaAccessorGetType get_type, + gboolean show_secrets, + GError ** error) +{ + const NMMetaPropertyInfo *property_info; + + g_return_val_if_fail(NM_IS_SETTING(setting), NULL); + g_return_val_if_fail(!error || !*error, NULL); + g_return_val_if_fail( + NM_IN_SET(get_type, NM_META_ACCESSOR_GET_TYPE_PARSABLE, NM_META_ACCESSOR_GET_TYPE_PRETTY), + NULL); + + if ((property_info = nm_meta_property_info_find_by_setting(setting, prop))) { + if (property_info->property_type->get_fcn) { + NMMetaAccessorGetOutFlags out_flags = NM_META_ACCESSOR_GET_OUT_FLAGS_NONE; + char * to_free = NULL; + const char * value; + + value = property_info->property_type->get_fcn( + property_info, + nmc_meta_environment, + (gpointer) nmc_meta_environment_arg, + setting, + get_type, + show_secrets ? NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS : 0, + &out_flags, + NULL, + (gpointer *) &to_free); + nm_assert(!out_flags); + return to_free ?: g_strdup(value); + } + } + + g_set_error_literal(error, 1, 0, _("don't know how to get the property value")); + return NULL; +} + +/* + * Generic function for getting property value. + * + * Gets property value as a string by calling specialized functions. + * + * Returns: current property value. The caller must free the returned string. + */ +char * +nmc_setting_get_property(NMSetting *setting, const char *prop, GError **error) +{ + return get_property_val(setting, prop, NM_META_ACCESSOR_GET_TYPE_PRETTY, TRUE, error); +} + +/* + * Similar to nmc_setting_get_property(), but returns the property in a string + * format that can be parsed via nmc_setting_set_property(). + */ +char * +nmc_setting_get_property_parsable(NMSetting *setting, const char *prop, GError **error) +{ + return get_property_val(setting, prop, NM_META_ACCESSOR_GET_TYPE_PARSABLE, TRUE, error); +} + +gboolean +nmc_setting_set_property(NMClient * client, + NMSetting * setting, + const char * prop, + NMMetaAccessorModifier modifier, + const char * value, + GError ** error) +{ + const NMMetaPropertyInfo *property_info; + gs_free char * value_to_free = NULL; + gboolean success; + + g_return_val_if_fail(NM_IS_SETTING(setting), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(NM_IN_SET(modifier, + NM_META_ACCESSOR_MODIFIER_SET, + NM_META_ACCESSOR_MODIFIER_DEL, + NM_META_ACCESSOR_MODIFIER_ADD), + FALSE); + + if (!(property_info = nm_meta_property_info_find_by_setting(setting, prop))) + goto out_fail_read_only; + if (!property_info->property_type->set_fcn) + goto out_fail_read_only; + + if (modifier == NM_META_ACCESSOR_MODIFIER_DEL + && !property_info->property_type->set_supports_remove) { + /* The property is a plain property. It does not support '-'. + * + * Maybe we should fail, but just return silently. */ + return TRUE; + } + + if (value) { + switch (property_info->setting_info->general->meta_type) { + case NM_META_SETTING_TYPE_CONNECTION: + if (nm_streq(property_info->property_name, NM_SETTING_CONNECTION_SECONDARIES)) { + if (!_set_fcn_precheck_connection_secondaries(client, value, &value_to_free, error)) + return FALSE; + if (value_to_free) + value = value_to_free; + } + break; + default: + break; + } + } + + if (NM_IN_SET(modifier, NM_META_ACCESSOR_MODIFIER_ADD, NM_META_ACCESSOR_MODIFIER_DEL) + && (!value || !value[0])) { + /* nothing to do. */ + return TRUE; + } + + g_object_freeze_notify(G_OBJECT(setting)); + success = property_info->property_type->set_fcn(property_info, + nmc_meta_environment, + (gpointer) nmc_meta_environment_arg, + setting, + modifier, + value, + error); + g_object_thaw_notify(G_OBJECT(setting)); + return success; + +out_fail_read_only: + nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("the property can't be changed")); + return FALSE; +} + +/* + * Get valid property names for a setting. + * + * Returns: string array with the properties or NULL on failure. + * The returned value should be freed with g_strfreev() + */ +char ** +nmc_setting_get_valid_properties(NMSetting *setting) +{ + const NMMetaSettingInfoEditor *setting_info; + char ** valid_props; + guint i, num; + + setting_info = nm_meta_setting_info_editor_find_by_setting(setting); + + num = setting_info ? setting_info->properties_num : 0; + + valid_props = g_new(char *, num + 1); + for (i = 0; i < num; i++) + valid_props[i] = g_strdup(setting_info->properties[i]->property_name); + + valid_props[num] = NULL; + return valid_props; +} + +const char *const * +nmc_setting_get_property_allowed_values(NMSetting *setting, const char *prop, char ***out_to_free) +{ + const NMMetaPropertyInfo *property_info; + + g_return_val_if_fail(NM_IS_SETTING(setting), FALSE); + g_return_val_if_fail(out_to_free, FALSE); + + *out_to_free = NULL; + + if ((property_info = nm_meta_property_info_find_by_setting(setting, prop))) { + if (property_info->property_type->values_fcn) { + return property_info->property_type->values_fcn(property_info, out_to_free); + } else if (property_info->property_typ_data + && property_info->property_typ_data->values_static) + return property_info->property_typ_data->values_static; + } + + return NULL; +} + +/* + * Create a description string for a property. + * + * It returns a description got from property documentation, concatenated with + * nmcli specific description (if it exists). + * + * Returns: property description or NULL on failure. The caller must free the string. + */ +char * +nmc_setting_get_property_desc(NMSetting *setting, const char *prop) +{ + gs_free char * desc_to_free = NULL; + const char * setting_desc = NULL; + const char * setting_desc_title = ""; + const char * nmcli_desc = NULL; + const char * nmcli_desc_title = ""; + const char * nmcli_nl = ""; + const NMMetaPropertyInfo *property_info; + const char * desc = NULL; + + g_return_val_if_fail(NM_IS_SETTING(setting), FALSE); + + property_info = nm_meta_property_info_find_by_setting(setting, prop); + if (!property_info) + return NULL; + + if (property_info->describe_doc) { + setting_desc = _(property_info->describe_doc); + setting_desc_title = _("[NM property description]"); + } + + if (property_info->property_type->describe_fcn) { + desc = property_info->property_type->describe_fcn(property_info, &desc_to_free); + } else + desc = _(property_info->describe_message); + + if (desc) { + nmcli_desc = desc; + nmcli_desc_title = _("[nmcli specific description]"); + nmcli_nl = "\n"; + } + + return g_strdup_printf("%s\n%s\n%s%s%s%s", + setting_desc_title, + setting_desc ?: "", + nmcli_nl, + nmcli_desc_title, + nmcli_nl, + nmcli_desc ?: ""); +} + +/*****************************************************************************/ + +gboolean +setting_details(const NmcConfig *nmc_config, NMSetting *setting, const char *one_prop) +{ + const NMMetaSettingInfoEditor *setting_info; + gs_free_error GError *error = NULL; + gs_free char * fields_str = NULL; + + g_return_val_if_fail(NM_IS_SETTING(setting), FALSE); + + setting_info = nm_meta_setting_info_editor_find_by_setting(setting); + if (!setting_info) + return FALSE; + + if (one_prop) { + /* hack around setting-details being called for one setting. Must prefix the + * property name with the setting name. Later we should remove setting_details() + * and merge it into the caller. */ + fields_str = g_strdup_printf("%s.%s", nm_setting_get_name(setting), one_prop); + } + + if (!nmc_print( + nmc_config, + (gpointer[]){setting, NULL}, + NULL, + NULL, + (const NMMetaAbstractInfo *const[]){(const NMMetaAbstractInfo *) setting_info, NULL}, + fields_str, + &error)) + return FALSE; + + return TRUE; +} |