about summary refs log tree commit diff
path: root/src/libnm-udev-aux
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
committerMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
commite74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch)
tree3469f17ea9af91f7ff169b890633bda68b0cf76e /src/libnm-udev-aux
parentbfe522304da217296e2a61040f58e35ec5d6f3f2 (diff)
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/libnm-udev-aux')
-rw-r--r--src/libnm-udev-aux/meson.build14
-rw-r--r--src/libnm-udev-aux/nm-udev-utils.c265
-rw-r--r--src/libnm-udev-aux/nm-udev-utils.h33
3 files changed, 312 insertions, 0 deletions
diff --git a/src/libnm-udev-aux/meson.build b/src/libnm-udev-aux/meson.build
new file mode 100644
index 00000000..8ba12485
--- /dev/null
+++ b/src/libnm-udev-aux/meson.build
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+libnm_udev_aux = static_library(
+  'nm-udev-aux',
+  sources: 'nm-udev-utils.c',
+  include_directories: [
+    src_inc,
+    top_inc,
+  ],
+  dependencies: [
+    glib_dep,
+    libudev_dep,
+  ],
+)
diff --git a/src/libnm-udev-aux/nm-udev-utils.c b/src/libnm-udev-aux/nm-udev-utils.c
new file mode 100644
index 00000000..ef1cf26b
--- /dev/null
+++ b/src/libnm-udev-aux/nm-udev-utils.c
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ */
+
+#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
+
+#include "nm-udev-utils.h"
+
+#include <libudev.h>
+
+struct _NMPUdevClient {
+    char **              subsystems;
+    GSource *            watch_source;
+    struct udev *        udev;
+    struct udev_monitor *monitor;
+    NMUdevClientEvent    event_handler;
+    gpointer             event_user_data;
+};
+
+/*****************************************************************************/
+
+gboolean
+nm_udev_utils_property_as_boolean(const char *uproperty)
+{
+    /* taken from g_udev_device_get_property_as_boolean() */
+
+    if (uproperty) {
+        if (strcmp(uproperty, "1") == 0 || g_ascii_strcasecmp(uproperty, "true") == 0)
+            return TRUE;
+    }
+    return FALSE;
+}
+
+const char *
+nm_udev_utils_property_decode(const char *uproperty, char **to_free)
+{
+    const char *p;
+    char *      unescaped = NULL;
+    char *      n         = NULL;
+
+    if (!uproperty) {
+        *to_free = NULL;
+        return NULL;
+    }
+
+    p = uproperty;
+    while (*p) {
+        int a, b;
+
+        if (p[0] == '\\' && p[1] == 'x' && (a = g_ascii_xdigit_value(p[2])) >= 0
+            && (b = g_ascii_xdigit_value(p[3])) >= 0 && (a || b)) {
+            if (!n) {
+                gssize l = p - uproperty;
+
+                unescaped = g_malloc(l + strlen(p) + 1 - 3);
+                memcpy(unescaped, uproperty, l);
+                n = &unescaped[l];
+            }
+            *n++ = (a << 4) | b;
+            p += 4;
+        } else {
+            if (n)
+                *n++ = *p;
+            p++;
+        }
+    }
+
+    if (!n) {
+        *to_free = NULL;
+        return uproperty;
+    }
+
+    *n++ = '\0';
+    return (*to_free = unescaped);
+}
+
+char *
+nm_udev_utils_property_decode_cp(const char *uproperty)
+{
+    char *cpy;
+
+    uproperty = nm_udev_utils_property_decode(uproperty, &cpy);
+    return cpy ?: g_strdup(uproperty);
+}
+
+/*****************************************************************************/
+
+static void
+_subsystem_split(const char * subsystem_full,
+                 const char **out_subsystem,
+                 const char **out_devtype,
+                 char **      to_free)
+{
+    char *tmp, *s;
+
+    nm_assert(subsystem_full);
+    nm_assert(out_subsystem);
+    nm_assert(out_devtype);
+    nm_assert(to_free);
+
+    s = strstr(subsystem_full, "/");
+    if (s) {
+        tmp            = g_strdup(subsystem_full);
+        s              = &tmp[s - subsystem_full];
+        *s             = '\0';
+        *out_subsystem = tmp;
+        *out_devtype   = &s[1];
+        *to_free       = tmp;
+    } else {
+        *out_subsystem = subsystem_full;
+        *out_devtype   = NULL;
+        *to_free       = NULL;
+    }
+}
+
+static struct udev_enumerate *
+nm_udev_utils_enumerate(struct udev *uclient, const char *const *subsystems)
+{
+    struct udev_enumerate *enumerate;
+    guint                  n;
+
+    enumerate = udev_enumerate_new(uclient);
+
+    if (subsystems) {
+        for (n = 0; subsystems[n]; n++) {
+            const char *  subsystem;
+            const char *  devtype;
+            gs_free char *to_free = NULL;
+
+            _subsystem_split(subsystems[n], &subsystem, &devtype, &to_free);
+
+            udev_enumerate_add_match_subsystem(enumerate, subsystem);
+
+            if (devtype != NULL)
+                udev_enumerate_add_match_property(enumerate, "DEVTYPE", devtype);
+        }
+    }
+
+    return enumerate;
+}
+
+struct udev *
+nm_udev_client_get_udev(NMUdevClient *self)
+{
+    g_return_val_if_fail(self, NULL);
+
+    return self->udev;
+}
+
+struct udev_enumerate *
+nm_udev_client_enumerate_new(NMUdevClient *self)
+{
+    g_return_val_if_fail(self, NULL);
+
+    return nm_udev_utils_enumerate(self->udev, (const char *const *) self->subsystems);
+}
+
+/*****************************************************************************/
+
+static gboolean
+monitor_event(int fd, GIOCondition condition, gpointer user_data)
+{
+    NMUdevClient *      self = user_data;
+    struct udev_device *udevice;
+
+    if (!self->monitor)
+        goto out;
+
+    udevice = udev_monitor_receive_device(self->monitor);
+    if (udevice == NULL)
+        goto out;
+
+    self->event_handler(self, udevice, self->event_user_data);
+    udev_device_unref(udevice);
+
+out:
+    return TRUE;
+}
+
+/**
+ * nm_udev_client_new:
+ * @subsystems: the subsystems
+ * @event_handler: callback for events
+ * @event_user_data: user-data for @event_handler
+ *
+ * Basically, it is g_udev_client_new(), and most notably
+ * g_udev_client_constructed().
+ *
+ * Returns: a new NMUdevClient instance.
+ */
+NMUdevClient *
+nm_udev_client_new(const char *const *subsystems,
+                   NMUdevClientEvent  event_handler,
+                   gpointer           event_user_data)
+{
+    NMUdevClient *self;
+    guint         n;
+
+    self = g_slice_new0(NMUdevClient);
+
+    self->event_handler   = event_handler;
+    self->event_user_data = event_user_data;
+    self->subsystems      = subsystems && subsystems[0] ? g_strdupv((char **) subsystems) : NULL;
+
+    self->udev = udev_new();
+    if (!self->udev)
+        goto fail;
+
+    /* connect to event source */
+    if (self->event_handler) {
+        self->monitor = udev_monitor_new_from_netlink(self->udev, "udev");
+        if (!self->monitor)
+            goto fail;
+
+        if (self->subsystems) {
+            /* install subsystem filters to only wake up for certain events */
+            for (n = 0; self->subsystems[n]; n++) {
+                gs_free char *to_free = NULL;
+                const char *  subsystem;
+                const char *  devtype;
+
+                _subsystem_split(self->subsystems[n], &subsystem, &devtype, &to_free);
+                udev_monitor_filter_add_match_subsystem_devtype(self->monitor, subsystem, devtype);
+            }
+
+            /* listen to events, and buffer them */
+            udev_monitor_set_receive_buffer_size(self->monitor, 4 * 1024 * 1024);
+            udev_monitor_enable_receiving(self->monitor);
+
+            self->watch_source = nm_g_unix_fd_source_new(udev_monitor_get_fd(self->monitor),
+                                                         G_IO_IN,
+                                                         G_PRIORITY_DEFAULT,
+                                                         monitor_event,
+                                                         self,
+                                                         NULL);
+            g_source_attach(self->watch_source, g_main_context_get_thread_default());
+        }
+    }
+
+    return self;
+
+fail:
+    return nm_udev_client_destroy(self);
+}
+
+NMUdevClient *
+nm_udev_client_destroy(NMUdevClient *self)
+{
+    if (!self)
+        return NULL;
+
+    nm_clear_g_source_inst(&self->watch_source);
+
+    udev_monitor_unref(self->monitor);
+    self->monitor = NULL;
+    udev_unref(self->udev);
+    self->udev = NULL;
+
+    g_strfreev(self->subsystems);
+
+    g_slice_free(NMUdevClient, self);
+
+    return NULL;
+}
diff --git a/src/libnm-udev-aux/nm-udev-utils.h b/src/libnm-udev-aux/nm-udev-utils.h
new file mode 100644
index 00000000..191f9a89
--- /dev/null
+++ b/src/libnm-udev-aux/nm-udev-utils.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_UDEV_UTILS_H__
+#define __NM_UDEV_UTILS_H__
+
+struct udev;
+struct udev_device;
+struct udev_enumerate;
+
+gboolean    nm_udev_utils_property_as_boolean(const char *uproperty);
+const char *nm_udev_utils_property_decode(const char *uproperty, char **to_free);
+char *      nm_udev_utils_property_decode_cp(const char *uproperty);
+
+typedef struct _NMPUdevClient NMUdevClient;
+
+typedef void (*NMUdevClientEvent)(NMUdevClient *      udev_client,
+                                  struct udev_device *udevice,
+                                  gpointer            event_user_data);
+
+NMUdevClient *nm_udev_client_new(const char *const *subsystems,
+                                 NMUdevClientEvent  event_handler,
+                                 gpointer           event_user_data);
+
+NMUdevClient *nm_udev_client_destroy(NMUdevClient *self);
+
+struct udev *nm_udev_client_get_udev(NMUdevClient *self);
+
+struct udev_enumerate *nm_udev_client_enumerate_new(NMUdevClient *self);
+
+#endif /* __NM_UDEV_UTILS_H__ */