Merge pull request #1515 from poettering/install-symlink

install: follow unit file symlinks in /usr, but not /etc when looking for [Install] data and more
This commit is contained in:
Daniel Mack 2015-11-12 18:18:45 +01:00
commit f1637bd3a8
17 changed files with 2223 additions and 1436 deletions

View file

@ -1493,7 +1493,8 @@ tests += \
test-verbs \
test-af-list \
test-arphrd-list \
test-dns-domain
test-dns-domain \
test-install-root
EXTRA_DIST += \
test/a.service \
@ -1836,6 +1837,12 @@ test_verbs_SOURCES = \
test_verbs_LDADD = \
libshared.la
test_install_root_SOURCES = \
src/test/test-install-root.c
test_install_root_LDADD = \
libshared.la
test_namespace_LDADD = \
libcore.la

View file

@ -961,10 +961,11 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<term><command>list-unit-files <optional><replaceable>PATTERN...</replaceable></optional></command></term>
<listitem>
<para>List installed unit files. If one or more
<replaceable>PATTERN</replaceable>s are specified, only
units whose filename (just the last component of the path)
matches one of them are shown.</para>
<para>List installed unit files and their enablement state
(as reported by <command>is-enabled</command>). If one or
more <replaceable>PATTERN</replaceable>s are specified,
only units whose filename (just the last component of the
path) matches one of them are shown.</para>
</listitem>
</varlistentry>
@ -1134,7 +1135,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<tbody>
<row>
<entry><literal>enabled</literal></entry>
<entry morerows='1'>Enabled through a symlink in <filename>.wants</filename> directory (permanently or just in <filename>/run</filename>).</entry>
<entry morerows='1'>Enabled through a symlink in a <filename>.wants/</filename> or <filename>.requires/</filename> subdirectory of <filename>/etc/systemd/system/</filename> (persistently) or <filename>/run/systemd/system/</filename> (transiently).</entry>
<entry morerows='1'>0</entry>
</row>
<row>
@ -1142,7 +1143,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</row>
<row>
<entry><literal>linked</literal></entry>
<entry morerows='1'>Made available through a symlink to the unit file (permanently or just in <filename>/run</filename>).</entry>
<entry morerows='1'>Made available through one or more symlinks to the unit file (permanently in <filename>/etc/systemd/system/</filename> or transiently in <filename>/run/systemd/system/</filename>), even though the unit file might reside outside of the unit file search path.</entry>
<entry morerows='1'>&gt; 0</entry>
</row>
<row>
@ -1150,7 +1151,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</row>
<row>
<entry><literal>masked</literal></entry>
<entry morerows='1'>Disabled entirely (permanently or just in <filename>/run</filename>).</entry>
<entry morerows='1'>Completely disabled, so that any start operation on it fails (permanently in <filename>/etc/systemd/system/</filename> or transiently in <filename>/run/systemd/systemd/</filename>).</entry>
<entry morerows='1'>&gt; 0</entry>
</row>
<row>
@ -1168,7 +1169,12 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</row>
<row>
<entry><literal>disabled</literal></entry>
<entry>The unit file is not enabled.</entry>
<entry>Unit file is not enabled, but contains an <literal>[Install]</literal> section with installation instructions.</entry>
<entry>&gt; 0</entry>
</row>
<row>
<entry><literal>bad</literal></entry>
<entry>Unit file is invalid or another error occured. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
<entry>&gt; 0</entry>
</row>
</tbody>

View file

