mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-29 13:50:13 +01:00
Use a union, it makes more sense.
Note that with union, C's struct initialization might not sufficiently
set all fields to the default. In practice yes, but theoretically in C
a NULL pointer and floats must not have all zero bits, so the following
is not guaranteed to work:
struct {
int some_field;
union {
void *v_ptr;
int v_int;
};
} variable = {
.some_field = 24,
};
assert(variable.union.v_ptr == 0);
assert(variable.union.v_int == 0);
When initializing the variable, we should not rely on automatically
initialize all union members correctly. It cannot at the same time
set NULL pointers and zero integers -- well, on our architectures it
probably can, but not as far as guaranteed by C language.
We need to know which union field we are going to use and initialize
it explicitly.
As we know the provider type, we can do that.
Also, maybe in the future we need special free/unref calls when
destroying the type specific data in NMCSProviderGetConfigIfaceData.
As we know the provider, we can.
Note that having type specific data in NMCSProviderGetConfigIfaceData.priv
is a layering violation. But it is still simpler than implementing
type specific handlers (callbacks) or tracking the data somewhere else.
After all, we know at compile time all the existing provider types.
(cherry picked from commit 1e696c7e93)
396 lines
13 KiB
C
396 lines
13 KiB
C
/* 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);
|
|
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){
|
|
/* "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);
|
|
}
|