}
/**
- * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
- * wc should be the full path in the host file system.
+ * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
+ * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
+ * If outside the unit search path, paths must be identical.
*/
-static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
- assert(path_is_absolute(wd));
+static int chroot_unit_symlinks_equivalent(
+ const LookupPaths *lp,
+ const char *src,
+ const char *target_a,
+ const char *target_b) {
+
+ assert(lp);
+ assert(src);
+ assert(target_a);
+ assert(target_b);
/* This will give incorrect results if the paths are relative and go outside
* of the chroot. False negatives are possible. */
- if (!root)
- root = "/";
+ const char *root = lp->root_dir ?: "/";
+ _cleanup_free_ char *dirname = NULL;
+ int r;
+
+ if (!path_is_absolute(target_a) || !path_is_absolute(target_b)) {
+ r = path_extract_directory(src, &dirname);
+ if (r < 0)
+ return r;
+ }
- a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
- b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
- return path_equal_or_files_same(a, b, 0);
+ _cleanup_free_ char *a = path_join(path_is_absolute(target_a) ? root : dirname, target_a);
+ _cleanup_free_ char *b = path_join(path_is_absolute(target_b) ? root : dirname, target_b);
+ if (!a || !b)
+ return log_oom();
+
+ r = path_equal_or_files_same(a, b, 0);
+ if (r != 0)
+ return r;
+
+ _cleanup_free_ char *a_name = NULL, *b_name = NULL;
+ r = path_extract_filename(a, &a_name);
+ if (r < 0)
+ return r;
+ r = path_extract_filename(b, &b_name);
+ if (r < 0)
+ return r;
+
+ return streq(a_name, b_name) &&
+ path_startswith_strv(a, lp->search_path) &&
+ path_startswith_strv(b, lp->search_path);
}
static int create_symlink(
UnitFileChange **changes,
size_t *n_changes) {
- _cleanup_free_ char *dest = NULL, *dirname = NULL;
+ _cleanup_free_ char *dest = NULL;
const char *rp;
int r;
return r;
}
- dirname = dirname_malloc(new_path);
- if (!dirname)
- return -ENOMEM;
-
- if (chroot_symlinks_same(lp->root_dir, dirname, dest, old_path)) {
+ if (chroot_unit_symlinks_equivalent(lp, new_path, dest, old_path)) {
log_debug("Symlink %s → %s already exists", new_path, dest);
return 1;
}
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" disable test1.service
-test ! -e "$root/etc/systemd/system/default.target.wants/test1.service"
-test ! -e "$root/etc/systemd/system/special.target.requires/test1.service"
+test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
+test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
+
+: '------enable when link already exists-----------------------'
+# We don't read the symlink target, so it's OK for the symlink to point
+# to something else. We should just silently accept this.
+
+mkdir -p "$root/etc/systemd/system/default.target.wants"
+mkdir -p "$root/etc/systemd/system/special.target.requires"
+ln -s /usr/lib/systemd/system/test1.service "$root/etc/systemd/system/default.target.wants/test1.service"
+ln -s /usr/lib/systemd/system/test1.service "$root/etc/systemd/system/special.target.requires/test1.service"
+
+"$systemctl" --root="$root" enable test1.service
+test -h "$root/etc/systemd/system/default.target.wants/test1.service"
+test -h "$root/etc/systemd/system/special.target.requires/test1.service"
+
+"$systemctl" --root="$root" reenable test1.service
+test -h "$root/etc/systemd/system/default.target.wants/test1.service"
+test -h "$root/etc/systemd/system/special.target.requires/test1.service"
+
+"$systemctl" --root="$root" disable test1.service
+test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
+test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
: '------suffix guessing---------------------------------------'
"$systemctl" --root="$root" enable test1
test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
test ! -h "$root/etc/systemd/system/test1-goodalias.service"
+: '-------aliases when link already exists---------------------'
+cat >"$root/etc/systemd/system/test1a.service" <<EOF
+[Install]
+Alias=test1a-alias.service
+EOF
+
+ln -s /usr/lib/systemd/system/test1a.service "$root/etc/systemd/system/test1a-alias.service"
+
+"$systemctl" --root="$root" enable test1a.service
+test -h "$root/etc/systemd/system/test1a-alias.service"
+
+"$systemctl" --root="$root" disable test1a.service
+test ! -h "$root/etc/systemd/system/test1a-alias.service"
+
: '-------also units-------------------------------------------'
cat >"$root/etc/systemd/system/test2.socket" <<EOF
[Install]