@ -1227,22 +1227,22 @@
<row>
<entry><literal>%u</literal></entry>
<entry>User name</entry>
<entry>This is the name of the configured user of the unit, or (if none is set) the user running the systemd instance.</entry>
<entry>This is the name of the user running the service manager instance. In case of the system manager this resolves to <literal>root</literal>.</entry>
</row>
<row>
<entry><literal>%U</literal></entry>
<entry>User UID</entry>
<entry>This is the numeric UID of the configured user of the unit, or (if none is set) the user running the systemd user instance. Note that this specifier is not available for units run by the systemd system instance (as opposed to those run by a systemd user instance), unless the user has been configured as a numeric UID in the first place or the configured user is the root user.</entry>
<entry>This is the numeric UID of the user running the service manager instance. In case of the system manager this resolves to <literal>0</literal>.</entry>
</row>
<row>
<entry><literal>%h</literal></entry>
<entry>User home directory</entry>
<entry>This is the home directory of the configured user of the unit, or (if none is set) the user running the systemd user instance. Similar to <literal>%U</literal>, this specifier is not available for units run by the systemd system instance, unless the configured user is the root user.</entry>
<entry>This is the home directory of the user running the service manager instance. In case of the system manager this resolves to <literal>/root</literal>.</entry>
</row>
<row>
<entry><literal>%s</literal></entry>
<entry>User shell</entry>
<entry>This is the shell of the configured user of the unit, or (if none is set) the user running the systemd user instance. Similar to <literal>%U</literal>, this specifier is not available for units run by the systemd system instance, unless the configured user is the root user.</entry>
<entry>This is the shell of the user running the service manager instance. In case of the system manager this resolves to <literal>/bin/sh</literal>.</entry>
</row>
<row>
<entry><literal>%m</literal></entry>

View file

@ -233,6 +233,26 @@ int readlink_and_canonicalize(const char *p, char **r) {
return 0;
}
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
_cleanup_free_ char *target = NULL, *t = NULL;
const char *full;
int r;
full = prefix_roota(root, path);
r = readlink_malloc(full, &target);
if (r < 0)
return r;
t = file_in_same_dir(path, target);
if (!t)
return -ENOMEM;
*ret = t;
t = NULL;
return 0;
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);

View file

@ -40,6 +40,7 @@ int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
int readlink_and_canonicalize(const char *p, char **r);
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);

View file

@ -1521,9 +1521,9 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
state = unit_file_get_state(scope, NULL, name);
if (state < 0)
return state;
r = unit_file_get_state(scope, NULL, name, &state);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state));
}
@ -1651,6 +1651,8 @@ static int method_enable_unit_files_generic(
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
if (r == -ESHUTDOWN)
return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
if (r < 0)
return r;
@ -1885,6 +1887,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
if (r == -ESHUTDOWN)
return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
if (r < 0)
return r;

View file

@ -159,162 +159,43 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
char *printed = NULL;
Unit *u = userdata;
ExecContext *c;
int r = 0;
char *t;
assert(u);
/* 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. */
c = unit_get_exec_context(u);
if (!c)
return -EOPNOTSUPP;
if (u->manager->running_as == MANAGER_SYSTEM) {
/* We cannot use NSS from PID 1, hence try to make the
* best of it in that case, and fail if we can't help
* it */
if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
printed = strdup(specifier == 'u' ? "root" : "0");
else {
if (specifier == 'u')
printed = strdup(c->user);
else {
uid_t uid;
r = parse_uid(c->user, &uid);
if (r < 0)
return -ENODATA;
r = asprintf(&printed, UID_FMT, uid);
}
}
} else {
_cleanup_free_ char *tmp = NULL;
const char *username = NULL;
uid_t uid;
if (c->user)
username = c->user;
else
/* get USER env from env or our own uid */
username = tmp = getusername_malloc();
/* fish username from passwd */
r = get_user_creds(&username, &uid, NULL, NULL, NULL);
if (r < 0)
return r;
if (specifier == 'u')
printed = strdup(username);
else
r = asprintf(&printed, UID_FMT, uid);
}
if (r < 0 || !printed)
t = getusername_malloc();
if (!t)
return -ENOMEM;
*ret = t;
return 0;
}
static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
*ret = printed;
return 0;
}
static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
ExecContext *c;
char *n;
int r;
assert(u);
/* On PID 1 (which runs as root) this will not result in NSS,
* which is good. See above */
c = unit_get_exec_context(u);
if (!c)
return -EOPNOTSUPP;
if (u->manager->running_as == MANAGER_SYSTEM) {
/* We cannot use NSS from PID 1, hence try to make the
* best of it if we can, but fail if we can't */
if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
n = strdup("/root");
else
return -EOPNOTSUPP;
} else {
/* return HOME if set, otherwise from passwd */
if (!c || !c->user) {
r = get_home_dir(&n);
if (r < 0)
return r;
} else {
const char *username, *home;
username = c->user;
r = get_user_creds(&username, NULL, NULL, &home, NULL);
if (r < 0)
return r;
n = strdup(home);
}
}
if (!n)
return -ENOMEM;
*ret = n;
return 0;
return get_home_dir(ret);
}
static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
ExecContext *c;
char *n;
int r;
assert(u);
/* On PID 1 (which runs as root) this will not result in NSS,
* which is good. See above */
c = unit_get_exec_context(u);
if (!c)
return -EOPNOTSUPP;
if (u->manager->running_as == MANAGER_SYSTEM) {
/* We cannot use NSS from PID 1, hence try to make the
* best of it if we can, but fail if we can't */
if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
n = strdup("/bin/sh");
else
return -EOPNOTSUPP;
} else {
/* return /bin/sh for root, otherwise the value from passwd */
if (!c->user) {
r = get_shell(&n);
if (r < 0)
return r;
} else {
const char *username, *shell;
username = c->user;
r = get_user_creds(&username, NULL, NULL, NULL, &shell);
if (r < 0)
return r;
n = strdup(shell);
}
}
if (!n)
return -ENOMEM;
*ret = n;
return 0;
return get_shell(ret);
}
int unit_name_printf(Unit *u, const char* format, char **ret) {
@ -354,10 +235,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
* %r where units in this slice are placed in the cgroup tree
* %R the root of this systemd's instance tree
* %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
* %U the UID of the configured user or running user
* %u the username of the configured user or running user
* %h the homedir of the configured user or running user
* %s the shell of the configured user or running user
* %U the UID of the running user
* %u the username of the running user
* %h the homedir of the running user
* %s the shell of the running user
* %m the machine ID of the running system
* %H the host name of the running system
* %b the boot ID of the running system
@ -377,7 +258,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
{ 'r', specifier_cgroup_slice, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_runtime, NULL },
{ 'U', specifier_user_name, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },

View file

@ -3148,12 +3148,19 @@ int unit_following_set(Unit *u, Set **s) {
}
UnitFileState unit_get_unit_file_state(Unit *u) {
int r;
assert(u);
if (u->unit_file_state < 0 && u->fragment_path)
u->unit_file_state = unit_file_get_state(
if (u->unit_file_state < 0 && u->fragment_path) {
r = unit_file_get_state(
u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
NULL, basename(u->fragment_path));
NULL,
basename(u->fragment_path),
&u->unit_file_state);
if (r < 0)
u->unit_file_state = UNIT_FILE_BAD;
}
return u->unit_file_state;
}
@ -3164,7 +3171,8 @@ int unit_get_unit_file_preset(Unit *u) {
if (u->unit_file_preset < 0 && u->fragment_path)
u->unit_file_preset = unit_file_query_preset(
u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
NULL, basename(u->fragment_path));
NULL,
basename(u->fragment_path));
return u->unit_file_preset;
}

View file

@ -67,42 +67,28 @@ static int specifier_instance(char specifier, void *data, void *userdata, char *
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
UnitFileInstallInfo *i = userdata;
const char *username;
_cleanup_free_ char *tmp = NULL;
char *printed = NULL;
char *t;
assert(i);
/* 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 (i->user)
username = i->user;
else
/* get USER env from env or our own uid */
username = tmp = getusername_malloc();
t = getusername_malloc();
if (!t)
return -ENOMEM;
switch (specifier) {
case 'u':
printed = strdup(username);
break;
case 'U': {
/* fish username from passwd */
uid_t uid;
int r;
r = get_user_creds(&username, &uid, NULL, NULL, NULL);
if (r < 0)
return r;
if (asprintf(&printed, UID_FMT, uid) < 0)
return -ENOMEM;
break;
}}
*ret = printed;
*ret = t;
return 0;
}
static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
return 0;
}
int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) {
@ -114,8 +100,8 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret)
* %p: the prefix (foo)
* %i: the instance (bar)
* %U the UID of the configured user or running user
* %u the username of the configured user or running user
* %U the UID of the running user
* %u the username of running user
* %m the machine ID of the running system
* %H the host name of the running system
* %b the boot ID of the running system
@ -128,7 +114,7 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret)
{ 'p', specifier_prefix, NULL },
{ 'i', specifier_instance, NULL },
{ 'U', specifier_user_name, NULL },
{ 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'm', specifier_machine_id, NULL },

