From a467358b2a18b611e48e62ed89167a04e0f7634e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 6 Jan 2025 17:26:52 +0900 Subject: [PATCH 1/2] core/device: do not drop backslashes in SYSTEMD_WANTS=/SYSTEMD_USER_WANTS= Let consider the following udev rules: === PROGRAM="/usr/bin/systemd-escape foo-bar-baz", ENV{SYSTEMD_WANTS}+="test1@$result.service" PROGRAM="/usr/bin/systemd-escape aaa-bbb-ccc", ENV{SYSTEMD_WANTS}+="test2@$result.service" === Then, a device expectedly gains a property: === SYSTEMD_WANTS=test1@foo\x2dbar\x2dbaz.service test2@aaa\x2dbbb\x2dccc.service === After the event being processed by udevd, PID1 processes the device, the property previously was parsed with extract_first_word(EXTRACT_UNQUOTE), then the device unit gained the following dependencies: === Wants=test1@foox2dbarx2dbaz.service test2@aaax2dbbbx2dccc.service === So both '%i' and '%I' for the template services did not match with the original data, and it was hard to use systemd-escape in PROGRAM= udev rule token. This makes the property parsed with extract_first_word(EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE), hence the device unit now gains the following dependencies: === Wants=test1@foo\x2dbar\x2dbaz.service test2@aaa\x2dbbb\x2dccc.service === and '%I' for the template services match with the original data. Fixes a bug caused by ceed8f0c8b9a46300eccd1afa2dd8d3c2cb6b47c (v233). Fixes #16735. Replaces #16737 and #35768. --- src/core/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/device.c b/src/core/device.c index e201d09976..15746094a4 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -558,7 +558,7 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; - r = extract_first_word(&wants, &word, NULL, EXTRACT_UNQUOTE); + r = extract_first_word(&wants, &word, NULL, EXTRACT_UNQUOTE | EXTRACT_RETAIN_ESCAPE); if (r == 0) break; if (r == -ENOMEM) From 4c9da4dc91c6f875683a6c9fc00df19686f242cc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 6 Jan 2025 19:16:23 +0900 Subject: [PATCH 2/2] test: add test cases for parsing SYSTEMD_WANTS=/SYSTEMD_USER_WANTS= --- src/test/test-extract-word.c | 43 ++++++++++++ .../TEST-17-UDEV.SYSTEMD_WANTS-escape.sh | 65 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100755 test/units/TEST-17-UDEV.SYSTEMD_WANTS-escape.sh diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index 1bc4088fb4..4c18a45231 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -547,6 +547,49 @@ TEST(extract_first_word) { ASSERT_STREQ(t, "가너도루"); free(t); assert_se(isempty(p)); + + /* For issue #16735. */ + p = "test1@foo\\x2dbar\\x2dbaz.service test2@aaa\\x2dbbb\\x2dccc.service test3@escaped-path-like-data.service test4@/pure/path/like/data.service"; + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE)); + ASSERT_STREQ(t, "test1@foox2dbarx2dbaz.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE)); + ASSERT_STREQ(t, "test2@aaax2dbbbx2dccc.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE)); + ASSERT_STREQ(t, "test3@escaped-path-like-data.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE)); + ASSERT_STREQ(t, "test4@/pure/path/like/data.service"); + free(t); + + p = "test1@foo\\x2dbar\\x2dbaz.service test2@aaa\\x2dbbb\\x2dccc.service test3@escaped-path-like-data.service test4@/pure/path/like/data.service"; + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_RETAIN_ESCAPE)); + ASSERT_STREQ(t, "test1@foo\\x2dbar\\x2dbaz.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_RETAIN_ESCAPE)); + ASSERT_STREQ(t, "test2@aaa\\x2dbbb\\x2dccc.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_RETAIN_ESCAPE)); + ASSERT_STREQ(t, "test3@escaped-path-like-data.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_RETAIN_ESCAPE)); + ASSERT_STREQ(t, "test4@/pure/path/like/data.service"); + free(t); + + p = "test1@foo\\x2dbar\\x2dbaz.service test2@aaa\\x2dbbb\\x2dccc.service test3@escaped-path-like-data.service test4@/pure/path/like/data.service"; + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_CUNESCAPE)); + ASSERT_STREQ(t, "test1@foo-bar-baz.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_CUNESCAPE)); + ASSERT_STREQ(t, "test2@aaa-bbb-ccc.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_CUNESCAPE)); + ASSERT_STREQ(t, "test3@escaped-path-like-data.service"); + free(t); + ASSERT_OK_POSITIVE(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE | EXTRACT_CUNESCAPE)); + ASSERT_STREQ(t, "test4@/pure/path/like/data.service"); + free(t); } TEST(extract_first_word_and_warn) { diff --git a/test/units/TEST-17-UDEV.SYSTEMD_WANTS-escape.sh b/test/units/TEST-17-UDEV.SYSTEMD_WANTS-escape.sh new file mode 100755 index 0000000000..709161aa64 --- /dev/null +++ b/test/units/TEST-17-UDEV.SYSTEMD_WANTS-escape.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2317 +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# This is a test case for #16735. + +IFNAME=test-netif-foo + +at_exit() { + set +e + + rm -f /tmp/output-i /tmp/output-I + + rm -rf /run/udev/rules.d/ + udevadm control --reload + + rm -f /run/systemd/system/test@.service + systemctl daemon-reload + + ip link del "$IFNAME" +} + +trap at_exit EXIT + +udevadm settle --timeout 30 + +mkdir -p /run/systemd/system/ +cat >/run/systemd/system/test@.service </tmp/output-i; echo "I=/%I" >/tmp/output-I' +RemainAfterExit=yes +EOF + +systemctl daemon-reload + +mkdir -p /run/udev/rules.d/ +cat >/run/udev/rules.d/99-testsuite.rules <