From 35779c6675728fa6f0fd0a21cefb904408509c23 Mon Sep 17 00:00:00 2001 From: Sebastien Bacher Date: Mon, 5 Jul 2021 20:35:03 +0200 Subject: New upstream version 1.32.2 --- src/libnmc-setting/meson.build | 52 + src/libnmc-setting/nm-meta-setting-access.c | 653 ++ src/libnmc-setting/nm-meta-setting-access.h | 99 + src/libnmc-setting/nm-meta-setting-base-impl.c | 717 ++ src/libnmc-setting/nm-meta-setting-base-impl.h | 189 + src/libnmc-setting/nm-meta-setting-base.h | 10 + src/libnmc-setting/nm-meta-setting-desc.c | 8688 ++++++++++++++++++++++++ src/libnmc-setting/nm-meta-setting-desc.h | 541 ++ src/libnmc-setting/settings-docs-input.xml | 1 + src/libnmc-setting/settings-docs.h | 424 ++ src/libnmc-setting/settings-docs.h.in | 424 ++ src/libnmc-setting/settings-docs.xsl | 49 + src/libnmc-setting/tests/meson.build | 26 + src/libnmc-setting/tests/test-libnmc-setting.c | 366 + src/libnmc-setting/tests/wg-test0.conf | 20 + src/libnmc-setting/tests/wg-test1.conf | 3 + src/libnmc-setting/tests/wg-test2.conf | 8 + src/libnmc-setting/tests/wg-test3.conf | 3 + 18 files changed, 12273 insertions(+) create mode 100644 src/libnmc-setting/meson.build create mode 100644 src/libnmc-setting/nm-meta-setting-access.c create mode 100644 src/libnmc-setting/nm-meta-setting-access.h create mode 100644 src/libnmc-setting/nm-meta-setting-base-impl.c create mode 100644 src/libnmc-setting/nm-meta-setting-base-impl.h create mode 100644 src/libnmc-setting/nm-meta-setting-base.h create mode 100644 src/libnmc-setting/nm-meta-setting-desc.c create mode 100644 src/libnmc-setting/nm-meta-setting-desc.h create mode 100644 src/libnmc-setting/settings-docs-input.xml create mode 100644 src/libnmc-setting/settings-docs.h create mode 100644 src/libnmc-setting/settings-docs.h.in create mode 100644 src/libnmc-setting/settings-docs.xsl create mode 100644 src/libnmc-setting/tests/meson.build create mode 100644 src/libnmc-setting/tests/test-libnmc-setting.c create mode 100644 src/libnmc-setting/tests/wg-test0.conf create mode 100644 src/libnmc-setting/tests/wg-test1.conf create mode 100644 src/libnmc-setting/tests/wg-test2.conf create mode 100644 src/libnmc-setting/tests/wg-test3.conf (limited to 'src/libnmc-setting') diff --git a/src/libnmc-setting/meson.build b/src/libnmc-setting/meson.build new file mode 100644 index 00000000..8f07ae63 --- /dev/null +++ b/src/libnmc-setting/meson.build @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +if enable_docs + settings_docs_input_xml = custom_target( + 'settings-docs-input.xml', + input: [nm_settings_docs_xml_gir, nm_property_infos_xml['nmcli']], + output: 'settings-docs-input.xml', + command: [ + python.path(), + join_paths(meson.source_root(), 'tools', 'generate-docs-nm-settings-docs-merge.py'), + '@OUTPUT@', + nm_property_infos_xml['nmcli'], + nm_settings_docs_xml_gir, + ], + ) + + settings_docs_source = custom_target( + 'settings-docs.h', + input: settings_docs_input_xml, + output: 'settings-docs.h', + command: [xsltproc, '--output', '@OUTPUT@', join_paths(meson.current_source_dir(), 'settings-docs.xsl'), '@INPUT@'], + ) + + test( + 'check-settings-docs', + find_program(join_paths(source_root, 'tools', 'check-compare-generated.sh')), + args: [ + source_root, + build_root, + 'src/libnmc-setting/settings-docs.h', + ], + ) +else + settings_docs_source = configure_file( + input: 'settings-docs.h.in', + output: '@BASENAME@', + configuration: configuration_data(), + ) +endif + +libnmc_setting = static_library( + 'nmc-setting', + sources: [settings_docs_source] + files( + 'nm-meta-setting-access.c', + 'nm-meta-setting-base-impl.c', + 'nm-meta-setting-desc.c', + ), + dependencies: [ + libnm_dep, + ], + link_depends: settings_docs_source, +) diff --git a/src/libnmc-setting/nm-meta-setting-access.c b/src/libnmc-setting/nm-meta-setting-access.c new file mode 100644 index 00000000..cd4cbdd5 --- /dev/null +++ b/src/libnmc-setting/nm-meta-setting-access.c @@ -0,0 +1,653 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2017 Red Hat, Inc. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nm-meta-setting-access.h" + +/*****************************************************************************/ + +static const NMMetaSettingInfoEditor * +_get_meta_setting_info_editor_from_msi(const NMMetaSettingInfo *meta_setting_info) +{ + const NMMetaSettingInfoEditor *setting_info; + + if (!meta_setting_info) + return NULL; + + nm_assert(meta_setting_info->get_setting_gtype); + nm_assert(meta_setting_info->meta_type < G_N_ELEMENTS(nm_meta_setting_infos_editor)); + + setting_info = &nm_meta_setting_infos_editor[meta_setting_info->meta_type]; + + nm_assert(setting_info->general == meta_setting_info); + return setting_info; +} + +const NMMetaSettingInfoEditor * +nm_meta_setting_info_editor_find_by_name(const char *setting_name, gboolean use_alias) +{ + const NMMetaSettingInfoEditor *setting_info; + guint i; + + g_return_val_if_fail(setting_name, NULL); + + setting_info = + _get_meta_setting_info_editor_from_msi(nm_meta_setting_infos_by_name(setting_name)); + if (!setting_info && use_alias) { + for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { + if (nm_streq0(nm_meta_setting_infos_editor[i].alias, setting_name)) { + setting_info = &nm_meta_setting_infos_editor[i]; + break; + } + } + } + + return setting_info; +} + +const NMMetaSettingInfoEditor * +nm_meta_setting_info_editor_find_by_gtype(GType gtype) +{ + return _get_meta_setting_info_editor_from_msi(nm_meta_setting_infos_by_gtype(gtype)); +} + +const NMMetaSettingInfoEditor * +nm_meta_setting_info_editor_find_by_setting(NMSetting *setting) +{ + const NMMetaSettingInfoEditor *setting_info; + + g_return_val_if_fail(NM_IS_SETTING(setting), NULL); + + setting_info = nm_meta_setting_info_editor_find_by_gtype(G_OBJECT_TYPE(setting)); + + nm_assert(setting_info); + nm_assert(G_TYPE_CHECK_INSTANCE_TYPE(setting, setting_info->general->get_setting_gtype())); + return setting_info; +} + +/*****************************************************************************/ + +const NMMetaPropertyInfo * +nm_meta_setting_info_editor_get_property_info(const NMMetaSettingInfoEditor *setting_info, + const char * property_name) +{ + guint i; + + g_return_val_if_fail(setting_info, NULL); + g_return_val_if_fail(property_name, NULL); + + for (i = 0; i < setting_info->properties_num; i++) { + nm_assert(setting_info->properties[i]->property_name); + nm_assert(setting_info->properties[i]->setting_info == setting_info); + if (nm_streq(setting_info->properties[i]->property_name, property_name)) + return setting_info->properties[i]; + } + + return NULL; +} + +gboolean +nm_meta_setting_info_editor_has_secrets(const NMMetaSettingInfoEditor *setting_info) +{ + guint i; + + if (!setting_info) + return FALSE; + + for (i = 0; i < setting_info->properties_num; i++) { + if (setting_info->properties[i]->is_secret) + return TRUE; + } + + return FALSE; +} + +const NMMetaPropertyInfo * +nm_meta_property_info_find_by_name(const char *setting_name, const char *property_name) +{ + const NMMetaSettingInfoEditor *setting_info; + const NMMetaPropertyInfo * property_info; + + setting_info = nm_meta_setting_info_editor_find_by_name(setting_name, FALSE); + if (!setting_info) + return NULL; + + property_info = nm_meta_setting_info_editor_get_property_info(setting_info, property_name); + if (!property_info) + return NULL; + + nm_assert(property_info->setting_info == setting_info); + + return property_info; +} + +const NMMetaPropertyInfo * +nm_meta_property_info_find_by_setting(NMSetting *setting, const char *property_name) +{ + const NMMetaSettingInfoEditor *setting_info; + const NMMetaPropertyInfo * property_info; + + setting_info = nm_meta_setting_info_editor_find_by_setting(setting); + if (!setting_info) + return NULL; + property_info = nm_meta_setting_info_editor_get_property_info(setting_info, property_name); + if (!property_info) + return NULL; + + nm_assert(property_info->setting_info == setting_info); + nm_assert(property_info + == nm_meta_property_info_find_by_name(nm_setting_get_name(setting), property_name)); + + return property_info; +} + +NMSetting * +nm_meta_setting_info_editor_new_setting(const NMMetaSettingInfoEditor *setting_info, + NMMetaAccessorSettingInitType init_type) +{ + NMSetting *setting; + + g_return_val_if_fail(setting_info, NULL); + + setting = g_object_new(setting_info->general->get_setting_gtype(), NULL); + + if (setting_info->setting_init_fcn && init_type != NM_META_ACCESSOR_SETTING_INIT_TYPE_DEFAULT) { + setting_info->setting_init_fcn(setting_info, setting, init_type); + } + + return setting; +} + +/*****************************************************************************/ + +const NMMetaSettingInfoEditor *const * +nm_meta_setting_infos_editor_p(void) +{ + static const NMMetaSettingInfoEditor *cache[_NM_META_SETTING_TYPE_NUM + 1] = {NULL}; + guint i; + + if (G_UNLIKELY(!cache[0])) { + for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) + cache[i] = &nm_meta_setting_infos_editor[i]; + } + return cache; +} + +/*****************************************************************************/ + +const char * +nm_meta_abstract_info_get_name(const NMMetaAbstractInfo *abstract_info, gboolean for_header) +{ + const char *n; + + nm_assert(abstract_info); + nm_assert(abstract_info->meta_type); + nm_assert(abstract_info->meta_type->get_name); + n = abstract_info->meta_type->get_name(abstract_info, for_header); + nm_assert(n && n[0]); + return n; +} + +const NMMetaAbstractInfo *const * +nm_meta_abstract_info_get_nested(const NMMetaAbstractInfo *abstract_info, + guint * out_len, + gpointer * nested_to_free) +{ + const NMMetaAbstractInfo *const *nested; + guint l = 0; + gs_free gpointer f = NULL; + + nm_assert(abstract_info); + nm_assert(abstract_info->meta_type); + nm_assert(nested_to_free && !*nested_to_free); + + if (abstract_info->meta_type->get_nested) { + nested = abstract_info->meta_type->get_nested(abstract_info, &l, &f); + nm_assert(NM_PTRARRAY_LEN(nested) == l); + nm_assert(!f || nested == f); + if (nested && nested[0]) { + NM_SET_OUT(out_len, l); + *nested_to_free = g_steal_pointer(&f); + return nested; + } + } + NM_SET_OUT(out_len, 0); + return NULL; +} + +gconstpointer +nm_meta_abstract_info_get(const NMMetaAbstractInfo * abstract_info, + const NMMetaEnvironment * environment, + gpointer environment_user_data, + gpointer target, + gpointer target_data, + NMMetaAccessorGetType get_type, + NMMetaAccessorGetFlags get_flags, + NMMetaAccessorGetOutFlags *out_flags, + gboolean * out_is_default, + gpointer * out_to_free) +{ + nm_assert(abstract_info); + nm_assert(abstract_info->meta_type); + nm_assert(!out_to_free || !*out_to_free); + nm_assert(out_flags); + + *out_flags = NM_META_ACCESSOR_GET_OUT_FLAGS_NONE; + NM_SET_OUT(out_is_default, FALSE); + + if (!abstract_info->meta_type->get_fcn) + g_return_val_if_reached(NULL); + + return abstract_info->meta_type->get_fcn(abstract_info, + environment, + environment_user_data, + target, + target_data, + get_type, + get_flags, + out_flags, + out_is_default, + out_to_free); +} + +const char *const * +nm_meta_abstract_info_complete(const NMMetaAbstractInfo * abstract_info, + const NMMetaEnvironment * environment, + gpointer environment_user_data, + const NMMetaOperationContext *operation_context, + const char * text, + gboolean * out_complete_filename, + char *** out_to_free) +{ + const char *const *values; + gsize i, j, text_len; + + nm_assert(abstract_info); + nm_assert(abstract_info->meta_type); + nm_assert(out_to_free && !*out_to_free); + + *out_to_free = NULL; + + if (!abstract_info->meta_type->complete_fcn) + return NULL; + + values = abstract_info->meta_type->complete_fcn(abstract_info, + environment, + environment_user_data, + operation_context, + text, + out_complete_filename, + out_to_free); + + nm_assert(!*out_to_free || values == (const char *const *) *out_to_free); + + if (!values) + return NULL; + + if (!values[0]) { + nm_clear_g_free(out_to_free); + return NULL; + } + + if (!text || !text[0]) + return values; + + /* for convenience, we allow the complete_fcn() implementations to + * ignore "text". We filter out invalid matches here. */ + + text_len = strlen(text); + + if (*out_to_free) { + char **v = *out_to_free; + + for (i = 0, j = 0; v[i]; i++) { + if (strncmp(v[i], text, text_len) != 0) { + g_free(v[i]); + continue; + } + v[j++] = v[i]; + } + if (j) + v[j++] = NULL; + else { + g_free(v); + *out_to_free = v = NULL; + } + return (const char *const *) v; + } else { + const char *const *v = values; + char ** r; + + for (i = 0, j = 0; v[i]; i++) { + if (strncmp(v[i], text, text_len) != 0) + continue; + j++; + } + if (j == i) + return values; + else if (!j) + return NULL; + + r = g_new(char *, j + 1); + v = values; + for (i = 0, j = 0; v[i]; i++) { + if (strncmp(v[i], text, text_len) != 0) + continue; + r[j++] = g_strdup(v[i]); + } + r[j++] = NULL; + return (const char *const *) (*out_to_free = r); + } +} + +/*****************************************************************************/ + +char * +nm_meta_abstract_info_get_nested_names_str(const NMMetaAbstractInfo *abstract_info, + const char * name_prefix) +{ + gs_free gpointer nested_to_free = NULL; + const NMMetaAbstractInfo *const *nested; + + nested = nm_meta_abstract_info_get_nested(abstract_info, NULL, &nested_to_free); + if (!nested) + return NULL; + + if (!name_prefix) + name_prefix = nm_meta_abstract_info_get_name(abstract_info, FALSE); + + return nm_meta_abstract_infos_get_names_str(nested, name_prefix); +} + +char * +nm_meta_abstract_infos_get_names_str(const NMMetaAbstractInfo *const *fields_array, + const char * name_prefix) +{ + GString *str; + guint i; + + if (!fields_array || !fields_array[0]) + return NULL; + + str = g_string_sized_new(128); + for (i = 0; fields_array[i]; i++) { + if (str->len > 0) + g_string_append_c(str, ','); + if (name_prefix) { + g_string_append(str, name_prefix); + g_string_append_c(str, '.'); + } + g_string_append(str, nm_meta_abstract_info_get_name(fields_array[i], FALSE)); + } + return g_string_free(str, FALSE); +} + +/*****************************************************************************/ + +typedef struct { + guint idx; + gsize self_offset_plus_1; + gsize sub_offset_plus_1; +} OutputSelectionItem; + +static NMMetaSelectionResultList * +_output_selection_pack(const NMMetaAbstractInfo *const *fields_array, GArray *array, GString *str) +{ + NMMetaSelectionResultList *result; + guint i; + guint len; + + len = array ? array->len : 0; + + /* re-organize the collected output data in one buffer that can be freed using + * g_free(). This makes allocation more complicated, but saves us from special + * handling for free. */ + result = g_malloc0(sizeof(NMMetaSelectionResultList) + (len * sizeof(NMMetaSelectionItem)) + + (str ? str->len : 0)); + *((guint *) &result->num) = len; + if (len > 0) { + char *pdata = + &((char *) + result)[sizeof(NMMetaSelectionResultList) + (len * sizeof(NMMetaSelectionItem))]; + + if (str) + memcpy(pdata, str->str, str->len); + for (i = 0; i < len; i++) { + const OutputSelectionItem *a = &g_array_index(array, OutputSelectionItem, i); + NMMetaSelectionItem * p = (NMMetaSelectionItem *) &result->items[i]; + + p->info = fields_array[a->idx]; + p->idx = a->idx; + if (a->self_offset_plus_1 > 0) + p->self_selection = &pdata[a->self_offset_plus_1 - 1]; + if (a->sub_offset_plus_1 > 0) + p->sub_selection = &pdata[a->sub_offset_plus_1 - 1]; + } + } + + return result; +} + +static gboolean +_output_selection_select_one(const NMMetaAbstractInfo *const *fields_array, + const char * fields_prefix, + const char * fields_str, + gboolean validate_nested, + GArray ** p_array, + GString ** p_str, + GError ** error) +{ + guint i, j; + const char * i_name; + const char * right; + gboolean found = FALSE; + const NMMetaAbstractInfo *fields_array_failure = NULL; + gs_free char * fields_str_clone = NULL; + + nm_assert(fields_str); + nm_assert(p_array); + nm_assert(p_str); + nm_assert(!error || !*error); + + right = strchr(fields_str, '.'); + if (right) { + fields_str_clone = g_strdup(fields_str); + fields_str_clone[right - fields_str] = '\0'; + i_name = fields_str_clone; + right = &fields_str_clone[right - fields_str + 1]; + } else + i_name = fields_str; + + if (!fields_array) + goto not_found; + + for (i = 0; fields_array[i]; i++) { + const NMMetaAbstractInfo * fi = fields_array[i]; + const NMMetaAbstractInfo *const *nested; + gs_free gpointer nested_to_free = NULL; + + if (g_ascii_strcasecmp(i_name, nm_meta_abstract_info_get_name(fi, FALSE)) != 0) + continue; + + if (!right || !validate_nested) { + found = TRUE; + break; + } + + nested = nm_meta_abstract_info_get_nested(fi, NULL, &nested_to_free); + if (nested) { + for (j = 0; nested[j]; nested++) { + if (g_ascii_strcasecmp(right, nm_meta_abstract_info_get_name(nested[j], FALSE)) + == 0) { + found = TRUE; + break; + } + } + } + fields_array_failure = fields_array[i]; + break; + } + + if (!found) { +not_found: + if (!right && !fields_prefix + && (!g_ascii_strcasecmp(i_name, "all") || !g_ascii_strcasecmp(i_name, "common"))) + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("field '%s' has to be alone"), + i_name); + else { + gs_free char *allowed_fields = NULL; + + if (fields_array_failure) { + gs_free char *p = NULL; + + if (fields_prefix) { + p = g_strdup_printf( + "%s.%s", + fields_prefix, + nm_meta_abstract_info_get_name(fields_array_failure, FALSE)); + } + allowed_fields = + nm_meta_abstract_info_get_nested_names_str(fields_array_failure, p); + } else + allowed_fields = nm_meta_abstract_infos_get_names_str(fields_array, NULL); + + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("invalid field '%s%s%s%s%s'; %s%s%s"), + fields_prefix ?: "", + fields_prefix ? "." : "", + i_name, + right ? "." : "", + right ?: "", + NM_PRINT_FMT_QUOTED(allowed_fields, + "allowed fields: ", + allowed_fields, + "", + "no fields")); + } + return FALSE; + } + + { + GString * str; + OutputSelectionItem s = { + .idx = i, + }; + + if (!*p_str) + *p_str = g_string_sized_new(64); + str = *p_str; + + s.self_offset_plus_1 = str->len + 1; + if (fields_prefix) { + g_string_append(str, fields_prefix); + g_string_append_c(str, '.'); + } + g_string_append_len(str, i_name, strlen(i_name) + 1); + + if (right) { + s.sub_offset_plus_1 = str->len + 1; + g_string_append_len(str, right, strlen(right) + 1); + } + + if (!*p_array) + *p_array = g_array_new(FALSE, FALSE, sizeof(OutputSelectionItem)); + g_array_append_val(*p_array, s); + } + + return TRUE; +} + +NMMetaSelectionResultList * +nm_meta_selection_create_all(const NMMetaAbstractInfo *const *fields_array) +{ + gs_unref_array GArray *array = NULL; + guint i; + + if (fields_array) { + array = g_array_new(FALSE, FALSE, sizeof(OutputSelectionItem)); + for (i = 0; fields_array[i]; i++) { + OutputSelectionItem s = { + .idx = i, + }; + + g_array_append_val(array, s); + } + } + + return _output_selection_pack(fields_array, array, NULL); +} + +NMMetaSelectionResultList * +nm_meta_selection_create_parse_one( + const NMMetaAbstractInfo *const *fields_array, + const char * fields_prefix, + const char * + fields_str, /* one field selector (contains no commas) and is already stripped of spaces. */ + gboolean validate_nested, + GError **error) +{ + gs_unref_array GArray *array = NULL; + nm_auto_free_gstring GString *str = NULL; + + g_return_val_if_fail(!error || !*error, NULL); + nm_assert(fields_str && !strchr(fields_str, ',')); + + if (!_output_selection_select_one(fields_array, + fields_prefix, + fields_str, + validate_nested, + &array, + &str, + error)) + return NULL; + return _output_selection_pack(fields_array, array, str); +} + +NMMetaSelectionResultList * +nm_meta_selection_create_parse_list( + const NMMetaAbstractInfo *const *fields_array, + const char * fields_str, /* a comma separated list of selectors */ + gboolean validate_nested, + GError ** error) +{ + gs_unref_array GArray *array = NULL; + nm_auto_free_gstring GString *str = NULL; + gs_free char * fields_str_clone = NULL; + char * fields_str_cur; + char * fields_str_next; + + g_return_val_if_fail(!error || !*error, NULL); + + if (!fields_str) + return nm_meta_selection_create_all(fields_array); + + fields_str_clone = g_strdup(fields_str); + for (fields_str_cur = fields_str_clone; fields_str_cur; fields_str_cur = fields_str_next) { + fields_str_cur = nm_str_skip_leading_spaces(fields_str_cur); + fields_str_next = strchr(fields_str_cur, ','); + if (fields_str_next) + *fields_str_next++ = '\0'; + + g_strchomp(fields_str_cur); + if (!fields_str_cur[0]) + continue; + if (!_output_selection_select_one(fields_array, + NULL, + fields_str_cur, + validate_nested, + &array, + &str, + error)) + return NULL; + } + + return _output_selection_pack(fields_array, array, str); +} diff --git a/src/libnmc-setting/nm-meta-setting-access.h b/src/libnmc-setting/nm-meta-setting-access.h new file mode 100644 index 00000000..81aa3b6f --- /dev/null +++ b/src/libnmc-setting/nm-meta-setting-access.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2017 Red Hat, Inc. + */ + +#ifndef _NM_META_SETTING_ACCESS_H__ +#define _NM_META_SETTING_ACCESS_H__ + +#include "nm-meta-setting-desc.h" + +/*****************************************************************************/ + +NMSetting *nm_meta_setting_info_editor_new_setting(const NMMetaSettingInfoEditor *setting_info, + NMMetaAccessorSettingInitType init_type); + +const NMMetaSettingInfoEditor *nm_meta_setting_info_editor_find_by_name(const char *setting_name, + gboolean use_alias); +const NMMetaSettingInfoEditor *nm_meta_setting_info_editor_find_by_gtype(GType gtype); +const NMMetaSettingInfoEditor *nm_meta_setting_info_editor_find_by_setting(NMSetting *setting); + +const NMMetaPropertyInfo * +nm_meta_setting_info_editor_get_property_info(const NMMetaSettingInfoEditor *setting_info, + const char * property_name); +const NMMetaPropertyInfo *nm_meta_property_info_find_by_name(const char *setting_name, + const char *property_name); +const NMMetaPropertyInfo *nm_meta_property_info_find_by_setting(NMSetting * setting, + const char *property_name); + +gboolean nm_meta_setting_info_editor_has_secrets(const NMMetaSettingInfoEditor *setting_info); + +/*****************************************************************************/ + +const NMMetaSettingInfoEditor *const *nm_meta_setting_infos_editor_p(void); + +/*****************************************************************************/ + +const char *nm_meta_abstract_info_get_name(const NMMetaAbstractInfo *abstract_info, + gboolean for_header); + +const NMMetaAbstractInfo *const * +nm_meta_abstract_info_get_nested(const NMMetaAbstractInfo *abstract_info, + guint * out_len, + gpointer * nested_to_free); + +gconstpointer nm_meta_abstract_info_get(const NMMetaAbstractInfo * abstract_info, + const NMMetaEnvironment * environment, + gpointer environment_user_data, + gpointer target, + gpointer target_data, + NMMetaAccessorGetType get_type, + NMMetaAccessorGetFlags get_flags, + NMMetaAccessorGetOutFlags *out_flags, + gboolean * out_is_default, + gpointer * out_to_free); + +const char *const *nm_meta_abstract_info_complete(const NMMetaAbstractInfo *abstract_info, + const NMMetaEnvironment * environment, + gpointer environment_user_data, + const NMMetaOperationContext *operation_context, + const char * text, + gboolean *out_complete_filename, + char *** out_to_free); + +/*****************************************************************************/ + +char *nm_meta_abstract_info_get_nested_names_str(const NMMetaAbstractInfo *abstract_info, + const char * name_prefix); +char *nm_meta_abstract_infos_get_names_str(const NMMetaAbstractInfo *const *fields_array, + const char * name_prefix); + +/*****************************************************************************/ + +typedef struct { + const NMMetaAbstractInfo *info; + const char * self_selection; + const char * sub_selection; + guint idx; +} NMMetaSelectionItem; + +typedef struct { + const guint num; + const NMMetaSelectionItem items[]; +} NMMetaSelectionResultList; + +NMMetaSelectionResultList * +nm_meta_selection_create_all(const NMMetaAbstractInfo *const *fields_array); +NMMetaSelectionResultList * +nm_meta_selection_create_parse_one(const NMMetaAbstractInfo *const *fields_array, + const char * fields_prefix, + const char * fields_str, + gboolean validate_nested, + GError ** error); +NMMetaSelectionResultList * +nm_meta_selection_create_parse_list(const NMMetaAbstractInfo *const *fields_array, + const char * fields_str, + gboolean validate_nested, + GError ** error); + +#endif /* _NM_META_SETTING_ACCESS_H__ */ diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c new file mode 100644 index 00000000..a952ced9 --- /dev/null +++ b/src/libnmc-setting/nm-meta-setting-base-impl.c @@ -0,0 +1,717 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2018 Red Hat, Inc. + */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nm-meta-setting-base.h" + +#if _NM_META_SETTING_BASE_IMPL_LIBNM + #include "libnm-core-impl/nm-default-libnm-core.h" +#endif + +#include "nm-setting-6lowpan.h" +#include "nm-setting-8021x.h" +#include "nm-setting-adsl.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-bond.h" +#include "nm-setting-bridge-port.h" +#include "nm-setting-bridge.h" +#include "nm-setting-cdma.h" +#include "nm-setting-connection.h" +#include "nm-setting-dcb.h" +#include "nm-setting-dummy.h" +#include "nm-setting-ethtool.h" +#include "nm-setting-generic.h" +#include "nm-setting-gsm.h" +#include "nm-setting-hostname.h" +#include "nm-setting-infiniband.h" +#include "nm-setting-ip-config.h" +#include "nm-setting-ip-tunnel.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-macsec.h" +#include "nm-setting-macvlan.h" +#include "nm-setting-match.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-dpdk.h" +#include "nm-setting-ovs-external-ids.h" +#include "nm-setting-ovs-patch.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-ppp.h" +#include "nm-setting-pppoe.h" +#include "nm-setting-proxy.h" +#include "nm-setting-serial.h" +#include "nm-setting-tc-config.h" +#include "nm-setting-team-port.h" +#include "nm-setting-team.h" +#include "nm-setting-tun.h" +#include "nm-setting-user.h" +#include "nm-setting-veth.h" +#include "nm-setting-vlan.h" +#include "nm-setting-vpn.h" +#include "nm-setting-vrf.h" +#include "nm-setting-vxlan.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-setting-wimax.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireguard.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wpan.h" + +#if _NM_META_SETTING_BASE_IMPL_LIBNM + #include "nm-setting-private.h" +#endif + +/*****************************************************************************/ + +const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { + +#define _D(_scheme_type, ...) [(_scheme_type)] = {.scheme_type = (_scheme_type), __VA_ARGS__} + + _D(NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN), + + _D(NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, + .setting_key = NM_SETTING_802_1X_CA_CERT, + .scheme_func = nm_setting_802_1x_get_ca_cert_scheme, + .format_func = NULL, + .path_func = nm_setting_802_1x_get_ca_cert_path, + .blob_func = nm_setting_802_1x_get_ca_cert_blob, + .uri_func = nm_setting_802_1x_get_ca_cert_uri, + .passwd_func = nm_setting_802_1x_get_ca_cert_password, + .pwflag_func = nm_setting_802_1x_get_ca_cert_password_flags, + .set_cert_func = nm_setting_802_1x_set_ca_cert, + .file_suffix = "ca-cert", ), + + _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, + .setting_key = NM_SETTING_802_1X_PHASE2_CA_CERT, + .scheme_func = nm_setting_802_1x_get_phase2_ca_cert_scheme, + .format_func = NULL, + .path_func = nm_setting_802_1x_get_phase2_ca_cert_path, + .blob_func = nm_setting_802_1x_get_phase2_ca_cert_blob, + .uri_func = nm_setting_802_1x_get_phase2_ca_cert_uri, + .passwd_func = nm_setting_802_1x_get_phase2_ca_cert_password, + .pwflag_func = nm_setting_802_1x_get_phase2_ca_cert_password_flags, + .set_cert_func = nm_setting_802_1x_set_phase2_ca_cert, + .file_suffix = "inner-ca-cert", ), + + _D(NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, + .setting_key = NM_SETTING_802_1X_CLIENT_CERT, + .scheme_func = nm_setting_802_1x_get_client_cert_scheme, + .format_func = NULL, + .path_func = nm_setting_802_1x_get_client_cert_path, + .blob_func = nm_setting_802_1x_get_client_cert_blob, + .uri_func = nm_setting_802_1x_get_client_cert_uri, + .passwd_func = nm_setting_802_1x_get_client_cert_password, + .pwflag_func = nm_setting_802_1x_get_client_cert_password_flags, + .set_cert_func = nm_setting_802_1x_set_client_cert, + .file_suffix = "client-cert", ), + + _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, + .setting_key = NM_SETTING_802_1X_PHASE2_CLIENT_CERT, + .scheme_func = nm_setting_802_1x_get_phase2_client_cert_scheme, + .format_func = NULL, + .path_func = nm_setting_802_1x_get_phase2_client_cert_path, + .blob_func = nm_setting_802_1x_get_phase2_client_cert_blob, + .uri_func = nm_setting_802_1x_get_phase2_client_cert_uri, + .passwd_func = nm_setting_802_1x_get_phase2_client_cert_password, + .pwflag_func = nm_setting_802_1x_get_phase2_client_cert_password_flags, + .set_cert_func = nm_setting_802_1x_set_phase2_client_cert, + .file_suffix = "inner-client-cert", ), + + _D(NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, + .setting_key = NM_SETTING_802_1X_PRIVATE_KEY, + .scheme_func = nm_setting_802_1x_get_private_key_scheme, + .format_func = nm_setting_802_1x_get_private_key_format, + .path_func = nm_setting_802_1x_get_private_key_path, + .blob_func = nm_setting_802_1x_get_private_key_blob, + .uri_func = nm_setting_802_1x_get_private_key_uri, + .passwd_func = nm_setting_802_1x_get_private_key_password, + .pwflag_func = nm_setting_802_1x_get_private_key_password_flags, + .set_private_key_func = nm_setting_802_1x_set_private_key, + .file_suffix = "private-key", + .is_secret = TRUE, ), + + _D(NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, + .setting_key = NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, + .scheme_func = nm_setting_802_1x_get_phase2_private_key_scheme, + .format_func = nm_setting_802_1x_get_phase2_private_key_format, + .path_func = nm_setting_802_1x_get_phase2_private_key_path, + .blob_func = nm_setting_802_1x_get_phase2_private_key_blob, + .uri_func = nm_setting_802_1x_get_phase2_private_key_uri, + .passwd_func = nm_setting_802_1x_get_phase2_private_key_password, + .pwflag_func = nm_setting_802_1x_get_phase2_private_key_password_flags, + .set_private_key_func = nm_setting_802_1x_set_phase2_private_key, + .file_suffix = "inner-private-key", + .is_secret = TRUE, ), + +#undef _D +}; + +/*****************************************************************************/ + +const NMMetaSettingInfo nm_meta_setting_infos[] = { + [NM_META_SETTING_TYPE_6LOWPAN] = + { + .meta_type = NM_META_SETTING_TYPE_6LOWPAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_6LOWPAN_SETTING_NAME, + .get_setting_gtype = nm_setting_6lowpan_get_type, + }, + [NM_META_SETTING_TYPE_802_1X] = + { + .meta_type = NM_META_SETTING_TYPE_802_1X, + .setting_priority = NM_SETTING_PRIORITY_HW_AUX, + .setting_name = NM_SETTING_802_1X_SETTING_NAME, + .get_setting_gtype = nm_setting_802_1x_get_type, + }, + [NM_META_SETTING_TYPE_ADSL] = + { + .meta_type = NM_META_SETTING_TYPE_ADSL, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_ADSL_SETTING_NAME, + .get_setting_gtype = nm_setting_adsl_get_type, + }, + [NM_META_SETTING_TYPE_BLUETOOTH] = + { + .meta_type = NM_META_SETTING_TYPE_BLUETOOTH, + .setting_priority = NM_SETTING_PRIORITY_HW_NON_BASE, + .setting_name = NM_SETTING_BLUETOOTH_SETTING_NAME, + .get_setting_gtype = nm_setting_bluetooth_get_type, + }, + [NM_META_SETTING_TYPE_BOND] = + { + .meta_type = NM_META_SETTING_TYPE_BOND, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_BOND_SETTING_NAME, + .get_setting_gtype = nm_setting_bond_get_type, + }, + [NM_META_SETTING_TYPE_BRIDGE] = + { + .meta_type = NM_META_SETTING_TYPE_BRIDGE, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_BRIDGE_SETTING_NAME, + .get_setting_gtype = nm_setting_bridge_get_type, + }, + [NM_META_SETTING_TYPE_BRIDGE_PORT] = + { + .meta_type = NM_META_SETTING_TYPE_BRIDGE_PORT, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_BRIDGE_PORT_SETTING_NAME, + .get_setting_gtype = nm_setting_bridge_port_get_type, + }, + [NM_META_SETTING_TYPE_CDMA] = + { + .meta_type = NM_META_SETTING_TYPE_CDMA, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_CDMA_SETTING_NAME, + .get_setting_gtype = nm_setting_cdma_get_type, + }, + [NM_META_SETTING_TYPE_CONNECTION] = + { + .meta_type = NM_META_SETTING_TYPE_CONNECTION, + .setting_priority = NM_SETTING_PRIORITY_CONNECTION, + .setting_name = NM_SETTING_CONNECTION_SETTING_NAME, + .get_setting_gtype = nm_setting_connection_get_type, + }, + [NM_META_SETTING_TYPE_DCB] = + { + .meta_type = NM_META_SETTING_TYPE_DCB, + .setting_priority = NM_SETTING_PRIORITY_HW_AUX, + .setting_name = NM_SETTING_DCB_SETTING_NAME, + .get_setting_gtype = nm_setting_dcb_get_type, + }, + [NM_META_SETTING_TYPE_DUMMY] = + { + .meta_type = NM_META_SETTING_TYPE_DUMMY, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_DUMMY_SETTING_NAME, + .get_setting_gtype = nm_setting_dummy_get_type, + }, + [NM_META_SETTING_TYPE_ETHTOOL] = + { + .meta_type = NM_META_SETTING_TYPE_ETHTOOL, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_ETHTOOL_SETTING_NAME, + .get_setting_gtype = nm_setting_ethtool_get_type, + }, + [NM_META_SETTING_TYPE_GENERIC] = + { + .meta_type = NM_META_SETTING_TYPE_GENERIC, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_GENERIC_SETTING_NAME, + .get_setting_gtype = nm_setting_generic_get_type, + }, + [NM_META_SETTING_TYPE_GSM] = + { + .meta_type = NM_META_SETTING_TYPE_GSM, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_GSM_SETTING_NAME, + .get_setting_gtype = nm_setting_gsm_get_type, + }, + [NM_META_SETTING_TYPE_HOSTNAME] = + { + .meta_type = NM_META_SETTING_TYPE_HOSTNAME, + .setting_priority = NM_SETTING_PRIORITY_IP, + .setting_name = NM_SETTING_HOSTNAME_SETTING_NAME, + .get_setting_gtype = nm_setting_hostname_get_type, + }, + [NM_META_SETTING_TYPE_INFINIBAND] = + { + .meta_type = NM_META_SETTING_TYPE_INFINIBAND, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_INFINIBAND_SETTING_NAME, + .get_setting_gtype = nm_setting_infiniband_get_type, + }, + [NM_META_SETTING_TYPE_IP4_CONFIG] = + { + .meta_type = NM_META_SETTING_TYPE_IP4_CONFIG, + .setting_priority = NM_SETTING_PRIORITY_IP, + .setting_name = NM_SETTING_IP4_CONFIG_SETTING_NAME, + .get_setting_gtype = nm_setting_ip4_config_get_type, + }, + [NM_META_SETTING_TYPE_IP6_CONFIG] = + { + .meta_type = NM_META_SETTING_TYPE_IP6_CONFIG, + .setting_priority = NM_SETTING_PRIORITY_IP, + .setting_name = NM_SETTING_IP6_CONFIG_SETTING_NAME, + .get_setting_gtype = nm_setting_ip6_config_get_type, + }, + [NM_META_SETTING_TYPE_IP_TUNNEL] = + { + .meta_type = NM_META_SETTING_TYPE_IP_TUNNEL, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME, + .get_setting_gtype = nm_setting_ip_tunnel_get_type, + }, + [NM_META_SETTING_TYPE_MACSEC] = + { + .meta_type = NM_META_SETTING_TYPE_MACSEC, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_MACSEC_SETTING_NAME, + .get_setting_gtype = nm_setting_macsec_get_type, + }, + [NM_META_SETTING_TYPE_MACVLAN] = + { + .meta_type = NM_META_SETTING_TYPE_MACVLAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_MACVLAN_SETTING_NAME, + .get_setting_gtype = nm_setting_macvlan_get_type, + }, + [NM_META_SETTING_TYPE_MATCH] = + { + .meta_type = NM_META_SETTING_TYPE_MATCH, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_MATCH_SETTING_NAME, + .get_setting_gtype = nm_setting_match_get_type, + }, + [NM_META_SETTING_TYPE_OLPC_MESH] = + { + .meta_type = NM_META_SETTING_TYPE_OLPC_MESH, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_OLPC_MESH_SETTING_NAME, + .get_setting_gtype = nm_setting_olpc_mesh_get_type, + }, + [NM_META_SETTING_TYPE_OVS_BRIDGE] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_BRIDGE, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_OVS_BRIDGE_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_bridge_get_type, + }, + [NM_META_SETTING_TYPE_OVS_DPDK] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_DPDK, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_OVS_DPDK_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_dpdk_get_type, + }, + [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_external_ids_get_type, + }, + [NM_META_SETTING_TYPE_OVS_INTERFACE] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_INTERFACE, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_OVS_INTERFACE_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_interface_get_type, + }, + [NM_META_SETTING_TYPE_OVS_PATCH] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_PATCH, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_OVS_PATCH_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_patch_get_type, + }, + [NM_META_SETTING_TYPE_OVS_PORT] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_PORT, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_OVS_PORT_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_port_get_type, + }, + [NM_META_SETTING_TYPE_PPPOE] = + { + .meta_type = NM_META_SETTING_TYPE_PPPOE, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_PPPOE_SETTING_NAME, + .get_setting_gtype = nm_setting_pppoe_get_type, + }, + [NM_META_SETTING_TYPE_PPP] = + { + .meta_type = NM_META_SETTING_TYPE_PPP, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_PPP_SETTING_NAME, + .get_setting_gtype = nm_setting_ppp_get_type, + }, + [NM_META_SETTING_TYPE_PROXY] = + { + .meta_type = NM_META_SETTING_TYPE_PROXY, + .setting_priority = NM_SETTING_PRIORITY_IP, + .setting_name = NM_SETTING_PROXY_SETTING_NAME, + .get_setting_gtype = nm_setting_proxy_get_type, + }, + [NM_META_SETTING_TYPE_SERIAL] = + { + .meta_type = NM_META_SETTING_TYPE_SERIAL, + .setting_priority = NM_SETTING_PRIORITY_HW_AUX, + .setting_name = NM_SETTING_SERIAL_SETTING_NAME, + .get_setting_gtype = nm_setting_serial_get_type, + }, + [NM_META_SETTING_TYPE_SRIOV] = + { + .meta_type = NM_META_SETTING_TYPE_SRIOV, + .setting_priority = NM_SETTING_PRIORITY_HW_AUX, + .setting_name = NM_SETTING_SRIOV_SETTING_NAME, + .get_setting_gtype = nm_setting_sriov_get_type, + }, + [NM_META_SETTING_TYPE_TC_CONFIG] = + { + .meta_type = NM_META_SETTING_TYPE_TC_CONFIG, + .setting_priority = NM_SETTING_PRIORITY_IP, + .setting_name = NM_SETTING_TC_CONFIG_SETTING_NAME, + .get_setting_gtype = nm_setting_tc_config_get_type, + }, + [NM_META_SETTING_TYPE_TEAM] = + { + .meta_type = NM_META_SETTING_TYPE_TEAM, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_TEAM_SETTING_NAME, + .get_setting_gtype = nm_setting_team_get_type, + }, + [NM_META_SETTING_TYPE_TEAM_PORT] = + { + .meta_type = NM_META_SETTING_TYPE_TEAM_PORT, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_TEAM_PORT_SETTING_NAME, + .get_setting_gtype = nm_setting_team_port_get_type, + }, + [NM_META_SETTING_TYPE_TUN] = + { + .meta_type = NM_META_SETTING_TYPE_TUN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_TUN_SETTING_NAME, + .get_setting_gtype = nm_setting_tun_get_type, + }, + [NM_META_SETTING_TYPE_USER] = + { + .meta_type = NM_META_SETTING_TYPE_USER, + .setting_priority = NM_SETTING_PRIORITY_USER, + .setting_name = NM_SETTING_USER_SETTING_NAME, + .get_setting_gtype = nm_setting_user_get_type, + }, + [NM_META_SETTING_TYPE_VETH] = + { + .meta_type = NM_META_SETTING_TYPE_VETH, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_VETH_SETTING_NAME, + .get_setting_gtype = nm_setting_veth_get_type, + }, + [NM_META_SETTING_TYPE_VLAN] = + { + .meta_type = NM_META_SETTING_TYPE_VLAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_VLAN_SETTING_NAME, + .get_setting_gtype = nm_setting_vlan_get_type, + }, + [NM_META_SETTING_TYPE_VPN] = + { + .meta_type = NM_META_SETTING_TYPE_VPN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_VPN_SETTING_NAME, + .get_setting_gtype = nm_setting_vpn_get_type, + }, + [NM_META_SETTING_TYPE_VRF] = + { + .meta_type = NM_META_SETTING_TYPE_VRF, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_VRF_SETTING_NAME, + .get_setting_gtype = nm_setting_vrf_get_type, + }, + [NM_META_SETTING_TYPE_VXLAN] = + { + .meta_type = NM_META_SETTING_TYPE_VXLAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_VXLAN_SETTING_NAME, + .get_setting_gtype = nm_setting_vxlan_get_type, + }, + [NM_META_SETTING_TYPE_WIFI_P2P] = + { + .meta_type = NM_META_SETTING_TYPE_WIFI_P2P, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIFI_P2P_SETTING_NAME, + .get_setting_gtype = nm_setting_wifi_p2p_get_type, + }, + [NM_META_SETTING_TYPE_WIMAX] = + { + .meta_type = NM_META_SETTING_TYPE_WIMAX, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIMAX_SETTING_NAME, + .get_setting_gtype = nm_setting_wimax_get_type, + }, + [NM_META_SETTING_TYPE_WIRED] = + { + .meta_type = NM_META_SETTING_TYPE_WIRED, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIRED_SETTING_NAME, + .get_setting_gtype = nm_setting_wired_get_type, + }, + [NM_META_SETTING_TYPE_WIREGUARD] = + { + .meta_type = NM_META_SETTING_TYPE_WIREGUARD, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIREGUARD_SETTING_NAME, + .get_setting_gtype = nm_setting_wireguard_get_type, + }, + [NM_META_SETTING_TYPE_WIRELESS] = + { + .meta_type = NM_META_SETTING_TYPE_WIRELESS, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WIRELESS_SETTING_NAME, + .get_setting_gtype = nm_setting_wireless_get_type, + }, + [NM_META_SETTING_TYPE_WIRELESS_SECURITY] = + { + .meta_type = NM_META_SETTING_TYPE_WIRELESS_SECURITY, + .setting_priority = NM_SETTING_PRIORITY_HW_AUX, + .setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + .get_setting_gtype = nm_setting_wireless_security_get_type, + }, + [NM_META_SETTING_TYPE_WPAN] = + { + .meta_type = NM_META_SETTING_TYPE_WPAN, + .setting_priority = NM_SETTING_PRIORITY_HW_BASE, + .setting_name = NM_SETTING_WPAN_SETTING_NAME, + .get_setting_gtype = nm_setting_wpan_get_type, + }, + + [NM_META_SETTING_TYPE_UNKNOWN] = + { + .meta_type = NM_META_SETTING_TYPE_UNKNOWN, + }, +}; + +const NMMetaSettingType nm_meta_setting_types_by_priority[] = { + + /* NM_SETTING_PRIORITY_CONNECTION */ + NM_META_SETTING_TYPE_CONNECTION, + + /* NM_SETTING_PRIORITY_HW_BASE */ + NM_META_SETTING_TYPE_6LOWPAN, + NM_META_SETTING_TYPE_OLPC_MESH, + NM_META_SETTING_TYPE_WIRELESS, + NM_META_SETTING_TYPE_WIRED, + NM_META_SETTING_TYPE_ADSL, + NM_META_SETTING_TYPE_BOND, + NM_META_SETTING_TYPE_BRIDGE, + NM_META_SETTING_TYPE_CDMA, + NM_META_SETTING_TYPE_DUMMY, + NM_META_SETTING_TYPE_GENERIC, + NM_META_SETTING_TYPE_GSM, + NM_META_SETTING_TYPE_INFINIBAND, + NM_META_SETTING_TYPE_IP_TUNNEL, + NM_META_SETTING_TYPE_MACSEC, + NM_META_SETTING_TYPE_MACVLAN, + NM_META_SETTING_TYPE_OVS_BRIDGE, + NM_META_SETTING_TYPE_OVS_DPDK, + NM_META_SETTING_TYPE_OVS_INTERFACE, + NM_META_SETTING_TYPE_OVS_PATCH, + NM_META_SETTING_TYPE_OVS_PORT, + NM_META_SETTING_TYPE_TEAM, + NM_META_SETTING_TYPE_TUN, + NM_META_SETTING_TYPE_VETH, + NM_META_SETTING_TYPE_VLAN, + NM_META_SETTING_TYPE_VPN, + NM_META_SETTING_TYPE_VRF, + NM_META_SETTING_TYPE_VXLAN, + NM_META_SETTING_TYPE_WIFI_P2P, + NM_META_SETTING_TYPE_WIMAX, + NM_META_SETTING_TYPE_WIREGUARD, + NM_META_SETTING_TYPE_WPAN, + + /* NM_SETTING_PRIORITY_HW_NON_BASE */ + NM_META_SETTING_TYPE_BLUETOOTH, + + /* NM_SETTING_PRIORITY_HW_AUX */ + NM_META_SETTING_TYPE_WIRELESS_SECURITY, + NM_META_SETTING_TYPE_802_1X, + NM_META_SETTING_TYPE_DCB, + NM_META_SETTING_TYPE_SERIAL, + NM_META_SETTING_TYPE_SRIOV, + + /* NM_SETTING_PRIORITY_AUX */ + NM_META_SETTING_TYPE_BRIDGE_PORT, + NM_META_SETTING_TYPE_ETHTOOL, + NM_META_SETTING_TYPE_MATCH, + NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + NM_META_SETTING_TYPE_PPP, + NM_META_SETTING_TYPE_PPPOE, + NM_META_SETTING_TYPE_TEAM_PORT, + + /* NM_SETTING_PRIORITY_IP */ + NM_META_SETTING_TYPE_HOSTNAME, + NM_META_SETTING_TYPE_IP4_CONFIG, + NM_META_SETTING_TYPE_IP6_CONFIG, + NM_META_SETTING_TYPE_PROXY, + NM_META_SETTING_TYPE_TC_CONFIG, + + /* NM_SETTING_PRIORITY_USER */ + NM_META_SETTING_TYPE_USER, +}; + +const NMMetaSettingInfo * +nm_meta_setting_infos_by_name(const char *name) +{ + gssize idx; + + if (NM_MORE_ASSERTS > 10) { + guint i, j; + + for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { + const NMMetaSettingInfo *setting_info = &nm_meta_setting_infos[i]; + + nm_assert(setting_info->meta_type == (NMMetaSettingType) i); + nm_assert(setting_info->setting_name); + nm_assert(setting_info->setting_name[0]); + nm_assert(setting_info->get_setting_gtype); + nm_assert(setting_info->setting_priority != NM_SETTING_PRIORITY_INVALID); + if (i > 0 + && strcmp(nm_meta_setting_infos[i - 1].setting_name, setting_info->setting_name) + >= 0) { + g_error("nm_meta_setting_infos[%u, \"%s\"] is wrongly sorted before " + "nm_meta_setting_infos[%u, \"%s\"]. Rearange NMMetaSettingType enum", + i - 1, + nm_meta_setting_infos[i - 1].setting_name, + i, + setting_info->setting_name); + } + for (j = 0; j < i; j++) { + const NMMetaSettingInfo *s = &nm_meta_setting_infos[j]; + + nm_assert(setting_info->get_setting_gtype != s->get_setting_gtype); + } + } + } + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMMetaSettingInfo, setting_name) == 0); + idx = nm_utils_array_find_binary_search(nm_meta_setting_infos, + sizeof(NMMetaSettingInfo), + _NM_META_SETTING_TYPE_NUM, + &name, + nm_strcmp_p_with_data, + NULL); + + return idx >= 0 ? &nm_meta_setting_infos[idx] : NULL; +} + +const NMMetaSettingInfo * +nm_meta_setting_infos_by_gtype(GType gtype) +{ +#if _NM_META_SETTING_BASE_IMPL_LIBNM + nm_auto_unref_gtypeclass GTypeClass *gtypeclass_unref = NULL; + GTypeClass * gtypeclass; + NMSettingClass * klass; + + if (!g_type_is_a(gtype, NM_TYPE_SETTING)) + goto out_none; + + gtypeclass = g_type_class_peek(gtype); + if (!gtypeclass) + gtypeclass = gtypeclass_unref = g_type_class_ref(gtype); + + nm_assert(NM_IS_SETTING_CLASS(gtypeclass)); + + klass = (NMSettingClass *) gtypeclass; + + if (!klass->setting_info) + goto out_none; + + nm_assert(klass->setting_info->get_setting_gtype); + nm_assert(klass->setting_info->get_setting_gtype() == gtype); + + return klass->setting_info; + +out_none: + + if (NM_MORE_ASSERTS > 10) { + int i; + + /* this might hint to a bug, but it would be expected for NM_TYPE_SETTING + * and NM_TYPE_SETTING_IP_CONFIG. + * + * Assert that we didn't lookup for a gtype, which we would expect to find. + * An assertion failure here, hints to a bug in nm_setting_*_class_init(). + */ + for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) + nm_assert(nm_meta_setting_infos[i].get_setting_gtype() != gtype); + } + + return NULL; +#else + guint i; + + for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { + if (nm_meta_setting_infos[i].get_setting_gtype() == gtype) + return &nm_meta_setting_infos[i]; + } + return NULL; +#endif +} + +/*****************************************************************************/ + +NMSettingPriority +nm_meta_setting_info_get_base_type_priority(const NMMetaSettingInfo *setting_info, GType gtype) +{ + /* Historical oddity: PPPoE is a base-type even though it's not + * priority 1. It needs to be sorted *after* lower-level stuff like + * Wi-Fi security or 802.1x for secrets, but it's still allowed as a + * base type. + */ + + if (setting_info) { + if (NM_IN_SET(setting_info->setting_priority, + NM_SETTING_PRIORITY_HW_BASE, + NM_SETTING_PRIORITY_HW_NON_BASE) + || gtype == NM_TYPE_SETTING_PPPOE) + return setting_info->setting_priority; + } + + return NM_SETTING_PRIORITY_INVALID; +} + +NMSettingPriority +_nm_setting_type_get_base_type_priority(GType type) +{ + return nm_meta_setting_info_get_base_type_priority(nm_meta_setting_infos_by_gtype(type), type); +} + +/*****************************************************************************/ diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h new file mode 100644 index 00000000..44bd9024 --- /dev/null +++ b/src/libnmc-setting/nm-meta-setting-base-impl.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2017 - 2018 Red Hat, Inc. + */ + +#ifndef __NM_META_SETTING_BASE_IMPL_H__ +#define __NM_META_SETTING_BASE_IMPL_H__ + +#include "nm-setting-8021x.h" + +/*****************************************************************************/ + +/* + * A setting's priority should roughly follow the OSI layer model, but it also + * controls which settings get asked for secrets first. Thus settings which + * relate to things that must be working first, like hardware, should get a + * higher priority than things which layer on top of the hardware. For example, + * the GSM/CDMA settings should provide secrets before the PPP setting does, + * because a PIN is required to unlock the device before PPP can even start. + * Even settings without secrets should be assigned the right priority. + * + * 0: reserved for invalid + * + * 1: reserved for the Connection setting + * + * 2,3: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc. + * These priority 1 settings are also "base types", which means that at least + * one of them is required for the connection to be valid, and their name is + * valid in the 'type' property of the Connection setting. + * + * 4: hardware-related auxiliary settings that require a base setting to be + * successful first, like Wi-Fi security, 802.1x, etc. + * + * 5: hardware-independent settings that are required before IP connectivity + * can be established, like PPP, PPPoE, etc. + * + * 6: IP-level stuff + * + * 10: NMSettingUser + */ +typedef enum { /*< skip >*/ + NM_SETTING_PRIORITY_INVALID = 0, + NM_SETTING_PRIORITY_CONNECTION = 1, + NM_SETTING_PRIORITY_HW_BASE = 2, + NM_SETTING_PRIORITY_HW_NON_BASE = 3, + NM_SETTING_PRIORITY_HW_AUX = 4, + NM_SETTING_PRIORITY_AUX = 5, + NM_SETTING_PRIORITY_IP = 6, + NM_SETTING_PRIORITY_USER = 10, +} NMSettingPriority; + +/*****************************************************************************/ + +typedef enum { + NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, + + NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN, + + _NM_SETTING_802_1X_SCHEME_TYPE_NUM = NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN, +} NMSetting8021xSchemeType; + +typedef struct { + const char *setting_key; + NMSetting8021xCKScheme (*scheme_func)(NMSetting8021x *setting); + NMSetting8021xCKFormat (*format_func)(NMSetting8021x *setting); + const char *(*path_func)(NMSetting8021x *setting); + GBytes *(*blob_func)(NMSetting8021x *setting); + const char *(*uri_func)(NMSetting8021x *setting); + const char *(*passwd_func)(NMSetting8021x *setting); + NMSettingSecretFlags (*pwflag_func)(NMSetting8021x *setting); + gboolean (*set_cert_func)(NMSetting8021x * setting, + const char * value, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError ** error); + gboolean (*set_private_key_func)(NMSetting8021x * setting, + const char * value, + const char * password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError ** error); + const char * file_suffix; + NMSetting8021xSchemeType scheme_type; + bool is_secret : 1; +} NMSetting8021xSchemeVtable; + +extern const NMSetting8021xSchemeVtable + nm_setting_8021x_scheme_vtable[_NM_SETTING_802_1X_SCHEME_TYPE_NUM + 1]; + +/*****************************************************************************/ + +typedef enum _nm_packed { + /* the enum (and their numeric values) are internal API. Do not assign + * any meaning the numeric values, because they already have one: + * + * they are sorted in a way, that corresponds to the asciibetical sort + * order of the corresponding setting-name. */ + + NM_META_SETTING_TYPE_6LOWPAN, + NM_META_SETTING_TYPE_OLPC_MESH, + NM_META_SETTING_TYPE_WIRELESS, + NM_META_SETTING_TYPE_WIRELESS_SECURITY, + NM_META_SETTING_TYPE_802_1X, + NM_META_SETTING_TYPE_WIRED, + NM_META_SETTING_TYPE_ADSL, + NM_META_SETTING_TYPE_BLUETOOTH, + NM_META_SETTING_TYPE_BOND, + NM_META_SETTING_TYPE_BRIDGE, + NM_META_SETTING_TYPE_BRIDGE_PORT, + NM_META_SETTING_TYPE_CDMA, + NM_META_SETTING_TYPE_CONNECTION, + NM_META_SETTING_TYPE_DCB, + NM_META_SETTING_TYPE_DUMMY, + NM_META_SETTING_TYPE_ETHTOOL, + NM_META_SETTING_TYPE_GENERIC, + NM_META_SETTING_TYPE_GSM, + NM_META_SETTING_TYPE_HOSTNAME, + NM_META_SETTING_TYPE_INFINIBAND, + NM_META_SETTING_TYPE_IP_TUNNEL, + NM_META_SETTING_TYPE_IP4_CONFIG, + NM_META_SETTING_TYPE_IP6_CONFIG, + NM_META_SETTING_TYPE_MACSEC, + NM_META_SETTING_TYPE_MACVLAN, + NM_META_SETTING_TYPE_MATCH, + NM_META_SETTING_TYPE_OVS_BRIDGE, + NM_META_SETTING_TYPE_OVS_DPDK, + NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + NM_META_SETTING_TYPE_OVS_INTERFACE, + NM_META_SETTING_TYPE_OVS_PATCH, + NM_META_SETTING_TYPE_OVS_PORT, + NM_META_SETTING_TYPE_PPP, + NM_META_SETTING_TYPE_PPPOE, + NM_META_SETTING_TYPE_PROXY, + NM_META_SETTING_TYPE_SERIAL, + NM_META_SETTING_TYPE_SRIOV, + NM_META_SETTING_TYPE_TC_CONFIG, + NM_META_SETTING_TYPE_TEAM, + NM_META_SETTING_TYPE_TEAM_PORT, + NM_META_SETTING_TYPE_TUN, + NM_META_SETTING_TYPE_USER, + NM_META_SETTING_TYPE_VETH, + NM_META_SETTING_TYPE_VLAN, + NM_META_SETTING_TYPE_VPN, + NM_META_SETTING_TYPE_VRF, + NM_META_SETTING_TYPE_VXLAN, + NM_META_SETTING_TYPE_WIFI_P2P, + NM_META_SETTING_TYPE_WIMAX, + NM_META_SETTING_TYPE_WIREGUARD, + NM_META_SETTING_TYPE_WPAN, + + NM_META_SETTING_TYPE_UNKNOWN, + + _NM_META_SETTING_TYPE_NUM = NM_META_SETTING_TYPE_UNKNOWN, +} NMMetaSettingType; + +#if _NM_META_SETTING_BASE_IMPL_LIBNM + #define _NMMetaSettingInfo_Alias _NMMetaSettingInfo +#else + #define _NMMetaSettingInfo_Alias _NMMetaSettingInfoCli +#endif + +struct _NMMetaSettingInfo_Alias { + const char *setting_name; + GType (*get_setting_gtype)(void); + NMMetaSettingType meta_type; + NMSettingPriority setting_priority; +}; + +typedef struct _NMMetaSettingInfo_Alias NMMetaSettingInfo; + +extern const NMMetaSettingInfo nm_meta_setting_infos[_NM_META_SETTING_TYPE_NUM + 1]; + +extern const NMMetaSettingType nm_meta_setting_types_by_priority[_NM_META_SETTING_TYPE_NUM]; + +const NMMetaSettingInfo *nm_meta_setting_infos_by_name(const char *name); +const NMMetaSettingInfo *nm_meta_setting_infos_by_gtype(GType gtype); + +/*****************************************************************************/ + +NMSettingPriority nm_meta_setting_info_get_base_type_priority(const NMMetaSettingInfo *setting_info, + GType gtype); +NMSettingPriority _nm_setting_type_get_base_type_priority(GType type); + +#endif /* __NM_META_SETTING_BASE_IMPL_H__ */ diff --git a/src/libnmc-setting/nm-meta-setting-base.h b/src/libnmc-setting/nm-meta-setting-base.h new file mode 100644 index 00000000..85d790a3 --- /dev/null +++ b/src/libnmc-setting/nm-meta-setting-base.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NM_META_SETTING_BASE_H__ +#define __NM_META_SETTING_BASE_H__ + +#define _NM_META_SETTING_BASE_IMPL_LIBNM 0 + +#include "nm-meta-setting-base-impl.h" + +#endif /* __NM_META_SETTING_BASE_H__ */ diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c new file mode 100644 index 00000000..3ac310bf --- /dev/null +++ b/src/libnmc-setting/nm-meta-setting-desc.c @@ -0,0 +1,8688 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nm-meta-setting-desc.h" + +#include +#include +#include +#include + +#include "libnm-core-aux-intern/nm-common-macros.h" +#include "libnm-glib-aux/nm-enum-utils.h" +#include "libnm-glib-aux/nm-secret-utils.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" +#include "libnm-core-aux-extern/nm-libnm-core-aux.h" + +#include "libnmc-base/nm-vpn-helpers.h" +#include "libnmc-base/nm-client-utils.h" +#include "nm-meta-setting-access.h" + +/*****************************************************************************/ + +static char *secret_flags_to_string(guint32 flags, NMMetaAccessorGetType get_type); + +#define ALL_SECRET_FLAGS \ + (NM_SETTING_SECRET_FLAG_NONE | NM_SETTING_SECRET_FLAG_AGENT_OWNED \ + | NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED) + +/*****************************************************************************/ + +static GType +_gobject_property_get_gtype(GObject *gobject, const char *property_name) +{ + GParamSpec *param_spec; + + param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(gobject), property_name); + if (param_spec) + return param_spec->value_type; + g_return_val_if_reached(G_TYPE_INVALID); +} + +static GType +_gtype_property_get_gtype(GType gtype, const char *property_name) +{ + /* given @gtype, a type for a GObject, lookup the property @property_name + * and return its value_type. */ + if (G_TYPE_IS_CLASSED(gtype)) { + GParamSpec * param_spec; + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref(gtype); + + if (G_IS_OBJECT_CLASS(gtypeclass)) { + param_spec = g_object_class_find_property(G_OBJECT_CLASS(gtypeclass), property_name); + if (param_spec) + return param_spec->value_type; + } + } + g_return_val_if_reached(G_TYPE_INVALID); +} + +/*****************************************************************************/ + +static char * +bytes_to_string(GBytes *bytes) +{ + const guint8 *data; + gsize len; + + if (!bytes) + return NULL; + + data = g_bytes_get_data(bytes, &len); + return nm_utils_bin2hexstr_full(data, len, '\0', TRUE, NULL); +} + +/*****************************************************************************/ + +static int +_int64_cmp_desc(gconstpointer a, gconstpointer b, gpointer user_data) +{ + NM_CMP_DIRECT(*((const gint64 *) b), *((const gint64 *) a)); + return 0; +} + +static gint64 * +_value_str_as_index_list(const char *value, gsize *out_len) +{ + gs_free char *str_clone_free = NULL; + gboolean str_cloned = FALSE; + char * str; + gsize i, j; + gsize n_alloc; + gsize len; + gs_free gint64 *arr = NULL; + + *out_len = 0; + + if (!value) + return NULL; + + str = (char *) value; + n_alloc = 0; + len = 0; + while (TRUE) { + gint64 i64; + const char *s; + gsize good; + + good = strcspn(str, "," NM_ASCII_SPACES); + if (good == 0) { + if (str[0] == '\0') + break; + str++; + continue; + } + if (str[good] == '\0') { + s = str; + str += good; + } else { + if (!str_cloned) { + /* we use alloca() inside a loop here, but it is guarded to happen at most once. */ + str_cloned = TRUE; + str = nm_strndup_a(/* lgtm [cpp/alloca-in-loop] */ + 200, + str, + strlen(str), + &str_clone_free); + } + s = str; + str[good] = '\0'; + str += good + 1; + } + + i64 = _nm_utils_ascii_str_to_int64(s, 10, 0, G_MAXINT64, -1); + if (i64 == -1) + return NULL; + + if (len >= n_alloc) { + if (n_alloc > 0) { + n_alloc = n_alloc * 2; + arr = g_realloc(arr, n_alloc * sizeof(gint64)); + } else { + n_alloc = 4; + arr = g_new(gint64, n_alloc); + } + } + arr[len++] = i64; + } + + if (len > 1) { + /* sort the list of indexes descendingly, and drop duplicates. */ + g_qsort_with_data(arr, len, sizeof(gint64), _int64_cmp_desc, NULL); + j = 1; + for (i = 1; i < len; i++) { + nm_assert(arr[i - 1] >= arr[i]); + if (arr[i - 1] > arr[i]) + arr[j++] = arr[i]; + } + len = j; + } + + *out_len = len; + return g_steal_pointer(&arr); +} + +#define ESCAPED_TOKENS_WITH_SPACES_DELIMTER ' ' +#define ESCAPED_TOKENS_WITH_SPACES_DELIMTERS NM_ASCII_SPACES "," + +#define ESCAPED_TOKENS_DELIMITER ',' +#define ESCAPED_TOKENS_DELIMITERS "," + +typedef enum { + VALUE_STRSPLIT_MODE_OBJLIST, + VALUE_STRSPLIT_MODE_MULTILIST, + VALUE_STRSPLIT_MODE_ESCAPED_TOKENS, + VALUE_STRSPLIT_MODE_ESCAPED_TOKENS_WITH_SPACES, +} ValueStrsplitMode; + +static const char ** +_value_strsplit(const char *value, ValueStrsplitMode split_mode, gsize *out_len) +{ + gs_free const char **strv = NULL; + + /* FIXME: some modes should support backslash escaping. + * In particular, to distinguish from _value_str_as_index_list(), which + * does not accept '\\'. */ + + /* note that all modes remove empty tokens (",", "a,,b", ",,"). */ + switch (split_mode) { + case VALUE_STRSPLIT_MODE_OBJLIST: + strv = nm_utils_strsplit_set_full(value, + ESCAPED_TOKENS_DELIMITERS, + NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); + break; + case VALUE_STRSPLIT_MODE_MULTILIST: + strv = nm_utils_strsplit_set_full(value, + ESCAPED_TOKENS_WITH_SPACES_DELIMTERS, + NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); + break; + case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS: + strv = nm_utils_escaped_tokens_split(value, ESCAPED_TOKENS_DELIMITERS); + break; + case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS_WITH_SPACES: + strv = nm_utils_escaped_tokens_split(value, ESCAPED_TOKENS_WITH_SPACES_DELIMTERS); + break; + } + + NM_SET_OUT(out_len, NM_PTRARRAY_LEN(strv)); + return g_steal_pointer(&strv); +} + +static gboolean +_value_strsplit_assert_unsplitable(const char *str) +{ +#if NM_MORE_ASSERTS > 5 + gs_free const char **strv_test = NULL; + gsize j, l; + + /* Assert that we cannot split the token and that it + * has no unescaped delimiters. */ + + strv_test = _value_strsplit(str, VALUE_STRSPLIT_MODE_ESCAPED_TOKENS, NULL); + nm_assert(NM_PTRARRAY_LEN(strv_test) == 1); + + for (j = 0; str[j] != '\0';) { + if (str[j] == '\\') { + j++; + nm_assert(str[j] != '\0'); + } else + nm_assert(!NM_IN_SET(str[j], '\0', ',')); + j++; + } + l = j; + nm_assert(!g_ascii_isspace(str[l - 1]) || (l >= 2 && str[l - 2] == '\\')); +#endif + + return TRUE; +} + +static NMIPAddress * +_parse_ip_address(int family, const char *address, GError **error) +{ + gs_free char *ip_str = NULL; + const int MAX_PREFIX = (family == AF_INET) ? 32 : 128; + NMIPAddress * addr; + char * plen; + int prefix; + GError * local = NULL; + + g_return_val_if_fail(address, NULL); + g_return_val_if_fail(!error || !*error, NULL); + + ip_str = g_strstrip(g_strdup(address)); + + prefix = MAX_PREFIX; + + plen = strchr(ip_str, '/'); + if (plen) { + *plen++ = '\0'; + if ((prefix = _nm_utils_ascii_str_to_int64(plen, 10, 1, MAX_PREFIX, -1)) == -1) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid prefix '%s'; <1-%d> allowed"), + plen, + MAX_PREFIX); + return NULL; + } + } + + addr = nm_ip_address_new(family, ip_str, prefix, &local); + if (!addr) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid IP address: %s"), + local->message); + g_clear_error(&local); + } + return addr; +} + +static NMIPRoute * +_parse_ip_route(int family, const char *str, GError **error) +{ + const int MAX_PREFIX = (family == AF_INET) ? 32 : 128; + const char * next_hop = NULL; + int prefix; + NMIPRoute * route = NULL; + GError * local = NULL; + gint64 metric = -1; + guint i; + gs_free const char **routev = NULL; + gs_free char * str_clean_free = NULL; + const char * str_clean; + gs_free char * dest_clone = NULL; + const char * dest; + const char * plen; + gs_unref_hashtable GHashTable *attrs = NULL; +#define ROUTE_SYNTAX \ + _("The valid syntax is: 'ip[/prefix] [next-hop] [metric] [attribute=val]... [,ip[/prefix] " \ + "...]'") + + nm_assert(NM_IN_SET(family, AF_INET, AF_INET6)); + nm_assert(str); + nm_assert(!error || !*error); + + str_clean = nm_strstrip_avoid_copy_a(300, str, &str_clean_free); + routev = nm_utils_strsplit_set(str_clean, " \t"); + if (!routev) { + g_set_error(error, 1, 0, "'%s' is not valid. %s", str, ROUTE_SYNTAX); + return NULL; + } + + dest = routev[0]; + plen = strchr(dest, '/'); /* prefix delimiter */ + if (plen) { + dest_clone = g_strdup(dest); + plen = &dest_clone[plen - dest]; + dest = dest_clone; + *((char *) plen) = '\0'; + plen++; + } + prefix = MAX_PREFIX; + if (plen) { + if ((prefix = _nm_utils_ascii_str_to_int64(plen, 10, 0, MAX_PREFIX, -1)) == -1) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid prefix '%s'; <1-%d> allowed"), + plen, + MAX_PREFIX); + return NULL; + } + } + + for (i = 1; routev[i]; i++) { + gint64 tmp64; + + if (nm_utils_ipaddr_is_valid(family, routev[i])) { + if (metric != -1 || attrs) { + g_set_error(error, 1, 0, _("the next hop ('%s') must be first"), routev[i]); + return NULL; + } + next_hop = routev[i]; + } else if ((tmp64 = _nm_utils_ascii_str_to_int64(routev[i], 10, 0, G_MAXUINT32, -1)) + != -1) { + if (attrs) { + g_set_error(error, + 1, + 0, + _("the metric ('%s') must be before attributes"), + routev[i]); + return NULL; + } + metric = tmp64; + } else if (strchr(routev[i], '=')) { + GHashTableIter iter; + char * iter_key; + GVariant * iter_value; + gs_unref_hashtable GHashTable *tmp_attrs = NULL; + + tmp_attrs = nm_utils_parse_variant_attributes(routev[i], + ' ', + '=', + FALSE, + nm_ip_route_get_variant_attribute_spec(), + error); + if (!tmp_attrs) { + g_prefix_error(error, "invalid option '%s': ", routev[i]); + return NULL; + } + + if (!attrs) { + attrs = g_hash_table_new_full(nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); + } + + g_hash_table_iter_init(&iter, tmp_attrs); + while ( + g_hash_table_iter_next(&iter, (gpointer *) &iter_key, (gpointer *) &iter_value)) { + /* need to sink the reference, because nm_utils_parse_variant_attributes() returns + * floating refs. */ + g_variant_ref_sink(iter_value); + + if (!nm_ip_route_attribute_validate(iter_key, iter_value, family, NULL, error)) { + g_prefix_error(error, "%s: ", iter_key); + return NULL; + } + g_hash_table_insert(attrs, iter_key, iter_value); + g_hash_table_iter_steal(&iter); + } + } else { + g_set_error(error, 1, 0, "%s", ROUTE_SYNTAX); + return NULL; + } + } + + route = nm_ip_route_new(family, dest, prefix, next_hop, metric, &local); + if (!route) { + g_set_error(error, 1, 0, _("invalid route: %s. %s"), local->message, ROUTE_SYNTAX); + g_clear_error(&local); + return NULL; + } + + if (attrs) { + GHashTableIter iter; + char * name; + GVariant * variant; + + g_hash_table_iter_init(&iter, attrs); + while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &variant)) + nm_ip_route_set_attribute(route, name, variant); + } + + return route; +} + +/*****************************************************************************/ + +/* Max priority values from libnm-core/nm-setting-vlan.c */ +#define MAX_SKB_PRIO G_MAXUINT32 +#define MAX_8021P_PRIO 7 /* Max 802.1p priority */ + +/* + * nmc_proxy_check_script: + * @script: file name with PAC script, or raw PAC Script data + * @out_script: raw PAC Script (with removed new-line characters) + * @error: location to store error, or %NULL + * + * Check PAC Script from @script parameter and return the checked/sanitized + * config in @out_script. + * + * Returns: %TRUE if the script is valid, %FALSE if it is invalid + */ +static gboolean +nmc_proxy_check_script(const char *script, char **out_script, GError **error) +{ + enum { + _PAC_SCRIPT_TYPE_GUESS, + _PAC_SCRIPT_TYPE_FILE, + _PAC_SCRIPT_TYPE_JSON, + } desired_type = _PAC_SCRIPT_TYPE_GUESS; + const char * filename = NULL; + size_t c_len = 0; + gs_free char *script_clone = NULL; + + *out_script = NULL; + + if (!script || !script[0]) + return TRUE; + + if (g_str_has_prefix(script, "file://")) { + script += NM_STRLEN("file://"); + desired_type = _PAC_SCRIPT_TYPE_FILE; + } else if (g_str_has_prefix(script, "js://")) { + script += NM_STRLEN("js://"); + desired_type = _PAC_SCRIPT_TYPE_JSON; + } + + if (NM_IN_SET(desired_type, _PAC_SCRIPT_TYPE_FILE, _PAC_SCRIPT_TYPE_GUESS)) { + gs_free char *contents = NULL; + + if (!g_file_get_contents(script, &contents, &c_len, NULL)) { + if (desired_type == _PAC_SCRIPT_TYPE_FILE) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("cannot read pac-script from file '%s'"), + script); + return FALSE; + } + } else { + if (c_len != strlen(contents)) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("file '%s' contains non-valid utf-8"), + script); + return FALSE; + } + filename = script; + script = script_clone = g_steal_pointer(&contents); + } + } + + if (!strstr(script, "FindProxyForURL") || !g_utf8_validate(script, -1, NULL)) { + if (filename) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' does not contain a valid PAC Script"), + filename); + } else { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("Not a valid PAC Script")); + } + return FALSE; + } + + *out_script = (script == script_clone) ? g_steal_pointer(&script_clone) : g_strdup(script); + return TRUE; +} + +/* + * nmc_team_check_config: + * @config: file name with team config, or raw team JSON config data + * @out_config: raw team JSON config data + * The value must be freed with g_free(). + * @error: location to store error, or %NUL + * + * Check team config from @config parameter and return the checked + * config in @out_config. + * + * Returns: %TRUE if the config is valid, %FALSE if it is invalid + */ +static gboolean +nmc_team_check_config(const char *config, char **out_config, GError **error) +{ + enum { + _TEAM_CONFIG_TYPE_GUESS, + _TEAM_CONFIG_TYPE_FILE, + _TEAM_CONFIG_TYPE_JSON, + } desired_type = _TEAM_CONFIG_TYPE_GUESS; + size_t c_len = 0; + gs_free char *config_clone = NULL; + + *out_config = NULL; + + if (!config || !config[0]) + return TRUE; + + if (g_str_has_prefix(config, "file://")) { + config += NM_STRLEN("file://"); + desired_type = _TEAM_CONFIG_TYPE_FILE; + } else if (g_str_has_prefix(config, "json://")) { + config += NM_STRLEN("json://"); + desired_type = _TEAM_CONFIG_TYPE_JSON; + } + + if (NM_IN_SET(desired_type, _TEAM_CONFIG_TYPE_FILE, _TEAM_CONFIG_TYPE_GUESS)) { + gs_free char *contents = NULL; + + if (!g_file_get_contents(config, &contents, &c_len, NULL)) { + if (desired_type == _TEAM_CONFIG_TYPE_FILE) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("cannot read team config from file '%s'"), + config); + return FALSE; + } + } else { + if (c_len != strlen(contents)) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("team config file '%s' contains non-valid utf-8"), + config); + return FALSE; + } + config = config_clone = g_steal_pointer(&contents); + } + } + + *out_config = (config == config_clone) ? g_steal_pointer(&config_clone) : g_strdup(config); + return TRUE; +} + +static const char * +_get_text_hidden(NMMetaAccessorGetType get_type) +{ + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) + return _(NM_META_TEXT_HIDDEN); + return NM_META_TEXT_HIDDEN; +} + +/*****************************************************************************/ + +G_GNUC_PRINTF(4, 5) +static void +_env_warn_fcn(const NMMetaEnvironment *environment, + gpointer environment_user_data, + NMMetaEnvWarnLevel warn_level, + const char * fmt_l10n, + ...) +{ + va_list ap; + + if (!environment || !environment->warn_fcn) + return; + + va_start(ap, fmt_l10n); + environment->warn_fcn(environment, environment_user_data, warn_level, fmt_l10n, ap); + va_end(ap); +} + +/*****************************************************************************/ + +#define ARGS_DESCRIBE_FCN const NMMetaPropertyInfo *property_info, char **out_to_free + +#define ARGS_GET_FCN \ + const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, \ + gpointer environment_user_data, NMSetting *setting, NMMetaAccessorGetType get_type, \ + NMMetaAccessorGetFlags get_flags, NMMetaAccessorGetOutFlags *out_flags, \ + gboolean *out_is_default, gpointer *out_to_free + +#define ARGS_SET_FCN \ + const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, \ + gpointer environment_user_data, NMSetting *setting, NMMetaAccessorModifier modifier, \ + const char *value, GError **error + +#define ARGS_REMOVE_FCN \ + const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, \ + gpointer environment_user_data, NMSetting *setting, const char *value, GError **error + +#define ARGS_COMPLETE_FCN \ + const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, \ + gpointer environment_user_data, const NMMetaOperationContext *operation_context, \ + const char *text, gboolean *out_complete_filename, char ***out_to_free + +#define ARGS_VALUES_FCN const NMMetaPropertyInfo *property_info, char ***out_to_free + +#define ARGS_SETTING_INIT_FCN \ + const NMMetaSettingInfoEditor *setting_info, NMSetting *setting, \ + NMMetaAccessorSettingInitType init_type + +static gboolean _set_fcn_optionlist(ARGS_SET_FCN); + +static gboolean +_SET_FCN_DO_RESET_DEFAULT(const NMMetaPropertyInfo *property_info, + NMMetaAccessorModifier modifier, + const char * value) +{ + nm_assert(property_info); + nm_assert(!property_info->property_type->set_supports_remove); + nm_assert(NM_IN_SET(modifier, NM_META_ACCESSOR_MODIFIER_SET, NM_META_ACCESSOR_MODIFIER_ADD)); + nm_assert(value || modifier == NM_META_ACCESSOR_MODIFIER_SET); + + return value == NULL; +} + +static gboolean +_SET_FCN_DO_RESET_DEFAULT_WITH_SUPPORTS_REMOVE(const NMMetaPropertyInfo *property_info, + NMMetaAccessorModifier modifier, + const char * value) +{ + nm_assert(property_info); + nm_assert(property_info->property_type->set_supports_remove); + nm_assert(NM_IN_SET(modifier, + NM_META_ACCESSOR_MODIFIER_SET, + NM_META_ACCESSOR_MODIFIER_ADD, + NM_META_ACCESSOR_MODIFIER_DEL)); + nm_assert(value || modifier == NM_META_ACCESSOR_MODIFIER_SET); + + return value == NULL; +} + +static gboolean +_SET_FCN_DO_SET_ALL(NMMetaAccessorModifier modifier, const char *value) +{ + nm_assert(NM_IN_SET(modifier, + NM_META_ACCESSOR_MODIFIER_SET, + NM_META_ACCESSOR_MODIFIER_ADD, + NM_META_ACCESSOR_MODIFIER_DEL)); + nm_assert(value); + + return modifier == NM_META_ACCESSOR_MODIFIER_SET; +} + +static gboolean +_SET_FCN_DO_REMOVE(NMMetaAccessorModifier modifier, const char *value) +{ + nm_assert(NM_IN_SET(modifier, + NM_META_ACCESSOR_MODIFIER_SET, + NM_META_ACCESSOR_MODIFIER_ADD, + NM_META_ACCESSOR_MODIFIER_DEL)); + nm_assert(value); + + return modifier == NM_META_ACCESSOR_MODIFIER_DEL; +} + +#define RETURN_UNSUPPORTED_GET_TYPE() \ + G_STMT_START \ + { \ + if (!NM_IN_SET(get_type, \ + NM_META_ACCESSOR_GET_TYPE_PARSABLE, \ + NM_META_ACCESSOR_GET_TYPE_PRETTY)) { \ + nm_assert_not_reached(); \ + return NULL; \ + } \ + } \ + G_STMT_END; + +#define RETURN_STR_TO_FREE(val) \ + G_STMT_START \ + { \ + char *_val = (val); \ + \ + return ((*(out_to_free)) = _val); \ + } \ + G_STMT_END + +#define RETURN_STR_TEMPORARY(val) \ + G_STMT_START \ + { \ + const char *_val = (val); \ + \ + if (_val == NULL) \ + return NULL; \ + if (_val[0] == '\0') \ + return ""; \ + return ((*(out_to_free)) = g_strdup(_val)); \ + } \ + G_STMT_END + +static gboolean +_gobject_property_is_default(NMSetting *setting, const char *prop_name) +{ + nm_auto_unset_gvalue GValue v = G_VALUE_INIT; + GParamSpec * pspec; + GHashTable * ht; + char ** strv; + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(setting)), prop_name); + if (!G_IS_PARAM_SPEC(pspec)) + g_return_val_if_reached(FALSE); + + g_value_init(&v, pspec->value_type); + g_object_get_property(G_OBJECT(setting), prop_name, &v); + + if (pspec->value_type == G_TYPE_STRV) { + strv = g_value_get_boxed(&v); + return !strv || !strv[0]; + } else if (pspec->value_type == G_TYPE_HASH_TABLE) { + ht = g_value_get_boxed(&v); + return !ht || !g_hash_table_size(ht); + } + + return g_param_value_defaults(pspec, &v); +} + +static gboolean +_gobject_property_reset(NMSetting *setting, const char *prop_name, gboolean reset_default) +{ + nm_auto_unset_gvalue GValue v = G_VALUE_INIT; + GParamSpec * pspec; + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(setting)), prop_name); + if (!G_IS_PARAM_SPEC(pspec)) + g_return_val_if_reached(FALSE); + + g_value_init(&v, pspec->value_type); + if (reset_default) + g_param_value_set_default(pspec, &v); + g_object_set_property(G_OBJECT(setting), prop_name, &v); + return TRUE; +} + +static gboolean +_gobject_property_reset_default(NMSetting *setting, const char *prop_name) +{ + return _gobject_property_reset(setting, prop_name, TRUE); +} + +static const char * +_coerce_str_emptyunset(NMMetaAccessorGetType get_type, + gboolean is_default, + const char * cstr, + char ** out_str) +{ + nm_assert(out_str && !*out_str); + nm_assert((!is_default && cstr && cstr[0] != '\0') || NM_IN_STRSET(cstr, NULL, "")); + + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) { + if (!cstr || cstr[0] == '\0') { + if (is_default) + return ""; + else + return "\"\""; + } + nm_assert(!is_default); + return (*out_str = g_strdup_printf("\"%s\"", cstr)); + } + + /* we coerce NULL/"" to either "" or " ". */ + if (!cstr || cstr[0] == '\0') { + if (is_default) + return ""; + else + return " "; + } + nm_assert(!is_default); + return cstr; +} + +#define RETURN_STR_EMPTYUNSET(get_type, is_default, cstr) \ + G_STMT_START \ + { \ + char * _str = NULL; \ + const char *_cstr; \ + \ + _cstr = _coerce_str_emptyunset((get_type), (is_default), (cstr), &_str); \ + if (_str) \ + RETURN_STR_TO_FREE(_str); \ + RETURN_STR_TEMPORARY(_cstr); \ + } \ + G_STMT_END + +static gboolean +_is_default(const NMMetaPropertyInfo *property_info, NMSetting *setting) +{ + if (property_info->property_typ_data && property_info->property_typ_data->is_default_fcn) + return !!(property_info->property_typ_data->is_default_fcn(setting)); + + return _gobject_property_is_default(setting, property_info->property_name); +} + +static gconstpointer +_get_fcn_gobject_impl(const NMMetaPropertyInfo *property_info, + NMSetting * setting, + NMMetaAccessorGetType get_type, + gboolean handle_emptyunset, + gboolean * out_is_default, + gpointer * out_to_free) +{ + const char * cstr; + GType gtype_prop; + nm_auto_unset_gvalue GValue val = G_VALUE_INIT; + gboolean is_default; + gboolean glib_handles_str_transform; + + RETURN_UNSUPPORTED_GET_TYPE(); + + is_default = _is_default(property_info, setting); + + NM_SET_OUT(out_is_default, is_default); + + gtype_prop = _gobject_property_get_gtype(G_OBJECT(setting), property_info->property_name); + + glib_handles_str_transform = + !NM_IN_SET(gtype_prop, G_TYPE_BOOLEAN, G_TYPE_STRV, G_TYPE_BYTES, G_TYPE_HASH_TABLE); + + if (glib_handles_str_transform) { + /* We rely on the type conversion of the gobject property to string. */ + g_value_init(&val, G_TYPE_STRING); + } else + g_value_init(&val, gtype_prop); + + g_object_get_property(G_OBJECT(setting), property_info->property_name, &val); + + /* Currently, only one particular property asks us to "handle_emptyunset". + * So, don't implement it (yet) for the other types, where it's unneeded. */ + nm_assert(!handle_emptyunset || (gtype_prop == G_TYPE_STRV && !glib_handles_str_transform)); + + if (gtype_prop == G_TYPE_STRING) { + nm_assert(glib_handles_str_transform); + nm_assert(!handle_emptyunset); + if (property_info->property_typ_data + && property_info->property_typ_data->subtype.gobject_string.handle_emptyunset) { + /* This string property can both be empty and NULL. We need to + * signal them differently. */ + cstr = g_value_get_string(&val); + nm_assert((!!is_default) == (cstr == NULL)); + RETURN_STR_EMPTYUNSET(get_type, is_default, cstr); + } + } + + if (glib_handles_str_transform) + RETURN_STR_TEMPORARY(g_value_get_string(&val)); + + if (gtype_prop == G_TYPE_BOOLEAN) { + gboolean b; + + b = g_value_get_boolean(&val); + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) + cstr = b ? _("yes") : _("no"); + else + cstr = b ? "yes" : "no"; + return cstr; + } + + if (gtype_prop == G_TYPE_STRV) { + const char *const *strv; + + strv = g_value_get_boxed(&val); + if (strv && strv[0]) + RETURN_STR_TO_FREE(g_strjoinv(",", (char **) strv)); + + if (handle_emptyunset) { + /* we need to express empty lists from unset lists differently. */ + RETURN_STR_EMPTYUNSET(get_type, is_default, NULL); + } + + return ""; + } + + if (gtype_prop == G_TYPE_BYTES) { + char *str; + + str = bytes_to_string(g_value_get_boxed(&val)); + NM_SET_OUT(out_is_default, !str || !str[0]); + RETURN_STR_TO_FREE(str); + } + + if (gtype_prop == G_TYPE_HASH_TABLE) { + GHashTable * strdict; + gs_free const char **keys = NULL; + GString * str; + gsize i; + + nm_assert(property_info->setting_info + == &nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_WIRED] + && NM_IN_STRSET(property_info->property_name, NM_SETTING_WIRED_S390_OPTIONS)); + nm_assert(property_info->property_type->set_fcn == _set_fcn_optionlist); + + strdict = g_value_get_boxed(&val); + keys = nm_utils_strdict_get_keys(strdict, TRUE, NULL); + if (!keys) + return NULL; + + str = g_string_new(NULL); + for (i = 0; keys[i]; i++) { + const char * key = keys[i]; + const char * v = g_hash_table_lookup(strdict, key); + gs_free char *escaped_key = NULL; + gs_free char *escaped_val = NULL; + + if (str->len > 0) + g_string_append_c(str, ','); + g_string_append(str, nm_utils_escaped_tokens_options_escape_key(key, &escaped_key)); + g_string_append_c(str, '='); + g_string_append(str, nm_utils_escaped_tokens_options_escape_val(v, &escaped_val)); + } + RETURN_STR_TO_FREE(g_string_free(str, FALSE)); + } + + nm_assert_not_reached(); + return NULL; +} + +static gconstpointer _get_fcn_gobject(ARGS_GET_FCN) +{ + return _get_fcn_gobject_impl(property_info, + setting, + get_type, + FALSE, + out_is_default, + out_to_free); +} + +static gconstpointer _get_fcn_gobject_int(ARGS_GET_FCN) +{ + GParamSpec * pspec; + nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; + gboolean is_uint64 = FALSE; + NMMetaSignUnsignInt64 v; + guint base = 10; + const NMMetaUtilsIntValueInfo *value_infos; + char * return_str; + + RETURN_UNSUPPORTED_GET_TYPE(); + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(setting)), + property_info->property_name); + if (!G_IS_PARAM_SPEC(pspec)) + g_return_val_if_reached(FALSE); + + g_value_init(&gval, pspec->value_type); + g_object_get_property(G_OBJECT(setting), property_info->property_name, &gval); + NM_SET_OUT(out_is_default, g_param_value_defaults(pspec, &gval)); + switch (pspec->value_type) { + case G_TYPE_INT: + v.i64 = g_value_get_int(&gval); + break; + case G_TYPE_UINT: + v.u64 = g_value_get_uint(&gval); + is_uint64 = TRUE; + break; + case G_TYPE_INT64: + v.i64 = g_value_get_int64(&gval); + break; + case G_TYPE_UINT64: + v.u64 = g_value_get_uint64(&gval); + is_uint64 = TRUE; + break; + default: + g_return_val_if_reached(NULL); + break; + } + + if (property_info->property_typ_data + && property_info->property_typ_data->subtype.gobject_int.base > 0) { + base = property_info->property_typ_data->subtype.gobject_int.base; + } + + switch (base) { + case 10: + if (is_uint64) + return_str = g_strdup_printf("%" G_GUINT64_FORMAT, v.u64); + else + return_str = g_strdup_printf("%" G_GINT64_FORMAT, v.i64); + break; + case 16: + if (is_uint64) + return_str = g_strdup_printf("0x%" G_GINT64_MODIFIER "x", v.u64); + else + return_str = g_strdup_printf("0x%" G_GINT64_MODIFIER "x", (guint64) v.i64); + break; + default: + return_str = NULL; + g_assert_not_reached(); + } + + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY && property_info->property_typ_data + && (value_infos = property_info->property_typ_data->subtype.gobject_int.value_infos)) { + for (; value_infos->nick; value_infos++) { + if ((is_uint64 && value_infos->value.u64 == v.u64) + || (!is_uint64 && value_infos->value.i64 == v.i64)) { + gs_free char *old_str = return_str; + + return_str = g_strdup_printf("%s (%s)", old_str, value_infos->nick); + break; + } + } + } + + RETURN_STR_TO_FREE(return_str); +} + +static gconstpointer _get_fcn_gobject_mtu(ARGS_GET_FCN) +{ + guint32 mtu; + + RETURN_UNSUPPORTED_GET_TYPE(); + + if (!property_info->property_typ_data || !property_info->property_typ_data->subtype.mtu.get_fcn) + return _get_fcn_gobject_impl(property_info, + setting, + get_type, + FALSE, + out_is_default, + out_to_free); + + mtu = property_info->property_typ_data->subtype.mtu.get_fcn(setting); + if (mtu == 0) { + NM_SET_OUT(out_is_default, TRUE); + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) + return _("auto"); + return "auto"; + } + RETURN_STR_TO_FREE(g_strdup_printf("%u", (unsigned) mtu)); +} + +static gconstpointer _get_fcn_gobject_secret_flags(ARGS_GET_FCN) +{ + guint v; + GValue val = G_VALUE_INIT; + + RETURN_UNSUPPORTED_GET_TYPE(); + + g_value_init(&val, G_TYPE_UINT); + g_object_get_property(G_OBJECT(setting), property_info->property_name, &val); + v = g_value_get_uint(&val); + g_value_unset(&val); + RETURN_STR_TO_FREE(secret_flags_to_string(v, get_type)); +} + +static gconstpointer _get_fcn_gobject_enum(ARGS_GET_FCN) +{ + GType gtype = 0; + nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL; + nm_auto_unref_gtypeclass GTypeClass *gtype_prop_class = NULL; + const struct _NMUtilsEnumValueInfo * value_infos = NULL; + gboolean has_gtype = FALSE; + nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; + gint64 v; + gboolean format_numeric = FALSE; + gboolean format_numeric_hex = FALSE; + gboolean format_numeric_hex_unknown = FALSE; + gboolean format_text = FALSE; + gboolean format_text_l10n = FALSE; + gs_free char * s = NULL; + char s_numeric[64]; + GParamSpec * pspec; + + RETURN_UNSUPPORTED_GET_TYPE(); + + if (property_info->property_typ_data) { + if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { + gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype(); + has_gtype = TRUE; + } + } + + if (property_info->property_typ_data && get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY + && NM_FLAGS_ANY(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC + | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC_HEX + | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT + | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT_L10N)) { + format_numeric_hex = NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC_HEX); + format_numeric = format_numeric_hex + || NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_NUMERIC); + format_text_l10n = NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT_L10N); + format_text = format_text_l10n + || NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PRETTY_TEXT); + } else if (property_info->property_typ_data && get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY + && NM_FLAGS_ANY(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC + | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC_HEX + | NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT)) { + format_numeric_hex = NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC_HEX); + format_numeric = format_numeric + && NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_NUMERIC); + format_text = NM_FLAGS_HAS(property_info->property_typ_data->typ_flags, + NM_META_PROPERTY_TYP_FLAG_ENUM_GET_PARSABLE_TEXT); + } else if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) { + /* by default, output in format "%u (%s)" (with hex for flags and l10n). */ + format_numeric = TRUE; + format_numeric_hex_unknown = TRUE; + format_text = TRUE; + format_text_l10n = TRUE; + } else { + /* by default, output only numeric (with hex for flags). */ + format_numeric = TRUE; + format_numeric_hex_unknown = TRUE; + } + + nm_assert(format_text || format_numeric); + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(setting), property_info->property_name); + g_return_val_if_fail(pspec, NULL); + + g_value_init(&gval, pspec->value_type); + g_object_get_property(G_OBJECT(setting), property_info->property_name, &gval); + NM_SET_OUT(out_is_default, g_param_value_defaults(pspec, &gval)); + + if (pspec->value_type == G_TYPE_INT + || (G_TYPE_IS_CLASSED(pspec->value_type) + && G_IS_ENUM_CLASS( + (gtype_prop_class ?: (gtype_prop_class = g_type_class_ref(pspec->value_type)))))) { + if (pspec->value_type == G_TYPE_INT) { + if (!has_gtype) + g_return_val_if_reached(NULL); + v = g_value_get_int(&gval); + } else + v = g_value_get_enum(&gval); + } else if (pspec->value_type == G_TYPE_UINT + || (G_TYPE_IS_CLASSED(pspec->value_type) + && G_IS_FLAGS_CLASS( + (gtype_prop_class + ?: (gtype_prop_class = g_type_class_ref(pspec->value_type)))))) { + if (pspec->value_type == G_TYPE_UINT) { + if (!has_gtype) + g_return_val_if_reached(NULL); + v = g_value_get_uint(&gval); + } else + v = g_value_get_flags(&gval); + } else + g_return_val_if_reached(NULL); + + if (!has_gtype) { + gtype = pspec->value_type; + gtype_class = g_steal_pointer(>ype_prop_class); + } + + nm_assert(({ + nm_auto_unref_gtypeclass GTypeClass *t = NULL; + + (G_TYPE_IS_CLASSED(gtype) && (t = g_type_class_ref(gtype)) + && (G_IS_ENUM_CLASS(t) || G_IS_FLAGS_CLASS(t))); + })); + + if (format_numeric && !format_text) { + s = format_numeric_hex + || (format_numeric_hex_unknown + && !G_IS_ENUM_CLASS(gtype_class ?: (gtype_class = g_type_class_ref(gtype)))) + ? g_strdup_printf("0x%" G_GINT64_MODIFIER "x", v) + : g_strdup_printf("%" G_GINT64_FORMAT, v); + RETURN_STR_TO_FREE(g_steal_pointer(&s)); + } + + /* the gobject_enum.value_infos are currently ignored for the getter. They + * only declare additional aliases for the setter. */ + + if (property_info->property_typ_data) + value_infos = property_info->property_typ_data->subtype.gobject_enum.value_infos_get; + s = _nm_utils_enum_to_str_full(gtype, (int) v, ", ", value_infos); + + if (!format_numeric) + RETURN_STR_TO_FREE(g_steal_pointer(&s)); + + if (format_numeric_hex + || (format_numeric_hex_unknown + && !G_IS_ENUM_CLASS(gtype_class ?: (gtype_class = g_type_class_ref(gtype))))) + nm_sprintf_buf(s_numeric, "0x%" G_GINT64_MODIFIER "x", v); + else + nm_sprintf_buf(s_numeric, "%" G_GINT64_FORMAT, v); + + if (nm_streq0(s, s_numeric)) + RETURN_STR_TO_FREE(g_steal_pointer(&s)); + + if (format_text_l10n) + RETURN_STR_TO_FREE(g_strdup_printf(_("%s (%s)"), s_numeric, s)); + else + RETURN_STR_TO_FREE(g_strdup_printf("%s (%s)", s_numeric, s)); +} + +/*****************************************************************************/ + +static gboolean _set_fcn_gobject_string(ARGS_SET_FCN) +{ + gs_free char *to_free = NULL; + + if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + if (property_info->property_typ_data) { + if (property_info->property_typ_data->subtype.gobject_string.handle_emptyunset) { + if (value && value[0] && NM_STRCHAR_ALL(value, ch, ch == ' ')) { + /* this string property can both be %NULL and empty. To express that, we coerce + * a value of all whitespaces to dropping the first whitespace. That means, + * " " gives "", " " gives " ", and so on. + * + * This way the user can set the string value to "" (meaning NULL) and to + * " " (meaning ""), and any other string. + * + * This is and non-obvious escaping mechanism. But out of all the possible + * solutions, it seems the most sensible one. */ + value++; + } + } + if (property_info->property_typ_data->subtype.gobject_string.validate_fcn) { + value = property_info->property_typ_data->subtype.gobject_string.validate_fcn(value, + &to_free, + error); + if (!value) + return FALSE; + } else if (property_info->property_typ_data->values_static) { + value = + nmc_string_is_valid(value, + (const char **) property_info->property_typ_data->values_static, + error); + if (!value) + return FALSE; + } + } + g_object_set(setting, property_info->property_name, value, NULL); + return TRUE; +} + +static gboolean +_set_fcn_gobject_bool_impl(const NMMetaPropertyInfo *property_info, + NMSetting * setting, + NMMetaAccessorModifier modifier, + const char * value, + GError ** error) +{ + gboolean val_bool; + + if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + if (!nmc_string_to_bool(value, &val_bool, error)) + return FALSE; + + g_object_set(setting, property_info->property_name, val_bool, NULL); + 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; + const GParamSpec * pspec; + nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; + gboolean is_uint64; + NMMetaSignUnsignInt64 v; + gboolean has_minmax = FALSE; + NMMetaSignUnsignInt64 min = {0}; + NMMetaSignUnsignInt64 max = {0}; + guint base = 10; + const NMMetaUtilsIntValueInfo *value_infos; + + if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(setting)), + property_info->property_name); + if (!G_IS_PARAM_SPEC(pspec)) + g_return_val_if_reached(FALSE); + + is_uint64 = NM_IN_SET(pspec->value_type, G_TYPE_UINT, G_TYPE_UINT64); + + if (property_info->property_typ_data) { + if (value + && (value_infos = property_info->property_typ_data->subtype.gobject_int.value_infos)) { + gs_free char *vv_free = NULL; + const char * vv; + + vv = nm_strstrip_avoid_copy_a(300, value, &vv_free); + for (; value_infos->nick; value_infos++) { + if (nm_streq(value_infos->nick, vv)) { + v = value_infos->value; + goto have_value_from_nick; + } + } + } + + if (property_info->property_typ_data->subtype.gobject_int.base > 0) + base = property_info->property_typ_data->subtype.gobject_int.base; + + if ((is_uint64 + && (property_info->property_typ_data->subtype.gobject_int.min.u64 + || property_info->property_typ_data->subtype.gobject_int.max.u64)) + || (!is_uint64 + && (property_info->property_typ_data->subtype.gobject_int.min.i64 + || property_info->property_typ_data->subtype.gobject_int.max.i64))) { + min = property_info->property_typ_data->subtype.gobject_int.min; + max = property_info->property_typ_data->subtype.gobject_int.max; + has_minmax = TRUE; + } + } + + if (!has_minmax) { + switch (pspec->value_type) { + case G_TYPE_INT: + { + const GParamSpecInt *p = (GParamSpecInt *) pspec; + + min.i64 = p->minimum; + max.i64 = p->maximum; + } break; + case G_TYPE_UINT: + { + const GParamSpecUInt *p = (GParamSpecUInt *) pspec; + + min.u64 = p->minimum; + max.u64 = p->maximum; + } break; + case G_TYPE_INT64: + { + const GParamSpecInt64 *p = (GParamSpecInt64 *) pspec; + + min.i64 = p->minimum; + max.i64 = p->maximum; + } break; + case G_TYPE_UINT64: + { + const GParamSpecUInt64 *p = (GParamSpecUInt64 *) pspec; + + min.u64 = p->minimum; + max.u64 = p->maximum; + } break; + default: + g_return_val_if_reached(FALSE); + } + } + + if (is_uint64) + v.u64 = _nm_utils_ascii_str_to_uint64(value, base, min.u64, max.u64, 0); + else + v.i64 = _nm_utils_ascii_str_to_int64(value, base, min.i64, max.i64, 0); + + if ((errsv = errno) != 0) { + if (errsv == ERANGE) { + if (is_uint64) { + g_set_error( + error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is out of range [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "]"), + value, + min.u64, + max.u64); + } else { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is out of range [%" G_GINT64_FORMAT ", %" G_GINT64_FORMAT "]"), + value, + min.i64, + max.i64); + } + } else { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is not a valid number"), + value); + } + return FALSE; + } + +have_value_from_nick: + + g_value_init(&gval, pspec->value_type); + switch (pspec->value_type) { + case G_TYPE_INT: + g_value_set_int(&gval, v.i64); + break; + case G_TYPE_UINT: + g_value_set_uint(&gval, v.u64); + break; + case G_TYPE_INT64: + g_value_set_int64(&gval, v.i64); + break; + case G_TYPE_UINT64: + g_value_set_uint64(&gval, v.u64); + break; + default: + g_return_val_if_reached(FALSE); + break; + } + + /* Validate the number according to the property spec */ + if (!nm_g_object_set_property(G_OBJECT(setting), property_info->property_name, &gval, error)) + g_return_val_if_reached(FALSE); + + return TRUE; +} + +static gboolean _set_fcn_gobject_mtu(ARGS_SET_FCN) +{ + nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; + const GParamSpec * pspec; + gint64 v; + + if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + if (nm_streq(value, "auto")) + value = "0"; + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(setting)), + property_info->property_name); + if (!pspec || pspec->value_type != G_TYPE_UINT) + g_return_val_if_reached(FALSE); + + v = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT32, -1); + if (v < 0) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is out of range [0, %u]"), + value, + (unsigned) G_MAXUINT32); + return FALSE; + } + + g_value_init(&gval, pspec->value_type); + g_value_set_uint(&gval, v); + + if (!nm_g_object_set_property(G_OBJECT(setting), property_info->property_name, &gval, error)) + g_return_val_if_reached(FALSE); + + return TRUE; +} + +/* Ideally we'll be able to get this from a public header. */ +#ifndef IEEE802154_ADDR_LEN + #define IEEE802154_ADDR_LEN 8 +#endif + +static gboolean _set_fcn_gobject_mac(ARGS_SET_FCN) +{ + NMMetaPropertyTypeMacMode mode; + gboolean valid; + + if (_SET_FCN_DO_RESET_DEFAULT(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + if (property_info->property_typ_data) + mode = property_info->property_typ_data->subtype.mac.mode; + else + mode = NM_META_PROPERTY_TYPE_MAC_MODE_DEFAULT; + + if (mode == NM_META_PROPERTY_TYPE_MAC_MODE_INFINIBAND) { + valid = nm_utils_hwaddr_valid(value, INFINIBAND_ALEN); + } else if (mode == NM_META_PROPERTY_TYPE_MAC_MODE_WPAN) { + valid = nm_utils_hwaddr_valid(value, IEEE802154_ADDR_LEN); + } else { + valid = + nm_utils_hwaddr_valid(value, ETH_ALEN) + || (mode == NM_META_PROPERTY_TYPE_MAC_MODE_CLONED && NM_CLONED_MAC_IS_SPECIAL(value)); + } + + if (!valid) { + g_set_error(error, 1, 0, _("'%s' is not a valid Ethernet MAC"), value); + return FALSE; + } + + g_object_set(setting, property_info->property_name, value, NULL); + return TRUE; +} + +static gboolean _set_fcn_gobject_enum(ARGS_SET_FCN) +{ + GType gtype = 0; + GType gtype_prop; + gboolean has_gtype = FALSE; + nm_auto_unset_gvalue GValue gval = G_VALUE_INIT; + nm_auto_unref_gtypeclass GTypeClass *gtype_prop_class = NULL; + nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL; + gboolean is_flags; + int v; + + if (_SET_FCN_DO_RESET_DEFAULT_WITH_SUPPORTS_REMOVE(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + if (property_info->property_typ_data) { + if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { + gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype(); + has_gtype = TRUE; + } + } + + gtype_prop = _gobject_property_get_gtype(G_OBJECT(setting), property_info->property_name); + + if (has_gtype && NM_IN_SET(gtype_prop, G_TYPE_INT, G_TYPE_UINT) && G_TYPE_IS_CLASSED(gtype) + && (gtype_prop_class = g_type_class_ref(gtype)) + && ((is_flags = G_IS_FLAGS_CLASS(gtype_prop_class)) || G_IS_ENUM_CLASS(gtype_prop_class))) { + /* valid */ + } else if (!has_gtype && G_TYPE_IS_CLASSED(gtype_prop) + && (gtype_prop_class = g_type_class_ref(gtype_prop)) + && ((is_flags = G_IS_FLAGS_CLASS(gtype_prop_class)) + || G_IS_ENUM_CLASS(gtype_prop_class))) { + gtype = gtype_prop; + } else + g_return_val_if_reached(FALSE); + + if (!_nm_utils_enum_from_str_full( + gtype, + value, + &v, + NULL, + property_info->property_typ_data + ? property_info->property_typ_data->subtype.gobject_enum.value_infos + : NULL)) + goto fail; + + if (property_info->property_typ_data + && property_info->property_typ_data->subtype.gobject_enum.pre_set_notify) { + property_info->property_typ_data->subtype.gobject_enum.pre_set_notify(property_info, + environment, + environment_user_data, + setting, + v); + } + + gtype_class = g_type_class_ref(gtype); + + if (G_IS_FLAGS_CLASS(gtype_class) && !_SET_FCN_DO_SET_ALL(modifier, value)) { + nm_auto_unset_gvalue GValue int_value = {}; + guint v_flag; + + g_value_init(&int_value, G_TYPE_UINT); + g_object_get_property(G_OBJECT(setting), property_info->property_name, &int_value); + v_flag = g_value_get_uint(&int_value); + + if (_SET_FCN_DO_REMOVE(modifier, value)) + v = (int) (v_flag & ~((guint) v)); + else + v = (int) (v_flag | ((guint) v)); + } + + g_value_init(&gval, gtype_prop); + if (gtype_prop == G_TYPE_INT) + g_value_set_int(&gval, v); + else if (gtype_prop == G_TYPE_UINT) + g_value_set_uint(&gval, v); + else if (is_flags) { + nm_assert(G_IS_FLAGS_CLASS(gtype_prop_class)); + g_value_set_flags(&gval, v); + } else { + nm_assert(G_IS_ENUM_CLASS(gtype_prop_class)); + g_value_set_enum(&gval, v); + } + + if (!nm_g_object_set_property(G_OBJECT(setting), property_info->property_name, &gval, NULL)) + goto fail; + + return TRUE; + +fail: + if (error) { + gs_free const char **valid_all = NULL; + gs_free const char * valid_str = NULL; + gboolean has_minmax = FALSE; + int min = G_MININT; + int max = G_MAXINT; + + if (property_info->property_typ_data) { + if (property_info->property_typ_data->subtype.gobject_enum.min + || property_info->property_typ_data->subtype.gobject_enum.max) { + min = property_info->property_typ_data->subtype.gobject_enum.min; + max = property_info->property_typ_data->subtype.gobject_enum.max; + has_minmax = TRUE; + } + } + + if (!has_minmax && is_flags) { + min = 0; + max = (int) G_MAXUINT; + } + + valid_all = nm_utils_enum_get_values(gtype, min, max); + valid_str = g_strjoinv(",", (char **) valid_all); + if (is_flags) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid option '%s', use a combination of [%s]"), + value, + valid_str); + } else { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("invalid option '%s', use one of [%s]"), + value, + valid_str); + } + } + return FALSE; +} + +/*****************************************************************************/ + +static const char *const *_values_fcn_gobject_enum(ARGS_VALUES_FCN) +{ + GType gtype = 0; + gboolean has_gtype = FALSE; + gboolean has_minmax = FALSE; + int min = G_MININT; + int max = G_MAXINT; + char ** v; + + if (property_info->property_typ_data) { + if (property_info->property_typ_data->subtype.gobject_enum.min + || property_info->property_typ_data->subtype.gobject_enum.max) { + min = property_info->property_typ_data->subtype.gobject_enum.min; + max = property_info->property_typ_data->subtype.gobject_enum.max; + has_minmax = TRUE; + } + if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { + gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype(); + has_gtype = TRUE; + } + } + + if (!has_gtype) { + gtype = _gtype_property_get_gtype(property_info->setting_info->general->get_setting_gtype(), + property_info->property_name); + } + + if (!has_minmax && G_TYPE_IS_CLASSED(gtype)) { + nm_auto_unref_gtypeclass GTypeClass *class = NULL; + + class = g_type_class_ref(gtype); + if (G_IS_FLAGS_CLASS(class)) { + min = 0; + max = (int) G_MAXUINT; + } + } + + /* the gobject_enum.value_infos are currently ignored for the list of + * values. They only declare additional (hidden) aliases for the setter. */ + + v = nm_utils_strv_make_deep_copied(nm_utils_enum_get_values(gtype, min, max)); + return (const char *const *) (*out_to_free = v); +} + +/*****************************************************************************/ + +static const char *const *_complete_fcn_gobject_bool(ARGS_COMPLETE_FCN) +{ + static const char *const v[] = { + "true", + "false", + "on", + "off", + "1", + "0", + "yes", + "no", + NULL, + }; + + if (!text || !text[0]) + return &v[6]; + 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; + guint i, j; + guint len = 0; + char ** ifnames; + + if (environment && environment->get_nm_devices) { + devices = environment->get_nm_devices(environment, environment_user_data, &len); + } + + if (len == 0) + return NULL; + + ifnames = g_new(char *, len + 1); + for (i = 0, j = 0; i < len; i++) { + const char *ifname; + + nm_assert(NM_IS_DEVICE(devices[i])); + + ifname = nm_device_get_iface(devices[i]); + if (ifname) + ifnames[j++] = g_strdup(ifname); + } + ifnames[j++] = NULL; + + *out_to_free = ifnames; + return (const char *const *) ifnames; +} + +/*****************************************************************************/ + +static char * +wep_key_type_to_string(NMWepKeyType type) +{ + switch (type) { + case NM_WEP_KEY_TYPE_KEY: + return g_strdup_printf(_("%d (key)"), type); + case NM_WEP_KEY_TYPE_PASSPHRASE: + return g_strdup_printf(_("%d (passphrase)"), type); + case NM_WEP_KEY_TYPE_UNKNOWN: + default: + return g_strdup_printf(_("%d (unknown)"), type); + } +} + +static char * +vlan_flags_to_string(guint32 flags, NMMetaAccessorGetType get_type) +{ + GString *flag_str; + + if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) + return g_strdup_printf("%u", flags); + + if (flags == 0) + return g_strdup(_("0 (NONE)")); + + flag_str = g_string_new(NULL); + g_string_printf(flag_str, "%d (", flags); + + if (flags & NM_VLAN_FLAG_REORDER_HEADERS) + g_string_append(flag_str, _("REORDER_HEADERS, ")); + if (flags & NM_VLAN_FLAG_GVRP) + g_string_append(flag_str, _("GVRP, ")); + if (flags & NM_VLAN_FLAG_LOOSE_BINDING) + g_string_append(flag_str, _("LOOSE_BINDING, ")); + if (flags & NM_VLAN_FLAG_MVRP) + g_string_append(flag_str, _("MVRP, ")); + + if (flag_str->str[flag_str->len - 1] == '(') + g_string_append(flag_str, _("unknown")); + else + g_string_truncate(flag_str, flag_str->len - 2); /* chop off trailing ', ' */ + + g_string_append_c(flag_str, ')'); + + return g_string_free(flag_str, FALSE); +} + +static char * +secret_flags_to_string(guint32 flags, NMMetaAccessorGetType get_type) +{ + GString *flag_str; + + if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) + return g_strdup_printf("%u", flags); + + if (flags == 0) + return g_strdup(_("0 (none)")); + + flag_str = g_string_new(NULL); + g_string_printf(flag_str, "%u (", flags); + + if (flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED) + g_string_append(flag_str, _("agent-owned, ")); + if (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) + g_string_append(flag_str, _("not saved, ")); + if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) + g_string_append(flag_str, _("not required, ")); + + if (flag_str->str[flag_str->len - 1] == '(') + g_string_append(flag_str, _("unknown")); + else + g_string_truncate(flag_str, flag_str->len - 2); /* chop off trailing ', ' */ + + g_string_append_c(flag_str, ')'); + + return g_string_free(flag_str, FALSE); +} + +static const char * +_multilist_do_validate(const NMMetaPropertyInfo *property_info, + NMSetting * setting, + const char * item, + GError ** error) +{ + if (property_info->property_typ_data->values_static) { + nm_assert(!property_info->property_typ_data->subtype.multilist.validate_fcn); + return nmc_string_is_valid(item, + (const char **) property_info->property_typ_data->values_static, + error); + } + if (property_info->property_typ_data->subtype.multilist.validate_fcn) { + return property_info->property_typ_data->subtype.multilist.validate_fcn(item, error); + } + if (property_info->property_typ_data->subtype.multilist.validate2_fcn) { + return property_info->property_typ_data->subtype.multilist.validate2_fcn(setting, + item, + error); + } + + return item; +} + +static gconstpointer _get_fcn_multilist(ARGS_GET_FCN) +{ + return _get_fcn_gobject_impl( + property_info, + setting, + get_type, + property_info->property_typ_data->subtype.multilist.clear_emptyunset_fcn != NULL, + out_is_default, + out_to_free); +} + +static gboolean +_multilist_clear_property(const NMMetaPropertyInfo *property_info, + NMSetting * setting, + gboolean is_set /* or else set default */) +{ + if (property_info->property_typ_data->subtype.multilist.clear_emptyunset_fcn) { + property_info->property_typ_data->subtype.multilist.clear_emptyunset_fcn(setting, is_set); + return TRUE; + } + if (property_info->property_typ_data->subtype.multilist.clear_all_fcn) { + property_info->property_typ_data->subtype.multilist.clear_all_fcn(setting); + return TRUE; + } + return _gobject_property_reset(setting, property_info->property_name, FALSE); +} + +static gboolean _set_fcn_multilist(ARGS_SET_FCN) +{ + gs_free const char **strv = NULL; + gsize i, j, nstrv; + + if (_SET_FCN_DO_RESET_DEFAULT_WITH_SUPPORTS_REMOVE(property_info, modifier, value)) + return _multilist_clear_property(property_info, setting, FALSE); + + if (_SET_FCN_DO_REMOVE(modifier, value) + && (property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_u32 + || property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_s + || property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_u)) { + gs_free gint64 *indexes = NULL; + + indexes = _value_str_as_index_list(value, &nstrv); + if (indexes) { + gint64 num; + + if (property_info->property_typ_data->subtype.multilist.get_num_fcn_u32) + num = property_info->property_typ_data->subtype.multilist.get_num_fcn_u32(setting); + else + num = property_info->property_typ_data->subtype.multilist.get_num_fcn_u(setting); + for (i = 0; i < nstrv; i++) { + gint64 idx = indexes[i]; + + if (idx >= num) + continue; + + if (property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_u32) + property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_u32( + setting, + idx); + else if (property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_s) + property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_s(setting, + idx); + else + property_info->property_typ_data->subtype.multilist.remove_by_idx_fcn_u(setting, + idx); + } + return TRUE; + } + } + + if (_SET_FCN_DO_SET_ALL(modifier, value) + && property_info->property_typ_data->subtype.multilist.clear_emptyunset_fcn + && value[0] == '\0') + return _multilist_clear_property(property_info, setting, FALSE); + + strv = _value_strsplit( + value, + property_info->property_typ_data->subtype.multilist.strsplit_plain + ? VALUE_STRSPLIT_MODE_MULTILIST + : (property_info->property_typ_data->subtype.multilist.strsplit_with_spaces + ? VALUE_STRSPLIT_MODE_ESCAPED_TOKENS_WITH_SPACES + : VALUE_STRSPLIT_MODE_ESCAPED_TOKENS), + &nstrv); + + j = 0; + for (i = 0; i < nstrv; i++) { + const char *item = strv[i]; + + item = _multilist_do_validate(property_info, setting, item, error); + if (!item) + return FALSE; + strv[j++] = item; + } + nstrv = j; + + if (_SET_FCN_DO_SET_ALL(modifier, value)) + _multilist_clear_property(property_info, setting, TRUE); + else if (property_info->property_typ_data->subtype.multilist.clear_emptyunset_fcn + && _is_default(property_info, setting)) { + /* the property is already the default. But we hav here a '+' / '-' modifier, so + * that always makes it non-default (empty) first. */ + _multilist_clear_property(property_info, setting, TRUE); + } + + for (i = 0; i < nstrv; i++) { + if (_SET_FCN_DO_REMOVE(modifier, value)) { + property_info->property_typ_data->subtype.multilist.remove_by_value_fcn(setting, + strv[i]); + } else { + if (property_info->property_typ_data->subtype.multilist.add2_fcn) + property_info->property_typ_data->subtype.multilist.add2_fcn(setting, strv[i]); + else + property_info->property_typ_data->subtype.multilist.add_fcn(setting, strv[i]); + } + } + return TRUE; +} + +static gboolean _set_fcn_optionlist(ARGS_SET_FCN) +{ + gs_free const char **strv = NULL; + gs_free const char **strv_val = NULL; + gsize strv_len; + gsize i, nstrv; + + nm_assert(!error || !*error); + + if (_SET_FCN_DO_RESET_DEFAULT_WITH_SUPPORTS_REMOVE(property_info, modifier, value)) + return _gobject_property_reset_default(setting, property_info->property_name); + + nstrv = 0; + strv = nm_utils_escaped_tokens_options_split_list(value); + if (strv) { + strv_len = NM_PTRARRAY_LEN(strv); + + strv_val = g_new(const char *, strv_len); + for (i = 0; strv[i]; i++) { + const char *opt_name; + const char *opt_value; + + nm_utils_escaped_tokens_options_split((char *) strv[i], &opt_name, &opt_value); + + if (property_info->property_type->values_fcn + || property_info->property_typ_data->values_static) { + gs_strfreev char **valid_options_to_free = NULL; + const char *const *valid_options; + + if (property_info->property_type->values_fcn) + valid_options = + property_info->property_type->values_fcn(property_info, + &valid_options_to_free); + else + valid_options = property_info->property_typ_data->values_static; + + opt_name = nmc_string_is_valid(opt_name, (const char **) valid_options, error); + if (!opt_name) + return FALSE; + } + + if (opt_value) { + if (_SET_FCN_DO_REMOVE(modifier, value)) + opt_value = NULL; + } else { + if (!_SET_FCN_DO_REMOVE(modifier, value)) { + nm_utils_error_set(error, + NM_UTILS_ERROR_INVALID_ARGUMENT, + _("'%s' is not valid; use