File diff suppressed because it is too large Load diff

View file

@ -25,13 +25,15 @@ typedef enum UnitFileScope UnitFileScope;
typedef enum UnitFileState UnitFileState;
typedef enum UnitFilePresetMode UnitFilePresetMode;
typedef enum UnitFileChangeType UnitFileChangeType;
typedef enum UnitFileType UnitFileType;
typedef struct UnitFileChange UnitFileChange;
typedef struct UnitFileList UnitFileList;
typedef struct UnitFileInstallInfo UnitFileInstallInfo;
#include "hashmap.h"
#include "unit-name.h"
#include "path-lookup.h"
#include "strv.h"
#include "unit-name.h"
enum UnitFileScope {
UNIT_FILE_SYSTEM,
@ -51,7 +53,7 @@ enum UnitFileState {
UNIT_FILE_STATIC,
UNIT_FILE_DISABLED,
UNIT_FILE_INDIRECT,
UNIT_FILE_INVALID,
UNIT_FILE_BAD,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
};
@ -82,10 +84,17 @@ struct UnitFileList {
UnitFileState state;
};
enum UnitFileType {
UNIT_FILE_TYPE_REGULAR,
UNIT_FILE_TYPE_SYMLINK,
UNIT_FILE_TYPE_MASKED,
_UNIT_FILE_TYPE_MAX,
_UNIT_FILE_TYPE_INVALID = -1,
};
struct UnitFileInstallInfo {
char *name;
char *path;
char *user;
char **aliases;
char **wanted_by;
@ -93,8 +102,26 @@ struct UnitFileInstallInfo {
char **also;
char *default_instance;
UnitFileType type;
char *symlink_target;
};
static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) {
assert(i);
return !strv_isempty(i->aliases) ||
!strv_isempty(i->wanted_by) ||
!strv_isempty(i->required_by);
}
static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) {
assert(i);
return !strv_isempty(i->also);
}
int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
@ -105,21 +132,14 @@ int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char
int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name);
int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
UnitFileState unit_file_lookup_state(
UnitFileScope scope,
const char *root_dir,
const LookupPaths *paths,
const char *name);
UnitFileState unit_file_get_state(
UnitFileScope scope,
const char *root_dir,
const char *filename);
int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,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_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
Hashmap* unit_file_list_free(Hashmap *h);
void unit_file_list_free(Hashmap *h);
int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);

View file

@ -1335,7 +1335,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
UNIT_FILE_MASKED,
UNIT_FILE_MASKED_RUNTIME,
UNIT_FILE_DISABLED,
UNIT_FILE_INVALID)) {
UNIT_FILE_BAD)) {
on = ansi_highlight_red();
off = ansi_normal();
} else if (u->state == UNIT_FILE_ENABLED) {
@ -5437,10 +5437,10 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
else
assert_not_reached("Unknown verb");
if (r < 0) {
log_error_errno(r, "Operation failed: %m");
goto finish;
}
if (r == -ESHUTDOWN)
return log_error_errno(r, "Unit file is masked.");
if (r < 0)
return log_error_errno(r, "Operation failed: %m");
if (!arg_quiet)
dump_unit_file_changes(changes, n_changes);
@ -5557,7 +5557,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
goto finish;
new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop");
for (i = 0; i < n_changes; i++)
@ -5603,7 +5603,8 @@ static int add_dependency(int argc, char *argv[], void *userdata) {
unsigned n_changes = 0;
r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes);
if (r == -ESHUTDOWN)
return log_error_errno(r, "Unit file is masked.");
if (r < 0)
return log_error_errno(r, "Can't add dependency: %m");
@ -5740,8 +5741,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
STRV_FOREACH(name, names) {
UnitFileState state;
state = unit_file_get_state(arg_scope, arg_root, *name);
if (state < 0)
r = unit_file_get_state(arg_scope, arg_root, *name, &state);
if (r < 0)
return log_error_errno(state, "Failed to get unit file state for %s: %m", *name);
if (IN_SET(state,

View file

@ -742,6 +742,7 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
char **path;
int r;
assert(lp);
assert(all_services);
@ -761,7 +762,6 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
_cleanup_free_ char *fpath = NULL, *name = NULL;
_cleanup_(free_sysvstubp) SysvStub *service = NULL;
struct stat st;
int r;
if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
@ -781,8 +781,12 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
if (hashmap_contains(all_services, name))
continue;
if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) {
log_debug("Native unit for %s already exists, skipping", name);
r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL);
if (r < 0 && r != -ENOENT) {
log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
continue;
} else if (r >= 0) {
log_debug("Native unit for %s already exists, skipping.", name);
continue;
}

View file

@ -0,0 +1,665 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "alloc-util.h"
#include "fileio.h"
#include "install.h"
#include "mkdir.h"
#include "rm-rf.h"
#include "string-util.h"
static void test_basic_mask_and_enable(const char *root) {
const char *p;
UnitFileState state;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT);
p = strjoina(root, "/usr/lib/systemd/system/a.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
p = strjoina(root, "/usr/lib/systemd/system/b.service");
assert_se(symlink("a.service", p) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
p = strjoina(root, "/usr/lib/systemd/system/c.service");
assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
p = strjoina(root, "/usr/lib/systemd/system/d.service");
assert_se(symlink("c.service", p) >= 0);
/* This one is interesting, as d follows a relative, then an absolute symlink */
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_mask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/dev/null"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED);
/* Enabling a masked unit should fail! */
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
/* Enabling it again should succeed but be a NOP */
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
assert_se(n_changes == 0);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
/* Disabling a disabled unit must suceed but be a NOP */
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 0);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
/* Let's enable this indirectly via a symlink */
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("d.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
/* Let's try to reenable */
assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("b.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 2);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
assert_se(streq(changes[0].path, p));
assert_se(changes[1].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service"));
assert_se(streq(changes[1].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
}
static void test_linked_units(const char *root) {
const char *p, *q;
UnitFileState state;
UnitFileChange *changes = NULL;
unsigned n_changes = 0, i;
/*
* We'll test three cases here:
*
* a) a unit file in /opt, that we use "systemctl link" and
* "systemctl enable" on to make it available to the system
*
* b) a unit file in /opt, that is statically linked into
* /usr/lib/systemd/system, that "enable" should work on
* correctly.
*
* c) a unit file in /opt, that is linked into
* /etc/systemd/system, and where "enable" should result in
* -ELOOP, since using information from /etc to generate
* information in /etc should not be allowed.
*/
p = strjoina(root, "/opt/linked.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/opt/linked2.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/opt/linked3.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT);
p = strjoina(root, "/usr/lib/systemd/system/linked2.service");
assert_se(symlink("/opt/linked2.service", p) >= 0);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service");
assert_se(symlink("/opt/linked3.service", p) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED);
/* First, let's link the unit into the search path */
assert_se(unit_file_link(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/opt/linked.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED);
/* Let's unlink it from the search path again */
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
/* Now, let's not just link it, but also enable it */
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 2);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
for (i = 0 ; i < n_changes; i++) {
assert_se(changes[i].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[i].source, "/opt/linked.service"));
if (p && streq(changes[i].path, p))
p = NULL;
else if (q && streq(changes[i].path, q))
q = NULL;
else
assert_not_reached("wut?");
}
assert(!p && !q);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
/* And let's unlink it again */
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 2);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
for (i = 0; i < n_changes; i++) {
assert_se(changes[i].type == UNIT_FILE_UNLINK);
if (p && streq(changes[i].path, p))
p = NULL;
else if (q && streq(changes[i].path, q))
q = NULL;
else
assert_not_reached("wut?");
}
assert(!p && !q);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked2.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 2);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service");
q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service");
for (i = 0 ; i < n_changes; i++) {
assert_se(changes[i].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[i].source, "/opt/linked2.service"));
if (p && streq(changes[i].path, p))
p = NULL;
else if (q && streq(changes[i].path, q))
q = NULL;
else
assert_not_reached("wut?");
}
assert(!p && !q);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked3.service"), false, &changes, &n_changes) == -ELOOP);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
static void test_default(const char *root) {
_cleanup_free_ char *def = NULL;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
const char *p;
p = strjoina(root, "/usr/lib/systemd/system/test-default-real.target");
assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/test-default.target");
assert_se(symlink("test-default-real.target", p) >= 0);
assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "idontexist.target", false, &changes, &n_changes) == -ENOENT);
assert_se(n_changes == 0);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "test-default.target", false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/default.target");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0);
assert_se(streq_ptr(def, "test-default-real.target"));
}
static void test_add_dependency(const char *root) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
const char *p;
p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-target.target");
assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-target.target");
assert_se(symlink("real-add-dependency-test-target.target", p) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service");
assert_se(symlink("real-add-dependency-test-service.service", p) >= 0);
assert_se(unit_file_add_dependency(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
static void test_template_enable(const char *root) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
UnitFileState state;
const char *p;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT);
p = strjoina(root, "/usr/lib/systemd/system/template@.service");
assert_se(write_string_file(p,
"[Install]\n"
"DefaultInstance=def\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service");
assert_se(symlink("template@.service", p) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), false, &changes, &n_changes) >= 0);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@foo.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template-symlink@quux.service"), false, &changes, &n_changes) >= 0);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@quux.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
}
static void test_indirect(const char *root) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
UnitFileState state;
const char *p;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT);
p = strjoina(root, "/usr/lib/systemd/system/indirecta.service");
assert_se(write_string_file(p,
"[Install]\n"
"Also=indirectb.service\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/indirectb.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/indirectc.service");
assert_se(symlink("indirecta.service", p) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
static void test_preset_and_list(const char *root) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0, i;
const char *p, *q;
UnitFileState state;
bool got_yes = false, got_no = false;
Iterator j;
UnitFileList *fl;
Hashmap *h;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT);
p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
assert_se(write_string_file(p,
"[Install]\n"
"WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
assert_se(write_string_file(p,
"enable *-yes.*\n"
"disable *\n", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_SYMLINK);
assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service"));
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == UNIT_FILE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
assert_se(streq(changes[0].path, p));
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
assert_se(n_changes == 0);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, false, root, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
assert_se(n_changes > 0);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
for (i = 0; i < n_changes; i++) {
if (changes[i].type == UNIT_FILE_SYMLINK) {
assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service"));
assert_se(streq(changes[i].path, p));
} else
assert_se(changes[i].type == UNIT_FILE_UNLINK);
}
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(h = hashmap_new(&string_hash_ops));
assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
HASHMAP_FOREACH(fl, h, j) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0);
assert_se(fl->state == state);
if (streq(fl->path, p)) {
got_yes = true;
assert_se(fl->state == UNIT_FILE_ENABLED);
} else if (streq(fl->path, q)) {
got_no = true;
assert_se(fl->state == UNIT_FILE_DISABLED);
} else
assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT));
}
unit_file_list_free(h);
assert_se(got_yes && got_no);
}
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
assert_se(mkdtemp(root));
p = strjoina(root, "/usr/lib/systemd/system/");
assert_se(mkdir_p(p, 0755) >= 0);
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/");
assert_se(mkdir_p(p, 0755) >= 0);
p = strjoina(root, "/run/systemd/system/");
assert_se(mkdir_p(p, 0755) >= 0);
p = strjoina(root, "/opt/");
assert_se(mkdir_p(p, 0755) >= 0);
p = strjoina(root, "/usr/lib/systemd/system-preset/");
assert_se(mkdir_p(p, 0755) >= 0);
test_basic_mask_and_enable(root);
test_linked_units(root);
test_default(root);
test_add_dependency(root);
test_template_enable(root);
test_indirect(root);
test_preset_and_list(root);
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0;
}

View file

@ -46,17 +46,19 @@ int main(int argc, char* argv[]) {
const char *const files2[] = { "/home/lennart/test.service", NULL };
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
UnitFileState state = 0;
h = hashmap_new(&string_hash_ops);
r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
assert_se(r == 0);
HASHMAP_FOREACH(p, h, i) {
UnitFileState s;
UnitFileState s = _UNIT_FILE_STATE_INVALID;
s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path));
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s);
assert_se(p->state == s);
assert_se((r < 0 && p->state == UNIT_FILE_BAD) ||
(p->state == s));
fprintf(stderr, "%s (%s)\n",
p->path,
@ -78,7 +80,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
log_error("disable");
@ -91,7 +95,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_DISABLED);
log_error("mask");
changes = NULL;
@ -106,7 +112,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_MASKED);
log_error("unmask");
changes = NULL;
@ -121,7 +129,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_DISABLED);
log_error("mask");
changes = NULL;
@ -133,7 +143,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_MASKED);
log_error("disable");
changes = NULL;
@ -148,7 +160,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_MASKED);
log_error("umask");
changes = NULL;
@ -160,7 +174,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_DISABLED);
log_error("enable files2");
changes = NULL;
@ -172,19 +188,22 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
log_error("disable files2");
changes = NULL;
n_changes = 0;
r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
r = unit_file_disable(UNIT_FILE_SYSTEM, false, 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);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r < 0);
log_error("link files2");
changes = NULL;
@ -196,19 +215,22 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_LINKED);
log_error("disable files2");
changes = NULL;
n_changes = 0;
r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
r = unit_file_disable(UNIT_FILE_SYSTEM, false, 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);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r < 0);
log_error("link files2");
changes = NULL;
@ -220,7 +242,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_LINKED);
log_error("reenable files2");
changes = NULL;
@ -232,19 +256,22 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
log_error("disable files2");
changes = NULL;
n_changes = 0;
r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
r = unit_file_disable(UNIT_FILE_SYSTEM, false, 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);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
assert_se(r < 0);
log_error("preset files");
changes = NULL;
n_changes = 0;
@ -255,7 +282,9 @@ int main(int argc, char* argv[]) {
dump_changes(changes, n_changes);
unit_file_changes_free(changes, n_changes);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0])) == UNIT_FILE_ENABLED);
r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state);
assert_se(r >= 0);
assert_se(state == UNIT_FILE_ENABLED);
return 0;
}

View file

@ -40,6 +40,7 @@
#include "string-util.h"
#include "strv.h"
#include "test-helper.h"
#include "user-util.h"
#include "util.h"
static int test_unit_file_get_set(void) {
@ -558,76 +559,66 @@ static void test_load_env_file_5(void) {
static void test_install_printf(void) {
char name[] = "name.service",
path[] = "/run/systemd/system/name.service",
user[] = "xxxx-no-such-user";
UnitFileInstallInfo i = {name, path, user};
UnitFileInstallInfo i2 = {name, path, NULL};
path[] = "/run/systemd/system/name.service";
UnitFileInstallInfo i = { .name = name, .path = path, };
UnitFileInstallInfo i2 = { .name= name, .path = path, };
char name3[] = "name@inst.service",
path3[] = "/run/systemd/system/name.service";
UnitFileInstallInfo i3 = {name3, path3, user};
UnitFileInstallInfo i4 = {name3, path3, NULL};
UnitFileInstallInfo i3 = { .name = name3, .path = path3, };
UnitFileInstallInfo i4 = { .name = name3, .path = path3, };
_cleanup_free_ char *mid, *bid, *host;
_cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL;
assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
assert_se((host = gethostname_malloc()));
assert_se((user = getusername_malloc()));
assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0);
#define expect(src, pattern, result) \
do { \
_cleanup_free_ char *t = NULL; \
_cleanup_free_ char \
*d1 = strdup(i.name), \
*d2 = strdup(i.path), \
*d3 = strdup(i.user); \
*d2 = strdup(i.path); \
assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
memzero(i.name, strlen(i.name)); \
memzero(i.path, strlen(i.path)); \
memzero(i.user, strlen(i.user)); \
assert_se(d1 && d2 && d3); \
assert_se(d1 && d2); \
if (result) { \
printf("%s\n", t); \
assert_se(streq(t, result)); \
} else assert_se(t == NULL); \
assert_se(streq(t, result)); \
} else assert_se(t == NULL); \
strcpy(i.name, d1); \
strcpy(i.path, d2); \
strcpy(i.user, d3); \
} while(false)
assert_se(setenv("USER", "root", 1) == 0);
expect(i, "%n", "name.service");
expect(i, "%N", "name");
expect(i, "%p", "name");
expect(i, "%i", "");
expect(i, "%u", "xxxx-no-such-user");
DISABLE_WARNING_NONNULL;
expect(i, "%U", NULL);
REENABLE_WARNING;
expect(i, "%u", user);
expect(i, "%U", uid);
expect(i, "%m", mid);
expect(i, "%b", bid);
expect(i, "%H", host);
expect(i2, "%u", "root");
expect(i2, "%U", "0");
expect(i2, "%u", user);
expect(i2, "%U", uid);
expect(i3, "%n", "name@inst.service");
expect(i3, "%N", "name@inst");
expect(i3, "%p", "name");
expect(i3, "%u", "xxxx-no-such-user");
DISABLE_WARNING_NONNULL;
expect(i3, "%U", NULL);
REENABLE_WARNING;
expect(i3, "%u", user);
expect(i3, "%U", uid);
expect(i3, "%m", mid);
expect(i3, "%b", bid);
expect(i3, "%H", host);
expect(i4, "%u", "root");
expect(i4, "%U", "0");
expect(i4, "%u", user);
expect(i4, "%U", uid);
}
static uint64_t make_cap(int cap) {

View file

@ -37,6 +37,7 @@
#include "unit-name.h"
#include "unit-printf.h"
#include "unit.h"
#include "user-util.h"
#include "util.h"
static void test_unit_name_is_valid(void) {
@ -193,15 +194,15 @@ static int test_unit_printf(void) {
Unit *u, *u2;
int r;
_cleanup_free_ char *mid, *bid, *host, *root_uid;
struct passwd *root;
_cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
assert_se((host = gethostname_malloc()));
assert_se((root = getpwnam("root")));
assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0);
assert_se(host = gethostname_malloc());
assert_se(user = getusername_malloc());
assert_se(asprintf(&uid, UID_FMT, getuid()));
assert_se(get_home_dir(&home) >= 0);
assert_se(get_shell(&shell) >= 0);
r = manager_new(MANAGER_USER, true, &m);
if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
@ -222,8 +223,6 @@ static int test_unit_printf(void) {
assert_se(streq(t, expected)); \
}
assert_se(setenv("USER", "root", 1) == 0);
assert_se(setenv("HOME", "/root", 1) == 0);
assert_se(setenv("XDG_RUNTIME_DIR", "/run/user/1/", 1) == 0);
assert_se(u = unit_new(m, sizeof(Service)));
@ -242,9 +241,9 @@ static int test_unit_printf(void) {
expect(u, "%p", "blah");
expect(u, "%P", "blah");
expect(u, "%i", "");
expect(u, "%u", root->pw_name);
expect(u, "%U", root_uid);
expect(u, "%h", root->pw_dir);
expect(u, "%u", user);
expect(u, "%U", uid);
expect(u, "%h", home);
expect(u, "%m", mid);
expect(u, "%b", bid);
expect(u, "%H", host);
@ -262,9 +261,9 @@ static int test_unit_printf(void) {
expect(u2, "%P", "blah");
expect(u2, "%i", "foo-foo");
expect(u2, "%I", "foo/foo");
expect(u2, "%u", root->pw_name);
expect(u2, "%U", root_uid);
expect(u2, "%h", root->pw_dir);
expect(u2, "%u", user);
expect(u2, "%U", uid);
expect(u2, "%h", home);
expect(u2, "%m", mid);
expect(u2, "%b", bid);
expect(u2, "%H", host);