[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1107920: unblock: dbus-broker/37-1



Package: release.debian.org
Severity: normal
Control: affects -1 + src:dbus-broker
User: release.debian.org@packages.debian.org
Usertags: unblock

Dear RT,

[ Reason ]

There is a new bugfix release of dbus-broker, that fixes several
issues. It will likely be the last version before some major overhauls,
so I'd like to have it in trixie.

I am not really sure why dbus-broker is marked as a 'key package', as
it's a completely optional, non-default, alternative implementation.

[ Impact ]

Small, it's a non-default, alternative implementation. No packaging
changes besides dropping a backported patch that is included upstream.

[ Tests ]

Works and boots, and there is large code coverage thanks to the systemd
debci job.

[ Risks ]

Low, as it's a non-default, alternative implementation, and there's
lots of test coverage.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

unblock dbus-broker/37-1

Thanks!
diff -Nru dbus-broker-36/AUTHORS dbus-broker-37/AUTHORS
--- dbus-broker-36/AUTHORS	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/AUTHORS	2025-06-16 13:26:27.000000000 +0100
@@ -18,11 +18,14 @@
 
 AUTHORS: (ordered alphabetically)
         Allison Karlitskaya <allison.karlitskaya@redhat.com>
+        Attila Lakatos <alakatos@redhat.com>
+        Barnabás Pőcze <pobrn@protonmail.com>
         Camron Carter <reefoe@proton.me>
         Chris Paulson-Ellis <chris.paulson-ellis@motorolasolutions.com>
         Chris PeBenito <chpebeni@linux.microsoft.com>
         Daniel Rusek <mail@asciiwolf.com>
         Daniele Nicolodi <daniele@grinta.net>
+        darkblaze69
         David Rheinsberg <david.rheinsberg@gmail.com>
         Evgeny Vereshchagin <evvers@ya.ru>
         Frantisek Sumsal <frantisek@sumsal.cz>
@@ -30,8 +33,9 @@
         Hugo Osvaldo Barrera <hugo@barrera.io>
         Jacob Alzén <jacob.alzen@gmail.com>
         Jake Dane
-        Lily Danzig <ldenardo@dealerinspire.com>
+        Jeffrey Bosboom <jbosboom@jeffreybosboom.com>
         Khem Raj <raj.khem@gmail.com>
+        Lily Danzig <ldenardo@dealerinspire.com>
         Laurent Bigonville <bigon@bigon.be>
         Luca Boccassi <luca.boccassi@microsoft.com>
         Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
@@ -40,9 +44,12 @@
         Michal Schmidt <mschmidt@redhat.com>
         Mike Gilbert <floppym@gentoo.org>
         msizanoen1 <msizanoen@qtmlabs.xyz>
+        Ryan Wilson <ryantimwilson@gmail.com>
+        seaeunlee <seaeunlee@microsoft.com>
         Stefan Agner <stefan@agner.ch>
         Thomas Mühlbacher <tmuehlbacher@posteo.net>
         Tim Gates <tim.gates@iress.com>
         Tom Gundersen <teg@jklm.no>
+        Tomas Korbar <tkorbar@redhat.com>
         Yanko Kaneti <yaneti@declera.com>
         Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
diff -Nru dbus-broker-36/debian/changelog dbus-broker-37/debian/changelog
--- dbus-broker-36/debian/changelog	2025-04-25 19:30:26.000000000 +0100
+++ dbus-broker-37/debian/changelog	2025-06-16 14:58:24.000000000 +0100
@@ -1,3 +1,12 @@
+dbus-broker (37-1) unstable; urgency=medium
+
+  * Update upstream source from tag 'upstream/37'
+    For a full list of changes, see:
+    https://github.com/bus1/dbus-broker/releases/tag/v37
+  * Drop util-sockopt-also-check-for-ESRCH.patch
+
+ -- Luca Boccassi <bluca@debian.org>  Mon, 16 Jun 2025 14:58:24 +0100
+
 dbus-broker (36-2) unstable; urgency=medium
 
   * Drop libaudit/libselinux compat patch, not needed since bookworm
diff -Nru dbus-broker-36/debian/patches/series dbus-broker-37/debian/patches/series
--- dbus-broker-36/debian/patches/series	2025-04-25 19:23:03.000000000 +0100
+++ dbus-broker-37/debian/patches/series	2025-06-16 14:58:00.000000000 +0100
@@ -1,2 +1 @@
-util-sockopt-also-check-for-ESRCH.patch
 github_apparmor_support.patch
diff -Nru dbus-broker-36/debian/patches/util-sockopt-also-check-for-ESRCH.patch dbus-broker-37/debian/patches/util-sockopt-also-check-for-ESRCH.patch
--- dbus-broker-36/debian/patches/util-sockopt-also-check-for-ESRCH.patch	2025-04-25 19:24:32.000000000 +0100
+++ dbus-broker-37/debian/patches/util-sockopt-also-check-for-ESRCH.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
-Author: Luca Boccassi <bluca@debian.org>
-Forwarded: https://github.com/bus1/dbus-broker/pull/389
-Origin: commit:4b1d9da51dc2703b7596243cb555a689bb27b4c1
-Description: util/sockopt: also check for ESRCH when the process has exited
---- a/src/util/sockopt.c
-+++ b/src/util/sockopt.c
-@@ -238,7 +238,7 @@
-                         return SOCKOPT_E_UNSUPPORTED;
-                 if (errno == ENODATA)
-                         return SOCKOPT_E_UNAVAILABLE;
--                if (errno == EINVAL)
-+                if (errno == EINVAL || errno == ESRCH)
-                         return SOCKOPT_E_REAPED;
- 
-                 return error_origin(-errno);
diff -Nru dbus-broker-36/.github/workflows/codeql.yml dbus-broker-37/.github/workflows/codeql.yml
--- dbus-broker-36/.github/workflows/codeql.yml	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/.github/workflows/codeql.yml	2025-06-16 13:26:27.000000000 +0100
@@ -32,7 +32,7 @@
         sudo apt -y install dbus expat libaudit-dev libselinux-dev libsystemd-dev python3-pip
         sudo pip3 install meson ninja
     - name: Checkout repository
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
     - name: Initialize CodeQL
       uses: github/codeql-action/init@v2
       with:
diff -Nru dbus-broker-36/.github/workflows/fuzz.yml dbus-broker-37/.github/workflows/fuzz.yml
--- dbus-broker-36/.github/workflows/fuzz.yml	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/.github/workflows/fuzz.yml	2025-06-16 13:26:27.000000000 +0100
@@ -46,7 +46,7 @@
         sanitizer: ${{ matrix.sanitizer }}
     - name: Upload Crash
       if: failure() && steps.build.outcome == 'success'
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: ${{ matrix.sanitizer }}-${{ matrix.architecture }}-artifacts
         path: ./out/artifacts
diff -Nru dbus-broker-36/.github/workflows/meson-tests.yml dbus-broker-37/.github/workflows/meson-tests.yml
--- dbus-broker-36/.github/workflows/meson-tests.yml	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/.github/workflows/meson-tests.yml	2025-06-16 13:26:27.000000000 +0100
@@ -36,29 +36,29 @@
         # since it clashes with sd-bus used by the launcher. This test also
         # builds documentation and related resources.
         - id: "release"
-          name: "RELEASE @ CLANG-X86_64 @ +TEST -VALGRIND @ -APPARMOR +AUDIT +DOCS +LAUNCHER +SELINUX"
+          name: "RELEASE @ CLANG-X86_64 @ +TEST -VALGRIND @ -APPARMOR +AUDIT +DOCS +LAUNCHER -SELINUX"
 
           # Explicitly set all options here to document them.
           buildtype: "debugoptimized"
           cc: "clang"
           cflags: "-Werror"
-          image: "ghcr.io/bus1/dbrk-ci-fedora:latest"
-          m32: "no"
-          setupargs: "-Daudit=true -Ddocs=true -Dlauncher=true -Dselinux=true"
+          image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest"
+          rustflags: ""
+          setupargs: "-Daudit=true -Ddocs=true -Dlauncher=true"
           test: "yes"
           valgrind: "no"
           warnlevel: "2"
 
-        # A release build with `-m32` to test on 32-bit.
+        # A release build with `--cross-file lib32` to test on 32-bit.
         - id: "32bit"
-          name: "RELEASE @ CLANG-I686 @ +TEST -VALGRIND @ -APPARMOR +AUDIT -DOCS +LAUNCHER -SELINUX"
+          name: "RELEASE @ CLANG-I686 @ +TEST -VALGRIND @ -APPARMOR -AUDIT -DOCS +LAUNCHER -SELINUX"
 
           buildtype: "debugoptimized"
           cc: "clang"
-          cflags: "-m32 -Werror"
-          image: "ghcr.io/bus1/dbrk-ci-fedora:latest"
-          m32: "yes"
-          setupargs: "-Daudit=true -Dlauncher=true"
+          cflags: "-Werror"
+          image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest"
+          rustflags: "--target i686-unknown-linux-gnu"
+          setupargs: "-Dlauncher=true --cross-file lib32"
           test: "yes"
           warnlevel: "2"
 
@@ -71,7 +71,7 @@
           buildtype: "debugoptimized"
           cc: "clang"
           cflags: "-Werror"
-          image: "ghcr.io/bus1/dbrk-ci-fedora:latest"
+          image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest"
           setupargs: "-Daudit=true -Dlauncher=false"
           test: "yes"
           valgrind: "yes"
@@ -80,26 +80,26 @@
         # A reduced build with `-O0` to verify we do not rely on dead-code
         # elimination.
         - id: "O0-PLAIN"
-          name: "PLAIN @ GCC-X86_64 @ +TEST -VALGRIND @ -APPARMOR +AUDIT -DOCS +LAUNCHER +SELINUX"
+          name: "PLAIN @ GCC-X86_64 @ +TEST -VALGRIND @ -APPARMOR +AUDIT -DOCS +LAUNCHER -SELINUX"
 
           buildtype: "plain"
           cc: "gcc"
           cflags: "-O0 -Werror"
-          image: "ghcr.io/bus1/dbrk-ci-fedora:latest"
-          setupargs: "-Daudit=true -Dlauncher=true -Dselinux=true"
+          image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest"
+          setupargs: "-Daudit=true -Dlauncher=true"
           test: "yes"
           warnlevel: "2"
 
         # An aggressive -O3 -DNDEBUG build that verfies that we properly
         # follow strict aliasing rules and do not rely on debug builds.
         - id: "O3-NDEBUG"
-          name: "OPTIMIZED @ GCC-X86_64 @ +TEST -VALGRIND @ -APPARMOR +AUDIT -DOCS +LAUNCHER +SELINUX"
+          name: "OPTIMIZED @ GCC-X86_64 @ +TEST -VALGRIND @ -APPARMOR +AUDIT -DOCS +LAUNCHER -SELINUX"
 
           buildtype: "release"
           cc: "gcc"
           cflags: "-O3 -Werror -DNDEBUG"
-          image: "ghcr.io/bus1/dbrk-ci-fedora:latest"
-          setupargs: "-Daudit=true -Dlauncher=true -Dselinux=true"
+          image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest"
+          setupargs: "-Daudit=true -Dlauncher=true"
           test: "yes"
           warnlevel: "2"
 
@@ -111,11 +111,12 @@
           buildtype: "debugoptimized"
           cc: "clang"
           cflags: "-Werror"
-          image: "ghcr.io/bus1/dbrk-ci-fedora:latest"
+          image: "ghcr.io/readaheadeu/rae-ci-archlinux:latest"
           setupargs: "-Dlauncher=false"
           test: "yes"
           warnlevel: "2"
 
+        # Run on Ubuntu to test the AppArmor integration.
         - id: "ubuntu"
           name: "RELEASE @ CLANG-X86_64 @ +TEST -VALGRIND @ +APPARMOR +AUDIT -DOCS +LAUNCHER -SELINUX"
 
@@ -129,6 +130,7 @@
 
     container:
       image: ${{ matrix.image }}
+      options: --user root
 
     env:
       CC: ${{ matrix.cc }}
@@ -138,14 +140,7 @@
 
     steps:
     - name: "Fetch Sources"
-      uses: actions/checkout@v3
-
-    - name: "Setup 32-bit Environment"
-      if: matrix.m32 == 'yes'
-      run: |
-        echo \
-          "PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig" \
-          >> $GITHUB_ENV
+      uses: actions/checkout@v4
 
     - name: "Setup Meson"
       run: |
diff -Nru dbus-broker-36/.github/workflows/publish.yml dbus-broker-37/.github/workflows/publish.yml
--- dbus-broker-36/.github/workflows/publish.yml	1970-01-01 01:00:00.000000000 +0100
+++ dbus-broker-37/.github/workflows/publish.yml	2025-06-16 13:26:27.000000000 +0100
@@ -0,0 +1,53 @@
+#
+# Publish Releases
+#
+# This workflow can be manually triggered and will then publish the
+# specified release to the configured release channels.
+#
+
+name: "Publish Releases"
+
+on:
+  workflow_dispatch:
+    inputs:
+      tag:
+        description: "Git-Tag to Publish"
+        default: ""
+        required: true
+
+concurrency:
+  cancel-in-progress: false
+  group: "publish"
+
+defaults:
+  run:
+    shell: "bash"
+
+jobs:
+  publish:
+    name: "Publish GitHub"
+
+    env:
+      CTX_GITHUB_EVENT_INPUTS_TAG: ${{ github.event.inputs.tag }}
+    permissions:
+      contents: write
+    runs-on: "ubuntu-latest"
+
+    steps:
+    - name: "Verify Input"
+      run: |
+        if [[ -z "${CTX_GITHUB_EVENT_INPUTS_TAG}" ]] ; then
+          echo "error: empty git-tag specified as input"
+          exit 1
+        fi
+
+    - name: "Clone Repository"
+      uses: actions/checkout@v4
+      with:
+        ref: ${{ github.event.inputs.tag }}
+
+    - name: "Publish GitHub"
+      uses: readaheadeu/rae-actions/publish-github@v1
+      with:
+        ghtoken: ${{ secrets.GITHUB_TOKEN }}
+        tag: ${{ github.event.inputs.tag }}
diff -Nru dbus-broker-36/Makefile dbus-broker-37/Makefile
--- dbus-broker-36/Makefile	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/Makefile	2025-06-16 13:26:27.000000000 +0100
@@ -69,7 +69,6 @@
 		--buildtype "debugoptimized" \
 		--reconfigure \
 		--warnlevel "2" \
-		-D "audit=true" \
 		-D "docs=true" \
 		-D "launcher=true" \
 		-- \
@@ -89,6 +88,13 @@
 meson-setup: | $(BUILDDIR)/
 	$(MESON_SETUP)
 
+.PHONY: meson-setup
+meson-setup-32: | $(BUILDDIR)/
+	CFLAGS="-m32" \
+	PKG_CONFIG_LIBDIR="/usr/lib32/pkgconfig:/usr/share/pkgconfig" \
+	RUSTFLAGS="--target i686-unknown-linux" \
+		$(MESON_SETUP)
+
 .PHONY: meson-test
 meson-test: $(BUILDDIR)/meson/
 	meson \
diff -Nru dbus-broker-36/meson.build dbus-broker-37/meson.build
--- dbus-broker-36/meson.build	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/meson.build	2025-06-16 13:26:27.000000000 +0100
@@ -4,15 +4,16 @@
 
 project(
         'dbus-broker',
-        'c',
         default_options: [
                 'c_std=c11',
         ],
-        license: 'Apache',
+        license: 'Apache-2.0',
         meson_version: '>=0.60.0',
-        version: '36',
+        version: '37',
 )
 
+add_languages('c', native: false)
+
 cc = meson.get_compiler('c')
 conf = configuration_data()
 mod_pkgconfig = import('pkgconfig')
diff -Nru dbus-broker-36/NEWS.md dbus-broker-37/NEWS.md
--- dbus-broker-36/NEWS.md	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/NEWS.md	2025-06-16 13:26:27.000000000 +0100
@@ -1,5 +1,30 @@
 # dbus-broker - Linux D-Bus Message Broker
 
+## CHANGES WITH 37:
+
+        * Add `/etc` and `/run` to the search-paths for system services. This
+          change is aligned with recent changes to the reference
+          implementation.
+
+        * Support systemd's `notify-reload` to trigger a reload operation.
+          This replaces the old `busctl call ...ReloadConfig` operation.
+
+        * Extend `org.freedesktop.DBus.Debug.Stats.GetStats` with all the
+          fields defined by the specification.
+
+        * Fix a bug in match-rule processing which caused argument processing
+          to fail for any but the first message argument.
+
+        * Fix a memory leak in configuration processing when parsing invalid
+          user or group IDs.
+
+        Contributions from: Attila Lakatos, Barnabás Pőcze, darkblaze69, David
+                            Rheinsberg, Evgeny Vereshchagin, Frantisek Sumsal,
+                            Jeffrey Bosboom, Luca Boccassi, Ryan Wilson,
+                            seaeunlee, Tomas Korbar
+
+        - Dußlingen, 2025-06-16
+
 ## CHANGES WITH 36:
 
         * Fix possible file-descriptor use-after-close, which can lead to
diff -Nru dbus-broker-36/packit.yml dbus-broker-37/packit.yml
--- dbus-broker-36/packit.yml	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/packit.yml	2025-06-16 13:26:27.000000000 +0100
@@ -1,3 +1,4 @@
+create_sync_note: false
 downstream_package_name: "dbus-broker"
 upstream_package_name: "dbus-broker"
 upstream_tag_template: "v{version}"
diff -Nru dbus-broker-36/src/broker/broker.c dbus-broker-37/src/broker/broker.c
--- dbus-broker-36/src/broker/broker.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/broker/broker.c	2025-06-16 13:26:27.000000000 +0100
@@ -40,19 +40,12 @@
         return DISPATCH_E_EXIT;
 }
 
