/* NetworkManager * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright 2010 - 2017 Red Hat, Inc. */ #include "nm-default.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, 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_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); }