cloud-setup: add tool for automatic IP configuration in cloud
This is a tool for automatically configuring networking in a cloud environment. Currently it only supports IPv4 on EC2, but it's intended for extending to other cloud providers (Azure). See [1] and [2] for how to configure secondary IP addresses on EC2. This is what the tool currently aims to do (but in the future it might do more). [1] https://aws.amazon.com/premiumsupport/knowledge-center/ec2-ubuntu-secondary-network-interface/ It is inspired by SuSE's cloud-netconfig ([1], [2]) and ec2-net-utils package on Amazon Linux ([3], [4]). [1] https://www.suse.com/c/multi-nic-cloud-netconfig-ec2-azure/ [2] https://github.com/SUSE-Enceladus/cloud-netconfig [3] https://github.com/aws/ec2-net-utils [4] https://github.com/lorengordon/ec2-net-utils.git It is also intended to work without configuration. The main point is that you boot an image with NetworkManager and nm-cloud-setup enabled, and it just works.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -65,6 +65,8 @@ test-*.trs
|
|||||||
/dispatcher/tests/test-dispatcher-envp
|
/dispatcher/tests/test-dispatcher-envp
|
||||||
|
|
||||||
/clients/cli/nmcli
|
/clients/cli/nmcli
|
||||||
|
/clients/cloud-setup/nm-cloud-setup
|
||||||
|
/clients/cloud-setup/nm-cloud-setup.service
|
||||||
/clients/common/settings-docs.h
|
/clients/common/settings-docs.h
|
||||||
/clients/common/tests/test-clients-common
|
/clients/common/tests/test-clients-common
|
||||||
/clients/common/tests/test-libnm-core-aux
|
/clients/common/tests/test-libnm-core-aux
|
||||||
|
81
Makefile.am
81
Makefile.am
@@ -4636,6 +4636,87 @@ EXTRA_DIST += \
|
|||||||
clients/tui/meson.build \
|
clients/tui/meson.build \
|
||||||
clients/tui/newt/meson.build
|
clients/tui/newt/meson.build
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# clients/nm-cloud-setup
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
if BUILD_NM_CLOUD_SETUP
|
||||||
|
|
||||||
|
libexec_PROGRAMS += clients/cloud-setup/nm-cloud-setup
|
||||||
|
|
||||||
|
clients_cloud_setup_nm_cloud_setup_SOURCES = \
|
||||||
|
clients/cloud-setup/main.c \
|
||||||
|
clients/cloud-setup/nm-cloud-setup-utils.c \
|
||||||
|
clients/cloud-setup/nm-cloud-setup-utils.h \
|
||||||
|
clients/cloud-setup/nm-http-client.c \
|
||||||
|
clients/cloud-setup/nm-http-client.h \
|
||||||
|
clients/cloud-setup/nmcs-provider.c \
|
||||||
|
clients/cloud-setup/nmcs-provider.h \
|
||||||
|
clients/cloud-setup/nmcs-provider-ec2.c \
|
||||||
|
clients/cloud-setup/nmcs-provider-ec2.h \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
clients_cloud_setup_nm_cloud_setup_CPPFLAGS = \
|
||||||
|
$(clients_cppflags) \
|
||||||
|
-DG_LOG_DOMAIN=\""nm-cloud-setup"\" \
|
||||||
|
$(LIBCURL_CFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
clients_cloud_setup_nm_cloud_setup_LDFLAGS = \
|
||||||
|
-Wl,--version-script="$(srcdir)/linker-script-binary.ver" \
|
||||||
|
$(SANITIZER_EXEC_LDFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
clients_cloud_setup_nm_cloud_setup_LDADD = \
|
||||||
|
shared/nm-libnm-core-aux/libnm-libnm-core-aux.la \
|
||||||
|
shared/nm-libnm-core-intern/libnm-libnm-core-intern.la \
|
||||||
|
shared/nm-glib-aux/libnm-glib-aux.la \
|
||||||
|
shared/nm-std-aux/libnm-std-aux.la \
|
||||||
|
shared/libcsiphash.la \
|
||||||
|
libnm/libnm.la \
|
||||||
|
$(GLIB_LIBS) \
|
||||||
|
$(LIBCURL_LIBS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
$(clients_cloud_setup_nm_cloud_setup_OBJECTS): $(libnm_core_lib_h_pub_mkenums)
|
||||||
|
$(clients_cloud_setup_nm_cloud_setup_OBJECTS): $(libnm_lib_h_pub_mkenums)
|
||||||
|
|
||||||
|
if HAVE_SYSTEMD
|
||||||
|
|
||||||
|
systemdsystemunit_DATA += \
|
||||||
|
clients/cloud-setup/nm-cloud-setup.service \
|
||||||
|
clients/cloud-setup/nm-cloud-setup.timer \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
clients/cloud-setup/nm-cloud-setup.service: $(srcdir)/clients/cloud-setup/nm-cloud-setup.service.in
|
||||||
|
$(AM_V_GEN) $(data_edit) $< >$@
|
||||||
|
|
||||||
|
install-data-hook-cloud-setup: install-data-hook-dispatcher
|
||||||
|
$(INSTALL_SCRIPT) "$(srcdir)/clients/cloud-setup/90-nm-cloud-setup.sh" "$(DESTDIR)$(nmlibdir)/dispatcher.d/no-wait.d/"
|
||||||
|
ln -fs no-wait.d/90-nm-cloud-setup.sh "$(DESTDIR)$(nmlibdir)/dispatcher.d/90-nm-cloud-setup.sh"
|
||||||
|
|
||||||
|
install_data_hook += install-data-hook-cloud-setup
|
||||||
|
|
||||||
|
uninstall-hook-cloud-setup:
|
||||||
|
rm -f "$(DESTDIR)$(nmlibdir)/dispatcher.d/no-wait.d/90-nm-cloud-setup.sh"
|
||||||
|
rm -f "$(DESTDIR)$(nmlibdir)/dispatcher.d/90-nm-cloud-setup.sh"
|
||||||
|
|
||||||
|
uninstall_hook += uninstall-hook-cloud-setup
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
EXTRA_DIST += \
|
||||||
|
clients/cloud-setup/90-nm-cloud-setup.sh \
|
||||||
|
clients/cloud-setup/meson.build \
|
||||||
|
clients/cloud-setup/nm-cloud-setup.service.in \
|
||||||
|
clients/cloud-setup/nm-cloud-setup.timer \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
CLEANFILES += \
|
||||||
|
clients/cloud-setup/nm-cloud-setup.service
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# clients/tests
|
# clients/tests
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
2
NEWS
2
NEWS
@@ -29,6 +29,8 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
|
|||||||
* libnm: heavily internal rework NMClient. This slims down libnm and makes the
|
* libnm: heavily internal rework NMClient. This slims down libnm and makes the
|
||||||
implementation more efficient. NMClient should work now well with a separate
|
implementation more efficient. NMClient should work now well with a separate
|
||||||
GMainContext.
|
GMainContext.
|
||||||
|
* nm-cloud-setup: add new tool for automatically configuring NetworkManager
|
||||||
|
in cloud. Currently only EC2 and IPv4 is supported.
|
||||||
|
|
||||||
=============================================
|
=============================================
|
||||||
NetworkManager-1.20
|
NetworkManager-1.20
|
||||||
|
7
clients/cloud-setup/90-nm-cloud-setup.sh
Executable file
7
clients/cloud-setup/90-nm-cloud-setup.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
case "$2" in
|
||||||
|
up|dhcp4-change)
|
||||||
|
exec systemctl --no-block restart nm-cloud-setup.service
|
||||||
|
;;
|
||||||
|
esac
|
646
clients/cloud-setup/main.c
Normal file
646
clients/cloud-setup/main.c
Normal file
@@ -0,0 +1,646 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#include "nm-default.h"
|
||||||
|
|
||||||
|
#include "nm-cloud-setup-utils.h"
|
||||||
|
|
||||||
|
#include "nmcs-provider-ec2.h"
|
||||||
|
#include "nm-libnm-core-intern/nm-libnm-core-utils.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainLoop *main_loop;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
NMCSProvider *provider_result;
|
||||||
|
guint detect_count;
|
||||||
|
} ProviderDetectData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_provider_detect_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gs_unref_object NMCSProvider *provider = NMCS_PROVIDER (source);
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
ProviderDetectData *dd;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
success = nmcs_provider_detect_finish (provider, result, &error);
|
||||||
|
|
||||||
|
nm_assert (success != (!!error));
|
||||||
|
|
||||||
|
if (nm_utils_error_is_cancelled (error, FALSE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dd = user_data;
|
||||||
|
|
||||||
|
nm_assert (dd->detect_count > 0);
|
||||||
|
dd->detect_count--;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
_LOGI ("provider %s not detected: %s", nmcs_provider_get_name (provider), error->message);
|
||||||
|
if (dd->detect_count > 0) {
|
||||||
|
/* wait longer. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGI ("no provider detected");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGI ("provider %s detected", nmcs_provider_get_name (provider));
|
||||||
|
dd->provider_result = g_steal_pointer (&provider);
|
||||||
|
|
||||||
|
done:
|
||||||
|
g_cancellable_cancel (dd->cancellable);
|
||||||
|
g_main_loop_quit (dd->main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_provider_detect_sigterm_cb (GCancellable *source,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ProviderDetectData *dd = user_data;
|
||||||
|
|
||||||
|
g_cancellable_cancel (dd->cancellable);
|
||||||
|
g_clear_object (&dd->provider_result);
|
||||||
|
dd->detect_count = 0;
|
||||||
|
g_main_loop_quit (dd->main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NMCSProvider *
|
||||||
|
_provider_detect (GCancellable *sigterm_cancellable)
|
||||||
|
{
|
||||||
|
nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
gs_unref_object GCancellable *cancellable = g_cancellable_new ();
|
||||||
|
gs_unref_object NMHttpClient *http_client = NULL;
|
||||||
|
ProviderDetectData dd = {
|
||||||
|
.cancellable = cancellable,
|
||||||
|
.main_loop = main_loop,
|
||||||
|
.detect_count = 0,
|
||||||
|
.provider_result = NULL,
|
||||||
|
};
|
||||||
|
const GType gtypes[] = {
|
||||||
|
NMCS_TYPE_PROVIDER_EC2,
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
gulong cancellable_signal_id;
|
||||||
|
|
||||||
|
cancellable_signal_id = g_cancellable_connect (sigterm_cancellable,
|
||||||
|
G_CALLBACK (_provider_detect_sigterm_cb),
|
||||||
|
&dd,
|
||||||
|
NULL);
|
||||||
|
if (!cancellable_signal_id)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
http_client = nmcs_wait_for_objects_register (nm_http_client_new ());
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (gtypes); i++) {
|
||||||
|
NMCSProvider *provider;
|
||||||
|
|
||||||
|
provider = g_object_new (gtypes[i],
|
||||||
|
NMCS_PROVIDER_HTTP_CLIENT, http_client,
|
||||||
|
NULL);
|
||||||
|
nmcs_wait_for_objects_register (provider);
|
||||||
|
|
||||||
|
_LOGD ("start detecting %s provider...", nmcs_provider_get_name (provider));
|
||||||
|
dd.detect_count++;
|
||||||
|
nmcs_provider_detect (provider,
|
||||||
|
cancellable,
|
||||||
|
_provider_detect_cb,
|
||||||
|
&dd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dd.detect_count > 0)
|
||||||
|
g_main_loop_run (main_loop);
|
||||||
|
|
||||||
|
out:
|
||||||
|
nm_clear_g_signal_handler (sigterm_cancellable, &cancellable_signal_id);
|
||||||
|
return dd.provider_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainLoop *main_loop;
|
||||||
|
NMClient *nmc;
|
||||||
|
} ClientCreateData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_nmc_create_cb (GObject *source_object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gs_unref_object NMClient *nmc = NULL;
|
||||||
|
ClientCreateData *data = user_data;
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
|
||||||
|
nmc = nm_client_new_finish (result, &error);
|
||||||
|
if (!nmc) {
|
||||||
|
if (!nm_utils_error_is_cancelled (error, FALSE))
|
||||||
|
_LOGI ("failure to talk to NetworkManager: %s", error->message);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_client_get_nm_running (nmc)) {
|
||||||
|
_LOGI ("NetworkManager is not running");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGD ("NetworkManager is running");
|
||||||
|
nmcs_wait_for_objects_register (nmc);
|
||||||
|
nmcs_wait_for_objects_register (nm_client_get_context_busy_watcher (nmc));
|
||||||
|
|
||||||
|
data->nmc = g_steal_pointer (&nmc);
|
||||||
|
out:
|
||||||
|
g_main_loop_quit (data->main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NMClient *
|
||||||
|
_nmc_create (GCancellable *sigterm_cancellable)
|
||||||
|
{
|
||||||
|
nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
ClientCreateData data = {
|
||||||
|
.main_loop = main_loop,
|
||||||
|
};
|
||||||
|
|
||||||
|
nm_client_new_async (sigterm_cancellable, _nmc_create_cb, &data);
|
||||||
|
|
||||||
|
g_main_loop_run (main_loop);
|
||||||
|
|
||||||
|
return data.nmc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static char **
|
||||||
|
_nmc_get_hwaddrs (NMClient *nmc)
|
||||||
|
{
|
||||||
|
gs_unref_ptrarray GPtrArray *hwaddrs = NULL;
|
||||||
|
const GPtrArray *devices;
|
||||||
|
char **hwaddrs_v;
|
||||||
|
gs_free char *str = NULL;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
devices = nm_client_get_devices (nmc);
|
||||||
|
|
||||||
|
for (i = 0; i < devices->len; i++) {
|
||||||
|
NMDevice *device = devices->pdata[i];
|
||||||
|
const char *hwaddr;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
if (!NM_IS_DEVICE_ETHERNET (device))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (nm_device_get_state (device) < NM_DEVICE_STATE_UNAVAILABLE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hwaddr = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
|
||||||
|
if (!hwaddr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
s = nmcs_utils_hwaddr_normalize (hwaddr, -1);
|
||||||
|
if (!s)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!hwaddrs)
|
||||||
|
hwaddrs = g_ptr_array_new_with_free_func (g_free);
|
||||||
|
g_ptr_array_add (hwaddrs, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hwaddrs) {
|
||||||
|
_LOGD ("found interfaces: none");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ptr_array_add (hwaddrs, NULL);
|
||||||
|
hwaddrs_v = (char **) g_ptr_array_free (g_steal_pointer (&hwaddrs), FALSE);
|
||||||
|
|
||||||
|
_LOGD ("found interfaces: %s", (str = g_strjoinv (", ", hwaddrs_v)));
|
||||||
|
|
||||||
|
return hwaddrs_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NMDevice *
|
||||||
|
_nmc_get_device_by_hwaddr (NMClient *nmc,
|
||||||
|
const char *hwaddr)
|
||||||
|
{
|
||||||
|
const GPtrArray *devices;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
devices = nm_client_get_devices (nmc);
|
||||||
|
|
||||||
|
for (i = 0; i < devices->len; i++) {
|
||||||
|
NMDevice *device = devices->pdata[i];
|
||||||
|
const char *hwaddr_dev;
|
||||||
|
gs_free char *s = NULL;
|
||||||
|
|
||||||
|
if (!NM_IS_DEVICE_ETHERNET (device))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hwaddr_dev = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
|
||||||
|
if (!hwaddr_dev)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
s = nmcs_utils_hwaddr_normalize (hwaddr_dev, -1);
|
||||||
|
if (s && nm_streq (s, hwaddr))
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainLoop *main_loop;
|
||||||
|
GHashTable *config_dict;
|
||||||
|
} GetConfigData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GetConfigData *data = user_data;
|
||||||
|
gs_unref_hashtable GHashTable *config_dict = NULL;
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
|
||||||
|
config_dict = nmcs_provider_get_config_finish (NMCS_PROVIDER (source), result, &error);
|
||||||
|
|
||||||
|
if (!config_dict) {
|
||||||
|
if (!nm_utils_error_is_cancelled (error, FALSE))
|
||||||
|
_LOGI ("failure to get meta data: %s", error->message);
|
||||||
|
} else
|
||||||
|
_LOGD ("meta data received");
|
||||||
|
|
||||||
|
data->config_dict = g_steal_pointer (&config_dict);
|
||||||
|
g_main_loop_quit (data->main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GHashTable *
|
||||||
|
_get_config (GCancellable *sigterm_cancellable,
|
||||||
|
NMCSProvider *provider,
|
||||||
|
NMClient *nmc)
|
||||||
|
{
|
||||||
|
nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
GetConfigData data = {
|
||||||
|
.main_loop = main_loop,
|
||||||
|
};
|
||||||
|
gs_strfreev char **hwaddrs = NULL;
|
||||||
|
|
||||||
|
hwaddrs = _nmc_get_hwaddrs (nmc);
|
||||||
|
|
||||||
|
nmcs_provider_get_config (provider,
|
||||||
|
TRUE,
|
||||||
|
(const char *const*) hwaddrs,
|
||||||
|
sigterm_cancellable,
|
||||||
|
_get_config_cb,
|
||||||
|
&data);
|
||||||
|
|
||||||
|
g_main_loop_run (main_loop);
|
||||||
|
|
||||||
|
return data.config_dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_nmc_skip_connection (NMConnection *connection)
|
||||||
|
{
|
||||||
|
NMSettingUser *s_user;
|
||||||
|
const char *v;
|
||||||
|
|
||||||
|
s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER));
|
||||||
|
if (!s_user)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
#define USER_TAG_SKIP "org.freedesktop.nm-cloud-setup.skip"
|
||||||
|
|
||||||
|
nm_assert (nm_setting_user_check_key (USER_TAG_SKIP, NULL));
|
||||||
|
|
||||||
|
v = nm_setting_user_get_data (s_user, USER_TAG_SKIP);
|
||||||
|
return _nm_utils_ascii_str_to_bool (v, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_nmc_mangle_connection (NMDevice *device,
|
||||||
|
NMConnection *connection,
|
||||||
|
gboolean is_single_nic,
|
||||||
|
const NMCSProviderGetConfigIfaceData *config_data,
|
||||||
|
gboolean *out_changed)
|
||||||
|
{
|
||||||
|
NMSettingIPConfig *s_ip;
|
||||||
|
gboolean addrs_changed;
|
||||||
|
gboolean routes_changed;
|
||||||
|
gboolean rules_changed;
|
||||||
|
gsize i;
|
||||||
|
in_addr_t gateway;
|
||||||
|
gint64 rt_metric;
|
||||||
|
guint32 rt_table;
|
||||||
|
gs_unref_ptrarray GPtrArray *addrs_new = NULL;
|
||||||
|
gs_unref_ptrarray GPtrArray *rules_new = NULL;
|
||||||
|
nm_auto_unref_ip_route NMIPRoute *route_new = NULL;
|
||||||
|
|
||||||
|
if (!nm_streq0 (nm_connection_get_connection_type (connection), NM_SETTING_WIRED_SETTING_NAME))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
s_ip = nm_connection_get_setting_ip4_config (connection);
|
||||||
|
if (!s_ip)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
addrs_new = g_ptr_array_new_full (config_data->ipv4s_len, (GDestroyNotify) nm_ip_address_unref);
|
||||||
|
for (i = 0; i < config_data->ipv4s_len; i++) {
|
||||||
|
NMIPAddress *entry;
|
||||||
|
|
||||||
|
entry = nm_ip_address_new_binary (AF_INET,
|
||||||
|
&config_data->ipv4s_arr[i],
|
||||||
|
config_data->cidr_prefix,
|
||||||
|
NULL);
|
||||||
|
if (entry)
|
||||||
|
g_ptr_array_add (addrs_new, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
gateway = nm_utils_ip4_address_clear_host_address (config_data->cidr_addr, config_data->cidr_prefix);
|
||||||
|
((guint8 *) &gateway)[3] += 1;
|
||||||
|
|
||||||
|
rt_metric = 10;
|
||||||
|
rt_table = 30400 + config_data->iface_idx;
|
||||||
|
|
||||||
|
route_new = nm_ip_route_new_binary (AF_INET,
|
||||||
|
&nm_ip_addr_zero,
|
||||||
|
0,
|
||||||
|
&gateway,
|
||||||
|
rt_metric,
|
||||||
|
NULL);
|
||||||
|
nm_ip_route_set_attribute (route_new,
|
||||||
|
NM_IP_ROUTE_ATTRIBUTE_TABLE,
|
||||||
|
g_variant_new_uint32 (rt_table));
|
||||||
|
|
||||||
|
rules_new = g_ptr_array_new_full (config_data->ipv4s_len, (GDestroyNotify) nm_ip_routing_rule_unref);
|
||||||
|
for (i = 0; i < config_data->ipv4s_len; i++) {
|
||||||
|
NMIPRoutingRule *entry;
|
||||||
|
char sbuf[NM_UTILS_INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
entry = nm_ip_routing_rule_new (AF_INET);
|
||||||
|
nm_ip_routing_rule_set_priority (entry, rt_table);
|
||||||
|
nm_ip_routing_rule_set_from (entry,
|
||||||
|
nm_utils_inet4_ntop (config_data->ipv4s_arr[i], sbuf),
|
||||||
|
32);
|
||||||
|
nm_ip_routing_rule_set_table (entry, rt_table);
|
||||||
|
|
||||||
|
nm_assert (nm_ip_routing_rule_validate (entry, NULL));
|
||||||
|
|
||||||
|
g_ptr_array_add (rules_new, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs_changed = nmcs_setting_ip_replace_ipv4_addresses (s_ip,
|
||||||
|
(NMIPAddress **) addrs_new->pdata,
|
||||||
|
addrs_new->len);
|
||||||
|
|
||||||
|
routes_changed = nmcs_setting_ip_replace_ipv4_routes (s_ip,
|
||||||
|
&route_new,
|
||||||
|
1);
|
||||||
|
|
||||||
|
rules_changed = nmcs_setting_ip_replace_ipv4_rules (s_ip,
|
||||||
|
(NMIPRoutingRule **) rules_new->pdata,
|
||||||
|
rules_new->len);
|
||||||
|
|
||||||
|
NM_SET_OUT (out_changed, addrs_changed
|
||||||
|
|| routes_changed
|
||||||
|
|| rules_changed);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static guint
|
||||||
|
_config_data_get_num_valid (GHashTable *config_dict)
|
||||||
|
{
|
||||||
|
const NMCSProviderGetConfigIfaceData *config_data;
|
||||||
|
GHashTableIter h_iter;
|
||||||
|
guint n = 0;
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&h_iter, config_dict);
|
||||||
|
while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &config_data)) {
|
||||||
|
if (nmcs_provider_get_config_iface_data_is_valid (config_data))
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_config_one (GCancellable *sigterm_cancellable,
|
||||||
|
NMClient *nmc,
|
||||||
|
gboolean is_single_nic,
|
||||||
|
const char *hwaddr,
|
||||||
|
const NMCSProviderGetConfigIfaceData *config_data)
|
||||||
|
{
|
||||||
|
gs_unref_object NMDevice *device = NULL;
|
||||||
|
gs_unref_object NMConnection *applied_connection = NULL;
|
||||||
|
guint64 applied_version_id;
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
gboolean changed;
|
||||||
|
gboolean version_id_changed;
|
||||||
|
guint try_count;
|
||||||
|
gboolean any_changes = FALSE;
|
||||||
|
|
||||||
|
g_main_context_iteration (NULL, FALSE);
|
||||||
|
|
||||||
|
if (g_cancellable_is_cancelled (sigterm_cancellable))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
device = nm_g_object_ref (_nmc_get_device_by_hwaddr (nmc, hwaddr));
|
||||||
|
if (!device) {
|
||||||
|
_LOGD ("config device %s: skip because device not found", hwaddr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nmcs_provider_get_config_iface_data_is_valid (config_data)) {
|
||||||
|
_LOGD ("config device %s: skip because meta data not successfully fetched", hwaddr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGD ("config device %s: configuring \"%s\" (%s)...",
|
||||||
|
hwaddr,
|
||||||
|
nm_device_get_iface (device) ?: "/unknown/",
|
||||||
|
nm_object_get_path (NM_OBJECT (device)));
|
||||||
|
|
||||||
|
try_count = 0;
|
||||||
|
|
||||||
|
try_again:
|
||||||
|
|
||||||
|
applied_connection = nmcs_device_get_applied_connection (device,
|
||||||
|
sigterm_cancellable,
|
||||||
|
&applied_version_id,
|
||||||
|
&error);
|
||||||
|
if (!applied_connection) {
|
||||||
|
if (!nm_utils_error_is_cancelled (error, FALSE))
|
||||||
|
_LOGD ("config device %s: device has no applied connection (%s). Skip", hwaddr, error->message);
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_nmc_skip_connection (applied_connection)) {
|
||||||
|
_LOGD ("config device %s: skip applied connection due to user data %s", hwaddr, USER_TAG_SKIP);
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_nmc_mangle_connection (device,
|
||||||
|
applied_connection,
|
||||||
|
is_single_nic,
|
||||||
|
config_data,
|
||||||
|
&changed)) {
|
||||||
|
_LOGD ("config device %s: device has no suitable applied connection. Skip", hwaddr);
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
_LOGD ("config device %s: device needs no update to applied connection \"%s\" (%s). Skip",
|
||||||
|
hwaddr,
|
||||||
|
nm_connection_get_id (applied_connection),
|
||||||
|
nm_connection_get_uuid (applied_connection));
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGD ("config device %s: reapply connection \"%s\" (%s)",
|
||||||
|
hwaddr,
|
||||||
|
nm_connection_get_id (applied_connection),
|
||||||
|
nm_connection_get_uuid (applied_connection));
|
||||||
|
|
||||||
|
/* we are about to call Reapply(). If if that fails, it counts as if we changed something. */
|
||||||
|
any_changes = TRUE;
|
||||||
|
|
||||||
|
if (!nmcs_device_reapply (device,
|
||||||
|
sigterm_cancellable,
|
||||||
|
applied_connection,
|
||||||
|
applied_version_id,
|
||||||
|
&version_id_changed,
|
||||||
|
&error)) {
|
||||||
|
if ( version_id_changed
|
||||||
|
&& try_count < 5) {
|
||||||
|
_LOGD ("config device %s: applied connection changed in the meantime. Retry...",
|
||||||
|
hwaddr);
|
||||||
|
g_clear_object (&applied_connection);
|
||||||
|
g_clear_error (&error);
|
||||||
|
try_count++;
|
||||||
|
goto try_again;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_utils_error_is_cancelled (error, FALSE)) {
|
||||||
|
_LOGD ("config device %s: failure to reapply connection \"%s\" (%s): %s",
|
||||||
|
hwaddr,
|
||||||
|
nm_connection_get_id (applied_connection),
|
||||||
|
nm_connection_get_uuid (applied_connection),
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGD ("config device %s: connection \"%s\" (%s) reapplied",
|
||||||
|
hwaddr,
|
||||||
|
nm_connection_get_id (applied_connection),
|
||||||
|
nm_connection_get_uuid (applied_connection));
|
||||||
|
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_config_all (GCancellable *sigterm_cancellable,
|
||||||
|
NMClient *nmc,
|
||||||
|
GHashTable *config_dict)
|
||||||
|
{
|
||||||
|
GHashTableIter h_iter;
|
||||||
|
const NMCSProviderGetConfigIfaceData *c_config_data;
|
||||||
|
const char *c_hwaddr;
|
||||||
|
gboolean is_single_nic;
|
||||||
|
gboolean any_changes = FALSE;
|
||||||
|
|
||||||
|
is_single_nic = (_config_data_get_num_valid (config_dict) <= 1);
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&h_iter, config_dict);
|
||||||
|
while (g_hash_table_iter_next (&h_iter, (gpointer *) &c_hwaddr, (gpointer *) &c_config_data)) {
|
||||||
|
if (_config_one (sigterm_cancellable, nmc, is_single_nic, c_hwaddr, c_config_data))
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sigterm_handler (gpointer user_data)
|
||||||
|
{
|
||||||
|
GCancellable *sigterm_cancellable = user_data;
|
||||||
|
|
||||||
|
if (!g_cancellable_is_cancelled (sigterm_cancellable)) {
|
||||||
|
_LOGD ("SIGTERM received");
|
||||||
|
g_cancellable_cancel (user_data);
|
||||||
|
} else
|
||||||
|
_LOGD ("SIGTERM received (again)");
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, const char *const*argv)
|
||||||
|
{
|
||||||
|
gs_unref_object GCancellable *sigterm_cancellable = NULL;
|
||||||
|
nm_auto_destroy_and_unref_gsource GSource *sigterm_source = NULL;
|
||||||
|
gs_unref_object NMCSProvider *provider = NULL;
|
||||||
|
gs_unref_object NMClient *nmc = NULL;
|
||||||
|
gs_unref_hashtable GHashTable *config_dict = NULL;
|
||||||
|
|
||||||
|
_nm_logging_enabled_init (g_getenv ("NM_CLOUD_SETUP_LOG"));
|
||||||
|
|
||||||
|
_LOGD ("nm-cloud-setup %s starting...", NM_DIST_VERSION);
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
g_printerr ("%s: no command line arguments supported\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigterm_cancellable = g_cancellable_new ();
|
||||||
|
|
||||||
|
sigterm_source = nm_g_source_attach (nm_g_unix_signal_source_new (SIGTERM,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
sigterm_handler,
|
||||||
|
sigterm_cancellable,
|
||||||
|
NULL),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
provider = _provider_detect (sigterm_cancellable);
|
||||||
|
if (!provider)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
nmc = _nmc_create (sigterm_cancellable);
|
||||||
|
if (!nmc)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
config_dict = _get_config (sigterm_cancellable, provider, nmc);
|
||||||
|
if (!config_dict)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (_config_all (sigterm_cancellable, nmc, config_dict))
|
||||||
|
_LOGI ("some changes were applied for provider %s", nmcs_provider_get_name (provider));
|
||||||
|
else
|
||||||
|
_LOGD ("no changes were applied for provider %s", nmcs_provider_get_name (provider));
|
||||||
|
|
||||||
|
done:
|
||||||
|
nm_clear_pointer (&config_dict, g_hash_table_unref);
|
||||||
|
g_clear_object (&nmc);
|
||||||
|
g_clear_object (&provider);
|
||||||
|
|
||||||
|
if (!nmcs_wait_for_objects_iterate_until_done (NULL, 2000)) {
|
||||||
|
_LOGE ("shutdown: timeout waiting to application to quit. This is a bug");
|
||||||
|
nm_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_clear_g_source_inst (&sigterm_source);
|
||||||
|
g_clear_object (&sigterm_cancellable);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
49
clients/cloud-setup/meson.build
Normal file
49
clients/cloud-setup/meson.build
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
name = 'nm-cloud-setup'
|
||||||
|
|
||||||
|
if install_systemdunitdir
|
||||||
|
|
||||||
|
nm_cloud_setup_service = configure_file(
|
||||||
|
input: 'nm-cloud-setup.service.in',
|
||||||
|
output: '@BASENAME@',
|
||||||
|
install_dir: systemd_systemdsystemunitdir,
|
||||||
|
configuration: data_conf,
|
||||||
|
)
|
||||||
|
|
||||||
|
install_data(
|
||||||
|
'nm-cloud-setup.timer',
|
||||||
|
install_dir: systemd_systemdsystemunitdir,
|
||||||
|
)
|
||||||
|
|
||||||
|
install_data(
|
||||||
|
'90-nm-cloud-setup.sh',
|
||||||
|
install_dir: join_paths(nm_pkglibdir, 'dispatcher.d', 'no-wait.d'),
|
||||||
|
)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
sources = files(
|
||||||
|
'main.c',
|
||||||
|
'nm-cloud-setup-utils.c',
|
||||||
|
'nm-http-client.c',
|
||||||
|
'nmcs-provider-ec2.c',
|
||||||
|
'nmcs-provider.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
deps = [
|
||||||
|
libnmc_base_dep,
|
||||||
|
libnmc_dep,
|
||||||
|
libcurl_dep,
|
||||||
|
]
|
||||||
|
|
||||||
|
executable(
|
||||||
|
name,
|
||||||
|
sources,
|
||||||
|
dependencies: deps,
|
||||||
|
c_args: clients_c_flags +
|
||||||
|
['-DG_LOG_DOMAIN="@0@"'.format(name)],
|
||||||
|
link_with: libnm_systemd_logging_stub,
|
||||||
|
link_args: ldflags_linker_script_binary,
|
||||||
|
link_depends: linker_script_binary,
|
||||||
|
install: true,
|
||||||
|
install_dir: nm_libexecdir,
|
||||||
|
)
|
835
clients/cloud-setup/nm-cloud-setup-utils.c
Normal file
835
clients/cloud-setup/nm-cloud-setup-utils.c
Normal file
@@ -0,0 +1,835 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#include "nm-default.h"
|
||||||
|
|
||||||
|
#include "nm-cloud-setup-utils.h"
|
||||||
|
|
||||||
|
#include "nm-glib-aux/nm-time-utils.h"
|
||||||
|
#include "nm-glib-aux/nm-logging-base.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
volatile NMLogLevel _nm_logging_configured_level = LOGL_TRACE;
|
||||||
|
|
||||||
|
void
|
||||||
|
_nm_logging_enabled_init (const char *level_str)
|
||||||
|
{
|
||||||
|
NMLogLevel level;
|
||||||
|
|
||||||
|
if (!_nm_log_parse_level (level_str, &level))
|
||||||
|
level = LOGL_WARN;
|
||||||
|
else if (level == _LOGL_KEEP)
|
||||||
|
level = LOGL_WARN;
|
||||||
|
|
||||||
|
_nm_logging_configured_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_nm_log_impl_cs (NMLogLevel level,
|
||||||
|
const char *fmt,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
gs_free char *msg = NULL;
|
||||||
|
va_list ap;
|
||||||
|
const char *level_str;
|
||||||
|
gint64 ts;
|
||||||
|
|
||||||
|
va_start (ap, fmt);
|
||||||
|
msg = g_strdup_vprintf (fmt, ap);
|
||||||
|
va_end (ap);
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case LOGL_TRACE: level_str = "<trace>"; break;
|
||||||
|
case LOGL_DEBUG: level_str = "<debug>"; break;
|
||||||
|
case LOGL_INFO: level_str = "<info> "; break;
|
||||||
|
case LOGL_WARN: level_str = "<warn> "; break;
|
||||||
|
default:
|
||||||
|
nm_assert (level == LOGL_ERR);
|
||||||
|
level_str = "<error>";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts = nm_utils_clock_gettime_ns (CLOCK_BOOTTIME);
|
||||||
|
|
||||||
|
g_print ("[%"G_GINT64_FORMAT".%05"G_GINT64_FORMAT"] %s %s\n",
|
||||||
|
ts / NM_UTILS_NS_PER_SECOND,
|
||||||
|
(ts / (NM_UTILS_NS_PER_SECOND / 10000)) % 10000,
|
||||||
|
level_str,
|
||||||
|
msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_nm_utils_monotonic_timestamp_initialized (const struct timespec *tp,
|
||||||
|
gint64 offset_sec,
|
||||||
|
gboolean is_boottime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
G_LOCK_DEFINE_STATIC (_wait_for_objects_lock);
|
||||||
|
static GSList *_wait_for_objects_list;
|
||||||
|
static GSList *_wait_for_objects_iterate_loops;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_wait_for_objects_maybe_quit_mainloops_with_lock (void)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
|
||||||
|
if (!_wait_for_objects_list) {
|
||||||
|
for (iter = _wait_for_objects_iterate_loops; iter; iter = iter->next)
|
||||||
|
g_main_loop_quit (iter->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_wait_for_objects_weak_cb (gpointer data,
|
||||||
|
GObject *where_the_object_was)
|
||||||
|
{
|
||||||
|
G_LOCK (_wait_for_objects_lock);
|
||||||
|
nm_assert (g_slist_find (_wait_for_objects_list, where_the_object_was));
|
||||||
|
_wait_for_objects_list = g_slist_remove (_wait_for_objects_list, where_the_object_was);
|
||||||
|
_wait_for_objects_maybe_quit_mainloops_with_lock ();
|
||||||
|
G_UNLOCK (_wait_for_objects_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nmcs_wait_for_objects_register:
|
||||||
|
* @target: a #GObject to wait for.
|
||||||
|
*
|
||||||
|
* Registers @target as a pointer to wait during shutdown. Using
|
||||||
|
* nmcs_wait_for_objects_iterate_until_done() we keep waiting until
|
||||||
|
* @target gets destroyed, which means that it gets completely unreferenced.
|
||||||
|
*/
|
||||||
|
gpointer
|
||||||
|
nmcs_wait_for_objects_register (gpointer target)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (G_IS_OBJECT (target), NULL);
|
||||||
|
|
||||||
|
G_LOCK (_wait_for_objects_lock);
|
||||||
|
_wait_for_objects_list = g_slist_prepend (_wait_for_objects_list, target);
|
||||||
|
G_UNLOCK (_wait_for_objects_lock);
|
||||||
|
|
||||||
|
g_object_weak_ref (target,
|
||||||
|
_wait_for_objects_weak_cb,
|
||||||
|
NULL);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainLoop *loop;
|
||||||
|
gboolean got_timeout;
|
||||||
|
} WaitForObjectsData;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_wait_for_objects_iterate_until_done_timeout_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
WaitForObjectsData *data = user_data;
|
||||||
|
|
||||||
|
data->got_timeout = TRUE;
|
||||||
|
g_main_loop_quit (data->loop);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_wait_for_objects_iterate_until_done_idle_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
/* This avoids a race where:
|
||||||
|
*
|
||||||
|
* - we check whether there are objects to wait for.
|
||||||
|
* - the last object to wait for gets removed (issuing g_main_loop_quit()).
|
||||||
|
* - we run the mainloop (and missed our signal).
|
||||||
|
*
|
||||||
|
* It's really a missing feature of GMainLoop where the "is-running" flag is always set to
|
||||||
|
* TRUE by g_main_loop_run(). That means, you cannot catch a g_main_loop_quit() in a race
|
||||||
|
* free way while not iterating the loop.
|
||||||
|
*
|
||||||
|
* Avoid this, by checking once again after we start running the mainloop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
G_LOCK (_wait_for_objects_lock);
|
||||||
|
_wait_for_objects_maybe_quit_mainloops_with_lock ();
|
||||||
|
G_UNLOCK (_wait_for_objects_lock);
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nmcs_wait_for_objects_iterate_until_done:
|
||||||
|
* @context: the #GMainContext to iterate.
|
||||||
|
* @timeout_ms: timeout or -1 for no timeout.
|
||||||
|
*
|
||||||
|
* Iterates the provided @context until all objects that we wait for
|
||||||
|
* are destroyed.
|
||||||
|
*
|
||||||
|
* The purpose of this is to cleanup all objects that we have on exit. That
|
||||||
|
* is especially because objects have asynchronous operations pending that
|
||||||
|
* should be cancelled and properly completed during exit.
|
||||||
|
*
|
||||||
|
* Returns: %FALSE on timeout or %TRUE if all objects destroyed before timeout.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
nmcs_wait_for_objects_iterate_until_done (GMainContext *context,
|
||||||
|
int timeout_ms)
|
||||||
|
{
|
||||||
|
nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new (context, FALSE);
|
||||||
|
nm_auto_destroy_and_unref_gsource GSource *timeout_source = NULL;
|
||||||
|
WaitForObjectsData data;
|
||||||
|
gboolean has_more_objects;
|
||||||
|
|
||||||
|
G_LOCK (_wait_for_objects_lock);
|
||||||
|
if (!_wait_for_objects_list) {
|
||||||
|
G_UNLOCK (_wait_for_objects_lock);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
_wait_for_objects_iterate_loops = g_slist_prepend (_wait_for_objects_iterate_loops, loop);
|
||||||
|
G_UNLOCK (_wait_for_objects_lock);
|
||||||
|
|
||||||
|
data = (WaitForObjectsData) {
|
||||||
|
.loop = loop,
|
||||||
|
.got_timeout = FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (timeout_ms >= 0) {
|
||||||
|
timeout_source = nm_g_source_attach (nm_g_timeout_source_new (timeout_ms,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
_wait_for_objects_iterate_until_done_timeout_cb,
|
||||||
|
&data,
|
||||||
|
NULL),
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
has_more_objects = TRUE;
|
||||||
|
while ( has_more_objects
|
||||||
|
&& !data.got_timeout) {
|
||||||
|
nm_auto_destroy_and_unref_gsource GSource *idle_source = NULL;
|
||||||
|
|
||||||
|
idle_source = nm_g_source_attach (nm_g_idle_source_new (G_PRIORITY_DEFAULT,
|
||||||
|
_wait_for_objects_iterate_until_done_idle_cb,
|
||||||
|
&data,
|
||||||
|
NULL),
|
||||||
|
context);
|
||||||
|
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
|
||||||
|
G_LOCK (_wait_for_objects_lock);
|
||||||
|
has_more_objects = (!!_wait_for_objects_list);
|
||||||
|
if ( data.got_timeout
|
||||||
|
|| !has_more_objects)
|
||||||
|
_wait_for_objects_iterate_loops = g_slist_remove (_wait_for_objects_iterate_loops, loop);
|
||||||
|
G_UNLOCK (_wait_for_objects_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !data.got_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GTask *task;
|
||||||
|
GSource *source_timeout;
|
||||||
|
GSource *source_next_poll;
|
||||||
|
GMainContext *context;
|
||||||
|
GCancellable *internal_cancellable;
|
||||||
|
NMCSUtilsPollProbeStartFcn probe_start_fcn;
|
||||||
|
NMCSUtilsPollProbeFinishFcn probe_finish_fcn;
|
||||||
|
gpointer probe_user_data;
|
||||||
|
gulong cancellable_id;
|
||||||
|
gint64 last_poll_start_ms;
|
||||||
|
int sleep_timeout_ms;
|
||||||
|
int ratelimit_timeout_ms;
|
||||||
|
bool completed:1;
|
||||||
|
} PollTaskData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_task_data_free (gpointer data)
|
||||||
|
{
|
||||||
|
PollTaskData *poll_task_data = data;
|
||||||
|
|
||||||
|
nm_assert (G_IS_TASK (poll_task_data->task));
|
||||||
|
nm_assert (!poll_task_data->source_next_poll);
|
||||||
|
nm_assert (!poll_task_data->source_timeout);
|
||||||
|
nm_assert (poll_task_data->cancellable_id == 0);
|
||||||
|
|
||||||
|
g_main_context_unref (poll_task_data->context);
|
||||||
|
|
||||||
|
nm_g_slice_free (poll_task_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_return (PollTaskData *poll_task_data,
|
||||||
|
gboolean success,
|
||||||
|
GError *error_take)
|
||||||
|
{
|
||||||
|
nm_clear_g_source_inst (&poll_task_data->source_next_poll);
|
||||||
|
nm_clear_g_source_inst (&poll_task_data->source_timeout);
|
||||||
|
nm_clear_g_cancellable_disconnect (g_task_get_cancellable (poll_task_data->task),
|
||||||
|
&poll_task_data->cancellable_id);
|
||||||
|
|
||||||
|
nm_clear_g_cancellable (&poll_task_data->internal_cancellable);
|
||||||
|
|
||||||
|
if (error_take)
|
||||||
|
g_task_return_error (poll_task_data->task, g_steal_pointer (&error_take));
|
||||||
|
else
|
||||||
|
g_task_return_boolean (poll_task_data->task, success);
|
||||||
|
|
||||||
|
g_object_unref (poll_task_data->task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean _poll_start_cb (gpointer user_data);
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_done_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PollTaskData *poll_task_data = user_data;
|
||||||
|
_nm_unused gs_unref_object GTask *task = poll_task_data->task; /* balance ref from _poll_start_cb() */
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
gint64 now_ms;
|
||||||
|
gint64 wait_ms;
|
||||||
|
gboolean is_finished;
|
||||||
|
|
||||||
|
is_finished = poll_task_data->probe_finish_fcn (source,
|
||||||
|
result,
|
||||||
|
poll_task_data->probe_user_data,
|
||||||
|
&error);
|
||||||
|
|
||||||
|
if (nm_utils_error_is_cancelled (error, FALSE)) {
|
||||||
|
/* we already handle this differently. Nothing to do. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( error
|
||||||
|
|| is_finished) {
|
||||||
|
_poll_return (poll_task_data, TRUE, g_steal_pointer (&error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
now_ms = nm_utils_get_monotonic_timestamp_ms ();
|
||||||
|
if (poll_task_data->ratelimit_timeout_ms > 0)
|
||||||
|
wait_ms = (poll_task_data->last_poll_start_ms + poll_task_data->ratelimit_timeout_ms) - now_ms;
|
||||||
|
else
|
||||||
|
wait_ms = 0;
|
||||||
|
if (poll_task_data->sleep_timeout_ms > 0)
|
||||||
|
wait_ms = MAX (wait_ms, poll_task_data->sleep_timeout_ms);
|
||||||
|
|
||||||
|
poll_task_data->source_next_poll = nm_g_source_attach (nm_g_timeout_source_new (MAX (1, wait_ms),
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
_poll_start_cb,
|
||||||
|
poll_task_data,
|
||||||
|
NULL),
|
||||||
|
poll_task_data->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_poll_start_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
PollTaskData *poll_task_data = user_data;
|
||||||
|
|
||||||
|
nm_clear_g_source_inst (&poll_task_data->source_next_poll);
|
||||||
|
|
||||||
|
poll_task_data->last_poll_start_ms = nm_utils_get_monotonic_timestamp_ms ();
|
||||||
|
|
||||||
|
g_object_ref (poll_task_data->task); /* balanced by _poll_done_cb() */
|
||||||
|
|
||||||
|
poll_task_data->probe_start_fcn (poll_task_data->internal_cancellable,
|
||||||
|
poll_task_data->probe_user_data,
|
||||||
|
_poll_done_cb,
|
||||||
|
poll_task_data);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_poll_timeout_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
PollTaskData *poll_task_data = user_data;
|
||||||
|
|
||||||
|
_poll_return (poll_task_data, FALSE, NULL);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_cancelled_cb (GObject *object, gpointer user_data)
|
||||||
|
{
|
||||||
|
PollTaskData *poll_task_data = user_data;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
_LOGD (">> poll cancelled");
|
||||||
|
nm_clear_g_signal_handler (g_task_get_cancellable (poll_task_data->task),
|
||||||
|
&poll_task_data->cancellable_id);
|
||||||
|
nm_utils_error_set_cancelled (&error, FALSE, NULL);
|
||||||
|
_poll_return (poll_task_data, FALSE, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nmcs_utils_poll:
|
||||||
|
* @poll_timeout_ms: if >= 0, then this is the overall timeout for how long we poll.
|
||||||
|
* When this timeout expires, the request completes with failure (but no error set).
|
||||||
|
* @ratelimit_timeout_ms: if > 0, we ratelimit the starts from one prope_start_fcn
|
||||||
|
* call to the next.
|
||||||
|
* @sleep_timeout_ms: if > 0, then we wait after a probe finished this timeout
|
||||||
|
* before the next. Together with @ratelimit_timeout_ms this determines how
|
||||||
|
* frequently we probe.
|
||||||
|
* @probe_start_fcn: used to start a (asynchrnous) probe. A probe must be completed
|
||||||
|
* by calling the provided callback. While a probe is in progress, we will not
|
||||||
|
* start another. This function is already invoked the first time synchronously,
|
||||||
|
* during nmcs_utils_poll().
|
||||||
|
* @probe_finish_fcn: will be called from the callback of @probe_start_fcn. If the
|
||||||
|
* function returns %TRUE (polling done) or an error, polling stops. Otherwise,
|
||||||
|
* another poll will be started.
|
||||||
|
* @probe_user_data: user_data for the probe functions.
|
||||||
|
* @cancellable: cancellable for polling.
|
||||||
|
* @callback: when polling completes.
|
||||||
|
* @user_data: for @callback.
|
||||||
|
*
|
||||||
|
* This uses the current g_main_context_get_thread_default() for scheduling
|
||||||
|
* actions.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
nmcs_utils_poll (int poll_timeout_ms,
|
||||||
|
int sleep_timeout_ms,
|
||||||
|
int ratelimit_timeout_ms,
|
||||||
|
NMCSUtilsPollProbeStartFcn probe_start_fcn,
|
||||||
|
NMCSUtilsPollProbeFinishFcn probe_finish_fcn,
|
||||||
|
gpointer probe_user_data,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PollTaskData *poll_task_data;
|
||||||
|
|
||||||
|
poll_task_data = g_slice_new (PollTaskData);
|
||||||
|
*poll_task_data = (PollTaskData) {
|
||||||
|
.task = nm_g_task_new (NULL, cancellable, nmcs_utils_poll, callback, user_data),
|
||||||
|
.probe_start_fcn = probe_start_fcn,
|
||||||
|
.probe_finish_fcn = probe_finish_fcn,
|
||||||
|
.probe_user_data = probe_user_data,
|
||||||
|
.completed = FALSE,
|
||||||
|
.context = g_main_context_ref_thread_default (),
|
||||||
|
.sleep_timeout_ms = sleep_timeout_ms,
|
||||||
|
.ratelimit_timeout_ms = ratelimit_timeout_ms,
|
||||||
|
.internal_cancellable = g_cancellable_new (),
|
||||||
|
};
|
||||||
|
|
||||||
|
nmcs_wait_for_objects_register (poll_task_data->task);
|
||||||
|
|
||||||
|
g_task_set_task_data (poll_task_data->task, poll_task_data, _poll_task_data_free);
|
||||||
|
|
||||||
|
if (poll_timeout_ms >= 0) {
|
||||||
|
poll_task_data->source_timeout = nm_g_source_attach (nm_g_timeout_source_new (poll_timeout_ms,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
_poll_timeout_cb,
|
||||||
|
poll_task_data,
|
||||||
|
NULL),
|
||||||
|
poll_task_data->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_task_data->source_next_poll = nm_g_source_attach (nm_g_idle_source_new (G_PRIORITY_DEFAULT,
|
||||||
|
_poll_start_cb,
|
||||||
|
poll_task_data,
|
||||||
|
NULL),
|
||||||
|
poll_task_data->context);
|
||||||
|
|
||||||
|
if (cancellable) {
|
||||||
|
gulong signal_id;
|
||||||
|
|
||||||
|
signal_id = g_cancellable_connect (cancellable,
|
||||||
|
G_CALLBACK (_poll_cancelled_cb),
|
||||||
|
poll_task_data,
|
||||||
|
NULL);
|
||||||
|
if (signal_id == 0) {
|
||||||
|
/* the request is already cancelled. Return. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
poll_task_data->cancellable_id = signal_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nmcs_utils_poll_finish:
|
||||||
|
* @result: the GAsyncResult from the GAsyncReadyCallback callback.
|
||||||
|
* @probe_user_data: the user data provided to nmcs_utils_poll().
|
||||||
|
* @error: the failure code.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the polling completed with success. In that case,
|
||||||
|
* the error won't be set.
|
||||||
|
* If the request was cancelled, this is indicated by @error and
|
||||||
|
* %FALSE will be returned.
|
||||||
|
* If the probe returned a failure, this returns %FALSE and the error
|
||||||
|
* provided by @probe_finish_fcn.
|
||||||
|
* If the request times out, this returns %FALSE without error set.
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
nmcs_utils_poll_finish (GAsyncResult *result,
|
||||||
|
gpointer *probe_user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GTask *task;
|
||||||
|
PollTaskData *poll_task_data;
|
||||||
|
|
||||||
|
g_return_val_if_fail (nm_g_task_is_valid (result, NULL, nmcs_utils_poll), FALSE);
|
||||||
|
g_return_val_if_fail (!error || !*error, FALSE);
|
||||||
|
|
||||||
|
task = G_TASK (result);
|
||||||
|
|
||||||
|
if (probe_user_data) {
|
||||||
|
poll_task_data = g_task_get_task_data (task);
|
||||||
|
NM_SET_OUT (probe_user_data, poll_task_data->probe_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_task_propagate_boolean (task, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
char *
|
||||||
|
nmcs_utils_hwaddr_normalize (const char *hwaddr, gssize len)
|
||||||
|
{
|
||||||
|
gs_free char *hwaddr_clone = NULL;
|
||||||
|
guint8 buf[ETH_ALEN];
|
||||||
|
|
||||||
|
nm_assert (len >= -1);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
if (!hwaddr)
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
if (len == 0)
|
||||||
|
return NULL;
|
||||||
|
nm_assert (hwaddr);
|
||||||
|
hwaddr = nm_strndup_a (300, hwaddr, len, &hwaddr_clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_utils_hwaddr_aton (hwaddr, buf, sizeof (buf)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return nm_utils_hwaddr_ntoa (buf, sizeof (buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
const char *
|
||||||
|
nmcs_utils_parse_memmem (GBytes *mem, const char *needle)
|
||||||
|
{
|
||||||
|
const char *mem_data;
|
||||||
|
gsize mem_size;
|
||||||
|
|
||||||
|
g_return_val_if_fail (mem, NULL);
|
||||||
|
g_return_val_if_fail (needle, NULL);
|
||||||
|
|
||||||
|
mem_data = g_bytes_get_data (mem, &mem_size);
|
||||||
|
return memmem (mem_data, mem_size, needle, strlen (needle));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
nmcs_utils_parse_get_full_line (GBytes *mem, const char *needle)
|
||||||
|
{
|
||||||
|
const char *mem_data;
|
||||||
|
gsize mem_size;
|
||||||
|
gsize c;
|
||||||
|
gsize l;
|
||||||
|
|
||||||
|
const char *line;
|
||||||
|
|
||||||
|
line = nmcs_utils_parse_memmem (mem, needle);
|
||||||
|
if (!line)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mem_data = g_bytes_get_data (mem, &mem_size);
|
||||||
|
|
||||||
|
if ( line != mem_data
|
||||||
|
&& line[-1] != '\n') {
|
||||||
|
/* the line must be preceeded either by the begin of the data or
|
||||||
|
* by a newline. */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = mem_size - (line - mem_data);
|
||||||
|
l = strlen (needle);
|
||||||
|
|
||||||
|
if ( c != l
|
||||||
|
&& line[l] != '\n') {
|
||||||
|
/* the end of the needle must be either a newline or the end of the buffer. */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
char *
|
||||||
|
nmcs_utils_uri_build_concat_v (const char *base,
|
||||||
|
const char **components,
|
||||||
|
gsize n_components)
|
||||||
|
{
|
||||||
|
GString *uri;
|
||||||
|
|
||||||
|
nm_assert (base);
|
||||||
|
nm_assert (base[0]);
|
||||||
|
nm_assert (!NM_STR_HAS_SUFFIX (base, "/"));
|
||||||
|
|
||||||
|
uri = g_string_sized_new (100);
|
||||||
|
|
||||||
|
g_string_append (uri, base);
|
||||||
|
|
||||||
|
if ( n_components > 0
|
||||||
|
&& components[0]
|
||||||
|
&& components[0][0] == '/') {
|
||||||
|
/* the first component starts with a slash. We allow that, and don't add a duplicate
|
||||||
|
* slash. Otherwise, we add a separator after base.
|
||||||
|
*
|
||||||
|
* We only do that for the first component. */
|
||||||
|
} else
|
||||||
|
g_string_append_c (uri, '/');
|
||||||
|
|
||||||
|
while (n_components > 0) {
|
||||||
|
if (!components[0]) {
|
||||||
|
/* we allow NULL, to indicate nothing to append*/
|
||||||
|
} else
|
||||||
|
g_string_append (uri, components[0]);
|
||||||
|
components++;
|
||||||
|
n_components--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_string_free (uri, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nmcs_setting_ip_replace_ipv4_addresses (NMSettingIPConfig *s_ip,
|
||||||
|
NMIPAddress **entries_arr,
|
||||||
|
guint entries_len)
|
||||||
|
{
|
||||||
|
gboolean any_changes = FALSE;
|
||||||
|
guint i_next;
|
||||||
|
guint num;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
num = nm_setting_ip_config_get_num_addresses (s_ip);
|
||||||
|
|
||||||
|
i_next = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < entries_len; i++) {
|
||||||
|
NMIPAddress *entry = entries_arr[i];
|
||||||
|
|
||||||
|
if (!any_changes) {
|
||||||
|
if (i_next < num) {
|
||||||
|
if (nm_ip_address_cmp_full (entry,
|
||||||
|
nm_setting_ip_config_get_address (s_ip, i_next),
|
||||||
|
NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS) == 0) {
|
||||||
|
i_next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i_next < num)
|
||||||
|
nm_setting_ip_config_remove_address (s_ip, --num);
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_setting_ip_config_add_address (s_ip, entry))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
i_next++;
|
||||||
|
}
|
||||||
|
if (any_changes) {
|
||||||
|
while (i_next < num) {
|
||||||
|
nm_setting_ip_config_remove_address (s_ip, --num);
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nmcs_setting_ip_replace_ipv4_routes (NMSettingIPConfig *s_ip,
|
||||||
|
NMIPRoute **entries_arr,
|
||||||
|
guint entries_len)
|
||||||
|
{
|
||||||
|
gboolean any_changes = FALSE;
|
||||||
|
guint i_next;
|
||||||
|
guint num;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
num = nm_setting_ip_config_get_num_routes (s_ip);
|
||||||
|
|
||||||
|
i_next = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < entries_len; i++) {
|
||||||
|
NMIPRoute *entry = entries_arr[i];
|
||||||
|
|
||||||
|
if (!any_changes) {
|
||||||
|
if (i_next < num) {
|
||||||
|
if (nm_ip_route_equal_full (entry,
|
||||||
|
nm_setting_ip_config_get_route (s_ip, i_next),
|
||||||
|
NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS)) {
|
||||||
|
i_next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i_next < num)
|
||||||
|
nm_setting_ip_config_remove_route (s_ip, --num);
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nm_setting_ip_config_add_route (s_ip, entry))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
i_next++;
|
||||||
|
}
|
||||||
|
if (!any_changes) {
|
||||||
|
while (i_next < num) {
|
||||||
|
nm_setting_ip_config_remove_route (s_ip, --num);
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nmcs_setting_ip_replace_ipv4_rules (NMSettingIPConfig *s_ip,
|
||||||
|
NMIPRoutingRule **entries_arr,
|
||||||
|
guint entries_len)
|
||||||
|
{
|
||||||
|
gboolean any_changes = FALSE;
|
||||||
|
guint i_next;
|
||||||
|
guint num;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
num = nm_setting_ip_config_get_num_routing_rules (s_ip);
|
||||||
|
|
||||||
|
i_next = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < entries_len; i++) {
|
||||||
|
NMIPRoutingRule *entry = entries_arr[i];
|
||||||
|
|
||||||
|
if (!any_changes) {
|
||||||
|
if (i_next < num) {
|
||||||
|
if (nm_ip_routing_rule_cmp (entry,
|
||||||
|
nm_setting_ip_config_get_routing_rule (s_ip, i_next)) == 0) {
|
||||||
|
i_next++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i_next < num)
|
||||||
|
nm_setting_ip_config_remove_routing_rule (s_ip, --num);
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_setting_ip_config_add_routing_rule (s_ip, entry);
|
||||||
|
i_next++;
|
||||||
|
}
|
||||||
|
if (!any_changes) {
|
||||||
|
while (i_next < num) {
|
||||||
|
nm_setting_ip_config_remove_routing_rule (s_ip, --num);
|
||||||
|
any_changes = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return any_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainLoop *main_loop;
|
||||||
|
NMConnection *connection;
|
||||||
|
GError *error;
|
||||||
|
guint64 version_id;
|
||||||
|
} DeviceGetAppliedConnectionData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_nmcs_device_get_applied_connection_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
DeviceGetAppliedConnectionData *data = user_data;
|
||||||
|
|
||||||
|
data->connection = nm_device_get_applied_connection_finish (NM_DEVICE (source),
|
||||||
|
result,
|
||||||
|
&data->version_id,
|
||||||
|
&data->error);
|
||||||
|
g_main_loop_quit (data->main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
NMConnection *
|
||||||
|
nmcs_device_get_applied_connection (NMDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
guint64 *version_id,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
DeviceGetAppliedConnectionData data = {
|
||||||
|
.main_loop = main_loop,
|
||||||
|
};
|
||||||
|
|
||||||
|
nm_device_get_applied_connection_async (device,
|
||||||
|
0,
|
||||||
|
cancellable,
|
||||||
|
_nmcs_device_get_applied_connection_cb,
|
||||||
|
&data);
|
||||||
|
|
||||||
|
g_main_loop_run (main_loop);
|
||||||
|
|
||||||
|
if (data.error)
|
||||||
|
g_propagate_error (error, data.error);
|
||||||
|
NM_SET_OUT (version_id, data.version_id);
|
||||||
|
return data.connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainLoop *main_loop;
|
||||||
|
GError *error;
|
||||||
|
} DeviceReapplyData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_nmcs_device_reapply_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
DeviceReapplyData *data = user_data;
|
||||||
|
|
||||||
|
nm_device_reapply_finish (NM_DEVICE (source),
|
||||||
|
result,
|
||||||
|
&data->error);
|
||||||
|
g_main_loop_quit (data->main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nmcs_device_reapply (NMDevice *device,
|
||||||
|
GCancellable *sigterm_cancellable,
|
||||||
|
NMConnection *connection,
|
||||||
|
guint64 version_id,
|
||||||
|
gboolean *out_version_id_changed,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
DeviceReapplyData data = {
|
||||||
|
.main_loop = main_loop,
|
||||||
|
};
|
||||||
|
|
||||||
|
nm_device_reapply_async (device,
|
||||||
|
connection,
|
||||||
|
version_id,
|
||||||
|
0,
|
||||||
|
sigterm_cancellable,
|
||||||
|
_nmcs_device_reapply_cb,
|
||||||
|
&data);
|
||||||
|
|
||||||
|
g_main_loop_run (main_loop);
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
NM_SET_OUT (out_version_id_changed, g_error_matches (data.error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_VERSION_ID_MISMATCH));
|
||||||
|
g_propagate_error (error, data.error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NM_SET_OUT (out_version_id_changed, FALSE);
|
||||||
|
return TRUE;
|
||||||
|
}
|
121
clients/cloud-setup/nm-cloud-setup-utils.h
Normal file
121
clients/cloud-setup/nm-cloud-setup-utils.h
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#ifndef __NM_CLOUD_SETUP_UTILS_H__
|
||||||
|
#define __NM_CLOUD_SETUP_UTILS_H__
|
||||||
|
|
||||||
|
#include "nm-glib-aux/nm-logging-fwd.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
extern volatile NMLogLevel _nm_logging_configured_level;
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_logging_enabled (NMLogLevel level)
|
||||||
|
{
|
||||||
|
return level >= _nm_logging_configured_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _nm_logging_enabled_init (const char *level_str);
|
||||||
|
|
||||||
|
void _nm_log_impl_cs (NMLogLevel level,
|
||||||
|
const char *fmt,
|
||||||
|
...) _nm_printf (2, 3);
|
||||||
|
|
||||||
|
#define _nm_log(level, ...) \
|
||||||
|
_nm_log_impl_cs ((level), __VA_ARGS__);
|
||||||
|
|
||||||
|
#define _NMLOG(level, ...) \
|
||||||
|
G_STMT_START { \
|
||||||
|
const NMLogLevel _level = (level); \
|
||||||
|
\
|
||||||
|
if (nm_logging_enabled (_level)) { \
|
||||||
|
_nm_log (_level, __VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef NM_DIST_VERSION
|
||||||
|
#define NM_DIST_VERSION VERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gpointer nmcs_wait_for_objects_register (gpointer target);
|
||||||
|
|
||||||
|
gboolean nmcs_wait_for_objects_iterate_until_done (GMainContext *context,
|
||||||
|
int timeout_ms);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef void (*NMCSUtilsPollProbeStartFcn) (GCancellable *cancellable,
|
||||||
|
gpointer probe_user_data,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
typedef gboolean (*NMCSUtilsPollProbeFinishFcn) (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer probe_user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void nmcs_utils_poll (int poll_timeout_ms,
|
||||||
|
int ratelimit_timeout_ms,
|
||||||
|
int sleep_timeout_ms,
|
||||||
|
NMCSUtilsPollProbeStartFcn probe_start_fcn,
|
||||||
|
NMCSUtilsPollProbeFinishFcn probe_finish_fcn,
|
||||||
|
gpointer probe_user_data,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean nmcs_utils_poll_finish (GAsyncResult *result,
|
||||||
|
gpointer *probe_user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
char *nmcs_utils_hwaddr_normalize (const char *hwaddr, gssize len);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
const char *nmcs_utils_parse_memmem (GBytes *mem, const char *needle);
|
||||||
|
|
||||||
|
const char *nmcs_utils_parse_get_full_line (GBytes *mem, const char *needle);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
char *nmcs_utils_uri_build_concat_v (const char *base,
|
||||||
|
const char **components,
|
||||||
|
gsize n_components);
|
||||||
|
|
||||||
|
#define nmcs_utils_uri_build_concat(base, ...) nmcs_utils_uri_build_concat_v (base, ((const char *[]) { __VA_ARGS__ }), NM_NARG (__VA_ARGS__))
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean nmcs_setting_ip_replace_ipv4_addresses (NMSettingIPConfig *s_ip,
|
||||||
|
NMIPAddress **entries_arr,
|
||||||
|
guint entries_len);
|
||||||
|
|
||||||
|
gboolean nmcs_setting_ip_replace_ipv4_routes (NMSettingIPConfig *s_ip,
|
||||||
|
NMIPRoute **entries_arr,
|
||||||
|
guint entries_len);
|
||||||
|
|
||||||
|
gboolean nmcs_setting_ip_replace_ipv4_rules (NMSettingIPConfig *s_ip,
|
||||||
|
NMIPRoutingRule **entries_arr,
|
||||||
|
guint entries_len);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
NMConnection *nmcs_device_get_applied_connection (NMDevice *device,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
guint64 *version_id,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean nmcs_device_reapply (NMDevice *device,
|
||||||
|
GCancellable *sigterm_cancellable,
|
||||||
|
NMConnection *connection,
|
||||||
|
guint64 version_id,
|
||||||
|
gboolean *out_version_id_changed,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
#endif /* __NM_CLOUD_SETUP_UTILS_H__ */
|
8
clients/cloud-setup/nm-cloud-setup.service.in
Normal file
8
clients/cloud-setup/nm-cloud-setup.service.in
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Automatically configure NetworkManager in cloud
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=@libexecdir@/nm-cloud-setup
|
||||||
|
|
||||||
|
#Environment=NM_CLOUD_SETUP_LOG=TRACE
|
9
clients/cloud-setup/nm-cloud-setup.timer
Normal file
9
clients/cloud-setup/nm-cloud-setup.timer
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Periodically run nm-cloud-setup
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=5min
|
||||||
|
OnUnitActiveSec=5min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
746
clients/cloud-setup/nm-http-client.c
Normal file
746
clients/cloud-setup/nm-http-client.c
Normal file
@@ -0,0 +1,746 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#include "nm-default.h"
|
||||||
|
|
||||||
|
#include "nm-http-client.h"
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
|
||||||
|
#include "nm-cloud-setup-utils.h"
|
||||||
|
|
||||||
|
#define NM_CURL_DEBUG 0
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GMainContext *context;
|
||||||
|
CURLM *mhandle;
|
||||||
|
GSource *mhandle_source_timeout;
|
||||||
|
GSource *mhandle_source_socket;
|
||||||
|
} NMHttpClientPrivate;
|
||||||
|
|
||||||
|
struct _NMHttpClient {
|
||||||
|
GObject parent;
|
||||||
|
NMHttpClientPrivate _priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _NMHttpClientClass {
|
||||||
|
GObjectClass parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (NMHttpClient, nm_http_client, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
#define NM_HTTP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMHttpClient, NM_IS_HTTP_CLIENT)
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define _NMLOG2(level, edata, ...) \
|
||||||
|
G_STMT_START { \
|
||||||
|
EHandleData *_edata = (edata); \
|
||||||
|
\
|
||||||
|
_NMLOG (level, \
|
||||||
|
"http-request["NM_HASH_OBFUSCATE_PTR_FMT", \"%s\"]: " \
|
||||||
|
_NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
|
||||||
|
NM_HASH_OBFUSCATE_PTR (_edata), \
|
||||||
|
(_edata)->url \
|
||||||
|
_NM_UTILS_MACRO_REST (__VA_ARGS__)); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
G_LOCK_DEFINE_STATIC (_my_curl_initalized_lock);
|
||||||
|
static bool _my_curl_initialized = FALSE;
|
||||||
|
|
||||||
|
__attribute__((destructor))
|
||||||
|
static void
|
||||||
|
_my_curl_global_cleanup (void)
|
||||||
|
{
|
||||||
|
G_LOCK (_my_curl_initalized_lock);
|
||||||
|
if (_my_curl_initialized) {
|
||||||
|
_my_curl_initialized = FALSE;
|
||||||
|
curl_global_cleanup ();
|
||||||
|
}
|
||||||
|
G_UNLOCK (_my_curl_initalized_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nm_http_client_curl_global_init (void)
|
||||||
|
{
|
||||||
|
G_LOCK (_my_curl_initalized_lock);
|
||||||
|
if (!_my_curl_initialized) {
|
||||||
|
_my_curl_initialized = TRUE;
|
||||||
|
if (curl_global_init (CURL_GLOBAL_ALL) != CURLE_OK) {
|
||||||
|
/* Even if this fails, we are partly initialized. WTF. */
|
||||||
|
_LOGE ("curl: curl_global_init() failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
G_UNLOCK (_my_curl_initalized_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
GMainContext *
|
||||||
|
nm_http_client_get_main_context (NMHttpClient *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (NM_IS_HTTP_CLIENT (self), NULL);
|
||||||
|
|
||||||
|
return NM_HTTP_CLIENT_GET_PRIVATE (self)->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static GSource *
|
||||||
|
_source_attach (NMHttpClient *self,
|
||||||
|
GSource *source)
|
||||||
|
{
|
||||||
|
return nm_g_source_attach (source, NM_HTTP_CLIENT_GET_PRIVATE (self)->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long response_code;
|
||||||
|
GBytes *response_data;
|
||||||
|
} GetResult;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_result_free (gpointer data)
|
||||||
|
{
|
||||||
|
GetResult *get_result = data;
|
||||||
|
|
||||||
|
g_bytes_unref (get_result->response_data);
|
||||||
|
nm_g_slice_free (get_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GTask *task;
|
||||||
|
GSource *timeout_source;
|
||||||
|
CURLcode ehandle_result;
|
||||||
|
CURL *ehandle;
|
||||||
|
char *url;
|
||||||
|
GString *recv_data;
|
||||||
|
gssize max_data;
|
||||||
|
gulong cancellable_id;
|
||||||
|
} EHandleData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ehandle_free_ehandle (EHandleData *edata)
|
||||||
|
{
|
||||||
|
if (edata->ehandle) {
|
||||||
|
NMHttpClient *self = g_task_get_source_object (edata->task);
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
curl_multi_remove_handle (priv->mhandle, edata->ehandle);
|
||||||
|
curl_easy_cleanup (g_steal_pointer (&edata->ehandle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ehandle_free (EHandleData *edata)
|
||||||
|
{
|
||||||
|
nm_assert (!edata->ehandle);
|
||||||
|
nm_assert (!edata->timeout_source);
|
||||||
|
|
||||||
|
g_object_unref (edata->task);
|
||||||
|
|
||||||
|
if (edata->recv_data)
|
||||||
|
g_string_free (edata->recv_data, TRUE);
|
||||||
|
g_free (edata->url);
|
||||||
|
nm_g_slice_free (edata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ehandle_complete (EHandleData *edata,
|
||||||
|
GError *error_take)
|
||||||
|
{
|
||||||
|
GetResult *get_result;
|
||||||
|
gs_free char *str_tmp_1 = NULL;
|
||||||
|
long response_code = -1;
|
||||||
|
|
||||||
|
nm_clear_pointer (&edata->timeout_source, nm_g_source_destroy_and_unref);
|
||||||
|
|
||||||
|
nm_clear_g_cancellable_disconnect (g_task_get_cancellable (edata->task),
|
||||||
|
&edata->cancellable_id);
|
||||||
|
|
||||||
|
if (error_take) {
|
||||||
|
if (nm_utils_error_is_cancelled (error_take, FALSE))
|
||||||
|
_LOG2T (edata, "cancelled");
|
||||||
|
else
|
||||||
|
_LOG2D (edata, "failed with %s", error_take->message);
|
||||||
|
} else if (edata->ehandle_result != CURLE_OK) {
|
||||||
|
_LOG2D (edata, "failed with curl error \"%s\"", curl_easy_strerror (edata->ehandle_result));
|
||||||
|
nm_utils_error_set (&error_take,
|
||||||
|
NM_UTILS_ERROR_UNKNOWN,
|
||||||
|
"failed with curl error \"%s\"",
|
||||||
|
curl_easy_strerror (edata->ehandle_result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_take) {
|
||||||
|
_ehandle_free_ehandle (edata);
|
||||||
|
g_task_return_error (edata->task, error_take);
|
||||||
|
_ehandle_free (edata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curl_easy_getinfo (edata->ehandle,
|
||||||
|
CURLINFO_RESPONSE_CODE,
|
||||||
|
&response_code) != CURLE_OK)
|
||||||
|
_LOG2E (edata, "failed to get response code from curl easy handle");
|
||||||
|
|
||||||
|
_LOG2D (edata, "success getting %"G_GSIZE_FORMAT" bytes (response code %ld)",
|
||||||
|
edata->recv_data->len,
|
||||||
|
response_code);
|
||||||
|
|
||||||
|
_LOG2T (edata, "received %"G_GSIZE_FORMAT" bytes: [[%s]]",
|
||||||
|
edata->recv_data->len,
|
||||||
|
nm_utils_buf_utf8safe_escape (edata->recv_data->str, edata->recv_data->len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &str_tmp_1));
|
||||||
|
|
||||||
|
_ehandle_free_ehandle (edata);
|
||||||
|
|
||||||
|
get_result = g_slice_new (GetResult);
|
||||||
|
*get_result = (GetResult) {
|
||||||
|
.response_code = response_code,
|
||||||
|
/* This ensures that response_data is always NUL terminated. This is an important guarantee
|
||||||
|
* that NMHttpClient makes. */
|
||||||
|
.response_data = g_string_free_to_bytes (g_steal_pointer (&edata->recv_data)),
|
||||||
|
};
|
||||||
|
|
||||||
|
g_task_return_pointer (edata->task, get_result, _get_result_free);
|
||||||
|
|
||||||
|
_ehandle_free (edata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
_get_writefunction_cb (char *ptr, size_t size, size_t nmemb, void *user_data)
|
||||||
|
{
|
||||||
|
EHandleData *edata = user_data;
|
||||||
|
gsize nconsume;
|
||||||
|
|
||||||
|
/* size should always be 1, but still. Multiply them to be sure. */
|
||||||
|
nmemb *= size;
|
||||||
|
|
||||||
|
if (edata->max_data >= 0) {
|
||||||
|
nm_assert (edata->recv_data->len <= edata->max_data);
|
||||||
|
nconsume = (((gsize) edata->max_data) - edata->recv_data->len);
|
||||||
|
if (nconsume > nmemb)
|
||||||
|
nconsume = nmemb;
|
||||||
|
} else
|
||||||
|
nconsume = nmemb;
|
||||||
|
|
||||||
|
g_string_append_len (edata->recv_data, ptr, nconsume);
|
||||||
|
return nconsume;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_get_timeout_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
_ehandle_complete (user_data,
|
||||||
|
g_error_new_literal (NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_UNKNOWN,
|
||||||
|
"HTTP request timed out"));
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_cancelled_cb (GObject *object, gpointer user_data)
|
||||||
|
{
|
||||||
|
EHandleData *edata = user_data;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
nm_clear_g_signal_handler (g_task_get_cancellable (edata->task),
|
||||||
|
&edata->cancellable_id);
|
||||||
|
nm_utils_error_set_cancelled (&error, FALSE, NULL);
|
||||||
|
_ehandle_complete (edata, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_http_client_get (NMHttpClient *self,
|
||||||
|
const char *url,
|
||||||
|
int timeout_ms,
|
||||||
|
gssize max_data,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
NMHttpClientPrivate *priv;
|
||||||
|
EHandleData *edata;
|
||||||
|
|
||||||
|
g_return_if_fail (NM_IS_HTTP_CLIENT (self));
|
||||||
|
g_return_if_fail (url);
|
||||||
|
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||||
|
g_return_if_fail (timeout_ms >= 0);
|
||||||
|
g_return_if_fail (max_data >= -1);
|
||||||
|
|
||||||
|
priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
edata = g_slice_new (EHandleData);
|
||||||
|
*edata = (EHandleData) {
|
||||||
|
.task = nm_g_task_new (self, cancellable, nm_http_client_get, callback, user_data),
|
||||||
|
.recv_data = g_string_sized_new (NM_MIN (max_data, 245)),
|
||||||
|
.max_data = max_data,
|
||||||
|
.url = g_strdup (url),
|
||||||
|
};
|
||||||
|
|
||||||
|
nmcs_wait_for_objects_register (edata->task);
|
||||||
|
|
||||||
|
_LOG2D (edata, "start get ...");
|
||||||
|
|
||||||
|
edata->ehandle = curl_easy_init ();
|
||||||
|
if (!edata->ehandle) {
|
||||||
|
_ehandle_complete (edata,
|
||||||
|
g_error_new_literal (NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_UNKNOWN,
|
||||||
|
"HTTP request failed to create curl handle"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_setopt (edata->ehandle, CURLOPT_URL, url);
|
||||||
|
|
||||||
|
curl_easy_setopt (edata->ehandle, CURLOPT_WRITEFUNCTION, _get_writefunction_cb);
|
||||||
|
curl_easy_setopt (edata->ehandle, CURLOPT_WRITEDATA, edata);
|
||||||
|
curl_easy_setopt (edata->ehandle, CURLOPT_PRIVATE, edata);
|
||||||
|
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
edata->timeout_source = _source_attach (self,
|
||||||
|
nm_g_timeout_source_new (timeout_ms,
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
_get_timeout_cb,
|
||||||
|
edata,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_add_handle (priv->mhandle, edata->ehandle);
|
||||||
|
|
||||||
|
if (cancellable) {
|
||||||
|
gulong signal_id;
|
||||||
|
|
||||||
|
signal_id = g_cancellable_connect (cancellable,
|
||||||
|
G_CALLBACK (_get_cancelled_cb),
|
||||||
|
edata,
|
||||||
|
NULL);
|
||||||
|
if (signal_id == 0) {
|
||||||
|
/* the request is already cancelled. Return. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
edata->cancellable_id = signal_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_http_client_get_finish (NMHttpClient *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
long *out_response_code,
|
||||||
|
GBytes **out_response_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GetResult *get_result;
|
||||||
|
|
||||||
|
g_return_val_if_fail (NM_IS_HTTP_CLIENT (self), FALSE);
|
||||||
|
g_return_val_if_fail (nm_g_task_is_valid (result, self, nm_http_client_get), FALSE);
|
||||||
|
|
||||||
|
get_result = g_task_propagate_pointer (G_TASK (result), error);
|
||||||
|
if (!get_result) {
|
||||||
|
NM_SET_OUT (out_response_code, -1);
|
||||||
|
NM_SET_OUT (out_response_data, NULL);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NM_SET_OUT (out_response_code, get_result->response_code);
|
||||||
|
|
||||||
|
/* response_data is binary, but is also guaranteed to be NUL terminated! */
|
||||||
|
NM_SET_OUT (out_response_data, g_steal_pointer (&get_result->response_data));
|
||||||
|
|
||||||
|
_get_result_free (get_result);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GTask *task;
|
||||||
|
char *uri;
|
||||||
|
NMHttpClientPollGetCheckFcn check_fcn;
|
||||||
|
gpointer check_user_data;
|
||||||
|
GBytes *response_data;
|
||||||
|
gsize request_max_data;
|
||||||
|
long response_code;
|
||||||
|
int request_timeout_ms;
|
||||||
|
} PollGetData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_get_data_free (gpointer data)
|
||||||
|
{
|
||||||
|
PollGetData *poll_get_data = data;
|
||||||
|
|
||||||
|
g_free (poll_get_data->uri);
|
||||||
|
|
||||||
|
nm_clear_pointer (&poll_get_data->response_data, g_bytes_unref);
|
||||||
|
|
||||||
|
nm_g_slice_free (poll_get_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_get_probe_start_fcn (GCancellable *cancellable,
|
||||||
|
gpointer probe_user_data,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PollGetData *poll_get_data = probe_user_data;
|
||||||
|
|
||||||
|
/* balanced by _poll_get_probe_finish_fcn() */
|
||||||
|
g_object_ref (poll_get_data->task);
|
||||||
|
|
||||||
|
nm_http_client_get (g_task_get_source_object (poll_get_data->task),
|
||||||
|
poll_get_data->uri,
|
||||||
|
poll_get_data->request_timeout_ms,
|
||||||
|
poll_get_data->request_max_data,
|
||||||
|
cancellable,
|
||||||
|
callback,
|
||||||
|
user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_poll_get_probe_finish_fcn (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer probe_user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
PollGetData *poll_get_data = probe_user_data;
|
||||||
|
_nm_unused gs_unref_object GTask *task = poll_get_data->task; /* balance ref from _poll_get_probe_start_fcn() */
|
||||||
|
gboolean success;
|
||||||
|
gs_free_error GError *local_error = NULL;
|
||||||
|
long response_code;
|
||||||
|
gs_unref_bytes GBytes *response_data = NULL;
|
||||||
|
|
||||||
|
success = nm_http_client_get_finish (g_task_get_source_object (poll_get_data->task),
|
||||||
|
result,
|
||||||
|
&response_code,
|
||||||
|
&response_data,
|
||||||
|
&local_error);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
if (nm_utils_error_is_cancelled (local_error, FALSE)) {
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poll_get_data->check_fcn) {
|
||||||
|
success = poll_get_data->check_fcn (response_code,
|
||||||
|
response_data,
|
||||||
|
poll_get_data->check_user_data,
|
||||||
|
&local_error);
|
||||||
|
} else
|
||||||
|
success = (response_code == 200);
|
||||||
|
|
||||||
|
if (local_error) {
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
poll_get_data->response_code = response_code;
|
||||||
|
poll_get_data->response_data = g_steal_pointer (&response_data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_poll_get_done_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PollGetData *poll_get_data = user_data;
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
success = nmcs_utils_poll_finish (result, NULL, &error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
g_task_return_error (poll_get_data->task, g_steal_pointer (&error));
|
||||||
|
else
|
||||||
|
g_task_return_boolean (poll_get_data->task, success);
|
||||||
|
|
||||||
|
g_object_unref (poll_get_data->task);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_http_client_poll_get (NMHttpClient *self,
|
||||||
|
const char *uri,
|
||||||
|
int request_timeout_ms,
|
||||||
|
gssize request_max_data,
|
||||||
|
int poll_timeout_ms,
|
||||||
|
int ratelimit_timeout_ms,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
NMHttpClientPollGetCheckFcn check_fcn,
|
||||||
|
gpointer check_user_data,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
nm_auto_pop_gmaincontext GMainContext *context = NULL;
|
||||||
|
PollGetData *poll_get_data;
|
||||||
|
|
||||||
|
g_return_if_fail (NM_IS_HTTP_CLIENT (self));
|
||||||
|
g_return_if_fail (uri && uri[0]);
|
||||||
|
g_return_if_fail (request_timeout_ms >= -1);
|
||||||
|
g_return_if_fail (request_max_data >= -1);
|
||||||
|
g_return_if_fail (poll_timeout_ms >= -1);
|
||||||
|
g_return_if_fail (ratelimit_timeout_ms >= -1);
|
||||||
|
g_return_if_fail (!cancellable || G_CANCELLABLE (cancellable));
|
||||||
|
|
||||||
|
poll_get_data = g_slice_new (PollGetData);
|
||||||
|
*poll_get_data = (PollGetData) {
|
||||||
|
.task = nm_g_task_new (self, cancellable, nm_http_client_poll_get, callback, user_data),
|
||||||
|
.uri = g_strdup (uri),
|
||||||
|
.request_timeout_ms = request_timeout_ms,
|
||||||
|
.request_max_data = request_max_data,
|
||||||
|
.check_fcn = check_fcn,
|
||||||
|
.check_user_data = check_user_data,
|
||||||
|
.response_code = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
nmcs_wait_for_objects_register (poll_get_data->task);
|
||||||
|
|
||||||
|
g_task_set_task_data (poll_get_data->task,
|
||||||
|
poll_get_data,
|
||||||
|
_poll_get_data_free);
|
||||||
|
|
||||||
|
context = nm_g_main_context_push_thread_default_if_necessary (nm_http_client_get_main_context (self));
|
||||||
|
|
||||||
|
nmcs_utils_poll (poll_timeout_ms,
|
||||||
|
ratelimit_timeout_ms,
|
||||||
|
0,
|
||||||
|
_poll_get_probe_start_fcn,
|
||||||
|
_poll_get_probe_finish_fcn,
|
||||||
|
poll_get_data,
|
||||||
|
cancellable,
|
||||||
|
_poll_get_done_cb,
|
||||||
|
poll_get_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_http_client_poll_get_finish (NMHttpClient *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
long *out_response_code,
|
||||||
|
GBytes **out_response_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
PollGetData *poll_get_data;
|
||||||
|
GTask *task;
|
||||||
|
gboolean success;
|
||||||
|
gs_free_error GError *local_error = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (NM_HTTP_CLIENT (self), FALSE);
|
||||||
|
g_return_val_if_fail (nm_g_task_is_valid (result, self, nm_http_client_poll_get), FALSE);
|
||||||
|
|
||||||
|
task = G_TASK (result);
|
||||||
|
|
||||||
|
success = g_task_propagate_boolean (task, &local_error);
|
||||||
|
if ( local_error
|
||||||
|
|| !success) {
|
||||||
|
if (local_error)
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
NM_SET_OUT (out_response_code, -1);
|
||||||
|
NM_SET_OUT (out_response_data, NULL);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_get_data = g_task_get_task_data (task);
|
||||||
|
|
||||||
|
NM_SET_OUT (out_response_code, poll_get_data->response_code);
|
||||||
|
NM_SET_OUT (out_response_data, g_steal_pointer (&poll_get_data->response_data));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
_mhandle_action (NMHttpClient *self,
|
||||||
|
int sockfd,
|
||||||
|
int ev_bitmask)
|
||||||
|
{
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
EHandleData *edata;
|
||||||
|
CURLMsg *msg;
|
||||||
|
CURLcode eret;
|
||||||
|
int m_left;
|
||||||
|
CURLMcode ret;
|
||||||
|
int running_handles;
|
||||||
|
|
||||||
|
ret = curl_multi_socket_action (priv->mhandle, sockfd, ev_bitmask, &running_handles);
|
||||||
|
if (ret != CURLM_OK) {
|
||||||
|
_LOGE ("curl: curl_multi_socket_action() failed: (%d) %s", ret, curl_multi_strerror (ret));
|
||||||
|
/* really unexpected. Not clear how to handle this. */
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((msg = curl_multi_info_read (priv->mhandle, &m_left))) {
|
||||||
|
|
||||||
|
if (msg->msg != CURLMSG_DONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &edata);
|
||||||
|
|
||||||
|
nm_assert (eret == CURLE_OK);
|
||||||
|
nm_assert (edata);
|
||||||
|
|
||||||
|
edata->ehandle_result = msg->data.result;
|
||||||
|
_ehandle_complete (edata, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_mhandle_socket_cb (int fd,
|
||||||
|
GIOCondition condition,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
int ev_bitmask = 0;
|
||||||
|
|
||||||
|
if (condition & G_IO_IN)
|
||||||
|
ev_bitmask |= CURL_CSELECT_IN;
|
||||||
|
if (condition & G_IO_OUT)
|
||||||
|
ev_bitmask |= CURL_CSELECT_OUT;
|
||||||
|
if (condition & G_IO_ERR)
|
||||||
|
ev_bitmask |= CURL_CSELECT_ERR;
|
||||||
|
|
||||||
|
_mhandle_action (user_data, fd, ev_bitmask);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_mhandle_socketfunction_cb (CURL *e_handle, curl_socket_t fd, int what, void *user_data, void *socketp)
|
||||||
|
{
|
||||||
|
NMHttpClient *self = user_data;
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
(void) _NM_ENSURE_TYPE (int, fd);
|
||||||
|
|
||||||
|
nm_clear_g_source_inst (&priv->mhandle_source_socket);
|
||||||
|
|
||||||
|
if (what != CURL_POLL_REMOVE) {
|
||||||
|
GIOCondition condition = 0;
|
||||||
|
|
||||||
|
if (what == CURL_POLL_IN)
|
||||||
|
condition = G_IO_IN;
|
||||||
|
else if (what == CURL_POLL_OUT)
|
||||||
|
condition = G_IO_OUT;
|
||||||
|
else if (what == CURL_POLL_INOUT)
|
||||||
|
condition = G_IO_IN | G_IO_OUT;
|
||||||
|
else
|
||||||
|
condition = 0;
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
priv->mhandle_source_socket = g_unix_fd_source_new (fd, condition);
|
||||||
|
g_source_set_callback (priv->mhandle_source_socket, G_SOURCE_FUNC (_mhandle_socket_cb), self, NULL);
|
||||||
|
g_source_attach (priv->mhandle_source_socket, priv->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CURLM_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_mhandle_timeout_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
_mhandle_action (user_data, CURL_SOCKET_TIMEOUT, 0);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_mhandle_timerfunction_cb (CURLM *multi, long timeout_ms, void *user_data)
|
||||||
|
{
|
||||||
|
NMHttpClient *self = user_data;
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
nm_clear_pointer (&priv->mhandle_source_timeout, nm_g_source_destroy_and_unref);
|
||||||
|
if (timeout_ms >= 0) {
|
||||||
|
priv->mhandle_source_timeout = _source_attach (self,
|
||||||
|
nm_g_timeout_source_new (NM_MIN (timeout_ms, G_MAXINT),
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
_mhandle_timeout_cb,
|
||||||
|
self,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
nm_http_client_init (NMHttpClient *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
constructed (GObject *object)
|
||||||
|
{
|
||||||
|
NMHttpClient *self = NM_HTTP_CLIENT (object);
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
priv->context = g_main_context_ref_thread_default ();
|
||||||
|
|
||||||
|
priv->mhandle = curl_multi_init ();
|
||||||
|
if (!priv->mhandle)
|
||||||
|
_LOGE ("curl: failed to create multi-handle");
|
||||||
|
else {
|
||||||
|
curl_multi_setopt (priv->mhandle, CURLMOPT_SOCKETFUNCTION, _mhandle_socketfunction_cb);
|
||||||
|
curl_multi_setopt (priv->mhandle, CURLMOPT_SOCKETDATA, self);
|
||||||
|
curl_multi_setopt (priv->mhandle, CURLMOPT_TIMERFUNCTION, _mhandle_timerfunction_cb);
|
||||||
|
curl_multi_setopt (priv->mhandle, CURLMOPT_TIMERDATA, self);
|
||||||
|
if (NM_CURL_DEBUG)
|
||||||
|
curl_multi_setopt (priv->mhandle, CURLOPT_VERBOSE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (nm_http_client_parent_class)->constructed (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
NMHttpClient *
|
||||||
|
nm_http_client_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (NM_TYPE_HTTP_CLIENT, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispose (GObject *object)
|
||||||
|
{
|
||||||
|
NMHttpClient *self = NM_HTTP_CLIENT (object);
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
nm_clear_pointer (&priv->mhandle, curl_multi_cleanup);
|
||||||
|
|
||||||
|
nm_clear_g_source_inst (&priv->mhandle_source_timeout);
|
||||||
|
nm_clear_g_source_inst (&priv->mhandle_source_socket);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (nm_http_client_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
finalize (GObject *object)
|
||||||
|
{
|
||||||
|
NMHttpClient *self = NM_HTTP_CLIENT (object);
|
||||||
|
NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (nm_http_client_parent_class)->finalize (object);
|
||||||
|
|
||||||
|
g_main_context_unref (priv->context);
|
||||||
|
|
||||||
|
curl_global_cleanup ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nm_http_client_class_init (NMHttpClientClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->constructed = constructed;
|
||||||
|
object_class->dispose = dispose;
|
||||||
|
object_class->finalize = finalize;
|
||||||
|
|
||||||
|
nm_http_client_curl_global_init ();
|
||||||
|
}
|
67
clients/cloud-setup/nm-http-client.h
Normal file
67
clients/cloud-setup/nm-http-client.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#ifndef __NM_HTTP_CLIENT_C__
|
||||||
|
#define __NM_HTTP_CLIENT_C__
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct _NMHttpClient NMHttpClient;
|
||||||
|
typedef struct _NMHttpClientClass NMHttpClientClass;
|
||||||
|
|
||||||
|
#define NM_TYPE_HTTP_CLIENT (nm_http_client_get_type ())
|
||||||
|
#define NM_HTTP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_HTTP_CLIENT, NMHttpClient))
|
||||||
|
#define NM_HTTP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_HTTP_CLIENT, NMHttpClientClass))
|
||||||
|
#define NM_IS_HTTP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_HTTP_CLIENT))
|
||||||
|
#define NM_IS_HTTP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_HTTP_CLIENT))
|
||||||
|
#define NM_HTTP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_HTTP_CLIENT, NMHttpClientClass))
|
||||||
|
|
||||||
|
GType nm_http_client_get_type (void);
|
||||||
|
|
||||||
|
NMHttpClient *nm_http_client_new (void);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
GMainContext *nm_http_client_get_main_context (NMHttpClient *self);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void nm_http_client_get (NMHttpClient *self,
|
||||||
|
const char *uri,
|
||||||
|
int timeout_ms,
|
||||||
|
gssize max_data,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean nm_http_client_get_finish (NMHttpClient *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
long *out_response_code,
|
||||||
|
GBytes **out_response_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
typedef gboolean (*NMHttpClientPollGetCheckFcn) (long response_code,
|
||||||
|
GBytes *response_data,
|
||||||
|
gpointer check_user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void nm_http_client_poll_get (NMHttpClient *self,
|
||||||
|
const char *uri,
|
||||||
|
int request_timeout_ms,
|
||||||
|
gssize request_max_data,
|
||||||
|
int poll_timeout_ms,
|
||||||
|
int ratelimit_timeout_ms,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
NMHttpClientPollGetCheckFcn check_fcn,
|
||||||
|
gpointer check_user_data,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean nm_http_client_poll_get_finish (NMHttpClient *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
long *out_response_code,
|
||||||
|
GBytes **out_response_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#endif /* __NM_HTTP_CLIENT_C__ */
|
551
clients/cloud-setup/nmcs-provider-ec2.c
Normal file
551
clients/cloud-setup/nmcs-provider-ec2.c
Normal file
@@ -0,0 +1,551 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#include "nm-default.h"
|
||||||
|
|
||||||
|
#include "nmcs-provider-ec2.h"
|
||||||
|
|
||||||
|
#include "nm-cloud-setup-utils.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define HTTP_TIMEOUT_MS 3000
|
||||||
|
|
||||||
|
#define NM_EC2_HOST "169.254.169.254"
|
||||||
|
#define NM_EC2_BASE "http://" NM_EC2_HOST
|
||||||
|
#define NM_EC2_API_VERSION "2018-09-24"
|
||||||
|
#define NM_EC2_METADATA_URL_BASE /* $NM_EC2_BASE/$NM_EC2_API_VERSION */ "/meta-data/network/interfaces/macs/"
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
_ec2_base (void)
|
||||||
|
{
|
||||||
|
static const char *base_cached = NULL;
|
||||||
|
const char *base;
|
||||||
|
|
||||||
|
again:
|
||||||
|
base = g_atomic_pointer_get (&base_cached);
|
||||||
|
if (G_UNLIKELY (!base)) {
|
||||||
|
/* The base URI can be set via environment variable.
|
||||||
|
* This is only for testing, not really to be configurable! */
|
||||||
|
base = g_getenv ("NM_CLOUD_SETUP_EC2_HOST");
|
||||||
|
if ( base
|
||||||
|
&& base[0]
|
||||||
|
&& !strchr (base, '/')) {
|
||||||
|
if ( NM_STR_HAS_PREFIX (base, "http://")
|
||||||
|
|| NM_STR_HAS_PREFIX (base, "https://"))
|
||||||
|
base = g_intern_string (base);
|
||||||
|
else {
|
||||||
|
gs_free char *s = NULL;
|
||||||
|
|
||||||
|
s = g_strconcat ("http://", base, NULL);
|
||||||
|
base = g_intern_string (s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!base)
|
||||||
|
base = NM_EC2_BASE;
|
||||||
|
|
||||||
|
nm_assert (!NM_STR_HAS_SUFFIX (base, "/"));
|
||||||
|
|
||||||
|
if (!g_atomic_pointer_compare_and_exchange (&base_cached, NULL, base))
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _ec2_uri_concat(...) nmcs_utils_uri_build_concat (_ec2_base (), __VA_ARGS__)
|
||||||
|
#define _ec2_uri_interfaces(...) _ec2_uri_concat (NM_EC2_API_VERSION, NM_EC2_METADATA_URL_BASE, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
struct _NMCSProviderEC2 {
|
||||||
|
NMCSProvider parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _NMCSProviderEC2Class {
|
||||||
|
NMCSProviderClass parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (NMCSProviderEC2, nmcs_provider_ec2, NMCS_TYPE_PROVIDER);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_detect_get_meta_data_check_cb (long response_code,
|
||||||
|
GBytes *response_data,
|
||||||
|
gpointer check_user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return response_code == 200
|
||||||
|
&& nmcs_utils_parse_get_full_line (response_data, "ami-id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_detect_get_meta_data_done_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gs_unref_object GTask *task = user_data;
|
||||||
|
gs_free_error GError *get_error = NULL;
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
success = nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
|
||||||
|
result,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&get_error);
|
||||||
|
|
||||||
|
if (nm_utils_error_is_cancelled (get_error, FALSE)) {
|
||||||
|
g_task_return_error (task, g_steal_pointer (&get_error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_error) {
|
||||||
|
nm_utils_error_set (&error,
|
||||||
|
NM_UTILS_ERROR_UNKNOWN,
|
||||||
|
"failure to get EC2 metadata: %s",
|
||||||
|
get_error->message);
|
||||||
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
nm_utils_error_set (&error,
|
||||||
|
NM_UTILS_ERROR_UNKNOWN,
|
||||||
|
"failure to detect EC2 metadata");
|
||||||
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
detect (NMCSProvider *provider,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
NMHttpClient *http_client;
|
||||||
|
gs_free char *uri = NULL;
|
||||||
|
|
||||||
|
http_client = nmcs_provider_get_http_client (provider);
|
||||||
|
|
||||||
|
nm_http_client_poll_get (http_client,
|
||||||
|
(uri = _ec2_uri_concat ("latest/meta-data/")),
|
||||||
|
HTTP_TIMEOUT_MS,
|
||||||
|
256*1024,
|
||||||
|
7000,
|
||||||
|
1000,
|
||||||
|
g_task_get_cancellable (task),
|
||||||
|
_detect_get_meta_data_check_cb,
|
||||||
|
NULL,
|
||||||
|
_detect_get_meta_data_done_cb,
|
||||||
|
task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
gulong cancelled_id;
|
||||||
|
guint n_pending;
|
||||||
|
} GetConfigIfaceData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_task_return (GetConfigIfaceData *iface_data,
|
||||||
|
GError *error_take)
|
||||||
|
{
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data = iface_data->get_config_data;
|
||||||
|
|
||||||
|
nm_clear_g_cancellable_disconnect (g_task_get_cancellable (get_config_data->task),
|
||||||
|
&iface_data->cancelled_id);
|
||||||
|
|
||||||
|
nm_clear_g_cancellable (&iface_data->cancellable);
|
||||||
|
|
||||||
|
nm_g_slice_free (iface_data);
|
||||||
|
|
||||||
|
if (error_take) {
|
||||||
|
if (nm_utils_error_is_cancelled (error_take, FALSE))
|
||||||
|
_LOGD ("get-config: cancelled");
|
||||||
|
else
|
||||||
|
_LOGD ("get-config: failed: %s", error_take->message);
|
||||||
|
g_task_return_error (get_config_data->task, error_take);
|
||||||
|
} else {
|
||||||
|
_LOGD ("get-config: success");
|
||||||
|
g_task_return_pointer (get_config_data->task,
|
||||||
|
g_hash_table_ref (get_config_data->result_dict),
|
||||||
|
(GDestroyNotify) g_hash_table_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (get_config_data->task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_fetch_done_cb (NMHttpClient *http_client,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data,
|
||||||
|
gboolean is_local_ipv4)
|
||||||
|
{
|
||||||
|
GetConfigIfaceData *iface_data;
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data;
|
||||||
|
const char *hwaddr = NULL;
|
||||||
|
gs_unref_bytes GBytes *response_data = NULL;
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
gboolean success;
|
||||||
|
NMCSProviderGetConfigIfaceData *config_iface_data;
|
||||||
|
|
||||||
|
nm_utils_user_data_unpack (user_data, &iface_data, &hwaddr);
|
||||||
|
|
||||||
|
success = nm_http_client_poll_get_finish (http_client,
|
||||||
|
result,
|
||||||
|
NULL,
|
||||||
|
&response_data,
|
||||||
|
&error);
|
||||||
|
if (nm_utils_error_is_cancelled (error, FALSE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_config_data = iface_data->get_config_data;
|
||||||
|
|
||||||
|
config_iface_data = g_hash_table_lookup (get_config_data->result_dict, hwaddr);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
in_addr_t tmp_addr;
|
||||||
|
int tmp_prefix;
|
||||||
|
|
||||||
|
if (is_local_ipv4) {
|
||||||
|
gs_free const char **s_addrs = NULL;
|
||||||
|
gsize i, len;
|
||||||
|
|
||||||
|
s_addrs = nm_utils_strsplit_set_full (g_bytes_get_data (response_data, NULL), "\n", NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
|
||||||
|
len = NM_PTRARRAY_LEN (s_addrs);
|
||||||
|
|
||||||
|
nm_assert (!config_iface_data->has_ipv4s);
|
||||||
|
nm_assert (!config_iface_data->ipv4s_arr);
|
||||||
|
config_iface_data->has_ipv4s = TRUE;
|
||||||
|
config_iface_data->ipv4s_len = 0;
|
||||||
|
if (len > 0) {
|
||||||
|
config_iface_data->ipv4s_arr = g_new (in_addr_t, len);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (nm_utils_parse_inaddr_bin (AF_INET,
|
||||||
|
s_addrs[i],
|
||||||
|
NULL,
|
||||||
|
&tmp_addr))
|
||||||
|
config_iface_data->ipv4s_arr[config_iface_data->ipv4s_len++] = tmp_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nm_utils_parse_inaddr_prefix_bin (AF_INET,
|
||||||
|
g_bytes_get_data (response_data, NULL),
|
||||||
|
NULL,
|
||||||
|
&tmp_addr,
|
||||||
|
&tmp_prefix)) {
|
||||||
|
nm_assert (!config_iface_data->has_cidr);
|
||||||
|
config_iface_data->has_cidr = TRUE;
|
||||||
|
config_iface_data->cidr_prefix = tmp_prefix;
|
||||||
|
config_iface_data->cidr_addr = tmp_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--iface_data->n_pending > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_get_config_task_return (iface_data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_fetch_done_cb_subnet_ipv4_cidr_block (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
_get_config_fetch_done_cb (NM_HTTP_CLIENT (source), result, user_data, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_fetch_done_cb_local_ipv4s (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
_get_config_fetch_done_cb (NM_HTTP_CLIENT (source), result, user_data, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_fetch_cancelled_cb (GObject *object, gpointer user_data)
|
||||||
|
{
|
||||||
|
GetConfigIfaceData *iface_data = user_data;
|
||||||
|
|
||||||
|
if (iface_data->cancelled_id == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nm_clear_g_signal_handler (g_task_get_cancellable (iface_data->get_config_data->task),
|
||||||
|
&iface_data->cancelled_id);
|
||||||
|
_get_config_task_return (iface_data,
|
||||||
|
nm_utils_error_new_cancelled (FALSE, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data;
|
||||||
|
GHashTable *response_parsed;
|
||||||
|
} GetConfigMetadataData;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gssize iface_idx;
|
||||||
|
char path[0];
|
||||||
|
} GetConfigMetadataMac;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_metadata_ready_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GetConfigMetadataData *metadata_data = user_data;
|
||||||
|
GetConfigIfaceData *iface_data;
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data = metadata_data->get_config_data;
|
||||||
|
gs_unref_hashtable GHashTable *response_parsed = g_steal_pointer (&metadata_data->response_parsed);
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
GetConfigMetadataMac *v_mac_data;
|
||||||
|
const char *v_hwaddr;
|
||||||
|
GHashTableIter h_iter;
|
||||||
|
NMHttpClient *http_client;
|
||||||
|
|
||||||
|
nm_g_slice_free (metadata_data);
|
||||||
|
|
||||||
|
nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
|
||||||
|
result,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&error);
|
||||||
|
|
||||||
|
iface_data = g_slice_new (GetConfigIfaceData);
|
||||||
|
*iface_data = (GetConfigIfaceData) {
|
||||||
|
.get_config_data = get_config_data,
|
||||||
|
.n_pending = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (nm_utils_error_is_cancelled (error, FALSE)) {
|
||||||
|
_get_config_task_return (iface_data, g_steal_pointer (&error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We ignore errors. Only if we got no response at all, it's a problem.
|
||||||
|
* Otherwise, we proceed with whatever we could fetch. */
|
||||||
|
if (!response_parsed) {
|
||||||
|
_get_config_task_return (iface_data,
|
||||||
|
nm_utils_error_new (NM_UTILS_ERROR_UNKNOWN,
|
||||||
|
"meta data for interfaces not found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellable = g_task_get_cancellable (get_config_data->task);
|
||||||
|
if (cancellable) {
|
||||||
|
gulong cancelled_id;
|
||||||
|
|
||||||
|
cancelled_id = g_cancellable_connect (cancellable,
|
||||||
|
G_CALLBACK (_get_config_fetch_cancelled_cb),
|
||||||
|
iface_data,
|
||||||
|
NULL);
|
||||||
|
if (cancelled_id == 0) {
|
||||||
|
_get_config_task_return (iface_data,
|
||||||
|
nm_utils_error_new_cancelled (FALSE, NULL));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface_data->cancelled_id = cancelled_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface_data->cancellable = g_cancellable_new ();
|
||||||
|
|
||||||
|
http_client = nmcs_provider_get_http_client (g_task_get_source_object (get_config_data->task));
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&h_iter, response_parsed);
|
||||||
|
while (g_hash_table_iter_next (&h_iter, (gpointer *) &v_hwaddr, (gpointer *) &v_mac_data)) {
|
||||||
|
NMCSProviderGetConfigIfaceData *config_iface_data;
|
||||||
|
gs_free char *uri1 = NULL;
|
||||||
|
gs_free char *uri2 = NULL;
|
||||||
|
const char *hwaddr;
|
||||||
|
|
||||||
|
if (!g_hash_table_lookup_extended (get_config_data->result_dict, v_hwaddr, (gpointer *) &hwaddr, (gpointer *) &config_iface_data)) {
|
||||||
|
if (!get_config_data->any) {
|
||||||
|
_LOGD ("get-config: skip fetching meta data for %s (%s)", v_hwaddr, v_mac_data->path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
config_iface_data = nmcs_provider_get_config_iface_data_new (FALSE);
|
||||||
|
g_hash_table_insert (get_config_data->result_dict,
|
||||||
|
(char *) (hwaddr = g_strdup (v_hwaddr)),
|
||||||
|
config_iface_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_assert (config_iface_data->iface_idx == -1);
|
||||||
|
config_iface_data->iface_idx = v_mac_data->iface_idx;
|
||||||
|
|
||||||
|
_LOGD ("get-config: start fetching meta data for #%"G_GSSIZE_FORMAT", %s (%s)", config_iface_data->iface_idx, hwaddr, v_mac_data->path);
|
||||||
|
|
||||||
|
iface_data->n_pending++;
|
||||||
|
nm_http_client_poll_get (http_client,
|
||||||
|
(uri1 = _ec2_uri_interfaces (v_mac_data->path,
|
||||||
|
NM_STR_HAS_SUFFIX (v_mac_data->path, "/")
|
||||||
|
? ""
|
||||||
|
: "/",
|
||||||
|
"subnet-ipv4-cidr-block")),
|
||||||
|
HTTP_TIMEOUT_MS,
|
||||||
|
512*1024,
|
||||||
|
10000,
|
||||||
|
1000,
|
||||||
|
iface_data->cancellable,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
_get_config_fetch_done_cb_subnet_ipv4_cidr_block,
|
||||||
|
nm_utils_user_data_pack (iface_data, hwaddr));
|
||||||
|
|
||||||
|
iface_data->n_pending++;
|
||||||
|
nm_http_client_poll_get (http_client,
|
||||||
|
(uri2 = _ec2_uri_interfaces (v_mac_data->path,
|
||||||
|
NM_STR_HAS_SUFFIX (v_mac_data->path, "/")
|
||||||
|
? ""
|
||||||
|
: "/",
|
||||||
|
"local-ipv4s")),
|
||||||
|
HTTP_TIMEOUT_MS,
|
||||||
|
512*1024,
|
||||||
|
10000,
|
||||||
|
1000,
|
||||||
|
iface_data->cancellable,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
_get_config_fetch_done_cb_local_ipv4s,
|
||||||
|
nm_utils_user_data_pack (iface_data, hwaddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface_data->n_pending == 0)
|
||||||
|
_get_config_task_return (iface_data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_get_config_metadata_ready_check (long response_code,
|
||||||
|
GBytes *response_data,
|
||||||
|
gpointer check_user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GetConfigMetadataData *metadata_data = check_user_data;
|
||||||
|
gs_unref_hashtable GHashTable *response_parsed = NULL;
|
||||||
|
const guint8 *r_data;
|
||||||
|
gsize r_len;
|
||||||
|
GHashTableIter h_iter;
|
||||||
|
gboolean has_all;
|
||||||
|
const char *c_hwaddr;
|
||||||
|
gssize iface_idx_counter = 0;
|
||||||
|
|
||||||
|
if ( response_code != 200
|
||||||
|
|| !response_data) {
|
||||||
|
/* we wait longer. */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
r_data = g_bytes_get_data (response_data, &r_len);
|
||||||
|
|
||||||
|
while (r_len > 0) {
|
||||||
|
const guint8 *p_eol;
|
||||||
|
const char *p_start;
|
||||||
|
gsize p_start_l;
|
||||||
|
gsize p_start_l_2;
|
||||||
|
char *hwaddr;
|
||||||
|
GetConfigMetadataMac *mac_data;
|
||||||
|
|
||||||
|
p_start = (const char *) r_data;
|
||||||
|
|
||||||
|
p_eol = memchr (r_data, '\n', r_len);
|
||||||
|
if (p_eol) {
|
||||||
|
p_start_l = (p_eol - r_data);
|
||||||
|
r_len -= p_start_l + 1;
|
||||||
|
r_data = &p_eol[1];
|
||||||
|
} else {
|
||||||
|
p_start_l = r_len;
|
||||||
|
r_data = &r_data[r_len];
|
||||||
|
r_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_start_l == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
p_start_l_2 = p_start_l;
|
||||||
|
if (p_start[p_start_l_2 - 1] == '/') {
|
||||||
|
/* trim the trailing "/". */
|
||||||
|
p_start_l_2--;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwaddr = nmcs_utils_hwaddr_normalize (p_start, p_start_l_2);
|
||||||
|
if (!hwaddr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!response_parsed)
|
||||||
|
response_parsed = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
|
||||||
|
|
||||||
|
mac_data = g_malloc (sizeof (GetConfigMetadataData) + 1 + p_start_l);
|
||||||
|
mac_data->iface_idx = iface_idx_counter++;
|
||||||
|
memcpy (mac_data->path, p_start, p_start_l);
|
||||||
|
mac_data->path[p_start_l] = '\0';
|
||||||
|
|
||||||
|
g_hash_table_insert (response_parsed, hwaddr, mac_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
has_all = TRUE;
|
||||||
|
g_hash_table_iter_init (&h_iter, metadata_data->get_config_data->result_dict);
|
||||||
|
while (g_hash_table_iter_next (&h_iter, (gpointer *) &c_hwaddr, NULL)) {
|
||||||
|
if ( !response_parsed
|
||||||
|
|| !g_hash_table_contains (response_parsed, c_hwaddr)) {
|
||||||
|
has_all = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_clear_pointer (&metadata_data->response_parsed, g_hash_table_unref);
|
||||||
|
metadata_data->response_parsed = g_steal_pointer (&response_parsed);
|
||||||
|
return has_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_config (NMCSProvider *provider,
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data)
|
||||||
|
{
|
||||||
|
gs_free char *uri = NULL;
|
||||||
|
GetConfigMetadataData *metadata_data;
|
||||||
|
|
||||||
|
metadata_data = g_slice_new (GetConfigMetadataData);
|
||||||
|
*metadata_data = (GetConfigMetadataData) {
|
||||||
|
.get_config_data = get_config_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* First we fetch the "macs/". If the caller requested some particular
|
||||||
|
* MAC addresses, then we poll until we see them. They might not yet be
|
||||||
|
* around from the start...
|
||||||
|
*/
|
||||||
|
nm_http_client_poll_get (nmcs_provider_get_http_client (provider),
|
||||||
|
(uri = _ec2_uri_interfaces ()),
|
||||||
|
HTTP_TIMEOUT_MS,
|
||||||
|
256 * 1024,
|
||||||
|
15000,
|
||||||
|
1000,
|
||||||
|
g_task_get_cancellable (get_config_data->task),
|
||||||
|
_get_config_metadata_ready_check,
|
||||||
|
metadata_data,
|
||||||
|
_get_config_metadata_ready_cb,
|
||||||
|
metadata_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
nmcs_provider_ec2_init (NMCSProviderEC2 *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nmcs_provider_ec2_class_init (NMCSProviderEC2Class *klass)
|
||||||
|
{
|
||||||
|
NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS (klass);
|
||||||
|
|
||||||
|
provider_class->_name = "ec2";
|
||||||
|
provider_class->detect = detect;
|
||||||
|
provider_class->get_config = get_config;
|
||||||
|
}
|
24
clients/cloud-setup/nmcs-provider-ec2.h
Normal file
24
clients/cloud-setup/nmcs-provider-ec2.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#ifndef __NMCS_PROVIDER_EC2_H__
|
||||||
|
#define __NMCS_PROVIDER_EC2_H__
|
||||||
|
|
||||||
|
#include "nmcs-provider.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct _NMCSProviderEC2 NMCSProviderEC2;
|
||||||
|
typedef struct _NMCSProviderEC2Class NMCSProviderEC2Class;
|
||||||
|
|
||||||
|
#define NMCS_TYPE_PROVIDER_EC2 (nmcs_provider_ec2_get_type ())
|
||||||
|
#define NMCS_PROVIDER_EC2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMCS_TYPE_PROVIDER_EC2, NMCSProviderEC2))
|
||||||
|
#define NMCS_PROVIDER_EC2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMCS_TYPE_PROVIDER_EC2, NMCSProviderEC2Class))
|
||||||
|
#define NMCS_IS_PROVIDER_EC2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMCS_TYPE_PROVIDER_EC2))
|
||||||
|
#define NMCS_IS_PROVIDER_EC2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMCS_TYPE_PROVIDER_EC2))
|
||||||
|
#define NMCS_PROVIDER_EC2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMCS_TYPE_PROVIDER_EC2, NMCSProviderEC2Class))
|
||||||
|
|
||||||
|
GType nmcs_provider_ec2_get_type (void);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#endif /* __NMCS_PROVIDER_EC2_H__ */
|
236
clients/cloud-setup/nmcs-provider.c
Normal file
236
clients/cloud-setup/nmcs-provider.c
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#include "nm-default.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
nmcs_provider_detect (NMCSProvider *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gs_unref_object GTask *task = NULL;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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_new (gboolean was_requested)
|
||||||
|
{
|
||||||
|
NMCSProviderGetConfigIfaceData *iface_data;
|
||||||
|
|
||||||
|
iface_data = g_slice_new (NMCSProviderGetConfigIfaceData);
|
||||||
|
*iface_data = (NMCSProviderGetConfigIfaceData) {
|
||||||
|
.iface_idx = -1,
|
||||||
|
.was_requested = was_requested,
|
||||||
|
};
|
||||||
|
return iface_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_iface_data_free (gpointer data)
|
||||||
|
{
|
||||||
|
NMCSProviderGetConfigIfaceData *iface_data = data;
|
||||||
|
|
||||||
|
g_free (iface_data->ipv4s_arr);
|
||||||
|
|
||||||
|
nm_g_slice_free (iface_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_get_config_data_free (gpointer data)
|
||||||
|
{
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data = data;
|
||||||
|
|
||||||
|
if (get_config_data->extra_destroy)
|
||||||
|
get_config_data->extra_destroy (get_config_data->extra_data);
|
||||||
|
|
||||||
|
nm_clear_pointer (&get_config_data->result_dict, g_hash_table_unref);
|
||||||
|
|
||||||
|
nm_g_slice_free (get_config_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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,
|
||||||
|
g_free,
|
||||||
|
_iface_data_free),
|
||||||
|
};
|
||||||
|
|
||||||
|
g_task_set_task_data (get_config_data->task, get_config_data, _get_config_data_free);
|
||||||
|
|
||||||
|
nmcs_wait_for_objects_register (get_config_data->task);
|
||||||
|
|
||||||
|
for (; hwaddrs && hwaddrs[0]; hwaddrs++) {
|
||||||
|
g_hash_table_insert (get_config_data->result_dict,
|
||||||
|
g_strdup (hwaddrs[0]),
|
||||||
|
nmcs_provider_get_config_iface_data_new (TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGD ("get-config: starting");
|
||||||
|
|
||||||
|
NMCS_PROVIDER_GET_CLASS (self)->get_config (self, get_config_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
GHashTable *
|
||||||
|
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);
|
||||||
|
}
|
107
clients/cloud-setup/nmcs-provider.h
Normal file
107
clients/cloud-setup/nmcs-provider.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
|
||||||
|
#ifndef __NMCS_PROVIDER_H__
|
||||||
|
#define __NMCS_PROVIDER_H__
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#include "nm-http-client.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
in_addr_t *ipv4s_arr;
|
||||||
|
gsize ipv4s_len;
|
||||||
|
gssize iface_idx;
|
||||||
|
in_addr_t cidr_addr;
|
||||||
|
guint8 cidr_prefix;
|
||||||
|
bool has_ipv4s:1;
|
||||||
|
bool has_cidr:1;
|
||||||
|
|
||||||
|
/* TRUE, if the configuration was requested via hwaddrs argument to
|
||||||
|
* nmcs_provider_get_config(). */
|
||||||
|
bool was_requested:1;
|
||||||
|
|
||||||
|
} NMCSProviderGetConfigIfaceData;
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nmcs_provider_get_config_iface_data_is_valid (const NMCSProviderGetConfigIfaceData *config_data)
|
||||||
|
{
|
||||||
|
return config_data
|
||||||
|
&& config_data->iface_idx >= 0
|
||||||
|
&& config_data->has_cidr
|
||||||
|
&& config_data->has_ipv4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
NMCSProviderGetConfigIfaceData *nmcs_provider_get_config_iface_data_new (gboolean was_requested);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GTask *task;
|
||||||
|
GHashTable *result_dict;
|
||||||
|
gpointer extra_data;
|
||||||
|
GDestroyNotify extra_destroy;
|
||||||
|
bool any:1;
|
||||||
|
} NMCSProviderGetConfigTaskData;
|
||||||
|
|
||||||
|
#define NMCS_TYPE_PROVIDER (nmcs_provider_get_type ())
|
||||||
|
#define NMCS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMCS_TYPE_PROVIDER, NMCSProvider))
|
||||||
|
#define NMCS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMCS_TYPE_PROVIDER, NMCSProviderClass))
|
||||||
|
#define NMCS_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMCS_TYPE_PROVIDER))
|
||||||
|
#define NMCS_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMCS_TYPE_PROVIDER))
|
||||||
|
#define NMCS_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMCS_TYPE_PROVIDER, NMCSProviderClass))
|
||||||
|
|
||||||
|
#define NMCS_PROVIDER_HTTP_CLIENT "http-client"
|
||||||
|
|
||||||
|
struct _NMCSProviderPrivate;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GObject parent;
|
||||||
|
struct _NMCSProviderPrivate *_priv;
|
||||||
|
} NMCSProvider;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GObjectClass parent;
|
||||||
|
const char *_name;
|
||||||
|
|
||||||
|
void (*detect) (NMCSProvider *self,
|
||||||
|
GTask *task);
|
||||||
|
|
||||||
|
void (*get_config) (NMCSProvider *self,
|
||||||
|
NMCSProviderGetConfigTaskData *get_config_data);
|
||||||
|
|
||||||
|
} NMCSProviderClass;
|
||||||
|
|
||||||
|
GType nmcs_provider_get_type (void);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
const char *nmcs_provider_get_name (NMCSProvider *provider);
|
||||||
|
|
||||||
|
NMHttpClient *nmcs_provider_get_http_client (NMCSProvider *provider);
|
||||||
|
GMainContext *nmcs_provider_get_main_context (NMCSProvider *provider);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void nmcs_provider_detect (NMCSProvider *provider,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean nmcs_provider_detect_finish (NMCSProvider *provider,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void nmcs_provider_get_config (NMCSProvider *provider,
|
||||||
|
gboolean any,
|
||||||
|
const char *const*hwaddrs,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
GHashTable *nmcs_provider_get_config_finish (NMCSProvider *provider,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
#endif /* __NMCS_PROVIDER_H__ */
|
@@ -26,3 +26,7 @@ endif
|
|||||||
if enable_nmtui
|
if enable_nmtui
|
||||||
subdir('tui')
|
subdir('tui')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if enable_nm_cloud_setup
|
||||||
|
subdir('cloud-setup')
|
||||||
|
endif
|
||||||
|
12
configure.ac
12
configure.ac
@@ -1014,6 +1014,17 @@ else
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(BUILD_NMCLI, test "$build_nmcli" = yes)
|
AM_CONDITIONAL(BUILD_NMCLI, test "$build_nmcli" = yes)
|
||||||
|
|
||||||
|
AC_ARG_WITH(nm-cloud-setup,
|
||||||
|
AS_HELP_STRING([--with-nm-cloud-setup=yes|no], [Build nm-cloud-setup]))
|
||||||
|
if test "$with_nm_cloud_setup" != no; then
|
||||||
|
PKG_CHECK_MODULES(LIBCURL, [libcurl >= 7.24.0], [have_libcurl=yes], [have_libcurl=no])
|
||||||
|
if test "$have_libcurl" != "yes"; then
|
||||||
|
AC_MSG_ERROR(--with-nm-cloud-setup requires libcurl library.)
|
||||||
|
fi
|
||||||
|
with_nm_cloud_setup=yes
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL(BUILD_NM_CLOUD_SETUP, test "$with_nm_cloud_setup" == yes)
|
||||||
|
|
||||||
AC_ARG_WITH(nmtui,
|
AC_ARG_WITH(nmtui,
|
||||||
AS_HELP_STRING([--with-nmtui=yes|no], [Build nmtui]))
|
AS_HELP_STRING([--with-nmtui=yes|no], [Build nmtui]))
|
||||||
if test "$with_nmtui" != no; then
|
if test "$with_nmtui" != no; then
|
||||||
@@ -1303,6 +1314,7 @@ echo " libteamdctl: $enable_teamdctl"
|
|||||||
echo " ovs: $enable_ovs"
|
echo " ovs: $enable_ovs"
|
||||||
echo " nmcli: $build_nmcli"
|
echo " nmcli: $build_nmcli"
|
||||||
echo " nmtui: $build_nmtui"
|
echo " nmtui: $build_nmtui"
|
||||||
|
echo " nm-cloud-setup: $with_nm_cloud_setup"
|
||||||
echo " iwd: $ac_with_iwd"
|
echo " iwd: $ac_with_iwd"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
@@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
%global systemd_units NetworkManager.service NetworkManager-wait-online.service NetworkManager-dispatcher.service
|
%global systemd_units NetworkManager.service NetworkManager-wait-online.service NetworkManager-dispatcher.service
|
||||||
|
|
||||||
|
%global systemd_units_cloud_setup nm-cloud-setup.service nm-cloud-setup.timer
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
%bcond_with meson
|
%bcond_with meson
|
||||||
@@ -53,6 +55,7 @@
|
|||||||
%bcond_without ovs
|
%bcond_without ovs
|
||||||
%bcond_without ppp
|
%bcond_without ppp
|
||||||
%bcond_without nmtui
|
%bcond_without nmtui
|
||||||
|
%bcond_without nm_cloud_setup
|
||||||
%bcond_without regen_docs
|
%bcond_without regen_docs
|
||||||
%bcond_with debug
|
%bcond_with debug
|
||||||
%bcond_with test
|
%bcond_with test
|
||||||
@@ -480,6 +483,19 @@ by nm-connection-editor and nm-applet in a non-graphical environment.
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
%package cloud-setup
|
||||||
|
Summary: Automatically configure NetworkManager in cloud
|
||||||
|
Group: System Environment/Base
|
||||||
|
Requires: %{name} = %{epoch}:%{version}-%{release}
|
||||||
|
Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release}
|
||||||
|
|
||||||
|
%description cloud-setup
|
||||||
|
Installs a nm-cloud-setup tool that can automatically configure
|
||||||
|
NetworkManager in cloud setups. Currently only EC2 is supported.
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -p1 -n NetworkManager-%{real_version}
|
%autosetup -p1 -n NetworkManager-%{real_version}
|
||||||
|
|
||||||
@@ -538,6 +554,11 @@ by nm-connection-editor and nm-applet in a non-graphical environment.
|
|||||||
-Dnmtui=true \
|
-Dnmtui=true \
|
||||||
%else
|
%else
|
||||||
-Dnmtui=false \
|
-Dnmtui=false \
|
||||||
|
%endif
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
-Dnm_cloud_setup=true \
|
||||||
|
%else
|
||||||
|
-Dnm_cloud_setup=false \
|
||||||
%endif
|
%endif
|
||||||
-Dvapi=true \
|
-Dvapi=true \
|
||||||
-Dintrospection=true \
|
-Dintrospection=true \
|
||||||
@@ -659,6 +680,11 @@ intltoolize --automake --copy --force
|
|||||||
--with-nmtui=yes \
|
--with-nmtui=yes \
|
||||||
%else
|
%else
|
||||||
--with-nmtui=no \
|
--with-nmtui=no \
|
||||||
|
%endif
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
--with-nm-cloud-setup=yes \
|
||||||
|
%else
|
||||||
|
--with-nm-cloud-setup=no \
|
||||||
%endif
|
%endif
|
||||||
--enable-vala=yes \
|
--enable-vala=yes \
|
||||||
--enable-introspection \
|
--enable-introspection \
|
||||||
@@ -801,6 +827,12 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
%post cloud-setup
|
||||||
|
%systemd_post %{systemd_units_cloud_setup}
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
%preun
|
%preun
|
||||||
if [ $1 -eq 0 ]; then
|
if [ $1 -eq 0 ]; then
|
||||||
# Package removal, not upgrade
|
# Package removal, not upgrade
|
||||||
@@ -814,6 +846,12 @@ fi
|
|||||||
%systemd_preun NetworkManager-wait-online.service NetworkManager-dispatcher.service
|
%systemd_preun NetworkManager-wait-online.service NetworkManager-dispatcher.service
|
||||||
|
|
||||||
|
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
%preun cloud-setup
|
||||||
|
%systemd_preun %{systemd_units_cloud_setup}
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
%postun
|
%postun
|
||||||
/usr/bin/udevadm control --reload-rules || :
|
/usr/bin/udevadm control --reload-rules || :
|
||||||
/usr/bin/udevadm trigger --subsystem-match=net || :
|
/usr/bin/udevadm trigger --subsystem-match=net || :
|
||||||
@@ -827,6 +865,12 @@ fi
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
%postun cloud-setup
|
||||||
|
%systemd_postun %{systemd_units_cloud_setup}
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
%files
|
%files
|
||||||
%{dbus_sys_dir}/org.freedesktop.NetworkManager.conf
|
%{dbus_sys_dir}/org.freedesktop.NetworkManager.conf
|
||||||
%{dbus_sys_dir}/nm-dispatcher.conf
|
%{dbus_sys_dir}/nm-dispatcher.conf
|
||||||
@@ -995,5 +1039,15 @@ fi
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|
||||||
|
%if %{with nm_cloud_setup}
|
||||||
|
%files cloud-setup
|
||||||
|
%{_libexecdir}/nm-cloud-setup
|
||||||
|
%{systemd_dir}/nm-cloud-setup.service
|
||||||
|
%{systemd_dir}/nm-cloud-setup.timer
|
||||||
|
%{nmlibdir}/dispatcher.d/90-nm-cloud-setup.sh
|
||||||
|
%{nmlibdir}/dispatcher.d/no-wait.d/90-nm-cloud-setup.sh
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
__CHANGELOG__
|
__CHANGELOG__
|
||||||
|
@@ -154,6 +154,7 @@ if [[ $NO_DIST != 1 ]]; then
|
|||||||
--with-config-logging-backend-default=syslog \
|
--with-config-logging-backend-default=syslog \
|
||||||
--with-libaudit=yes-disabled-by-default \
|
--with-libaudit=yes-disabled-by-default \
|
||||||
--enable-polkit=yes \
|
--enable-polkit=yes \
|
||||||
|
--with-nm-cloud-setup=yes \
|
||||||
--with-config-dhcp-default=internal \
|
--with-config-dhcp-default=internal \
|
||||||
--with-config-dns-rc-manager-default=symlink \
|
--with-config-dns-rc-manager-default=symlink \
|
||||||
|| die "Error autogen.sh"
|
|| die "Error autogen.sh"
|
||||||
|
11
meson.build
11
meson.build
@@ -661,9 +661,10 @@ if enable_libpsl
|
|||||||
endif
|
endif
|
||||||
config_h.set10('WITH_LIBPSL', enable_libpsl)
|
config_h.set10('WITH_LIBPSL', enable_libpsl)
|
||||||
|
|
||||||
|
libcurl_dep = dependency('libcurl', version: '>= 7.24.0', required: false)
|
||||||
|
|
||||||
enable_concheck = get_option('concheck')
|
enable_concheck = get_option('concheck')
|
||||||
if enable_concheck
|
if enable_concheck
|
||||||
libcurl_dep = dependency('libcurl', version: '>= 7.24.0', required: false)
|
|
||||||
assert(libcurl_dep.found(), 'concheck requires libcurl library. Use -Dconcheck=false to disable it')
|
assert(libcurl_dep.found(), 'concheck requires libcurl library. Use -Dconcheck=false to disable it')
|
||||||
endif
|
endif
|
||||||
config_h.set10('WITH_CONCHECK', enable_concheck)
|
config_h.set10('WITH_CONCHECK', enable_concheck)
|
||||||
@@ -694,6 +695,11 @@ if enable_nmtui
|
|||||||
assert(newt_dep.found(), 'You must have libnewt installed to build nmtui. Use -Dnmtui=false to disable it')
|
assert(newt_dep.found(), 'You must have libnewt installed to build nmtui. Use -Dnmtui=false to disable it')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
enable_nm_cloud_setup = get_option('nm_cloud_setup')
|
||||||
|
if enable_nm_cloud_setup
|
||||||
|
assert(libcurl_dep.found(), 'nm-cloud-setup requires libcurl library. Use -Dnm_cloud_setup=false to disable it')
|
||||||
|
endif
|
||||||
|
|
||||||
more_asserts = get_option('more_asserts')
|
more_asserts = get_option('more_asserts')
|
||||||
if more_asserts == 'no'
|
if more_asserts == 'no'
|
||||||
more_asserts = 0
|
more_asserts = 0
|
||||||
@@ -910,6 +916,8 @@ meson.add_install_script(
|
|||||||
nm_sysconfdir,
|
nm_sysconfdir,
|
||||||
enable_docs ? '1' : '0',
|
enable_docs ? '1' : '0',
|
||||||
enable_ifcfg_rh ? '1' : '0',
|
enable_ifcfg_rh ? '1' : '0',
|
||||||
|
enable_nm_cloud_setup ? '1' : '0',
|
||||||
|
install_systemdunitdir ? '1' : '0',
|
||||||
)
|
)
|
||||||
|
|
||||||
output = '\nSystem paths:\n'
|
output = '\nSystem paths:\n'
|
||||||
@@ -954,6 +962,7 @@ output += ' libteamdctl: ' + enable_teamdctl.to_string() + '\n'
|
|||||||
output += ' ovs: ' + enable_ovs.to_string() + '\n'
|
output += ' ovs: ' + enable_ovs.to_string() + '\n'
|
||||||
output += ' nmcli: ' + enable_nmcli.to_string() + '\n'
|
output += ' nmcli: ' + enable_nmcli.to_string() + '\n'
|
||||||
output += ' nmtui: ' + enable_nmtui.to_string() + '\n'
|
output += ' nmtui: ' + enable_nmtui.to_string() + '\n'
|
||||||
|
output += ' nm-cloud-setup: ' + enable_nm_cloud_setup.to_string() + '\n'
|
||||||
output += '\nConfiguration_plugins (main.plugins=' + config_plugins_default + ')\n'
|
output += '\nConfiguration_plugins (main.plugins=' + config_plugins_default + ')\n'
|
||||||
output += ' ifcfg-rh: ' + enable_ifcfg_rh.to_string() + '\n'
|
output += ' ifcfg-rh: ' + enable_ifcfg_rh.to_string() + '\n'
|
||||||
output += ' ifupdown: ' + enable_ifupdown.to_string() + '\n'
|
output += ' ifupdown: ' + enable_ifupdown.to_string() + '\n'
|
||||||
|
@@ -36,6 +36,7 @@ option('teamdctl', type: 'boolean', value: false, description: 'enable Teamd con
|
|||||||
option('ovs', type: 'boolean', value: true, description: 'enable Open vSwitch support')
|
option('ovs', type: 'boolean', value: true, description: 'enable Open vSwitch support')
|
||||||
option('nmcli', type: 'boolean', value: true, description: 'Build nmcli')
|
option('nmcli', type: 'boolean', value: true, description: 'Build nmcli')
|
||||||
option('nmtui', type: 'boolean', value: true, description: 'Build nmtui')
|
option('nmtui', type: 'boolean', value: true, description: 'Build nmtui')
|
||||||
|
option('nm_cloud_setup', type: 'boolean', value: false, description: 'Build nm_cloud_setup')
|
||||||
option('bluez5_dun', type: 'boolean', value: false, description: 'enable Bluez5 DUN support')
|
option('bluez5_dun', type: 'boolean', value: false, description: 'enable Bluez5 DUN support')
|
||||||
option('ebpf', type: 'combo', choices : ['auto', 'true', 'false'], description: 'Enable eBPF support')
|
option('ebpf', type: 'combo', choices : ['auto', 'true', 'false'], description: 'Enable eBPF support')
|
||||||
|
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
|
clients/cloud-setup/nm-cloud-setup.service.in
|
||||||
|
contrib/fedora/rpm/
|
||||||
data/NetworkManager.service.in
|
data/NetworkManager.service.in
|
||||||
|
data/org.freedesktop.NetworkManager.policy.in
|
||||||
examples/python/NetworkManager.py
|
examples/python/NetworkManager.py
|
||||||
examples/python/systray/eggtrayicon.c
|
examples/python/systray/eggtrayicon.c
|
||||||
data/org.freedesktop.NetworkManager.policy.in
|
shared/nm-utils/nm-vpn-editor-plugin-call.h
|
||||||
|
shared/nm-utils/nm-vpn-plugin-utils.c
|
||||||
vpn-daemons/openvpn
|
vpn-daemons/openvpn
|
||||||
vpn-daemons/pptp
|
vpn-daemons/pptp
|
||||||
vpn-daemons/vpnc
|
vpn-daemons/vpnc
|
||||||
contrib/fedora/rpm/
|
|
||||||
shared/nm-utils/nm-vpn-editor-plugin-call.h
|
|
||||||
shared/nm-utils/nm-vpn-plugin-utils.c
|
|
||||||
# https://bugs.launchpad.net/intltool/+bug/1117944
|
# https://bugs.launchpad.net/intltool/+bug/1117944
|
||||||
sub/data/org.freedesktop.NetworkManager.policy.in
|
sub/data/org.freedesktop.NetworkManager.policy.in
|
||||||
|
|
||||||
|
@@ -9,6 +9,8 @@ nm_mandir="$6"
|
|||||||
nm_sysconfdir="$7"
|
nm_sysconfdir="$7"
|
||||||
enable_docs="$8"
|
enable_docs="$8"
|
||||||
enable_ifcfg_rh="$9"
|
enable_ifcfg_rh="$9"
|
||||||
|
enable_nm_cloud_setup="${10}"
|
||||||
|
install_systemdunitdir="${11}"
|
||||||
|
|
||||||
[ -n "$DESTDIR" ] && DESTDIR="${DESTDIR%%/}/"
|
[ -n "$DESTDIR" ] && DESTDIR="${DESTDIR%%/}/"
|
||||||
|
|
||||||
@@ -55,3 +57,8 @@ fi
|
|||||||
if [ "$enable_ifcfg_rh" = 1 ]; then
|
if [ "$enable_ifcfg_rh" = 1 ]; then
|
||||||
mkdir -p "${DESTDIR}${nm_sysconfdir}/sysconfig/network-scripts"
|
mkdir -p "${DESTDIR}${nm_sysconfdir}/sysconfig/network-scripts"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$enable_nm_cloud_setup" = 1 -a "$install_systemdunitdir" = 1 ]; then
|
||||||
|
ln -s 'no-wait.d/90-nm-cloud-setup.sh' "${DESTDIR}${nm_pkglibdir}/dispatcher.d/90-nm-cloud-setup.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user