-int broker_new(Broker **brokerp, const char *machine_id, int log_fd, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects) {
+int broker_new(Broker **brokerp, Log *log, const char *machine_id, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects) {
         _c_cleanup_(broker_freep) Broker *broker = NULL;
         struct ucred ucred;
         socklen_t z;
         sigset_t sigmask;
-        int r, log_type;
-
-        if (log_fd >= 0) {
-                z = sizeof(log_type);
-                r = getsockopt(log_fd, SOL_SOCKET, SO_TYPE, &log_type, &z);
-                if (r < 0)
-                        return error_origin(-errno);
-        }
+        int r;
 
         z = sizeof(ucred);
         r = getsockopt(controller_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &z);
@@ -63,26 +56,14 @@
         if (!broker)
                 return error_origin(-ENOMEM);
 
-        broker->log = (Log)LOG_NULL;
+        broker->log = log;
         broker->bus = (Bus)BUS_NULL(broker->bus);
         broker->dispatcher = (DispatchContext)DISPATCH_CONTEXT_NULL(broker->dispatcher);
         broker->signals_fd = -1;
         broker->signals_file = (DispatchFile)DISPATCH_FILE_NULL(broker->signals_file);
         broker->controller = (Controller)CONTROLLER_NULL(broker->controller);
 
-        if (log_fd < 0)
-                log_init(&broker->log);
-        else if (log_type == SOCK_STREAM)
-                log_init_stderr(&broker->log, log_fd);
-        else if (log_type == SOCK_DGRAM)
-                log_init_journal(&broker->log, log_fd);
-        else
-                return error_origin(-ENOTRECOVERABLE);
-
-        /* XXX: make this run-time optional */
-        log_set_lossy(&broker->log, true);
-
-        r = bus_init(&broker->bus, &broker->log, machine_id, max_bytes, max_fds, max_matches, max_objects);
+        r = bus_init(&broker->bus, broker->log, machine_id, max_bytes, max_fds, max_matches, max_objects);
         if (r)
                 return error_fold(r);
 
@@ -104,7 +85,7 @@
                 return error_fold(r);
 
         r = sockopt_get_peergroups(controller_fd,
-                                   &broker->log,
+                                   broker->log,
                                    ucred.uid,
                                    ucred.gid,
                                    &broker->bus.gids,
@@ -168,7 +149,6 @@
         c_close(broker->signals_fd);
         dispatch_context_deinit(&broker->dispatcher);
         bus_deinit(&broker->bus);
-        log_deinit(&broker->log);
         free(broker);
 
         return NULL;
diff -Nru dbus-broker-36/src/broker/broker.h dbus-broker-37/src/broker/broker.h
--- dbus-broker-36/src/broker/broker.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/broker/broker.h	2025-06-16 13:26:27.000000000 +0100
@@ -21,7 +21,7 @@
 typedef struct User User;
 
 struct Broker {
-        Log log;
+        Log *log;
         Bus bus;
         DispatchContext dispatcher;
 
@@ -33,7 +33,7 @@
 
 /* broker */
 
-int broker_new(Broker **brokerp, const char *machine_id, int log_fd, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects);
+int broker_new(Broker **brokerp, Log *log, const char *machine_id, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects);
 Broker *broker_free(Broker *broker);
 
 int broker_run(Broker *broker);
diff -Nru dbus-broker-36/src/broker/main.c dbus-broker-37/src/broker/main.c
--- dbus-broker-36/src/broker/main.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/broker/main.c	2025-06-16 13:26:27.000000000 +0100
@@ -13,6 +13,7 @@
 #include "broker/main.h"
 #include "util/audit.h"
 #include "util/error.h"
+#include "util/log.h"
 #include "util/selinux.h"
 #include "util/string.h"
 
@@ -234,8 +235,9 @@
         return 0;
 }
 
-static int setup(void) {
-        int r;
+static int setup(Log *logp) {
+        socklen_t z;
+        int r, log_type;
 
         /*
          * We never spawn external applications from within the broker itself,
@@ -247,14 +249,33 @@
         if (r < 0)
                 return error_origin(-errno);
 
+        if (main_arg_log >= 0) {
+                z = sizeof(log_type);
+                r = getsockopt(main_arg_log, SOL_SOCKET, SO_TYPE, &log_type, &z);
+                if (r < 0)
+                        return error_origin(-errno);
+        }
+
+        if (main_arg_log < 0)
+                log_init(logp);
+        else if (log_type == SOCK_STREAM)
+                log_init_stderr(logp, main_arg_log);
+        else if (log_type == SOCK_DGRAM)
+                log_init_journal(logp, main_arg_log);
+        else
+                return error_origin(-ENOTRECOVERABLE);
+
+        /* XXX: make this run-time optional */
+        log_set_lossy(logp, true);
+
         return 0;
 }
 
-static int run(void) {
+static int run(Log *log) {
         _c_cleanup_(broker_freep) Broker *broker = NULL;
         int r;
 
-        r = broker_new(&broker, main_arg_machine_id, main_arg_log, main_arg_controller, main_arg_max_bytes, main_arg_max_fds, main_arg_max_matches, main_arg_max_objects);
+        r = broker_new(&broker, log, main_arg_machine_id, main_arg_controller, main_arg_max_bytes, main_arg_max_fds, main_arg_max_matches, main_arg_max_objects);
         if (!r)
                 r = broker_run(broker);
 
@@ -262,13 +283,14 @@
 }
 
 int main(int argc, char **argv) {
+        Log log = LOG_NULL;
         int r;
 
         r = parse_argv(argc, argv);
         if (r)
                 goto exit;
 
-        r = setup();
+        r = setup(&log);
         if (r)
                 goto exit;
 
@@ -280,17 +302,18 @@
                 }
         }
 
-        r = bus_selinux_init_global();
+        r = bus_selinux_init_global(&log);
         if (r) {
                 r = error_fold(r);
                 goto exit;
         }
 
-        r = run();
+        r = run(&log);
 
 exit:
         bus_selinux_deinit_global();
         util_audit_deinit_global();
+        log_deinit(&log);
 
         r = error_trace(r);
         return (r == 0 || r == MAIN_EXIT) ? 0 : 1;
diff -Nru dbus-broker-36/src/bus/bus.h dbus-broker-37/src/bus/bus.h
--- dbus-broker-36/src/bus/bus.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/bus.h	2025-06-16 13:26:27.000000000 +0100
@@ -46,6 +46,7 @@
 
         UserRegistry users;
         NameRegistry names;
+        MatchCounters match_counters;
         MatchRegistry wildcard_matches;
         MatchRegistry sender_matches;
         PeerRegistry peers;
@@ -53,6 +54,7 @@
         uint64_t n_monitors;
         uint64_t listener_ids;
         uint64_t activation_ids;
+        uint64_t stats_ids;
 
         Metrics metrics;
 };
@@ -61,6 +63,7 @@
                 .pid_fd = -1,                                                   \
                 .users = USER_REGISTRY_NULL,                                    \
                 .names = NAME_REGISTRY_INIT,                                    \
+                .match_counters = MATCH_COUNTERS_INIT,                          \
                 .wildcard_matches = MATCH_REGISTRY_INIT((_x).wildcard_matches), \
                 .sender_matches = MATCH_REGISTRY_INIT((_x).sender_matches),     \
                 .peers = PEER_REGISTRY_INIT,                                    \
diff -Nru dbus-broker-36/src/bus/driver.c dbus-broker-37/src/bus/driver.c
--- dbus-broker-36/src/bus/driver.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/driver.c	2025-06-16 13:26:27.000000000 +0100
@@ -21,6 +21,7 @@
 #include "dbus/socket.h"
 #include "util/apparmor.h"
 #include "util/error.h"
+#include "util/misc.h"
 #include "util/selinux.h"
 #include "util/string.h"
 
@@ -2274,6 +2275,7 @@
 }
 
 static int driver_method_get_stats(Peer *peer, const char *path, CDVar *in_v, uint32_t serial, CDVar *out_v) {
+        Bus *bus = peer->bus;
         int r;
 
         c_dvar_read(in_v, "()");
@@ -2282,16 +2284,40 @@
         if (r)
                 return error_trace(r);
 
-        /*
-         * Append all supported statistics. So far, none of the dbus-daemon
-         * statistics are appended, since they are very specific to how the bus
-         * is implemented. We do, however, add our own (namespaced) statistics.
-         */
         c_dvar_write(out_v, "([{s", "org.bus1.DBus.Debug.Stats.PeerAccounting");
         driver_append_peer_accounting(out_v, peer->bus);
         c_dvar_write(out_v, "}{s", "org.bus1.DBus.Debug.Stats.UserAccounting");
         driver_append_user_accounting(out_v, peer->bus);
-        c_dvar_write(out_v, "}])");
+        c_dvar_write(out_v, "}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "{s<u>}"
+                            "])",
+                            "Serial", c_dvar_type_u,
+                                util_t2u_saturating(++peer->bus->stats_ids),
+                            "ActiveConnections", c_dvar_type_u,
+                                util_z2u_saturating(bus->peers.n_registered),
+                            "IncompleteConnections", c_dvar_type_u,
+                                util_z2u_saturating(bus->peers.n_peers - bus->peers.n_registered),
+                            "BusNames", c_dvar_type_u,
+                                util_z2u_saturating(bus->names.n_primaries),
+                            "PeakBusNames", c_dvar_type_u,
+                                util_z2u_saturating(bus->names.n_primaries_peak),
+                            "PeakBusNamesPerConnection", c_dvar_type_u,
+                                util_z2u_saturating(bus->names.n_owner_primaries_peak),
+                            "MatchRules", c_dvar_type_u,
+                                util_z2u_saturating(bus->match_counters.n_subscriptions),
+                            "PeakMatchRules", c_dvar_type_u,
+                                util_z2u_saturating(bus->match_counters.n_subscriptions_peak),
+                            "PeakMatchRulesPerConnection", c_dvar_type_u,
+                                util_z2u_saturating(bus->match_counters.n_owner_subscriptions_peak)
+        );
 
         r = driver_send_reply(peer, out_v, serial);
         if (r)
@@ -2777,7 +2803,7 @@
                 r = driver_send_error(peer, message_read_serial(message), "org.freedesktop.DBus.Error.UnknownMethod", driver_error_to_string(r));
                 break;
         case DRIVER_E_UNEXPECTED_PROPERTY:
-                r = driver_send_error(peer, message_read_serial(message), "org.freedesktop.DBus.Error.UnkonwnProperty", driver_error_to_string(r));
+                r = driver_send_error(peer, message_read_serial(message), "org.freedesktop.DBus.Error.UnknownProperty", driver_error_to_string(r));
                 break;
         case DRIVER_E_READONLY_PROPERTY:
                 r = driver_send_error(peer, message_read_serial(message), "org.freedesktop.DBus.Error.PropertyReadOnly", driver_error_to_string(r));
diff -Nru dbus-broker-36/src/bus/match.c dbus-broker-37/src/bus/match.c
--- dbus-broker-36/src/bus/match.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/match.c	2025-06-16 13:26:27.000000000 +0100
@@ -11,6 +11,7 @@
 #include "dbus/message.h"
 #include "dbus/protocol.h"
 #include "util/error.h"
+#include "util/misc.h"
 #include "util/string.h"
 
 static bool match_key_equal(const char *key1, const char *key2, size_t n_key2) {
@@ -422,12 +423,12 @@
                 return false;
 
         for (unsigned int i = 0; i < keys->filter.n_args || i < keys->filter.n_argpaths; i ++) {
-                if (keys->filter.args[i] && !(metadata->args[0].element == 's' && string_equal(keys->filter.args[i], metadata->args[i].value)))
+                if (keys->filter.args[i] && !(metadata->args[i].element == 's' && string_equal(keys->filter.args[i], metadata->args[i].value)))
                         return false;
 
                 if (keys->filter.argpaths[i]) {
                         if (!match_string_prefix(metadata->args[i].value, keys->filter.argpaths[i], '/', true) &&
-                            !match_string_prefix(keys->filter.argpaths[i], metadata->args[0].value, '/', true))
+                            !match_string_prefix(keys->filter.argpaths[i], metadata->args[i].value, '/', true))
                                 return false;
                 }
         }
@@ -871,7 +872,7 @@
 /**
  * match_rule_link() - XXX
  */
-int match_rule_link(MatchRule *rule, MatchRegistry *registry, bool monitor) {
+int match_rule_link(MatchRule *rule, MatchCounters *counters, MatchRegistry *registry, bool monitor) {
         _c_cleanup_(match_registry_by_path_unrefp) MatchRegistryByPath *registry_by_path = NULL;
         CRBTree *tree;
         CRBNode **slot, *parent;
@@ -880,6 +881,7 @@
         if (rule->registry) {
                 c_assert(registry == rule->registry);
                 c_assert(c_list_is_linked(&rule->registry_link));
+                c_assert(counters == rule->counters);
 
                 return 0;
         }
@@ -903,7 +905,16 @@
         r = match_rule_link_by_path(rule, registry_by_path);
         if (r)
                 return error_trace(r);
+
         rule->registry = registry;
+        rule->counters = counters;
+
+        if (counters) {
+                ++counters->n_subscriptions;
+                ++rule->owner->n_owner_subscriptions;
+                util_peak_update(&counters->n_subscriptions_peak, counters->n_subscriptions);
+                util_peak_update(&counters->n_owner_subscriptions_peak, rule->owner->n_owner_subscriptions);
+        }
 
         return 0;
 }
@@ -913,8 +924,14 @@
  */
 void match_rule_unlink(MatchRule *rule) {
         if (rule->registry) {
+                if (rule->counters) {
+                        --rule->owner->n_owner_subscriptions;
+                        --rule->counters->n_subscriptions;
+                }
+
                 c_list_unlink(&rule->registry_link);
                 rule->registry_by_keys = match_registry_by_keys_unref(rule->registry_by_keys);
+                rule->counters = NULL;
                 rule->registry = NULL;
         }
 }
diff -Nru dbus-broker-36/src/bus/match.h dbus-broker-37/src/bus/match.h
--- dbus-broker-36/src/bus/match.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/match.h	2025-06-16 13:26:27.000000000 +0100
@@ -11,6 +11,7 @@
 #include "dbus/address.h"
 #include "util/user.h"
 
+typedef struct MatchCounters MatchCounters;
 typedef struct MatchFilter MatchFilter;
 typedef struct MatchKeys MatchKeys;
 typedef struct MatchOwner MatchOwner;
@@ -71,6 +72,7 @@
         MatchRegistry *registry;
         MatchOwner *owner;
         CRBNode owner_node;
+        MatchCounters *counters;
 
         UserCharge charge[2];
         MatchKeys keys;
@@ -85,6 +87,7 @@
         }
 
 struct MatchOwner {
+        size_t n_owner_subscriptions;
         CRBTree rule_tree;
         CList destinations_link;
 };
@@ -161,12 +164,20 @@
                 .monitor_tree = C_RBTREE_INIT,          \
         }
 
+struct MatchCounters {
+        size_t n_subscriptions;
+        size_t n_subscriptions_peak;
+        size_t n_owner_subscriptions_peak;
+};
+
+#define MATCH_COUNTERS_INIT {}
+
 /* rules */
 
 MatchRule *match_rule_user_ref(MatchRule *rule);
 MatchRule *match_rule_user_unref(MatchRule *rule);
 
-int match_rule_link(MatchRule *rule, MatchRegistry *registry, bool monitor);
+int match_rule_link(MatchRule *rule, MatchCounters *counters, MatchRegistry *registry, bool monitor);
 void match_rule_unlink(MatchRule *rule);
 
 C_DEFINE_CLEANUP(MatchRule *, match_rule_user_unref);
diff -Nru dbus-broker-36/src/bus/name.c dbus-broker-37/src/bus/name.c
--- dbus-broker-36/src/bus/name.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/name.c	2025-06-16 13:26:27.000000000 +0100
@@ -11,6 +11,7 @@
 #include "dbus/protocol.h"
 #include "dbus/socket.h"
 #include "util/error.h"
+#include "util/misc.h"
 #include "util/user.h"
 
 /**
@@ -115,20 +116,31 @@
  */
 void name_ownership_release(NameOwnership *ownership, NameChange *change) {
         NameOwnership *primary;
+        Name *name = ownership->name;
 
         c_assert(!change->name);
         c_assert(!change->old_owner);
         c_assert(!change->new_owner);
 
-        primary = name_primary(ownership->name);
+        primary = name_primary(name);
         c_list_unlink(&ownership->name_link);
 
         if (ownership == primary) {
-                primary = name_primary(ownership->name);
+                --ownership->owner->n_owner_primaries;
+                --name->registry->n_primaries;
 
-                change->name = name_ref(ownership->name);
+                primary = name_primary(name);
+
+                change->name = name_ref(name);
                 change->old_owner = ownership->owner;
                 change->new_owner = primary ? primary->owner : NULL;
+
+                if (primary) {
+                        // skip `n_primaries_peak` as `n_primaries` did not change
+                        ++name->registry->n_primaries;
+                        ++primary->owner->n_owner_primaries;
+                        util_peak_update(&name->registry->n_owner_primaries_peak, primary->owner->n_owner_primaries);
+                }
         }
 
         name_ownership_free(ownership);
@@ -156,6 +168,12 @@
                 c_assert(!c_list_is_linked(&ownership->name_link));
 
                 c_list_link_front(&name->ownership_list, &ownership->name_link);
+
+                ++name->registry->n_primaries;
+                ++ownership->owner->n_owner_primaries;
+                util_peak_update(&name->registry->n_primaries_peak, name->registry->n_primaries);
+                util_peak_update(&name->registry->n_owner_primaries_peak, ownership->owner->n_owner_primaries);
+
                 r = 0;
         } else if (primary == ownership) {
                 /* we are already the primary owner */
@@ -170,6 +188,11 @@
                 c_list_unlink(&ownership->name_link);
                 c_list_link_front(&name->ownership_list, &ownership->name_link);
 
+                // skip `n_primaries_peak` as `n_primaries` did not change
+                --primary->owner->n_owner_primaries;
+                ++ownership->owner->n_owner_primaries;
+                util_peak_update(&name->registry->n_owner_primaries_peak, ownership->owner->n_owner_primaries);
+
                 /* drop previous primary owner, if queuing is not requested */
                 if (primary->flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) {
                         c_list_unlink(&primary->name_link);
diff -Nru dbus-broker-36/src/bus/name.h dbus-broker-37/src/bus/name.h
--- dbus-broker-36/src/bus/name.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/name.h	2025-06-16 13:26:27.000000000 +0100
@@ -76,6 +76,7 @@
         }
 
 struct NameOwner {
+        size_t n_owner_primaries;
         CRBTree ownership_tree;
 };
 
@@ -84,6 +85,9 @@
         }
 
 struct NameRegistry {
+        size_t n_primaries;
+        size_t n_primaries_peak;
+        size_t n_owner_primaries_peak;
         CRBTree name_tree;
 };
 
diff -Nru dbus-broker-36/src/bus/peer.c dbus-broker-37/src/bus/peer.c
--- dbus-broker-36/src/bus/peer.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/peer.c	2025-06-16 13:26:27.000000000 +0100
@@ -298,6 +298,7 @@
         *peer = (Peer)PEER_INIT(*peer);
 
         peer->bus = bus;
+        ++peer->bus->peers.n_peers;
         peer->user = user;
         user = NULL;
         peer->pid = ucred.pid;
@@ -378,6 +379,7 @@
         free(peer->seclabel);
         free(peer->gids);
         user_unref(peer->user);
+        --peer->bus->peers.n_peers;
         free(peer);
 
         close(fd);
@@ -394,12 +396,14 @@
         c_assert(!peer->monitor);
 
         peer->registered = true;
+        ++peer->bus->peers.n_registered;
 }
 
 void peer_unregister(Peer *peer) {
         c_assert(peer->registered);
         c_assert(!peer->monitor);
 
+        --peer->bus->peers.n_registered;
         peer->registered = false;
 }
 
@@ -477,12 +481,16 @@
 }
 
 static int peer_link_match(Peer *peer, MatchRule *rule, bool monitor) {
+        MatchCounters *counters = NULL;
         Address addr;
         Peer *sender, *owner;
         int r;
 
+        if (!monitor)
+                counters = &peer->bus->match_counters;
+
         if (!rule->keys.sender) {
-                r = match_rule_link(rule, &peer->bus->wildcard_matches, monitor);
+                r = match_rule_link(rule, counters, &peer->bus->wildcard_matches, monitor);
                 if (r)
                         return error_fold(r);
         } else if (strcmp(rule->keys.sender, "org.freedesktop.DBus") == 0) {
@@ -499,7 +507,7 @@
                         case ADDRESS_TYPE_ID: {
                                 owner = peer_registry_find_peer(&peer->bus->peers, addr.id);
                                 if (owner) {
-                                        r = match_rule_link(rule, &owner->name_owner_changed_matches, monitor);
+                                        r = match_rule_link(rule, counters, &owner->name_owner_changed_matches, monitor);
                                         if (r)
                                                 return error_fold(r);
                                 } else if (addr.id >= peer->bus->peers.ids) {
@@ -513,7 +521,7 @@
                                          * forthcoming peer, so this is most likely
                                          * a bug in a client.
                                          */
-                                        r = match_rule_link(rule, &peer->bus->sender_matches, monitor);
+                                        r = match_rule_link(rule, counters, &peer->bus->sender_matches, monitor);
                                         if (r)
                                                 return error_fold(r);
                                 } else {
@@ -537,7 +545,7 @@
                                 if (r)
                                         return error_fold(r);
 
-                                r = match_rule_link(rule, &name->name_owner_changed_matches, monitor);
+                                r = match_rule_link(rule, counters, &name->name_owner_changed_matches, monitor);
                                 if (r)
                                         return error_fold(r);
                                 name_ref(name); /* this reference must be explicitly released */
@@ -552,7 +560,7 @@
                          * install other (unexpected) matches here, they will always be false negatives
                          * but for the sake of simplicity we do not attempt to optimize them away.
                          */
-                        r = match_rule_link(rule, &peer->bus->sender_matches, monitor);
+                        r = match_rule_link(rule, counters, &peer->bus->sender_matches, monitor);
                         if (r)
                                 return error_fold(r);
                 }
@@ -562,7 +570,7 @@
                 case ADDRESS_TYPE_ID: {
                         sender = peer_registry_find_peer(&peer->bus->peers, addr.id);
                         if (sender) {
-                                r = match_rule_link(rule, &sender->sender_matches, monitor);
+                                r = match_rule_link(rule, counters, &sender->sender_matches, monitor);
                                 if (r)
                                         return error_fold(r);
                         } else if (addr.id >= peer->bus->peers.ids) {
@@ -571,7 +579,7 @@
                                  * reasoning as above, keep it as a wildcard
                                  * match.
                                  */
-                                r = match_rule_link(rule, &peer->bus->wildcard_matches, monitor);
+                                r = match_rule_link(rule, counters, &peer->bus->wildcard_matches, monitor);
                                 if (r)
                                         return error_fold(r);
                         } else {
@@ -591,7 +599,7 @@
                         if (r)
                                 return error_fold(r);
 
-                        r = match_rule_link(rule, &name->sender_matches, monitor);
+                        r = match_rule_link(rule, counters, &name->sender_matches, monitor);
                         if (r)
                                 return error_fold(r);
                         name_ref(name); /* this reference must be explicitly released */
diff -Nru dbus-broker-36/src/bus/peer.h dbus-broker-37/src/bus/peer.h
--- dbus-broker-36/src/bus/peer.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/peer.h	2025-06-16 13:26:27.000000000 +0100
@@ -96,6 +96,8 @@
 struct PeerRegistry {
         CRBTree peer_tree;
         uint64_t ids;
+        size_t n_peers;
+        size_t n_registered;
 };
 
 #define PEER_REGISTRY_INIT {}
diff -Nru dbus-broker-36/src/bus/test-match.c dbus-broker-37/src/bus/test-match.c
--- dbus-broker-36/src/bus/test-match.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/test-match.c	2025-06-16 13:26:27.000000000 +0100
@@ -161,7 +161,7 @@
         r = match_owner_ref_rule(&owner, &rule, NULL, match_string, false);
         c_assert(!r);
 
-        r = match_rule_link(rule, &registry, false);
+        r = match_rule_link(rule, NULL, &registry, false);
         c_assert(!r);
 
         match_registry_get_subscribers(&registry, &subscribers, metadata);
@@ -254,6 +254,33 @@
         c_assert(!test_match("arg0=com.example.foobar", &metadata));
         c_assert(!test_match("arg0=com.example", &metadata));
 
+        /* arg1 */
+        metadata = (MessageMetadata)MESSAGE_METADATA_INIT;
+        c_assert(!test_match("arg1=/com/example/foo/", &metadata));
+        metadata.args[0].value = "unrelated string";
+        metadata.args[0].element = 's';
+        metadata.args[1].value = "/com/example/foo/";
+        metadata.args[1].element = 's';
+        metadata.n_args = 2;
+        c_assert(test_match("arg1=/com/example/foo/", &metadata));
+        c_assert(!test_match("arg1=/com/example/foo/bar", &metadata));
+        c_assert(!test_match("arg1=/com/example/foobar", &metadata));
+        c_assert(!test_match("arg1=/com/example/", &metadata));
+        c_assert(!test_match("arg1=/com/example", &metadata));
+        metadata.args[1].value = "/com/example/foo";
+        metadata.args[1].element = 's';
+        c_assert(test_match("arg1=/com/example/foo", &metadata));
+        c_assert(!test_match("arg1=/com/example/foo/bar", &metadata));
+        c_assert(!test_match("arg1=/com/example/foobar", &metadata));
+        c_assert(!test_match("arg1=/com/example/", &metadata));
+        c_assert(!test_match("arg1=/com/example", &metadata));
+        metadata.args[1].value = "com.example.foo";
+        metadata.args[1].element = 's';
+        c_assert(test_match("arg1=com.example.foo", &metadata));
+        c_assert(!test_match("arg1=com.example.foo.bar", &metadata));
+        c_assert(!test_match("arg1=com.example.foobar", &metadata));
+        c_assert(!test_match("arg1=com.example", &metadata));
+
         /* arg0path - parent */
         metadata = (MessageMetadata)MESSAGE_METADATA_INIT;
         c_assert(!test_match("arg0path=/com/example/foo/", &metadata));
@@ -278,6 +305,34 @@
         c_assert(test_match("arg0path=/com/example/", &metadata));
         c_assert(!test_match("arg0path=/com/example", &metadata));
 
+        /* arg1path - parent */
+        metadata = (MessageMetadata)MESSAGE_METADATA_INIT;
+        c_assert(!test_match("arg1path=/com/example/foo/", &metadata));
+        metadata.args[0].value = "unrelated string";
+        metadata.args[0].element = 's';
+        metadata.args[1].value = "/com/example/foo/";
+        metadata.args[1].element = 'o';
+        metadata.n_args = 2;
+        c_assert(test_match("arg1path=/com/example/foo/", &metadata));
+        c_assert(test_match("arg1path=/com/example/foo/bar", &metadata));
+        c_assert(!test_match("arg1path=/com/example/foobar", &metadata));
+        c_assert(test_match("arg1path=/com/example/", &metadata));
+        c_assert(!test_match("arg1path=/com/example", &metadata));
+
+        /* arg1path - child */
+        metadata = (MessageMetadata)MESSAGE_METADATA_INIT;
+        c_assert(!test_match("arg1path=/com/example/foo", &metadata));
+        metadata.args[0].value = "unrelated string";
+        metadata.args[0].element = 's';
+        metadata.args[1].value = "/com/example/foo";
+        metadata.args[1].element = 'o';
+        metadata.n_args = 2;
+        c_assert(test_match("arg1path=/com/example/foo", &metadata));
+        c_assert(!test_match("arg1path=/com/example/foo/bar", &metadata));
+        c_assert(!test_match("arg1path=/com/example/foobar", &metadata));
+        c_assert(test_match("arg1path=/com/example/", &metadata));
+        c_assert(!test_match("arg1path=/com/example", &metadata));
+
         /* arg0namespace */
         metadata = (MessageMetadata)MESSAGE_METADATA_INIT;
         c_assert(!test_match("arg0namespace=com.example.foo", &metadata));
@@ -306,25 +361,25 @@
         r = match_owner_ref_rule(&owner1, &rule1, NULL, "", false);
         c_assert(!r);
 
-        r = match_rule_link(rule1, &registry, false);
+        r = match_rule_link(rule1, NULL, &registry, false);
         c_assert(!r);
 
         r = match_owner_ref_rule(&owner1, &rule2, NULL, "", false);
         c_assert(!r);
 
-        r = match_rule_link(rule2, &registry, false);
+        r = match_rule_link(rule2, NULL, &registry, false);
         c_assert(!r);
 
         r = match_owner_ref_rule(&owner2, &rule3, NULL, "", false);
         c_assert(!r);
 
-        r = match_rule_link(rule3, &registry, false);
+        r = match_rule_link(rule3, NULL, &registry, false);
         c_assert(!r);
 
         r = match_owner_ref_rule(&owner2, &rule4, NULL, "", false);
         c_assert(!r);
 
-        r = match_rule_link(rule4, &registry, false);
+        r = match_rule_link(rule4, NULL, &registry, false);
         c_assert(!r);
 
         match_registry_get_subscribers(&registry, &subscribers, &metadata);
@@ -344,7 +399,94 @@
         match_owner_deinit(&owner2);
         match_owner_deinit(&owner1);
         match_registry_deinit(&registry);
+}
+
+static void test_counters(void) {
+        MatchCounters counters = MATCH_COUNTERS_INIT;
+        MatchRegistry registry = MATCH_REGISTRY_INIT(registry);
+        MatchOwner owner1, owner2;
+        MatchRule *rule1, *rule2, *rule3, *rule4;
+        int r;
+
+        match_owner_init(&owner1);
+        match_owner_init(&owner2);
+
+        c_assert(owner1.n_owner_subscriptions == 0);
+        c_assert(owner2.n_owner_subscriptions == 0);
+        c_assert(counters.n_subscriptions == 0);
+        c_assert(counters.n_subscriptions_peak == 0);
+        c_assert(counters.n_owner_subscriptions_peak == 0);
+
+        /* owner1: install a new match */
+
+        r = match_owner_ref_rule(&owner1, &rule1, NULL, "path=/a", false);
+        c_assert(!r);
+
+        r = match_rule_link(rule1, &counters, &registry, false);
+        c_assert(!r);
+
+        c_assert(owner1.n_owner_subscriptions == 1);
+        c_assert(owner2.n_owner_subscriptions == 0);
+        c_assert(counters.n_subscriptions == 1);
+        c_assert(counters.n_subscriptions_peak == 1);
+        c_assert(counters.n_owner_subscriptions_peak == 1);
+
+        /* owner1: install the same match again */
+
+        r = match_owner_ref_rule(&owner1, &rule2, NULL, "path=/a", false);
+        c_assert(!r);
+
+        r = match_rule_link(rule2, &counters, &registry, false);
+        c_assert(!r);
+
+        c_assert(owner1.n_owner_subscriptions == 1);
+        c_assert(owner2.n_owner_subscriptions == 0);
+        c_assert(counters.n_subscriptions == 1);
+        c_assert(counters.n_subscriptions_peak == 1);
+        c_assert(counters.n_owner_subscriptions_peak == 1);
+
+        /* owner2: install a new match */
+
+        r = match_owner_ref_rule(&owner2, &rule3, NULL, "path=/a", false);
+        c_assert(!r);
+
+        r = match_rule_link(rule3, &counters, &registry, false);
+        c_assert(!r);
+
+        c_assert(owner1.n_owner_subscriptions == 1);
+        c_assert(owner2.n_owner_subscriptions == 1);
+        c_assert(counters.n_subscriptions == 2);
+        c_assert(counters.n_subscriptions_peak == 2);
+        c_assert(counters.n_owner_subscriptions_peak == 1);
+
+        /* owner2: install another match */
+
+        r = match_owner_ref_rule(&owner2, &rule4, NULL, "path=/b", false);
+        c_assert(!r);
 
+        r = match_rule_link(rule4, &counters, &registry, false);
+        c_assert(!r);
+
+        c_assert(owner1.n_owner_subscriptions == 1);
+        c_assert(owner2.n_owner_subscriptions == 2);
+        c_assert(counters.n_subscriptions == 3);
+        c_assert(counters.n_subscriptions_peak == 3);
+        c_assert(counters.n_owner_subscriptions_peak == 2);
+
+        match_rule_user_unref(rule4);
+        match_rule_user_unref(rule3);
+        match_rule_user_unref(rule2);
+        match_rule_user_unref(rule1);
+
+        c_assert(owner1.n_owner_subscriptions == 0);
+        c_assert(owner2.n_owner_subscriptions == 0);
+        c_assert(counters.n_subscriptions == 0);
+        c_assert(counters.n_subscriptions_peak == 3);
+        c_assert(counters.n_owner_subscriptions_peak == 2);
+
+        match_owner_deinit(&owner2);
+        match_owner_deinit(&owner1);
+        match_registry_deinit(&registry);
 }
 
 int main(int argc, char **argv) {
@@ -358,8 +500,8 @@
         test_eavesdrop(&owner);
 
         test_individual_matches();
-
         test_iterator();
+        test_counters();
 
         match_owner_deinit(&owner);
         return 0;
diff -Nru dbus-broker-36/src/bus/test-name.c dbus-broker-37/src/bus/test-name.c
--- dbus-broker-36/src/bus/test-name.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/bus/test-name.c	2025-06-16 13:26:27.000000000 +0100
@@ -220,9 +220,219 @@
         name_registry_deinit(&registry);
 }
 
+static void test_queue_counters(void) {
+        NameRegistry registry;
+        NameOwner owner1, owner2;
+        NameChange change;
+        int r;
+
+        /*
+         * Verify that primary counters are adjusted correctly on queue updates
+         * and replacements. They should only count primary ownerships, not any
+         * secondary name queuing.
+         */
+
+        name_registry_init(&registry);
+        name_owner_init(&owner1);
+        name_owner_init(&owner2);
+        name_change_init(&change);
+
+        c_assert(owner1.n_owner_primaries == 0);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 0);
+        c_assert(registry.n_primaries_peak == 0);
+        c_assert(registry.n_owner_primaries_peak == 0);
+
+        /* owner1: foobar */
+        r = name_registry_request_name(&registry, &owner1, NULL, "foobar", 0, &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 1);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* re-request and queuing should not affect counters */
+        r = name_registry_request_name(&registry, &owner1, NULL, "foobar", 0, &change);
+        c_assert(r == NAME_E_ALREADY_OWNER);
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar", DBUS_NAME_FLAG_DO_NOT_QUEUE, &change);
+        c_assert(r == NAME_E_EXISTS);
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar", DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_REPLACE_EXISTING, &change);
+        c_assert(r == NAME_E_EXISTS);
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar", 0, &change);
+        c_assert(r == NAME_E_IN_QUEUE);
+
+        c_assert(owner1.n_owner_primaries == 1);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* dequeue again */
+        r = name_registry_release_name(&registry, &owner2, "foobar", &change);
+        c_assert(r == 0);
+
+        c_assert(owner1.n_owner_primaries == 1);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* queue owner2 then allow replacement (takes effect on next request) */
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar", DBUS_NAME_FLAG_REPLACE_EXISTING, &change);
+        c_assert(r == NAME_E_IN_QUEUE);
+        r = name_registry_request_name(&registry, &owner1, NULL, "foobar", DBUS_NAME_FLAG_ALLOW_REPLACEMENT, &change);
+        c_assert(r == NAME_E_ALREADY_OWNER);
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar", 0, &change);
+        c_assert(r == NAME_E_IN_QUEUE);
+
+        c_assert(owner1.n_owner_primaries == 1);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* now overtake the primary */
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar",
+                                       DBUS_NAME_FLAG_REPLACE_EXISTING |
+                                       DBUS_NAME_FLAG_ALLOW_REPLACEMENT |
+                                       DBUS_NAME_FLAG_DO_NOT_QUEUE,
+                                       &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 0);
+        c_assert(owner2.n_owner_primaries == 1);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* overtake again */
+        r = name_registry_request_name(&registry, &owner1, NULL, "foobar", DBUS_NAME_FLAG_REPLACE_EXISTING, &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 1);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* release names */
+        r = name_registry_release_name(&registry, &owner1, "foobar", &change);
+        c_assert(r == 0);
+        name_change_deinit(&change);
+
+        name_owner_deinit(&owner2);
+        name_owner_deinit(&owner1);
+        name_registry_deinit(&registry);
+}
+
+static void test_peak_counters(void) {
+        NameRegistry registry;
+        NameOwner owner1, owner2;
+        NameChange change;
+        int r;
+
+        /*
+         * Verify that primary peak-counters are adjusted correctly when peers
+         * acquire primary names.
+         */
+
+        name_registry_init(&registry);
+        name_owner_init(&owner1);
+        name_owner_init(&owner2);
+        name_change_init(&change);
+
+        c_assert(owner1.n_owner_primaries == 0);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 0);
+        c_assert(registry.n_primaries_peak == 0);
+        c_assert(registry.n_owner_primaries_peak == 0);
+
+        /* owner1: foobar0 */
+        r = name_registry_request_name(&registry, &owner1, NULL, "foobar0", 0, &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 1);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 1);
+        c_assert(registry.n_primaries_peak == 1);
+        c_assert(registry.n_owner_primaries_peak == 1);
+
+        /* owner1: foobar1 */
+        r = name_registry_request_name(&registry, &owner1, NULL, "foobar1", 0, &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 2);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 2);
+        c_assert(registry.n_primaries_peak == 2);
+        c_assert(registry.n_owner_primaries_peak == 2);
+
+        /* owner2: foobar2 */
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar2", 0, &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 2);
+        c_assert(owner2.n_owner_primaries == 1);
+        c_assert(registry.n_primaries == 3);
+        c_assert(registry.n_primaries_peak == 3);
+        c_assert(registry.n_owner_primaries_peak == 2);
+
+        /* owner2: foobar3 */
+        r = name_registry_request_name(&registry, &owner2, NULL, "foobar3", 0, &change);
+        c_assert(!r);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 2);
+        c_assert(owner2.n_owner_primaries == 2);
+        c_assert(registry.n_primaries == 4);
+        c_assert(registry.n_primaries_peak == 4);
+        c_assert(registry.n_owner_primaries_peak == 2);
+
+        /* release names */
+        r = name_registry_release_name(&registry, &owner2, "foobar3", &change);
+        c_assert(r == 0);
+        name_change_deinit(&change);
+        r = name_registry_release_name(&registry, &owner2, "foobar2", &change);
+        c_assert(r == 0);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 2);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 2);
+        c_assert(registry.n_primaries_peak == 4);
+        c_assert(registry.n_owner_primaries_peak == 2);
+
+        r = name_registry_release_name(&registry, &owner1, "foobar1", &change);
+        c_assert(r == 0);
+        name_change_deinit(&change);
+        r = name_registry_release_name(&registry, &owner1, "foobar0", &change);
+        c_assert(r == 0);
+        name_change_deinit(&change);
+
+        c_assert(owner1.n_owner_primaries == 0);
+        c_assert(owner2.n_owner_primaries == 0);
+        c_assert(registry.n_primaries == 0);
+        c_assert(registry.n_primaries_peak == 4);
+        c_assert(registry.n_owner_primaries_peak == 2);
+
+        name_owner_deinit(&owner2);
+        name_owner_deinit(&owner1);
+        name_registry_deinit(&registry);
+}
+
 int main(int argc, char **argv) {
         test_setup();
         test_release();
         test_queue();
+        test_queue_counters();
+        test_peak_counters();
         return 0;
 }
