]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #10928 from yuwata/fix-9940
authorLennart Poettering <lennart@poettering.net>
Mon, 26 Nov 2018 17:46:47 +0000 (18:46 +0100)
committerGitHub <noreply@github.com>
Mon, 26 Nov 2018 17:46:47 +0000 (18:46 +0100)
network: also load foo.netdev.d/*.conf

83 files changed:
TODO
man/loader.conf.xml
src/backlight/backlight.c
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/basic/list.h
src/basic/path-util.c
src/basic/path-util.h
src/basic/strv.h
src/basic/terminal-util.c
src/basic/time-util.c
src/boot/efi/boot.c
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-mount.c
src/core/dbus-scope.c
src/core/dbus-service.c
src/core/dbus-slice.c
src/core/dbus-socket.c
src/core/dbus-swap.c
src/core/load-fragment.c
src/core/slice.c
src/core/umount.c
src/core/unit.c
src/core/unit.h
src/cryptsetup/cryptsetup-generator.c
src/fuzz/fuzz-main.c
src/fuzz/fuzz-udev-rules.c [new file with mode: 0644]
src/fuzz/meson.build
src/import/curl-util.c
src/import/curl-util.h
src/import/export-raw.c
src/import/export-tar.c
src/import/import-raw.c
src/import/import-tar.c
src/import/importd.c
src/import/pull-job.c
src/import/pull-raw.c
src/import/pull-tar.c
src/journal-remote/journal-remote-main.c
src/journal-remote/journal-upload.c
src/journal/journald-native.c
src/journal/sd-journal.c
src/libsystemd/sd-device/device-monitor.c
src/login/logind-core.c
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn.c
src/run/run.c
src/shared/acl-util.c
src/shared/generator.c
src/shared/web-util.c
src/test/test-path-util.c
src/test/test-strv.c
src/udev/net/ethtool-util.c
src/udev/net/link-config.c
src/udev/udev-builtin-keyboard.c
src/udev/udev-ctrl.c
src/udev/udev-rules.c
src/udev/udevd.c
test/TEST-19-DELEGATE/testsuite.sh
test/fuzz/fuzz-udev-rules/50-udev-default.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-block.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-cdrom_id.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-drm.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-evdev.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-input-id.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-persistent-input.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-persistent-storage.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-sensor.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/60-serial.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/64-btrfs.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/70-joystick.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/70-mouse.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/70-touchpad.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/75-net-description.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/75-probe_mtd.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/78-sound-card.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/80-drivers.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/80-net-setup-link.rules [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/99-systemd.rules [new file with mode: 0644]

diff --git a/TODO b/TODO
index 29ccde88a2a598b5919e11a8c6e5ed5b4cfed47d..ae1bedcd29e0732f1e7130c0d5104faffae006dc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -23,6 +23,8 @@ Janitorial Clean-ups:
 
 Features:
 
+* when we fork off generators and such, lower LIMIT_NOFILE soft limit to 1K
+
 * add a concept for automatically loading per-unit secrets off disk and
   inserting them into the kernel keyring. Maybe SecretsDirectory= similar to
   ConfigurationDirectory=.
index 6f8d0489d283affeb80ea188b71ef6b2dea1caf2..f9d98dd4d93cb5d55452e7dd329a0dfd4dc62ae5 100644 (file)
@@ -23,7 +23,7 @@
 
   <refsynopsisdiv>
     <para><filename><replaceable>ESP</replaceable>/loader/loader.conf</filename>,
-    <filename><replaceable>ESP</replaceable>/loader/loader.conf.d/*.conf</filename>
+    <filename><replaceable>ESP</replaceable>/loader/entries/*.conf</filename>
     </para>
   </refsynopsisdiv>
 
@@ -32,9 +32,9 @@
 
     <para>
     <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-    will read <filename>/loader/loader.conf</filename> and any files with the
+    will read <filename><replaceable>ESP</replaceable>/loader/loader.conf</filename> and any files with the
     <literal>.conf</literal> extension under
-    <filename>/loader/loader.conf.d/</filename> on the EFI system partition (ESP).
+    <filename><replaceable>ESP</replaceable>/loader/entries/</filename> on the EFI system partition (ESP).
     </para>
 
     <para>Each configuration file must consist of an option name, followed by
@@ -50,7 +50,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>The following configuration options are understood:</para>
+    <para>The following configuration options in <filename>loader.conf</filename> are understood:</para>
 
     <variablelist>
       <varlistentry>
index a866c6882336254c186782c341782052b904560b..780ad56eb1daa3e988b83525b6573468e8b3746a 100644 (file)
@@ -44,9 +44,7 @@ static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
                 c += strspn(c, DIGITS);
                 if (*c == '-') {
                         /* A connector DRM device, let's ignore all but LVDS and eDP! */
-
-                        if (!startswith(c, "-LVDS-") &&
-                            !startswith(c, "-Embedded DisplayPort-"))
+                        if (!STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
                                 return -EOPNOTSUPP;
                 }
 
index 94ae37dcb06b31004e8575ab770d89479c2fb021..ec29a6f4c97c5eacacb3a089aabbc33f7e021e32 100644 (file)
@@ -1852,9 +1852,7 @@ char *cg_escape(const char *p) {
          * needs free()! */
 
         if (IN_SET(p[0], 0, '_', '.') ||
-            streq(p, "notify_on_release") ||
-            streq(p, "release_agent") ||
-            streq(p, "tasks") ||
+            STR_IN_SET(p, "notify_on_release", "release_agent", "tasks") ||
             startswith(p, "cgroup."))
                 need_prefix = true;
         else {
@@ -2368,9 +2366,9 @@ int cg_mask_supported(CGroupMask *ret) {
         CGroupMask mask;
         int r;
 
-        /* Determines the mask of supported cgroup controllers. Only
-         * includes controllers we can make sense of and that are
-         * actually accessible. */
+        /* Determines the mask of supported cgroup controllers. Only includes controllers we can make sense of and that
+         * are actually accessible. Only covers real controllers, i.e. not the CGROUP_CONTROLLER_BPF_xyz
+         * pseudo-controllers. */
 
         r = cg_all_unified();
         if (r < 0)
@@ -2595,22 +2593,45 @@ int cg_unified_flush(void) {
         return cg_unified_update();
 }
 
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
+int cg_enable_everywhere(
+                CGroupMask supported,
+                CGroupMask mask,
+                const char *p,
+                CGroupMask *ret_result_mask) {
+
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *fs = NULL;
         CGroupController c;
+        CGroupMask ret = 0;
         int r;
 
         assert(p);
 
-        if (supported == 0)
+        if (supported == 0) {
+                if (ret_result_mask)
+                        *ret_result_mask = 0;
                 return 0;
+        }
 
         r = cg_all_unified();
         if (r < 0)
                 return r;
-        if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
+        if (r == 0) {
+                /* On the legacy hiearchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
+                 * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
+                 * caller tends to use the returned mask later on to compare if all controllers where properly joined,
+                 * and if not requeues realization. This use is the primary purpose of the return value, hence let's
+                 * minimize surprises here and reduce triggers for re-realization by always saying we fully
+                 * succeeded.) */
+                if (ret_result_mask)
+                        *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
+                                                                               * CGROUP_MASK_V2: The 'supported' mask
+                                                                               * might contain pure-V1 or BPF
+                                                                               * controllers, and we never want to
+                                                                               * claim that we could enable those with
+                                                                               * cgroup.subtree_control */
                 return 0;
+        }
 
         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
         if (r < 0)
@@ -2635,20 +2656,48 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
 
                         if (!f) {
                                 f = fopen(fs, "we");
-                                if (!f) {
-                                        log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
-                                        break;
-                                }
+                                if (!f)
+                                        return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
                         }
 
                         r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
                         if (r < 0) {
-                                log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
+                                log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
+                                                FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
                                 clearerr(f);
+
+                                /* If we can't turn off a controller, leave it on in the reported resulting mask. This
+                                 * happens for example when we attempt to turn off a controller up in the tree that is
+                                 * used down in the tree. */
+                                if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
+                                                                           * only here, and not follow the same logic
+                                                                           * for other errors such as EINVAL or
+                                                                           * EOPNOTSUPP or anything else. That's
+                                                                           * because EBUSY indicates that the
+                                                                           * controllers is currently enabled and
+                                                                           * cannot be disabled because something down
+                                                                           * the hierarchy is still using it. Any other
+                                                                           * error most likely means something like "I
+                                                                           * never heard of this controller" or
+                                                                           * similar. In the former case it's hence
+                                                                           * safe to assume the controller is still on
+                                                                           * after the failed operation, while in the
+                                                                           * latter case it's safer to assume the
+                                                                           * controller is unknown and hence certainly
+                                                                           * not enabled. */
+                                        ret |= bit;
+                        } else {
+                                /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
+                                if (FLAGS_SET(mask, bit))
+                                        ret |= bit;
                         }
                 }
         }
 
+        /* Let's return the precise set of controllers now enabled for the cgroup. */
+        if (ret_result_mask)
+                *ret_result_mask = ret;
+
         return 0;
 }
 
index 7919864df9198f6919ecff351ce67e2922d7fc37..ea9a3332904e99e129bdd2f4aa9f5f3a59d58724 100644 (file)
@@ -245,7 +245,7 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
 int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
 int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
 int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p);
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
 
 int cg_mask_supported(CGroupMask *ret);
 int cg_mask_from_string(const char *s, CGroupMask *ret);
index 040680c30ad907250d259f6a3ae0f148b3892e2f..f7f97000e0a2e0e15b0b9628238096e2980bc40b 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "macro.h"
+
 /* The head of the linked list. Use this in the structure that shall
  * contain the head of the linked list */
 #define LIST_HEAD(t,name)                                               \
@@ -13,8 +15,8 @@
 /* Initialize the list's head */
 #define LIST_HEAD_INIT(head)                                            \
         do {                                                            \
-                (head) = NULL; }                                        \
-        while (false)
+                (head) = NULL;                                          \
+        while (false)
 
 /* Initialize a list item */
 #define LIST_INIT(name,item)                                            \
index 2deb176240e55d44d4dee3ef53f5a306e22a221a..eb64c886e6c340e5da0974e1d9defc5e8f2ad197 100644 (file)
@@ -919,8 +919,7 @@ bool valid_device_allow_pattern(const char *path) {
         /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
          * accept it */
 
-        if (startswith(path, "block-") ||
-            startswith(path, "char-"))
+        if (STARTSWITH_SET(path, "block-", "char-"))
                 return true;
 
         return valid_device_node_path(path);
index 52a93b1c3fb5d3db37170d1fd110d1c973bd5daa..e2a51ff33a29757f3c98a3078b8c8c3e5cf280b9 100644 (file)
@@ -71,13 +71,13 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
 
 #define PATH_STARTSWITH_SET(p, ...)                             \
         ({                                                      \
-                char **s;                                       \
-                bool _found = false;                            \
-                STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__))         \
-                        if (path_startswith(p, *s)) {           \
-                               _found = true;                   \
-                               break;                           \
-                        }                                       \
+                const char *_p = (p);                           \
+                char *_found = NULL, **_i;                      \
+                STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
+                        _found = path_startswith(_p, *_i);      \
+                        if (_found)                             \
+                                break;                          \
+                }                                               \
                 _found;                                         \
         })
 
index 5f1803d87db593b85e8664e174f9d58433aea55a..aa4cd4aacabbe665c5646d4c7c033850b9612894 100644 (file)
@@ -144,6 +144,18 @@ void strv_print(char **l);
                 _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
         })
 
+#define STARTSWITH_SET(p, ...)                                  \
+        ({                                                      \
+                const char *_p = (p);                           \
+                char  *_found = NULL, **_i;                     \
+                STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
+                        _found = startswith(_p, *_i);           \
+                        if (_found)                             \
+                                break;                          \
+                }                                               \
+                _found;                                         \
+        })
+
 #define FOREACH_STRING(x, ...)                               \
         for (char **_l = ({                                  \
                 char **_ll = STRV_MAKE(__VA_ARGS__);         \
index 7fce84bf8213fafb8daad0326f2140b54df4aeb4..a5e4de00b0818e49d910e5e817d2a633864d9528 100644 (file)
@@ -1014,11 +1014,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
                                 return -ENOMEM;
                 }
         } else {
-                if (startswith(s, "/dev/"))
-                        p = s + 5;
-                else if (startswith(s, "../"))
-                        p = s + 3;
-                else
+                p = PATH_STARTSWITH_SET(s, "/dev/", "../");
+                if (!p)
                         p = s;
 
                 b = strdup(p);
index 30ad83d0a76b6e293ddf84e21fe9f525404463b9..f1f52f1beb057016d631cf05cf7ce10cb09f3029 100644 (file)
@@ -1384,9 +1384,7 @@ int get_timezone(char **tz) {
         if (r < 0)
                 return r; /* returns EINVAL if not a symlink */
 
-        e = path_startswith(t, "/usr/share/zoneinfo/");
-        if (!e)
-                e = path_startswith(t, "../usr/share/zoneinfo/");
+        e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
         if (!e)
                 return -EINVAL;
 
index d3a26fd7a0e0d0c18197ce1dc4f8febf0a2a85e9..4719eeaf4b806b2a76f1cc02802480fa8e997f95 100644 (file)
@@ -981,7 +981,7 @@ skip:
         }
 
         /* remove trailing whitespace */
-        while (linelen > 0 && strchra(sep, line[linelen-1]))
+        while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
                 linelen--;
         line[linelen] = '\0';
 
index 0fb09935e1fb0a2c0f68b213247ec7c5e28a0916..598b396186b83b6d0080e5d8b3e2e4d916a0a47f 100644 (file)
 
 #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
 
-bool manager_owns_root_cgroup(Manager *m) {
+/* Returns the log level to use when cgroup attribute writes fail. When an attribute is missing or we have access
+ * problems we downgrade to LOG_DEBUG. This is supposed to be nice to container managers and kernels which want to mask
+ * out specific attributes from us. */
+#define LOG_LEVEL_CGROUP_WRITE(r) (IN_SET(abs(r), ENOENT, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING)
+
+bool manager_owns_host_root_cgroup(Manager *m) {
         assert(m);
 
         /* Returns true if we are managing the root cgroup. Note that it isn't sufficient to just check whether the
@@ -34,24 +39,38 @@ bool manager_owns_root_cgroup(Manager *m) {
          * appears to be no nice way to detect whether we are in a CLONE_NEWCGROUP namespace we instead just check if
          * we run in any kind of container virtualization. */
 
+        if (MANAGER_IS_USER(m))
+                return false;
+
         if (detect_container() > 0)
                 return false;
 
         return empty_or_root(m->cgroup_root);
 }
 
-bool unit_has_root_cgroup(Unit *u) {
+bool unit_has_host_root_cgroup(Unit *u) {
         assert(u);
 
         /* Returns whether this unit manages the root cgroup. This will return true if this unit is the root slice and
          * the manager manages the root cgroup. */
 
-        if (!manager_owns_root_cgroup(u->manager))
+        if (!manager_owns_host_root_cgroup(u->manager))
                 return false;
 
         return unit_has_name(u, SPECIAL_ROOT_SLICE);
 }
 
+static int set_attribute_and_warn(Unit *u, const char *controller, const char *attribute, const char *value) {
+        int r;
+
+        r = cg_set_attribute(controller, u->cgroup_path, attribute, value);
+        if (r < 0)
+                log_unit_full(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%.*s': %m",
+                              strna(attribute), isempty(u->cgroup_path) ? "/" : u->cgroup_path, (int) strcspn(value, NEWLINE), value);
+
+        return r;
+}
+
 static void cgroup_compat_warn(void) {
         static bool cgroup_compat_warned = false;
 
@@ -72,29 +91,30 @@ static void cgroup_compat_warn(void) {
 void cgroup_context_init(CGroupContext *c) {
         assert(c);
 
-        /* Initialize everything to the kernel defaults, assuming the
-         * structure is preinitialized to 0 */
+        /* Initialize everything to the kernel defaults. */
 
-        c->cpu_weight = CGROUP_WEIGHT_INVALID;
-        c->startup_cpu_weight = CGROUP_WEIGHT_INVALID;
-        c->cpu_quota_per_sec_usec = USEC_INFINITY;
+        *c = (CGroupContext) {
+                .cpu_weight = CGROUP_WEIGHT_INVALID,
+                .startup_cpu_weight = CGROUP_WEIGHT_INVALID,
+                .cpu_quota_per_sec_usec = USEC_INFINITY,
 
-        c->cpu_shares = CGROUP_CPU_SHARES_INVALID;
-        c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID;
+                .cpu_shares = CGROUP_CPU_SHARES_INVALID,
+                .startup_cpu_shares = CGROUP_CPU_SHARES_INVALID,
 
-        c->memory_high = CGROUP_LIMIT_MAX;
-        c->memory_max = CGROUP_LIMIT_MAX;
-        c->memory_swap_max = CGROUP_LIMIT_MAX;
+                .memory_high = CGROUP_LIMIT_MAX,
+                .memory_max = CGROUP_LIMIT_MAX,
+                .memory_swap_max = CGROUP_LIMIT_MAX,
 
-        c->memory_limit = CGROUP_LIMIT_MAX;
+                .memory_limit = CGROUP_LIMIT_MAX,
 
-        c->io_weight = CGROUP_WEIGHT_INVALID;
-        c->startup_io_weight = CGROUP_WEIGHT_INVALID;
+                .io_weight = CGROUP_WEIGHT_INVALID,
+                .startup_io_weight = CGROUP_WEIGHT_INVALID,
 
-        c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
-        c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
+                .blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID,
+                .startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID,
 
-        c->tasks_max = (uint64_t) -1;
+                .tasks_max = CGROUP_LIMIT_MAX,
+        };
 }
 
 void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
@@ -430,9 +450,11 @@ static int whitelist_device(BPFProgram *prog, const char *path, const char *node
                         major(st.st_rdev), minor(st.st_rdev),
                         acc);
 
+                /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore EINVAL here. */
+
                 r = cg_set_attribute("devices", path, "devices.allow", buf);
                 if (r < 0)
-                        return log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING,
+                        return log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING,
                                               r, "Failed to set devices.allow on %s: %m", path);
 
                 return 0;
@@ -516,9 +538,12 @@ static int whitelist_major(BPFProgram *prog, const char *path, const char *name,
                                 maj,
                                 acc);
 
+                        /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore EINVAL
+                         * here. */
+
                         r = cg_set_attribute("devices", path, "devices.allow", buf);
                         if (r < 0)
-                                log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING,
+                                log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING,
                                                r, "Failed to set devices.allow on %s: %m", path);
                 }
         }
@@ -556,53 +581,42 @@ static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state)
                 return CGROUP_CPU_SHARES_DEFAULT;
 }
 
-static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota) {
-        char buf[MAX(DECIMAL_STR_MAX(uint64_t) + 1, (DECIMAL_STR_MAX(usec_t) + 1) * 2)];
-        int r;
+static void cgroup_apply_unified_cpu_weight(Unit *u, uint64_t weight) {
+        char buf[DECIMAL_STR_MAX(uint64_t) + 2];
 
         xsprintf(buf, "%" PRIu64 "\n", weight);
-        r = cg_set_attribute("cpu", u->cgroup_path, "cpu.weight", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set cpu.weight: %m");
+        (void) set_attribute_and_warn(u, "cpu", "cpu.weight", buf);
+}
+
+static void cgroup_apply_unified_cpu_quota(Unit *u, usec_t quota) {
+        char buf[(DECIMAL_STR_MAX(usec_t) + 1) * 2 + 1];
 
         if (quota != USEC_INFINITY)
                 xsprintf(buf, USEC_FMT " " USEC_FMT "\n",
                          quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC, CGROUP_CPU_QUOTA_PERIOD_USEC);
         else
                 xsprintf(buf, "max " USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
-
-        r = cg_set_attribute("cpu", u->cgroup_path, "cpu.max", buf);
-
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set cpu.max: %m");
+        (void) set_attribute_and_warn(u, "cpu", "cpu.max", buf);
 }
 
-static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota) {
-        char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1];
-        int r;
+static void cgroup_apply_legacy_cpu_shares(Unit *u, uint64_t shares) {
+        char buf[DECIMAL_STR_MAX(uint64_t) + 2];
 
         xsprintf(buf, "%" PRIu64 "\n", shares);
-        r = cg_set_attribute("cpu", u->cgroup_path, "cpu.shares", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set cpu.shares: %m");
+        (void) set_attribute_and_warn(u, "cpu", "cpu.shares", buf);
+}
+
+static void cgroup_apply_legacy_cpu_quota(Unit *u, usec_t quota) {
+        char buf[DECIMAL_STR_MAX(usec_t) + 2];
 
         xsprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
-        r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_period_us", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set cpu.cfs_period_us: %m");
+        (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_period_us", buf);
 
         if (quota != USEC_INFINITY) {
                 xsprintf(buf, USEC_FMT "\n", quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
-                r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf);
+                (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_quota_us", buf);
         } else
-                r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1");
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set cpu.cfs_quota_us: %m");
+                (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_quota_us", "-1\n");
 }
 
 static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) {
@@ -672,10 +686,7 @@ static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_
                 return;
 
         xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight);
-        r = cg_set_attribute("io", u->cgroup_path, "io.weight", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set io.weight: %m");
+        (void) set_attribute_and_warn(u, "io", "io.weight", buf);
 }
 
 static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint64_t blkio_weight) {
@@ -688,10 +699,7 @@ static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint
                 return;
 
         xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight);
-        r = cg_set_attribute("blkio", u->cgroup_path, "blkio.weight_device", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set blkio.weight_device: %m");
+        (void) set_attribute_and_warn(u, "blkio", "blkio.weight_device", buf);
 }
 
 static void cgroup_apply_io_device_latency(Unit *u, const char *dev_path, usec_t target) {
@@ -708,10 +716,7 @@ static void cgroup_apply_io_device_latency(Unit *u, const char *dev_path, usec_t
         else
                 xsprintf(buf, "%u:%u target=max\n", major(dev), minor(dev));
 
-        r = cg_set_attribute("io", u->cgroup_path, "io.latency", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set io.latency on cgroup %s: %m", u->cgroup_path);
+        (void) set_attribute_and_warn(u, "io", "io.latency", buf);
 }
 
 static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) {
@@ -734,10 +739,7 @@ static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t
         xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev),
                  limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX],
                  limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]);
-        r = cg_set_attribute("io", u->cgroup_path, "io.max", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set io.max: %m");
+        (void) set_attribute_and_warn(u, "io", "io.max", buf);
 }
 
 static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) {
@@ -750,16 +752,10 @@ static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint6
                 return;
 
         sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps);
-        r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set blkio.throttle.read_bps_device: %m");
+        (void) set_attribute_and_warn(u, "blkio", "blkio.throttle.read_bps_device", buf);
 
         sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps);
-        r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set blkio.throttle.write_bps_device: %m");
+        (void) set_attribute_and_warn(u, "blkio", "blkio.throttle.write_bps_device", buf);
 }
 
 static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
@@ -767,16 +763,12 @@ static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
 }
 
 static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
-        char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max";
-        int r;
+        char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max\n";
 
         if (v != CGROUP_LIMIT_MAX)
                 xsprintf(buf, "%" PRIu64 "\n", v);
 
-        r = cg_set_attribute("memory", u->cgroup_path, file, buf);
-        if (r < 0)
-                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                              "Failed to set %s: %m", file);
+        (void) set_attribute_and_warn(u, "memory", file, buf);
 }
 
 static void cgroup_apply_firewall(Unit *u) {
@@ -797,7 +789,7 @@ static void cgroup_context_apply(
 
         const char *path;
         CGroupContext *c;
-        bool is_root;
+        bool is_host_root, is_local_root;
         int r;
 
         assert(u);
@@ -806,120 +798,135 @@ static void cgroup_context_apply(
         if (apply_mask == 0)
                 return;
 
-        /* Some cgroup attributes are not supported on the root cgroup, hence silently ignore */
-        is_root = unit_has_root_cgroup(u);
+        /* Some cgroup attributes are not supported on the host root cgroup, hence silently ignore them here. And other
+         * attributes should only be managed for cgroups further down the tree. */
+        is_local_root = unit_has_name(u, SPECIAL_ROOT_SLICE);
+        is_host_root = unit_has_host_root_cgroup(u);
 
         assert_se(c = unit_get_cgroup_context(u));
         assert_se(path = u->cgroup_path);
 
-        if (is_root) /* Make sure we don't try to display messages with an empty path. */
+        if (is_local_root) /* Make sure we don't try to display messages with an empty path. */
                 path = "/";
 
         /* We generally ignore errors caused by read-only mounted
          * cgroup trees (assuming we are running in a container then),
          * and missing cgroups, i.e. EROFS and ENOENT. */
 
-        if ((apply_mask & CGROUP_MASK_CPU) && !is_root) {
+        if (apply_mask & CGROUP_MASK_CPU) {
                 bool has_weight, has_shares;
 
                 has_weight = cgroup_context_has_cpu_weight(c);
                 has_shares = cgroup_context_has_cpu_shares(c);
 
                 if (cg_all_unified() > 0) {
-                        uint64_t weight;
 
-                        if (has_weight)
-                                weight = cgroup_context_cpu_weight(c, state);
-                        else if (has_shares) {
-                                uint64_t shares = cgroup_context_cpu_shares(c, state);
+                        /* In fully unified mode these attributes don't exist on the host cgroup root, and inside of
+                         * containers we want to leave control of these to the container manager (and if delegation is
+                         * used we couldn't even write to them if we wanted to). */
+                        if (!is_local_root) {
+                                uint64_t weight;
 
-                                weight = cgroup_cpu_shares_to_weight(shares);
+                                if (has_weight)
+                                        weight = cgroup_context_cpu_weight(c, state);
+                                else if (has_shares) {
+                                        uint64_t shares;
 
-                                log_cgroup_compat(u, "Applying [Startup]CPUShares %" PRIu64 " as [Startup]CPUWeight %" PRIu64 " on %s",
-                                                  shares, weight, path);
-                        } else
-                                weight = CGROUP_WEIGHT_DEFAULT;
+                                        shares = cgroup_context_cpu_shares(c, state);
+                                        weight = cgroup_cpu_shares_to_weight(shares);
 
-                        cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec);
-                } else {
-                        uint64_t shares;
+                                        log_cgroup_compat(u, "Applying [Startup]CPUShares %" PRIu64 " as [Startup]CPUWeight %" PRIu64 " on %s",
+                                                          shares, weight, path);
+                                } else
+                                        weight = CGROUP_WEIGHT_DEFAULT;
 
-                        if (has_weight) {
-                                uint64_t weight = cgroup_context_cpu_weight(c, state);
+                                cgroup_apply_unified_cpu_weight(u, weight);
+                                cgroup_apply_unified_cpu_quota(u, c->cpu_quota_per_sec_usec);
+                        }
 
-                                shares = cgroup_cpu_weight_to_shares(weight);
+                } else {
+                        /* Setting the weight makes very little sense on the host root cgroup, as there are no other
+                         * cgroups at this level. And for containers we want to leave management of this to the
+                         * container manager */
+                        if (!is_local_root) {
+                                uint64_t shares;
+
+                                if (has_weight) {
+                                        uint64_t weight;
+
+                                        weight = cgroup_context_cpu_weight(c, state);
+                                        shares = cgroup_cpu_weight_to_shares(weight);
+
+                                        log_cgroup_compat(u, "Applying [Startup]CPUWeight %" PRIu64 " as [Startup]CPUShares %" PRIu64 " on %s",
+                                                          weight, shares, path);
+                                } else if (has_shares)
+                                        shares = cgroup_context_cpu_shares(c, state);
+                                else
+                                        shares = CGROUP_CPU_SHARES_DEFAULT;
 
-                                log_cgroup_compat(u, "Applying [Startup]CPUWeight %" PRIu64 " as [Startup]CPUShares %" PRIu64 " on %s",
-                                                  weight, shares, path);
-                        } else if (has_shares)
-                                shares = cgroup_context_cpu_shares(c, state);
-                        else
-                                shares = CGROUP_CPU_SHARES_DEFAULT;
+                                cgroup_apply_legacy_cpu_shares(u, shares);
+                        }
 
-                        cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec);
+                        /* The "cpu" quota attribute is available on the host root, hence manage it there. But in
+                         * containers let's leave this to the container manager. */
+                        if (is_host_root || !is_local_root)
+                                cgroup_apply_legacy_cpu_quota(u, c->cpu_quota_per_sec_usec);
                 }
         }
 
-        if (apply_mask & CGROUP_MASK_IO) {
-                bool has_io = cgroup_context_has_io_config(c);
-                bool has_blockio = cgroup_context_has_blockio_config(c);
+        /* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroupsv2
+         * controller), and in case of containers we want to leave control of these attributes to the container manager
+         * (and we couldn't access that stuff anyway, even if we tried if proper delegation is used). */
+        if ((apply_mask & CGROUP_MASK_IO) && !is_local_root) {
+                char buf[8+DECIMAL_STR_MAX(uint64_t)+1];
+                bool has_io, has_blockio;
+                uint64_t weight;
 
-                if (!is_root) {
-                        char buf[8+DECIMAL_STR_MAX(uint64_t)+1];
-                        uint64_t weight;
+                has_io = cgroup_context_has_io_config(c);
+                has_blockio = cgroup_context_has_blockio_config(c);
 
-                        if (has_io)
-                                weight = cgroup_context_io_weight(c, state);
-                        else if (has_blockio) {
-                                uint64_t blkio_weight = cgroup_context_blkio_weight(c, state);
+                if (has_io)
+                        weight = cgroup_context_io_weight(c, state);
+                else if (has_blockio) {
+                        uint64_t blkio_weight;
 
-                                weight = cgroup_weight_blkio_to_io(blkio_weight);
+                        blkio_weight = cgroup_context_blkio_weight(c, state);
+                        weight = cgroup_weight_blkio_to_io(blkio_weight);
 
-                                log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64,
-                                                  blkio_weight, weight);
-                        } else
-                                weight = CGROUP_WEIGHT_DEFAULT;
-
-                        xsprintf(buf, "default %" PRIu64 "\n", weight);
-                        r = cg_set_attribute("io", path, "io.weight", buf);
-                        if (r < 0)
-                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                                              "Failed to set io.weight: %m");
+                        log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64,
+                                          blkio_weight, weight);
+                } else
+                        weight = CGROUP_WEIGHT_DEFAULT;
 
-                        if (has_io) {
-                                CGroupIODeviceWeight *w;
+                xsprintf(buf, "default %" PRIu64 "\n", weight);
+                (void) set_attribute_and_warn(u, "io", "io.weight", buf);
 
-                                LIST_FOREACH(device_weights, w, c->io_device_weights)
-                                        cgroup_apply_io_device_weight(u, w->path, w->weight);
-                        } else if (has_blockio) {
-                                CGroupBlockIODeviceWeight *w;
+                if (has_io) {
+                        CGroupIODeviceLatency *latency;
+                        CGroupIODeviceLimit *limit;
+                        CGroupIODeviceWeight *w;
 
-                                LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
-                                        weight = cgroup_weight_blkio_to_io(w->weight);
+                        LIST_FOREACH(device_weights, w, c->io_device_weights)
+                                cgroup_apply_io_device_weight(u, w->path, w->weight);
 
-                                        log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s",
-                                                          w->weight, weight, w->path);
-
-                                        cgroup_apply_io_device_weight(u, w->path, weight);
-                                }
-                        }
+                        LIST_FOREACH(device_limits, limit, c->io_device_limits)
+                                cgroup_apply_io_device_limit(u, limit->path, limit->limits);
 
-                        if (has_io) {
-                                CGroupIODeviceLatency *l;
+                        LIST_FOREACH(device_latencies, latency, c->io_device_latencies)
+                                cgroup_apply_io_device_latency(u, latency->path, latency->target_usec);
 
-                                LIST_FOREACH(device_latencies, l, c->io_device_latencies)
-                                        cgroup_apply_io_device_latency(u, l->path, l->target_usec);
-                        }
-                }
+                } else if (has_blockio) {
+                        CGroupBlockIODeviceWeight *w;
+                        CGroupBlockIODeviceBandwidth *b;
 
-                if (has_io) {
-                        CGroupIODeviceLimit *l;
+                        LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
+                                weight = cgroup_weight_blkio_to_io(w->weight);
 
-                        LIST_FOREACH(device_limits, l, c->io_device_limits)
-                                cgroup_apply_io_device_limit(u, l->path, l->limits);
+                                log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s",
+                                                  w->weight, weight, w->path);
 
-                } else if (has_blockio) {
-                        CGroupBlockIODeviceBandwidth *b;
+                                cgroup_apply_io_device_weight(u, w->path, weight);
+                        }
 
                         LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
                                 uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX];
@@ -940,16 +947,21 @@ static void cgroup_context_apply(
         }
 
         if (apply_mask & CGROUP_MASK_BLKIO) {
-                bool has_io = cgroup_context_has_io_config(c);
-                bool has_blockio = cgroup_context_has_blockio_config(c);
+                bool has_io, has_blockio;
+
+                has_io = cgroup_context_has_io_config(c);
+                has_blockio = cgroup_context_has_blockio_config(c);
 
-                if (!is_root) {
+                /* Applying a 'weight' never makes sense for the host root cgroup, and for containers this should be
+                 * left to our container manager, too. */
+                if (!is_local_root) {
                         char buf[DECIMAL_STR_MAX(uint64_t)+1];
                         uint64_t weight;
 
                         if (has_io) {
-                                uint64_t io_weight = cgroup_context_io_weight(c, state);
+                                uint64_t io_weight;
 
+                                io_weight = cgroup_context_io_weight(c, state);
                                 weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state));
 
                                 log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64,
@@ -960,10 +972,7 @@ static void cgroup_context_apply(
                                 weight = CGROUP_BLKIO_WEIGHT_DEFAULT;
 
                         xsprintf(buf, "%" PRIu64 "\n", weight);
-                        r = cg_set_attribute("blkio", path, "blkio.weight", buf);
-                        if (r < 0)
-                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                                              "Failed to set blkio.weight: %m");
+                        (void) set_attribute_and_warn(u, "blkio", "blkio.weight", buf);
 
                         if (has_io) {
                                 CGroupIODeviceWeight *w;
@@ -984,65 +993,80 @@ static void cgroup_context_apply(
                         }
                 }
 
-                if (has_io) {
-                        CGroupIODeviceLimit *l;
+                /* The bandwith limits are something that make sense to be applied to the host's root but not container
+                 * roots, as there we want the container manager to handle it */
+                if (is_host_root || !is_local_root) {
+                        if (has_io) {
+                                CGroupIODeviceLimit *l;
 
-                        LIST_FOREACH(device_limits, l, c->io_device_limits) {
-                                log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s",
-                                                  l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path);
+                                LIST_FOREACH(device_limits, l, c->io_device_limits) {
+                                        log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s",
+                                                          l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path);
 
-                                cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]);
-                        }
-                } else if (has_blockio) {
-                        CGroupBlockIODeviceBandwidth *b;
+                                        cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]);
+                                }
+                        } else if (has_blockio) {
+                                CGroupBlockIODeviceBandwidth *b;
 
-                        LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths)
-                                cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps);
+                                LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths)
+                                        cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps);
+                        }
                 }
         }
 
