|
|
1abbee |
From 272378080241106862a9cca2e4e84857fe1aaf5c Mon Sep 17 00:00:00 2001
|
|
|
1abbee |
From: Lennart Poettering <lennart@poettering.net>
|
|
|
1abbee |
Date: Thu, 8 Oct 2015 22:31:56 +0200
|
|
|
1abbee |
Subject: [PATCH] install: follow unit file symlinks in /usr, but not /etc when
|
|
|
1abbee |
looking for [Install] data
|
|
|
1abbee |
|
|
|
1abbee |
Some distributions use alias unit files via symlinks in /usr to cover
|
|
|
1abbee |
for legacy service names. With this change we'll allow "systemctl
|
|
|
1abbee |
enable" on such aliases.
|
|
|
1abbee |
|
|
|
1abbee |
Previously, our rule was that symlinks are user configuration that
|
|
|
1abbee |
"systemctl enable" + "systemctl disable" creates and removes, while unit
|
|
|
1abbee |
files is where the instructions to do so are store. As a result of the
|
|
|
1abbee |
rule we'd never read install information through symlinks, since that
|
|
|
1abbee |
would mix enablement state with installation instructions.
|
|
|
1abbee |
|
|
|
1abbee |
Now, the new rule is that only symlinks inside of /etc are
|
|
|
1abbee |
configuration. Unit files, and symlinks in /usr are now valid for
|
|
|
1abbee |
installation instructions.
|
|
|
1abbee |
|
|
|
1abbee |
This patch is quite a rework of the whole install logic, and makes the
|
|
|
1abbee |
following addional changes:
|
|
|
1abbee |
|
|
|
1abbee |
- Adds a complete test "test-instal-root" that tests the install logic
|
|
|
1abbee |
pretty comprehensively.
|
|
|
1abbee |
|
|
|
1abbee |
- Never uses canonicalize_file_name(), because that's incompatible with
|
|
|
1abbee |
operation relative to a specific root directory.
|
|
|
1abbee |
|
|
|
1abbee |
- unit_file_get_state() is reworked to return a proper error, and
|
|
|
1abbee |
returns the state in a call-by-ref parameter. This cleans up confusion
|
|
|
1abbee |
between the enum type and errno-like errors.
|
|
|
1abbee |
|
|
|
1abbee |
- The new logic puts a limit on how long to follow unit file symlinks:
|
|
|
1abbee |
it will do so only for 64 steps at max.
|
|
|
1abbee |
|
|
|
1abbee |
- The InstallContext object's fields are renamed to will_process and
|
|
|
1abbee |
has_processed (will_install and has_installed) since they are also
|
|
|
1abbee |
used for deinstallation and all kinds of other operations.
|
|
|
1abbee |
|
|
|
1abbee |
- The root directory is always verified before use.
|
|
|
1abbee |
|
|
|
1abbee |
- install.c is reordered to place the exported functions together.
|
|
|
1abbee |
|
|
|
1abbee |
- Stricter rules are followed when traversing symlinks: the unit suffix
|
|
|
1abbee |
must say identical, and it's not allowed to link between regular units
|
|
|
1abbee |
and templated units.
|
|
|
1abbee |
|
|
|
1abbee |
- Various modernizations
|
|
|
1abbee |
|
|
|
1abbee |
- The "invalid" unit file state has been renamed to "bad", in order to
|
|
|
1abbee |
avoid confusion between UNIT_FILE_INVALID and
|
|
|
1abbee |
_UNIT_FILE_STATE_INVALID. Given that the state should normally not be
|
|
|
1abbee |
seen and is not documented this should not be a problematic change.
|
|
|
1abbee |
The new name is now documented however.
|
|
|
1abbee |
|
|
|
1abbee |
Fixes #1375, #1718, #1706
|
|
|
1abbee |
|
|
|
1abbee |
Cherry-picked from: 79413b673b45adc98dfeaec882bbdda2343cb2f9
|
|
|
1abbee |
Resolves: #1159308
|
|
|
1abbee |
---
|
|
|
1abbee |
Makefile.am | 12 +-
|
|
|
1abbee |
man/systemctl.xml | 14 +-
|
|
|
1abbee |
src/core/dbus-manager.c | 6 +-
|
|
|
1abbee |
src/core/dbus-unit.c | 4 +-
|
|
|
1abbee |
src/core/load-fragment.c | 2 +-
|
|
|
1abbee |
src/core/manager.c | 2 +-
|
|
|
1abbee |
src/core/snapshot.c | 2 +-
|
|
|
1abbee |
src/core/unit.c | 19 +-
|
|
|
1abbee |
src/dbus1-generator/dbus1-generator.c | 2 +-
|
|
|
1abbee |
src/firstboot/firstboot.c | 18 +-
|
|
|
1abbee |
src/shared/cgroup-util.c | 6 +-
|
|
|
1abbee |
src/shared/install.c | 1909 +++++++++++++++++++--------------
|
|
|
1abbee |
src/shared/install.h | 46 +-
|
|
|
1abbee |
src/shared/path-util.c | 34 +
|
|
|
1abbee |
src/shared/path-util.h | 27 +
|
|
|
1abbee |
src/shared/unit-name.c | 32 +-
|
|
|
1abbee |
src/shared/unit-name.h | 14 +-
|
|
|
1abbee |
src/shared/util.c | 21 +
|
|
|
1abbee |
src/shared/util.h | 1 +
|
|
|
1abbee |
src/systemctl/systemctl.c | 6 +-
|
|
|
1abbee |
src/sysv-generator/sysv-generator.c | 10 +-
|
|
|
1abbee |
src/test/test-install-root.c | 663 ++++++++++++
|
|
|
1abbee |
src/test/test-install.c | 71 +-
|
|
|
1abbee |
23 files changed, 1998 insertions(+), 923 deletions(-)
|
|
|
1abbee |
create mode 100644 src/test/test-install-root.c
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/Makefile.am b/Makefile.am
|
|
|
1abbee |
index 3af720b..3a09e0a 100644
|
|
|
1abbee |
--- a/Makefile.am
|
|
|
1abbee |
+++ b/Makefile.am
|
|
|
1abbee |
@@ -1427,7 +1427,8 @@ tests += \
|
|
|
1abbee |
test-copy \
|
|
|
1abbee |
test-cap-list \
|
|
|
1abbee |
test-sigbus \
|
|
|
1abbee |
- test-verbs
|
|
|
1abbee |
+ test-verbs \
|
|
|
1abbee |
+ test-install-root
|
|
|
1abbee |
|
|
|
1abbee |
EXTRA_DIST += \
|
|
|
1abbee |
test/a.service \
|
|
|
1abbee |
@@ -1721,6 +1722,15 @@ test_verbs_SOURCES = \
|
|
|
1abbee |
test_verbs_LDADD = \
|
|
|
1abbee |
libsystemd-shared.la
|
|
|
1abbee |
|
|
|
1abbee |
+test_install_root_SOURCES = \
|
|
|
1abbee |
+ src/test/test-install-root.c
|
|
|
1abbee |
+
|
|
|
1abbee |
+test_install_root_LDADD = \
|
|
|
1abbee |
+ libsystemd-units.la \
|
|
|
1abbee |
+ libsystemd-label.la \
|
|
|
1abbee |
+ libsystemd-internal.la \
|
|
|
1abbee |
+ libsystemd-shared.la
|
|
|
1abbee |
+
|
|
|
1abbee |
test_namespace_LDADD = \
|
|
|
1abbee |
libsystemd-core.la
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/man/systemctl.xml b/man/systemctl.xml
|
|
|
1abbee |
index c6f5842..2d0678d 100644
|
|
|
1abbee |
--- a/man/systemctl.xml
|
|
|
1abbee |
+++ b/man/systemctl.xml
|
|
|
1abbee |
@@ -905,10 +905,11 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
|
|
|
1abbee |
<term><command>list-unit-files <optional><replaceable>PATTERN...</replaceable></optional></command></term>
|
|
|
1abbee |
|
|
|
1abbee |
<listitem>
|
|
|
1abbee |
- <para>List installed unit files. If one or more
|
|
|
1abbee |
- <replaceable>PATTERN</replaceable>s are specified, only
|
|
|
1abbee |
- units whose filename (just the last component of the path)
|
|
|
1abbee |
- matches one of them are shown.</para>
|
|
|
1abbee |
+ <para>List installed unit files and their enablement state
|
|
|
1abbee |
+ (as reported by <command>is-enabled</command>). If one or
|
|
|
1abbee |
+ more <replaceable>PATTERN</replaceable>s are specified,
|
|
|
1abbee |
+ only units whose filename (just the last component of the
|
|
|
1abbee |
+ path) matches one of them are shown.</para>
|
|
|
1abbee |
</listitem>
|
|
|
1abbee |
</varlistentry>
|
|
|
1abbee |
|
|
|
1abbee |
@@ -1108,6 +1109,11 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
|
|
|
1abbee |
<entry>Unit file is not enabled.</entry>
|
|
|
1abbee |
<entry>1</entry>
|
|
|
1abbee |
</row>
|
|
|
1abbee |
+ <row>
|
|
|
1abbee |
+ <entry><literal>bad</literal></entry>
|
|
|
1abbee |
+ <entry>Unit file is invalid or another error occured. Note that <command>is-enabled</command> wil 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>
|
|
|
1abbee |
+ <entry>> 0</entry>
|
|
|
1abbee |
+ </row>
|
|
|
1abbee |
|
|
|
1abbee |
</tgroup>
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
|
|
|
1abbee |
index 1ec350e..faa124d 100644
|
|
|
1abbee |
--- a/src/core/dbus-manager.c
|
|
|
1abbee |
+++ b/src/core/dbus-manager.c
|
|
|
1abbee |
@@ -1530,9 +1530,9 @@ static int method_get_unit_file_state(sd_bus *bus, sd_bus_message *message, void
|
|
|
1abbee |
|
|
|
1abbee |
scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
|
|
1abbee |
|
|
|
1abbee |
- state = unit_file_get_state(scope, NULL, name);
|
|
|
1abbee |
- if (state < 0)
|
|
|
1abbee |
- return state;
|
|
|
1abbee |
+ r = unit_file_get_state(scope, NULL, name, &state);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state));
|
|
|
1abbee |
}
|
|
|
1abbee |
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
|
|
|
1abbee |
index 625d21a..227915e 100644
|
|
|
1abbee |
--- a/src/core/dbus-unit.c
|
|
|
1abbee |
+++ b/src/core/dbus-unit.c
|
|
|
1abbee |
@@ -919,7 +919,7 @@ static int bus_unit_set_transient_property(
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(s, TEMPLATE_INVALID) || !endswith(s, ".slice"))
|
|
|
1abbee |
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice"))
|
|
|
1abbee |
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
|
|
|
1abbee |
|
|
|
1abbee |
if (isempty(s)) {
|
|
|
1abbee |
@@ -967,7 +967,7 @@ static int bus_unit_set_transient_property(
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
while ((r = sd_bus_message_read(message, "s", &other)) > 0) {
|
|
|
1abbee |
- if (!unit_name_is_valid(other, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(other, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
|
|
|
1abbee |
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other);
|
|
|
1abbee |
|
|
|
1abbee |
if (mode != UNIT_CHECK) {
|
|
|
1abbee |
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
|
|
|
1abbee |
index ec4cf4e..70c0918 100644
|
|
|
1abbee |
--- a/src/core/load-fragment.c
|
|
|
1abbee |
+++ b/src/core/load-fragment.c
|
|
|
1abbee |
@@ -3409,7 +3409,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
|
|
|
1abbee |
* unit name. */
|
|
|
1abbee |
name = basename(*filename);
|
|
|
1abbee |
|
|
|
1abbee |
- if (unit_name_is_valid(name, TEMPLATE_VALID)) {
|
|
|
1abbee |
+ if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
|
|
|
1abbee |
|
|
|
1abbee |
id = set_get(names, name);
|
|
|
1abbee |
if (!id) {
|
|
|
1abbee |
diff --git a/src/core/manager.c b/src/core/manager.c
|
|
|
1abbee |
index 7483a96..bde17ce 100644
|
|
|
1abbee |
--- a/src/core/manager.c
|
|
|
1abbee |
+++ b/src/core/manager.c
|
|
|
1abbee |
@@ -1328,7 +1328,7 @@ int manager_load_unit_prepare(
|
|
|
1abbee |
|
|
|
1abbee |
t = unit_name_to_type(name);
|
|
|
1abbee |
|
|
|
1abbee |
- if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
|
|
|
1abbee |
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
|
|
|
1abbee |
|
|
|
1abbee |
ret = manager_get_unit(m, name);
|
|
|
1abbee |
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
|
|
|
1abbee |
index b1d8448..f222ec2 100644
|
|
|
1abbee |
--- a/src/core/snapshot.c
|
|
|
1abbee |
+++ b/src/core/snapshot.c
|
|
|
1abbee |
@@ -201,7 +201,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e,
|
|
|
1abbee |
assert(_s);
|
|
|
1abbee |
|
|
|
1abbee |
if (name) {
|
|
|
1abbee |
- if (!unit_name_is_valid(name, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
|
|
|
1abbee |
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
|
|
|
1abbee |
|
|
|
1abbee |
if (unit_name_to_type(name) != UNIT_SNAPSHOT)
|
|
|
1abbee |
diff --git a/src/core/unit.c b/src/core/unit.c
|
|
|
1abbee |
index 4fb2fd3..db5aa98 100644
|
|
|
1abbee |
--- a/src/core/unit.c
|
|
|
1abbee |
+++ b/src/core/unit.c
|
|
|
1abbee |
@@ -158,7 +158,7 @@ int unit_add_name(Unit *u, const char *text) {
|
|
|
1abbee |
if (!s)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(s, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
assert_se((t = unit_name_to_type(s)) >= 0);
|
|
|
1abbee |
@@ -3119,12 +3119,18 @@ int unit_following_set(Unit *u, Set **s) {
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
UnitFileState unit_get_unit_file_state(Unit *u) {
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
assert(u);
|
|
|
1abbee |
|
|
|
1abbee |
- if (u->unit_file_state < 0 && u->fragment_path)
|
|
|
1abbee |
- u->unit_file_state = unit_file_get_state(
|
|
|
1abbee |
- u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
|
|
|
1abbee |
- NULL, basename(u->fragment_path));
|
|
|
1abbee |
+ if (u->unit_file_state < 0 && u->fragment_path) {
|
|
|
1abbee |
+ r = unit_file_get_state(u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
|
|
|
1abbee |
+ NULL,
|
|
|
1abbee |
+ basename(u->fragment_path),
|
|
|
1abbee |
+ &u->unit_file_state);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ u->unit_file_state = UNIT_FILE_BAD;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
|
|
|
1abbee |
return u->unit_file_state;
|
|
|
1abbee |
}
|
|
|
1abbee |
@@ -3135,7 +3141,8 @@ int unit_get_unit_file_preset(Unit *u) {
|
|
|
1abbee |
if (u->unit_file_preset < 0 && u->fragment_path)
|
|
|
1abbee |
u->unit_file_preset = unit_file_query_preset(
|
|
|
1abbee |
u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
|
|
|
1abbee |
- NULL, basename(u->fragment_path));
|
|
|
1abbee |
+ NULL,
|
|
|
1abbee |
+ basename(u->fragment_path));
|
|
|
1abbee |
|
|
|
1abbee |
return u->unit_file_preset;
|
|
|
1abbee |
}
|
|
|
1abbee |
diff --git a/src/dbus1-generator/dbus1-generator.c b/src/dbus1-generator/dbus1-generator.c
|
|
|
1abbee |
index 2e08af2..c909a4b 100644
|
|
|
1abbee |
--- a/src/dbus1-generator/dbus1-generator.c
|
|
|
1abbee |
+++ b/src/dbus1-generator/dbus1-generator.c
|
|
|
1abbee |
@@ -188,7 +188,7 @@ static int add_dbus(const char *path, const char *fname, const char *type) {
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
if (service) {
|
|
|
1abbee |
- if (!unit_name_is_valid(service, TEMPLATE_INVALID)) {
|
|
|
1abbee |
+ if (!unit_name_is_valid(service, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) {
|
|
|
1abbee |
log_warning("Unit name %s is not valid, ignoring.", service);
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
|
|
|
1abbee |
index a37ca17..b77c1d8 100644
|
|
|
1abbee |
--- a/src/firstboot/firstboot.c
|
|
|
1abbee |
+++ b/src/firstboot/firstboot.c
|
|
|
1abbee |
@@ -50,8 +50,6 @@ static bool arg_copy_locale = false;
|
|
|
1abbee |
static bool arg_copy_timezone = false;
|
|
|
1abbee |
static bool arg_copy_root_password = false;
|
|
|
1abbee |
|
|
|
1abbee |
-#define prefix_roota(p) (arg_root ? (const char*) strjoina(arg_root, p) : (const char*) p)
|
|
|
1abbee |
-
|
|
|
1abbee |
static void clear_string(char *x) {
|
|
|
1abbee |
|
|
|
1abbee |
if (!x)
|
|
|
1abbee |
@@ -85,13 +83,13 @@ static void print_welcome(void) {
|
|
|
1abbee |
if (done)
|
|
|
1abbee |
return;
|
|
|
1abbee |
|
|
|
1abbee |
- os_release = prefix_roota("/etc/os-release");
|
|
|
1abbee |
+ os_release = prefix_roota(arg_root, "/etc/os-release");
|
|
|
1abbee |
r = parse_env_file(os_release, NEWLINE,
|
|
|
1abbee |
"PRETTY_NAME", &pretty_name,
|
|
|
1abbee |
NULL);
|
|
|
1abbee |
if (r == -ENOENT) {
|
|
|
1abbee |
|
|
|
1abbee |
- os_release = prefix_roota("/usr/lib/os-release");
|
|
|
1abbee |
+ os_release = prefix_roota(arg_root, "/usr/lib/os-release");
|
|
|
1abbee |
r = parse_env_file(os_release, NEWLINE,
|
|
|
1abbee |
"PRETTY_NAME", &pretty_name,
|
|
|
1abbee |
NULL);
|
|
|
1abbee |
@@ -249,7 +247,7 @@ static int process_locale(void) {
|
|
|
1abbee |
unsigned i = 0;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
- etc_localeconf = prefix_roota("/etc/locale.conf");
|
|
|
1abbee |
+ etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
|
|
|
1abbee |
if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -323,7 +321,7 @@ static int process_timezone(void) {
|
|
|
1abbee |
const char *etc_localtime, *e;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
- etc_localtime = prefix_roota("/etc/localtime");
|
|
|
1abbee |
+ etc_localtime = prefix_roota(arg_root, "/etc/localtime");
|
|
|
1abbee |
if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -402,7 +400,7 @@ static int process_hostname(void) {
|
|
|
1abbee |
const char *etc_hostname;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
- etc_hostname = prefix_roota("/etc/hostname");
|
|
|
1abbee |
+ etc_hostname = prefix_roota(arg_root, "/etc/hostname");
|
|
|
1abbee |
if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -427,7 +425,7 @@ static int process_machine_id(void) {
|
|
|
1abbee |
char id[SD_ID128_STRING_MAX];
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
- etc_machine_id = prefix_roota("/etc/machine-id");
|
|
|
1abbee |
+ etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
|
|
|
1abbee |
if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -453,7 +451,7 @@ static int prompt_root_password(void) {
|
|
|
1abbee |
if (!arg_prompt_root_password)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
- etc_shadow = prefix_roota("/etc/shadow");
|
|
|
1abbee |
+ etc_shadow = prefix_roota(arg_root, "/etc/shadow");
|
|
|
1abbee |
if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -542,7 +540,7 @@ static int process_root_password(void) {
|
|
|
1abbee |
const char *etc_shadow;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
- etc_shadow = prefix_roota("/etc/shadow");
|
|
|
1abbee |
+ etc_shadow = prefix_roota(arg_root, "/etc/shadow");
|
|
|
1abbee |
if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
|
|
|
1abbee |
index dfd8689..cf757d2 100644
|
|
|
1abbee |
--- a/src/shared/cgroup-util.c
|
|
|
1abbee |
+++ b/src/shared/cgroup-util.c
|
|
|
1abbee |
@@ -1147,7 +1147,7 @@ int cg_path_decode_unit(const char *cgroup, char **unit){
|
|
|
1abbee |
c = strndupa(cgroup, e - cgroup);
|
|
|
1abbee |
c = cg_unescape(c);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(c, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
s = strdup(c);
|
|
|
1abbee |
@@ -1536,7 +1536,7 @@ int cg_slice_to_path(const char *unit, char **ret) {
|
|
|
1abbee |
assert(unit);
|
|
|
1abbee |
assert(ret);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
if (!endswith(unit, ".slice"))
|
|
|
1abbee |
@@ -1553,7 +1553,7 @@ int cg_slice_to_path(const char *unit, char **ret) {
|
|
|
1abbee |
|
|
|
1abbee |
strcpy(stpncpy(n, p, dash - p), ".slice");
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(n, TEMPLATE_INVALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
escaped = cg_escape(n);
|
|
|
1abbee |
diff --git a/src/shared/install.c b/src/shared/install.c
|
|
|
1abbee |
index aa197e9..5288bb4 100644
|
|
|
1abbee |
--- a/src/shared/install.c
|
|
|
1abbee |
+++ b/src/shared/install.c
|
|
|
1abbee |
@@ -39,15 +39,24 @@
|
|
|
1abbee |
#include "specifier.h"
|
|
|
1abbee |
#include "install-printf.h"
|
|
|
1abbee |
#include "special.h"
|
|
|
1abbee |
+#include "fileio.h"
|
|
|
1abbee |
+
|
|
|
1abbee |
+#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
|
|
|
1abbee |
+
|
|
|
1abbee |
+typedef enum SearchFlags {
|
|
|
1abbee |
+ SEARCH_LOAD = 1,
|
|
|
1abbee |
+ SEARCH_FOLLOW_CONFIG_SYMLINKS = 2,
|
|
|
1abbee |
+} SearchFlags;
|
|
|
1abbee |
|
|
|
1abbee |
typedef struct {
|
|
|
1abbee |
- OrderedHashmap *will_install;
|
|
|
1abbee |
- OrderedHashmap *have_installed;
|
|
|
1abbee |
+ OrderedHashmap *will_process;
|
|
|
1abbee |
+ OrderedHashmap *have_processed;
|
|
|
1abbee |
} InstallContext;
|
|
|
1abbee |
|
|
|
1abbee |
static int in_search_path(const char *path, char **search) {
|
|
|
1abbee |
_cleanup_free_ char *parent = NULL;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
+ char **i;
|
|
|
1abbee |
|
|
|
1abbee |
assert(path);
|
|
|
1abbee |
|
|
|
1abbee |
@@ -55,7 +64,11 @@ static int in_search_path(const char *path, char **search) {
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- return strv_contains(search, parent);
|
|
|
1abbee |
+ STRV_FOREACH(i, search)
|
|
|
1abbee |
+ if (path_equal(parent, *i))
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
|
|
|
1abbee |
@@ -66,6 +79,9 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(ret);
|
|
|
1abbee |
|
|
|
1abbee |
+ /* This determines where we shall create or remove our
|
|
|
1abbee |
+ * installation ("configuration") symlinks */
|
|
|
1abbee |
+
|
|
|
1abbee |
switch (scope) {
|
|
|
1abbee |
|
|
|
1abbee |
case UNIT_FILE_SYSTEM:
|
|
|
1abbee |
@@ -113,6 +129,186 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+static bool is_config_path(UnitFileScope scope, const char *path) {
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(scope >= 0);
|
|
|
1abbee |
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
+ assert(path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Checks whether the specified path is intended for
|
|
|
1abbee |
+ * configuration or is outside of it */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ switch (scope) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case UNIT_FILE_SYSTEM:
|
|
|
1abbee |
+ case UNIT_FILE_GLOBAL:
|
|
|
1abbee |
+ return path_startswith(path, "/etc") ||
|
|
|
1abbee |
+ path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) ||
|
|
|
1abbee |
+ path_startswith(path, "/run");
|
|
|
1abbee |
+
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case UNIT_FILE_USER: {
|
|
|
1abbee |
+ _cleanup_free_ char *p = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = user_config_home(&p);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (r > 0 && path_startswith(path, p))
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ free(p);
|
|
|
1abbee |
+ p = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = user_runtime_dir(&p);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (r > 0 && path_startswith(path, p))
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ default:
|
|
|
1abbee |
+ assert_not_reached("Bad scope");
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+
|
|
|
1abbee |
+static int verify_root_dir(UnitFileScope scope, const char **root_dir) {
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(root_dir);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Verifies that the specified root directory to operate on
|
|
|
1abbee |
+ * makes sense. Reset it to NULL if it is the root directory
|
|
|
1abbee |
+ * or set to empty */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (isempty(*root_dir) || path_equal(*root_dir, "/")) {
|
|
|
1abbee |
+ *root_dir = NULL;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (scope != UNIT_FILE_SYSTEM)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = is_dir(*root_dir, true);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ return -ENOTDIR;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int unit_file_changes_add(
|
|
|
1abbee |
+ UnitFileChange **changes,
|
|
|
1abbee |
+ unsigned *n_changes,
|
|
|
1abbee |
+ UnitFileChangeType type,
|
|
|
1abbee |
+ const char *path,
|
|
|
1abbee |
+ const char *source) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ UnitFileChange *c;
|
|
|
1abbee |
+ unsigned i;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(path);
|
|
|
1abbee |
+ assert(!changes == !n_changes);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!changes)
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
|
|
|
1abbee |
+ if (!c)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *changes = c;
|
|
|
1abbee |
+ i = *n_changes;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ c[i].type = type;
|
|
|
1abbee |
+ c[i].path = strdup(path);
|
|
|
1abbee |
+ if (!c[i].path)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ path_kill_slashes(c[i].path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (source) {
|
|
|
1abbee |
+ c[i].source = strdup(source);
|
|
|
1abbee |
+ if (!c[i].source) {
|
|
|
1abbee |
+ free(c[i].path);
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ path_kill_slashes(c[i].path);
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ c[i].source = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *n_changes = i+1;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
|
|
|
1abbee |
+ unsigned i;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(changes || n_changes == 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!changes)
|
|
|
1abbee |
+ return;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (i = 0; i < n_changes; i++) {
|
|
|
1abbee |
+ free(changes[i].path);
|
|
|
1abbee |
+ free(changes[i].source);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ free(changes);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static int create_symlink(
|
|
|
1abbee |
+ const char *old_path,
|
|
|
1abbee |
+ const char *new_path,
|
|
|
1abbee |
+ bool force,
|
|
|
1abbee |
+ UnitFileChange **changes,
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ _cleanup_free_ char *dest = NULL;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(old_path);
|
|
|
1abbee |
+ assert(new_path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Actually create a symlink, and remember that we did. Is
|
|
|
1abbee |
+ * smart enough to check if there's already a valid symlink in
|
|
|
1abbee |
+ * place. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ mkdir_parents_label(new_path, 0755);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (symlink(old_path, new_path) >= 0) {
|
|
|
1abbee |
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (errno != EEXIST)
|
|
|
1abbee |
+ return -errno;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = readlink_malloc(new_path, &dest);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (path_equal(dest, old_path))
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!force)
|
|
|
1abbee |
+ return -EEXIST;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = symlink_atomic(old_path, new_path);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
|
|
|
1abbee |
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
static int mark_symlink_for_removal(
|
|
|
1abbee |
Set **remove_symlinks_to,
|
|
|
1abbee |
const char *p) {
|
|
|
1abbee |
@@ -136,7 +332,7 @@ static int mark_symlink_for_removal(
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r == -EEXIST ? 0 : r;
|
|
|
1abbee |
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ return 1;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int remove_marked_symlinks_fd(
|
|
|
1abbee |
@@ -144,10 +340,9 @@ static int remove_marked_symlinks_fd(
|
|
|
1abbee |
int fd,
|
|
|
1abbee |
const char *path,
|
|
|
1abbee |
const char *config_path,
|
|
|
1abbee |
- bool *deleted,
|
|
|
1abbee |
+ bool *restart,
|
|
|
1abbee |
UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes,
|
|
|
1abbee |
- char** instance_whitelist) {
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_closedir_ DIR *d = NULL;
|
|
|
1abbee |
int r = 0;
|
|
|
1abbee |
@@ -156,7 +351,7 @@ static int remove_marked_symlinks_fd(
|
|
|
1abbee |
assert(fd >= 0);
|
|
|
1abbee |
assert(path);
|
|
|
1abbee |
assert(config_path);
|
|
|
1abbee |
- assert(deleted);
|
|
|
1abbee |
+ assert(restart);
|
|
|
1abbee |
|
|
|
1abbee |
d = fdopendir(fd);
|
|
|
1abbee |
if (!d) {
|
|
|
1abbee |
@@ -205,42 +400,23 @@ static int remove_marked_symlinks_fd(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
/* This will close nfd, regardless whether it succeeds or not */
|
|
|
1abbee |
- q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist);
|
|
|
1abbee |
+ q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes);
|
|
|
1abbee |
if (q < 0 && r == 0)
|
|
|
1abbee |
r = q;
|
|
|
1abbee |
|
|
|
1abbee |
} else if (de->d_type == DT_LNK) {
|
|
|
1abbee |
_cleanup_free_ char *p = NULL, *dest = NULL;
|
|
|
1abbee |
- int q;
|
|
|
1abbee |
bool found;
|
|
|
1abbee |
+ int q;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
- if (unit_name_is_instance(de->d_name) &&
|
|
|
1abbee |
- instance_whitelist &&
|
|
|
1abbee |
- !strv_contains(instance_whitelist, de->d_name)) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- _cleanup_free_ char *w;
|
|
|
1abbee |
-
|
|
|
1abbee |
- /* OK, the file is not listed directly
|
|
|
1abbee |
- * in the whitelist, so let's check if
|
|
|
1abbee |
- * the template of it might be
|
|
|
1abbee |
- * listed. */
|
|
|
1abbee |
-
|
|
|
1abbee |
- w = unit_name_template(de->d_name);
|
|
|
1abbee |
- if (!w)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!strv_contains(instance_whitelist, w))
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
p = path_make_absolute(de->d_name, path);
|
|
|
1abbee |
if (!p)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- q = readlink_and_canonicalize(p, &dest);
|
|
|
1abbee |
+ q = readlink_malloc(p, &dest);
|
|
|
1abbee |
if (q < 0) {
|
|
|
1abbee |
if (q == -ENOENT)
|
|
|
1abbee |
continue;
|
|
|
1abbee |
@@ -250,9 +426,15 @@ static int remove_marked_symlinks_fd(
|
|
|
1abbee |
continue;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+ /* We remove all links pointing to a file or
|
|
|
1abbee |
+ * path that is marked, as well as all files
|
|
|
1abbee |
+ * sharing the same name as a file that is
|
|
|
1abbee |
+ * marked. */
|
|
|
1abbee |
+
|
|
|
1abbee |
found =
|
|
|
1abbee |
- set_get(remove_symlinks_to, dest) ||
|
|
|
1abbee |
- set_get(remove_symlinks_to, basename(dest));
|
|
|
1abbee |
+ set_contains(remove_symlinks_to, dest) ||
|
|
|
1abbee |
+ set_contains(remove_symlinks_to, basename(dest)) ||
|
|
|
1abbee |
+ set_contains(remove_symlinks_to, de->d_name);
|
|
|
1abbee |
|
|
|
1abbee |
if (!found)
|
|
|
1abbee |
continue;
|
|
|
1abbee |
@@ -264,18 +446,15 @@ static int remove_marked_symlinks_fd(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
path_kill_slashes(p);
|
|
|
1abbee |
- rmdir_parents(p, config_path);
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
|
|
|
1abbee |
+ (void) rmdir_parents(p, config_path);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!set_get(remove_symlinks_to, p)) {
|
|
|
1abbee |
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
|
|
|
1abbee |
|
|
|
1abbee |
- q = mark_symlink_for_removal(&remove_symlinks_to, p);
|
|
|
1abbee |
- if (q < 0) {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- } else
|
|
|
1abbee |
- *deleted = true;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ q = mark_symlink_for_removal(&remove_symlinks_to, p);
|
|
|
1abbee |
+ if (q < 0)
|
|
|
1abbee |
+ return q;
|
|
|
1abbee |
+ if (q > 0)
|
|
|
1abbee |
+ *restart = true;
|
|
|
1abbee |
}
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
@@ -286,12 +465,11 @@ static int remove_marked_symlinks(
|
|
|
1abbee |
Set *remove_symlinks_to,
|
|
|
1abbee |
const char *config_path,
|
|
|
1abbee |
UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes,
|
|
|
1abbee |
- char** instance_whitelist) {
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_close_ int fd = -1;
|
|
|
1abbee |
int r = 0;
|
|
|
1abbee |
- bool deleted;
|
|
|
1abbee |
+ bool restart;
|
|
|
1abbee |
|
|
|
1abbee |
assert(config_path);
|
|
|
1abbee |
|
|
|
1abbee |
@@ -304,7 +482,7 @@ static int remove_marked_symlinks(
|
|
|
1abbee |
|
|
|
1abbee |
do {
|
|
|
1abbee |
int q, cfd;
|
|
|
1abbee |
- deleted = false;
|
|
|
1abbee |
+ restart = false;
|
|
|
1abbee |
|
|
|
1abbee |
cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
|
|
1abbee |
if (cfd < 0) {
|
|
|
1abbee |
@@ -313,15 +491,16 @@ static int remove_marked_symlinks(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
/* This takes possession of cfd and closes it */
|
|
|
1abbee |
- q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist);
|
|
|
1abbee |
+ q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes);
|
|
|
1abbee |
if (r == 0)
|
|
|
1abbee |
r = q;
|
|
|
1abbee |
- } while (deleted);
|
|
|
1abbee |
+ } while (restart);
|
|
|
1abbee |
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int find_symlinks_fd(
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
const char *name,
|
|
|
1abbee |
int fd,
|
|
|
1abbee |
const char *path,
|
|
|
1abbee |
@@ -380,7 +559,7 @@ static int find_symlinks_fd(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
/* This will close nfd, regardless whether it succeeds or not */
|
|
|
1abbee |
- q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
|
|
|
1abbee |
+ q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link);
|
|
|
1abbee |
if (q > 0)
|
|
|
1abbee |
return 1;
|
|
|
1abbee |
if (r == 0)
|
|
|
1abbee |
@@ -397,7 +576,7 @@ static int find_symlinks_fd(
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
/* Acquire symlink destination */
|
|
|
1abbee |
- q = readlink_and_canonicalize(p, &dest);
|
|
|
1abbee |
+ q = readlink_malloc(p, &dest);
|
|
|
1abbee |
if (q < 0) {
|
|
|
1abbee |
if (q == -ENOENT)
|
|
|
1abbee |
continue;
|
|
|
1abbee |
@@ -407,6 +586,18 @@ static int find_symlinks_fd(
|
|
|
1abbee |
continue;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+ /* Make absolute */
|
|
|
1abbee |
+ if (!path_is_absolute(dest)) {
|
|
|
1abbee |
+ char *x;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ x = prefix_root(root_dir, dest);
|
|
|
1abbee |
+ if (!x)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ free(dest);
|
|
|
1abbee |
+ dest = x;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
/* Check if the symlink itself matches what we
|
|
|
1abbee |
* are looking for */
|
|
|
1abbee |
if (path_is_absolute(name))
|
|
|
1abbee |
@@ -442,6 +633,7 @@ static int find_symlinks_fd(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int find_symlinks(
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
const char *name,
|
|
|
1abbee |
const char *config_path,
|
|
|
1abbee |
bool *same_name_link) {
|
|
|
1abbee |
@@ -460,7 +652,7 @@ static int find_symlinks(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
/* This takes possession of fd and closes it */
|
|
|
1abbee |
- return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
|
|
|
1abbee |
+ return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int find_symlinks_in_scope(
|
|
|
1abbee |
@@ -469,385 +661,104 @@ static int find_symlinks_in_scope(
|
|
|
1abbee |
const char *name,
|
|
|
1abbee |
UnitFileState *state) {
|
|
|
1abbee |
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
_cleanup_free_ char *normal_path = NULL, *runtime_path = NULL;
|
|
|
1abbee |
bool same_name_link_runtime = false, same_name_link = false;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
- /* First look in runtime config path */
|
|
|
1abbee |
- r = get_config_path(scope, true, root_dir, &normal_path);
|
|
|
1abbee |
+ /* First look in the normal config path */
|
|
|
1abbee |
+ r = get_config_path(scope, false, root_dir, &normal_path);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = find_symlinks(name, normal_path, &same_name_link_runtime);
|
|
|
1abbee |
+ r = find_symlinks(root_dir, name, normal_path, &same_name_link);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
- else if (r > 0) {
|
|
|
1abbee |
- *state = UNIT_FILE_ENABLED_RUNTIME;
|
|
|
1abbee |
+ if (r > 0) {
|
|
|
1abbee |
+ *state = UNIT_FILE_ENABLED;
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- /* Then look in the normal config path */
|
|
|
1abbee |
- r = get_config_path(scope, false, root_dir, &runtime_path);
|
|
|
1abbee |
+ /* Then look in runtime config path */
|
|
|
1abbee |
+ r = get_config_path(scope, true, root_dir, &runtime_path);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = find_symlinks(name, runtime_path, &same_name_link);
|
|
|
1abbee |
+ r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
- else if (r > 0) {
|
|
|
1abbee |
- *state = UNIT_FILE_ENABLED;
|
|
|
1abbee |
+ if (r > 0) {
|
|
|
1abbee |
+ *state = UNIT_FILE_ENABLED_RUNTIME;
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
/* Hmm, we didn't find it, but maybe we found the same name
|
|
|
1abbee |
* link? */
|
|
|
1abbee |
+ if (same_name_link) {
|
|
|
1abbee |
+ *state = UNIT_FILE_LINKED;
|
|
|
1abbee |
+ return 1;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
if (same_name_link_runtime) {
|
|
|
1abbee |
*state = UNIT_FILE_LINKED_RUNTIME;
|
|
|
1abbee |
return 1;
|
|
|
1abbee |
- } else if (same_name_link) {
|
|
|
1abbee |
- *state = UNIT_FILE_LINKED;
|
|
|
1abbee |
- return 1;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-int unit_file_mask(
|
|
|
1abbee |
- UnitFileScope scope,
|
|
|
1abbee |
- bool runtime,
|
|
|
1abbee |
- const char *root_dir,
|
|
|
1abbee |
- char **files,
|
|
|
1abbee |
- bool force,
|
|
|
1abbee |
- UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- char **i;
|
|
|
1abbee |
- _cleanup_free_ char *prefix = NULL;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
+static void install_info_free(InstallInfo *i) {
|
|
|
1abbee |
|
|
|
1abbee |
- assert(scope >= 0);
|
|
|
1abbee |
- assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
+ if (!i)
|
|
|
1abbee |
+ return;
|
|
|
1abbee |
|
|
|
1abbee |
- r = get_config_path(scope, runtime, root_dir, &prefix);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ free(i->name);
|
|
|
1abbee |
+ free(i->path);
|
|
|
1abbee |
+ strv_free(i->aliases);
|
|
|
1abbee |
+ strv_free(i->wanted_by);
|
|
|
1abbee |
+ strv_free(i->required_by);
|
|
|
1abbee |
+ strv_free(i->also);
|
|
|
1abbee |
+ free(i->default_instance);
|
|
|
1abbee |
+ free(i->symlink_target);
|
|
|
1abbee |
+ free(i);
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- STRV_FOREACH(i, files) {
|
|
|
1abbee |
- _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
+static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) {
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -EINVAL;
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ while ((i = ordered_hashmap_steal_first(m)))
|
|
|
1abbee |
+ install_info_free(i);
|
|
|
1abbee |
|
|
|
1abbee |
- path = path_make_absolute(*i, prefix);
|
|
|
1abbee |
- if (!path) {
|
|
|
1abbee |
- r = -ENOMEM;
|
|
|
1abbee |
- break;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ ordered_hashmap_free(m);
|
|
|
1abbee |
|
|
|
1abbee |
- if (symlink("/dev/null", path) >= 0) {
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ return NULL;
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (errno == EEXIST) {
|
|
|
1abbee |
+static void install_context_done(InstallContext *c) {
|
|
|
1abbee |
+ assert(c);
|
|
|
1abbee |
|
|
|
1abbee |
- if (null_or_empty_path(path) > 0)
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
+ c->will_process = install_info_hashmap_free(c->will_process);
|
|
|
1abbee |
+ c->have_processed = install_info_hashmap_free(c->have_processed);
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (force) {
|
|
|
1abbee |
- if (symlink_atomic("/dev/null", path) >= 0) {
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
- }
|
|
|
1abbee |
+static InstallInfo *install_info_find(InstallContext *c, const char *name) {
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -EEXIST;
|
|
|
1abbee |
- } else {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -errno;
|
|
|
1abbee |
- }
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ i = ordered_hashmap_get(c->have_processed, name);
|
|
|
1abbee |
+ if (i)
|
|
|
1abbee |
+ return i;
|
|
|
1abbee |
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-int unit_file_unmask(
|
|
|
1abbee |
- UnitFileScope scope,
|
|
|
1abbee |
- bool runtime,
|
|
|
1abbee |
- const char *root_dir,
|
|
|
1abbee |
- char **files,
|
|
|
1abbee |
- UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- char **i, *config_path = NULL;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
- Set *remove_symlinks_to = NULL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- assert(scope >= 0);
|
|
|
1abbee |
- assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = get_config_path(scope, runtime, root_dir, &config_path);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- goto finish;
|
|
|
1abbee |
-
|
|
|
1abbee |
- STRV_FOREACH(i, files) {
|
|
|
1abbee |
- _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -EINVAL;
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- path = path_make_absolute(*i, config_path);
|
|
|
1abbee |
- if (!path) {
|
|
|
1abbee |
- r = -ENOMEM;
|
|
|
1abbee |
- break;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = null_or_empty_path(path);
|
|
|
1abbee |
- if (q > 0) {
|
|
|
1abbee |
- if (unlink(path) < 0)
|
|
|
1abbee |
- q = -errno;
|
|
|
1abbee |
- else {
|
|
|
1abbee |
- q = mark_symlink_for_removal(&remove_symlinks_to, path);
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
|
|
|
1abbee |
- }
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (q != -ENOENT && r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
-
|
|
|
1abbee |
-finish:
|
|
|
1abbee |
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
-
|
|
|
1abbee |
- set_free_free(remove_symlinks_to);
|
|
|
1abbee |
- free(config_path);
|
|
|
1abbee |
-
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-int unit_file_link(
|
|
|
1abbee |
- UnitFileScope scope,
|
|
|
1abbee |
- bool runtime,
|
|
|
1abbee |
- const char *root_dir,
|
|
|
1abbee |
- char **files,
|
|
|
1abbee |
- bool force,
|
|
|
1abbee |
- UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- _cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
- char **i;
|
|
|
1abbee |
- _cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
-
|
|
|
1abbee |
- assert(scope >= 0);
|
|
|
1abbee |
- assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = get_config_path(scope, runtime, root_dir, &config_path);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-
|
|
|
1abbee |
- STRV_FOREACH(i, files) {
|
|
|
1abbee |
- _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
- char *fn;
|
|
|
1abbee |
- struct stat st;
|
|
|
1abbee |
-
|
|
|
1abbee |
- fn = basename(*i);
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!path_is_absolute(*i) ||
|
|
|
1abbee |
- !unit_name_is_valid(fn, TEMPLATE_VALID)) {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -EINVAL;
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (lstat(*i, &st) < 0) {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -errno;
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!S_ISREG(st.st_mode)) {
|
|
|
1abbee |
- r = -ENOENT;
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = in_search_path(*i, paths.unit_path);
|
|
|
1abbee |
- if (q < 0)
|
|
|
1abbee |
- return q;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (q > 0)
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
-
|
|
|
1abbee |
- path = path_make_absolute(fn, config_path);
|
|
|
1abbee |
- if (!path)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (symlink(*i, path) >= 0) {
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (errno == EEXIST) {
|
|
|
1abbee |
- _cleanup_free_ char *dest = NULL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = readlink_and_make_absolute(path, &dest);
|
|
|
1abbee |
- if (q < 0 && errno != ENOENT) {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (q >= 0 && path_equal(dest, *i))
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (force) {
|
|
|
1abbee |
- if (symlink_atomic(*i, path) >= 0) {
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- }
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -EEXIST;
|
|
|
1abbee |
- } else {
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = -errno;
|
|
|
1abbee |
- }
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-void unit_file_list_free(Hashmap *h) {
|
|
|
1abbee |
- UnitFileList *i;
|
|
|
1abbee |
-
|
|
|
1abbee |
- while ((i = hashmap_steal_first(h))) {
|
|
|
1abbee |
- free(i->path);
|
|
|
1abbee |
- free(i);
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- hashmap_free(h);
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-int unit_file_changes_add(
|
|
|
1abbee |
- UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes,
|
|
|
1abbee |
- UnitFileChangeType type,
|
|
|
1abbee |
- const char *path,
|
|
|
1abbee |
- const char *source) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- UnitFileChange *c;
|
|
|
1abbee |
- unsigned i;
|
|
|
1abbee |
-
|
|
|
1abbee |
- assert(path);
|
|
|
1abbee |
- assert(!changes == !n_changes);
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!changes)
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
-
|
|
|
1abbee |
- c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
|
|
|
1abbee |
- if (!c)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
-
|
|
|
1abbee |
- *changes = c;
|
|
|
1abbee |
- i = *n_changes;
|
|
|
1abbee |
-
|
|
|
1abbee |
- c[i].type = type;
|
|
|
1abbee |
- c[i].path = strdup(path);
|
|
|
1abbee |
- if (!c[i].path)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
-
|
|
|
1abbee |
- path_kill_slashes(c[i].path);
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (source) {
|
|
|
1abbee |
- c[i].source = strdup(source);
|
|
|
1abbee |
- if (!c[i].source) {
|
|
|
1abbee |
- free(c[i].path);
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- path_kill_slashes(c[i].path);
|
|
|
1abbee |
- } else
|
|
|
1abbee |
- c[i].source = NULL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- *n_changes = i+1;
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
|
|
|
1abbee |
- unsigned i;
|
|
|
1abbee |
-
|
|
|
1abbee |
- assert(changes || n_changes == 0);
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!changes)
|
|
|
1abbee |
- return;
|
|
|
1abbee |
-
|
|
|
1abbee |
- for (i = 0; i < n_changes; i++) {
|
|
|
1abbee |
- free(changes[i].path);
|
|
|
1abbee |
- free(changes[i].source);
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- free(changes);
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-static void install_info_free(InstallInfo *i) {
|
|
|
1abbee |
- assert(i);
|
|
|
1abbee |
-
|
|
|
1abbee |
- free(i->name);
|
|
|
1abbee |
- free(i->path);
|
|
|
1abbee |
- strv_free(i->aliases);
|
|
|
1abbee |
- strv_free(i->wanted_by);
|
|
|
1abbee |
- strv_free(i->required_by);
|
|
|
1abbee |
- strv_free(i->also);
|
|
|
1abbee |
- free(i->default_instance);
|
|
|
1abbee |
- free(i);
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-static void install_info_hashmap_free(OrderedHashmap *m) {
|
|
|
1abbee |
- InstallInfo *i;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!m)
|
|
|
1abbee |
- return;
|
|
|
1abbee |
-
|
|
|
1abbee |
- while ((i = ordered_hashmap_steal_first(m)))
|
|
|
1abbee |
- install_info_free(i);
|
|
|
1abbee |
-
|
|
|
1abbee |
- ordered_hashmap_free(m);
|
|
|
1abbee |
-}
|
|
|
1abbee |
-
|
|
|
1abbee |
-static void install_context_done(InstallContext *c) {
|
|
|
1abbee |
- assert(c);
|
|
|
1abbee |
-
|
|
|
1abbee |
- install_info_hashmap_free(c->will_install);
|
|
|
1abbee |
- install_info_hashmap_free(c->have_installed);
|
|
|
1abbee |
-
|
|
|
1abbee |
- c->will_install = c->have_installed = NULL;
|
|
|
1abbee |
+ return ordered_hashmap_get(c->will_process, name);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int install_info_add(
|
|
|
1abbee |
InstallContext *c,
|
|
|
1abbee |
const char *name,
|
|
|
1abbee |
- const char *path) {
|
|
|
1abbee |
+ const char *path,
|
|
|
1abbee |
+ InstallInfo **ret) {
|
|
|
1abbee |
+
|
|
|
1abbee |
InstallInfo *i = NULL;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -857,20 +768,24 @@ static int install_info_add(
|
|
|
1abbee |
if (!name)
|
|
|
1abbee |
name = basename(path);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(name, TEMPLATE_VALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
- if (ordered_hashmap_get(c->have_installed, name) ||
|
|
|
1abbee |
- ordered_hashmap_get(c->will_install, name))
|
|
|
1abbee |
+ i = install_info_find(c, name);
|
|
|
1abbee |
+ if (i) {
|
|
|
1abbee |
+ if (ret)
|
|
|
1abbee |
+ *ret = i;
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
|
|
|
1abbee |
- r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops);
|
|
|
1abbee |
+ r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
i = new0(InstallInfo, 1);
|
|
|
1abbee |
if (!i)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
+ i->type = _UNIT_FILE_TYPE_INVALID;
|
|
|
1abbee |
|
|
|
1abbee |
i->name = strdup(name);
|
|
|
1abbee |
if (!i->name) {
|
|
|
1abbee |
@@ -886,10 +801,13 @@ static int install_info_add(
|
|
|
1abbee |
}
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- r = ordered_hashmap_put(c->will_install, i->name, i);
|
|
|
1abbee |
+ r = ordered_hashmap_put(c->will_process, i->name, i);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
goto fail;
|
|
|
1abbee |
|
|
|
1abbee |
+ if (ret)
|
|
|
1abbee |
+ *ret = i;
|
|
|
1abbee |
+
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
|
|
|
1abbee |
fail:
|
|
|
1abbee |
@@ -901,15 +819,16 @@ fail:
|
|
|
1abbee |
|
|
|
1abbee |
static int install_info_add_auto(
|
|
|
1abbee |
InstallContext *c,
|
|
|
1abbee |
- const char *name_or_path) {
|
|
|
1abbee |
+ const char *name_or_path,
|
|
|
1abbee |
+ InstallInfo **ret) {
|
|
|
1abbee |
|
|
|
1abbee |
assert(c);
|
|
|
1abbee |
assert(name_or_path);
|
|
|
1abbee |
|
|
|
1abbee |
if (path_is_absolute(name_or_path))
|
|
|
1abbee |
- return install_info_add(c, NULL, name_or_path);
|
|
|
1abbee |
+ return install_info_add(c, NULL, name_or_path, ret);
|
|
|
1abbee |
else
|
|
|
1abbee |
- return install_info_add(c, name_or_path, NULL);
|
|
|
1abbee |
+ return install_info_add(c, name_or_path, NULL, ret);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int config_parse_also(
|
|
|
1abbee |
@@ -928,6 +847,7 @@ static int config_parse_also(
|
|
|
1abbee |
const char *word, *state;
|
|
|
1abbee |
InstallContext *c = data;
|
|
|
1abbee |
InstallInfo *i = userdata;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(filename);
|
|
|
1abbee |
assert(lvalue);
|
|
|
1abbee |
@@ -935,19 +855,20 @@ static int config_parse_also(
|
|
|
1abbee |
|
|
|
1abbee |
FOREACH_WORD_QUOTED(word, l, rvalue, state) {
|
|
|
1abbee |
_cleanup_free_ char *n;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
|
|
|
1abbee |
n = strndup(word, l);
|
|
|
1abbee |
if (!n)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- r = install_info_add(c, n, NULL);
|
|
|
1abbee |
+ r = install_info_add(c, n, NULL, NULL);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = strv_extend(&i->also, n);
|
|
|
1abbee |
+ r = strv_push(&i->also, n);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ n = NULL;
|
|
|
1abbee |
}
|
|
|
1abbee |
if (!isempty(state))
|
|
|
1abbee |
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
|
|
|
1abbee |
@@ -1026,9 +947,7 @@ static int unit_file_load(
|
|
|
1abbee |
InstallInfo *info,
|
|
|
1abbee |
const char *path,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
- bool allow_symlink,
|
|
|
1abbee |
- bool load,
|
|
|
1abbee |
- bool *also) {
|
|
|
1abbee |
+ SearchFlags flags) {
|
|
|
1abbee |
|
|
|
1abbee |
const ConfigTableItem items[] = {
|
|
|
1abbee |
{ "Install", "Alias", config_parse_strv, 0, &info->aliases },
|
|
|
1abbee |
@@ -1041,7 +960,9 @@ static int unit_file_load(
|
|
|
1abbee |
};
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_fclose_ FILE *f = NULL;
|
|
|
1abbee |
- int fd, r;
|
|
|
1abbee |
+ _cleanup_close_ int fd = -1;
|
|
|
1abbee |
+ struct stat st;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(c);
|
|
|
1abbee |
assert(info);
|
|
|
1abbee |
@@ -1050,20 +971,43 @@ static int unit_file_load(
|
|
|
1abbee |
if (!isempty(root_dir))
|
|
|
1abbee |
path = strjoina(root_dir, "/", path);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!load) {
|
|
|
1abbee |
- r = access(path, F_OK) ? -errno : 0;
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ if (!(flags & SEARCH_LOAD)) {
|
|
|
1abbee |
+ r = lstat(path, &st);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return -errno;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (null_or_empty(&st))
|
|
|
1abbee |
+ info->type = UNIT_FILE_TYPE_MASKED;
|
|
|
1abbee |
+ else if (S_ISREG(st.st_mode))
|
|
|
1abbee |
+ info->type = UNIT_FILE_TYPE_REGULAR;
|
|
|
1abbee |
+ else if (S_ISLNK(st.st_mode))
|
|
|
1abbee |
+ return -ELOOP;
|
|
|
1abbee |
+ else if (S_ISDIR(st.st_mode))
|
|
|
1abbee |
+ return -EISDIR;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ return -ENOTTY;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
|
|
|
1abbee |
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
|
|
|
1abbee |
if (fd < 0)
|
|
|
1abbee |
return -errno;
|
|
|
1abbee |
+ if (fstat(fd, &st) < 0)
|
|
|
1abbee |
+ return -errno;
|
|
|
1abbee |
+ if (null_or_empty(&st)) {
|
|
|
1abbee |
+ info->type = UNIT_FILE_MASKED;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ if (S_ISDIR(st.st_mode))
|
|
|
1abbee |
+ return -EISDIR;
|
|
|
1abbee |
+ if (!S_ISREG(st.st_mode))
|
|
|
1abbee |
+ return -ENOTTY;
|
|
|
1abbee |
|
|
|
1abbee |
f = fdopen(fd, "re");
|
|
|
1abbee |
- if (!f) {
|
|
|
1abbee |
- safe_close(fd);
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ if (!f)
|
|
|
1abbee |
+ return -errno;
|
|
|
1abbee |
+ fd = -1;
|
|
|
1abbee |
|
|
|
1abbee |
r = config_parse(NULL, path, f,
|
|
|
1abbee |
NULL,
|
|
|
1abbee |
@@ -1072,8 +1016,7 @@ static int unit_file_load(
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- if (also)
|
|
|
1abbee |
- *also = !strv_isempty(info->also);
|
|
|
1abbee |
+ info->type = UNIT_FILE_TYPE_REGULAR;
|
|
|
1abbee |
|
|
|
1abbee |
return
|
|
|
1abbee |
(int) strv_length(info->aliases) +
|
|
|
1abbee |
@@ -1081,14 +1024,73 @@ static int unit_file_load(
|
|
|
1abbee |
(int) strv_length(info->required_by);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+static int unit_file_load_or_readlink(
|
|
|
1abbee |
+ InstallContext *c,
|
|
|
1abbee |
+ InstallInfo *info,
|
|
|
1abbee |
+ const char *path,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ SearchFlags flags) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ _cleanup_free_ char *np = NULL;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = unit_file_load(c, info, path, root_dir, flags);
|
|
|
1abbee |
+ if (r != -ELOOP)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* This is a symlink, let's read it. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = readlink_and_make_absolute_root(root_dir, path, &np);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (path_equal(np, "/dev/null"))
|
|
|
1abbee |
+ info->type = UNIT_FILE_TYPE_MASKED;
|
|
|
1abbee |
+ else {
|
|
|
1abbee |
+ const char *bn;
|
|
|
1abbee |
+ UnitType a, b;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ bn = basename(np);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Enforce that the symlink destination does not
|
|
|
1abbee |
+ * change the unit file type. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ a = unit_name_to_type(info->name);
|
|
|
1abbee |
+ b = unit_name_to_type(bn);
|
|
|
1abbee |
+ if (a < 0 || b < 0 || a != b)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ info->type = UNIT_FILE_TYPE_SYMLINK;
|
|
|
1abbee |
+ info->symlink_target = np;
|
|
|
1abbee |
+ np = NULL;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
static int unit_file_search(
|
|
|
1abbee |
InstallContext *c,
|
|
|
1abbee |
InstallInfo *info,
|
|
|
1abbee |
const LookupPaths *paths,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
- bool allow_symlink,
|
|
|
1abbee |
- bool load,
|
|
|
1abbee |
- bool *also) {
|
|
|
1abbee |
+ SearchFlags flags) {
|
|
|
1abbee |
|
|
|
1abbee |
char **p;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
@@ -1097,8 +1099,12 @@ static int unit_file_search(
|
|
|
1abbee |
assert(info);
|
|
|
1abbee |
assert(paths);
|
|
|
1abbee |
|
|
|
1abbee |
+ /* Was this unit already loaded? */
|
|
|
1abbee |
+ if (info->type != _UNIT_FILE_TYPE_INVALID)
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
if (info->path)
|
|
|
1abbee |
- return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also);
|
|
|
1abbee |
+ return unit_file_load_or_readlink(c, info, info->path, root_dir, flags);
|
|
|
1abbee |
|
|
|
1abbee |
assert(info->name);
|
|
|
1abbee |
|
|
|
1abbee |
@@ -1109,14 +1115,15 @@ static int unit_file_search(
|
|
|
1abbee |
if (!path)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
|
|
|
1abbee |
- if (r >= 0) {
|
|
|
1abbee |
+ r = unit_file_load_or_readlink(c, info, path, root_dir, flags);
|
|
|
1abbee |
+ if (r < 0) {
|
|
|
1abbee |
+ if (r != -ENOENT)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
info->path = path;
|
|
|
1abbee |
path = NULL;
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
- if (r != -ENOENT && r != -ELOOP)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
if (unit_name_is_instance(info->name)) {
|
|
|
1abbee |
@@ -1138,92 +1145,149 @@ static int unit_file_search(
|
|
|
1abbee |
if (!path)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
|
|
|
1abbee |
- if (r >= 0) {
|
|
|
1abbee |
+ r = unit_file_load_or_readlink(c, info, path, root_dir, flags);
|
|
|
1abbee |
+ if (r < 0) {
|
|
|
1abbee |
+ if (r != -ENOENT)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
info->path = path;
|
|
|
1abbee |
path = NULL;
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
- if (r != -ENOENT && r != -ELOOP)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
return -ENOENT;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-static int unit_file_can_install(
|
|
|
1abbee |
- const LookupPaths *paths,
|
|
|
1abbee |
+static int install_info_follow(
|
|
|
1abbee |
+ InstallContext *c,
|
|
|
1abbee |
+ InstallInfo *i,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
- const char *name,
|
|
|
1abbee |
- bool allow_symlink,
|
|
|
1abbee |
- bool *also) {
|
|
|
1abbee |
+ SearchFlags flags) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(c);
|
|
|
1abbee |
+ assert(i);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (i->type != UNIT_FILE_TYPE_SYMLINK)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ if (!i->symlink_target)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* If the basename doesn't match, the caller should add a
|
|
|
1abbee |
+ * complete new entry for this. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!streq(basename(i->symlink_target), i->name))
|
|
|
1abbee |
+ return -EXDEV;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ free(i->path);
|
|
|
1abbee |
+ i->path = i->symlink_target;
|
|
|
1abbee |
+ i->symlink_target = NULL;
|
|
|
1abbee |
+ i->type = _UNIT_FILE_TYPE_INVALID;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static int install_info_traverse(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ InstallContext *c,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ const LookupPaths *paths,
|
|
|
1abbee |
+ InstallInfo *start,
|
|
|
1abbee |
+ SearchFlags flags,
|
|
|
1abbee |
+ InstallInfo **ret) {
|
|
|
1abbee |
|
|
|
1abbee |
- _cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
InstallInfo *i;
|
|
|
1abbee |
+ unsigned k = 0;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(paths);
|
|
|
1abbee |
- assert(name);
|
|
|
1abbee |
+ assert(start);
|
|
|
1abbee |
+ assert(c);
|
|
|
1abbee |
|
|
|
1abbee |
- r = install_info_add_auto(&c, name);
|
|
|
1abbee |
+ r = unit_file_search(c, start, paths, root_dir, flags);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(i = ordered_hashmap_first(c.will_install));
|
|
|
1abbee |
+ i = start;
|
|
|
1abbee |
+ while (i->type == UNIT_FILE_TYPE_SYMLINK) {
|
|
|
1abbee |
+ /* Follow the symlink */
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also);
|
|
|
1abbee |
+ if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
|
|
|
1abbee |
+ return -ELOOP;
|
|
|
1abbee |
|
|
|
1abbee |
- if (r >= 0)
|
|
|
1abbee |
- r =
|
|
|
1abbee |
- (int) strv_length(i->aliases) +
|
|
|
1abbee |
- (int) strv_length(i->wanted_by) +
|
|
|
1abbee |
- (int) strv_length(i->required_by);
|
|
|
1abbee |
+ if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path))
|
|
|
1abbee |
+ return -ELOOP;
|
|
|
1abbee |
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-}
|
|
|
1abbee |
+ r = install_info_follow(c, i, root_dir, flags);
|
|
|
1abbee |
+ if (r < 0) {
|
|
|
1abbee |
+ _cleanup_free_ char *buffer = NULL;
|
|
|
1abbee |
+ const char *bn;
|
|
|
1abbee |
|
|
|
1abbee |
-static int create_symlink(
|
|
|
1abbee |
- const char *old_path,
|
|
|
1abbee |
- const char *new_path,
|
|
|
1abbee |
- bool force,
|
|
|
1abbee |
- UnitFileChange **changes,
|
|
|
1abbee |
- unsigned *n_changes) {
|
|
|
1abbee |
+ if (r != -EXDEV)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
- _cleanup_free_ char *dest = NULL;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
+ /* Target has a different name, create a new
|
|
|
1abbee |
+ * install info object for that, and continue
|
|
|
1abbee |
+ * with that. */
|
|
|
1abbee |
|
|
|
1abbee |
- assert(old_path);
|
|
|
1abbee |
- assert(new_path);
|
|
|
1abbee |
+ bn = basename(i->symlink_target);
|
|
|
1abbee |
|
|
|
1abbee |
- mkdir_parents_label(new_path, 0755);
|
|
|
1abbee |
+ if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
|
|
|
1abbee |
+ unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
|
|
|
1abbee |
|
|
|
1abbee |
- if (symlink(old_path, new_path) >= 0) {
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ _cleanup_free_ char *instance = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = unit_name_to_instance(i->name, &instance);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ buffer = unit_name_replace_instance(bn, instance);
|
|
|
1abbee |
+ if (!buffer)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ bn = buffer;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = install_info_add(c, bn, NULL, &i);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = unit_file_search(c, i, paths, root_dir, flags);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Try again, with the new target we found. */
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- if (errno != EEXIST)
|
|
|
1abbee |
- return -errno;
|
|
|
1abbee |
+ if (ret)
|
|
|
1abbee |
+ *ret = i;
|
|
|
1abbee |
|
|
|
1abbee |
- r = readlink_and_make_absolute(new_path, &dest);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static int install_info_discover(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ InstallContext *c,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ const LookupPaths *paths,
|
|
|
1abbee |
+ const char *name,
|
|
|
1abbee |
+ SearchFlags flags,
|
|
|
1abbee |
+ InstallInfo **ret) {
|
|
|
1abbee |
|
|
|
1abbee |
- if (path_equal(dest, old_path))
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!force)
|
|
|
1abbee |
- return -EEXIST;
|
|
|
1abbee |
+ assert(c);
|
|
|
1abbee |
+ assert(paths);
|
|
|
1abbee |
+ assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
- r = symlink_atomic(old_path, new_path);
|
|
|
1abbee |
+ r = install_info_add_auto(c, name, &i);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
|
|
|
1abbee |
- unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
|
|
|
1abbee |
-
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ return install_info_traverse(scope, c, root_dir, paths, i, flags, ret);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int install_info_symlink_alias(
|
|
|
1abbee |
@@ -1298,7 +1362,7 @@ static int install_info_symlink_wants(
|
|
|
1abbee |
if (q < 0)
|
|
|
1abbee |
return q;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(dst, TEMPLATE_VALID)) {
|
|
|
1abbee |
+ if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
|
|
|
1abbee |
r = -EINVAL;
|
|
|
1abbee |
continue;
|
|
|
1abbee |
}
|
|
|
1abbee |
@@ -1358,6 +1422,9 @@ static int install_info_apply(
|
|
|
1abbee |
assert(paths);
|
|
|
1abbee |
assert(config_path);
|
|
|
1abbee |
|
|
|
1abbee |
+ if (i->type != UNIT_FILE_TYPE_REGULAR)
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
|
|
|
1abbee |
@@ -1376,53 +1443,59 @@ static int install_info_apply(
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int install_context_apply(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
InstallContext *c,
|
|
|
1abbee |
const LookupPaths *paths,
|
|
|
1abbee |
const char *config_path,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
bool force,
|
|
|
1abbee |
+ SearchFlags flags,
|
|
|
1abbee |
UnitFileChange **changes,
|
|
|
1abbee |
unsigned *n_changes) {
|
|
|
1abbee |
|
|
|
1abbee |
InstallInfo *i;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(c);
|
|
|
1abbee |
assert(paths);
|
|
|
1abbee |
assert(config_path);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!ordered_hashmap_isempty(c->will_install)) {
|
|
|
1abbee |
- r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ if (ordered_hashmap_isempty(c->will_process))
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
|
|
|
1abbee |
- r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
r = 0;
|
|
|
1abbee |
- while ((i = ordered_hashmap_first(c->will_install))) {
|
|
|
1abbee |
- assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
|
|
|
1abbee |
+ while ((i = ordered_hashmap_first(c->will_process))) {
|
|
|
1abbee |
+ int q;
|
|
|
1abbee |
|
|
|
1abbee |
- q = unit_file_search(c, i, paths, root_dir, false, true, NULL);
|
|
|
1abbee |
- if (q < 0) {
|
|
|
1abbee |
- if (r >= 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
+ q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
|
|
|
1abbee |
+ if (q < 0)
|
|
|
1abbee |
+ return q;
|
|
|
1abbee |
|
|
|
1abbee |
+ r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
- } else if (r >= 0)
|
|
|
1abbee |
- r += q;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (i->type != UNIT_FILE_TYPE_REGULAR)
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
|
|
|
1abbee |
q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes);
|
|
|
1abbee |
- if (r >= 0 && q < 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
+ if (r >= 0) {
|
|
|
1abbee |
+ if (q < 0)
|
|
|
1abbee |
+ r = q;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ r+= q;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int install_context_mark_for_removal(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
InstallContext *c,
|
|
|
1abbee |
const LookupPaths *paths,
|
|
|
1abbee |
Set **remove_symlinks_to,
|
|
|
1abbee |
@@ -1430,96 +1503,109 @@ static int install_context_mark_for_removal(
|
|
|
1abbee |
const char *root_dir) {
|
|
|
1abbee |
|
|
|
1abbee |
InstallInfo *i;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(c);
|
|
|
1abbee |
assert(paths);
|
|
|
1abbee |
assert(config_path);
|
|
|
1abbee |
|
|
|
1abbee |
/* Marks all items for removal */
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!ordered_hashmap_isempty(c->will_install)) {
|
|
|
1abbee |
- r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
|
|
|
1abbee |
+ if (!ordered_hashmap_isempty(c->will_process)) {
|
|
|
1abbee |
+ r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
|
|
|
1abbee |
+ r = ordered_hashmap_reserve(c->have_processed, ordered_hashmap_size(c->will_process));
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
r = 0;
|
|
|
1abbee |
- while ((i = ordered_hashmap_first(c->will_install))) {
|
|
|
1abbee |
- assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = unit_file_search(c, i, paths, root_dir, false, true, NULL);
|
|
|
1abbee |
- if (q == -ENOENT) {
|
|
|
1abbee |
- /* do nothing */
|
|
|
1abbee |
- } else if (q < 0) {
|
|
|
1abbee |
- if (r >= 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
+ while ((i = ordered_hashmap_first(c->will_process))) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
+ r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
- } else if (r >= 0)
|
|
|
1abbee |
- r += q;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (unit_name_is_instance(i->name)) {
|
|
|
1abbee |
- char *unit_file;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (i->path) {
|
|
|
1abbee |
- unit_file = basename(i->path);
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (unit_name_is_instance(unit_file))
|
|
|
1abbee |
- /* unit file named as instance exists, thus all symlinks
|
|
|
1abbee |
- * pointing to it will be removed */
|
|
|
1abbee |
- q = mark_symlink_for_removal(remove_symlinks_to, i->name);
|
|
|
1abbee |
- else
|
|
|
1abbee |
- /* does not exist, thus we will mark for removal symlinks
|
|
|
1abbee |
- * to template unit file */
|
|
|
1abbee |
- q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
|
|
|
1abbee |
- } else {
|
|
|
1abbee |
- /* If i->path is not set, it means that we didn't actually find
|
|
|
1abbee |
- * the unit file. But we can still remove symlinks to the
|
|
|
1abbee |
- * nonexistent template. */
|
|
|
1abbee |
- unit_file = unit_name_template(i->name);
|
|
|
1abbee |
- if (!unit_file)
|
|
|
1abbee |
- return log_oom();
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
|
|
|
1abbee |
- free(unit_file);
|
|
|
1abbee |
- }
|
|
|
1abbee |
- } else
|
|
|
1abbee |
- q = mark_symlink_for_removal(remove_symlinks_to, i->name);
|
|
|
1abbee |
|
|
|
1abbee |
- if (r >= 0 && q < 0)
|
|
|
1abbee |
+ if (i->type != UNIT_FILE_TYPE_REGULAR)
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = mark_symlink_for_removal(remove_symlinks_to, i->name);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int unit_file_mask(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ bool runtime,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ char **files,
|
|
|
1abbee |
+ bool force,
|
|
|
1abbee |
+ UnitFileChange **changes,
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ _cleanup_free_ char *prefix = NULL;
|
|
|
1abbee |
+ char **i;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(scope >= 0);
|
|
|
1abbee |
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = get_config_path(scope, runtime, root_dir, &prefix);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ STRV_FOREACH(i, files) {
|
|
|
1abbee |
+ _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
+ int q;
|
|
|
1abbee |
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ r = -EINVAL;
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ path = path_make_absolute(*i, prefix);
|
|
|
1abbee |
+ if (!path)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ q = create_symlink("/dev/null", path, force, changes, n_changes);
|
|
|
1abbee |
+ if (q < 0 && r >= 0)
|
|
|
1abbee |
r = q;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-int unit_file_add_dependency(
|
|
|
1abbee |
+int unit_file_unmask(
|
|
|
1abbee |
UnitFileScope scope,
|
|
|
1abbee |
bool runtime,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
char **files,
|
|
|
1abbee |
- char *target,
|
|
|
1abbee |
- UnitDependency dep,
|
|
|
1abbee |
- bool force,
|
|
|
1abbee |
UnitFileChange **changes,
|
|
|
1abbee |
unsigned *n_changes) {
|
|
|
1abbee |
|
|
|
1abbee |
- _cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
- _cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
|
|
|
1abbee |
_cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
+ _cleanup_free_ char **todo = NULL;
|
|
|
1abbee |
+ size_t n_todo = 0, n_allocated = 0;
|
|
|
1abbee |
char **i;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
- InstallInfo *info;
|
|
|
1abbee |
+ int r, q;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
|
|
|
1abbee |
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -1528,57 +1614,222 @@ int unit_file_add_dependency(
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
STRV_FOREACH(i, files) {
|
|
|
1abbee |
- UnitFileState state;
|
|
|
1abbee |
+ _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
|
|
|
1abbee |
- state = unit_file_get_state(scope, root_dir, *i);
|
|
|
1abbee |
- if (state < 0)
|
|
|
1abbee |
- return log_error_errno(state, "Failed to get unit file state for %s: %m", *i);
|
|
|
1abbee |
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
- if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
|
|
|
1abbee |
- log_error("Failed to enable unit: Unit %s is masked", *i);
|
|
|
1abbee |
- return -ENOTSUP;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ path = path_make_absolute(*i, config_path);
|
|
|
1abbee |
+ if (!path)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- r = install_info_add_auto(&c, *i);
|
|
|
1abbee |
+ r = null_or_empty_path(path);
|
|
|
1abbee |
+ if (r == -ENOENT)
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ todo[n_todo++] = *i;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- if (!ordered_hashmap_isempty(c.will_install)) {
|
|
|
1abbee |
- r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ strv_uniq(todo);
|
|
|
1abbee |
|
|
|
1abbee |
- r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install));
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ r = 0;
|
|
|
1abbee |
+ STRV_FOREACH(i, todo) {
|
|
|
1abbee |
+ _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ path = path_make_absolute(*i, config_path);
|
|
|
1abbee |
+ if (!path)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (unlink(path) < 0) {
|
|
|
1abbee |
+ if (errno != -ENOENT && r >= 0)
|
|
|
1abbee |
+ r = -errno;
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
+ q = mark_symlink_for_removal(&remove_symlinks_to, path);
|
|
|
1abbee |
+ if (q < 0)
|
|
|
1abbee |
+ return q;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- while ((info = ordered_hashmap_first(c.will_install))) {
|
|
|
1abbee |
- assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0);
|
|
|
1abbee |
+ q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
|
|
|
1abbee |
+ if (r >= 0)
|
|
|
1abbee |
+ r = q;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (dep == UNIT_WANTS)
|
|
|
1abbee |
- r = strv_extend(&info->wanted_by, target);
|
|
|
1abbee |
- else if (dep == UNIT_REQUIRES)
|
|
|
1abbee |
- r = strv_extend(&info->required_by, target);
|
|
|
1abbee |
- else
|
|
|
1abbee |
- r = -EINVAL;
|
|
|
1abbee |
+int unit_file_link(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ bool runtime,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ char **files,
|
|
|
1abbee |
+ bool force,
|
|
|
1abbee |
+ UnitFileChange **changes,
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
+ _cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
+ _cleanup_free_ char **todo = NULL;
|
|
|
1abbee |
+ size_t n_todo = 0, n_allocated = 0;
|
|
|
1abbee |
+ char **i;
|
|
|
1abbee |
+ int r,q;
|
|
|
1abbee |
|
|
|
1abbee |
- r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ assert(scope >= 0);
|
|
|
1abbee |
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = get_config_path(scope, runtime, root_dir, &config_path);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ STRV_FOREACH(i, files) {
|
|
|
1abbee |
+ _cleanup_free_ char *full = NULL;
|
|
|
1abbee |
+ struct stat st;
|
|
|
1abbee |
+ char *fn;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!path_is_absolute(*i))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ fn = basename(*i);
|
|
|
1abbee |
+ if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ full = prefix_root(root_dir, *i);
|
|
|
1abbee |
+ if (!full)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (lstat(full, &st) < 0)
|
|
|
1abbee |
+ return -errno;
|
|
|
1abbee |
+ if (S_ISLNK(st.st_mode))
|
|
|
1abbee |
+ return -ELOOP;
|
|
|
1abbee |
+ if (S_ISDIR(st.st_mode))
|
|
|
1abbee |
+ return -EISDIR;
|
|
|
1abbee |
+ if (!S_ISREG(st.st_mode))
|
|
|
1abbee |
+ return -ENOTTY;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ q = in_search_path(*i, paths.unit_path);
|
|
|
1abbee |
+ if (q < 0)
|
|
|
1abbee |
+ return q;
|
|
|
1abbee |
+ if (q > 0)
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ todo[n_todo++] = *i;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ strv_uniq(todo);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = 0;
|
|
|
1abbee |
+ STRV_FOREACH(i, todo) {
|
|
|
1abbee |
+ _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ path = path_make_absolute(basename(*i), config_path);
|
|
|
1abbee |
+ if (!path)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ q = create_symlink(*i, path, force, changes, n_changes);
|
|
|
1abbee |
+ if (q < 0 && r >= 0)
|
|
|
1abbee |
+ r = q;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int unit_file_add_dependency(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ bool runtime,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ char **files,
|
|
|
1abbee |
+ const char *target,
|
|
|
1abbee |
+ UnitDependency dep,
|
|
|
1abbee |
+ bool force,
|
|
|
1abbee |
+ UnitFileChange **changes,
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ _cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
+ _cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
+ _cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
+ InstallInfo *i, *target_info;
|
|
|
1abbee |
+ char **f;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(scope >= 0);
|
|
|
1abbee |
+ assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
+ assert(target);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!unit_name_is_valid(target, UNIT_NAME_ANY))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = get_config_path(scope, runtime, root_dir, &config_path);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (target_info->type == UNIT_FILE_TYPE_MASKED)
|
|
|
1abbee |
+ return -ESHUTDOWN;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ STRV_FOREACH(f, files) {
|
|
|
1abbee |
+ char ***l;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (i->type == UNIT_FILE_TYPE_MASKED)
|
|
|
1abbee |
+ return -ESHUTDOWN;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(i->type == UNIT_FILE_TYPE_REGULAR);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* We didn't actually load anything from the unit
|
|
|
1abbee |
+ * file, but instead just add in our new symlink to
|
|
|
1abbee |
+ * create. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (dep == UNIT_WANTS)
|
|
|
1abbee |
+ l = &i->wanted_by;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ l = &i->required_by;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ strv_free(*l);
|
|
|
1abbee |
+ *l = strv_new(target_info->name, NULL);
|
|
|
1abbee |
+ if (!*l)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+
|
|
|
1abbee |
int unit_file_enable(
|
|
|
1abbee |
UnitFileScope scope,
|
|
|
1abbee |
bool runtime,
|
|
|
1abbee |
@@ -1590,13 +1841,18 @@ int unit_file_enable(
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
_cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
- char **i;
|
|
|
1abbee |
_cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
+ char **f;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
@@ -1605,21 +1861,14 @@ int unit_file_enable(
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- STRV_FOREACH(i, files) {
|
|
|
1abbee |
- UnitFileState state;
|
|
|
1abbee |
-
|
|
|
1abbee |
- /* We only want to know if this unit is masked, so we ignore
|
|
|
1abbee |
- * errors from unit_file_get_state, deferring other checks.
|
|
|
1abbee |
- * This allows templated units to be enabled on the fly. */
|
|
|
1abbee |
- state = unit_file_get_state(scope, root_dir, *i);
|
|
|
1abbee |
- if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
|
|
|
1abbee |
- log_error("Failed to enable unit: Unit %s is masked", *i);
|
|
|
1abbee |
- return -ENOTSUP;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = install_info_add_auto(&c, *i);
|
|
|
1abbee |
+ STRV_FOREACH(f, files) {
|
|
|
1abbee |
+ r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
+ if (i->type == UNIT_FILE_TYPE_MASKED)
|
|
|
1abbee |
+ return -ESHUTDOWN;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(i->type == UNIT_FILE_TYPE_REGULAR);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
/* This will return the number of symlink rules that were
|
|
|
1abbee |
@@ -1627,7 +1876,7 @@ int unit_file_enable(
|
|
|
1abbee |
useful to determine whether the passed files had any
|
|
|
1abbee |
installation data at all. */
|
|
|
1abbee |
|
|
|
1abbee |
- return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
|
|
|
1abbee |
+ return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_disable(
|
|
|
1abbee |
@@ -1640,14 +1889,18 @@ int unit_file_disable(
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
_cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
- char **i;
|
|
|
1abbee |
_cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
+ char **i;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
@@ -1657,18 +1910,19 @@ int unit_file_disable(
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
STRV_FOREACH(i, files) {
|
|
|
1abbee |
- r = install_info_add_auto(&c, *i);
|
|
|
1abbee |
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = install_info_add(&c, *i, NULL, NULL);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
|
|
|
1abbee |
- if (r >= 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
+ r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_reenable(
|
|
|
1abbee |
@@ -1679,21 +1933,30 @@ int unit_file_reenable(
|
|
|
1abbee |
bool force,
|
|
|
1abbee |
UnitFileChange **changes,
|
|
|
1abbee |
unsigned *n_changes) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ char **n;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
+ size_t l, i;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* First, we invoke the disable command with only the basename... */
|
|
|
1abbee |
+ l = strv_length(files);
|
|
|
1abbee |
+ n = newa(char*, l+1);
|
|
|
1abbee |
+ for (i = 0; i < l; i++)
|
|
|
1abbee |
+ n[i] = basename(files[i]);
|
|
|
1abbee |
+ n[i] = NULL;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_disable(scope, runtime, root_dir, files,
|
|
|
1abbee |
- changes, n_changes);
|
|
|
1abbee |
+ r = unit_file_disable(scope, runtime, root_dir, n, changes, n_changes);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- return unit_file_enable(scope, runtime, root_dir, files, force,
|
|
|
1abbee |
- changes, n_changes);
|
|
|
1abbee |
+ /* But the enable command with the full name */
|
|
|
1abbee |
+ return unit_file_enable(scope, runtime, root_dir, files, force, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_set_default(
|
|
|
1abbee |
UnitFileScope scope,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
- const char *file,
|
|
|
1abbee |
+ const char *name,
|
|
|
1abbee |
bool force,
|
|
|
1abbee |
UnitFileChange **changes,
|
|
|
1abbee |
unsigned *n_changes) {
|
|
|
1abbee |
@@ -1701,42 +1964,40 @@ int unit_file_set_default(
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
_cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
_cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
- char *path;
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
+ const char *path;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
- InstallInfo *i = NULL;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
- assert(file);
|
|
|
1abbee |
+ assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
- if (unit_name_to_type(file) != UNIT_TARGET)
|
|
|
1abbee |
+ if (unit_name_to_type(name) != UNIT_TARGET)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ if (streq(name, SPECIAL_DEFAULT_TARGET))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = get_config_path(scope, false, root_dir, &config_path);
|
|
|
1abbee |
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = install_info_add_auto(&c, file);
|
|
|
1abbee |
+ r = get_config_path(scope, false, root_dir, &config_path);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(i = ordered_hashmap_first(c.will_install));
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL);
|
|
|
1abbee |
+ r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
+ if (i->type == UNIT_FILE_TYPE_MASKED)
|
|
|
1abbee |
+ return -ESHUTDOWN;
|
|
|
1abbee |
|
|
|
1abbee |
path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET);
|
|
|
1abbee |
|
|
|
1abbee |
- r = create_symlink(i->path, path, force, changes, n_changes);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ return create_symlink(i->path, path, force, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_get_default(
|
|
|
1abbee |
@@ -1745,127 +2006,100 @@ int unit_file_get_default(
|
|
|
1abbee |
char **name) {
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
- char **p;
|
|
|
1abbee |
+ _cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
+ char *n;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
- r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- STRV_FOREACH(p, paths.unit_path) {
|
|
|
1abbee |
- _cleanup_free_ char *path = NULL, *tmp = NULL;
|
|
|
1abbee |
- char *n;
|
|
|
1abbee |
-
|
|
|
1abbee |
- path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET);
|
|
|
1abbee |
- if (!path)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = readlink_malloc(path, &tmp);
|
|
|
1abbee |
- if (r == -ENOENT)
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- else if (r == -EINVAL)
|
|
|
1abbee |
- /* not a symlink */
|
|
|
1abbee |
- n = strdup(SPECIAL_DEFAULT_TARGET);
|
|
|
1abbee |
- else if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- else
|
|
|
1abbee |
- n = strdup(basename(tmp));
|
|
|
1abbee |
+ r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!n)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
+ r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (i->type == UNIT_FILE_TYPE_MASKED)
|
|
|
1abbee |
+ return -ESHUTDOWN;
|
|
|
1abbee |
|
|
|
1abbee |
- *name = n;
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ n = strdup(i->name);
|
|
|
1abbee |
+ if (!n)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- return -ENOENT;
|
|
|
1abbee |
+ *name = n;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
UnitFileState unit_file_lookup_state(
|
|
|
1abbee |
UnitFileScope scope,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
const LookupPaths *paths,
|
|
|
1abbee |
- const char *name) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- UnitFileState state = _UNIT_FILE_STATE_INVALID;
|
|
|
1abbee |
- char **i;
|
|
|
1abbee |
- _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
+ const char *name,
|
|
|
1abbee |
+ UnitFileState *ret) {
|
|
|
1abbee |
+ _cleanup_(install_context_done) InstallContext c = {};
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
+ UnitFileState state;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(paths);
|
|
|
1abbee |
+ assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(name, TEMPLATE_VALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
- STRV_FOREACH(i, paths->unit_path) {
|
|
|
1abbee |
- struct stat st;
|
|
|
1abbee |
- char *partial;
|
|
|
1abbee |
- bool also = false;
|
|
|
1abbee |
-
|
|
|
1abbee |
- free(path);
|
|
|
1abbee |
- path = path_join(root_dir, *i, name);
|
|
|
1abbee |
- if (!path)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
- if (root_dir)
|
|
|
1abbee |
- partial = path + strlen(root_dir);
|
|
|
1abbee |
- else
|
|
|
1abbee |
- partial = path;
|
|
|
1abbee |
+ r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
- /*
|
|
|
1abbee |
- * Search for a unit file in our default paths, to
|
|
|
1abbee |
- * be sure, that there are no broken symlinks.
|
|
|
1abbee |
- */
|
|
|
1abbee |
- if (lstat(path, &st) < 0) {
|
|
|
1abbee |
- r = -errno;
|
|
|
1abbee |
- if (errno != ENOENT)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ /* Shortcut things, if the caller just wants to know if this unit exists. */
|
|
|
1abbee |
+ if (!ret)
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_instance(name))
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
- } else {
|
|
|
1abbee |
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
|
|
|
1abbee |
- return -ENOENT;
|
|
|
1abbee |
+ switch (i->type) {
|
|
|
1abbee |
|
|
|
1abbee |
- r = null_or_empty_path(path);
|
|
|
1abbee |
- if (r < 0 && r != -ENOENT)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- else if (r > 0) {
|
|
|
1abbee |
- state = path_startswith(*i, "/run") ?
|
|
|
1abbee |
- UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
|
|
|
1abbee |
- return state;
|
|
|
1abbee |
- }
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ case UNIT_FILE_TYPE_MASKED:
|
|
|
1abbee |
+ state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
|
|
|
1abbee |
- r = find_symlinks_in_scope(scope, root_dir, name, &state);
|
|
|
1abbee |
+ case UNIT_FILE_TYPE_REGULAR:
|
|
|
1abbee |
+ r = find_symlinks_in_scope(scope, root_dir, i->name, &state);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
- else if (r > 0)
|
|
|
1abbee |
- return state;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = unit_file_can_install(paths, root_dir, partial, true, &also);
|
|
|
1abbee |
- if (r < 0 && errno != ENOENT)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- else if (r > 0)
|
|
|
1abbee |
- return UNIT_FILE_DISABLED;
|
|
|
1abbee |
- else if (r == 0) {
|
|
|
1abbee |
- if (also)
|
|
|
1abbee |
- return UNIT_FILE_INDIRECT;
|
|
|
1abbee |
- return UNIT_FILE_STATIC;
|
|
|
1abbee |
+ if (r == 0) {
|
|
|
1abbee |
+ if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i))
|
|
|
1abbee |
+ state = UNIT_FILE_DISABLED;
|
|
|
1abbee |
+ else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i))
|
|
|
1abbee |
+ state = UNIT_FILE_INDIRECT;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ state = UNIT_FILE_STATIC;
|
|
|
1abbee |
}
|
|
|
1abbee |
+
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ default:
|
|
|
1abbee |
+ assert_not_reached("Unexpect unit file type.");
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- return r < 0 ? r : state;
|
|
|
1abbee |
+ *ret = state;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-UnitFileState unit_file_get_state(
|
|
|
1abbee |
+int unit_file_get_state(
|
|
|
1abbee |
UnitFileScope scope,
|
|
|
1abbee |
const char *root_dir,
|
|
|
1abbee |
- const char *name) {
|
|
|
1abbee |
+ const char *name,
|
|
|
1abbee |
+ UnitFileState *ret) {
|
|
|
1abbee |
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
int r;
|
|
|
1abbee |
@@ -1874,14 +2108,15 @@ UnitFileState unit_file_get_state(
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
- if (root_dir && scope != UNIT_FILE_SYSTEM)
|
|
|
1abbee |
- return -EINVAL;
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
- return unit_file_lookup_state(scope, root_dir, &paths, name);
|
|
|
1abbee |
+ return unit_file_lookup_state(scope, root_dir, &paths, name, ret);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
|
|
|
1abbee |
@@ -1893,6 +2128,13 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(name);
|
|
|
1abbee |
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!unit_name_is_valid(name, UNIT_NAME_ANY))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
if (scope == UNIT_FILE_SYSTEM)
|
|
|
1abbee |
r = conf_files_list(&files, ".preset", root_dir,
|
|
|
1abbee |
"/etc/systemd/system-preset",
|
|
|
1abbee |
@@ -1909,13 +2151,14 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
|
|
|
1abbee |
"/usr/lib/systemd/user-preset",
|
|
|
1abbee |
NULL);
|
|
|
1abbee |
else
|
|
|
1abbee |
- return 1;
|
|
|
1abbee |
+ return 1; /* Default is "enable" */
|
|
|
1abbee |
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
STRV_FOREACH(p, files) {
|
|
|
1abbee |
_cleanup_fclose_ FILE *f;
|
|
|
1abbee |
+ char line[LINE_MAX];
|
|
|
1abbee |
|
|
|
1abbee |
f = fopen(*p, "re");
|
|
|
1abbee |
if (!f) {
|
|
|
1abbee |
@@ -1925,39 +2168,38 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
|
|
|
1abbee |
return -errno;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- for (;;) {
|
|
|
1abbee |
- char line[LINE_MAX], *l;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!fgets(line, sizeof(line), f))
|
|
|
1abbee |
- break;
|
|
|
1abbee |
+ FOREACH_LINE(line, f, return -errno) {
|
|
|
1abbee |
+ const char *parameter;
|
|
|
1abbee |
+ char *l;
|
|
|
1abbee |
|
|
|
1abbee |
l = strstrip(line);
|
|
|
1abbee |
- if (!*l)
|
|
|
1abbee |
- continue;
|
|
|
1abbee |
|
|
|
1abbee |
- if (strchr(COMMENTS "\n", *l))
|
|
|
1abbee |
+ if (isempty(l))
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+ if (strchr(COMMENTS, *l))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
- if (first_word(l, "enable")) {
|
|
|
1abbee |
- l += 6;
|
|
|
1abbee |
- l += strspn(l, WHITESPACE);
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
|
|
|
1abbee |
+ parameter = first_word(l, "enable");
|
|
|
1abbee |
+ if (parameter) {
|
|
|
1abbee |
+ if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
|
|
|
1abbee |
log_debug("Preset file says enable %s.", name);
|
|
|
1abbee |
return 1;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- } else if (first_word(l, "disable")) {
|
|
|
1abbee |
- l += 7;
|
|
|
1abbee |
- l += strspn(l, WHITESPACE);
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
|
|
|
1abbee |
- if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
|
|
|
1abbee |
+ parameter = first_word(l, "disable");
|
|
|
1abbee |
+ if (parameter) {
|
|
|
1abbee |
+ if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
|
|
|
1abbee |
log_debug("Preset file says disable %s.", name);
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- } else
|
|
|
1abbee |
- log_debug("Couldn't parse line '%s'", l);
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ log_debug("Couldn't parse line '%s'", l);
|
|
|
1abbee |
}
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
@@ -1966,6 +2208,86 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
|
|
|
1abbee |
return 1;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+static int execute_preset(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ InstallContext *plus,
|
|
|
1abbee |
+ InstallContext *minus,
|
|
|
1abbee |
+ const LookupPaths *paths,
|
|
|
1abbee |
+ const char *config_path,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ char **files,
|
|
|
1abbee |
+ UnitFilePresetMode mode,
|
|
|
1abbee |
+ bool force,
|
|
|
1abbee |
+ UnitFileChange **changes,
|
|
|
1abbee |
+ unsigned *n_changes) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(plus);
|
|
|
1abbee |
+ assert(minus);
|
|
|
1abbee |
+ assert(paths);
|
|
|
1abbee |
+ assert(config_path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
|
|
|
1abbee |
+ _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ r = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
|
|
|
1abbee |
+ int q;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Returns number of symlinks that where supposed to be installed. */
|
|
|
1abbee |
+ q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
|
|
|
1abbee |
+ if (r >= 0) {
|
|
|
1abbee |
+ if (q < 0)
|
|
|
1abbee |
+ r = q;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ r+= q;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static int preset_prepare_one(
|
|
|
1abbee |
+ UnitFileScope scope,
|
|
|
1abbee |
+ InstallContext *plus,
|
|
|
1abbee |
+ InstallContext *minus,
|
|
|
1abbee |
+ LookupPaths *paths,
|
|
|
1abbee |
+ const char *root_dir,
|
|
|
1abbee |
+ UnitFilePresetMode mode,
|
|
|
1abbee |
+ const char *name) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ InstallInfo *i;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (install_info_find(plus, name) ||
|
|
|
1abbee |
+ install_info_find(minus, name))
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = unit_file_query_preset(scope, root_dir, name);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (r > 0) {
|
|
|
1abbee |
+ r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (i->type == UNIT_FILE_TYPE_MASKED)
|
|
|
1abbee |
+ return -ESHUTDOWN;
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
int unit_file_preset(
|
|
|
1abbee |
UnitFileScope scope,
|
|
|
1abbee |
bool runtime,
|
|
|
1abbee |
@@ -1980,12 +2302,16 @@ int unit_file_preset(
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
_cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
char **i;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(mode < _UNIT_FILE_PRESET_MAX);
|
|
|
1abbee |
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
@@ -1995,44 +2321,15 @@ int unit_file_preset(
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
STRV_FOREACH(i, files) {
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (!unit_name_is_valid(*i, TEMPLATE_VALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
|
|
|
1abbee |
return -EINVAL;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_query_preset(scope, root_dir, *i);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
|
|
|
1abbee |
- r = install_info_add_auto(&plus, *i);
|
|
|
1abbee |
- else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
|
|
|
1abbee |
- r = install_info_add_auto(&minus, *i);
|
|
|
1abbee |
- else
|
|
|
1abbee |
- r = 0;
|
|
|
1abbee |
+ r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- r = 0;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
|
|
|
1abbee |
- _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
|
|
|
1abbee |
- /* Returns number of symlinks that where supposed to be installed. */
|
|
|
1abbee |
- q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_preset_all(
|
|
|
1abbee |
@@ -2048,12 +2345,16 @@ int unit_file_preset_all(
|
|
|
1abbee |
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
|
|
1abbee |
_cleanup_free_ char *config_path = NULL;
|
|
|
1abbee |
char **i;
|
|
|
1abbee |
- int r, q;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(scope >= 0);
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(mode < _UNIT_FILE_PRESET_MAX);
|
|
|
1abbee |
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
@@ -2092,48 +2393,21 @@ int unit_file_preset_all(
|
|
|
1abbee |
if (hidden_file(de->d_name))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
dirent_ensure_type(d, de);
|
|
|
1abbee |
|
|
|
1abbee |
- if (de->d_type != DT_REG)
|
|
|
1abbee |
+ if (!IN_SET(de->d_type, DT_LNK, DT_REG))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_query_preset(scope, root_dir, de->d_name);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
|
|
|
1abbee |
- r = install_info_add_auto(&plus, de->d_name);
|
|
|
1abbee |
- else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
|
|
|
1abbee |
- r = install_info_add_auto(&minus, de->d_name);
|
|
|
1abbee |
- else
|
|
|
1abbee |
- r = 0;
|
|
|
1abbee |
+ r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- r = 0;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
|
|
|
1abbee |
- _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
|
|
|
1abbee |
-
|
|
|
1abbee |
- q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
|
|
|
1abbee |
- q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
|
|
|
1abbee |
- if (r == 0)
|
|
|
1abbee |
- r = q;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
+ return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static void unit_file_list_free_one(UnitFileList *f) {
|
|
|
1abbee |
@@ -2144,6 +2418,17 @@ static void unit_file_list_free_one(UnitFileList *f) {
|
|
|
1abbee |
free(f);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+Hashmap* unit_file_list_free(Hashmap *h) {
|
|
|
1abbee |
+ UnitFileList *i;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ while ((i = hashmap_steal_first(h)))
|
|
|
1abbee |
+ unit_file_list_free_one(i);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ hashmap_free(h);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return NULL;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_get_list(
|
|
|
1abbee |
@@ -2159,14 +2444,9 @@ int unit_file_get_list(
|
|
|
1abbee |
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
|
|
1abbee |
assert(h);
|
|
|
1abbee |
|
|
|
1abbee |
- if (root_dir && scope != UNIT_FILE_SYSTEM)
|
|
|
1abbee |
- return -EINVAL;
|
|
|
1abbee |
-
|
|
|
1abbee |
- if (root_dir) {
|
|
|
1abbee |
- r = access(root_dir, F_OK);
|
|
|
1abbee |
- if (r < 0)
|
|
|
1abbee |
- return -errno;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ r = verify_root_dir(scope, &root_dir);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
|
|
|
1abbee |
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
@@ -2191,7 +2471,6 @@ int unit_file_get_list(
|
|
|
1abbee |
for (;;) {
|
|
|
1abbee |
_cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
|
|
|
1abbee |
struct dirent *de;
|
|
|
1abbee |
- _cleanup_free_ char *path = NULL;
|
|
|
1abbee |
|
|
|
1abbee |
errno = 0;
|
|
|
1abbee |
de = readdir(d);
|
|
|
1abbee |
@@ -2204,7 +2483,7 @@ int unit_file_get_list(
|
|
|
1abbee |
if (hidden_file(de->d_name))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
|
|
|
1abbee |
+ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
|
|
|
1abbee |
if (hashmap_get(h, de->d_name))
|
|
|
1abbee |
@@ -2223,44 +2502,14 @@ int unit_file_get_list(
|
|
|
1abbee |
if (!f->path)
|
|
|
1abbee |
return -ENOMEM;
|
|
|
1abbee |
|
|
|
1abbee |
- r = null_or_empty_path(f->path);
|
|
|
1abbee |
- if (r < 0 && r != -ENOENT)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- else if (r > 0) {
|
|
|
1abbee |
- f->state =
|
|
|
1abbee |
- path_startswith(*i, "/run") ?
|
|
|
1abbee |
- UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
|
|
|
1abbee |
- goto found;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
|
|
|
1abbee |
+ r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- else if (r > 0) {
|
|
|
1abbee |
- f->state = UNIT_FILE_ENABLED;
|
|
|
1abbee |
- goto found;
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- path = path_make_absolute(de->d_name, *i);
|
|
|
1abbee |
- if (!path)
|
|
|
1abbee |
- return -ENOMEM;
|
|
|
1abbee |
+ f->state = UNIT_FILE_BAD;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_can_install(&paths, root_dir, path, true, NULL);
|
|
|
1abbee |
- if (r == -EINVAL || /* Invalid setting? */
|
|
|
1abbee |
- r == -EBADMSG || /* Invalid format? */
|
|
|
1abbee |
- r == -ENOENT /* Included file not found? */)
|
|
|
1abbee |
- f->state = UNIT_FILE_INVALID;
|
|
|
1abbee |
- else if (r < 0)
|
|
|
1abbee |
- return r;
|
|
|
1abbee |
- else if (r > 0)
|
|
|
1abbee |
- f->state = UNIT_FILE_DISABLED;
|
|
|
1abbee |
- else
|
|
|
1abbee |
- f->state = UNIT_FILE_STATIC;
|
|
|
1abbee |
-
|
|
|
1abbee |
- found:
|
|
|
1abbee |
r = hashmap_put(h, basename(f->path), f);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
f = NULL; /* prevent cleanup */
|
|
|
1abbee |
}
|
|
|
1abbee |
}
|
|
|
1abbee |
@@ -2278,7 +2527,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
|
|
|
1abbee |
[UNIT_FILE_STATIC] = "static",
|
|
|
1abbee |
[UNIT_FILE_DISABLED] = "disabled",
|
|
|
1abbee |
[UNIT_FILE_INDIRECT] = "indirect",
|
|
|
1abbee |
- [UNIT_FILE_INVALID] = "invalid",
|
|
|
1abbee |
+ [UNIT_FILE_BAD] = "bad",
|
|
|
1abbee |
};
|
|
|
1abbee |
|
|
|
1abbee |
DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
|
|
|
1abbee |
diff --git a/src/shared/install.h b/src/shared/install.h
|
|
|
1abbee |
index d729e6e..87a40b6 100644
|
|
|
1abbee |
--- a/src/shared/install.h
|
|
|
1abbee |
+++ b/src/shared/install.h
|
|
|
1abbee |
@@ -24,6 +24,7 @@
|
|
|
1abbee |
#include "hashmap.h"
|
|
|
1abbee |
#include "unit-name.h"
|
|
|
1abbee |
#include "path-lookup.h"
|
|
|
1abbee |
+#include "strv.h"
|
|
|
1abbee |
|
|
|
1abbee |
typedef enum UnitFileScope {
|
|
|
1abbee |
UNIT_FILE_SYSTEM,
|
|
|
1abbee |
@@ -43,7 +44,7 @@ typedef enum UnitFileState {
|
|
|
1abbee |
UNIT_FILE_STATIC,
|
|
|
1abbee |
UNIT_FILE_DISABLED,
|
|
|
1abbee |
UNIT_FILE_INDIRECT,
|
|
|
1abbee |
- UNIT_FILE_INVALID,
|
|
|
1abbee |
+ UNIT_FILE_BAD,
|
|
|
1abbee |
_UNIT_FILE_STATE_MAX,
|
|
|
1abbee |
_UNIT_FILE_STATE_INVALID = -1
|
|
|
1abbee |
} UnitFileState;
|
|
|
1abbee |
@@ -74,6 +75,14 @@ typedef struct UnitFileList {
|
|
|
1abbee |
UnitFileState state;
|
|
|
1abbee |
} UnitFileList;
|
|
|
1abbee |
|
|
|
1abbee |
+typedef enum UnitFileType {
|
|
|
1abbee |
+ UNIT_FILE_TYPE_REGULAR,
|
|
|
1abbee |
+ UNIT_FILE_TYPE_SYMLINK,
|
|
|
1abbee |
+ UNIT_FILE_TYPE_MASKED,
|
|
|
1abbee |
+ _UNIT_FILE_TYPE_MAX,
|
|
|
1abbee |
+ _UNIT_FILE_TYPE_INVALID = -1,
|
|
|
1abbee |
+} UnitFileType;
|
|
|
1abbee |
+
|
|
|
1abbee |
typedef struct {
|
|
|
1abbee |
char *name;
|
|
|
1abbee |
char *path;
|
|
|
1abbee |
@@ -85,8 +94,26 @@ typedef struct {
|
|
|
1abbee |
char **also;
|
|
|
1abbee |
|
|
|
1abbee |
char *default_instance;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ UnitFileType type;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ char *symlink_target;
|
|
|
1abbee |
} InstallInfo;
|
|
|
1abbee |
|
|
|
1abbee |
+static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(InstallInfo *i) {
|
|
|
1abbee |
+ assert(i);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return !strv_isempty(i->aliases) ||
|
|
|
1abbee |
+ !strv_isempty(i->wanted_by) ||
|
|
|
1abbee |
+ !strv_isempty(i->required_by);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(InstallInfo *i) {
|
|
|
1abbee |
+ assert(i);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return !strv_isempty(i->also);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
|
|
1abbee |
int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
|
|
|
1abbee |
int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
|
|
1abbee |
@@ -97,21 +124,14 @@ int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char
|
|
|
1abbee |
int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
|
|
|
1abbee |
int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
|
|
|
1abbee |
int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name);
|
|
|
1abbee |
-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);
|
|
|
1abbee |
-
|
|
|
1abbee |
-UnitFileState unit_file_lookup_state(
|
|
|
1abbee |
- UnitFileScope scope,
|
|
|
1abbee |
- const char *root_dir,
|
|
|
1abbee |
- const LookupPaths *paths,
|
|
|
1abbee |
- const char *name);
|
|
|
1abbee |
-UnitFileState unit_file_get_state(
|
|
|
1abbee |
- UnitFileScope scope,
|
|
|
1abbee |
- const char *root_dir,
|
|
|
1abbee |
- const char *filename);
|
|
|
1abbee |
+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);
|
|
|
1abbee |
+
|
|
|
1abbee |
+int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret);
|
|
|
1abbee |
+int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
|
|
|
1abbee |
|
|
|
1abbee |
int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
|
|
|
1abbee |
+Hashmap* unit_file_list_free(Hashmap *h);
|
|
|
1abbee |
|
|
|
1abbee |
-void unit_file_list_free(Hashmap *h);
|
|
|
1abbee |
int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
|
|
|
1abbee |
void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
|
|
|
1abbee |
index d5510bf..1181ffb 100644
|
|
|
1abbee |
--- a/src/shared/path-util.c
|
|
|
1abbee |
+++ b/src/shared/path-util.c
|
|
|
1abbee |
@@ -704,3 +704,37 @@ int fsck_exists(const char *fstype) {
|
|
|
1abbee |
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
+
|
|
|
1abbee |
+char *prefix_root(const char *root, const char *path) {
|
|
|
1abbee |
+ char *n, *p;
|
|
|
1abbee |
+ size_t l;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* If root is passed, prefixes path with it. Otherwise returns
|
|
|
1abbee |
+ * it as is. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* First, drop duplicate prefixing slashes from the path */
|
|
|
1abbee |
+ while (path[0] == '/' && path[1] == '/')
|
|
|
1abbee |
+ path++;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (isempty(root) || path_equal(root, "/"))
|
|
|
1abbee |
+ return strdup(path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ l = strlen(root) + 1 + strlen(path) + 1;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ n = new(char, l);
|
|
|
1abbee |
+ if (!n)
|
|
|
1abbee |
+ return NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = stpcpy(n, root);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ while (p > n && p[-1] == '/')
|
|
|
1abbee |
+ p--;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (path[0] != '/')
|
|
|
1abbee |
+ *(p++) = '/';
|
|
|
1abbee |
+
|
|
|
1abbee |
+ strcpy(p, path);
|
|
|
1abbee |
+ return n;
|
|
|
1abbee |
+}
|
|
|
1abbee |
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
|
|
|
1abbee |
index ca81b49..71bb740 100644
|
|
|
1abbee |
--- a/src/shared/path-util.h
|
|
|
1abbee |
+++ b/src/shared/path-util.h
|
|
|
1abbee |
@@ -63,6 +63,33 @@ bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool
|
|
|
1abbee |
|
|
|
1abbee |
int fsck_exists(const char *fstype);
|
|
|
1abbee |
|
|
|
1abbee |
+char *prefix_root(const char *root, const char *path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+/* Similar to prefix_root(), but returns an alloca() buffer, or
|
|
|
1abbee |
+ * possibly a const pointer into the path parameter */
|
|
|
1abbee |
+#define prefix_roota(root, path) \
|
|
|
1abbee |
+ ({ \
|
|
|
1abbee |
+ const char* _path = (path), *_root = (root), *_ret; \
|
|
|
1abbee |
+ char *_p, *_n; \
|
|
|
1abbee |
+ size_t _l; \
|
|
|
1abbee |
+ while (_path[0] == '/' && _path[1] == '/') \
|
|
|
1abbee |
+ _path ++; \
|
|
|
1abbee |
+ if (isempty(_root) || path_equal(_root, "/")) \
|
|
|
1abbee |
+ _ret = _path; \
|
|
|
1abbee |
+ else { \
|
|
|
1abbee |
+ _l = strlen(_root) + 1 + strlen(_path) + 1; \
|
|
|
1abbee |
+ _n = alloca(_l); \
|
|
|
1abbee |
+ _p = stpcpy(_n, _root); \
|
|
|
1abbee |
+ while (_p > _n && _p[-1] == '/') \
|
|
|
1abbee |
+ _p--; \
|
|
|
1abbee |
+ if (_path[0] != '/') \
|
|
|
1abbee |
+ *(_p++) = '/'; \
|
|
|
1abbee |
+ strcpy(_p, _path); \
|
|
|
1abbee |
+ _ret = _n; \
|
|
|
1abbee |
+ } \
|
|
|
1abbee |
+ _ret; \
|
|
|
1abbee |
+ })
|
|
|
1abbee |
+
|
|
|
1abbee |
/* Iterates through the path prefixes of the specified path, going up
|
|
|
1abbee |
* the tree, to root. Also returns "" (and not "/"!) for the root
|
|
|
1abbee |
* directory. Excludes the specified directory itself */
|
|
|
1abbee |
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
|
|
|
1abbee |
index f728af4..b7827d1 100644
|
|
|
1abbee |
--- a/src/shared/unit-name.c
|
|
|
1abbee |
+++ b/src/shared/unit-name.c
|
|
|
1abbee |
@@ -63,16 +63,13 @@ static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
|
|
|
1abbee |
|
|
|
1abbee |
DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
|
|
|
1abbee |
|
|
|
1abbee |
-bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
|
|
|
1abbee |
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
|
|
|
1abbee |
const char *e, *i, *at;
|
|
|
1abbee |
|
|
|
1abbee |
- /* Valid formats:
|
|
|
1abbee |
- *
|
|
|
1abbee |
- * string@instance.suffix
|
|
|
1abbee |
- * string.suffix
|
|
|
1abbee |
- */
|
|
|
1abbee |
+ assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
|
|
|
1abbee |
|
|
|
1abbee |
- assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
|
|
|
1abbee |
+ if (_unlikely_(flags == 0))
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
|
|
|
1abbee |
if (isempty(n))
|
|
|
1abbee |
return false;
|
|
|
1abbee |
@@ -96,15 +93,22 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
|
|
|
1abbee |
return false;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
- if (at) {
|
|
|
1abbee |
- if (at == n)
|
|
|
1abbee |
- return false;
|
|
|
1abbee |
+ if (at == n)
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
|
|
|
1abbee |
- if (template_ok != TEMPLATE_VALID && at+1 == e)
|
|
|
1abbee |
- return false;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ if (flags & UNIT_NAME_PLAIN)
|
|
|
1abbee |
+ if (!at)
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (flags & UNIT_NAME_INSTANCE)
|
|
|
1abbee |
+ if (at && e > at + 1)
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (flags & UNIT_NAME_TEMPLATE)
|
|
|
1abbee |
+ if (at && e == at + 1)
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
|
|
|
1abbee |
- return true;
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
bool unit_instance_is_valid(const char *i) {
|
|
|
1abbee |
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
|
|
|
1abbee |
index 6f139cc..4364860 100644
|
|
|
1abbee |
--- a/src/shared/unit-name.h
|
|
|
1abbee |
+++ b/src/shared/unit-name.h
|
|
|
1abbee |
@@ -107,6 +107,13 @@ enum UnitDependency {
|
|
|
1abbee |
_UNIT_DEPENDENCY_INVALID = -1
|
|
|
1abbee |
};
|
|
|
1abbee |
|
|
|
1abbee |
+typedef enum UnitNameFlags {
|
|
|
1abbee |
+ UNIT_NAME_PLAIN = 1, /* Allow foo.service */
|
|
|
1abbee |
+ UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */
|
|
|
1abbee |
+ UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */
|
|
|
1abbee |
+ UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE,
|
|
|
1abbee |
+} UnitNameFlags;
|
|
|
1abbee |
+
|
|
|
1abbee |
const char *unit_type_to_string(UnitType i) _const_;
|
|
|
1abbee |
UnitType unit_type_from_string(const char *s) _pure_;
|
|
|
1abbee |
|
|
|
1abbee |
@@ -117,12 +124,7 @@ int unit_name_to_instance(const char *n, char **instance);
|
|
|
1abbee |
char* unit_name_to_prefix(const char *n);
|
|
|
1abbee |
char* unit_name_to_prefix_and_instance(const char *n);
|
|
|
1abbee |
|
|
|
1abbee |
-enum template_valid {
|
|
|
1abbee |
- TEMPLATE_INVALID,
|
|
|
1abbee |
- TEMPLATE_VALID,
|
|
|
1abbee |
-};
|
|
|
1abbee |
-
|
|
|
1abbee |
-bool unit_name_is_valid(const char *n, enum template_valid template_ok) _pure_;
|
|
|
1abbee |
+bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_;
|
|
|
1abbee |
bool unit_prefix_is_valid(const char *p) _pure_;
|
|
|
1abbee |
bool unit_instance_is_valid(const char *i) _pure_;
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/shared/util.c b/src/shared/util.c
|
|
|
1abbee |
index a24aa7f..036677e 100644
|
|
|
1abbee |
--- a/src/shared/util.c
|
|
|
1abbee |
+++ b/src/shared/util.c
|
|
|
1abbee |
@@ -1094,6 +1094,27 @@ int readlink_and_canonicalize(const char *p, char **r) {
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+
|
|
|
1abbee |
+int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
|
|
|
1abbee |
+ _cleanup_free_ char *target = NULL, *t = NULL;
|
|
|
1abbee |
+ const char *full;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ full = prefix_roota(root, path);
|
|
|
1abbee |
+ r = readlink_malloc(full, &target);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ t = file_in_same_dir(path, target);
|
|
|
1abbee |
+ if (!t)
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *ret = t;
|
|
|
1abbee |
+ t = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
int reset_all_signal_handlers(void) {
|
|
|
1abbee |
int sig, r = 0;
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/shared/util.h b/src/shared/util.h
|
|
|
1abbee |
index b4a4a49..a441e44 100644
|
|
|
1abbee |
--- a/src/shared/util.h
|
|
|
1abbee |
+++ b/src/shared/util.h
|
|
|
1abbee |
@@ -281,6 +281,7 @@ int readlink_malloc(const char *p, char **r);
|
|
|
1abbee |
int readlink_value(const char *p, char **ret);
|
|
|
1abbee |
int readlink_and_make_absolute(const char *p, char **r);
|
|
|
1abbee |
int readlink_and_canonicalize(const char *p, char **r);
|
|
|
1abbee |
+int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
|
|
|
1abbee |
|
|
|
1abbee |
int reset_all_signal_handlers(void);
|
|
|
1abbee |
int reset_signal_mask(void);
|
|
|
1abbee |
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
|
|
|
1abbee |
index bf5bb39..95ddf3b 100644
|
|
|
1abbee |
--- a/src/systemctl/systemctl.c
|
|
|
1abbee |
+++ b/src/systemctl/systemctl.c
|
|
|
1abbee |
@@ -1304,7 +1304,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
|
|
|
1abbee |
if (u->state == UNIT_FILE_MASKED ||
|
|
|
1abbee |
u->state == UNIT_FILE_MASKED_RUNTIME ||
|
|
|
1abbee |
u->state == UNIT_FILE_DISABLED ||
|
|
|
1abbee |
- u->state == UNIT_FILE_INVALID) {
|
|
|
1abbee |
+ u->state == UNIT_FILE_BAD) {
|
|
|
1abbee |
on = ansi_highlight_red();
|
|
|
1abbee |
off = ansi_highlight_off();
|
|
|
1abbee |
} else if (u->state == UNIT_FILE_ENABLED) {
|
|
|
1abbee |
@@ -5637,8 +5637,8 @@ static int unit_is_enabled(sd_bus *bus, char **args) {
|
|
|
1abbee |
STRV_FOREACH(name, names) {
|
|
|
1abbee |
UnitFileState state;
|
|
|
1abbee |
|
|
|
1abbee |
- state = unit_file_get_state(arg_scope, arg_root, *name);
|
|
|
1abbee |
- if (state < 0)
|
|
|
1abbee |
+ r = unit_file_get_state(arg_scope, arg_root, *name, &state);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
return log_error_errno(state, "Failed to get unit file state for %s: %m", *name);
|
|
|
1abbee |
|
|
|
1abbee |
if (state == UNIT_FILE_ENABLED ||
|
|
|
1abbee |
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
|
|
|
1abbee |
index d60e75a..7e0e7fc 100644
|
|
|
1abbee |
--- a/src/sysv-generator/sysv-generator.c
|
|
|
1abbee |
+++ b/src/sysv-generator/sysv-generator.c
|
|
|
1abbee |
@@ -712,6 +712,7 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
|
|
|
1abbee |
|
|
|
1abbee |
static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
|
|
1abbee |
char **path;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
|
|
|
1abbee |
STRV_FOREACH(path, lp->sysvinit_path) {
|
|
|
1abbee |
_cleanup_closedir_ DIR *d = NULL;
|
|
|
1abbee |
@@ -728,7 +729,6 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
|
|
1abbee |
_cleanup_free_ char *fpath = NULL, *name = NULL;
|
|
|
1abbee |
_cleanup_free_ SysvStub *service = NULL;
|
|
|
1abbee |
struct stat st;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
|
|
|
1abbee |
if (hidden_file(de->d_name))
|
|
|
1abbee |
continue;
|
|
|
1abbee |
@@ -755,8 +755,12 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
|
|
1abbee |
if (!fpath)
|
|
|
1abbee |
return log_oom();
|
|
|
1abbee |
|
|
|
1abbee |
- if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) {
|
|
|
1abbee |
- log_debug("Native unit for %s already exists, skipping", name);
|
|
|
1abbee |
+ r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL);
|
|
|
1abbee |
+ if (r < 0 && r != -ENOENT) {
|
|
|
1abbee |
+ log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
|
|
|
1abbee |
+ continue;
|
|
|
1abbee |
+ } else if (r >= 0) {
|
|
|
1abbee |
+ log_debug("Native unit for %s already exists, skipping.", name);
|
|
|
1abbee |
continue;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
|
|
|
1abbee |
new file mode 100644
|
|
|
1abbee |
index 0000000..89d91d3
|
|
|
1abbee |
--- /dev/null
|
|
|
1abbee |
+++ b/src/test/test-install-root.c
|
|
|
1abbee |
@@ -0,0 +1,663 @@
|
|
|
1abbee |
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
|
1abbee |
+
|
|
|
1abbee |
+/***
|
|
|
1abbee |
+ This file is part of systemd.
|
|
|
1abbee |
+
|
|
|
1abbee |
+ Copyright 2011 Lennart Poettering
|
|
|
1abbee |
+
|
|
|
1abbee |
+ systemd is free software; you can redistribute it and/or modify it
|
|
|
1abbee |
+ under the terms of the GNU Lesser General Public License as published by
|
|
|
1abbee |
+ the Free Software Foundation; either version 2.1 of the License, or
|
|
|
1abbee |
+ (at your option) any later version.
|
|
|
1abbee |
+
|
|
|
1abbee |
+ systemd is distributed in the hope that it will be useful, but
|
|
|
1abbee |
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
1abbee |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
1abbee |
+ Lesser General Public License for more details.
|
|
|
1abbee |
+
|
|
|
1abbee |
+ You should have received a copy of the GNU Lesser General Public License
|
|
|
1abbee |
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
1abbee |
+***/
|
|
|
1abbee |
+
|
|
|
1abbee |
+#include "fileio.h"
|
|
|
1abbee |
+#include "install.h"
|
|
|
1abbee |
+#include "mkdir.h"
|
|
|
1abbee |
+#include "util.h"
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_basic_mask_and_enable(const char *root) {
|
|
|
1abbee |
+ const char *p;
|
|
|
1abbee |
+ UnitFileState state;
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/a.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/b.service");
|
|
|
1abbee |
+ assert_se(symlink("a.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/c.service");
|
|
|
1abbee |
+ assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/d.service");
|
|
|
1abbee |
+ assert_se(symlink("c.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* This one is interesting, as d follows a relative, then an absolute symlink */
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_mask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/dev/null"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Enabling a masked unit should fail! */
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Enabling it again should succeed but be a NOP */
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1);
|
|
|
1abbee |
+ assert_se(n_changes == 0);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Disabling a disabled unit must suceed but be a NOP */
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 0);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Let's enable this indirectly via a symlink */
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("d.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Let's try to reenable */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("b.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 2);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ assert_se(changes[1].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service"));
|
|
|
1abbee |
+ assert_se(streq(changes[1].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_linked_units(const char *root) {
|
|
|
1abbee |
+ const char *p, *q;
|
|
|
1abbee |
+ UnitFileState state;
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0, i;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /*
|
|
|
1abbee |
+ * We'll test three cases here:
|
|
|
1abbee |
+ *
|
|
|
1abbee |
+ * a) a unit file in /opt, that we use "systemctl link" and
|
|
|
1abbee |
+ * "systemctl enable" on to make it available to the system
|
|
|
1abbee |
+ *
|
|
|
1abbee |
+ * b) a unit file in /opt, that is statically linked into
|
|
|
1abbee |
+ * /usr/lib/systemd/system, that "enable" should work on
|
|
|
1abbee |
+ * correctly.
|
|
|
1abbee |
+ *
|
|
|
1abbee |
+ * c) a unit file in /opt, that is linked into
|
|
|
1abbee |
+ * /etc/systemd/system, and where "enable" should result in
|
|
|
1abbee |
+ * -ELOOP, since using information from /etc to generate
|
|
|
1abbee |
+ * information in /etc should not be allowed.
|
|
|
1abbee |
+ */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/opt/linked.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/opt/linked2.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/opt/linked3.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/linked2.service");
|
|
|
1abbee |
+ assert_se(symlink("/opt/linked2.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service");
|
|
|
1abbee |
+ assert_se(symlink("/opt/linked3.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* First, let's link the unit into the search path */
|
|
|
1abbee |
+ assert_se(unit_file_link(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/opt/linked.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Let's unlink it from the search path again */
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Now, let's not just link it, but also enable it */
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 2);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
|
|
|
1abbee |
+ q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
|
|
|
1abbee |
+ for (i = 0 ; i < n_changes; i++) {
|
|
|
1abbee |
+ assert_se(changes[i].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[i].source, "/opt/linked.service"));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (p && streq(changes[i].path, p))
|
|
|
1abbee |
+ p = NULL;
|
|
|
1abbee |
+ else if (q && streq(changes[i].path, q))
|
|
|
1abbee |
+ q = NULL;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ assert_not_reached("wut?");
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ assert(!p && !q);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* And let's unlink it again */
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 2);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
|
|
|
1abbee |
+ q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
|
|
|
1abbee |
+ for (i = 0; i < n_changes; i++) {
|
|
|
1abbee |
+ assert_se(changes[i].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (p && streq(changes[i].path, p))
|
|
|
1abbee |
+ p = NULL;
|
|
|
1abbee |
+ else if (q && streq(changes[i].path, q))
|
|
|
1abbee |
+ q = NULL;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ assert_not_reached("wut?");
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ assert(!p && !q);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked2.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 2);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service");
|
|
|
1abbee |
+ q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service");
|
|
|
1abbee |
+ for (i = 0 ; i < n_changes; i++) {
|
|
|
1abbee |
+ assert_se(changes[i].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[i].source, "/opt/linked2.service"));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (p && streq(changes[i].path, p))
|
|
|
1abbee |
+ p = NULL;
|
|
|
1abbee |
+ else if (q && streq(changes[i].path, q))
|
|
|
1abbee |
+ q = NULL;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ assert_not_reached("wut?");
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ assert(!p && !q);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked3.service"), false, &changes, &n_changes) == -ELOOP);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_default(const char *root) {
|
|
|
1abbee |
+ _cleanup_free_ char *def = NULL;
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0;
|
|
|
1abbee |
+ const char *p;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/test-default-real.target");
|
|
|
1abbee |
+ assert_se(write_string_file(p, "# pretty much empty") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/test-default.target");
|
|
|
1abbee |
+ assert_se(symlink("test-default-real.target", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "idontexist.target", false, &changes, &n_changes) == -ENOENT);
|
|
|
1abbee |
+ assert_se(n_changes == 0);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "test-default.target", false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/default.target");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0);
|
|
|
1abbee |
+ assert_se(streq_ptr(def, "test-default-real.target"));
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_add_dependency(const char *root) {
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0;
|
|
|
1abbee |
+ const char *p;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-target.target");
|
|
|
1abbee |
+ assert_se(write_string_file(p, "# pretty much empty") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-target.target");
|
|
|
1abbee |
+ assert_se(symlink("real-add-dependency-test-target.target", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p, "# pretty much empty") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service");
|
|
|
1abbee |
+ assert_se(symlink("real-add-dependency-test-service.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ 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);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_template_enable(const char *root) {
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0;
|
|
|
1abbee |
+ UnitFileState state;
|
|
|
1abbee |
+ const char *p;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/template@.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "DefaultInstance=def\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service");
|
|
|
1abbee |
+ assert_se(symlink("template@.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@foo.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template-symlink@quux.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@quux.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_indirect(const char *root) {
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0;
|
|
|
1abbee |
+ UnitFileState state;
|
|
|
1abbee |
+ const char *p;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/indirecta.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "Also=indirectb.service\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/indirectb.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/indirectc.service");
|
|
|
1abbee |
+ assert_se(symlink("indirecta.service", p) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+static void test_preset_and_list(const char *root) {
|
|
|
1abbee |
+ UnitFileChange *changes = NULL;
|
|
|
1abbee |
+ unsigned n_changes = 0, i;
|
|
|
1abbee |
+ const char *p, *q;
|
|
|
1abbee |
+ UnitFileState state;
|
|
|
1abbee |
+ bool got_yes = false, got_no = false;
|
|
|
1abbee |
+ Iterator j;
|
|
|
1abbee |
+ UnitFileList *fl;
|
|
|
1abbee |
+ Hashmap *h;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "[Install]\n"
|
|
|
1abbee |
+ "WantedBy=multi-user.target\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
|
|
|
1abbee |
+ assert_se(write_string_file(p,
|
|
|
1abbee |
+ "enable *-yes.*\n"
|
|
|
1abbee |
+ "disable *\n") >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
|
|
|
1abbee |
+ assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service"));
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 1);
|
|
|
1abbee |
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
|
|
|
1abbee |
+ assert_se(streq(changes[0].path, p));
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+ assert_se(n_changes == 0);
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, false, root, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(n_changes > 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (i = 0; i < n_changes; i++) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (changes[i].type == UNIT_FILE_SYMLINK) {
|
|
|
1abbee |
+ assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service"));
|
|
|
1abbee |
+ assert_se(streq(changes[i].path, p));
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ assert_se(changes[i].type == UNIT_FILE_UNLINK);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
+ changes = NULL; n_changes = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(h = hashmap_new(&string_hash_ops));
|
|
|
1abbee |
+ assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
|
|
|
1abbee |
+ q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
|
|
|
1abbee |
+
|
|
|
1abbee |
+ HASHMAP_FOREACH(fl, h, j) {
|
|
|
1abbee |
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0);
|
|
|
1abbee |
+ assert_se(fl->state == state);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (streq(fl->path, p)) {
|
|
|
1abbee |
+ got_yes = true;
|
|
|
1abbee |
+ assert_se(fl->state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ } else if (streq(fl->path, q)) {
|
|
|
1abbee |
+ got_no = true;
|
|
|
1abbee |
+ assert_se(fl->state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT));
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ unit_file_list_free(h);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(got_yes && got_no);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int main(int argc, char *argv[]) {
|
|
|
1abbee |
+ char root[] = "/tmp/rootXXXXXX";
|
|
|
1abbee |
+ const char *p;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(mkdtemp(root));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system/");
|
|
|
1abbee |
+ assert_se(mkdir_p(p, 0755) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/");
|
|
|
1abbee |
+ assert_se(mkdir_p(p, 0755) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/run/systemd/system/");
|
|
|
1abbee |
+ assert_se(mkdir_p(p, 0755) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/opt/");
|
|
|
1abbee |
+ assert_se(mkdir_p(p, 0755) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ p = strjoina(root, "/usr/lib/systemd/system-preset/");
|
|
|
1abbee |
+ assert_se(mkdir_p(p, 0755) >= 0);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ test_basic_mask_and_enable(root);
|
|
|
1abbee |
+ test_linked_units(root);
|
|
|
1abbee |
+ test_default(root);
|
|
|
1abbee |
+ test_add_dependency(root);
|
|
|
1abbee |
+ test_template_enable(root);
|
|
|
1abbee |
+ test_indirect(root);
|
|
|
1abbee |
+ test_preset_and_list(root);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(rm_rf_dangerous(root, false, true, false));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+}
|
|
|
1abbee |
diff --git a/src/test/test-install.c b/src/test/test-install.c
|
|
|
1abbee |
index 467970b..08a1faf 100644
|
|
|
1abbee |
--- a/src/test/test-install.c
|
|
|
1abbee |
+++ b/src/test/test-install.c
|
|
|
1abbee |
@@ -50,17 +50,19 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
const char *const files2[] = { "/home/lennart/test.service", NULL };
|
|
|
1abbee |
UnitFileChange *changes = NULL;
|
|
|
1abbee |
unsigned n_changes = 0;
|
|
|
1abbee |
+ UnitFileState state = 0;
|
|
|
1abbee |
|
|
|
1abbee |
h = hashmap_new(&string_hash_ops);
|
|
|
1abbee |
r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
|
|
|
1abbee |
assert_se(r == 0);
|
|
|
1abbee |
|
|
|
1abbee |
HASHMAP_FOREACH(p, h, i) {
|
|
|
1abbee |
- UnitFileState s;
|
|
|
1abbee |
+ UnitFileState s = _UNIT_FILE_STATE_INVALID;
|
|
|
1abbee |
|
|
|
1abbee |
- s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path));
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(p->state == s);
|
|
|
1abbee |
+ assert_se((r < 0 && p->state == UNIT_FILE_BAD) ||
|
|
|
1abbee |
+ (p->state == s));
|
|
|
1abbee |
|
|
|
1abbee |
fprintf(stderr, "%s (%s)\n",
|
|
|
1abbee |
p->path,
|
|
|
1abbee |
@@ -82,7 +84,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("disable");
|
|
|
1abbee |
|
|
|
1abbee |
@@ -95,7 +99,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("mask");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -110,7 +116,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_MASKED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("unmask");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -125,7 +133,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("mask");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -137,7 +147,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_MASKED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("disable");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -152,7 +164,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_MASKED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("umask");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -164,7 +178,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_DISABLED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("enable files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -176,19 +192,22 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("disable files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
n_changes = 0;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
|
|
|
1abbee |
+ r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes);
|
|
|
1abbee |
assert_se(r >= 0);
|
|
|
1abbee |
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r < 0);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("link files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -200,19 +219,22 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_LINKED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("disable files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
n_changes = 0;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
|
|
|
1abbee |
+ r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes);
|
|
|
1abbee |
assert_se(r >= 0);
|
|
|
1abbee |
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r < 0);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("link files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -224,7 +246,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_LINKED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("reenable files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
@@ -236,19 +260,22 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
|
|
|
1abbee |
log_error("disable files2");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
n_changes = 0;
|
|
|
1abbee |
|
|
|
1abbee |
- r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
|
|
|
1abbee |
+ r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes);
|
|
|
1abbee |
assert_se(r >= 0);
|
|
|
1abbee |
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state);
|
|
|
1abbee |
+ assert_se(r < 0);
|
|
|
1abbee |
log_error("preset files");
|
|
|
1abbee |
changes = NULL;
|
|
|
1abbee |
n_changes = 0;
|
|
|
1abbee |
@@ -259,7 +286,9 @@ int main(int argc, char* argv[]) {
|
|
|
1abbee |
dump_changes(changes, n_changes);
|
|
|
1abbee |
unit_file_changes_free(changes, n_changes);
|
|
|
1abbee |
|
|
|
1abbee |
- assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0])) == UNIT_FILE_ENABLED);
|
|
|
1abbee |
+ r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state);
|
|
|
1abbee |
+ assert_se(r >= 0);
|
|
|
1abbee |
+ assert_se(state == UNIT_FILE_ENABLED);
|
|
|
1abbee |
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|