diff -Nru dbus-broker-36/src/dbus/protocol.h dbus-broker-37/src/dbus/protocol.h
--- dbus-broker-36/src/dbus/protocol.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/dbus/protocol.h	2025-06-16 13:26:27.000000000 +0100
@@ -63,5 +63,5 @@
 bool dbus_validate_name(const char *name, size_t n_name);
 bool dbus_validate_namespace(const char *namespace, size_t n_namespace);
 bool dbus_validate_interface(const char *interface, size_t n_interface);
-bool dbus_validate_member(const char *memebr, size_t n_member);
+bool dbus_validate_member(const char *member, size_t n_member);
 bool dbus_validate_error_name(const char *name, size_t n_name);
diff -Nru dbus-broker-36/src/dbus/socket.c dbus-broker-37/src/dbus/socket.c
--- dbus-broker-36/src/dbus/socket.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/dbus/socket.c	2025-06-16 13:26:27.000000000 +0100
@@ -173,9 +173,13 @@
 
         for ( ; !socket_buffer_is_consumed(buffer); ++buffer->writer) {
                 t = c_min(buffer->writer->iov_len, n);
-                buffer->writer->iov_len -= t;
-                buffer->writer->iov_base += t;
-                n -= t;
+                // IOVs can be empty/NULL. Ensure we do not calculate
+                // `NULL + 0`, as this is, unfortunately, UB.
+                if (t) {
+                        buffer->writer->iov_len -= t;
+                        buffer->writer->iov_base += t;
+                        n -= t;
+                }
                 if (buffer->writer->iov_len)
                         break;
         }
