summary refs log tree commit diff
path: root/shared/nm-glib-aux
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-glib-aux')
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.c113
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.h16
-rw-r--r--shared/nm-glib-aux/nm-errno.c14
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h29
-rw-r--r--shared/nm-glib-aux/nm-random-utils.c4
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c79
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h4
7 files changed, 235 insertions, 24 deletions
diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c
index ec409ff1..9fb02a3f 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.c
+++ b/shared/nm-glib-aux/nm-dbus-aux.c
@@ -268,6 +268,119 @@ nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(GObject *     source,
 
 /*****************************************************************************/
 
+typedef struct {
+    char *              bus_name;
+    char *              object_path;
+    char *              interface_name;
+    char *              method_name;
+    GVariant *          parameters;
+    GDBusConnection *   connection;
+    const GVariantType *reply_type;
+    int                 timeout_msec;
+} CallAsyncInfo;
+
+static void
+call_async_info_destroy(CallAsyncInfo *info)
+{
+    g_free(info->bus_name);
+    g_free(info->object_path);
+    g_free(info->interface_name);
+    g_free(info->method_name);
+    g_variant_unref(info->parameters);
+    nm_g_object_unref(info->connection);
+    g_free(info);
+}
+
+static void
+call_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    gs_unref_object GTask *task  = user_data;
+    GError *               error = NULL;
+    GVariant *             ret;
+
+    ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
+    if (!ret) {
+        g_task_return_error(task, error);
+        return;
+    }
+
+    g_task_return_pointer(task, ret, (GDestroyNotify) g_variant_unref);
+}
+
+static void
+call_bus_get_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    gs_unref_object GTask *task = user_data;
+    GCancellable *         cancellable;
+    CallAsyncInfo *        info;
+    GError *               error = NULL;
+
+    info             = g_task_get_task_data(task);
+    info->connection = g_bus_get_finish(result, &error);
+    cancellable      = g_task_get_cancellable(task);
+
+    if (!info->connection) {
+        g_task_return_error(task, g_steal_pointer(&error));
+        return;
+    }
+
+    g_dbus_connection_call(info->connection,
+                           info->bus_name,
+                           info->object_path,
+                           info->interface_name,
+                           info->method_name,
+                           info->parameters,
+                           info->reply_type,
+                           G_DBUS_CALL_FLAGS_NONE,
+                           info->timeout_msec,
+                           cancellable,
+                           call_cb,
+                           g_steal_pointer(&task));
+}
+
+void
+nm_dbus_call(GBusType            bus_type,
+             const char *        bus_name,
+             const char *        object_path,
+             const char *        interface_name,
+             const char *        method_name,
+             GVariant *          parameters,
+             const GVariantType *reply_type,
+             GCancellable *      cancellable,
+             int                 timeout_msec,
+             GAsyncReadyCallback callback,
+             gpointer            user_data)
+{
+    GTask *        task;
+    CallAsyncInfo *info;
+
+    info  = g_new(CallAsyncInfo, 1);
+    *info = (CallAsyncInfo){
+        .bus_name       = g_strdup(bus_name),
+        .object_path    = g_strdup(object_path),
+        .interface_name = g_strdup(interface_name),
+        .method_name    = g_strdup(method_name),
+        .parameters     = g_variant_ref_sink(parameters),
+        .reply_type     = reply_type,
+        .timeout_msec   = timeout_msec,
+    };
+
+    task = nm_g_task_new(NULL, cancellable, nm_dbus_call, callback, user_data);
+    g_task_set_task_data(task, info, (GDestroyNotify) call_async_info_destroy);
+
+    g_bus_get(bus_type, cancellable, call_bus_get_cb, task);
+}
+
+GVariant *
+nm_dbus_call_finish(GAsyncResult *result, GError **error)
+{
+    nm_assert(nm_g_task_is_valid(result, NULL, nm_dbus_call));
+
+    return g_task_propagate_pointer(G_TASK(result), error);
+}
+
+/*****************************************************************************/
+
 gboolean
 _nm_dbus_error_is(GError *error, ...)
 {
diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h
index 4e3ae22d..c8107082 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.h
+++ b/shared/nm-glib-aux/nm-dbus-aux.h
@@ -186,6 +186,22 @@ void nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(GObject *     so
 
 /*****************************************************************************/
 
+void nm_dbus_call(GBusType            bus_type,
+                  const char *        bus_name,
+                  const char *        object_path,
+                  const char *        interface_name,
+                  const char *        method_name,
+                  GVariant *          parameters,
+                  const GVariantType *reply_type,
+                  GCancellable *      cancellable,
+                  int                 timeout_msec,
+                  GAsyncReadyCallback callback,
+                  gpointer            user_data);
+
+GVariant *nm_dbus_call_finish(GAsyncResult *result, GError **error);
+
+/*****************************************************************************/
+
 gboolean _nm_dbus_error_is(GError *error, ...) G_GNUC_NULL_TERMINATED;
 
 #define nm_dbus_error_is(error, ...)                           \
diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c
index 668606ca..c05379ec 100644
--- a/shared/nm-glib-aux/nm-errno.c
+++ b/shared/nm-glib-aux/nm-errno.c
@@ -7,8 +7,6 @@
 
 #include "nm-errno.h"
 
-#include <pthread.h>
-
 /*****************************************************************************/
 
 static NM_UTILS_LOOKUP_STR_DEFINE(
@@ -162,19 +160,9 @@ nm_strerror_native(int errsv)
 
     buf = buf_static;
     if (G_UNLIKELY(!buf)) {
-        int           errno_saved = errno;
-        pthread_key_t key;
-
         buf        = g_malloc(NM_STRERROR_BUFSIZE);
         buf_static = buf;
-
-        if (pthread_key_create(&key, g_free) != 0 || pthread_setspecific(key, buf) != 0) {
-            /* Failure. We will leak the buffer when the thread exits.
-             *
-             * Nothing we can do about it really. For Debug builds we fail with an assertion. */
-            nm_assert_not_reached();
-        }
-        errno = errno_saved;
+        nm_utils_thread_local_register_destroy(buf, g_free);
     }
 
     return nm_strerror_native_r(errsv, buf, NM_STRERROR_BUFSIZE);
diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h
index 113a67a0..6f63f204 100644
--- a/shared/nm-glib-aux/nm-macros-internal.h
+++ b/shared/nm-glib-aux/nm-macros-internal.h
@@ -121,14 +121,6 @@ _nm_auto_free_gstring(GString **str)
 }
 #define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring)
 
-static inline void
-_nm_auto_protect_errno(int *p_saved_errno)
-{
-    errno = *p_saved_errno;
-}
-#define NM_AUTO_PROTECT_ERRNO(errsv_saved) \
-    nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno)
-
 NM_AUTO_DEFINE_FCN0(GSource *, _nm_auto_unref_gsource, g_source_unref);
 #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
 
@@ -1112,6 +1104,18 @@ nm_clear_g_variant(GVariant **variant)
 }
 
 static inline gboolean