-        if ((apply_mask & CGROUP_MASK_MEMORY) && !is_root) {
-                if (cg_all_unified() > 0) {
-                        uint64_t max, swap_max = CGROUP_LIMIT_MAX;
+        if (apply_mask & CGROUP_MASK_MEMORY) {
 
-                        if (cgroup_context_has_unified_memory_config(c)) {
-                                max = c->memory_max;
-                                swap_max = c->memory_swap_max;
-                        } else {
-                                max = c->memory_limit;
+                if (cg_all_unified() > 0) {
+                        /* In unified mode 'memory' attributes do not exist on the root cgroup. And if we run in a
+                         * container we want to leave control to the container manager (and if proper delegation is
+                         * used we couldn't even write to this if we wanted to. */
+                        if (!is_local_root) {
+                                uint64_t max, swap_max = CGROUP_LIMIT_MAX;
+
+                                if (cgroup_context_has_unified_memory_config(c)) {
+                                        max = c->memory_max;
+                                        swap_max = c->memory_swap_max;
+                                } else {
+                                        max = c->memory_limit;
+
+                                        if (max != CGROUP_LIMIT_MAX)
+                                                log_cgroup_compat(u, "Applying MemoryLimit=%" PRIu64 " as MemoryMax=", max);
+                                }
 
-                                if (max != CGROUP_LIMIT_MAX)
-                                        log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max);
+                                cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min);
+                                cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
+                                cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
+                                cgroup_apply_unified_memory_limit(u, "memory.max", max);
+                                cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
                         }
-
-                        cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min);
-                        cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
-                        cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
-                        cgroup_apply_unified_memory_limit(u, "memory.max", max);
-                        cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
                 } else {
-                        char buf[DECIMAL_STR_MAX(uint64_t) + 1];
-                        uint64_t val;
 
-                        if (cgroup_context_has_unified_memory_config(c)) {
-                                val = c->memory_max;
-                                log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", val);
-                        } else
-                                val = c->memory_limit;
+                        /* In legacy mode 'memory' exists on the host root, but in container mode we want to leave it
+                         * to the container manager around us */
+                        if (is_host_root || !is_local_root) {
+                                char buf[DECIMAL_STR_MAX(uint64_t) + 1];
+                                uint64_t val;
 
-                        if (val == CGROUP_LIMIT_MAX)
-                                strncpy(buf, "-1\n", sizeof(buf));
-                        else
-                                xsprintf(buf, "%" PRIu64 "\n", val);
+                                if (cgroup_context_has_unified_memory_config(c)) {
+                                        val = c->memory_max;
+                                        log_cgroup_compat(u, "Applying MemoryMax=%" PRIi64 " as MemoryLimit=", val);
+                                } else
+                                        val = c->memory_limit;
 
-                        r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
-                        if (r < 0)
-                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                                              "Failed to set memory.limit_in_bytes: %m");
+                                if (val == CGROUP_LIMIT_MAX)
+                                        strncpy(buf, "-1\n", sizeof(buf));
+                                else
+                                        xsprintf(buf, "%" PRIu64 "\n", val);
+
+                                (void) set_attribute_and_warn(u, "memory", "memory.limit_in_bytes", buf);
+                        }
                 }
         }
 
