summaryrefslogtreecommitdiff
path: root/src/libudev/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/libudev/src')
l---------src/libudev/src/GNUmakefile1
-rw-r--r--src/libudev/src/Makefile45
-rw-r--r--src/libudev/src/libudev-device-internal.h59
-rw-r--r--src/libudev/src/libudev-device-private.c412
-rw-r--r--src/libudev/src/libudev-device.c960
-rw-r--r--src/libudev/src/libudev-enumerate.c420
-rw-r--r--src/libudev/src/libudev-hwdb.c146
-rw-r--r--src/libudev/src/libudev-list.c351
-rw-r--r--src/libudev/src/libudev-monitor.c846
-rw-r--r--src/libudev/src/libudev-private.h150
-rw-r--r--src/libudev/src/libudev-queue.c269
-rw-r--r--src/libudev/src/libudev-util.c269
-rw-r--r--src/libudev/src/libudev.c254
-rw-r--r--src/libudev/src/udev.h217
14 files changed, 4399 insertions, 0 deletions
diff --git a/src/libudev/src/GNUmakefile b/src/libudev/src/GNUmakefile
new file mode 120000
index 0000000000..95e5924740
--- /dev/null
+++ b/src/libudev/src/GNUmakefile
@@ -0,0 +1 @@
+../../../GNUmakefile \ No newline at end of file
diff --git a/src/libudev/src/Makefile b/src/libudev/src/Makefile
new file mode 100644
index 0000000000..46f122d52d
--- /dev/null
+++ b/src/libudev/src/Makefile
@@ -0,0 +1,45 @@
+# -*- Mode: makefile; indent-tabs-mode: t -*-
+#
+# This file is part of systemd.
+#
+# Copyright 2010-2012 Lennart Poettering
+# Copyright 2010-2012 Kay Sievers
+# Copyright 2013 Zbigniew Jędrzejewski-Szmek
+# Copyright 2013 David Strauss
+# Copyright 2016 Luke Shumaker
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk
+include $(topsrcdir)/build-aux/Makefile.head.mk
+
+libudev_la_SOURCES =\
+ src/libudev/libudev.sym \
+ src/libudev/libudev-private.h \
+ src/libudev/libudev-device-internal.h \
+ src/libudev/libudev.c \
+ src/libudev/libudev-list.c \
+ src/libudev/libudev-util.c \
+ src/libudev/libudev-device.c \
+ src/libudev/libudev-device-private.c \
+ src/libudev/libudev-enumerate.c \
+ src/libudev/libudev-monitor.c \
+ src/libudev/libudev-queue.c \
+ src/libudev/libudev-hwdb.c
+noinst_LTLIBRARIES += \
+ libudev-internal.la
+
+libudev_internal_la_SOURCES =\
+ $(libudev_la_SOURCES)
+
+include $(topsrcdir)/build-aux/Makefile.tail.mk
diff --git a/src/libudev/src/libudev-device-internal.h b/src/libudev/src/libudev-device-internal.h
new file mode 100644
index 0000000000..9987f80499
--- /dev/null
+++ b/src/libudev/src/libudev-device-internal.h
@@ -0,0 +1,59 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <libudev.h>
+
+#include "systemd-staging/sd-device.h"
+
+#include "libudev-private.h"
+
+/**
+ * udev_device:
+ *
+ * Opaque object representing one kernel sys device.
+ */
+struct udev_device {
+ struct udev *udev;
+
+ /* real device object */
+ sd_device *device;
+
+ /* legacy */
+ int refcount;
+
+ struct udev_device *parent;
+ bool parent_set;
+
+ struct udev_list properties;
+ uint64_t properties_generation;
+ struct udev_list tags;
+ uint64_t tags_generation;
+ struct udev_list devlinks;
+ uint64_t devlinks_generation;
+ bool properties_read:1;
+ bool tags_read:1;
+ bool devlinks_read:1;
+ struct udev_list sysattrs;
+ bool sysattrs_read;
+};
+
+struct udev_device *udev_device_new(struct udev *udev);
diff --git a/src/libudev/src/libudev-device-private.c b/src/libudev/src/libudev-device-private.c
new file mode 100644
index 0000000000..97b60da3f1
--- /dev/null
+++ b/src/libudev/src/libudev-device-private.c
@@ -0,0 +1,412 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <libudev.h>
+
+#include "sd-device/device-private.h"
+
+#include "libudev-device-internal.h"
+#include "libudev-private.h"
+
+int udev_device_tag_index(struct udev_device *udev_device, struct udev_device *udev_device_old, bool add) {
+ sd_device *device_old = NULL;
+ int r;
+
+ assert(udev_device);
+
+ if (udev_device_old)
+ device_old = udev_device_old->device;
+
+ r = device_tag_index(udev_device->device, device_old, add);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_update_db(struct udev_device *udev_device) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_update_db(udev_device->device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_delete_db(struct udev_device *udev_device) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_delete_db(udev_device->device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_get_ifindex(struct udev_device *udev_device) {
+ int r, ifindex;
+
+ assert(udev_device);
+
+ r = sd_device_get_ifindex(udev_device->device, &ifindex);
+ if (r < 0)
+ return r;
+
+ return ifindex;
+}
+
+const char *udev_device_get_devpath_old(struct udev_device *udev_device) {
+ const char *devpath_old = NULL;
+ int r;
+
+ assert(udev_device);
+
+ r = sd_device_get_property_value(udev_device->device, "DEVPATH_OLD", &devpath_old);
+ if (r < 0 && r != -ENOENT) {
+ errno = -r;
+ return NULL;
+ }
+
+ return devpath_old;
+}
+
+mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) {
+ mode_t mode;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_devnode_mode(udev_device->device, &mode);
+ if (r < 0) {
+ errno = -r;
+ return 0;
+ }
+
+ return mode;
+}
+
+uid_t udev_device_get_devnode_uid(struct udev_device *udev_device) {
+ uid_t uid;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_devnode_uid(udev_device->device, &uid);
+ if (r < 0) {
+ errno = -r;
+ return 0;
+ }
+
+ return uid;
+}
+
+gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) {
+ gid_t gid;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_devnode_gid(udev_device->device, &gid);
+ if (r < 0) {
+ errno = -r;
+ return 0;
+ }
+
+ return gid;
+}
+
+void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *udev_device_old) {
+ assert(udev_device);
+
+ device_ensure_usec_initialized(udev_device->device,
+ udev_device_old ? udev_device_old->device : NULL);
+}
+
+char **udev_device_get_properties_envp(struct udev_device *udev_device) {
+ char **envp;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_properties_strv(udev_device->device, &envp);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return envp;
+}
+
+ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) {
+ const char *nulstr;
+ size_t len;
+ int r;
+
+ assert(udev_device);
+ assert(buf);
+
+ r = device_get_properties_nulstr(udev_device->device, (const uint8_t **)&nulstr, &len);
+ if (r < 0)
+ return r;
+
+ *buf = nulstr;
+
+ return len;
+}
+
+int udev_device_get_devlink_priority(struct udev_device *udev_device) {
+ int priority, r;
+
+ assert(udev_device);
+
+ r = device_get_devlink_priority(udev_device->device, &priority);
+ if (r < 0)
+ return r;
+
+ return priority;
+}
+
+int udev_device_get_watch_handle(struct udev_device *udev_device) {
+ int handle, r;
+
+ assert(udev_device);
+
+ r = device_get_watch_handle(udev_device->device, &handle);
+ if (r < 0)
+ return r;
+
+ return handle;
+}
+
+void udev_device_set_is_initialized(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_set_is_initialized(udev_device->device);
+}
+
+int udev_device_rename(struct udev_device *udev_device, const char *name) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_rename(udev_device->device, name);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+struct udev_device *udev_device_shallow_clone(struct udev_device *old_device) {
+ struct udev_device *device;
+ int r;
+
+ assert(old_device);
+
+ device = udev_device_new(old_device->udev);
+ if (!device)
+ return NULL;
+
+ r = device_shallow_clone(old_device->device, &device->device);
+ if (r < 0) {
+ udev_device_unref(device);
+ errno = -r;
+ return NULL;
+ }
+
+ return device;
+}
+
+struct udev_device *udev_device_clone_with_db(struct udev_device *udev_device_old) {
+ struct udev_device *udev_device;
+ int r;
+
+ assert(udev_device_old);
+
+ udev_device = udev_device_new(udev_device_old->udev);
+ if (!udev_device)
+ return NULL;
+
+ r = device_clone_with_db(udev_device_old->device, &udev_device->device);
+ if (r < 0) {
+ udev_device_unref(udev_device);
+ errno = -r;
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) {
+ struct udev_device *device;
+ int r;
+
+ device = udev_device_new(udev);
+ if (!device)
+ return NULL;
+
+ r = device_new_from_nulstr(&device->device, (uint8_t*)nulstr, buflen);
+ if (r < 0) {
+ udev_device_unref(device);
+ errno = -r;
+ return NULL;
+ }
+
+ return device;
+}
+
+struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action) {
+ struct udev_device *device;
+ int r;
+
+ device = udev_device_new(udev);
+ if (!device)
+ return NULL;
+
+ r = device_new_from_synthetic_event(&device->device, syspath, action);
+ if (r < 0) {
+ udev_device_unref(device);
+ errno = -r;
+ return NULL;
+ }
+
+ return device;
+}
+
+int udev_device_copy_properties(struct udev_device *udev_device_dst, struct udev_device *udev_device_src) {
+ int r;
+
+ assert(udev_device_dst);
+ assert(udev_device_src);
+
+ r = device_copy_properties(udev_device_dst->device, udev_device_src->device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+const char *udev_device_get_id_filename(struct udev_device *udev_device) {
+ const char *filename;
+ int r;
+
+ assert(udev_device);
+
+ r = device_get_id_filename(udev_device->device, &filename);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return filename;
+}
+
+int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) {
+
+ assert(udev_device);
+
+ device_set_watch_handle(udev_device->device, handle);
+
+ return 0;
+}
+
+void udev_device_set_db_persist(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_set_db_persist(udev_device->device);
+}
+
+int udev_device_set_devlink_priority(struct udev_device *udev_device, int priority) {
+ assert(udev_device);
+
+ device_set_devlink_priority(udev_device->device, priority);
+
+ return 0;
+}
+
+int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_add_devlink(udev_device->device, devlink);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_add_property(struct udev_device *udev_device, const char *property, const char *value) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_add_property(udev_device->device, property, value);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int udev_device_add_tag(struct udev_device *udev_device, const char *tag) {
+ int r;
+
+ assert(udev_device);
+
+ r = device_add_tag(udev_device->device, tag);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+void udev_device_remove_tag(struct udev_device *udev_device, const char *tag) {
+ assert(udev_device);
+
+ device_remove_tag(udev_device->device, tag);
+}
+
+void udev_device_cleanup_tags_list(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_cleanup_tags(udev_device->device);
+}
+
+void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_cleanup_devlinks(udev_device->device);
+}
+
+void udev_device_set_info_loaded(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_seal(udev_device->device);
+}
+
+void udev_device_read_db(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ device_read_db_force(udev_device->device);
+}
diff --git a/src/libudev/src/libudev-device.c b/src/libudev/src/libudev-device.c
new file mode 100644
index 0000000000..d6e2c5fa6e
--- /dev/null
+++ b/src/libudev/src/libudev-device.c
@@ -0,0 +1,960 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <linux/sockios.h>
+
+#include <libudev.h>
+
+#include "sd-device/device-private.h"
+#include "sd-device/device-util.h"
+#include "systemd-basic/alloc-util.h"
+#include "systemd-basic/parse-util.h"
+#include "systemd-staging/sd-device.h"
+
+#include "libudev-device-internal.h"
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev-device
+ * @short_description: kernel sys devices
+ *
+ * Representation of kernel sys devices. Devices are uniquely identified
+ * by their syspath, every device has exactly one path in the kernel sys
+ * filesystem. Devices usually belong to a kernel subsystem, and have
+ * a unique name inside that subsystem.
+ */
+
+/**
+ * udev_device_get_seqnum:
+ * @udev_device: udev device
+ *
+ * This is only valid if the device was received through a monitor. Devices read from
+ * sys do not have a sequence number.
+ *
+ * Returns: the kernel event sequence number, or 0 if there is no sequence number available.
+ **/
+_public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device)
+{
+ const char *seqnum;
+ unsigned long long ret;
+ int r;
+
+ assert_return_errno(udev_device, 0, EINVAL);
+
+ r = sd_device_get_property_value(udev_device->device, "SEQNUM", &seqnum);
+ if (r == -ENOENT)
+ return 0;
+ else if (r < 0) {
+ errno = -r;
+ return 0;
+ }
+
+ r = safe_atollu(seqnum, &ret);
+ if (r < 0) {
+ errno = -r;
+ return 0;
+ }
+
+ return ret;
+}
+
+/**
+ * udev_device_get_devnum:
+ * @udev_device: udev device
+ *
+ * Get the device major/minor number.
+ *
+ * Returns: the dev_t number.
+ **/
+_public_ dev_t udev_device_get_devnum(struct udev_device *udev_device)
+{
+ dev_t devnum;
+ int r;
+
+ assert_return_errno(udev_device, makedev(0, 0), EINVAL);
+
+ r = sd_device_get_devnum(udev_device->device, &devnum);
+ if (r < 0) {
+ errno = -r;
+ return makedev(0, 0);
+ }
+
+ return devnum;
+}
+
+/**
+ * udev_device_get_driver:
+ * @udev_device: udev device
+ *
+ * Get the kernel driver name.
+ *
+ * Returns: the driver name string, or #NULL if there is no driver attached.
+ **/
+_public_ const char *udev_device_get_driver(struct udev_device *udev_device)
+{
+ const char *driver;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_driver(udev_device->device, &driver);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return driver;
+}
+
+/**
+ * udev_device_get_devtype:
+ * @udev_device: udev device
+ *
+ * Retrieve the devtype string of the udev device.
+ *
+ * Returns: the devtype name of the udev device, or #NULL if it can not be determined
+ **/
+_public_ const char *udev_device_get_devtype(struct udev_device *udev_device)
+{
+ const char *devtype;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_devtype(udev_device->device, &devtype);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return devtype;
+}
+
+/**
+ * udev_device_get_subsystem:
+ * @udev_device: udev device
+ *
+ * Retrieve the subsystem string of the udev device. The string does not
+ * contain any "/".
+ *
+ * Returns: the subsystem name of the udev device, or #NULL if it can not be determined
+ **/
+_public_ const char *udev_device_get_subsystem(struct udev_device *udev_device)
+{
+ const char *subsystem;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_subsystem(udev_device->device, &subsystem);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ } else if (!subsystem)
+ errno = ENODATA;
+
+ return subsystem;
+}
+
+/**
+ * udev_device_get_property_value:
+ * @udev_device: udev device
+ * @key: property name
+ *
+ * Get the value of a given property.
+ *
+ * Returns: the property string, or #NULL if there is no such property.
+ **/
+_public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key)
+{
+ const char *value = NULL;
+ int r;
+
+ assert_return_errno(udev_device && key, NULL, EINVAL);
+
+ r = sd_device_get_property_value(udev_device->device, key, &value);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return value;
+}
+
+struct udev_device *udev_device_new(struct udev *udev) {
+ struct udev_device *udev_device;
+
+ assert_return_errno(udev, NULL, EINVAL);
+
+ udev_device = new0(struct udev_device, 1);
+ if (!udev_device) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ udev_device->refcount = 1;
+ udev_device->udev = udev;
+ udev_list_init(udev, &udev_device->properties, true);
+ udev_list_init(udev, &udev_device->tags, true);
+ udev_list_init(udev, &udev_device->sysattrs, true);
+ udev_list_init(udev, &udev_device->devlinks, true);
+
+ return udev_device;
+}
+
+/**
+ * udev_device_new_from_syspath:
+ * @udev: udev library context
+ * @syspath: sys device path including sys directory
+ *
+ * Create new udev device, and fill in information from the sys
+ * device and the udev database entry. The syspath is the absolute
+ * path to the device, including the sys mount point.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev device.
+ *
+ * Returns: a new udev device, or #NULL, if it does not exist
+ **/
+_public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) {
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
+
+ r = sd_device_new_from_syspath(&udev_device->device, syspath);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+/**
+ * udev_device_new_from_devnum:
+ * @udev: udev library context
+ * @type: char or block device
+ * @devnum: device major/minor number
+ *
+ * Create new udev device, and fill in information from the sys
+ * device and the udev database entry. The device is looked-up
+ * by its major/minor number and type. Character and block device
+ * numbers are not unique across the two types.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev device.
+ *
+ * Returns: a new udev device, or #NULL, if it does not exist
+ **/
+_public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum)
+{
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
+
+ r = sd_device_new_from_devnum(&udev_device->device, type, devnum);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+/**
+ * udev_device_new_from_device_id:
+ * @udev: udev library context
+ * @id: text string identifying a kernel device
+ *
+ * Create new udev device, and fill in information from the sys
+ * device and the udev database entry. The device is looked-up
+ * by a special string:
+ * b8:2 - block device major:minor
+ * c128:1 - char device major:minor
+ * n3 - network device ifindex
+ * +sound:card29 - kernel driver core subsystem:device name
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev device.
+ *
+ * Returns: a new udev device, or #NULL, if it does not exist
+ **/
+_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id)
+{
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
+
+ r = sd_device_new_from_device_id(&udev_device->device, id);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+/**
+ * udev_device_new_from_subsystem_sysname:
+ * @udev: udev library context
+ * @subsystem: the subsystem of the device
+ * @sysname: the name of the device
+ *
+ * Create new udev device, and fill in information from the sys device
+ * and the udev database entry. The device is looked up by the subsystem
+ * and name string of the device, like "mem" / "zero", or "block" / "sda".
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev device.
+ *
+ * Returns: a new udev device, or #NULL, if it does not exist
+ **/
+_public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
+{
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
+
+ r = sd_device_new_from_subsystem_sysname(&udev_device->device, subsystem, sysname);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+/**
+ * udev_device_new_from_environment
+ * @udev: udev library context
+ *
+ * Create new udev device, and fill in information from the
+ * current process environment. This only works reliable if
+ * the process is called from a udev rule. It is usually used
+ * for tools executed from IMPORT= rules.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev device.
+ *
+ * Returns: a new udev device, or #NULL, if it does not exist
+ **/
+_public_ struct udev_device *udev_device_new_from_environment(struct udev *udev)
+{
+ struct udev_device *udev_device;
+ int r;
+
+ udev_device = udev_device_new(udev);
+ if (!udev_device)
+ return NULL;
+
+ r = device_new_from_strv(&udev_device->device, environ);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(udev_device);
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+static struct udev_device *device_new_from_parent(struct udev_device *child)
+{
+ struct udev_device *parent;
+ int r;
+
+ assert_return_errno(child, NULL, EINVAL);
+
+ parent = udev_device_new(child->udev);
+ if (!parent)
+ return NULL;
+
+ r = sd_device_get_parent(child->device, &parent->device);
+ if (r < 0) {
+ errno = -r;
+ udev_device_unref(parent);
+ return NULL;
+ }
+
+ /* the parent is unref'ed with the child, so take a ref from libudev as well */
+ sd_device_ref(parent->device);
+
+ return parent;
+}
+
+/**
+ * udev_device_get_parent:
+ * @udev_device: the device to start searching from
+ *
+ * Find the next parent device, and fill in information from the sys
+ * device and the udev database entry.
+ *
+ * Returned device is not referenced. It is attached to the child
+ * device, and will be cleaned up when the child device is cleaned up.
+ *
+ * It is not necessarily just the upper level directory, empty or not
+ * recognized sys directories are ignored.
+ *
+ * It can be called as many times as needed, without caring about
+ * references.
+ *
+ * Returns: a new udev device, or #NULL, if it no parent exist.
+ **/
+_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
+{
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (!udev_device->parent_set) {
+ udev_device->parent_set = true;
+ udev_device->parent = device_new_from_parent(udev_device);
+ }
+
+ /* TODO: errno will differ here in case parent == NULL */
+ return udev_device->parent;
+}
+
+/**
+ * udev_device_get_parent_with_subsystem_devtype:
+ * @udev_device: udev device to start searching from
+ * @subsystem: the subsystem of the device
+ * @devtype: the type (DEVTYPE) of the device
+ *
+ * Find the next parent device, with a matching subsystem and devtype
+ * value, and fill in information from the sys device and the udev
+ * database entry.
+ *
+ * If devtype is #NULL, only subsystem is checked, and any devtype will
+ * match.
+ *
+ * Returned device is not referenced. It is attached to the child
+ * device, and will be cleaned up when the child device is cleaned up.
+ *
+ * It can be called as many times as needed, without caring about
+ * references.
+ *
+ * Returns: a new udev device, or #NULL if no matching parent exists.
+ **/
+_public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype)
+{
+ sd_device *parent;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ /* this relies on the fact that finding the subdevice of a parent or the
+ parent of a subdevice commute */
+
+ /* first find the correct sd_device */
+ r = sd_device_get_parent_with_subsystem_devtype(udev_device->device, subsystem, devtype, &parent);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ /* then walk the chain of udev_device parents until the corresponding
+ one is found */
+ while ((udev_device = udev_device_get_parent(udev_device))) {
+ if (udev_device->device == parent)
+ return udev_device;
+ }
+
+ errno = ENOENT;
+ return NULL;
+}
+
+/**
+ * udev_device_get_udev:
+ * @udev_device: udev device
+ *
+ * Retrieve the udev library context the device was created with.
+ *
+ * Returns: the udev library context
+ **/
+_public_ struct udev *udev_device_get_udev(struct udev_device *udev_device)
+{
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ return udev_device->udev;
+}
+
+/**
+ * udev_device_ref:
+ * @udev_device: udev device
+ *
+ * Take a reference of a udev device.
+ *
+ * Returns: the passed udev device
+ **/
+_public_ struct udev_device *udev_device_ref(struct udev_device *udev_device)
+{
+ if (udev_device)
+ udev_device->refcount++;
+
+ return udev_device;
+}
+
+/**
+ * udev_device_unref:
+ * @udev_device: udev device
+ *
+ * Drop a reference of a udev device. If the refcount reaches zero,
+ * the resources of the device will be released.
+ *
+ * Returns: #NULL
+ **/
+_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device)
+{
+ if (udev_device && (-- udev_device->refcount) == 0) {
+ sd_device_unref(udev_device->device);
+ udev_device_unref(udev_device->parent);
+
+ udev_list_cleanup(&udev_device->properties);
+ udev_list_cleanup(&udev_device->sysattrs);
+ udev_list_cleanup(&udev_device->tags);
+ udev_list_cleanup(&udev_device->devlinks);
+
+ free(udev_device);
+ }
+
+ return NULL;
+}
+
+/**
+ * udev_device_get_devpath:
+ * @udev_device: udev device
+ *
+ * Retrieve the kernel devpath value of the udev device. The path
+ * does not contain the sys mount point, and starts with a '/'.
+ *
+ * Returns: the devpath of the udev device
+ **/
+_public_ const char *udev_device_get_devpath(struct udev_device *udev_device)
+{
+ const char *devpath;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_devpath(udev_device->device, &devpath);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return devpath;
+}
+
+/**
+ * udev_device_get_syspath:
+ * @udev_device: udev device
+ *
+ * Retrieve the sys path of the udev device. The path is an
+ * absolute path and starts with the sys mount point.
+ *
+ * Returns: the sys path of the udev device
+ **/
+_public_ const char *udev_device_get_syspath(struct udev_device *udev_device)
+{
+ const char *syspath;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_syspath(udev_device->device, &syspath);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return syspath;
+}
+
+/**
+ * udev_device_get_sysname:
+ * @udev_device: udev device
+ *
+ * Get the kernel device name in /sys.
+ *
+ * Returns: the name string of the device
+ **/
+_public_ const char *udev_device_get_sysname(struct udev_device *udev_device)
+{
+ const char *sysname;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_sysname(udev_device->device, &sysname);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return sysname;
+}
+
+/**
+ * udev_device_get_sysnum:
+ * @udev_device: udev device
+ *
+ * Get the instance number of the device.
+ *
+ * Returns: the trailing number string of the device name
+ **/
+_public_ const char *udev_device_get_sysnum(struct udev_device *udev_device)
+{
+ const char *sysnum;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_sysnum(udev_device->device, &sysnum);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return sysnum;
+}
+
+/**
+ * udev_device_get_devnode:
+ * @udev_device: udev device
+ *
+ * Retrieve the device node file name belonging to the udev device.
+ * The path is an absolute path, and starts with the device directory.
+ *
+ * Returns: the device node file name of the udev device, or #NULL if no device node exists
+ **/
+_public_ const char *udev_device_get_devnode(struct udev_device *udev_device)
+{
+ const char *devnode;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_devname(udev_device->device, &devnode);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return devnode;
+}
+
+/**
+ * udev_device_get_devlinks_list_entry:
+ * @udev_device: udev device
+ *
+ * Retrieve the list of device links pointing to the device file of
+ * the udev device. The next list entry can be retrieved with
+ * udev_list_entry_get_next(), which returns #NULL if no more entries exist.
+ * The devlink path can be retrieved from the list entry by
+ * udev_list_entry_get_name(). The path is an absolute path, and starts with
+ * the device directory.
+ *
+ * Returns: the first entry of the device node link list
+ **/
+_public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device)
+{
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (device_get_devlinks_generation(udev_device->device) != udev_device->devlinks_generation ||
+ !udev_device->devlinks_read) {
+ const char *devlink;
+
+ udev_list_cleanup(&udev_device->devlinks);
+
+ FOREACH_DEVICE_DEVLINK(udev_device->device, devlink)
+ udev_list_entry_add(&udev_device->devlinks, devlink, NULL);
+
+ udev_device->devlinks_read = true;
+ udev_device->devlinks_generation = device_get_devlinks_generation(udev_device->device);
+ }
+
+ return udev_list_get_entry(&udev_device->devlinks);
+}
+
+/**
+ * udev_device_get_event_properties_entry:
+ * @udev_device: udev device
+ *
+ * Retrieve the list of key/value device properties of the udev
+ * device. The next list entry can be retrieved with udev_list_entry_get_next(),
+ * which returns #NULL if no more entries exist. The property name
+ * can be retrieved from the list entry by udev_list_entry_get_name(),
+ * the property value by udev_list_entry_get_value().
+ *
+ * Returns: the first entry of the property list
+ **/
+_public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device)
+{
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (device_get_properties_generation(udev_device->device) != udev_device->properties_generation ||
+ !udev_device->properties_read) {
+ const char *key, *value;
+
+ udev_list_cleanup(&udev_device->properties);
+
+ FOREACH_DEVICE_PROPERTY(udev_device->device, key, value)
+ udev_list_entry_add(&udev_device->properties, key, value);
+
+ udev_device->properties_read = true;
+ udev_device->properties_generation = device_get_properties_generation(udev_device->device);
+ }
+
+ return udev_list_get_entry(&udev_device->properties);
+}
+
+/**
+ * udev_device_get_action:
+ * @udev_device: udev device
+ *
+ * This is only valid if the device was received through a monitor. Devices read from
+ * sys do not have an action string. Usual actions are: add, remove, change, online,
+ * offline.
+ *
+ * Returns: the kernel action value, or #NULL if there is no action value available.
+ **/
+_public_ const char *udev_device_get_action(struct udev_device *udev_device) {
+ const char *action = NULL;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_property_value(udev_device->device, "ACTION", &action);
+ if (r < 0 && r != -ENOENT) {
+ errno = -r;
+ return NULL;
+ }
+
+ return action;
+}
+
+/**
+ * udev_device_get_usec_since_initialized:
+ * @udev_device: udev device
+ *
+ * Return the number of microseconds passed since udev set up the
+ * device for the first time.
+ *
+ * This is only implemented for devices with need to store properties
+ * in the udev database. All other devices return 0 here.
+ *
+ * Returns: the number of microseconds since the device was first seen.
+ **/
+_public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device)
+{
+ usec_t ts;
+ int r;
+
+ assert_return(udev_device, -EINVAL);
+
+ r = sd_device_get_usec_since_initialized(udev_device->device, &ts);
+ if (r < 0) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ return ts;
+}
+
+/**
+ * udev_device_get_sysattr_value:
+ * @udev_device: udev device
+ * @sysattr: attribute name
+ *
+ * The retrieved value is cached in the device. Repeated calls will return the same
+ * value and not open the attribute again.
+ *
+ * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value.
+ **/
+_public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr)
+{
+ const char *value;
+ int r;
+
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ r = sd_device_get_sysattr_value(udev_device->device, sysattr, &value);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ return value;
+}
+
+/**
+ * udev_device_set_sysattr_value:
+ * @udev_device: udev device
+ * @sysattr: attribute name
+ * @value: new value to be set
+ *
+ * Update the contents of the sys attribute and the cached value of the device.
+ *
+ * Returns: Negative error code on failure or 0 on success.
+ **/
+_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value)
+{
+ int r;
+
+ assert_return(udev_device, -EINVAL);
+
+ r = sd_device_set_sysattr_value(udev_device->device, sysattr, value);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/**
+ * udev_device_get_sysattr_list_entry:
+ * @udev_device: udev device
+ *
+ * Retrieve the list of available sysattrs, with value being empty;
+ * This just return all available sysfs attributes for a particular
+ * device without reading their values.
+ *
+ * Returns: the first entry of the property list
+ **/
+_public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device)
+{
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (!udev_device->sysattrs_read) {
+ const char *sysattr;
+
+ udev_list_cleanup(&udev_device->sysattrs);
+
+ FOREACH_DEVICE_SYSATTR(udev_device->device, sysattr)
+ udev_list_entry_add(&udev_device->sysattrs, sysattr, NULL);
+
+ udev_device->sysattrs_read = true;
+ }
+
+ return udev_list_get_entry(&udev_device->sysattrs);
+}
+
+/**
+ * udev_device_get_is_initialized:
+ * @udev_device: udev device
+ *
+ * Check if udev has already handled the device and has set up
+ * device node permissions and context, or has renamed a network
+ * device.
+ *
+ * This is only implemented for devices with a device node
+ * or network interfaces. All other devices return 1 here.
+ *
+ * Returns: 1 if the device is set up. 0 otherwise.
+ **/
+_public_ int udev_device_get_is_initialized(struct udev_device *udev_device)
+{
+ int r, initialized;
+
+ assert_return(udev_device, -EINVAL);
+
+ r = sd_device_get_is_initialized(udev_device->device, &initialized);
+ if (r < 0) {
+ errno = -r;
+
+ return 0;
+ }
+
+ return initialized;
+}
+
+/**
+ * udev_device_get_tags_list_entry:
+ * @udev_device: udev device
+ *
+ * Retrieve the list of tags attached to the udev device. The next
+ * list entry can be retrieved with udev_list_entry_get_next(),
+ * which returns #NULL if no more entries exist. The tag string
+ * can be retrieved from the list entry by udev_list_entry_get_name().
+ *
+ * Returns: the first entry of the tag list
+ **/
+_public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device)
+{
+ assert_return_errno(udev_device, NULL, EINVAL);
+
+ if (device_get_tags_generation(udev_device->device) != udev_device->tags_generation ||
+ !udev_device->tags_read) {
+ const char *tag;
+
+ udev_list_cleanup(&udev_device->tags);
+
+ FOREACH_DEVICE_TAG(udev_device->device, tag)
+ udev_list_entry_add(&udev_device->tags, tag, NULL);
+
+ udev_device->tags_read = true;
+ udev_device->tags_generation = device_get_tags_generation(udev_device->device);
+ }
+
+ return udev_list_get_entry(&udev_device->tags);
+}
+
+/**
+ * udev_device_has_tag:
+ * @udev_device: udev device
+ * @tag: tag name
+ *
+ * Check if a given device has a certain tag associated.
+ *
+ * Returns: 1 if the tag is found. 0 otherwise.
+ **/
+_public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
+{
+ assert_return(udev_device, 0);
+
+ return sd_device_has_tag(udev_device->device, tag);
+}
diff --git a/src/libudev/src/libudev-enumerate.c b/src/libudev/src/libudev-enumerate.c
new file mode 100644
index 0000000000..53de8c1a41
--- /dev/null
+++ b/src/libudev/src/libudev-enumerate.c
@@ -0,0 +1,420 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2015 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <libudev.h>
+
+#include "sd-device/device-enumerator-private.h"
+#include "sd-device/device-util.h"
+#include "systemd-basic/alloc-util.h"
+#include "systemd-staging/sd-device.h"
+
+#include "libudev-device-internal.h"
+
+/**
+ * SECTION:libudev-enumerate
+ * @short_description: lookup and sort sys devices
+ *
+ * Lookup devices in the sys filesystem, filter devices by properties,
+ * and return a sorted list of devices.
+ */
+
+/**
+ * udev_enumerate:
+ *
+ * Opaque object representing one device lookup/sort context.
+ */
+struct udev_enumerate {
+ struct udev *udev;
+ int refcount;
+ struct udev_list devices_list;
+ bool devices_uptodate:1;
+
+ sd_device_enumerator *enumerator;
+};
+
+/**
+ * udev_enumerate_new:
+ * @udev: udev library context
+ *
+ * Create an enumeration context to scan /sys.
+ *
+ * Returns: an enumeration context.
+ **/
+_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
+ _cleanup_free_ struct udev_enumerate *udev_enumerate = NULL;
+ struct udev_enumerate *ret;
+ int r;
+
+ assert_return_errno(udev, NULL, EINVAL);
+
+ udev_enumerate = new0(struct udev_enumerate, 1);
+ if (!udev_enumerate) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ r = sd_device_enumerator_new(&udev_enumerate->enumerator);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ r = sd_device_enumerator_allow_uninitialized(udev_enumerate->enumerator);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ udev_enumerate->refcount = 1;
+ udev_enumerate->udev = udev;
+
+ udev_list_init(udev, &udev_enumerate->devices_list, false);
+
+ ret = udev_enumerate;
+ udev_enumerate = NULL;
+
+ return ret;
+}
+
+/**
+ * udev_enumerate_ref:
+ * @udev_enumerate: context
+ *
+ * Take a reference of a enumeration context.
+ *
+ * Returns: the passed enumeration context
+ **/
+_public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) {
+ if (udev_enumerate)
+ udev_enumerate->refcount++;
+
+ return udev_enumerate;
+}
+
+/**
+ * udev_enumerate_unref:
+ * @udev_enumerate: context
+ *
+ * Drop a reference of an enumeration context. If the refcount reaches zero,
+ * all resources of the enumeration context will be released.
+ *
+ * Returns: #NULL
+ **/
+_public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) {
+ if (udev_enumerate && (-- udev_enumerate->refcount) == 0) {
+ udev_list_cleanup(&udev_enumerate->devices_list);
+ sd_device_enumerator_unref(udev_enumerate->enumerator);
+ free(udev_enumerate);
+ }
+
+ return NULL;
+}
+
+/**
+ * udev_enumerate_get_udev:
+ * @udev_enumerate: context
+ *
+ * Get the udev library context.
+ *
+ * Returns: a pointer to the context.
+ */
+_public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) {
+ assert_return_errno(udev_enumerate, NULL, EINVAL);
+
+ return udev_enumerate->udev;
+}
+
+/**
+ * udev_enumerate_get_list_entry:
+ * @udev_enumerate: context
+ *
+ * Get the first entry of the sorted list of device paths.
+ *
+ * Returns: a udev_list_entry.
+ */
+_public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) {
+ assert_return_errno(udev_enumerate, NULL, EINVAL);
+
+ if (!udev_enumerate->devices_uptodate) {
+ sd_device *device;
+
+ udev_list_cleanup(&udev_enumerate->devices_list);
+
+ FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) {
+ const char *syspath;
+ int r;
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0) {
+ errno = -r;
+ return NULL;
+ }
+
+ udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL);
+ }
+
+ udev_enumerate->devices_uptodate = true;
+ }
+
+ return udev_list_get_entry(&udev_enumerate->devices_list);
+}
+
+/**
+ * udev_enumerate_add_match_subsystem:
+ * @udev_enumerate: context
+ * @subsystem: filter for a subsystem of the device to include in the list
+ *
+ * Match only devices belonging to a certain kernel subsystem.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!subsystem)
+ return 0;
+
+ return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true);
+}
+
+/**
+ * udev_enumerate_add_nomatch_subsystem:
+ * @udev_enumerate: context
+ * @subsystem: filter for a subsystem of the device to exclude from the list
+ *
+ * Match only devices not belonging to a certain kernel subsystem.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!subsystem)
+ return 0;
+
+ return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false);
+}
+
+/**
+ * udev_enumerate_add_match_sysattr:
+ * @udev_enumerate: context
+ * @sysattr: filter for a sys attribute at the device to include in the list
+ * @value: optional value of the sys attribute
+ *
+ * Match only devices with a certain /sys device attribute.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!sysattr)
+ return 0;
+
+ return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true);
+}
+
+/**
+ * udev_enumerate_add_nomatch_sysattr:
+ * @udev_enumerate: context
+ * @sysattr: filter for a sys attribute at the device to exclude from the list
+ * @value: optional value of the sys attribute
+ *
+ * Match only devices not having a certain /sys device attribute.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!sysattr)
+ return 0;
+
+ return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false);
+}
+
+/**
+ * udev_enumerate_add_match_property:
+ * @udev_enumerate: context
+ * @property: filter for a property of the device to include in the list
+ * @value: value of the property
+ *
+ * Match only devices with a certain property.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!property)
+ return 0;
+
+ return sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value);
+}
+
+/**
+ * udev_enumerate_add_match_tag:
+ * @udev_enumerate: context
+ * @tag: filter for a tag of the device to include in the list
+ *
+ * Match only devices with a certain tag.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!tag)
+ return 0;
+
+ return sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag);
+}
+
+/**
+ * udev_enumerate_add_match_parent:
+ * @udev_enumerate: context
+ * @parent: parent device where to start searching
+ *
+ * Return the devices on the subtree of one given device. The parent
+ * itself is included in the list.
+ *
+ * A reference for the device is held until the udev_enumerate context
+ * is cleaned up.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!parent)
+ return 0;
+
+ return sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, parent->device);
+}
+
+/**
+ * udev_enumerate_add_match_is_initialized:
+ * @udev_enumerate: context
+ *
+ * Match only devices which udev has set up already. This makes
+ * sure, that the device node permissions and context are properly set
+ * and that network devices are fully renamed.
+ *
+ * Usually, devices which are found in the kernel but not already
+ * handled by udev, have still pending events. Services should subscribe
+ * to monitor events and wait for these devices to become ready, instead
+ * of using uninitialized devices.
+ *
+ * For now, this will not affect devices which do not have a device node
+ * and are not network interfaces.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ return device_enumerator_add_match_is_initialized(udev_enumerate->enumerator);
+}
+
+/**
+ * udev_enumerate_add_match_sysname:
+ * @udev_enumerate: context
+ * @sysname: filter for the name of the device to include in the list
+ *
+ * Match only devices with a given /sys device name.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!sysname)
+ return 0;
+
+ return sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname);
+}
+
+/**
+ * udev_enumerate_add_syspath:
+ * @udev_enumerate: context
+ * @syspath: path of a device
+ *
+ * Add a device to the list of devices, to retrieve it back sorted in dependency order.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ int r;
+
+ assert_return(udev_enumerate, -EINVAL);
+
+ if (!syspath)
+ return 0;
+
+ r = sd_device_new_from_syspath(&device, syspath);
+ if (r < 0)
+ return r;
+
+ r = device_enumerator_add_device(udev_enumerate->enumerator, device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/**
+ * udev_enumerate_scan_devices:
+ * @udev_enumerate: udev enumeration context
+ *
+ * Scan /sys for all devices which match the given filters. No matches
+ * will return all currently available devices.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ **/
+_public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ return device_enumerator_scan_devices(udev_enumerate->enumerator);
+}
+
+/**
+ * udev_enumerate_scan_subsystems:
+ * @udev_enumerate: udev enumeration context
+ *
+ * Scan /sys for all kernel subsystems, including buses, classes, drivers.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ **/
+_public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) {
+ assert_return(udev_enumerate, -EINVAL);
+
+ return device_enumerator_scan_subsystems(udev_enumerate->enumerator);
+}
diff --git a/src/libudev/src/libudev-hwdb.c b/src/libudev/src/libudev-hwdb.c
new file mode 100644
index 0000000000..e381ffc62e
--- /dev/null
+++ b/src/libudev/src/libudev-hwdb.c
@@ -0,0 +1,146 @@
+/***
+ This file is part of systemd.
+
+ Copyright Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-hwdb/hwdb-util.h"
+#include "systemd-basic/alloc-util.h"
+#include "systemd-staging/sd-hwdb.h"
+
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev-hwdb
+ * @short_description: retrieve properties from the hardware database
+ *
+ * Libudev hardware database interface.
+ */
+
+/**
+ * udev_hwdb:
+ *
+ * Opaque object representing the hardware database.
+ */
+struct udev_hwdb {
+ struct udev *udev;
+ int refcount;
+
+ sd_hwdb *hwdb;
+
+ struct udev_list properties_list;
+};
+
+/**
+ * udev_hwdb_new:
+ * @udev: udev library context
+ *
+ * Create a hardware database context to query properties for devices.
+ *
+ * Returns: a hwdb context.
+ **/
+_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb_internal = NULL;
+ struct udev_hwdb *hwdb;
+ int r;
+
+ assert_return(udev, NULL);
+
+ r = sd_hwdb_new(&hwdb_internal);
+ if (r < 0)
+ return NULL;
+
+ hwdb = new0(struct udev_hwdb, 1);
+ if (!hwdb)
+ return NULL;
+
+ hwdb->refcount = 1;
+ hwdb->hwdb = hwdb_internal;
+ hwdb_internal = NULL;
+
+ udev_list_init(udev, &hwdb->properties_list, true);
+
+ return hwdb;
+}
+
+/**
+ * udev_hwdb_ref:
+ * @hwdb: context
+ *
+ * Take a reference of a hwdb context.
+ *
+ * Returns: the passed enumeration context
+ **/
+_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
+ if (!hwdb)
+ return NULL;
+ hwdb->refcount++;
+ return hwdb;
+}
+
+/**
+ * udev_hwdb_unref:
+ * @hwdb: context
+ *
+ * Drop a reference of a hwdb context. If the refcount reaches zero,
+ * all resources of the hwdb context will be released.
+ *
+ * Returns: #NULL
+ **/
+_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
+ if (!hwdb)
+ return NULL;
+ hwdb->refcount--;
+ if (hwdb->refcount > 0)
+ return NULL;
+ sd_hwdb_unref(hwdb->hwdb);
+ udev_list_cleanup(&hwdb->properties_list);
+ free(hwdb);
+ return NULL;
+}
+
+/**
+ * udev_hwdb_get_properties_list_entry:
+ * @hwdb: context
+ * @modalias: modalias string
+ * @flags: (unused)
+ *
+ * Lookup a matching device in the hardware database. The lookup key is a
+ * modalias string, whose formats are defined for the Linux kernel modules.
+ * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry
+ * of a list of retrieved properties is returned.
+ *
+ * Returns: a udev_list_entry.
+ */
+_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {
+ const char *key, *value;
+
+ if (!hwdb || !modalias) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ udev_list_cleanup(&hwdb->properties_list);
+
+ SD_HWDB_FOREACH_PROPERTY(hwdb->hwdb, modalias, key, value) {
+ if (udev_list_entry_add(&hwdb->properties_list, key, value) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ return udev_list_get_entry(&hwdb->properties_list);
+}
diff --git a/src/libudev/src/libudev-list.c b/src/libudev/src/libudev-list.c
new file mode 100644
index 0000000000..3700ba2697
--- /dev/null
+++ b/src/libudev/src/libudev-list.c
@@ -0,0 +1,351 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "systemd-basic/alloc-util.h"
+
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev-list
+ * @short_description: list operation
+ *
+ * Libudev list operations.
+ */
+
+/**
+ * udev_list_entry:
+ *
+ * Opaque object representing one entry in a list. An entry contains
+ * contains a name, and optionally a value.
+ */
+struct udev_list_entry {
+ struct udev_list_node node;
+ struct udev_list *list;
+ char *name;
+ char *value;
+ int num;
+};
+
+/* the list's head points to itself if empty */
+void udev_list_node_init(struct udev_list_node *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+int udev_list_node_is_empty(struct udev_list_node *list)
+{
+ return list->next == list;
+}
+
+static void udev_list_node_insert_between(struct udev_list_node *new,
+ struct udev_list_node *prev,
+ struct udev_list_node *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list)
+{
+ udev_list_node_insert_between(new, list->prev, list);
+}
+
+void udev_list_node_remove(struct udev_list_node *entry)
+{
+ struct udev_list_node *prev = entry->prev;
+ struct udev_list_node *next = entry->next;
+
+ next->prev = prev;
+ prev->next = next;
+
+ entry->prev = NULL;
+ entry->next = NULL;
+}
+
+/* return list entry which embeds this node */
+static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node)
+{
+ return container_of(node, struct udev_list_entry, node);
+}
+
+void udev_list_init(struct udev *udev, struct udev_list *list, bool unique)
+{
+ memzero(list, sizeof(struct udev_list));
+ list->udev = udev;
+ list->unique = unique;
+ udev_list_node_init(&list->node);
+}
+
+/* insert entry into a list as the last element */
+static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list)
+{
+ /* inserting before the list head make the node the last node in the list */
+ udev_list_node_insert_between(&new->node, list->node.prev, &list->node);
+ new->list = list;
+}
+
+/* insert entry into a list, before a given existing entry */
+static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry)
+{
+ udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node);
+ new->list = entry->list;
+}
+
+/* binary search in sorted array */
+static int list_search(struct udev_list *list, const char *name)
+{
+ unsigned int first, last;
+
+ first = 0;
+ last = list->entries_cur;
+ while (first < last) {
+ unsigned int i;
+ int cmp;
+
+ i = (first + last)/2;
+ cmp = strcmp(name, list->entries[i]->name);
+ if (cmp < 0)
+ last = i;
+ else if (cmp > 0)
+ first = i+1;
+ else
+ return i;
+ }
+
+ /* not found, return negative insertion-index+1 */
+ return -(first+1);
+}
+
+struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value)
+{
+ struct udev_list_entry *entry;
+ int i = 0;
+
+ if (list->unique) {
+ /* lookup existing name or insertion-index */
+ i = list_search(list, name);
+ if (i >= 0) {
+ entry = list->entries[i];
+
+ free(entry->value);
+ if (value == NULL) {
+ entry->value = NULL;
+ return entry;
+ }
+ entry->value = strdup(value);
+ if (entry->value == NULL)
+ return NULL;
+ return entry;
+ }
+ }
+
+ /* add new name */
+ entry = new0(struct udev_list_entry, 1);
+ if (entry == NULL)
+ return NULL;
+
+ entry->name = strdup(name);
+ if (entry->name == NULL)
+ return mfree(entry);
+
+ if (value != NULL) {
+ entry->value = strdup(value);
+ if (entry->value == NULL) {
+ free(entry->name);
+ return mfree(entry);
+ }
+ }
+
+ if (list->unique) {
+ /* allocate or enlarge sorted array if needed */
+ if (list->entries_cur >= list->entries_max) {
+ struct udev_list_entry **entries;
+ unsigned int add;
+
+ add = list->entries_max;
+ if (add < 1)
+ add = 64;
+ entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *));
+ if (entries == NULL) {
+ free(entry->name);
+ free(entry->value);
+ return mfree(entry);
+ }
+ list->entries = entries;
+ list->entries_max += add;
+ }
+
+ /* the negative i returned the insertion index */
+ i = (-i)-1;
+
+ /* insert into sorted list */
+ if ((unsigned int)i < list->entries_cur)
+ udev_list_entry_insert_before(entry, list->entries[i]);
+ else
+ udev_list_entry_append(entry, list);
+
+ /* insert into sorted array */
+ memmove(&list->entries[i+1], &list->entries[i],
+ (list->entries_cur - i) * sizeof(struct udev_list_entry *));
+ list->entries[i] = entry;
+ list->entries_cur++;
+ } else {
+ udev_list_entry_append(entry, list);
+ }
+
+ return entry;
+}
+
+void udev_list_entry_delete(struct udev_list_entry *entry)
+{
+ if (entry->list->entries != NULL) {
+ int i;
+ struct udev_list *list = entry->list;
+
+ /* remove entry from sorted array */
+ i = list_search(list, entry->name);
+ if (i >= 0) {
+ memmove(&list->entries[i], &list->entries[i+1],
+ ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *));
+ list->entries_cur--;
+ }
+ }
+
+ udev_list_node_remove(&entry->node);
+ free(entry->name);
+ free(entry->value);
+ free(entry);
+}
+
+void udev_list_cleanup(struct udev_list *list)
+{
+ struct udev_list_entry *entry_loop;
+ struct udev_list_entry *entry_tmp;
+
+ list->entries = mfree(list->entries);
+ list->entries_cur = 0;
+ list->entries_max = 0;
+ udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list))
+ udev_list_entry_delete(entry_loop);
+}
+
+struct udev_list_entry *udev_list_get_entry(struct udev_list *list)
+{
+ if (udev_list_node_is_empty(&list->node))
+ return NULL;
+ return list_node_to_entry(list->node.next);
+}
+
+/**
+ * udev_list_entry_get_next:
+ * @list_entry: current entry
+ *
+ * Get the next entry from the list.
+ *
+ * Returns: udev_list_entry, #NULL if no more entries are available.
+ */
+_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry)
+{
+ struct udev_list_node *next;
+
+ if (list_entry == NULL)
+ return NULL;
+ next = list_entry->node.next;
+ /* empty list or no more entries */
+ if (next == &list_entry->list->node)
+ return NULL;
+ return list_node_to_entry(next);
+}
+
+/**
+ * udev_list_entry_get_by_name:
+ * @list_entry: current entry
+ * @name: name string to match
+ *
+ * Lookup an entry in the list with a certain name.
+ *
+ * Returns: udev_list_entry, #NULL if no matching entry is found.
+ */
+_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
+{
+ int i;
+
+ if (list_entry == NULL)
+ return NULL;
+
+ if (!list_entry->list->unique)
+ return NULL;
+
+ i = list_search(list_entry->list, name);
+ if (i < 0)
+ return NULL;
+ return list_entry->list->entries[i];
+}
+
+/**
+ * udev_list_entry_get_name:
+ * @list_entry: current entry
+ *
+ * Get the name of a list entry.
+ *
+ * Returns: the name string of this entry.
+ */
+_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry)
+{
+ if (list_entry == NULL)
+ return NULL;
+ return list_entry->name;
+}
+
+/**
+ * udev_list_entry_get_value:
+ * @list_entry: current entry
+ *
+ * Get the value of list entry.
+ *
+ * Returns: the value string of this entry.
+ */
+_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
+{
+ if (list_entry == NULL)
+ return NULL;
+ return list_entry->value;
+}
+
+int udev_list_entry_get_num(struct udev_list_entry *list_entry)
+{
+ if (list_entry == NULL)
+ return -EINVAL;
+ return list_entry->num;
+}
+
+void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num)
+{
+ if (list_entry == NULL)
+ return;
+ list_entry->num = num;
+}
diff --git a/src/libudev/src/libudev-monitor.c b/src/libudev/src/libudev-monitor.c
new file mode 100644
index 0000000000..db433d6243
--- /dev/null
+++ b/src/libudev/src/libudev-monitor.c
@@ -0,0 +1,846 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <linux/filter.h>
+#include <linux/netlink.h>
+
+#include <libudev.h>
+
+#include "systemd-basic/alloc-util.h"
+#include "systemd-basic/fd-util.h"
+#include "systemd-basic/fileio.h"
+#include "systemd-basic/formats-util.h"
+#include "systemd-basic/missing.h"
+#include "systemd-basic/mount-util.h"
+#include "systemd-basic/socket-util.h"
+#include "systemd-basic/string-util.h"
+
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev-monitor
+ * @short_description: device event source
+ *
+ * Connects to a device event source.
+ */
+
+/**
+ * udev_monitor:
+ *
+ * Opaque object handling an event source.
+ */
+struct udev_monitor {
+ struct udev *udev;
+ int refcount;
+ int sock;
+ union sockaddr_union snl;
+ union sockaddr_union snl_trusted_sender;
+ union sockaddr_union snl_destination;
+ socklen_t addrlen;
+ struct udev_list filter_subsystem_list;
+ struct udev_list filter_tag_list;
+ bool bound;
+};
+
+enum udev_monitor_netlink_group {
+ UDEV_MONITOR_NONE,
+ UDEV_MONITOR_KERNEL,
+ UDEV_MONITOR_UDEV,
+};
+
+#define UDEV_MONITOR_MAGIC 0xfeedcafe
+struct udev_monitor_netlink_header {
+ /* "libudev" prefix to distinguish libudev and kernel messages */
+ char prefix[8];
+ /*
+ * magic to protect against daemon <-> library message format mismatch
+ * used in the kernel from socket filter rules; needs to be stored in network order
+ */
+ unsigned int magic;
+ /* total length of header structure known to the sender */
+ unsigned int header_size;
+ /* properties string buffer */
+ unsigned int properties_off;
+ unsigned int properties_len;
+ /*
+ * hashes of primary device properties strings, to let libudev subscribers
+ * use in-kernel socket filters; values need to be stored in network order
+ */
+ unsigned int filter_subsystem_hash;
+ unsigned int filter_devtype_hash;
+ unsigned int filter_tag_bloom_hi;
+ unsigned int filter_tag_bloom_lo;
+};
+
+static struct udev_monitor *udev_monitor_new(struct udev *udev)
+{
+ struct udev_monitor *udev_monitor;
+
+ udev_monitor = new0(struct udev_monitor, 1);
+ if (udev_monitor == NULL)
+ return NULL;
+ udev_monitor->refcount = 1;
+ udev_monitor->udev = udev;
+ udev_list_init(udev, &udev_monitor->filter_subsystem_list, false);
+ udev_list_init(udev, &udev_monitor->filter_tag_list, true);
+ return udev_monitor;
+}
+
+/* we consider udev running when /dev is on devtmpfs */
+static bool udev_has_devtmpfs(struct udev *udev) {
+
+ union file_handle_union h = FILE_HANDLE_INIT;
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX], *e;
+ int mount_id;
+ int r;
+
+ r = name_to_handle_at(AT_FDCWD, "/dev", &h.handle, &mount_id, 0);
+ if (r < 0) {
+ if (errno != EOPNOTSUPP)
+ log_debug_errno(errno, "name_to_handle_at on /dev: %m");
+ return false;
+ }
+
+ f = fopen("/proc/self/mountinfo", "re");
+ if (!f)
+ return false;
+
+ FOREACH_LINE(line, f, return false) {
+ int mid;
+
+ if (sscanf(line, "%i", &mid) != 1)
+ continue;
+
+ if (mid != mount_id)
+ continue;
+
+ e = strstr(line, " - ");
+ if (!e)
+ continue;
+
+ /* accept any name that starts with the currently expected type */
+ if (startswith(e + 3, "devtmpfs"))
+ return true;
+ }
+
+ return false;
+}
+
+static void monitor_set_nl_address(struct udev_monitor *udev_monitor) {
+ union sockaddr_union snl;
+ socklen_t addrlen;
+ int r;
+
+ assert(udev_monitor);
+
+ /* get the address the kernel has assigned us
+ * it is usually, but not necessarily the pid
+ */
+ addrlen = sizeof(struct sockaddr_nl);
+ r = getsockname(udev_monitor->sock, &snl.sa, &addrlen);
+ if (r >= 0)
+ udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid;
+}
+
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd)
+{
+ struct udev_monitor *udev_monitor;
+ unsigned int group;
+
+ if (udev == NULL)
+ return NULL;
+
+ if (name == NULL)
+ group = UDEV_MONITOR_NONE;
+ else if (streq(name, "udev")) {
+ /*
+ * We do not support subscribing to uevents if no instance of
+ * udev is running. Uevents would otherwise broadcast the
+ * processing data of the host into containers, which is not
+ * desired.
+ *
+ * Containers will currently not get any udev uevents, until
+ * a supporting infrastructure is available.
+ *
+ * We do not set a netlink multicast group here, so the socket
+ * will not receive any messages.
+ */
+ if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) {
+ log_debug("the udev service seems not to be active, disable the monitor");
+ group = UDEV_MONITOR_NONE;
+ } else
+ group = UDEV_MONITOR_UDEV;
+ } else if (streq(name, "kernel"))
+ group = UDEV_MONITOR_KERNEL;
+ else
+ return NULL;
+
+ udev_monitor = udev_monitor_new(udev);
+ if (udev_monitor == NULL)
+ return NULL;
+
+ if (fd < 0) {
+ udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
+ if (udev_monitor->sock < 0) {
+ log_debug_errno(errno, "error getting socket: %m");
+ return mfree(udev_monitor);
+ }
+ } else {
+ udev_monitor->bound = true;
+ udev_monitor->sock = fd;
+ monitor_set_nl_address(udev_monitor);
+ }
+
+ udev_monitor->snl.nl.nl_family = AF_NETLINK;
+ udev_monitor->snl.nl.nl_groups = group;
+
+ /* default destination for sending */
+ udev_monitor->snl_destination.nl.nl_family = AF_NETLINK;
+ udev_monitor->snl_destination.nl.nl_groups = UDEV_MONITOR_UDEV;
+
+ return udev_monitor;
+}
+
+/**
+ * udev_monitor_new_from_netlink:
+ * @udev: udev library context
+ * @name: name of event source
+ *
+ * Create new udev monitor and connect to a specified event
+ * source. Valid sources identifiers are "udev" and "kernel".
+ *
+ * Applications should usually not connect directly to the
+ * "kernel" events, because the devices might not be useable
+ * at that time, before udev has configured them, and created
+ * device nodes. Accessing devices at the same time as udev,
+ * might result in unpredictable behavior. The "udev" events
+ * are sent out after udev has finished its event processing,
+ * all rules have been processed, and needed device nodes are
+ * created.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev monitor.
+ *
+ * Returns: a new udev monitor, or #NULL, in case of an error
+ **/
+_public_ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+{
+ return udev_monitor_new_from_netlink_fd(udev, name, -1);
+}
+
+static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i,
+ unsigned short code, unsigned int data)
+{
+ struct sock_filter *ins = &inss[*i];
+
+ ins->code = code;
+ ins->k = data;
+ (*i)++;
+}
+
+static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i,
+ unsigned short code, unsigned int data,
+ unsigned short jt, unsigned short jf)
+{
+ struct sock_filter *ins = &inss[*i];
+
+ ins->code = code;
+ ins->jt = jt;
+ ins->jf = jf;
+ ins->k = data;
+ (*i)++;
+}
+
+/**
+ * udev_monitor_filter_update:
+ * @udev_monitor: monitor
+ *
+ * Update the installed socket filter. This is only needed,
+ * if the filter was removed or changed.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
+{
+ struct sock_filter ins[512];
+ struct sock_fprog filter;
+ unsigned int i;
+ struct udev_list_entry *list_entry;
+ int err;
+
+ if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL &&
+ udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
+ return 0;
+
+ memzero(ins, sizeof(ins));
+ i = 0;
+
+ /* load magic in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic));
+ /* jump if magic matches */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
+ /* wrong magic, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) {
+ int tag_matches;
+
+ /* count tag matches, to calculate end of tag match block */
+ tag_matches = 0;
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list))
+ tag_matches++;
+
+ /* add all tags matches */
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
+ uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry));
+ uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
+ uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
+
+ /* load device bloom bits in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi));
+ /* clear bits (tag bits & bloom bits) */
+ bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi);
+ /* jump to next tag if it does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3);
+
+ /* load device bloom bits in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo));
+ /* clear bits (tag bits & bloom bits) */
+ bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo);
+ /* jump behind end of tag match block if tag matches */
+ tag_matches--;
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0);
+ }
+
+ /* nothing matched, drop packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ }
+
+ /* add all subsystem matches */
+ if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) {
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
+ unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry));
+
+ /* load device subsystem value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash));
+ if (udev_list_entry_get_value(list_entry) == NULL) {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ } else {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
+
+ /* load device devtype value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash));
+ /* jump if value does not match */
+ hash = util_string_hash32(udev_list_entry_get_value(list_entry));
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ }
+
+ /* matched, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ if (i+1 >= ELEMENTSOF(ins))
+ return -E2BIG;
+ }
+
+ /* nothing matched, drop packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ }
+
+ /* matched, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ /* install filter */
+ memzero(&filter, sizeof(filter));
+ filter.len = i;
+ filter.filter = ins;
+ err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
+ return err < 0 ? -errno : 0;
+}
+
+int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender)
+{
+ udev_monitor->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid;
+ return 0;
+}
+
+/**
+ * udev_monitor_enable_receiving:
+ * @udev_monitor: the monitor which should receive events
+ *
+ * Binds the @udev_monitor socket to the event source.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
+{
+ int err = 0;
+ const int on = 1;
+
+ udev_monitor_filter_update(udev_monitor);
+
+ if (!udev_monitor->bound) {
+ err = bind(udev_monitor->sock,
+ &udev_monitor->snl.sa, sizeof(struct sockaddr_nl));
+ if (err == 0)
+ udev_monitor->bound = true;
+ }
+
+ if (err >= 0)
+ monitor_set_nl_address(udev_monitor);
+ else
+ return log_debug_errno(errno, "bind failed: %m");
+
+ /* enable receiving of sender credentials */
+ err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ if (err < 0)
+ log_debug_errno(errno, "setting SO_PASSCRED failed: %m");
+
+ return 0;
+}
+
+/**
+ * udev_monitor_set_receive_buffer_size:
+ * @udev_monitor: the monitor which should receive events
+ * @size: the size in bytes
+ *
+ * Set the size of the kernel socket buffer. This call needs the
+ * appropriate privileges to succeed.
+ *
+ * Returns: 0 on success, otherwise -1 on error.
+ */
+_public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
+{
+ if (udev_monitor == NULL)
+ return -EINVAL;
+ return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
+}
+
+int udev_monitor_disconnect(struct udev_monitor *udev_monitor)
+{
+ int err;
+
+ err = close(udev_monitor->sock);
+ udev_monitor->sock = -1;
+ return err < 0 ? -errno : 0;
+}
+
+/**
+ * udev_monitor_ref:
+ * @udev_monitor: udev monitor
+ *
+ * Take a reference of a udev monitor.
+ *
+ * Returns: the passed udev monitor
+ **/
+_public_ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return NULL;
+ udev_monitor->refcount++;
+ return udev_monitor;
+}
+
+/**
+ * udev_monitor_unref:
+ * @udev_monitor: udev monitor
+ *
+ * Drop a reference of a udev monitor. If the refcount reaches zero,
+ * the bound socket will be closed, and the resources of the monitor
+ * will be released.
+ *
+ * Returns: #NULL
+ **/
+_public_ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return NULL;
+ udev_monitor->refcount--;
+ if (udev_monitor->refcount > 0)
+ return NULL;
+ if (udev_monitor->sock >= 0)
+ close(udev_monitor->sock);
+ udev_list_cleanup(&udev_monitor->filter_subsystem_list);
+ udev_list_cleanup(&udev_monitor->filter_tag_list);
+ free(udev_monitor);
+ return NULL;
+}
+
+/**
+ * udev_monitor_get_udev:
+ * @udev_monitor: udev monitor
+ *
+ * Retrieve the udev library context the monitor was created with.
+ *
+ * Returns: the udev library context
+ **/
+_public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return NULL;
+ return udev_monitor->udev;
+}
+
+/**
+ * udev_monitor_get_fd:
+ * @udev_monitor: udev monitor
+ *
+ * Retrieve the socket file descriptor associated with the monitor.
+ *
+ * Returns: the socket file descriptor
+ **/
+_public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
+{
+ if (udev_monitor == NULL)
+ return -EINVAL;
+ return udev_monitor->sock;
+}
+
+static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
+{
+ struct udev_list_entry *list_entry;
+
+ if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL)
+ goto tag;
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
+ const char *subsys = udev_list_entry_get_name(list_entry);
+ const char *dsubsys = udev_device_get_subsystem(udev_device);
+ const char *devtype;
+ const char *ddevtype;
+
+ if (!streq(dsubsys, subsys))
+ continue;
+
+ devtype = udev_list_entry_get_value(list_entry);
+ if (devtype == NULL)
+ goto tag;
+ ddevtype = udev_device_get_devtype(udev_device);
+ if (ddevtype == NULL)
+ continue;
+ if (streq(ddevtype, devtype))
+ goto tag;
+ }
+ return 0;
+
+tag:
+ if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
+ return 1;
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
+ const char *tag = udev_list_entry_get_name(list_entry);
+
+ if (udev_device_has_tag(udev_device, tag))
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * udev_monitor_receive_device:
+ * @udev_monitor: udev monitor
+ *
+ * Receive data from the udev monitor socket, allocate a new udev
+ * device, fill in the received data, and return the device.
+ *
+ * Only socket connections with uid=0 are accepted.
+ *
+ * The monitor socket is by default set to NONBLOCK. A variant of poll() on
+ * the file descriptor returned by udev_monitor_get_fd() should to be used to
+ * wake up when new devices arrive, or alternatively the file descriptor
+ * switched into blocking mode.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev device.
+ *
+ * Returns: a new udev device, or #NULL, in case of an error
+ **/
+_public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
+{
+ struct udev_device *udev_device;
+ struct msghdr smsg;
+ struct iovec iov;
+ char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+ struct cmsghdr *cmsg;
+ union sockaddr_union snl;
+ struct ucred *cred;
+ union {
+ struct udev_monitor_netlink_header nlh;
+ char raw[8192];
+ } buf;
+ ssize_t buflen;
+ ssize_t bufpos;
+ bool is_initialized = false;
+
+retry:
+ if (udev_monitor == NULL)
+ return NULL;
+ iov.iov_base = &buf;
+ iov.iov_len = sizeof(buf);
+ memzero(&smsg, sizeof(struct msghdr));
+ smsg.msg_iov = &iov;
+ smsg.msg_iovlen = 1;
+ smsg.msg_control = cred_msg;
+ smsg.msg_controllen = sizeof(cred_msg);
+ smsg.msg_name = &snl;
+ smsg.msg_namelen = sizeof(snl);
+
+ buflen = recvmsg(udev_monitor->sock, &smsg, 0);
+ if (buflen < 0) {
+ if (errno != EINTR)
+ log_debug("unable to receive message");
+ return NULL;
+ }
+
+ if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC)) {
+ log_debug("invalid message length");
+ return NULL;
+ }
+
+ if (snl.nl.nl_groups == 0) {
+ /* unicast message, check if we trust the sender */
+ if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 ||
+ snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) {
+ log_debug("unicast netlink message ignored");
+ return NULL;
+ }
+ } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) {
+ if (snl.nl.nl_pid > 0) {
+ log_debug("multicast kernel netlink message from PID %"PRIu32" ignored",
+ snl.nl.nl_pid);
+ return NULL;
+ }
+ }
+
+ cmsg = CMSG_FIRSTHDR(&smsg);
+ if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ log_debug("no sender credentials received, message ignored");
+ return NULL;
+ }
+
+ cred = (struct ucred *)CMSG_DATA(cmsg);
+ if (cred->uid != 0) {
+ log_debug("sender uid="UID_FMT", message ignored", cred->uid);
+ return NULL;
+ }
+
+ if (memcmp(buf.raw, "libudev", 8) == 0) {
+ /* udev message needs proper version magic */
+ if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC)) {
+ log_debug("unrecognized message signature (%x != %x)",
+ buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
+ return NULL;
+ }
+ if (buf.nlh.properties_off+32 > (size_t)buflen) {
+ log_debug("message smaller than expected (%u > %zd)",
+ buf.nlh.properties_off+32, buflen);
+ return NULL;
+ }
+
+ bufpos = buf.nlh.properties_off;
+
+ /* devices received from udev are always initialized */
+ is_initialized = true;
+ } else {
+ /* kernel message with header */
+ bufpos = strlen(buf.raw) + 1;
+ if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) {
+ log_debug("invalid message length");
+ return NULL;
+ }
+
+ /* check message header */
+ if (strstr(buf.raw, "@/") == NULL) {
+ log_debug("unrecognized message header");
+ return NULL;
+ }
+ }
+
+ udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos);
+ if (!udev_device) {
+ log_debug("could not create device: %m");
+ return NULL;
+ }
+
+ if (is_initialized)
+ udev_device_set_is_initialized(udev_device);
+
+ /* skip device, if it does not pass the current filter */
+ if (!passes_filter(udev_monitor, udev_device)) {
+ struct pollfd pfd[1];
+ int rc;
+
+ udev_device_unref(udev_device);
+
+ /* if something is queued, get next device */
+ pfd[0].fd = udev_monitor->sock;
+ pfd[0].events = POLLIN;
+ rc = poll(pfd, 1, 0);
+ if (rc > 0)
+ goto retry;
+ return NULL;
+ }
+
+ return udev_device;
+}
+
+int udev_monitor_send_device(struct udev_monitor *udev_monitor,
+ struct udev_monitor *destination, struct udev_device *udev_device)
+{
+ const char *buf, *val;
+ ssize_t blen, count;
+ struct udev_monitor_netlink_header nlh = {
+ .prefix = "libudev",
+ .magic = htobe32(UDEV_MONITOR_MAGIC),
+ .header_size = sizeof nlh,
+ };
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof nlh },
+ };
+ struct msghdr smsg = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+ struct udev_list_entry *list_entry;
+ uint64_t tag_bloom_bits;
+
+ blen = udev_device_get_properties_monitor_buf(udev_device, &buf);
+ if (blen < 32) {
+ log_debug("device buffer is too small to contain a valid device");
+ return -EINVAL;
+ }
+
+ /* fill in versioned header */
+ val = udev_device_get_subsystem(udev_device);
+ nlh.filter_subsystem_hash = htobe32(util_string_hash32(val));
+
+ val = udev_device_get_devtype(udev_device);
+ if (val != NULL)
+ nlh.filter_devtype_hash = htobe32(util_string_hash32(val));
+
+ /* add tag bloom filter */
+ tag_bloom_bits = 0;
+ udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
+ tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry));
+ if (tag_bloom_bits > 0) {
+ nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32);
+ nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff);
+ }
+
+ /* add properties list */
+ nlh.properties_off = iov[0].iov_len;
+ nlh.properties_len = blen;
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = blen;
+
+ /*
+ * Use custom address for target, or the default one.
+ *
+ * If we send to a multicast group, we will get
+ * ECONNREFUSED, which is expected.
+ */
+ if (destination)
+ smsg.msg_name = &destination->snl;
+ else
+ smsg.msg_name = &udev_monitor->snl_destination;
+ smsg.msg_namelen = sizeof(struct sockaddr_nl);
+ count = sendmsg(udev_monitor->sock, &smsg, 0);
+ if (count < 0) {
+ if (!destination && errno == ECONNREFUSED) {
+ log_debug("passed device to netlink monitor %p", udev_monitor);
+ return 0;
+ } else
+ return -errno;
+ }
+
+ log_debug("passed %zi byte device to netlink monitor %p", count, udev_monitor);
+ return count;
+}
+
+/**
+ * udev_monitor_filter_add_match_subsystem_devtype:
+ * @udev_monitor: the monitor
+ * @subsystem: the subsystem value to match the incoming devices against
+ * @devtype: the devtype value to match the incoming devices against
+ *
+ * This filter is efficiently executed inside the kernel, and libudev subscribers
+ * will usually not be woken up for devices which do not match.
+ *
+ * The filter must be installed before the monitor is switched to listening mode.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
+{
+ if (udev_monitor == NULL)
+ return -EINVAL;
+ if (subsystem == NULL)
+ return -EINVAL;
+ if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * udev_monitor_filter_add_match_tag:
+ * @udev_monitor: the monitor
+ * @tag: the name of a tag
+ *
+ * This filter is efficiently executed inside the kernel, and libudev subscribers
+ * will usually not be woken up for devices which do not match.
+ *
+ * The filter must be installed before the monitor is switched to listening mode.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
+{
+ if (udev_monitor == NULL)
+ return -EINVAL;
+ if (tag == NULL)
+ return -EINVAL;
+ if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * udev_monitor_filter_remove:
+ * @udev_monitor: monitor
+ *
+ * Remove all filters from monitor.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
+{
+ static struct sock_fprog filter = { 0, NULL };
+
+ udev_list_cleanup(&udev_monitor->filter_subsystem_list);
+ return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
+}
diff --git a/src/libudev/src/libudev-private.h b/src/libudev/src/libudev-private.h
new file mode 100644
index 0000000000..da646370e6
--- /dev/null
+++ b/src/libudev/src/libudev-private.h
@@ -0,0 +1,150 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef _LIBUDEV_PRIVATE_H_
+#define _LIBUDEV_PRIVATE_H_
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <libudev.h>
+
+#include "systemd-basic/macro.h"
+#include "systemd-basic/mkdir.h"
+#include "systemd-basic/strxcpyx.h"
+#include "systemd-basic/util.h"
+
+#define READ_END 0
+#define WRITE_END 1
+
+/* libudev.c */
+int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]);
+
+/* libudev-device.c */
+struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen);
+struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action);
+struct udev_device *udev_device_shallow_clone(struct udev_device *old_device);
+struct udev_device *udev_device_clone_with_db(struct udev_device *old_device);
+int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src);
+mode_t udev_device_get_devnode_mode(struct udev_device *udev_device);
+uid_t udev_device_get_devnode_uid(struct udev_device *udev_device);
+gid_t udev_device_get_devnode_gid(struct udev_device *udev_device);
+int udev_device_rename(struct udev_device *udev_device, const char *new_name);
+int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink);
+void udev_device_cleanup_devlinks_list(struct udev_device *udev_device);
+int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value);
+char **udev_device_get_properties_envp(struct udev_device *udev_device);
+ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf);
+const char *udev_device_get_devpath_old(struct udev_device *udev_device);
+const char *udev_device_get_id_filename(struct udev_device *udev_device);
+void udev_device_set_is_initialized(struct udev_device *udev_device);
+int udev_device_add_tag(struct udev_device *udev_device, const char *tag);
+void udev_device_remove_tag(struct udev_device *udev_device, const char *tag);
+void udev_device_cleanup_tags_list(struct udev_device *udev_device);
+usec_t udev_device_get_usec_initialized(struct udev_device *udev_device);
+void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *old_device);
+int udev_device_get_devlink_priority(struct udev_device *udev_device);
+int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio);
+int udev_device_get_watch_handle(struct udev_device *udev_device);
+int udev_device_set_watch_handle(struct udev_device *udev_device, int handle);
+int udev_device_get_ifindex(struct udev_device *udev_device);
+void udev_device_set_info_loaded(struct udev_device *device);
+bool udev_device_get_db_persist(struct udev_device *udev_device);
+void udev_device_set_db_persist(struct udev_device *udev_device);
+void udev_device_read_db(struct udev_device *udev_device);
+
+/* libudev-device-private.c */
+int udev_device_update_db(struct udev_device *udev_device);
+int udev_device_delete_db(struct udev_device *udev_device);
+int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add);
+
+/* libudev-monitor.c - netlink/unix socket communication */
+int udev_monitor_disconnect(struct udev_monitor *udev_monitor);
+int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender);
+int udev_monitor_send_device(struct udev_monitor *udev_monitor,
+ struct udev_monitor *destination, struct udev_device *udev_device);
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd);
+
+/* libudev-list.c */
+struct udev_list_node {
+ struct udev_list_node *next, *prev;
+};
+struct udev_list {
+ struct udev *udev;
+ struct udev_list_node node;
+ struct udev_list_entry **entries;
+ unsigned int entries_cur;
+ unsigned int entries_max;
+ bool unique;
+};
+void udev_list_node_init(struct udev_list_node *list);
+int udev_list_node_is_empty(struct udev_list_node *list);
+void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list);
+void udev_list_node_remove(struct udev_list_node *entry);
+#define udev_list_node_foreach(node, list) \
+ for (node = (list)->next; \
+ node != list; \
+ node = (node)->next)
+#define udev_list_node_foreach_safe(node, tmp, list) \
+ for (node = (list)->next, tmp = (node)->next; \
+ node != list; \
+ node = tmp, tmp = (tmp)->next)
+void udev_list_init(struct udev *udev, struct udev_list *list, bool unique);
+void udev_list_cleanup(struct udev_list *list);
+struct udev_list_entry *udev_list_get_entry(struct udev_list *list);
+struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value);
+void udev_list_entry_delete(struct udev_list_entry *entry);
+int udev_list_entry_get_num(struct udev_list_entry *list_entry);
+void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num);
+#define udev_list_entry_foreach_safe(entry, tmp, first) \
+ for (entry = first, tmp = udev_list_entry_get_next(entry); \
+ entry != NULL; \
+ entry = tmp, tmp = udev_list_entry_get_next(tmp))
+
+/* libudev-queue.c */
+unsigned long long int udev_get_kernel_seqnum(struct udev *udev);
+int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum);
+ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size);
+ssize_t udev_queue_skip_devpath(FILE *queue_file);
+
+/* libudev-queue-private.c */
+struct udev_queue_export *udev_queue_export_new(struct udev *udev);
+struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export);
+void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export);
+int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
+int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
+
+/* libudev-util.c */
+#define UTIL_PATH_SIZE 1024
+#define UTIL_NAME_SIZE 512
+#define UTIL_LINE_SIZE 16384
+#define UDEV_ALLOWED_CHARS_INPUT "/ $%?,"
+int util_log_priority(const char *priority);
+size_t util_path_encode(const char *src, char *dest, size_t size);
+void util_remove_trailing_chars(char *path, char c);
+int util_replace_whitespace(const char *str, char *to, size_t len);
+int util_replace_chars(char *str, const char *white);
+unsigned int util_string_hash32(const char *key);
+uint64_t util_string_bloom64(const char *str);
+
+/* libudev-util-private.c */
+int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value);
+
+#endif
diff --git a/src/libudev/src/libudev-queue.c b/src/libudev/src/libudev-queue.c
new file mode 100644
index 0000000000..c2a1c55d1c
--- /dev/null
+++ b/src/libudev/src/libudev-queue.c
@@ -0,0 +1,269 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+ Copyright 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include "systemd-basic/alloc-util.h"
+#include "systemd-basic/fd-util.h"
+#include "systemd-basic/io-util.h"
+
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev-queue
+ * @short_description: access to currently active events
+ *
+ * This exports the current state of the udev processing queue.
+ */
+
+/**
+ * udev_queue:
+ *
+ * Opaque object representing the current event queue in the udev daemon.
+ */
+struct udev_queue {
+ struct udev *udev;
+ int refcount;
+ int fd;
+};
+
+/**
+ * udev_queue_new:
+ * @udev: udev library context
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev queue context.
+ *
+ * Returns: the udev queue context, or #NULL on error.
+ **/
+_public_ struct udev_queue *udev_queue_new(struct udev *udev)
+{
+ struct udev_queue *udev_queue;
+
+ if (udev == NULL)
+ return NULL;
+
+ udev_queue = new0(struct udev_queue, 1);
+ if (udev_queue == NULL)
+ return NULL;
+
+ udev_queue->refcount = 1;
+ udev_queue->udev = udev;
+ udev_queue->fd = -1;
+ return udev_queue;
+}
+
+/**
+ * udev_queue_ref:
+ * @udev_queue: udev queue context
+ *
+ * Take a reference of a udev queue context.
+ *
+ * Returns: the same udev queue context.
+ **/
+_public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
+{
+ if (udev_queue == NULL)
+ return NULL;
+
+ udev_queue->refcount++;
+ return udev_queue;
+}
+
+/**
+ * udev_queue_unref:
+ * @udev_queue: udev queue context
+ *
+ * Drop a reference of a udev queue context. If the refcount reaches zero,
+ * the resources of the queue context will be released.
+ *
+ * Returns: #NULL
+ **/
+_public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
+{
+ if (udev_queue == NULL)
+ return NULL;
+
+ udev_queue->refcount--;
+ if (udev_queue->refcount > 0)
+ return NULL;
+
+ safe_close(udev_queue->fd);
+
+ free(udev_queue);
+ return NULL;
+}
+
+/**
+ * udev_queue_get_udev:
+ * @udev_queue: udev queue context
+ *
+ * Retrieve the udev library context the queue context was created with.
+ *
+ * Returns: the udev library context.
+ **/
+_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
+{
+ if (udev_queue == NULL)
+ return NULL;
+ return udev_queue->udev;
+}
+
+/**
+ * udev_queue_get_kernel_seqnum:
+ * @udev_queue: udev queue context
+ *
+ * This function is deprecated.
+ *
+ * Returns: 0.
+ **/
+_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
+{
+ return 0;
+}
+
+/**
+ * udev_queue_get_udev_seqnum:
+ * @udev_queue: udev queue context
+ *
+ * This function is deprecated.
+ *
+ * Returns: 0.
+ **/
+_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
+{
+ return 0;
+}
+
+/**
+ * udev_queue_get_udev_is_active:
+ * @udev_queue: udev queue context
+ *
+ * Check if udev is active on the system.
+ *
+ * Returns: a flag indicating if udev is active.
+ **/
+_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
+{
+ return access("/run/udev/control", F_OK) >= 0;
+}
+
+/**
+ * udev_queue_get_queue_is_empty:
+ * @udev_queue: udev queue context
+ *
+ * Check if udev is currently processing any events.
+ *
+ * Returns: a flag indicating if udev is currently handling events.
+ **/
+_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
+{
+ return access("/run/udev/queue", F_OK) < 0;
+}
+
+/**
+ * udev_queue_get_seqnum_sequence_is_finished:
+ * @udev_queue: udev queue context
+ * @start: first event sequence number
+ * @end: last event sequence number
+ *
+ * This function is deprecated, it just returns the result of
+ * udev_queue_get_queue_is_empty().
+ *
+ * Returns: a flag indicating if udev is currently handling events.
+ **/
+_public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
+ unsigned long long int start, unsigned long long int end)
+{
+ return udev_queue_get_queue_is_empty(udev_queue);
+}
+
+/**
+ * udev_queue_get_seqnum_is_finished:
+ * @udev_queue: udev queue context
+ * @seqnum: sequence number
+ *
+ * This function is deprecated, it just returns the result of
+ * udev_queue_get_queue_is_empty().
+ *
+ * Returns: a flag indicating if udev is currently handling events.
+ **/
+_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
+{
+ return udev_queue_get_queue_is_empty(udev_queue);
+}
+
+/**
+ * udev_queue_get_queued_list_entry:
+ * @udev_queue: udev queue context
+ *
+ * This function is deprecated.
+ *
+ * Returns: NULL.
+ **/
+_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
+{
+ return NULL;
+}
+
+/**
+ * udev_queue_get_fd:
+ * @udev_queue: udev queue context
+ *
+ * Returns: a file descriptor to watch for a queue to become empty.
+ */
+_public_ int udev_queue_get_fd(struct udev_queue *udev_queue) {
+ int fd;
+ int r;
+
+ if (udev_queue->fd >= 0)
+ return udev_queue->fd;
+
+ fd = inotify_init1(IN_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ r = inotify_add_watch(fd, "/run/udev" , IN_DELETE);
+ if (r < 0) {
+ r = -errno;
+ close(fd);
+ return r;
+ }
+
+ udev_queue->fd = fd;
+ return fd;
+}
+
+/**
+ * udev_queue_flush:
+ * @udev_queue: udev queue context
+ *
+ * Returns: the result of clearing the watch for queue changes.
+ */
+_public_ int udev_queue_flush(struct udev_queue *udev_queue) {
+ if (udev_queue->fd < 0)
+ return -EINVAL;
+
+ return flush_fd(udev_queue->fd);
+}
diff --git a/src/libudev/src/libudev-util.c b/src/libudev/src/libudev-util.c
new file mode 100644
index 0000000000..c881dffbc4
--- /dev/null
+++ b/src/libudev/src/libudev-util.c
@@ -0,0 +1,269 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libudev.h>
+
+#include "systemd-basic/MurmurHash2.h"
+#include "systemd-basic/device-nodes.h"
+#include "systemd-basic/syslog-util.h"
+#include "systemd-basic/utf8.h"
+
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev-util
+ * @short_description: utils
+ *
+ * Utilities useful when dealing with devices and device node names.
+ */
+
+/* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
+int util_resolve_subsys_kernel(struct udev *udev, const char *string,
+ char *result, size_t maxsize, int read_value)
+{
+ char temp[UTIL_PATH_SIZE];
+ char *subsys;
+ char *sysname;
+ struct udev_device *dev;
+ char *attr;
+
+ if (string[0] != '[')
+ return -1;
+
+ strscpy(temp, sizeof(temp), string);
+
+ subsys = &temp[1];
+
+ sysname = strchr(subsys, '/');
+ if (sysname == NULL)
+ return -1;
+ sysname[0] = '\0';
+ sysname = &sysname[1];
+
+ attr = strchr(sysname, ']');
+ if (attr == NULL)
+ return -1;
+ attr[0] = '\0';
+ attr = &attr[1];
+ if (attr[0] == '/')
+ attr = &attr[1];
+ if (attr[0] == '\0')
+ attr = NULL;
+
+ if (read_value && attr == NULL)
+ return -1;
+
+ dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname);
+ if (dev == NULL)
+ return -1;
+
+ if (read_value) {
+ const char *val;
+
+ val = udev_device_get_sysattr_value(dev, attr);
+ if (val != NULL)
+ strscpy(result, maxsize, val);
+ else
+ result[0] = '\0';
+ log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
+ } else {
+ size_t l;
+ char *s;
+
+ s = result;
+ l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
+ if (attr != NULL)
+ strpcpyl(&s, l, "/", attr, NULL);
+ log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
+ }
+ udev_device_unref(dev);
+ return 0;
+}
+
+int util_log_priority(const char *priority)
+{
+ char *endptr;
+ int prio;
+
+ prio = strtoul(priority, &endptr, 10);
+ if (endptr[0] == '\0' || isspace(endptr[0])) {
+ if (prio >= 0 && prio <= 7)
+ return prio;
+ else
+ return -ERANGE;
+ }
+
+ return log_level_from_string(priority);
+}
+
+size_t util_path_encode(const char *src, char *dest, size_t size)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; src[i] != '\0'; i++) {
+ if (src[i] == '/') {
+ if (j+4 >= size) {
+ j = 0;
+ break;
+ }
+ memcpy(&dest[j], "\\x2f", 4);
+ j += 4;
+ } else if (src[i] == '\\') {
+ if (j+4 >= size) {
+ j = 0;
+ break;
+ }
+ memcpy(&dest[j], "\\x5c", 4);
+ j += 4;
+ } else {
+ if (j+1 >= size) {
+ j = 0;
+ break;
+ }
+ dest[j] = src[i];
+ j++;
+ }
+ }
+ dest[j] = '\0';
+ return j;
+}
+
+void util_remove_trailing_chars(char *path, char c)
+{
+ size_t len;
+
+ if (path == NULL)
+ return;
+ len = strlen(path);
+ while (len > 0 && path[len-1] == c)
+ path[--len] = '\0';
+}
+
+int util_replace_whitespace(const char *str, char *to, size_t len)
+{
+ size_t i, j;
+
+ /* strip trailing whitespace */
+ len = strnlen(str, len);
+ while (len && isspace(str[len-1]))
+ len--;
+
+ /* strip leading whitespace */
+ i = 0;
+ while ((i < len) && isspace(str[i]))
+ i++;
+
+ j = 0;
+ while (i < len) {
+ /* substitute multiple whitespace with a single '_' */
+ if (isspace(str[i])) {
+ while (isspace(str[i]))
+ i++;
+ to[j++] = '_';
+ }
+ to[j++] = str[i++];
+ }
+ to[j] = '\0';
+ return 0;
+}
+
+/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
+int util_replace_chars(char *str, const char *white)
+{
+ size_t i = 0;
+ int replaced = 0;
+
+ while (str[i] != '\0') {
+ int len;
+
+ if (whitelisted_char_for_devnode(str[i], white)) {
+ i++;
+ continue;
+ }
+
+ /* accept hex encoding */
+ if (str[i] == '\\' && str[i+1] == 'x') {
+ i += 2;
+ continue;
+ }
+
+ /* accept valid utf8 */
+ len = utf8_encoded_valid_unichar(&str[i]);
+ if (len > 1) {
+ i += len;
+ continue;
+ }
+
+ /* if space is allowed, replace whitespace with ordinary space */
+ if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
+ str[i] = ' ';
+ i++;
+ replaced++;
+ continue;
+ }
+
+ /* everything else is replaced with '_' */
+ str[i] = '_';
+ i++;
+ replaced++;
+ }
+ return replaced;
+}
+
+/**
+ * udev_util_encode_string:
+ * @str: input string to be encoded
+ * @str_enc: output string to store the encoded input string
+ * @len: maximum size of the output string, which may be
+ * four times as long as the input string
+ *
+ * Encode all potentially unsafe characters of a string to the
+ * corresponding 2 char hex value prefixed by '\x'.
+ *
+ * Returns: 0 if the entire string was copied, non-zero otherwise.
+ **/
+_public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len)
+{
+ return encode_devnode_name(str, str_enc, len);
+}
+
+unsigned int util_string_hash32(const char *str)
+{
+ return MurmurHash2(str, strlen(str), 0);
+}
+
+/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */
+uint64_t util_string_bloom64(const char *str)
+{
+ uint64_t bits = 0;
+ unsigned int hash = util_string_hash32(str);
+
+ bits |= 1LLU << (hash & 63);
+ bits |= 1LLU << ((hash >> 6) & 63);
+ bits |= 1LLU << ((hash >> 12) & 63);
+ bits |= 1LLU << ((hash >> 18) & 63);
+ return bits;
+}
diff --git a/src/libudev/src/libudev.c b/src/libudev/src/libudev.c
new file mode 100644
index 0000000000..97d263ef3e
--- /dev/null
+++ b/src/libudev/src/libudev.c
@@ -0,0 +1,254 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2008-2014 Kay Sievers <kay@vrfy.org>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libudev.h>
+
+#include "systemd-basic/alloc-util.h"
+#include "systemd-basic/fd-util.h"
+#include "systemd-basic/missing.h"
+#include "systemd-basic/string-util.h"
+
+#include "libudev-private.h"
+
+/**
+ * SECTION:libudev
+ * @short_description: libudev context
+ *
+ * The context contains the default values read from the udev config file,
+ * and is passed to all library operations.
+ */
+
+/**
+ * udev:
+ *
+ * Opaque object representing the library context.
+ */
+struct udev {
+ int refcount;
+ void (*log_fn)(struct udev *udev,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, va_list args);
+ void *userdata;
+};
+
+/**
+ * udev_get_userdata:
+ * @udev: udev library context
+ *
+ * Retrieve stored data pointer from library context. This might be useful
+ * to access from callbacks.
+ *
+ * Returns: stored userdata
+ **/
+_public_ void *udev_get_userdata(struct udev *udev) {
+ if (udev == NULL)
+ return NULL;
+ return udev->userdata;
+}
+
+/**
+ * udev_set_userdata:
+ * @udev: udev library context
+ * @userdata: data pointer
+ *
+ * Store custom @userdata in the library context.
+ **/
+_public_ void udev_set_userdata(struct udev *udev, void *userdata) {
+ if (udev == NULL)
+ return;
+ udev->userdata = userdata;
+}
+
+/**
+ * udev_new:
+ *
+ * Create udev library context. This reads the udev configuration
+ * file, and fills in the default values.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev library context.
+ *
+ * Returns: a new udev library context
+ **/
+_public_ struct udev *udev_new(void) {
+ struct udev *udev;
+ _cleanup_fclose_ FILE *f = NULL;
+
+ udev = new0(struct udev, 1);
+ if (udev == NULL)
+ return NULL;
+ udev->refcount = 1;
+
+ f = fopen("/etc/udev/udev.conf", "re");
+ if (f != NULL) {
+ char line[UTIL_LINE_SIZE];
+ unsigned line_nr = 0;
+
+ while (fgets(line, sizeof(line), f)) {
+ size_t len;
+ char *key;
+ char *val;
+
+ line_nr++;
+
+ /* find key */
+ key = line;
+ while (isspace(key[0]))
+ key++;
+
+ /* comment or empty line */
+ if (key[0] == '#' || key[0] == '\0')
+ continue;
+
+ /* split key/value */
+ val = strchr(key, '=');
+ if (val == NULL) {
+ log_debug("/etc/udev/udev.conf:%u: missing assignment, skipping line.", line_nr);
+ continue;
+ }
+ val[0] = '\0';
+ val++;
+
+ /* find value */
+ while (isspace(val[0]))
+ val++;
+
+ /* terminate key */
+ len = strlen(key);
+ if (len == 0)
+ continue;
+ while (isspace(key[len-1]))
+ len--;
+ key[len] = '\0';
+
+ /* terminate value */
+ len = strlen(val);
+ if (len == 0)
+ continue;
+ while (isspace(val[len-1]))
+ len--;
+ val[len] = '\0';
+
+ if (len == 0)
+ continue;
+
+ /* unquote */
+ if (val[0] == '"' || val[0] == '\'') {
+ if (val[len-1] != val[0]) {
+ log_debug("/etc/udev/udev.conf:%u: inconsistent quoting, skipping line.", line_nr);
+ continue;
+ }
+ val[len-1] = '\0';
+ val++;
+ }
+
+ if (streq(key, "udev_log")) {
+ int prio;
+
+ prio = util_log_priority(val);
+ if (prio < 0)
+ log_debug("/etc/udev/udev.conf:%u: invalid log level '%s', ignoring.", line_nr, val);
+ else
+ log_set_max_level(prio);
+ continue;
+ }
+ }
+ }
+
+ return udev;
+}
+
+/**
+ * udev_ref:
+ * @udev: udev library context
+ *
+ * Take a reference of the udev library context.
+ *
+ * Returns: the passed udev library context
+ **/
+_public_ struct udev *udev_ref(struct udev *udev) {
+ if (udev == NULL)
+ return NULL;
+ udev->refcount++;
+ return udev;
+}
+
+/**
+ * udev_unref:
+ * @udev: udev library context
+ *
+ * Drop a reference of the udev library context. If the refcount
+ * reaches zero, the resources of the context will be released.
+ *
+ * Returns: the passed udev library context if it has still an active reference, or #NULL otherwise.
+ **/
+_public_ struct udev *udev_unref(struct udev *udev) {
+ if (udev == NULL)
+ return NULL;
+ udev->refcount--;
+ if (udev->refcount > 0)
+ return udev;
+ free(udev);
+ return NULL;
+}
+
+/**
+ * udev_set_log_fn:
+ * @udev: udev library context
+ * @log_fn: function to be called for log messages
+ *
+ * This function is deprecated.
+ *
+ **/
+_public_ void udev_set_log_fn(struct udev *udev,
+ void (*log_fn)(struct udev *udev,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, va_list args)) {
+ return;
+}
+
+/**
+ * udev_get_log_priority:
+ * @udev: udev library context
+ *
+ * This function is deprecated.
+ *
+ **/
+_public_ int udev_get_log_priority(struct udev *udev) {
+ return log_get_max_level();
+}
+
+/**
+ * udev_set_log_priority:
+ * @udev: udev library context
+ * @priority: the new log priority
+ *
+ * This function is deprecated.
+ *
+ **/
+_public_ void udev_set_log_priority(struct udev *udev, int priority) {
+ log_set_max_level(priority);
+}
diff --git a/src/libudev/src/udev.h b/src/libudev/src/udev.h
new file mode 100644
index 0000000000..eeffef86bc
--- /dev/null
+++ b/src/libudev/src/udev.h
@@ -0,0 +1,217 @@
+#pragma once
+
+/*
+ * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+#include <libudev.h>
+
+#include "systemd-basic/label.h"
+#include "systemd-basic/macro.h"
+#include "systemd-basic/strv.h"
+#include "systemd-basic/util.h"
+#include "systemd-staging/sd-netlink.h"
+
+#include "libudev-private.h"
+
+struct udev_event {
+ struct udev *udev;
+ struct udev_device *dev;
+ struct udev_device *dev_parent;
+ struct udev_device *dev_db;
+ char *name;
+ char *program_result;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ struct udev_list seclabel_list;
+ struct udev_list run_list;
+ int exec_delay;
+ usec_t birth_usec;
+ sd_netlink *rtnl;
+ unsigned int builtin_run;
+ unsigned int builtin_ret;
+ bool inotify_watch;
+ bool inotify_watch_final;
+ bool group_set;
+ bool group_final;
+ bool owner_set;
+ bool owner_final;
+ bool mode_set;
+ bool mode_final;
+ bool name_final;
+ bool devlink_final;
+ bool run_final;
+};
+
+struct udev_watch {
+ struct udev_list_node node;
+ int handle;
+ char *name;
+};
+
+/* udev-rules.c */
+struct udev_rules;
+struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
+struct udev_rules *udev_rules_unref(struct udev_rules *rules);
+bool udev_rules_check_timestamp(struct udev_rules *rules);
+void udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event,
+ usec_t timeout_usec, usec_t timeout_warn_usec,
+ struct udev_list *properties_list);
+int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
+
+/* udev-event.c */
+struct udev_event *udev_event_new(struct udev_device *dev);
+void udev_event_unref(struct udev_event *event);
+size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size);
+int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
+ char *result, size_t maxsize, int read_value);
+int udev_event_spawn(struct udev_event *event,
+ usec_t timeout_usec,
+ usec_t timeout_warn_usec,
+ bool accept_failure,
+ const char *cmd, char *result, size_t ressize);
+void udev_event_execute_rules(struct udev_event *event,
+ usec_t timeout_usec, usec_t timeout_warn_usec,
+ struct udev_list *properties_list,
+ struct udev_rules *rules);
+void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec);
+int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]);
+
+/* udev-watch.c */
+int udev_watch_init(struct udev *udev);
+void udev_watch_restore(struct udev *udev);
+void udev_watch_begin(struct udev *udev, struct udev_device *dev);
+void udev_watch_end(struct udev *udev, struct udev_device *dev);
+struct udev_device *udev_watch_lookup(struct udev *udev, int wd);
+
+/* udev-node.c */
+void udev_node_add(struct udev_device *dev, bool apply,
+ mode_t mode, uid_t uid, gid_t gid,
+ struct udev_list *seclabel_list);
+void udev_node_remove(struct udev_device *dev);
+void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old);
+
+/* udev-ctrl.c */
+struct udev_ctrl;
+struct udev_ctrl *udev_ctrl_new(struct udev *udev);
+struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd);
+int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
+struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
+int udev_ctrl_cleanup(struct udev_ctrl *uctrl);
+struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl);
+int udev_ctrl_get_fd(struct udev_ctrl *uctrl);
+int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout);
+int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout);
+int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout);
+struct udev_ctrl_connection;
+struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl);
+struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn);
+struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn);
+struct udev_ctrl_msg;
+struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn);
+struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg);
+const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
+
+/* built-in commands */
+enum udev_builtin_cmd {
+#ifdef HAVE_BLKID
+ UDEV_BUILTIN_BLKID,
+#endif
+ UDEV_BUILTIN_BTRFS,
+ UDEV_BUILTIN_HWDB,
+ UDEV_BUILTIN_INPUT_ID,
+ UDEV_BUILTIN_KEYBOARD,
+#ifdef HAVE_KMOD
+ UDEV_BUILTIN_KMOD,
+#endif
+ UDEV_BUILTIN_NET_ID,
+ UDEV_BUILTIN_NET_LINK,
+ UDEV_BUILTIN_PATH_ID,
+ UDEV_BUILTIN_USB_ID,
+#ifdef HAVE_ACL
+ UDEV_BUILTIN_UACCESS,
+#endif
+ UDEV_BUILTIN_MAX
+};
+struct udev_builtin {
+ const char *name;
+ int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test);
+ const char *help;
+ int (*init)(struct udev *udev);
+ void (*exit)(struct udev *udev);
+ bool (*validate)(struct udev *udev);
+ bool run_once;
+};
+#ifdef HAVE_BLKID
+extern const struct udev_builtin udev_builtin_blkid;
+#endif
+extern const struct udev_builtin udev_builtin_btrfs;
+extern const struct udev_builtin udev_builtin_hwdb;
+extern const struct udev_builtin udev_builtin_input_id;
+extern const struct udev_builtin udev_builtin_keyboard;
+#ifdef HAVE_KMOD
+extern const struct udev_builtin udev_builtin_kmod;
+#endif
+extern const struct udev_builtin udev_builtin_net_id;
+extern const struct udev_builtin udev_builtin_net_setup_link;
+extern const struct udev_builtin udev_builtin_path_id;
+extern const struct udev_builtin udev_builtin_usb_id;
+extern const struct udev_builtin udev_builtin_uaccess;
+void udev_builtin_init(struct udev *udev);
+void udev_builtin_exit(struct udev *udev);
+enum udev_builtin_cmd udev_builtin_lookup(const char *command);
+const char *udev_builtin_name(enum udev_builtin_cmd cmd);
+bool udev_builtin_run_once(enum udev_builtin_cmd cmd);
+int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test);
+void udev_builtin_list(struct udev *udev);
+bool udev_builtin_validate(struct udev *udev);
+int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val);
+int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias,
+ const char *filter, bool test);
+
+/* udevadm commands */
+struct udevadm_cmd {
+ const char *name;
+ int (*cmd)(struct udev *udev, int argc, char *argv[]);
+ const char *help;
+ int debug;
+};
+extern const struct udevadm_cmd udevadm_info;
+extern const struct udevadm_cmd udevadm_trigger;
+extern const struct udevadm_cmd udevadm_settle;
+extern const struct udevadm_cmd udevadm_control;
+extern const struct udevadm_cmd udevadm_monitor;
+extern const struct udevadm_cmd udevadm_hwdb;
+extern const struct udevadm_cmd udevadm_test;
+extern const struct udevadm_cmd udevadm_test_builtin;