diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 5477110bcc..0cbe5cfb6b 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -43,6 +43,11 @@ All tools: debugging, in order to test generators and other code against specific kernel command lines. +* `$SYSTEMD_OS_RELEASE` — if set, use this path instead of `/etc/os-release` or + `/usr/lib/os-release`. When operating under some root (e.g. `systemctl + --root=…`), the path is taken relative to the outside root. Only useful for + debugging. + * `$SYSTEMD_FSTAB` — if set, use this path instead of `/etc/fstab`. Only useful for debugging. diff --git a/man/os-release.xml b/man/os-release.xml index 4acd1a9a57..7e3e9a73a8 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -75,6 +75,10 @@ from earliest boot on, and hence must be located on the root file system. + os-release must not contain repeating keys. Nevertheless, readers should pick + the entries later in the file in case of repeats, similarly to how a shell sourcing the file would. A + reader may warn about repeating entries. + For a longer rationale for os-release please refer to the Announcement of /etc/os-release. diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index c0c98a0e88..dd3553aa18 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -138,7 +138,7 @@ a symlink, so when systemd is asked through D-Bus to load dbus-org.freedesktop.network1.service, it'll load systemd-networkd.service. As another example, default.target — - the default system target started at boot — is commonly symlinked (aliased) to either + the default system target started at boot — is commonly aliased to either multi-user.target or graphical.target to select what is started by default. Alias names may be used in commands like disable, start, stop, status, and similar, and in all @@ -154,8 +154,12 @@ template instance (e.g. alias@inst.service) may be a symlink to different template (e.g. template@inst.service). In that case, just this specific instance is aliased, while other instances of the template (e.g. alias@foo.service, - alias@bar.service) are not aliased. Those rule preserve the requirement that the - instance (if any) is always uniquely defined for a given unit and all its aliases. + alias@bar.service) are not aliased. Those rules preserve the requirement that the + instance (if any) is always uniquely defined for a given unit and all its aliases. The target of alias + symlink must point to a valid unit file location, i.e. the symlink target name must match the symlink + source name as described, and the destination path must be in one of the unit search paths, see UNIT FILE + LOAD PATH section below for more details. Note that the target file may not exist, i.e. the symlink may + be dangling. Unit files may specify aliases through the Alias= directive in the [Install] section. When the unit is enabled, symlinks will be created for those names, and removed when the unit is @@ -175,11 +179,18 @@ exists for Requires= type dependencies as well, the directory suffix is .requires/ in this case. This functionality is useful to hook units into the start-up of other units, without having to modify their unit files. For details about the semantics of - Wants=, see below. The preferred way to create symlinks in the - .wants/ or .requires/ directory of a unit file is by embedding - the dependency in [Install] section of the target unit, and creating the symlink in the file system with - the enable or preset commands of - systemctl1. + Wants= and Requires=, see below. The preferred way to create + symlinks in the .wants/ or .requires/ directories is by + specifying the dependency in [Install] section of the target unit, and creating the symlink in the file + system with the enable or preset commands of + systemctl1. The + target can be a normal unit (either plain or a specific instance of a template unit). In case when the + source unit is a template, the target can also be a template, in which case the instance will be + "propagated" to the target unit to form a valid unit instance. The target of symlinks in + .wants/ or .requires/ must thus point to a valid unit file + location, i.e. the symlink target name must satisfy the described requirements, and the destination path + must be in one of the unit search paths, see UNIT FILE LOAD PATH section below for more details. Note + that the target file may not exist, i.e. the symlink may be dangling. Along with a unit file foo.service, a "drop-in" directory foo.service.d/ may exist. All files with the suffix @@ -501,13 +512,30 @@ systemd-analyze --user unit-paths - Moreover, additional units might be loaded into systemd from - directories not on the unit load path by creating a symlink pointing to a - unit file in the directories. You can use systemctl link - for this operation. See - systemctl1 - for its usage and precaution. - + Moreover, additional units might be loaded into systemd from directories not on the unit load path + by creating a symlink pointing to a unit file in the directories. You can use systemctl + link for this; see + systemctl1. The file + system where the linked unit files are located must be accessible when systemd is started (e.g. anything + underneath /home/ or /var/ is not allowed, unless those + directories are located on the root file system). + + It is important to distinguish "linked unit files" from "unit file aliases": any symlink where the + symlink target is within the unit load path becomes an alias: the source name and + the target file name must satisfy specific constraints listed above in the discussion of aliases, but the + symlink target doesn't have to exist, and in fact the symlink target path is not used, except to check + whether the target is within the unit load path. In constrast, a symlink which goes outside of the unit + load path signifies a linked unit file. The symlink is followed when loading the file, but the + destination name is otherwise unused (and may even not be a valid unit file name). For example, symlinks + /etc/systemd/system/alias1.service → service1.service, + /etc/systemd/system/alias2.service → /usr/lib/systemd/service1.service, + /etc/systemd/system/alias3.service → /etc/systemd/system/service1.service + are all valid aliases and service1.service will have + four names, even if the unit file is located at + /run/systemd/system/service1.service. In contrast, + a symlink /etc/systemd/system/link1.service → ../link1_service_file + means that link1.service is a "linked unit" and the contents of + /etc/systemd/link1_service_file provide its configuration. @@ -1876,34 +1904,31 @@ WantedBy= RequiredBy= - This option may be used more than once, or a - space-separated list of unit names may be given. A symbolic - link is created in the .wants/ or - .requires/ directory of each of the - listed units when this unit is installed by systemctl - enable. This has the effect that a dependency of - type Wants= or Requires= - is added from the listed unit to the current unit. The primary - result is that the current unit will be started when the - listed unit is started. See the description of - Wants= and Requires= in - the [Unit] section for details. + This option may be used more than once, or a space-separated list of unit names may + be given. A symbolic link is created in the .wants/ or + .requires/ directory of each of the listed units when this unit is installed by + systemctl enable. This has the effect of a dependency of type + Wants= or Requires= being added from the listed unit to the + current unit. The primary result is that the current unit will be started when the listed unit is + started, see the description of Wants= and Requires= in the + [Unit] section for details. - WantedBy=foo.service in a service - bar.service is mostly equivalent to - Alias=foo.service.wants/bar.service in the - same file. In case of template units listing non template units, - systemctl enable must be called with an - instance name, and this instance will be added to the - .wants/ or - .requires/ list of the listed unit. E.g. - WantedBy=getty.target in a service - getty@.service will result in - systemctl enable getty@tty2.service - creating a - getty.target.wants/getty@tty2.service - link to getty@.service. - + In case of template units listing non template units, the listing unit must have + DefaultInstance= set, or systemctl enable must be called with + an instance name. The instance (default or specified) will be added to the + .wants/ or .requires/ list of the listed unit. For example, + WantedBy=getty.target in a service getty@.service will result + in systemctl enable getty@tty2.service creating a + getty.target.wants/getty@tty2.service link to + getty@.service. This also applies to listing specific instances of templated + units: this specific instance will gain the dependency. A template unit may also list a template + unit, in which case a generic dependency will be added where each instance of the listing unit will + have a dependency on an instance of the listed template with the same instance value. For example, + WantedBy=container@.target in a service monitor@.service will + result in systemctl enable monitor@.service creating a + container@.target.wants/monitor@.service link to + monitor@.service, which applies to all instances of + container@.target. diff --git a/meson.build b/meson.build index a2d397d5fc..e68791b8b4 100644 --- a/meson.build +++ b/meson.build @@ -2450,7 +2450,7 @@ public_programs += executable( install_rpath : rootlibexecdir, install : true) -public_programs += executable( +systemctl = executable( 'systemctl', systemctl_sources, include_directories : includes, @@ -2464,6 +2464,7 @@ public_programs += executable( install_rpath : rootlibexecdir, install : true, install_dir : rootbindir) +public_programs += systemctl if conf.get('ENABLE_PORTABLED') == 1 dbus_programs += executable( @@ -3282,13 +3283,22 @@ executable( install : true, install_dir : rootlibexecdir) -public_programs += executable( +systemd_id128 = executable( 'systemd-id128', 'src/id128/id128.c', include_directories : includes, link_with : [libshared], install_rpath : rootlibexecdir, install : true) +public_programs += systemd_id128 + +if want_tests != 'false' + test('test-systemctl-enable', + test_systemctl_enable_sh, + # https://github.com/mesonbuild/meson/issues/2681 + args : [systemctl.full_path(), + systemd_id128.full_path()]) +endif public_programs += executable( 'systemd-path', diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c index 58986bc406..011066f148 100644 --- a/src/analyze/analyze-condition.c +++ b/src/analyze/analyze-condition.c @@ -74,7 +74,7 @@ static int log_helper(void *userdata, int level, int error, const char *file, in return r; } -static int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const char *root) { +static int verify_conditions(char **lines, LookupScope scope, const char *unit, const char *root) { _cleanup_(manager_freep) Manager *m = NULL; Unit *u; int r, q = 1; diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c index d663a6561c..edfdfba144 100644 --- a/src/analyze/analyze-plot.c +++ b/src/analyze/analyze-plot.c @@ -78,7 +78,7 @@ static int acquire_host_info(sd_bus *bus, HostInfo **hi) { if (!host) return log_oom(); - if (arg_scope != UNIT_FILE_SYSTEM) { + if (arg_scope != LOOKUP_SCOPE_SYSTEM) { r = bus_connect_transport(arg_transport, arg_host, false, &system_bus); if (r < 0) { log_debug_errno(r, "Failed to connect to system bus, ignoring: %m"); @@ -183,7 +183,7 @@ int verb_plot(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; _cleanup_free_ char *pretty_times = NULL; - bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM; + bool use_full_bus = arg_scope == LOOKUP_SCOPE_SYSTEM; BootTimes *boot; UnitTimes *u; int n, m = 1, y = 0, r; @@ -201,7 +201,7 @@ int verb_plot(int argc, char *argv[], void *userdata) { if (n < 0) return n; - if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) { + if (use_full_bus || arg_scope != LOOKUP_SCOPE_SYSTEM) { n = acquire_host_info(bus, &host); if (n < 0) return n; diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index 458b681143..aa41751dd1 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -2646,7 +2646,7 @@ static int offline_security_check(Unit *u, static int offline_security_checks(char **filenames, JsonVariant *policy, - UnitFileScope scope, + LookupScope scope, bool check_man, bool run_generators, unsigned threshold, @@ -2758,7 +2758,7 @@ static int offline_security_checks(char **filenames, static int analyze_security(sd_bus *bus, char **units, JsonVariant *policy, - UnitFileScope scope, + LookupScope scope, bool check_man, bool run_generators, bool offline, diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c index a259d47395..aa42d69322 100644 --- a/src/analyze/analyze-time-data.c +++ b/src/analyze/analyze-time-data.c @@ -67,9 +67,9 @@ int acquire_boot_times(sd_bus *bus, BootTimes **ret) { "Please try again later.\n" "Hint: Use 'systemctl%s list-jobs' to see active jobs", times.finish_time, - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user"); - if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) { + if (arg_scope == LOOKUP_SCOPE_SYSTEM && times.security_start_time > 0) { /* security_start_time is set when systemd is not running under container environment. */ if (times.initrd_time > 0) times.kernel_done_time = times.initrd_time; diff --git a/src/analyze/analyze-unit-files.c b/src/analyze/analyze-unit-files.c index 60fbdcb230..f07ec3e56c 100644 --- a/src/analyze/analyze-unit-files.c +++ b/src/analyze/analyze-unit-files.c @@ -21,9 +21,9 @@ int verb_unit_files(int argc, char *argv[], void *userdata) { char **v; int r; - r = lookup_paths_init(&lp, arg_scope, 0, NULL); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, NULL); if (r < 0) - return log_error_errno(r, "lookup_paths_init() failed: %m"); + return r; r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL); if (r < 0) diff --git a/src/analyze/analyze-unit-paths.c b/src/analyze/analyze-unit-paths.c index 32c97b2e52..6fa1527dd6 100644 --- a/src/analyze/analyze-unit-paths.c +++ b/src/analyze/analyze-unit-paths.c @@ -9,9 +9,9 @@ int verb_unit_paths(int argc, char *argv[], void *userdata) { _cleanup_(lookup_paths_free) LookupPaths paths = {}; int r; - r = lookup_paths_init(&paths, arg_scope, 0, NULL); + r = lookup_paths_init_or_warn(&paths, arg_scope, 0, NULL); if (r < 0) - return log_error_errno(r, "lookup_paths_init() failed: %m"); + return r; STRV_FOREACH(p, paths.search_path) puts(*p); diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c index 7702d83ddc..531144ba57 100644 --- a/src/analyze/analyze-verify-util.c +++ b/src/analyze/analyze-verify-util.c @@ -242,7 +242,7 @@ static void set_destroy_ignore_pointer_max(Set** s) { set_free_free(*s); } -int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) { +int verify_units(char **filenames, LookupScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) { const ManagerTestRunFlags flags = MANAGER_TEST_RUN_MINIMAL | MANAGER_TEST_RUN_ENV_GENERATORS | diff --git a/src/analyze/analyze-verify-util.h b/src/analyze/analyze-verify-util.h index 47b78a8158..385d635e33 100644 --- a/src/analyze/analyze-verify-util.h +++ b/src/analyze/analyze-verify-util.h @@ -17,7 +17,7 @@ typedef enum RecursiveErrors { int verify_generate_path(char **var, char **filenames); int verify_prepare_filename(const char *filename, char **ret); int verify_executable(Unit *u, const ExecCommand *exec, const char *root); -int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root); +int verify_units(char **filenames, LookupScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root); const char* recursive_errors_to_string(RecursiveErrors i) _const_; RecursiveErrors recursive_errors_from_string(const char *s) _pure_; diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 5c64bf6b4c..905ff2c27f 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -89,7 +89,7 @@ usec_t arg_fuzz = 0; PagerFlags arg_pager_flags = 0; BusTransport arg_transport = BUS_TRANSPORT_LOCAL; const char *arg_host = NULL; -UnitFileScope arg_scope = UNIT_FILE_SYSTEM; +LookupScope arg_scope = LOOKUP_SCOPE_SYSTEM; RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES; bool arg_man = true; bool arg_generators = false; @@ -114,7 +114,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_unit, freep); STATIC_DESTRUCTOR_REGISTER(arg_profile, freep); int acquire_bus(sd_bus **bus, bool *use_full_bus) { - bool user = arg_scope != UNIT_FILE_SYSTEM; + bool user = arg_scope != LOOKUP_SCOPE_SYSTEM; int r; if (use_full_bus && *use_full_bus) { @@ -351,15 +351,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SYSTEM: - arg_scope = UNIT_FILE_SYSTEM; + arg_scope = LOOKUP_SCOPE_SYSTEM; break; case ARG_USER: - arg_scope = UNIT_FILE_USER; + arg_scope = LOOKUP_SCOPE_USER; break; case ARG_GLOBAL: - arg_scope = UNIT_FILE_GLOBAL; + arg_scope = LOOKUP_SCOPE_GLOBAL; break; case ARG_ORDER: @@ -500,12 +500,12 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --threshold= is only supported for security right now."); - if (arg_scope == UNIT_FILE_GLOBAL && + if (arg_scope == LOOKUP_SCOPE_GLOBAL && !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --global only makes sense with verbs dot, unit-paths, verify."); - if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER) + if (streq_ptr(argv[optind], "cat-config") && arg_scope == LOOKUP_SCOPE_USER) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --user is not supported for cat-config right now."); diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h index 71a682cdc1..da12058c43 100644 --- a/src/analyze/analyze.h +++ b/src/analyze/analyze.h @@ -22,7 +22,7 @@ extern usec_t arg_fuzz; extern PagerFlags arg_pager_flags; extern BusTransport arg_transport; extern const char *arg_host; -extern UnitFileScope arg_scope; +extern LookupScope arg_scope; extern RecursiveErrors arg_recursive_errors; extern bool arg_man; extern bool arg_generators; diff --git a/src/basic/env-file.c b/src/basic/env-file.c index 3efd77909c..2ab5c7bc0d 100644 --- a/src/basic/env-file.c +++ b/src/basic/env-file.c @@ -16,9 +16,8 @@ static int parse_env_file_internal( FILE *f, const char *fname, int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { + const char *key, char *value, void *userdata), + void *userdata) { size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; @@ -99,7 +98,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -142,7 +141,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -261,7 +260,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -299,8 +298,7 @@ static int check_utf8ness_and_warn( static int parse_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { const char *k; va_list aq, *ap = userdata; @@ -322,9 +320,6 @@ static int parse_env_file_push( free(*v); *v = value; - if (n_pushed) - (*n_pushed)++; - return 1; } } @@ -340,16 +335,13 @@ int parse_env_filev( const char *fname, va_list ap) { - int r, n_pushed = 0; + int r; va_list aq; va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq); va_end(aq); - if (r < 0) - return r; - - return n_pushed; + return r; } int parse_env_file_sentinel( @@ -370,8 +362,7 @@ int parse_env_file_sentinel( static int load_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***m = userdata; char *p; int r; @@ -388,78 +379,69 @@ static int load_env_file_push( if (r < 0) return r; - if (n_pushed) - (*n_pushed)++; - free(value); return 0; } int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int load_env_file_push_pairs( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; + void *userdata) { + + char ***m = ASSERT_PTR(userdata); int r; r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; + /* Check if the key is present */ + for (char **t = *m; t && *t; t += 2) + if (streq(t[0], key)) { + if (value) + return free_and_replace(t[1], value); + else + return free_and_strdup(t+1, ""); + } + r = strv_extend(m, key); if (r < 0) - return -ENOMEM; + return r; - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; + if (value) + return strv_push(m, value); + else + return strv_extend(m, ""); } int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int merge_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***env = userdata; char *expanded_value; @@ -488,7 +470,7 @@ static int merge_env_file_push( log_debug("%s:%u: setting %s=%s", filename, line, key, value); - return load_env_file_push(filename, line, key, value, env, n_pushed); + return load_env_file_push(filename, line, key, value, env); } int merge_env_file( @@ -500,7 +482,7 @@ int merge_env_file( * plus "extended" substitutions, unlike other exported parsing functions. */ - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); + return parse_env_file_internal(f, fname, merge_env_file_push, env); } static void write_env_var(FILE *f, const char *v) { diff --git a/src/basic/os-util.c b/src/basic/os-util.c index bf86f64348..acfff24319 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -170,13 +170,19 @@ int open_extension_release(const char *root, const char *extension, char **ret_p } } } else { - FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") { - r = chase_symlinks(p, root, CHASE_PREFIX_ROOT, + const char *var = secure_getenv("SYSTEMD_OS_RELEASE"); + if (var) + r = chase_symlinks(var, root, 0, ret_path ? &q : NULL, ret_fd ? &fd : NULL); - if (r != -ENOENT) - break; - } + else + FOREACH_STRING(path, "/etc/os-release", "/usr/lib/os-release") { + r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, + ret_path ? &q : NULL, + ret_fd ? &fd : NULL); + if (r != -ENOENT) + break; + } } if (r < 0) return r; diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c index 2dd587fd8a..1f4331a8bf 100644 --- a/src/basic/path-lookup.c +++ b/src/basic/path-lookup.c @@ -232,7 +232,7 @@ bool path_is_user_config_dir(const char *path) { } static int acquire_generator_dirs( - UnitFileScope scope, + LookupScope scope, const char *tempdir, char **generator, char **generator_early, @@ -244,17 +244,17 @@ static int acquire_generator_dirs( assert(generator); assert(generator_early); assert(generator_late); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL)); + assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER, LOOKUP_SCOPE_GLOBAL)); - if (scope == UNIT_FILE_GLOBAL) + if (scope == LOOKUP_SCOPE_GLOBAL) return -EOPNOTSUPP; if (tempdir) prefix = tempdir; - else if (scope == UNIT_FILE_SYSTEM) + else if (scope == LOOKUP_SCOPE_SYSTEM) prefix = "/run/systemd"; else { - /* UNIT_FILE_USER */ + /* LOOKUP_SCOPE_USER */ const char *e; e = getenv("XDG_RUNTIME_DIR"); @@ -288,21 +288,21 @@ static int acquire_generator_dirs( } static int acquire_transient_dir( - UnitFileScope scope, + LookupScope scope, const char *tempdir, char **ret) { char *transient; assert(ret); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL)); + assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER, LOOKUP_SCOPE_GLOBAL)); - if (scope == UNIT_FILE_GLOBAL) + if (scope == LOOKUP_SCOPE_GLOBAL) return -EOPNOTSUPP; if (tempdir) transient = path_join(tempdir, "transient"); - else if (scope == UNIT_FILE_SYSTEM) + else if (scope == LOOKUP_SCOPE_SYSTEM) transient = strdup("/run/systemd/transient"); else return xdg_user_runtime_dir(ret, "/systemd/transient"); @@ -313,7 +313,7 @@ static int acquire_transient_dir( return 0; } -static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { +static int acquire_config_dirs(LookupScope scope, char **persistent, char **runtime) { _cleanup_free_ char *a = NULL, *b = NULL; int r; @@ -322,17 +322,17 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru switch (scope) { - case UNIT_FILE_SYSTEM: + case LOOKUP_SCOPE_SYSTEM: a = strdup(SYSTEM_CONFIG_UNIT_DIR); b = strdup("/run/systemd/system"); break; - case UNIT_FILE_GLOBAL: + case LOOKUP_SCOPE_GLOBAL: a = strdup(USER_CONFIG_UNIT_DIR); b = strdup("/run/systemd/user"); break; - case UNIT_FILE_USER: + case LOOKUP_SCOPE_USER: r = xdg_user_config_dir(&a, "/systemd/user"); if (r < 0 && r != -ENXIO) return r; @@ -364,7 +364,7 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru return 0; } -static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) { +static int acquire_control_dirs(LookupScope scope, char **persistent, char **runtime) { _cleanup_free_ char *a = NULL; int r; @@ -373,7 +373,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r switch (scope) { - case UNIT_FILE_SYSTEM: { + case LOOKUP_SCOPE_SYSTEM: { _cleanup_free_ char *b = NULL; a = strdup("/etc/systemd/system.control"); @@ -389,7 +389,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r break; } - case UNIT_FILE_USER: + case LOOKUP_SCOPE_USER: r = xdg_user_config_dir(&a, "/systemd/user.control"); if (r < 0 && r != -ENXIO) return r; @@ -406,7 +406,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r break; - case UNIT_FILE_GLOBAL: + case LOOKUP_SCOPE_GLOBAL: return -EOPNOTSUPP; default: @@ -419,7 +419,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r } static int acquire_attached_dirs( - UnitFileScope scope, + LookupScope scope, char **ret_persistent, char **ret_runtime) { @@ -429,7 +429,7 @@ static int acquire_attached_dirs( assert(ret_runtime); /* Portable services are not available to regular users for now. */ - if (scope != UNIT_FILE_SYSTEM) + if (scope != LOOKUP_SCOPE_SYSTEM) return -EOPNOTSUPP; a = strdup("/etc/systemd/system.attached"); @@ -508,8 +508,8 @@ static int get_paths_from_environ(const char *var, char ***paths, bool *append) } int lookup_paths_init( - LookupPaths *p, - UnitFileScope scope, + LookupPaths *lp, + LookupScope scope, LookupPathsFlags flags, const char *root_dir) { @@ -526,16 +526,16 @@ int lookup_paths_init( _cleanup_strv_free_ char **paths = NULL; int r; - assert(p); + assert(lp); assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); #if HAVE_SPLIT_USR flags |= LOOKUP_PATHS_SPLIT_USR; #endif if (!empty_or_root(root_dir)) { - if (scope == UNIT_FILE_USER) + if (scope == LOOKUP_SCOPE_USER) return -EINVAL; r = is_dir(root_dir, true); @@ -560,8 +560,8 @@ int lookup_paths_init( if (r < 0) return r; - if (scope == UNIT_FILE_USER) { - r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config); + if (scope == LOOKUP_SCOPE_USER) { + r = acquire_config_dirs(LOOKUP_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config); if (r < 0) return r; } @@ -606,7 +606,7 @@ int lookup_paths_init( switch (scope) { - case UNIT_FILE_SYSTEM: + case LOOKUP_SCOPE_SYSTEM: add = strv_new( /* If you modify this you also want to modify * systemdsystemunitpath= in systemd.pc.in! */ @@ -629,7 +629,7 @@ int lookup_paths_init( STRV_IFNOTNULL(generator_late)); break; - case UNIT_FILE_GLOBAL: + case LOOKUP_SCOPE_GLOBAL: add = strv_new( /* If you modify this you also want to modify * systemduserunitpath= in systemd.pc.in, and @@ -652,7 +652,7 @@ int lookup_paths_init( STRV_IFNOTNULL(generator_late)); break; - case UNIT_FILE_USER: + case LOOKUP_SCOPE_USER: add = user_dirs(persistent_config, runtime_config, global_persistent_config, global_runtime_config, generator, generator_early, generator_late, @@ -716,7 +716,7 @@ int lookup_paths_init( if (r < 0) return -ENOMEM; - *p = (LookupPaths) { + *lp = (LookupPaths) { .search_path = strv_uniq(TAKE_PTR(paths)), .persistent_config = TAKE_PTR(persistent_config), @@ -741,46 +741,56 @@ int lookup_paths_init( return 0; } -void lookup_paths_free(LookupPaths *p) { - if (!p) - return; +int lookup_paths_init_or_warn(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir) { + int r; - p->search_path = strv_free(p->search_path); - - p->persistent_config = mfree(p->persistent_config); - p->runtime_config = mfree(p->runtime_config); - - p->persistent_attached = mfree(p->persistent_attached); - p->runtime_attached = mfree(p->runtime_attached); - - p->generator = mfree(p->generator); - p->generator_early = mfree(p->generator_early); - p->generator_late = mfree(p->generator_late); - - p->transient = mfree(p->transient); - - p->persistent_control = mfree(p->persistent_control); - p->runtime_control = mfree(p->runtime_control); - - p->root_dir = mfree(p->root_dir); - p->temporary_dir = mfree(p->temporary_dir); + r = lookup_paths_init(lp, scope, flags, root_dir); + if (r < 0) + return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m", + isempty(root_dir) ? "" : " for root directory ", strempty(root_dir)); + return r; } -void lookup_paths_log(LookupPaths *p) { - assert(p); +void lookup_paths_free(LookupPaths *lp) { + if (!lp) + return; - if (strv_isempty(p->search_path)) { + lp->search_path = strv_free(lp->search_path); + + lp->persistent_config = mfree(lp->persistent_config); + lp->runtime_config = mfree(lp->runtime_config); + + lp->persistent_attached = mfree(lp->persistent_attached); + lp->runtime_attached = mfree(lp->runtime_attached); + + lp->generator = mfree(lp->generator); + lp->generator_early = mfree(lp->generator_early); + lp->generator_late = mfree(lp->generator_late); + + lp->transient = mfree(lp->transient); + + lp->persistent_control = mfree(lp->persistent_control); + lp->runtime_control = mfree(lp->runtime_control); + + lp->root_dir = mfree(lp->root_dir); + lp->temporary_dir = mfree(lp->temporary_dir); +} + +void lookup_paths_log(LookupPaths *lp) { + assert(lp); + + if (strv_isempty(lp->search_path)) { log_debug("Ignoring unit files."); - p->search_path = strv_free(p->search_path); + lp->search_path = strv_free(lp->search_path); } else { _cleanup_free_ char *t = NULL; - t = strv_join(p->search_path, "\n\t"); + t = strv_join(lp->search_path, "\n\t"); log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t)); } } -char **generator_binary_paths(UnitFileScope scope) { +char **generator_binary_paths(LookupScope scope) { bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */ _cleanup_strv_free_ char **paths = NULL; int r; @@ -795,15 +805,15 @@ char **generator_binary_paths(UnitFileScope scope) { switch (scope) { - case UNIT_FILE_SYSTEM: + case LOOKUP_SCOPE_SYSTEM: add = strv_new("/run/systemd/system-generators", "/etc/systemd/system-generators", "/usr/local/lib/systemd/system-generators", SYSTEM_GENERATOR_DIR); break; - case UNIT_FILE_GLOBAL: - case UNIT_FILE_USER: + case LOOKUP_SCOPE_GLOBAL: + case LOOKUP_SCOPE_USER: add = strv_new("/run/systemd/user-generators", "/etc/systemd/user-generators", "/usr/local/lib/systemd/user-generators", diff --git a/src/basic/path-lookup.h b/src/basic/path-lookup.h index af85dc7b4f..aed72defe7 100644 --- a/src/basic/path-lookup.h +++ b/src/basic/path-lookup.h @@ -3,10 +3,7 @@ #include -typedef struct LookupPaths LookupPaths; - #include "def.h" -#include "unit-file.h" #include "macro.h" typedef enum LookupPathsFlags { @@ -15,7 +12,15 @@ typedef enum LookupPathsFlags { LOOKUP_PATHS_SPLIT_USR = 1 << 2, } LookupPathsFlags; -struct LookupPaths { +typedef enum LookupScope { + LOOKUP_SCOPE_SYSTEM, + LOOKUP_SCOPE_GLOBAL, + LOOKUP_SCOPE_USER, + _LOOKUP_SCOPE_MAX, + _LOOKUP_SCOPE_INVALID = -EINVAL, +} LookupScope; + +typedef struct LookupPaths { /* Where we look for unit files. This includes the individual special paths below, but also any vendor * supplied, static unit file paths. */ char **search_path; @@ -52,9 +57,10 @@ struct LookupPaths { /* A temporary directory when running in test mode, to be nuked */ char *temporary_dir; -}; +} LookupPaths; -int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); +int lookup_paths_init(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir); +int lookup_paths_init_or_warn(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir); int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs); int xdg_user_runtime_dir(char **ret, const char *suffix); @@ -67,7 +73,7 @@ bool path_is_user_config_dir(const char *path); void lookup_paths_log(LookupPaths *p); void lookup_paths_free(LookupPaths *p); -char **generator_binary_paths(UnitFileScope scope); +char **generator_binary_paths(LookupScope scope); char **env_generator_binary_paths(bool is_system); #define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network")) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 96364b81ac..b25cabc6b4 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -127,17 +127,22 @@ bool null_or_empty(struct stat *st) { return false; } -int null_or_empty_path(const char *fn) { +int null_or_empty_path_with_root(const char *fn, const char *root) { struct stat st; + int r; assert(fn); - /* If we have the path, let's do an easy text comparison first. */ - if (path_equal(fn, "/dev/null")) + /* A symlink to /dev/null or an empty file? + * When looking under root_dir, we can't expect /dev/ to be mounted, + * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ + + if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null")) return true; - if (stat(fn, &st) < 0) - return -errno; + r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL); + if (r < 0) + return r; return null_or_empty(&st); } diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 77732db05e..37513a43e7 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -31,9 +31,13 @@ static inline int dir_is_populated(const char *path) { } bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); +int null_or_empty_path_with_root(const char *fn, const char *root); int null_or_empty_fd(int fd); +static inline int null_or_empty_path(const char *fn) { + return null_or_empty_path_with_root(fn, NULL); +} + int path_is_read_only_fs(const char *path); int files_same(const char *filea, const char *fileb, int flags); diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c index d44b886440..7c1ae515e1 100644 --- a/src/basic/unit-file.c +++ b/src/basic/unit-file.c @@ -69,7 +69,7 @@ int unit_symlink_name_compatible(const char *symlink, const char *target, bool i return 0; } -int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) { +int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target) { const char *src, *dst; _cleanup_free_ char *src_instance = NULL, *dst_instance = NULL; UnitType src_unit_type, dst_unit_type; @@ -82,7 +82,8 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe * * -EINVAL is returned if the something is wrong with the source filename or the source unit type is * not allowed to symlink, - * -EXDEV if the target filename is not a valid unit name or doesn't match the source. + * -EXDEV if the target filename is not a valid unit name or doesn't match the source, + * -ELOOP for an alias to self. */ src = basename(filename); @@ -92,51 +93,56 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe src_name_type = unit_name_to_instance(src, &src_instance); if (src_name_type < 0) - return log_notice_errno(src_name_type, - "%s: not a valid unit name \"%s\": %m", filename, src); + return log_full_errno(log_level, src_name_type, + "%s: not a valid unit name \"%s\": %m", filename, src); src_unit_type = unit_name_to_type(src); assert(src_unit_type >= 0); /* unit_name_to_instance() checked the suffix already */ if (!unit_type_may_alias(src_unit_type)) - return log_notice_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: symlinks are not allowed for units of this type, rejecting.", - filename); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL), + "%s: symlinks are not allowed for units of this type, rejecting.", + filename); if (src_name_type != UNIT_NAME_PLAIN && !unit_type_may_template(src_unit_type)) - return log_notice_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: templates not allowed for %s units, rejecting.", - filename, unit_type_to_string(src_unit_type)); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL), + "%s: templates not allowed for %s units, rejecting.", + filename, unit_type_to_string(src_unit_type)); /* dst checks */ + if (streq(src, dst)) + return log_debug_errno(SYNTHETIC_ERRNO(ELOOP), + "%s: unit self-alias: %s → %s, ignoring.", + filename, src, dst); + dst_name_type = unit_name_to_instance(dst, &dst_instance); if (dst_name_type < 0) - return log_notice_errno(dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type, - "%s points to \"%s\" which is not a valid unit name: %m", - filename, dst); + return log_full_errno(log_level, dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type, + "%s points to \"%s\" which is not a valid unit name: %m", + filename, dst); if (!(dst_name_type == src_name_type || (src_name_type == UNIT_NAME_INSTANCE && dst_name_type == UNIT_NAME_TEMPLATE))) - return log_notice_errno(SYNTHETIC_ERRNO(EXDEV), - "%s: symlink target name type \"%s\" does not match source, rejecting.", - filename, dst); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV), + "%s: symlink target name type \"%s\" does not match source, rejecting.", + filename, dst); if (dst_name_type == UNIT_NAME_INSTANCE) { assert(src_instance); assert(dst_instance); if (!streq(src_instance, dst_instance)) - return log_notice_errno(SYNTHETIC_ERRNO(EXDEV), - "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.", - filename, dst); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV), + "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.", + filename, dst); } dst_unit_type = unit_name_to_type(dst); if (dst_unit_type != src_unit_type) - return log_notice_errno(SYNTHETIC_ERRNO(EXDEV), - "%s: symlink target \"%s\" has incompatible suffix, rejecting.", - filename, dst); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV), + "%s: symlink target \"%s\" has incompatible suffix, rejecting.", + filename, dst); return 0; } @@ -259,6 +265,110 @@ static int directory_name_is_valid(const char *name) { return false; } +int unit_file_resolve_symlink( + const char *root_dir, + char **search_path, + const char *dir, + int dirfd, + const char *filename, + bool resolve_destination_target, + char **ret_destination) { + + _cleanup_free_ char *target = NULL, *simplified = NULL, *dst = NULL, *_dir = NULL, *_filename = NULL; + int r; + + /* This can be called with either dir+dirfd valid and filename just a name, + * or !dir && dirfd==AT_FDCWD, and filename being a full path. + * + * If resolve_destination_target is true, an absolute path will be returned. + * If not, an absolute path is returned for linked unit files, and a relative + * path otherwise. + * + * Returns an error, false if this is an alias, true if it's a linked unit file. */ + + assert(filename); + assert(ret_destination); + assert(dir || path_is_absolute(filename)); + assert(dirfd >= 0 || dirfd == AT_FDCWD); + + r = readlinkat_malloc(dirfd, filename, &target); + if (r < 0) + return log_warning_errno(r, "Failed to read symlink %s%s%s: %m", + dir, dir ? "/" : "", filename); + + if (!dir) { + r = path_extract_directory(filename, &_dir); + if (r < 0) + return r; + dir = _dir; + + r = path_extract_filename(filename, &_filename); + if (r < 0) + return r; + if (r == O_DIRECTORY) + return log_warning_errno(SYNTHETIC_ERRNO(EISDIR), + "Unexpected path to a directory \"%s\", refusing.", filename); + filename = _filename; + } + + bool is_abs = path_is_absolute(target); + if (root_dir || !is_abs) { + char *target_abs = path_join(is_abs ? root_dir : dir, target); + if (!target_abs) + return log_oom(); + + free_and_replace(target, target_abs); + } + + /* Get rid of "." and ".." components in target path */ + r = chase_symlinks(target, root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to resolve symlink %s/%s pointing to %s: %m", + dir, filename, target); + + assert(path_is_absolute(simplified)); + + /* Check if the symlink remain inside of of our search path. + * If yes, it is an alias. Verify that it is valid. + * + * If no, then this is a linked unit file or mask, and we don't care about the target name + * when loading units, and we return the link *source* (resolve_destination_target == false); + * When this is called for installation purposes, we want the final destination, + * so we return the *target*. + */ + const char *tail = path_startswith_strv(simplified, search_path); + if (tail) { /* An alias */ + _cleanup_free_ char *target_name = NULL; + + r = path_extract_filename(simplified, &target_name); + if (r < 0) + return r; + + r = unit_validate_alias_symlink_or_warn(LOG_NOTICE, filename, simplified); + if (r < 0) + return r; + if (is_path(tail)) + log_warning("Suspicious symlink %s/%s→%s, treating as alias.", + dir, filename, simplified); + + dst = resolve_destination_target ? TAKE_PTR(simplified) : TAKE_PTR(target_name); + + } else { + log_debug("Linked unit file: %s/%s → %s", dir, filename, simplified); + + if (resolve_destination_target) + dst = TAKE_PTR(simplified); + else { + dst = path_join(dir, filename); + if (!dst) + return log_oom(); + } + } + + *ret_destination = TAKE_PTR(dst); + return !tail; /* true if linked unit file */ +} + int unit_file_build_name_map( const LookupPaths *lp, uint64_t *cache_timestamp_hash, @@ -308,10 +418,9 @@ int unit_file_build_name_map( FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) { _unused_ _cleanup_free_ char *_filename_free = NULL; - _cleanup_free_ char *simplified = NULL; - bool symlink_to_dir = false; - const char *dst = NULL; char *filename; + _cleanup_free_ char *dst = NULL; + bool symlink_to_dir = false; /* We only care about valid units and dirs with certain suffixes, let's ignore the * rest. */ @@ -395,77 +504,35 @@ int unit_file_build_name_map( /* We don't explicitly check for alias loops here. unit_ids_map_get() which * limits the number of hops should be used to access the map. */ - _cleanup_free_ char *target = NULL; - - r = readlinkat_malloc(dirfd(d), de->d_name, &target); - if (r < 0) { - log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m", - *dir, de->d_name); + r = unit_file_resolve_symlink(lp->root_dir, lp->search_path, + *dir, dirfd(d), de->d_name, + /* resolve_destination_target= */ false, + &dst); + if (r == -ENOMEM) + return r; + if (r < 0) /* we ignore other errors here */ continue; - } - - const bool is_abs = path_is_absolute(target); - if (lp->root_dir || !is_abs) { - char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target); - if (!target_abs) - return log_oom(); - - free_and_replace(target, target_abs); - } - - /* Get rid of "." and ".." components in target path */ - r = chase_symlinks(target, lp->root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL); - if (r < 0) { - log_warning_errno(r, "Failed to resolve symlink %s pointing to %s, ignoring: %m", - filename, target); - continue; - } - - /* Check if the symlink goes outside of our search path. - * If yes, it's a linked unit file or mask, and we don't care about the target name. - * Let's just store the link source directly. - * If not, let's verify that it's a good symlink. */ - char *tail = path_startswith_strv(simplified, lp->search_path); - if (!tail) { - log_debug("%s: linked unit file: %s → %s", - __func__, filename, simplified); - - dst = filename; - } else { - - bool self_alias; - - dst = basename(simplified); - self_alias = streq(dst, de->d_name); - - if (is_path(tail)) - log_full(self_alias ? LOG_DEBUG : LOG_WARNING, - "Suspicious symlink %s→%s, treating as alias.", - filename, simplified); - - r = unit_validate_alias_symlink_and_warn(filename, simplified); - if (r < 0) - continue; - - if (self_alias) { - /* A self-alias that has no effect */ - log_debug("%s: self-alias: %s/%s → %s, ignoring.", - __func__, *dir, de->d_name, dst); - continue; - } - - log_debug("%s: alias: %s/%s → %s", __func__, *dir, de->d_name, dst); - } } else { - dst = filename; + dst = TAKE_PTR(_filename_free); /* Grab the copy we made previously, if available. */ + if (!dst) { + dst = strdup(filename); + if (!dst) + return log_oom(); + } + log_debug("%s: normal unit file: %s", __func__, dst); } - r = hashmap_put_strdup(&ids, de->d_name, dst); + _cleanup_free_ char *key = strdup(de->d_name); + if (!key) + return log_oom(); + + r = hashmap_ensure_put(&ids, &string_hash_ops_free_free, key, dst); if (r < 0) return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m", de->d_name, dst); + key = dst = NULL; } } diff --git a/src/basic/unit-file.h b/src/basic/unit-file.h index cc731a9e06..1c43861f00 100644 --- a/src/basic/unit-file.h +++ b/src/basic/unit-file.h @@ -4,12 +4,11 @@ #include #include "hashmap.h" +#include "path-lookup.h" #include "time-util.h" #include "unit-name.h" typedef enum UnitFileState UnitFileState; -typedef enum UnitFileScope UnitFileScope; -typedef struct LookupPaths LookupPaths; enum UnitFileState { UNIT_FILE_ENABLED, @@ -29,21 +28,23 @@ enum UnitFileState { _UNIT_FILE_STATE_INVALID = -EINVAL, }; -enum UnitFileScope { - UNIT_FILE_SYSTEM, - UNIT_FILE_GLOBAL, - UNIT_FILE_USER, - _UNIT_FILE_SCOPE_MAX, - _UNIT_FILE_SCOPE_INVALID = -EINVAL, -}; - bool unit_type_may_alias(UnitType type) _const_; bool unit_type_may_template(UnitType type) _const_; int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation); -int unit_validate_alias_symlink_and_warn(const char *filename, const char *target); +int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target); bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new); + +int unit_file_resolve_symlink( + const char *root_dir, + char **search_path, + const char *dir, + int dirfd, + const char *filename, + bool resolve_destination_target, + char **ret_destination); + int unit_file_build_name_map( const LookupPaths *lp, uint64_t *cache_timestamp_hash, diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index c78e9ef2b2..572c59dda4 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2288,7 +2288,7 @@ fail: static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), + int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), bool carries_install_info, sd_bus_error *error) { @@ -2352,7 +2352,7 @@ static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bu return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); } -static int unit_file_preset_without_mode(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { +static int unit_file_preset_without_mode(LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { return unit_file_preset(scope, flags, root_dir, files, UNIT_FILE_PRESET_FULL, changes, n_changes); } @@ -2412,7 +2412,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use static int method_disable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), + int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; @@ -2638,7 +2638,7 @@ static int method_get_unit_file_links(sd_bus_message *message, void *userdata, s flags = UNIT_FILE_DRY_RUN | (runtime ? UNIT_FILE_RUNTIME : 0); - r = unit_file_disable(UNIT_FILE_SYSTEM, flags, NULL, p, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, flags, NULL, p, &changes, &n_changes); if (r < 0) return log_error_errno(r, "Failed to get file links for %s: %m", name); diff --git a/src/core/fuzz-unit-file.c b/src/core/fuzz-unit-file.c index 780dd3988d..c12e874e2d 100644 --- a/src/core/fuzz-unit-file.c +++ b/src/core/fuzz-unit-file.c @@ -62,7 +62,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (!getenv("SYSTEMD_LOG_LEVEL")) log_set_max_level(LOG_CRIT); - assert_se(manager_new(UNIT_FILE_SYSTEM, MANAGER_TEST_RUN_MINIMAL, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_SYSTEM, MANAGER_TEST_RUN_MINIMAL, &m) >= 0); name = strjoina("a.", unit_type_to_string(t)); assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0); diff --git a/src/core/main.c b/src/core/main.c index 289a1ee01f..f39c7b0e37 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -2885,7 +2885,7 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + r = manager_new(arg_system ? LOOKUP_SCOPE_SYSTEM : LOOKUP_SCOPE_USER, arg_action == ACTION_TEST ? MANAGER_TEST_FULL : 0, &m); if (r < 0) { diff --git a/src/core/manager.c b/src/core/manager.c index d10fb58a61..4493103298 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -775,13 +775,13 @@ static int manager_setup_sigchld_event_source(Manager *m) { return 0; } -int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) { +int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) { _cleanup_(manager_freep) Manager *m = NULL; const char *e; int r; assert(_m); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER)); + assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER)); m = new(Manager, 1); if (!m) @@ -1700,7 +1700,7 @@ static void manager_preset_all(Manager *m) { return; /* If this is the first boot, and we are in the host system, then preset everything */ - r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); + r = unit_file_preset_all(LOOKUP_SCOPE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); if (r < 0) log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, "Failed to populate /etc with preset unit settings, ignoring: %m"); @@ -1751,11 +1751,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo /* If we are running in test mode, we still want to run the generators, * but we should not touch the real generator directories. */ - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, - MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0, - root); + r = lookup_paths_init_or_warn(&m->lookup_paths, m->unit_file_scope, + MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0, + root); if (r < 0) - return log_error_errno(r, "Failed to initialize path lookup table: %m"); + return r; dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_GENERATORS_START)); r = manager_run_environment_generators(m); @@ -3343,9 +3343,9 @@ int manager_reload(Manager *m) { m->uid_refs = hashmap_free(m->uid_refs); m->gid_refs = hashmap_free(m->gid_refs); - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + r = lookup_paths_init_or_warn(&m->lookup_paths, m->unit_file_scope, 0, NULL); if (r < 0) - log_warning_errno(r, "Failed to initialize path lookup table, ignoring: %m"); + return r; (void) manager_run_environment_generators(m); (void) manager_run_generators(m); diff --git a/src/core/manager.h b/src/core/manager.h index c989ce9c32..dc6718ada1 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -235,7 +235,7 @@ struct Manager { int user_lookup_fds[2]; sd_event_source *user_lookup_event_source; - UnitFileScope unit_file_scope; + LookupScope unit_file_scope; LookupPaths lookup_paths; Hashmap *unit_id_map; Hashmap *unit_name_map; @@ -463,8 +463,8 @@ static inline usec_t manager_default_timeout_abort_usec(Manager *m) { return m->default_timeout_abort_set ? m->default_timeout_abort_usec : m->default_timeout_stop_usec; } -#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) -#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM) +#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == LOOKUP_SCOPE_SYSTEM) +#define MANAGER_IS_USER(m) ((m)->unit_file_scope != LOOKUP_SCOPE_SYSTEM) #define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) @@ -475,7 +475,7 @@ static inline usec_t manager_default_timeout_abort_usec(Manager *m) { #define MANAGER_IS_TEST_RUN(m) ((m)->test_run_flags != 0) -int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager **m); +int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **m); Manager* manager_free(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 4a86a3c0d6..0cdfcd6efd 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -183,7 +183,7 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) { COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(u->manager->unit_file_scope), {} }; @@ -253,7 +253,7 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(u->manager->unit_file_scope), COMMON_TMP_SPECIFIERS, {} diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index 8d9cfbc0af..385cfd3006 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -601,8 +601,8 @@ static int get_search(uint64_t type, char ***list) { case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT: case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ? - UNIT_FILE_SYSTEM : UNIT_FILE_USER; + const LookupScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ? + LOOKUP_SCOPE_SYSTEM : LOOKUP_SCOPE_USER; r = lookup_paths_init(&lp, scope, 0, NULL); if (r < 0) @@ -615,8 +615,8 @@ static int get_search(uint64_t type, char ***list) { case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR: case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: { char **t; - const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ? - UNIT_FILE_SYSTEM : UNIT_FILE_USER; + const LookupScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ? + LOOKUP_SCOPE_SYSTEM : LOOKUP_SCOPE_USER; t = generator_binary_paths(scope); if (!t) diff --git a/src/portable/portable.c b/src/portable/portable.c index 155f731936..0c10b161f0 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -231,7 +231,7 @@ static int extract_now( /* Then, send unit file data to the parent (or/and add it to the hashmap). For that we use our usual unit * discovery logic. Note that we force looking inside of /lib/systemd/system/ for units too, as we mightbe * compiled for a split-usr system but the image might be a legacy-usr one. */ - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where); if (r < 0) return log_debug_errno(r, "Failed to acquire lookup paths: %m"); @@ -1340,12 +1340,12 @@ int portable_attach( strempty(extensions_joined)); } - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); if (r < 0) return r; HASHMAP_FOREACH(item, unit_files) { - r = unit_file_exists(UNIT_FILE_SYSTEM, &paths, item->name); + r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, &paths, item->name); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name); if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0) @@ -1539,7 +1539,7 @@ int portable_detach( assert(name_or_path); - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); if (r < 0) return r; @@ -1573,7 +1573,7 @@ int portable_detach( if (r == 0) continue; - r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state); + r = unit_file_lookup_state(LOOKUP_SCOPE_SYSTEM, &paths, de->d_name, &state); if (r < 0) return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name); if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME, UNIT_FILE_LINKED_RUNTIME)) @@ -1717,7 +1717,7 @@ static int portable_get_state_internal( assert(name_or_path); assert(ret); - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); if (r < 0) return r; @@ -1753,7 +1753,7 @@ static int portable_get_state_internal( if (r == 0) continue; - r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state); + r = unit_file_lookup_state(LOOKUP_SCOPE_SYSTEM, &paths, de->d_name, &state); if (r < 0) return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name); if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME)) diff --git a/src/shared/condition.c b/src/shared/condition.c index b9fca7bf0b..8165513cc9 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -785,7 +785,8 @@ static int condition_test_needs_update(Condition *c, char **env) { if (r < 0) { log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p); return true; - } else if (r == 0) { + } + if (isempty(timestamp_str)) { log_debug("No data in timestamp file '%s', using mtime.", p); return true; } diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 9c121cbaf7..5436aa483b 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -97,7 +97,11 @@ static int specifier_last_component(char specifier, const void *data, const char return 0; } -int install_name_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret) { +int install_name_printf( + LookupScope scope, + const UnitFileInstallInfo *info, + const char *format, + char **ret) { /* This is similar to unit_name_printf() */ const Specifier table[] = { @@ -109,13 +113,13 @@ int install_name_printf(const UnitFileInstallInfo *i, const char *format, const COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(scope), {} }; - assert(i); + assert(info); assert(format); assert(ret); - return specifier_printf(format, UNIT_NAME_MAX, table, root, i, ret); + return specifier_printf(format, UNIT_NAME_MAX, table, info->root, info, ret); } diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h index 5ca9406797..6a9ab24e15 100644 --- a/src/shared/install-printf.h +++ b/src/shared/install-printf.h @@ -4,4 +4,8 @@ #include "install.h" #include "unit-name.h" -int install_name_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret); +int install_name_printf( + LookupScope scope, + const UnitFileInstallInfo *info, + const char *format, + char **ret); diff --git a/src/shared/install.c b/src/shared/install.c index 53b5418804..f807947a3c 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -47,6 +47,7 @@ typedef enum SearchFlags { } SearchFlags; typedef struct { + LookupScope scope; OrderedHashmap *will_process; OrderedHashmap *have_processed; } InstallContext; @@ -92,8 +93,9 @@ void unit_file_presets_freep(UnitFilePresets *p) { 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); @@ -319,7 +321,7 @@ void unit_file_changes_free(UnitFileChange *changes, size_t n_changes) { } void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet) { - bool logged = false; + int err = 0; assert(changes || n_changes == 0); /* If verb is not specified, errors are not allowed! */ @@ -338,7 +340,7 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang break; case UNIT_FILE_UNLINK: if (!quiet) - log_info("Removed %s.", changes[i].path); + log_info("Removed \"%s\".", changes[i].path); break; case UNIT_FILE_IS_MASKED: if (!quiet) @@ -360,75 +362,115 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang break; case -EEXIST: if (changes[i].source) - log_error_errno(changes[i].type_or_errno, - "Failed to %s unit, file %s already exists and is a symlink to %s.", - verb, changes[i].path, changes[i].source); + err = log_error_errno(changes[i].type_or_errno, + "Failed to %s unit, file \"%s\" already exists and is a symlink to \"%s\".", + verb, changes[i].path, changes[i].source); else - log_error_errno(changes[i].type_or_errno, - "Failed to %s unit, file %s already exists.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, + "Failed to %s unit, file \"%s\" already exists.", + verb, changes[i].path); break; case -ERFKILL: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is masked.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is masked.", + verb, changes[i].path); break; case -EADDRNOTAVAIL: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is transient or generated.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is transient or generated.", + verb, changes[i].path); + break; + case -EBADSLT: + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, invalid specifier in \"%s\".", + verb, changes[i].path); break; case -EIDRM: - log_error_errno(changes[i].type_or_errno, "Failed to %s %s, destination unit %s is a non-template unit.", - verb, changes[i].source, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s %s, destination unit %s is a non-template unit.", + verb, changes[i].source, changes[i].path); break; case -EUCLEAN: - log_error_errno(changes[i].type_or_errno, - "Failed to %s unit, \"%s\" is not a valid unit name.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, + "Failed to %s unit, \"%s\" is not a valid unit name.", + verb, changes[i].path); break; case -ELOOP: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, refusing to operate on linked unit file %s", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, refusing to operate on linked unit file %s.", + verb, changes[i].path); + break; + case -EXDEV: + if (changes[i].source) + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, cannot alias %s as %s.", + verb, changes[i].source, changes[i].path); + else + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, invalid unit reference \"%s\".", + verb, changes[i].path); break; - case -ENOENT: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s does not exist.", + verb, changes[i].path); + break; + case -EUNATCH: + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, cannot resolve specifiers in \"%s\".", + verb, changes[i].path); break; - default: assert(changes[i].type_or_errno < 0); - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, file %s: %m.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, file \"%s\": %m", + verb, changes[i].path); } } - if (r < 0 && !logged) + if (r < 0 && err >= 0) log_error_errno(r, "Failed to %s: %m.", verb); } /** - * 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; - 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); + if (!path_is_absolute(target_a) || !path_is_absolute(target_b)) { + r = path_extract_directory(src, &dirname); + if (r < 0) + return r; + } + + _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( @@ -439,7 +481,7 @@ 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; @@ -479,11 +521,7 @@ static int create_symlink( 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; } @@ -590,8 +628,7 @@ static int remove_marked_symlinks_fd( 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; @@ -603,21 +640,42 @@ static int remove_marked_symlinks_fd( 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; @@ -638,7 +696,7 @@ static int remove_marked_symlinks_fd( /* 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; @@ -717,8 +775,9 @@ static int find_symlinks_in_directory( DIR *dir, const char *dir_path, const char *root_dir, - const UnitFileInstallInfo *i, - bool match_aliases, + const UnitFileInstallInfo *info, + bool ignore_destination, + bool match_name, bool ignore_same_name, const char *config_path, bool *same_name_link) { @@ -726,53 +785,69 @@ static int find_symlinks_in_directory( 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; + + /* 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; + } + + /* Make absolute */ + if (!path_is_absolute(dest)) { + char *x; + + x = path_join(dir_path, dest); + if (!x) + return -ENOMEM; + + 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); } - /* Make absolute */ - if (!path_is_absolute(dest)) { - char *x; + assert(unit_name_is_valid(info->name, UNIT_NAME_ANY)); - x = path_join(dir_path, dest); - if (!x) - return -ENOMEM; + /* 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. */ - free_and_replace(dest, x); + 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); } - assert(unit_name_is_valid(i->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, i->name); - - /* Check if what the symlink points to matches what we are looking for */ - found_dest = streq(basename(dest), i->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(i->name, config_path); + t = path_make_absolute(info->name, config_path); if (!p || !t) return -ENOMEM; @@ -783,11 +858,11 @@ static int find_symlinks_in_directory( 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] */ - q = is_symlink_with_known_name(i, de->d_name); + q = is_symlink_with_known_name(info, de->d_name); if (q < 0) return q; if (q > 0) @@ -838,26 +913,36 @@ static int find_symlinks( d = opendir(path); if (!d) { - log_error_errno(errno, "Failed to open directory '%s' while scanning for symlinks, ignoring: %m", path); + log_error_errno(errno, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path); 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) - log_debug_errno(r, "Failed to lookup for symlinks in '%s': %m", path); + log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path); } /* 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 *i, + const UnitFileInstallInfo *info, bool match_name, UnitFileState *state) { @@ -867,7 +952,7 @@ static int find_symlinks_in_scope( int r; assert(lp); - assert(i); + assert(info); /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name" * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are @@ -876,7 +961,7 @@ static int find_symlinks_in_scope( STRV_FOREACH(p, lp->search_path) { bool same_name_link = false; - r = find_symlinks(lp->root_dir, i, match_name, ignore_same_name, *p, &same_name_link); + r = find_symlinks(lp->root_dir, info, match_name, ignore_same_name, *p, &same_name_link); if (r < 0) return r; if (r > 0) { @@ -889,7 +974,7 @@ static int find_symlinks_in_scope( } /* 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; } @@ -916,7 +1001,7 @@ static int find_symlinks_in_scope( /* Check if next iteration will be "below" the unit file (either a regular file * or a symlink), and hence should be ignored */ - if (!ignore_same_name && path_startswith(i->path, *p)) + if (!ignore_same_name && path_startswith(info->path, *p)) ignore_same_name = true; } @@ -929,7 +1014,7 @@ static int find_symlinks_in_scope( * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate * something, and hence are a much stronger concept. */ - if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { + if (enabled_at_all && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { *state = UNIT_FILE_STATIC; return 1; } @@ -949,7 +1034,6 @@ static int find_symlinks_in_scope( } static void install_info_free(UnitFileInstallInfo *i) { - if (!i) return; @@ -965,21 +1049,21 @@ static void install_info_free(UnitFileInstallInfo *i) { free(i); } -static void install_context_done(InstallContext *c) { - assert(c); +static void install_context_done(InstallContext *ctx) { + assert(ctx); - c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free); - c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free); + ctx->will_process = ordered_hashmap_free_with_destructor(ctx->will_process, install_info_free); + ctx->have_processed = ordered_hashmap_free_with_destructor(ctx->have_processed, install_info_free); } -static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { +static UnitFileInstallInfo *install_info_find(InstallContext *ctx, const char *name) { UnitFileInstallInfo *i; - i = ordered_hashmap_get(c->have_processed, name); + i = ordered_hashmap_get(ctx->have_processed, name); if (i) return i; - return ordered_hashmap_get(c->will_process, name); + return ordered_hashmap_get(ctx->will_process, name); } static int install_info_may_process( @@ -1013,7 +1097,7 @@ static int install_info_may_process( * Returns negative on error, 0 if the unit was already known, 1 otherwise. */ static int install_info_add( - InstallContext *c, + InstallContext *ctx, const char *name, const char *path, const char *root, @@ -1023,7 +1107,7 @@ static int install_info_add( UnitFileInstallInfo *i = NULL; int r; - assert(c); + assert(ctx); if (!name) { /* 'name' and 'path' must not both be null. Check here 'path' using assert_se() to @@ -1036,7 +1120,7 @@ static int install_info_add( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - i = install_info_find(c, name); + i = install_info_find(ctx, name); if (i) { i->auxiliary = i->auxiliary && auxiliary; @@ -1076,7 +1160,7 @@ static int install_info_add( } } - r = ordered_hashmap_ensure_put(&c->will_process, &string_hash_ops, i->name, i); + r = ordered_hashmap_ensure_put(&ctx->will_process, &string_hash_ops, i->name, i); if (r < 0) goto fail; @@ -1131,8 +1215,8 @@ static int config_parse_also( void *data, void *userdata) { - UnitFileInstallInfo *info = userdata; - InstallContext *c = data; + UnitFileInstallInfo *info = ASSERT_PTR(userdata); + InstallContext *ctx = ASSERT_PTR(data); int r; assert(unit); @@ -1149,11 +1233,12 @@ static int config_parse_also( if (r == 0) break; - r = install_name_printf(info, word, info->root, &printed); + r = install_name_printf(ctx->scope, info, word, &printed); if (r < 0) - return r; + return log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit name in Also=\"%s\": %m", word); - r = install_info_add(c, printed, NULL, info->root, /* auxiliary= */ true, NULL); + r = install_info_add(ctx, printed, NULL, info->root, /* auxiliary= */ true, NULL); if (r < 0) return r; @@ -1179,7 +1264,8 @@ static int config_parse_default_instance( void *data, void *userdata) { - UnitFileInstallInfo *i = data; + InstallContext *ctx = ASSERT_PTR(data); + UnitFileInstallInfo *info = ASSERT_PTR(userdata); _cleanup_free_ char *printed = NULL; int r; @@ -1196,24 +1282,23 @@ static int config_parse_default_instance( return log_syntax(unit, LOG_WARNING, filename, line, 0, "DefaultInstance= only makes sense for template units, ignoring."); - r = install_name_printf(i, rvalue, i->root, &printed); + r = install_name_printf(ctx->scope, info, rvalue, &printed); if (r < 0) - return r; + return log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue); - if (isempty(printed)) { - i->default_instance = mfree(i->default_instance); - return 0; - } + if (isempty(printed)) + printed = mfree(printed); - if (!unit_instance_is_valid(printed)) + if (printed && !unit_instance_is_valid(printed)) return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid DefaultInstance= value \"%s\".", printed); - return free_and_replace(i->default_instance, printed); + return free_and_replace(info->default_instance, printed); } static int unit_file_load( - InstallContext *c, + InstallContext *ctx, UnitFileInstallInfo *info, const char *path, const char *root_dir, @@ -1224,7 +1309,7 @@ static int unit_file_load( { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, - { "Install", "Also", config_parse_also, 0, c }, + { "Install", "Also", config_parse_also, 0, ctx }, {} }; @@ -1297,8 +1382,8 @@ static int unit_file_load( if (!f) return -errno; - /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */ - assert(c); + /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */ + assert(ctx); r = config_parse(info->name, path, f, "Install\0" @@ -1318,7 +1403,7 @@ static int unit_file_load( 0, info, NULL); if (r < 0) - return log_debug_errno(r, "Failed to parse %s: %m", info->name); + return log_debug_errno(r, "Failed to parse \"%s\": %m", info->name); if ((flags & SEARCH_DROPIN) == 0) info->type = UNIT_FILE_TYPE_REGULAR; @@ -1330,87 +1415,40 @@ static int unit_file_load( } static int unit_file_load_or_readlink( - InstallContext *c, + InstallContext *ctx, UnitFileInstallInfo *info, const char *path, const LookupPaths *lp, SearchFlags flags) { - - _cleanup_free_ char *resolved = NULL; int r; - r = unit_file_load(c, info, path, lp->root_dir, flags); + r = unit_file_load(ctx, info, path, lp->root_dir, flags); if (r != -ELOOP || (flags & SEARCH_DROPIN)) return r; - r = chase_symlinks(path, lp->root_dir, CHASE_WARN | CHASE_NONEXISTENT, &resolved, NULL); - if (r >= 0 && - lp->root_dir && - path_equal_ptr(path_startswith(resolved, lp->root_dir), "dev/null")) - /* When looking under root_dir, we can't expect /dev/ to be mounted, - * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ + /* This is a symlink, let's read and verify it. */ + r = unit_file_resolve_symlink(lp->root_dir, lp->search_path, + NULL, AT_FDCWD, path, + 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 (r > 0 && null_or_empty_path(resolved) > 0) - - info->type = UNIT_FILE_TYPE_MASKED; - - else { - _cleanup_free_ char *target = NULL; - const char *bn; - UnitType a, b; - - /* This is a symlink, let's read it. We read the link again, because last time - * we followed the link until resolution, and here we need to do one step. */ - - r = readlink_malloc(path, &target); - if (r < 0) - return r; - - bn = basename(target); - - if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN)) - return -EINVAL; - - } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - - } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) - return -EINVAL; - } else - return -EINVAL; - - /* Enforce that the symlink destination does not - * change the unit file type. */ - - a = unit_name_to_type(info->name); - b = unit_name_to_type(bn); - if (a < 0 || b < 0 || a != b) - return -EINVAL; - - if (path_is_absolute(target)) - /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ - info->symlink_target = path_join(lp->root_dir, target); - else - /* This is a relative path, take it relative to the dir the symlink is located in. */ - info->symlink_target = file_in_same_dir(path, target); - if (!info->symlink_target) - return -ENOMEM; - - info->type = UNIT_FILE_TYPE_SYMLINK; - } + else if (outside_search_path) + info->type = UNIT_FILE_TYPE_LINKED; + else + info->type = UNIT_FILE_TYPE_ALIAS; return 0; } static int unit_file_search( - InstallContext *c, + InstallContext *ctx, UnitFileInstallInfo *info, const LookupPaths *lp, SearchFlags flags) { @@ -1429,7 +1467,7 @@ static int unit_file_search( return 0; if (info->path) - return unit_file_load_or_readlink(c, info, info->path, lp, flags); + return unit_file_load_or_readlink(ctx, info, info->path, lp, flags); assert(info->name); @@ -1446,7 +1484,7 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, lp, flags); + r = unit_file_load_or_readlink(ctx, info, path, lp, flags); if (r >= 0) { info->path = TAKE_PTR(path); result = r; @@ -1469,7 +1507,7 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, lp, flags); + r = unit_file_load_or_readlink(ctx, info, path, lp, flags); if (r >= 0) { info->path = TAKE_PTR(path); result = r; @@ -1525,38 +1563,38 @@ static int unit_file_search( return log_debug_errno(r, "Failed to get list of conf files: %m"); STRV_FOREACH(p, files) { - r = unit_file_load_or_readlink(c, info, *p, lp, flags | SEARCH_DROPIN); + r = unit_file_load_or_readlink(ctx, info, *p, lp, flags | SEARCH_DROPIN); if (r < 0) - return log_debug_errno(r, "Failed to load conf file %s: %m", *p); + return log_debug_errno(r, "Failed to load conf file \"%s\": %m", *p); } return result; } static int install_info_follow( - InstallContext *c, - UnitFileInstallInfo *i, + InstallContext *ctx, + UnitFileInstallInfo *info, const LookupPaths *lp, SearchFlags flags, bool ignore_different_name) { - assert(c); - assert(i); + assert(ctx); + assert(info); - if (i->type != UNIT_FILE_TYPE_SYMLINK) + if (!IN_SET(info->type, UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_LINKED)) return -EINVAL; - if (!i->symlink_target) + if (!info->symlink_target) return -EINVAL; /* If the basename doesn't match, the caller should add a complete new entry for this. */ - if (!ignore_different_name && !streq(basename(i->symlink_target), i->name)) + if (!ignore_different_name && !streq(basename(info->symlink_target), info->name)) return -EXDEV; - free_and_replace(i->path, i->symlink_target); - i->type = _UNIT_FILE_TYPE_INVALID; + free_and_replace(info->path, info->symlink_target); + info->type = _UNIT_FILE_TYPE_INVALID; - return unit_file_load_or_readlink(c, i, i->path, lp, flags); + return unit_file_load_or_readlink(ctx, info, info->path, lp, flags); } /** @@ -1564,8 +1602,7 @@ static int install_info_follow( * target, maybe more than once. Propagate the instance name if present. */ static int install_info_traverse( - UnitFileScope scope, - InstallContext *c, + InstallContext *ctx, const LookupPaths *lp, UnitFileInstallInfo *start, SearchFlags flags, @@ -1577,14 +1614,14 @@ static int install_info_traverse( assert(lp); assert(start); - assert(c); + assert(ctx); - r = unit_file_search(c, start, lp, flags); + r = unit_file_search(ctx, start, lp, flags); if (r < 0) 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) @@ -1598,7 +1635,9 @@ static int install_info_traverse( return -ELOOP; } - r = install_info_follow(c, 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; @@ -1625,7 +1664,7 @@ static int install_info_traverse( /* We filled in the instance, and the target stayed the same? If so, * then let's honour the link as it is. */ - r = install_info_follow(c, i, lp, flags, true); + r = install_info_follow(ctx, i, lp, flags, true); if (r < 0) return r; @@ -1635,12 +1674,12 @@ static int install_info_traverse( bn = buffer; } - r = install_info_add(c, bn, NULL, lp->root_dir, /* auxiliary= */ false, &i); + r = install_info_add(ctx, bn, NULL, lp->root_dir, /* auxiliary= */ false, &i); if (r < 0) return r; /* Try again, with the new target we found. */ - r = unit_file_search(c, i, lp, flags); + r = unit_file_search(ctx, i, lp, flags); if (r == -ENOENT) /* Translate error code to highlight this specific case */ return -ENOLINK; @@ -1661,12 +1700,12 @@ static int install_info_traverse( * or the name (otherwise). root_dir is prepended to the path. */ static int install_info_add_auto( - InstallContext *c, + InstallContext *ctx, const LookupPaths *lp, const char *name_or_path, UnitFileInstallInfo **ret) { - assert(c); + assert(ctx); assert(name_or_path); if (path_is_absolute(name_or_path)) { @@ -1674,14 +1713,13 @@ static int install_info_add_auto( pp = prefix_roota(lp->root_dir, name_or_path); - return install_info_add(c, NULL, pp, lp->root_dir, /* auxiliary= */ false, ret); + return install_info_add(ctx, NULL, pp, lp->root_dir, /* auxiliary= */ false, ret); } else - return install_info_add(c, name_or_path, NULL, lp->root_dir, /* auxiliary= */ false, ret); + return install_info_add(ctx, name_or_path, NULL, lp->root_dir, /* auxiliary= */ false, ret); } static int install_info_discover( - UnitFileScope scope, - InstallContext *c, + InstallContext *ctx, const LookupPaths *lp, const char *name, SearchFlags flags, @@ -1689,16 +1727,16 @@ static int install_info_discover( UnitFileChange **changes, size_t *n_changes) { - UnitFileInstallInfo *i; + UnitFileInstallInfo *info; int r; - assert(c); + assert(ctx); assert(lp); assert(name); - r = install_info_add_auto(c, lp, name, &i); + r = install_info_add_auto(ctx, lp, name, &info); if (r >= 0) - r = install_info_traverse(scope, c, lp, i, flags, ret); + r = install_info_traverse(ctx, lp, info, flags, ret); if (r < 0) unit_file_changes_add(changes, n_changes, r, name, NULL); @@ -1706,25 +1744,30 @@ static int install_info_discover( } static int install_info_discover_and_check( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *lp, - const char *name, - SearchFlags flags, - UnitFileInstallInfo **ret, - UnitFileChange **changes, - size_t *n_changes) { + InstallContext *ctx, + const LookupPaths *lp, + const char *name, + SearchFlags flags, + UnitFileInstallInfo **ret, + UnitFileChange **changes, + size_t *n_changes) { int r; - r = install_info_discover(scope, c, lp, name, flags, ret, changes, n_changes); + r = install_info_discover(ctx, lp, name, flags, ret, changes, n_changes); if (r < 0) return r; return install_info_may_process(ret ? *ret : NULL, lp, changes, n_changes); } -int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst) { +int unit_file_verify_alias( + const UnitFileInstallInfo *info, + const char *dst, + char **ret_dst, + UnitFileChange **changes, + size_t *n_changes) { + _cleanup_free_ char *dst_updated = NULL; int r; @@ -1734,6 +1777,11 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is * inserted into dst. It is not normally set, even on success, so that the caller can easily * distinguish the case where instance propagation occurred. + * + * Returns: + * -EXDEV when the alias doesn't match the unit, + * -EUCLEAN when the name is invalid, + * -ELOOP when the alias it to the unit itself. */ const char *path_alias = strrchr(dst, '/'); @@ -1751,26 +1799,32 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * 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); + if (!p) { + unit_file_changes_add(changes, n_changes, -EXDEV, dst, NULL); + return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), "Invalid path \"%s\" in alias.", dir); + } + *p = '\0'; /* dir should now be a unit name */ UnitNameFlags type = unit_name_classify(dir); - if (type < 0) - return log_warning_errno(SYNTHETIC_ERRNO(EXDEV), - "Invalid unit name component \"%s\" in alias.", dir); + if (type < 0) { + unit_file_changes_add(changes, n_changes, -EXDEV, dst, NULL); + return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), + "Invalid unit name component \"%s\" in alias.", dir); + } const bool instance_propagation = type == UNIT_NAME_TEMPLATE; /* That's the name we want to use for verification. */ - r = unit_symlink_name_compatible(path_alias, i->name, instance_propagation); + r = unit_symlink_name_compatible(path_alias, info->name, instance_propagation); if (r < 0) return log_error_errno(r, "Failed to verify alias validity: %m"); - if (r == 0) - return log_warning_errno(SYNTHETIC_ERRNO(EXDEV), - "Invalid unit %s symlink %s.", - i->name, dst); + if (r == 0) { + unit_file_changes_add(changes, n_changes, -EXDEV, dst, info->name); + return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), + "Invalid unit \"%s\" symlink \"%s\".", + info->name, dst); + } } else { /* If the symlink target has an instance set and the symlink source doesn't, we "propagate @@ -1778,9 +1832,11 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) { _cleanup_free_ char *inst = NULL; - UnitNameFlags type = unit_name_to_instance(i->name, &inst); - if (type < 0) - return log_error_errno(type, "Failed to extract instance name from %s: %m", i->name); + UnitNameFlags type = unit_name_to_instance(info->name, &inst); + if (type < 0) { + unit_file_changes_add(changes, n_changes, -EUCLEAN, info->name, NULL); + return log_debug_errno(type, "Failed to extract instance name from \"%s\": %m", info->name); + } if (type == UNIT_NAME_INSTANCE) { r = unit_name_replace_instance(dst, inst, &dst_updated); @@ -1790,10 +1846,16 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * } } - r = unit_validate_alias_symlink_and_warn(dst_updated ?: dst, i->name); - if (r < 0) + r = unit_validate_alias_symlink_or_warn(LOG_DEBUG, dst_updated ?: dst, info->name); + if (r == -ELOOP) /* -ELOOP means self-alias, which we (quietly) ignore */ return r; - + if (r < 0) { + unit_file_changes_add(changes, n_changes, + r == -EINVAL ? -EXDEV : r, + dst_updated ?: dst, + info->name); + return r; + } } *ret_dst = TAKE_PTR(dst_updated); @@ -1801,7 +1863,8 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * } static int install_info_symlink_alias( - UnitFileInstallInfo *i, + LookupScope scope, + UnitFileInstallInfo *info, const LookupPaths *lp, const char *config_path, bool force, @@ -1810,37 +1873,43 @@ static int install_info_symlink_alias( int r = 0, q; - assert(i); + assert(info); assert(lp); assert(config_path); - STRV_FOREACH(s, i->aliases) { + STRV_FOREACH(s, info->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL, *dst_updated = NULL; - q = install_name_printf(i, *s, i->root, &dst); - if (q < 0) - return q; - - q = unit_file_verify_alias(i, dst, &dst_updated); - if (q < 0) + 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; continue; + } + + q = unit_file_verify_alias(info, dst, &dst_updated, changes, n_changes); + if (q == -ELOOP) + continue; + if (q < 0) { + r = r < 0 ? r : q; + continue; + } alias_path = path_make_absolute(dst_updated ?: dst, config_path); if (!alias_path) return -ENOMEM; - q = create_symlink(lp, i->path, alias_path, force, changes, n_changes); - if (r == 0) - r = q; + q = create_symlink(lp, info->name, alias_path, force, changes, n_changes); + r = r < 0 ? r : q; } return r; } static int install_info_symlink_wants( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, - UnitFileInstallInfo *i, + UnitFileInstallInfo *info, const LookupPaths *lp, const char *config_path, char **list, @@ -1853,18 +1922,18 @@ static int install_info_symlink_wants( const char *n; int r = 0, q; - assert(i); + assert(info); assert(lp); assert(config_path); if (strv_isempty(list)) return 0; - if (unit_name_is_valid(i->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)) + if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)) /* Not a template unit. Use the name directly. */ - n = i->name; + n = info->name; - else if (i->default_instance) { + else if (info->default_instance) { UnitFileInstallInfo instance = { .type = _UNIT_FILE_TYPE_INVALID, }; @@ -1872,7 +1941,7 @@ static int install_info_symlink_wants( /* If this is a template, and we have a default instance, use it. */ - r = unit_name_replace_instance(i->name, i->default_instance, &buf); + r = unit_name_replace_instance(info->name, info->default_instance, &buf); if (r < 0) return r; @@ -1895,15 +1964,17 @@ static int install_info_symlink_wants( * the instance from that unit. Cannot be used with non-instance units. */ valid_dst_type = UNIT_NAME_INSTANCE | UNIT_NAME_TEMPLATE; - n = i->name; + n = info->name; } STRV_FOREACH(s, list) { - _cleanup_free_ char *path = NULL, *dst = NULL; + _cleanup_free_ char *dst = NULL; - q = install_name_printf(i, *s, i->root, &dst); - if (q < 0) + q = install_name_printf(scope, info, *s, &dst); + if (q < 0) { + unit_file_changes_add(changes, n_changes, q, *s, NULL); return q; + } if (!unit_name_is_valid(dst, valid_dst_type)) { /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the @@ -1927,23 +1998,27 @@ static int install_info_symlink_wants( continue; } - path = strjoin(config_path, "/", dst, suffix, n); + _cleanup_free_ char *path = strjoin(config_path, "/", dst, suffix, n); if (!path) return -ENOMEM; - q = create_symlink(lp, i->path, path, true, changes, n_changes); + _cleanup_free_ char *target = strjoin("../", info->name); + if (!target) + return -ENOMEM; + + q = create_symlink(lp, target, path, true, changes, n_changes); if (r == 0) r = q; if (unit_file_exists(scope, lp, dst) == 0) - unit_file_changes_add(changes, n_changes, UNIT_FILE_DESTINATION_NOT_PRESENT, dst, i->path); + unit_file_changes_add(changes, n_changes, UNIT_FILE_DESTINATION_NOT_PRESENT, dst, info->path); } return r; } static int install_info_symlink_link( - UnitFileInstallInfo *i, + UnitFileInstallInfo *info, const LookupPaths *lp, const char *config_path, bool force, @@ -1953,28 +2028,28 @@ static int install_info_symlink_link( _cleanup_free_ char *path = NULL; int r; - assert(i); + assert(info); assert(lp); assert(config_path); - assert(i->path); + assert(info->path); - r = in_search_path(lp, i->path); + r = in_search_path(lp, info->path); if (r < 0) return r; if (r > 0) return 0; - path = path_join(config_path, i->name); + path = path_join(config_path, info->name); if (!path) return -ENOMEM; - return create_symlink(lp, i->path, path, force, changes, n_changes); + return create_symlink(lp, info->path, path, force, changes, n_changes); } static int install_info_apply( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, - UnitFileInstallInfo *i, + UnitFileInstallInfo *info, const LookupPaths *lp, const char *config_path, UnitFileChange **changes, @@ -1982,38 +2057,39 @@ static int install_info_apply( int r, q; - assert(i); + assert(info); assert(lp); assert(config_path); - if (i->type != UNIT_FILE_TYPE_REGULAR) + if (info->type != UNIT_FILE_TYPE_REGULAR) return 0; bool force = file_flags & UNIT_FILE_FORCE; - r = install_info_symlink_alias(i, lp, config_path, force, changes, n_changes); - - q = install_info_symlink_wants(scope, file_flags, i, lp, config_path, i->wanted_by, ".wants/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_wants(scope, file_flags, i, lp, config_path, i->required_by, ".requires/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_link(i, lp, config_path, force, changes, n_changes); + r = install_info_symlink_link(info, lp, config_path, force, changes, n_changes); /* Do not count links to the unit file towards the "carries_install_info" count */ - if (r == 0 && q < 0) + if (r < 0) + /* If linking of the file failed, do not try to create other symlinks, + * because they might would pointing to a non-existent or wrong unit. */ + return r; + + r = install_info_symlink_alias(scope, info, lp, config_path, force, changes, n_changes); + + q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes); + if (r == 0) + r = q; + + q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->required_by, ".requires/", changes, n_changes); + if (r == 0) r = q; return r; } static int install_context_apply( - UnitFileScope scope, - UnitFileFlags file_flags, - InstallContext *c, + InstallContext *ctx, const LookupPaths *lp, + UnitFileFlags file_flags, const char *config_path, SearchFlags flags, UnitFileChange **changes, @@ -2022,26 +2098,26 @@ static int install_context_apply( UnitFileInstallInfo *i; int r; - assert(c); + assert(ctx); assert(lp); assert(config_path); - if (ordered_hashmap_isempty(c->will_process)) + if (ordered_hashmap_isempty(ctx->will_process)) return 0; - r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &string_hash_ops); if (r < 0) return r; r = 0; - while ((i = ordered_hashmap_first(c->will_process))) { + while ((i = ordered_hashmap_first(ctx->will_process))) { int q; - q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + q = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name); if (q < 0) return q; - q = install_info_traverse(scope, c, lp, i, flags, NULL); + q = install_info_traverse(ctx, lp, i, flags, NULL); if (q < 0) { if (i->auxiliary) { q = unit_file_changes_add(changes, n_changes, UNIT_FILE_AUXILIARY_FAILED, NULL, i->name); @@ -2068,7 +2144,7 @@ static int install_context_apply( if (i->type != UNIT_FILE_TYPE_REGULAR) continue; - q = install_info_apply(scope, file_flags, i, lp, config_path, changes, n_changes); + q = install_info_apply(ctx->scope, file_flags, i, lp, config_path, changes, n_changes); if (r >= 0) { if (q < 0) r = q; @@ -2081,8 +2157,7 @@ static int install_context_apply( } static int install_context_mark_for_removal( - UnitFileScope scope, - InstallContext *c, + InstallContext *ctx, const LookupPaths *lp, Set **remove_symlinks_to, const char *config_path, @@ -2092,26 +2167,26 @@ static int install_context_mark_for_removal( UnitFileInstallInfo *i; int r; - assert(c); + assert(ctx); assert(lp); assert(config_path); /* Marks all items for removal */ - if (ordered_hashmap_isempty(c->will_process)) + if (ordered_hashmap_isempty(ctx->will_process)) return 0; - r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &string_hash_ops); if (r < 0) return r; - while ((i = ordered_hashmap_first(c->will_process))) { + while ((i = ordered_hashmap_first(ctx->will_process))) { - r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + r = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name); if (r < 0) return r; - r = install_info_traverse(scope, c, lp, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + r = install_info_traverse(ctx, lp, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r == -ENOLINK) { log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name); unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL); @@ -2145,7 +2220,7 @@ static int install_context_mark_for_removal( } int unit_file_mask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -2157,7 +2232,7 @@ int unit_file_mask( 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) @@ -2190,7 +2265,7 @@ int unit_file_mask( } int unit_file_unmask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -2205,7 +2280,7 @@ int unit_file_unmask( 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) @@ -2282,7 +2357,7 @@ int unit_file_unmask( } int unit_file_link( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -2296,7 +2371,7 @@ int unit_file_link( 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) @@ -2382,7 +2457,7 @@ static int path_shall_revert(const LookupPaths *lp, const char *path) { } int unit_file_revert( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **files, UnitFileChange **changes, @@ -2533,7 +2608,7 @@ int unit_file_revert( } int unit_file_add_dependency( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, const char *root_dir, char **files, @@ -2543,13 +2618,13 @@ int unit_file_add_dependency( size_t *n_changes) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i, *target_info; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info, *target_info; const char *config_path; 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)) @@ -2566,7 +2641,7 @@ int unit_file_add_dependency( if (!config_path) return -ENXIO; - r = install_info_discover_and_check(scope, &c, &lp, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, + r = install_info_discover_and_check(&ctx, &lp, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info, changes, n_changes); if (r < 0) return r; @@ -2576,21 +2651,21 @@ int unit_file_add_dependency( STRV_FOREACH(f, files) { char ***l; - r = install_info_discover_and_check(scope, &c, &lp, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(&ctx, &lp, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; - assert(i->type == UNIT_FILE_TYPE_REGULAR); + assert(info->type == UNIT_FILE_TYPE_REGULAR); /* We didn't actually load anything from the unit * file, but instead just add in our new symlink to * create. */ if (dep == UNIT_WANTS) - l = &i->wanted_by; + l = &info->wanted_by; else - l = &i->required_by; + l = &info->required_by; strv_free(*l); *l = strv_new(target_info->name); @@ -2598,42 +2673,30 @@ int unit_file_add_dependency( return -ENOMEM; } - return install_context_apply(scope, file_flags, &c, &lp, config_path, + return install_context_apply(&ctx, &lp, file_flags, config_path, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } -int unit_file_enable( - UnitFileScope scope, - UnitFileFlags file_flags, - const char *root_dir, +static int do_unit_file_enable( + const LookupPaths *lp, + LookupScope scope, + UnitFileFlags flags, + const char *config_path, char **files, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(install_context_done) InstallContext c = {}; - const char *config_path; - UnitFileInstallInfo *i; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; int r; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&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(scope, &c, &lp, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(&ctx, lp, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; - assert(i->type == UNIT_FILE_TYPE_REGULAR); + assert(info->type == UNIT_FILE_TYPE_REGULAR); } /* This will return the number of symlink rules that were @@ -2641,11 +2704,12 @@ int unit_file_enable( is useful to determine whether the passed files had any installation data at all. */ - return install_context_apply(scope, file_flags, &c, &lp, config_path, SEARCH_LOAD, changes, n_changes); + 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, @@ -2653,67 +2717,173 @@ int unit_file_disable( size_t *n_changes) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(install_context_done) InstallContext c = {}; - _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; + return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes); +} + +static int do_unit_file_disable( + const LookupPaths *lp, + LookupScope scope, + UnitFileFlags flags, + const char *config_path, + char **files, + 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(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = install_info_add(&c, *i, NULL, lp.root_dir, /* auxiliary= */ false, NULL); + r = install_info_add(&ctx, *i, NULL, lp->root_dir, /* auxiliary= */ false, NULL); if (r < 0) return r; } - r = install_context_mark_for_removal(scope, &c, &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; + + 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; + + 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 reenabled successfully. */ + + _cleanup_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) + /* 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... */ - l = strv_length(files); - n = newa(char*, l+1); - for (i = 0; i < l; i++) - n[i] = basename(files[i]); - n[i] = NULL; - - r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes); + 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, @@ -2721,13 +2891,13 @@ int unit_file_set_default( size_t *n_changes) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; const char *new_path; 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 */ @@ -2739,42 +2909,42 @@ int unit_file_set_default( if (r < 0) return r; - r = install_info_discover_and_check(scope, &c, &lp, name, 0, &i, changes, n_changes); + r = install_info_discover_and_check(&ctx, &lp, name, 0, &info, changes, n_changes); if (r < 0) return r; new_path = strjoina(lp.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - return create_symlink(&lp, i->path, new_path, flags & UNIT_FILE_FORCE, changes, n_changes); + return create_symlink(&lp, info->name, new_path, flags & UNIT_FILE_FORCE, changes, n_changes); } int unit_file_get_default( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **name) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; char *n; 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); if (r < 0) return r; - r = install_info_discover(scope, &c, &lp, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, NULL, NULL); + r = install_info_discover(&ctx, &lp, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, NULL, NULL); if (r < 0) return r; - r = install_info_may_process(i, &lp, NULL, 0); + r = install_info_may_process(info, &lp, NULL, 0); if (r < 0) return r; - n = strdup(i->name); + n = strdup(info->name); if (!n) return -ENOMEM; @@ -2783,13 +2953,13 @@ int unit_file_get_default( } int unit_file_lookup_state( - UnitFileScope scope, + LookupScope scope, const LookupPaths *lp, const char *name, UnitFileState *ret) { - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; UnitFileState state; int r; @@ -2799,23 +2969,23 @@ int unit_file_lookup_state( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = install_info_discover(scope, &c, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, NULL, NULL); + r = install_info_discover(&ctx, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, NULL, NULL); if (r < 0) return log_debug_errno(r, "Failed to discover unit %s: %m", name); - assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED)); - log_debug("Found unit %s at %s (%s)", name, strna(i->path), - i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask"); + assert(IN_SET(info->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED)); + log_debug("Found unit %s at %s (%s)", name, strna(info->path), + info->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask"); /* Shortcut things, if the caller just wants to know if this unit exists. */ if (!ret) return 0; - switch (i->type) { + switch (info->type) { case UNIT_FILE_TYPE_MASKED: - r = path_is_runtime(lp, i->path, true); + r = path_is_runtime(lp, info->path, true); if (r < 0) return r; @@ -2824,12 +2994,12 @@ int unit_file_lookup_state( case UNIT_FILE_TYPE_REGULAR: /* Check if the name we were querying is actually an alias */ - if (!streq(name, basename(i->path)) && !unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { + if (!streq(name, basename(info->path)) && !unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { state = UNIT_FILE_ALIAS; break; } - r = path_is_generator(lp, i->path); + r = path_is_generator(lp, info->path); if (r < 0) return r; if (r > 0) { @@ -2837,7 +3007,7 @@ int unit_file_lookup_state( break; } - r = path_is_transient(lp, i->path); + r = path_is_transient(lp, info->path); if (r < 0) return r; if (r > 0) { @@ -2848,7 +3018,7 @@ int unit_file_lookup_state( /* Check if any of the Alias= symlinks have been created. * We ignore other aliases, and only check those that would * be created by systemctl enable for this unit. */ - r = find_symlinks_in_scope(scope, lp, i, true, &state); + r = find_symlinks_in_scope(scope, lp, info, true, &state); if (r < 0) return r; if (r > 0) @@ -2856,15 +3026,15 @@ int unit_file_lookup_state( /* Check if the file is known under other names. If it is, * it might be in use. Report that as UNIT_FILE_INDIRECT. */ - r = find_symlinks_in_scope(scope, lp, i, false, &state); + r = find_symlinks_in_scope(scope, lp, info, false, &state); if (r < 0) return r; if (r > 0) state = UNIT_FILE_INDIRECT; else { - if (unit_file_install_info_has_rules(i)) + if (unit_file_install_info_has_rules(info)) state = UNIT_FILE_DISABLED; - else if (unit_file_install_info_has_also(i)) + else if (unit_file_install_info_has_also(info)) state = UNIT_FILE_INDIRECT; else state = UNIT_FILE_STATIC; @@ -2881,7 +3051,7 @@ int unit_file_lookup_state( } int unit_file_get_state( - UnitFileScope scope, + LookupScope scope, const char *root_dir, const char *name, UnitFileState *ret) { @@ -2890,7 +3060,7 @@ int unit_file_get_state( 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); @@ -2900,8 +3070,8 @@ int unit_file_get_state( return unit_file_lookup_state(scope, &lp, name, ret); } -int unit_file_exists(UnitFileScope scope, const LookupPaths *lp, const char *name) { - _cleanup_(install_context_done) InstallContext c = {}; +int unit_file_exists(LookupScope scope, const LookupPaths *lp, const char *name) { + _cleanup_(install_context_done) InstallContext c = { .scope = scope }; int r; assert(lp); @@ -2910,7 +3080,7 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *lp, const char *nam if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = install_info_discover(scope, &c, lp, name, 0, NULL, NULL, NULL); + r = install_info_discover(&c, lp, name, 0, NULL, NULL, NULL); if (r == -ENOENT) return 0; if (r < 0) @@ -2952,17 +3122,17 @@ static int split_pattern_into_name_and_instances(const char *pattern, char **out 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(); @@ -2970,13 +3140,13 @@ static int presets_find_config(UnitFileScope scope, const char *root_dir, char * 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); @@ -3150,7 +3320,7 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char } } -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; @@ -3166,7 +3336,6 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char } static int execute_preset( - UnitFileScope scope, UnitFileFlags file_flags, InstallContext *plus, InstallContext *minus, @@ -3187,7 +3356,7 @@ static int execute_preset( if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - r = install_context_mark_for_removal(scope, minus, lp, &remove_symlinks_to, config_path, changes, n_changes); + r = install_context_mark_for_removal(minus, lp, &remove_symlinks_to, config_path, changes, n_changes); if (r < 0) return r; @@ -3199,9 +3368,10 @@ static int execute_preset( int q; /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(scope, + q = install_context_apply(plus, lp, file_flags | UNIT_FILE_IGNORE_AUXILIARY_FAILURE, - plus, lp, config_path, SEARCH_LOAD, changes, n_changes); + config_path, + SEARCH_LOAD, changes, n_changes); if (r >= 0) { if (q < 0) r = q; @@ -3214,7 +3384,7 @@ static int execute_preset( } static int preset_prepare_one( - UnitFileScope scope, + LookupScope scope, InstallContext *plus, InstallContext *minus, LookupPaths *lp, @@ -3223,20 +3393,20 @@ static int preset_prepare_one( UnitFileChange **changes, size_t *n_changes) { - _cleanup_(install_context_done) InstallContext tmp = {}; + _cleanup_(install_context_done) InstallContext tmp = { .scope = scope }; _cleanup_strv_free_ char **instance_name_list = NULL; - UnitFileInstallInfo *i; + UnitFileInstallInfo *info; int r; if (install_info_find(plus, name) || install_info_find(minus, name)) return 0; - r = install_info_discover(scope, &tmp, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover(&tmp, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; - if (!streq(name, i->name)) { - log_debug("Skipping %s because it is an alias for %s.", name, i->name); + if (!streq(name, info->name)) { + log_debug("Skipping %s because it is an alias for %s.", name, info->name); return 0; } @@ -3247,27 +3417,27 @@ static int preset_prepare_one( if (r > 0) { if (instance_name_list) STRV_FOREACH(s, instance_name_list) { - r = install_info_discover_and_check(scope, plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; } else { - r = install_info_discover_and_check(scope, plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; } } else - r = install_info_discover(scope, minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); return r; } int unit_file_preset( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, const char *root_dir, char **files, @@ -3282,7 +3452,7 @@ int unit_file_preset( 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); @@ -3303,11 +3473,11 @@ int unit_file_preset( return r; } - return execute_preset(scope, file_flags, &plus, &minus, &lp, config_path, files, mode, changes, n_changes); + return execute_preset(file_flags, &plus, &minus, &lp, config_path, files, mode, changes, n_changes); } int unit_file_preset_all( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, const char *root_dir, UnitFilePresetMode mode, @@ -3321,7 +3491,7 @@ int unit_file_preset_all( 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); @@ -3357,14 +3527,14 @@ int unit_file_preset_all( r = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes); if (r < 0 && - !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT)) + !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EBADSLT, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT, -EUNATCH, -EXDEV)) /* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors. * Coordinate with unit_file_dump_changes() above. */ return r; } } - return execute_preset(scope, file_flags, &plus, &minus, &lp, config_path, NULL, mode, changes, n_changes); + return execute_preset(file_flags, &plus, &minus, &lp, config_path, NULL, mode, changes, n_changes); } static UnitFileList* unit_file_list_free_one(UnitFileList *f) { @@ -3382,7 +3552,7 @@ Hashmap* unit_file_list_free(Hashmap *h) { 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, @@ -3392,7 +3562,7 @@ int unit_file_get_list( 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); diff --git a/src/shared/install.h b/src/shared/install.h index cdc5435035..2ba7e8aea0 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -15,6 +15,7 @@ typedef struct UnitFileInstallInfo UnitFileInstallInfo; #include "macro.h" #include "path-lookup.h" #include "strv.h" +#include "unit-file.h" #include "unit-name.h" enum UnitFilePresetMode { @@ -70,7 +71,8 @@ struct UnitFileList { enum UnitFileType { UNIT_FILE_TYPE_REGULAR, - UNIT_FILE_TYPE_SYMLINK, + UNIT_FILE_TYPE_LINKED, + UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_MASKED, _UNIT_FILE_TYPE_MAX, _UNIT_FILE_TYPE_INVALID = -EINVAL, @@ -94,28 +96,28 @@ struct UnitFileInstallInfo { }; int unit_file_enable( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_disable( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_reenable( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, - char **files, + char **names_or_paths, UnitFileChange **changes, size_t *n_changes); int unit_file_preset( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -123,52 +125,52 @@ int unit_file_preset( UnitFileChange **changes, size_t *n_changes); int unit_file_preset_all( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, UnitFilePresetMode mode, UnitFileChange **changes, size_t *n_changes); int unit_file_mask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_unmask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_link( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_revert( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_set_default( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, const char *file, UnitFileChange **changes, size_t *n_changes); int unit_file_get_default( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **name); int unit_file_add_dependency( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -178,22 +180,27 @@ int unit_file_add_dependency( size_t *n_changes); int unit_file_lookup_state( - UnitFileScope scope, + LookupScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); -int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); -int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); +int unit_file_get_state(LookupScope scope, const char *root_dir, const char *filename, UnitFileState *ret); +int unit_file_exists(LookupScope scope, const LookupPaths *paths, const char *name); -int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); +int unit_file_get_list(LookupScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); Hashmap* unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, size_t *n_changes, int type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, size_t n_changes); void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet); -int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst); +int unit_file_verify_alias( + const UnitFileInstallInfo *info, + const char *dst, + char **ret_dst, + UnitFileChange **changes, + size_t *n_changes); typedef struct UnitFilePresetRule UnitFilePresetRule; @@ -204,7 +211,7 @@ typedef struct { } UnitFilePresets; void unit_file_presets_freep(UnitFilePresets *p); -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); const char *unit_file_state_to_string(UnitFileState s) _const_; UnitFileState unit_file_state_from_string(const char *s) _pure_; diff --git a/src/shared/specifier.c b/src/shared/specifier.c index a02012407b..16eb8830dc 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -18,6 +18,7 @@ #include "id128-util.h" #include "macro.h" #include "os-util.h" +#include "path-lookup.h" #include "path-util.h" #include "specifier.h" #include "string-util.h" @@ -162,7 +163,8 @@ int specifier_machine_id(char specifier, const void *data, const char *root, con fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); if (fd < 0) - return fd; + /* Translate error for missing os-release file to EUNATCH. */ + return fd == -ENOENT ? -EUNATCH : fd; r = id128_read_fd(fd, ID128_PLAIN, &id); } else @@ -269,44 +271,54 @@ int specifier_architecture(char specifier, const void *data, const char *root, c } /* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid - * otherwise. We'll return an empty value or NULL in that case from the functions below. */ + * otherwise. We'll return an empty value or NULL in that case from the functions below. But if the + * os-release file is missing, we'll return -EUNATCH. This means that something is seriously wrong with the + * installation. */ + +static int parse_os_release_specifier(const char *root, const char *id, char **ret) { + int r; + + assert(ret); + + /* Translate error for missing os-release file to EUNATCH. */ + r = parse_os_release(root, id, ret); + return r == -ENOENT ? -EUNATCH : r; +} int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - assert(ret); - return parse_os_release(root, "ID", ret); + return parse_os_release_specifier(root, "ID", ret); } int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - assert(ret); - return parse_os_release(root, "VERSION_ID", ret); + return parse_os_release_specifier(root, "VERSION_ID", ret); } int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - assert(ret); - return parse_os_release(root, "BUILD_ID", ret); + return parse_os_release_specifier(root, "BUILD_ID", ret); } int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - assert(ret); - return parse_os_release(root, "VARIANT_ID", ret); + return parse_os_release_specifier(root, "VARIANT_ID", ret); } int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - assert(ret); - return parse_os_release(root, "IMAGE_ID", ret); + return parse_os_release_specifier(root, "IMAGE_ID", ret); } int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - assert(ret); - return parse_os_release(root, "IMAGE_VERSION", ret); + return parse_os_release_specifier(root, "IMAGE_VERSION", ret); } int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); char *t; assert(ret); - t = gid_to_name(getgid()); + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + t = gid_to_name(scope == LOOKUP_SCOPE_USER ? getgid() : 0); if (!t) return -ENOMEM; @@ -315,27 +327,42 @@ int specifier_group_name(char specifier, const void *data, const char *root, con } int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); + gid_t gid; + assert(ret); - if (asprintf(ret, UID_FMT, getgid()) < 0) + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + gid = scope == LOOKUP_SCOPE_USER ? getgid() : 0; + + if (asprintf(ret, UID_FMT, gid) < 0) return -ENOMEM; return 0; } int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); + uid_t uid; char *t; assert(ret); - /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able - * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; - * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with - * specifer_user_id() below. + uid = scope == LOOKUP_SCOPE_USER ? getuid() : 0; + + /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want + * to be able to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + + * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain + * consistent with specifer_user_id() below. */ - t = uid_to_name(getuid()); + t = uid_to_name(uid); if (!t) return -ENOMEM; @@ -344,9 +371,17 @@ int specifier_user_name(char specifier, const void *data, const char *root, cons } int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); + uid_t uid; + assert(ret); - if (asprintf(ret, UID_FMT, getuid()) < 0) + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + uid = scope == LOOKUP_SCOPE_USER ? getuid() : 0; + + if (asprintf(ret, UID_FMT, uid) < 0) return -ENOMEM; return 0; diff --git a/src/shared/specifier.h b/src/shared/specifier.h index 2f2553cfdc..78b10f8467 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -84,11 +84,11 @@ int specifier_var_tmp_dir(char specifier, const void *data, const char *root, co { 'w', specifier_os_version_id, NULL }, \ { 'W', specifier_os_variant_id, NULL } -#define COMMON_CREDS_SPECIFIERS \ - { 'g', specifier_group_name, NULL }, \ - { 'G', specifier_group_id, NULL }, \ - { 'u', specifier_user_name, NULL }, \ - { 'U', specifier_user_id, NULL } +#define COMMON_CREDS_SPECIFIERS(scope) \ + { 'g', specifier_group_name, INT_TO_PTR(scope) }, \ + { 'G', specifier_group_id, INT_TO_PTR(scope) }, \ + { 'u', specifier_user_name, INT_TO_PTR(scope) }, \ + { 'U', specifier_user_id, INT_TO_PTR(scope) } #define COMMON_TMP_SPECIFIERS \ { 'T', specifier_tmp_dir, NULL }, \ diff --git a/src/shared/tests.c b/src/shared/tests.c index a624fd7d10..70fbbb45a5 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -25,6 +25,7 @@ #include "cgroup-util.h" #include "env-file.h" #include "env-util.h" +#include "fd-util.h" #include "fs-util.h" #include "log.h" #include "namespace-util.h" @@ -33,6 +34,7 @@ #include "random-util.h" #include "strv.h" #include "tests.h" +#include "tmpfile-util.h" char* setup_fake_runtime_dir(void) { char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; @@ -132,6 +134,23 @@ int log_tests_skipped_errno(int r, const char *message) { return EXIT_TEST_SKIP; } +int write_tmpfile(char *pattern, const char *contents) { + _cleanup_close_ int fd = -1; + + assert(pattern); + assert(contents); + + fd = mkostemp_safe(pattern); + if (fd < 0) + return fd; + + ssize_t l = strlen(contents); + errno = 0; + if (write(fd, contents, l) != l) + return errno_or_else(EIO); + return 0; +} + bool have_namespaces(void) { siginfo_t si = {}; pid_t pid; diff --git a/src/shared/tests.h b/src/shared/tests.h index ef6acd368e..ade527590b 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -30,6 +30,8 @@ void test_setup_logging(int level); int log_tests_skipped(const char *message); int log_tests_skipped_errno(int r, const char *message); +int write_tmpfile(char *pattern, const char *contents); + bool have_namespaces(void); /* We use the small but non-trivial limit here */ diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c index 6ef6cc54a8..446dfd7dd6 100644 --- a/src/systemctl/systemctl-edit.c +++ b/src/systemctl/systemctl-edit.c @@ -37,9 +37,9 @@ int verb_cat(int argc, char *argv[], void *userdata) { if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot remotely cat units."); - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, arg_root); if (r < 0) - return log_error_errno(r, "Failed to determine unit paths: %m"); + return r; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -99,7 +99,7 @@ int verb_cat(int argc, char *argv[], void *userdata) { ansi_highlight_red(), ansi_highlight_red(), ansi_highlight_red(), - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user", ansi_normal()); r = cat_files(fragment_path, dropin_paths, 0); @@ -406,8 +406,8 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { if (!path) { if (!arg_force) { log_info("Run 'systemctl edit%s --force --full %s' to create a new unit.", - arg_scope == UNIT_FILE_GLOBAL ? " --global" : - arg_scope == UNIT_FILE_USER ? " --user" : "", + arg_scope == LOOKUP_SCOPE_GLOBAL ? " --global" : + arg_scope == LOOKUP_SCOPE_USER ? " --user" : "", *name); return -ENOENT; } @@ -507,9 +507,9 @@ int verb_edit(int argc, char *argv[], void *userdata) { if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units remotely."); - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, arg_root); if (r < 0) - return log_error_errno(r, "Failed to determine unit paths: %m"); + return r; r = mac_selinux_init(); if (r < 0) diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c index b18c516b26..a1ca837d4d 100644 --- a/src/systemctl/systemctl-enable.c +++ b/src/systemctl/systemctl-enable.c @@ -141,7 +141,7 @@ int verb_enable(int argc, char *argv[], void *userdata) { if (STR_IN_SET(verb, "mask", "unmask")) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, arg_root); if (r < 0) return r; diff --git a/src/systemctl/systemctl-is-enabled.c b/src/systemctl/systemctl-is-enabled.c index c280d55d69..6f3d0b6eaa 100644 --- a/src/systemctl/systemctl-is-enabled.c +++ b/src/systemctl/systemctl-is-enabled.c @@ -18,7 +18,7 @@ static int show_installation_targets_client_side(const char *name) { flags = UNIT_FILE_DRY_RUN | (arg_runtime ? UNIT_FILE_RUNTIME : 0); - r = unit_file_disable(UNIT_FILE_SYSTEM, flags, NULL, p, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, flags, NULL, p, &changes, &n_changes); if (r < 0) return log_error_errno(r, "Failed to get file links for %s: %m", name); diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 0f3099b7b5..24e43321cf 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -762,7 +762,7 @@ static void print_status_info( getuid(), get_output_flags() | OUTPUT_BEGIN_NEWLINE, SD_JOURNAL_LOCAL_ONLY, - arg_scope == UNIT_FILE_SYSTEM, + arg_scope == LOOKUP_SCOPE_SYSTEM, ellipsized); if (i->need_daemon_reload) diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index 7a6cef3c5e..cfd0078964 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -246,10 +246,10 @@ int verb_start_special(int argc, char *argv[], void *userdata) { int verb_start_system_special(int argc, char *argv[], void *userdata) { /* Like start_special above, but raises an error when running in user mode */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Bad action for %s mode.", - arg_scope == UNIT_FILE_GLOBAL ? "--global" : "--user"); + arg_scope == LOOKUP_SCOPE_GLOBAL ? "--global" : "--user"); return verb_start_special(argc, argv, userdata); } diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index 9b215c2e8a..42a5b086c3 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -168,8 +168,8 @@ fail: BUS_ERROR_UNIT_MASKED, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE)) log_error("See %s logs and 'systemctl%s status%s %s' for details.", - arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + arg_scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user", + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user", name[0] == '-' ? " --" : "", name); @@ -242,7 +242,7 @@ static const char** make_extra_args(const char *extra_args[static 4]) { assert(extra_args); - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) extra_args[n++] = "--user"; if (arg_transport == BUS_TRANSPORT_REMOTE) { diff --git a/src/systemctl/systemctl-sysv-compat.c b/src/systemctl/systemctl-sysv-compat.c index 017dba2034..f6889993ed 100644 --- a/src/systemctl/systemctl-sysv-compat.c +++ b/src/systemctl/systemctl-sysv-compat.c @@ -116,7 +116,7 @@ int enable_sysv_units(const char *verb, char **args) { /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return 0; if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0) @@ -128,7 +128,7 @@ int enable_sysv_units(const char *verb, char **args) { "is-enabled")) return 0; - r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); + r = lookup_paths_init_or_warn(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); if (r < 0) return r; diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c index 0334b8c1fc..6a71bce5fb 100644 --- a/src/systemctl/systemctl-util.c +++ b/src/systemctl/systemctl-util.c @@ -46,7 +46,7 @@ int acquire_bus(BusFocus focus, sd_bus **ret) { if (!buses[focus]) { bool user; - user = arg_scope != UNIT_FILE_SYSTEM; + user = arg_scope != LOOKUP_SCOPE_SYSTEM; if (focus == BUS_MANAGER) r = bus_connect_transport_systemd(arg_transport, arg_host, user, &buses[focus]); @@ -73,7 +73,7 @@ void ask_password_agent_open_maybe(void) { if (arg_dry_run) return; - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return; ask_password_agent_open_if_enabled(arg_transport, arg_ask_password); @@ -82,7 +82,7 @@ void ask_password_agent_open_maybe(void) { void polkit_agent_open_maybe(void) { /* Open the polkit agent as a child process if necessary */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return; polkit_agent_open_if_enabled(arg_transport, arg_ask_password); @@ -380,7 +380,7 @@ void warn_unit_file_changed(const char *unit) { ansi_highlight_red(), ansi_normal(), unit, - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user"); } int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) { @@ -814,7 +814,7 @@ bool install_client_side(void) { if (!isempty(arg_root)) return true; - if (arg_scope == UNIT_FILE_GLOBAL) + if (arg_scope == LOOKUP_SCOPE_GLOBAL) return true; /* Unsupported environment variable, mostly for debugging purposes */ diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index e81703ebe8..97ec51b7d9 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -66,7 +66,7 @@ char **arg_properties = NULL; bool arg_all = false; enum dependency arg_dependency = DEPENDENCY_FORWARD; const char *_arg_job_mode = NULL; -UnitFileScope arg_scope = UNIT_FILE_SYSTEM; +LookupScope arg_scope = LOOKUP_SCOPE_SYSTEM; bool arg_wait = false; bool arg_no_block = false; int arg_legend = -1; /* -1: true, unless --quiet is passed, 1: true */ @@ -616,15 +616,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_USER: - arg_scope = UNIT_FILE_USER; + arg_scope = LOOKUP_SCOPE_USER; break; case ARG_SYSTEM: - arg_scope = UNIT_FILE_SYSTEM; + arg_scope = LOOKUP_SCOPE_SYSTEM; break; case ARG_GLOBAL: - arg_scope = UNIT_FILE_GLOBAL; + arg_scope = LOOKUP_SCOPE_GLOBAL; break; case ARG_WAIT: @@ -924,10 +924,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* If we are in --user mode, there's no point in talking to PolicyKit or the infra to query system * passwords */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) arg_ask_password = false; - if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM) + if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != LOOKUP_SCOPE_SYSTEM) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot access user instance remotely."); diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index d6b9d7495c..7507398c4a 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -51,7 +51,7 @@ extern char **arg_properties; extern bool arg_all; extern enum dependency arg_dependency; extern const char *_arg_job_mode; -extern UnitFileScope arg_scope; +extern LookupScope arg_scope; extern bool arg_wait; extern bool arg_no_block; extern int arg_legend; diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index a81df6600b..14ae873dc0 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -747,7 +747,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name); + r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, lp, name); if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) { log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name); continue; @@ -891,9 +891,9 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) assert_se(arg_dest = dest_late); - r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); + r = lookup_paths_init_or_warn(&lp, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); if (r < 0) - return log_error_errno(r, "Failed to find lookup paths: %m"); + return r; all_services = hashmap_new(&string_hash_ops); if (!all_services) diff --git a/src/test/test-bpf-firewall.c b/src/test/test-bpf-firewall.c index cbcb525f52..8f4b485073 100644 --- a/src/test/test-bpf-firewall.c +++ b/src/test/test-bpf-firewall.c @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) { /* The simple tests succeeded. Now let's try full unit-based use-case. */ - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(u = unit_new(m, sizeof(Service))); diff --git a/src/test/test-bpf-foreign-programs.c b/src/test/test-bpf-foreign-programs.c index 377019f193..061426f287 100644 --- a/src/test/test-bpf-foreign-programs.c +++ b/src/test/test-bpf-foreign-programs.c @@ -301,7 +301,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(test_bpf_cgroup_programs(m, diff --git a/src/test/test-bpf-lsm.c b/src/test/test-bpf-lsm.c index 480865d0bc..e0e1b7f38f 100644 --- a/src/test/test-bpf-lsm.c +++ b/src/test/test-bpf-lsm.c @@ -90,7 +90,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); /* We need to enable access to the filesystem where the binary is so we diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index 6058f32b0e..57483f72c2 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -42,7 +42,7 @@ TEST_RET(cgroup_mask, .sd_booted = true) { assert_se(get_testdata_dir("units", &unit_dir) >= 0); assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (IN_SET(r, -EPERM, -EACCES)) { log_error_errno(r, "manager_new: %m"); return log_tests_skipped("cannot create manager"); diff --git a/src/test/test-cgroup-unit-default.c b/src/test/test-cgroup-unit-default.c index 4c8de1e382..94201a3ccc 100644 --- a/src/test/test-cgroup-unit-default.c +++ b/src/test/test-cgroup-unit-default.c @@ -26,7 +26,7 @@ TEST_RET(default_memory_low, .sd_booted = true) { assert_se(get_testdata_dir("units", &unit_dir) >= 0); assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (IN_SET(r, -EPERM, -EACCES)) { log_error_errno(r, "manager_new: %m"); return log_tests_skipped("cannot create manager"); diff --git a/src/test/test-engine.c b/src/test/test-engine.c index cd01d1358b..ef055360fb 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r >= 0); diff --git a/src/test/test-env-file.c b/src/test/test-env-file.c index f97206b4d6..461a0f0810 100644 --- a/src/test/test-env-file.c +++ b/src/test/test-env-file.c @@ -9,7 +9,12 @@ #include "tests.h" #include "tmpfile-util.h" +/* In case of repeating keys, later entries win. */ + #define env_file_1 \ + "a=a\n" \ + "a=b\n" \ + "a=b\n" \ "a=a\n" \ "b=b\\\n" \ "c\n" \ @@ -55,18 +60,11 @@ TEST(load_env_file_1) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_1) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_1, strlen(env_file_1)) == strlen(env_file_1)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a=a")); assert_se(streq(data[1], "b=bc")); assert_se(streq(data[2], "d=de f")); @@ -77,50 +75,30 @@ TEST(load_env_file_1) { } TEST(load_env_file_2) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_2) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_2, strlen(env_file_2)) == strlen(env_file_2)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a=a")); assert_se(data[1] == NULL); } TEST(load_env_file_3) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_3) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_3, strlen(env_file_3)) == strlen(env_file_3)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(data == NULL); } TEST(load_env_file_4) { - _cleanup_strv_free_ char **data = NULL; _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; - int r; + assert_se(write_tmpfile(name, env_file_4) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_4, strlen(env_file_4)) == strlen(env_file_4)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg")); assert_se(streq(data[1], "MODULE_0=coretemp")); assert_se(streq(data[2], "MODULE_1=f71882fg")); @@ -128,36 +106,22 @@ TEST(load_env_file_4) { } TEST(load_env_file_5) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_5) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_5, strlen(env_file_5)) == strlen(env_file_5)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a=")); assert_se(streq(data[1], "b=")); assert_se(data[2] == NULL); } TEST(load_env_file_6) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_6) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_6, strlen(env_file_6)) == strlen(env_file_6)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a= n t x y '")); assert_se(streq(data[1], "b=$'")); assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n")); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index ba1be2b453..6fce05bb58 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -1117,7 +1117,7 @@ typedef struct test_entry { #define entry(x) {x, #x} -static int run_tests(UnitFileScope scope, const test_entry tests[], char **patterns) { +static int run_tests(LookupScope scope, const test_entry tests[], char **patterns) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -1239,11 +1239,11 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); - r = run_tests(UNIT_FILE_USER, user_tests, argv + 1); + r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1); if (r != 0) return r; - r = run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); + r = run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1); if (r != 0) return r; @@ -1265,11 +1265,11 @@ int main(int argc, char *argv[]) { can_unshare = false; - r = run_tests(UNIT_FILE_USER, user_tests, argv + 1); + r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1); if (r != 0) return r; - return run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); + return run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1); #else return 0; #endif diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 3e626dbe93..3e98d94019 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -109,8 +109,7 @@ TEST(parse_env_file) { "eleven", &eleven, "twelve", &twelve, "thirteen", &thirteen); - - assert_se(r >= 0); + assert_se(r == 0); log_info("one=[%s]", strna(one)); log_info("two=[%s]", strna(two)); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index f718689c3a..a36536b85b 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -23,41 +23,41 @@ TEST(basic_mask_and_enable) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", NULL) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/a.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/b.service"); assert_se(symlink("a.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); p = strjoina(root, "/usr/lib/systemd/system/c.service"); assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); p = strjoina(root, "/usr/lib/systemd/system/d.service"); assert_se(symlink("c.service", p) >= 0); /* This one is interesting, as d follows a relative, then an absolute symlink */ - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_mask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/dev/null")); @@ -67,17 +67,17 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); /* Enabling a masked unit should fail! */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == -ERFKILL); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == -ERFKILL); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service"); @@ -85,27 +85,27 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[0].source, "../a.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Enabling it again should succeed but be a NOP */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); @@ -113,63 +113,63 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Disabling a disabled unit must succeed but be a NOP */ - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; /* Let's enable this indirectly via a symlink */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[0].source, "../a.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Let's try to reenable */ - assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("b.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_reenable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("b.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); assert_se(streq(changes[0].path, p)); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[1].source, "../a.service")); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Test masking with relative symlinks */ p = strjoina(root, "/usr/lib/systemd/system/e.service"); assert_se(symlink("../../../../../../dev/null", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); assert_se(unlink(p) == 0); assert_se(symlink("/usr/../dev/null", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); assert_se(unlink(p) == 0); @@ -180,13 +180,13 @@ TEST(basic_mask_and_enable) { "[Install]\n" "WantedBy=x.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/f.service")); + assert_se(streq(changes[0].source, "../f.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/x.target.wants/f.service"); assert_se(streq(changes[0].path, p)); assert_se(changes[1].type_or_errno == UNIT_FILE_DESTINATION_NOT_PRESENT); @@ -196,7 +196,7 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(linked_units) { @@ -236,9 +236,9 @@ TEST(linked_units) { "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked2.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked3.service", NULL) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/linked2.service"); assert_se(symlink("/opt/linked2.service", p) >= 0); @@ -246,12 +246,12 @@ TEST(linked_units) { p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked3.service"); assert_se(symlink("/opt/linked3.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED); /* First, let's link the unit into the search path */ - assert_se(unit_file_link(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_link(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/opt/linked.service")); @@ -260,10 +260,10 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED); /* Let's unlink it from the search path again */ - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); @@ -271,16 +271,17 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT); /* Now, let's not just link it, but also enable it */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service"); q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); for (i = 0 ; i < n_changes; i++) { assert_se(changes[i].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[i].source, "/opt/linked.service")); + assert_se(STR_IN_SET(changes[i].source, + "../linked.service", "/opt/linked.service")); if (p && streq(changes[i].path, p)) p = NULL; @@ -293,10 +294,10 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED); /* And let's unlink it again */ - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service"); q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); @@ -314,15 +315,16 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked2.service"); q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked2.service"); for (i = 0 ; i < n_changes; i++) { assert_se(changes[i].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[i].source, "/opt/linked2.service")); + assert_se(STR_IN_SET(changes[i].source, + "../linked2.service", "/opt/linked2.service")); if (p && streq(changes[i].path, p)) p = NULL; @@ -335,12 +337,12 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked3.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked3.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(startswith(changes[0].path, root)); assert_se(endswith(changes[0].path, "linked3.service")); - assert_se(streq(changes[0].source, "/opt/linked3.service")); + assert_se(streq(changes[0].source, "../linked3.service")); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; } @@ -357,27 +359,27 @@ TEST(default) { p = strjoina(root, "/usr/lib/systemd/system/test-default.target"); assert_se(symlink("test-default-real.target", p) >= 0); - assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + assert_se(unit_file_get_default(LOOKUP_SCOPE_SYSTEM, root, &def) == -ENOENT); - assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT); + assert_se(unit_file_set_default(LOOKUP_SCOPE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == -ENOENT); assert_se(streq_ptr(changes[0].path, "idontexist.target")); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + assert_se(unit_file_get_default(LOOKUP_SCOPE_SYSTEM, root, &def) == -ENOENT); - assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0); + assert_se(unit_file_set_default(LOOKUP_SCOPE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target")); + assert_se(streq(changes[0].source, "test-default-real.target")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR "/" SPECIAL_DEFAULT_TARGET); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0); + assert_se(unit_file_get_default(LOOKUP_SCOPE_SYSTEM, root, &def) >= 0); assert_se(streq_ptr(def, "test-default-real.target")); } @@ -398,10 +400,10 @@ TEST(add_dependency) { p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service"); assert_se(symlink("real-add-dependency-test-service.service", p) >= 0); - assert_se(unit_file_add_dependency(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0); + assert_se(unit_file_add_dependency(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service")); + assert_se(streq(changes[0].source, "../real-add-dependency-test-service.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); @@ -416,10 +418,10 @@ TEST(template_enable) { log_info("== %s ==", __func__); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/template@.service"); assert_se(write_string_file(p, @@ -430,96 +432,97 @@ TEST(template_enable) { p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service"); assert_se(symlink("template@.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); log_info("== %s with template@.service enabled ==", __func__); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + assert_se(streq(changes[0].source, "../template@.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@def.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); log_info("== %s with template@foo.service enabled ==", __func__); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + assert_se(streq(changes[0].source, "../template@foo.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@foo.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0); + assert_se(state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); log_info("== %s with template-symlink@quux.service enabled ==", __func__); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + assert_se(streq(changes[0].source, "../template@quux.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@quux.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(indirect) { @@ -528,9 +531,9 @@ TEST(indirect) { UnitFileState state; const char *p; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirecta.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectb.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectc.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/indirecta.service"); assert_se(write_string_file(p, @@ -545,24 +548,24 @@ TEST(indirect) { p = strjoina(root, "/usr/lib/systemd/system/indirectc.service"); assert_se(symlink("indirecta.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service")); + assert_se(streq(changes[0].source, "../indirectb.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service"); @@ -580,8 +583,8 @@ TEST(preset_and_list) { UnitFileList *fl; Hashmap *h; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); assert_se(write_string_file(p, @@ -598,22 +601,22 @@ TEST(preset_and_list) { "enable *-yes.*\n" "disable *\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service")); + assert_se(streq(changes[0].source, "../preset-yes.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service"); @@ -621,18 +624,18 @@ TEST(preset_and_list) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset_all(LOOKUP_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes > 0); @@ -641,7 +644,7 @@ TEST(preset_and_list) { for (i = 0; i < n_changes; i++) { if (changes[i].type_or_errno == UNIT_FILE_SYMLINK) { - assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service")); + assert_se(streq(changes[i].source, "../preset-yes.service")); assert_se(streq(changes[i].path, p)); } else assert_se(changes[i].type_or_errno == UNIT_FILE_UNLINK); @@ -650,17 +653,17 @@ TEST(preset_and_list) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(h = hashmap_new(&string_hash_ops)); - assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h, NULL, NULL) >= 0); + assert_se(unit_file_get_list(LOOKUP_SCOPE_SYSTEM, root, h, NULL, NULL) >= 0); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); q = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); HASHMAP_FOREACH(fl, h) { - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, basename(fl->path), &state) >= 0); assert_se(fl->state == state); if (streq(fl->path, p)) { @@ -684,17 +687,17 @@ TEST(revert) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "xx.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "yy.service", NULL) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/xx.service"); assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "xx.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); /* Initially there's nothing to revert */ - assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_revert(LOOKUP_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -703,7 +706,7 @@ TEST(revert) { assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0); /* Revert the override file */ - assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_revert(LOOKUP_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); @@ -714,7 +717,7 @@ TEST(revert) { assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); /* Revert the dropin file */ - assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_revert(LOOKUP_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); @@ -732,8 +735,8 @@ TEST(preset_order) { const char *p; UnitFileState state; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/prefix-1.service"); assert_se(write_string_file(p, @@ -751,48 +754,48 @@ TEST(preset_order) { "disable prefix-*.service\n" "enable prefix-2.service\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service")); + assert_se(streq(changes[0].source, "../prefix-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/prefix-1.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); } TEST(static_instance) { UnitFileState state; const char *p; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/static-instance@.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/static-instance@foo.service"); assert_se(symlink("static-instance@.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC); } TEST(with_dropin) { @@ -801,11 +804,11 @@ TEST(with_dropin) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1.service"); assert_se(write_string_file(p, @@ -817,7 +820,7 @@ TEST(with_dropin) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"); assert_se(write_string_file(p, @@ -829,7 +832,7 @@ TEST(with_dropin) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3.service"); assert_se(write_string_file(p, @@ -841,7 +844,7 @@ TEST(with_dropin) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4a.service"); assert_se(write_string_file(p, @@ -853,21 +856,21 @@ TEST(with_dropin) { "[Install]\n" "Also=with-dropin-4b.service\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4b.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service")); + assert_se(streq(changes[0].source, "../with-dropin-1.service")); + assert_se(streq(changes[1].source, "../with-dropin-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service"); @@ -875,13 +878,13 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2.service"), &changes, &n_changes) == 1); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2.service"), &changes, &n_changes) == 1); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service")); - assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service")); + assert_se(streq(changes[0].source, "../with-dropin-2.service")); + assert_se(streq(changes[1].source, "../with-dropin-2.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service"); @@ -889,13 +892,13 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3.service"), &changes, &n_changes) == 1); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3.service"), &changes, &n_changes) == 1); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service")); + assert_se(streq(changes[0].source, "../with-dropin-3.service")); + assert_se(streq(changes[1].source, "../with-dropin-3.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service"); @@ -903,13 +906,13 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 2); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 2); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service")); + assert_se(streq(changes[0].source, "../with-dropin-4a.service")); + assert_se(streq(changes[1].source, "../with-dropin-4b.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4a.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service"); @@ -917,11 +920,11 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(with_dropin_template) { @@ -930,9 +933,9 @@ TEST(with_dropin_template) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1@.service"); assert_se(write_string_file(p, @@ -944,7 +947,7 @@ TEST(with_dropin_template) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2@.service"); assert_se(write_string_file(p, @@ -956,7 +959,7 @@ TEST(with_dropin_template) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3@.service"); assert_se(write_string_file(p, @@ -969,14 +972,14 @@ TEST(with_dropin_template) { "[Install]\n" "DefaultInstance=instance-2\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1@instance-1.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1@instance-1.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service")); + assert_se(streq(changes[0].source, "../with-dropin-1@instance-1.service")); + assert_se(streq(changes[1].source, "../with-dropin-1@instance-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1@instance-1.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service"); @@ -984,12 +987,12 @@ TEST(with_dropin_template) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-1.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-1.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service")); + assert_se(streq(changes[0].source, "../with-dropin-2@instance-1.service")); + assert_se(streq(changes[1].source, "../with-dropin-2@instance-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-1.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service"); @@ -997,29 +1000,29 @@ TEST(with_dropin_template) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service")); + assert_se(streq(changes[0].source, "../with-dropin-2@instance-2.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-2.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service")); + assert_se(streq(changes[0].source, "../with-dropin-3@.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3@instance-2.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@instance-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(preset_multiple_instances) { @@ -1035,7 +1038,7 @@ TEST(preset_multiple_instances) { "DefaultInstance=def\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); assert_se(write_string_file(p, @@ -1043,11 +1046,11 @@ TEST(preset_multiple_instances) { "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */ "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED); /* Preset a single instantiated unit specified in the list */ - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service"); @@ -1055,7 +1058,7 @@ TEST(preset_multiple_instances) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service"); @@ -1064,17 +1067,17 @@ TEST(preset_multiple_instances) { changes = NULL; n_changes = 0; /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */ - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset_all(LOOKUP_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes > 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); unit_file_changes_free(changes, n_changes); } @@ -1091,7 +1094,7 @@ static void verify_one( if (i != last_info) log_info("-- %s --", (last_info = i)->name); - r = unit_file_verify_alias(i, alias, &alias2); + r = unit_file_verify_alias(i, alias, &alias2, NULL, NULL); log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s", i->name, alias, r, expected, alias2 ? " [" : "", strempty(alias2), diff --git a/src/test/test-install.c b/src/test/test-install.c index 7a0beb2d24..6c5a036465 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -32,13 +32,13 @@ int main(int argc, char* argv[]) { test_setup_logging(LOG_DEBUG); h = hashmap_new(&string_hash_ops); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); + r = unit_file_get_list(LOOKUP_SCOPE_SYSTEM, NULL, h, NULL, NULL); assert_se(r == 0); HASHMAP_FOREACH(p, h) { UnitFileState s = _UNIT_FILE_STATE_INVALID; - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(p->path), &s); assert_se((r < 0 && p->state == UNIT_FILE_BAD) || (p->state == s)); @@ -52,18 +52,18 @@ int main(int argc, char* argv[]) { log_info("/*** enable **/"); - r = unit_file_enable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** enable2 **/"); - r = unit_file_enable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); @@ -71,13 +71,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); @@ -85,16 +85,16 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_mask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** mask2 ***/"); - r = unit_file_mask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); @@ -102,16 +102,16 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_unmask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** unmask2 ***/"); - r = unit_file_unmask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); @@ -119,13 +119,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_mask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); @@ -133,16 +133,16 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** disable2 ***/"); - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); @@ -150,13 +150,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_unmask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); @@ -164,13 +164,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_enable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); @@ -178,26 +178,26 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; - r = unit_file_link(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_link(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); @@ -205,26 +205,26 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; - r = unit_file_link(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_link(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); @@ -232,13 +232,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_reenable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_reenable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); @@ -246,25 +246,25 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); log_info("/*** preset files ***/"); changes = NULL; n_changes = 0; - r = unit_file_preset(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, UNIT_FILE_PRESET_FULL, &changes, &n_changes); + r = unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, UNIT_FILE_PRESET_FULL, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); diff --git a/src/test/test-load-fragment.c b/src/test/test-load-fragment.c index 52f6096eee..c3206e1e7a 100644 --- a/src/test/test-load-fragment.c +++ b/src/test/test-load-fragment.c @@ -42,7 +42,7 @@ TEST_RET(unit_file_get_set) { h = hashmap_new(&string_hash_ops); assert_se(h); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); + r = unit_file_get_list(LOOKUP_SCOPE_SYSTEM, NULL, h, NULL, NULL); if (IN_SET(r, -EPERM, -EACCES)) return log_tests_skipped_errno(r, "unit_file_get_list"); @@ -101,7 +101,7 @@ TEST(config_parse_exec) { _cleanup_(manager_freep) Manager *m = NULL; _cleanup_(unit_freep) Unit *u = NULL; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -445,7 +445,7 @@ TEST(config_parse_log_extra_fields) { _cleanup_(unit_freep) Unit *u = NULL; ExecContext c = {}; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -510,59 +510,74 @@ TEST(install_printf, .sd_booted = true) { assert_se(user = uid_to_name(getuid())); assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0); -#define expect(src, pattern, result) \ +#define expect(scope, src, pattern, result) \ do { \ - _cleanup_free_ char *t = NULL; \ - _cleanup_free_ char \ - *d1 = strdup(i.name), \ - *d2 = strdup(i.path); \ - assert_se(install_name_printf(&src, pattern, NULL, &t) >= 0 || !result); \ + _cleanup_free_ char *t = NULL, \ + *d1 = ASSERT_PTR(strdup(i.name)), \ + *d2 = ASSERT_PTR(strdup(i.path)); \ + int r = install_name_printf(scope, &src, pattern, &t); \ + assert_se(result ? r >= 0 : r < 0); \ memzero(i.name, strlen(i.name)); \ memzero(i.path, strlen(i.path)); \ - assert_se(d1 && d2); \ if (result) { \ printf("%s\n", t); \ assert_se(streq(t, result)); \ - } else assert_se(t == NULL); \ + } else \ + assert_se(!t); \ strcpy(i.name, d1); \ strcpy(i.path, d2); \ } while (false) - expect(i, "%n", "name.service"); - expect(i, "%N", "name"); - expect(i, "%p", "name"); - expect(i, "%i", ""); - expect(i, "%j", "name"); - expect(i, "%g", group); - expect(i, "%G", gid); - expect(i, "%u", user); - expect(i, "%U", uid); + expect(LOOKUP_SCOPE_SYSTEM, i, "%n", "name.service"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%N", "name"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%p", "name"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%i", ""); + expect(LOOKUP_SCOPE_SYSTEM, i, "%j", "name"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%g", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%G", "0"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%u", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%U", "0"); - expect(i, "%m", mid); - expect(i, "%b", bid); - expect(i, "%H", host); + expect(LOOKUP_SCOPE_SYSTEM, i, "%m", mid); + expect(LOOKUP_SCOPE_SYSTEM, i, "%b", bid); + expect(LOOKUP_SCOPE_SYSTEM, i, "%H", host); - expect(i2, "%g", group); - expect(i2, "%G", gid); - expect(i2, "%u", user); - expect(i2, "%U", uid); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%g", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%G", "0"); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%u", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%U", "0"); - expect(i3, "%n", "name@inst.service"); - expect(i3, "%N", "name@inst"); - expect(i3, "%p", "name"); - expect(i3, "%g", group); - expect(i3, "%G", gid); - expect(i3, "%u", user); - expect(i3, "%U", uid); + expect(LOOKUP_SCOPE_USER, i2, "%g", group); + expect(LOOKUP_SCOPE_USER, i2, "%G", gid); + expect(LOOKUP_SCOPE_USER, i2, "%u", user); + expect(LOOKUP_SCOPE_USER, i2, "%U", uid); - expect(i3, "%m", mid); - expect(i3, "%b", bid); - expect(i3, "%H", host); + /* gcc-12.0.1-0.9.fc36.x86_64 insist that streq(…, NULL) is called, + * even though the call is inside of a conditional where the pointer is checked. :( */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" + expect(LOOKUP_SCOPE_GLOBAL, i2, "%g", NULL); + expect(LOOKUP_SCOPE_GLOBAL, i2, "%G", NULL); + expect(LOOKUP_SCOPE_GLOBAL, i2, "%u", NULL); + expect(LOOKUP_SCOPE_GLOBAL, i2, "%U", NULL); +#pragma GCC diagnostic pop - expect(i4, "%g", group); - expect(i4, "%G", gid); - expect(i4, "%u", user); - expect(i4, "%U", uid); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%n", "name@inst.service"); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%N", "name@inst"); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%p", "name"); + expect(LOOKUP_SCOPE_USER, i3, "%g", group); + expect(LOOKUP_SCOPE_USER, i3, "%G", gid); + expect(LOOKUP_SCOPE_USER, i3, "%u", user); + expect(LOOKUP_SCOPE_USER, i3, "%U", uid); + + expect(LOOKUP_SCOPE_SYSTEM, i3, "%m", mid); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%b", bid); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%H", host); + + expect(LOOKUP_SCOPE_USER, i4, "%g", group); + expect(LOOKUP_SCOPE_USER, i4, "%G", gid); + expect(LOOKUP_SCOPE_USER, i4, "%u", user); + expect(LOOKUP_SCOPE_USER, i4, "%U", uid); } static uint64_t make_cap(int cap) { @@ -791,7 +806,7 @@ TEST(config_parse_unit_env_file) { _cleanup_strv_free_ char **files = NULL; int r; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -924,7 +939,7 @@ TEST(unit_is_recursive_template_dependency) { Unit *u; int r; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; diff --git a/src/test/test-os-util.c b/src/test/test-os-util.c index eb5466259a..2cee6470c4 100644 --- a/src/test/test-os-util.c +++ b/src/test/test-os-util.c @@ -2,8 +2,11 @@ #include +#include "fs-util.h" #include "log.h" #include "os-util.h" +#include "string-util.h" +#include "strv.h" #include "tests.h" TEST(path_is_os_tree) { @@ -12,4 +15,61 @@ TEST(path_is_os_tree) { assert_se(path_is_os_tree("/idontexist") == -ENOENT); } +TEST(parse_os_release) { + /* Let's assume that we're running in a valid system, so os-release is available */ + _cleanup_free_ char *id = NULL, *id2 = NULL, *name = NULL, *foobar = NULL; + assert_se(parse_os_release(NULL, "ID", &id) == 0); + log_info("ID: %s", id); + + assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0); + assert_se(parse_os_release(NULL, "ID", &id2) == 0); + log_info("ID: %s", strnull(id2)); + + _cleanup_(unlink_tempfilep) char tmpfile[] = "/tmp/test-os-util.XXXXXX"; + assert_se(write_tmpfile(tmpfile, + "ID=the-id \n" + "NAME=the-name") == 0); + + assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1) == 0); + assert_se(parse_os_release(NULL, "ID", &id, "NAME", &name) == 0); + log_info("ID: %s NAME: %s", id, name); + assert_se(streq(id, "the-id")); + assert_se(streq(name, "the-name")); + + _cleanup_(unlink_tempfilep) char tmpfile2[] = "/tmp/test-os-util.XXXXXX"; + assert_se(write_tmpfile(tmpfile2, + "ID=\"ignored\" \n" + "ID=\"the-id\" \n" + "NAME='the-name'") == 0); + + assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile2, 1) == 0); + assert_se(parse_os_release(NULL, "ID", &id, "NAME", &name) == 0); + log_info("ID: %s NAME: %s", id, name); + assert_se(streq(id, "the-id")); + assert_se(streq(name, "the-name")); + + assert_se(parse_os_release(NULL, "FOOBAR", &foobar) == 0); + log_info("FOOBAR: %s", strnull(foobar)); + assert_se(foobar == NULL); + + assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); +} + +TEST(load_os_release_pairs) { + _cleanup_(unlink_tempfilep) char tmpfile[] = "/tmp/test-os-util.XXXXXX"; + assert_se(write_tmpfile(tmpfile, + "ID=\"ignored\" \n" + "ID=\"the-id\" \n" + "NAME='the-name'") == 0); + + assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1) == 0); + + _cleanup_strv_free_ char **pairs = NULL; + assert_se(load_os_release_pairs(NULL, &pairs) == 0); + assert_se(strv_equal(pairs, STRV_MAKE("ID", "the-id", + "NAME", "the-name"))); + + assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index dfb7550543..c98a1f4b73 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -11,7 +11,7 @@ #include "tests.h" #include "tmpfile-util.h" -static void test_paths_one(UnitFileScope scope) { +static void test_paths_one(LookupScope scope) { _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; _cleanup_(lookup_paths_free) LookupPaths lp_without_env = {}; _cleanup_(lookup_paths_free) LookupPaths lp_with_env = {}; @@ -34,9 +34,9 @@ static void test_paths_one(UnitFileScope scope) { } TEST(paths) { - test_paths_one(UNIT_FILE_SYSTEM); - test_paths_one(UNIT_FILE_USER); - test_paths_one(UNIT_FILE_GLOBAL); + test_paths_one(LOOKUP_SCOPE_SYSTEM); + test_paths_one(LOOKUP_SCOPE_USER); + test_paths_one(LOOKUP_SCOPE_GLOBAL); } TEST(user_and_global_paths) { @@ -48,8 +48,8 @@ TEST(user_and_global_paths) { assert_se(unsetenv("XDG_DATA_DIRS") == 0); assert_se(unsetenv("XDG_CONFIG_DIRS") == 0); - assert_se(lookup_paths_init(&lp_global, UNIT_FILE_GLOBAL, 0, NULL) == 0); - assert_se(lookup_paths_init(&lp_user, UNIT_FILE_USER, 0, NULL) == 0); + assert_se(lookup_paths_init(&lp_global, LOOKUP_SCOPE_GLOBAL, 0, NULL) == 0); + assert_se(lookup_paths_init(&lp_user, LOOKUP_SCOPE_USER, 0, NULL) == 0); g = lp_global.search_path; u = lp_user.search_path; @@ -70,7 +70,7 @@ TEST(user_and_global_paths) { log_info("+ %s", *p); } -static void test_generator_binary_paths_one(UnitFileScope scope) { +static void test_generator_binary_paths_one(LookupScope scope) { _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; _cleanup_strv_free_ char **gp_without_env = NULL; _cleanup_strv_free_ char **env_gp_without_env = NULL; @@ -85,13 +85,13 @@ static void test_generator_binary_paths_one(UnitFileScope scope) { assert_se(unsetenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH") == 0); gp_without_env = generator_binary_paths(scope); - env_gp_without_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false); + env_gp_without_env = env_generator_binary_paths(scope == LOOKUP_SCOPE_SYSTEM ? true : false); - log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, gp_without_env) log_info(" %s", *dir); - log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Environment generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, env_gp_without_env) log_info(" %s", *dir); @@ -104,13 +104,13 @@ static void test_generator_binary_paths_one(UnitFileScope scope) { assert_se(setenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", systemd_env_generator_path, 1) == 0); gp_with_env = generator_binary_paths(scope); - env_gp_with_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false); + env_gp_with_env = env_generator_binary_paths(scope == LOOKUP_SCOPE_SYSTEM ? true : false); - log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, gp_with_env) log_info(" %s", *dir); - log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Environment generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, env_gp_with_env) log_info(" %s", *dir); @@ -119,8 +119,8 @@ static void test_generator_binary_paths_one(UnitFileScope scope) { } TEST(generator_binary_paths) { - test_generator_binary_paths_one(UNIT_FILE_SYSTEM); - test_generator_binary_paths_one(UNIT_FILE_USER); + test_generator_binary_paths_one(LOOKUP_SCOPE_SYSTEM); + test_generator_binary_paths_one(LOOKUP_SCOPE_USER); } DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-path.c b/src/test/test-path.c index 2690dc0aa4..7fb1f7363c 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -33,7 +33,7 @@ static int setup_test(Manager **m) { if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &tmp); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &tmp); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r >= 0); diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 35f7be491a..721c4b61a1 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r >= 0); diff --git a/src/test/test-socket-bind.c b/src/test/test-socket-bind.c index 62300c4ae4..59d8cc965b 100644 --- a/src/test/test-socket-bind.c +++ b/src/test/test-socket-bind.c @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("2000"), STRV_MAKE("any")) >= 0); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 9ee651a5fa..3245516f9a 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -228,17 +228,12 @@ TEST(passfd_read) { if (r == 0) { /* Child */ - char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX"; - _cleanup_close_ int tmpfd = -1; - pair[0] = safe_close(pair[0]); - tmpfd = mkostemp_safe(tmpfile); - assert_se(tmpfd >= 0); - assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents)); - tmpfd = safe_close(tmpfd); + char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX"; + assert_se(write_tmpfile(tmpfile, file_contents) == 0); - tmpfd = open(tmpfile, O_RDONLY); + _cleanup_close_ int tmpfd = open(tmpfile, O_RDONLY); assert_se(tmpfd >= 0); assert_se(unlink(tmpfile) == 0); @@ -277,16 +272,12 @@ TEST(passfd_contents_read) { /* Child */ struct iovec iov = IOVEC_INIT_STRING(wire_contents); char tmpfile[] = "/tmp/test-socket-util-passfd-contents-read-XXXXXX"; - _cleanup_close_ int tmpfd = -1; pair[0] = safe_close(pair[0]); - tmpfd = mkostemp_safe(tmpfile); - assert_se(tmpfd >= 0); - assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents)); - tmpfd = safe_close(tmpfd); + assert_se(write_tmpfile(tmpfile, file_contents) == 0); - tmpfd = open(tmpfile, O_RDONLY); + _cleanup_close_ int tmpfd = open(tmpfile, O_RDONLY); assert_se(tmpfd >= 0); assert_se(unlink(tmpfile) == 0); diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index 231625de95..49b4f5a2a3 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -8,6 +8,7 @@ #include "string-util.h" #include "strv.h" #include "tests.h" +#include "unit-file.h" static void test_specifier_escape_one(const char *a, const char *b) { _cleanup_free_ char *x = NULL; @@ -46,7 +47,7 @@ TEST(specifier_escape_strv) { static const Specifier specifier_table[] = { COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(LOOKUP_SCOPE_USER), { 'h', specifier_user_home, NULL }, COMMON_TMP_SPECIFIERS, @@ -138,4 +139,18 @@ TEST(specifiers) { } } +TEST(specifiers_missing_data_ok) { + _cleanup_free_ char *resolved = NULL; + + assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0); + assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) >= 0); + assert_se(streq(resolved, "-----")); + + assert_se(setenv("SYSTEMD_OS_RELEASE", "/nosuchfileordirectory", 1) == 0); + assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) == -EUNATCH); + assert_se(streq(resolved, "-----")); + + assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 5f744b0288..9975a1848d 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -18,6 +18,30 @@ #include "tests.h" #include "tmpfile-util.h" +TEST(null_or_empty_path) { + assert_se(null_or_empty_path("/dev/null") == 1); + assert_se(null_or_empty_path("/dev/tty") == 1); /* We assume that any character device is "empty", bleh. */ + assert_se(null_or_empty_path("../../../../../../../../../../../../../../../../../../../../dev/null") == 1); + assert_se(null_or_empty_path("/proc/self/exe") == 0); + assert_se(null_or_empty_path("/nosuchfileordir") == -ENOENT); +} + +TEST(null_or_empty_path_with_root) { + assert_se(null_or_empty_path_with_root("/dev/null", NULL) == 1); + assert_se(null_or_empty_path_with_root("/dev/null", "/") == 1); + assert_se(null_or_empty_path_with_root("/dev/null", "/.././../") == 1); + assert_se(null_or_empty_path_with_root("/dev/null", "/.././..") == 1); + assert_se(null_or_empty_path_with_root("../../../../../../../../../../../../../../../../../../../../dev/null", NULL) == 1); + assert_se(null_or_empty_path_with_root("../../../../../../../../../../../../../../../../../../../../dev/null", "/") == 1); + assert_se(null_or_empty_path_with_root("/proc/self/exe", NULL) == 0); + assert_se(null_or_empty_path_with_root("/proc/self/exe", "/") == 0); + assert_se(null_or_empty_path_with_root("/nosuchfileordir", NULL) == -ENOENT); + assert_se(null_or_empty_path_with_root("/nosuchfileordir", "/.././../") == -ENOENT); + assert_se(null_or_empty_path_with_root("/nosuchfileordir", "/.././..") == -ENOENT); + assert_se(null_or_empty_path_with_root("/foobar/barbar/dev/null", "/foobar/barbar") == 1); + assert_se(null_or_empty_path_with_root("/foobar/barbar/dev/null", "/foobar/barbar/") == 1); +} + TEST(files_same) { _cleanup_close_ int fd = -1; char name[] = "/tmp/test-files_same.XXXXXX"; diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 9b05a191e6..dffa2822e6 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -8,20 +8,20 @@ #include "unit-file.h" TEST(unit_validate_alias_symlink_and_warn) { - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.socket") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.foobar") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@.socket") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@YYY.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@XXX.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b@.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.slice", "/other/b.slice") == -EINVAL); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.slice", "/other/b.slice") == -EINVAL); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.socket") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.foobar") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.socket") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@YYY.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@XXX.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b@.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.slice", "/other/b.slice") == -EINVAL); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.slice", "/other/b.slice") == -EINVAL); } TEST(unit_file_build_name_map) { @@ -35,7 +35,7 @@ TEST(unit_file_build_name_map) { ids = strv_skip(saved_argv, 1); - assert_se(lookup_paths_init(&lp, UNIT_FILE_SYSTEM, 0, NULL) >= 0); + assert_se(lookup_paths_init(&lp, LOOKUP_SCOPE_SYSTEM, 0, NULL) >= 0); assert_se(unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_names, NULL) == 1); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 8cd0e0b4a1..d4dd8dc91c 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -228,7 +228,7 @@ TEST_RET(unit_printf, .sd_booted = true) { assert_se(get_home_dir(&home) >= 0); assert_se(get_shell(&shell) >= 0); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r == 0); diff --git a/src/test/test-unit-serialize.c b/src/test/test-unit-serialize.c index 3ef15f3b1e..f84435f480 100644 --- a/src/test/test-unit-serialize.c +++ b/src/test/test-unit-serialize.c @@ -31,7 +31,7 @@ TEST(deserialize_exec_command) { _cleanup_(manager_freep) Manager *m = NULL; int r; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c index 885ed802d4..8c355c1d5f 100644 --- a/src/test/test-watch-pid.c +++ b/src/test/test-watch-pid.c @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(a = unit_new(m, sizeof(Service))); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index f0190f9b4c..023207bc60 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -204,31 +204,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep); static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret); static int specifier_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret); -static const Specifier specifier_table[] = { - { 'a', specifier_architecture, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'B', specifier_os_build_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'l', specifier_short_host_name, NULL }, - { 'm', specifier_machine_id_safe, NULL }, - { 'o', specifier_os_id, NULL }, - { 'v', specifier_kernel_release, NULL }, - { 'w', specifier_os_version_id, NULL }, - { 'W', specifier_os_variant_id, NULL }, - - { 'h', specifier_user_home, NULL }, - - { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, - { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, - { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, - { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, - - COMMON_CREDS_SPECIFIERS, - - COMMON_TMP_SPECIFIERS, - {} -}; - static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret) { int r; @@ -2737,7 +2712,7 @@ static bool should_include_path(const char *path) { return false; } -static int specifier_expansion_from_arg(Item *i) { +static int specifier_expansion_from_arg(const Specifier *specifier_table, Item *i) { int r; assert(i); @@ -2944,6 +2919,30 @@ static int parse_line( assert(line >= 1); assert(buffer); + const Specifier specifier_table[] = { + { 'a', specifier_architecture, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, + { 'm', specifier_machine_id_safe, NULL }, + { 'o', specifier_os_id, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, + + { 'h', specifier_user_home, NULL }, + + { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, + { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, + { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, + { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, + + COMMON_CREDS_SPECIFIERS(arg_user ? LOOKUP_SCOPE_USER : LOOKUP_SCOPE_SYSTEM), + COMMON_TMP_SPECIFIERS, + {} + }; + r = extract_many_words( &buffer, NULL, @@ -3148,7 +3147,7 @@ static int parse_line( if (!should_include_path(i.path)) return 0; - r = specifier_expansion_from_arg(&i); + r = specifier_expansion_from_arg(specifier_table, &i); if (r == -ENXIO) return log_unresolvable_specifier(fname, line); if (r < 0) { diff --git a/test/meson.build b/test/meson.build index 4fe93f6e51..34f78d5c62 100644 --- a/test/meson.build +++ b/test/meson.build @@ -88,6 +88,7 @@ endif test_fstab_generator_sh = find_program('test-fstab-generator.sh') test_network_generator_conversion_sh = find_program('test-network-generator-conversion.sh') +test_systemctl_enable_sh = find_program('test-systemctl-enable.sh') test_systemd_tmpfiles_py = find_program('test-systemd-tmpfiles.py') hwdb_test_sh = find_program('hwdb-test.sh') diff --git a/test/test-systemctl-enable.sh b/test/test-systemctl-enable.sh new file mode 100644 index 0000000000..ac1bcc1cc7 --- /dev/null +++ b/test/test-systemctl-enable.sh @@ -0,0 +1,693 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex + +# Silence warning from running_in_chroot_or_offline() +export SYSTEMD_IGNORE_CHROOT=1 + +systemctl=${1:-systemctl} +systemd_id128=${2:-systemd-id128} + +unset root +cleanup() { + [ -n "$root" ] && rm -rf "$root" +} +trap cleanup exit +root=$(mktemp -d --tmpdir systemctl-test.XXXXXX) + +islink() { + test -h "$1" || return 1 + test "$(readlink "$1")" = "$2" || return 2 +} + +: '------enable nonexistent------------------------------------' +( ! "$systemctl" --root="$root" enable test1.service ) + +: '------basic enablement--------------------------------------' +mkdir -p "$root/etc/systemd/system" +cat >"$root/etc/systemd/system/test1.service" <>"$root/etc/systemd/system/test1.service" <"$root/etc/systemd/system/test1a.service" <"$root/etc/systemd/system/test2.socket" <"$root/etc/systemd/system/test2.service" <"$root/link1.path" <"$root/link3.suffix" <"$root/etc/systemd/system/link5-also.service" <"$root/etc/systemd/system/myown.d/link5.service" <"$root/etc/systemd/system/templ1@.service" <"$root/etc/systemd/system/templ1@.service" <"$root/etc/systemd/system/templ2@.service" <"$root/etc/systemd/system/link4.service" <"$root/etc/systemd/system/link4.service" <"$root/etc/systemd/system/link5.service" <"$root/link5copy.service" <"$root/etc/systemd/system/link5@.path" <"$root/etc/systemd/system/multilink.mount" <"$root/etc/systemd/system/some-some-link6@.socket" <"$root/etc/os-release" <"$root/etc/os-release" <"$root/etc/machine-id" +check_alias m "$(cat "$root/etc/machine-id")" + +check_alias n 'some-some-link6@.socket' +check_alias N 'some-some-link6@' + +check_alias p 'some-some-link6' + +check_alias v "$(uname -r)" + +# % is not legal in unit name +( ! check_alias % '%' ) + +# %z is not defined +( ! check_alias z 'z' ) + +: '-------specifiers in WantedBy-------------------------------' +# We don't need to repeat all the tests. Let's do a basic check that specifier +# expansion is performed. + +cat >"$root/etc/systemd/system/some-some-link7.socket" <"$root/etc/os-release2" <