diff --git a/SOURCES/0144-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch b/SOURCES/0144-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch
new file mode 100644
index 0000000000000000000000000000000000000000..97cb963987f7d12a53ced506ddb0a346a179a288
--- /dev/null
+++ b/SOURCES/0144-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch
@@ -0,0 +1,25 @@
+From 2c7a7aadc438ed266539e2ed705dfdf457a9e347 Mon Sep 17 00:00:00 2001
+From: Jan Macku <jamacku@redhat.com>
+Date: Tue, 14 Jan 2025 13:26:05 +0100
+Subject: [PATCH] ci: use ubuntu 22:04 for deploy of man pages
+
+rhel-only: ci
+
+Related: RHEL-57603
+---
+ .github/workflows/deploy-man-pages.yml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/.github/workflows/deploy-man-pages.yml b/.github/workflows/deploy-man-pages.yml
+index 9739228a87..82fab81072 100644
+--- a/.github/workflows/deploy-man-pages.yml
++++ b/.github/workflows/deploy-man-pages.yml
+@@ -26,7 +26,7 @@ jobs:
+     environment:
+       name: github-pages
+       url: ${{ steps.deployment.outputs.page_url }}
+-    runs-on: ubuntu-latest
++    runs-on: ubuntu-22.04
+ 
+     permissions:
+       pages: write
diff --git a/SOURCES/0145-tree-wide-Fix-python-formatting.patch b/SOURCES/0145-tree-wide-Fix-python-formatting.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c624880ea8a11ab3bee96b0e8ce19c43d303bea7
--- /dev/null
+++ b/SOURCES/0145-tree-wide-Fix-python-formatting.patch
@@ -0,0 +1,78 @@
+From 879603eda7440bf84a4094863283d556c8562907 Mon Sep 17 00:00:00 2001
+From: Daan De Meyer <daan.j.demeyer@gmail.com>
+Date: Thu, 9 Jan 2025 16:24:22 +0100
+Subject: [PATCH] tree-wide: Fix python formatting
+
+The new release of ruff formats a few more things which causes linter
+failures in CI so let's fix those formatting nits.
+
+(cherry picked from commit 96403d5121d93dd47dbe9dab5b90ff973e664ac3)
+
+Related: RHEL-57603
+---
+ src/ukify/ukify.py               | 6 +++---
+ test/integration-test-wrapper.py | 6 +++---
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
+index 3f36aa7af6..be4e30eb8e 100755
+--- a/src/ukify/ukify.py
++++ b/src/ukify/ukify.py
+@@ -763,7 +763,7 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
+             cmd = [
+                 measure_tool,
+                 'calculate',
+-                *(f"--{s.name.removeprefix('.')}={s.content}" for s in to_measure.values()),
++                *(f'--{s.name.removeprefix(".")}={s.content}' for s in to_measure.values()),
+                 *(f'--bank={bank}' for bank in banks),
+                 # For measurement, the keys are not relevant, so we can lump all the phase paths
+                 # into one call to systemd-measure calculate.
+@@ -786,7 +786,7 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
+             cmd = [
+                 measure_tool,
+                 'sign',
+-                *(f"--{s.name.removeprefix('.')}={s.content}" for s in to_measure.values()),
++                *(f'--{s.name.removeprefix(".")}={s.content}' for s in to_measure.values()),
+                 *(f'--bank={bank}' for bank in banks),
+             ]
+ 
+@@ -1284,7 +1284,7 @@ def make_uki(opts: UkifyConfig) -> None:
+         os.umask(umask := os.umask(0))
+         os.chmod(opts.output, 0o777 & ~umask)
+ 
+-    print(f"Wrote {'signed' if sign_args_present else 'unsigned'} {opts.output}")
++    print(f'Wrote {"signed" if sign_args_present else "unsigned"} {opts.output}')
+ 
+ 
+ @contextlib.contextmanager
+diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py
+index ef6df8840f..eacfeffe9c 100755
+--- a/test/integration-test-wrapper.py
++++ b/test/integration-test-wrapper.py
+@@ -429,7 +429,7 @@ def main() -> None:
+         dropin += textwrap.dedent(
+             f"""
+             [Service]
+-            Environment=TEST_MATCH_SUBTEST={os.environ["TEST_MATCH_SUBTEST"]}
++            Environment=TEST_MATCH_SUBTEST={os.environ['TEST_MATCH_SUBTEST']}
+             """
+         )
+ 
+@@ -437,7 +437,7 @@ def main() -> None:
+         dropin += textwrap.dedent(
+             f"""
+             [Service]
+-            Environment=TEST_MATCH_TESTCASE={os.environ["TEST_MATCH_TESTCASE"]}
++            Environment=TEST_MATCH_TESTCASE={os.environ['TEST_MATCH_TESTCASE']}
+             """
+         )
+ 
+@@ -559,7 +559,7 @@ def main() -> None:
+ 
+     ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info']
+ 
+-    print("Test failed, relevant logs can be viewed with: \n\n" f"{(' && '.join(ops))}\n", file=sys.stderr)
++    print(f'Test failed, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr)
+ 
+     # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
+     exit(result.returncode or 1)
diff --git a/SOURCES/0146-ci-fix-Packit.patch b/SOURCES/0146-ci-fix-Packit.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8fa20ad8717574d6c1c63bedf148373ffc2e0b29
--- /dev/null
+++ b/SOURCES/0146-ci-fix-Packit.patch
@@ -0,0 +1,25 @@
+From 5588bd5336067976b13857b443b883b27fe8606e Mon Sep 17 00:00:00 2001
+From: Jan Macku <jamacku@redhat.com>
+Date: Wed, 15 Jan 2025 15:35:00 +0100
+Subject: [PATCH] ci: fix Packit
+
+rhel-only: ci
+
+Related: RHEL-57603
+---
+ .packit.yml | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/.packit.yml b/.packit.yml
+index d16e630817..9d6e6c1e4e 100644
+--- a/.packit.yml
++++ b/.packit.yml
+@@ -7,6 +7,8 @@ files_to_sync:
+   - .packit.yml
+   - src: .packit_rpm/systemd.spec
+     dest: systemd.spec
++upstream_package_name: systemd
++downstream_package_name: systemd
+ # `git describe` returns in systemd's case 'v245-xxx' which breaks RPM version
+ # detection (that expects 245-xxxx'). Let's tweak the version string accordingly
+ upstream_tag_template: "v{version}"
diff --git a/SOURCES/0147-ci-drop-testing-farm-test.patch b/SOURCES/0147-ci-drop-testing-farm-test.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8820e696feaddb5bb363cbabe8179437fd3f4cdc
--- /dev/null
+++ b/SOURCES/0147-ci-drop-testing-farm-test.patch
@@ -0,0 +1,29 @@
+From 956076a7fd7f02f10b28ef6de5c365f77fed4516 Mon Sep 17 00:00:00 2001
+From: Jan Macku <jamacku@redhat.com>
+Date: Thu, 16 Jan 2025 14:42:48 +0100
+Subject: [PATCH] ci: drop testing farm test
+
+Test doesn't work on source-git repos, so let's drop it.
+
+rhel-only: ci
+
+Related: RHEL-57603
+---
+ .packit.yml | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/.packit.yml b/.packit.yml
+index 9d6e6c1e4e..a5d9aca0e1 100644
+--- a/.packit.yml
++++ b/.packit.yml
+@@ -48,10 +48,3 @@ jobs:
+   - centos-stream-10-ppc64le
+   - centos-stream-10-s390x
+   - centos-stream-10-x86_64
+-
+-- job: tests
+-  trigger: pull_request
+-  fmf_path: test/fmf
+-  tmt_plan: ci
+-  targets:
+-  - centos-stream-10-x86_64
diff --git a/SOURCES/0148-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch b/SOURCES/0148-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch
new file mode 100644
index 0000000000000000000000000000000000000000..28746729c5648c3265c44e929ccc0e467c511d45
--- /dev/null
+++ b/SOURCES/0148-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch
@@ -0,0 +1,180 @@
+From 24c7b86ce25b4bc1b55cbf0dffe4fa78925476b5 Mon Sep 17 00:00:00 2001
+From: Ronan Pigott <ronan@rjp.ie>
+Date: Thu, 28 Nov 2024 12:53:32 -0700
+Subject: [PATCH] dbus: stash the subscriber list when we disconenct from the
+ bus
+
+If we unexpectly disconnect from the bus, systemd would end up dropping
+the list of subscribers, which breaks the ability of clients like logind
+to monitor the state of units.
+
+Stash the list of subscribers into the deserialized state in the event
+of a disconnect so that when we recover we can renew the broken
+subscriptions.
+
+(cherry picked from commit 8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98)
+
+Resolves: RHEL-73780
+---
+ src/core/dbus.c       | 21 +++++++++++++++------
+ src/core/dbus.h       |  2 +-
+ src/core/manager.c    |  8 ++++----
+ src/shared/bus-util.c | 22 ++++++++++++++++++++++
+ src/shared/bus-util.h |  1 +
+ 5 files changed, 43 insertions(+), 11 deletions(-)
+
+diff --git a/src/core/dbus.c b/src/core/dbus.c
+index 3f0f40e702..9abc35888d 100644
+--- a/src/core/dbus.c
++++ b/src/core/dbus.c
+@@ -860,6 +860,8 @@ int bus_init_api(Manager *m) {
+         if (r < 0)
+                 return log_error_errno(r, "Failed to set up API bus: %m");
+ 
++        (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->deserialized_subscribed);
++        m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+         m->api_bus = TAKE_PTR(bus);
+ 
+         return 0;
+@@ -1004,8 +1006,17 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
+         }
+ 
+         /* Get rid of tracked clients on this bus */
+-        if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus)
++        if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus) {
++                _cleanup_strv_free_ char **subscribed = NULL;
++                int r;
++
++                r = bus_track_to_strv(m->subscribed, &subscribed);
++                if (r < 0)
++                        log_warning_errno(r, "Failed to serialize api subscribers, ignoring: %m");
++                strv_free_and_replace(m->deserialized_subscribed, subscribed);
++
+                 m->subscribed = sd_bus_track_unref(m->subscribed);
++        }
+ 
+         HASHMAP_FOREACH(j, m->jobs)
+                 if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
+@@ -1064,7 +1075,6 @@ void bus_done(Manager *m) {
+ 
+         assert(!m->subscribed);
+ 
+-        m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+         m->polkit_registry = hashmap_free(m->polkit_registry);
+ }
+ 
+@@ -1153,20 +1163,19 @@ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
+         }
+ }
+ 
+-int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
++int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, bool recursive, char **l) {
+         int r;
+ 
+-        assert(m);
+         assert(t);
+ 
+         if (strv_isempty(l))
+                 return 0;
+ 
+-        if (!m->api_bus)
++        if (!bus)
+                 return 0;
+ 
+         if (!*t) {
+-                r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
++                r = sd_bus_track_new(bus, t, NULL, NULL);
+                 if (r < 0)
+                         return r;
+         }
+diff --git a/src/core/dbus.h b/src/core/dbus.h
+index d00c14d3b7..eb1baf6049 100644
+--- a/src/core/dbus.h
++++ b/src/core/dbus.h
+@@ -19,7 +19,7 @@ void bus_done(Manager *m);
+ int bus_fdset_add_all(Manager *m, FDSet *fds);
+ 
+ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+-int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
++int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, bool recursive, char **l);
+ 
+ int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
+ 
+diff --git a/src/core/manager.c b/src/core/manager.c
+index 485fdd1a66..068ea2ab03 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -1803,6 +1803,9 @@ Manager* manager_free(Manager *m) {
+         free(m->switch_root);
+         free(m->switch_root_init);
+ 
++        sd_bus_track_unref(m->subscribed);
++        strv_free(m->deserialized_subscribed);
++
+         unit_defaults_done(&m->defaults);
+ 
+         FOREACH_ARRAY(map, m->units_needing_mounts_for, _UNIT_MOUNT_DEPENDENCY_TYPE_MAX) {
+@@ -2139,7 +2142,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo
+                 manager_setup_bus(m);
+ 
+                 /* Now that we are connected to all possible buses, let's deserialize who is tracking us. */
+-                r = bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
++                r = bus_track_coldplug(m->api_bus, &m->subscribed, false, m->deserialized_subscribed);
+                 if (r < 0)
+                         log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m");
+                 m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+@@ -3813,9 +3816,6 @@ int manager_reload(Manager *m) {
+         /* Clean up runtime objects no longer referenced */
+         manager_vacuum(m);
+ 
+-        /* Clean up deserialized tracked clients */
+-        m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+-
+         /* Consider the reload process complete now. */
+         assert(m->n_reloading > 0);
+         m->n_reloading--;
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index ff80e580fc..2cdde97b78 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -698,6 +698,28 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) {
+         return r;
+ }
+ 
++int bus_track_to_strv(sd_bus_track *t, char ***ret) {
++        _cleanup_strv_free_ char **subscribed = NULL;
++        int r = 0;
++
++        assert(ret);
++
++        for (const char *n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
++                r = sd_bus_track_count_name(t, n);
++                if (r < 0)
++                        return r;
++
++                for (int j = 0; j < r; j++) {
++                        r = strv_extend(&subscribed, n);
++                        if (r < 0)
++                                return r;
++                }
++        }
++
++        *ret = TAKE_PTR(subscribed);
++        return r;
++}
++
+ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description) {
+         _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
+         const char *e;
+diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
+index fbccb24314..83522ad1c1 100644
+--- a/src/shared/bus-util.h
++++ b/src/shared/bus-util.h
+@@ -63,6 +63,7 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id,
+ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
+ 
+ int bus_track_add_name_many(sd_bus_track *t, char **l);
++int bus_track_to_strv(sd_bus_track *t, char ***ret);
+ 
+ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description);
+ static inline int bus_open_system_watch_bind(sd_bus **ret) {
diff --git a/SOURCES/0149-manager-s-deserialized_subscribed-subscribed_as_strv.patch b/SOURCES/0149-manager-s-deserialized_subscribed-subscribed_as_strv.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ed050cdf70cb9439d4beeb627acf8642474add05
--- /dev/null
+++ b/SOURCES/0149-manager-s-deserialized_subscribed-subscribed_as_strv.patch
@@ -0,0 +1,94 @@
+From dedad9da3b54cbbe61086cac8cfe699feafeabd6 Mon Sep 17 00:00:00 2001
+From: Ronan Pigott <ronan@rjp.ie>
+Date: Wed, 11 Dec 2024 12:47:10 -0700
+Subject: [PATCH] manager: s/deserialized_subscribed/subscribed_as_strv
+
+Now that this field may get populated at runtime, the deserialized name
+is misleading. Change the name to reflect its updated purpose.
+
+(cherry picked from commit e1315a621ae26473fcc9cd0d6013836f5f498d40)
+
+Resolves: RHEL-73780
+---
+ src/core/dbus.c              | 6 +++---
+ src/core/manager-serialize.c | 2 +-
+ src/core/manager.c           | 6 +++---
+ src/core/manager.h           | 2 +-
+ 4 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/core/dbus.c b/src/core/dbus.c
+index 9abc35888d..58cd1ee175 100644
+--- a/src/core/dbus.c
++++ b/src/core/dbus.c
+@@ -860,8 +860,8 @@ int bus_init_api(Manager *m) {
+         if (r < 0)
+                 return log_error_errno(r, "Failed to set up API bus: %m");
+ 
+-        (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->deserialized_subscribed);
+-        m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
++        (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->subscribed_as_strv);
++        m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
+         m->api_bus = TAKE_PTR(bus);
+ 
+         return 0;
+@@ -1013,7 +1013,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
+                 r = bus_track_to_strv(m->subscribed, &subscribed);
+                 if (r < 0)
+                         log_warning_errno(r, "Failed to serialize api subscribers, ignoring: %m");
+-                strv_free_and_replace(m->deserialized_subscribed, subscribed);
++                strv_free_and_replace(m->subscribed_as_strv, subscribed);
+ 
+                 m->subscribed = sd_bus_track_unref(m->subscribed);
+         }
+diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
+index 3f624619df..bc29ac91c6 100644
+--- a/src/core/manager-serialize.c
++++ b/src/core/manager-serialize.c
+@@ -493,7 +493,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+                         (void) exec_shared_runtime_deserialize_one(m, val, fds);
+                 else if ((val = startswith(l, "subscribed="))) {
+ 
+-                        r = strv_extend(&m->deserialized_subscribed, val);
++                        r = strv_extend(&m->subscribed_as_strv, val);
+                         if (r < 0)
+                                 return r;
+                 } else if ((val = startswith(l, "varlink-server-socket-address="))) {
+diff --git a/src/core/manager.c b/src/core/manager.c
+index 068ea2ab03..4a7132274f 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -1804,7 +1804,7 @@ Manager* manager_free(Manager *m) {
+         free(m->switch_root_init);
+ 
+         sd_bus_track_unref(m->subscribed);
+-        strv_free(m->deserialized_subscribed);
++        strv_free(m->subscribed_as_strv);
+ 
+         unit_defaults_done(&m->defaults);
+ 
+@@ -2142,10 +2142,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo
+                 manager_setup_bus(m);
+ 
+                 /* Now that we are connected to all possible buses, let's deserialize who is tracking us. */
+-                r = bus_track_coldplug(m->api_bus, &m->subscribed, false, m->deserialized_subscribed);
++                r = bus_track_coldplug(m->api_bus, &m->subscribed, false, m->subscribed_as_strv);
+                 if (r < 0)
+                         log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m");
+-                m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
++                m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
+ 
+                 r = manager_varlink_init(m);
+                 if (r < 0)
+diff --git a/src/core/manager.h b/src/core/manager.h
+index e4cada80ff..7016eab2d3 100644
+--- a/src/core/manager.h
++++ b/src/core/manager.h
+@@ -340,7 +340,7 @@ struct Manager {
+         considered subscribes, since they last for very short only,
+         and it is much simpler that way. */
+         sd_bus_track *subscribed;
+-        char **deserialized_subscribed;
++        char **subscribed_as_strv;
+ 
+         /* This is used during reloading: before the reload we queue
+          * the reply message here, and afterwards we send it */
diff --git a/SOURCES/0150-shared-bus-util-move-bus_message_read_id128-to-bus-m.patch b/SOURCES/0150-shared-bus-util-move-bus_message_read_id128-to-bus-m.patch
new file mode 100644
index 0000000000000000000000000000000000000000..de1174775d946affa652374e9d1bb7cc4a213ee6
--- /dev/null
+++ b/SOURCES/0150-shared-bus-util-move-bus_message_read_id128-to-bus-m.patch
@@ -0,0 +1,197 @@
+From 744459cb070eff2c30853bfd799f48878cea987a Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Sat, 11 Jan 2025 16:52:05 +0100
+Subject: [PATCH] shared/bus-util: move bus_message_read_id128() to
+ bus-message-util
+
+(cherry picked from commit ab33edb05b7d4c90fb80f46aa6b951c505048798)
+
+Resolves: RHEL-73780
+---
+ src/core/dbus-manager.c         |  1 +
+ src/hostname/hostnamectl.c      |  1 +
+ src/machine/machinectl.c        |  1 +
+ src/machine/machined-dbus.c     |  1 +
+ src/run/run.c                   |  1 +
+ src/shared/bus-map-properties.c |  1 +
+ src/shared/bus-message-util.c   | 29 +++++++++++++++++++++++++++++
+ src/shared/bus-message-util.h   |  2 ++
+ src/shared/bus-util.c           | 28 ----------------------------
+ src/shared/bus-util.h           |  2 --
+ 10 files changed, 37 insertions(+), 30 deletions(-)
+
+diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
+index c4a7e2a20b..8e39d67a00 100644
+--- a/src/core/dbus-manager.c
++++ b/src/core/dbus-manager.c
+@@ -11,6 +11,7 @@
+ #include "bus-common-errors.h"
+ #include "bus-get-properties.h"
+ #include "bus-log-control-api.h"
++#include "bus-message-util.h"
+ #include "bus-util.h"
+ #include "chase.h"
+ #include "confidential-virt.h"
+diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
+index cd4f86af80..2d1334f654 100644
+--- a/src/hostname/hostnamectl.c
++++ b/src/hostname/hostnamectl.c
+@@ -18,6 +18,7 @@
+ #include "bus-error.h"
+ #include "bus-locator.h"
+ #include "bus-map-properties.h"
++#include "bus-message-util.h"
+ #include "format-table.h"
+ #include "hostname-setup.h"
+ #include "hostname-util.h"
+diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
+index 7843b9aa63..8854d9a550 100644
+--- a/src/machine/machinectl.c
++++ b/src/machine/machinectl.c
+@@ -21,6 +21,7 @@
+ #include "bus-error.h"
+ #include "bus-locator.h"
+ #include "bus-map-properties.h"
++#include "bus-message-util.h"
+ #include "bus-print-properties.h"
+ #include "bus-unit-procs.h"
+ #include "bus-unit-util.h"
+diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
+index f4915f67da..05e44b5030 100644
+--- a/src/machine/machined-dbus.c
++++ b/src/machine/machined-dbus.c
+@@ -10,6 +10,7 @@
+ #include "bus-common-errors.h"
+ #include "bus-get-properties.h"
+ #include "bus-locator.h"
++#include "bus-message-util.h"
+ #include "bus-polkit.h"
+ #include "cgroup-util.h"
+ #include "discover-image.h"
+diff --git a/src/run/run.c b/src/run/run.c
+index 1b13e74b83..c4e79adc7a 100644
+--- a/src/run/run.c
++++ b/src/run/run.c
+@@ -15,6 +15,7 @@
+ #include "bus-error.h"
+ #include "bus-locator.h"
+ #include "bus-map-properties.h"
++#include "bus-message-util.h"
+ #include "bus-unit-util.h"
+ #include "bus-wait-for-jobs.h"
+ #include "calendarspec.h"
+diff --git a/src/shared/bus-map-properties.c b/src/shared/bus-map-properties.c
+index a6cd752894..18d83c336f 100644
+--- a/src/shared/bus-map-properties.c
++++ b/src/shared/bus-map-properties.c
+@@ -5,6 +5,7 @@
+ #include "bus-util.h"
+ #include "strv.h"
+ #include "bus-message.h"
++#include "bus-message-util.h"
+ 
+ int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+         sd_id128_t *p = userdata;
+diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
+index d8c483ef62..e93be9b3c5 100644
+--- a/src/shared/bus-message-util.c
++++ b/src/shared/bus-message-util.c
+@@ -7,6 +7,35 @@
+ #include "copy.h"
+ #include "resolve-util.h"
+ 
++int bus_message_read_id128(sd_bus_message *m, sd_id128_t *ret) {
++        const void *a;
++        size_t sz;
++        int r;
++
++        assert(m);
++
++        r = sd_bus_message_read_array(m, 'y', &a, &sz);
++        if (r < 0)
++                return r;
++
++        switch (sz) {
++
++        case 0:
++                if (ret)
++                        *ret = SD_ID128_NULL;
++                return 0;
++
++        case sizeof(sd_id128_t):
++                if (ret)
++                        memcpy(ret, a, sz);
++                return !memeqzero(a, sz); /* This mimics sd_id128_is_null(), but ret may be NULL,
++                                           * and a may be misaligned, so use memeqzero() here. */
++
++        default:
++                return -EINVAL;
++        }
++}
++
+ int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) {
+         int ifindex, r;
+ 
+diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
+index 50025766c2..698960561c 100644
+--- a/src/shared/bus-message-util.h
++++ b/src/shared/bus-message-util.h
+@@ -6,6 +6,8 @@
+ #include "in-addr-util.h"
+ #include "socket-netlink.h"
+ 
++int bus_message_read_id128(sd_bus_message *m, sd_id128_t *ret);
++
+ int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret);
+ int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret);
+ int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr);
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index 2cdde97b78..d3d3f1733e 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -953,34 +953,6 @@ int bus_query_sender_pidref(
+         return bus_creds_get_pidref(creds, ret);
+ }
+ 
+-int bus_message_read_id128(sd_bus_message *m, sd_id128_t *ret) {
+-        const void *a;
+-        size_t sz;
+-        int r;
+-
+-        assert(m);
+-
+-        r = sd_bus_message_read_array(m, 'y', &a, &sz);
+-        if (r < 0)
+-                return r;
+-
+-        switch (sz) {
+-        case 0:
+-                if (ret)
+-                        *ret = SD_ID128_NULL;
+-                return 0;
+-
+-        case sizeof(sd_id128_t):
+-                if (ret)
+-                        memcpy(ret, a, sz);
+-                return !memeqzero(a, sz); /* This mimics sd_id128_is_null(), but ret may be NULL,
+-                                           * and a may be misaligned, so use memeqzero() here. */
+-
+-        default:
+-                return -EINVAL;
+-        }
+-}
+-
+ static const char* const bus_transport_table[] = {
+         [BUS_TRANSPORT_LOCAL]   = "local",
+         [BUS_TRANSPORT_REMOTE]  = "remote",
+diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
+index 83522ad1c1..c2b9b126c8 100644
+--- a/src/shared/bus-util.h
++++ b/src/shared/bus-util.h
+@@ -84,6 +84,4 @@ int bus_property_get_string_set(sd_bus *bus, const char *path, const char *inter
+ int bus_creds_get_pidref(sd_bus_creds *c, PidRef *ret);
+ int bus_query_sender_pidref(sd_bus_message *m, PidRef *ret);
+ 
+-int bus_message_read_id128(sd_bus_message *m, sd_id128_t *ret);
+-
+ const char* bus_transport_to_string(BusTransport transport) _const_;
diff --git a/SOURCES/0151-shared-bus-util-move-bus_message_hash_ops-to-bus-mes.patch b/SOURCES/0151-shared-bus-util-move-bus_message_hash_ops-to-bus-mes.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a98f6057cd93b3bc9aab8e144c5534d194356859
--- /dev/null
+++ b/SOURCES/0151-shared-bus-util-move-bus_message_hash_ops-to-bus-mes.patch
@@ -0,0 +1,157 @@
+From 3a71e1dba1e5d56a3f0784df2ac53f5ad73a9451 Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Sat, 11 Jan 2025 17:10:43 +0100
+Subject: [PATCH] shared/bus-util: move bus_message_hash_ops to
+ bus-message-util
+
+(cherry picked from commit e3d37628aabff92e4b756e63ef0a6cd4569ce743)
+
+Resolves: RHEL-73780
+---
+ src/home/homed-manager-bus.c         |  9 +++++----
+ src/login/logind-brightness.c        |  4 ++--
+ src/shared/bus-message-util.c        |  4 ++++
+ src/shared/bus-message-util.h        |  2 ++
+ src/shared/bus-util.c                | 10 ----------
+ src/shared/bus-util.h                |  2 --
+ src/systemctl/systemctl-list-units.c |  1 +
+ src/timedate/timedated.c             |  1 +
+ 8 files changed, 15 insertions(+), 18 deletions(-)
+
+diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c
+index 69c7680b9e..08c917aee2 100644
+--- a/src/home/homed-manager-bus.c
++++ b/src/home/homed-manager-bus.c
+@@ -4,6 +4,7 @@
+ 
+ #include "alloc-util.h"
+ #include "bus-common-errors.h"
++#include "bus-message-util.h"
+ #include "bus-polkit.h"
+ #include "format-util.h"
+ #include "home-util.h"
+@@ -704,17 +705,17 @@ static int method_rebalance(sd_bus_message *message, void *userdata, sd_bus_erro
+         int r;
+ 
+         r = manager_schedule_rebalance(m, /* immediately= */ true);
+-        if (r == 0)
+-                return sd_bus_reply_method_errorf(message, BUS_ERROR_REBALANCE_NOT_NEEDED, "No home directories need rebalancing.");
+         if (r < 0)
+                 return r;
++        if (r == 0)
++                return sd_bus_reply_method_errorf(message, BUS_ERROR_REBALANCE_NOT_NEEDED, "No home directories need rebalancing.");
+ 
+         /* Keep a reference to this message, so that we can reply to it once we are done */
+-        r = set_ensure_put(&m->rebalance_queued_method_calls, &bus_message_hash_ops, message);
++        r = set_ensure_consume(&m->rebalance_queued_method_calls, &bus_message_hash_ops, sd_bus_message_ref(message));
+         if (r < 0)
+                 return log_error_errno(r, "Failed to track rebalance bus message: %m");
++        assert(r > 0);
+ 
+-        sd_bus_message_ref(message);
+         return 1;
+ }
+ 
+diff --git a/src/login/logind-brightness.c b/src/login/logind-brightness.c
+index 40bcb39ce0..b3e7718394 100644
+--- a/src/login/logind-brightness.c
++++ b/src/login/logind-brightness.c
+@@ -1,5 +1,6 @@
+ /* SPDX-License-Identifier: LGPL-2.1-or-later */
+ 
++#include "bus-message-util.h"
+ #include "bus-util.h"
+ #include "device-util.h"
+ #include "hash-funcs.h"
+@@ -173,10 +174,9 @@ static int set_add_message(Set **set, sd_bus_message *message) {
+         if (r <= 0)
+                 return r;
+ 
+-        r = set_ensure_put(set, &bus_message_hash_ops, message);
++        r = set_ensure_consume(set, &bus_message_hash_ops, sd_bus_message_ref(message));
+         if (r <= 0)
+                 return r;
+-        sd_bus_message_ref(message);
+ 
+         return 1;
+ }
+diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
+index e93be9b3c5..a6523ff00e 100644
+--- a/src/shared/bus-message-util.c
++++ b/src/shared/bus-message-util.c
+@@ -246,3 +246,7 @@ int bus_message_dump_fd(sd_bus_message *message) {
+ 
+         return 0;
+ }
++
++DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(bus_message_hash_ops,
++                                      void, trivial_hash_func, trivial_compare_func,
++                                      sd_bus_message, sd_bus_message_unref);
+diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
+index 698960561c..baec1cb92b 100644
+--- a/src/shared/bus-message-util.h
++++ b/src/shared/bus-message-util.h
+@@ -21,3 +21,5 @@ int bus_message_read_dns_servers(
+ 
+ int bus_message_dump_string(sd_bus_message *message);
+ int bus_message_dump_fd(sd_bus_message *message);
++
++extern const struct hash_ops bus_message_hash_ops;
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index d3d3f1733e..362dd78537 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -862,16 +862,6 @@ int bus_register_malloc_status(sd_bus *bus, const char *destination) {
+         return 0;
+ }
+ 
+-static void bus_message_unref_wrapper(void *m) {
+-        sd_bus_message_unref(m);
+-}
+-
+-const struct hash_ops bus_message_hash_ops = {
+-        .hash = trivial_hash_func,
+-        .compare = trivial_compare_func,
+-        .free_value = bus_message_unref_wrapper,
+-};
+-
+ int bus_message_append_string_set(sd_bus_message *m, Set *set) {
+         const char *s;
+         int r;
+diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
+index c2b9b126c8..a2193ebf97 100644
+--- a/src/shared/bus-util.h
++++ b/src/shared/bus-util.h
+@@ -75,8 +75,6 @@ int bus_reply_pair_array(sd_bus_message *m, char **l);
+ /* Listen to GetMallocInfo() calls to 'destination' and return malloc_info() via FD */
+ int bus_register_malloc_status(sd_bus *bus, const char *destination);
+ 
+-extern const struct hash_ops bus_message_hash_ops;
+-
+ int bus_message_append_string_set(sd_bus_message *m, Set *s);
+ 
+ int bus_property_get_string_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c
+index a2f3074358..b7cb103513 100644
+--- a/src/systemctl/systemctl-list-units.c
++++ b/src/systemctl/systemctl-list-units.c
+@@ -5,6 +5,7 @@
+ #include "ansi-color.h"
+ #include "bus-error.h"
+ #include "bus-locator.h"
++#include "bus-message-util.h"
+ #include "format-table.h"
+ #include "locale-util.h"
+ #include "path-util.h"
+diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
+index c79bb864df..b196034a25 100644
+--- a/src/timedate/timedated.c
++++ b/src/timedate/timedated.c
+@@ -17,6 +17,7 @@
+ #include "bus-locator.h"
+ #include "bus-log-control-api.h"
+ #include "bus-map-properties.h"
++#include "bus-message-util.h"
+ #include "bus-polkit.h"
+ #include "bus-unit-util.h"
+ #include "clock-util.h"
diff --git a/SOURCES/0152-shared-bus-util-move-string-set-append-get-funcs-to-.patch b/SOURCES/0152-shared-bus-util-move-string-set-append-get-funcs-to-.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7f3997eb56fb4330c3bb746eb1b528ee799d51db
--- /dev/null
+++ b/SOURCES/0152-shared-bus-util-move-string-set-append-get-funcs-to-.patch
@@ -0,0 +1,184 @@
+From b1e0b30efe89592eff532a35b14345bc48b2afce Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Sat, 11 Jan 2025 18:04:37 +0100
+Subject: [PATCH] shared/bus-util: move string set append/get funcs to
+ bus-message-util and bus-get-properties, respectively
+
+(cherry picked from commit 91080bc9733b5b2478bfc0ed58f6a7ae5da7e639)
+
+Resolves: RHEL-73780
+---
+ src/core/dbus-cgroup.c          |  1 +
+ src/shared/bus-get-properties.c | 19 +++++++++++++++++
+ src/shared/bus-get-properties.h |  2 ++
+ src/shared/bus-message-util.c   | 19 +++++++++++++++++
+ src/shared/bus-message-util.h   |  2 ++
+ src/shared/bus-util.c           | 37 ---------------------------------
+ src/shared/bus-util.h           |  4 ----
+ 7 files changed, 43 insertions(+), 41 deletions(-)
+
+diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
+index fd48f3b07c..c99f1e29ac 100644
+--- a/src/core/dbus-cgroup.c
++++ b/src/core/dbus-cgroup.c
+@@ -7,6 +7,7 @@
+ #include "bpf-firewall.h"
+ #include "bpf-foreign.h"
+ #include "bus-get-properties.h"
++#include "bus-message-util.h"
+ #include "bus-util.h"
+ #include "cgroup-util.h"
+ #include "cgroup.h"
+diff --git a/src/shared/bus-get-properties.c b/src/shared/bus-get-properties.c
+index 53e5d6b99f..bf267a23a5 100644
+--- a/src/shared/bus-get-properties.c
++++ b/src/shared/bus-get-properties.c
+@@ -1,6 +1,7 @@
+ /* SPDX-License-Identifier: LGPL-2.1-or-later */
+ 
+ #include "bus-get-properties.h"
++#include "bus-message-util.h"
+ #include "rlimit-util.h"
+ #include "stdio-util.h"
+ #include "string-util.h"
+@@ -164,3 +165,21 @@ int bus_property_get_rlimit(
+ 
+         return sd_bus_message_append(reply, "t", u);
+ }
++
++int bus_property_get_string_set(
++                sd_bus *bus,
++                const char *path,
++                const char *interface,
++                const char *property,
++                sd_bus_message *reply,
++                void *userdata,
++                sd_bus_error *error) {
++
++        Set **s = ASSERT_PTR(userdata);
++
++        assert(bus);
++        assert(property);
++        assert(reply);
++
++        return bus_message_append_string_set(reply, *s);
++}
+diff --git a/src/shared/bus-get-properties.h b/src/shared/bus-get-properties.h
+index 4c35126502..9ddf5454de 100644
+--- a/src/shared/bus-get-properties.h
++++ b/src/shared/bus-get-properties.h
+@@ -52,6 +52,8 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t));
+ 
+ int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+ 
++int bus_property_get_string_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
++
+ #define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val)         \
+         int function(sd_bus *bus,                                       \
+                      const char *path,                                  \
+diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
+index a6523ff00e..8da112cacc 100644
+--- a/src/shared/bus-message-util.c
++++ b/src/shared/bus-message-util.c
+@@ -216,6 +216,25 @@ clear:
+         return r;
+ }
+ 
++int bus_message_append_string_set(sd_bus_message *m, const Set *set) {
++        int r;
++
++        assert(m);
++
++        r = sd_bus_message_open_container(m, 'a', "s");
++        if (r < 0)
++                return r;
++
++        const char *s;
++        SET_FOREACH(s, set) {
++                r = sd_bus_message_append(m, "s", s);
++                if (r < 0)
++                        return r;
++        }
++
++        return sd_bus_message_close_container(m);
++}
++
+ int bus_message_dump_string(sd_bus_message *message) {
+         const char *s;
+         int r;
+diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
+index baec1cb92b..d4a05f5b9c 100644
+--- a/src/shared/bus-message-util.h
++++ b/src/shared/bus-message-util.h
+@@ -19,6 +19,8 @@ int bus_message_read_dns_servers(
+                         struct in_addr_full ***ret_dns,
+                         size_t *ret_n_dns);
+ 
++int bus_message_append_string_set(sd_bus_message *m, const Set *s);
++
+ int bus_message_dump_string(sd_bus_message *message);
+ int bus_message_dump_fd(sd_bus_message *message);
+ 
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index 362dd78537..bdce97790d 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -862,43 +862,6 @@ int bus_register_malloc_status(sd_bus *bus, const char *destination) {
+         return 0;
+ }
+ 
+-int bus_message_append_string_set(sd_bus_message *m, Set *set) {
+-        const char *s;
+-        int r;
+-
+-        assert(m);
+-
+-        r = sd_bus_message_open_container(m, 'a', "s");
+-        if (r < 0)
+-                return r;
+-
+-        SET_FOREACH(s, set) {
+-                r = sd_bus_message_append(m, "s", s);
+-                if (r < 0)
+-                        return r;
+-        }
+-
+-        return sd_bus_message_close_container(m);
+-}
+-
+-int bus_property_get_string_set(
+-                sd_bus *bus,
+-                const char *path,
+-                const char *interface,
+-                const char *property,
+-                sd_bus_message *reply,
+-                void *userdata,
+-                sd_bus_error *error) {
+-
+-        Set **s = ASSERT_PTR(userdata);
+-
+-        assert(bus);
+-        assert(property);
+-        assert(reply);
+-
+-        return bus_message_append_string_set(reply, *s);
+-}
+-
+ int bus_creds_get_pidref(
+                 sd_bus_creds *c,
+                 PidRef *ret) {
+diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
+index a2193ebf97..e49ba66a10 100644
+--- a/src/shared/bus-util.h
++++ b/src/shared/bus-util.h
+@@ -75,10 +75,6 @@ int bus_reply_pair_array(sd_bus_message *m, char **l);
+ /* Listen to GetMallocInfo() calls to 'destination' and return malloc_info() via FD */
+ int bus_register_malloc_status(sd_bus *bus, const char *destination);
+ 
+-int bus_message_append_string_set(sd_bus_message *m, Set *s);
+-
+-int bus_property_get_string_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+-
+ int bus_creds_get_pidref(sd_bus_creds *c, PidRef *ret);
+ int bus_query_sender_pidref(sd_bus_message *m, PidRef *ret);
+ 
diff --git a/SOURCES/0153-shared-serialize-make-input-params-const.patch b/SOURCES/0153-shared-serialize-make-input-params-const.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0e4852cf29dbd07306d8cc4ec725ca2c3f530148
--- /dev/null
+++ b/SOURCES/0153-shared-serialize-make-input-params-const.patch
@@ -0,0 +1,68 @@
+From 47224a5d1eae82421419fb9bcf2916ee1de041dd Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Mon, 13 Jan 2025 16:35:13 +0100
+Subject: [PATCH] shared/serialize: make input params const
+
+(cherry picked from commit 3f03d39ca3b2f25f521342f2b0e49f60c51246e1)
+
+Resolves: RHEL-73780
+---
+ src/shared/serialize.c | 9 ++++++---
+ src/shared/serialize.h | 4 ++--
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/src/shared/serialize.c b/src/shared/serialize.c
+index 735caf4978..58773e5ddd 100644
+--- a/src/shared/serialize.c
++++ b/src/shared/serialize.c
+@@ -164,11 +164,14 @@ int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t)
+         return serialize_item_format(f, name, USEC_FMT " " USEC_FMT, t->realtime, t->monotonic);
+ }
+ 
+-int serialize_strv(FILE *f, const char *key, char **l) {
++int serialize_strv(FILE *f, const char *key, char * const *l) {
+         int ret = 0, r;
+ 
+         /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
+ 
++        assert(f);
++        assert(key);
++
+         STRV_FOREACH(i, l) {
+                 r = serialize_item_escaped(f, key, *i);
+                 if ((ret >= 0 && r < 0) ||
+@@ -267,8 +270,7 @@ int serialize_item_base64mem(FILE *f, const char *key, const void *p, size_t l)
+         return 1;
+ }
+ 
+-int serialize_string_set(FILE *f, const char *key, Set *s) {
+-        const char *e;
++int serialize_string_set(FILE *f, const char *key, const Set *s) {
+         int r;
+ 
+         assert(f);
+@@ -279,6 +281,7 @@ int serialize_string_set(FILE *f, const char *key, Set *s) {
+ 
+         /* Serialize as individual items, as each element might contain separators and escapes */
+ 
++        const char *e;
+         SET_FOREACH(e, s) {
+                 r = serialize_item(f, key, e);
+                 if (r < 0)
+diff --git a/src/shared/serialize.h b/src/shared/serialize.h
+index 355eff9b8f..a1350533cf 100644
+--- a/src/shared/serialize.h
++++ b/src/shared/serialize.h
+@@ -21,10 +21,10 @@ int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd);
+ int serialize_fd_many(FILE *f, FDSet *fds, const char *key, const int fd_array[], size_t n_fd_array);
+ int serialize_usec(FILE *f, const char *key, usec_t usec);
+ int serialize_dual_timestamp(FILE *f, const char *key, const dual_timestamp *t);
+-int serialize_strv(FILE *f, const char *key, char **l);
++int serialize_strv(FILE *f, const char *key, char * const *l);
+ int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref);
+ int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl);
+-int serialize_string_set(FILE *f, const char *key, Set *s);
++int serialize_string_set(FILE *f, const char *key, const Set *s);
+ int serialize_image_policy(FILE *f, const char *key, const ImagePolicy *p);
+ 
+ static inline int serialize_bool(FILE *f, const char *key, bool b) {
diff --git a/SOURCES/0154-shared-serialize-introduce-serialize_id128.patch b/SOURCES/0154-shared-serialize-introduce-serialize_id128.patch
new file mode 100644
index 0000000000000000000000000000000000000000..731858ae2b80bfa83e4756abcf01f68026d16b16
--- /dev/null
+++ b/SOURCES/0154-shared-serialize-introduce-serialize_id128.patch
@@ -0,0 +1,70 @@
+From 6eda0675d5a30080c6dd68c863707e22192b9e42 Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Mon, 13 Jan 2025 16:35:58 +0100
+Subject: [PATCH] shared/serialize: introduce serialize_id128()
+
+(cherry picked from commit 38a2c2bf6a89def24007c0dac529c07da713abfb)
+
+Resolves: RHEL-73780
+---
+ src/core/unit-serialize.c |  3 +--
+ src/shared/serialize.c    | 10 ++++++++++
+ src/shared/serialize.h    |  3 +++
+ 3 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c
+index 82102c0c32..2b5b472c47 100644
+--- a/src/core/unit-serialize.c
++++ b/src/core/unit-serialize.c
+@@ -117,8 +117,7 @@ int unit_serialize_state(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
+         if (gid_is_valid(u->ref_gid))
+                 (void) serialize_item_format(f, "ref-gid", GID_FMT, u->ref_gid);
+ 
+-        if (!sd_id128_is_null(u->invocation_id))
+-                (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
++        (void) serialize_id128(f, "invocation-id", u->invocation_id);
+ 
+         (void) serialize_item(f, "freezer-state", freezer_state_to_string(u->freezer_state));
+ 
+diff --git a/src/shared/serialize.c b/src/shared/serialize.c
+index 58773e5ddd..44148bea2c 100644
+--- a/src/shared/serialize.c
++++ b/src/shared/serialize.c
+@@ -182,6 +182,16 @@ int serialize_strv(FILE *f, const char *key, char * const *l) {
+         return ret;
+ }
+ 
++int serialize_id128(FILE *f, const char *key, sd_id128_t id) {
++        assert(f);
++        assert(key);
++
++        if (sd_id128_is_null(id))
++                return 0;
++
++        return serialize_item_format(f, key, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
++}
++
+ int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
+         int r;
+ 
+diff --git a/src/shared/serialize.h b/src/shared/serialize.h
+index a1350533cf..f2be84807a 100644
+--- a/src/shared/serialize.h
++++ b/src/shared/serialize.h
+@@ -3,6 +3,8 @@
+ 
+ #include <stdio.h>
+ 
++#include "sd-id128.h"
++
+ #include "fdset.h"
+ #include "image-policy.h"
+ #include "macro.h"
+@@ -22,6 +24,7 @@ int serialize_fd_many(FILE *f, FDSet *fds, const char *key, const int fd_array[]
+ int serialize_usec(FILE *f, const char *key, usec_t usec);
+ int serialize_dual_timestamp(FILE *f, const char *key, const dual_timestamp *t);
+ int serialize_strv(FILE *f, const char *key, char * const *l);
++int serialize_id128(FILE *f, const char *key, sd_id128_t id);
+ int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref);
+ int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl);
+ int serialize_string_set(FILE *f, const char *key, const Set *s);
diff --git a/SOURCES/0155-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch b/SOURCES/0155-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch
new file mode 100644
index 0000000000000000000000000000000000000000..44ebd68c2aa97ffd588955aaf7d0ff1a0305e448
--- /dev/null
+++ b/SOURCES/0155-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch
@@ -0,0 +1,55 @@
+From 370d94580947ffbc2e3e1bee30564b98896d9d02 Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Sat, 11 Jan 2025 16:26:55 +0100
+Subject: [PATCH] bus-util: do not reset the count returned by
+ sd_bus_track_count_name()
+
+Follow-up for 8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98
+
+While at it, turn the retval check for sd_bus_track_count_name()
+into assertion, given we're working with already established tracks
+(service_name_is_valid() should never yield false in this case).
+
+Addresses https://github.com/systemd/systemd/pull/35406#discussion_r1912066774
+
+(cherry picked from commit 33eeea4128f31df7ab4bd8866b582062d70114ae)
+
+Resolves: RHEL-73780
+---
+ src/shared/bus-util.c | 11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index bdce97790d..1fda013697 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -700,16 +700,15 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) {
+ 
+ int bus_track_to_strv(sd_bus_track *t, char ***ret) {
+         _cleanup_strv_free_ char **subscribed = NULL;
+-        int r = 0;
++        int r;
+ 
+         assert(ret);
+ 
+         for (const char *n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+-                r = sd_bus_track_count_name(t, n);
+-                if (r < 0)
+-                        return r;
++                int c = sd_bus_track_count_name(t, n);
++                assert(c >= 0);
+ 
+-                for (int j = 0; j < r; j++) {
++                for (int j = 0; j < c; j++) {
+                         r = strv_extend(&subscribed, n);
+                         if (r < 0)
+                                 return r;
+@@ -717,7 +716,7 @@ int bus_track_to_strv(sd_bus_track *t, char ***ret) {
+         }
+ 
+         *ret = TAKE_PTR(subscribed);
+-        return r;
++        return 0;
+ }
+ 
+ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description) {
diff --git a/SOURCES/0156-core-manager-use-FOREACH_ARRAY-at-one-more-place.patch b/SOURCES/0156-core-manager-use-FOREACH_ARRAY-at-one-more-place.patch
new file mode 100644
index 0000000000000000000000000000000000000000..be7e0c0cd4edca1ec72611f064e7b5d0098d4e5f
--- /dev/null
+++ b/SOURCES/0156-core-manager-use-FOREACH_ARRAY-at-one-more-place.patch
@@ -0,0 +1,36 @@
+From 19b9d9140cbb9270119f807e8b7fe4047d5aae4a Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Mon, 13 Jan 2025 17:06:21 +0100
+Subject: [PATCH] core/manager: use FOREACH_ARRAY at one more place
+
+(cherry picked from commit a7516260b32dd26fb61b1dd702b9bc718cd420f9)
+
+Resolves: RHEL-73780
+---
+ src/core/manager.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/core/manager.c b/src/core/manager.c
+index 4a7132274f..96fc8941eb 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -1816,15 +1816,16 @@ Manager* manager_free(Manager *m) {
+         hashmap_free(m->uid_refs);
+         hashmap_free(m->gid_refs);
+ 
+-        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
+-                m->prefix[dt] = mfree(m->prefix[dt]);
++        FOREACH_ARRAY(i, m->prefix, _EXEC_DIRECTORY_TYPE_MAX)
++                free(*i);
++
+         free(m->received_credentials_directory);
+         free(m->received_encrypted_credentials_directory);
+ 
+         free(m->watchdog_pretimeout_governor);
+         free(m->watchdog_pretimeout_governor_overridden);
+ 
+-        m->fw_ctx = fw_ctx_free(m->fw_ctx);
++        fw_ctx_free(m->fw_ctx);
+ 
+ #if BPF_FRAMEWORK
+         bpf_restrict_fs_destroy(m->restrict_fs);
diff --git a/SOURCES/0157-core-manager-drop-duplicate-bus-track-deserializatio.patch b/SOURCES/0157-core-manager-drop-duplicate-bus-track-deserializatio.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d4f34e742719537233b5a11a4acee7f793ef667f
--- /dev/null
+++ b/SOURCES/0157-core-manager-drop-duplicate-bus-track-deserializatio.patch
@@ -0,0 +1,32 @@
+From 860690d1fb190600ec6c8db9052ec4c9fac0666f Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Sat, 11 Jan 2025 18:38:49 +0100
+Subject: [PATCH] core/manager: drop duplicate bus track deserialization
+
+bus_init_api() now does this internally
+(after 8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98).
+
+(cherry picked from commit af0e10354e567bfd0b9521376b2aad55f12a4e3d)
+
+Resolves: RHEL-73780
+---
+ src/core/manager.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/src/core/manager.c b/src/core/manager.c
+index 96fc8941eb..38fe53c319 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -2142,12 +2142,6 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo
+                 /* Connect to the bus if we are good for it */
+                 manager_setup_bus(m);
+ 
+-                /* Now that we are connected to all possible buses, let's deserialize who is tracking us. */
+-                r = bus_track_coldplug(m->api_bus, &m->subscribed, false, m->subscribed_as_strv);
+-                if (r < 0)
+-                        log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m");
+-                m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
+-
+                 r = manager_varlink_init(m);
+                 if (r < 0)
+                         log_warning_errno(r, "Failed to set up Varlink, ignoring: %m");
diff --git a/SOURCES/0158-bus-util-introduce-bus_get_instance_id.patch b/SOURCES/0158-bus-util-introduce-bus_get_instance_id.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1a8bb9fddd39e9b81920351a90542df02551be59
--- /dev/null
+++ b/SOURCES/0158-bus-util-introduce-bus_get_instance_id.patch
@@ -0,0 +1,58 @@
+From b5b61bf95b6d7d7ab1b01eb7e5195eb99b06635d Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Mon, 13 Jan 2025 16:42:34 +0100
+Subject: [PATCH] bus-util: introduce bus_get_instance_id()
+
+(cherry picked from commit a9a8d2e12fe01b928135895f00c5bca465b7d13b)
+
+Resolves: RHEL-73780
+---
+ src/shared/bus-util.c | 23 +++++++++++++++++++++++
+ src/shared/bus-util.h |  2 ++
+ 2 files changed, 25 insertions(+)
+
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index 1fda013697..8eb3fbbf54 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -905,6 +905,29 @@ int bus_query_sender_pidref(
+         return bus_creds_get_pidref(creds, ret);
+ }
+ 
++int bus_get_instance_id(sd_bus *bus, sd_id128_t *ret) {
++        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
++        int r;
++
++        assert(bus);
++        assert(ret);
++
++        r = sd_bus_call_method(bus,
++                               "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetId",
++                               /* error = */ NULL, &reply,
++                               NULL);
++        if (r < 0)
++                return r;
++
++        const char *id;
++
++        r = sd_bus_message_read_basic(reply, 's', &id);
++        if (r < 0)
++                return r;
++
++        return sd_id128_from_string(id, ret);
++}
++
+ static const char* const bus_transport_table[] = {
+         [BUS_TRANSPORT_LOCAL]   = "local",
+         [BUS_TRANSPORT_REMOTE]  = "remote",
+diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
+index e49ba66a10..fe85d815b8 100644
+--- a/src/shared/bus-util.h
++++ b/src/shared/bus-util.h
+@@ -78,4 +78,6 @@ int bus_register_malloc_status(sd_bus *bus, const char *destination);
+ int bus_creds_get_pidref(sd_bus_creds *c, PidRef *ret);
+ int bus_query_sender_pidref(sd_bus_message *m, PidRef *ret);
+ 
++int bus_get_instance_id(sd_bus *bus, sd_id128_t *ret);
++
+ const char* bus_transport_to_string(BusTransport transport) _const_;
diff --git a/SOURCES/0159-core-serialize-API-bus-id-and-validate-before-deseri.patch b/SOURCES/0159-core-serialize-API-bus-id-and-validate-before-deseri.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9bd03a19c978e6850a4de3a735be659d91423276
--- /dev/null
+++ b/SOURCES/0159-core-serialize-API-bus-id-and-validate-before-deseri.patch
@@ -0,0 +1,203 @@
+From 1c3965954b88c0ac3811d29a186382633f336db7 Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Mon, 13 Jan 2025 17:06:35 +0100
+Subject: [PATCH] core: serialize API bus id and validate before deserializing
+ bus tracks
+
+(cherry picked from commit 1446e3c3921067e3a6228a3e172b5dfd95437136)
+
+Resolves: RHEL-73780
+---
+ src/core/dbus.c              | 55 +++++++++++++++++++-----------------
+ src/core/dbus.h              |  1 -
+ src/core/manager-serialize.c |  8 +++++-
+ src/core/manager.h           | 11 +++++---
+ src/shared/bus-util.c        |  2 +-
+ src/shared/bus-util.h        |  2 +-
+ 6 files changed, 45 insertions(+), 34 deletions(-)
+
+diff --git a/src/core/dbus.c b/src/core/dbus.c
+index 58cd1ee175..a2a2611a03 100644
+--- a/src/core/dbus.c
++++ b/src/core/dbus.c
+@@ -5,6 +5,7 @@
+ #include <unistd.h>
+ 
+ #include "sd-bus.h"
++#include "sd-id128.h"
+ 
+ #include "alloc-util.h"
+ #include "bus-common-errors.h"
+@@ -774,6 +775,24 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
+         return 0;
+ }
+ 
++static int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, char * const *l) {
++        int r;
++
++        assert(bus);
++        assert(t);
++
++        if (strv_isempty(l))
++                return 0;
++
++        if (!*t) {
++                r = sd_bus_track_new(bus, t, NULL, NULL);
++                if (r < 0)
++                        return r;
++        }
++
++        return bus_track_add_name_many(*t, l);
++}
++
+ static int bus_setup_api(Manager *m, sd_bus *bus) {
+         char *name;
+         Unit *u;
+@@ -860,10 +879,15 @@ int bus_init_api(Manager *m) {
+         if (r < 0)
+                 return log_error_errno(r, "Failed to set up API bus: %m");
+ 
+-        (void) bus_track_coldplug(bus, &m->subscribed, /* recursive= */ false, m->subscribed_as_strv);
++        r = bus_get_instance_id(bus, &m->bus_id);
++        if (r < 0)
++                log_warning_errno(r, "Failed to query API bus instance ID, not deserializing subscriptions: %m");
++        else if (sd_id128_is_null(m->deserialized_bus_id) || sd_id128_equal(m->bus_id, m->deserialized_bus_id))
++                (void) bus_track_coldplug(bus, &m->subscribed, m->subscribed_as_strv);
+         m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
+-        m->api_bus = TAKE_PTR(bus);
++        m->deserialized_bus_id = SD_ID128_NULL;
+ 
++        m->api_bus = TAKE_PTR(bus);
+         return 0;
+ }
+ 
+@@ -1015,6 +1039,9 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
+                         log_warning_errno(r, "Failed to serialize api subscribers, ignoring: %m");
+                 strv_free_and_replace(m->subscribed_as_strv, subscribed);
+ 
++                m->deserialized_bus_id = m->bus_id;
++                m->bus_id = SD_ID128_NULL;
++
+                 m->subscribed = sd_bus_track_unref(m->subscribed);
+         }
+ 
+@@ -1163,30 +1190,6 @@ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
+         }
+ }
+ 
+-int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, bool recursive, char **l) {
+-        int r;
+-
+-        assert(t);
+-
+-        if (strv_isempty(l))
+-                return 0;
+-
+-        if (!bus)
+-                return 0;
+-
+-        if (!*t) {
+-                r = sd_bus_track_new(bus, t, NULL, NULL);
+-                if (r < 0)
+-                        return r;
+-        }
+-
+-        r = sd_bus_track_set_recursive(*t, recursive);
+-        if (r < 0)
+-                return r;
+-
+-        return bus_track_add_name_many(*t, l);
+-}
+-
+ uint64_t manager_bus_n_queued_write(Manager *m) {
+         uint64_t c = 0;
+         sd_bus *b;
+diff --git a/src/core/dbus.h b/src/core/dbus.h
+index eb1baf6049..0f81102fc1 100644
+--- a/src/core/dbus.h
++++ b/src/core/dbus.h
+@@ -19,7 +19,6 @@ void bus_done(Manager *m);
+ int bus_fdset_add_all(Manager *m, FDSet *fds);
+ 
+ void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+-int bus_track_coldplug(sd_bus *bus, sd_bus_track **t, bool recursive, char **l);
+ 
+ int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
+ 
+diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
+index bc29ac91c6..b225799ed9 100644
+--- a/src/core/manager-serialize.c
++++ b/src/core/manager-serialize.c
+@@ -158,6 +158,7 @@ int manager_serialize(
+         (void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
+         (void) serialize_ratelimit(f, "reload-reexec-ratelimit", &m->reload_reexec_ratelimit);
+ 
++        (void) serialize_id128(f, "bus-id", m->bus_id);
+         bus_track_serialize(m->subscribed, f, "subscribed");
+ 
+         r = dynamic_user_serialize(m, f, fds);
+@@ -491,7 +492,12 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+                         manager_deserialize_gid_refs_one(m, val);
+                 else if ((val = startswith(l, "exec-runtime=")))
+                         (void) exec_shared_runtime_deserialize_one(m, val, fds);
+-                else if ((val = startswith(l, "subscribed="))) {
++                else if ((val = startswith(l, "bus-id="))) {
++
++                        r = sd_id128_from_string(val, &m->deserialized_bus_id);
++                        if (r < 0)
++                                return r;
++                } else if ((val = startswith(l, "subscribed="))) {
+ 
+                         r = strv_extend(&m->subscribed_as_strv, val);
+                         if (r < 0)
+diff --git a/src/core/manager.h b/src/core/manager.h
+index 7016eab2d3..c5bd242968 100644
+--- a/src/core/manager.h
++++ b/src/core/manager.h
+@@ -335,13 +335,16 @@ struct Manager {
+         int private_listen_fd;
+         sd_event_source *private_listen_event_source;
+ 
+-        /* Contains all the clients that are subscribed to signals via
+-        the API bus. Note that private bus connections are always
+-        considered subscribes, since they last for very short only,
+-        and it is much simpler that way. */
++        /* Contains all the clients that are subscribed to signals via the API bus. Note that private bus
++         * connections are always considered subscribes, since they last for very short only, and it is
++         * much simpler that way. */
+         sd_bus_track *subscribed;
+         char **subscribed_as_strv;
+ 
++        /* The bus id of API bus acquired through org.freedesktop.DBus.GetId, which before deserializing
++         * subscriptions we'd use to verify the bus is still the same instance as before. */
++        sd_id128_t bus_id, deserialized_bus_id;
++
+         /* This is used during reloading: before the reload we queue
+          * the reply message here, and afterwards we send it */
+         sd_bus_message *pending_reload_message;
+diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
+index 8eb3fbbf54..9255dbc9b6 100644
+--- a/src/shared/bus-util.c
++++ b/src/shared/bus-util.c
+@@ -686,7 +686,7 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
+         return 1;
+ }
+ 
+-int bus_track_add_name_many(sd_bus_track *t, char **l) {
++int bus_track_add_name_many(sd_bus_track *t, char * const *l) {
+         int r = 0;
+ 
+         assert(t);
+diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
+index fe85d815b8..024b54648d 100644
+--- a/src/shared/bus-util.h
++++ b/src/shared/bus-util.h
+@@ -62,7 +62,7 @@ int bus_log_connect_error(int r, BusTransport transport, RuntimeScope scope);
+ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path);
+ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
+ 
+-int bus_track_add_name_many(sd_bus_track *t, char **l);
++int bus_track_add_name_many(sd_bus_track *t, char * const *l);
+ int bus_track_to_strv(sd_bus_track *t, char ***ret);
+ 
+ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description);
diff --git a/SOURCES/0160-core-manager-restore-bus-track-deserialization-clean.patch b/SOURCES/0160-core-manager-restore-bus-track-deserialization-clean.patch
new file mode 100644
index 0000000000000000000000000000000000000000..25f9afaaa1e398413efb9b12d8d076cc240ee1a1
--- /dev/null
+++ b/SOURCES/0160-core-manager-restore-bus-track-deserialization-clean.patch
@@ -0,0 +1,32 @@
+From ce3d2da71072a3bb4d2ea49159cba8a671bcde90 Mon Sep 17 00:00:00 2001
+From: Mike Yuan <me@yhndnzj.com>
+Date: Mon, 13 Jan 2025 17:30:51 +0100
+Subject: [PATCH] core/manager: restore bus track deserialization cleanup in
+ manager_reload()
+
+There's zero explanation why it got (spuriously) removed in
+8402ca04d1a063c3d8a9e3d5c16df8bb8778ae98...
+
+(cherry picked from commit 34f4b817f67b002eae7e2c09b19bf4b66c4791b6)
+
+Resolves: RHEL-73780
+---
+ src/core/manager.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/core/manager.c b/src/core/manager.c
+index 38fe53c319..cd8cdd87a5 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -3805,6 +3805,11 @@ int manager_reload(Manager *m) {
+         (void) manager_setup_handoff_timestamp_fd(m);
+         (void) manager_setup_pidref_transport_fd(m);
+ 
++        /* Clean up deserialized bus track information. They're never consumed during reload (as opposed to
++         * reexec) since we do not disconnect from the bus. */
++        m->subscribed_as_strv = strv_free(m->subscribed_as_strv);
++        m->deserialized_bus_id = SD_ID128_NULL;
++
+         /* Third, fire things up! */
+         manager_coldplug(m);
+ 
diff --git a/SOURCES/0161-shared-bus-util-add-missing-set.h-include.patch b/SOURCES/0161-shared-bus-util-add-missing-set.h-include.patch
new file mode 100644
index 0000000000000000000000000000000000000000..662894a0e00b0a4e88578c692112be448fe2c7b6
--- /dev/null
+++ b/SOURCES/0161-shared-bus-util-add-missing-set.h-include.patch
@@ -0,0 +1,40 @@
+From 4ffeaa184cc33612e7229328a9272059f4d3ce34 Mon Sep 17 00:00:00 2001
+From: Jan Macku <jamacku@redhat.com>
+Date: Wed, 15 Jan 2025 15:06:46 +0100
+Subject: [PATCH] shared/bus-util: add missing `set.h` include
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+```
+In file included from ../src/shared/bus-get-properties.c:4:
+../src/shared/bus-message-util.h:22:60: error: unknown type name ‘Set’
+   22 | int bus_message_append_string_set(sd_bus_message *m, const Set *s);
+      |                                                            ^~~
+../src/shared/bus-get-properties.c: In function ‘bus_property_get_string_set’:
+../src/shared/bus-get-properties.c:178:9: error: unknown type name ‘Set’
+  178 |         Set **s = ASSERT_PTR(userdata);
+      |         ^~~
+```
+
+follow-up to https://github.com/systemd/systemd/commit/91080bc9733b5b2478bfc0ed58f6a7ae5da7e639
+
+(cherry picked from commit ca97d48bd425685d4ec20397812c3b1b006f5070)
+
+Resolves: RHEL-73780
+---
+ src/shared/bus-message-util.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
+index d4a05f5b9c..02c670695e 100644
+--- a/src/shared/bus-message-util.h
++++ b/src/shared/bus-message-util.h
+@@ -4,6 +4,7 @@
+ #include "sd-bus.h"
+ 
+ #include "in-addr-util.h"
++#include "set.h"
+ #include "socket-netlink.h"
+ 
+ int bus_message_read_id128(sd_bus_message *m, sd_id128_t *ret);
diff --git a/SOURCES/0162-udevadm-test-add-missing-oom-check.patch b/SOURCES/0162-udevadm-test-add-missing-oom-check.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3ca19bf97796a2e6e83da906d1cb6f1ba092d205
--- /dev/null
+++ b/SOURCES/0162-udevadm-test-add-missing-oom-check.patch
@@ -0,0 +1,27 @@
+From 6368b96d9333723c70413511d87630df896ff48c Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sat, 11 Jan 2025 05:27:44 +0900
+Subject: [PATCH] udevadm-test: add missing oom check
+
+(cherry picked from commit 22ca9406c45bb07a02d28da13f6e3909071c9176)
+
+Resolves: RHEL-75774
+---
+ src/udev/udevadm-test.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
+index 748bcaf364..34bc80ff4b 100644
+--- a/src/udev/udevadm-test.c
++++ b/src/udev/udevadm-test.c
+@@ -129,6 +129,10 @@ int test_main(int argc, char *argv[], void *userdata) {
+         device_seal(dev);
+ 
+         event = udev_event_new(dev, NULL, EVENT_UDEVADM_TEST);
++        if (!event) {
++                log_oom();
++                goto out;
++        }
+ 
+         assert_se(sigfillset(&mask) >= 0);
+         assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
diff --git a/SOURCES/0163-udev-rules-replace-type-func-type-func.patch b/SOURCES/0163-udev-rules-replace-type-func-type-func.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c97ed27450ba2d53989cd3ea52b50419840adf5f
--- /dev/null
+++ b/SOURCES/0163-udev-rules-replace-type-func-type-func.patch
@@ -0,0 +1,43 @@
+From 69af60a47f87b85bc4f4bd82f61baa791c631138 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sat, 11 Jan 2025 03:57:34 +0900
+Subject: [PATCH] udev-rules: replace 'type *func()' -> 'type* func()'
+
+(cherry picked from commit a75aeca78c3ad88dc9840f45963f86097d20cf6f)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 85ae1c2163..8d00702595 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -345,7 +345,7 @@ static void log_event_truncated(
+ 
+ /*** Other functions ***/
+ 
+-static UdevRuleToken *udev_rule_token_free(UdevRuleToken *token) {
++static UdevRuleToken* udev_rule_token_free(UdevRuleToken *token) {
+         if (!token)
+                 return NULL;
+ 
+@@ -364,7 +364,7 @@ static void udev_rule_line_clear_tokens(UdevRuleLine *rule_line) {
+                 udev_rule_token_free(i);
+ }
+ 
+-static UdevRuleLine *udev_rule_line_free(UdevRuleLine *rule_line) {
++static UdevRuleLine* udev_rule_line_free(UdevRuleLine *rule_line) {
+         if (!rule_line)
+                 return NULL;
+ 
+@@ -379,7 +379,7 @@ static UdevRuleLine *udev_rule_line_free(UdevRuleLine *rule_line) {
+ 
+ DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRuleLine*, udev_rule_line_free);
+ 
+-static UdevRuleFile *udev_rule_file_free(UdevRuleFile *rule_file) {
++static UdevRuleFile* udev_rule_file_free(UdevRuleFile *rule_file) {
+         if (!rule_file)
+                 return NULL;
+ 
diff --git a/SOURCES/0164-udev-rules-do-not-change-maximum-log-level-when-runn.patch b/SOURCES/0164-udev-rules-do-not-change-maximum-log-level-when-runn.patch
new file mode 100644
index 0000000000000000000000000000000000000000..37f70684842fd73d82f9541064cd32cff938d502
--- /dev/null
+++ b/SOURCES/0164-udev-rules-do-not-change-maximum-log-level-when-runn.patch
@@ -0,0 +1,42 @@
+From be7684d1738dfd62415a5f1f05318a7a4caacd18 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sat, 11 Jan 2025 06:07:55 +0900
+Subject: [PATCH] udev-rules: do not change maximum log level when running in
+ test mode
+
+When udev rules are being tested, log level specified by SYSTEMD_LOG_LEVEL=
+environment variable should be honored, and should not be overridden by
+the rules.
+
+(cherry picked from commit 0363a45898b304b6040cd20e132a3dd946faae50)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 8d00702595..60e1976c5a 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -2394,11 +2394,18 @@ static int udev_rule_apply_token_to_event(
+                 if (level < 0)
+                         level = event->default_log_level;
+ 
+-                log_set_max_level(level);
++                if (event->event_mode == EVENT_UDEV_WORKER)
++                        log_set_max_level(level);
++                else {
++                        _cleanup_free_ char *level_str = NULL;
++                        (void) log_level_to_string_alloc(level, &level_str);
++                        log_event_debug(dev, token, "Running in test mode, skipping changing maximum log level to %s.", strna(level_str));
++                }
+ 
+                 if (level == LOG_DEBUG && !event->log_level_was_debug) {
+                         /* The log level becomes LOG_DEBUG at first time. Let's log basic information. */
+-                        log_device_uevent(dev, "The log level is changed to 'debug' while processing device");
++                        if (event->event_mode == EVENT_UDEV_WORKER)
++                                log_device_uevent(dev, "The log level is changed to 'debug' while processing device");
+                         event->log_level_was_debug = true;
+                 }
+ 
diff --git a/SOURCES/0165-udevadm-test-introduce-v-verbose-option-to-show-verb.patch b/SOURCES/0165-udevadm-test-introduce-v-verbose-option-to-show-verb.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5c101575fdfe0577b200c0b5ed1eb0fa6c5ba3b2
--- /dev/null
+++ b/SOURCES/0165-udevadm-test-introduce-v-verbose-option-to-show-verb.patch
@@ -0,0 +1,209 @@
+From e1941a839bc22c7a84b78a68537b03e4e0b995b3 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Mon, 13 Jan 2025 05:09:15 +0900
+Subject: [PATCH] udevadm-test: introduce -v/--verbose option to show verbose
+ log messages
+
+Currently this does not show any extra log messages. In later commits,
+more verbose log messages will be added.
+
+(cherry picked from commit 8846df63fa30d2fb8749ed1753ddc397b1ff411e)
+
+Resolves: RHEL-75774
+---
+ man/udevadm.xml               |  9 +++++++++
+ shell-completion/bash/udevadm | 13 +++++--------
+ shell-completion/zsh/_udevadm |  1 +
+ src/udev/udev-event.h         |  1 +
+ src/udev/udevadm-test.c       | 25 +++++++++++++++++++------
+ test/units/TEST-17-UDEV.10.sh |  1 +
+ 6 files changed, 36 insertions(+), 14 deletions(-)
+
+diff --git a/man/udevadm.xml b/man/udevadm.xml
+index eb77356d86..caa41bd204 100644
+--- a/man/udevadm.xml
++++ b/man/udevadm.xml
+@@ -869,6 +869,15 @@
+             <xi:include href="version-info.xml" xpointer="v209"/>
+           </listitem>
+         </varlistentry>
++        <varlistentry>
++          <term><option>-v</option></term>
++          <term><option>--verbose</option></term>
++          <listitem>
++            <para>Shows verbose logs in processing udev rules.</para>
++
++            <xi:include href="version-info.xml" xpointer="v258"/>
++          </listitem>
++        </varlistentry>
+ 
+         <xi:include href="standard-options.xml" xpointer="help" />
+       </variablelist>
+diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm
+index 3842d722e7..af3a0b9058 100644
+--- a/shell-completion/bash/udevadm
++++ b/shell-completion/bash/udevadm
+@@ -69,7 +69,8 @@ _udevadm() {
+         [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout'
+         [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
+         [MONITOR_ARG]='-s --subsystem-match -t --tag-match'
+-        [TEST]='-a --action -N --resolve-names'
++        [TEST_STANDALONE]='-v --verbose'
++        [TEST_ARG]='-a --action -N --resolve-names'
+         [TEST_BUILTIN]='-a --action'
+         [VERIFY]='-N --resolve-names --root --no-summary --no-style'
+         [WAIT]='-t --timeout --initialized=no --removed --settle'
+@@ -216,7 +217,7 @@ _udevadm() {
+             ;;
+ 
+         'test')
+-            if __contains_word "$prev" ${OPTS[TEST]}; then
++            if __contains_word "$prev" ${OPTS[TEST_ARG]}; then
+                 case $prev in
+                     -a|--action)
+                         comps=$( udevadm test --action help )
+@@ -225,12 +226,8 @@ _udevadm() {
+                         comps='early late never'
+                         ;;
+                 esac
+-                COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+-                return 0
+-            fi
+-
+-            if [[ $cur = -* ]]; then
+-                comps="${OPTS[COMMON]} ${OPTS[TEST]}"
++            elif [[ $cur = -* ]]; then
++                comps="${OPTS[COMMON]} ${OPTS[TEST_ARG]} ${OPTS[TEST_STANDALONE]}"
+             else
+                 comps=$( __get_all_devices )
+                 local IFS=$'\n'
+diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm
+index 9ff87d8312..5c3d3c93ef 100644
+--- a/shell-completion/zsh/_udevadm
++++ b/shell-completion/zsh/_udevadm
+@@ -87,6 +87,7 @@ _udevadm_test(){
+         '(-)'{-V,--version}'[Show package version]' \
+         '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
+         '--subsystem=[The subsystem string.]' \
++        '(-v --verbose)'{-v,--verbose}'[Show verbose logs.]' \
+         '*::devpath:_files -P /sys/ -W /sys'
+ }
+ 
+diff --git a/src/udev/udev-event.h b/src/udev/udev-event.h
+index 3dc89365bb..22388fe904 100644
+--- a/src/udev/udev-event.h
++++ b/src/udev/udev-event.h
+@@ -53,6 +53,7 @@ typedef struct UdevEvent {
+         bool name_final;
+         bool devlink_final;
+         bool run_final;
++        bool trace;
+         bool log_level_was_debug;
+         int default_log_level;
+         EventMode event_mode;
+diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
+index 34bc80ff4b..199ee0c18d 100644
+--- a/src/udev/udevadm-test.c
++++ b/src/udev/udevadm-test.c
+@@ -33,6 +33,7 @@
+ static sd_device_action_t arg_action = SD_DEVICE_ADD;
+ static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
+ static const char *arg_syspath = NULL;
++static bool arg_verbose = false;
+ 
+ static int help(void) {
+ 
+@@ -41,7 +42,8 @@ static int help(void) {
+                "  -h --help                            Show this help\n"
+                "  -V --version                         Show package version\n"
+                "  -a --action=ACTION|help              Set action string\n"
+-               "  -N --resolve-names=early|late|never  When to resolve names\n",
++               "  -N --resolve-names=early|late|never  When to resolve names\n"
++               "  -v --verbose                         Show verbose logs\n",
+                program_invocation_short_name);
+ 
+         return 0;
+@@ -51,6 +53,7 @@ static int parse_argv(int argc, char *argv[]) {
+         static const struct option options[] = {
+                 { "action",        required_argument, NULL, 'a' },
+                 { "resolve-names", required_argument, NULL, 'N' },
++                { "verbose",       no_argument,       NULL, 'v' },
+                 { "version",       no_argument,       NULL, 'V' },
+                 { "help",          no_argument,       NULL, 'h' },
+                 {}
+@@ -58,7 +61,7 @@ static int parse_argv(int argc, char *argv[]) {
+ 
+         int r, c;
+ 
+-        while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0)
++        while ((c = getopt_long(argc, argv, "a:N:vVh", options, NULL)) >= 0)
+                 switch (c) {
+                 case 'a':
+                         r = parse_device_action(optarg, &arg_action);
+@@ -73,6 +76,9 @@ static int parse_argv(int argc, char *argv[]) {
+                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                        "--resolve-names= must be early, late or never");
+                         break;
++                case 'v':
++                        arg_verbose = true;
++                        break;
+                 case 'V':
+                         return print_version();
+                 case 'h':
+@@ -104,20 +110,23 @@ int test_main(int argc, char *argv[], void *userdata) {
+         if (r <= 0)
+                 return r;
+ 
+-        printf("This program is for debugging only, it does not run any program\n"
+-               "specified by a RUN key. It may show incorrect results, because\n"
+-               "some values may be different, or not available at a simulation run.\n"
+-               "\n");
++        puts("This program is for debugging only, it does not run any program\n"
++             "specified by a RUN key. It may show incorrect results, because\n"
++             "some values may be different, or not available at a simulation run.");
+ 
+         assert_se(sigprocmask(SIG_SETMASK, NULL, &sigmask_orig) >= 0);
+ 
++        puts("\nLoading builtins...");
+         udev_builtin_init();
++        puts("Loading builtins done.");
+ 
++        puts("\nLoading udev rules files...");
+         r = udev_rules_load(&rules, arg_resolve_name_timing);
+         if (r < 0) {
+                 log_error_errno(r, "Failed to read udev rules: %m");
+                 goto out;
+         }
++        puts("Loading udev rules files done.");
+ 
+         r = find_device_with_action(arg_syspath, arg_action, &dev);
+         if (r < 0) {
+@@ -133,12 +142,16 @@ int test_main(int argc, char *argv[], void *userdata) {
+                 log_oom();
+                 goto out;
+         }
++        event->trace = arg_verbose;
+ 
+         assert_se(sigfillset(&mask) >= 0);
+         assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
+ 
++        printf("\nProcessing udev rules%s...\n", arg_verbose ? "" : " (verbose logs can be shown by -v/--verbose)");
+         udev_event_execute_rules(event, rules);
++        puts("Processing udev rules done.");
+ 
++        puts("");
+         printf("%sProperties:%s\n", ansi_highlight(), ansi_normal());
+         FOREACH_DEVICE_PROPERTY(dev, key, value)
+                 printf("  %s=%s\n", key, value);
+diff --git a/test/units/TEST-17-UDEV.10.sh b/test/units/TEST-17-UDEV.10.sh
+index 7ca05f5287..6820e76679 100755
+--- a/test/units/TEST-17-UDEV.10.sh
++++ b/test/units/TEST-17-UDEV.10.sh
+@@ -130,6 +130,7 @@ udevadm test -N early /sys/class/net/$netdev
+ udevadm test -N late /sys/class/net/$netdev
+ udevadm test --resolve-names never /sys/class/net/$netdev
+ (! udevadm test -N hello /sys/class/net/$netdev)
++udevadm test -v /sys/class/net/$netdev
+ udevadm test -h
+ 
+ # udevadm test-builtin path_id "$loopdev"
diff --git a/SOURCES/0166-udev-rules-show-original-token-string-in-log_event_e.patch b/SOURCES/0166-udev-rules-show-original-token-string-in-log_event_e.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ba32f18506b2b618a73f13d1573f10fcfc75c3b0
--- /dev/null
+++ b/SOURCES/0166-udev-rules-show-original-token-string-in-log_event_e.patch
@@ -0,0 +1,1343 @@
+From 8ec8cd19867cacbf653c05caed80cf97569cbda4 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Mon, 13 Jan 2025 05:12:40 +0900
+Subject: [PATCH] udev-rules: show original token string in log_event_error()
+ and friends
+
+This also makes
+- make many loggers refactored,
+- log_event_error() and friends UdevEvent object,
+- introduces log_event_trace().
+
+No functional change, just refactoring and preparation for later
+commits.
+
+(cherry picked from commit 6809bae6434707dbdbea7a7500612ebd8958f3cd)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 458 +++++++++++++++++++++++-------------------
+ 1 file changed, 250 insertions(+), 208 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 60e1976c5a..c33d17aa47 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -165,12 +165,15 @@ struct UdevRuleToken {
+         const char *value;
+         void *data;
+ 
++        const char *token_str; /* original token string for logging */
++
+         UdevRuleLine *rule_line;
+         LIST_FIELDS(UdevRuleToken, tokens);
+ };
+ 
+ struct UdevRuleLine {
+         char *line;
++        char *line_for_logging;
+         unsigned line_number;
+         UdevRuleLineType type;
+ 
+@@ -203,82 +206,109 @@ struct UdevRules {
+ #define LINE_GET_RULES(line)                                            \
+         ASSERT_PTR(ASSERT_PTR(ASSERT_PTR(line)->rule_file)->rules)
+ 
++static bool token_is_for_parents(UdevRuleToken *token) {
++        return token->type >= TK_M_PARENTS_KERNEL && token->type <= TK_M_PARENTS_TAG;
++}
++
+ /*** Logging helpers ***/
+ 
+-#define log_udev_rule_internal(device, file, line_nr, level, error, fmt, ...) \
++#define _log_udev_rule_file_full(device, device_u, file, file_u, line_nr, level, level_u, error, fmt, ...) \
+         ({                                                              \
+-                int _lv = (level);                                      \
+-                sd_device *_dev = (device);                             \
+-                UdevRuleFile *_f = (file);                              \
+-                const char *_n = _f ? _f->filename : NULL;              \
++                int level_u = (level);                                  \
++                sd_device *device_u = (device);                         \
++                UdevRuleFile *file_u = (file);                          \
+                                                                         \
+-                if (!_dev && _f)                                        \
+-                        _f->issues |= (1U << _lv);                      \
++                if (!device_u && file_u)                                \
++                        file_u->issues |= (1U << level_u);              \
+                                                                         \
+                 log_device_full_errno_zerook(                           \
+-                                _dev, _lv, error, "%s:%u " fmt,         \
+-                                strna(_n), line_nr,                     \
+-                                ##__VA_ARGS__);                         \
++                                device_u, level_u, error, "%s:%u " fmt, \
++                                strna(file_u ? file_u->filename : NULL), \
++                                line_nr,  ##__VA_ARGS__);               \
+         })
+ 
+-/* Mainly used when applying tokens to the event device. */
+-#define log_event_full_errno_zerook(device, token, ...)                 \
++#define log_udev_rule_file_full(device, file, line_nr, level, error, fmt, ...) \
++        _log_udev_rule_file_full(device, UNIQ_T(d, UNIQ), file, UNIQ_T(f, UNIQ), line_nr, level, UNIQ_T(l, UNIQ), error, fmt, ##__VA_ARGS__)
++
++#define _log_udev_rule_line_full(device, line, line_u, ...)             \
+         ({                                                              \
+-                UdevRuleToken *_t = (token);                            \
+-                UdevRuleLine *_l = _t ? _t->rule_line : NULL;           \
++                UdevRuleLine *line_u = ASSERT_PTR(line);                \
+                                                                         \
+-                log_udev_rule_internal(                                 \
++                log_udev_rule_file_full(                                \
+                                 device,                                 \
+-                                _l ? _l->rule_file : NULL,              \
+-                                _l ? _l->line_number : 0,               \
++                                line_u->rule_file, line_u->line_number, \
+                                 __VA_ARGS__);                           \
+         })
+ 
+-#define log_event_full_errno(device, token, level, error, ...)          \
++#define log_udev_rule_line_full(device, line, ...)     \
++        _log_udev_rule_line_full(device, line, UNIQ_T(l, UNIQ), __VA_ARGS__)
++
++/* Mainly used when applying tokens to the event device. */
++#define _log_event_full_errno_zerook(event, event_u, token, token_u, level, error, fmt, ...) \
++        ({                                                              \
++                UdevEvent *event_u = ASSERT_PTR(event);                 \
++                UdevRuleToken *token_u = ASSERT_PTR(token);             \
++                                                                        \
++                log_udev_rule_line_full(                                \
++                                token_is_for_parents(token_u) ? event_u->dev_parent : event_u->dev, \
++                                token_u->rule_line,                     \
++                                level, error, "%s: " fmt,               \
++                                token_u->token_str, ##__VA_ARGS__);     \
++        })
++
++#define log_event_full_errno_zerook(event, token, ...)  \
++        _log_event_full_errno_zerook(event, UNIQ_T(e, UNIQ), token, UNIQ_T(t, UNIQ), __VA_ARGS__)
++
++#define _log_event_full_errno(event, token, level, error, error_u, ...) \
+         ({                                                              \
+-                int _error = (error);                                   \
+-                ASSERT_NON_ZERO(_error);                                \
++                int error_u = (error);                                  \
++                ASSERT_NON_ZERO(error_u);                               \
+                 log_event_full_errno_zerook(                            \
+-                        device, token, level, _error, ##__VA_ARGS__);   \
++                                event, token, level, error_u,           \
++                                __VA_ARGS__);                           \
+         })
+ 
+-#define log_event_full(device, token, level, ...)   (void) log_event_full_errno_zerook(device, token, level, 0, __VA_ARGS__)
++#define log_event_full_errno(event, token, level, error, ...)   \
++        _log_event_full_errno(event, token, level, error, UNIQ_T(e, UNIQ), __VA_ARGS__)
+ 
+-#define log_event_debug(device, token, ...)   log_event_full(device, token, LOG_DEBUG, __VA_ARGS__)
+-#define log_event_info(device, token, ...)    log_event_full(device, token, LOG_INFO, __VA_ARGS__)
+-#define log_event_notice(device, token, ...)  log_event_full(device, token, LOG_NOTICE, __VA_ARGS__)
+-#define log_event_warning(device, token, ...) log_event_full(device, token, LOG_WARNING, __VA_ARGS__)
+-#define log_event_error(device, token, ...)   log_event_full(device, token, LOG_ERR, __VA_ARGS__)
++#define log_event_full(event, token, level, ...)   (void) log_event_full_errno_zerook(event, token, level, 0, __VA_ARGS__)
+ 
+-#define log_event_debug_errno(device, token, error, ...)   log_event_full_errno(device, token, LOG_DEBUG, error, __VA_ARGS__)
+-#define log_event_info_errno(device, token, error, ...)    log_event_full_errno(device, token, LOG_INFO, error, __VA_ARGS__)
+-#define log_event_notice_errno(device, token, error, ...)  log_event_full_errno(device, token, LOG_NOTICE, error, __VA_ARGS__)
+-#define log_event_warning_errno(device, token, error, ...) log_event_full_errno(device, token, LOG_WARNING, error, __VA_ARGS__)
+-#define log_event_error_errno(device, token, error, ...)   log_event_full_errno(device, token, LOG_ERR, error, __VA_ARGS__)
++#define log_event_debug(event, token, ...)   log_event_full(event, token, LOG_DEBUG, __VA_ARGS__)
++#define log_event_info(event, token, ...)    log_event_full(event, token, LOG_INFO, __VA_ARGS__)
++#define log_event_notice(event, token, ...)  log_event_full(event, token, LOG_NOTICE, __VA_ARGS__)
++#define log_event_warning(event, token, ...) log_event_full(event, token, LOG_WARNING, __VA_ARGS__)
++#define log_event_error(event, token, ...)   log_event_full(event, token, LOG_ERR, __VA_ARGS__)
++
++#define log_event_debug_errno(event, token, error, ...)   log_event_full_errno(event, token, LOG_DEBUG, error, __VA_ARGS__)
++#define log_event_info_errno(event, token, error, ...)    log_event_full_errno(event, token, LOG_INFO, error, __VA_ARGS__)
++#define log_event_notice_errno(event, token, error, ...)  log_event_full_errno(event, token, LOG_NOTICE, error, __VA_ARGS__)
++#define log_event_warning_errno(event, token, error, ...) log_event_full_errno(event, token, LOG_WARNING, error, __VA_ARGS__)
++#define log_event_error_errno(event, token, error, ...)   log_event_full_errno(event, token, LOG_ERR, error, __VA_ARGS__)
++
++#define _log_event_trace(event, event_u, ...)                           \
++        ({                                                              \
++                UdevEvent *event_u = ASSERT_PTR(event);                 \
++                                                                        \
++                event_u->trace ?                                        \
++                        log_event_debug(event_u, __VA_ARGS__) :         \
++                        (void) 0;                                       \
++        })
++
++#define log_event_trace(event, ...)                                     \
++        _log_event_trace(event, UNIQ_T(e, UNIQ), __VA_ARGS__)
+ 
+ /* Mainly used when parsing .rules files. */
+-#define log_file_full_errno_zerook(...) \
+-        log_udev_rule_internal(NULL, __VA_ARGS__)
++#define log_file_full_errno_zerook(...)                                 \
++        log_udev_rule_file_full(NULL, __VA_ARGS__)
+ 
+ #define log_file_error(file, line_nr, ...)                              \
+         log_file_full_errno_zerook(file, line_nr, LOG_ERR, 0, __VA_ARGS__)
+ 
+-#define log_line_full_errno_zerook(line, ...)                           \
+-        ({                                                              \
+-                UdevRuleLine *_l = (line);                              \
+-                log_file_full_errno_zerook(                             \
+-                                _l ? _l->rule_file : NULL,              \
+-                                _l ? _l->line_number : 0,               \
+-                                __VA_ARGS__);                           \
+-        })
++#define log_line_full_errno_zerook(...)                                 \
++        log_udev_rule_line_full(NULL, __VA_ARGS__)
+ 
+ #define log_line_full_errno(line, level, error, ...)                    \
+-        ({                                                              \
+-                int _error = (error);                                   \
+-                ASSERT_NON_ZERO(_error);                                \
+-                log_line_full_errno_zerook(                             \
+-                        line, level, _error, ##__VA_ARGS__);            \
+-        })
++        log_udev_rule_line_full(NULL, line, level, error, __VA_ARGS__)
+ 
+ #define log_line_full(line, level, ...)  (void) log_line_full_errno_zerook(line, level, 0, __VA_ARGS__)
+ 
+@@ -316,15 +346,13 @@ static void log_unknown_owner(sd_device *dev, UdevRuleLine *line, int error, con
+         ASSERT_NON_ZERO(error);
+ 
+         if (IN_SET(abs(error), ENOENT, ESRCH))
+-                log_udev_rule_internal(dev, line->rule_file, line->line_number, LOG_ERR, error,
+-                                       "Unknown %s '%s', ignoring.", entity, name);
++                log_udev_rule_line_full(dev, line, LOG_ERR, error, "Unknown %s '%s', ignoring.", entity, name);
+         else
+-                log_udev_rule_internal(dev, line->rule_file, line->line_number, LOG_ERR, error,
+-                                       "Failed to resolve %s '%s', ignoring: %m", entity, name);
++                log_udev_rule_line_full(dev, line, LOG_ERR, error, "Failed to resolve %s '%s', ignoring: %m", entity, name);
+ }
+ 
+ static void log_event_truncated(
+-                sd_device *dev,
++                UdevEvent *event,
+                 UdevRuleToken *token,
+                 const char *what,
+                 const char *format,
+@@ -332,12 +360,12 @@ static void log_event_truncated(
+                 bool is_match) {
+ 
+         if (is_match)
+-                log_event_debug(dev, token,
++                log_event_debug(event, token,
+                                 "The %s is truncated while substituting into '%s', "
+                                 "assuming the %s key does not match.",
+                                 what, format, key);
+         else
+-                log_event_warning(dev, token,
++                log_event_warning(event, token,
+                                   "The %s is truncated while substituting into '%s', "
+                                   "refusing to apply the %s key.",
+                                   what, format, key);
+@@ -374,6 +402,7 @@ static UdevRuleLine* udev_rule_line_free(UdevRuleLine *rule_line) {
+                 LIST_REMOVE(rule_lines, rule_line->rule_file->rule_lines, rule_line);
+ 
+         free(rule_line->line);
++        free(rule_line->line_for_logging);
+         return mfree(rule_line);
+ }
+ 
+@@ -494,7 +523,15 @@ static bool type_has_nulstr_value(UdevRuleTokenType type) {
+         return type < TK_M_TEST || type == TK_M_RESULT;
+ }
+ 
+-static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data, bool is_case_insensitive) {
++static int rule_line_add_token(
++                UdevRuleLine *rule_line,
++                UdevRuleTokenType type,
++                UdevRuleOperatorType op,
++                char *value,
++                void *data,
++                bool is_case_insensitive,
++                const char *token_str) {
++
+         _cleanup_(udev_rule_token_freep) UdevRuleToken *token = NULL;
+         UdevRuleMatchType match_type = _MATCH_TYPE_INVALID;
+         UdevRuleSubstituteType subst_type = _SUBST_TYPE_INVALID;
+@@ -582,6 +619,7 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
+                 .data = data,
+                 .match_type = match_type,
+                 .attr_subst_type = subst_type,
++                .token_str = token_str,
+                 .rule_line = rule_line,
+         };
+ 
+@@ -629,7 +667,15 @@ static int check_attr_format_and_warn(UdevRuleLine *line, const char *key, const
+         return 0;
+ }
+ 
+-static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value, bool is_case_insensitive) {
++static int parse_token(
++                UdevRuleLine *rule_line,
++                const char *key,
++                char *attr,
++                UdevRuleOperatorType op,
++                char *value,
++                bool is_case_insensitive,
++                const char *token_str) {
++
+         ResolveNameTiming resolve_name_timing = LINE_GET_RULES(rule_line)->resolve_name_timing;
+         bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
+         int r;
+@@ -647,29 +693,29 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "DEVPATH")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "KERNEL")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "SYMLINK")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match) {
+                         check_value_format_and_warn(rule_line, key, value, false);
+-                        r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 } else
+-                        r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL, is_case_insensitive);
++                        r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "NAME")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+@@ -689,9 +735,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                                                             "Ignoring NAME=\"\", as udev will not delete any network interfaces.");
+                         check_value_format_and_warn(rule_line, key, value, false);
+ 
+-                        r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 } else
+-                        r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL, is_case_insensitive);
++                        r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "ENV")) {
+                 if (isempty(attr))
+                         return log_line_invalid_attr(rule_line, key);
+@@ -709,15 +755,15 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+ 
+                         check_value_format_and_warn(rule_line, key, value, false);
+ 
+-                        r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr, /* is_case_insensitive = */ false, token_str);
+                 } else
+-                        r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive);
++                        r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive, token_str);
+         } else if (streq(key, "CONST")) {
+                 if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt"))
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+-                r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr, is_case_insensitive, token_str);
+         } else if (streq(key, "TAG")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+@@ -729,9 +775,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (!is_match) {
+                         check_value_format_and_warn(rule_line, key, value, true);
+ 
+-                        r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 } else
+-                        r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL, is_case_insensitive);
++                        r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "SUBSYSTEM")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+@@ -741,14 +787,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (STR_IN_SET(value, "bus", "class"))
+                         log_line_warning(rule_line, "\"%s\" must be specified as \"subsystem\".", value);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "DRIVER")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "ATTR")) {
+                 r = check_attr_format_and_warn(rule_line, key, attr);
+                 if (r < 0)
+@@ -762,9 +808,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+ 
+                 if (!is_match) {
+                         check_value_format_and_warn(rule_line, key, value, false);
+-                        r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr, /* is_case_insensitive = */ false, token_str);
+                 } else
+-                        r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr, is_case_insensitive);
++                        r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr, is_case_insensitive, token_str);
+         } else if (streq(key, "SYSCTL")) {
+                 r = check_attr_format_and_warn(rule_line, key, attr);
+                 if (r < 0)
+@@ -778,30 +824,30 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+ 
+                 if (!is_match) {
+                         check_value_format_and_warn(rule_line, key, value, false);
+-                        r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr, /* is_case_insensitive = */ false, token_str);
+                 } else
+-                        r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr, is_case_insensitive);
++                        r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr, is_case_insensitive, token_str);
+         } else if (streq(key, "KERNELS")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "SUBSYSTEMS")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "DRIVERS")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "ATTRS")) {
+                 r = check_attr_format_and_warn(rule_line, key, attr);
+                 if (r < 0)
+@@ -814,14 +860,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (strstr(attr, "../"))
+                         log_line_warning(rule_line, "Direct reference to parent sysfs directory, may break in future kernels.");
+ 
+-                r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr, is_case_insensitive, token_str);
+         } else if (streq(key, "TAGS")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "TEST")) {
+                 mode_t mode = MODE_INVALID;
+ 
+@@ -836,7 +882,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (is_case_insensitive)
+                         return log_line_invalid_prefix(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode), is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode), is_case_insensitive, token_str);
+         } else if (streq(key, "PROGRAM")) {
+                 if (attr)
+                         return log_line_invalid_attr(rule_line, key);
+@@ -848,7 +894,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (is_case_insensitive)
+                         return log_line_invalid_prefix(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL, /* is_case_insensitive */ false);
++                r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL, /* is_case_insensitive */ false, token_str);
+         } else if (streq(key, "IMPORT")) {
+                 if (isempty(attr))
+                         return log_line_invalid_attr(rule_line, key);
+@@ -861,16 +907,16 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                         return log_line_invalid_prefix(rule_line, key);
+ 
+                 if (streq(attr, "file"))
+-                        r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(attr, "program")) {
+                         UdevBuiltinCommand cmd;
+ 
+                         cmd = udev_builtin_lookup(value);
+                         if (cmd >= 0) {
+                                 log_line_debug(rule_line, "Found builtin command '%s' for %s, replacing attribute.", value, key);
+-                                r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
++                                r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false, token_str);
+                         } else
+-                                r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
++                                r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 } else if (streq(attr, "builtin")) {
+                         UdevBuiltinCommand cmd;
+ 
+@@ -878,13 +924,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                         if (cmd < 0)
+                                 return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
+                                                             "Unknown builtin command: %s", value);
+-                        r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false, token_str);
+                 } else if (streq(attr, "db"))
+-                        r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(attr, "cmdline"))
+-                        r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(attr, "parent"))
+-                        r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 else
+                         return log_line_invalid_attr(rule_line, key);
+         } else if (streq(key, "RESULT")) {
+@@ -893,7 +939,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 if (!is_match)
+                         return log_line_invalid_op(rule_line, key);
+ 
+-                r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL, is_case_insensitive);
++                r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL, is_case_insensitive, token_str);
+         } else if (streq(key, "OPTIONS")) {
+                 char *tmp;
+ 
+@@ -905,24 +951,24 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                         op = OP_ASSIGN;
+ 
+                 if (streq(value, "string_escape=none"))
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(value, "string_escape=replace"))
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(value, "db_persist"))
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(value, "watch"))
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1), /* is_case_insensitive = */ false, token_str);
+                 else if (streq(value, "nowatch"))
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0), /* is_case_insensitive = */ false, token_str);
+                 else if ((tmp = startswith(value, "static_node=")))
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if ((tmp = startswith(value, "link_priority="))) {
+                         int prio;
+ 
+                         r = safe_atoi(tmp, &prio);
+                         if (r < 0)
+                                 return log_line_error_errno(rule_line, r, "Failed to parse link priority '%s': %m", tmp);
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio), /* is_case_insensitive = */ false, token_str);
+                 } else if ((tmp = startswith(value, "log_level="))) {
+                         int level;
+ 
+@@ -933,7 +979,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                                 if (level < 0)
+                                         return log_line_error_errno(rule_line, level, "Failed to parse log level '%s': %m", tmp);
+                         }
+-                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level), /* is_case_insensitive = */ false, token_str);
+                 } else {
+                         log_line_warning(rule_line, "Invalid value for OPTIONS key, ignoring: '%s'", value);
+                         return 0;
+@@ -951,17 +997,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 }
+ 
+                 if (parse_uid(value, &uid) >= 0)
+-                        r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false, token_str);
+                 else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
+                            rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
+                         r = rule_resolve_user(rule_line, value, &uid);
+                         if (r < 0)
+                                 return log_line_error_errno(rule_line, r, "Failed to resolve user name '%s': %m", value);
+ 
+-                        r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false, token_str);
+                 } else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
+                         check_value_format_and_warn(rule_line, key, value, true);
+-                        r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 } else {
+                         log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=\"%s\".", key, value);
+                         return 0;
+@@ -979,17 +1025,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 }
+ 
+                 if (parse_gid(value, &gid) >= 0)
+-                        r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false, token_str);
+                 else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
+                            rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
+                         r = rule_resolve_group(rule_line, value, &gid);
+                         if (r < 0)
+                                 return log_line_error_errno(rule_line, r, "Failed to resolve group name '%s': %m", value);
+ 
+-                        r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false, token_str);
+                 } else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
+                         check_value_format_and_warn(rule_line, key, value, true);
+-                        r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 } else {
+                         log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\".", value);
+                         return 0;
+@@ -1007,10 +1053,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                 }
+ 
+                 if (parse_mode(value, &mode) >= 0)
+-                        r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode), /* is_case_insensitive = */ false, token_str);
+                 else {
+                         check_value_format_and_warn(rule_line, key, value, true);
+-                        r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 }
+         } else if (streq(key, "SECLABEL")) {
+                 if (isempty(attr))
+@@ -1023,13 +1069,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                         op = OP_ASSIGN;
+                 }
+ 
+-                r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr, /* is_case_insensitive = */ false);
++                r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr, /* is_case_insensitive = */ false, token_str);
+         } else if (streq(key, "RUN")) {
+                 if (is_match || op == OP_REMOVE)
+                         return log_line_invalid_op(rule_line, key);
+                 check_value_format_and_warn(rule_line, key, value, true);
+                 if (!attr || streq(attr, "program"))
+-                        r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false, token_str);
+                 else if (streq(attr, "builtin")) {
+                         UdevBuiltinCommand cmd;
+ 
+@@ -1037,7 +1083,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
+                         if (cmd < 0)
+                                 return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
+                                                              "Unknown builtin command '%s', ignoring.", value);
+-                        r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
++                        r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false, token_str);
+                 } else
+                         return log_line_invalid_attr(rule_line, key);
+         } else if (streq(key, "GOTO")) {
+@@ -1297,32 +1343,30 @@ static void sort_tokens(UdevRuleLine *rule_line) {
+         }
+ }
+ 
+-static int rule_add_line(UdevRuleFile *rule_file, const char *line_str, unsigned line_nr, bool extra_checks) {
++static int rule_add_line(UdevRuleFile *rule_file, const char *line, unsigned line_nr, bool extra_checks) {
+         _cleanup_(udev_rule_line_freep) UdevRuleLine *rule_line = NULL;
+-        _cleanup_free_ char *line = NULL;
+         char *p;
+         int r;
+ 
+         assert(rule_file);
+-        assert(line_str);
++        assert(line);
+ 
+-        if (isempty(line_str))
++        if (isempty(line))
+                 return 0;
+ 
+-        line = strdup(line_str);
+-        if (!line)
+-                return log_oom();
+-
+         rule_line = new(UdevRuleLine, 1);
+         if (!rule_line)
+                 return log_oom();
+ 
+         *rule_line = (UdevRuleLine) {
+-                .line = TAKE_PTR(line),
++                .line = strdup(line),
++                .line_for_logging = strdup(line),
+                 .line_number = line_nr,
+-                .rule_file = rule_file,
+         };
++        if (!rule_line->line || !rule_line->line_for_logging)
++                return log_oom();
+ 
++        rule_line->rule_file = rule_file;
+         LIST_APPEND(rule_lines, rule_file->rule_lines, rule_line);
+ 
+         for (p = rule_line->line; !isempty(p); ) {
+@@ -1339,7 +1383,9 @@ static int rule_add_line(UdevRuleFile *rule_file, const char *line_str, unsigned
+                 if (r == 0)
+                         break;
+ 
+-                r = parse_token(rule_line, key, attr, op, value, is_case_insensitive);
++                char *token_str = rule_line->line_for_logging + (key - rule_line->line);
++                token_str[p - key] = '\0';
++                r = parse_token(rule_line, key, attr, op, value, is_case_insensitive, token_str);
+                 if (r < 0)
+                         return r;
+         }
+@@ -1801,7 +1847,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
+         case SUBST_TYPE_FORMAT:
+                 (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "sysfs attribute name", name,
++                        log_event_truncated(event, token, "sysfs attribute name", name,
+                                             token->type == TK_M_ATTR ? "ATTR" : "ATTRS", /* is_match = */ true);
+                         return false;
+                 }
+@@ -1986,7 +2032,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = sd_device_get_action(dev, &a);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to get uevent action type: %m");
++                        return log_event_error_errno(event, token, r, "Failed to get uevent action type: %m");
+ 
+                 return token_match_string(token, device_action_to_string(a));
+         }
+@@ -1995,7 +2041,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = sd_device_get_devpath(dev, &val);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to get devpath: %m");
++                        return log_event_error_errno(event, token, r, "Failed to get devpath: %m");
+ 
+                 return token_match_string(token, val);
+         }
+@@ -2005,7 +2051,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = sd_device_get_sysname(dev, &val);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to get sysname: %m");
++                        return log_event_error_errno(event, token, r, "Failed to get sysname: %m");
+ 
+                 return token_match_string(token, val);
+         }
+@@ -2050,7 +2096,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOENT)
+                         val = NULL;
+                 else if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to get subsystem: %m");
++                        return log_event_error_errno(event, token, r, "Failed to get subsystem: %m");
+ 
+                 return token_match_string(token, val);
+         }
+@@ -2062,7 +2108,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOENT)
+                         val = NULL;
+                 else if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to get driver: %m");
++                        return log_event_error_errno(event, token, r, "Failed to get driver: %m");
+ 
+                 return token_match_string(token, val);
+         }
+@@ -2076,13 +2122,13 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ true);
++                        log_event_truncated(event, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ true);
+                         return false;
+                 }
+ 
+                 r = sysctl_read(sysctl_normalize(buf), &value);
+                 if (r < 0 && r != -ENOENT)
+-                        return log_event_error_errno(dev, token, r, "Failed to read sysctl '%s': %m", buf);
++                        return log_event_error_errno(event, token, r, "Failed to read sysctl '%s': %m", buf);
+ 
+                 return token_match_string(token, strstrip(value));
+         }
+@@ -2094,7 +2140,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "file name", token->value, "TEST", /* is_match = */ true);
++                        log_event_truncated(event, token, "file name", token->value, "TEST", /* is_match = */ true);
+                         return false;
+                 }
+ 
+@@ -2105,7 +2151,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = sd_device_get_syspath(dev, &val);
+                         if (r < 0)
+-                                return log_event_error_errno(dev, token, r, "Failed to get syspath: %m");
++                                return log_event_error_errno(event, token, r, "Failed to get syspath: %m");
+ 
+                         strscpy_full(tmp, sizeof(tmp), buf, &truncated);
+                         assert(!truncated);
+@@ -2118,7 +2164,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOENT)
+                         return token->op == OP_NOMATCH;
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to test for the existence of '%s': %m", buf);
++                        return log_event_error_errno(event, token, r, "Failed to test for the existence of '%s': %m", buf);
+ 
+                 if (stat(buf, &statbuf) < 0)
+                         return token->op == OP_NOMATCH;
+@@ -2137,25 +2183,25 @@ static int udev_rule_apply_token_to_event(
+                 event->program_result = mfree(event->program_result);
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "command", token->value, "PROGRAM", /* is_match = */ true);
++                        log_event_truncated(event, token, "command", token->value, "PROGRAM", /* is_match = */ true);
+                         return false;
+                 }
+ 
+-                log_event_debug(dev, token, "Running PROGRAM=\"%s\"", buf);
++                log_event_debug(event, token, "Running PROGRAM=\"%s\"", buf);
+ 
+                 r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof(result), NULL);
+                 if (r != 0) {
+                         if (r < 0)
+-                                log_event_warning_errno(dev, token, r, "Failed to execute \"%s\": %m", buf);
++                                log_event_warning_errno(event, token, r, "Failed to execute \"%s\": %m", buf);
+                         else /* returned value is positive when program fails */
+-                                log_event_debug(dev, token, "Command \"%s\" returned %d (error)", buf, r);
++                                log_event_debug(event, token, "Command \"%s\" returned %d (error)", buf, r);
+                         return token->op == OP_NOMATCH;
+                 }
+ 
+                 delete_trailing_chars(result, "\n");
+                 count = udev_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
+                 if (count > 0)
+-                        log_event_debug(dev, token,
++                        log_event_debug(event, token,
+                                         "Replaced %zu character(s) in result of \"%s\"",
+                                         count, buf);
+ 
+@@ -2169,16 +2215,16 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "file name to be imported", token->value, "IMPORT", /* is_match = */ true);
++                        log_event_truncated(event, token, "file name to be imported", token->value, "IMPORT", /* is_match = */ true);
+                         return false;
+                 }
+ 
+-                log_event_debug(dev, token, "Importing properties from '%s'", buf);
++                log_event_debug(event, token, "Importing properties from '%s'", buf);
+ 
+                 f = fopen(buf, "re");
+                 if (!f) {
+                         if (errno != ENOENT)
+-                                return log_event_error_errno(dev, token, errno, "Failed to open '%s': %m", buf);
++                                return log_event_error_errno(event, token, errno, "Failed to open '%s': %m", buf);
+                         return token->op == OP_NOMATCH;
+                 }
+ 
+@@ -2188,7 +2234,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = read_line(f, LONG_LINE_MAX, &line);
+                         if (r < 0) {
+-                                log_event_debug_errno(dev, token, r, "Failed to read '%s', ignoring: %m", buf);
++                                log_event_debug_errno(event, token, r, "Failed to read '%s', ignoring: %m", buf);
+                                 return token->op == OP_NOMATCH;
+                         }
+                         if (r == 0)
+@@ -2196,7 +2242,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = get_property_from_string(line, &key, &value);
+                         if (r < 0) {
+-                                log_event_debug_errno(dev, token, r,
++                                log_event_debug_errno(event, token, r,
+                                                       "Failed to parse key and value from '%s', ignoring: %m",
+                                                       line);
+                                 continue;
+@@ -2206,7 +2252,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = device_add_property(dev, key, value);
+                         if (r < 0)
+-                                return log_event_error_errno(dev, token, r,
++                                return log_event_error_errno(event, token, r,
+                                                              "Failed to add property %s=%s: %m",
+                                                              key, value);
+                 }
+@@ -2220,18 +2266,18 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "command", token->value, "IMPORT", /* is_match = */ true);
++                        log_event_truncated(event, token, "command", token->value, "IMPORT", /* is_match = */ true);
+                         return false;
+                 }
+ 
+-                log_event_debug(dev, token, "Importing properties from results of '%s'", buf);
++                log_event_debug(event, token, "Importing properties from results of '%s'", buf);
+ 
+                 r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof result, &truncated);
+                 if (r != 0) {
+                         if (r < 0)
+-                                log_event_warning_errno(dev, token, r, "Failed to execute '%s', ignoring: %m", buf);
++                                log_event_warning_errno(event, token, r, "Failed to execute '%s', ignoring: %m", buf);
+                         else /* returned value is positive when program fails */
+-                                log_event_debug(dev, token, "Command \"%s\" returned %d (error), ignoring", buf, r);
++                                log_event_debug(event, token, "Command \"%s\" returned %d (error), ignoring", buf, r);
+                         return token->op == OP_NOMATCH;
+                 }
+ 
+@@ -2251,7 +2297,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOMEM)
+                         return log_oom();
+                 if (r < 0) {
+-                        log_event_warning_errno(dev, token, r,
++                        log_event_warning_errno(event, token, r,
+                                                 "Failed to extract lines from result of command \"%s\", ignoring: %m", buf);
+                         return false;
+                 }
+@@ -2261,7 +2307,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = get_property_from_string(*line, &key, &value);
+                         if (r < 0) {
+-                                log_event_debug_errno(dev, token, r,
++                                log_event_debug_errno(event, token, r,
+                                                       "Failed to parse key and value from '%s', ignoring: %m",
+                                                       *line);
+                                 continue;
+@@ -2271,7 +2317,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = device_add_property(dev, key, value);
+                         if (r < 0)
+-                                return log_event_error_errno(dev, token, r,
++                                return log_event_error_errno(event, token, r,
+                                                              "Failed to add property %s=%s: %m",
+                                                              key, value);
+                 }
+@@ -2288,7 +2334,7 @@ static int udev_rule_apply_token_to_event(
+                 if (udev_builtin_run_once(cmd)) {
+                         /* check if we ran already */
+                         if (event->builtin_run & mask) {
+-                                log_event_debug(dev, token, "Skipping builtin '%s' in IMPORT key",
++                                log_event_debug(event, token, "Skipping builtin '%s' in IMPORT key",
+                                                 udev_builtin_name(cmd));
+                                 /* return the result from earlier run */
+                                 return token->op == (event->builtin_ret & mask ? OP_NOMATCH : OP_MATCH);
+@@ -2299,16 +2345,16 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "builtin command", token->value, "IMPORT", /* is_match = */ true);
++                        log_event_truncated(event, token, "builtin command", token->value, "IMPORT", /* is_match = */ true);
+                         return false;
+                 }
+ 
+-                log_event_debug(dev, token, "Importing properties from results of builtin command '%s'", buf);
++                log_event_debug(event, token, "Importing properties from results of builtin command '%s'", buf);
+ 
+                 r = udev_builtin_run(event, cmd, buf);
+                 if (r < 0) {
+                         /* remember failure */
+-                        log_event_debug_errno(dev, token, r, "Failed to run builtin '%s': %m", buf);
++                        log_event_debug_errno(event, token, r, "Failed to run builtin '%s': %m", buf);
+                         event->builtin_ret |= mask;
+                 }
+                 return token->op == (r >= 0 ? OP_MATCH : OP_NOMATCH);
+@@ -2322,13 +2368,13 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOENT)
+                         return token->op == OP_NOMATCH;
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r,
++                        return log_event_error_errno(event, token, r,
+                                                      "Failed to get property '%s' from database: %m",
+                                                      token->value);
+ 
+                 r = device_add_property(dev, token->value, val);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to add property '%s=%s': %m",
++                        return log_event_error_errno(event, token, r, "Failed to add property '%s=%s': %m",
+                                                      token->value, val);
+                 return token->op == OP_MATCH;
+         }
+@@ -2337,7 +2383,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_IGNORE_EFI_OPTIONS, &value);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r,
++                        return log_event_error_errno(event, token, r,
+                                                      "Failed to read '%s' option from /proc/cmdline: %m",
+                                                      token->value);
+                 if (r == 0)
+@@ -2345,7 +2391,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = device_add_property(dev, token->value, value ?: "1");
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to add property '%s=%s': %m",
++                        return log_event_error_errno(event, token, r, "Failed to add property '%s=%s': %m",
+                                                      token->value, value ?: "1");
+                 return token->op == OP_MATCH;
+         }
+@@ -2355,13 +2401,13 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "property name", token->value, "IMPORT", /* is_match = */ true);
++                        log_event_truncated(event, token, "property name", token->value, "IMPORT", /* is_match = */ true);
+                         return false;
+                 }
+ 
+                 r = import_parent_into_properties(dev, buf);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r,
++                        return log_event_error_errno(event, token, r,
+                                                      "Failed to import properties '%s' from parent: %m",
+                                                      buf);
+                 return token->op == (r > 0 ? OP_MATCH : OP_NOMATCH);
+@@ -2399,7 +2445,7 @@ static int udev_rule_apply_token_to_event(
+                 else {
+                         _cleanup_free_ char *level_str = NULL;
+                         (void) log_level_to_string_alloc(level, &level_str);
+-                        log_event_debug(dev, token, "Running in test mode, skipping changing maximum log level to %s.", strna(level_str));
++                        log_event_debug(event, token, "Running in test mode, skipping changing maximum log level to %s.", strna(level_str));
+                 }
+ 
+                 if (level == LOG_DEBUG && !event->log_level_was_debug) {
+@@ -2423,7 +2469,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "user name", token->value, "OWNER", /* is_match = */ false);
++                        log_event_truncated(event, token, "user name", token->value, "OWNER", /* is_match = */ false);
+                         break;
+                 }
+ 
+@@ -2431,7 +2477,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         log_unknown_owner(dev, token->rule_line, r, "user", owner);
+                 else
+-                        log_event_debug(dev, token, "OWNER %s(%u)", owner, event->uid);
++                        log_event_debug(event, token, "OWNER %s(%u)", owner, event->uid);
+                 break;
+         }
+         case TK_A_GROUP: {
+@@ -2446,7 +2492,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "group name", token->value, "GROUP", /* is_match = */ false);
++                        log_event_truncated(event, token, "group name", token->value, "GROUP", /* is_match = */ false);
+                         break;
+                 }
+ 
+@@ -2454,7 +2500,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         log_unknown_owner(dev, token->rule_line, r, "group", group);
+                 else
+-                        log_event_debug(dev, token, "GROUP %s(%u)", group, event->gid);
++                        log_event_debug(event, token, "GROUP %s(%u)", group, event->gid);
+                 break;
+         }
+         case TK_A_MODE: {
+@@ -2468,15 +2514,15 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "mode", token->value, "MODE", /* is_match = */ false);
++                        log_event_truncated(event, token, "mode", token->value, "MODE", /* is_match = */ false);
+                         break;
+                 }
+ 
+                 r = parse_mode(mode_str, &event->mode);
+                 if (r < 0)
+-                        log_event_error_errno(dev, token, r, "Failed to parse mode '%s', ignoring: %m", mode_str);
++                        log_event_error_errno(event, token, r, "Failed to parse mode '%s', ignoring: %m", mode_str);
+                 else
+-                        log_event_debug(dev, token, "MODE %#o", event->mode);
++                        log_event_debug(event, token, "MODE %#o", event->mode);
+                 break;
+         }
+         case TK_A_OWNER_ID:
+@@ -2487,7 +2533,7 @@ static int udev_rule_apply_token_to_event(
+                 if (!token->data)
+                         break;
+                 event->uid = PTR_TO_UID(token->data);
+-                log_event_debug(dev, token, "OWNER %u", event->uid);
++                log_event_debug(event, token, "OWNER %u", event->uid);
+                 break;
+         case TK_A_GROUP_ID:
+                 if (event->group_final)
+@@ -2497,7 +2543,7 @@ static int udev_rule_apply_token_to_event(
+                 if (!token->data)
+                         break;
+                 event->gid = PTR_TO_GID(token->data);
+-                log_event_debug(dev, token, "GROUP %u", event->gid);
++                log_event_debug(event, token, "GROUP %u", event->gid);
+                 break;
+         case TK_A_MODE_ID:
+                 if (event->mode_final)
+@@ -2507,7 +2553,7 @@ static int udev_rule_apply_token_to_event(
+                 if (!token->data)
+                         break;
+                 event->mode = PTR_TO_MODE(token->data);
+-                log_event_debug(dev, token, "MODE %#o", event->mode);
++                log_event_debug(event, token, "MODE %#o", event->mode);
+                 break;
+         case TK_A_SECLABEL: {
+                 _cleanup_free_ char *name = NULL, *label = NULL;
+@@ -2520,7 +2566,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "security label", token->value, "SECLABEL", /* is_match = */ false);
++                        log_event_truncated(event, token, "security label", token->value, "SECLABEL", /* is_match = */ false);
+                         break;
+                 }
+ 
+@@ -2538,9 +2584,9 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOMEM)
+                         return log_oom();
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to store SECLABEL{%s}='%s': %m", name, label);
++                        return log_event_error_errno(event, token, r, "Failed to store SECLABEL{%s}='%s': %m", name, label);
+ 
+-                log_event_debug(dev, token, "SECLABEL{%s}='%s'", name, label);
++                log_event_debug(event, token, "SECLABEL{%s}='%s'", name, label);
+ 
+                 TAKE_PTR(name);
+                 TAKE_PTR(label);
+@@ -2557,7 +2603,7 @@ static int udev_rule_apply_token_to_event(
+                                 break;
+                         r = device_add_property(dev, name, NULL);
+                         if (r < 0)
+-                                return log_event_error_errno(dev, token, r, "Failed to remove property '%s': %m", name);
++                                return log_event_error_errno(event, token, r, "Failed to remove property '%s': %m", name);
+                         break;
+                 }
+ 
+@@ -2565,7 +2611,7 @@ static int udev_rule_apply_token_to_event(
+                     device_get_property_value_with_fallback(dev, name, event->worker ? event->worker->properties : NULL, &val) >= 0) {
+                         l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
+                         if (truncated) {
+-                                log_event_warning(dev, token,
++                                log_event_warning(event, token,
+                                                   "The buffer for the property '%s' is full, "
+                                                   "refusing to append the new value '%s'.", name, token->value);
+                                 break;
+@@ -2575,7 +2621,7 @@ static int udev_rule_apply_token_to_event(
+                 (void) udev_event_apply_format(event, token->value, p, l, false, &truncated);
+                 if (truncated) {
+                         _cleanup_free_ char *key_with_name = strjoin("ENV{", name, "}");
+-                        log_event_truncated(dev, token, "property value", token->value,
++                        log_event_truncated(event, token, "property value", token->value,
+                                             key_with_name ?: "ENV", /* is_match = */ false);
+                         break;
+                 }
+@@ -2583,14 +2629,14 @@ static int udev_rule_apply_token_to_event(
+                 if (event->esc == ESCAPE_REPLACE) {
+                         count = udev_replace_chars(p, NULL);
+                         if (count > 0)
+-                                log_event_debug(dev, token,
++                                log_event_debug(event, token,
+                                                 "Replaced %zu slash(es) from result of ENV{%s}%s=\"%s\"",
+                                                 count, name, token->op == OP_ADD ? "+" : "", token->value);
+                 }
+ 
+                 r = device_add_property(dev, name, value_new);
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to add property '%s=%s': %m", name, value_new);
++                        return log_event_error_errno(event, token, r, "Failed to add property '%s=%s': %m", name, value_new);
+                 break;
+         }
+         case TK_A_TAG: {
+@@ -2599,7 +2645,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "tag name", token->value, "TAG", /* is_match = */ false);
++                        log_event_truncated(event, token, "tag name", token->value, "TAG", /* is_match = */ false);
+                         break;
+                 }
+ 
+@@ -2613,7 +2659,7 @@ static int udev_rule_apply_token_to_event(
+                         if (r == -ENOMEM)
+                                 return log_oom();
+                         if (r < 0)
+-                                log_event_warning_errno(dev, token, r, "Failed to add tag '%s', ignoring: %m", buf);
++                                log_event_warning_errno(event, token, r, "Failed to add tag '%s', ignoring: %m", buf);
+                 }
+                 break;
+         }
+@@ -2628,7 +2674,7 @@ static int udev_rule_apply_token_to_event(
+                         event->name_final = true;
+ 
+                 if (sd_device_get_ifindex(dev, NULL) < 0) {
+-                        log_event_error(dev, token,
++                        log_event_error(event, token,
+                                         "Only network interfaces can be renamed, ignoring NAME=\"%s\".",
+                                         token->value);
+                         break;
+@@ -2636,7 +2682,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "network interface name", token->value, "NAME", /* is_match = */ false);
++                        log_event_truncated(event, token, "network interface name", token->value, "NAME", /* is_match = */ false);
+                         break;
+                 }
+ 
+@@ -2646,7 +2692,7 @@ static int udev_rule_apply_token_to_event(
+                         else
+                                 count = udev_replace_chars(buf, "/");
+                         if (count > 0)
+-                                log_event_debug(dev, token,
++                                log_event_debug(event, token,
+                                                 "Replaced %zu character(s) from result of NAME=\"%s\"",
+                                                 count, token->value);
+                 }
+@@ -2654,7 +2700,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         return r;
+ 
+-                log_event_debug(dev, token, "NAME '%s'", event->name);
++                log_event_debug(event, token, "NAME '%s'", event->name);
+                 break;
+         }
+         case TK_A_DEVLINK: {
+@@ -2674,7 +2720,7 @@ static int udev_rule_apply_token_to_event(
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf),
+                                                /* replace_whitespace = */ event->esc != ESCAPE_NONE, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "symbolic link path", token->value, "SYMLINK", /* is_match = */ false);
++                        log_event_truncated(event, token, "symbolic link path", token->value, "SYMLINK", /* is_match = */ false);
+                         break;
+                 }
+ 
+@@ -2686,7 +2732,7 @@ static int udev_rule_apply_token_to_event(
+                 else
+                         count = 0;
+                 if (count > 0)
+-                        log_event_debug(dev, token,
++                        log_event_debug(event, token,
+                                         "Replaced %zu character(s) from result of SYMLINK=\"%s\"",
+                                         count, token->value);
+ 
+@@ -2708,17 +2754,17 @@ static int udev_rule_apply_token_to_event(
+                                 if (r == -ENOMEM)
+                                         return log_oom();
+                                 if (r < 0)
+-                                        log_event_warning_errno(dev, token, r, "Failed to remove devlink '%s', ignoring: %m", path);
++                                        log_event_warning_errno(event, token, r, "Failed to remove devlink '%s', ignoring: %m", path);
+                                 else if (r > 0)
+-                                        log_event_debug(dev, token, "Dropped SYMLINK '%s'", path);
++                                        log_event_debug(event, token, "Dropped SYMLINK '%s'", path);
+                         } else {
+                                 r = device_add_devlink(dev, path);
+                                 if (r == -ENOMEM)
+                                         return log_oom();
+                                 if (r < 0)
+-                                        log_event_warning_errno(dev, token, r, "Failed to add devlink '%s', ignoring: %m", path);
++                                        log_event_warning_errno(event, token, r, "Failed to add devlink '%s', ignoring: %m", path);
+                                 else if (r > 0)
+-                                        log_event_debug(dev, token, "Added SYMLINK '%s'", path);
++                                        log_event_debug(event, token, "Added SYMLINK '%s'", path);
+                         }
+                 }
+                 break;
+@@ -2732,7 +2778,7 @@ static int udev_rule_apply_token_to_event(
+                     sd_device_get_syspath(dev, &val) >= 0) {
+                         strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL);
+                         if (truncated) {
+-                                log_event_warning(dev, token,
++                                log_event_warning(event, token,
+                                                   "The path to the attribute '%s/%s' is too long, refusing to set the attribute.",
+                                                   val, key_name);
+                                 break;
+@@ -2741,26 +2787,26 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = attr_subst_subdir(buf);
+                 if (r < 0) {
+-                        log_event_error_errno(dev, token, r, "Could not find file matches '%s', ignoring: %m", buf);
++                        log_event_error_errno(event, token, r, "Could not find file matches '%s', ignoring: %m", buf);
+                         break;
+                 }
+                 (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "attribute value", token->value, "ATTR", /* is_match = */ false);
++                        log_event_truncated(event, token, "attribute value", token->value, "ATTR", /* is_match = */ false);
+                         break;
+                 }
+ 
+                 if (EVENT_MODE_DESTRUCTIVE(event)) {
+-                        log_event_debug(dev, token, "Writing ATTR{'%s'}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Writing ATTR{'%s'}=\"%s\".", buf, value);
+                         r = write_string_file(buf, value,
+                                               WRITE_STRING_FILE_VERIFY_ON_FAILURE |
+                                               WRITE_STRING_FILE_DISABLE_BUFFER |
+                                               WRITE_STRING_FILE_AVOID_NEWLINE |
+                                               WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
+                         if (r < 0)
+-                                log_event_error_errno(dev, token, r, "Failed to write ATTR{%s}=\"%s\", ignoring: %m", buf, value);
++                                log_event_error_errno(event, token, r, "Failed to write ATTR{%s}=\"%s\", ignoring: %m", buf, value);
+                 } else
+-                        log_event_debug(dev, token, "Running in test mode, skipping writing ATTR{%s}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Running in test mode, skipping writing ATTR{%s}=\"%s\".", buf, value);
+ 
+                 break;
+         }
+@@ -2770,14 +2816,14 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ false);
++                        log_event_truncated(event, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ false);
+                         break;
+                 }
+ 
+                 (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
+                 if (truncated) {
+                         _cleanup_free_ char *key_with_name = strjoin("SYSCTL{", buf, "}");
+-                        log_event_truncated(dev, token, "sysctl value", token->value,
++                        log_event_truncated(event, token, "sysctl value", token->value,
+                                             key_with_name ?: "SYSCTL", /* is_match = */ false);
+                         break;
+                 }
+@@ -2785,12 +2831,12 @@ static int udev_rule_apply_token_to_event(
+                 sysctl_normalize(buf);
+ 
+                 if (EVENT_MODE_DESTRUCTIVE(event)) {
+-                        log_event_debug(dev, token, "Writing SYSCTL{%s}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Writing SYSCTL{%s}=\"%s\".", buf, value);
+                         r = sysctl_write(buf, value);
+                         if (r < 0)
+-                                log_event_error_errno(dev, token, r, "Failed to write SYSCTL{%s}=\"%s\", ignoring: %m", buf, value);
++                                log_event_error_errno(event, token, r, "Failed to write SYSCTL{%s}=\"%s\", ignoring: %m", buf, value);
+                 } else
+-                        log_event_debug(dev, token, "Running in test mode, skipping writing SYSCTL{%s}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Running in test mode, skipping writing SYSCTL{%s}=\"%s\".", buf, value);
+ 
+                 break;
+         }
+@@ -2810,7 +2856,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+                 if (truncated) {
+-                        log_event_truncated(dev, token, "command", token->value,
++                        log_event_truncated(event, token, "command", token->value,
+                                             token->type == TK_A_RUN_BUILTIN ? "RUN{builtin}" : "RUN{program}",
+                                             /* is_match = */ false);
+                         break;
+@@ -2824,11 +2870,11 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOMEM)
+                         return log_oom();
+                 if (r < 0)
+-                        return log_event_error_errno(dev, token, r, "Failed to store command '%s': %m", cmd);
++                        return log_event_error_errno(event, token, r, "Failed to store command '%s': %m", cmd);
+ 
+                 TAKE_PTR(cmd);
+ 
+-                log_event_debug(dev, token, "RUN '%s'", token->value);
++                log_event_debug(event, token, "RUN '%s'", token->value);
+                 break;
+         }
+         case TK_A_OPTIONS_STATIC_NODE:
+@@ -2841,10 +2887,6 @@ static int udev_rule_apply_token_to_event(
+         return true;
+ }
+ 
+-static bool token_is_for_parents(UdevRuleToken *token) {
+-        return token->type >= TK_M_PARENTS_KERNEL && token->type <= TK_M_PARENTS_TAG;
+-}
+-
+ static int udev_rule_apply_parent_token_to_event(UdevRuleToken *head_token, UdevEvent *event) {
+         int r;
+ 
diff --git a/SOURCES/0167-udev-rules-logs-result-of-format-substitution.patch b/SOURCES/0167-udev-rules-logs-result-of-format-substitution.patch
new file mode 100644
index 0000000000000000000000000000000000000000..db27c4a80e100f3be33b4701f9666662d2b68194
--- /dev/null
+++ b/SOURCES/0167-udev-rules-logs-result-of-format-substitution.patch
@@ -0,0 +1,524 @@
+From 11c7cc1bd95c80d8a92656aa432de4a9cadf69c2 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Sun, 12 Jan 2025 00:12:52 +0900
+Subject: [PATCH] udev-rules: logs result of format substitution
+
+This also drops redundant token string in log message on truncation.
+
+No functional change, but should improve debuggability.
+
+(cherry picked from commit a3ab06ab21528138160eb3d40c3e661fc53a8158)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 243 ++++++++++++++++++------------------------
+ 1 file changed, 105 insertions(+), 138 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index c33d17aa47..3a61403338 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -297,6 +297,23 @@ static bool token_is_for_parents(UdevRuleToken *token) {
+ #define log_event_trace(event, ...)                                     \
+         _log_event_trace(event, UNIQ_T(e, UNIQ), __VA_ARGS__)
+ 
++#define _log_event_truncated(event, token, token_u, what, format)       \
++        ({                                                              \
++                UdevRuleToken *token_u = ASSERT_PTR(token);             \
++                                                                        \
++                token_u->type < _TK_M_MAX ?                             \
++                        log_event_debug(event, token_u,                 \
++                                        "The %s is truncated while substituting into \"%s\", assuming the token fails.", \
++                                        what, (const char*) format) :   \
++                        log_event_warning(                              \
++                                        event, token_u,                 \
++                                        "The %s is truncated while substituting into \"%s\", refusing to apply the token.", \
++                                        what, (const char*) format);    \
++        })
++
++#define log_event_truncated(event, token, what, format)                 \
++        _log_event_truncated(event, token, UNIQ_T(t, UNIQ), what, format)
++
+ /* Mainly used when parsing .rules files. */
+ #define log_file_full_errno_zerook(...)                                 \
+         log_udev_rule_file_full(NULL, __VA_ARGS__)
+@@ -351,26 +368,6 @@ static void log_unknown_owner(sd_device *dev, UdevRuleLine *line, int error, con
+                 log_udev_rule_line_full(dev, line, LOG_ERR, error, "Failed to resolve %s '%s', ignoring: %m", entity, name);
+ }
+ 
+-static void log_event_truncated(
+-                UdevEvent *event,
+-                UdevRuleToken *token,
+-                const char *what,
+-                const char *format,
+-                const char *key,
+-                bool is_match) {
+-
+-        if (is_match)
+-                log_event_debug(event, token,
+-                                "The %s is truncated while substituting into '%s', "
+-                                "assuming the %s key does not match.",
+-                                what, format, key);
+-        else
+-                log_event_warning(event, token,
+-                                  "The %s is truncated while substituting into '%s', "
+-                                  "refusing to apply the %s key.",
+-                                  what, format, key);
+-}
+-
+ /*** Other functions ***/
+ 
+ static UdevRuleToken* udev_rule_token_free(UdevRuleToken *token) {
+@@ -1776,6 +1773,54 @@ bool udev_rules_should_reload(UdevRules *rules) {
+         return false;
+ }
+ 
++static bool apply_format_full(
++                UdevEvent *event,
++                UdevRuleToken *token,
++                const char *format,
++                char *result,
++                size_t result_size,
++                bool replace_whitespace,
++                const char *what) {
++
++        assert(event);
++        assert(token);
++        assert(format);
++        assert(result);
++        assert(what);
++
++        bool truncated = false;
++        (void) udev_event_apply_format(event, format, result, result_size, replace_whitespace, &truncated);
++        if (truncated) {
++                log_event_truncated(event, token, what, format);
++                return false;
++        }
++
++        if (event->trace && !streq(format, result))
++                log_event_trace(event, token, "Format substitution: \"%s\" -> \"%s\"", format, result);
++
++        return true;
++}
++
++static bool apply_format_value(
++                UdevEvent *event,
++                UdevRuleToken *token,
++                char *result,
++                size_t result_size,
++                const char *what) {
++
++        return apply_format_full(event, token, token->value, result, result_size, /* replace_whitespace = */ false, what);
++}
++
++static bool apply_format_attr(
++                UdevEvent *event,
++                UdevRuleToken *token,
++                char *result,
++                size_t result_size,
++                const char *what) {
++
++        return apply_format_full(event, token, token->data, result, result_size, /* replace_whitespace = */ false, what);
++}
++
+ static bool token_match_string(UdevRuleToken *token, const char *str) {
+         const char *value;
+         bool match = false, case_insensitive;
+@@ -1834,7 +1879,6 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
+ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
+         char nbuf[UDEV_NAME_SIZE], vbuf[UDEV_NAME_SIZE];
+         const char *name, *value;
+-        bool truncated;
+ 
+         assert(token);
+         assert(IN_SET(token->type, TK_M_ATTR, TK_M_PARENTS_ATTR));
+@@ -1845,12 +1889,8 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
+ 
+         switch (token->attr_subst_type) {
+         case SUBST_TYPE_FORMAT:
+-                (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "sysfs attribute name", name,
+-                                            token->type == TK_M_ATTR ? "ATTR" : "ATTRS", /* is_match = */ true);
++                if (!apply_format_attr(event, token, nbuf, sizeof(nbuf), "sysfs attribute name"))
+                         return false;
+-                }
+ 
+                 name = nbuf;
+                 _fallthrough_;
+@@ -2118,13 +2158,9 @@ static int udev_rule_apply_token_to_event(
+         case TK_M_SYSCTL: {
+                 _cleanup_free_ char *value = NULL;
+                 char buf[UDEV_PATH_SIZE];
+-                bool truncated;
+ 
+-                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ true);
++                if (!apply_format_attr(event, token, buf, sizeof(buf), "sysctl entry name"))
+                         return false;
+-                }
+ 
+                 r = sysctl_read(sysctl_normalize(buf), &value);
+                 if (r < 0 && r != -ENOENT)
+@@ -2136,13 +2172,10 @@ static int udev_rule_apply_token_to_event(
+                 mode_t mode = PTR_TO_MODE(token->data);
+                 char buf[UDEV_PATH_SIZE];
+                 struct stat statbuf;
+-                bool match, truncated;
++                bool match;
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "file name", token->value, "TEST", /* is_match = */ true);
++                if (!apply_format_value(event, token, buf, sizeof(buf), "file name"))
+                         return false;
+-                }
+ 
+                 if (!path_is_absolute(buf) &&
+                     udev_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) {
+@@ -2153,6 +2186,7 @@ static int udev_rule_apply_token_to_event(
+                         if (r < 0)
+                                 return log_event_error_errno(event, token, r, "Failed to get syspath: %m");
+ 
++                        bool truncated;
+                         strscpy_full(tmp, sizeof(tmp), buf, &truncated);
+                         assert(!truncated);
+                         strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL);
+@@ -2177,15 +2211,12 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_M_PROGRAM: {
+                 char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE];
+-                bool truncated;
+                 size_t count;
+ 
+                 event->program_result = mfree(event->program_result);
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "command", token->value, "PROGRAM", /* is_match = */ true);
++
++                if (!apply_format_value(event, token, buf, sizeof(buf), "command"))
+                         return false;
+-                }
+ 
+                 log_event_debug(event, token, "Running PROGRAM=\"%s\"", buf);
+ 
+@@ -2211,13 +2242,9 @@ static int udev_rule_apply_token_to_event(
+         case TK_M_IMPORT_FILE: {
+                 _cleanup_fclose_ FILE *f = NULL;
+                 char buf[UDEV_PATH_SIZE];
+-                bool truncated;
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "file name to be imported", token->value, "IMPORT", /* is_match = */ true);
++                if (!apply_format_value(event, token, buf, sizeof(buf), "file name to be imported"))
+                         return false;
+-                }
+ 
+                 log_event_debug(event, token, "Importing properties from '%s'", buf);
+ 
+@@ -2264,11 +2291,8 @@ static int udev_rule_apply_token_to_event(
+                 char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE];
+                 bool truncated;
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "command", token->value, "IMPORT", /* is_match = */ true);
++                if (!apply_format_value(event, token, buf, sizeof(buf), "command"))
+                         return false;
+-                }
+ 
+                 log_event_debug(event, token, "Importing properties from results of '%s'", buf);
+ 
+@@ -2329,7 +2353,6 @@ static int udev_rule_apply_token_to_event(
+                 assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
+                 unsigned mask = 1U << (int) cmd;
+                 char buf[UDEV_LINE_SIZE];
+-                bool truncated;
+ 
+                 if (udev_builtin_run_once(cmd)) {
+                         /* check if we ran already */
+@@ -2343,11 +2366,8 @@ static int udev_rule_apply_token_to_event(
+                         event->builtin_run |= mask;
+                 }
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "builtin command", token->value, "IMPORT", /* is_match = */ true);
++                if (!apply_format_value(event, token, buf, sizeof(buf), "builtin command"))
+                         return false;
+-                }
+ 
+                 log_event_debug(event, token, "Importing properties from results of builtin command '%s'", buf);
+ 
+@@ -2397,13 +2417,9 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_M_IMPORT_PARENT: {
+                 char buf[UDEV_PATH_SIZE];
+-                bool truncated;
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "property name", token->value, "IMPORT", /* is_match = */ true);
++                if (!apply_format_value(event, token, buf, sizeof(buf), "property name"))
+                         return false;
+-                }
+ 
+                 r = import_parent_into_properties(dev, buf);
+                 if (r < 0)
+@@ -2460,18 +2476,14 @@ static int udev_rule_apply_token_to_event(
+         case TK_A_OWNER: {
+                 char owner[UDEV_NAME_SIZE];
+                 const char *ow = owner;
+-                bool truncated;
+ 
+                 if (event->owner_final)
+                         break;
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->owner_final = true;
+ 
+-                (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "user name", token->value, "OWNER", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, owner, sizeof(owner), "user name"))
++                        return true;
+ 
+                 r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+                 if (r < 0)
+@@ -2483,18 +2495,14 @@ static int udev_rule_apply_token_to_event(
+         case TK_A_GROUP: {
+                 char group[UDEV_NAME_SIZE];
+                 const char *gr = group;
+-                bool truncated;
+ 
+                 if (event->group_final)
+                         break;
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->group_final = true;
+ 
+-                (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "group name", token->value, "GROUP", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, group, sizeof(group), "group name"))
++                        return true;
+ 
+                 r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
+                 if (r < 0)
+@@ -2505,18 +2513,14 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_MODE: {
+                 char mode_str[UDEV_NAME_SIZE];
+-                bool truncated;
+ 
+                 if (event->mode_final)
+                         break;
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->mode_final = true;
+ 
+-                (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "mode", token->value, "MODE", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, mode_str, sizeof(mode_str), "mode"))
++                        return true;
+ 
+                 r = parse_mode(mode_str, &event->mode);
+                 if (r < 0)
+@@ -2558,17 +2562,13 @@ static int udev_rule_apply_token_to_event(
+         case TK_A_SECLABEL: {
+                 _cleanup_free_ char *name = NULL, *label = NULL;
+                 char label_str[UDEV_LINE_SIZE] = {};
+-                bool truncated;
+ 
+                 name = strdup(token->data);
+                 if (!name)
+                         return log_oom();
+ 
+-                (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "security label", token->value, "SECLABEL", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, label_str, sizeof(label_str), "security label"))
++                        return true;
+ 
+                 if (!isempty(label_str))
+                         label = strdup(label_str);
+@@ -2596,7 +2596,6 @@ static int udev_rule_apply_token_to_event(
+                 const char *val, *name = token->data;
+                 char value_new[UDEV_NAME_SIZE], *p = value_new;
+                 size_t count, l = sizeof(value_new);
+-                bool truncated;
+ 
+                 if (isempty(token->value)) {
+                         if (token->op == OP_ADD)
+@@ -2609,6 +2608,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 if (token->op == OP_ADD &&
+                     device_get_property_value_with_fallback(dev, name, event->worker ? event->worker->properties : NULL, &val) >= 0) {
++                        bool truncated;
+                         l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
+                         if (truncated) {
+                                 log_event_warning(event, token,
+@@ -2618,13 +2618,8 @@ static int udev_rule_apply_token_to_event(
+                         }
+                 }
+ 
+-                (void) udev_event_apply_format(event, token->value, p, l, false, &truncated);
+-                if (truncated) {
+-                        _cleanup_free_ char *key_with_name = strjoin("ENV{", name, "}");
+-                        log_event_truncated(event, token, "property value", token->value,
+-                                            key_with_name ?: "ENV", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, p, l, "property value"))
++                        return true;
+ 
+                 if (event->esc == ESCAPE_REPLACE) {
+                         count = udev_replace_chars(p, NULL);
+@@ -2641,13 +2636,9 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_TAG: {
+                 char buf[UDEV_PATH_SIZE];
+-                bool truncated;
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "tag name", token->value, "TAG", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, buf, sizeof(buf), "tag name"))
++                        return true;
+ 
+                 if (token->op == OP_ASSIGN)
+                         device_cleanup_tags(dev);
+@@ -2665,7 +2656,6 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_NAME: {
+                 char buf[UDEV_PATH_SIZE];
+-                bool truncated;
+                 size_t count;
+ 
+                 if (event->name_final)
+@@ -2680,11 +2670,8 @@ static int udev_rule_apply_token_to_event(
+                         break;
+                 }
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "network interface name", token->value, "NAME", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, buf, sizeof(buf), "network interface name"))
++                        return true;
+ 
+                 if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
+                         if (naming_scheme_has(NAMING_REPLACE_STRICTLY))
+@@ -2705,7 +2692,6 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_DEVLINK: {
+                 char buf[UDEV_PATH_SIZE];
+-                bool truncated;
+                 size_t count;
+ 
+                 if (event->devlink_final)
+@@ -2717,12 +2703,10 @@ static int udev_rule_apply_token_to_event(
+                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
+                         device_cleanup_devlinks(dev);
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf),
+-                                               /* replace_whitespace = */ event->esc != ESCAPE_NONE, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "symbolic link path", token->value, "SYMLINK", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_full(event, token, token->value, buf, sizeof(buf),
++                                       /* replace_whitespace = */ event->esc != ESCAPE_NONE,
++                                       "symbolic link path"))
++                        return true;
+ 
+                 /* By default or string_escape=none, allow multiple symlinks separated by spaces. */
+                 if (event->esc == ESCAPE_UNSET)
+@@ -2772,10 +2756,10 @@ static int udev_rule_apply_token_to_event(
+         case TK_A_ATTR: {
+                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
+                 const char *val, *key_name = token->data;
+-                bool truncated;
+ 
+                 if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 &&
+                     sd_device_get_syspath(dev, &val) >= 0) {
++                        bool truncated;
+                         strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL);
+                         if (truncated) {
+                                 log_event_warning(event, token,
+@@ -2790,11 +2774,9 @@ static int udev_rule_apply_token_to_event(
+                         log_event_error_errno(event, token, r, "Could not find file matches '%s', ignoring: %m", buf);
+                         break;
+                 }
+-                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "attribute value", token->value, "ATTR", /* is_match = */ false);
+-                        break;
+-                }
++
++                if (!apply_format_value(event, token, value, sizeof(value), "attribute value"))
++                        return true;
+ 
+                 if (EVENT_MODE_DESTRUCTIVE(event)) {
+                         log_event_debug(event, token, "Writing ATTR{'%s'}=\"%s\".", buf, value);
+@@ -2812,21 +2794,12 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_SYSCTL: {
+                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
+-                bool truncated;
+ 
+-                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_attr(event, token, buf, sizeof(buf), "sysctl entry name"))
++                        return true;
+ 
+-                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
+-                if (truncated) {
+-                        _cleanup_free_ char *key_with_name = strjoin("SYSCTL{", buf, "}");
+-                        log_event_truncated(event, token, "sysctl value", token->value,
+-                                            key_with_name ?: "SYSCTL", /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, value, sizeof(value), "sysctl value"))
++                        return true;
+ 
+                 sysctl_normalize(buf);
+ 
+@@ -2844,7 +2817,6 @@ static int udev_rule_apply_token_to_event(
+         case TK_A_RUN_PROGRAM: {
+                 _cleanup_free_ char *cmd = NULL;
+                 char buf[UDEV_LINE_SIZE];
+-                bool truncated;
+ 
+                 if (event->run_final)
+                         break;
+@@ -2854,13 +2826,8 @@ static int udev_rule_apply_token_to_event(
+                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
+                         ordered_hashmap_clear_free_key(event->run_list);
+ 
+-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
+-                if (truncated) {
+-                        log_event_truncated(event, token, "command", token->value,
+-                                            token->type == TK_A_RUN_BUILTIN ? "RUN{builtin}" : "RUN{program}",
+-                                            /* is_match = */ false);
+-                        break;
+-                }
++                if (!apply_format_value(event, token, buf, sizeof(buf), "command"))
++                        return true;
+ 
+                 cmd = strdup(buf);
+                 if (!cmd)
diff --git a/SOURCES/0168-udev-rules-add-more-trace-logs-for-string-match.patch b/SOURCES/0168-udev-rules-add-more-trace-logs-for-string-match.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b6f3b47cbd113d2dfd66076d194ad200a2beba15
--- /dev/null
+++ b/SOURCES/0168-udev-rules-add-more-trace-logs-for-string-match.patch
@@ -0,0 +1,205 @@
+From 49cc87a059b24a6c81e954425599a7253c05beb4 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Mon, 13 Jan 2025 04:03:11 +0900
+Subject: [PATCH] udev-rules: add more trace logs for string match
+
+(cherry picked from commit 0febeccfdc4839ace732280f06005e81f5cadff7)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 81 ++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 65 insertions(+), 16 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 3a61403338..35a3a9cc69 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -1821,10 +1821,11 @@ static bool apply_format_attr(
+         return apply_format_full(event, token, token->data, result, result_size, /* replace_whitespace = */ false, what);
+ }
+ 
+-static bool token_match_string(UdevRuleToken *token, const char *str) {
++static bool token_match_string(UdevEvent *event, UdevRuleToken *token, const char *str, bool log_result) {
+         const char *value;
+         bool match = false, case_insensitive;
+ 
++        assert(event);
+         assert(token);
+         assert(token->value);
+         assert(token->type < _TK_M_MAX);
+@@ -1873,7 +1874,51 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
+                 assert_not_reached();
+         }
+ 
+-        return token->op == (match ? OP_MATCH : OP_NOMATCH);
++        bool result = token->op == (match ? OP_MATCH : OP_NOMATCH);
++
++        if (event->trace)
++                switch (token->match_type & _MATCH_TYPE_MASK) {
++                case MATCH_TYPE_EMPTY:
++                        log_event_trace(event, token, "String \"%s\" is%s empty%s",
++                                        strempty(str), match ? "" : " not",
++                                        log_result ? result ? ": PASS" : ": FAIL" : ".");
++                        break;
++                case MATCH_TYPE_SUBSYSTEM:
++                        log_event_trace(event, token,
++                                        "String \"%s\" matches %s of \"subsystem\", \"class\", or \"bus\"%s",
++                                        strempty(str), match ? "one" : "neither",
++                                        log_result ? result ? ": PASS" : ": FAIL" : ".");
++                        break;
++                case MATCH_TYPE_PLAIN_WITH_EMPTY:
++                case MATCH_TYPE_PLAIN:
++                case MATCH_TYPE_GLOB_WITH_EMPTY:
++                case MATCH_TYPE_GLOB: {
++                        _cleanup_free_ char *joined = NULL;
++                        unsigned c = 0;
++
++                        if (IN_SET(token->match_type & _MATCH_TYPE_MASK, MATCH_TYPE_PLAIN_WITH_EMPTY, MATCH_TYPE_GLOB_WITH_EMPTY)) {
++                                (void) strextend_with_separator(&joined, ", ", "\"\"");
++                                c++;
++                        }
++
++                        NULSTR_FOREACH(i, value) {
++                                (void) strextendf_with_separator(&joined, ", ", "\"%s\"", i);
++                                c++;
++                        }
++
++                        assert(c > 0);
++                        log_event_trace(event, token, "String \"%s\" %s %s%s",
++                                        strempty(str),
++                                        match ? (c > 1 ? "matches one of" : "matches") : (c > 1 ? "matches neither of" : "does not match"),
++                                        strempty(joined),
++                                        log_result ? result ? ": PASS" : ": FAIL" : ".");
++                        break;
++                }
++                default:
++                        assert_not_reached();
++                }
++
++        return result;
+ }
+ 
+ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
+@@ -1904,7 +1949,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
+                         value = delete_trailing_chars(vbuf, NULL);
+                 }
+ 
+-                return token_match_string(token, value);
++                return token_match_string(event, token, value, /* log_result = */ true);
+ 
+         case SUBST_TYPE_SUBSYS:
+                 if (udev_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) < 0)
+@@ -1914,7 +1959,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
+                 if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE))
+                         delete_trailing_chars(vbuf, NULL);
+ 
+-                return token_match_string(token, vbuf);
++                return token_match_string(event, token, vbuf, /* log_result = */ true);
+ 
+         default:
+                 assert_not_reached();
+@@ -2074,7 +2119,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         return log_event_error_errno(event, token, r, "Failed to get uevent action type: %m");
+ 
+-                return token_match_string(token, device_action_to_string(a));
++                return token_match_string(event, token, device_action_to_string(a), /* log_result = */ true);
+         }
+         case TK_M_DEVPATH: {
+                 const char *val;
+@@ -2083,7 +2128,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         return log_event_error_errno(event, token, r, "Failed to get devpath: %m");
+ 
+-                return token_match_string(token, val);
++                return token_match_string(event, token, val, /* log_result = */ true);
+         }
+         case TK_M_KERNEL:
+         case TK_M_PARENTS_KERNEL: {
+@@ -2093,21 +2138,23 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         return log_event_error_errno(event, token, r, "Failed to get sysname: %m");
+ 
+-                return token_match_string(token, val);
++                return token_match_string(event, token, val, /* log_result = */ true);
+         }
+         case TK_M_DEVLINK:
+                 FOREACH_DEVICE_DEVLINK(dev, val)
+-                        if (token_match_string(token, strempty(startswith(val, "/dev/"))) == (token->op == OP_MATCH))
++                        if (token_match_string(event, token, strempty(startswith(val, "/dev/")), /* log_result = */ false) == (token->op == OP_MATCH))
+                                 return token->op == OP_MATCH;
+                 return token->op == OP_NOMATCH;
++
+         case TK_M_NAME:
+-                return token_match_string(token, event->name);
++                return token_match_string(event, token, event->name, /* log_result = */ true);
++
+         case TK_M_ENV: {
+                 const char *val = NULL;
+ 
+                 (void) device_get_property_value_with_fallback(dev, token->data, event->worker ? event->worker->properties : NULL, &val);
+ 
+-                return token_match_string(token, val);
++                return token_match_string(event, token, val, /* log_result = */ true);
+         }
+         case TK_M_CONST: {
+                 const char *val, *k = token->data;
+@@ -2120,14 +2167,15 @@ static int udev_rule_apply_token_to_event(
+                         val = confidential_virtualization_to_string(detect_confidential_virtualization());
+                 else
+                         assert_not_reached();
+-                return token_match_string(token, val);
++                return token_match_string(event, token, val, /* log_result = */ true);
+         }
+         case TK_M_TAG:
+         case TK_M_PARENTS_TAG:
+                 FOREACH_DEVICE_CURRENT_TAG(dev, val)
+-                        if (token_match_string(token, val) == (token->op == OP_MATCH))
++                        if (token_match_string(event, token, val, /* log_result = */ false) == (token->op == OP_MATCH))
+                                 return token->op == OP_MATCH;
+                 return token->op == OP_NOMATCH;
++
+         case TK_M_SUBSYSTEM:
+         case TK_M_PARENTS_SUBSYSTEM: {
+                 const char *val;
+@@ -2138,7 +2186,7 @@ static int udev_rule_apply_token_to_event(
+                 else if (r < 0)
+                         return log_event_error_errno(event, token, r, "Failed to get subsystem: %m");
+ 
+-                return token_match_string(token, val);
++                return token_match_string(event, token, val, /* log_result = */ true);
+         }
+         case TK_M_DRIVER:
+         case TK_M_PARENTS_DRIVER: {
+@@ -2150,11 +2198,12 @@ static int udev_rule_apply_token_to_event(
+                 else if (r < 0)
+                         return log_event_error_errno(event, token, r, "Failed to get driver: %m");
+ 
+-                return token_match_string(token, val);
++                return token_match_string(event, token, val, /* log_result = */ true);
+         }
+         case TK_M_ATTR:
+         case TK_M_PARENTS_ATTR:
+                 return token_match_attr(token, dev, event);
++
+         case TK_M_SYSCTL: {
+                 _cleanup_free_ char *value = NULL;
+                 char buf[UDEV_PATH_SIZE];
+@@ -2166,7 +2215,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0 && r != -ENOENT)
+                         return log_event_error_errno(event, token, r, "Failed to read sysctl '%s': %m", buf);
+ 
+-                return token_match_string(token, strstrip(value));
++                return token_match_string(event, token, strstrip(value), /* log_result = */ true);
+         }
+         case TK_M_TEST: {
+                 mode_t mode = PTR_TO_MODE(token->data);
+@@ -2429,7 +2478,7 @@ static int udev_rule_apply_token_to_event(
+                 return token->op == (r > 0 ? OP_MATCH : OP_NOMATCH);
+         }
+         case TK_M_RESULT:
+-                return token_match_string(token, event->program_result);
++                return token_match_string(event, token, event->program_result, /* log_result = */ true);
+         case TK_A_OPTIONS_STRING_ESCAPE_NONE:
+                 event->esc = ESCAPE_NONE;
+                 break;
diff --git a/SOURCES/0169-udev-rules-introduce-udev_replace_chars_and_log.patch b/SOURCES/0169-udev-rules-introduce-udev_replace_chars_and_log.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e28599ab13b2065a75db1da2dec238fc04aa494e
--- /dev/null
+++ b/SOURCES/0169-udev-rules-introduce-udev_replace_chars_and_log.patch
@@ -0,0 +1,163 @@
+From 4106dbe04d90031197b4317b7c8ae36e2fb76984 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Wed, 15 Jan 2025 22:09:05 +0900
+Subject: [PATCH] udev-rules: introduce udev_replace_chars_and_log()
+
+And logs about replacement only when trace logging is enabled.
+
+(cherry picked from commit a79cd8c2be447367dc2d70615dc9e3e0c91d5cf4)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 74 +++++++++++++++++++++++--------------------
+ 1 file changed, 39 insertions(+), 35 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 35a3a9cc69..53370f6085 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -2079,7 +2079,7 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
+         return -ENOENT;
+ }
+ 
+-static size_t udev_replace_ifname(char *str) {
++static size_t udev_replace_ifname_strict(char *str) {
+         size_t replaced = 0;
+ 
+         assert(str);
+@@ -2095,6 +2095,35 @@ static size_t udev_replace_ifname(char *str) {
+         return replaced;
+ }
+ 
++static void udev_replace_ifname(UdevEvent *event, UdevRuleToken *token, char *buf) {
++        assert(event);
++        assert(token);
++        assert(buf);
++
++        size_t count;
++        if (naming_scheme_has(NAMING_REPLACE_STRICTLY))
++                count = udev_replace_ifname_strict(buf);
++        else
++                count = udev_replace_chars(buf, "/");
++        if (count > 0)
++                log_event_trace(event, token,
++                                "Replaced %zu character(s) from network interface name, results to \"%s\"",
++                                count, buf);
++}
++
++static void udev_replace_chars_and_log(UdevEvent *event, UdevRuleToken *token, char *buf, const char *allow, const char *what) {
++        assert(event);
++        assert(token);
++        assert(buf);
++        assert(what);
++
++        size_t count = udev_replace_chars(buf, allow);
++        if (count > 0)
++                log_event_trace(event, token,
++                                "Replaced %zu character(s) from %s, results to \"%s\"",
++                                count, what, buf);
++}
++
+ static int udev_rule_apply_token_to_event(
+                 UdevRuleToken *token,
+                 sd_device *dev,
+@@ -2260,7 +2289,6 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_M_PROGRAM: {
+                 char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE];
+-                size_t count;
+ 
+                 event->program_result = mfree(event->program_result);
+ 
+@@ -2279,11 +2307,7 @@ static int udev_rule_apply_token_to_event(
+                 }
+ 
+                 delete_trailing_chars(result, "\n");
+-                count = udev_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
+-                if (count > 0)
+-                        log_event_debug(event, token,
+-                                        "Replaced %zu character(s) in result of \"%s\"",
+-                                        count, buf);
++                udev_replace_chars_and_log(event, token, result, UDEV_ALLOWED_CHARS_INPUT, "command output");
+ 
+                 event->program_result = strdup(result);
+                 return token->op == OP_MATCH;
+@@ -2644,7 +2668,7 @@ static int udev_rule_apply_token_to_event(
+         case TK_A_ENV: {
+                 const char *val, *name = token->data;
+                 char value_new[UDEV_NAME_SIZE], *p = value_new;
+-                size_t count, l = sizeof(value_new);
++                size_t l = sizeof(value_new);
+ 
+                 if (isempty(token->value)) {
+                         if (token->op == OP_ADD)
+@@ -2670,13 +2694,8 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, p, l, "property value"))
+                         return true;
+ 
+-                if (event->esc == ESCAPE_REPLACE) {
+-                        count = udev_replace_chars(p, NULL);
+-                        if (count > 0)
+-                                log_event_debug(event, token,
+-                                                "Replaced %zu slash(es) from result of ENV{%s}%s=\"%s\"",
+-                                                count, name, token->op == OP_ADD ? "+" : "", token->value);
+-                }
++                if (event->esc == ESCAPE_REPLACE)
++                        udev_replace_chars_and_log(event, token, p, /* allow = */ NULL, "property value");
+ 
+                 r = device_add_property(dev, name, value_new);
+                 if (r < 0)
+@@ -2705,7 +2724,6 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_NAME: {
+                 char buf[UDEV_PATH_SIZE];
+-                size_t count;
+ 
+                 if (event->name_final)
+                         break;
+@@ -2722,16 +2740,9 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "network interface name"))
+                         return true;
+ 
+-                if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
+-                        if (naming_scheme_has(NAMING_REPLACE_STRICTLY))
+-                                count = udev_replace_ifname(buf);
+-                        else
+-                                count = udev_replace_chars(buf, "/");
+-                        if (count > 0)
+-                                log_event_debug(event, token,
+-                                                "Replaced %zu character(s) from result of NAME=\"%s\"",
+-                                                count, token->value);
+-                }
++                if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE))
++                        udev_replace_ifname(event, token, buf);
++
+                 r = free_and_strdup_warn(&event->name, buf);
+                 if (r < 0)
+                         return r;
+@@ -2741,7 +2752,6 @@ static int udev_rule_apply_token_to_event(
+         }
+         case TK_A_DEVLINK: {
+                 char buf[UDEV_PATH_SIZE];
+-                size_t count;
+ 
+                 if (event->devlink_final)
+                         break;
+@@ -2759,15 +2769,9 @@ static int udev_rule_apply_token_to_event(
+ 
+                 /* By default or string_escape=none, allow multiple symlinks separated by spaces. */
+                 if (event->esc == ESCAPE_UNSET)
+-                        count = udev_replace_chars(buf, /* allow = */ "/ ");
++                        udev_replace_chars_and_log(event, token, buf, /* allow = */ "/ ", "device node symlink");
+                 else if (event->esc == ESCAPE_REPLACE)
+-                        count = udev_replace_chars(buf, /* allow = */ "/");
+-                else
+-                        count = 0;
+-                if (count > 0)
+-                        log_event_debug(event, token,
+-                                        "Replaced %zu character(s) from result of SYMLINK=\"%s\"",
+-                                        count, token->value);
++                        udev_replace_chars_and_log(event, token, buf, /* allow = */ "/", "device node symlink");
+ 
+                 for (const char *p = buf;;) {
+                         _cleanup_free_ char *path = NULL;
diff --git a/SOURCES/0170-udev-rules-ignore-whole-command-result-if-it-is-too-.patch b/SOURCES/0170-udev-rules-ignore-whole-command-result-if-it-is-too-.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a18736f21b22a67948fa5af99234161ddf6e044b
--- /dev/null
+++ b/SOURCES/0170-udev-rules-ignore-whole-command-result-if-it-is-too-.patch
@@ -0,0 +1,38 @@
+From 95534ef8f7558b5f7f93991043441adb9c7ddf33 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Wed, 15 Jan 2025 23:43:37 +0900
+Subject: [PATCH] udev-rules: ignore whole command result if it is too long and
+ does not contain newline
+
+(cherry picked from commit 68b1e1b61ab333bb4dee4a43373bad54080e755f)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 53370f6085..9aa98a3b3c 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -2379,15 +2379,18 @@ static int udev_rule_apply_token_to_event(
+                 }
+ 
+                 if (truncated) {
+-                        bool found = false;
++                        log_event_debug(event, token, "Result of \"%s\" is too long and truncated, ignoring the last line of the result.", buf);
+ 
+                         /* Drop the last line. */
++                        bool found = false;
+                         for (char *p = PTR_SUB1(buf + strlen(buf), buf); p; p = PTR_SUB1(p, buf))
+                                 if (strchr(NEWLINE, *p)) {
+                                         *p = '\0';
+                                         found = true;
+-                                } else if (found)
+                                         break;
++                                }
++                        if (!found)
++                                buf[0] = '\0';
+                 }
+ 
+                 r = strv_split_newlines_full(&lines, result, EXTRACT_RETAIN_ESCAPE);
diff --git a/SOURCES/0171-udev-rules-update-log-messages.patch b/SOURCES/0171-udev-rules-update-log-messages.patch
new file mode 100644
index 0000000000000000000000000000000000000000..683d5a96a46dc5529609d3d48059f60ea7224df6
--- /dev/null
+++ b/SOURCES/0171-udev-rules-update-log-messages.patch
@@ -0,0 +1,900 @@
+From 9345b1fd8d04c0b222b94dd68e0f255c7358c792 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Wed, 15 Jan 2025 23:47:05 +0900
+Subject: [PATCH] udev-rules: update log messages
+
+This also adds trace logs about token result, and skipping assignment
+caused by final value already assigned.
+
+(cherry picked from commit 354d063fbcadb277094e47a79e7cde62ae269199)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 433 ++++++++++++++++++++++++------------------
+ 1 file changed, 248 insertions(+), 185 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index 9aa98a3b3c..c5fa2c660c 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -297,6 +297,37 @@ static bool token_is_for_parents(UdevRuleToken *token) {
+ #define log_event_trace(event, ...)                                     \
+         _log_event_trace(event, UNIQ_T(e, UNIQ), __VA_ARGS__)
+ 
++#define _log_event_result(event, token, result, result_u)               \
++        ({                                                              \
++                bool result_u = (result);                               \
++                                                                        \
++                log_event_trace(event, token, "%s",                     \
++                                result_u ? "PASS" : "FAIL");            \
++                result_u;                                               \
++        })
++
++#define log_event_result(event, token, result)                          \
++        _log_event_result(event, token, result, UNIQ_T(r, UNIQ))
++
++#define log_event_done(event, token)                                    \
++        ({                                                              \
++                log_event_trace(event, token, "DONE");                  \
++                true;                                                   \
++        })
++
++#define _log_event_final_set(event, token, token_u)                     \
++        ({                                                              \
++                UdevRuleToken *token_u = ASSERT_PTR(token);             \
++                                                                        \
++                log_event_trace(event, token_u,                         \
++                                "Already assigned final value, ignoring further %s.", \
++                                token_u->op == OP_REMOVE ? "modification" : "assignment"); \
++                true;                                                   \
++        })
++
++#define log_event_final_set(event, token)                       \
++        _log_event_final_set(event, token, UNIQ_T(t, UNIQ))
++
+ #define _log_event_truncated(event, token, token_u, what, format)       \
+         ({                                                              \
+                 UdevRuleToken *token_u = ASSERT_PTR(token);             \
+@@ -358,16 +389,6 @@ static bool token_is_for_parents(UdevRuleToken *token) {
+                              "Invalid value \"%s\" for %s (char %zu: %s), ignoring.", \
+                              value, key, offset, hint)
+ 
+-static void log_unknown_owner(sd_device *dev, UdevRuleLine *line, int error, const char *entity, const char *name) {
+-        assert(line);
+-        ASSERT_NON_ZERO(error);
+-
+-        if (IN_SET(abs(error), ENOENT, ESRCH))
+-                log_udev_rule_line_full(dev, line, LOG_ERR, error, "Unknown %s '%s', ignoring.", entity, name);
+-        else
+-                log_udev_rule_line_full(dev, line, LOG_ERR, error, "Failed to resolve %s '%s', ignoring: %m", entity, name);
+-}
+-
+ /*** Other functions ***/
+ 
+ static UdevRuleToken* udev_rule_token_free(UdevRuleToken *token) {
+@@ -452,7 +473,11 @@ static int rule_resolve_user(UdevRuleLine *rule_line, const char *name, uid_t *r
+ 
+         r = get_user_creds(&name, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+         if (r < 0) {
+-                log_unknown_owner(NULL, rule_line, r, "user", name);
++                if (IN_SET(r, -ENOENT, -ESRCH))
++                        log_line_error_errno(rule_line, r, "Unknown user '%s', ignoring.", name);
++                else
++                        log_line_error_errno(rule_line, r, "Failed to resolve user '%s', ignoring: %m", name);
++
+                 *ret = UID_INVALID;
+                 return 0;
+         }
+@@ -488,7 +513,11 @@ static int rule_resolve_group(UdevRuleLine *rule_line, const char *name, gid_t *
+ 
+         r = get_group_creds(&name, &gid, USER_CREDS_ALLOW_MISSING);
+         if (r < 0) {
+-                log_unknown_owner(NULL, rule_line, r, "group", name);
++                if (IN_SET(r, -ENOENT, -ESRCH))
++                        log_line_error_errno(rule_line, r, "Unknown group '%s', ignoring.", name);
++                else
++                        log_line_error_errno(rule_line, r, "Failed to resolve group '%s', ignoring: %m", name);
++
+                 *ret = GID_INVALID;
+                 return 0;
+         }
+@@ -2011,30 +2040,6 @@ static int get_property_from_string(char *line, char **ret_key, char **ret_value
+         return 1;
+ }
+ 
+-static int import_parent_into_properties(sd_device *dev, const char *filter) {
+-        sd_device *parent;
+-        int r;
+-
+-        assert(dev);
+-        assert(filter);
+-
+-        r = sd_device_get_parent(dev, &parent);
+-        if (r == -ENOENT)
+-                return 0;
+-        if (r < 0)
+-                return r;
+-
+-        FOREACH_DEVICE_PROPERTY(parent, key, val) {
+-                if (fnmatch(filter, key, 0) != 0)
+-                        continue;
+-                r = device_add_property(dev, key, val);
+-                if (r < 0)
+-                        return r;
+-        }
+-
+-        return 1;
+-}
+-
+ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
+         _cleanup_closedir_ DIR *dir = NULL;
+         char buf[UDEV_PATH_SIZE], *p;
+@@ -2172,8 +2177,8 @@ static int udev_rule_apply_token_to_event(
+         case TK_M_DEVLINK:
+                 FOREACH_DEVICE_DEVLINK(dev, val)
+                         if (token_match_string(event, token, strempty(startswith(val, "/dev/")), /* log_result = */ false) == (token->op == OP_MATCH))
+-                                return token->op == OP_MATCH;
+-                return token->op == OP_NOMATCH;
++                                return log_event_result(event, token, token->op == OP_MATCH);
++                return log_event_result(event, token, token->op == OP_NOMATCH);
+ 
+         case TK_M_NAME:
+                 return token_match_string(event, token, event->name, /* log_result = */ true);
+@@ -2202,8 +2207,8 @@ static int udev_rule_apply_token_to_event(
+         case TK_M_PARENTS_TAG:
+                 FOREACH_DEVICE_CURRENT_TAG(dev, val)
+                         if (token_match_string(event, token, val, /* log_result = */ false) == (token->op == OP_MATCH))
+-                                return token->op == OP_MATCH;
+-                return token->op == OP_NOMATCH;
++                                return log_event_result(event, token, token->op == OP_MATCH);
++                return log_event_result(event, token, token->op == OP_NOMATCH);
+ 
+         case TK_M_SUBSYSTEM:
+         case TK_M_PARENTS_SUBSYSTEM: {
+@@ -2242,7 +2247,7 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = sysctl_read(sysctl_normalize(buf), &value);
+                 if (r < 0 && r != -ENOENT)
+-                        return log_event_error_errno(event, token, r, "Failed to read sysctl '%s': %m", buf);
++                        return log_event_error_errno(event, token, r, "Failed to read sysctl \"%s\": %m", buf);
+ 
+                 return token_match_string(event, token, strstrip(value), /* log_result = */ true);
+         }
+@@ -2269,23 +2274,26 @@ static int udev_rule_apply_token_to_event(
+                         assert(!truncated);
+                         strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL);
+                         if (truncated)
+-                                return false;
++                                return log_event_result(event, token, false);
+                 }
+ 
+                 r = attr_subst_subdir(buf);
+                 if (r == -ENOENT)
+-                        return token->op == OP_NOMATCH;
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r, "Failed to test for the existence of '%s': %m", buf);
++                        return log_event_error_errno(event, token, r, "Failed to test for the existence of \"%s\": %m", buf);
+ 
+-                if (stat(buf, &statbuf) < 0)
+-                        return token->op == OP_NOMATCH;
++                if (stat(buf, &statbuf) < 0) {
++                        if (errno != ENOENT)
++                                log_event_warning_errno(event, token, errno, "Failed to stat \"%s\", ignoring: %m", buf);
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
++                }
+ 
+                 if (mode == MODE_INVALID)
+-                        return token->op == OP_MATCH;
++                        return log_event_result(event, token, token->op == OP_MATCH);
+ 
+                 match = (statbuf.st_mode & mode) > 0;
+-                return token->op == (match ? OP_MATCH : OP_NOMATCH);
++                return log_event_result(event, token, token->op == (match ? OP_MATCH : OP_NOMATCH));
+         }
+         case TK_M_PROGRAM: {
+                 char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE];
+@@ -2295,7 +2303,7 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "command"))
+                         return false;
+ 
+-                log_event_debug(event, token, "Running PROGRAM=\"%s\"", buf);
++                log_event_debug(event, token, "Running command \"%s\"", buf);
+ 
+                 r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof(result), NULL);
+                 if (r != 0) {
+@@ -2303,14 +2311,14 @@ static int udev_rule_apply_token_to_event(
+                                 log_event_warning_errno(event, token, r, "Failed to execute \"%s\": %m", buf);
+                         else /* returned value is positive when program fails */
+                                 log_event_debug(event, token, "Command \"%s\" returned %d (error)", buf, r);
+-                        return token->op == OP_NOMATCH;
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 }
+ 
+                 delete_trailing_chars(result, "\n");
+                 udev_replace_chars_and_log(event, token, result, UDEV_ALLOWED_CHARS_INPUT, "command output");
+ 
+                 event->program_result = strdup(result);
+-                return token->op == OP_MATCH;
++                return log_event_result(event, token, token->op == OP_MATCH);
+         }
+         case TK_M_IMPORT_FILE: {
+                 _cleanup_fclose_ FILE *f = NULL;
+@@ -2319,13 +2327,13 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "file name to be imported"))
+                         return false;
+ 
+-                log_event_debug(event, token, "Importing properties from '%s'", buf);
++                log_event_debug(event, token, "Importing properties from \"%s\"", buf);
+ 
+                 f = fopen(buf, "re");
+                 if (!f) {
+                         if (errno != ENOENT)
+-                                return log_event_error_errno(event, token, errno, "Failed to open '%s': %m", buf);
+-                        return token->op == OP_NOMATCH;
++                                return log_event_error_errno(event, token, errno, "Failed to open \"%s\": %m", buf);
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 }
+ 
+                 for (;;) {
+@@ -2334,16 +2342,16 @@ static int udev_rule_apply_token_to_event(
+ 
+                         r = read_line(f, LONG_LINE_MAX, &line);
+                         if (r < 0) {
+-                                log_event_debug_errno(event, token, r, "Failed to read '%s', ignoring: %m", buf);
+-                                return token->op == OP_NOMATCH;
++                                log_event_debug_errno(event, token, r, "Failed to read \"%s\", ignoring: %m", buf);
++                                return log_event_result(event, token, token->op == OP_NOMATCH);
+                         }
+                         if (r == 0)
+-                                break;
++                                return log_event_result(event, token, token->op == OP_MATCH);
+ 
+                         r = get_property_from_string(line, &key, &value);
+                         if (r < 0) {
+                                 log_event_debug_errno(event, token, r,
+-                                                      "Failed to parse key and value from '%s', ignoring: %m",
++                                                      "Failed to parse key and value from \"%s\", ignoring: %m",
+                                                       line);
+                                 continue;
+                         }
+@@ -2355,9 +2363,10 @@ static int udev_rule_apply_token_to_event(
+                                 return log_event_error_errno(event, token, r,
+                                                              "Failed to add property %s=%s: %m",
+                                                              key, value);
++                        log_event_trace(event, token, "Imported property \"%s=%s\".", key, value);
+                 }
+ 
+-                return token->op == OP_MATCH;
++                assert_not_reached();
+         }
+         case TK_M_IMPORT_PROGRAM: {
+                 _cleanup_strv_free_ char **lines = NULL;
+@@ -2367,15 +2376,15 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "command"))
+                         return false;
+ 
+-                log_event_debug(event, token, "Importing properties from results of '%s'", buf);
++                log_event_debug(event, token, "Importing properties from results of \"%s\"", buf);
+ 
+                 r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof result, &truncated);
+                 if (r != 0) {
+                         if (r < 0)
+-                                log_event_warning_errno(event, token, r, "Failed to execute '%s', ignoring: %m", buf);
++                                log_event_warning_errno(event, token, r, "Failed to execute \"%s\", ignoring: %m", buf);
+                         else /* returned value is positive when program fails */
+                                 log_event_debug(event, token, "Command \"%s\" returned %d (error), ignoring", buf, r);
+-                        return token->op == OP_NOMATCH;
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 }
+ 
+                 if (truncated) {
+@@ -2399,7 +2408,7 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0) {
+                         log_event_warning_errno(event, token, r,
+                                                 "Failed to extract lines from result of command \"%s\", ignoring: %m", buf);
+-                        return false;
++                        return log_event_result(event, token, false);
+                 }
+ 
+                 STRV_FOREACH(line, lines) {
+@@ -2408,7 +2417,7 @@ static int udev_rule_apply_token_to_event(
+                         r = get_property_from_string(*line, &key, &value);
+                         if (r < 0) {
+                                 log_event_debug_errno(event, token, r,
+-                                                      "Failed to parse key and value from '%s', ignoring: %m",
++                                                      "Failed to parse key and value from \"%s\", ignoring: %m",
+                                                       *line);
+                                 continue;
+                         }
+@@ -2420,9 +2429,10 @@ static int udev_rule_apply_token_to_event(
+                                 return log_event_error_errno(event, token, r,
+                                                              "Failed to add property %s=%s: %m",
+                                                              key, value);
++                        log_event_trace(event, token, "Imported property \"%s=%s\".", key, value);
+                 }
+ 
+-                return token->op == OP_MATCH;
++                return log_event_result(event, token, token->op == OP_MATCH);
+         }
+         case TK_M_IMPORT_BUILTIN: {
+                 UdevBuiltinCommand cmd = PTR_TO_UDEV_BUILTIN_CMD(token->data);
+@@ -2433,10 +2443,10 @@ static int udev_rule_apply_token_to_event(
+                 if (udev_builtin_run_once(cmd)) {
+                         /* check if we ran already */
+                         if (event->builtin_run & mask) {
+-                                log_event_debug(event, token, "Skipping builtin '%s' in IMPORT key",
++                                log_event_debug(event, token, "Builtin command \"%s\" has already run, skipping.",
+                                                 udev_builtin_name(cmd));
+                                 /* return the result from earlier run */
+-                                return token->op == (event->builtin_ret & mask ? OP_NOMATCH : OP_MATCH);
++                                return log_event_result(event, token, token->op == (event->builtin_ret & mask ? OP_NOMATCH : OP_MATCH));
+                         }
+                         /* mark as ran */
+                         event->builtin_run |= mask;
+@@ -2445,34 +2455,36 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "builtin command"))
+                         return false;
+ 
+-                log_event_debug(event, token, "Importing properties from results of builtin command '%s'", buf);
++                log_event_debug(event, token, "Importing properties from results of builtin command \"%s\".", buf);
+ 
+                 r = udev_builtin_run(event, cmd, buf);
+                 if (r < 0) {
+                         /* remember failure */
+-                        log_event_debug_errno(event, token, r, "Failed to run builtin '%s': %m", buf);
++                        log_event_debug_errno(event, token, r, "Failed to run builtin \"%s\": %m", buf);
+                         event->builtin_ret |= mask;
+                 }
+-                return token->op == (r >= 0 ? OP_MATCH : OP_NOMATCH);
++                return log_event_result(event, token, token->op == (r >= 0 ? OP_MATCH : OP_NOMATCH));
+         }
+         case TK_M_IMPORT_DB: {
+                 const char *val;
+ 
+                 if (!event->dev_db_clone)
+-                        return token->op == OP_NOMATCH;
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 r = sd_device_get_property_value(event->dev_db_clone, token->value, &val);
+                 if (r == -ENOENT)
+-                        return token->op == OP_NOMATCH;
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 if (r < 0)
+                         return log_event_error_errno(event, token, r,
+-                                                     "Failed to get property '%s' from database: %m",
++                                                     "Failed to get property \"%s\" from database: %m",
+                                                      token->value);
+ 
+                 r = device_add_property(dev, token->value, val);
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r, "Failed to add property '%s=%s': %m",
++                        return log_event_error_errno(event, token, r, "Failed to add property \"%s=%s\": %m",
+                                                      token->value, val);
+-                return token->op == OP_MATCH;
++                log_event_trace(event, token, "Imported property \"%s=%s\".", token->value, val);
++
++                return log_event_result(event, token, token->op == OP_MATCH);
+         }
+         case TK_M_IMPORT_CMDLINE: {
+                 _cleanup_free_ char *value = NULL;
+@@ -2480,16 +2492,19 @@ static int udev_rule_apply_token_to_event(
+                 r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_IGNORE_EFI_OPTIONS, &value);
+                 if (r < 0)
+                         return log_event_error_errno(event, token, r,
+-                                                     "Failed to read '%s' option from /proc/cmdline: %m",
++                                                     "Failed to read \"%s\" option from /proc/cmdline: %m",
+                                                      token->value);
+                 if (r == 0)
+-                        return token->op == OP_NOMATCH;
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+ 
+-                r = device_add_property(dev, token->value, value ?: "1");
++                const char *val = value ?: "1";
++                r = device_add_property(dev, token->value, val);
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r, "Failed to add property '%s=%s': %m",
+-                                                     token->value, value ?: "1");
+-                return token->op == OP_MATCH;
++                        return log_event_error_errno(event, token, r, "Failed to add property \"%s=%s\": %m",
++                                                     token->value, val);
++                log_event_trace(event, token, "Imported property \"%s=%s\".", token->value, val);
++
++                return log_event_result(event, token, token->op == OP_MATCH);
+         }
+         case TK_M_IMPORT_PARENT: {
+                 char buf[UDEV_PATH_SIZE];
+@@ -2497,35 +2512,56 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "property name"))
+                         return false;
+ 
+-                r = import_parent_into_properties(dev, buf);
++                sd_device *parent;
++                r = sd_device_get_parent(dev, &parent);
++                if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
++                        return log_event_result(event, token, token->op == OP_NOMATCH);
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r,
+-                                                     "Failed to import properties '%s' from parent: %m",
+-                                                     buf);
+-                return token->op == (r > 0 ? OP_MATCH : OP_NOMATCH);
++                        return log_event_error_errno(event, token, r, "Failed to get parent device: %m");
++
++                bool have = false;
++                FOREACH_DEVICE_PROPERTY(parent, key, val) {
++                        if (fnmatch(buf, key, 0) != 0)
++                                continue;
++
++                        r = device_add_property(dev, key, val);
++                        if (r < 0)
++                                return log_event_error_errno(event, token, r, "Failed to add property \"%s=%s\": %m", key, val);
++                        log_event_trace(event, token, "Imported property \"%s=%s\".", key, val);
++                        have = true;
++                }
++
++                return log_event_result(event, token, token->op == (have ? OP_MATCH : OP_NOMATCH));
+         }
+         case TK_M_RESULT:
+                 return token_match_string(event, token, event->program_result, /* log_result = */ true);
++
+         case TK_A_OPTIONS_STRING_ESCAPE_NONE:
+                 event->esc = ESCAPE_NONE;
+-                break;
++                return log_event_done(event, token);
++
+         case TK_A_OPTIONS_STRING_ESCAPE_REPLACE:
+                 event->esc = ESCAPE_REPLACE;
+-                break;
++                return log_event_done(event, token);
++
+         case TK_A_OPTIONS_DB_PERSIST:
+                 device_set_db_persist(dev);
+-                break;
++                return log_event_done(event, token);
++
+         case TK_A_OPTIONS_INOTIFY_WATCH:
+                 if (event->inotify_watch_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->inotify_watch_final = true;
+ 
+                 event->inotify_watch = token->data;
+-                break;
++                return log_event_done(event, token);
++
+         case TK_A_OPTIONS_DEVLINK_PRIORITY:
+                 device_set_devlink_priority(dev, PTR_TO_INT(token->data));
+-                break;
++                return log_event_done(event, token);
++
+         case TK_A_OPTIONS_LOG_LEVEL: {
+                 int level = PTR_TO_INT(token->data);
+ 
+@@ -2547,14 +2583,15 @@ static int udev_rule_apply_token_to_event(
+                         event->log_level_was_debug = true;
+                 }
+ 
+-                break;
++                return log_event_done(event, token);
+         }
+         case TK_A_OWNER: {
+                 char owner[UDEV_NAME_SIZE];
+                 const char *ow = owner;
+ 
+                 if (event->owner_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->owner_final = true;
+ 
+@@ -2562,18 +2599,21 @@ static int udev_rule_apply_token_to_event(
+                         return true;
+ 
+                 r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+-                if (r < 0)
+-                        log_unknown_owner(dev, token->rule_line, r, "user", owner);
++                if (IN_SET(r, -ENOENT, -ESRCH))
++                        log_event_error_errno(event, token, r, "Unknown user \"%s\", ignoring.", owner);
++                else if (r < 0)
++                        log_event_error_errno(event, token, r, "Failed to resolve user \"%s\", ignoring: %m", owner);
+                 else
+-                        log_event_debug(event, token, "OWNER %s(%u)", owner, event->uid);
+-                break;
++                        log_event_debug(event, token, "Set owner: %s(%u)", owner, event->uid);
++                return true;
+         }
+         case TK_A_GROUP: {
+                 char group[UDEV_NAME_SIZE];
+                 const char *gr = group;
+ 
+                 if (event->group_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->group_final = true;
+ 
+@@ -2581,17 +2621,20 @@ static int udev_rule_apply_token_to_event(
+                         return true;
+ 
+                 r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
+-                if (r < 0)
+-                        log_unknown_owner(dev, token->rule_line, r, "group", group);
++                if (IN_SET(r, -ENOENT, -ESRCH))
++                        log_event_error_errno(event, token, r, "Unknown group \"%s\", ignoring.", group);
++                else if (r < 0)
++                        log_event_error_errno(event, token, r, "Failed to resolve group \"%s\", ignoring: %m", group);
+                 else
+-                        log_event_debug(event, token, "GROUP %s(%u)", group, event->gid);
+-                break;
++                        log_event_debug(event, token, "Set group: %s(%u)", group, event->gid);
++                return true;
+         }
+         case TK_A_MODE: {
+                 char mode_str[UDEV_NAME_SIZE];
+ 
+                 if (event->mode_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->mode_final = true;
+ 
+@@ -2600,41 +2643,44 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = parse_mode(mode_str, &event->mode);
+                 if (r < 0)
+-                        log_event_error_errno(event, token, r, "Failed to parse mode '%s', ignoring: %m", mode_str);
++                        log_event_error_errno(event, token, r, "Failed to parse mode \"%s\", ignoring: %m", mode_str);
+                 else
+-                        log_event_debug(event, token, "MODE %#o", event->mode);
+-                break;
++                        log_event_debug(event, token, "Set mode: %#o", event->mode);
++                return true;
+         }
+         case TK_A_OWNER_ID:
+                 if (event->owner_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->owner_final = true;
+-                if (!token->data)
+-                        break;
+-                event->uid = PTR_TO_UID(token->data);
+-                log_event_debug(event, token, "OWNER %u", event->uid);
+-                break;
++
++                event->uid = PTR_TO_UID(ASSERT_PTR(token->data));
++                log_event_debug(event, token, "Set owner ID: %u", event->uid);
++                return true;
++
+         case TK_A_GROUP_ID:
+                 if (event->group_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->group_final = true;
+-                if (!token->data)
+-                        break;
+-                event->gid = PTR_TO_GID(token->data);
+-                log_event_debug(event, token, "GROUP %u", event->gid);
+-                break;
++
++                event->gid = PTR_TO_GID(ASSERT_PTR(token->data));
++                log_event_debug(event, token, "Set group ID: %u", event->gid);
++                return true;
++
+         case TK_A_MODE_ID:
+                 if (event->mode_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->mode_final = true;
+-                if (!token->data)
+-                        break;
+-                event->mode = PTR_TO_MODE(token->data);
+-                log_event_debug(event, token, "MODE %#o", event->mode);
+-                break;
++
++                event->mode = PTR_TO_MODE(ASSERT_PTR(token->data));
++                log_event_debug(event, token, "Set mode: %#o", event->mode);
++                return true;
++
+         case TK_A_SECLABEL: {
+                 _cleanup_free_ char *name = NULL, *label = NULL;
+                 char label_str[UDEV_LINE_SIZE] = {};
+@@ -2660,13 +2706,13 @@ static int udev_rule_apply_token_to_event(
+                 if (r == -ENOMEM)
+                         return log_oom();
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r, "Failed to store SECLABEL{%s}='%s': %m", name, label);
++                        return log_event_error_errno(event, token, r, "Failed to store security label \"%s=%s\": %m", name, label);
+ 
+-                log_event_debug(event, token, "SECLABEL{%s}='%s'", name, label);
++                log_event_debug(event, token, "Set security label: %s=%s", name, label);
+ 
+                 TAKE_PTR(name);
+                 TAKE_PTR(label);
+-                break;
++                return true;
+         }
+         case TK_A_ENV: {
+                 const char *val, *name = token->data;
+@@ -2675,11 +2721,13 @@ static int udev_rule_apply_token_to_event(
+ 
+                 if (isempty(token->value)) {
+                         if (token->op == OP_ADD)
+-                                break;
++                                return log_event_done(event, token);
++
+                         r = device_add_property(dev, name, NULL);
+                         if (r < 0)
+-                                return log_event_error_errno(event, token, r, "Failed to remove property '%s': %m", name);
+-                        break;
++                                return log_event_error_errno(event, token, r, "Failed to remove property \"%s\": %m", name);
++                        log_event_trace(event, token, "Removed property \"%s\".", name);
++                        return true;
+                 }
+ 
+                 if (token->op == OP_ADD &&
+@@ -2688,9 +2736,9 @@ static int udev_rule_apply_token_to_event(
+                         l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
+                         if (truncated) {
+                                 log_event_warning(event, token,
+-                                                  "The buffer for the property '%s' is full, "
+-                                                  "refusing to append the new value '%s'.", name, token->value);
+-                                break;
++                                                  "The buffer for the property is full, refusing to append new property \"%s=%s\".",
++                                                  name, token->value);
++                                return true;
+                         }
+                 }
+ 
+@@ -2702,8 +2750,9 @@ static int udev_rule_apply_token_to_event(
+ 
+                 r = device_add_property(dev, name, value_new);
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r, "Failed to add property '%s=%s': %m", name, value_new);
+-                break;
++                        return log_event_error_errno(event, token, r, "Failed to set property \"%s=%s\": %m", name, value_new);
++                log_event_trace(event, token, "Set property \"%s=%s\".", name, value_new);
++                return true;
+         }
+         case TK_A_TAG: {
+                 char buf[UDEV_PATH_SIZE];
+@@ -2711,33 +2760,39 @@ static int udev_rule_apply_token_to_event(
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "tag name"))
+                         return true;
+ 
++                if (token->op == OP_REMOVE) {
++                        device_remove_tag(dev, buf);
++                        log_event_trace(event, token, "Removed tag \"%s\".", buf);
++                        return true;
++                }
++
++                assert(IN_SET(token->op, OP_ASSIGN, OP_ADD));
++
+                 if (token->op == OP_ASSIGN)
+                         device_cleanup_tags(dev);
+ 
+-                if (token->op == OP_REMOVE)
+-                        device_remove_tag(dev, buf);
+-                else {
+-                        r = device_add_tag(dev, buf, true);
+-                        if (r == -ENOMEM)
+-                                return log_oom();
+-                        if (r < 0)
+-                                log_event_warning_errno(event, token, r, "Failed to add tag '%s', ignoring: %m", buf);
+-                }
+-                break;
++                r = device_add_tag(dev, buf, /* both = */ true);
++                if (r == -ENOMEM)
++                        return log_oom();
++                if (r < 0)
++                        log_event_warning_errno(event, token, r, "Failed to %s tag \"%s\", ignoring: %m",
++                                                token->op == OP_ASSIGN ? "set" : "add", buf);
++                else
++                        log_event_trace(event, token, "%s tag \"%s\".", token->op == OP_ASSIGN ? "Set" : "Added", buf);
++                return true;
+         }
+         case TK_A_NAME: {
+                 char buf[UDEV_PATH_SIZE];
+ 
+                 if (event->name_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->name_final = true;
+ 
+                 if (sd_device_get_ifindex(dev, NULL) < 0) {
+-                        log_event_error(event, token,
+-                                        "Only network interfaces can be renamed, ignoring NAME=\"%s\".",
+-                                        token->value);
+-                        break;
++                        log_event_warning(event, token, "Only network interfaces can be renamed, ignoring.");
++                        return true;
+                 }
+ 
+                 if (!apply_format_value(event, token, buf, sizeof(buf), "network interface name"))
+@@ -2750,18 +2805,23 @@ static int udev_rule_apply_token_to_event(
+                 if (r < 0)
+                         return r;
+ 
+-                log_event_debug(event, token, "NAME '%s'", event->name);
+-                break;
++                log_event_debug(event, token, "Set network interface name: %s", event->name);
++                return true;
+         }
+         case TK_A_DEVLINK: {
+                 char buf[UDEV_PATH_SIZE];
+ 
+                 if (event->devlink_final)
+-                        break;
+-                if (sd_device_get_devnum(dev, NULL) < 0)
+-                        break;
++                        return log_event_final_set(event, token);
++
++                if (sd_device_get_devnum(dev, NULL) < 0) {
++                        log_event_debug(event, token, "Device does not have device node, ignoring to manage device node symlink.");
++                        return true;
++                }
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->devlink_final = true;
++
+                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
+                         device_cleanup_devlinks(dev);
+ 
+@@ -2783,31 +2843,31 @@ static int udev_rule_apply_token_to_event(
+                         if (r == -ENOMEM)
+                                 return log_oom();
+                         if (r < 0) {
+-                                log_warning_errno(r, "Failed to extract first path in SYMLINK=, ignoring: %m");
+-                                break;
++                                log_warning_errno(r, "Failed to extract first path in device node symlinks, ignoring: %m");
++                                return true;
+                         }
+                         if (r == 0)
+-                                break;
++                                return true;
+ 
+                         if (token->op == OP_REMOVE) {
+                                 r = device_remove_devlink(dev, path);
+                                 if (r == -ENOMEM)
+                                         return log_oom();
+                                 if (r < 0)
+-                                        log_event_warning_errno(event, token, r, "Failed to remove devlink '%s', ignoring: %m", path);
++                                        log_event_warning_errno(event, token, r, "Failed to remove device node symlink \"%s\", ignoring: %m", path);
+                                 else if (r > 0)
+-                                        log_event_debug(event, token, "Dropped SYMLINK '%s'", path);
++                                        log_event_debug(event, token, "Removed device node symlink \"%s\"", path);
+                         } else {
+                                 r = device_add_devlink(dev, path);
+                                 if (r == -ENOMEM)
+                                         return log_oom();
+                                 if (r < 0)
+-                                        log_event_warning_errno(event, token, r, "Failed to add devlink '%s', ignoring: %m", path);
++                                        log_event_warning_errno(event, token, r, "Failed to add device node symlink \"%s\", ignoring: %m", path);
+                                 else if (r > 0)
+-                                        log_event_debug(event, token, "Added SYMLINK '%s'", path);
++                                        log_event_debug(event, token, "Added device node symlink \"%s\".", path);
+                         }
+                 }
+-                break;
++                assert_not_reached();
+         }
+         case TK_A_ATTR: {
+                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
+@@ -2819,34 +2879,36 @@ static int udev_rule_apply_token_to_event(
+                         strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL);
+                         if (truncated) {
+                                 log_event_warning(event, token,
+-                                                  "The path to the attribute '%s/%s' is too long, refusing to set the attribute.",
++                                                  "The path to the attribute \"%s/%s\" is too long, refusing to set the attribute.",
+                                                   val, key_name);
+-                                break;
++                                return true;
+                         }
+                 }
+ 
+                 r = attr_subst_subdir(buf);
+                 if (r < 0) {
+-                        log_event_error_errno(event, token, r, "Could not find file matches '%s', ignoring: %m", buf);
+-                        break;
++                        log_event_error_errno(event, token, r, "Could not find file matches \"%s\", ignoring: %m", buf);
++                        return true;
+                 }
+ 
+                 if (!apply_format_value(event, token, value, sizeof(value), "attribute value"))
+                         return true;
+ 
+                 if (EVENT_MODE_DESTRUCTIVE(event)) {
+-                        log_event_debug(event, token, "Writing ATTR{'%s'}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Writing \"%s\" to sysfs attribute \"%s\".", value, buf);
+                         r = write_string_file(buf, value,
+                                               WRITE_STRING_FILE_VERIFY_ON_FAILURE |
+                                               WRITE_STRING_FILE_DISABLE_BUFFER |
+                                               WRITE_STRING_FILE_AVOID_NEWLINE |
+                                               WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
+                         if (r < 0)
+-                                log_event_error_errno(event, token, r, "Failed to write ATTR{%s}=\"%s\", ignoring: %m", buf, value);
++                                log_event_error_errno(event, token, r, "Failed to write \"%s\" to sysfs attribute \"%s\", ignoring: %m", value, buf);
++                        else
++                                log_event_done(event, token);
+                 } else
+-                        log_event_debug(event, token, "Running in test mode, skipping writing ATTR{%s}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Running in test mode, skipping writing \"%s\" to sysfs attribute \"%s\".", value, buf);
+ 
+-                break;
++                return true;
+         }
+         case TK_A_SYSCTL: {
+                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
+@@ -2860,14 +2922,16 @@ static int udev_rule_apply_token_to_event(
+                 sysctl_normalize(buf);
+ 
+                 if (EVENT_MODE_DESTRUCTIVE(event)) {
+-                        log_event_debug(event, token, "Writing SYSCTL{%s}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Writing \"%s\" to sysctl entry \"%s\".", value, buf);
+                         r = sysctl_write(buf, value);
+                         if (r < 0)
+-                                log_event_error_errno(event, token, r, "Failed to write SYSCTL{%s}=\"%s\", ignoring: %m", buf, value);
++                                log_event_error_errno(event, token, r, "Failed to write \"%s\" to sysctl entry \"%s\", ignoring: %m", value, buf);
++                        else
++                                log_event_done(event, token);
+                 } else
+-                        log_event_debug(event, token, "Running in test mode, skipping writing SYSCTL{%s}=\"%s\".", buf, value);
++                        log_event_debug(event, token, "Running in test mode, skipping writing \"%s\" to sysctl entry \"%s\".", value, buf);
+ 
+-                break;
++                return true;
+         }
+         case TK_A_RUN_BUILTIN:
+         case TK_A_RUN_PROGRAM: {
+@@ -2875,7 +2939,8 @@ static int udev_rule_apply_token_to_event(
+                 char buf[UDEV_LINE_SIZE];
+ 
+                 if (event->run_final)
+-                        break;
++                        return log_event_final_set(event, token);
++
+                 if (token->op == OP_ASSIGN_FINAL)
+                         event->run_final = true;
+ 
+@@ -2890,24 +2955,22 @@ static int udev_rule_apply_token_to_event(
+                         return log_oom();
+ 
+                 r = ordered_hashmap_ensure_put(&event->run_list, NULL, cmd, token->data);
+-                if (r == -ENOMEM)
+-                        return log_oom();
+                 if (r < 0)
+-                        return log_event_error_errno(event, token, r, "Failed to store command '%s': %m", cmd);
++                        return log_event_error_errno(event, token, r, "Failed to store command \"%s\": %m", cmd);
+ 
++                log_event_debug(event, token, "Set command: %s", cmd);
+                 TAKE_PTR(cmd);
+-
+-                log_event_debug(event, token, "RUN '%s'", token->value);
+-                break;
++                return true;
+         }
+         case TK_A_OPTIONS_STATIC_NODE:
+                 /* do nothing for events. */
+-                break;
++                return true;
++
+         default:
+                 assert_not_reached();
+         }
+ 
+-        return true;
++        assert_not_reached();
+ }
+ 
+ static int udev_rule_apply_parent_token_to_event(UdevRuleToken *head_token, UdevEvent *event) {
diff --git a/SOURCES/0172-udev-rules-add-trace-logs-for-GOTO-and-parent-condit.patch b/SOURCES/0172-udev-rules-add-trace-logs-for-GOTO-and-parent-condit.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5a63580dfa7170ad3e9eda560e99088d8a09e877
--- /dev/null
+++ b/SOURCES/0172-udev-rules-add-trace-logs-for-GOTO-and-parent-condit.patch
@@ -0,0 +1,103 @@
+From 7f14a15e480fd4392bc86f7927c9b14dfe833d83 Mon Sep 17 00:00:00 2001
+From: Yu Watanabe <watanabe.yu+github@gmail.com>
+Date: Mon, 13 Jan 2025 05:18:25 +0900
+Subject: [PATCH] udev-rules: add trace logs for GOTO and parent conditions
+
+(cherry picked from commit c547b9e278b86cd2318cb69319aaadad9004d96a)
+
+Resolves: RHEL-75774
+---
+ src/udev/udev-rules.c | 48 ++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 43 insertions(+), 5 deletions(-)
+
+diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
+index c5fa2c660c..4f2021b8c4 100644
+--- a/src/udev/udev-rules.c
++++ b/src/udev/udev-rules.c
+@@ -345,6 +345,20 @@ static bool token_is_for_parents(UdevRuleToken *token) {
+ #define log_event_truncated(event, token, what, format)                 \
+         _log_event_truncated(event, token, UNIQ_T(t, UNIQ), what, format)
+ 
++#define _log_event_line(event, event_u, line, ...)                      \
++        ({                                                              \
++                UdevEvent *event_u = ASSERT_PTR(event);                 \
++                                                                        \
++                event_u->trace ?                                        \
++                        log_udev_rule_line_full(                        \
++                                        event_u->dev, line,             \
++                                        LOG_DEBUG, 0, __VA_ARGS__) :    \
++                        0;                                              \
++        })
++
++#define log_event_line(event, line, ...)                                \
++        _log_event_line(event, UNIQ_T(e, UNIQ), line, __VA_ARGS__)
++
+ /* Mainly used when parsing .rules files. */
+ #define log_file_full_errno_zerook(...)                                 \
+         log_udev_rule_file_full(NULL, __VA_ARGS__)
+@@ -2977,14 +2991,30 @@ static int udev_rule_apply_parent_token_to_event(UdevRuleToken *head_token, Udev
+         int r;
+ 
+         assert(head_token);
++        assert(token_is_for_parents(head_token));
+         assert(event);
+ 
++        UdevRuleLine *line = head_token->rule_line;
++        if (event->trace) {
++                _cleanup_free_ char *joined = NULL;
++
++                LIST_FOREACH(tokens, token, head_token)
++                        if (token_is_for_parents(token))
++                                (void) strextend_with_separator(&joined, ", ", token->token_str);
++                        else
++                                break;
++
++                log_event_line(event, line, "Checking conditions for parent devices: %s", strna(joined));
++        }
++
+         event->dev_parent = ASSERT_PTR(event->dev);
+ 
+         for (;;) {
+                 LIST_FOREACH(tokens, token, head_token) {
+-                        if (!token_is_for_parents(token))
+-                                return true; /* All parent tokens match. */
++                        if (!token_is_for_parents(token)) {
++                                r = 1; /* avoid false maybe-uninitialized warning */
++                                break; /* All parent tokens match. */
++                        }
+ 
+                         r = udev_rule_apply_token_to_event(token, event->dev_parent, event);
+                         if (r < 0)
+@@ -2992,12 +3022,18 @@ static int udev_rule_apply_parent_token_to_event(UdevRuleToken *head_token, Udev
+                         if (r == 0)
+                                 break;
+                 }
+-                if (r > 0)
+-                        /* All parent tokens match, and no more token (except for GOTO) in the line. */
++                if (r > 0) {
++                        if (event->trace) {
++                                const char *s = NULL;
++                                (void) sd_device_get_syspath(event->dev_parent, &s);
++                                log_event_line(event, line, "Parent device \"%s\" passed all parent conditions.", strna(s));
++                        }
+                         return true;
++                }
+ 
+                 if (sd_device_get_parent(event->dev_parent, &event->dev_parent) < 0) {
+                         event->dev_parent = NULL;
++                        log_event_line(event, line, "No parent device passed parent conditions.");
+                         return false;
+                 }
+         }
+@@ -3054,8 +3090,10 @@ static int udev_rule_apply_line_to_event(
+                         return r;
+         }
+ 
+-        if (line->goto_line)
++        if (line->goto_line) {
++                log_event_line(event, line, "GOTO=%s", strna(line->goto_label));
+                 *next_line = line->goto_line; /* update next_line only when the line has GOTO token. */
++        }
+ 
+         return 0;
+ }
diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec
index e389beb8202baf0321d8a5741410b6643c664196..c3585effb806f1ab0885d3c61c55f1f12ee09b80 100644
--- a/SPECS/systemd.spec
+++ b/SPECS/systemd.spec
@@ -48,7 +48,7 @@ Url:            https://systemd.io
 # Allow users to specify the version and release when building the rpm by 
 # setting the %%version_override and %%release_override macros.
 Version:        %{?version_override}%{!?version_override:257}
-Release:        3%{?dist}
+Release:        4%{?dist}
 
 %global stable %(c="%version"; [ "$c" = "${c#*.*}" ]; echo $?)
 
@@ -252,6 +252,35 @@ Patch0140: 0140-netif-naming-scheme-rename-rhel-10.0-to-rhel-10.0.be.patch
 Patch0141: 0141-net-naming-scheme-disable-NAMING_FIRMWARE_NODE_SUN.patch
 Patch0142: 0142-netif-naming-scheme-introduce-rhel-10.0-scheme.patch
 Patch0143: 0143-udev-net_id-introduce-naming-scheme-for-RHEL-9.6.patch
+Patch0144: 0144-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch
+Patch0145: 0145-tree-wide-Fix-python-formatting.patch
+Patch0146: 0146-ci-fix-Packit.patch
+Patch0147: 0147-ci-drop-testing-farm-test.patch
+Patch0148: 0148-dbus-stash-the-subscriber-list-when-we-disconenct-fr.patch
+Patch0149: 0149-manager-s-deserialized_subscribed-subscribed_as_strv.patch
+Patch0150: 0150-shared-bus-util-move-bus_message_read_id128-to-bus-m.patch
+Patch0151: 0151-shared-bus-util-move-bus_message_hash_ops-to-bus-mes.patch
+Patch0152: 0152-shared-bus-util-move-string-set-append-get-funcs-to-.patch
+Patch0153: 0153-shared-serialize-make-input-params-const.patch
+Patch0154: 0154-shared-serialize-introduce-serialize_id128.patch
+Patch0155: 0155-bus-util-do-not-reset-the-count-returned-by-sd_bus_t.patch
+Patch0156: 0156-core-manager-use-FOREACH_ARRAY-at-one-more-place.patch
+Patch0157: 0157-core-manager-drop-duplicate-bus-track-deserializatio.patch
+Patch0158: 0158-bus-util-introduce-bus_get_instance_id.patch
+Patch0159: 0159-core-serialize-API-bus-id-and-validate-before-deseri.patch
+Patch0160: 0160-core-manager-restore-bus-track-deserialization-clean.patch
+Patch0161: 0161-shared-bus-util-add-missing-set.h-include.patch
+Patch0162: 0162-udevadm-test-add-missing-oom-check.patch
+Patch0163: 0163-udev-rules-replace-type-func-type-func.patch
+Patch0164: 0164-udev-rules-do-not-change-maximum-log-level-when-runn.patch
+Patch0165: 0165-udevadm-test-introduce-v-verbose-option-to-show-verb.patch
+Patch0166: 0166-udev-rules-show-original-token-string-in-log_event_e.patch
+Patch0167: 0167-udev-rules-logs-result-of-format-substitution.patch
+Patch0168: 0168-udev-rules-add-more-trace-logs-for-string-match.patch
+Patch0169: 0169-udev-rules-introduce-udev_replace_chars_and_log.patch
+Patch0170: 0170-udev-rules-ignore-whole-command-result-if-it-is-too-.patch
+Patch0171: 0171-udev-rules-update-log-messages.patch
+Patch0172: 0172-udev-rules-add-trace-logs-for-GOTO-and-parent-condit.patch
 
 # Downstream-only patches (9000–9999)
 
@@ -1182,7 +1211,38 @@ rm -f .file-list-*
 rm -f %{name}.lang
 
 %changelog
-* Wed Jan 08 2024 Jan Macku <jamacku@redhat.com> - 257-3
+* Thu Jan 30 2025 systemd maintenance team <systemd-maint@redhat.com> - 257-4
+- ci: use ubuntu 22:04 for deploy of man pages (RHEL-57603)
+- tree-wide: Fix python formatting (RHEL-57603)
+- ci: fix Packit (RHEL-57603)
+- ci: drop testing farm test (RHEL-57603)
+- dbus: stash the subscriber list when we disconenct from the bus (RHEL-73780)
+- manager: s/deserialized_subscribed/subscribed_as_strv (RHEL-73780)
+- shared/bus-util: move bus_message_read_id128() to bus-message-util (RHEL-73780)
+- shared/bus-util: move bus_message_hash_ops to bus-message-util (RHEL-73780)
+- shared/bus-util: move string set append/get funcs to bus-message-util and bus-get-properties, respectively (RHEL-73780)
+- shared/serialize: make input params const (RHEL-73780)
+- shared/serialize: introduce serialize_id128() (RHEL-73780)
+- bus-util: do not reset the count returned by sd_bus_track_count_name() (RHEL-73780)
+- core/manager: use FOREACH_ARRAY at one more place (RHEL-73780)
+- core/manager: drop duplicate bus track deserialization (RHEL-73780)
+- bus-util: introduce bus_get_instance_id() (RHEL-73780)
+- core: serialize API bus id and validate before deserializing bus tracks (RHEL-73780)
+- core/manager: restore bus track deserialization cleanup in manager_reload() (RHEL-73780)
+- shared/bus-util: add missing `set.h` include (RHEL-73780)
+- udevadm-test: add missing oom check (RHEL-75774)
+- udev-rules: replace 'type *func()' -> 'type* func()' (RHEL-75774)
+- udev-rules: do not change maximum log level when running in test mode (RHEL-75774)
+- udevadm-test: introduce -v/--verbose option to show verbose log messages (RHEL-75774)
+- udev-rules: show original token string in log_event_error() and friends (RHEL-75774)
+- udev-rules: logs result of format substitution (RHEL-75774)
+- udev-rules: add more trace logs for string match (RHEL-75774)
+- udev-rules: introduce udev_replace_chars_and_log() (RHEL-75774)
+- udev-rules: ignore whole command result if it is too long and does not contain newline (RHEL-75774)
+- udev-rules: update log messages (RHEL-75774)
+- udev-rules: add trace logs for GOTO and parent conditions (RHEL-75774)
+
+* Wed Jan 08 2025 Jan Macku <jamacku@redhat.com> - 257-3
 - udev/net_id: introduce naming scheme for RHEL-9.6 (RHEL-44417)
 - Rebase to new upstream stable release v257.2 (RHEL-71409)