diff -Nru dbus-broker-36/src/launch/launcher.c dbus-broker-37/src/launch/launcher.c
--- dbus-broker-36/src/launch/launcher.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/launch/launcher.c	2025-06-16 13:26:27.000000000 +0100
@@ -29,6 +29,7 @@
 #include "util/fs.h"
 #include "util/log.h"
 #include "util/misc.h"
+#include "util/nsec.h"
 #include "util/string.h"
 
 /*
@@ -159,7 +160,7 @@
         return 1;
 }
 
-static int launcher_open_log(Launcher *launcher) {
+static int launcher_open_journal(int *fdp) {
         _c_cleanup_(c_closep) int fd = -1;
         struct sockaddr_un address = {
                 .sun_family = AF_UNIX,
@@ -167,8 +168,6 @@
         };
         int r;
 
-        c_assert(log_get_fd(&launcher->log) < 0);
-
         fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (fd < 0)
                 return error_origin(-errno);
@@ -179,8 +178,21 @@
         if (r < 0)
                 return error_origin(-errno);
 
-        log_init_journal_consume(&launcher->log, fd);
+        *fdp = fd;
         fd = -1;
+        return 0;
+}
+
+static int launcher_open_log(Launcher *launcher) {
+        int r, fd;
+
+        c_assert(log_get_fd(&launcher->log) < 0);
+
+        r = launcher_open_journal(&fd);
+        if (r)
+                return error_fold(r);
+
+        log_init_journal_consume(&launcher->log, fd);
 
         /* XXX: make this run-time optional */
         log_set_lossy(&launcher->log, true);