-        if ((apply_mask & (CGROUP_MASK_DEVICES | CGROUP_MASK_BPF_DEVICES)) && !is_root) {
+        /* On cgroupsv2 we can apply BPF everywhre. On cgroupsv1 we apply it everywhere except for the root of
+         * containers, where we leave this to the manager */
+        if ((apply_mask & (CGROUP_MASK_DEVICES | CGROUP_MASK_BPF_DEVICES)) &&
+            (is_host_root || cg_all_unified() > 0 || !is_local_root)) {
                 _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
                 CGroupDeviceAllow *a;
 
@@ -1051,8 +1075,7 @@ static void cgroup_context_apply(
                         if (r < 0)
                                 log_unit_warning_errno(u, r, "Failed to initialize device control bpf program: %m");
                 } else {
-                        /* Changing the devices list of a populated cgroup
-                         * might result in EINVAL, hence ignore EINVAL
+                        /* Changing the devices list of a populated cgroup might result in EINVAL, hence ignore EINVAL
                          * here. */
 
                         if (c->device_allow || c->device_policy != CGROUP_AUTO)
@@ -1060,8 +1083,8 @@ static void cgroup_context_apply(
                         else
                                 r = cg_set_attribute("devices", path, "devices.allow", "a");
                         if (r < 0)
-                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                                              "Failed to reset devices.list: %m");
+                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
+                                              "Failed to reset devices.allow/devices.deny: %m");
                 }
 
                 if (c->device_policy == CGROUP_CLOSED ||
@@ -1128,7 +1151,7 @@ static void cgroup_context_apply(
 
         if (apply_mask & CGROUP_MASK_PIDS) {
 
-                if (is_root) {
+                if (is_host_root) {
                         /* So, the "pids" controller does not expose anything on the root cgroup, in order not to
                          * replicate knobs exposed elsewhere needlessly. We abstract this away here however, and when
                          * the knobs of the root cgroup are modified propagate this to the relevant sysctls. There's a
@@ -1151,20 +1174,20 @@ static void cgroup_context_apply(
                                 r = 0;
 
                         if (r < 0)
-                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+                                log_unit_full(u, LOG_LEVEL_CGROUP_WRITE(r), r,
                                               "Failed to write to tasks limit sysctls: %m");
+                }
 
-                } else {
+                /* The attribute itself is not available on the host root cgroup, and in the container case we want to
+                 * leave it for the container manager. */
+                if (!is_local_root) {
                         if (c->tasks_max != CGROUP_LIMIT_MAX) {
                                 char buf[DECIMAL_STR_MAX(uint64_t) + 2];
 
                                 sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
-                                r = cg_set_attribute("pids", path, "pids.max", buf);
+                                (void) set_attribute_and_warn(u, "pids", "pids.max", buf);
                         } else
-                                r = cg_set_attribute("pids", path, "pids.max", "max");
-                        if (r < 0)
-                                log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
-                                              "Failed to set pids.max: %m");
+                                (void) set_attribute_and_warn(u, "pids", "pids.max", "max\n");
                 }
         }
 
@@ -1172,7 +1195,35 @@ static void cgroup_context_apply(
                 cgroup_apply_firewall(u);
 }
 
-CGroupMask cgroup_context_get_mask(CGroupContext *c) {
+static bool unit_get_needs_bpf_firewall(Unit *u) {
+        CGroupContext *c;
+        Unit *p;
+        assert(u);
+
+        c = unit_get_cgroup_context(u);
+        if (!c)
+                return false;
+
+        if (c->ip_accounting ||
+            c->ip_address_allow ||
+            c->ip_address_deny)
+                return true;
+
+        /* If any parent slice has an IP access list defined, it applies too */
+        for (p = UNIT_DEREF(u->slice); p; p = UNIT_DEREF(p->slice)) {
+                c = unit_get_cgroup_context(p);
+                if (!c)
+                        return false;
+
+                if (c->ip_address_allow ||
+                    c->ip_address_deny)
+                        return true;
+        }
+
+        return false;
+}
+
+static CGroupMask cgroup_context_get_mask(CGroupContext *c) {
         CGroupMask mask = 0;
 
         /* Figure out which controllers we need, based on the cgroup context object */
@@ -1204,7 +1255,7 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) {
         return CGROUP_MASK_EXTEND_JOINED(mask);
 }
 
-CGroupMask unit_get_bpf_mask(Unit *u) {
+static CGroupMask unit_get_bpf_mask(Unit *u) {
         CGroupMask mask = 0;
 
         /* Figure out which controllers we need, based on the cgroup context, possibly taking into account children
@@ -1219,7 +1270,11 @@ CGroupMask unit_get_bpf_mask(Unit *u) {
 CGroupMask unit_get_own_mask(Unit *u) {
         CGroupContext *c;
 
-        /* Returns the mask of controllers the unit needs for itself */
+        /* Returns the mask of controllers the unit needs for itself. If a unit is not properly loaded, return an empty
+         * mask, as we shouldn't reflect it in the cgroup hierarchy then. */
+
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
         c = unit_get_cgroup_context(u);
         if (!c)
@@ -1257,7 +1312,7 @@ CGroupMask unit_get_members_mask(Unit *u) {
         /* Returns the mask of controllers all of the unit's children require, merged */
 
         if (u->cgroup_members_mask_valid)
-                return u->cgroup_members_mask;
+                return u->cgroup_members_mask; /* Use cached value if possible */
 
         u->cgroup_members_mask = 0;
 
@@ -1333,81 +1388,14 @@ CGroupMask unit_get_enable_mask(Unit *u) {
         return mask;
 }
 
-bool unit_get_needs_bpf_firewall(Unit *u) {
-        CGroupContext *c;
-        Unit *p;
+void unit_invalidate_cgroup_members_masks(Unit *u) {
         assert(u);
 
-        c = unit_get_cgroup_context(u);
-        if (!c)
-                return false;
-
-        if (c->ip_accounting ||
-            c->ip_address_allow ||
-            c->ip_address_deny)
-                return true;
+        /* Recurse invalidate the member masks cache all the way up the tree */
+        u->cgroup_members_mask_valid = false;
 
-        /* If any parent slice has an IP access list defined, it applies too */
-        for (p = UNIT_DEREF(u->slice); p; p = UNIT_DEREF(p->slice)) {
-                c = unit_get_cgroup_context(p);
-                if (!c)
-                        return false;
-
-                if (c->ip_address_allow ||
-                    c->ip_address_deny)
-                        return true;
-        }
-
-        return false;
-}
-
-/* Recurse from a unit up through its containing slices, propagating
- * mask bits upward. A unit is also member of itself. */
-void unit_update_cgroup_members_masks(Unit *u) {
-        CGroupMask m;
-        bool more;
-
-        assert(u);
-
-        /* Calculate subtree mask */
-        m = unit_get_subtree_mask(u);
-
-        /* See if anything changed from the previous invocation. If
-         * not, we're done. */
-        if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask)
-                return;
-
-        more =
-                u->cgroup_subtree_mask_valid &&
-                ((m & ~u->cgroup_subtree_mask) != 0) &&
-                ((~m & u->cgroup_subtree_mask) == 0);
-
-        u->cgroup_subtree_mask = m;
-        u->cgroup_subtree_mask_valid = true;
-
-        if (UNIT_ISSET(u->slice)) {
-                Unit *s = UNIT_DEREF(u->slice);
-
-                if (more)
-                        /* There's more set now than before. We
-                         * propagate the new mask to the parent's mask
-                         * (not caring if it actually was valid or
-                         * not). */
-
-                        s->cgroup_members_mask |= m;
-
-                else
-                        /* There's less set now than before (or we
-                         * don't know), we need to recalculate
-                         * everything, so let's invalidate the
-                         * parent's members mask */
-
-                        s->cgroup_members_mask_valid = false;
-
-                /* And now make sure that this change also hits our
-                 * grandparents */
-                unit_update_cgroup_members_masks(s);
-        }
+        if (UNIT_ISSET(u->slice))
+                unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
 }
 
 const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
@@ -1565,14 +1553,12 @@ static int unit_create_cgroup(
                 CGroupMask target_mask,
                 CGroupMask enable_mask) {
 
-        CGroupContext *c;
-        int r;
         bool created;
+        int r;
 
         assert(u);
 
-        c = unit_get_cgroup_context(u);
-        if (!c)
+        if (!UNIT_HAS_CGROUP_CONTEXT(u))
                 return 0;
 
         /* Figure out our cgroup path */
@@ -1590,19 +1576,38 @@ static int unit_create_cgroup(
         (void) unit_watch_cgroup(u);
 
         /* Preserve enabled controllers in delegated units, adjust others. */
-        if (created || !unit_cgroup_delegate(u)) {
+        if (created || !u->cgroup_realized || !unit_cgroup_delegate(u)) {
+                CGroupMask result_mask = 0;
 
                 /* Enable all controllers we need */
-                r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path);
+                r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path, &result_mask);
                 if (r < 0)
-                        log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m",
-                                               u->cgroup_path);
+                        log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
+
+                /* If we just turned off a controller, this might release the controller for our parent too, let's
+                 * enqueue the parent for re-realization in that case again. */
+                if (UNIT_ISSET(u->slice)) {
+                        CGroupMask turned_off;
+
+                        turned_off = (u->cgroup_realized ? u->cgroup_enabled_mask & ~result_mask : 0);
+                        if (turned_off != 0) {
+                                Unit *parent;
+
+                                /* Force the parent to propagate the enable mask to the kernel again, by invalidating
+                                 * the controller we just turned off. */
+
+                                for (parent = UNIT_DEREF(u->slice); parent; parent = UNIT_DEREF(parent->slice))
+                                        unit_invalidate_cgroup(parent, turned_off);
+                        }
+                }
+
+                /* Remember what's actually enabled now */
+                u->cgroup_enabled_mask = result_mask;
         }
 
         /* Keep track that this is now realized */
         u->cgroup_realized = true;
         u->cgroup_realized_mask = target_mask;
-        u->cgroup_enabled_mask = enable_mask;
 
         if (u->type != UNIT_SLICE && !unit_cgroup_delegate(u)) {
 
@@ -1782,13 +1787,28 @@ static bool unit_has_mask_realized(
 
         assert(u);
 
+        /* Returns true if this unit is fully realized. We check four things:
+         *
+         * 1. Whether the cgroup was created at all
+         * 2. Whether the cgroup was created in all the hierarchies we need it to be created in (in case of cgroupsv1)
+         * 3. Whether the cgroup has all the right controllers enabled (in case of cgroupsv2)
+         * 4. Whether the invalidation mask is currently zero
+         *
+         * If you wonder why we mask the target realization and enable mask with CGROUP_MASK_V1/CGROUP_MASK_V2: note
+         * that there are three sets of bitmasks: CGROUP_MASK_V1 (for real cgroupv1 controllers), CGROUP_MASK_V2 (for
+         * real cgroupv2 controllers) and CGROUP_MASK_BPF (for BPF-based pseudo-controllers). Now, cgroup_realized_mask
+         * is only matters for cgroupsv1 controllers, and cgroup_enabled_mask only used for cgroupsv2, and if they
+         * differ in the others, we don't really care. (After all, the cgroup_enabled_mask tracks with controllers are
+         * enabled through cgroup.subtree_control, and since the BPF pseudo-controllers don't show up there, they
+         * simply don't matter. */
+
         return u->cgroup_realized &&
-                u->cgroup_realized_mask == target_mask &&
-                u->cgroup_enabled_mask == enable_mask &&
+                ((u->cgroup_realized_mask ^ target_mask) & CGROUP_MASK_V1) == 0 &&
+                ((u->cgroup_enabled_mask ^ enable_mask) & CGROUP_MASK_V2) == 0 &&
                 u->cgroup_invalidated_mask == 0;
 }
 
-static void unit_add_to_cgroup_realize_queue(Unit *u) {
+void unit_add_to_cgroup_realize_queue(Unit *u) {
         assert(u);
 
         if (u->in_cgroup_realize_queue)
@@ -1946,7 +1966,8 @@ int unit_realize_cgroup(Unit *u) {
 void unit_release_cgroup(Unit *u) {
         assert(u);
 
-        /* Forgets all cgroup details for this cgroup */
+        /* Forgets all cgroup details for this cgroup â€” but does *not* destroy the cgroup. This is hence OK to call
+         * when we close down everything for reexecution, where we really want to leave the cgroup in place. */
 
         if (u->cgroup_path) {
                 (void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
@@ -2371,7 +2392,7 @@ int manager_setup_cgroup(Manager *m) {
 
                 (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify");
 
-        } else if (MANAGER_IS_SYSTEM(m) && !MANAGER_IS_TEST_RUN(m)) {
+        } else if (MANAGER_IS_SYSTEM(m) && manager_owns_host_root_cgroup(m) && !MANAGER_IS_TEST_RUN(m)) {
 
                 /* On the legacy hierarchy we only get notifications via cgroup agents. (Which isn't really reliable,
                  * since it does not generate events when control groups with children run empty. */
@@ -2549,7 +2570,7 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
                 return -ENODATA;
 
         /* The root cgroup doesn't expose this information, let's get it from /proc instead */
-        if (unit_has_root_cgroup(u))
+        if (unit_has_host_root_cgroup(u))
                 return procfs_memory_get_current(ret);
 
         if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
@@ -2584,7 +2605,7 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret) {
                 return -ENODATA;
 
         /* The root cgroup doesn't expose this information, let's get it from /proc instead */
-        if (unit_has_root_cgroup(u))
+        if (unit_has_host_root_cgroup(u))
                 return procfs_tasks_get_current(ret);
 
         if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0)
@@ -2611,7 +2632,7 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
                 return -ENODATA;
 
         /* The root cgroup doesn't expose this information, let's get it from /proc instead */
-        if (unit_has_root_cgroup(u))
+        if (unit_has_host_root_cgroup(u))
                 return procfs_cpu_get_usage(ret);
 
         r = cg_all_unified();
@@ -2778,10 +2799,10 @@ void unit_invalidate_cgroup(Unit *u, CGroupMask m) {
         if (m & (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT))
                 m |= CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT;
 
-        if ((u->cgroup_realized_mask & m) == 0) /* NOP? */
+        if (FLAGS_SET(u->cgroup_invalidated_mask, m)) /* NOP? */
                 return;
 
-        u->cgroup_realized_mask &= ~m;
+        u->cgroup_invalidated_mask |= m;
         unit_add_to_cgroup_realize_queue(u);
 }
 
index d7daed3fe052863d6c47034edf876c3373ab65ec..828b6f07951ab77411f6a634275aab4f3dee4d62 100644 (file)
@@ -137,8 +137,6 @@ void cgroup_context_init(CGroupContext *c);
 void cgroup_context_done(CGroupContext *c);
 void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix);
 
-CGroupMask cgroup_context_get_mask(CGroupContext *c);
-
 void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
 void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
 void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l);
@@ -153,14 +151,12 @@ CGroupMask unit_get_delegate_mask(Unit *u);
 CGroupMask unit_get_members_mask(Unit *u);
 CGroupMask unit_get_siblings_mask(Unit *u);
 CGroupMask unit_get_subtree_mask(Unit *u);
-
 CGroupMask unit_get_target_mask(Unit *u);
 CGroupMask unit_get_enable_mask(Unit *u);
 
-bool unit_get_needs_bpf_firewall(Unit *u);
-CGroupMask unit_get_bpf_mask(Unit *u);
+void unit_invalidate_cgroup_members_masks(Unit *u);
 
-void unit_update_cgroup_members_masks(Unit *u);
+void unit_add_to_cgroup_realize_queue(Unit *u);
 
 const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask);
 char *unit_default_cgroup_path(Unit *u);
@@ -204,8 +200,8 @@ int unit_reset_ip_accounting(Unit *u);
         cc ? cc->name : false;                          \
         })
 
-bool manager_owns_root_cgroup(Manager *m);
-bool unit_has_root_cgroup(Unit *u);
+bool manager_owns_host_root_cgroup(Manager *m);
+bool unit_has_host_root_cgroup(Unit *u);
 
 int manager_notify_cgroup_empty(Manager *m, const char *group);
 
index 3f98d3ecf0dd0ca91ddef300953659446abeff84..b6d61627ebb13b446c269856d42db10aa5acc5c3 100644 (file)
@@ -145,7 +145,7 @@ int bus_mount_set_property(
 int bus_mount_commit_properties(Unit *u) {
         assert(u);
 
-        unit_update_cgroup_members_masks(u);
+        unit_invalidate_cgroup_members_masks(u);
         unit_realize_cgroup(u);
 
         return 0;
index 5d9fe98857a40ae54289f44fcfd97c52eeef2427..bb807df2e928357a5f4cf3985d5473042fbad771 100644 (file)
@@ -186,7 +186,7 @@ int bus_scope_set_property(
 int bus_scope_commit_properties(Unit *u) {
         assert(u);
 
-        unit_update_cgroup_members_masks(u);
+        unit_invalidate_cgroup_members_masks(u);
         unit_realize_cgroup(u);
 
         return 0;
index fdf612061056ec88996002ba5df87531ab0d3593..10f53ef4016c05ea1174898a1b04028979a3b9e6 100644 (file)
@@ -424,7 +424,7 @@ int bus_service_set_property(
 int bus_service_commit_properties(Unit *u) {
         assert(u);
 
-        unit_update_cgroup_members_masks(u);
+        unit_invalidate_cgroup_members_masks(u);
         unit_realize_cgroup(u);
 
         return 0;
index 722a5688a52161eeb13c6cae168d64d56c4860b1..effd5fa5d76b7de0613f657bb42e68be76e226ae 100644 (file)
@@ -28,7 +28,7 @@ int bus_slice_set_property(
 int bus_slice_commit_properties(Unit *u) {
         assert(u);
 
-        unit_update_cgroup_members_masks(u);
+        unit_invalidate_cgroup_members_masks(u);
         unit_realize_cgroup(u);
 
         return 0;
index 4ea5b6c6e599e9f907350d3a287f7ec0e47f59db..3819653908fd850558f9c4cc21d86b5d2a5e0252 100644 (file)
@@ -461,7 +461,7 @@ int bus_socket_set_property(
 int bus_socket_commit_properties(Unit *u) {
         assert(u);
 
-        unit_update_cgroup_members_masks(u);
+        unit_invalidate_cgroup_members_masks(u);
         unit_realize_cgroup(u);
 
         return 0;
index b272d10113cffe2c51389516787548dd629a2450..353fa201321a900d57f6f9afbc9fb463dfeae2b4 100644 (file)
@@ -63,7 +63,7 @@ int bus_swap_set_property(
 int bus_swap_commit_properties(Unit *u) {
         assert(u);
 
-        unit_update_cgroup_members_masks(u);
+        unit_invalidate_cgroup_members_masks(u);
         unit_realize_cgroup(u);
 
         return 0;
index efefdaed9967ad2168a273307b5f2379fb5955d8..ce222e402308d102861a1657d8952accbd156b40 100644 (file)
@@ -3242,7 +3242,7 @@ int config_parse_device_allow(
                 return 0;
         }
 
-        if (!startswith(resolved, "block-") && !startswith(resolved, "char-")) {
+        if (!STARTSWITH_SET(resolved, "block-", "char-")) {
 
                 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
                 if (r < 0)
index 74d056f7bc53ff3d7db54c63379c80f3621b6d48..dc087680e1931dcc74feb701c9b55d7bd9b05e93 100644 (file)
@@ -330,7 +330,7 @@ static void slice_enumerate_perpetual(Manager *m) {
         assert(m);
 
         r = slice_make_perpetual(m, SPECIAL_ROOT_SLICE, &u);
-        if (r >= 0 && manager_owns_root_cgroup(m)) {
+        if (r >= 0 && manager_owns_host_root_cgroup(m)) {
                 Slice *s = SLICE(u);
 
                 /* If we are managing the root cgroup then this means our root slice covers the whole system, which
index 9e6c404ab960e0e4016476975f27e41413714135..bd4e4e7257dfd5a657220a820285dde05cf8dde1 100644 (file)
@@ -29,6 +29,7 @@
 #include "process-util.h"
 #include "signal-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "umount.h"
 #include "util.h"
 #include "virt.h"
index f6c2e08b55a02d3657b16454af017e8fc1276465..df340548bb7acd3cfb1251030547c1f9d743384e 100644 (file)
@@ -570,6 +570,14 @@ void unit_free(Unit *u) {
         if (!u)
                 return;
 
+        if (UNIT_ISSET(u->slice)) {
+                /* A unit is being dropped from the tree, make sure our parent slice recalculates the member mask */
+                unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
+
+                /* And make sure the parent is realized again, updating cgroup memberships */
+                unit_add_to_cgroup_realize_queue(UNIT_DEREF(u->slice));
+        }
+
         u->transient_file = safe_fclose(u->transient_file);
 
         if (!MANAGER_IS_RELOADING(u->manager))
@@ -1155,17 +1163,20 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 (void) cg_mask_to_string(u->cgroup_realized_mask, &s);
                 fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
         }
+
         if (u->cgroup_enabled_mask != 0) {
                 _cleanup_free_ char *s = NULL;
                 (void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
                 fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
         }
+
         m = unit_get_own_mask(u);
         if (m != 0) {
                 _cleanup_free_ char *s = NULL;
                 (void) cg_mask_to_string(m, &s);
                 fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
         }
+
         m = unit_get_members_mask(u);
         if (m != 0) {
                 _cleanup_free_ char *s = NULL;
@@ -1173,6 +1184,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
         }
 
+        m = unit_get_delegate_mask(u);
+        if (m != 0) {
+                _cleanup_free_ char *s = NULL;
+                (void) cg_mask_to_string(m, &s);
+                fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
+        }
+
         SET_FOREACH(t, u->names, i)
                 fprintf(f, "%s\tName: %s\n", prefix, t);
 
@@ -1537,7 +1555,8 @@ int unit_load(Unit *u) {
                 if (u->job_running_timeout != USEC_INFINITY && u->job_running_timeout > u->job_timeout)
                         log_unit_warning(u, "JobRunningTimeoutSec= is greater than JobTimeoutSec=, it has no effect.");
 
-                unit_update_cgroup_members_masks(u);
+                /* We finished loading, let's ensure our parents recalculate the members mask */
+                unit_invalidate_cgroup_members_masks(u);
         }
 
         assert((u->load_state != UNIT_MERGED) == !u->merged_into);
index 15cd02e23a995efb3f2f9c3f185acda0c13abcce..613d7b32e68040c5ece9583055f4d34cca974346 100644 (file)
@@ -247,11 +247,10 @@ typedef struct Unit {
 
         /* Counterparts in the cgroup filesystem */
         char *cgroup_path;
-        CGroupMask cgroup_realized_mask;
-        CGroupMask cgroup_enabled_mask;
-        CGroupMask cgroup_invalidated_mask;
-        CGroupMask cgroup_subtree_mask;
-        CGroupMask cgroup_members_mask;
+        CGroupMask cgroup_realized_mask;           /* In which hierarchies does this unit's cgroup exist? (only relevant on cgroupsv1) */
+        CGroupMask cgroup_enabled_mask;            /* Which controllers are enabled (or more correctly: enabled for the children) for this unit's cgroup? (only relevant on cgroupsv2) */
+        CGroupMask cgroup_invalidated_mask;        /* A mask specifiying controllers which shall be considered invalidated, and require re-realization */
+        CGroupMask cgroup_members_mask;            /* A cache for the controllers required by all children of this cgroup (only relevant for slice units) */
         int cgroup_inotify_wd;
 
         /* Device Controller BPF program */
@@ -330,7 +329,6 @@ typedef struct Unit {
 
         bool cgroup_realized:1;
         bool cgroup_members_mask_valid:1;
-        bool cgroup_subtree_mask_valid:1;
 
         /* Reset cgroup accounting next time we fork something off */
         bool reset_accounting:1;
index 7da13dc2076c28ab8d788209a0436ad3f8a5fadc..b0b1065e2f9d2402bdc08cefdc677057e172d0b6 100644 (file)
@@ -510,11 +510,9 @@ static int add_crypttab_devices(void) {
                         continue;
                 }
 
-                uuid = startswith(device, "UUID=");
+                uuid = STARTSWITH_SET(device, "UUID=", "luks-");
                 if (!uuid)
                         uuid = path_startswith(device, "/dev/disk/by-uuid/");
-                if (!uuid)
-                        uuid = startswith(name, "luks-");
                 if (uuid)
                         d = hashmap_get(arg_disks, uuid);
 
index cc9252377a25551cd02e92853bcfcc36cfdeb345..d5c998498971a88c16fa35a2a3545ad1a6f8d22f 100644 (file)
@@ -36,7 +36,8 @@ int main(int argc, char **argv) {
                 printf("%s... ", name);
                 fflush(stdout);
                 for (int j = 0; j < MIN_NUMBER_OF_RUNS; j++)
-                        (void) LLVMFuzzerTestOneInput((uint8_t*)buf, size);
+                        if (LLVMFuzzerTestOneInput((uint8_t*)buf, size) == EXIT_TEST_SKIP)
+                                return EXIT_TEST_SKIP;
                 printf("ok\n");
         }
 
diff --git a/src/fuzz/fuzz-udev-rules.c b/src/fuzz/fuzz-udev-rules.c
new file mode 100644 (file)
index 0000000..36c39e1
--- /dev/null
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "fuzz.h"
+#include "log.h"
+#include "mkdir.h"
+#include "missing.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "tests.h"
+#include "udev.h"
+
+static const struct fakefs {
+        const char *target;
+        bool ignore_mount_error;
+} fakefss[] = {
+        { "/sys",                    false },
+        { "/dev",                    false },
+        { "/run",                    false },
+        { "/etc",                    false },
+        { UDEVLIBEXECDIR "/rules.d", true },
+};
+
+static int setup_mount_namespace(void) {
+        static thread_local bool is_namespaced = false;
+
+        if (is_namespaced)
+                return 1;
+
+        if (unshare(CLONE_NEWNS) < 0)
+                return log_error_errno(errno, "Failed to call unshare(): %m");
+
+        if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+                return log_error_errno(errno, "Failed to mount / as private: %m");
+
+        is_namespaced = true;
+
+        return 1;
+}
+
+static int setup_fake_filesystems(const char *runtime_dir) {
+        for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++)
+                if (mount(runtime_dir, fakefss[i].target, NULL, MS_BIND, NULL) < 0) {
+                        log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to mount %s: %m", fakefss[i].target);
+                        if (!fakefss[i].ignore_mount_error)
+                                return -errno;
+                }
+
+        return 0;
+}
+
+static int cleanup_fake_filesystems(const char *runtime_dir) {
+        for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++)
+                if (umount(fakefss[i].target) < 0) {
+                        log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to umount %s: %m", fakefss[i].target);
+                        if (!fakefss[i].ignore_mount_error)
+                                return -errno;
+                }
+        return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        _cleanup_(udev_rules_freep) struct udev_rules *rules = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+        FILE *f = NULL;
+
+        if (!getenv("SYSTEMD_LOG_LEVEL")) {
+                log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT);
+                log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT);
+        }
+
+        if (setup_mount_namespace() < 0)
+                return EXIT_TEST_SKIP;
+
+        assert_se(runtime_dir = setup_fake_runtime_dir());
+
+        assert_se(setup_fake_filesystems(runtime_dir) >= 0);
+        assert_se(mkdir_p("/etc/udev/rules.d", 0755) >= 0);
+        f = fopen("/etc/udev/rules.d/fuzz.rules", "we");
+        assert_se(f);
+        if (size != 0)
+                assert_se(fwrite(data, size, 1, f) == 1);
+        assert_se(fclose(f) == 0);
+        rules = udev_rules_new(RESOLVE_NAME_EARLY);
+
+        assert_se(cleanup_fake_filesystems(runtime_dir) >= 0);
+
+        return 0;
+}
index 0af787b3003b3d4f88c4c551e7028b32e39ce89e..f628001a2a791b0b217d7e0813322698f4995425 100644 (file)
@@ -97,6 +97,14 @@ fuzzers += [
           libshared],
          []],
 
+        [['src/fuzz/fuzz-udev-rules.c'],
+         [libudev_core,
+          libudev_static,
+          libsystemd_network,
+          libshared],
+         [threads,
+          libacl]],
+
         [['src/fuzz/fuzz-compress.c'],
          [libshared],
          []],
index 48dc9d19634c23b0ae36b627b4aee43608a307b2..05b17c3c7820b5ab974026e7cf67a7569b96fdcd 100644 (file)
@@ -222,24 +222,31 @@ CurlGlue *curl_glue_unref(CurlGlue *g) {
 
 int curl_glue_new(CurlGlue **glue, sd_event *event) {
         _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
+        _cleanup_(curl_multi_cleanupp) CURL *c = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         int r;
 
-        g = new0(CurlGlue, 1);
-        if (!g)
-                return -ENOMEM;
-
         if (event)
-                g->event = sd_event_ref(event);
+                e = sd_event_ref(event);
         else {
-                r = sd_event_default(&g->event);
+                r = sd_event_default(&e);
                 if (r < 0)
                         return r;
         }
 
-        g->curl = curl_multi_init();
-        if (!g->curl)
+        c = curl_multi_init();
+        if (!c)
+                return -ENOMEM;
+
+        g = new(CurlGlue, 1);
+        if (!g)
                 return -ENOMEM;
 
+        *g = (CurlGlue) {
+                .event = TAKE_PTR(e),
+                .curl = TAKE_PTR(c),
+        };
+
         if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK)
                 return -EINVAL;
 
@@ -258,9 +265,8 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) {
 }
 
 int curl_glue_make(CURL **ret, const char *url, void *userdata) {
+        _cleanup_(curl_easy_cleanupp) CURL *c = NULL;
         const char *useragent;
-        CURL *c;
-        int r;
 
         assert(ret);
         assert(url);
@@ -271,33 +277,21 @@ int curl_glue_make(CURL **ret, const char *url, void *userdata) {
 
         /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */
 
-        if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) {
-                r = -EIO;
-                goto fail;
-        }
+        if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK)
+                return -EIO;
 
-        if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) {
-                r = -EIO;
-                goto fail;
-        }
+        if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK)
+                return -EIO;
 
         useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION);
-        if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) {
-                r = -EIO;
-                goto fail;
-        }
+        if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK)
+                return -EIO;
 
-        if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) {
-                r = -EIO;
-                goto fail;
-        }
+        if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
+                return -EIO;
 
         *ret = c;
         return 0;
-
-fail:
-        curl_easy_cleanup(c);
-        return r;
 }
 
 int curl_glue_add(CurlGlue *g, CURL *c) {
index 6626eefc5688cf8c9172d845089f50bf9cd79250..a03e8440305082511807854ce1ef27ddf34c2e8a 100644 (file)
@@ -35,4 +35,5 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char
 int curl_parse_http_time(const char *t, usec_t *ret);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_easy_cleanup);
+DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_multi_cleanup);
 DEFINE_TRIVIAL_CLEANUP_FUNC(struct curl_slist*, curl_slist_free_all);
index f3a34d40c51b1d718237291c441c166c8165b7e0..04ad129ca138a934c4fe87e3cb46296ee8b435cb 100644 (file)
@@ -86,16 +86,19 @@ int raw_export_new(
 
         assert(ret);
 
-        e = new0(RawExport, 1);
+        e = new(RawExport, 1);
         if (!e)
                 return -ENOMEM;
 
-        e->output_fd = e->input_fd = -1;
-        e->on_finished = on_finished;
-        e->userdata = userdata;
+        *e = (RawExport) {
+                .output_fd = -1,
+                .input_fd = -1,
+                .on_finished = on_finished,
+                .userdata = userdata,
+                .last_percent = (unsigned) -1,
+        };
 
         RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
-        e->last_percent = (unsigned) -1;
 
         if (event)
                 e->event = sd_event_ref(event);
index 9e7c0eaa9d6ddc51c206d8d47d46fd19a3e26c6c..5acb09432a69ea4537546277df108d2021df6a47 100644 (file)
@@ -88,17 +88,20 @@ int tar_export_new(
 
         assert(ret);
 
-        e = new0(TarExport, 1);
+        e = new(TarExport, 1);
         if (!e)
                 return -ENOMEM;
 
-        e->output_fd = e->tar_fd = -1;
-        e->on_finished = on_finished;
-        e->userdata = userdata;
-        e->quota_referenced = (uint64_t) -1;
+        *e = (TarExport) {
+                .output_fd = -1,
+                .tar_fd = -1,
+                .on_finished = on_finished,
+                .userdata = userdata,
+                .quota_referenced = (uint64_t) -1,
+                .last_percent = (unsigned) -1,
+        };
 
         RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
-        e->last_percent = (unsigned) -1;
 
         if (event)
                 e->event = sd_event_ref(event);
index f62247cabdf12b74b04577480ed84da54766a069..e8adaae74096e2565f68698c73fe1e74a87c2a28 100644 (file)
@@ -94,26 +94,33 @@ int raw_import_new(
                 void *userdata) {
 
         _cleanup_(raw_import_unrefp) RawImport *i = NULL;
+        _cleanup_free_ char *root = NULL;
+        bool grow;
         int r;
 
         assert(ret);
 
-        i = new0(RawImport, 1);
-        if (!i)
+        root = strdup(image_root ?: "/var/lib/machines");
+        if (!root)
                 return -ENOMEM;
 
-        i->input_fd = i->output_fd = -1;
-        i->on_finished = on_finished;
-        i->userdata = userdata;
-
-        RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
-        i->last_percent = (unsigned) -1;
+        grow = path_startswith(root, "/var/lib/machines");
 
-        i->image_root = strdup(image_root ?: "/var/lib/machines");
-        if (!i->image_root)
+        i = new(RawImport, 1);
+        if (!i)
                 return -ENOMEM;
 
-        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+        *i = (RawImport) {
+                .input_fd = -1,
+                .output_fd = -1,
+                .on_finished = on_finished,
+                .userdata = userdata,
+                .last_percent = (unsigned) -1,
+                .image_root = TAKE_PTR(root),
+                .grow_machine_directory = grow,
+        };
+
+        RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
 
         if (event)
                 i->event = sd_event_ref(event);
index 89c95af279ef6e2c5b937865b1b064ce44965ee4..0399b03747f8f2fe3dd6b1934724b9efaa13140b 100644 (file)
@@ -101,26 +101,33 @@ int tar_import_new(
                 void *userdata) {
 
         _cleanup_(tar_import_unrefp) TarImport *i = NULL;
+        _cleanup_free_ char *root = NULL;
+        bool grow;
         int r;
 
         assert(ret);
 
-        i = new0(TarImport, 1);
-        if (!i)
+        root = strdup(image_root ?: "/var/lib/machines");
+        if (!root)
                 return -ENOMEM;
 
-        i->input_fd = i->tar_fd = -1;
-        i->on_finished = on_finished;
-        i->userdata = userdata;
-
-        RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
-        i->last_percent = (unsigned) -1;
+        grow = path_startswith(root, "/var/lib/machines");
 
-        i->image_root = strdup(image_root ?: "/var/lib/machines");
-        if (!i->image_root)
+        i = new(TarImport, 1);
+        if (!i)
                 return -ENOMEM;
 
-        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+        *i = (TarImport) {
+                .input_fd = -1,
+                .tar_fd = -1,
+                .on_finished = on_finished,
+                .userdata = userdata,
+                .last_percent = (unsigned) -1,
+                .image_root = TAKE_PTR(root),
+                .grow_machine_directory = grow,
+        };
+
+        RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
 
         if (event)
                 i->event = sd_event_ref(event);
index 5d2a051533eca5ecc8fd93cbe39217f41190c8a1..2c88d353da0a96f1506f87dc1b488f0902225164 100644 (file)
@@ -146,15 +146,17 @@ static int transfer_new(Manager *m, Transfer **ret) {
         if (r < 0)
                 return r;
 
-        t = new0(Transfer, 1);
+        t = new(Transfer, 1);
         if (!t)
                 return -ENOMEM;
 
-        t->type = _TRANSFER_TYPE_INVALID;
-        t->log_fd = -1;
-        t->stdin_fd = -1;
-        t->stdout_fd = -1;
-        t->verify = _IMPORT_VERIFY_INVALID;
+        *t = (Transfer) {
+                .type = _TRANSFER_TYPE_INVALID,
+                .log_fd = -1,
+                .stdin_fd = -1,
+                .stdout_fd = -1,
+                .verify = _IMPORT_VERIFY_INVALID,
+        };
 
         id = m->current_transfer_id + 1;
 
index 3d3a8713d72853dc8b50b3ba900041a4a0a2c70c..0b7d9df32dc6fa922ce3c62256a42c617e4be151 100644 (file)
@@ -537,28 +537,33 @@ static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_o
 
 int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) {
         _cleanup_(pull_job_unrefp) PullJob *j = NULL;
+        _cleanup_free_ char *u = NULL;
 
         assert(url);
         assert(glue);
         assert(ret);
 
-        j = new0(PullJob, 1);
-        if (!j)
+        u = strdup(url);
+        if (u)
                 return -ENOMEM;
 
-        j->state = PULL_JOB_INIT;
-        j->disk_fd = -1;
-        j->userdata = userdata;
-        j->glue = glue;
-        j->content_length = (uint64_t) -1;
-        j->start_usec = now(CLOCK_MONOTONIC);
-        j->compressed_max = j->uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU; /* 64GB safety limit */
-        j->style = VERIFICATION_STYLE_UNSET;
-
-        j->url = strdup(url);
-        if (!j->url)
+        j = new(PullJob, 1);
+        if (!j)
                 return -ENOMEM;
 
+        *j = (PullJob) {
+                .state = PULL_JOB_INIT,
+                .disk_fd = -1,
+                .userdata = userdata,
+                .glue = glue,
+                .content_length = (uint64_t) -1,
+                .start_usec = now(CLOCK_MONOTONIC),
+                .compressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
+                .uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
+                .style = VERIFICATION_STYLE_UNSET,
+                .url = TAKE_PTR(u),
+        };
+
         *ret = TAKE_PTR(j);
 
         return 0;
index 96800ac8a6d9f5522ccbf68cd34a193baaf5c302..2ceca68c0785c5842ddaa258aba629e86515a588 100644 (file)
@@ -115,36 +115,46 @@ int raw_pull_new(
                 RawPullFinished on_finished,
                 void *userdata) {
 
+        _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
+        _cleanup_free_ char *root = NULL;
+        bool grow;
         int r;
 
         assert(ret);
 
-        i = new0(RawPull, 1);
-        if (!i)
-                return -ENOMEM;
-
-        i->on_finished = on_finished;
-        i->userdata = userdata;
-
-        i->image_root = strdup(image_root ?: "/var/lib/machines");
-        if (!i->image_root)
+        root = strdup(image_root ?: "/var/lib/machines");
+        if (!root)
                 return -ENOMEM;
 
-        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+        grow = path_startswith(root, "/var/lib/machines");
 
         if (event)
-                i->event = sd_event_ref(event);
+                e = sd_event_ref(event);
         else {
-                r = sd_event_default(&i->event);
+                r = sd_event_default(&e);
                 if (r < 0)
                         return r;
         }
 
-        r = curl_glue_new(&i->glue, i->event);
+        r = curl_glue_new(&g, e);
         if (r < 0)
                 return r;
 
+        i = new(RawPull, 1);
+        if (!i)
+                return -ENOMEM;
+
+        *i = (RawPull) {
+                .on_finished = on_finished,
+                .userdata = userdata,
+                .image_root = TAKE_PTR(root),
+                .grow_machine_directory = grow,
+                .event = TAKE_PTR(e),
+                .glue = TAKE_PTR(g),
+        };
+
         i->glue->on_finished = pull_job_curl_on_finished;
         i->glue->userdata = i;
 
index 56ec252e98e7b618fa956b63f79c760fd7c2bad9..67ea8813e1736e28b8646d69ccef83a1065372c6 100644 (file)
@@ -108,36 +108,46 @@ int tar_pull_new(
                 TarPullFinished on_finished,
                 void *userdata) {
 
+        _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
+        _cleanup_free_ char *root = NULL;
+        bool grow;
         int r;
 
         assert(ret);
 
-        i = new0(TarPull, 1);
-        if (!i)
-                return -ENOMEM;
-
-        i->on_finished = on_finished;
-        i->userdata = userdata;
-
-        i->image_root = strdup(image_root ?: "/var/lib/machines");
-        if (!i->image_root)
+        root = strdup(image_root ?: "/var/lib/machines");
+        if (!root)
                 return -ENOMEM;
 
-        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
+        grow = path_startswith(root, "/var/lib/machines");
 
         if (event)
-                i->event = sd_event_ref(event);
+                e = sd_event_ref(event);
         else {
-                r = sd_event_default(&i->event);
+                r = sd_event_default(&e);
                 if (r < 0)
                         return r;
         }
 
-        r = curl_glue_new(&i->glue, i->event);
+        r = curl_glue_new(&g, e);
         if (r < 0)
                 return r;
 
+        i = new(TarPull, 1);
+        if (!i)
+                return -ENOMEM;
+
+        *i = (TarPull) {
+                .on_finished = on_finished,
+                .userdata = userdata,
+                .image_root = TAKE_PTR(root),
+                .grow_machine_directory = grow,
+                .event = TAKE_PTR(e),
+                .glue = TAKE_PTR(g),
+        };
+
         i->glue->on_finished = pull_job_curl_on_finished;
         i->glue->userdata = i;
 
index 44f3450d87d8fd24934838ad532d551233fdc5a1..c46e0acdd369354d4a20ec64484b1d4e6d2e2614 100644 (file)
@@ -614,15 +614,14 @@ static int create_remoteserver(
 
         if (arg_url) {
                 const char *url;
-                char *hostname, *p;
+                char *hostname;
 
                 if (!strstr(arg_url, "/entries")) {
                         if (endswith(arg_url, "/"))
                                 url = strjoina(arg_url, "entries");
                         else
                                 url = strjoina(arg_url, "/entries");
-                }
-                else
+                } else
                         url = strdupa(arg_url);
 
                 log_info("Spawning curl %s...", url);
@@ -630,16 +629,11 @@ static int create_remoteserver(
                 if (fd < 0)
                         return fd;
 
-                hostname =
-                        startswith(arg_url, "https://") ?:
-                        startswith(arg_url, "http://") ?:
-                        arg_url;
+                hostname = STARTSWITH_SET(arg_url, "https://", "http://");
+                if (!hostname)
+                        hostname = arg_url;
 
-                hostname = strdupa(hostname);
-                if ((p = strchr(hostname, '/')))
-                        *p = '\0';
-                if ((p = strchr(hostname, ':')))
-                        *p = '\0';
+                hostname = strndupa(hostname, strcspn(hostname, "/:"));
 
                 r = journal_remote_add_source(s, fd, hostname, false);
                 if (r < 0)
index f7b91fddde346027004d41ca488a90c240cf8a20..924ea4936e6be35247d9f43268ec28b9e5d5ec00 100644 (file)
@@ -25,6 +25,7 @@
 #include "sigbus.h"
 #include "signal-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "util.h"
 
 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
@@ -405,10 +406,12 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file)
         assert(u);
         assert(url);
 
-        memzero(u, sizeof(Uploader));
-        u->input = -1;
+        *u = (Uploader) {
+                .input = -1
+        };
 
-        if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) {
+        host = STARTSWITH_SET(url, "http://", "https://");
+        if (!host) {
                 host = url;
                 proto = "https://";
         }
index 7dd9ca62d51af5de08727b45800583bcf8a1c807..5ea4601ba35fdf29f78283608435630bae9e7b98 100644 (file)
@@ -25,6 +25,7 @@
 #include "selinux-util.h"
 #include "socket-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "unaligned.h"
 
 static bool allow_object_pid(const struct ucred *ucred) {
@@ -337,11 +338,7 @@ void server_process_native_file(
                         return;
                 }
 
-                e = path_startswith(k, "/dev/shm/");
-                if (!e)
-                        e = path_startswith(k, "/tmp/");
-                if (!e)
-                        e = path_startswith(k, "/var/tmp/");
+                e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/");
                 if (!e) {
                         log_error("Received file outside of allowed directories. Refusing.");
                         return;
index a81e0e94c73ed6bc8374add8f18d59aa02411e84..952f67a0d90a6e97838ac9408ccdafe8d117600b 100644 (file)
@@ -1183,8 +1183,7 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) {
         tilded = strjoina(full, "~");
         atted = strjoina(prefix, "@");
 
-        return streq(filename, full) ||
-               streq(filename, tilded) ||
+        return STR_IN_SET(filename, full, tilded) ||
                startswith(filename, atted);
 }
 
index 05e7ec07159bf83563710eafd77ba23384c5e23d..8f2eec1bda7c8cc85bd9c660808c61fc9f571b48 100644 (file)
@@ -210,9 +210,11 @@ _public_ int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_han
                         return r;
         }
 
-        r = device_monitor_enable_receiving(m);
-        if (r < 0)
-                return r;
+        if (!m->bound) {
+                r = device_monitor_enable_receiving(m);
+                if (r < 0)
+                        return r;
+        }
 
         m->callback = callback;
         m->userdata = userdata;
@@ -246,7 +248,7 @@ _public_ int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *even
         else {
                 r = sd_event_default(&m->event);
                 if (r < 0)
-                        return 0;
+                        return r;
         }
 
         return 0;
@@ -269,15 +271,13 @@ int device_monitor_enable_receiving(sd_device_monitor *m) {
 
         assert_return(m, -EINVAL);
 
-        if (!m->filter_uptodate) {
-                r = sd_device_monitor_filter_update(m);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
-        }
+        r = sd_device_monitor_filter_update(m);
+        if (r < 0)
+                return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
 
         if (!m->bound) {
                 if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0)
-                        return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket to event source: %m");
+                        return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket: %m");
 
                 m->bound = true;
         }
@@ -595,6 +595,9 @@ _public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
 
         assert_return(m, -EINVAL);
 
+        if (m->filter_uptodate)
+                return 0;
+
         if (hashmap_isempty(m->subsystem_filter) &&
             set_isempty(m->tag_filter)) {
                 m->filter_uptodate = true;
index 2bab2fd2f5fe32e88e93b2cf1238aa42d4ef709f..60831bb1c327b463e79cb7f1b70fbbf58e08d3aa 100644 (file)
@@ -606,9 +606,8 @@ static int manager_count_external_displays(Manager *m) {
                 return r;
 
         FOREACH_DEVICE(e, d) {
+                const char *status, *enabled, *dash, *nn, *subsys;
                 sd_device *p;
-                const char *status, *enabled, *dash, *nn, *i, *subsys;
-                bool external = false;
 
                 if (sd_device_get_parent(d, &p) < 0)
                         continue;
@@ -631,16 +630,10 @@ static int manager_count_external_displays(Manager *m) {
                         continue;
 
                 dash++;
-                FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
-                               "Composite-", "SVIDEO-", "Component-",
-                               "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
-
-                        if (startswith(dash, i)) {
-                                external = true;
-                                break;
-                        }
-                }
-                if (!external)
+                if (!STARTSWITH_SET(dash,
+                                    "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
+                                    "Composite-", "SVIDEO-", "Component-",
+                                    "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-"))
                         continue;
 
                 /* Ignore ports that are not enabled */
index 0197474dbc6f2ceb242808142b8f254130f74d92..53c42f0ee478e08a955ee53172dd41c376193c13 100644 (file)
@@ -189,7 +189,7 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
         }
 
         /* Try to enable as many controllers as possible for the new payload. */
-        (void) cg_enable_everywhere(supported, supported, cgroup);
+        (void) cg_enable_everywhere(supported, supported, cgroup, NULL);
         return 0;
 }
 
index 47fe7d4865db87299956aa1ed71fbd310aff22f6..c581514b297ddbb108b182393e587eab74e73819 100644 (file)
@@ -1427,17 +1427,10 @@ static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t u
 }
 
 static const char *timezone_from_path(const char *path) {
-        const char *z;
-
-        z = path_startswith(path, "../usr/share/zoneinfo/");
-        if (z)
-                return z;
-
-        z = path_startswith(path, "/usr/share/zoneinfo/");
-        if (z)
-                return z;
-
-        return NULL;
+        return PATH_STARTSWITH_SET(
+                        path,
+                        "../usr/share/zoneinfo/",
+                        "/usr/share/zoneinfo/");
 }
 
 static int setup_timezone(const char *dest) {
index 0475156a43b8f21fed740bf20cccbd5b0085949e..e9e31282b6fc2e8f591f9a3caa27f60e8831c8aa 100644 (file)
@@ -384,12 +384,13 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_oom();
 
                         with_timer = with_timer ||
-                                !!startswith(optarg, "OnActiveSec=") ||
-                                !!startswith(optarg, "OnBootSec=") ||
-                                !!startswith(optarg, "OnStartupSec=") ||
-                                !!startswith(optarg, "OnUnitActiveSec=") ||
-                                !!startswith(optarg, "OnUnitInactiveSec=") ||
-                                !!startswith(optarg, "OnCalendar=");
+                                STARTSWITH_SET(optarg,
+                                               "OnActiveSec=",
+                                               "OnBootSec=",
+                                               "OnStartupSec=",
+                                               "OnUnitActiveSec=",
+                                               "OnUnitInactiveSec=",
+                                               "OnCalendar=");
                         break;
 
                 case ARG_PATH_PROPERTY:
index 1eaf653103e952e86f81de3ddd48eb9619112165..6f0657174cbe264f1e84bf3da226364a9057a4f1 100644 (file)
@@ -219,14 +219,11 @@ int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want
         STRV_FOREACH(entry, split) {
                 char *p;
 
-                p = startswith(*entry, "default:");
+                p = STARTSWITH_SET(*entry, "default:", "d:");
                 if (!p)
-                        p = startswith(*entry, "d:");
+                        p = *entry;
 
-                if (p)
-                        r = strv_push(&d, p);
-                else
-                        r = strv_push(&a, *entry);
+                r = strv_push(&d, p);
                 if (r < 0)
                         return r;
         }
index e4a15f8b115c1c9f536d211960515744d50e508e..cf5f3d6bcbe2b4ffea6ae9c432518e5012451590 100644 (file)
@@ -105,6 +105,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
                 "BindsTo=%3$s\n"
                 "After=initrd-root-device.target local-fs-pre.target %3$s\n"
                 "Before=shutdown.target\n"
+                "Conflicts=shutdown.target\n"
                 "\n"
                 "[Service]\n"
                 "Type=oneshot\n"
index 82221af19493027b9d6a3e544331b034f904e3e9..edf650d200454b49654778b4c7856760646552f0 100644 (file)
@@ -3,6 +3,7 @@
 #include <stdbool.h>
 
 #include "string-util.h"
+#include "strv.h"
 #include "utf8.h"
 #include "web-util.h"
 
@@ -13,7 +14,7 @@ bool http_etag_is_valid(const char *etag) {
         if (!endswith(etag, "\""))
                 return false;
 
-        if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
+        if (!STARTSWITH_SET(etag, "\"", "W/\""))
                 return false;
 
         return true;
@@ -25,9 +26,7 @@ bool http_url_is_valid(const char *url) {
         if (isempty(url))
                 return false;
 
-        p = startswith(url, "http://");
-        if (!p)
-                p = startswith(url, "https://");
+        p = STARTSWITH_SET(url, "http://", "https://");
         if (!p)
                 return false;
 
@@ -46,12 +45,7 @@ bool documentation_url_is_valid(const char *url) {
         if (http_url_is_valid(url))
                 return true;
 
-        p = startswith(url, "file:/");
-        if (!p)
-                p = startswith(url, "info:");
-        if (!p)
-                p = startswith(url, "man:");
-
+        p = STARTSWITH_SET(url, "file:/", "info:", "man:");
         if (isempty(p))
                 return false;
 
index fd5f5987017a33c75389a9587bdfd633d9675c0a..b5030ea494a081f6c005f702b91b16080fc85421 100644 (file)
@@ -506,6 +506,27 @@ static void test_empty_or_root(void) {
         assert_se(!empty_or_root("//yy//"));
 }
 
+static void test_path_startswith_set(void) {
+
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/bar", "/zzz"), ""));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/", "/zzz"), "bar"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo", "/zzz"), "bar"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/", "/zzz"), "foo/bar"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "", "/zzz"), NULL));
+
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/bar", "/zzz"), NULL));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/", "/zzz"), "bar2"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo", "/zzz"), "bar2"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/", "/zzz"), "foo/bar2"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "", "/zzz"), NULL));
+
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/bar", "/zzz"), NULL));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/", "/zzz"), NULL));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo", "/zzz"), NULL));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/", "/zzz"), "foo2/bar"));
+        assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL));
+}
+
 int main(int argc, char **argv) {
         test_setup_logging(LOG_DEBUG);
 
@@ -525,6 +546,7 @@ int main(int argc, char **argv) {
         test_hidden_or_backup_file();
         test_skip_dev_prefix();
         test_empty_or_root();
+        test_path_startswith_set();
 
         test_systemd_installation_has_version(argv[1]); /* NULL is OK */
 
index 63757afdce479ec38aa6da83def82692a546432c..31ef1abb4441c7f88c8054d44e957c0fa1f42e6e 100644 (file)
@@ -56,6 +56,20 @@ static void test_strptr_in_set(void) {
         assert_se(!STRPTR_IN_SET(NULL, NULL));
 }
 
+static void test_startswith_set(void) {
+        assert_se(!STARTSWITH_SET("foo", "bar", "baz", "waldo"));
+        assert_se(!STARTSWITH_SET("foo", "bar"));
+
+        assert_se(STARTSWITH_SET("abc", "a", "ab", "abc"));
+        assert_se(STARTSWITH_SET("abc", "ax", "ab", "abc"));
+        assert_se(STARTSWITH_SET("abc", "ax", "abx", "abc"));
+        assert_se(!STARTSWITH_SET("abc", "ax", "abx", "abcx"));
+
+        assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar"));
+        assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar"));
+        assert_se(streq_ptr(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), ""));
+}
+
 static const char* const input_table_multiple[] = {
         "one",
         "two",
@@ -847,6 +861,7 @@ int main(int argc, char *argv[]) {
         test_specifier_printf();
         test_str_in_set();
         test_strptr_in_set();
+        test_startswith_set();
         test_strv_foreach();
         test_strv_foreach_backwards();
         test_strv_foreach_pair();
index 177b273e48694d0049256b7add6bf50eadedfcea..bc0deaf347c111165cc85d5ec3607b90badbd008 100644 (file)
@@ -574,7 +574,7 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
 
 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
  * mode and are writable if the driver supports multiple link modes. If it is
- * enabled then they are read-only. If the link  is up they represent the negotiated
+ * enabled then they are read-only. If the link is up they represent the negotiated
  * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
  * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
  */
index becaffe0d6cb2911749f8579cb1700d332c2baa8..6703a9d7a3867e40e69fa56f478b17220e492ab0 100644 (file)
@@ -370,7 +370,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
         if (r < 0) {
 
                 if (config->port != _NET_DEV_PORT_INVALID)
-                        log_warning_errno(r,  "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name);
+                        log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name);
 
                 if (!eqzero(config->advertise))
                         log_warning_errno(r, "Could not set advertise mode: %m"); /* TODO: include modes in the log message. */
index 2df3d92acf60852ab7c98b360b79d96711619071..cb49a17c33d2057f9888a5d669b88c29e8a5ec2f 100644 (file)
@@ -193,7 +193,7 @@ static int builtin_keyboard(sd_device *dev, int argc, char *argv[], bool test) {
                                 keycode++;
 
                                 release[release_count] = scancode;
-                                if (release_count <  ELEMENTSOF(release)-1)
+                                if (release_count < ELEMENTSOF(release)-1)
                                         release_count++;
 
                                 if (keycode[0] == '\0')
index 2dd0f2e39df47e59ba74bead7a548b4f5c2e8718..6ea74c7bf70e66bc9d229185d5739433718f1313 100644 (file)
@@ -76,14 +76,14 @@ struct udev_ctrl *udev_ctrl_new_from_fd(int fd) {
         int r;
 
         uctrl = new0(struct udev_ctrl, 1);
-        if (uctrl == NULL)
+        if (!uctrl)
                 return NULL;
         uctrl->n_ref = 1;
 
         if (fd < 0) {
                 uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
                 if (uctrl->sock < 0) {
-                        log_error_errno(errno, "error getting socket: %m");
+                        log_error_errno(errno, "Failed to create socket: %m");
                         udev_ctrl_unref(uctrl);
                         return NULL;
                 }
@@ -98,7 +98,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(int fd) {
          */
         r = setsockopt_int(uctrl->sock, SOL_SOCKET, SO_PASSCRED, true);
         if (r < 0)
-                log_warning_errno(r, "could not set SO_PASSCRED: %m");
+                log_warning_errno(r, "Failed to set SO_PASSCRED: %m");
 
         uctrl->saddr.un = (struct sockaddr_un) {
                 .sun_family = AF_UNIX,
@@ -124,11 +124,11 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
                 }
 
                 if (err < 0)
-                        return log_error_errno(errno, "bind failed: %m");
+                        return log_error_errno(errno, "Failed to bind socket: %m");
 
                 err = listen(uctrl->sock, 0);
                 if (err < 0)
-                        return log_error_errno(errno, "listen failed: %m");
+                        return log_error_errno(errno, "Failed to listen: %m");
 
                 uctrl->bound = true;
                 uctrl->cleanup_socket = true;
@@ -147,7 +147,7 @@ DEFINE_PRIVATE_TRIVIAL_REF_FUNC(struct udev_ctrl, udev_ctrl);
 DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free);
 
 int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
-        if (uctrl == NULL)
+        if (!uctrl)
                 return 0;
         if (uctrl->cleanup_socket)
                 sockaddr_un_unlink(&uctrl->saddr.un);
@@ -155,7 +155,7 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
 }
 
 int udev_ctrl_get_fd(struct udev_ctrl *uctrl) {
-        if (uctrl == NULL)
+        if (!uctrl)
                 return -EINVAL;
         return uctrl->sock;
 }
@@ -166,7 +166,7 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
         int r;
 
         conn = new(struct udev_ctrl_connection, 1);
-        if (conn == NULL)
+        if (!conn)
                 return NULL;
         conn->n_ref = 1;
         conn->uctrl = uctrl;
@@ -174,25 +174,25 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) {
         conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
         if (conn->sock < 0) {
                 if (errno != EINTR)
-                        log_error_errno(errno, "unable to receive ctrl connection: %m");
+                        log_error_errno(errno, "Failed to receive ctrl connection: %m");
                 goto err;
         }
 
         /* check peer credential of connection */
         r = getpeercred(conn->sock, &ucred);
         if (r < 0) {
-                log_error_errno(r, "unable to receive credentials of ctrl connection: %m");
+                log_error_errno(r, "Failed to receive credentials of ctrl connection: %m");
                 goto err;
         }
         if (ucred.uid > 0) {
-                log_error("sender uid="UID_FMT", message ignored", ucred.uid);
+                log_error("Sender uid="UID_FMT", message ignored", ucred.uid);
                 goto err;
         }
 
         /* enable receiving of the sender credentials in the messages */
         r = setsockopt_int(conn->sock, SOL_SOCKET, SO_PASSCRED, true);
         if (r < 0)
-                log_warning_errno(r, "could not set SO_PASSCRED: %m");
+                log_warning_errno(r, "Failed to set SO_PASSCRED: %m");
 
         udev_ctrl_ref(uctrl);
         return conn;
@@ -220,7 +220,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
         ctrl_msg_wire.magic = UDEV_CTRL_MAGIC;
         ctrl_msg_wire.type = type;
 
-        if (buf != NULL)
+        if (buf)
                 strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf);
         else
                 ctrl_msg_wire.intval = intval;
@@ -245,7 +245,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
                 pfd[0].fd = uctrl->sock;
                 pfd[0].events = POLLIN;
                 r = poll(pfd, 1, timeout * MSEC_PER_SEC);
-                if (r  < 0) {
+                if (r < 0) {
                         if (errno == EINTR)
                                 continue;
                         err = -errno;
@@ -312,7 +312,7 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
         struct ucred *cred;
 
         uctrl_msg = new0(struct udev_ctrl_msg, 1);
-        if (uctrl_msg == NULL)
+        if (!uctrl_msg)
                 return NULL;
         uctrl_msg->n_ref = 1;
         uctrl_msg->conn = conn;
@@ -327,16 +327,16 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
                 pfd[0].events = POLLIN;
 
                 r = poll(pfd, 1, 10000);
-                if (r  < 0) {
+                if (r < 0) {
                         if (errno == EINTR)
                                 continue;
                         goto err;
                 } else if (r == 0) {
-                        log_error("timeout waiting for ctrl message");
+                        log_error("Timeout waiting for ctrl message");
                         goto err;
                 } else {
                         if (!(pfd[0].revents & POLLIN)) {
-                                log_error_errno(errno, "ctrl connection error: %m");
+                                log_error("Invalid ctrl connection: %m");
                                 goto err;
                         }
                 }
@@ -348,8 +348,8 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
         iov.iov_len = sizeof(struct udev_ctrl_msg_wire);
 
         size = recvmsg(conn->sock, &smsg, 0);
-        if (size <  0) {
-                log_error_errno(errno, "unable to receive ctrl message: %m");
+        if (size < 0) {
+                log_error_errno(errno, "Failed to receive ctrl message: %m");
                 goto err;
         }
 
@@ -357,20 +357,20 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) {
 
         cmsg = CMSG_FIRSTHDR(&smsg);
 
-        if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
-                log_error("no sender credentials received, message ignored");
+        if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
+                log_error("No sender credentials received, ignoring message");
                 goto err;
         }
 
         cred = (struct ucred *) CMSG_DATA(cmsg);
 
         if (cred->uid != 0) {
-                log_error("sender uid="UID_FMT", message ignored", cred->uid);
+                log_error("Sender uid="UID_FMT", ignoring message", cred->uid);
                 goto err;
         }
 
         if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) {
-                log_error("message magic 0x%08x doesn't match, ignore it", uctrl_msg->ctrl_msg_wire.magic);
+                log_error("Message magic 0x%08x doesn't match, ignoring", uctrl_msg->ctrl_msg_wire.magic);
                 goto err;
         }
 
index bdaece5daa1b639c11b6985158062a8eb1e227c8..f6f640a21614384abdb8da095d6f21086aff3562 100644 (file)
@@ -25,6 +25,7 @@
 #include "glob-util.h"
 #include "libudev-util.h"
 #include "mkdir.h"
+#include "parse-util.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
 #include "stat-util.h"
@@ -202,7 +203,7 @@ struct token {
                         union {
                                 unsigned attr_off;
                                 unsigned rule_goto;
-                                mode_t  mode;
+                                mode_t mode;
                                 uid_t uid;
                                 gid_t gid;
                                 int devlink_prio;
@@ -233,7 +234,7 @@ static const char *operation_str(enum operation_type type) {
                 [OP_REMOVE] =           "remove",
                 [OP_ASSIGN] =           "assign",
                 [OP_ASSIGN_FINAL] =     "assign-final",
-}        ;
+        };
 
         return operation_strs[type];
 }
@@ -466,11 +467,11 @@ static int add_token(struct udev_rules *rules, struct token *token) {
         return 0;
 }
 
-static void log_unknown_owner(int error, const char *entity, const char *owner) {
+static void log_unknown_owner(sd_device *dev, int error, const char *entity, const char *owner) {
         if (IN_SET(abs(error), ENOENT, ESRCH))
-                log_error("Specified %s '%s' unknown", entity, owner);
+                log_device_error(dev, "Specified %s '%s' unknown", entity, owner);
         else
-                log_error_errno(error, "Failed to resolve %s '%s': %m", entity, owner);
+                log_device_error_errno(dev, error, "Failed to resolve %s '%s': %m", entity, owner);
 }
 
 static uid_t add_uid(struct udev_rules *rules, const char *owner) {
@@ -489,7 +490,7 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) {
         }
         r = get_user_creds(&owner, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
         if (r < 0)
-                log_unknown_owner(r, "user", owner);
+                log_unknown_owner(NULL, r, "user", owner);
 
         /* grow buffer if needed */
         if (rules->uids_cur+1 >= rules->uids_max) {
@@ -532,7 +533,7 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) {
         }
         r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
         if (r < 0)
-                log_unknown_owner(r, "group", group);
+                log_unknown_owner(NULL, r, "group", group);
 
         /* grow buffer if needed */
         if (rules->gids_cur+1 >= rules->gids_max) {
@@ -1957,10 +1958,10 @@ int udev_rules_apply_to_event(
 
                         event->program_result = mfree(event->program_result);
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
-                        log_debug("PROGRAM '%s' %s:%u",
-                                  program,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "PROGRAM '%s' %s:%u",
+                                         program,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
 
                         if (udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result)) < 0) {
                                 if (cur->key.op != OP_NOMATCH)
@@ -1972,7 +1973,7 @@ int udev_rules_apply_to_event(
                                 if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
                                         count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
                                         if (count > 0)
-                                                log_debug("Replaced %i character(s)" , count);
+                                                log_device_debug(dev, "Replaced %i character(s) from result of '%s'" , count, program);
                                 }
                                 event->program_result = strdup(result);
                                 if (cur->key.op == OP_NOMATCH)
@@ -1993,10 +1994,10 @@ int udev_rules_apply_to_event(
                         char import[UTIL_PATH_SIZE];
 
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
-                        log_debug("IMPORT '%s' %s:%u",
-                                  import,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "IMPORT '%s' %s:%u",
+                                         import,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
 
                         if (import_program_into_properties(event, timeout_usec, import) != 0)
                                 if (cur->key.op != OP_NOMATCH)
@@ -2009,10 +2010,10 @@ int udev_rules_apply_to_event(
                         if (udev_builtin_run_once(cur->key.builtin_cmd)) {
                                 /* check if we ran already */
                                 if (event->builtin_run & (1 << cur->key.builtin_cmd)) {
-                                        log_debug("IMPORT builtin skip '%s' %s:%u",
-                                                  udev_builtin_name(cur->key.builtin_cmd),
-                                                  rules_str(rules, rule->rule.filename_off),
-                                                  rule->rule.filename_line);
+                                        log_device_debug(dev, "IMPORT builtin skip '%s' %s:%u",
+                                                         udev_builtin_name(cur->key.builtin_cmd),
+                                                         rules_str(rules, rule->rule.filename_off),
+                                                         rule->rule.filename_line);
                                         /* return the result from earlier run */
                                         if (event->builtin_ret & (1 << cur->key.builtin_cmd))
                                                 if (cur->key.op != OP_NOMATCH)
@@ -2024,16 +2025,16 @@ int udev_rules_apply_to_event(
                         }
 
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
-                        log_debug("IMPORT builtin '%s' %s:%u",
-                                  udev_builtin_name(cur->key.builtin_cmd),
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "IMPORT builtin '%s' %s:%u",
+                                         udev_builtin_name(cur->key.builtin_cmd),
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
 
                         r = udev_builtin_run(dev, cur->key.builtin_cmd, command, false);
                         if (r < 0) {
                                 /* remember failure */
-                                log_debug_errno(r, "IMPORT builtin '%s' fails: %m",
-                                                udev_builtin_name(cur->key.builtin_cmd));
+                                log_device_debug_errno(dev, r, "IMPORT builtin '%s' fails: %m",
+                                                       udev_builtin_name(cur->key.builtin_cmd));
                                 event->builtin_ret |= (1 << cur->key.builtin_cmd);
                                 if (cur->key.op != OP_NOMATCH)
                                         goto nomatch;
@@ -2059,7 +2060,7 @@ int udev_rules_apply_to_event(
                         key = rules_str(rules, cur->key.value_off);
                         r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &value);
                         if (r < 0)
-                                log_debug_errno(r, "Failed to read %s from /proc/cmdline, ignoring: %m", key);
+                                log_device_debug_errno(dev, r, "Failed to read %s from /proc/cmdline, ignoring: %m", key);
                         else if (r > 0) {
                                 imported = true;
 
@@ -2118,13 +2119,13 @@ int udev_rules_apply_to_event(
                         event->owner_set = true;
                         r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
                         if (r < 0) {
-                                log_unknown_owner(r, "user", owner);
+                                log_unknown_owner(dev, r, "user", owner);
                                 event->uid = 0;
                         }
-                        log_debug("OWNER %u %s:%u",
-                                  event->uid,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "OWNER %u %s:%u",
+                                         event->uid,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 }
                 case TK_A_GROUP: {
@@ -2139,35 +2140,35 @@ int udev_rules_apply_to_event(
                         event->group_set = true;
                         r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
                         if (r < 0) {
-                                log_unknown_owner(r, "group", group);
+                                log_unknown_owner(dev, r, "group", group);
                                 event->gid = 0;
                         }
-                        log_debug("GROUP %u %s:%u",
-                                  event->gid,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "GROUP %u %s:%u",
+                                         event->gid,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 }
                 case TK_A_MODE: {
-                        char mode_str[UTIL_NAME_SIZE], *endptr;
+                        char mode_str[UTIL_NAME_SIZE];
                         mode_t mode;
 
                         if (event->mode_final)
                                 break;
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
-                        mode = strtol(mode_str, &endptr, 8);
-                        if (endptr[0] != '\0') {
-                                log_error("ignoring invalid mode '%s'", mode_str);
+                        r = parse_mode(mode_str, &mode);
+                        if (r < 0) {
+                                log_device_error_errno(dev, r, "Failed to parse mode '%s': %m", mode_str);
                                 break;
                         }
                         if (cur->key.op == OP_ASSIGN_FINAL)
                                 event->mode_final = true;
                         event->mode_set = true;
                         event->mode = mode;
-                        log_debug("MODE %#o %s:%u",
-                                  event->mode,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "MODE %#o %s:%u",
+                                         event->mode,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 }
                 case TK_A_OWNER_ID:
@@ -2177,10 +2178,10 @@ int udev_rules_apply_to_event(
                                 event->owner_final = true;
                         event->owner_set = true;
                         event->uid = cur->key.uid;
-                        log_debug("OWNER %u %s:%u",
-                                  event->uid,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "OWNER %u %s:%u",
+                                         event->uid,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 case TK_A_GROUP_ID:
                         if (event->group_final)
@@ -2189,10 +2190,10 @@ int udev_rules_apply_to_event(
                                 event->group_final = true;
                         event->group_set = true;
                         event->gid = cur->key.gid;
-                        log_debug("GROUP %u %s:%u",
-                                  event->gid,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "GROUP %u %s:%u",
+                                         event->gid,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 case TK_A_MODE_ID:
                         if (event->mode_final)
@@ -2201,10 +2202,10 @@ int udev_rules_apply_to_event(
                                 event->mode_final = true;
                         event->mode_set = true;
                         event->mode = cur->key.mode;
-                        log_debug("MODE %#o %s:%u",
-                                  event->mode,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "MODE %#o %s:%u",
+                                         event->mode,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 case TK_A_SECLABEL: {
                         _cleanup_free_ char *name = NULL, *label = NULL;
@@ -2235,10 +2236,10 @@ int udev_rules_apply_to_event(
 
                         name = label = NULL;
 
-                        log_debug("SECLABEL{%s}='%s' %s:%u",
-                                  name, label,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "SECLABEL{%s}='%s' %s:%u",
+                                         name, label,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 }
                 case TK_A_ENV: {
@@ -2280,7 +2281,7 @@ int udev_rules_apply_to_event(
                                     (*p >= '0' && *p <= '9') ||
                                     IN_SET(*p, '-', '_'))
                                         continue;
-                                log_error("Ignoring invalid tag name '%s'", tag);
+                                log_device_error(dev, "Ignoring invalid tag name '%s'", tag);
                                 break;
                         }
                         if (cur->key.op == OP_REMOVE)
@@ -2303,24 +2304,24 @@ int udev_rules_apply_to_event(
                         if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
                                 count = util_replace_chars(name_str, "/");
                                 if (count > 0)
-                                        log_debug("Replaced %i character(s)", count);
+                                        log_device_debug(dev, "Replaced %i character(s) from result of NAME=\"%s\"", count, name);
                         }
                         if (sd_device_get_devnum(dev, NULL) >= 0 &&
                             (sd_device_get_devname(dev, &val) < 0 ||
                              !streq(name_str, val + STRLEN("/dev/")))) {
-                                log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n",
-                                          name,
-                                          rules_str(rules, rule->rule.filename_off),
-                                          rule->rule.filename_line);
+                                log_device_error(dev, "Kernel device nodes cannot be renamed, ignoring NAME=\"%s\"; please fix it in %s:%u\n",
+                                                 name,
+                                                 rules_str(rules, rule->rule.filename_off),
+                                                 rule->rule.filename_line);
                                 break;
                         }
                         if (free_and_strdup(&event->name, name_str) < 0)
                                 return log_oom();
 
-                        log_debug("NAME '%s' %s:%u",
-                                  event->name,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "NAME '%s' %s:%u",
+                                         event->name,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 }
                 case TK_A_DEVLINK: {
@@ -2343,15 +2344,15 @@ int udev_rules_apply_to_event(
                         else if (esc == ESCAPE_REPLACE)
                                 count = util_replace_chars(temp, "/");
                         if (count > 0)
-                                log_debug("Replaced %i character(s)" , count);
+                                log_device_debug(dev, "Replaced %i character(s) from result of LINK" , count);
                         pos = temp;
                         while (isspace(pos[0]))
                                 pos++;
                         next = strchr(pos, ' ');
                         while (next) {
                                 next[0] = '\0';
-                                log_debug("LINK '%s' %s:%u", pos,
-                                          rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+                                log_device_debug(dev, "LINK '%s' %s:%u", pos,
+                                                 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
                                 strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
                                 device_add_devlink(dev, filename);
                                 while (isspace(next[1]))
@@ -2360,8 +2361,8 @@ int udev_rules_apply_to_event(
                                 next = strchr(pos, ' ');
                         }
                         if (pos[0] != '\0') {
-                                log_debug("LINK '%s' %s:%u", pos,
-                                          rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+                                log_device_debug(dev, "LINK '%s' %s:%u", pos,
+                                                 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
                                 strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
                                 device_add_devlink(dev, filename);
                         }
@@ -2379,14 +2380,14 @@ int udev_rules_apply_to_event(
                         attr_subst_subdir(attr, sizeof(attr));
 
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
-                        log_debug("ATTR '%s' writing '%s' %s:%u", attr, value,
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "ATTR '%s' writing '%s' %s:%u", attr, value,
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         f = fopen(attr, "we");
                         if (!f)
-                                log_error_errno(errno, "Failed to open ATTR{%s} for writing: %m", attr);
+                                log_device_error_errno(dev, errno, "Failed to open ATTR{%s} for writing: %m", attr);
                         else if (fprintf(f, "%s", value) <= 0)
-                                log_error_errno(errno, "Failed to write ATTR{%s}: %m", attr);
+                                log_device_error_errno(dev, errno, "Failed to write ATTR{%s}: %m", attr);
                         break;
                 }
                 case TK_A_SYSCTL: {
@@ -2395,11 +2396,11 @@ int udev_rules_apply_to_event(
                         udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
                         sysctl_normalize(filename);
                         udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
-                        log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
-                                  rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
+                        log_device_debug(dev, "SYSCTL '%s' writing '%s' %s:%u", filename, value,
+                                         rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
                         r = sysctl_write(filename, value);
                         if (r < 0)
-                                log_error_errno(r, "Failed to write SYSCTL{%s}='%s': %m", filename, value);
+                                log_device_error_errno(dev, r, "Failed to write SYSCTL{%s}='%s': %m", filename, value);
                         break;
                 }
                 case TK_A_RUN_BUILTIN:
@@ -2427,10 +2428,10 @@ int udev_rules_apply_to_event(
 
                         cmd = NULL;
 
-                        log_debug("RUN '%s' %s:%u",
-                                  rules_str(rules, cur->key.value_off),
-                                  rules_str(rules, rule->rule.filename_off),
-                                  rule->rule.filename_line);
+                        log_device_debug(dev, "RUN '%s' %s:%u",
+                                         rules_str(rules, cur->key.value_off),
+                                         rules_str(rules, rule->rule.filename_off),
+                                         rule->rule.filename_line);
                         break;
                 }
                 case TK_A_GOTO:
@@ -2445,7 +2446,7 @@ int udev_rules_apply_to_event(
                 case TK_M_PARENTS_MAX:
                 case TK_M_MAX:
                 case TK_UNSET:
-                        log_error("Wrong type %u", cur->type);
+                        log_device_error(dev, "Wrong type %u", cur->type);
                         goto nomatch;
                 }
 
index 6eacfe811f6e817c9ba78c9537aeaff496985a9d..c9e3d426b30a17f06de59817b408a8f0ff09c988 100644 (file)
@@ -57,6 +57,7 @@
 #include "signal-util.h"
 #include "socket-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "strxcpyx.h"
 #include "syslog-util.h"
 #include "udev-builtin.h"
@@ -361,9 +362,7 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) {
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to get sysname: %m");
 
-        if (startswith(val, "dm-") ||
-            startswith(val, "md") ||
-            startswith(val, "drbd"))
+        if (STARTSWITH_SET(val, "dm-", "md", "drbd"))
                 return 0;
 
         r = sd_device_get_devtype(dev, &val);
@@ -1617,9 +1616,11 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
         if (!manager->ctrl)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize udev control socket");
 
-        r = udev_ctrl_enable_receiving(manager->ctrl);
-        if (r < 0)
-                return log_error_errno(r, "Failed to bind udev control socket: %m");
+        if (fd_ctrl < 0) {
+                r = udev_ctrl_enable_receiving(manager->ctrl);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to bind udev control socket: %m");
+        }
 
         fd_ctrl = udev_ctrl_get_fd(manager->ctrl);
         if (fd_ctrl < 0)
@@ -1629,7 +1630,8 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize device monitor: %m");
 
-        (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
+        if (fd_uevent < 0)
+                (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
 
         /* unnamed socket from workers to the main daemon */
         r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
index c738bea10e5884a24901aa3089855f807db8baa9..abfcee3cd252814e01b30edb2cc2ccb4fe8ac216 100755 (executable)
@@ -11,10 +11,27 @@ if grep -q cgroup2 /proc/filesystems ; then
                     -w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control
 
         systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \
-                    grep memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
+                    grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
 
         systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \
-                    grep pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+                    grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+
+        # "io" is not among the controllers enabled by default for all units, verify that
+        grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+        # Run a service with "io" enabled, and verify it works
+        systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice"  \
+                    grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers
+
+        # We want to check if "io" is removed again from the controllers
+        # list. However, PID 1 (rightfully) does this asynchronously. In order
+        # to force synchronization on this, let's start a short-lived service
+        # which requires PID 1 to refresh the cgroup tree, so that we can
+        # verify that this all works.
+        systemd-run --wait --unit=test4.service true
+
+        # And now check again, "io" should have vanished
+        grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
 else
         echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroupsv2" >&2
 fi
diff --git a/test/fuzz/fuzz-udev-rules/50-udev-default.rules b/test/fuzz/fuzz-udev-rules/50-udev-default.rules
new file mode 100644 (file)
index 0000000..8e06c13
--- /dev/null
@@ -0,0 +1,86 @@
+# do not edit this file, it will be overwritten on update
+
+# run a command on remove events
+ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}"
+ACTION=="remove", GOTO="default_end"
+
+SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}"
+
+# select "system RTC" or just use the first one
+SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc"
+SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100"
+
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}"
+
+ACTION!="add", GOTO="default_end"
+
+SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
+SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
+SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
+KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout"
+
+SUBSYSTEM=="mem", KERNEL=="mem|kmem|port", GROUP="kmem", MODE="0640"
+
+SUBSYSTEM=="input", GROUP="input"
+SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664"
+
+SUBSYSTEM=="video4linux", GROUP="video"
+SUBSYSTEM=="graphics", GROUP="video"
+SUBSYSTEM=="drm", KERNEL!="renderD*", GROUP="video"
+SUBSYSTEM=="dvb", GROUP="video"
+SUBSYSTEM=="media", GROUP="video"
+SUBSYSTEM=="cec", GROUP="video"
+
+SUBSYSTEM=="drm", KERNEL=="renderD*", GROUP="render", MODE="0666"
+SUBSYSTEM=="kfd", GROUP="render", MODE="0666"
+
+SUBSYSTEM=="sound", GROUP="audio", \
+  OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer"
+
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664"
+
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video"
+
+KERNEL=="parport[0-9]*", GROUP="lp"
+SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp"
+SUBSYSTEM=="ppdev", GROUP="lp"
+KERNEL=="lp[0-9]*", GROUP="lp"
+KERNEL=="irlpt[0-9]*", GROUP="lp"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp"
+
+SUBSYSTEM=="block", GROUP="disk"
+SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom"
+KERNEL=="sch[0-9]*", GROUP="cdrom"
+KERNEL=="pktcdvd[0-9]*", GROUP="cdrom"
+KERNEL=="pktcdvd", GROUP="cdrom"
+
+SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk"
+KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk"
+KERNEL=="loop-control", GROUP="disk", OPTIONS+="static_node=loop-control"
+KERNEL=="btrfs-control", GROUP="disk"
+KERNEL=="rawctl", GROUP="disk"
+SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk"
+SUBSYSTEM=="aoe", GROUP="disk", MODE="0220"
+SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440"
+
+KERNEL=="rfkill", MODE="0664"
+KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun"
+
+KERNEL=="fuse", MODE="0666", OPTIONS+="static_node=fuse"
+
+# The static_node is required on s390x and ppc (they are using MODULE_ALIAS)
+KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"
+
+SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK += "ptp_kvm"
+
+LABEL="default_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-block.rules b/test/fuzz/fuzz-udev-rules/60-block.rules
new file mode 100644 (file)
index 0000000..343fc06
--- /dev/null
@@ -0,0 +1,11 @@
+# do not edit this file, it will be overwritten on update
+
+# enable in-kernel media-presence polling
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", \
+  ATTR{parameters/events_dfl_poll_msecs}="2000"
+
+# forward scsi device event to corresponding block device
+ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change"
+
+# watch metadata changes, caused by tools closing the device node which was opened for writing
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*", OPTIONS+="watch"
diff --git a/test/fuzz/fuzz-udev-rules/60-cdrom_id.rules b/test/fuzz/fuzz-udev-rules/60-cdrom_id.rules
new file mode 100644 (file)
index 0000000..288f8ce
--- /dev/null
@@ -0,0 +1,29 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="cdrom_end"
+SUBSYSTEM!="block", GOTO="cdrom_end"
+KERNEL!="sr[0-9]*|vdisk*|xvd*", GOTO="cdrom_end"
+ENV{DEVTYPE}!="disk", GOTO="cdrom_end"
+
+# unconditionally tag device as CDROM
+KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1"
+
+# stop automatically any mount units bound to the device if the media eject
+# button is pressed.
+ENV{ID_CDROM}=="1", ENV{SYSTEMD_MOUNT_DEVICE_BOUND}="1"
+
+# media eject button pressed
+ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end"
+
+# import device and media properties and lock tray to
+# enable the receiving of media eject button events
+IMPORT{program}="cdrom_id --lock-media $devnode"
+
+# ejecting a CD does not remove the device node, so mark the systemd device
+# unit as inactive while there is no medium; this automatically cleans up of
+# stale mounts after ejecting
+ENV{DISK_MEDIA_CHANGE}=="?*", ENV{ID_CDROM_MEDIA}!="?*", ENV{SYSTEMD_READY}="0"
+
+KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100"
+
+LABEL="cdrom_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-drm.rules b/test/fuzz/fuzz-udev-rules/60-drm.rules
new file mode 100644 (file)
index 0000000..f7f3435
--- /dev/null
@@ -0,0 +1,8 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="remove", SUBSYSTEM=="drm", SUBSYSTEMS=="pci|usb|platform", IMPORT{builtin}="path_id"
+
+# by-path
+ENV{ID_PATH}=="?*", KERNEL=="card*", SYMLINK+="dri/by-path/$env{ID_PATH}-card"
+ENV{ID_PATH}=="?*", KERNEL=="controlD*", SYMLINK+="dri/by-path/$env{ID_PATH}-control"
+ENV{ID_PATH}=="?*", KERNEL=="renderD*", SYMLINK+="dri/by-path/$env{ID_PATH}-render"
diff --git a/test/fuzz/fuzz-udev-rules/60-evdev.rules b/test/fuzz/fuzz-udev-rules/60-evdev.rules
new file mode 100644 (file)
index 0000000..e5e608a
--- /dev/null
@@ -0,0 +1,23 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="evdev_end"
+KERNEL!="event*", GOTO="evdev_end"
+
+# skip later rules when we find something for this input device
+IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
+  RUN{builtin}+="keyboard", GOTO="evdev_end"
+
+# AT keyboard matching by the machine's DMI data
+DRIVERS=="atkbd", \
+  IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
+  RUN{builtin}+="keyboard", GOTO="evdev_end"
+
+# device matching the input device name + properties + the machine's DMI data
+KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
+  RUN{builtin}+="keyboard", GOTO="evdev_end"
+
+# device matching the input device name and the machine's DMI data
+KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
+  RUN{builtin}+="keyboard", GOTO="evdev_end"
+
+LABEL="evdev_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-input-id.rules b/test/fuzz/fuzz-udev-rules/60-input-id.rules
new file mode 100644 (file)
index 0000000..bb8a812
--- /dev/null
@@ -0,0 +1,8 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="id_input_end"
+
+SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id"
+SUBSYSTEM=="input", IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=id-input:modalias:"
+
+LABEL="id_input_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules b/test/fuzz/fuzz-udev-rules/60-persistent-alsa.rules
new file mode 100644 (file)
index 0000000..8154e2d
--- /dev/null
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="persistent_alsa_end"
+SUBSYSTEM!="sound", GOTO="persistent_alsa_end"
+KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end"
+
+SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id"
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}"
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}"
+
+LABEL="persistent_alsa_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-input.rules b/test/fuzz/fuzz-udev-rules/60-persistent-input.rules
new file mode 100644 (file)
index 0000000..255547d
--- /dev/null
@@ -0,0 +1,42 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="persistent_input_end"
+SUBSYSTEM!="input", GOTO="persistent_input_end"
+SUBSYSTEMS=="bluetooth", ENV{ID_BUS}="bluetooth", GOTO="persistent_input_end"
+# Bluetooth devices don't always have the bluetooth subsystem
+ATTRS{id/bustype}=="0005", ENV{ID_BUS}="bluetooth", GOTO="persistent_input_end"
+SUBSYSTEMS=="rmi4", ENV{ID_BUS}="rmi"
+SUBSYSTEMS=="serio", ENV{ID_BUS}="i8042"
+
+SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id"
+
+# determine class name for persistent symlinks
+ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd"
+ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse"
+ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse"
+ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse"
+ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick"
+DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr"
+ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir"
+
+# fill empty serial number
+ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial"
+
+# by-id links
+KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}"
+KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}"
+KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}"
+KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}"
+# allow empty class for USB devices, by appending the interface number
+SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \
+  SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}"
+
+# by-path
+SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}"
+ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}"
+# allow empty class for platform and usb devices; platform supports only a single interface that way
+SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \
+  SYMLINK+="input/by-path/$env{ID_PATH}-event"
+
+LABEL="persistent_input_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules b/test/fuzz/fuzz-udev-rules/60-persistent-storage-tape.rules
new file mode 100644 (file)
index 0000000..0136140
--- /dev/null
@@ -0,0 +1,36 @@
+# do not edit this file, it will be overwritten on update
+
+# persistent storage links: /dev/tape/{by-id,by-path}
+
+ACTION=="remove", GOTO="persistent_storage_tape_end"
+ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_tape_end"
+
+# type 8 devices are "Medium Changers"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \
+  SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}"
+
+# iSCSI devices from the same host have all the same ID_SERIAL,
+# but additionally a property named ID_SCSI_SERIAL.
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", ENV{ID_SCSI_SERIAL}=="?*", \
+  SYMLINK+="tape/by-id/scsi-$env{ID_SCSI_SERIAL}"
+
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{builtin}="path_id", \
+  SYMLINK+="tape/by-path/$env{ID_PATH}-changer"
+
+SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end"
+
+KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394"
+KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id"
+KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi"
+KERNEL=="st*[0-9]",  ENV{ID_SERIAL}=="?*",      SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+KERNEL=="st*[0-9]",  ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}"
+KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*",      SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst"
+KERNEL=="nst*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}-nst"
+
+# by-path (parent device path)
+KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id"
+KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}"
+KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst"
+
+LABEL="persistent_storage_tape_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules b/test/fuzz/fuzz-udev-rules/60-persistent-storage.rules
new file mode 100644 (file)
index 0000000..1d8880e
--- /dev/null
@@ -0,0 +1,109 @@
+# do not edit this file, it will be overwritten on update
+
+# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path}
+# scheme based on "Linux persistent device names", 2004, Hannes Reinecke <hare@suse.de>
+
+ACTION=="remove", GOTO="persistent_storage_end"
+ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_end"
+
+SUBSYSTEM!="block", GOTO="persistent_storage_end"
+KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*|nbd*|zd*", GOTO="persistent_storage_end"
+
+# ignore partitions that span the entire disk
+TEST=="whole_disk", GOTO="persistent_storage_end"
+
+# for partitions import parent information
+ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"
+
+# NVMe
+KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n"
+
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{wwid}=="?*", ENV{ID_WWN}="$attr{wwid}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
+  ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}"
+
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \
+  ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n"
+
+# virtio-blk
+KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}"
+KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n"
+
+# ATA
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"
+
+# ATAPI devices (SPC-3 or later)
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode"
+
+# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures)
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode"
+
+# Fall back usb_id for USB devices
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+
+# SCSI devices
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi"
+KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss"
+KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
+KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
+
+# PMEM devices
+KERNEL=="pmem*", ENV{DEVTYPE}=="disk", ATTRS{uuid}=="?*", SYMLINK+="disk/by-id/pmem-$attr{uuid}"
+
+# FireWire
+KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}"
+KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n"
+
+# MMC
+KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
+  ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}"
+KERNEL=="mmcblk[0-9]p[0-9]*", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
+
+# UBI-MTD
+SUBSYSTEM=="ubi", KERNEL=="ubi*_*", ATTRS{mtd_num}=="*", SYMLINK+="ubi_mtd%s{mtd_num}_%s{name}"
+
+# Memstick
+KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
+  ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}"
+KERNEL=="msblk[0-9]p[0-9]|mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
+
+# by-path
+ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
+KERNEL=="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-boot%n"
+KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
+ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
+
+# legacy virtio-pci by-path links (deprecated)
+KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}"
+KERNEL=="vd*[0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}-part%n"
+
+# probe filesystem metadata of optical drives which have a media inserted
+KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \
+  IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}"
+# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET
+KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \
+  IMPORT{builtin}="blkid --noraid"
+
+# probe filesystem metadata of disks
+KERNEL!="sr*", IMPORT{builtin}="blkid"
+
+# by-label/by-uuid links (filesystem metadata)
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+
+# by-id (World Wide Name)
+ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}"
+ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n"
+
+# by-partlabel/by-partuuid links (partition metadata)
+ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}"
+ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}"
+
+LABEL="persistent_storage_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules b/test/fuzz/fuzz-udev-rules/60-persistent-v4l.rules
new file mode 100644 (file)
index 0000000..93c5ee8
--- /dev/null
@@ -0,0 +1,20 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="persistent_v4l_end"
+SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end"
+ENV{MAJOR}=="", GOTO="persistent_v4l_end"
+
+IMPORT{program}="v4l_id $devnode"
+
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}"
+
+# check for valid "index" number
+TEST!="index", GOTO="persistent_v4l_end"
+ATTR{index}!="?*", GOTO="persistent_v4l_end"
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}"
+ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}"
+
+LABEL="persistent_v4l_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-sensor.rules b/test/fuzz/fuzz-udev-rules/60-sensor.rules
new file mode 100644 (file)
index 0000000..7ad2c36
--- /dev/null
@@ -0,0 +1,18 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="sensor_end"
+
+# device matching the sensor's name and the machine's DMI data for IIO devices
+SUBSYSTEM=="iio", KERNEL=="iio*", SUBSYSTEMS=="usb|i2c", \
+  IMPORT{builtin}="hwdb 'sensor:modalias:$attr{modalias}:$attr{[dmi/id]modalias}'", \
+  GOTO="sensor_end"
+
+SUBSYSTEM=="input", ENV{ID_INPUT_ACCELEROMETER}=="1", SUBSYSTEMS=="acpi", \
+  IMPORT{builtin}="hwdb 'sensor:modalias:acpi:$attr{hid}:$attr{[dmi/id]modalias}'", \
+  GOTO="sensor_end"
+
+SUBSYSTEM=="input", ENV{ID_INPUT_ACCELEROMETER}=="1", SUBSYSTEMS=="platform", \
+  IMPORT{builtin}="hwdb 'sensor:modalias:platform:$id:$attr{[dmi/id]modalias}'", \
+  GOTO="sensor_end"
+
+LABEL="sensor_end"
diff --git a/test/fuzz/fuzz-udev-rules/60-serial.rules b/test/fuzz/fuzz-udev-rules/60-serial.rules
new file mode 100644 (file)
index 0000000..f303e27
--- /dev/null
@@ -0,0 +1,26 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="serial_end"
+SUBSYSTEM!="tty", GOTO="serial_end"
+
+SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+
+# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices
+KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end"
+
+SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}"
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}"
+
+IMPORT{builtin}="usb_id"
+ENV{ID_SERIAL}=="", GOTO="serial_end"
+SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}"
+ENV{ID_USB_INTERFACE_NUM}=="", GOTO="serial_end"
+ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}"
+ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"
+
+LABEL="serial_end"
diff --git a/test/fuzz/fuzz-udev-rules/64-btrfs.rules b/test/fuzz/fuzz-udev-rules/64-btrfs.rules
new file mode 100644 (file)
index 0000000..0fa79df
--- /dev/null
@@ -0,0 +1,17 @@
+# do not edit this file, it will be overwritten on update
+
+SUBSYSTEM!="block", GOTO="btrfs_end"
+ACTION=="remove", GOTO="btrfs_end"
+ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end"
+ENV{SYSTEMD_READY}=="0", GOTO="btrfs_end"
+
+# let the kernel know about this btrfs filesystem, and check if it is complete
+IMPORT{builtin}="btrfs ready $devnode"
+
+# mark the device as not ready to be used by the system
+ENV{ID_BTRFS_READY}=="0", ENV{SYSTEMD_READY}="0"
+
+# reconsider pending devices in case when multidevice volume awaits
+ENV{ID_BTRFS_READY}=="1", RUN+="/usr/bin/udevadm trigger -s block -p ID_BTRFS_READY=0"
+
+LABEL="btrfs_end"
diff --git a/test/fuzz/fuzz-udev-rules/70-joystick.rules b/test/fuzz/fuzz-udev-rules/70-joystick.rules
new file mode 100644 (file)
index 0000000..b80d203
--- /dev/null
@@ -0,0 +1,12 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="joystick_end"
+ENV{ID_INPUT_JOYSTICK}=="", GOTO="joystick_end"
+KERNEL!="event*", GOTO="joystick_end"
+
+# joystick:<bustype>:v<vid>p<pid>:name:<name>:*
+KERNELS=="input*", ENV{ID_BUS}!="", \
+        IMPORT{builtin}="hwdb 'joystick:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+        GOTO="joystick_end"
+
+LABEL="joystick_end"
diff --git a/test/fuzz/fuzz-udev-rules/70-mouse.rules b/test/fuzz/fuzz-udev-rules/70-mouse.rules
new file mode 100644 (file)
index 0000000..3ea743a
--- /dev/null
@@ -0,0 +1,18 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="mouse_end"
+KERNEL!="event*", GOTO="mouse_end"
+ENV{ID_INPUT_MOUSE}=="", GOTO="mouse_end"
+
+# mouse:<subsystem>:v<vid>p<pid>:name:<name>:*
+KERNELS=="input*", ENV{ID_BUS}=="usb", \
+        IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+        GOTO="mouse_end"
+KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \
+        IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+        GOTO="mouse_end"
+DRIVERS=="psmouse", SUBSYSTEMS=="serio", \
+        IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \
+        GOTO="mouse_end"
+
+LABEL="mouse_end"
diff --git a/test/fuzz/fuzz-udev-rules/70-touchpad.rules b/test/fuzz/fuzz-udev-rules/70-touchpad.rules
new file mode 100644 (file)
index 0000000..7bede02
--- /dev/null
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="touchpad_end"
+ENV{ID_INPUT}=="", GOTO="touchpad_end"
+ENV{ID_INPUT_TOUCHPAD}=="", GOTO="touchpad_end"
+KERNEL!="event*", GOTO="touchpad_end"
+
+# touchpad:<subsystem>:v<vid>p<pid>:name:<name>:*
+KERNELS=="input*", ENV{ID_BUS}!="", \
+        IMPORT{builtin}="hwdb 'touchpad:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
+        GOTO="touchpad_end"
+
+LABEL="touchpad_end"
diff --git a/test/fuzz/fuzz-udev-rules/75-net-description.rules b/test/fuzz/fuzz-udev-rules/75-net-description.rules
new file mode 100644 (file)
index 0000000..7e62f8b
--- /dev/null
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="net_end"
+SUBSYSTEM!="net", GOTO="net_end"
+
+IMPORT{builtin}="net_id"
+
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+SUBSYSTEMS=="usb", GOTO="net_end"
+
+SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
+
+LABEL="net_end"
diff --git a/test/fuzz/fuzz-udev-rules/75-probe_mtd.rules b/test/fuzz/fuzz-udev-rules/75-probe_mtd.rules
new file mode 100644 (file)
index 0000000..8848aee
--- /dev/null
@@ -0,0 +1,7 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add", GOTO="mtd_probe_end"
+
+KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode"
+
+LABEL="mtd_probe_end"
diff --git a/test/fuzz/fuzz-udev-rules/78-sound-card.rules b/test/fuzz/fuzz-udev-rules/78-sound-card.rules
new file mode 100644 (file)
index 0000000..f2fc277
--- /dev/null
@@ -0,0 +1,96 @@
+# do not edit this file, it will be overwritten on update
+
+SUBSYSTEM!="sound", GOTO="sound_end"
+
+ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change"
+ACTION!="change", GOTO="sound_end"
+
+# Ok, we probably need a little explanation here for what the two lines above
+# are good for.
+#
+# The story goes like this: when ALSA registers a new sound card it emits a
+# series of 'add' events to userspace, for the main card device and for all the
+# child device nodes that belong to it. udev relays those to applications,
+# however only maintains the order between father and child, but not between
+# the siblings. The control device node creation can be used as synchronization
+# point. All other devices that belong to a card are created in the kernel
+# before it. However unfortunately due to the fact that siblings are forwarded
+# out of order by udev this fact is lost to applications.
+#
+# OTOH before an application can open a device it needs to make sure that all
+# its device nodes are completely created and set up.
+#
+# As a workaround for this issue we have added the udev rule above which will
+# generate a 'change' event on the main card device from the 'add' event of the
+# card's control device. Due to the ordering semantics of udev this event will
+# only be relayed after all child devices have finished processing properly.
+# When an application needs to listen for appearing devices it can hence look
+# for 'change' events only, and ignore the actual 'add' events.
+#
+# When the application is initialized at the same time as a device is plugged
+# in it may need to figure out if the 'change' event has already been triggered
+# or not for a card. To find that out we store the flag environment variable
+# SOUND_INITIALIZED on the device which simply tells us if the card 'change'
+# event has already been processed.
+
+KERNEL!="card*", GOTO="sound_end"
+
+ENV{SOUND_INITIALIZED}="1"
+
+IMPORT{builtin}="hwdb"
+SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
+SUBSYSTEMS=="usb", GOTO="skip_pci"
+
+SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", \
+  ENV{ID_BUS}="firewire", ENV{ID_SERIAL}="$attr{guid}", ENV{ID_SERIAL_SHORT}="$attr{guid}", \
+  ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{model}", \
+  ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}"
+SUBSYSTEMS=="firewire", GOTO="skip_pci"
+
+SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
+SUBSYSTEMS=="pci", GOTO="skip_pci"
+
+# If we reach here, the device nor any of its parents are USB/PCI/firewire bus devices.
+# If we now find a parent that is a platform device, assume that we're working with
+# an internal sound card.
+SUBSYSTEMS=="platform", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end"
+
+LABEL="skip_pci"
+
+# Define ID_ID if ID_BUS and ID_SERIAL are set. This will work for both
+# USB and firewire.
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}"
+ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}"
+
+IMPORT{builtin}="path_id"
+
+# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept
+# in sync with those defined for PulseAudio's src/pulse/proplist.h
+# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties.
+
+# If the first PCM device of this card has the pcm class 'modem', then the card is a modem
+ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end"
+
+# Identify cards on the internal PCI bus as internal
+SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end"
+
+# Devices that also support Image/Video interfaces are most likely webcams
+SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end"
+
+# Matching on the model strings is a bit ugly, I admit
+ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end"
+
+ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end"
+ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end"
+
+LABEL="sound_end"
diff --git a/test/fuzz/fuzz-udev-rules/80-drivers.rules b/test/fuzz/fuzz-udev-rules/80-drivers.rules
new file mode 100644 (file)
index 0000000..16fa5d8
--- /dev/null
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add", GOTO="drivers_end"
+
+ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}"
+SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd"
+SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN{builtin}+="kmod load tifm_ms"
+SUBSYSTEM=="memstick", RUN{builtin}+="kmod load ms_block mspro_block"
+SUBSYSTEM=="i2o", RUN{builtin}+="kmod load i2o_block"
+SUBSYSTEM=="module", KERNEL=="parport_pc", RUN{builtin}+="kmod load ppdev"
+KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", RUN{builtin}+="kmod load sm_ftl"
+
+LABEL="drivers_end"
diff --git a/test/fuzz/fuzz-udev-rules/80-net-setup-link.rules b/test/fuzz/fuzz-udev-rules/80-net-setup-link.rules
new file mode 100644 (file)
index 0000000..6e411a9
--- /dev/null
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+SUBSYSTEM!="net", GOTO="net_setup_link_end"
+
+IMPORT{builtin}="path_id"
+
+ACTION!="add", GOTO="net_setup_link_end"
+
+IMPORT{builtin}="net_setup_link"
+
+NAME=="", ENV{ID_NET_NAME}!="", NAME="$env{ID_NET_NAME}"
+
+LABEL="net_setup_link_end"
diff --git a/test/fuzz/fuzz-udev-rules/99-systemd.rules b/test/fuzz/fuzz-udev-rules/99-systemd.rules
new file mode 100644 (file)
index 0000000..6ae9898
--- /dev/null
@@ -0,0 +1,76 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  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.
+
+ACTION=="remove", GOTO="systemd_end"
+
+SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd"
+KERNEL=="vport*", TAG+="systemd"
+
+SUBSYSTEM=="ubi", TAG+="systemd"
+
+SUBSYSTEM=="block", TAG+="systemd"
+SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
+
+# Ignore encrypted devices with no identified superblock on it, since
+# we are probably still calling mke2fs or mkswap on it.
+SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
+
+# add symlink to GPT root disk
+SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
+SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
+SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root"
+
+# Ignore raid devices that are not yet assembled and started
+SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0"
+SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0"
+
+# Ignore loop devices that don't have any file attached
+SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0"
+
+# Ignore nbd devices until the PID file exists (which signals a connected device)
+SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0"
+
+# We need a hardware independent way to identify network devices. We
+# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names
+# should be treated as one namespace, like udev handles it. This is mostly
+# just an identification string for systemd, so whether the path actually is
+# accessible or not does not matter as long as it is unique and in the
+# filesystem namespace.
+#
+# http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955
+
+SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name"
+SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k"
+
+SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target"
+ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target"
+SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target"
+
+SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target"
+
+# Apply sysctl variables to network devices (and only to those) as they appear.
+ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name"
+
+# Pull in backlight save/restore for all backlight devices and
+# keyboard backlights
+SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service"
+SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service"
+
+# Pull in rfkill save/restore for all rfkill devices
+SUBSYSTEM=="rfkill", ENV{SYSTEMD_RFKILL}="1"
+SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id"
+SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket"
+
+# Asynchronously mount file systems implemented by these modules as soon as they are loaded.
+SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
+SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount"
+
+LABEL="systemd_end"