summary refs log tree commit diff
path: root/src/nm-cloud-setup/nmcs-provider.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-cloud-setup/nmcs-provider.c')
-rw-r--r--src/nm-cloud-setup/nmcs-provider.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/src/nm-cloud-setup/nmcs-provider.c b/src/nm-cloud-setup/nmcs-provider.c
new file mode 100644
index 00000000..138e78d4
--- /dev/null
+++ b/src/nm-cloud-setup/nmcs-provider.c
@@ -0,0 +1,380 @@
+/* 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(GHashTable *iface_datas,
+                                           gboolean    was_requested,
+                                           const char *hwaddr)
+{
+    NMCSProviderGetConfigIfaceData *iface_data;
+
+    nm_assert(hwaddr);
+
+    iface_data  = g_slice_new(NMCSProviderGetConfigIfaceData);
+    *iface_data = (NMCSProviderGetConfigIfaceData){
+        .hwaddr        = g_strdup(hwaddr),
+        .iface_idx     = -1,
+        .was_requested = was_requested,
+    };
+
+    /* 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(iface_datas, (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);
+    g_free(iface_data->iproutes_arr);
+    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){
+        .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->result_dict, 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);
+}