return -EINVAL;
/* Already a fully valid unit name? If so, no mangling is necessary... */
- if (unit_name_is_valid(name, UNIT_NAME_ANY))
+ if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
+ if (FLAGS_SET(flags, UNIT_NAME_MANGLE_STRICT) && !endswith(name, suffix)) {
+ const char *e = ASSERT_PTR(strrchr(name, '.'));
+
+ return log_full_errno(warn ? LOG_NOTICE : LOG_DEBUG,
+ SYNTHETIC_ERRNO(EINVAL),
+ "Unit name \"%s\" has unit type \"%s\", but \"%s\" is expected%s%s.",
+ name, e + 1, suffix + 1,
+ operation ? " " : "", strempty(operation));
+ }
+
goto good;
+ }
/* Already a fully valid globbing expression? If so, no mangling is necessary either... */
if (string_is_glob(name) && in_charset(name, VALID_CHARS_GLOB)) {
}
if (path_is_absolute(name)) {
- _cleanup_free_ char *n = NULL;
+ _cleanup_free_ char *n = NULL, *u = NULL;
r = path_simplify_alloc(name, &n);
if (r < 0)
return r;
if (is_device_path(n)) {
- r = unit_name_from_path(n, ".device", ret);
- if (r >= 0)
+ r = unit_name_from_path(n, ".device", &u);
+ if (r >= 0) {
+ if (FLAGS_SET(flags, UNIT_NAME_MANGLE_STRICT) && !streq(suffix, ".device"))
+ return log_full_errno(warn ? LOG_NOTICE : LOG_DEBUG,
+ SYNTHETIC_ERRNO(EINVAL),
+ "Path \"%s\" resolves to unit type \"device\", but \"%s\" is expected%s%s.",
+ name, suffix + 1,
+ operation ? " " : "", strempty(operation));
+
+ *ret = TAKE_PTR(u);
return 1;
+ }
if (r != -EINVAL)
return r;
}
- r = unit_name_from_path(n, ".mount", ret);
- if (r >= 0)
+ r = unit_name_from_path(n, ".mount", &u);
+ if (r >= 0) {
+ if (FLAGS_SET(flags, UNIT_NAME_MANGLE_STRICT) && !streq(suffix, ".mount"))
+ return log_full_errno(warn ? LOG_NOTICE : LOG_DEBUG,
+ SYNTHETIC_ERRNO(EINVAL),
+ "Path \"%s\" resolves to unit type \"mount\", but \"%s\" is expected%s%s.",
+ name, suffix + 1,
+ operation ? " " : "", strempty(operation));
+
+ *ret = TAKE_PTR(u);
return 1;
+ }
if (r != -EINVAL)
return r;
}
int unit_name_to_path(const char *name, char **ret);
typedef enum UnitNameMangle {
- UNIT_NAME_MANGLE_GLOB = 1 << 0,
- UNIT_NAME_MANGLE_WARN = 1 << 1,
+ UNIT_NAME_MANGLE_GLOB = 1 << 0,
+ UNIT_NAME_MANGLE_WARN = 1 << 1,
+ UNIT_NAME_MANGLE_STRICT = 1 << 2, /* Refuse if the resolved unit type doesn't match the requested suffix */
} UnitNameMangle;
int unit_name_mangle_with_suffix(const char *name, const char *operation, UnitNameMangle flags, const char *suffix, char **ret);
r = unit_name_mangle_with_suffix(
arg_unit,
"as unit",
- arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN) | UNIT_NAME_MANGLE_STRICT,
".service",
&c.unit);
if (r < 0)
if (arg_unit) {
r = unit_name_mangle_with_suffix(arg_unit, "as unit",
- arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN) | UNIT_NAME_MANGLE_STRICT,
".scope", &scope);
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
default:
r = unit_name_mangle_with_suffix(arg_unit, "as unit",
- arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN) | UNIT_NAME_MANGLE_STRICT,
".service", &service);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
r = unit_name_mangle_with_suffix(arg_unit, "as trigger",
- arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN) | UNIT_NAME_MANGLE_STRICT,
suffix, &trigger);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
ASSERT_STREQ(s, expected_name);
}
+static void test_unit_name_mangle_with_suffix_strict_one(
+ const char *arg, const char *suffix, int expected, const char *expected_name) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ r = unit_name_mangle_with_suffix(arg, NULL, UNIT_NAME_MANGLE_WARN | UNIT_NAME_MANGLE_STRICT, suffix, &s);
+ log_debug("%s: %s (suffix=%s) -> %d, %s", __func__, arg, suffix, r, strnull(s));
+
+ assert_se(r == expected);
+ ASSERT_STREQ(s, expected_name);
+}
+
TEST(unit_name_mangle_with_suffix) {
test_unit_name_mangle_with_suffix_one("", -EINVAL, NULL);
test_unit_name_mangle_with_suffix_one("/./.././../proc/", 1, "proc.mount");
}
+TEST(unit_name_mangle_with_suffix_strict) {
+ /* Matching suffix should succeed */
+ test_unit_name_mangle_with_suffix_strict_one("foo.service", ".service", 0, "foo.service");
+ test_unit_name_mangle_with_suffix_strict_one("foo.mount", ".mount", 0, "foo.mount");
+ test_unit_name_mangle_with_suffix_strict_one("/home", ".mount", 1, "home.mount");
+ test_unit_name_mangle_with_suffix_strict_one("/dev/sda", ".device", 1, "dev-sda.device");
+ test_unit_name_mangle_with_suffix_strict_one("foo@bar.service", ".service", 0, "foo@bar.service");
+ test_unit_name_mangle_with_suffix_strict_one("a.timer.service", ".service", 0, "a.timer.service");
+
+ /* Mismatched suffix should fail with -EINVAL */
+ test_unit_name_mangle_with_suffix_strict_one("foo.mount", ".service", -EINVAL, NULL);
+ test_unit_name_mangle_with_suffix_strict_one("foo.service", ".scope", -EINVAL, NULL);
+ test_unit_name_mangle_with_suffix_strict_one("/home", ".service", -EINVAL, NULL);
+ test_unit_name_mangle_with_suffix_strict_one("/dev/sda", ".service", -EINVAL, NULL);
+ test_unit_name_mangle_with_suffix_strict_one("foo.automount", ".mount", -EINVAL, NULL);
+ test_unit_name_mangle_with_suffix_strict_one("foo.mount", ".automount", -EINVAL, NULL);
+ test_unit_name_mangle_with_suffix_strict_one("foo@bar.timer", ".service", -EINVAL, NULL);
+
+ /* Non-path names that need mangling should get the requested suffix and thus pass */
+ test_unit_name_mangle_with_suffix_strict_one("foo", ".service", 1, "foo.service");
+}
+
TEST_RET(unit_printf, .sd_booted = true) {
_cleanup_free_ char
*architecture, *os_image_version, *boot_id = NULL, *os_build_id,