if (path_alias) {
/* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
_cleanup_free_ char *dir = NULL;
+ char *p;
path_alias ++; /* skip over slash */
if (!dir)
return log_oom();
- if (!endswith(dir, ".wants") && !endswith(dir, ".requires"))
+ p = endswith(dir, ".wants");
+ if (!p)
+ p = endswith(dir, ".requires");
+ if (!p)
return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
"Invalid path \"%s\" in alias.", dir);
+ *p = '\0'; /* dir should now be a unit name */
+
+ r = unit_name_classify(dir);
+ if (r < 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
+ "Invalid unit name component \"%s\" in alias.", dir);
+
+ const bool instance_propagation = r == UNIT_NAME_TEMPLATE;
/* That's the name we want to use for verification. */
- r = unit_symlink_name_compatible(path_alias, i->name);
+ r = unit_symlink_name_compatible(path_alias, i->name, instance_propagation);
if (r < 0)
return log_error_errno(r, "Failed to verify alias validity: %m");
if (r == 0)
UNIT_PATH);
}
-int unit_symlink_name_compatible(const char *symlink, const char *target) {
+int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
_cleanup_free_ char *template = NULL;
- int r;
+ int r, un_type1, un_type2;
+
+ un_type1 = unit_name_classify(symlink);
- /* The straightforward case: the symlink name matches the target */
- if (streq(symlink, target))
+ /* The straightforward case: the symlink name matches the target and we have a valid unit */
+ if (streq(symlink, target) &&
+ (un_type1 & (UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)))
return 1;
r = unit_name_template(symlink, &template);
if (r < 0)
return r;
+ un_type2 = unit_name_classify(target);
+
/* An instance name points to a target that is just the template name */
- return streq(template, target);
+ if (un_type1 == UNIT_NAME_INSTANCE &&
+ un_type2 == UNIT_NAME_TEMPLATE &&
+ streq(template, target))
+ return 1;
+
+ /* foo@.target.requires/bar@.service: instance will be propagated */
+ if (instance_propagation &&
+ un_type1 == UNIT_NAME_TEMPLATE &&
+ un_type2 == UNIT_NAME_TEMPLATE &&
+ streq(template, target))
+ return 1;
+
+ return 0;
}
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) {
verify_one(&plain_service, "foo.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL);
+ verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */
+ verify_one(&plain_service, "asdf.requires/plain.service", -EXDEV, NULL); /* invalid unit name component */
verify_one(&bare_template, "alias.service", -EXDEV, NULL);
verify_one(&bare_template, "alias.socket", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.requires/template1@inst.service", 0, NULL);
verify_one(&bare_template, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&bare_template, "foo.target.conf/plain.service", -EXDEV, NULL);
+ verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */
+ verify_one(&bare_template, "FOO@inst.target.requires/plain@.service", -EXDEV, NULL);
+ verify_one(&bare_template, "FOO@inst.target.requires/plain@inst.service", -EXDEV, NULL);
+ verify_one(&bare_template, "FOO@.target.requires/template1@.service", 0, NULL); /* instance propagated */
+ verify_one(&bare_template, "FOO@inst.target.requires/template1@.service", -EXDEV, NULL); /* instance missing */
+ verify_one(&bare_template, "FOO@inst.target.requires/template1@inst.service", 0, NULL); /* instance provided */
verify_one(&di_template, "alias.service", -EXDEV, NULL);
verify_one(&di_template, "alias.socket", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain@di.service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.requires/plain@foo.service", -EXDEV, NULL);
+ verify_one(&di_template, "foo.target.requires/template2@.service", -EXDEV, NULL); /* instance missing */
verify_one(&di_template, "foo.target.requires/template2@di.service", 0, NULL);
verify_one(&di_template, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&di_template, "foo.target.conf/plain.service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.requires/template3@inst.service", 0, NULL);
verify_one(&inst_template, "bar.target.requires/service", -EXDEV, NULL);
verify_one(&inst_template, "bar.target.conf/plain.service", -EXDEV, NULL);
+ verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */
+ verify_one(&inst_template, "BAR@inst.target.requires/plain@.service", -EXDEV, NULL);
+ verify_one(&inst_template, "BAR@inst.target.requires/plain@inst.service", -EXDEV, NULL);
+ verify_one(&inst_template, "BAR@.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
+ verify_one(&inst_template, "BAR@inst.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
+ verify_one(&inst_template, "BAR@inst.target.requires/template3@inst.service", 0, NULL); /* instance provided */
+ verify_one(&inst_template, "BAR@inst.target.requires/template3@ins2.service", -EXDEV, NULL); /* instance mismatch */
/* explicit alias overrides DefaultInstance */
verify_one(&di_inst_template, "alias.service", -EXDEV, NULL);