+nm_clear_g_string(GString **ptr)
+{
+    GString *s;
+
+    if (ptr && (s = *ptr)) {
+        *ptr = NULL;
+        g_string_free(s, TRUE);
+    };
+    return FALSE;
+}
+
+static inline gboolean
 nm_clear_g_cancellable(GCancellable **cancellable)
 {
     GCancellable *v;
@@ -1805,8 +1809,13 @@ NM_AUTO_DEFINE_FCN_VOID0(GMutex *, _nm_auto_unlock_g_mutex, g_mutex_unlock);
 
 #define nm_auto_unlock_g_mutex nm_auto(_nm_auto_unlock_g_mutex)
 
-#define _NM_G_MUTEX_LOCKED(lock, uniq) \
-    nm_auto_unlock_g_mutex GMutex *NM_UNIQ_T(nm_lock, uniq) = (lock)
+#define _NM_G_MUTEX_LOCKED(lock, uniq)                                      \
+    _nm_unused nm_auto_unlock_g_mutex GMutex *NM_UNIQ_T(nm_lock, uniq) = ({ \
+        GMutex *const _lock = (lock);                                       \
+                                                                            \
+        g_mutex_lock(_lock);                                                \
+        _lock;                                                              \
+    })
 
 #define NM_G_MUTEX_LOCKED(lock) _NM_G_MUTEX_LOCKED(lock, NM_UNIQ)
 
diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c
index c95d368d..97272ca3 100644
--- a/shared/nm-glib-aux/nm-random-utils.c
+++ b/shared/nm-glib-aux/nm-random-utils.c
@@ -122,8 +122,10 @@ fd_open:
          */
         has_high_quality = FALSE;
 
-        if (G_UNLIKELY(!rand))
+        if (G_UNLIKELY(!rand)) {
             rand = g_rand_new();
+            nm_utils_thread_local_register_destroy(rand, (GDestroyNotify) g_rand_free);
+        }
 
         nm_assert(n > 0);
         i = 0;
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index 3215a33b..e7cea23a 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -15,7 +15,9 @@
 #include <glib-unix.h>
 #include <net/if.h>
 #include <net/ethernet.h>
+#include <pthread.h>
 
+#include "c-list/src/c-list.h"
 #include "nm-errno.h"
 #include "nm-str-buf.h"
 
@@ -5708,3 +5710,80 @@ nm_utils_name_to_uid(const char *name, uid_t *out_uid)
         buf      = buf_heap;
     }
 }
