mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-01 13:55:36 +00:00
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:
parent
2b6f5a305c
commit
69f048bf0c
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -65,6 +65,8 @@ test-*.trs
|
|||
/dispatcher/tests/test-dispatcher-envp
|
||||
|
||||
/clients/cli/nmcli
|
||||
/clients/cloud-setup/nm-cloud-setup
|
||||
/clients/cloud-setup/nm-cloud-setup.service
|
||||
/clients/common/settings-docs.h
|
||||
/clients/common/tests/test-clients-common
|
||||
/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/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
|
||||
###############################################################################
|
||||
|
|
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
|
||||
implementation more efficient. NMClient should work now well with a separate
|
||||
GMainContext.
|
||||
* nm-cloud-setup: add new tool for automatically configuring NetworkManager
|
||||
in cloud. Currently only EC2 and IPv4 is supported.
|
||||
|
||||
=============================================
|
||||
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
|
||||
subdir('tui')
|
||||
endif
|
||||
|
||||
if enable_nm_cloud_setup
|
||||
subdir('cloud-setup')
|
||||
endif
|
||||
|
|
12
configure.ac
12
configure.ac
|
@ -1014,6 +1014,17 @@ else
|
|||
fi
|
||||
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,
|
||||
AS_HELP_STRING([--with-nmtui=yes|no], [Build nmtui]))
|
||||
if test "$with_nmtui" != no; then
|
||||
|
@ -1303,6 +1314,7 @@ echo " libteamdctl: $enable_teamdctl"
|
|||
echo " ovs: $enable_ovs"
|
||||
echo " nmcli: $build_nmcli"
|
||||
echo " nmtui: $build_nmtui"
|
||||
echo " nm-cloud-setup: $with_nm_cloud_setup"
|
||||
echo " iwd: $ac_with_iwd"
|
||||
echo
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
|
||||
%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
|
||||
|
@ -53,6 +55,7 @@
|
|||
%bcond_without ovs
|
||||
%bcond_without ppp
|
||||
%bcond_without nmtui
|
||||
%bcond_without nm_cloud_setup
|
||||
%bcond_without regen_docs
|
||||
%bcond_with debug
|
||||
%bcond_with test
|
||||
|
@ -480,6 +483,19 @@ by nm-connection-editor and nm-applet in a non-graphical environment.
|
|||
%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
|
||||
%autosetup -p1 -n NetworkManager-%{real_version}
|
||||
|
||||
|
@ -538,6 +554,11 @@ by nm-connection-editor and nm-applet in a non-graphical environment.
|
|||
-Dnmtui=true \
|
||||
%else
|
||||
-Dnmtui=false \
|
||||
%endif
|
||||
%if %{with nm_cloud_setup}
|
||||
-Dnm_cloud_setup=true \
|
||||
%else
|
||||
-Dnm_cloud_setup=false \
|
||||
%endif
|
||||
-Dvapi=true \
|
||||
-Dintrospection=true \
|
||||
|
@ -659,6 +680,11 @@ intltoolize --automake --copy --force
|
|||
--with-nmtui=yes \
|
||||
%else
|
||||
--with-nmtui=no \
|
||||
%endif
|
||||
%if %{with nm_cloud_setup}
|
||||
--with-nm-cloud-setup=yes \
|
||||
%else
|
||||
--with-nm-cloud-setup=no \
|
||||
%endif
|
||||
--enable-vala=yes \
|
||||
--enable-introspection \
|
||||
|
@ -801,6 +827,12 @@ else
|
|||
fi
|
||||
|
||||
|
||||
%if %{with nm_cloud_setup}
|
||||
%post cloud-setup
|
||||
%systemd_post %{systemd_units_cloud_setup}
|
||||
%endif
|
||||
|
||||
|
||||
%preun
|
||||
if [ $1 -eq 0 ]; then
|
||||
# Package removal, not upgrade
|
||||
|
@ -814,6 +846,12 @@ fi
|
|||
%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
|
||||
/usr/bin/udevadm control --reload-rules || :
|
||||
/usr/bin/udevadm trigger --subsystem-match=net || :
|
||||
|
@ -827,6 +865,12 @@ fi
|
|||
%endif
|
||||
|
||||
|
||||
%if %{with nm_cloud_setup}
|
||||
%postun cloud-setup
|
||||
%systemd_postun %{systemd_units_cloud_setup}
|
||||
%endif
|
||||
|
||||
|
||||
%files
|
||||
%{dbus_sys_dir}/org.freedesktop.NetworkManager.conf
|
||||
%{dbus_sys_dir}/nm-dispatcher.conf
|
||||
|
@ -995,5 +1039,15 @@ fi
|
|||
%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__
|
||||
|
|
|
@ -154,6 +154,7 @@ if [[ $NO_DIST != 1 ]]; then
|
|||
--with-config-logging-backend-default=syslog \
|
||||
--with-libaudit=yes-disabled-by-default \
|
||||
--enable-polkit=yes \
|
||||
--with-nm-cloud-setup=yes \
|
||||
--with-config-dhcp-default=internal \
|
||||
--with-config-dns-rc-manager-default=symlink \
|
||||
|| die "Error autogen.sh"
|
||||
|
|
11
meson.build
11
meson.build
|
@ -661,9 +661,10 @@ if enable_libpsl
|
|||
endif
|
||||
config_h.set10('WITH_LIBPSL', enable_libpsl)
|
||||
|
||||
libcurl_dep = dependency('libcurl', version: '>= 7.24.0', required: false)
|
||||
|
||||
enable_concheck = get_option('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')
|
||||
endif
|
||||
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')
|
||||
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')
|
||||
if more_asserts == 'no'
|
||||
more_asserts = 0
|
||||
|
@ -910,6 +916,8 @@ meson.add_install_script(
|
|||
nm_sysconfdir,
|
||||
enable_docs ? '1' : '0',
|
||||
enable_ifcfg_rh ? '1' : '0',
|
||||
enable_nm_cloud_setup ? '1' : '0',
|
||||
install_systemdunitdir ? '1' : '0',
|
||||
)
|
||||
|
||||
output = '\nSystem paths:\n'
|
||||
|
@ -954,6 +962,7 @@ output += ' libteamdctl: ' + enable_teamdctl.to_string() + '\n'
|
|||
output += ' ovs: ' + enable_ovs.to_string() + '\n'
|
||||
output += ' nmcli: ' + enable_nmcli.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 += ' ifcfg-rh: ' + enable_ifcfg_rh.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('nmcli', type: 'boolean', value: true, description: 'Build nmcli')
|
||||
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('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/org.freedesktop.NetworkManager.policy.in
|
||||
examples/python/NetworkManager.py
|
||||
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/pptp
|
||||
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
|
||||
sub/data/org.freedesktop.NetworkManager.policy.in
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ nm_mandir="$6"
|
|||
nm_sysconfdir="$7"
|
||||
enable_docs="$8"
|
||||
enable_ifcfg_rh="$9"
|
||||
enable_nm_cloud_setup="${10}"
|
||||
install_systemdunitdir="${11}"
|
||||
|
||||
[ -n "$DESTDIR" ] && DESTDIR="${DESTDIR%%/}/"
|
||||
|
||||
|
@ -55,3 +57,8 @@ fi
|
|||
if [ "$enable_ifcfg_rh" = 1 ]; then
|
||||
mkdir -p "${DESTDIR}${nm_sysconfdir}/sysconfig/network-scripts"
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in a new issue