@@ -258,7 +270,7 @@
         return NULL;
 }
 
-static noreturn void launcher_run_child(Launcher *launcher, int fd_log, int fd_controller) {
+static noreturn void launcher_run_child(Launcher *launcher, int fd_controller) {
         sd_id128_t machine_id;
         char str_log[C_DECIMAL_MAX(int) + 1],
              str_controller[C_DECIMAL_MAX(int) + 1],
@@ -283,7 +295,7 @@
                 launcher->audit ? "--audit" : NULL, /* note that this needs to be the last argument to work */
                 NULL,
         };
-        int r;
+        int r, fd_journal;
 
         if (launcher->uid != (uint32_t)-1) {
                 r = util_audit_drop_permissions(launcher->uid, launcher->gid);
@@ -297,13 +309,19 @@
                 goto exit;
         }
 
-        r = fcntl(fd_log, F_GETFD);
+        r = launcher_open_journal(&fd_journal);
+        if (r) {
+                r = error_trace(r);
+                goto exit;
+        }
+
+        r = fcntl(fd_journal, F_GETFD);
         if (r < 0) {
                 r = error_origin(-errno);
                 goto exit;
         }
 
-        r = fcntl(fd_log, F_SETFD, r & ~FD_CLOEXEC);
+        r = fcntl(fd_journal, F_SETFD, r & ~FD_CLOEXEC);
         if (r < 0) {
                 r = error_origin(-errno);
                 goto exit;
@@ -329,7 +347,7 @@
 
         sd_id128_to_string(machine_id, str_machine_id);
 
-        r = snprintf(str_log, sizeof(str_log), "%d", fd_log);
+        r = snprintf(str_log, sizeof(str_log), "%d", fd_journal);
         c_assert(r < (ssize_t)sizeof(str_log));
 
         r = snprintf(str_controller, sizeof(str_controller), "%d", fd_controller);
@@ -375,7 +393,7 @@
                 return error_origin(-errno);
 
         if (!pid)
-                launcher_run_child(launcher, log_get_fd(&launcher->log), fd_controller);
+                launcher_run_child(launcher, fd_controller);
 
         r = sd_event_add_child(launcher->event, NULL, pid, WEXITED, launcher_on_child_exit, launcher);
         if (r < 0)
@@ -925,6 +943,8 @@
 
 static int launcher_load_standard_system_services(Launcher *launcher, NSSCache *nss_cache) {
         static const char *default_data_dirs[] = {
+                "/etc",
+                "/run",
                 "/usr/local/share",
                 "/usr/share",
                 "/lib",
@@ -1165,7 +1185,10 @@
         Service *service;
         int r, res;
 
-        r = sd_notify(false, "RELOADING=1");
+        r = sd_notifyf(/* unset_environment = */ false,
+                       "RELOADING=1\n"
+                       "MONOTONIC_USEC=%" NSEC_PRI,
+                       nsec_to_usec(nsec_now(CLOCK_MONOTONIC)));
         if (r < 0)
                 return error_origin(r);
 
diff -Nru dbus-broker-36/src/launch/nss-cache.c dbus-broker-37/src/launch/nss-cache.c
--- dbus-broker-36/src/launch/nss-cache.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/launch/nss-cache.c	2025-06-16 13:26:27.000000000 +0100
@@ -491,9 +491,13 @@
         return 0;
 }
 
-int nss_cache_resolve_system_console_users(NSSCache *nss_cache, uint32_t **uidsp, size_t *n_uidsp) {
-        static const char * const usernames[] = { SYSTEM_CONSOLE_USERS };
-        static const size_t n_usernames = C_ARRAY_SIZE(usernames);
+static int nss_cache_resolve_names(
+        NSSCache *nss_cache,
+        uint32_t **uidsp,
+        size_t *n_uidsp,
+        const char * const *usernames,
+        size_t n_usernames
+) {
         _c_cleanup_(c_freep) uint32_t *uids = NULL;
         size_t i, n_uids = 0;
         uid_t uid;
@@ -526,3 +530,14 @@
         uids = NULL;
         return 0;
 }
+
+int nss_cache_resolve_system_console_users(NSSCache *nss_cache, uint32_t **uidsp, size_t *n_uidsp) {
+        static const char * const usernames[] = { SYSTEM_CONSOLE_USERS };
+        static const size_t n_usernames = C_ARRAY_SIZE(usernames);
+
+        // We avoid inlining `nss_cache_resolve_names()` here, as GCC will start
+        // complaining about use of `usernames` if it is empty, even though the
+        // function bails out early if it is empty.
+
+        return nss_cache_resolve_names(nss_cache, uidsp, n_uidsp, usernames, n_usernames);
+}
diff -Nru dbus-broker-36/src/launch/policy.c dbus-broker-37/src/launch/policy.c
--- dbus-broker-36/src/launch/policy.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/launch/policy.c	2025-06-16 13:26:27.000000000 +0100
@@ -396,16 +396,19 @@
         } else if (cnode->parent->policy.context == CONFIG_POLICY_AT_CONSOLE) {
                 c_list_link_tail(&policy->at_console_entries.own_list, &record->link);
         } else if (cnode->parent->policy.context == CONFIG_POLICY_GROUP) {
-                r = policy_at_gid(policy, &node, cnode->parent->policy.id);
-                if (r)
-                        return error_trace(r);
+                if (cnode->parent->policy.id != (uint32_t)-1) {
+                        r = policy_at_gid(policy, &node, cnode->parent->policy.id);
+                        if (r)
+                                return error_trace(r);
 
-                c_list_link_tail(&node->entries.own_list, &record->link);
+                        c_list_link_tail(&node->entries.own_list, &record->link);
+                }
         } else {
                 c_list_link_tail(&policy->default_entries.own_list, &record->link);
         }
 
-        record = NULL;
+        if (c_list_is_linked(&record->link))
+                record = NULL;
         return 0;
 }
 
@@ -497,7 +500,8 @@
                 c_list_link_tail(&policy->default_entries.send_list, &record->link);
         }
 
-        record = NULL;
+        if (c_list_is_linked(&record->link))
+                record = NULL;
         return 0;
 }
 
@@ -589,7 +593,8 @@
                 c_list_link_tail(&policy->default_entries.recv_list, &record->link);
         }
 
-        record = NULL;
+        if (c_list_is_linked(&record->link))
+                record = NULL;
         return 0;
 }
 