+
+/*****************************************************************************/
+
+typedef struct {
+    CList          lst;
+    gpointer       tls_data;
+    GDestroyNotify destroy_notify;
+} TlsRegData;
+
+static pthread_key_t _tls_reg_key;
+
+static void
+_tls_reg_destroy(gpointer data)
+{
+    CList *     lst_head = data;
+    TlsRegData *entry;
+
+    if (!lst_head)
+        return;
+
+    /* For no strong reason are we destroying the elements in reverse
+     * order than they were added. It seems a bit more sensible (but shouldn't
+     * matter nor should you rely on that). */
+    while ((entry = c_list_last_entry(lst_head, TlsRegData, lst))) {
+        c_list_unlink_stale(&entry->lst);
+        entry->destroy_notify(entry->tls_data);
+        nm_g_slice_free(entry);
+    }
+
+    nm_g_slice_free(lst_head);
+}
+
+static void
+_tls_reg_make_key(void)
+{
+    if (pthread_key_create(&_tls_reg_key, _tls_reg_destroy) != 0)
+        g_return_if_reached();
+}
+
+/**
+ * nm_utils_thread_local_register_destroy:
+ * @tls_data: the thread local storage data that should be destroyed when the thread
+ *   exits. This pointer will be "owned" by the current thread. There is no way
+ *   to un-register the destruction.
+ * @destroy_notify: the free function that will be called when the thread exits.
+ *
+ * If _nm_tread_local storage is heap allocated it requires freeing the pointer
+ * when the thread exits. Use this function to register the pointer to be
+ * released.
+ *
+ * This function does not change errno.
+ */
+void
+nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify)
+{
+    NM_AUTO_PROTECT_ERRNO(errsv);
+    static pthread_once_t key_once = PTHREAD_ONCE_INIT;
+    CList *               lst_head;
+    TlsRegData *          entry;
+
+    nm_assert(destroy_notify);
+
+    if (pthread_once(&key_once, _tls_reg_make_key) != 0)
+        g_return_if_reached();
+
+    if ((lst_head = pthread_getspecific(_tls_reg_key)) == NULL) {
+        lst_head = g_slice_new(CList);
+        c_list_init(lst_head);
+        if (pthread_setspecific(_tls_reg_key, lst_head) != 0)
+            g_return_if_reached();
+    }
+
+    entry                 = g_slice_new(TlsRegData);
+    entry->tls_data       = tls_data;
+    entry->destroy_notify = destroy_notify;
+    c_list_link_tail(lst_head, &entry->lst);
+}
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index 7d330458..f9c21a00 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -2477,4 +2477,8 @@ gboolean nm_utils_is_specific_hostname(const char *name);
 char *   nm_utils_uid_to_name(uid_t uid);
 gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid);
 
+/*****************************************************************************/
+
+void nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy_notify);
+
 #endif /* __NM_SHARED_UTILS_H__ */