Blame clients/cloud-setup/nm-cloud-setup-utils.c

Packit Service b23acc
// SPDX-License-Identifier: LGPL-2.1+
Packit Service b23acc
Packit Service b23acc
#include "nm-default.h"
Packit Service b23acc
Packit Service b23acc
#include "nm-cloud-setup-utils.h"
Packit Service b23acc
Packit Service b23acc
#include "nm-glib-aux/nm-time-utils.h"
Packit Service b23acc
#include "nm-glib-aux/nm-logging-base.h"
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
volatile NMLogLevel _nm_logging_configured_level = LOGL_TRACE;
Packit Service b23acc
Packit Service b23acc
void
Packit Service b23acc
_nm_logging_enabled_init (const char *level_str)
Packit Service b23acc
{
Packit Service b23acc
	NMLogLevel level;
Packit Service b23acc
Packit Service b23acc
	if (!_nm_log_parse_level (level_str, &level))
Packit Service b23acc
		level = LOGL_WARN;
Packit Service b23acc
	else if (level == _LOGL_KEEP)
Packit Service b23acc
		level = LOGL_WARN;
Packit Service b23acc
Packit Service b23acc
	_nm_logging_configured_level = level;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
void
Packit Service b23acc
_nm_log_impl_cs (NMLogLevel level,
Packit Service b23acc
                 const char *fmt,
Packit Service b23acc
                 ...)
Packit Service b23acc
{
Packit Service b23acc
	gs_free char *msg = NULL;
Packit Service b23acc
	va_list ap;
Packit Service b23acc
	const char *level_str;
Packit Service b23acc
	gint64 ts;
Packit Service b23acc
Packit Service b23acc
	va_start (ap, fmt);
Packit Service b23acc
	msg = g_strdup_vprintf (fmt, ap);
Packit Service b23acc
	va_end (ap);
Packit Service b23acc
Packit Service b23acc
	switch (level) {
Packit Service b23acc
	case LOGL_TRACE: level_str = "<trace>"; break;
Packit Service b23acc
	case LOGL_DEBUG: level_str = "<debug>"; break;
Packit Service b23acc
	case LOGL_INFO:  level_str = "<info> "; break;
Packit Service b23acc
	case LOGL_WARN:  level_str = "<warn> "; break;
Packit Service b23acc
	default:
Packit Service b23acc
		nm_assert (level == LOGL_ERR);
Packit Service b23acc
		level_str = "<error>";
Packit Service b23acc
		break;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	ts = nm_utils_clock_gettime_nsec (CLOCK_BOOTTIME);
Packit Service b23acc
Packit Service b23acc
	g_print ("[%"G_GINT64_FORMAT".%05"G_GINT64_FORMAT"] %s %s\n",
Packit Service b23acc
	         ts / NM_UTILS_NSEC_PER_SEC,
Packit Service b23acc
	         (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000,
Packit Service b23acc
	         level_str,
Packit Service b23acc
	         msg);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
void
Packit Service b23acc
_nm_utils_monotonic_timestamp_initialized (const struct timespec *tp,
Packit Service b23acc
                                           gint64 offset_sec,
Packit Service b23acc
                                           gboolean is_boottime)
Packit Service b23acc
{
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
G_LOCK_DEFINE_STATIC  (_wait_for_objects_lock);
Packit Service b23acc
static GSList *_wait_for_objects_list;
Packit Service b23acc
static GSList *_wait_for_objects_iterate_loops;
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_wait_for_objects_maybe_quit_mainloops_with_lock (void)
Packit Service b23acc
{
Packit Service b23acc
	GSList *iter;
Packit Service b23acc
Packit Service b23acc
	if (!_wait_for_objects_list) {
Packit Service b23acc
		for (iter = _wait_for_objects_iterate_loops; iter; iter = iter->next)
Packit Service b23acc
			g_main_loop_quit (iter->data);
Packit Service b23acc
	}
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_wait_for_objects_weak_cb (gpointer data,
Packit Service b23acc
                           GObject *where_the_object_was)
Packit Service b23acc
{
Packit Service b23acc
	G_LOCK (_wait_for_objects_lock);
Packit Service b23acc
	nm_assert (g_slist_find (_wait_for_objects_list, where_the_object_was));
Packit Service b23acc
	_wait_for_objects_list = g_slist_remove (_wait_for_objects_list, where_the_object_was);
Packit Service b23acc
	_wait_for_objects_maybe_quit_mainloops_with_lock ();
Packit Service b23acc
	G_UNLOCK (_wait_for_objects_lock);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/**
Packit Service b23acc
 * nmcs_wait_for_objects_register:
Packit Service b23acc
 * @target: a #GObject to wait for.
Packit Service b23acc
 *
Packit Service b23acc
 * Registers @target as a pointer to wait during shutdown. Using
Packit Service b23acc
 * nmcs_wait_for_objects_iterate_until_done() we keep waiting until
Packit Service b23acc
 * @target gets destroyed, which means that it gets completely unreferenced.
Packit Service b23acc
 */
Packit Service b23acc
gpointer
Packit Service b23acc
nmcs_wait_for_objects_register (gpointer target)
Packit Service b23acc
{
Packit Service b23acc
	g_return_val_if_fail (G_IS_OBJECT (target), NULL);
Packit Service b23acc
Packit Service b23acc
	G_LOCK (_wait_for_objects_lock);
Packit Service b23acc
	_wait_for_objects_list = g_slist_prepend (_wait_for_objects_list, target);
Packit Service b23acc
	G_UNLOCK (_wait_for_objects_lock);
Packit Service b23acc
Packit Service b23acc
	g_object_weak_ref (target,
Packit Service b23acc
	                   _wait_for_objects_weak_cb,
Packit Service b23acc
	                   NULL);
Packit Service b23acc
	return target;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
typedef struct {
Packit Service b23acc
	GMainLoop *loop;
Packit Service b23acc
	gboolean got_timeout;
Packit Service b23acc
} WaitForObjectsData;
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
_wait_for_objects_iterate_until_done_timeout_cb (gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	WaitForObjectsData *data = user_data;
Packit Service b23acc
Packit Service b23acc
	data->got_timeout = TRUE;
Packit Service b23acc
	g_main_loop_quit (data->loop);
Packit Service b23acc
	return G_SOURCE_CONTINUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
_wait_for_objects_iterate_until_done_idle_cb (gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	/* This avoids a race where:
Packit Service b23acc
	 *
Packit Service b23acc
	 *   - we check whether there are objects to wait for.
Packit Service b23acc
	 *   - the last object to wait for gets removed (issuing g_main_loop_quit()).
Packit Service b23acc
	 *   - we run the mainloop (and missed our signal).
Packit Service b23acc
	 *
Packit Service b23acc
	 * It's really a missing feature of GMainLoop where the "is-running" flag is always set to
Packit Service b23acc
	 * TRUE by g_main_loop_run(). That means, you cannot catch a g_main_loop_quit() in a race
Packit Service b23acc
	 * free way while not iterating the loop.
Packit Service b23acc
	 *
Packit Service b23acc
	 * Avoid this, by checking once again after we start running the mainloop.
Packit Service b23acc
	 */
Packit Service b23acc
Packit Service b23acc
	G_LOCK (_wait_for_objects_lock);
Packit Service b23acc
	_wait_for_objects_maybe_quit_mainloops_with_lock ();
Packit Service b23acc
	G_UNLOCK (_wait_for_objects_lock);
Packit Service b23acc
	return G_SOURCE_REMOVE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/**
Packit Service b23acc
 * nmcs_wait_for_objects_iterate_until_done:
Packit Service b23acc
 * @context: the #GMainContext to iterate.
Packit Service b23acc
 * @timeout_msec: timeout or -1 for no timeout.
Packit Service b23acc
 *
Packit Service b23acc
 * Iterates the provided @context until all objects that we wait for
Packit Service b23acc
 * are destroyed.
Packit Service b23acc
 *
Packit Service b23acc
 * The purpose of this is to cleanup all objects that we have on exit. That
Packit Service b23acc
 * is especially because objects have asynchronous operations pending that
Packit Service b23acc
 * should be cancelled and properly completed during exit.
Packit Service b23acc
 *
Packit Service b23acc
 * Returns: %FALSE on timeout or %TRUE if all objects destroyed before timeout.
Packit Service b23acc
 */
Packit Service b23acc
gboolean
Packit Service b23acc
nmcs_wait_for_objects_iterate_until_done (GMainContext *context,
Packit Service b23acc
                                          int timeout_msec)
Packit Service b23acc
{
Packit Service b23acc
	nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new (context, FALSE);
Packit Service b23acc
	nm_auto_destroy_and_unref_gsource GSource *timeout_source = NULL;
Packit Service b23acc
	WaitForObjectsData data;
Packit Service b23acc
	gboolean has_more_objects;
Packit Service b23acc
Packit Service b23acc
	G_LOCK (_wait_for_objects_lock);
Packit Service b23acc
	if (!_wait_for_objects_list) {
Packit Service b23acc
		G_UNLOCK (_wait_for_objects_lock);
Packit Service b23acc
		return TRUE;
Packit Service b23acc
	}
Packit Service b23acc
	_wait_for_objects_iterate_loops = g_slist_prepend (_wait_for_objects_iterate_loops, loop);
Packit Service b23acc
	G_UNLOCK (_wait_for_objects_lock);
Packit Service b23acc
Packit Service b23acc
	data = (WaitForObjectsData) {
Packit Service b23acc
		.loop        = loop,
Packit Service b23acc
		.got_timeout = FALSE,
Packit Service b23acc
	};
Packit Service b23acc
Packit Service b23acc
	if (timeout_msec >= 0) {
Packit Service b23acc
		timeout_source = nm_g_source_attach (nm_g_timeout_source_new (timeout_msec,
Packit Service b23acc
		                                                              G_PRIORITY_DEFAULT,
Packit Service b23acc
		                                                              _wait_for_objects_iterate_until_done_timeout_cb,
Packit Service b23acc
		                                                              &data,
Packit Service b23acc
		                                                              NULL),
Packit Service b23acc
		                                     context);
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	has_more_objects = TRUE;
Packit Service b23acc
	while (   has_more_objects
Packit Service b23acc
	       && !data.got_timeout) {
Packit Service b23acc
		nm_auto_destroy_and_unref_gsource GSource *idle_source = NULL;
Packit Service b23acc
Packit Service b23acc
		idle_source = nm_g_source_attach (nm_g_idle_source_new (G_PRIORITY_DEFAULT,
Packit Service b23acc
		                                                        _wait_for_objects_iterate_until_done_idle_cb,
Packit Service b23acc
		                                                        &data,
Packit Service b23acc
		                                                        NULL),
Packit Service b23acc
		                                  context);
Packit Service b23acc
Packit Service b23acc
		g_main_loop_run (loop);
Packit Service b23acc
Packit Service b23acc
		G_LOCK (_wait_for_objects_lock);
Packit Service b23acc
		has_more_objects = (!!_wait_for_objects_list);
Packit Service b23acc
		if (   data.got_timeout
Packit Service b23acc
			|| !has_more_objects)
Packit Service b23acc
			_wait_for_objects_iterate_loops = g_slist_remove (_wait_for_objects_iterate_loops, loop);
Packit Service b23acc
		G_UNLOCK (_wait_for_objects_lock);
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return !data.got_timeout;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
typedef struct {
Packit Service b23acc
	GTask *task;
Packit Service b23acc
	GSource *source_timeout;
Packit Service b23acc
	GSource *source_next_poll;
Packit Service b23acc
	GMainContext *context;
Packit Service b23acc
	GCancellable *internal_cancellable;
Packit Service b23acc
	NMCSUtilsPollProbeStartFcn probe_start_fcn;
Packit Service b23acc
	NMCSUtilsPollProbeFinishFcn probe_finish_fcn;
Packit Service b23acc
	gpointer probe_user_data;
Packit Service b23acc
	gulong cancellable_id;
Packit Service b23acc
	gint64 last_poll_start_ms;
Packit Service b23acc
	int sleep_timeout_ms;
Packit Service b23acc
	int ratelimit_timeout_ms;
Packit Service b23acc
	bool completed:1;
Packit Service b23acc
} PollTaskData;
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_poll_task_data_free (gpointer data)
Packit Service b23acc
{
Packit Service b23acc
	PollTaskData *poll_task_data = data;
Packit Service b23acc
Packit Service b23acc
	nm_assert (G_IS_TASK (poll_task_data->task));
Packit Service b23acc
	nm_assert (!poll_task_data->source_next_poll);
Packit Service b23acc
	nm_assert (!poll_task_data->source_timeout);
Packit Service b23acc
	nm_assert (poll_task_data->cancellable_id == 0);
Packit Service b23acc
Packit Service b23acc
	g_main_context_unref (poll_task_data->context);
Packit Service b23acc
Packit Service b23acc
	nm_g_slice_free (poll_task_data);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_poll_return (PollTaskData *poll_task_data,
Packit Service b23acc
              gboolean success,
Packit Service b23acc
              GError *error_take)
Packit Service b23acc
{
Packit Service b23acc
	nm_clear_g_source_inst (&poll_task_data->source_next_poll);
Packit Service b23acc
	nm_clear_g_source_inst (&poll_task_data->source_timeout);
Packit Service b23acc
	nm_clear_g_cancellable_disconnect (g_task_get_cancellable (poll_task_data->task),
Packit Service b23acc
	                                   &poll_task_data->cancellable_id);
Packit Service b23acc
Packit Service b23acc
	nm_clear_g_cancellable (&poll_task_data->internal_cancellable);
Packit Service b23acc
Packit Service b23acc
	if (error_take)
Packit Service b23acc
		g_task_return_error (poll_task_data->task, g_steal_pointer (&error_take));
Packit Service b23acc
	else
Packit Service b23acc
		g_task_return_boolean (poll_task_data->task, success);
Packit Service b23acc
Packit Service b23acc
	g_object_unref (poll_task_data->task);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean _poll_start_cb (gpointer user_data);
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_poll_done_cb (GObject *source,
Packit Service b23acc
               GAsyncResult *result,
Packit Service b23acc
               gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	PollTaskData *poll_task_data = user_data;
Packit Service b23acc
	_nm_unused gs_unref_object GTask *task = poll_task_data->task; /* balance ref from _poll_start_cb() */
Packit Service b23acc
	gs_free_error GError *error = NULL;
Packit Service b23acc
	gint64 now_ms;
Packit Service b23acc
	gint64 wait_ms;
Packit Service b23acc
	gboolean is_finished;
Packit Service b23acc
Packit Service b23acc
	is_finished = poll_task_data->probe_finish_fcn (source,
Packit Service b23acc
	                                                result,
Packit Service b23acc
	                                                poll_task_data->probe_user_data,
Packit Service b23acc
	                                                &error);
Packit Service b23acc
Packit Service b23acc
	if (nm_utils_error_is_cancelled (error)) {
Packit Service b23acc
		/* we already handle this differently. Nothing to do. */
Packit Service b23acc
		return;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	if (   error
Packit Service b23acc
	    || is_finished) {
Packit Service b23acc
		_poll_return (poll_task_data, TRUE, g_steal_pointer (&error));
Packit Service b23acc
		return;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	now_ms = nm_utils_get_monotonic_timestamp_msec ();
Packit Service b23acc
	if (poll_task_data->ratelimit_timeout_ms > 0)
Packit Service b23acc
		wait_ms = (poll_task_data->last_poll_start_ms + poll_task_data->ratelimit_timeout_ms) - now_ms;
Packit Service b23acc
	else
Packit Service b23acc
		wait_ms = 0;
Packit Service b23acc
	if (poll_task_data->sleep_timeout_ms > 0)
Packit Service b23acc
		wait_ms = MAX (wait_ms, poll_task_data->sleep_timeout_ms);
Packit Service b23acc
Packit Service b23acc
	poll_task_data->source_next_poll = nm_g_source_attach (nm_g_timeout_source_new (MAX (1, wait_ms),
Packit Service b23acc
	                                                                                G_PRIORITY_DEFAULT,
Packit Service b23acc
	                                                                                _poll_start_cb,
Packit Service b23acc
	                                                                                poll_task_data,
Packit Service b23acc
	                                                                                NULL),
Packit Service b23acc
	                                                       poll_task_data->context);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
_poll_start_cb (gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	PollTaskData *poll_task_data = user_data;
Packit Service b23acc
Packit Service b23acc
	nm_clear_g_source_inst (&poll_task_data->source_next_poll);
Packit Service b23acc
Packit Service b23acc
	poll_task_data->last_poll_start_ms = nm_utils_get_monotonic_timestamp_msec ();
Packit Service b23acc
Packit Service b23acc
	g_object_ref (poll_task_data->task); /* balanced by _poll_done_cb() */
Packit Service b23acc
Packit Service b23acc
	poll_task_data->probe_start_fcn (poll_task_data->internal_cancellable,
Packit Service b23acc
	                                 poll_task_data->probe_user_data,
Packit Service b23acc
	                                 _poll_done_cb,
Packit Service b23acc
	                                 poll_task_data);
Packit Service b23acc
Packit Service b23acc
	return G_SOURCE_CONTINUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static gboolean
Packit Service b23acc
_poll_timeout_cb (gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	PollTaskData *poll_task_data = user_data;
Packit Service b23acc
Packit Service b23acc
	_poll_return (poll_task_data, FALSE, nm_utils_error_new (NM_UTILS_ERROR_UNKNOWN,
Packit Service b23acc
	                                                         "timeout expired"));
Packit Service b23acc
	return G_SOURCE_CONTINUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_poll_cancelled_cb (GObject *object, gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	PollTaskData *poll_task_data = user_data;
Packit Service b23acc
	GError *error = NULL;
Packit Service b23acc
Packit Service b23acc
	_LOGD (">> poll cancelled");
Packit Service b23acc
	nm_clear_g_signal_handler (g_task_get_cancellable (poll_task_data->task),
Packit Service b23acc
	                           &poll_task_data->cancellable_id);
Packit Service b23acc
	nm_utils_error_set_cancelled (&error, FALSE, NULL);
Packit Service b23acc
	_poll_return (poll_task_data, FALSE, error);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/**
Packit Service b23acc
 * nmcs_utils_poll:
Packit Service b23acc
 * @poll_timeout_ms: if >= 0, then this is the overall timeout for how long we poll.
Packit Service b23acc
 *   When this timeout expires, the request completes with failure (but no error set).
Packit Service b23acc
 * @ratelimit_timeout_ms: if > 0, we ratelimit the starts from one prope_start_fcn
Packit Service b23acc
 *   call to the next.
Packit Service b23acc
 * @sleep_timeout_ms: if > 0, then we wait after a probe finished this timeout
Packit Service b23acc
 *   before the next. Together with @ratelimit_timeout_ms this determines how
Packit Service b23acc
 *   frequently we probe.
Packit Service b23acc
 * @probe_start_fcn: used to start a (asynchrnous) probe. A probe must be completed
Packit Service b23acc
 *   by calling the provided callback. While a probe is in progress, we will not
Packit Service b23acc
 *   start another. This function is already invoked the first time synchronously,
Packit Service b23acc
 *   during nmcs_utils_poll().
Packit Service b23acc
 * @probe_finish_fcn: will be called from the callback of @probe_start_fcn. If the
Packit Service b23acc
 *   function returns %TRUE (polling done) or an error, polling stops. Otherwise,
Packit Service b23acc
 *   another poll will be started.
Packit Service b23acc
 * @probe_user_data: user_data for the probe functions.
Packit Service b23acc
 * @cancellable: cancellable for polling.
Packit Service b23acc
 * @callback: when polling completes.
Packit Service b23acc
 * @user_data: for @callback.
Packit Service b23acc
 *
Packit Service b23acc
 * This uses the current g_main_context_get_thread_default() for scheduling
Packit Service b23acc
 * actions.
Packit Service b23acc
 */
Packit Service b23acc
void
Packit Service b23acc
nmcs_utils_poll (int poll_timeout_ms,
Packit Service b23acc
                 int ratelimit_timeout_ms,
Packit Service b23acc
                 int sleep_timeout_ms,
Packit Service b23acc
                 NMCSUtilsPollProbeStartFcn probe_start_fcn,
Packit Service b23acc
                 NMCSUtilsPollProbeFinishFcn probe_finish_fcn,
Packit Service b23acc
                 gpointer probe_user_data,
Packit Service b23acc
                 GCancellable *cancellable,
Packit Service b23acc
                 GAsyncReadyCallback callback,
Packit Service b23acc
                 gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	PollTaskData *poll_task_data;
Packit Service b23acc
Packit Service b23acc
	poll_task_data = g_slice_new (PollTaskData);
Packit Service b23acc
	*poll_task_data = (PollTaskData) {
Packit Service b23acc
		.task                 = nm_g_task_new (NULL, cancellable, nmcs_utils_poll, callback, user_data),
Packit Service b23acc
		.probe_start_fcn      = probe_start_fcn,
Packit Service b23acc
		.probe_finish_fcn     = probe_finish_fcn,
Packit Service b23acc
		.probe_user_data      = probe_user_data,
Packit Service b23acc
		.completed            = FALSE,
Packit Service b23acc
		.context              = g_main_context_ref_thread_default (),
Packit Service b23acc
		.sleep_timeout_ms     = sleep_timeout_ms,
Packit Service b23acc
		.ratelimit_timeout_ms = ratelimit_timeout_ms,
Packit Service b23acc
		.internal_cancellable = g_cancellable_new (),
Packit Service b23acc
	};
Packit Service b23acc
Packit Service b23acc
	nmcs_wait_for_objects_register (poll_task_data->task);
Packit Service b23acc
Packit Service b23acc
	g_task_set_task_data (poll_task_data->task, poll_task_data, _poll_task_data_free);
Packit Service b23acc
Packit Service b23acc
	if (poll_timeout_ms >= 0) {
Packit Service b23acc
		poll_task_data->source_timeout = nm_g_source_attach (nm_g_timeout_source_new (poll_timeout_ms,
Packit Service b23acc
		                                                                              G_PRIORITY_DEFAULT,
Packit Service b23acc
		                                                                              _poll_timeout_cb,
Packit Service b23acc
		                                                                              poll_task_data,
Packit Service b23acc
		                                                                              NULL),
Packit Service b23acc
		                                                     poll_task_data->context);
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	poll_task_data->source_next_poll = nm_g_source_attach (nm_g_idle_source_new (G_PRIORITY_DEFAULT,
Packit Service b23acc
	                                                                             _poll_start_cb,
Packit Service b23acc
	                                                                             poll_task_data,
Packit Service b23acc
	                                                                             NULL),
Packit Service b23acc
	                                                       poll_task_data->context);
Packit Service b23acc
Packit Service b23acc
	if (cancellable) {
Packit Service b23acc
		gulong signal_id;
Packit Service b23acc
Packit Service b23acc
		signal_id = g_cancellable_connect (cancellable,
Packit Service b23acc
		                                   G_CALLBACK (_poll_cancelled_cb),
Packit Service b23acc
		                                   poll_task_data,
Packit Service b23acc
		                                   NULL);
Packit Service b23acc
		if (signal_id == 0) {
Packit Service b23acc
			/* the request is already cancelled. Return. */
Packit Service b23acc
			return;
Packit Service b23acc
		}
Packit Service b23acc
		poll_task_data->cancellable_id = signal_id;
Packit Service b23acc
	}
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/**
Packit Service b23acc
 * nmcs_utils_poll_finish:
Packit Service b23acc
 * @result: the GAsyncResult from the GAsyncReadyCallback callback.
Packit Service b23acc
 * @probe_user_data: the user data provided to nmcs_utils_poll().
Packit Service b23acc
 * @error: the failure code.
Packit Service b23acc
 *
Packit Service b23acc
 * Returns: %TRUE if the polling completed with success. In that case,
Packit Service b23acc
 *   the error won't be set.
Packit Service b23acc
 *   If the request was cancelled, this is indicated by @error and
Packit Service b23acc
 *   %FALSE will be returned.
Packit Service b23acc
 *   If the probe returned a failure, this returns %FALSE and the error
Packit Service b23acc
 *   provided by @probe_finish_fcn.
Packit Service b23acc
 *   If the request times out, this returns %FALSE without error set.
Packit Service b23acc
 */
Packit Service b23acc
gboolean
Packit Service b23acc
nmcs_utils_poll_finish (GAsyncResult *result,
Packit Service b23acc
                        gpointer *probe_user_data,
Packit Service b23acc
                        GError **error)
Packit Service b23acc
{
Packit Service b23acc
	GTask *task;
Packit Service b23acc
	PollTaskData *poll_task_data;
Packit Service b23acc
Packit Service b23acc
	g_return_val_if_fail (nm_g_task_is_valid (result, NULL, nmcs_utils_poll), FALSE);
Packit Service b23acc
	g_return_val_if_fail (!error || !*error, FALSE);
Packit Service b23acc
Packit Service b23acc
	task = G_TASK (result);
Packit Service b23acc
Packit Service b23acc
	if (probe_user_data) {
Packit Service b23acc
		poll_task_data = g_task_get_task_data (task);
Packit Service b23acc
		NM_SET_OUT (probe_user_data, poll_task_data->probe_user_data);
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return g_task_propagate_boolean (task, error);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
char *
Packit Service b23acc
nmcs_utils_hwaddr_normalize (const char *hwaddr, gssize len)
Packit Service b23acc
{
Packit Service b23acc
	gs_free char *hwaddr_clone = NULL;
Packit Service b23acc
	guint8 buf[ETH_ALEN];
Packit Service b23acc
Packit Service b23acc
	nm_assert (len >= -1);
Packit Service b23acc
Packit Service b23acc
	if (len < 0) {
Packit Service b23acc
		if (!hwaddr)
Packit Service b23acc
			return NULL;
Packit Service b23acc
	} else {
Packit Service b23acc
		if (len == 0)
Packit Service b23acc
			return NULL;
Packit Service b23acc
		nm_assert (hwaddr);
Packit Service b23acc
		hwaddr = nm_strndup_a (300, hwaddr, len, &hwaddr_clone);
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	if (!nm_utils_hwaddr_aton (hwaddr, buf, sizeof (buf)))
Packit Service b23acc
		return NULL;
Packit Service b23acc
Packit Service b23acc
	return nm_utils_hwaddr_ntoa (buf, sizeof (buf));
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
const char *
Packit Service b23acc
nmcs_utils_parse_memmem (GBytes *mem, const char *needle)
Packit Service b23acc
{
Packit Service b23acc
	const char *mem_data;
Packit Service b23acc
	gsize mem_size;
Packit Service b23acc
Packit Service b23acc
	g_return_val_if_fail (mem, NULL);
Packit Service b23acc
	g_return_val_if_fail (needle, NULL);
Packit Service b23acc
Packit Service b23acc
	mem_data = g_bytes_get_data (mem, &mem_size);
Packit Service b23acc
	return memmem (mem_data, mem_size, needle, strlen (needle));
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
const char *
Packit Service b23acc
nmcs_utils_parse_get_full_line (GBytes *mem, const char *needle)
Packit Service b23acc
{
Packit Service b23acc
	const char *mem_data;
Packit Service b23acc
	gsize mem_size;
Packit Service b23acc
	gsize c;
Packit Service b23acc
	gsize l;
Packit Service b23acc
Packit Service b23acc
	const char *line;
Packit Service b23acc
Packit Service b23acc
	line = nmcs_utils_parse_memmem (mem, needle);
Packit Service b23acc
	if (!line)
Packit Service b23acc
		return NULL;
Packit Service b23acc
Packit Service b23acc
	mem_data = g_bytes_get_data (mem, &mem_size);
Packit Service b23acc
Packit Service b23acc
	if (   line != mem_data
Packit Service b23acc
	    && line[-1] != '\n') {
Packit Service b23acc
		/* the line must be preceeded either by the begin of the data or
Packit Service b23acc
		 * by a newline. */
Packit Service b23acc
		return NULL;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	c = mem_size - (line - mem_data);
Packit Service b23acc
	l = strlen (needle);
Packit Service b23acc
Packit Service b23acc
	if (   c != l
Packit Service b23acc
	    && line[l] != '\n') {
Packit Service b23acc
		/* the end of the needle must be either a newline or the end of the buffer. */
Packit Service b23acc
		return NULL;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return line;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
char *
Packit Service b23acc
nmcs_utils_uri_build_concat_v (const char *base,
Packit Service b23acc
                               const char **components,
Packit Service b23acc
                               gsize n_components)
Packit Service b23acc
{
Packit Service b23acc
	GString *uri;
Packit Service b23acc
Packit Service b23acc
	nm_assert (base);
Packit Service b23acc
	nm_assert (base[0]);
Packit Service b23acc
	nm_assert (!NM_STR_HAS_SUFFIX (base, "/"));
Packit Service b23acc
Packit Service b23acc
	uri = g_string_sized_new (100);
Packit Service b23acc
Packit Service b23acc
	g_string_append (uri, base);
Packit Service b23acc
Packit Service b23acc
	if (   n_components > 0
Packit Service b23acc
	    && components[0]
Packit Service b23acc
	    && components[0][0] == '/') {
Packit Service b23acc
		/* the first component starts with a slash. We allow that, and don't add a duplicate
Packit Service b23acc
		 * slash. Otherwise, we add a separator after base.
Packit Service b23acc
		 *
Packit Service b23acc
		 * We only do that for the first component. */
Packit Service b23acc
	} else
Packit Service b23acc
		g_string_append_c (uri, '/');
Packit Service b23acc
Packit Service b23acc
	while (n_components > 0) {
Packit Service b23acc
		if (!components[0]) {
Packit Service b23acc
			/* we allow NULL, to indicate nothing to append*/
Packit Service b23acc
		} else
Packit Service b23acc
			g_string_append (uri, components[0]);
Packit Service b23acc
		components++;
Packit Service b23acc
		n_components--;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return g_string_free (uri, FALSE);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
gboolean
Packit Service b23acc
nmcs_setting_ip_replace_ipv4_addresses (NMSettingIPConfig *s_ip,
Packit Service b23acc
                                        NMIPAddress **entries_arr,
Packit Service b23acc
                                        guint entries_len)
Packit Service b23acc
{
Packit Service b23acc
	gboolean any_changes = FALSE;
Packit Service b23acc
	guint i_next;
Packit Service b23acc
	guint num;
Packit Service b23acc
	guint i;
Packit Service b23acc
Packit Service b23acc
	num = nm_setting_ip_config_get_num_addresses (s_ip);
Packit Service b23acc
Packit Service b23acc
	i_next = 0;
Packit Service b23acc
Packit Service b23acc
	for (i = 0; i < entries_len; i++) {
Packit Service b23acc
		NMIPAddress *entry = entries_arr[i];
Packit Service b23acc
Packit Service b23acc
		if (!any_changes) {
Packit Service b23acc
			if (i_next < num) {
Packit Service b23acc
				if (nm_ip_address_cmp_full (entry,
Packit Service b23acc
				                            nm_setting_ip_config_get_address (s_ip, i_next),
Packit Service b23acc
				                            NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS) == 0) {
Packit Service b23acc
					i_next++;
Packit Service b23acc
					continue;
Packit Service b23acc
				}
Packit Service b23acc
			}
Packit Service b23acc
			while (i_next < num)
Packit Service b23acc
				nm_setting_ip_config_remove_address (s_ip, --num);
Packit Service b23acc
			any_changes = TRUE;
Packit Service b23acc
		}
Packit Service b23acc
Packit Service b23acc
		if (!nm_setting_ip_config_add_address (s_ip, entry))
Packit Service b23acc
			continue;
Packit Service b23acc
Packit Service b23acc
		i_next++;
Packit Service b23acc
	}
Packit Service b23acc
	if (any_changes) {
Packit Service b23acc
		while (i_next < num) {
Packit Service b23acc
			nm_setting_ip_config_remove_address (s_ip, --num);
Packit Service b23acc
			any_changes = TRUE;
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return any_changes;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
gboolean
Packit Service b23acc
nmcs_setting_ip_replace_ipv4_routes (NMSettingIPConfig *s_ip,
Packit Service b23acc
                                     NMIPRoute **entries_arr,
Packit Service b23acc
                                     guint entries_len)
Packit Service b23acc
{
Packit Service b23acc
	gboolean any_changes = FALSE;
Packit Service b23acc
	guint i_next;
Packit Service b23acc
	guint num;
Packit Service b23acc
	guint i;
Packit Service b23acc
Packit Service b23acc
	num = nm_setting_ip_config_get_num_routes (s_ip);
Packit Service b23acc
Packit Service b23acc
	i_next = 0;
Packit Service b23acc
Packit Service b23acc
	for (i = 0; i < entries_len; i++) {
Packit Service b23acc
		NMIPRoute *entry = entries_arr[i];
Packit Service b23acc
Packit Service b23acc
		if (!any_changes) {
Packit Service b23acc
			if (i_next < num) {
Packit Service b23acc
				if (nm_ip_route_equal_full (entry,
Packit Service b23acc
				                            nm_setting_ip_config_get_route (s_ip, i_next),
Packit Service b23acc
				                            NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS)) {
Packit Service b23acc
					i_next++;
Packit Service b23acc
					continue;
Packit Service b23acc
				}
Packit Service b23acc
			}
Packit Service b23acc
			while (i_next < num)
Packit Service b23acc
				nm_setting_ip_config_remove_route (s_ip, --num);
Packit Service b23acc
			any_changes = TRUE;
Packit Service b23acc
		}
Packit Service b23acc
Packit Service b23acc
		if (!nm_setting_ip_config_add_route (s_ip, entry))
Packit Service b23acc
			continue;
Packit Service b23acc
Packit Service b23acc
		i_next++;
Packit Service b23acc
	}
Packit Service b23acc
	if (!any_changes) {
Packit Service b23acc
		while (i_next < num) {
Packit Service b23acc
			nm_setting_ip_config_remove_route (s_ip, --num);
Packit Service b23acc
			any_changes = TRUE;
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return any_changes;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
gboolean
Packit Service b23acc
nmcs_setting_ip_replace_ipv4_rules (NMSettingIPConfig *s_ip,
Packit Service b23acc
                                    NMIPRoutingRule **entries_arr,
Packit Service b23acc
                                    guint entries_len)
Packit Service b23acc
{
Packit Service b23acc
	gboolean any_changes = FALSE;
Packit Service b23acc
	guint i_next;
Packit Service b23acc
	guint num;
Packit Service b23acc
	guint i;
Packit Service b23acc
Packit Service b23acc
	num = nm_setting_ip_config_get_num_routing_rules (s_ip);
Packit Service b23acc
Packit Service b23acc
	i_next = 0;
Packit Service b23acc
Packit Service b23acc
	for (i = 0; i < entries_len; i++) {
Packit Service b23acc
		NMIPRoutingRule *entry = entries_arr[i];
Packit Service b23acc
Packit Service b23acc
		if (!any_changes) {
Packit Service b23acc
			if (i_next < num) {
Packit Service b23acc
				if (nm_ip_routing_rule_cmp (entry,
Packit Service b23acc
				                            nm_setting_ip_config_get_routing_rule (s_ip, i_next)) == 0) {
Packit Service b23acc
					i_next++;
Packit Service b23acc
					continue;
Packit Service b23acc
				}
Packit Service b23acc
			}
Packit Service b23acc
			while (i_next < num)
Packit Service b23acc
				nm_setting_ip_config_remove_routing_rule (s_ip, --num);
Packit Service b23acc
			any_changes = TRUE;
Packit Service b23acc
		}
Packit Service b23acc
Packit Service b23acc
		nm_setting_ip_config_add_routing_rule (s_ip, entry);
Packit Service b23acc
		i_next++;
Packit Service b23acc
	}
Packit Service b23acc
	if (!any_changes) {
Packit Service b23acc
		while (i_next < num) {
Packit Service b23acc
			nm_setting_ip_config_remove_routing_rule (s_ip, --num);
Packit Service b23acc
			any_changes = TRUE;
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	return any_changes;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
typedef struct {
Packit Service b23acc
	GMainLoop *main_loop;
Packit Service b23acc
	NMConnection *connection;
Packit Service b23acc
	GError *error;
Packit Service b23acc
	guint64 version_id;
Packit Service b23acc
} DeviceGetAppliedConnectionData;
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_nmcs_device_get_applied_connection_cb (GObject *source,
Packit Service b23acc
                                        GAsyncResult *result,
Packit Service b23acc
                                        gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	DeviceGetAppliedConnectionData *data = user_data;
Packit Service b23acc
Packit Service b23acc
	data->connection = nm_device_get_applied_connection_finish (NM_DEVICE (source),
Packit Service b23acc
	                                                            result,
Packit Service b23acc
	                                                            &data->version_id,
Packit Service b23acc
	                                                            &data->error);
Packit Service b23acc
	g_main_loop_quit (data->main_loop);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
NMConnection *
Packit Service b23acc
nmcs_device_get_applied_connection (NMDevice *device,
Packit Service b23acc
                                    GCancellable *cancellable,
Packit Service b23acc
                                    guint64 *version_id,
Packit Service b23acc
                                    GError **error)
Packit Service b23acc
{
Packit Service b23acc
	nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
Packit Service b23acc
	DeviceGetAppliedConnectionData data = {
Packit Service b23acc
		.main_loop = main_loop,
Packit Service b23acc
	};
Packit Service b23acc
Packit Service b23acc
	nm_device_get_applied_connection_async (device,
Packit Service b23acc
	                                        0,
Packit Service b23acc
	                                        cancellable,
Packit Service b23acc
	                                        _nmcs_device_get_applied_connection_cb,
Packit Service b23acc
	                                        &data);
Packit Service b23acc
Packit Service b23acc
	g_main_loop_run (main_loop);
Packit Service b23acc
Packit Service b23acc
	if (data.error)
Packit Service b23acc
		g_propagate_error (error, data.error);
Packit Service b23acc
	NM_SET_OUT (version_id, data.version_id);
Packit Service b23acc
	return data.connection;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
typedef struct {
Packit Service b23acc
	GMainLoop *main_loop;
Packit Service b23acc
	GError *error;
Packit Service b23acc
} DeviceReapplyData;
Packit Service b23acc
Packit Service b23acc
static void
Packit Service b23acc
_nmcs_device_reapply_cb (GObject *source,
Packit Service b23acc
                         GAsyncResult *result,
Packit Service b23acc
                         gpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	DeviceReapplyData *data = user_data;
Packit Service b23acc
Packit Service b23acc
	nm_device_reapply_finish (NM_DEVICE (source),
Packit Service b23acc
	                          result,
Packit Service b23acc
	                          &data->error);
Packit Service b23acc
	g_main_loop_quit (data->main_loop);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
gboolean
Packit Service b23acc
nmcs_device_reapply (NMDevice *device,
Packit Service b23acc
                     GCancellable *sigterm_cancellable,
Packit Service b23acc
                     NMConnection *connection,
Packit Service b23acc
                     guint64 version_id,
Packit Service b23acc
                     gboolean *out_version_id_changed,
Packit Service b23acc
                     GError **error)
Packit Service b23acc
{
Packit Service b23acc
	nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
Packit Service b23acc
	DeviceReapplyData data = {
Packit Service b23acc
		.main_loop = main_loop,
Packit Service b23acc
	};
Packit Service b23acc
Packit Service b23acc
	nm_device_reapply_async (device,
Packit Service b23acc
	                         connection,
Packit Service b23acc
	                         version_id,
Packit Service b23acc
	                         0,
Packit Service b23acc
	                         sigterm_cancellable,
Packit Service b23acc
	                         _nmcs_device_reapply_cb,
Packit Service b23acc
	                         &data);
Packit Service b23acc
Packit Service b23acc
	g_main_loop_run (main_loop);
Packit Service b23acc
Packit Service b23acc
	if (data.error) {
Packit Service b23acc
		NM_SET_OUT (out_version_id_changed, g_error_matches (data.error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_VERSION_ID_MISMATCH));
Packit Service b23acc
		g_propagate_error (error, data.error);
Packit Service b23acc
		return FALSE;
Packit Service b23acc
	}
Packit Service b23acc
Packit Service b23acc
	NM_SET_OUT (out_version_id_changed, FALSE);
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}