diff -Nru dbus-broker-36/src/meson.build dbus-broker-37/src/meson.build
--- dbus-broker-36/src/meson.build	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/meson.build	2025-06-16 13:26:27.000000000 +0100
@@ -40,6 +40,7 @@
         'util/log.c',
         'util/metrics.c',
         'util/misc.c',
+        'util/nsec.c',
         'util/proc.c',
         'util/sockopt.c',
         'util/string.c',
@@ -55,6 +56,8 @@
         dep_math,
 ]
 
+incs_bus = include_directories('.')
+
 if use_apparmor
         sources_bus += [
                 'util/apparmor.c',
@@ -119,7 +122,7 @@
 )
 
 dep_bus = declare_dependency(
-        include_directories: include_directories('.'),
+        include_directories: incs_bus,
         link_with: static_bus,
         dependencies: deps_bus,
         version: meson.project_version(),
@@ -217,6 +220,9 @@
 test_name = executable('test-name', sources: ['bus/test-name.c'], kwargs: test_kwargs)
 test('Name Registry', test_name, suite: 'unit')
 
+test_nsec = executable('test-nsec', sources: ['util/test-nsec.c'], kwargs: test_kwargs)
+test('Nanosecond Time', test_nsec, suite: 'unit')
+
 if use_launcher
         test_nss_cache = executable('test-nss-cache', sources: ['launch/test-nss-cache.c'], kwargs: test_kwargs)
         test('NSS Cache', test_nss_cache, suite: 'unit')
diff -Nru dbus-broker-36/src/units/system/dbus-broker.service.in dbus-broker-37/src/units/system/dbus-broker.service.in
--- dbus-broker-36/src/units/system/dbus-broker.service.in	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/units/system/dbus-broker.service.in	2025-06-16 13:26:27.000000000 +0100
@@ -8,7 +8,7 @@
 Conflicts=shutdown.target
 
 [Service]
-Type=notify
+Type=notify-reload
 Sockets=dbus.socket
 OOMScoreAdjust=-900
 LimitNOFILE=16384
@@ -16,7 +16,6 @@
 PrivateTmp=true
 PrivateDevices=true
 ExecStart=@bindir@/dbus-broker-launch --scope system --audit
-ExecReload=@bindir@/busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus ReloadConfig
 
 [Install]
 Alias=dbus.service
diff -Nru dbus-broker-36/src/units/user/dbus-broker.service.in dbus-broker-37/src/units/user/dbus-broker.service.in
--- dbus-broker-36/src/units/user/dbus-broker.service.in	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/units/user/dbus-broker.service.in	2025-06-16 13:26:27.000000000 +0100
@@ -8,10 +8,9 @@
 Conflicts=shutdown.target
 
 [Service]
-Type=notify
+Type=notify-reload
 Sockets=dbus.socket
 ExecStart=@bindir@/dbus-broker-launch --scope user
-ExecReload=@bindir@/busctl --user call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus ReloadConfig
 Slice=session.slice
 
 [Install]
diff -Nru dbus-broker-36/src/util/apparmor.c dbus-broker-37/src/util/apparmor.c
--- dbus-broker-36/src/util/apparmor.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/apparmor.c	2025-06-16 13:26:27.000000000 +0100
@@ -179,7 +179,7 @@
          * the right UID to use, follow dbus-daemon(1) and use our
          * own. */
         r = util_audit_log(UTIL_AUDIT_TYPE_AVC, message, getuid());
-        if (r)
+        if (r != UTIL_AUDIT_E_UNAVAILABLE) // XXX: use a log fallback
                 return error_fold(r);
 
         return 0;
diff -Nru dbus-broker-36/src/util/audit.c dbus-broker-37/src/util/audit.c
--- dbus-broker-36/src/util/audit.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/audit.c	2025-06-16 13:26:27.000000000 +0100
@@ -96,39 +96,58 @@
  * @message:    the message to be logged
  * @uid:        the UID of the user causing the message to be logged
  *
- * Log the message to the audit subsystem. If audit is disabled, log to
- * stderr instead.
+ * Log the message to the audit subsystem. If audit is disabled, return
+ * UTIL_AUDIT_E_UNAVAILABLE instead.
  *
- * Return: 0 on success, or a negative error code on failure.
+ * Return: 0 on success, UTIL_AUDIT_E_UNAVAILABLE if audit is not
+ *         available, or a negative error code on failure.
  */
 int util_audit_log(int type, const char *message, uid_t uid) {
-        int r, audit_type;
+        int r;
 
-        switch(type) {
+        if (audit_fd < 0)
+                return UTIL_AUDIT_E_UNAVAILABLE;
+
+        switch (type) {
         case UTIL_AUDIT_TYPE_AVC:
-                audit_type = AUDIT_USER_AVC;
+                r = audit_log_user_avc_message(
+                        audit_fd,
+                        AUDIT_USER_AVC,
+                        message,
+                        NULL,
+                        NULL,
+                        NULL,
+                        uid
+                );
                 break;
         case UTIL_AUDIT_TYPE_POLICYLOAD:
-                audit_type = AUDIT_USER_MAC_POLICY_LOAD;
+                r = audit_log_user_message(
+                        audit_fd,
+                        AUDIT_USER_MAC_POLICY_LOAD,
+                        message,
+                        NULL,
+                        NULL,
+                        NULL,
+                        1
+                );
                 break;
         case UTIL_AUDIT_TYPE_MAC_STATUS:
-                audit_type = AUDIT_USER_MAC_STATUS;
+                r = audit_log_user_message(
+                        audit_fd,
+                        AUDIT_USER_MAC_STATUS,
+                        message,
+                        NULL,
+                        NULL,
+                        NULL,
+                        1
+                );
                 break;
-        case UTIL_AUDIT_TYPE_NOAUDIT:
         default:
-                audit_type = 0;
-                break;
+                return error_origin(-ENOTRECOVERABLE);
         }
 
-        if (audit_fd >= 0 && type != UTIL_AUDIT_TYPE_NOAUDIT) {
-                r = audit_log_user_avc_message(audit_fd, audit_type, message, NULL, NULL, NULL, uid);
-                if (r <= 0)
-                        return error_origin(-errno);
-        } else {
-                r = fprintf(stderr, "%s\n", message);
-                if (r < 0)
-                        return error_origin(r);
-        }
+        if (r <= 0)
+                return error_origin(-errno);
 
         return 0;
 }
diff -Nru dbus-broker-36/src/util/audit-fallback.c dbus-broker-37/src/util/audit-fallback.c
--- dbus-broker-36/src/util/audit-fallback.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/audit-fallback.c	2025-06-16 13:26:27.000000000 +0100
@@ -22,13 +22,7 @@
 }
 
 int util_audit_log(int type, const char *message, uid_t uid) {
-        int r;
-
-        r = fprintf(stderr, "%s\n", message);
-        if (r < 0)
-                return error_origin(r);
-
-        return 0;
+        return UTIL_AUDIT_E_UNAVAILABLE;
 }
 
 int util_audit_init_global(void) {
diff -Nru dbus-broker-36/src/util/audit.h dbus-broker-37/src/util/audit.h
--- dbus-broker-36/src/util/audit.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/audit.h	2025-06-16 13:26:27.000000000 +0100
@@ -8,7 +8,12 @@
 #include <stdlib.h>
 
 enum {
-        UTIL_AUDIT_TYPE_NOAUDIT,
+        _UTIL_AUDIT_E_SUCCESS,
+
+        UTIL_AUDIT_E_UNAVAILABLE,
+};
+
+enum {
         UTIL_AUDIT_TYPE_AVC,
         UTIL_AUDIT_TYPE_POLICYLOAD,
         UTIL_AUDIT_TYPE_MAC_STATUS,
diff -Nru dbus-broker-36/src/util/misc.c dbus-broker-37/src/util/misc.c
--- dbus-broker-36/src/util/misc.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/misc.c	2025-06-16 13:26:27.000000000 +0100
@@ -49,7 +49,7 @@
  *    Throw in `MFD_CLOEXEC` or `F_SEAL_SEAL` as required.
  *
  *  * If `MFD_NOEXEC_SEAL` is used without `MFD_ALLOW_SEALING`, sealing will
- *    be disabled (even though the kernel implicitly enables it).
+ *    be disabled (even though some kernel versions implicitly enable it).
  *
  *  * An initial set of seals is applied to the memfd, if specified in
  *    @seals. Note that this is not allowed if sealing was not enabled.
@@ -60,6 +60,7 @@
         _c_cleanup_(c_closep) int fd = -1;
         unsigned int flags = uflags;
         unsigned int seals = useals;
+        unsigned int kseals;
         struct stat st;
         int r;
 
@@ -111,11 +112,11 @@
         }
 
         /*
-         * If we ended up passing `MFG_NOEXEC_SEAL` to the kernel, the kernel
-         * will implicitly enable sealing. This is very unfortunate, so we
-         * revert this if the caller did not explicitly allow it. To disable
-         * sealing, simply set `F_SEAL_SEAL`, which is also what the kernel
-         * does.
+         * If we ended up passing `MFG_NOEXEC_SEAL` to the kernel, some kernel
+         * versions will implicitly enable sealing. This is very unfortunate,
+         * so we revert this if the caller did not explicitly allow it. To
+         * disable sealing, simply set `F_SEAL_SEAL`, which is also what the
+         * kernel does.
          */
         if ((flags & MISC_MFD_NOEXEC_SEAL) && !(flags & MISC_MFD_ALLOW_SEALING))
                 seals |= MISC_F_SEAL_SEAL;
@@ -126,9 +127,15 @@
          * into the kernel again.
          */
         if (seals) {
-                r = misc_memfd_add_seals(fd, seals);
+                r = misc_memfd_get_seals(fd, &kseals);
                 if (r)
                         return error_fold(r);
+
+                if (seals & ~kseals) {
+                        r = misc_memfd_add_seals(fd, seals);
+                        if (r)
+                                return error_fold(r);
+                }
         }
 
         r = fd;
@@ -167,20 +174,24 @@
 /**
  * misc_memfd_get_seals() - query seals of a memfd
  * @fd:         memfd to operate on
+ * @sealsp:     output argument to store retrieved seals
  *
  * Query the seals of the memfd. If the FD does not refer to a memfd (or other
  * file that supports sealing), an error will be returned.
  *
- * Return: Seal mask of the memfd is returned, negative error code on failure.
+ * On success, the seals are written to @sealsp.
+ *
+ * Return: 0 on success, negative error code on failure.
  */
-int misc_memfd_get_seals(int fd) {
+int misc_memfd_get_seals(int fd, unsigned int *sealsp) {
         int seals;
 
         seals = fcntl(fd, MISC_F_GET_SEALS);
         if (seals < 0)
                 return error_origin(-errno);
 
-        return seals;
+        *sealsp = seals;
+        return 0;
 }
 
 /**
@@ -202,6 +213,44 @@
         return res;
 }
 
+/**
+ * util_z2u_saturating() - saturating cast of size_t to unsigned int
+ * @v:                  value to cast
+ *
+ * This will cast a value of `size_t` to `unsigned int`, saturating the
+ * value at `UINT_MAX` in case of overflow.
+ *
+ * Return: The casted, saturated value is returned.
+ */
+unsigned int util_z2u_saturating(size_t v) {
+        unsigned int cast;
+
+        cast = (unsigned int)v;
+        if ((size_t)cast != v)
+                return UINT_MAX;
+        else
+                return cast;
+}
+
+/**
+ * util_t2u_saturating() - saturating cast of uint64_t to unsigned int
+ * @v:                  value to cast
+ *
+ * This will cast a value of `uint64_t` to `unsigned int`, saturating the
+ * value at `UINT_MAX` in case of overflow.
+ *
+ * Return: The casted, saturated value is returned.
+ */
+unsigned int util_t2u_saturating(uint64_t v) {
+        unsigned int cast;
+
+        cast = (unsigned int)v;
+        if ((uint64_t)cast != v)
+                return UINT_MAX;
+        else
+                return cast;
+}
+
 int util_drop_permissions(uint32_t uid, uint32_t gid) {
         int r;
 
@@ -218,3 +267,8 @@
 
         return 0;
 }
+
+void util_peak_update(size_t *peak, size_t update) {
+        if (update > *peak)
+                *peak = update;
+}
diff -Nru dbus-broker-36/src/util/misc.h dbus-broker-37/src/util/misc.h
--- dbus-broker-36/src/util/misc.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/misc.h	2025-06-16 13:26:27.000000000 +0100
@@ -22,7 +22,11 @@
 
 int misc_memfd(const char *name, unsigned int uflags, unsigned int useals);
 int misc_memfd_add_seals(int fd, unsigned int seals);
-int misc_memfd_get_seals(int fd);
+int misc_memfd_get_seals(int fd, unsigned int *sealsp);
 
 uint64_t util_umul64_saturating(uint64_t a, uint64_t b);
+unsigned int util_z2u_saturating(size_t v);
+unsigned int util_t2u_saturating(uint64_t v);
 int util_drop_permissions(uint32_t uid, uint32_t gid);
+
+void util_peak_update(size_t *peak, size_t update);
diff -Nru dbus-broker-36/src/util/nsec.c dbus-broker-37/src/util/nsec.c
--- dbus-broker-36/src/util/nsec.c	1970-01-01 01:00:00.000000000 +0100
+++ dbus-broker-37/src/util/nsec.c	2025-06-16 13:26:27.000000000 +0100
@@ -0,0 +1,85 @@
+/*
+ * Nanosecond Time Management
+ *
+ * This module provides time management utilities around the `nsec_t` type,
+ * which carries time information encoded as nano-seconds since a
+ * clock-specific EPOCH. The clock source is not encoded at all but must be
+ * transferred via other means, if necessary.
+ *
+ * A 64-bit unsigned integer is used as backing data type. This can store
+ * seconds up to:
+ *
+ *     2^64 / 1_000_000_000 = 18,446,744,073.7
+ *
+ * or years up to:
+ *
+ *     2^64 / 1_000_000_000 / 60 / 60 / 24 / 365 ~= 584
+ *
+ * This is a suitable range for time-keeping in most situations. If any
+ * calendar, or other date-related functionality is needed, this type might not
+ * be suitable.
+ */
+
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include "util/nsec.h"
+
+#define NSEC_PER_SEC UINT64_C(1000000000)
+
+/* Similar to `intprops.h`: provides `_MAX` for signed types without it. */
+#define NSEC_TIME_T_MAX                                                 \
+        ((time_t)(                                                      \
+                (UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)      \
+        )
+
+/**
+ * nsec_now() - get the current time in nanoseconds
+ * @clock:      clock to query
+ *
+ * Read the current time and return it in nanoseconds. The clock must be
+ * specified by the caller. Only non-fallible clocks can be used with this
+ * function.
+ *
+ * Return: the timestamp in nano seconds.
+ */
+nsec_t nsec_now(clockid_t clock) {
+        struct timespec ts;
+        int r;
+
+        r = clock_gettime(clock, &ts);
+        c_assert(r >= 0);
+
+        return (uint64_t)ts.tv_sec * NSEC_PER_SEC + (uint64_t)ts.tv_nsec;
+}
+
+/**
+ * nsec_sleep() - pause execution
+ * @clock:      clock to use for time keeping
+ * @until:      absolute timeout of the pause
+ *
+ * Pause exeuction until the absolute timeout specified by `until` is reached.
+ * The timeout must be relative to the clock specified by `clock`.
+ *
+ * The operation is automatically repeated, if it is interrupted by a signal.
+ */
+void nsec_sleep(clockid_t clock, nsec_t until) {
+        struct timespec ts;
+        uint64_t tv_sec, tv_nsec;
+        int r;
+
+        tv_sec = until / NSEC_PER_SEC;
+        tv_nsec = until % NSEC_PER_SEC;
+
+        c_assert(tv_sec <= NSEC_TIME_T_MAX);
+
+        ts.tv_sec = (time_t)tv_sec;
+        ts.tv_nsec = (long)tv_nsec;
+
+        do {
+                /* Note that `clock_nanosleep()` does not use `errno`! */
+                r = clock_nanosleep(clock, TIMER_ABSTIME, &ts, NULL);
+                c_assert(r == 0 || r == EINTR);
+        } while (r == EINTR);
+}
diff -Nru dbus-broker-36/src/util/nsec.h dbus-broker-37/src/util/nsec.h
--- dbus-broker-36/src/util/nsec.h	1970-01-01 01:00:00.000000000 +0100
+++ dbus-broker-37/src/util/nsec.h	2025-06-16 13:26:27.000000000 +0100
@@ -0,0 +1,25 @@
+#pragma once
+
+/*
+ * Nanosecond Time Management
+ */
+
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+
+typedef uint64_t nsec_t;
+
+#define NSEC_PRI PRIu64
+
+/* nsec */
+
+nsec_t nsec_now(clockid_t clock);
+void nsec_sleep(clockid_t clock, nsec_t until);
+
+/* inline helpers */
+
+static inline uint64_t nsec_to_usec(nsec_t t) {
+        return t / 1000;
+}
diff -Nru dbus-broker-36/src/util/selinux.c dbus-broker-37/src/util/selinux.c
--- dbus-broker-36/src/util/selinux.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/selinux.c	2025-06-16 13:26:27.000000000 +0100
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 #include "util/audit.h"
 #include "util/error.h"
+#include "util/log.h"
 #include "util/ref.h"
 #include "util/selinux.h"
 
@@ -26,6 +27,7 @@
 
 typedef struct BusSELinuxName BusSELinuxName;
 
+static Log *bus_selinux_log = NULL;
 static bool bus_selinux_avc_open;
 static bool bus_selinux_status_open;
 
@@ -303,10 +305,11 @@
         return 0;
 }
 
-static int bus_selinux_log(int type, const char *fmt, ...) {
+static int bus_selinux_log_fn(int type, const char *fmt, ...) {
         _c_cleanup_(c_freep) char *message = NULL;
+        const char *loghdr = NULL;
         va_list ap;
-        int r, audit_type;
+        int r, loglvl = 0;
 
         va_start(ap, fmt);
         r = vasprintf(&message, fmt, ap);
@@ -314,50 +317,91 @@
         if (r < 0)
                 return r;
 
-        switch(type) {
+        switch (type) {
         case SELINUX_AVC:
-                audit_type = UTIL_AUDIT_TYPE_AVC;
+                /* XXX: we don't have access to any context, so can't find
+                 * the right UID to use, follow dbus-daemon(1) and use our
+                 * own. */
+                r = util_audit_log(UTIL_AUDIT_TYPE_AVC, message, getuid());
+                if (r == UTIL_AUDIT_E_UNAVAILABLE) {
+                        loghdr = "selinux/avc";
+                        loglvl = LOG_INFO;
+                } else if (r) {
+                        return error_fold(r);
+                }
+
                 break;
         case SELINUX_POLICYLOAD:
-                audit_type = UTIL_AUDIT_TYPE_POLICYLOAD;
+                r = util_audit_log(UTIL_AUDIT_TYPE_POLICYLOAD, message, getuid());
+                if (r == UTIL_AUDIT_E_UNAVAILABLE) {
+                        loghdr = "selinux/policyload";
+                        loglvl = LOG_INFO;
+                } else if (r) {
+                        return error_fold(r);
+                }
+
                 break;
         case SELINUX_SETENFORCE:
-                audit_type = UTIL_AUDIT_TYPE_MAC_STATUS;
+                r = util_audit_log(UTIL_AUDIT_TYPE_MAC_STATUS, message, getuid());
+                if (r == UTIL_AUDIT_E_UNAVAILABLE) {
+                        loghdr = "selinux/macstatus";
+                        loglvl = LOG_INFO;
+                } else if (r) {
+                        return error_fold(r);
+                }
+
+                break;
+        case SELINUX_ERROR:
+                loghdr = "selinux/error";
+                loglvl = LOG_ERR;
+                break;
+        case SELINUX_WARNING:
+                loghdr = "selinux/warning";
+                loglvl = LOG_WARNING;
+                break;
+        case SELINUX_INFO:
+                loghdr = "selinux/info";
+                loglvl = LOG_INFO;
                 break;
         default:
-                /* not an auditable message. */
-                audit_type = UTIL_AUDIT_TYPE_NOAUDIT;
+                loghdr = "selinux/unknown";
+                loglvl = LOG_WARNING;
                 break;
         }
 
-        /* XXX: we don't have access to any context, so can't find
-         * the right UID to use, follow dbus-daemon(1) and use our
-         * own. */
-        r = util_audit_log(audit_type, message, getuid());
-        if (r)
-                return error_fold(r);
+        if (loghdr && bus_selinux_log) {
+                log_append_here(bus_selinux_log, loglvl, 0, NULL);
+                r = log_commitf(bus_selinux_log, "%s: %s", loghdr, message);
+                if (r)
+                        return error_fold(r);
+        }
 
         return 0;
 }
 
 /**
  * bus_selinux_init_global() - initialize the global SELinux context
+ * @log:                log object to use
  *
  * Initialize the global SELinux context. This must be called before any
  * other SELinux function.
  *
  * Return: 0 on success, or a negative error code on failure.
  */
-int bus_selinux_init_global(void) {
+int bus_selinux_init_global(Log *log) {
         int r;
 
         if (!is_selinux_enabled())
                 return 0;
 
+        bus_selinux_log = log;
+
         if (!bus_selinux_avc_open) {
                 r = avc_open(NULL, 0);
-                if (r)
-                        return error_origin(-errno);
+                if (r) {
+                        r = error_origin(-errno);
+                        goto error;
+                }
 
                 bus_selinux_avc_open = true;
         }
@@ -385,11 +429,15 @@
                 }
         }
 
-        selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback)bus_selinux_log);
+        selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback)bus_selinux_log_fn);
 
         /* XXX: set audit callback to get more metadata in the audit log? */
 
         return 0;
+
+error:
+        bus_selinux_log = NULL;
+        return error_trace(r);
 }
 
 /**
@@ -412,4 +460,6 @@
                 avc_destroy();
                 bus_selinux_avc_open = false;
         }
+
+        bus_selinux_log = NULL;
 }
diff -Nru dbus-broker-36/src/util/selinux-fallback.c dbus-broker-37/src/util/selinux-fallback.c
--- dbus-broker-36/src/util/selinux-fallback.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/selinux-fallback.c	2025-06-16 13:26:27.000000000 +0100
@@ -53,7 +53,7 @@
         return 0;
 }
 
-int bus_selinux_init_global(void) {
+int bus_selinux_init_global(Log *log) {
         return 0;
 }
 
diff -Nru dbus-broker-36/src/util/selinux.h dbus-broker-37/src/util/selinux.h
--- dbus-broker-36/src/util/selinux.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/selinux.h	2025-06-16 13:26:27.000000000 +0100
@@ -8,6 +8,7 @@
 #include <stdlib.h>
 
 typedef struct BusSELinuxRegistry BusSELinuxRegistry;
+typedef struct Log Log;
 
 enum {
         _SELINUX_E_SUCCESS,
@@ -34,5 +35,5 @@
                            const char *context_sender,
                            const char *context_receiver);
 
-int bus_selinux_init_global(void);
+int bus_selinux_init_global(Log *log);
 void bus_selinux_deinit_global(void);
diff -Nru dbus-broker-36/src/util/sockopt.c dbus-broker-37/src/util/sockopt.c
--- dbus-broker-36/src/util/sockopt.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/sockopt.c	2025-06-16 13:26:27.000000000 +0100
@@ -238,7 +238,7 @@
                         return SOCKOPT_E_UNSUPPORTED;
                 if (errno == ENODATA)
                         return SOCKOPT_E_UNAVAILABLE;
-                if (errno == EINVAL)
+                if (errno == EINVAL || errno == ESRCH)
                         return SOCKOPT_E_REAPED;
 
                 return error_origin(-errno);
diff -Nru dbus-broker-36/src/util/string.h dbus-broker-37/src/util/string.h
--- dbus-broker-36/src/util/string.h	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/string.h	2025-06-16 13:26:27.000000000 +0100
@@ -78,7 +78,8 @@
  * destination buffer must be at least twice as big as the source.
  */
 static inline void string_to_hex(const char *str, size_t n, char *hex) {
-        static const char table[16] = "0123456789abcdef";
+        // Include terminating NUL to silence warnings about truncated strings.
+        static const char table[17] = "0123456789abcdef";
         size_t i;
 
         for (i = 0; i < n; ++i) {
diff -Nru dbus-broker-36/src/util/systemd.c dbus-broker-37/src/util/systemd.c
--- dbus-broker-36/src/util/systemd.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/systemd.c	2025-06-16 13:26:27.000000000 +0100
@@ -15,7 +15,8 @@
 }
 
 static char *escape_char(char *t, char c) {
-        static const char table[16] = "0123456789abcdef";
+        // Include terminating NUL to silence warnings about truncated strings.
+        static const char table[17] = "0123456789abcdef";
 
         *t++ = '\\';
         *t++ = 'x';
diff -Nru dbus-broker-36/src/util/test-misc.c dbus-broker-37/src/util/test-misc.c
--- dbus-broker-36/src/util/test-misc.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/src/util/test-misc.c	2025-06-16 13:26:27.000000000 +0100
@@ -87,10 +87,10 @@
                         .out_fmode = 0666,
                 },
         };
-        unsigned int seal_mask;
+        unsigned int seals, seal_mask;
         struct stat st;
         size_t i;
-        int r, fd, seals;
+        int r, fd;
 
         seal_mask = memfd_seals();
 
@@ -104,8 +104,8 @@
                 }
                 c_assert(!v[i].out_error);
 
-                seals = misc_memfd_get_seals(fd);
-                c_assert(seals >= 0);
+                r = misc_memfd_get_seals(fd, &seals);
+                c_assert(r >= 0);
                 c_assert((seals & seal_mask) == (v[i].out_seals & seal_mask));
 
                 r = fstat(fd, &st);
@@ -144,8 +144,53 @@
         }
 }
 
+static void test_casts(void) {
+        static const struct {
+                size_t from;
+                unsigned int to;
+        } casts_z2u_sat[] = {
+                { 0, 0 },
+                { 32, 32 },
+                { 256, 256 },
+                { SIZE_MAX, UINT_MAX },
+#if SIZE_MAX > UINT_MAX
+                { (size_t)UINT_MAX, UINT_MAX },
+                { (size_t)UINT_MAX + 1, UINT_MAX },
+                { (size_t)UINT_MAX * 2, UINT_MAX },
+#endif
+        };
+        static const struct {
+                uint64_t from;
+                unsigned int to;
+        } casts_t2u_sat[] = {
+                { 0, 0 },
+                { 32, 32 },
+                { 256, 256 },
+                { (uint64_t)UINT_MAX, UINT_MAX },
+                { (uint64_t)UINT_MAX + 1, UINT_MAX },
+                { (uint64_t)UINT_MAX * 2, UINT_MAX },
+                { UINT64_MAX, UINT_MAX },
+        };
+        size_t i;
+
+        for (i = 0; i < C_ARRAY_SIZE(casts_z2u_sat); ++i) {
+                unsigned int r;
+
+                r = util_z2u_saturating(casts_z2u_sat[i].from);
+                c_assert(r == casts_z2u_sat[i].to);
+        }
+
+        for (i = 0; i < C_ARRAY_SIZE(casts_t2u_sat); ++i) {
+                unsigned int r;
+
+                r = util_t2u_saturating(casts_t2u_sat[i].from);
+                c_assert(r == casts_t2u_sat[i].to);
+        }
+}
+
 int main(int argc, char **argv) {
         test_memfd();
         test_umul_saturating();
+        test_casts();
         return 0;
 }
