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

Bug#1107920: marked as done (unblock: dbus-broker/37-1)



Your message dated Thu, 26 Jun 2025 14:41:45 +0000
with message-id <E1uUnnN-00FDGd-1X@respighi.debian.org>
and subject line unblock dbus-broker
has caused the Debian Bug report #1107920,
regarding unblock: dbus-broker/37-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1107920: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1107920
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
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


--- End Message ---
--- Begin Message ---
Unblocked.

--- End Message ---

Reply to: