diff --git a/1097-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch b/1097-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch new file mode 100644 index 0000000..88a7ee0 --- /dev/null +++ b/1097-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch @@ -0,0 +1,25 @@ +From af3b1477daca2bd53c371275ef7c3022dca3011f Mon Sep 17 00:00:00 2001 +From: Jan Macku +Date: Tue, 14 Jan 2025 14:20:53 +0100 +Subject: [PATCH] ci: use ubuntu 22:04 for deploy of man pages + +rhel-only: ci + +Related: RHEL-70884 +--- + .github/workflows/deploy-man-pages.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.github/workflows/deploy-man-pages.yml b/.github/workflows/deploy-man-pages.yml +index 9739228a87..82fab81072 100644 +--- a/.github/workflows/deploy-man-pages.yml ++++ b/.github/workflows/deploy-man-pages.yml +@@ -26,7 +26,7 @@ jobs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} +- runs-on: ubuntu-latest ++ runs-on: ubuntu-22.04 + + permissions: + pages: write diff --git a/1098-man-tmpfiles-fix-off-by-one-in-example.patch b/1098-man-tmpfiles-fix-off-by-one-in-example.patch new file mode 100644 index 0000000..1cbaa2e --- /dev/null +++ b/1098-man-tmpfiles-fix-off-by-one-in-example.patch @@ -0,0 +1,27 @@ +From 77109fd5481a622121a9ca20e19d76fc97125e47 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 17 May 2023 11:16:56 +0200 +Subject: [PATCH] man/tmpfiles: fix off-by-one in example + +Reported and diagnosed by gitterman. Fixes #26617. + +(cherry picked from commit f90360eb7417e083650034ad819790df0c389bd1) + +Resolves: RHEL-74015 +--- + man/tmpfiles.d.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml +index fe2a1dadab..379ac9c34f 100644 +--- a/man/tmpfiles.d.xml ++++ b/man/tmpfiles.d.xml +@@ -644,7 +644,7 @@ w- /proc/sys/vm/swappiness - - - - 10 + For example: + # Files created and modified, and directories accessed more than + # an hour ago in "/tmp/foo/bar", are subject to time-based cleanup. +-d /tmp/foo/bar - - - - bmA:1h - ++d /tmp/foo/bar - - - bmA:1h - + + Note that while the aging algorithm is run a 'shared' BSD file lock (see flock2) is diff --git a/1099-test-drop-removed-SCSI-passthrough-feature.patch b/1099-test-drop-removed-SCSI-passthrough-feature.patch new file mode 100644 index 0000000..8f01464 --- /dev/null +++ b/1099-test-drop-removed-SCSI-passthrough-feature.patch @@ -0,0 +1,36 @@ +From 6f792eb8679a20dd1e604bcd9cd18566658c3ff3 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Wed, 18 Sep 2024 12:54:51 +0200 +Subject: [PATCH] test: drop removed SCSI passthrough feature + +This feature has been deprecated since QEMU 5.0 and finally removed in +QEMU 9.1 [0] which now causes issues when running the storage tests on +latest Arch: + +------ testcase_long_sysfs_path: BEGIN ------ +... +qemu-system-x86_64: -device virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge25: Property 'virtio-blk-pci.scsi' not found +E: qemu failed with exit code 1 + +[0] https://github.com/qemu/qemu/commit/a271b8d7b2f39275a05e49deb7c8edc20b7a8279 + +(cherry picked from commit cd57920fbf6a8f7769a82cfc9bebc12965de0199) + +Related: RHEL-70884 +--- + test/TEST-64-UDEV-STORAGE/test.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh +index f93b92e3c3..3911926b02 100755 +--- a/test/TEST-64-UDEV-STORAGE/test.sh ++++ b/test/TEST-64-UDEV-STORAGE/test.sh +@@ -474,7 +474,7 @@ EOF + qemu_opts+=("-device pci-bridge,id=pci_bridge$brid,bus=pci_bridge$((brid-1)),chassis_nr=$((64+brid))") + done + +- qemu_opts+=("-device virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge$brid") ++ qemu_opts+=("-device virtio-blk-pci,drive=drive0,bus=pci_bridge$brid") + + KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}" + QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" diff --git a/1100-nspawn-call-json_dispatch-with-a-correct-pointer.patch b/1100-nspawn-call-json_dispatch-with-a-correct-pointer.patch new file mode 100644 index 0000000..8e1fe86 --- /dev/null +++ b/1100-nspawn-call-json_dispatch-with-a-correct-pointer.patch @@ -0,0 +1,53 @@ +From ef5d328d13eae49e415c8968d0625fa29ff2daf4 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 15 May 2023 19:45:13 +0200 +Subject: [PATCH] nspawn: call json_dispatch() with a correct pointer + +Otherwise hilarity ensues: + + AddressSanitizer:DEADLYSIGNAL + ================================================================= + ==722==ERROR: AddressSanitizer: SEGV on unknown address 0xffffffff00000000 (pc 0x7f8d50ca9ffb bp 0x7fff11b0d4a0 sp 0x7fff11b0cc30 T0) + ==722==The signal is caused by a READ memory access. + #0 0x7f8d50ca9ffb in __interceptor_strcmp.part.0 (/lib64/libasan.so.8+0xa9ffb) + #1 0x7f8d4f9cf5a1 in strcmp_ptr ../src/fundamental/string-util-fundamental.h:33 + #2 0x7f8d4f9cf5f8 in streq_ptr ../src/fundamental/string-util-fundamental.h:46 + #3 0x7f8d4f9d74d2 in free_and_strdup ../src/basic/string-util.c:948 + #4 0x49139a in free_and_strdup_warn ../src/basic/string-util.h:197 + #5 0x4923eb in oci_absolute_path ../src/nspawn/nspawn-oci.c:139 + #6 0x7f8d4f6bd359 in json_dispatch ../src/shared/json.c:4395 + #7 0x4a8831 in oci_hooks_array ../src/nspawn/nspawn-oci.c:2089 + #8 0x7f8d4f6bd359 in json_dispatch ../src/shared/json.c:4395 + #9 0x4a8b56 in oci_hooks ../src/nspawn/nspawn-oci.c:2112 + #10 0x7f8d4f6bd359 in json_dispatch ../src/shared/json.c:4395 + #11 0x4aa298 in oci_load ../src/nspawn/nspawn-oci.c:2197 + #12 0x446cec in load_oci_bundle ../src/nspawn/nspawn.c:4744 + #13 0x44ffa7 in run ../src/nspawn/nspawn.c:5477 + #14 0x4552fb in main ../src/nspawn/nspawn.c:5920 + #15 0x7f8d4e04a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f) + #16 0x7f8d4e04a5c8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x275c8) + #17 0x40d284 in _start (/usr/bin/systemd-nspawn+0x40d284) + AddressSanitizer can not provide additional info. + SUMMARY: AddressSanitizer: SEGV (/lib64/libasan.so.8+0xa9ffb) in __interceptor_strcmp.part.0 + ==722==ABORTING + +(cherry picked from commit f4e5c042c9a5659a5eebb4c91c0f1132f02a2c59) + +Related: RHEL-55266 +--- + src/nspawn/nspawn-oci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c +index 02142a9352..117a31e6b2 100644 +--- a/src/nspawn/nspawn-oci.c ++++ b/src/nspawn/nspawn-oci.c +@@ -2083,7 +2083,7 @@ static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags f + .timeout = USEC_INFINITY, + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, userdata); ++ r = json_dispatch(e, table, oci_unexpected, flags, new_item); + if (r < 0) { + free(new_item->path); + strv_free(new_item->args); diff --git a/1101-varlink-json-introduce-new-varlink_dispatch-helper.patch b/1101-varlink-json-introduce-new-varlink_dispatch-helper.patch new file mode 100644 index 0000000..d3f9344 --- /dev/null +++ b/1101-varlink-json-introduce-new-varlink_dispatch-helper.patch @@ -0,0 +1,944 @@ +From a92665de198ca2ace3f5835f6c819b6e669cbb77 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 1 Nov 2023 18:36:12 +0100 +Subject: [PATCH] varlink,json: introduce new varlink_dispatch() helper + +varlink_dispatch() is a simple wrapper around json_dispatch() that +returns clean, standards-compliant InvalidParameter error back to +clients, if the specified JSON cannot be parsed properly. + +For this json_dispatch() is extended to return the offending field's +name. Because it already has quite a few parameters, I then renamed +json_dispatch() to json_dispatch_full() and made json_dispatch() a +wrapper around it that passes the new argument as NULL. While doing so I +figured we should also get rid of the bad= argument in the short +wrapper, since it's only used in the OCI code. + +To simplify the OCI code this adds a second wrapper oci_dispatch() +around json_dispatch_full(), that fills in bad= the way we want. + +Net result: instead of one json_dispatch() call there are now: + +1. json_dispatch_full() for the fully feature mother of all dispathers. +2. json_dispatch() for the simpler version that you want to use most of + the time. +3. varlink_dispatch() that generates nice Varlink errors +4. oci_dispatch() that does the OCI specific error handling + +And that's all there is. + +(cherry picked from commit f1b622a00ce614654fcdff309a2394cfae3b3a88) + +Related: RHEL-55266 +--- + src/core/core-varlink.c | 12 ++++---- + src/fuzz/fuzz-bootspec.c | 2 +- + src/home/homed-varlink.c | 12 ++++---- + src/machine/machined-varlink.c | 12 ++++---- + src/nspawn/nspawn-oci.c | 54 ++++++++++++++++++---------------- + src/nss-resolve/nss-resolve.c | 18 ++++++------ + src/oom/oomd-manager.c | 2 +- + src/resolve/resolvectl.c | 4 +-- + src/resolve/resolved-varlink.c | 8 ++--- + src/shared/group-record.c | 10 +++---- + src/shared/json.c | 41 ++++++++++++++++++++++++-- + src/shared/json.h | 3 +- + src/shared/user-record.c | 18 ++++++------ + src/shared/userdb.c | 6 ++-- + src/shared/varlink.c | 20 +++++++++++++ + src/shared/varlink.h | 3 ++ + src/userdb/userwork.c | 12 ++++---- + 17 files changed, 150 insertions(+), 87 deletions(-) + +diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c +index 843271593d..776a3eebab 100644 +--- a/src/core/core-varlink.c ++++ b/src/core/core-varlink.c +@@ -281,8 +281,8 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.DynamicUser")) +@@ -388,8 +388,8 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.DynamicUser")) +@@ -464,8 +464,8 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.DynamicUser")) +diff --git a/src/fuzz/fuzz-bootspec.c b/src/fuzz/fuzz-bootspec.c +index c08f76c14a..c6d24e7b55 100644 +--- a/src/fuzz/fuzz-bootspec.c ++++ b/src/fuzz/fuzz-bootspec.c +@@ -98,7 +98,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (r < 0) + return 0; + +- r = json_dispatch(v, data_dispatch, NULL, 0, &config); ++ r = json_dispatch(v, data_dispatch, 0, &config); + if (r < 0) + return 0; + +diff --git a/src/home/homed-varlink.c b/src/home/homed-varlink.c +index 540a612554..1cef25f563 100644 +--- a/src/home/homed-varlink.c ++++ b/src/home/homed-varlink.c +@@ -90,8 +90,8 @@ int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMet + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, m->userdb_service)) +@@ -204,8 +204,8 @@ int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMe + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, m->userdb_service)) +@@ -270,8 +270,8 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, m->userdb_service)) +diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c +index ec625ad7b4..8b230b0078 100644 +--- a/src/machine/machined-varlink.c ++++ b/src/machine/machined-varlink.c +@@ -156,8 +156,8 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.Machine")) +@@ -321,8 +321,8 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.Machine")) +@@ -366,8 +366,8 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.Machine")) +diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c +index 117a31e6b2..7fbc4dd6f0 100644 +--- a/src/nspawn/nspawn-oci.c ++++ b/src/nspawn/nspawn-oci.c +@@ -84,6 +84,10 @@ static int oci_unexpected(const char *name, JsonVariant *v, JsonDispatchFlags fl + "Unexpected OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v))); + } + ++static int oci_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchFlags flags, void *userdata) { ++ return json_dispatch_full(v, table, oci_unexpected, flags, userdata, /* reterr_bad_field= */ NULL); ++} ++ + static int oci_unsupported(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Unsupported OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v))); +@@ -122,7 +126,7 @@ static int oci_console_size(const char *name, JsonVariant *v, JsonDispatchFlags + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static int oci_absolute_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { +@@ -251,7 +255,7 @@ static int oci_rlimits(const char *name, JsonVariant *v, JsonDispatchFlags flags + {} + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) + return r; + +@@ -319,7 +323,7 @@ static int oci_capabilities(const char *name, JsonVariant *v, JsonDispatchFlags + Settings *s = ASSERT_PTR(userdata); + int r; + +- r = json_dispatch(v, table, oci_unexpected, flags, &s->full_capabilities); ++ r = oci_dispatch(v, table, flags, &s->full_capabilities); + if (r < 0) + return r; + +@@ -402,7 +406,7 @@ static int oci_user(const char *name, JsonVariant *v, JsonDispatchFlags flags, v + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static int oci_process(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { +@@ -423,7 +427,7 @@ static int oci_process(const char *name, JsonVariant *v, JsonDispatchFlags flags + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static int oci_root(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { +@@ -436,7 +440,7 @@ static int oci_root(const char *name, JsonVariant *v, JsonDispatchFlags flags, v + {} + }; + +- r = json_dispatch(v, table, oci_unexpected, flags, s); ++ r = oci_dispatch(v, table, flags, s); + if (r < 0) + return r; + +@@ -536,7 +540,7 @@ static int oci_mounts(const char *name, JsonVariant *v, JsonDispatchFlags flags, + CustomMount *m; + _cleanup_(cleanup_oci_mount_data) oci_mount_data data = {}; + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) + return r; + +@@ -631,7 +635,7 @@ static int oci_namespaces(const char *name, JsonVariant *v, JsonDispatchFlags fl + {} + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) { + free(data.path); + return r; +@@ -729,7 +733,7 @@ static int oci_uid_gid_mappings(const char *name, JsonVariant *v, JsonDispatchFl + + assert_se(e = json_variant_by_index(v, 0)); + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) + return r; + +@@ -858,7 +862,7 @@ static int oci_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags + .mode = 0644, + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, node); ++ r = oci_dispatch(e, table, flags, node); + if (r < 0) + goto fail_element; + +@@ -1008,7 +1012,7 @@ static int oci_cgroup_devices(const char *name, JsonVariant *v, JsonDispatchFlag + {} + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) + return r; + +@@ -1190,7 +1194,7 @@ static int oci_cgroup_memory(const char *name, JsonVariant *v, JsonDispatchFlags + Settings *s = userdata; + int r; + +- r = json_dispatch(v, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(v, table, flags, &data); + if (r < 0) + return r; + +@@ -1306,7 +1310,7 @@ static int oci_cgroup_cpu(const char *name, JsonVariant *v, JsonDispatchFlags fl + Settings *s = userdata; + int r; + +- r = json_dispatch(v, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(v, table, flags, &data); + if (r < 0) { + cpu_set_reset(&data.cpu_set); + return r; +@@ -1388,7 +1392,7 @@ static int oci_cgroup_block_io_weight_device(const char *name, JsonVariant *v, J + + _cleanup_free_ char *path = NULL; + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) + return r; + +@@ -1445,7 +1449,7 @@ static int oci_cgroup_block_io_throttle(const char *name, JsonVariant *v, JsonDi + + _cleanup_free_ char *path = NULL; + +- r = json_dispatch(e, table, oci_unexpected, flags, &data); ++ r = oci_dispatch(e, table, flags, &data); + if (r < 0) + return r; + +@@ -1482,7 +1486,7 @@ static int oci_cgroup_block_io(const char *name, JsonVariant *v, JsonDispatchFla + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static int oci_cgroup_pids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { +@@ -1497,7 +1501,7 @@ static int oci_cgroup_pids(const char *name, JsonVariant *v, JsonDispatchFlags f + uint64_t m; + int r; + +- r = json_dispatch(v, table, oci_unexpected, flags, &k); ++ r = oci_dispatch(v, table, flags, &k); + if (r < 0) + return r; + +@@ -1540,7 +1544,7 @@ static int oci_resources(const char *name, JsonVariant *v, JsonDispatchFlags fla + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static bool sysctl_key_valid(const char *s) { +@@ -1801,7 +1805,7 @@ static int oci_seccomp_args(const char *name, JsonVariant *v, JsonDispatchFlags + .op = 0, + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, p); ++ r = oci_dispatch(e, table, flags, p); + if (r < 0) + return r; + +@@ -1834,7 +1838,7 @@ static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFl + .action = UINT32_MAX, + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, &rule); ++ r = oci_dispatch(e, table, flags, &rule); + if (r < 0) + goto fail_rule; + +@@ -1901,7 +1905,7 @@ static int oci_seccomp(const char *name, JsonVariant *v, JsonDispatchFlags flags + if (!sc) + return json_log(v, flags, SYNTHETIC_ERRNO(ENOMEM), "Couldn't allocate seccomp object."); + +- r = json_dispatch(v, table, oci_unexpected, flags, sc); ++ r = oci_dispatch(v, table, flags, sc); + if (r < 0) + return r; + +@@ -2026,7 +2030,7 @@ static int oci_linux(const char *name, JsonVariant *v, JsonDispatchFlags flags, + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static int oci_hook_timeout(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { +@@ -2083,7 +2087,7 @@ static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags f + .timeout = USEC_INFINITY, + }; + +- r = json_dispatch(e, table, oci_unexpected, flags, new_item); ++ r = oci_dispatch(e, table, flags, new_item); + if (r < 0) { + free(new_item->path); + strv_free(new_item->args); +@@ -2106,7 +2110,7 @@ static int oci_hooks(const char *name, JsonVariant *v, JsonDispatchFlags flags, + {} + }; + +- return json_dispatch(v, table, oci_unexpected, flags, userdata); ++ return oci_dispatch(v, table, flags, userdata); + } + + static int oci_annotations(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { +@@ -2191,7 +2195,7 @@ int oci_load(FILE *f, const char *bundle, Settings **ret) { + if (!s->bundle) + return log_oom(); + +- r = json_dispatch(oci, table, oci_unexpected, 0, s); ++ r = oci_dispatch(oci, table, 0, s); + if (r < 0) + return r; + +diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c +index 0226752275..350bbffb04 100644 +--- a/src/nss-resolve/nss-resolve.c ++++ b/src/nss-resolve/nss-resolve.c +@@ -272,7 +272,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( + goto not_found; + } + +- r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p); ++ r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, json_dispatch_flags, &p); + if (r < 0) + goto fail; + if (json_variant_is_blank_object(p.addresses)) +@@ -282,7 +282,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; + +- r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q); ++ r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + if (r < 0) + goto fail; + +@@ -320,7 +320,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; + +- r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q); ++ r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + if (r < 0) + goto fail; + +@@ -428,7 +428,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( + goto not_found; + } + +- r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p); ++ r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, json_dispatch_flags, &p); + if (r < 0) + goto fail; + if (json_variant_is_blank_object(p.addresses)) +@@ -438,7 +438,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; + +- r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q); ++ r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + if (r < 0) + goto fail; + +@@ -484,7 +484,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; + +- r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q); ++ r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + if (r < 0) + goto fail; + +@@ -640,7 +640,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + goto not_found; + } + +- r = json_dispatch(rparams, resolve_address_reply_dispatch_table, NULL, json_dispatch_flags, &p); ++ r = json_dispatch(rparams, resolve_address_reply_dispatch_table, json_dispatch_flags, &p); + if (r < 0) + goto fail; + if (json_variant_is_blank_object(p.names)) +@@ -651,7 +651,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { + _cleanup_(name_parameters_destroy) NameParameters q = {}; + +- r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q); ++ r = json_dispatch(entry, name_parameters_dispatch_table, json_dispatch_flags, &q); + if (r < 0) + goto fail; + +@@ -692,7 +692,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { + _cleanup_(name_parameters_destroy) NameParameters q = {}; + +- r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q); ++ r = json_dispatch(entry, name_parameters_dispatch_table, json_dispatch_flags, &q); + if (r < 0) + goto fail; + +diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c +index 4a43807b87..66c41c1cfa 100644 +--- a/src/oom/oomd-manager.c ++++ b/src/oom/oomd-manager.c +@@ -72,7 +72,7 @@ static int process_managed_oom_message(Manager *m, uid_t uid, JsonVariant *param + if (!json_variant_is_object(c)) + continue; + +- r = json_dispatch(c, dispatch_table, NULL, 0, &message); ++ r = json_dispatch(c, dispatch_table, 0, &message); + if (r == -ENOMEM) + return r; + if (r < 0) +diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c +index e22e06d054..0782e48e04 100644 +--- a/src/resolve/resolvectl.c ++++ b/src/resolve/resolvectl.c +@@ -2598,7 +2598,7 @@ static int monitor_rkey_from_json(JsonVariant *v, DnsResourceKey **ret_key) { + assert(v); + assert(ret_key); + +- r = json_dispatch(v, dispatch_table, NULL, 0, NULL); ++ r = json_dispatch(v, dispatch_table, 0, NULL); + if (r < 0) + return r; + +@@ -2697,7 +2697,7 @@ static void monitor_query_dump(JsonVariant *v) { + {} + }; + +- r = json_dispatch(v, dispatch_table, NULL, 0, NULL); ++ r = json_dispatch(v, dispatch_table, 0, NULL); + if (r < 0) + return (void) log_warning("Received malformed monitor message, ignoring."); + +diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c +index 8ba5eb9833..1c5ce8315c 100644 +--- a/src/resolve/resolved-varlink.c ++++ b/src/resolve/resolved-varlink.c +@@ -312,8 +312,8 @@ static int vl_method_resolve_hostname(Varlink *link, JsonVariant *parameters, Va + if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) + return -EINVAL; + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (p.ifindex < 0) +@@ -492,8 +492,8 @@ static int vl_method_resolve_address(Varlink *link, JsonVariant *parameters, Var + if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) + return -EINVAL; + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + if (p.ifindex < 0) +diff --git a/src/shared/group-record.c b/src/shared/group-record.c +index 2f12ac1c22..728471b2b6 100644 +--- a/src/shared/group-record.c ++++ b/src/shared/group-record.c +@@ -50,7 +50,7 @@ static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispa + {}, + }; + +- return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(variant, privileged_dispatch_table, flags, userdata); + } + + static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { +@@ -78,7 +78,7 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch + if (!m) + return 0; + +- return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(m, binding_dispatch_table, flags, userdata); + } + + static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { +@@ -131,7 +131,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp + if (!matching) + continue; + +- r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata); ++ r = json_dispatch(e, per_machine_dispatch_table, flags, userdata); + if (r < 0) + return r; + } +@@ -164,7 +164,7 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF + if (!m) + return 0; + +- return json_dispatch(m, status_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(m, status_dispatch_table, flags, userdata); + } + + static int group_record_augment(GroupRecord *h, JsonDispatchFlags json_flags) { +@@ -230,7 +230,7 @@ int group_record_load( + if (r < 0) + return r; + +- r = json_dispatch(h->json, group_dispatch_table, NULL, json_flags, h); ++ r = json_dispatch(h->json, group_dispatch_table, json_flags, h); + if (r < 0) + return r; + +diff --git a/src/shared/json.c b/src/shared/json.c +index eda7bb1956..e346bb0a01 100644 +--- a/src/shared/json.c ++++ b/src/shared/json.c +@@ -4227,7 +4227,13 @@ static void *dispatch_userdata(const JsonDispatch *p, void *userdata) { + return SIZE_TO_PTR(p->offset); + } + +-int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) { ++int json_dispatch_full( ++ JsonVariant *v, ++ const JsonDispatch table[], ++ JsonDispatchCallback bad, ++ JsonDispatchFlags flags, ++ void *userdata, ++ const char **reterr_bad_field) { + size_t m; + int r, done = 0; + bool *found; +@@ -4238,6 +4244,9 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if (flags & JSON_PERMISSIVE) + return 0; + ++ if (reterr_bad_field) ++ *reterr_bad_field = NULL; ++ + return -EINVAL; + } + +@@ -4260,7 +4269,7 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + streq_ptr(json_variant_string(key), p->name)) + break; + +- if (p->name) { /* Found a matching entry! :-) */ ++ if (p->name) { /* Found a matching entry! 🙂 */ + JsonDispatchFlags merged_flags; + + merged_flags = flags | p->flags; +@@ -4275,6 +4284,9 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if (merged_flags & JSON_PERMISSIVE) + continue; + ++ if (reterr_bad_field) ++ *reterr_bad_field = p->name; ++ + return -EINVAL; + } + +@@ -4284,6 +4296,9 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if (merged_flags & JSON_PERMISSIVE) + continue; + ++ if (reterr_bad_field) ++ *reterr_bad_field = p->name; ++ + return -ENOTUNIQ; + } + +@@ -4295,13 +4310,16 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if (merged_flags & JSON_PERMISSIVE) + continue; + ++ if (reterr_bad_field) ++ *reterr_bad_field = json_variant_string(key); ++ + return r; + } + } + + done ++; + +- } else { /* Didn't find a matching entry! :-( */ ++ } else { /* Didn't find a matching entry! â˜šī¸ */ + + if (bad) { + r = bad(json_variant_string(key), value, flags, userdata); +@@ -4309,6 +4327,9 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if (flags & JSON_PERMISSIVE) + continue; + ++ if (reterr_bad_field) ++ *reterr_bad_field = json_variant_string(key); ++ + return r; + } else + done ++; +@@ -4319,6 +4340,9 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if (flags & JSON_PERMISSIVE) + continue; + ++ if (reterr_bad_field) ++ *reterr_bad_field = json_variant_string(key); ++ + return -EADDRNOTAVAIL; + } + } +@@ -4333,6 +4357,9 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + if ((merged_flags & JSON_PERMISSIVE)) + continue; + ++ if (reterr_bad_field) ++ *reterr_bad_field = p->name; ++ + return -ENXIO; + } + } +@@ -4340,6 +4367,14 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba + return done; + } + ++int json_dispatch( ++ JsonVariant *v, ++ const JsonDispatch table[], ++ JsonDispatchFlags flags, ++ void *userdata) { ++ return json_dispatch_full(v, table, NULL, flags, userdata, NULL); ++} ++ + int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + bool *b = ASSERT_PTR(userdata); + +diff --git a/src/shared/json.h b/src/shared/json.h +index 5993e05299..e62c71a249 100644 +--- a/src/shared/json.h ++++ b/src/shared/json.h +@@ -363,7 +363,8 @@ typedef struct JsonDispatch { + JsonDispatchFlags flags; + } JsonDispatch; + +-int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata); ++int json_dispatch_full(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata, const char **reterr_bad_field); ++int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchFlags flags, void *userdata); + + int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); + int json_dispatch_const_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); +diff --git a/src/shared/user-record.c b/src/shared/user-record.c +index 84cbdb1d30..1c37444349 100644 +--- a/src/shared/user-record.c ++++ b/src/shared/user-record.c +@@ -653,7 +653,7 @@ static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchF + {}, + }; + +- return json_dispatch(variant, secret_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(variant, secret_dispatch_table, flags, userdata); + } + + static int dispatch_pkcs11_uri(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { +@@ -782,7 +782,7 @@ static int dispatch_pkcs11_key(const char *name, JsonVariant *variant, JsonDispa + k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key; + *k = (Pkcs11EncryptedKey) {}; + +- r = json_dispatch(e, pkcs11_key_dispatch_table, NULL, flags, k); ++ r = json_dispatch(e, pkcs11_key_dispatch_table, flags, k); + if (r < 0) { + pkcs11_encrypted_key_done(k); + return r; +@@ -916,7 +916,7 @@ static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, Json + .client_pin = -1, + }; + +- r = json_dispatch(e, fido2_hmac_salt_dispatch_table, NULL, flags, k); ++ r = json_dispatch(e, fido2_hmac_salt_dispatch_table, flags, k); + if (r < 0) { + fido2_hmac_salt_done(k); + return r; +@@ -956,7 +956,7 @@ static int dispatch_recovery_key(const char *name, JsonVariant *variant, JsonDis + k = h->recovery_key + h->n_recovery_key; + *k = (RecoveryKey) {}; + +- r = json_dispatch(e, recovery_key_dispatch_table, NULL, flags, k); ++ r = json_dispatch(e, recovery_key_dispatch_table, flags, k); + if (r < 0) { + recovery_key_done(k); + return r; +@@ -1038,7 +1038,7 @@ static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispa + {}, + }; + +- return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(variant, privileged_dispatch_table, flags, userdata); + } + + static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { +@@ -1077,7 +1077,7 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch + if (!m) + return 0; + +- return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(m, binding_dispatch_table, flags, userdata); + } + + int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags) { +@@ -1276,7 +1276,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp + if (!matching) + continue; + +- r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata); ++ r = json_dispatch(e, per_machine_dispatch_table, flags, userdata); + if (r < 0) + return r; + } +@@ -1325,7 +1325,7 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF + if (!m) + return 0; + +- return json_dispatch(m, status_dispatch_table, NULL, flags, userdata); ++ return json_dispatch(m, status_dispatch_table, flags, userdata); + } + + int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) { +@@ -1615,7 +1615,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla + if (r < 0) + return r; + +- r = json_dispatch(h->json, user_dispatch_table, NULL, json_flags, h); ++ r = json_dispatch(h->json, user_dispatch_table, json_flags, h); + if (r < 0) + return r; + +diff --git a/src/shared/userdb.c b/src/shared/userdb.c +index 3f64ec8942..4ea4ac4d72 100644 +--- a/src/shared/userdb.c ++++ b/src/shared/userdb.c +@@ -189,7 +189,7 @@ static int userdb_on_query_reply( + + assert_se(!iterator->found_user); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &user_data); ++ r = json_dispatch(parameters, dispatch_table, 0, &user_data); + if (r < 0) + goto finish; + +@@ -246,7 +246,7 @@ static int userdb_on_query_reply( + + assert_se(!iterator->found_group); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &group_data); ++ r = json_dispatch(parameters, dispatch_table, 0, &group_data); + if (r < 0) + goto finish; + +@@ -302,7 +302,7 @@ static int userdb_on_query_reply( + assert(!iterator->found_user_name); + assert(!iterator->found_group_name); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &membership_data); ++ r = json_dispatch(parameters, dispatch_table, 0, &membership_data); + if (r < 0) + goto finish; + +diff --git a/src/shared/varlink.c b/src/shared/varlink.c +index 4d2cfee491..49593aa05a 100644 +--- a/src/shared/varlink.c ++++ b/src/shared/varlink.c +@@ -1771,6 +1771,26 @@ int varlink_notifyb(Varlink *v, ...) { + return varlink_notify(v, parameters); + } + ++int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch table[], void *userdata) { ++ const char *bad_field = NULL; ++ int r; ++ ++ assert_return(v, -EINVAL); ++ assert_return(table, -EINVAL); ++ ++ /* A wrapper around json_dispatch_full() that returns a nice InvalidParameter error if we hit a problem with some field. */ ++ ++ r = json_dispatch_full(parameters, table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field); ++ if (r < 0) { ++ if (bad_field) ++ return varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, ++ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(bad_field)))); ++ return r; ++ } ++ ++ return 0; ++} ++ + int varlink_bind_reply(Varlink *v, VarlinkReply callback) { + assert_return(v, -EINVAL); + +diff --git a/src/shared/varlink.h b/src/shared/varlink.h +index 9518cd9098..e51ccd9107 100644 +--- a/src/shared/varlink.h ++++ b/src/shared/varlink.h +@@ -107,6 +107,9 @@ int varlink_error_errno(Varlink *v, int error); + int varlink_notify(Varlink *v, JsonVariant *parameters); + int varlink_notifyb(Varlink *v, ...); + ++/* Parsing incoming data via json_dispatch() and generate a nice error on parse errors */ ++int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch table[], void *userdata); ++ + /* Bind a disconnect, reply or timeout callback */ + int varlink_bind_reply(Varlink *v, VarlinkReply reply); + +diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c +index 21a869df1b..569dba3285 100644 +--- a/src/userdb/userwork.c ++++ b/src/userdb/userwork.c +@@ -147,8 +147,8 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + r = userdb_flags_from_service(link, p.service, &userdb_flags); +@@ -283,8 +283,8 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + r = userdb_flags_from_service(link, p.service, &userdb_flags); +@@ -366,8 +366,8 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var + + assert(parameters); + +- r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); +- if (r < 0) ++ r = varlink_dispatch(link, parameters, dispatch_table, &p); ++ if (r != 0) + return r; + + r = userdb_flags_from_service(link, p.service, &userdb_flags); diff --git a/1102-json-add-json_dispatch_const_user_group_name.patch b/1102-json-add-json_dispatch_const_user_group_name.patch new file mode 100644 index 0000000..ef18155 --- /dev/null +++ b/1102-json-add-json_dispatch_const_user_group_name.patch @@ -0,0 +1,285 @@ +From ea5380d15cd5e479b45a5ea18d36feea665ae6be Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 30 Sep 2024 17:33:05 +0200 +Subject: [PATCH] json: add json_dispatch_const_user_group_name() + +This is the same as json_dispatch_user_group_name() but fills in the +string as "const char*" to the JSON field. Or in other words, it's what +sd_json_dispatch_const_string() is to sd_json_dispatch_string(). + +Note this drops the SD_JSON_STRICT flags from various dispatch tables +for these fields, and replaces this by SD_JSON_RELAX, i.e. the opposite +behaviour. As #34558 correctly suggests we should validate user names +in lookup functions using the lax rules, rather than the strict ones, +since clients not knowing the rules might ask us for arbitrary +resolution. + +(SD_JSON_RELAX internally translates to valid_user_group_name() with the +VALID_USER_RELAX flag). + +See: #34558 +(cherry picked from commit 0376ef36a1ff3768ad0c833f215064e34b40b86c) + +Related: RHEL-55266 +--- + src/core/core-varlink.c | 18 +++++++++--------- + src/home/homed-varlink.c | 21 +++++++++++---------- + src/machine/machined-varlink.c | 18 +++++++++--------- + src/shared/json.c | 21 ++++++++++++++++++++- + src/shared/json.h | 1 + + src/userdb/userwork.c | 21 +++++++++++---------- + 6 files changed, 61 insertions(+), 39 deletions(-) + +diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c +index 776a3eebab..d9457cfecc 100644 +--- a/src/core/core-varlink.c ++++ b/src/core/core-varlink.c +@@ -263,9 +263,9 @@ static int manager_varlink_send_managed_oom_initial(Manager *m) { + static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -370,9 +370,9 @@ static bool group_match_lookup_parameters(LookupParameters *p, const char *name, + static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -453,9 +453,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va + static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +diff --git a/src/home/homed-varlink.c b/src/home/homed-varlink.c +index 1cef25f563..7b769fd189 100644 +--- a/src/home/homed-varlink.c ++++ b/src/home/homed-varlink.c +@@ -1,12 +1,13 @@ + /* SPDX-License-Identifier: LGPL-2.1-or-later */ + ++#include "format-util.h" + #include "group-record.h" + #include "homed-varlink.h" ++#include "json.h" + #include "strv.h" + #include "user-record-util.h" + #include "user-record.h" + #include "user-util.h" +-#include "format-util.h" + + typedef struct LookupParameters { + const char *user_name; +@@ -73,9 +74,9 @@ static bool home_user_match_lookup_parameters(LookupParameters *p, Home *h) { + int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -188,9 +189,9 @@ static bool home_group_match_lookup_parameters(LookupParameters *p, Home *h) { + int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -257,9 +258,9 @@ int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMe + int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c +index 8b230b0078..e1a5b1b2ab 100644 +--- a/src/machine/machined-varlink.c ++++ b/src/machine/machined-varlink.c +@@ -138,9 +138,9 @@ static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char * + static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -303,9 +303,9 @@ static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char + static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -355,9 +355,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va + static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +diff --git a/src/shared/json.c b/src/shared/json.c +index e346bb0a01..dc3b79c3f5 100644 +--- a/src/shared/json.c ++++ b/src/shared/json.c +@@ -4613,7 +4613,7 @@ int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFl + } + + int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { +- char **s = userdata; ++ char **s = ASSERT_PTR(userdata); + const char *n; + int r; + +@@ -4636,6 +4636,25 @@ int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDi + return 0; + } + ++int json_dispatch_const_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { ++ const char **s = ASSERT_PTR(userdata), *n; ++ ++ if (json_variant_is_null(variant)) { ++ *s = NULL; ++ return 0; ++ } ++ ++ if (!json_variant_is_string(variant)) ++ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); ++ ++ n = json_variant_string(variant); ++ if (!valid_user_group_name(n, FLAGS_SET(flags, JSON_RELAX) ? VALID_USER_RELAX : 0)) ++ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid user/group name.", strna(name)); ++ ++ *s = n; ++ return 0; ++} ++ + int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + sd_id128_t *uuid = userdata; + int r; +diff --git a/src/shared/json.h b/src/shared/json.h +index e62c71a249..e353b7bb19 100644 +--- a/src/shared/json.h ++++ b/src/shared/json.h +@@ -380,6 +380,7 @@ int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFla + int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); + int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); + int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); ++int json_dispatch_const_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); + int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); + int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); + +diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c +index 569dba3285..7c8b49c588 100644 +--- a/src/userdb/userwork.c ++++ b/src/userdb/userwork.c +@@ -9,12 +9,13 @@ + #include "fd-util.h" + #include "group-record.h" + #include "io-util.h" ++#include "json.h" + #include "main-func.h" + #include "process-util.h" + #include "strv.h" + #include "time-util.h" +-#include "user-record-nss.h" + #include "user-record.h" ++#include "user-record-nss.h" + #include "user-util.h" + #include "userdb.h" + #include "varlink.h" +@@ -131,9 +132,9 @@ static int userdb_flags_from_service(Varlink *link, const char *service, UserDBF + static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -267,9 +268,9 @@ static int build_group_json(Varlink *link, GroupRecord *gr, JsonVariant **ret) { + static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { +- { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + +@@ -352,9 +353,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va + + static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + static const JsonDispatch dispatch_table[] = { +- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 }, +- { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 }, +- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, ++ { "userName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), JSON_RELAX }, ++ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), JSON_RELAX }, ++ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + diff --git a/1103-sd-varlink-add-new-sd_varlink_error_is_invalid_param.patch b/1103-sd-varlink-add-new-sd_varlink_error_is_invalid_param.patch new file mode 100644 index 0000000..52db9e9 --- /dev/null +++ b/1103-sd-varlink-add-new-sd_varlink_error_is_invalid_param.patch @@ -0,0 +1,137 @@ +From 3c1844015ff471ca28b7359624323948cc1303f5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 15 Oct 2024 13:40:24 +0200 +Subject: [PATCH] sd-varlink: add new sd_varlink_error_is_invalid_parameter() + helper + +(cherry picked from commit 12641ecd67875b7bf18db06c0afa40c37d804750) + +Related: RHEL-55266 +--- + src/shared/varlink.c | 20 +++++++++++++++ + src/shared/varlink.h | 2 ++ + src/test/test-varlink.c | 56 ++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 77 insertions(+), 1 deletion(-) + +diff --git a/src/shared/varlink.c b/src/shared/varlink.c +index 49593aa05a..0f5db09cea 100644 +--- a/src/shared/varlink.c ++++ b/src/shared/varlink.c +@@ -2687,3 +2687,23 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f + LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss)); + return 0; + } ++ ++int varlink_error_is_invalid_parameter(const char *error, JsonVariant *parameter, const char *name) { ++ ++ /* Returns true if the specified error result is an invalid parameter error for the parameter 'name' */ ++ ++ if (!streq_ptr(error, VARLINK_ERROR_INVALID_PARAMETER)) ++ return false; ++ ++ if (!name) ++ return true; ++ ++ if (!json_variant_is_object(parameter)) ++ return false; ++ ++ JsonVariant *e = json_variant_by_key(parameter, "parameter"); ++ if (!e || !json_variant_is_string(e)) ++ return false; ++ ++ return streq(json_variant_string(e), name); ++} +diff --git a/src/shared/varlink.h b/src/shared/varlink.h +index e51ccd9107..a4170737db 100644 +--- a/src/shared/varlink.h ++++ b/src/shared/varlink.h +@@ -161,6 +161,8 @@ unsigned varlink_server_current_connections(VarlinkServer *s); + + int varlink_server_set_description(VarlinkServer *s, const char *description); + ++int varlink_error_is_invalid_parameter(const char *error, JsonVariant *parameter, const char *name); ++ + DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref); + DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_close_unref); + DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_flush_close_unref); +diff --git a/src/test/test-varlink.c b/src/test/test-varlink.c +index 634baf1ae8..13f772cf4e 100644 +--- a/src/test/test-varlink.c ++++ b/src/test/test-varlink.c +@@ -10,6 +10,7 @@ + #include "json.h" + #include "rm-rf.h" + #include "strv.h" ++#include "tests.h" + #include "tmpfile-util.h" + #include "user-util.h" + #include "varlink.h" +@@ -184,7 +185,7 @@ static int block_fd_handler(sd_event_source *s, int fd, uint32_t revents, void * + return 0; + } + +-int main(int argc, char *argv[]) { ++TEST(chat) { + _cleanup_(sd_event_source_unrefp) sd_event_source *block_event = NULL; + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + _cleanup_(varlink_flush_close_unrefp) Varlink *c = NULL; +@@ -234,6 +235,59 @@ int main(int argc, char *argv[]) { + assert_se(sd_event_loop(e) >= 0); + + assert_se(pthread_join(t, NULL) == 0); ++} ++ ++static int method_invalid(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { ++ int r; ++ ++ JsonDispatch table[] = { ++ { "iexist", JSON_VARIANT_STRING, json_dispatch_const_string, 0, JSON_MANDATORY }, ++ {} ++ }; ++ ++ const char *p = NULL; ++ ++ r = varlink_dispatch(link, parameters, table, &p); ++ if (r != 0) ++ return r; + ++ assert_not_reached(); ++} ++ ++static int reply_invalid(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata) { ++ assert(varlink_error_is_invalid_parameter(error_id, parameters, "idontexist")); ++ assert(sd_event_exit(varlink_get_event(link), EXIT_SUCCESS) >= 0); + return 0; + } ++ ++TEST(invalid_parameter) { ++ _cleanup_(sd_event_unrefp) sd_event *e = NULL; ++ assert_se(sd_event_default(&e) >= 0); ++ ++ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; ++ assert_se(varlink_server_new(&s, 0) >= 0); ++ ++ assert_se(varlink_server_attach_event(s, e, 0) >= 0); ++ ++ assert_se(varlink_server_bind_method(s, "foo.mytest.Invalid", method_invalid) >= 0); ++ ++ int connfd[2]; ++ assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, connfd) >= 0); ++ assert_se(varlink_server_add_connection(s, connfd[0], /* ret= */ NULL) >= 0); ++ ++ _cleanup_(varlink_unrefp) Varlink *c = NULL; ++ assert_se(varlink_connect_fd(&c, connfd[1]) >= 0); ++ ++ assert_se(varlink_attach_event(c, e, 0) >= 0); ++ ++ assert_se(varlink_bind_reply(c, reply_invalid) >= 0); ++ ++ assert_se(varlink_invokeb(c, "foo.mytest.Invalid", JSON_BUILD_OBJECT( ++ JSON_BUILD_PAIR_STRING("iexist", "foo"), ++ JSON_BUILD_PAIR_STRING("idontexist", "bar"))) >= 0); ++ ++ ++ assert_se(sd_event_loop(e) >= 0); ++} ++ ++DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/1104-userdb-return-ESRCH-if-userdb-service-refuses-a-user.patch b/1104-userdb-return-ESRCH-if-userdb-service-refuses-a-user.patch new file mode 100644 index 0000000..16340b4 --- /dev/null +++ b/1104-userdb-return-ESRCH-if-userdb-service-refuses-a-user.patch @@ -0,0 +1,39 @@ +From ba1ea0689633b7e61f50277dcfdec80ff50eb746 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 15 Oct 2024 13:59:57 +0200 +Subject: [PATCH] userdb: return ESRCH if userdb service refuses a user/group + name as invalid + +if a userdb service refuse a user/group name as invalid, let's turn this +into ESRCH client-side following that there definitely is no user/group +record for a completely invalid user/group name. + +Replaces: #34558 +(cherry picked from commit 69cc4ee134f420dcdd6aac08446bd852d8739694) + +Resolves: RHEL-55266 +--- + src/shared/userdb.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/shared/userdb.c b/src/shared/userdb.c +index 4ea4ac4d72..ceb285c8d8 100644 +--- a/src/shared/userdb.c ++++ b/src/shared/userdb.c +@@ -159,9 +159,15 @@ static int userdb_on_query_reply( + if (error_id) { + log_debug("Got lookup error: %s", error_id); + ++ /* Convert various forms of record not found into -ESRCH, since NSS typically doesn't care, ++ * about the details. Note that if a userName specification is refused as invalid parameter, ++ * we also turn this into -ESRCH following the logic that there cannot be a user record for a ++ * completely invalid user name. */ + if (STR_IN_SET(error_id, + "io.systemd.UserDatabase.NoRecordFound", +- "io.systemd.UserDatabase.ConflictingRecordFound")) ++ "io.systemd.UserDatabase.ConflictingRecordFound") || ++ varlink_error_is_invalid_parameter(error_id, parameters, "userName") || ++ varlink_error_is_invalid_parameter(error_id, parameters, "groupName")) + r = -ESRCH; + else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable")) + r = -EHOSTDOWN; diff --git a/1105-test-rename-assert.sh-to-util.sh.patch b/1105-test-rename-assert.sh-to-util.sh.patch new file mode 100644 index 0000000..345f2b6 --- /dev/null +++ b/1105-test-rename-assert.sh-to-util.sh.patch @@ -0,0 +1,274 @@ +From 372af57b902d95dfbc1b5f785010862e4b1eb396 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 16 May 2023 19:09:13 +0200 +Subject: [PATCH] test: rename assert.sh to util.sh + +So we can extend it with additional utility functions without making it +confusing. + +No functional change. + +(cherry picked from commit cb153b4fe9046590eb8efba66b67a0db899cb96d) + +Related: RHEL-55301 +--- + test/units/testsuite-17.02.sh | 4 ++-- + test/units/testsuite-17.07.sh | 4 ++-- + test/units/testsuite-17.08.sh | 4 ++-- + test/units/testsuite-17.09.sh | 4 ++-- + test/units/testsuite-17.12.sh | 4 ++-- + test/units/testsuite-26.sh | 4 ++-- + test/units/testsuite-35.sh | 4 ++-- + test/units/testsuite-45.sh | 4 ++-- + test/units/testsuite-58.sh | 4 ++-- + test/units/testsuite-60.sh | 4 ++-- + test/units/testsuite-65.sh | 4 ++-- + test/units/testsuite-71.sh | 4 ++-- + test/units/testsuite-73.sh | 4 ++-- + test/units/testsuite-75.sh | 4 ++-- + test/units/testsuite-76.sh | 4 ++-- + test/units/{assert.sh => util.sh} | 2 +- + 16 files changed, 31 insertions(+), 31 deletions(-) + rename test/units/{assert.sh => util.sh} (95%) + +diff --git a/test/units/testsuite-17.02.sh b/test/units/testsuite-17.02.sh +index 82f9fd1f62..22b1552653 100755 +--- a/test/units/testsuite-17.02.sh ++++ b/test/units/testsuite-17.02.sh +@@ -7,8 +7,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + mkdir -p /run/udev/rules.d/ + +diff --git a/test/units/testsuite-17.07.sh b/test/units/testsuite-17.07.sh +index 549107af10..50dc16b14b 100755 +--- a/test/units/testsuite-17.07.sh ++++ b/test/units/testsuite-17.07.sh +@@ -3,8 +3,8 @@ + set -ex + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + wait_service_active() {( + set +ex +diff --git a/test/units/testsuite-17.08.sh b/test/units/testsuite-17.08.sh +index f740b337f7..3502538fd4 100755 +--- a/test/units/testsuite-17.08.sh ++++ b/test/units/testsuite-17.08.sh +@@ -3,8 +3,8 @@ + set -ex + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + # This is a test for issue #24518. + +diff --git a/test/units/testsuite-17.09.sh b/test/units/testsuite-17.09.sh +index 01ac5f1709..c3914d9356 100755 +--- a/test/units/testsuite-17.09.sh ++++ b/test/units/testsuite-17.09.sh +@@ -3,8 +3,8 @@ + set -ex + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + # This is a test for issue #24987. + +diff --git a/test/units/testsuite-17.12.sh b/test/units/testsuite-17.12.sh +index df74d356ee..ccc91bfe46 100755 +--- a/test/units/testsuite-17.12.sh ++++ b/test/units/testsuite-17.12.sh +@@ -3,8 +3,8 @@ + set -ex + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + create_link_file() { + name=${1?} +diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh +index debee91dde..340e02a561 100755 +--- a/test/units/testsuite-26.sh ++++ b/test/units/testsuite-26.sh +@@ -3,8 +3,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + : >/failed + +diff --git a/test/units/testsuite-35.sh b/test/units/testsuite-35.sh +index 1863c535a4..18c50279e2 100755 +--- a/test/units/testsuite-35.sh ++++ b/test/units/testsuite-35.sh +@@ -3,8 +3,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + cleanup_test_user() ( + set +ex +diff --git a/test/units/testsuite-45.sh b/test/units/testsuite-45.sh +index 7e757e4b00..c85e17da7b 100755 +--- a/test/units/testsuite-45.sh ++++ b/test/units/testsuite-45.sh +@@ -4,8 +4,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + restore_timezone() { + if [[ -f /tmp/timezone.bak ]]; then +diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh +index f41069ee04..850111b319 100755 +--- a/test/units/testsuite-58.sh ++++ b/test/units/testsuite-58.sh +@@ -8,8 +8,8 @@ if ! command -v systemd-repart &>/dev/null; then + exit 0 + fi + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + export SYSTEMD_LOG_LEVEL=debug + export PAGER=cat +diff --git a/test/units/testsuite-60.sh b/test/units/testsuite-60.sh +index a29364568d..920f5019d2 100755 +--- a/test/units/testsuite-60.sh ++++ b/test/units/testsuite-60.sh +@@ -3,8 +3,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + teardown_test_dependencies() ( + set +eux +diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh +index 0fb143bde7..4e12511a1f 100755 +--- a/test/units/testsuite-65.sh ++++ b/test/units/testsuite-65.sh +@@ -3,8 +3,8 @@ + # shellcheck disable=SC2016 + set -eux + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + # On RHEL9 we don't have the `util.sh` script, so we need to define the `runas` function here + runas() { +diff --git a/test/units/testsuite-71.sh b/test/units/testsuite-71.sh +index 2382cccb3a..49efa4f982 100755 +--- a/test/units/testsuite-71.sh ++++ b/test/units/testsuite-71.sh +@@ -4,8 +4,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + restore_hostname() { + if [[ -e /tmp/hostname.bak ]]; then +diff --git a/test/units/testsuite-73.sh b/test/units/testsuite-73.sh +index f9e2dce1bf..279507af31 100755 +--- a/test/units/testsuite-73.sh ++++ b/test/units/testsuite-73.sh +@@ -4,8 +4,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + enable_debug() { + mkdir -p /run/systemd/system/systemd-localed.service.d +diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh +index ddd86d09bb..370a91863f 100755 +--- a/test/units/testsuite-75.sh ++++ b/test/units/testsuite-75.sh +@@ -11,8 +11,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + : >/failed + +diff --git a/test/units/testsuite-76.sh b/test/units/testsuite-76.sh +index cb571f8b97..0436afec65 100755 +--- a/test/units/testsuite-76.sh ++++ b/test/units/testsuite-76.sh +@@ -3,8 +3,8 @@ + set -eux + set -o pipefail + +-# shellcheck source=test/units/assert.sh +-. "$(dirname "$0")"/assert.sh ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh + + export SYSTEMD_LOG_LEVEL=debug + +diff --git a/test/units/assert.sh b/test/units/util.sh +similarity index 95% +rename from test/units/assert.sh +rename to test/units/util.sh +index 2f4d93ab8c..00b8c5e393 100755 +--- a/test/units/assert.sh ++++ b/test/units/util.sh +@@ -1,7 +1,7 @@ + #!/usr/bin/env bash + # SPDX-License-Identifier: LGPL-2.1-or-later + +-# utility functions for shell tests ++# Utility functions for shell tests + + assert_true() {( + set +ex diff --git a/1106-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch b/1106-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch new file mode 100644 index 0000000..3701a3a --- /dev/null +++ b/1106-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch @@ -0,0 +1,89 @@ +From 14a2f6dd75ef4eefd0a7791a7c086e72a141bc69 Mon Sep 17 00:00:00 2001 +From: Richard Phibel +Date: Tue, 23 May 2023 16:09:40 +0200 +Subject: [PATCH] core: Don't GC unit if it is in cgroup_empty_queue + +The gc_unit_queue is dispatched before the cgroup_empty_queue. Because +of this, when we enter in on_cgroup_empty_event, the unit in +cgroup_empty_queue may already have been freed and we don't clean up the +corresponding cgroup. With this change, we prevent the unit from being +garbage collected if it is in the cgroup_empty_queue. + +(cherry picked from commit 8db998981a4fefd0122bcf5f965726b63c9045c2) + +Resolves: RHEL-55301 +--- + src/core/unit.c | 3 ++ + test/units/testsuite-19.cleanup-slice.sh | 49 ++++++++++++++++++++++++ + 2 files changed, 52 insertions(+) + create mode 100755 test/units/testsuite-19.cleanup-slice.sh + +diff --git a/src/core/unit.c b/src/core/unit.c +index f109d16eb3..08f87851d9 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -417,6 +417,9 @@ bool unit_may_gc(Unit *u) { + if (u->perpetual) + return false; + ++ if (u->in_cgroup_empty_queue) ++ return false; ++ + if (sd_bus_track_count(u->bus_track) > 0) + return false; + +diff --git a/test/units/testsuite-19.cleanup-slice.sh b/test/units/testsuite-19.cleanup-slice.sh +new file mode 100755 +index 0000000000..5d63160334 +--- /dev/null ++++ b/test/units/testsuite-19.cleanup-slice.sh +@@ -0,0 +1,49 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++set -eux ++set -o pipefail ++ ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh ++ ++export SYSTEMD_LOG_LEVEL=debug ++ ++# Create service with KillMode=none inside a slice ++cat </run/systemd/system/test19cleanup.service ++[Unit] ++Description=Test 19 cleanup Service ++[Service] ++Slice=test19cleanup.slice ++Type=exec ++ExecStart=sleep infinity ++KillMode=none ++EOF ++cat </run/systemd/system/test19cleanup.slice ++[Unit] ++Description=Test 19 cleanup Slice ++EOF ++ ++# Start service ++systemctl start test19cleanup.service ++assert_rc 0 systemd-cgls /test19cleanup.slice ++ ++pid=$(systemctl show --property MainPID --value test19cleanup) ++ps "$pid" ++ ++# Stop slice ++# The sleep process will not be killed because of KillMode=none ++# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed ++systemctl stop test19cleanup.slice ++ ++ps "$pid" ++ ++# Kill sleep process manually ++kill -s TERM "$pid" ++while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done ++ ++timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done' ++assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service ++ ++# Check that empty cgroup /test19cleanup.slice has been removed ++timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done' ++assert_rc 1 systemd-cgls /test19cleanup.slice diff --git a/1107-core-Handle-cgroup-pruning-in-on_cgroup_empty_event.patch b/1107-core-Handle-cgroup-pruning-in-on_cgroup_empty_event.patch new file mode 100644 index 0000000..dcba4a0 --- /dev/null +++ b/1107-core-Handle-cgroup-pruning-in-on_cgroup_empty_event.patch @@ -0,0 +1,62 @@ +From 250b4c9364ee455034ff93a2ac379268963a39a9 Mon Sep 17 00:00:00 2001 +From: Richard Phibel +Date: Thu, 25 May 2023 19:49:11 +0200 +Subject: [PATCH] core: Handle cgroup pruning in on_cgroup_empty_event + +This change removes the pruning of cgroups for FAILED/INACTIVE units +from per-unit-type handlers and moves it in on_cgroup_empty_event. + +(cherry picked from commit 380dd177798507ced9cde580c04539f54bb980c8) + +Related: RHEL-55301 +--- + src/core/cgroup.c | 4 +++- + src/core/scope.c | 5 ----- + src/core/service.c | 3 +-- + 3 files changed, 4 insertions(+), 8 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index c44966839c..fa3e7fca05 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -3007,7 +3007,9 @@ static int on_cgroup_empty_event(sd_event_source *s, void *userdata) { + + unit_add_to_gc_queue(u); + +- if (UNIT_VTABLE(u)->notify_cgroup_empty) ++ if (IN_SET(unit_active_state(u), UNIT_INACTIVE, UNIT_FAILED)) ++ unit_prune_cgroup(u); ++ else if (UNIT_VTABLE(u)->notify_cgroup_empty) + UNIT_VTABLE(u)->notify_cgroup_empty(u); + + return 0; +diff --git a/src/core/scope.c b/src/core/scope.c +index e2fc4cc995..087ac2853a 100644 +--- a/src/core/scope.c ++++ b/src/core/scope.c +@@ -628,11 +628,6 @@ static void scope_notify_cgroup_empty_event(Unit *u) { + + if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) + scope_enter_dead(s, SCOPE_SUCCESS); +- +- /* If the cgroup empty notification comes when the unit is not active, we must have failed to clean +- * up the cgroup earlier and should do it now. */ +- if (IN_SET(s->state, SCOPE_DEAD, SCOPE_FAILED)) +- unit_prune_cgroup(u); + } + + static void scope_notify_cgroup_oom_event(Unit *u, bool managed_oom) { +diff --git a/src/core/service.c b/src/core/service.c +index 902948905f..433df0afe3 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -3446,8 +3446,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { + + /* If the cgroup empty notification comes when the unit is not active, we must have failed to clean + * up the cgroup earlier and should do it now. */ +- case SERVICE_DEAD: +- case SERVICE_FAILED: ++ case SERVICE_AUTO_RESTART: + unit_prune_cgroup(u); + break; + diff --git a/1108-Fix-failing-test.patch b/1108-Fix-failing-test.patch new file mode 100644 index 0000000..7534f6d --- /dev/null +++ b/1108-Fix-failing-test.patch @@ -0,0 +1,50 @@ +From 0b3c0a40c1d4a6be0c259795248f963d1ac17e9f Mon Sep 17 00:00:00 2001 +From: Richard Phibel +Date: Tue, 30 May 2023 00:45:09 +0200 +Subject: [PATCH] Fix failing test + +In test-execute, only the unit was started, not the slice. Because of +that the slice cgroup was pruned even if it was still needed. From what +I can tell, this is because, in the test, we don't have all the +mechanics that starts the slice for a service. To fix the issue the +slice is started manually. + +(cherry picked from commit fc6172b1d844fb2e93cb1180810eba561aead3b8) + +Related: RHEL-55301 +--- + src/test/test-execute.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index 1f838e5c91..20d6035cee 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -207,6 +207,17 @@ static bool is_inaccessible_available(void) { + return true; + } + ++static void start_parent_slices(Unit *unit) { ++ Unit *slice; ++ ++ slice = UNIT_GET_SLICE(unit); ++ if (slice) { ++ start_parent_slices(slice); ++ int r = unit_start(slice, NULL); ++ assert_se(r >= 0 || r == -EALREADY); ++ } ++} ++ + static void _test(const char *file, unsigned line, const char *func, + Manager *m, const char *unit_name, int status_expected, int code_expected) { + Unit *unit; +@@ -214,6 +225,9 @@ static void _test(const char *file, unsigned line, const char *func, + assert_se(unit_name); + + assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0); ++ /* We need to start the slices as well otherwise the slice cgroups might be pruned ++ * in on_cgroup_empty_event. */ ++ start_parent_slices(unit); + assert_se(unit_start(unit, NULL) >= 0); + check_main_result(file, line, func, m, unit, status_expected, code_expected); + } diff --git a/1109-unit-don-t-gc-unit-in-oom-queue.patch b/1109-unit-don-t-gc-unit-in-oom-queue.patch new file mode 100644 index 0000000..f851fab --- /dev/null +++ b/1109-unit-don-t-gc-unit-in-oom-queue.patch @@ -0,0 +1,46 @@ +From 3f9329109403b981a01e64b41c0fcca02920bb23 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Jun 2023 11:11:28 +0200 +Subject: [PATCH] unit: don't gc unit in oom queue + +This is a follow-up for 8db998981a4fefd0122bcf5f965726b63c9045c2, and +follows a similar logic: a pending OOM event really trumps everything: +we should not GC a unit while it is pending. + +(cherry picked from commit 935f80428fd3220c83163cc4b5a637873e68babb) + +Related: RHEL-55301 +--- + src/core/cgroup.c | 2 ++ + src/core/unit.c | 5 ++++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index fa3e7fca05..7d6b1119be 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -3184,6 +3184,8 @@ static int on_cgroup_oom_event(sd_event_source *s, void *userdata) { + } + + (void) unit_check_oom(u); ++ unit_add_to_gc_queue(u); ++ + return 0; + } + +diff --git a/src/core/unit.c b/src/core/unit.c +index 08f87851d9..4360351bd9 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -417,7 +417,10 @@ bool unit_may_gc(Unit *u) { + if (u->perpetual) + return false; + +- if (u->in_cgroup_empty_queue) ++ /* if we saw a cgroup empty event for this unit, stay around until we processed it so that we remove ++ * the empty cgroup if possible. Similar, process any pending OOM events if they are already queued ++ * before we release the unit. */ ++ if (u->in_cgroup_empty_queue || u->in_cgroup_oom_queue) + return false; + + if (sd_bus_track_count(u->bus_track) > 0) diff --git a/1110-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch b/1110-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch new file mode 100644 index 0000000..89e4d38 --- /dev/null +++ b/1110-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch @@ -0,0 +1,81 @@ +From 31f8d699974e114868a90b49141d4130287a499d Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Jun 2023 11:11:49 +0200 +Subject: [PATCH] core: do not GC units/jobs that are in the D-Bus queue + +Let's make sure that D-Bus messages are always sent out when pending, +before we might GC a unit/job. + +This is kinda a follow-up for 8db998981a4fefd0122bcf5f965726b63c9045c2, +and a similar logic really applies: GC should only be done if we +processed everything else, generated evertyhing else and really don't +need it anymore. + +(cherry picked from commit af05bb971731fe7280e4e85fde71c2e671772c18) + +Related: RHEL-55301 +--- + src/core/dbus-job.c | 3 +++ + src/core/dbus-unit.c | 3 +++ + src/core/job.c | 4 ++++ + src/core/unit.c | 4 ++++ + 4 files changed, 14 insertions(+) + +diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c +index 9792a5c44a..c88d8c2dd5 100644 +--- a/src/core/dbus-job.c ++++ b/src/core/dbus-job.c +@@ -241,6 +241,9 @@ void bus_job_send_change_signal(Job *j) { + if (j->in_dbus_queue) { + LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); + j->in_dbus_queue = false; ++ ++ /* The job might be good to be GC once its pending signals have been sent */ ++ job_add_to_gc_queue(j); + } + + r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index b45b3fdb53..9d3c3be4e9 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -1629,6 +1629,9 @@ void bus_unit_send_change_signal(Unit *u) { + if (u->in_dbus_queue) { + LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); + u->in_dbus_queue = false; ++ ++ /* The unit might be good to be GC once its pending signals have been sent */ ++ unit_add_to_gc_queue(u); + } + + if (!u->id) +diff --git a/src/core/job.c b/src/core/job.c +index 032554a0ac..bd3a741ffd 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -1407,6 +1407,10 @@ bool job_may_gc(Job *j) { + if (!UNIT_VTABLE(j->unit)->gc_jobs) + return false; + ++ /* Make sure to send out pending D-Bus events before we unload the unit */ ++ if (j->in_dbus_queue) ++ return false; ++ + if (sd_bus_track_count(j->bus_track) > 0) + return false; + +diff --git a/src/core/unit.c b/src/core/unit.c +index 4360351bd9..03eb3aaecf 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -423,6 +423,10 @@ bool unit_may_gc(Unit *u) { + if (u->in_cgroup_empty_queue || u->in_cgroup_oom_queue) + return false; + ++ /* Make sure to send out D-Bus events before we unload the unit */ ++ if (u->in_dbus_queue) ++ return false; ++ + if (sd_bus_track_count(u->bus_track) > 0) + return false; + diff --git a/systemd.spec b/systemd.spec index fef7596..0dfcb30 100644 --- a/systemd.spec +++ b/systemd.spec @@ -21,7 +21,7 @@ Name: systemd Url: https://systemd.io Version: 252 -Release: 50%{?dist} +Release: 51%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -1179,6 +1179,20 @@ Patch1093: 1093-test-systemctl-enable-fix-typo.patch Patch1094: 1094-udev-Handle-PTP-device-symlink-properly-on-udev-acti.patch Patch1095: 1095-systemctl-print-warning-about-missing-proc-only-as-d.patch Patch1096: 1096-repart-avoid-use-of-uninitialized-TPM2B_PUBLIC-data.patch +Patch1097: 1097-ci-use-ubuntu-22-04-for-deploy-of-man-pages.patch +Patch1098: 1098-man-tmpfiles-fix-off-by-one-in-example.patch +Patch1099: 1099-test-drop-removed-SCSI-passthrough-feature.patch +Patch1100: 1100-nspawn-call-json_dispatch-with-a-correct-pointer.patch +Patch1101: 1101-varlink-json-introduce-new-varlink_dispatch-helper.patch +Patch1102: 1102-json-add-json_dispatch_const_user_group_name.patch +Patch1103: 1103-sd-varlink-add-new-sd_varlink_error_is_invalid_param.patch +Patch1104: 1104-userdb-return-ESRCH-if-userdb-service-refuses-a-user.patch +Patch1105: 1105-test-rename-assert.sh-to-util.sh.patch +Patch1106: 1106-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch +Patch1107: 1107-core-Handle-cgroup-pruning-in-on_cgroup_empty_event.patch +Patch1108: 1108-Fix-failing-test.patch +Patch1109: 1109-unit-don-t-gc-unit-in-oom-queue.patch +Patch1110: 1110-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch # Downstream-only patches (9000–9999) @@ -2056,6 +2070,22 @@ systemd-hwdb update &>/dev/null || : %{_prefix}/lib/dracut/modules.d/70rhel-net-naming-sysattrs/* %changelog +* Tue Jan 28 2025 systemd maintenance team - 252-51 +- ci: use ubuntu 22:04 for deploy of man pages (RHEL-70884) +- man/tmpfiles: fix off-by-one in example (RHEL-74015) +- test: drop removed SCSI passthrough feature (RHEL-70884) +- nspawn: call json_dispatch() with a correct pointer (RHEL-55266) +- varlink,json: introduce new varlink_dispatch() helper (RHEL-55266) +- json: add json_dispatch_const_user_group_name() (RHEL-55266) +- sd-varlink: add new sd_varlink_error_is_invalid_parameter() helper (RHEL-55266) +- userdb: return ESRCH if userdb service refuses a user/group name as invalid (RHEL-55266) +- test: rename assert.sh to util.sh (RHEL-55301) +- core: Don't GC unit if it is in cgroup_empty_queue (RHEL-55301) +- core: Handle cgroup pruning in on_cgroup_empty_event (RHEL-55301) +- Fix failing test (RHEL-55301) +- unit: don't gc unit in oom queue (RHEL-55301) +- core: do not GC units/jobs that are in the D-Bus queue (RHEL-55301) + * Tue Jan 14 2025 systemd maintenance team - 252-50 - journal: again create user journals for users with high uids (RHEL-70103) - install: don't translate unit instances to paths when reenabling them (RHEL-5956)