diff -Nru dbus-broker-36/src/util/test-nsec.c dbus-broker-37/src/util/test-nsec.c
--- dbus-broker-36/src/util/test-nsec.c	1970-01-01 01:00:00.000000000 +0100
+++ dbus-broker-37/src/util/test-nsec.c	2025-06-16 13:26:27.000000000 +0100
@@ -0,0 +1,28 @@
+/*
+ * Test nanosecond time management
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include "util/nsec.h"
+
+static void test_nsec(void) {
+        nsec_t n0, n1, n2;
+
+        n0 = nsec_now(CLOCK_MONOTONIC);
+        c_assert(n0 > 0);
+
+        n1 = n0 + 8000;
+        nsec_sleep(CLOCK_MONOTONIC, n1);
+
+        n2 = nsec_now(CLOCK_MONOTONIC);
+        c_assert(n2 > 0);
+        c_assert(n2 > n0);
+        c_assert(n2 - n0 >= 8000);
+}
+
+int main(int argc, char **argv) {
+        test_nsec();
+        return 0;
+}
diff -Nru dbus-broker-36/subprojects/libcdvar-1/meson.build dbus-broker-37/subprojects/libcdvar-1/meson.build
--- dbus-broker-36/subprojects/libcdvar-1/meson.build	2023-12-18 12:17:27.000000000 +0000
+++ dbus-broker-37/subprojects/libcdvar-1/meson.build	2025-06-16 10:15:36.000000000 +0100
@@ -6,7 +6,7 @@
         ],
         license: 'Apache',
         meson_version: '>=0.60.0',
-        version: '1.1.0',
+        version: '1.2.0',
 )
 major = meson.project_version().split('.')[0]
 project_description = 'D-Bus Variant Type-System'
diff -Nru dbus-broker-36/subprojects/libcdvar-1/NEWS.md dbus-broker-37/subprojects/libcdvar-1/NEWS.md
--- dbus-broker-36/subprojects/libcdvar-1/NEWS.md	2023-12-18 12:17:27.000000000 +0000
+++ dbus-broker-37/subprojects/libcdvar-1/NEWS.md	2025-06-16 10:15:36.000000000 +0100
@@ -1,5 +1,14 @@
 # c-dvar - D-Bus Variant Type-System
 
+## CHANGES WITH 1.2.0:
+
+        * Fix the variant-reader to use aliasing-safe accessors. This allows
+          parallel use of the variant-buffers with possibly aliasing pointers.
+
+        Contributions from: David Rheinsberg, Kostadin Shishmanov
+
+        - Dußlingen, 2025-06-16
+
 ## CHANGES WITH 1.1.0:
 
         * Update the c-stdaux dependency to provide the new build variables
diff -Nru dbus-broker-36/subprojects/libclist-3/.github/workflows/ci.yml dbus-broker-37/subprojects/libclist-3/.github/workflows/ci.yml
--- dbus-broker-36/subprojects/libclist-3/.github/workflows/ci.yml	2022-06-22 11:05:20.000000000 +0100
+++ dbus-broker-37/subprojects/libclist-3/.github/workflows/ci.yml	2022-07-07 13:07:43.000000000 +0100
@@ -7,38 +7,26 @@
   - cron:  '0 0 * * *'
 
 jobs:
-  ci:
-    name: CI with Default Configuration
+  ci-linux:
+    name: Linux CI
     uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
     with:
       cabuild_ref: "v1"
+      linux: true
       m32: true
       matrixmode: true
       valgrind: true
-
-  ci-msvc:
-    name: CI with MSVC
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        os: [windows-2019, windows-latest]
-
-    steps:
-    - name: Fetch Sources
-      uses: actions/checkout@v2
-    - name: Setup Python
-      uses: actions/setup-python@v2
-      with:
-        python-version: '3.x'
-    - name: Install Python Dependencies
-      run: pip install meson ninja
-    - name: Prepare MSVC
-      uses: bus1/cabuild/action/msdevshell@v1
-      with:
-        architecture: x64
-    - name: Prepare Build
-      run: meson setup build
-    - name: Run Build
-      run: meson compile -v -C build
-    - name: Run Test Suite
-      run: meson test -v -C build
+  ci-macos:
+    name: MacOS CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      macos: true
+  ci-windows:
+    name: Windows CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      windows: true
diff -Nru dbus-broker-36/subprojects/libcstdaux-1/meson.build dbus-broker-37/subprojects/libcstdaux-1/meson.build
--- dbus-broker-36/subprojects/libcstdaux-1/meson.build	2024-01-10 12:46:38.000000000 +0000
+++ dbus-broker-37/subprojects/libcstdaux-1/meson.build	2025-06-16 10:09:11.000000000 +0100
@@ -10,7 +10,7 @@
         ],
         license: 'Apache',
         meson_version: '>=0.60.0',
-        version: '1.5.0',
+        version: '1.6.0',
 )
 major = meson.project_version().split('.')[0]
 project_description = 'Auxiliary macros and functions for the C standard library'
diff -Nru dbus-broker-36/subprojects/libcstdaux-1/NEWS.md dbus-broker-37/subprojects/libcstdaux-1/NEWS.md
--- dbus-broker-36/subprojects/libcstdaux-1/NEWS.md	2024-01-10 12:46:38.000000000 +0000
+++ dbus-broker-37/subprojects/libcstdaux-1/NEWS.md	2025-06-16 10:09:11.000000000 +0100
@@ -1,5 +1,14 @@
 # c-stdaux - Auxiliary macros and functions for the C standard library
 
+## CHANGES WITH 1.6.0:
+
+        * Multiple fixes to the test-suite, which fix differences in the
+          optimizations performed by GCC and clang.
+
+        Contributions from: David Rheinsberg, ms178, Nikita Popov
+
+        - Dußlingen, 2025-06-16
+
 ## CHANGES WITH 1.5.0:
 
         * Change the meson variable exports to avoid dashes in variable names,
diff -Nru dbus-broker-36/subprojects/libcstdaux-1/src/test-basic.c dbus-broker-37/subprojects/libcstdaux-1/src/test-basic.c
--- dbus-broker-36/subprojects/libcstdaux-1/src/test-basic.c	2024-01-10 12:46:38.000000000 +0000
+++ dbus-broker-37/subprojects/libcstdaux-1/src/test-basic.c	2025-06-16 10:09:11.000000000 +0100
@@ -503,7 +503,7 @@
                 c_assert(foo == 11);
 
                 c_assert(__builtin_constant_p(c_div_round_up(1, 5)));
-                c_assert(!__builtin_constant_p(c_div_round_up(1, non_constant_expr)));
+                c_assert(!__builtin_constant_p(c_div_round_up(8, 1 + !non_constant_expr)));
 
                 /* alternative calculation is [(x + y - 1) / y], but it may overflow */
                 for (i = 0; i <= 0xffff; ++i) {
diff -Nru dbus-broker-36/test/dbus/test-driver.c dbus-broker-37/test/dbus/test-driver.c
--- dbus-broker-36/test/dbus/test-driver.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/test/dbus/test-driver.c	2025-06-16 13:26:27.000000000 +0100
@@ -2581,6 +2581,23 @@
 
                                 r = sd_bus_message_exit_container(reply);
                                 c_assert(r >= 0);
+                        } else if (strcmp(stat, "Serial") == 0 ||
+                                   strcmp(stat, "ActiveConnections") == 0 ||
+                                   strcmp(stat, "IncompleteConnections") == 0 ||
+                                   strcmp(stat, "BusNames") == 0 ||
+                                   strcmp(stat, "PeakBusNames") == 0 ||
+                                   strcmp(stat, "PeakBusNamesPerConnection") == 0 ||
+                                   strcmp(stat, "MatchRules") == 0 ||
+                                   strcmp(stat, "PeakMatchRules") == 0 ||
+                                   strcmp(stat, "PeakMatchRulesPerConnection") == 0) {
+                                r = sd_bus_message_enter_container(reply, 'v', "u");
+                                c_assert(r >= 0);
+
+                                r = sd_bus_message_skip(reply, "u");
+                                c_assert(r >= 0);
+
+                                r = sd_bus_message_exit_container(reply);
+                                c_assert(r >= 0);
                         } else {
                                 r = sd_bus_message_skip(reply, "v");
                                 c_assert(r >= 0);
diff -Nru dbus-broker-36/test/dbus/test-matches.c dbus-broker-37/test/dbus/test-matches.c
--- dbus-broker-36/test/dbus/test-matches.c	2024-04-12 15:07:35.000000000 +0100
+++ dbus-broker-37/test/dbus/test-matches.c	2025-06-16 13:26:27.000000000 +0100
@@ -258,6 +258,154 @@
         util_broker_terminate(broker);
 }
 
+static void test_arg(void) {
+        _c_cleanup_(util_broker_freep) Broker *broker = NULL;
+        _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *sender = NULL;
+        _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *receiver = NULL;
+        int r;
+
+        util_broker_new(&broker);
+        util_broker_spawn(broker);
+
+        util_broker_connect(broker, &sender);
+        util_broker_connect(broker, &receiver);
+
+        r = sd_bus_call_method(receiver, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
+                               "AddMatch", NULL, NULL,
+                               "s", "arg3='done'");
+        c_assert(r >= 0);
+
+        /* does not match: not a string */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "iiii", 0, 0, 0, 0);
+        c_assert(r >= 0);
+
+        /* does not match: wrong value */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "iiis", 0, 0, 0, "failed");
+        c_assert(r >= 0);
+
+        /* does not match: correct value in the wrong arguments */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "sssss",
+                               "done", "done", "done", "failed", "done");
+        c_assert(r >= 0);
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "iiis", 0, 0, 0, "done");
+        c_assert(r >= 0);
+
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        util_broker_terminate(broker);
+}
+
+static void test_args(void) {
+        _c_cleanup_(util_broker_freep) Broker *broker = NULL;
+        _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *sender = NULL;
+        _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *receiver = NULL;
+        int r;
+
+        util_broker_new(&broker);
+        util_broker_spawn(broker);
+
+        util_broker_connect(broker, &sender);
+        util_broker_connect(broker, &receiver);
+
+        r = sd_bus_call_method(receiver, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
+                               "AddMatch", NULL, NULL,
+                               "s", "arg0='zero',arg1='one'");
+        c_assert(r >= 0);
+
+        /* does not match: too few arguments */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "s", "zero");
+        c_assert(r >= 0);
+
+        /* does not match: one arg not a string */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "si", "zero", 0);
+        c_assert(r >= 0);
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "is", 0, "one");
+        c_assert(r >= 0);
+
+        /* does not match: incorrect value for one argument */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "zero", "wrong");
+        c_assert(r >= 0);
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "wrong", "one");
+        c_assert(r >= 0);
+
+        /* does not match: correct values in the wrong arguments */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "one", "zero");
+        c_assert(r >= 0);
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "ss", "zero", "one");
+        c_assert(r >= 0);
+
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        util_broker_terminate(broker);
+}
+
+static void test_argpath(void) {
+        _c_cleanup_(util_broker_freep) Broker *broker = NULL;
+        _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *sender = NULL;
+        _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *receiver = NULL;
+        int r;
+
+        util_broker_new(&broker);
+        util_broker_spawn(broker);
+
+        util_broker_connect(broker, &sender);
+        util_broker_connect(broker, &receiver);
+
+        /* example match rule, matches and nonmatches from D-Bus specification */
+        r = sd_bus_call_method(receiver, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
+                               "AddMatch", NULL, NULL,
+                               "s", "arg1path='/aa/bb/'");
+        c_assert(r >= 0);
+
+        /* does not match: arg not a string or object path */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "si", "foo", 0);
+        c_assert(r >= 0);
+
+        /* does not match: incorrect value */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "foo", "/aa/b");
+        c_assert(r >= 0);
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "foo", "/aa");
+        c_assert(r >= 0);
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "foo", "/aa/bb");
+        c_assert(r >= 0);
+
+        /* does not match: correct value in the wrong argument */
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "DoesNotMatch", "ss", "/aa/bb/", "foo");
+        c_assert(r >= 0);
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "ss", "foo", "/");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "ss", "foo", "/aa/");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "ss", "foo", "/aa/bb/");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "ss", "foo", "/aa/bb/cc/");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "ss", "foo", "/aa/bb/cc");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "so", "foo", "/");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        r = sd_bus_emit_signal(sender, "/org/example", "org.example", "Matches", "so", "foo", "/aa/bb/cc");
+        c_assert(r >= 0);
+        util_broker_consume_signal(receiver, "org.example", "Matches");
+
+        util_broker_terminate(broker);
+}
+
 int main(int argc, char **argv) {
         test_wildcard();
         test_unique_name();
@@ -267,4 +415,7 @@
         test_noc_unique();
         test_noc_well_known();
         test_noc_driver();
+        test_arg();
+        test_args();
+        test_argpath();
 }

Attachment: signature.asc
Description: This is a digitally signed message part


Reply to: