Merge pull request #22649 from keszybz/symlink-enablement-yet-again-punish-me-harder

Fixups to the unit enablement logic
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2022-03-29 21:10:03 +02:00 committed by GitHub
commit 53877d0385
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 2503 additions and 1372 deletions

View file

@ -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.

View file

@ -75,6 +75,10 @@
from earliest boot on, and hence must be located on the root file
system.</para>
<para><filename>os-release</filename> 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.</para>
<para>For a longer rationale for <filename>os-release</filename>
please refer to the <ulink
url="http://0pointer.de/blog/projects/os-release">Announcement of <filename>/etc/os-release</filename></ulink>.</para>

View file

@ -138,7 +138,7 @@
a symlink, so when <command>systemd</command> is asked through D-Bus to load
<filename>dbus-org.freedesktop.network1.service</filename>, it'll load
<filename>systemd-networkd.service</filename>. As another example, <filename>default.target</filename>
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
<filename>multi-user.target</filename> or <filename>graphical.target</filename> to select what is started
by default. Alias names may be used in commands like <command>disable</command>,
<command>start</command>, <command>stop</command>, <command>status</command>, and similar, and in all
@ -154,8 +154,12 @@
template instance (e.g. <literal>alias@inst.service</literal>) may be a symlink to different template
(e.g. <literal>template@inst.service</literal>). In that case, just this specific instance is aliased,
while other instances of the template (e.g. <literal>alias@foo.service</literal>,
<literal>alias@bar.service</literal>) 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.</para>
<literal>alias@bar.service</literal>) 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.</para>
<para>Unit files may specify aliases through the <varname>Alias=</varname> 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 <varname>Requires=</varname> type dependencies as well, the directory suffix is
<filename>.requires/</filename> 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
<varname>Wants=</varname>, see below. The preferred way to create symlinks in the
<filename>.wants/</filename> or <filename>.requires/</filename> 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 <command>enable</command> or <command>preset</command> commands of
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
<varname>Wants=</varname> and <varname>Requires=</varname>, see below. The preferred way to create
symlinks in the <filename>.wants/</filename> or <filename>.requires/</filename> directories is by
specifying the dependency in [Install] section of the target unit, and creating the symlink in the file
system with the <command>enable</command> or <command>preset</command> commands of
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. 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
<filename>.wants/</filename> or <filename>.requires/</filename> 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.</para>
<para>Along with a unit file <filename>foo.service</filename>, a "drop-in" directory
<filename>foo.service.d/</filename> may exist. All files with the suffix
@ -501,13 +512,30 @@
<programlisting>systemd-analyze --user unit-paths</programlisting>
</para>
<para>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 <command>systemctl link</command>
for this operation. See
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for its usage and precaution.
</para>
<para>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 <command>systemctl
link</command> for this; see
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. The file
system where the linked unit files are located must be accessible when systemd is started (e.g. anything
underneath <filename>/home/</filename> or <filename>/var/</filename> is not allowed, unless those
directories are located on the root file system).</para>
<para>It is important to distinguish "linked unit files" from "unit file aliases": any symlink where the
symlink <emphasis>target</emphasis> 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
<filename index='false'>/etc/systemd/system/alias1.service</filename><filename index='false'>service1.service</filename>,
<filename index='false'>/etc/systemd/system/alias2.service</filename><filename index='false'>/usr/lib/systemd/service1.service</filename>,
<filename index='false'>/etc/systemd/system/alias3.service</filename><filename index='false'>/etc/systemd/system/service1.service</filename>
are all valid aliases and <filename index='false'>service1.service</filename> will have
four names, even if the unit file is located at
<filename index='false'>/run/systemd/system/service1.service</filename>. In contrast,
a symlink <filename index='false'>/etc/systemd/system/link1.service</filename><filename index='false'>../link1_service_file</filename>
means that <filename index='false'>link1.service</filename> is a "linked unit" and the contents of
<filename index='false'>/etc/systemd/link1_service_file</filename> provide its configuration.</para>
</refsect1>
<refsect1>
@ -1876,34 +1904,31 @@
<term><varname>WantedBy=</varname></term>
<term><varname>RequiredBy=</varname></term>
<listitem><para>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 <filename>.wants/</filename> or
<filename>.requires/</filename> directory of each of the
listed units when this unit is installed by <command>systemctl
enable</command>. This has the effect that a dependency of
type <varname>Wants=</varname> or <varname>Requires=</varname>
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
<varname>Wants=</varname> and <varname>Requires=</varname> in
the [Unit] section for details.</para>
<listitem><para>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 <filename>.wants/</filename> or
<filename>.requires/</filename> directory of each of the listed units when this unit is installed by
<command>systemctl enable</command>. This has the effect of a dependency of type
<varname>Wants=</varname> or <varname>Requires=</varname> 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 <varname>Wants=</varname> and <varname>Requires=</varname> in the
[Unit] section for details.</para>
<para><command>WantedBy=foo.service</command> in a service
<filename>bar.service</filename> is mostly equivalent to
<command>Alias=foo.service.wants/bar.service</command> in the
same file. In case of template units listing non template units,
<command>systemctl enable</command> must be called with an
instance name, and this instance will be added to the
<filename>.wants/</filename> or
<filename>.requires/</filename> list of the listed unit. E.g.
<command>WantedBy=getty.target</command> in a service
<filename>getty@.service</filename> will result in
<command>systemctl enable getty@tty2.service</command>
creating a
<filename>getty.target.wants/getty@tty2.service</filename>
link to <filename>getty@.service</filename>.
</para></listitem>
<para>In case of template units listing non template units, the listing unit must have
<varname>DefaultInstance=</varname> set, or <command>systemctl enable</command> must be called with
an instance name. The instance (default or specified) will be added to the
<filename>.wants/</filename> or <filename>.requires/</filename> list of the listed unit. For example,
<command>WantedBy=getty.target</command> in a service <filename>getty@.service</filename> will result
in <command>systemctl enable getty@tty2.service</command> creating a
<filename>getty.target.wants/getty@tty2.service</filename> link to
<filename>getty@.service</filename>. 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,
<command>WantedBy=container@.target</command> in a service <filename>monitor@.service</filename> will
result in <command>systemctl enable monitor@.service</command> creating a
<filename>container@.target.wants/monitor@.service</filename> link to
<filename>monitor@.service</filename>, which applies to all instances of
<filename>container@.target</filename>.</para></listitem>
</varlistentry>
<varlistentry>

View file

@ -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',

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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;

View file

@ -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)

View file

@ -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);

View file

@ -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 |

View file

@ -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_;

View file

@ -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.");

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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",

View file

@ -3,10 +3,7 @@
#include <stdbool.h>
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"))

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -4,12 +4,11 @@
#include <stdbool.h>
#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,

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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,
{}

View file

@ -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)

View file

@ -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))

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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_;

View file

@ -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;

View file

@ -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 }, \

View file

@ -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;

View file

@ -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 */

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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);
}

View file

@ -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) {

View file

@ -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;

View file

@ -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 */

View file

@ -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.");

View file

@ -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;

View file

@ -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)

View file

@ -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)));

View file

@ -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,

View file

@ -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

View file

@ -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");

View file

@ -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");

View file

@ -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);

View file

@ -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"));

View file

@ -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

View file

@ -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));

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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;

View file

@ -2,8 +2,11 @@
#include <errno.h>
#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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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";

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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)));

View file

@ -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) {

View file

@ -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')

View file

@ -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" <<EOF
[Install]
WantedBy=default.target
RequiredBy=special.target
EOF
"$systemctl" --root="$root" enable test1.service
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" reenable test1.service
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" disable test1.service
test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
: '------enable when link already exists-----------------------'
# We don't read the symlink target, so it's OK for the symlink to point
# to something else. We should just silently accept this.
mkdir -p "$root/etc/systemd/system/default.target.wants"
mkdir -p "$root/etc/systemd/system/special.target.requires"
ln -s /usr/lib/systemd/system/test1.service "$root/etc/systemd/system/default.target.wants/test1.service"
ln -s /usr/lib/systemd/system/test1.service "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" enable test1.service
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" reenable test1.service
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" disable test1.service
test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
: '------suffix guessing---------------------------------------'
"$systemctl" --root="$root" enable test1
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" reenable test1
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
"$systemctl" --root="$root" disable test1
test ! -e "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -e "$root/etc/systemd/system/special.target.requires/test1.service"
: '-------aliases----------------------------------------------'
cat >>"$root/etc/systemd/system/test1.service" <<EOF
Alias=test1-goodalias.service
Alias=test1@badalias.service
Alias=test1-badalias.target
Alias=test1-badalias.socket
# we have a series of good, bad, and then good again
Alias=test1-goodalias2.service
EOF
( ! "$systemctl" --root="$root" enable test1 )
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
test -e "$root/etc/systemd/system/test1-goodalias.service"
test -h "$root/etc/systemd/system/test1-goodalias.service"
test ! -h "$root/etc/systemd/system/test1@badalias.service"
test ! -h "$root/etc/systemd/system/test1-badalias.target"
test ! -h "$root/etc/systemd/system/test1-badalias.socket"
test -e "$root/etc/systemd/system/test1-goodalias2.service"
test -h "$root/etc/systemd/system/test1-goodalias2.service"
: '-------aliases in reeanble----------------------------------'
( ! "$systemctl" --root="$root" reenable test1 )
islink "$root/etc/systemd/system/default.target.wants/test1.service" "../test1.service"
islink "$root/etc/systemd/system/test1-goodalias.service" "test1.service"
test ! -h "$root/etc/systemd/system/test1@badalias.service"
test ! -h "$root/etc/systemd/system/test1-badalias.target"
test ! -h "$root/etc/systemd/system/test1-badalias.socket"
"$systemctl" --root="$root" disable test1
test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
test ! -h "$root/etc/systemd/system/test1-goodalias.service"
: '-------aliases when link already exists---------------------'
cat >"$root/etc/systemd/system/test1a.service" <<EOF
[Install]
Alias=test1a-alias.service
EOF
ln -s /usr/lib/systemd/system/test1a.service "$root/etc/systemd/system/test1a-alias.service"
"$systemctl" --root="$root" enable test1a.service
test -h "$root/etc/systemd/system/test1a-alias.service"
"$systemctl" --root="$root" disable test1a.service
test ! -h "$root/etc/systemd/system/test1a-alias.service"
: '-------also units-------------------------------------------'
cat >"$root/etc/systemd/system/test2.socket" <<EOF
[Install]
WantedBy=sockets.target
Also=test2.service
EOF
cat >"$root/etc/systemd/system/test2.service" <<EOF
[Install]
WantedBy=default.target
Also=test2.socket
EOF
"$systemctl" --root="$root" reenable test2.service
test -h "$root/etc/systemd/system/default.target.wants/test2.service"
test -h "$root/etc/systemd/system/sockets.target.wants/test2.socket"
"$systemctl" --root="$root" reenable test2.socket
test -h "$root/etc/systemd/system/default.target.wants/test2.service"
test -h "$root/etc/systemd/system/sockets.target.wants/test2.socket"
"$systemctl" --root="$root" disable test2.socket
test ! -e "$root/etc/systemd/system/default.target.wants/test2.service"
test ! -e "$root/etc/systemd/system/sockets.target.wants/test2.socket"
: '-------link-------------------------------------------------'
# File doesn't exist yet
test ! -e "$root/link1.path"
( ! "$systemctl" --root="$root" link '/link1.path' )
test ! -e "$root/etc/systemd/system/link1.path"
cat >"$root/link1.path" <<EOF
[Install]
WantedBy=paths.target
EOF
"$systemctl" --root="$root" link '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
: '-------link already linked same path------------------------'
SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" link '/link1.path' # this passes
islink "$root/etc/systemd/system/link1.path" "/link1.path"
: '-------link already linked different path-------------------'
mkdir "$root/subdir"
cp "$root/link1.path" "$root/subdir/"
( ! "$systemctl" --root="$root" link '/subdir/link1.path' )
islink "$root/etc/systemd/system/link1.path" "/link1.path"
: '-------link bad suffix--------------------------------------'
cp "$root/link1.path" "$root/subdir/link1.suffix"
( ! "$systemctl" --root="$root" link '/subdir/link1.suffix' )
test ! -e "$root/etc/systemd/system/link1.suffix"
: '-------unlink by unit name----------------------------------'
"$systemctl" --root="$root" disable 'link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
: '-------unlink by path---------------------------------------'
"$systemctl" --root="$root" link '/link1.path'
test -h "$root/etc/systemd/system/link1.path"
"$systemctl" --root="$root" disable '/link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
: '-------unlink by wrong path---------------------------------'
"$systemctl" --root="$root" link '/link1.path'
test -h "$root/etc/systemd/system/link1.path"
"$systemctl" --root="$root" disable '/subdir/link1.path' # we only care about the name
test ! -e "$root/etc/systemd/system/link1.path"
: '-------link and enable--------------------------------------'
"$systemctl" --root="$root" enable '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "../link1.path"
: '-------enable already linked same path----------------------'
"$systemctl" --root="$root" enable '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "../link1.path"
: '-------enable already linked different path-----------------'
( ! "$systemctl" --root="$root" enable '/subdir/link1.path' )
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "../link1.path"
: '-------enable bad suffix------------------------------------'
cp "$root/link1.path" "$root/subdir/link1.suffix"
( ! "$systemctl" --root="$root" enable '/subdir/link1.suffix' )
test ! -e "$root/etc/systemd/system/link1.suffix"
test ! -e "$root/etc/systemd/system/paths.target.wants/link1.suffix"
: '-------disable by unit name---------------------------------'
"$systemctl" --root="$root" disable 'link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path"
: '-------disable by path--------------------------------------'
"$systemctl" --root="$root" enable '/link1.path'
test -h "$root/etc/systemd/system/link1.path"
test -h "$root/etc/systemd/system/paths.target.wants/link1.path"
"$systemctl" --root="$root" disable '/link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path"
: '-------link and enable-------------------------------------'
"$systemctl" --root="$root" link '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
test ! -h "$root/etc/systemd/system/paths.target.wants/link1.path"
"$systemctl" --root="$root" enable 'link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "../link1.path"
"$systemctl" --root="$root" reenable 'link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "../link1.path"
: '-------manual link------------------------------------------'
cat >"$root/link3.suffix" <<EOF
[Install]
WantedBy=services.target
EOF
# We wouldn't create such a link ourselves, but it should accept it when present.
ln -s "/link3.suffix" "$root/etc/systemd/system/link3.service"
SYSTEMD_LOG_LEVEL=debug SYSTEMD_LOG_LOCATION=1 "$systemctl" --root="$root" enable 'link3.service'
islink "$root/etc/systemd/system/link3.service" "/link3.suffix"
islink "$root/etc/systemd/system/services.target.wants/link3.service" "../link3.service"
SYSTEMD_LOG_LEVEL=debug SYSTEMD_LOG_LOCATION=1 "$systemctl" --root="$root" disable 'link3.service'
test ! -h "$root/etc/systemd/system/link3.service"
test ! -h "$root/etc/systemd/system/services.target.wants/link3.service"
: '-------enable on masked-------------------------------------'
ln -s "/dev/null" "$root/etc/systemd/system/masked.service"
( ! "$systemctl" --root="$root" enable 'masked.service' )
( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked.service' )
: '-------enable on masked alias-------------------------------'
test -h "$root/etc/systemd/system/masked.service"
ln -s "masked.service" "$root/etc/systemd/system/masked-alias.service"
( ! "$systemctl" --root="$root" enable 'masked-alias.service' )
( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked-alias.service' )
: '-------issue 22000: link in subdirectory--------------------'
mkdir -p "$root/etc/systemd/system/myown.d"
cat >"$root/etc/systemd/system/link5-also.service" <<EOF
[Install]
WantedBy=services.target
Also=link5.service
EOF
cat >"$root/etc/systemd/system/myown.d/link5.service" <<EOF
[Install]
WantedBy=services.target
Also=link5-also.service
EOF
( ! "$systemctl" --root="$root" enable 'link5.service' )
test ! -h "$root/etc/systemd/system/services.target.wants/link5.service"
test ! -h "$root/etc/systemd/system/services.target.wants/link5-also.service"
"$systemctl" --root="$root" enable 'link5-also.service'
test ! -h "$root/etc/systemd/system/services.target.wants/link5.service"
islink "$root/etc/systemd/system/services.target.wants/link5-also.service" "../link5-also.service"
: '-------template enablement----------------------------------'
cat >"$root/etc/systemd/system/templ1@.service" <<EOF
[Install]
WantedBy=services.target
EOF
# No instance here — this can't succeed.
( ! "$systemctl" --root="$root" enable 'templ1@.service' )
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
"$systemctl" --root="$root" enable 'templ1@one.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "../templ1@one.service"
"$systemctl" --root="$root" enable 'templ1@two.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "../templ1@one.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "../templ1@two.service"
"$systemctl" --root="$root" disable 'templ1@one.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "../templ1@two.service"
"$systemctl" --root="$root" disable 'templ1@two.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
: '-------template enablement w/ default instance--------------'
cat >"$root/etc/systemd/system/templ1@.service" <<EOF
[Install]
# check enablement with
WantedBy=services.target services.target
RequiredBy=other@templ1.target other@%p.target
DefaultInstance=333
EOF
"$systemctl" --root="$root" enable 'templ1@.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@333.service" "../templ1@.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@333.service" "../templ1@.service"
"$systemctl" --root="$root" enable 'templ1@one.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@333.service" "../templ1@.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@333.service" "../templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "../templ1@one.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.service" "../templ1@one.service"
"$systemctl" --root="$root" enable 'templ1@two.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@333.service" "../templ1@.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@333.service" "../templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "../templ1@one.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.service" "../templ1@one.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "../templ1@two.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@two.service" "../templ1@two.service"
"$systemctl" --root="$root" disable 'templ1@one.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@333.service" "../templ1@.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@333.service" "../templ1@.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "../templ1@two.service"
islink "$root/etc/systemd/system/other@templ1.target.requires/templ1@two.service" "../templ1@two.service"
# disable remaining links here
"$systemctl" --root="$root" disable 'templ1@.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@333.service"
test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@333.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@two.service"
: '-------removal of relative enablement symlinks--------------'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
ln -s '../templ1@one.service' "$root/etc/systemd/system/services.target.wants/templ1@one.service"
ln -s 'templ1@two.service' "$root/etc/systemd/system/services.target.wants/templ1@two.service"
ln -s '../templ1@.service' "$root/etc/systemd/system/services.target.wants/templ1@three.service"
ln -s 'templ1@.service' "$root/etc/systemd/system/services.target.wants/templ1@four.service"
ln -s '/usr/lib/systemd/system/templ1@.service' "$root/etc/systemd/system/services.target.wants/templ1@five.service"
ln -s '/etc/systemd/system/templ1@.service' "$root/etc/systemd/system/services.target.wants/templ1@six.service"
ln -s '/run/system/templ1@.service' "$root/etc/systemd/system/services.target.wants/templ1@seven.service"
# this should remove all links
"$systemctl" --root="$root" disable 'templ1@.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@three.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@four.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@five.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@six.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@seven.service"
: '-------template enablement for another template-------------'
cat >"$root/etc/systemd/system/templ2@.service" <<EOF
[Install]
RequiredBy=another-template@.target
EOF
"$systemctl" --root="$root" enable 'templ2@.service'
islink "$root/etc/systemd/system/another-template@.target.requires/templ2@.service" "../templ2@.service"
"$systemctl" --root="$root" enable 'templ2@two.service'
islink "$root/etc/systemd/system/another-template@.target.requires/templ2@.service" "../templ2@.service"
islink "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service" "../templ2@two.service"
"$systemctl" --root="$root" disable 'templ2@other.service'
islink "$root/etc/systemd/system/another-template@.target.requires/templ2@.service" "../templ2@.service"
islink "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service" "../templ2@two.service"
"$systemctl" --root="$root" disable 'templ2@two.service'
islink "$root/etc/systemd/system/another-template@.target.requires/templ2@.service" "../templ2@.service"
test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service"
"$systemctl" --root="$root" disable 'templ2@.service'
test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@.service"
test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service"
: '-------aliases w/ and w/o instance--------------------------'
test ! -e "$root/etc/systemd/system/link4.service"
cat >"$root/etc/systemd/system/link4.service" <<EOF
[Install]
Alias=link4.service
Alias=link4@.service
Alias=link4@inst.service
Alias=link4alias.service
Alias=link4alias2.service
EOF
( ! "$systemctl" --root="$root" enable 'link4.service' )
test ! -h "$root/etc/systemd/system/link4.service" # this is our file
test ! -h "$root/etc/systemd/system/link4@.service"
test ! -h "$root/etc/systemd/system/link4@inst.service"
islink "$root/etc/systemd/system/link4alias.service" "link4.service"
islink "$root/etc/systemd/system/link4alias2.service" "link4.service"
"$systemctl" --root="$root" disable 'link4.service'
test ! -h "$root/etc/systemd/system/link4.service"
test ! -h "$root/etc/systemd/system/link4@.service"
test ! -h "$root/etc/systemd/system/link4@inst.service"
test ! -h "$root/etc/systemd/system/link4alias.service"
test ! -h "$root/etc/systemd/system/link4alias2.service"
: '-------systemctl enable on path to unit file----------------'
cat >"$root/etc/systemd/system/link4.service" <<EOF
[Install]
Alias=link4alias.service
Alias=link4alias2.service
EOF
# Apparently this works. I'm not sure what to think.
"$systemctl" --root="$root" enable '/etc/systemd/system/link4.service'
test ! -h "$root/etc/systemd/system/link4.service" # this is our file
islink "$root/etc/systemd/system/link4alias.service" "link4.service"
islink "$root/etc/systemd/system/link4alias2.service" "link4.service"
"$systemctl" --root="$root" disable '/etc/systemd/system/link4.service'
test ! -h "$root/etc/systemd/system/link4.service"
test ! -h "$root/etc/systemd/system/link4alias.service"
test ! -h "$root/etc/systemd/system/link4alias2.service"
: '-------issue 661: enable on unit file--------------'
test ! -e "$root/etc/systemd/system/link5.service"
cat >"$root/etc/systemd/system/link5.service" <<EOF
[Install]
Alias=link5.service
Alias=link5alias.service
Alias=link5alias2.service
EOF
"$systemctl" --root="$root" enable 'link5.service'
test ! -h "$root/etc/systemd/system/link5.service" # this is our file
islink "$root/etc/systemd/system/link5alias.service" "link5.service"
islink "$root/etc/systemd/system/link5alias2.service" "link5.service"
"$systemctl" --root="$root" disable 'link5.service'
test ! -h "$root/etc/systemd/system/link5alias.service"
test ! -h "$root/etc/systemd/system/link5alias2.service"
: '-------issue 661: link and enable on unit file--------------'
test ! -e "$root/etc/systemd/system/link5copy.service"
cat >"$root/link5copy.service" <<EOF
[Install]
Alias=link5copy.service
Alias=link5alias.service
Alias=link5alias2.service
EOF
test ! -e "$root/etc/systemd/system/link5copy.service"
"$systemctl" --root="$root" link '/link5copy.service'
islink "$root/etc/systemd/system/link5copy.service" '/link5copy.service'
test ! -h "$root/etc/systemd/system/link5alias.service"
test ! -h "$root/etc/systemd/system/link5alias2.service"
"$systemctl" --root="$root" disable 'link5copy.service'
test ! -h "$root/etc/systemd/system/link5copy.service"
test ! -h "$root/etc/systemd/system/link5alias.service"
test ! -h "$root/etc/systemd/system/link5alias2.service"
"$systemctl" --root="$root" enable '/link5copy.service'
islink "$root/etc/systemd/system/link5copy.service" '/link5copy.service'
islink "$root/etc/systemd/system/link5alias.service" 'link5copy.service'
islink "$root/etc/systemd/system/link5alias2.service" 'link5copy.service'
"$systemctl" --root="$root" disable 'link5copy.service'
test ! -h "$root/etc/systemd/system/link5copy.service"
test ! -h "$root/etc/systemd/system/link5alias.service"
test ! -h "$root/etc/systemd/system/link5alias2.service"
: '----issue 19437: plain templates in .wants/ or .requires/---'
test ! -e "$root/etc/systemd/system/link5@.path"
cat >"$root/etc/systemd/system/link5@.path" <<EOF
[Install]
WantedBy=target5@.target
RequiredBy=target5@.target
WantedBy=target5@inst.target
RequiredBy=target5@inst.target
EOF
"$systemctl" --root="$root" enable 'link5@.path'
test ! -h "$root/etc/systemd/system/link5@.path" # this is our file
islink "$root/etc/systemd/system/target5@.target.wants/link5@.path" "../link5@.path"
islink "$root/etc/systemd/system/target5@.target.requires/link5@.path" "../link5@.path"
islink "$root/etc/systemd/system/target5@inst.target.wants/link5@.path" "../link5@.path"
islink "$root/etc/systemd/system/target5@inst.target.requires/link5@.path" "../link5@.path"
"$systemctl" --root="$root" disable 'link5@.path'
test ! -h "$root/etc/systemd/system/link5@.path" # this is our file
test ! -h "$root/etc/systemd/system/target5@.target.wants/link5@.path"
test ! -h "$root/etc/systemd/system/target5@.target.requires/link5@.path"
test ! -h "$root/etc/systemd/system/target5@inst.target.wants/link5@.path"
test ! -h "$root/etc/systemd/system/target5@inst.target.requires/link5@.path"
: '-------removal of symlinks not listed in [Install]----------'
# c.f. 66a19d85a533b15ed32f4066ec880b5a8c06babd
test ! -e "$root/etc/systemd/system/multilink.mount"
cat >"$root/etc/systemd/system/multilink.mount" <<EOF
[Install]
WantedBy=multilink.target
EOF
mkdir -p "$root/etc/systemd/system/default.target.wants"
ln -s ../multilink.mount "$root/etc/systemd/system/default.target.wants/"
ln -s ../multilink.mount "$root/etc/systemd/system/multilink-alias.mount"
ln -s ../multilink.mount "$root/etc/systemd/system/multilink-badalias.service"
"$systemctl" --root="$root" disable 'multilink.mount'
test -e "$root/etc/systemd/system/multilink.mount" # this is our file
test ! -h "$root/etc/systemd/system/default.target.wants/"
test ! -h "$root/etc/systemd/system/multilink-alias.mount"
test ! -h "$root/etc/systemd/system/multilink-badalias.service"
: '-------merge 20017: specifiers in the unit file-------------'
test ! -e "$root/etc/systemd/system/some-some-link6@.socket"
# c.f. de61a04b188f81a85cdb5c64ddb4987dcd9d30d3
check_alias() {
: "------------------ %$1 -------------------------------------"
cat >"$root/etc/systemd/system/some-some-link6@.socket" <<EOF
[Install]
Alias=target@$1:%$1.socket
EOF
SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" enable 'some-some-link6@.socket' || return 1
islink "$root/etc/systemd/system/target@$1:$2.socket" "some-some-link6@.socket" || return 2
}
# TODO: our architecture names are different than what uname -m returns.
# Add something like 'systemd-detect-virt --print-architecture' and use it here.
check_alias a "$(uname -m | tr '_' '-')" || :
test ! -e "$root/etc/os-release"
test ! -e "$root/usr/lib/os-release"
( ! check_alias A '' )
( ! check_alias B '' )
( ! check_alias M '' )
( ! check_alias o '' )
( ! check_alias w '' )
( ! check_alias W '' )
cat >"$root/etc/os-release" <<EOF
# empty
EOF
check_alias A ''
check_alias B ''
check_alias M ''
check_alias o ''
check_alias w ''
check_alias W ''
cat >"$root/etc/os-release" <<EOF
ID='the-id'
VERSION_ID=39a
BUILD_ID=build-id
VARIANT_ID=wrong
VARIANT_ID=right
IMAGE_ID="foobar"
IMAGE_VERSION='1-2-3'
EOF
check_alias A '1-2-3'
check_alias B 'build-id'
check_alias M 'foobar'
check_alias o 'the-id'
check_alias w '39a'
check_alias W 'right'
check_alias b "$("$systemd_id128" boot-id)"
# Specifiers not available for [Install]
( ! check_alias C '' )
( ! check_alias E '' )
( ! check_alias f '' )
( ! check_alias h '' )
( ! check_alias I '' )
( ! check_alias J '' )
( ! check_alias L '' )
( ! check_alias P '' )
( ! check_alias s '' )
( ! check_alias S '' )
( ! check_alias t '' )
( ! check_alias T '' )
( ! check_alias V '' )
check_alias g root
check_alias G 0
check_alias u root
check_alias U 0
check_alias i ""
check_alias j 'link6'
check_alias l "$(uname -n | sed 's/\..*//')"
test ! -e "$root/etc/machine-id"
( ! check_alias m '' )
"$systemd_id128" new >"$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" <<EOF
[Install]
WantedBy=target@%p.target
WantedBy=another-target@.target
RequiredBy=target2@%p.target
RequiredBy=another-target2@.target
EOF
"$systemctl" --root="$root" enable 'some-some-link7.socket'
islink "$root/etc/systemd/system/target@some-some-link7.target.wants/some-some-link7.socket" "../some-some-link7.socket"
islink "$root/etc/systemd/system/another-target@.target.wants/some-some-link7.socket" "../some-some-link7.socket"
islink "$root/etc/systemd/system/target2@some-some-link7.target.requires/some-some-link7.socket" "../some-some-link7.socket"
islink "$root/etc/systemd/system/another-target2@.target.requires/some-some-link7.socket" "../some-some-link7.socket"
"$systemctl" --root="$root" disable 'some-some-link7.socket'
test ! -h "$root/etc/systemd/system/target@some-some-link7.target.wants/some-some-link7.socket"
test ! -h "$root/etc/systemd/system/another-target@.target.wants/some-some-link7.socket"
test ! -h "$root/etc/systemd/system/target2@some-some-link7.target.requires/some-some-link7.socket"
test ! -h "$root/etc/systemd/system/another-target2@.target.requires/some-some-link7.socket"
# TODO: repeat the tests above for presets
: '-------SYSTEMD_OS_RELEASE relative to root-------------------'
# check that os-release overwriting works as expected with root
test -e "$root/etc/os-release"
cat >"$root/etc/os-release2" <<EOF
ID='the-id2'
EOF
SYSTEMD_OS_RELEASE="$root/etc/os-release2" check_alias o 'the-id2'

View file

@ -98,13 +98,13 @@ def test_valid_specifiers(*, user):
test_content('f {} - - - - %b', '{}'.format(id128.get_boot().hex), user=user)
test_content('f {} - - - - %H', '{}'.format(socket.gethostname()), user=user)
test_content('f {} - - - - %v', '{}'.format(os.uname().release), user=user)
test_content('f {} - - - - %U', '{}'.format(os.getuid()), user=user)
test_content('f {} - - - - %G', '{}'.format(os.getgid()), user=user)
test_content('f {} - - - - %U', '{}'.format(os.getuid() if user else 0), user=user)
test_content('f {} - - - - %G', '{}'.format(os.getgid() if user else 0), user=user)
puser = pwd.getpwuid(os.getuid())
puser = pwd.getpwuid(os.getuid() if user else 0)
test_content('f {} - - - - %u', '{}'.format(puser.pw_name), user=user)
pgroup = grp.getgrgid(os.getgid())
pgroup = grp.getgrgid(os.getgid() if user else 0)
test_content('f {} - - - - %g', '{}'.format(pgroup.gr_name), user=user)
# Note that %h is the only specifier in which we look the environment,