/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "libnm-client-aux-extern/nm-default-client.h" #include "nmcs-provider.h" #include "nm-cloud-setup-utils.h" /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_HTTP_CLIENT, ); typedef struct _NMCSProviderPrivate { NMHttpClient *http_client; } NMCSProviderPrivate; G_DEFINE_TYPE(NMCSProvider, nmcs_provider, G_TYPE_OBJECT); #define NMCS_PROVIDER_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMCSProvider, NMCS_IS_PROVIDER) /*****************************************************************************/ const char * nmcs_provider_get_name(NMCSProvider *self) { NMCSProviderClass *klass; g_return_val_if_fail(NMCS_IS_PROVIDER(self), NULL); klass = NMCS_PROVIDER_GET_CLASS(self); nm_assert(klass->_name); return klass->_name; } /*****************************************************************************/ NMHttpClient * nmcs_provider_get_http_client(NMCSProvider *self) { g_return_val_if_fail(NMCS_IS_PROVIDER(self), NULL); return NMCS_PROVIDER_GET_PRIVATE(self)->http_client; } GMainContext * nmcs_provider_get_main_context(NMCSProvider *self) { g_return_val_if_fail(NMCS_IS_PROVIDER(self), NULL); return nm_http_client_get_main_context(NMCS_PROVIDER_GET_PRIVATE(self)->http_client); } /*****************************************************************************/ static int _result_new_sort_iface_data(gconstpointer pa, gconstpointer pb) { const NMCSProviderGetConfigIfaceData *a = *((const NMCSProviderGetConfigIfaceData *const *) pa); const NMCSProviderGetConfigIfaceData *b = *((const NMCSProviderGetConfigIfaceData *const *) pb); /* negative iface_idx are sorted to the end. */ NM_CMP_DIRECT((a->iface_idx < 0), (b->iface_idx < 0)); NM_CMP_FIELD(a, b, iface_idx); return 0; } static NMCSProviderGetConfigResult * nmcs_provider_get_config_result_new(GHashTable *iface_datas) { const NMCSProviderGetConfigIfaceData *iface_data; NMCSProviderGetConfigResult *result; GHashTableIter h_iter; guint num_valid_ifaces = 0; guint num_ipv4s = 0; GPtrArray *ptrarr; guint n_iface_datas; n_iface_datas = g_hash_table_size(iface_datas); ptrarr = g_ptr_array_sized_new(n_iface_datas + 1u); g_hash_table_iter_init(&h_iter, iface_datas); while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &iface_data)) { if (nmcs_provider_get_config_iface_data_is_valid(iface_data)) { num_valid_ifaces++; num_ipv4s += iface_data->ipv4s_len; } g_ptr_array_add(ptrarr, (gpointer) iface_data); } g_ptr_array_sort(ptrarr, _result_new_sort_iface_data); nm_assert(n_iface_datas == ptrarr->len); g_ptr_array_add(ptrarr, NULL); result = g_new(NMCSProviderGetConfigResult, 1); *result = (NMCSProviderGetConfigResult){ .iface_datas = g_hash_table_ref(iface_datas), .n_iface_datas = n_iface_datas, .iface_datas_arr = (const NMCSProviderGetConfigIfaceData **) g_ptr_array_free(ptrarr, FALSE), .num_valid_ifaces = num_valid_ifaces, .num_ipv4s = num_ipv4s, }; #if NM_MORE_ASSERTS > 5 { gsize iface_idx_expected = 0; guint i; for (i = 0; i < result->n_iface_datas; i++) { if (result->iface_datas_arr[i]->iface_idx < 0) { nm_assert(result->iface_datas_arr[i]->iface_idx == -1); iface_idx_expected = -1; continue; } nm_assert(result->iface_datas_arr[i]->iface_idx == iface_idx_expected); iface_idx_expected++; } } #endif return result; } void nmcs_provider_get_config_result_free(NMCSProviderGetConfigResult *result) { if (result) { nm_g_hash_table_unref(result->iface_datas); g_free((gpointer) result->iface_datas_arr); g_free(result); } } /*****************************************************************************/ void nmcs_provider_detect(NMCSProvider *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { gs_unref_object GTask *task = NULL; const char *env; g_return_if_fail(NMCS_IS_PROVIDER(self)); g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); task = nm_g_task_new(self, cancellable, nmcs_provider_detect, callback, user_data); nmcs_wait_for_objects_register(task); env = g_getenv(NMCS_PROVIDER_GET_CLASS(self)->_env_provider_enabled); if (!_nm_utils_ascii_str_to_bool(env, FALSE)) { g_task_return_error(task, nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "provider is disabled")); return; } NMCS_PROVIDER_GET_CLASS(self)->detect(self, g_steal_pointer(&task)); } gboolean nmcs_provider_detect_finish(NMCSProvider *self, GAsyncResult *result, GError **error) { g_return_val_if_fail(NMCS_IS_PROVIDER(self), FALSE); g_return_val_if_fail(nm_g_task_is_valid(result, self, nmcs_provider_detect), FALSE); return g_task_propagate_boolean(G_TASK(result), error); } /*****************************************************************************/ NMCSProviderGetConfigIfaceData * nmcs_provider_get_config_iface_data_create(NMCSProviderGetConfigTaskData *get_config_data, gboolean was_requested, const char *hwaddr) { NMCSProviderGetConfigIfaceData *iface_data; nm_assert(hwaddr); nm_assert(get_config_data); nm_assert(NMCS_IS_PROVIDER(get_config_data->self)); iface_data = g_slice_new(NMCSProviderGetConfigIfaceData); *iface_data = (NMCSProviderGetConfigIfaceData){ .get_config_data = get_config_data, .hwaddr = g_strdup(hwaddr), .iface_idx = -1, .was_requested = was_requested, }; /* "priv" is a union, and according to C, it might not be properly initialized * that all union members are set to false/0/NULL/0.0. We need to know which * union field we are going to use, and that depends on the type of "self". * Also, knowing the type would allow us to initialize to something other than * false/0/NULL/0.0. */ if (G_OBJECT_TYPE(get_config_data->self) == nmcs_provider_aliyun_get_type()) { iface_data->priv.aliyun = (typeof(iface_data->priv.aliyun)){ .has_primary_ip_address = FALSE, }; } /* the has does not own the key (iface_datta->hwaddr), the lifetime of the * key is associated with the iface_data instance. */ g_hash_table_replace(get_config_data->result_dict, (char *) iface_data->hwaddr, iface_data); return iface_data; } static void _iface_data_free(gpointer data) { NMCSProviderGetConfigIfaceData *iface_data = data; g_free(iface_data->ipv4s_arr); nm_g_ptr_array_unref(iface_data->iproutes); g_free((char *) iface_data->hwaddr); nm_g_slice_free(iface_data); } static void _get_config_task_maybe_return(NMCSProviderGetConfigTaskData *get_config_data, GError *error_take) { gs_free_error GError *error = error_take; nm_assert(get_config_data); nm_assert(G_IS_TASK(get_config_data->task)); if (!error) { if (get_config_data->n_pending > 0) return; } g_cancellable_cancel(get_config_data->intern_cancellable); if (error) { if (nm_utils_error_is_cancelled(error)) _LOGD("get-config: cancelled"); else _LOGD("get-config: failed: %s", error->message); g_task_return_error(get_config_data->task, g_steal_pointer(&error)); } else { _LOGD("get-config: success"); g_task_return_pointer(get_config_data->task, nmcs_provider_get_config_result_new(get_config_data->result_dict), (GDestroyNotify) nmcs_provider_get_config_result_free); } nm_clear_g_signal_handler(g_task_get_cancellable(get_config_data->task), &get_config_data->extern_cancelled_id); if (get_config_data->extra_data_destroy) get_config_data->extra_data_destroy(get_config_data->extra_data); nm_clear_pointer(&get_config_data->result_dict, g_hash_table_unref); nm_g_object_unref(get_config_data->intern_cancellable); g_object_unref(get_config_data->task); nm_g_slice_free(get_config_data); } void _nmcs_provider_get_config_task_maybe_return(NMCSProviderGetConfigTaskData *get_config_data, GError *error_take) { nm_assert(!error_take || !nm_utils_error_is_cancelled(error_take)); _get_config_task_maybe_return(get_config_data, error_take); } static void _get_config_cancelled_cb(GObject *object, gpointer user_data) { _get_config_task_maybe_return(user_data, nm_utils_error_new_cancelled(FALSE, NULL)); } void nmcs_provider_get_config(NMCSProvider *self, gboolean any, const char *const *hwaddrs, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { NMCSProviderGetConfigTaskData *get_config_data; g_return_if_fail(NMCS_IS_PROVIDER(self)); g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); _LOGD("get-config: starting"); get_config_data = g_slice_new(NMCSProviderGetConfigTaskData); *get_config_data = (NMCSProviderGetConfigTaskData){ /* "self" is kept alive by "task". */ .self = self, .task = nm_g_task_new(self, cancellable, nmcs_provider_get_config, callback, user_data), .any = any, .result_dict = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, _iface_data_free), }; nmcs_wait_for_objects_register(get_config_data->task); for (; hwaddrs && hwaddrs[0]; hwaddrs++) nmcs_provider_get_config_iface_data_create(get_config_data, TRUE, hwaddrs[0]); if (cancellable) { gulong cancelled_id; cancelled_id = g_cancellable_connect(cancellable, G_CALLBACK(_get_config_cancelled_cb), get_config_data, NULL); if (cancelled_id == 0) { /* the callback was already invoked synchronously and the task already returned. */ return; } get_config_data->extern_cancelled_id = cancelled_id; get_config_data->intern_cancellable = g_cancellable_new(); } NMCS_PROVIDER_GET_CLASS(self)->get_config(self, get_config_data); } NMCSProviderGetConfigResult * nmcs_provider_get_config_finish(NMCSProvider *self, GAsyncResult *result, GError **error) { g_return_val_if_fail(NMCS_IS_PROVIDER(self), FALSE); g_return_val_if_fail(nm_g_task_is_valid(result, self, nmcs_provider_get_config), FALSE); return g_task_propagate_pointer(G_TASK(result), error); } /*****************************************************************************/ static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMCSProviderPrivate *priv = NMCS_PROVIDER_GET_PRIVATE(object); switch (prop_id) { case PROP_HTTP_CLIENT: priv->http_client = g_value_dup_object(value); g_return_if_fail(NM_IS_HTTP_CLIENT(priv->http_client)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } /*****************************************************************************/ static void nmcs_provider_init(NMCSProvider *self) { NMCSProviderPrivate *priv; priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NMCS_TYPE_PROVIDER, NMCSProviderPrivate); self->_priv = priv; } static void dispose(GObject *object) { NMCSProvider *self = NMCS_PROVIDER(object); NMCSProviderPrivate *priv = NMCS_PROVIDER_GET_PRIVATE(self); g_clear_object(&priv->http_client); G_OBJECT_CLASS(nmcs_provider_parent_class)->dispose(object); } static void nmcs_provider_class_init(NMCSProviderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); g_type_class_add_private(object_class, sizeof(NMCSProviderPrivate)); object_class->set_property = set_property; object_class->dispose = dispose; obj_properties[PROP_HTTP_CLIENT] = g_param_spec_object(NMCS_PROVIDER_HTTP_CLIENT, "", "", NM_TYPE_HTTP_CLIENT, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); }