diff options
Diffstat (limited to 'src/nm-cloud-setup')
| -rwxr-xr-x | src/nm-cloud-setup/90-nm-cloud-setup.sh | 16 | ||||
| -rw-r--r-- | src/nm-cloud-setup/main.c | 183 | ||||
| -rw-r--r-- | src/nm-cloud-setup/meson.build | 2 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nm-cloud-setup-utils.c | 252 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nm-cloud-setup-utils.h | 73 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nm-cloud-setup.service.in | 1 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nm-http-client.c | 34 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider-aliyun.c | 24 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider-azure.c | 6 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider-ec2.c | 24 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider-gcp.c | 29 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider.c | 4 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider.h | 7 |
13 files changed, 258 insertions, 397 deletions
diff --git a/src/nm-cloud-setup/90-nm-cloud-setup.sh b/src/nm-cloud-setup/90-nm-cloud-setup.sh index 817e2555..ae55b725 100755 --- a/src/nm-cloud-setup/90-nm-cloud-setup.sh +++ b/src/nm-cloud-setup/90-nm-cloud-setup.sh @@ -1,9 +1,17 @@ #!/bin/sh case "$2" in - up|dhcp4-change) - if systemctl -q is-enabled nm-cloud-setup.service ; then - exec systemctl --no-block restart nm-cloud-setup.service - fi + pre-up) + NO_BLOCK= ;; + dhcp4-change) + NO_BLOCK=--no-block + ;; + *) + exit 0 + ;; esac + +if systemctl -q is-enabled nm-cloud-setup.service ; then + exec systemctl $NO_BLOCK restart nm-cloud-setup.service +fi diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c index 630f26ef..084b5f62 100644 --- a/src/nm-cloud-setup/main.c +++ b/src/nm-cloud-setup/main.c @@ -22,10 +22,12 @@ typedef struct { } SigTermData; typedef struct { + SigTermData *sigterm_data; GMainLoop *main_loop; GCancellable *cancellable; NMCSProvider *provider_result; guint detect_count; + gboolean any_provider_enabled; } ProviderDetectData; static void @@ -33,38 +35,45 @@ _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; + ProviderDetectData *dd = user_data; gboolean success; + nm_assert(dd->detect_count > 0); + dd->detect_count--; + success = nmcs_provider_detect_finish(provider, result, &error); nm_assert(success != (!!error)); - if (nm_utils_error_is_cancelled(error)) - return; - - dd = user_data; - - nm_assert(dd->detect_count > 0); - dd->detect_count--; - + if (nm_utils_error_is_cancelled(error)) { + _LOGD("provider %s detection cancelled", nmcs_provider_get_name(provider)); + goto out; + } if (error) { + if (nm_g_error_matches(error, NM_UTILS_ERROR, NM_UTILS_ERROR_NOT_READY)) { + /* This error tells us, that the provider was not enabled in configuration. */ + } else + dd->any_provider_enabled = TRUE; _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; + goto out; } _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); + +out: + if (dd->detect_count == 0) { + if (!dd->provider_result) { + NMLogLevel level = LOGL_INFO; + + if (dd->any_provider_enabled && !dd->sigterm_data->signal_received) + level = LOGL_WARN; + + _NMLOG(level, "no provider detected"); + } + g_main_loop_quit(dd->main_loop); + } } static void @@ -74,21 +83,21 @@ _provider_detect_sigterm_cb(GCancellable *source, gpointer 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) +_provider_detect(SigTermData *sigterm_data) { 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, + .sigterm_data = sigterm_data, + .cancellable = cancellable, + .main_loop = main_loop, + .detect_count = 0, + .provider_result = NULL, + .any_provider_enabled = FALSE, }; const GType gtypes[] = { NMCS_TYPE_PROVIDER_EC2, @@ -99,7 +108,7 @@ _provider_detect(GCancellable *sigterm_cancellable) int i; gulong cancellable_signal_id; - cancellable_signal_id = g_cancellable_connect(sigterm_cancellable, + cancellable_signal_id = g_cancellable_connect(sigterm_data->cancellable, G_CALLBACK(_provider_detect_sigterm_cb), &dd, NULL); @@ -123,12 +132,113 @@ _provider_detect(GCancellable *sigterm_cancellable) g_main_loop_run(main_loop); out: - nm_clear_g_signal_handler(sigterm_cancellable, &cancellable_signal_id); + nm_clear_g_signal_handler(sigterm_data->cancellable, &cancellable_signal_id); return dd.provider_result; } /*****************************************************************************/ +static NMUtilsNamedValue * +_map_interfaces_parse(void) +{ + gs_free const char **split = NULL; + NMUtilsNamedValue *map_interfaces; + const char *env_var; + gsize i; + gsize j; + gsize alloc_len; + + env_var = g_getenv(NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES); + + if (nm_str_is_empty(env_var)) + return NULL; + + split = nm_strsplit_set_full(env_var, ";", NM_STRSPLIT_SET_FLAGS_STRSTRIP); + + alloc_len = NM_PTRARRAY_LEN(split) + 1u; + + map_interfaces = g_new(NMUtilsNamedValue, alloc_len); + + _LOGD("test: map interfaces via NM_CLOUD_SETUP_MAP_INTERFACES=\"%s\"", env_var); + + for (i = 0, j = 0; split && split[i]; i++) { + NMUtilsNamedValue *m; + const char *str = split[i]; + char *hwaddr; + const char *s; + + s = strchr(str, '='); + if (!s || str == s) + continue; + + hwaddr = nmcs_utils_hwaddr_normalize(&s[1], -1); + if (!hwaddr) + continue; + + nm_assert(j < alloc_len); + m = &map_interfaces[j++]; + + *m = (NMUtilsNamedValue){ + .name = g_strndup(str, s - str), + .value_str = hwaddr, + }; + + _LOGD("test: map \"%s\" -> %s", m->name, m->value_str); + } + + nm_assert(j < alloc_len); + map_interfaces[j++] = (NMUtilsNamedValue){ + .name = NULL, + .value_str = NULL, + }; + + return g_steal_pointer(&map_interfaces); +} + +static const char * +_device_get_hwaddr(NMDeviceEthernet *device) +{ + static const NMUtilsNamedValue *gl_map_interfaces_map = NULL; + static gsize gl_initialized = 0; + const NMUtilsNamedValue *map = NULL; + + nm_assert(NM_IS_DEVICE_ETHERNET(device)); + + /* Network interfaces in cloud environments are identified by their permanent + * MAC address. + * + * For testing, we can set NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES + * to a ';' separate list of "$INTERFACE=$HWADDR", which means that we + * pretend that device with ip-interface "$INTERFACE" has the specified permanent + * MAC address. */ + + if (g_once_init_enter(&gl_initialized)) { + gl_map_interfaces_map = _map_interfaces_parse(); + g_once_init_leave(&gl_initialized, 1); + } + + map = gl_map_interfaces_map; + if (G_UNLIKELY(map)) { + const char *const iface = nm_device_get_iface(NM_DEVICE(device)); + + /* For testing, the device<->hwaddr is remapped and the actual permanent + * MAC address of the device ignored. This mapping is configured via + * NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES environment variable. */ + + if (!iface) + return NULL; + + for (; map->name; map++) { + if (nm_streq(map->name, iface)) + return map->value_str; + } + + return NULL; + } + + return nm_device_ethernet_get_permanent_hw_address(device); +} + static char ** _nmc_get_hwaddrs(NMClient *nmc) { @@ -151,7 +261,7 @@ _nmc_get_hwaddrs(NMClient *nmc) if (nm_device_get_state(device) < NM_DEVICE_STATE_UNAVAILABLE) continue; - hwaddr = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device)); + hwaddr = _device_get_hwaddr(NM_DEVICE_ETHERNET(device)); if (!hwaddr) continue; @@ -193,7 +303,7 @@ _nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr) if (!NM_IS_DEVICE_ETHERNET(device)) continue; - hwaddr_dev = nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device)); + hwaddr_dev = _device_get_hwaddr(NM_DEVICE_ETHERNET(device)); if (!hwaddr_dev) continue; @@ -321,8 +431,9 @@ _nmc_mangle_connection(NMDevice *device, addrs_new = g_ptr_array_new_full(config_data->ipv4s_len, (GDestroyNotify) nm_ip_address_unref); rules_new = g_ptr_array_new_full(config_data->ipv4s_len, (GDestroyNotify) nm_ip_routing_rule_unref); - routes_new = g_ptr_array_new_full(config_data->iproutes_len + !!config_data->ipv4s_len, - (GDestroyNotify) nm_ip_route_unref); + routes_new = + g_ptr_array_new_full(nm_g_ptr_array_len(config_data->iproutes) + !!config_data->ipv4s_len, + (GDestroyNotify) nm_ip_route_unref); if (remote_s_ip) { guint len; @@ -428,8 +539,8 @@ _nmc_mangle_connection(NMDevice *device, } } - for (i = 0; i < config_data->iproutes_len; ++i) - g_ptr_array_add(routes_new, config_data->iproutes_arr[i]); + for (i = 0; i < nm_g_ptr_array_len(config_data->iproutes); i++) + g_ptr_array_add(routes_new, _nm_ip_route_ref(config_data->iproutes->pdata[i])); addrs_changed = nmcs_setting_ip_replace_ipv4_addresses(s_ip, (NMIPAddress **) addrs_new->pdata, @@ -644,7 +755,7 @@ main(int argc, const char *const *argv) gs_free_error GError *error = NULL; SigTermData sigterm_data; - _nm_logging_enabled_init(g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_LOG"))); + _nm_logging_enabled_init(g_getenv(NMCS_ENV_NM_CLOUD_SETUP_LOG)); _LOGD("nm-cloud-setup %s starting...", NM_DIST_VERSION); @@ -662,7 +773,7 @@ main(int argc, const char *const *argv) }; sigterm_source = nm_g_unix_signal_add_source(SIGTERM, sigterm_handler, &sigterm_data); - provider = _provider_detect(sigterm_cancellable); + provider = _provider_detect(&sigterm_data); if (!provider) goto done; diff --git a/src/nm-cloud-setup/meson.build b/src/nm-cloud-setup/meson.build index ea4ad113..b1269ec6 100644 --- a/src/nm-cloud-setup/meson.build +++ b/src/nm-cloud-setup/meson.build @@ -16,7 +16,7 @@ if install_systemdunitdir install_data( '90-nm-cloud-setup.sh', - install_dir: join_paths(nm_pkglibdir, 'dispatcher.d', 'no-wait.d'), + install_dir: join_paths(nm_pkglibdir, 'dispatcher.d', 'pre-up.d'), ) endif diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.c b/src/nm-cloud-setup/nm-cloud-setup-utils.c index fb4883b7..75739c7c 100644 --- a/src/nm-cloud-setup/nm-cloud-setup-utils.c +++ b/src/nm-cloud-setup/nm-cloud-setup-utils.c @@ -166,258 +166,6 @@ nmcs_wait_for_objects_iterate_until_done(GMainContext *context, int timeout_msec /*****************************************************************************/ -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, 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, TRUE); - - 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)) { - /* we already handle this differently. Nothing to do. */ - return; - } - - if (error || is_finished) { - _poll_return(poll_task_data, g_steal_pointer(&error)); - return; - } - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - 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_msec(); - - 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, nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "timeout expired")); - return G_SOURCE_CONTINUE; -} - -static void -_poll_cancelled_cb(GObject *object, gpointer user_data) -{ - PollTaskData *poll_task_data = user_data; - GError *error = NULL; - - 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, 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 (and 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 (asynchronous) 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 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) -{ - 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_IDLE, _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 with error set. - * Error is always set if (and only if) the function returns %FALSE. - */ -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) { diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.h b/src/nm-cloud-setup/nm-cloud-setup-utils.h index fed0f4b6..4ca4634c 100644 --- a/src/nm-cloud-setup/nm-cloud-setup-utils.h +++ b/src/nm-cloud-setup/nm-cloud-setup-utils.h @@ -7,9 +7,20 @@ /*****************************************************************************/ -/* mark names for variables that can be used as configuration. Search - * for NMCS_ENV_VARIABLE() to find all honored environment variables. */ -#define NMCS_ENV_VARIABLE(var) "" var "" +/* Environment variables for configuring nm-cloud-setup */ +#define NMCS_ENV_NM_CLOUD_SETUP_ALIYUN "NM_CLOUD_SETUP_ALIYUN" +#define NMCS_ENV_NM_CLOUD_SETUP_AZURE "NM_CLOUD_SETUP_AZURE" +#define NMCS_ENV_NM_CLOUD_SETUP_EC2 "NM_CLOUD_SETUP_EC2" +#define NMCS_ENV_NM_CLOUD_SETUP_GCP "NM_CLOUD_SETUP_GCP" +#define NMCS_ENV_NM_CLOUD_SETUP_LOG "NM_CLOUD_SETUP_LOG" + +/* Undocumented/internal environment variables for configuring nm-cloud-setup. + * These are mainly for testing/debugging. */ +#define NMCS_ENV_NM_CLOUD_SETUP_ALIYUN_HOST "NM_CLOUD_SETUP_ALIYUN_HOST" +#define NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST "NM_CLOUD_SETUP_AZURE_HOST" +#define NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST "NM_CLOUD_SETUP_EC2_HOST" +#define NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST "NM_CLOUD_SETUP_GCP_HOST" +#define NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES "NM_CLOUD_SETUP_MAP_INTERFACES" /*****************************************************************************/ @@ -40,30 +51,6 @@ gboolean nmcs_wait_for_objects_iterate_until_done(GMainContext *context, int tim /*****************************************************************************/ -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); static inline char * @@ -104,6 +91,38 @@ const char *nmcs_utils_parse_get_full_line(GBytes *mem, const char *needle); /*****************************************************************************/ +#define NMCS_DEFINE_HOST_BASE(base_fcn, nmcs_env_host, default_host) \ + static const char *base_fcn(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 mainly for testing, it's not usually supposed to be configured. \ + * Consider this private API! */ \ + base = g_getenv("" nmcs_env_host ""); \ + base = nmcs_utils_uri_complete_interned(base) ?: ("" default_host ""); \ + \ + if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) \ + goto again; \ + \ + if (!nm_streq(base, ("" default_host ""))) { \ + _LOGD("test: mock %s=\"%s\" (default \"%s\")", \ + "" nmcs_env_host "", \ + base, \ + "" default_host ""); \ + } \ + } \ + \ + return base; \ + } \ + _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON + +/*****************************************************************************/ + char *nmcs_utils_uri_build_concat_v(const char *base, const char **components, gsize n_components); #define nmcs_utils_uri_build_concat(base, ...) \ diff --git a/src/nm-cloud-setup/nm-cloud-setup.service.in b/src/nm-cloud-setup/nm-cloud-setup.service.in index f4b0e263..a2b4581f 100644 --- a/src/nm-cloud-setup/nm-cloud-setup.service.in +++ b/src/nm-cloud-setup/nm-cloud-setup.service.in @@ -1,6 +1,7 @@ [Unit] Description=Automatically configure NetworkManager in cloud Documentation=man:nm-cloud-setup(8) +Before=network-online.target After=NetworkManager.service [Service] diff --git a/src/nm-cloud-setup/nm-http-client.c b/src/nm-cloud-setup/nm-http-client.c index db123f10..20ef6473 100644 --- a/src/nm-cloud-setup/nm-http-client.c +++ b/src/nm-cloud-setup/nm-http-client.c @@ -359,8 +359,8 @@ nm_http_client_req(NMHttpClient *self, * nm_http_client_req_finish: * @self: the #NMHttpClient instance * @result: the #GAsyncResult which to complete. - * @out_response_code: (allow-none) (out): the HTTP response code or -1 on other error. - * @out_response_data: (allow-none) (transfer full): the HTTP response data, if any. + * @out_response_code: (out) (optional): the HTTP response code or -1 on other error. + * @out_response_data: (out) (optional) (nullable) (transfer full): the HTTP response data, if any. * The GBytes buffer is guaranteed to have a trailing NUL character *after* the * returned buffer size. That means, you can always trust that the buffer is NUL terminated * and that there is one additional hidden byte after the data. @@ -430,6 +430,12 @@ _poll_req_data_free(gpointer data) } static void +_poll_reg_probe_register_object_fcn(GObject *object, gpointer user_data) +{ + nmcs_wait_for_objects_register(object); +} + +static void _poll_req_probe_start_fcn(GCancellable *cancellable, gpointer probe_user_data, GAsyncReadyCallback callback, @@ -508,13 +514,14 @@ _poll_req_probe_finish_fcn(GObject *source, static void _poll_req_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) { - PollReqData *poll_req_data = user_data; + PollReqData *poll_req_data = NULL; gs_free_error GError *error = NULL; gboolean success; - success = nmcs_utils_poll_finish(result, NULL, &error); + success = nm_utils_poll_finish(result, (gpointer *) &poll_req_data, &error); nm_assert((!!success) == (!error)); + nm_assert(poll_req_data); if (error) g_task_return_error(poll_req_data->task, g_steal_pointer(&error)); @@ -575,15 +582,16 @@ nm_http_client_poll_req(NMHttpClient *self, 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_req_probe_start_fcn, - _poll_req_probe_finish_fcn, - poll_req_data, - cancellable, - _poll_req_done_cb, - poll_req_data); + nm_utils_poll(poll_timeout_ms, + ratelimit_timeout_ms, + 0, + _poll_reg_probe_register_object_fcn, + _poll_req_probe_start_fcn, + _poll_req_probe_finish_fcn, + poll_req_data, + cancellable, + _poll_req_done_cb, + NULL); } gboolean diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.c b/src/nm-cloud-setup/nmcs-provider-aliyun.c index 7f51f431..20010a2c 100644 --- a/src/nm-cloud-setup/nmcs-provider-aliyun.c +++ b/src/nm-cloud-setup/nmcs-provider-aliyun.c @@ -18,27 +18,7 @@ #define NM_ALIYUN_METADATA_URL_BASE /* $NM_ALIYUN_BASE/$NM_ALIYUN_API_VERSION */ \ "/meta-data/network/interfaces/macs/" -static const char * -_aliyun_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 mainly for testing, it's not usually supposed to be configured. - * Consider this private API! */ - base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN_HOST")); - base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_ALIYUN_HOST); - - if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) - goto again; - } - - return base; -} +NMCS_DEFINE_HOST_BASE(_aliyun_base, NMCS_ENV_NM_CLOUD_SETUP_ALIYUN_HOST, NM_ALIYUN_HOST); #define _aliyun_uri_concat(...) nmcs_utils_uri_build_concat(_aliyun_base(), __VA_ARGS__) #define _aliyun_uri_interfaces(...) \ @@ -558,7 +538,7 @@ nmcs_provider_aliyun_class_init(NMCSProviderAliyunClass *klass) NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass); provider_class->_name = "aliyun"; - provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_ALIYUN"); + provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_ALIYUN; provider_class->detect = detect; provider_class->get_config = get_config; } diff --git a/src/nm-cloud-setup/nmcs-provider-azure.c b/src/nm-cloud-setup/nmcs-provider-azure.c index 69946f5c..771c43d9 100644 --- a/src/nm-cloud-setup/nmcs-provider-azure.c +++ b/src/nm-cloud-setup/nmcs-provider-azure.c @@ -17,8 +17,10 @@ #define NM_AZURE_METADATA_URL_BASE /* $NM_AZURE_BASE/$NM_AZURE_API_VERSION */ \ "/metadata/instance/network/interface/" +NMCS_DEFINE_HOST_BASE(_azure_base, NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST, NM_AZURE_BASE); + #define _azure_uri_concat(...) \ - nmcs_utils_uri_build_concat(NM_AZURE_BASE, __VA_ARGS__, NM_AZURE_API_VERSION) + nmcs_utils_uri_build_concat(_azure_base(), __VA_ARGS__, NM_AZURE_API_VERSION) #define _azure_uri_interfaces(...) _azure_uri_concat(NM_AZURE_METADATA_URL_BASE, ##__VA_ARGS__) /*****************************************************************************/ @@ -564,7 +566,7 @@ nmcs_provider_azure_class_init(NMCSProviderAzureClass *klass) NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass); provider_class->_name = "azure"; - provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_AZURE"); + provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_AZURE; provider_class->detect = detect; provider_class->get_config = get_config; } diff --git a/src/nm-cloud-setup/nmcs-provider-ec2.c b/src/nm-cloud-setup/nmcs-provider-ec2.c index 65a8f629..9cdc7a0c 100644 --- a/src/nm-cloud-setup/nmcs-provider-ec2.c +++ b/src/nm-cloud-setup/nmcs-provider-ec2.c @@ -21,27 +21,7 @@ #define NM_EC2_TOKEN_TTL_HEADER "X-aws-ec2-metadata-token-ttl-seconds: 180" #define NM_EC2_TOKEN_HEADER "X-aws-ec2-metadata-token: " -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 mainly for testing, it's not usually supposed to be configured. - * Consider this private API! */ - base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2_HOST")); - base = nmcs_utils_uri_complete_interned(base) ?: ("" NM_EC2_BASE); - - if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base)) - goto again; - } - - return base; -} +NMCS_DEFINE_HOST_BASE(_ec2_base, NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST, NM_EC2_BASE); #define _ec2_uri_concat(...) nmcs_utils_uri_build_concat(_ec2_base(), __VA_ARGS__) #define _ec2_uri_interfaces(...) \ @@ -435,7 +415,7 @@ nmcs_provider_ec2_class_init(NMCSProviderEC2Class *klass) object_class->dispose = dispose; provider_class->_name = "ec2"; - provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2"); + provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_EC2; provider_class->detect = detect; provider_class->get_config = get_config; } diff --git a/src/nm-cloud-setup/nmcs-provider-gcp.c b/src/nm-cloud-setup/nmcs-provider-gcp.c index ca354865..4d9ef965 100644 --- a/src/nm-cloud-setup/nmcs-provider-gcp.c +++ b/src/nm-cloud-setup/nmcs-provider-gcp.c @@ -13,15 +13,19 @@ #define HTTP_POLL_TIMEOUT_MS 10000 #define HTTP_RATE_LIMIT_MS 1000 -#define NM_GCP_HOST "metadata.google.internal" -#define NM_GCP_BASE "http://" NM_GCP_HOST -#define NM_GCP_API_VERSION "/v1" -#define NM_GCP_METADATA_URL_BASE NM_GCP_BASE "/computeMetadata" NM_GCP_API_VERSION "/instance" -#define NM_GCP_METADATA_URL_NET "/network-interfaces/" +#define NM_GCP_HOST "metadata.google.internal" +#define NM_GCP_BASE "http://" NM_GCP_HOST +#define NM_GCP_API_VERSION "/v1" +#define NM_GCP_METADATA_URL_NET "/network-interfaces/" #define NM_GCP_METADATA_HEADER "Metadata-Flavor: Google" -#define _gcp_uri_concat(...) nmcs_utils_uri_build_concat(NM_GCP_METADATA_URL_BASE, __VA_ARGS__) +NMCS_DEFINE_HOST_BASE(_gcp_base, NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST, NM_GCP_BASE); + +#define _gcp_uri_concat(...) \ + nmcs_utils_uri_build_concat(_gcp_base(), \ + "/computeMetadata" NM_GCP_API_VERSION "/instance", \ + __VA_ARGS__) #define _gcp_uri_interfaces(...) _gcp_uri_concat(NM_GCP_METADATA_URL_NET, ##__VA_ARGS__) /*****************************************************************************/ @@ -73,7 +77,7 @@ detect(NMCSProvider *provider, GTask *task) http_client = nmcs_provider_get_http_client(provider); nm_http_client_poll_req(http_client, - (uri = _gcp_uri_concat("id")), + (uri = _gcp_uri_concat("/id")), HTTP_TIMEOUT_MS, 256 * 1024, 7000, @@ -112,7 +116,6 @@ _get_config_fip_cb(GObject *source, GAsyncResult *result, gpointer user_data) GCPIfaceData *iface_data = user_data; gs_free_error GError *error = NULL; gs_free char *ipaddr = NULL; - NMIPRoute **routes_arr; NMIPRoute *route_new; nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error); @@ -137,15 +140,14 @@ _get_config_fip_cb(GObject *source, GAsyncResult *result, gpointer user_data) ipaddr); iface_get_config = iface_data->iface_get_config; - routes_arr = iface_get_config->iproutes_arr; route_new = nm_ip_route_new(AF_INET, ipaddr, 32, NULL, 100, &error); if (error) goto out_done; nm_ip_route_set_attribute(route_new, NM_IP_ROUTE_ATTRIBUTE_TYPE, g_variant_new_string("local")); - routes_arr[iface_get_config->iproutes_len] = route_new; - ++iface_get_config->iproutes_len; + + g_ptr_array_add(iface_get_config->iproutes, route_new); out_done: if (!error) { @@ -215,7 +217,8 @@ _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat goto out_error; } - iface_data->iface_get_config->iproutes_arr = g_new(NMIPRoute *, iface_data->n_fips_pending); + iface_data->iface_get_config->iproutes = + g_ptr_array_new_full(iface_data->n_fips_pending, (GDestroyNotify) nm_ip_route_unref); for (i = 0; i < uri_arr->len; ++i) { const char *str = uri_arr->pdata[i]; @@ -459,7 +462,7 @@ nmcs_provider_gcp_class_init(NMCSProviderGCPClass *klass) NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass); provider_class->_name = "GCP"; - provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_GCP"); + provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_GCP; provider_class->detect = detect; provider_class->get_config = get_config; } diff --git a/src/nm-cloud-setup/nmcs-provider.c b/src/nm-cloud-setup/nmcs-provider.c index fd9a61b8..5b4412b3 100644 --- a/src/nm-cloud-setup/nmcs-provider.c +++ b/src/nm-cloud-setup/nmcs-provider.c @@ -155,7 +155,7 @@ nmcs_provider_detect(NMCSProvider *self, env = g_getenv(NMCS_PROVIDER_GET_CLASS(self)->_env_provider_enabled); if (!_nm_utils_ascii_str_to_bool(env, FALSE)) { g_task_return_error(task, - nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "provider is disabled")); + nm_utils_error_new(NM_UTILS_ERROR_NOT_READY, "provider is disabled")); return; } @@ -216,7 +216,7 @@ _iface_data_free(gpointer data) NMCSProviderGetConfigIfaceData *iface_data = data; g_free(iface_data->ipv4s_arr); - g_free(iface_data->iproutes_arr); + nm_g_ptr_array_unref(iface_data->iproutes); g_free((char *) iface_data->hwaddr); nm_g_slice_free(iface_data); diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h index 09cdb414..9e5eeebe 100644 --- a/src/nm-cloud-setup/nmcs-provider.h +++ b/src/nm-cloud-setup/nmcs-provider.h @@ -34,8 +34,8 @@ typedef struct { bool has_cidr : 1; bool has_gateway : 1; - NMIPRoute **iproutes_arr; - gsize iproutes_len; + /* Array of NMIPRoute (must own/free the entries). */ + GPtrArray *iproutes; /* TRUE, if the configuration was requested via hwaddrs argument to * nmcs_provider_get_config(). */ @@ -59,7 +59,8 @@ 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_ipv4s && config_data->has_cidr) || config_data->iproutes_len); + && ((config_data->has_ipv4s && config_data->has_cidr) + || nm_g_ptr_array_len(config_data->iproutes) > 0); } /*****************************************************************************/ |