} SearchFlags;
typedef struct {
- UnitFileScope scope;
+ LookupScope scope;
OrderedHashmap *will_process;
OrderedHashmap *have_processed;
} InstallContext;
static const char *const unit_file_type_table[_UNIT_FILE_TYPE_MAX] = {
[UNIT_FILE_TYPE_REGULAR] = "regular",
- [UNIT_FILE_TYPE_SYMLINK] = "symlink",
- [UNIT_FILE_TYPE_MASKED] = "masked",
+ [UNIT_FILE_TYPE_LINKED] = "linked",
+ [UNIT_FILE_TYPE_ALIAS] = "alias",
+ [UNIT_FILE_TYPE_MASKED] = "masked",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType);
static int in_search_path(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_strv_contains(lp->search_path, parent);
}
static int path_is_generator(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_equal_ptr(parent, lp->generator) ||
path_equal_ptr(parent, lp->generator_early) ||
static int path_is_transient(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_equal_ptr(parent, lp->transient);
}
static int path_is_control(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_equal_ptr(parent, lp->persistent_control) ||
path_equal_ptr(parent, lp->runtime_control);
static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
* them we couldn't discern configuration from transient or generated units */
if (check_parent) {
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
path = parent;
}
static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
const char *rpath;
+ int r;
assert(lp);
assert(path);
return true;
if (check_parent) {
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
path = parent;
}
for (size_t i = 0; i < n_changes; i++) {
assert(verb || changes[i].type_or_errno >= 0);
- switch(changes[i].type_or_errno) {
+ switch (changes[i].type_or_errno) {
case UNIT_FILE_SYMLINK:
if (!quiet)
log_info("Created symlink %s %s %s.",
}
/**
- * 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)) {
- log_debug("Symlink %s → %s already exists", new_path, dest);
+ if (chroot_unit_symlinks_equivalent(lp, new_path, dest, old_path)) {
+ log_debug("Symlink %s %s %s already exists",
+ new_path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dest);
return 1;
}
r = q;
} else if (de->d_type == DT_LNK) {
- _cleanup_free_ char *p = NULL, *dest = NULL;
- const char *rp;
+ _cleanup_free_ char *p = NULL;
bool found;
int q;
return -ENOMEM;
path_simplify(p);
- q = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &dest, NULL);
- if (q == -ENOENT)
- continue;
- if (q < 0) {
- if (r == 0)
- r = q;
- continue;
+ /* We remove all links pointing to a file or path that is marked, as well as all
+ * files sharing the same name as a file that is marked, and files sharing the same
+ * name after the instance has been removed. Do path chasing only if we don't already
+ * know that we want to remove the symlink. */
+ found = set_contains(remove_symlinks_to, de->d_name);
+
+ if (!found) {
+ _cleanup_free_ char *template = NULL;
+
+ q = unit_name_template(de->d_name, &template);
+ if (q < 0 && q != -EINVAL)
+ return q;
+ if (q >= 0)
+ found = set_contains(remove_symlinks_to, template);
}
- /* We remove all links pointing to a file or path that is marked, as well as all files sharing
- * the same name as a file that is marked. */
+ if (!found) {
+ _cleanup_free_ char *dest = NULL;
+
+ q = chase_symlinks(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL);
+ if (q == -ENOENT)
+ continue;
+ if (q < 0) {
+ log_debug_errno(q, "Failed to resolve symlink \"%s\": %m", p);
+ unit_file_changes_add(changes, n_changes, q, p, NULL);
+
+ if (r == 0)
+ r = q;
+ continue;
+ }
+
+ found = set_contains(remove_symlinks_to, dest) ||
+ set_contains(remove_symlinks_to, basename(dest));
+
+ }
- found = set_contains(remove_symlinks_to, dest) ||
- set_contains(remove_symlinks_to, basename(dest)) ||
- set_contains(remove_symlinks_to, de->d_name);
if (!found)
continue;
/* Now, remember the full path (but with the root prefix removed) of
* the symlink we just removed, and remove any symlinks to it, too. */
- rp = skip_root(lp->root_dir, p);
+ const char *rp = skip_root(lp->root_dir, p);
q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
if (q < 0)
return q;
const char *dir_path,
const char *root_dir,
const UnitFileInstallInfo *info,
- bool match_aliases,
+ bool ignore_destination,
+ bool match_name,
bool ignore_same_name,
const char *config_path,
bool *same_name_link) {
int r = 0;
FOREACH_DIRENT(de, dir, return -errno) {
- _cleanup_free_ char *dest = NULL;
- bool found_path = false, found_dest, b = false;
+ bool found_path = false, found_dest = false, b = false;
int q;
if (de->d_type != DT_LNK)
continue;
- /* Acquire symlink destination */
- q = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
- if (q == -ENOENT)
- continue;
- if (q < 0) {
- if (r == 0)
- r = q;
- continue;
- }
+ if (!ignore_destination) {
+ _cleanup_free_ char *dest = NULL;
- /* Make absolute */
- if (!path_is_absolute(dest)) {
- char *x;
+ /* Acquire symlink destination */
+ q = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
+ if (q == -ENOENT)
+ continue;
+ if (q < 0) {
+ if (r == 0)
+ r = q;
+ continue;
+ }
- x = path_join(dir_path, dest);
- if (!x)
- return -ENOMEM;
+ /* Make absolute */
+ if (!path_is_absolute(dest)) {
+ char *x;
+
+ x = path_join(dir_path, dest);
+ if (!x)
+ return -ENOMEM;
+
+ free_and_replace(dest, x);
+ }
- free_and_replace(dest, x);
+ /* Check if what the symlink points to matches what we are looking for */
+ found_dest = streq(basename(dest), info->name);
}
assert(unit_name_is_valid(info->name, UNIT_NAME_ANY));
- if (!ignore_same_name)
- /* Check if the symlink itself matches what we are looking for.
- *
- * If ignore_same_name is specified, we are in one of the directories which
- * have lower priority than the unit file, and even if a file or symlink with
- * this name was found, we should ignore it. */
- found_path = streq(de->d_name, info->name);
- /* Check if what the symlink points to matches what we are looking for */
- found_dest = streq(basename(dest), info->name);
+ /* Check if the symlink itself matches what we are looking for.
+ *
+ * If ignore_destination is specified, we only look at the source name.
+ *
+ * If ignore_same_name is specified, we are in one of the directories which
+ * have lower priority than the unit file, and even if a file or symlink with
+ * this name was found, we should ignore it. */
+
+ if (ignore_destination || !ignore_same_name)
+ found_path = streq(de->d_name, info->name);
+
+ if (!found_path && ignore_destination) {
+ _cleanup_free_ char *template = NULL;
+
+ q = unit_name_template(de->d_name, &template);
+ if (q < 0 && q != -EINVAL)
+ return q;
+ if (q >= 0)
+ found_dest = streq(template, info->name);
+ }
if (found_path && found_dest) {
_cleanup_free_ char *p = NULL, *t = NULL;
- /* Filter out same name links in the main
- * config path */
+ /* Filter out same name links in the main config path */
p = path_make_absolute(de->d_name, dir_path);
t = path_make_absolute(info->name, config_path);
if (b)
*same_name_link = true;
else if (found_path || found_dest) {
- if (!match_aliases)
+ if (!match_name)
return 1;
/* Check if symlink name is in the set of names used by [Install] */
continue;
}
- r = find_symlinks_in_directory(d, path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link);
+ r = find_symlinks_in_directory(d, path, root_dir, i,
+ /* ignore_destination= */ true,
+ /* match_name= */ match_name,
+ /* ignore_same_name= */ ignore_same_name,
+ config_path,
+ same_name_link);
if (r > 0)
return 1;
else if (r < 0)
/* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */
rewinddir(config_dir);
- return find_symlinks_in_directory(config_dir, config_path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link);
+ return find_symlinks_in_directory(config_dir, config_path, root_dir, i,
+ /* ignore_destination= */ false,
+ /* match_name= */ match_name,
+ /* ignore_same_name= */ ignore_same_name,
+ config_path,
+ same_name_link);
}
static int find_symlinks_in_scope(
- UnitFileScope scope,
+ LookupScope scope,
const LookupPaths *lp,
const UnitFileInstallInfo *info,
bool match_name,
}
/* look for global enablement of user units */
- if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) {
+ if (scope == LOOKUP_SCOPE_USER && path_is_user_config_dir(*p)) {
*state = UNIT_FILE_ENABLED;
return 1;
}
if (r == 0)
break;
- r = install_name_printf(ctx->scope, info, word, info->root, &printed);
+ r = install_name_printf(ctx->scope, info, word, &printed);
if (r < 0)
return log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to resolve unit name in Also=\"%s\": %m", word);
return log_syntax(unit, LOG_WARNING, filename, line, 0,
"DefaultInstance= only makes sense for template units, ignoring.");
- r = install_name_printf(ctx->scope, info, rvalue, info->root, &printed);
+ r = install_name_printf(ctx->scope, info, rvalue, &printed);
if (r < 0)
return log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue);
true, &info->symlink_target);
if (r < 0)
return r;
+ bool outside_search_path = r > 0;
r = null_or_empty_path_with_root(info->symlink_target, lp->root_dir);
if (r < 0 && r != -ENOENT)
return log_debug_errno(r, "Failed to stat %s: %m", info->symlink_target);
if (r > 0)
info->type = UNIT_FILE_TYPE_MASKED;
+ else if (outside_search_path)
+ info->type = UNIT_FILE_TYPE_LINKED;
else
- info->type = UNIT_FILE_TYPE_SYMLINK;
+ info->type = UNIT_FILE_TYPE_ALIAS;
return 0;
}
assert(ctx);
assert(info);
- if (info->type != UNIT_FILE_TYPE_SYMLINK)
+ if (!IN_SET(info->type, UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_LINKED))
return -EINVAL;
if (!info->symlink_target)
return -EINVAL;
return r;
i = start;
- while (i->type == UNIT_FILE_TYPE_SYMLINK) {
+ while (IN_SET(i->type, UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_LINKED)) {
/* Follow the symlink */
if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
return -ELOOP;
}
- r = install_info_follow(ctx, i, lp, flags, false);
+ r = install_info_follow(ctx, i, lp, flags,
+ /* If linked, don't look at the target name */
+ /* ignore_different_name= */ i->type == UNIT_FILE_TYPE_LINKED);
if (r == -EXDEV) {
_cleanup_free_ char *buffer = NULL;
const char *bn;
static int install_info_discover(
InstallContext *ctx,
const LookupPaths *lp,
- const char *name,
+ const char *name_or_path,
SearchFlags flags,
UnitFileInstallInfo **ret,
UnitFileChange **changes,
assert(ctx);
assert(lp);
- assert(name);
+ assert(name_or_path);
- r = install_info_add_auto(ctx, lp, name, &info);
+ r = install_info_add_auto(ctx, lp, name_or_path, &info);
if (r >= 0)
r = install_info_traverse(ctx, lp, info, flags, ret);
if (r < 0)
- unit_file_changes_add(changes, n_changes, r, name, NULL);
+ unit_file_changes_add(changes, n_changes, r, name_or_path, NULL);
return r;
}
static int install_info_discover_and_check(
InstallContext *ctx,
const LookupPaths *lp,
- const char *name,
+ const char *name_or_path,
SearchFlags flags,
UnitFileInstallInfo **ret,
UnitFileChange **changes,
int r;
- r = install_info_discover(ctx, lp, name, flags, ret, changes, n_changes);
+ r = install_info_discover(ctx, lp, name_or_path, flags, ret, changes, n_changes);
if (r < 0)
return r;
path_alias ++; /* skip over slash */
- dir = dirname_malloc(dst);
- if (!dir)
- return log_oom();
+ r = path_extract_directory(dst, &dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract parent directory from '%s': %m", dst);
p = endswith(dir, ".wants");
if (!p)
}
static int install_info_symlink_alias(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileInstallInfo *info,
const LookupPaths *lp,
const char *config_path,
STRV_FOREACH(s, info->aliases) {
_cleanup_free_ char *alias_path = NULL, *dst = NULL, *dst_updated = NULL;
- q = install_name_printf(scope, info, *s, info->root, &dst);
+ q = install_name_printf(scope, info, *s, &dst);
if (q < 0) {
unit_file_changes_add(changes, n_changes, q, *s, NULL);
r = r < 0 ? r : q;
}
static int install_info_symlink_wants(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags file_flags,
UnitFileInstallInfo *info,
const LookupPaths *lp,
STRV_FOREACH(s, list) {
_cleanup_free_ char *path = NULL, *dst = NULL;
- q = install_name_printf(scope, info, *s, info->root, &dst);
+ q = install_name_printf(scope, info, *s, &dst);
if (q < 0) {
unit_file_changes_add(changes, n_changes, q, *s, NULL);
return q;
}
static int install_info_apply(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags file_flags,
UnitFileInstallInfo *info,
const LookupPaths *lp,
}
int unit_file_mask(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags flags,
const char *root_dir,
- char **files,
+ char **names,
UnitFileChange **changes,
size_t *n_changes) {
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
r = lookup_paths_init(&lp, scope, 0, root_dir);
if (r < 0)
if (!config_path)
return -ENXIO;
- STRV_FOREACH(i, files) {
+ STRV_FOREACH(name, names) {
_cleanup_free_ char *path = NULL;
int q;
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
+ if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
}
- path = path_make_absolute(*i, config_path);
+ path = path_make_absolute(*name, config_path);
if (!path)
return -ENOMEM;
}
int unit_file_unmask(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags flags,
const char *root_dir,
- char **files,
+ char **names,
UnitFileChange **changes,
size_t *n_changes) {
int r, q;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
r = lookup_paths_init(&lp, scope, 0, root_dir);
if (r < 0)
bool dry_run = flags & UNIT_FILE_DRY_RUN;
- STRV_FOREACH(i, files) {
+ STRV_FOREACH(name, names) {
_cleanup_free_ char *path = NULL;
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
+ if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
return -EINVAL;
- path = path_make_absolute(*i, config_path);
+ path = path_make_absolute(*name, config_path);
if (!path)
return -ENOMEM;
if (!GREEDY_REALLOC0(todo, n_todo + 2))
return -ENOMEM;
- todo[n_todo] = strdup(*i);
+ todo[n_todo] = strdup(*name);
if (!todo[n_todo])
return -ENOMEM;
}
int unit_file_link(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags flags,
const char *root_dir,
char **files,
int r, q;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
r = lookup_paths_init(&lp, scope, 0, root_dir);
if (r < 0)
if (!config_path)
return -ENXIO;
- STRV_FOREACH(i, files) {
+ STRV_FOREACH(file, files) {
_cleanup_free_ char *full = NULL;
struct stat st;
char *fn;
- if (!path_is_absolute(*i))
+ if (!path_is_absolute(*file))
return -EINVAL;
- fn = basename(*i);
+ fn = basename(*file);
if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
return -EINVAL;
- full = path_join(lp.root_dir, *i);
+ full = path_join(lp.root_dir, *file);
if (!full)
return -ENOMEM;
if (r < 0)
return r;
- q = in_search_path(&lp, *i);
+ q = in_search_path(&lp, *file);
if (q < 0)
return q;
if (q > 0)
if (!GREEDY_REALLOC0(todo, n_todo + 2))
return -ENOMEM;
- todo[n_todo] = strdup(*i);
+ todo[n_todo] = strdup(*file);
if (!todo[n_todo])
return -ENOMEM;
}
int unit_file_revert(
- UnitFileScope scope,
+ LookupScope scope,
const char *root_dir,
- char **files,
+ char **names,
UnitFileChange **changes,
size_t *n_changes) {
/* Puts a unit file back into vendor state. This means:
*
- * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
- * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
+ * a) we remove all drop-in snippets added by the user ("config"), add to transient units
+ * ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
+ * generated ("generated").
*
- * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
- * "config", but not in "transient" or "control" or even "generated").
+ * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
+ * (i.e. in "config", but not in "transient" or "control" or even "generated").
*
* We remove all that in both the runtime and the persistent directories, if that applies.
*/
if (r < 0)
return r;
- STRV_FOREACH(i, files) {
+ STRV_FOREACH(name, names) {
bool has_vendor = false;
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
+ if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
return -EINVAL;
STRV_FOREACH(p, lp.search_path) {
_cleanup_free_ char *path = NULL, *dropin = NULL;
struct stat st;
- path = path_make_absolute(*i, *p);
+ path = path_make_absolute(*name, *p);
if (!path)
return -ENOMEM;
_cleanup_free_ char *path = NULL;
struct stat st;
- path = path_make_absolute(*i, *p);
+ path = path_make_absolute(*name, *p);
if (!path)
return -ENOMEM;
}
int unit_file_add_dependency(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags file_flags,
const char *root_dir,
- char **files,
+ char **names,
const char *target,
UnitDependency dep,
UnitFileChange **changes,
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(target);
if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
- STRV_FOREACH(f, files) {
+ STRV_FOREACH(name, names) {
char ***l;
- r = install_info_discover_and_check(&ctx, &lp, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ r = install_info_discover_and_check(&ctx, &lp, *name,
+ SEARCH_FOLLOW_CONFIG_SYMLINKS,
&info, changes, n_changes);
if (r < 0)
return r;
SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
}
-int unit_file_enable(
- UnitFileScope scope,
- UnitFileFlags file_flags,
- const char *root_dir,
- char **files,
+static int do_unit_file_enable(
+ const LookupPaths *lp,
+ LookupScope scope,
+ UnitFileFlags flags,
+ const char *config_path,
+ char **names_or_paths,
UnitFileChange **changes,
size_t *n_changes) {
- _cleanup_(lookup_paths_free) LookupPaths lp = {};
_cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
- const char *config_path;
UnitFileInstallInfo *info;
int r;
- assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
-
- r = lookup_paths_init_or_warn(&lp, scope, 0, root_dir);
- if (r < 0)
- return r;
-
- config_path = config_path_from_flags(&lp, file_flags);
- if (!config_path)
- return -ENXIO;
-
- STRV_FOREACH(f, files) {
- r = install_info_discover_and_check(&ctx, &lp, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ STRV_FOREACH(name, names_or_paths) {
+ r = install_info_discover_and_check(&ctx, lp, *name,
+ SEARCH_LOAD | SEARCH_FOLLOW_CONFIG_SYMLINKS,
&info, changes, n_changes);
if (r < 0)
return r;
/* This will return the number of symlink rules that were
supposed to be created, not the ones actually created. This
- is useful to determine whether the passed files had any
+ is useful to determine whether the passed units had any
installation data at all. */
- return install_context_apply(&ctx, &lp, file_flags, config_path,
+ return install_context_apply(&ctx, lp, flags, config_path,
SEARCH_LOAD, changes, n_changes);
}
-int unit_file_disable(
- UnitFileScope scope,
+int unit_file_enable(
+ LookupScope scope,
UnitFileFlags flags,
const char *root_dir,
- char **files,
+ char **names_or_paths,
UnitFileChange **changes,
size_t *n_changes) {
_cleanup_(lookup_paths_free) LookupPaths lp = {};
- _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
- _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- const char *config_path;
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
r = lookup_paths_init(&lp, scope, 0, root_dir);
if (r < 0)
return r;
- config_path = config_path_from_flags(&lp, flags);
+ const char *config_path = config_path_from_flags(&lp, flags);
if (!config_path)
return -ENXIO;
- STRV_FOREACH(i, files) {
- if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
+ return do_unit_file_enable(&lp, scope, flags, config_path, names_or_paths, changes, n_changes);
+}
+
+static int do_unit_file_disable(
+ const LookupPaths *lp,
+ LookupScope scope,
+ UnitFileFlags flags,
+ const char *config_path,
+ char **names,
+ UnitFileChange **changes,
+ size_t *n_changes) {
+
+ _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+ int r;
+
+ STRV_FOREACH(name, names) {
+ if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
return -EINVAL;
- r = install_info_add(&ctx, *i, NULL, lp.root_dir, /* auxiliary= */ false, NULL);
+ r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, NULL);
if (r < 0)
return r;
}
- r = install_context_mark_for_removal(&ctx, &lp, &remove_symlinks_to, config_path, changes, n_changes);
+ r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes);
if (r < 0)
return r;
- return remove_marked_symlinks(remove_symlinks_to, config_path, &lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
+ return remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
}
-int unit_file_reenable(
- UnitFileScope scope,
+
+int unit_file_disable(
+ LookupScope scope,
UnitFileFlags flags,
const char *root_dir,
char **files,
UnitFileChange **changes,
size_t *n_changes) {
- char **n;
+ _cleanup_(lookup_paths_free) LookupPaths lp = {};
int r;
- size_t l, i;
- /* First, we invoke the disable command with only the basename... */
- l = strv_length(files);
- n = newa(char*, l+1);
- for (i = 0; i < l; i++)
- n[i] = basename(files[i]);
- n[i] = NULL;
+ assert(scope >= 0);
+ assert(scope < _LOOKUP_SCOPE_MAX);
- r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes);
+ r = lookup_paths_init(&lp, scope, 0, root_dir);
+ if (r < 0)
+ return r;
+
+ const char *config_path = config_path_from_flags(&lp, flags);
+ if (!config_path)
+ return -ENXIO;
+
+ return do_unit_file_disable(&lp, scope, flags, config_path, files, changes, n_changes);
+}
+
+static int normalize_linked_files(
+ LookupScope scope,
+ const LookupPaths *lp,
+ char **names_or_paths,
+ char ***ret_names,
+ char ***ret_files) {
+
+ /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
+ * but operates on real unit names. For each argument we we look up the actual path
+ * where the unit is found. This way linked units can be re-enabled successfully. */
+
+ _cleanup_strv_free_ char **files = NULL, **names = NULL;
+ int r;
+
+ STRV_FOREACH(a, names_or_paths) {
+ _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
+ UnitFileInstallInfo *i = NULL;
+ _cleanup_free_ char *n = NULL;
+
+ r = path_extract_filename(*a, &n);
+ if (r < 0)
+ return r;
+ if (r == O_DIRECTORY)
+ return log_debug_errno(SYNTHETIC_ERRNO(EISDIR),
+ "Unexpected path to a directory \"%s\", refusing.", *a);
+
+ if (!is_path(*a)) {
+ r = install_info_discover(&ctx, lp, n, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i, NULL, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to discover unit \"%s\", operating on name: %m", n);
+ }
+
+ r = strv_consume(&names, TAKE_PTR(n));
+ if (r < 0)
+ return r;
+
+ const char *p = NULL;
+ if (i && i->path && i->root)
+ /* Use startswith here, because we know that paths are normalized, and
+ * path_startswith() would give us a relative path, but we need an absolute path
+ * relative to i->root.
+ *
+ * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
+ * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
+ * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
+ */
+ p = startswith(i->path, i->root);
+
+ r = strv_extend(&files, p ?: *a);
+ if (r < 0)
+ return r;
+ }
+
+ *ret_names = TAKE_PTR(names);
+ *ret_files = TAKE_PTR(files);
+ return 0;
+}
+
+int unit_file_reenable(
+ LookupScope scope,
+ UnitFileFlags flags,
+ const char *root_dir,
+ char **names_or_paths,
+ UnitFileChange **changes,
+ size_t *n_changes) {
+
+ _cleanup_(lookup_paths_free) LookupPaths lp = {};
+ _cleanup_strv_free_ char **names = NULL, **files = NULL;
+ int r;
+
+ assert(scope >= 0);
+ assert(scope < _LOOKUP_SCOPE_MAX);
+
+ r = lookup_paths_init(&lp, scope, 0, root_dir);
+ if (r < 0)
+ return r;
+
+ const char *config_path = config_path_from_flags(&lp, flags);
+ if (!config_path)
+ return -ENXIO;
+
+ r = normalize_linked_files(scope, &lp, names_or_paths, &names, &files);
+ if (r < 0)
+ return r;
+
+ /* First, we invoke the disable command with only the basename... */
+ r = do_unit_file_disable(&lp, scope, flags, config_path, names, changes, n_changes);
if (r < 0)
return r;
/* But the enable command with the full name */
- return unit_file_enable(scope, flags, root_dir, files, changes, n_changes);
+ return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes);
}
int unit_file_set_default(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags flags,
const char *root_dir,
const char *name,
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(name);
if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
}
int unit_file_get_default(
- UnitFileScope scope,
+ LookupScope scope,
const char *root_dir,
char **name) {
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(name);
r = lookup_paths_init(&lp, scope, 0, root_dir);
}
int unit_file_lookup_state(
- UnitFileScope scope,
+ LookupScope scope,
const LookupPaths *lp,
const char *name,
UnitFileState *ret) {
}
int unit_file_get_state(
- UnitFileScope scope,
+ LookupScope scope,
const char *root_dir,
const char *name,
UnitFileState *ret) {
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(name);
r = lookup_paths_init(&lp, scope, 0, root_dir);
return unit_file_lookup_state(scope, &lp, name, ret);
}
-int unit_file_exists(UnitFileScope scope, const LookupPaths *lp, const char *name) {
+int unit_file_exists(LookupScope scope, const LookupPaths *lp, const char *name) {
_cleanup_(install_context_done) InstallContext c = { .scope = scope };
int r;
return 0;
}
-static int presets_find_config(UnitFileScope scope, const char *root_dir, char ***files) {
+static int presets_find_config(LookupScope scope, const char *root_dir, char ***files) {
static const char* const system_dirs[] = {CONF_PATHS("systemd/system-preset"), NULL};
static const char* const user_dirs[] = {CONF_PATHS_USR("systemd/user-preset"), NULL};
const char* const* dirs;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
- if (scope == UNIT_FILE_SYSTEM)
+ if (scope == LOOKUP_SCOPE_SYSTEM)
dirs = system_dirs;
- else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER))
+ else if (IN_SET(scope, LOOKUP_SCOPE_GLOBAL, LOOKUP_SCOPE_USER))
dirs = user_dirs;
else
assert_not_reached();
return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
}
-static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePresets *presets) {
+static int read_presets(LookupScope scope, const char *root_dir, UnitFilePresets *presets) {
_cleanup_(unit_file_presets_freep) UnitFilePresets ps = {};
_cleanup_strv_free_ char **files = NULL;
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(presets);
r = presets_find_config(scope, root_dir, &files);
}
}
-int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
+int unit_file_query_preset(LookupScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
_cleanup_(unit_file_presets_freep) UnitFilePresets tmp = {};
int r;
}
static int preset_prepare_one(
- UnitFileScope scope,
+ LookupScope scope,
InstallContext *plus,
InstallContext *minus,
LookupPaths *lp,
}
int unit_file_preset(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags file_flags,
const char *root_dir,
- char **files,
+ char **names,
UnitFilePresetMode mode,
UnitFileChange **changes,
size_t *n_changes) {
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
r = lookup_paths_init(&lp, scope, 0, root_dir);
if (r < 0)
return r;
- STRV_FOREACH(i, files) {
- r = preset_prepare_one(scope, &plus, &minus, &lp, *i, &presets, changes, n_changes);
+ STRV_FOREACH(name, names) {
+ r = preset_prepare_one(scope, &plus, &minus, &lp, *name, &presets, changes, n_changes);
if (r < 0)
return r;
}
- return execute_preset(file_flags, &plus, &minus, &lp, config_path, files, mode, changes, n_changes);
+ return execute_preset(file_flags, &plus, &minus, &lp, config_path, names, mode, changes, n_changes);
}
int unit_file_preset_all(
- UnitFileScope scope,
+ LookupScope scope,
UnitFileFlags file_flags,
const char *root_dir,
UnitFilePresetMode mode,
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
r = lookup_paths_init(&lp, scope, 0, root_dir);
DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
int unit_file_get_list(
- UnitFileScope scope,
+ LookupScope scope,
const char *root_dir,
Hashmap *h,
char **states,
int r;
assert(scope >= 0);
- assert(scope < _UNIT_FILE_SCOPE_MAX);
+ assert(scope < _LOOKUP_SCOPE_MAX);
assert(h);
r = lookup_paths_init(&lp, scope, 0, root_dir);