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

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