1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-27 07:22:31 +03:00

Merge pull request #30214 from bluca/wants_mounts_for

Add WantsMountsFor= and use it in the cryptsetup generator
This commit is contained in:
Luca Boccassi 2023-12-06 21:00:37 +00:00 committed by GitHub
commit f9a284f02d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 272 additions and 140 deletions

2
TODO
View File

@ -1880,8 +1880,6 @@ Features:
- generate better errors when people try to set transient properties
that are not supported...
https://lists.freedesktop.org/archives/systemd-devel/2015-February/028076.html
- maybe introduce WantsMountsFor=? Use case:
https://lists.freedesktop.org/archives/systemd-devel/2015-January/027729.html
- recreate systemd's D-Bus private socket file on SIGUSR2
- move PAM code into its own binary
- when we automatically restart a service, ensure we restart its rdeps, too.

View File

@ -322,7 +322,9 @@
unsuccessful. Note that other units that depend on the unlocked device may still fail. In
particular, if the device is used for a mount point, the mount point itself also needs to
have the <option>nofail</option> option, or the boot will fail if the device is not unlocked
successfully.</para>
successfully. If a keyfile and/or a <option>header</option> are specified, the dependencies on
their respective directories will also not be fatal, so that umounting said directories will
not cause the generated cryptset unit to be deactivated.</para>
<xi:include href="version-info.xml" xpointer="v186"/></listitem>
</varlistentry>

View File

@ -1979,6 +1979,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as RequiresMountsFor = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as WantsMountsFor = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Documentation = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Description = '...';
@ -2135,6 +2137,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property SliceOf is not documented!-->
<!--property WantsMountsFor is not documented!-->
<!--property FreezerState is not documented!-->
<!--property DropInPaths is not documented!-->
@ -2295,6 +2299,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="RequiresMountsFor"/>
<variablelist class="dbus-property" generated="True" extra-ref="WantsMountsFor"/>
<variablelist class="dbus-property" generated="True" extra-ref="Documentation"/>
<variablelist class="dbus-property" generated="True" extra-ref="Description"/>
@ -11829,6 +11835,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>ActivationDetails</varname> were added in version 252.</para>
<para><function>QueueSignal()</function> was added in version 254.</para>
<para><varname>SurviveFinalKillSignal</varname> was added in version 255.</para>
<para><varname>WantsMountsFor</varname> was added in version 256.</para>
</refsect2>
<refsect2>
<title>Service Unit Objects</title>

View File

@ -255,13 +255,14 @@
</varlistentry>
<varlistentry>
<term><option>x-systemd.wants-mounts-for=</option></term>
<term><option>x-systemd.requires-mounts-for=</option></term>
<listitem><para>Configures a
<varname>RequiresMountsFor=</varname> dependency between the
created mount unit and other mount units. The argument must be
an absolute path. This option may be specified more than once.
See <varname>RequiresMountsFor=</varname> in
<varname>RequiresMountsFor=</varname> or <varname>WantsMountsFor=</varname>
dependency between the created mount unit and other mount units. The
argument must be an absolute path. This option may be specified more than
once. See <varname>RequiresMountsFor=</varname> or <varname>WantsMountsFor=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>

View File

@ -919,6 +919,16 @@
<xi:include href="version-info.xml" xpointer="v201"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>WantsMountsFor=</varname></term>
<listitem><para>Same as <varname>RequiresMountsFor=</varname>,
but adds dependencies of type <varname>Wants=</varname> instead
of <varname>Requires=</varname>.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>OnSuccessJobMode=</varname></term>
<term><varname>OnFailureJobMode=</varname></term>

View File

@ -126,7 +126,7 @@ static int automount_add_mount_dependencies(Automount *a) {
if (r < 0)
return r;
return unit_require_mounts_for(UNIT(a), parent, UNIT_DEPENDENCY_IMPLICIT);
return unit_add_mounts_for(UNIT(a), parent, UNIT_DEPENDENCY_IMPLICIT, UNIT_MOUNT_REQUIRES);
}
static int automount_add_default_dependencies(Automount *a) {

View File

@ -177,7 +177,7 @@ static int property_get_dependencies(
return sd_bus_message_close_container(reply);
}
static int property_get_requires_mounts_for(
static int property_get_mounts_for(
sd_bus *bus,
const char *path,
const char *interface,
@ -879,7 +879,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("StopPropagatedFrom", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SliceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_mounts_for, offsetof(Unit, mounts_for[UNIT_MOUNT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WantsMountsFor", "as", property_get_mounts_for, offsetof(Unit, mounts_for[UNIT_MOUNT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AccessSELinuxContext", "s", NULL, offsetof(Unit, access_selinux_context), SD_BUS_VTABLE_PROPERTY_CONST),
@ -2308,7 +2309,7 @@ static int bus_unit_set_transient_property(
return 1;
} else if (streq(name, "RequiresMountsFor")) {
} else if (STR_IN_SET(name, "RequiresMountsFor", "WantsMountsFor")) {
_cleanup_strv_free_ char **l = NULL;
r = sd_bus_message_read_strv(message, &l);
@ -2328,9 +2329,9 @@ static int bus_unit_set_transient_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s is not normalized: %s", name, *p);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = unit_require_mounts_for(u, *p, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, *p, UNIT_DEPENDENCY_FILE, unit_mount_dependency_type_from_string(name));
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to add required mount \"%s\": %m", *p);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to add requested mount \"%s\": %m", *p);
unit_write_settingf(u, flags, name, "%s=%s", name, *p);
}

View File

@ -309,7 +309,8 @@ Unit.PartOf, config_parse_unit_deps,
Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0
Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0
Unit.RequiresMountsFor, config_parse_unit_mounts_for, 0, 0
Unit.WantsMountsFor, config_parse_unit_mounts_for, 0, 0
Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded)
Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start)
Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)

View File

@ -3152,7 +3152,7 @@ int config_parse_unit_condition_string(
return 0;
}
int config_parse_unit_requires_mounts_for(
int config_parse_unit_mounts_for(
const char *unit,
const char *filename,
unsigned line,
@ -3171,6 +3171,7 @@ int config_parse_unit_requires_mounts_for(
assert(lvalue);
assert(rvalue);
assert(data);
assert(STR_IN_SET(lvalue, "RequiresMountsFor", "WantsMountsFor"));
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
@ -3196,9 +3197,9 @@ int config_parse_unit_requires_mounts_for(
if (r < 0)
continue;
r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE, unit_mount_dependency_type_from_string(lvalue));
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved);
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add requested mount '%s', ignoring: %m", resolved);
continue;
}
}
@ -6301,8 +6302,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_nsec, "NANOSECONDS" },
{ config_parse_namespace_path_strv, "PATH [...]" },
{ config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
{ config_parse_unit_requires_mounts_for,
"PATH [...]" },
{ config_parse_unit_mounts_for, "PATH [...]" },
{ config_parse_exec_mount_propagation_flag,
"MOUNTFLAG" },
{ config_parse_unit_string_printf, "STRING" },

View File

@ -71,7 +71,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_unit_condition_string);
CONFIG_PARSER_PROTOTYPE(config_parse_kill_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_notify_access);
CONFIG_PARSER_PROTOTYPE(config_parse_emergency_action);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_requires_mounts_for);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_mounts_for);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_archs);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_errno);

View File

@ -1691,8 +1691,10 @@ Manager* manager_free(Manager *m) {
unit_defaults_done(&m->defaults);
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
FOREACH_ARRAY(map, m->units_needing_mounts_for, _UNIT_MOUNT_DEPENDENCY_TYPE_MAX) {
assert(hashmap_isempty(*map));
hashmap_free(*map);
}
hashmap_free(m->uid_refs);
hashmap_free(m->gid_refs);
@ -4478,14 +4480,15 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons
va_end(ap);
}
Set* manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
Set* manager_get_units_needing_mounts_for(Manager *m, const char *path, UnitMountDependencyType t) {
assert(m);
assert(path);
assert(t >= 0 && t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX);
if (path_equal(path, "/"))
path = "";
return hashmap_get(m->units_requiring_mounts_for, path);
return hashmap_get(m->units_needing_mounts_for[t], path);
}
int manager_update_failed_units(Manager *m, Unit *u, bool failed) {

View File

@ -137,6 +137,7 @@ typedef enum WatchdogType {
#include "path-lookup.h"
#include "show-status.h"
#include "unit-name.h"
#include "unit.h"
typedef enum ManagerTestRunFlags {
MANAGER_TEST_NORMAL = 0, /* run normally */
@ -438,10 +439,9 @@ struct Manager {
/* This is true before and after switching root. */
bool switching_root;
/* This maps all possible path prefixes to the units needing
* them. It's a hashmap with a path string as key and a Set as
* value where Unit objects are contained. */
Hashmap *units_requiring_mounts_for;
/* These map all possible path prefixes to the units needing them. They are hashmaps with a path
* string as key, and a Set as value where Unit objects are contained. */
Hashmap *units_needing_mounts_for[_UNIT_MOUNT_DEPENDENCY_TYPE_MAX];
/* Used for processing polkit authorization responses */
Hashmap *polkit_registry;
@ -596,7 +596,7 @@ double manager_get_progress(Manager *m);
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
Set* manager_get_units_needing_mounts_for(Manager *m, const char *path, UnitMountDependencyType t);
ManagerState manager_state(Manager *m);

View File

@ -281,8 +281,6 @@ static int update_parameters_proc_self_mountinfo(
static int mount_add_mount_dependencies(Mount *m) {
MountParameters *pm;
Unit *other;
Set *s;
int r;
assert(m);
@ -296,7 +294,7 @@ static int mount_add_mount_dependencies(Mount *m) {
if (r < 0)
return r;
r = unit_require_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT);
r = unit_add_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}
@ -308,30 +306,43 @@ static int mount_add_mount_dependencies(Mount *m) {
path_is_absolute(pm->what) &&
(mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) {
r = unit_require_mounts_for(UNIT(m), pm->what, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(UNIT(m), pm->what, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}
/* Adds in dependencies to other units that use this path or paths further down in the hierarchy */
s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where);
SET_FOREACH(other, s) {
for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; ++t) {
Unit *other;
Set *s = manager_get_units_needing_mounts_for(UNIT(m)->manager, m->where, t);
if (other->load_state != UNIT_LOADED)
continue;
SET_FOREACH(other, s) {
if (other->load_state != UNIT_LOADED)
continue;
if (other == UNIT(m))
continue;
if (other == UNIT(m))
continue;
r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true, UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
if (UNIT(m)->fragment_path) {
/* If we have fragment configuration, then make this dependency required */
r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true, UNIT_DEPENDENCY_PATH);
r = unit_add_dependency(
other,
UNIT_AFTER,
UNIT(m),
/* add_reference= */ true,
UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
if (UNIT(m)->fragment_path) {
/* If we have fragment configuration, then make this dependency required/wanted */
r = unit_add_dependency(
other,
unit_mount_dependency_type_to_dependency_type(t),
UNIT(m),
/* add_reference= */ true,
UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
}
}
}

View File

@ -309,7 +309,7 @@ static int path_add_mount_dependencies(Path *p) {
assert(p);
LIST_FOREACH(spec, s, p->specs) {
r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}

View File

@ -221,7 +221,7 @@ static int socket_add_mount_dependencies(Socket *s) {
if (!path)
continue;
r = unit_require_mounts_for(UNIT(s), path, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(UNIT(s), path, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}

View File

@ -321,7 +321,7 @@ static int swap_add_extras(Swap *s) {
return r;
}
r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT);
r = unit_add_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;

View File

@ -141,7 +141,7 @@ static int timer_setup_persistent(Timer *t) {
if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;

View File

@ -831,21 +831,26 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
}
}
if (!hashmap_isempty(u->requires_mounts_for)) {
UnitDependencyInfo di;
const char *path;
for (UnitMountDependencyType type = 0; type < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; type++)
if (!hashmap_isempty(u->mounts_for[type])) {
UnitDependencyInfo di;
const char *path;
HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) {
bool space = false;
HASHMAP_FOREACH_KEY(di.data, path, u->mounts_for[type]) {
bool space = false;
fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path);
fprintf(f,
"%s\t%s: %s (",
prefix,
unit_mount_dependency_type_to_string(type),
path);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
fputs(")\n", f);
}
}
}
if (u->load_state == UNIT_LOADED) {

View File

@ -689,38 +689,39 @@ static void unit_remove_transient(Unit *u) {
}
}
static void unit_free_requires_mounts_for(Unit *u) {
static void unit_free_mounts_for(Unit *u) {
assert(u);
for (;;) {
_cleanup_free_ char *path = NULL;
for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; ++t) {
for (;;) {
_cleanup_free_ char *path = NULL;
path = hashmap_steal_first_key(u->mounts_for[t]);
if (!path)
break;
path = hashmap_steal_first_key(u->requires_mounts_for);
if (!path)
break;
else {
char s[strlen(path) + 1];
PATH_FOREACH_PREFIX_MORE(s, path) {
char *y;
Set *x;
x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y);
x = hashmap_get2(u->manager->units_needing_mounts_for[t], s, (void**) &y);
if (!x)
continue;
(void) set_remove(x, u);
if (set_isempty(x)) {
(void) hashmap_remove(u->manager->units_requiring_mounts_for, y);
assert_se(hashmap_remove(u->manager->units_needing_mounts_for[t], y));
free(y);
set_free(x);
}
}
}
}
u->requires_mounts_for = hashmap_free(u->requires_mounts_for);
u->mounts_for[t] = hashmap_free(u->mounts_for[t]);
}
}
static void unit_done(Unit *u) {
@ -769,7 +770,7 @@ Unit* unit_free(Unit *u) {
u->deserialized_refs = strv_free(u->deserialized_refs);
u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
unit_free_requires_mounts_for(u);
unit_free_mounts_for(u);
SET_FOREACH(t, u->aliases)
hashmap_remove_value(u->manager->units, t, u);
@ -1277,20 +1278,24 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
/* Unlike unit_add_dependency() or friends, this always returns 0 on success. */
if (c->working_directory && !c->working_directory_missing_ok) {
r = unit_require_mounts_for(u, c->working_directory, UNIT_DEPENDENCY_FILE);
if (c->working_directory) {
r = unit_add_mounts_for(
u,
c->working_directory,
UNIT_DEPENDENCY_FILE,
c->working_directory_missing_ok ? UNIT_MOUNT_WANTS : UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}
if (c->root_directory) {
r = unit_require_mounts_for(u, c->root_directory, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, c->root_directory, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}
if (c->root_image) {
r = unit_require_mounts_for(u, c->root_image, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, c->root_image, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}
@ -1306,7 +1311,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
if (!p)
return -ENOMEM;
r = unit_require_mounts_for(u, p, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, p, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
if (r < 0)
return r;
}
@ -1326,16 +1331,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
}
if (c->private_tmp) {
/* FIXME: for now we make a special case for /tmp and add a weak dependency on
* tmp.mount so /tmp being masked is supported. However there's no reason to treat
* /tmp specifically and masking other mount units should be handled more
* gracefully too, see PR#16894. */
r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, "tmp.mount", true, UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, "/tmp", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
if (r < 0)
return r;
r = unit_require_mounts_for(u, "/var/tmp", UNIT_DEPENDENCY_FILE);
r = unit_add_mounts_for(u, "/var/tmp", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
if (r < 0)
return r;
@ -1536,51 +1536,72 @@ static int unit_add_slice_dependencies(Unit *u) {
}
static int unit_add_mount_dependencies(Unit *u) {
UnitDependencyInfo di;
const char *path;
bool changed = false;
int r;
assert(u);
HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) {
char prefix[strlen(path) + 1];
for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; ++t) {
UnitDependencyInfo di;
const char *path;
PATH_FOREACH_PREFIX_MORE(prefix, path) {
_cleanup_free_ char *p = NULL;
Unit *m;
HASHMAP_FOREACH_KEY(di.data, path, u->mounts_for[t]) {
r = unit_name_from_path(prefix, ".mount", &p);
if (r == -EINVAL)
continue; /* If the path cannot be converted to a mount unit name, then it's
* not manageable as a unit by systemd, and hence we don't need a
* dependency on it. Let's thus silently ignore the issue. */
if (r < 0)
return r;
char prefix[strlen(ASSERT_PTR(path)) + 1];
m = manager_get_unit(u->manager, p);
if (!m) {
/* Make sure to load the mount unit if it exists. If so the dependencies on
* this unit will be added later during the loading of the mount unit. */
(void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m);
continue;
}
if (m == u)
continue;
PATH_FOREACH_PREFIX_MORE(prefix, path) {
_cleanup_free_ char *p = NULL;
Unit *m;
if (m->load_state != UNIT_LOADED)
continue;
r = unit_name_from_path(prefix, ".mount", &p);
if (r == -EINVAL)
continue; /* If the path cannot be converted to a mount unit name,
* then it's not manageable as a unit by systemd, and
* hence we don't need a dependency on it. Let's thus
* silently ignore the issue. */
if (r < 0)
return r;
r = unit_add_dependency(u, UNIT_AFTER, m, true, di.origin_mask);
if (r < 0)
return r;
changed = changed || r > 0;
m = manager_get_unit(u->manager, p);
if (!m) {
/* Make sure to load the mount unit if it exists. If so the
* dependencies on this unit will be added later during the loading
* of the mount unit. */
(void) manager_load_unit_prepare(
u->manager,
p,
/* path= */NULL,
/* e= */NULL,
&m);
continue;
}
if (m == u)
continue;
if (m->fragment_path) {
r = unit_add_dependency(u, UNIT_REQUIRES, m, true, di.origin_mask);
if (m->load_state != UNIT_LOADED)
continue;
r = unit_add_dependency(
u,
UNIT_AFTER,
m,
/* add_reference= */ true,
di.origin_mask);
if (r < 0)
return r;
changed = changed || r > 0;
if (m->fragment_path) {
r = unit_add_dependency(
u,
unit_mount_dependency_type_to_dependency_type(t),
m,
/* add_reference= */ true,
di.origin_mask);
if (r < 0)
return r;
changed = changed || r > 0;
}
}
}
}
@ -4942,11 +4963,16 @@ int unit_kill_context(
return wait_for_exit;
}
int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) {
int unit_add_mounts_for(Unit *u, const char *path, UnitDependencyMask mask, UnitMountDependencyType type) {
Hashmap **unit_map, **manager_map;
int r;
assert(u);
assert(path);
assert(type >= 0 && type < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX);
unit_map = &u->mounts_for[type];
manager_map = &u->manager->units_needing_mounts_for[type];
/* Registers a unit for requiring a certain path and all its prefixes. We keep a hashtable of these
* paths in the unit (from the path to the UnitDependencyInfo structure indicating how to the
@ -4956,7 +4982,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
if (!path_is_absolute(path))
return -EINVAL;
if (hashmap_contains(u->requires_mounts_for, path)) /* Exit quickly if the path is already covered. */
if (hashmap_contains(*unit_map, path)) /* Exit quickly if the path is already covered. */
return 0;
/* Use the canonical form of the path as the stored key. We call path_is_normalized()
@ -4975,7 +5001,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
.origin_mask = mask
};
r = hashmap_ensure_put(&u->requires_mounts_for, &path_hash_ops, p, di.data);
r = hashmap_ensure_put(unit_map, &path_hash_ops, p, di.data);
if (r < 0)
return r;
assert(r > 0);
@ -4985,11 +5011,11 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
PATH_FOREACH_PREFIX_MORE(prefix, path) {
Set *x;
x = hashmap_get(u->manager->units_requiring_mounts_for, prefix);
x = hashmap_get(*manager_map, prefix);
if (!x) {
_cleanup_free_ char *q = NULL;
r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &path_hash_ops);
r = hashmap_ensure_allocated(manager_map, &path_hash_ops);
if (r < 0)
return r;
@ -5001,7 +5027,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
if (!x)
return -ENOMEM;
r = hashmap_put(u->manager->units_requiring_mounts_for, q, x);
r = hashmap_put(*manager_map, q, x);
if (r < 0) {
set_free(x);
return r;
@ -6615,3 +6641,24 @@ int activation_details_append_pair(ActivationDetails *details, char ***strv) {
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(ActivationDetails, activation_details, activation_details_free);
static const char* const unit_mount_dependency_type_table[_UNIT_MOUNT_DEPENDENCY_TYPE_MAX] = {
[UNIT_MOUNT_WANTS] = "WantsMountsFor",
[UNIT_MOUNT_REQUIRES] = "RequiresMountsFor",
};
DEFINE_STRING_TABLE_LOOKUP(unit_mount_dependency_type, UnitMountDependencyType);
UnitDependency unit_mount_dependency_type_to_dependency_type(UnitMountDependencyType t) {
switch (t) {
case UNIT_MOUNT_WANTS:
return UNIT_WANTS;
case UNIT_MOUNT_REQUIRES:
return UNIT_REQUIRES;
default:
assert_not_reached();
}
}

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/socket.h>
@ -8,6 +9,14 @@
#include "sd-id128.h"
/* Circular dependency with manager.h, needs to be defined before local includes */
typedef enum UnitMountDependencyType {
UNIT_MOUNT_WANTS,
UNIT_MOUNT_REQUIRES,
_UNIT_MOUNT_DEPENDENCY_TYPE_MAX,
_UNIT_MOUNT_DEPENDENCY_TYPE_INVALID = -EINVAL,
} UnitMountDependencyType;
#include "bpf-program.h"
#include "cgroup.h"
#include "condition.h"
@ -216,9 +225,9 @@ typedef struct Unit {
* Hashmap(UnitDependency Hashmap(Unit* UnitDependencyInfo)) */
Hashmap *dependencies;
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the
* UnitDependencyInfo type */
Hashmap *requires_mounts_for;
/* Similar, for RequiresMountsFor= and WantsMountsFor= path dependencies. The key is the path, the
* value the UnitDependencyInfo type */
Hashmap *mounts_for[_UNIT_MOUNT_DEPENDENCY_TYPE_MAX];
char *description;
char **documentation;
@ -1001,7 +1010,7 @@ int unit_kill_context(Unit *u, KillContext *c, KillOperation k, PidRef *main_pid
int unit_make_transient(Unit *u);
int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask);
int unit_add_mounts_for(Unit *u, const char *path, UnitDependencyMask mask, UnitMountDependencyType type);
bool unit_type_supported(UnitType t);
@ -1101,6 +1110,10 @@ int unit_arm_timer(Unit *u, sd_event_source **source, bool relative, usec_t usec
int unit_compare_priority(Unit *a, Unit *b);
UnitMountDependencyType unit_mount_dependency_type_from_string(const char *s) _const_;
const char* unit_mount_dependency_type_to_string(UnitMountDependencyType t) _const_;
UnitDependency unit_mount_dependency_type_to_dependency_type(UnitMountDependencyType t) _pure_;
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full_errno_zerook(unit, level, error, ...) \

View File

@ -229,7 +229,8 @@ static int generate_device_umount(const char *name,
static int print_dependencies(FILE *f, const char* device_path, const char* timeout_value, bool canfail) {
int r;
assert(!canfail || timeout_value);
assert(f);
assert(device_path);
if (STR_IN_SET(device_path, "-", "none"))
/* None, nothing to do */
@ -263,11 +264,13 @@ static int print_dependencies(FILE *f, const char* device_path, const char* time
fprintf(f, "After=%1$s\n", unit);
if (canfail) {
fprintf(f, "Wants=%1$s\n", unit);
r = write_drop_in_format(arg_dest, unit, 90, "device-timeout",
"# Automatically generated by systemd-cryptsetup-generator \n\n"
"[Unit]\nJobRunningTimeoutSec=%s", timeout_value);
if (r < 0)
return log_error_errno(r, "Failed to write device drop-in: %m");
if (timeout_value) {
r = write_drop_in_format(arg_dest, unit, 90, "device-timeout",
"# Automatically generated by systemd-cryptsetup-generator \n\n"
"[Unit]\nJobRunningTimeoutSec=%s", timeout_value);
if (r < 0)
return log_error_errno(r, "Failed to write device drop-in: %m");
}
} else
fprintf(f, "Requires=%1$s\n", unit);
} else {
@ -276,7 +279,7 @@ static int print_dependencies(FILE *f, const char* device_path, const char* time
if (!escaped_path)
return log_oom();
fprintf(f, "RequiresMountsFor=%s\n", escaped_path);
fprintf(f, "%s=%s\n", canfail ? "WantsMountsFor" : "RequiresMountsFor", escaped_path);
}
return 0;
@ -486,7 +489,7 @@ static int create_disk(
if (key_file && !keydev) {
r = print_dependencies(f, key_file,
keyfile_timeout_value,
/* canfail= */ keyfile_can_timeout > 0);
/* canfail= */ keyfile_can_timeout > 0 || nofail);
if (r < 0)
return r;
}
@ -494,8 +497,8 @@ static int create_disk(
/* Check if a header option was specified */
if (detached_header > 0 && !headerdev) {
r = print_dependencies(f, header_path,
NULL,
/* canfail= */ false); /* header is always necessary */
/* timeout_value= */ NULL,
/* canfail= */ nofail);
if (r < 0)
return r;
}

View File

@ -416,15 +416,17 @@ static int write_before(FILE *f, const char *opts) {
"x-systemd.before\0", "Before=%1$s\n");
}
static int write_requires_mounts_for(FILE *f, const char *opts) {
static int write_mounts_for(const char *x_opt, const char *unit_setting, FILE *f, const char *opts) {
_cleanup_strv_free_ char **paths = NULL, **paths_escaped = NULL;
_cleanup_free_ char *res = NULL;
int r;
assert(x_opt);
assert(unit_setting);
assert(f);
assert(opts);
r = fstab_filter_options(opts, "x-systemd.requires-mounts-for\0", NULL, NULL, &paths, NULL);
r = fstab_filter_options(opts, x_opt, NULL, NULL, &paths, NULL);
if (r < 0)
return log_warning_errno(r, "Failed to parse options: %m");
if (r == 0)
@ -438,7 +440,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) {
if (!res)
return log_oom();
fprintf(f, "RequiresMountsFor=%s\n", res);
fprintf(f, "%s=%s\n", unit_setting, res);
return 0;
}
@ -458,7 +460,10 @@ static int write_extra_dependencies(FILE *f, const char *opts) {
r = write_before(f, opts);
if (r < 0)
return r;
r = write_requires_mounts_for(f, opts);
r = write_mounts_for("x-systemd.requires-mounts-for\0", "RequiresMountsFor", f, opts);
if (r < 0)
return r;
r = write_mounts_for("x-systemd.wants-mounts-for\0", "WantsMountsFor", f, opts);
if (r < 0)
return r;
}

View File

@ -2643,6 +2643,7 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
if (unit_dependency_from_string(field) >= 0 ||
STR_IN_SET(field, "Documentation",
"RequiresMountsFor",
"WantsMountsFor",
"Markers"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);

View File

@ -276,6 +276,7 @@ User=
WakeSystem=
WantedBy=
Wants=
WantsMountsFor=
WatchdogSec=
What=
Where=

View File

@ -12,4 +12,4 @@ After=sysinit.target sockets.target paths.target slices.target tmp.mount
# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as
# we support that unit being masked, and this should not be considered an error.
RequiresMountsFor=/var /var/tmp
Wants=tmp.mount
WantsMountsFor=/tmp

View File

@ -35,6 +35,7 @@ trap at_exit EXIT
cryptsetup_start_and_check() {
local expect_fail=0
local umount_header_and_key=0
local ec volume unit
if [[ "${1:?}" == "-f" ]]; then
@ -42,6 +43,11 @@ cryptsetup_start_and_check() {
shift
fi
if [[ "${1:?}" == "-u" ]]; then
umount_header_and_key=1
shift
fi
for volume in "$@"; do
unit="systemd-cryptsetup@$volume.service"
@ -64,6 +70,12 @@ cryptsetup_start_and_check() {
return 1
fi
if [[ "$umount_header_and_key" -ne 0 ]]; then
umount "$TMPFS_DETACHED_KEYFILE"
umount "$TMPFS_DETACHED_HEADER"
udevadm settle --timeout=30
fi
systemctl status "$unit"
test -e "/dev/mapper/$volume"
systemctl stop "$unit"
@ -148,6 +160,15 @@ mkfs.ext4 -L keyfile_store "/dev/disk/by-partlabel/keyfile_store"
mount "/dev/disk/by-partlabel/keyfile_store" /mnt
cp "$IMAGE_DETACHED_KEYFILE2" /mnt/keyfile
umount /mnt
# Also copy the key and header on a tmpfs that we will umount after unlocking
TMPFS_DETACHED_KEYFILE="$(mktemp -d)"
TMPFS_DETACHED_HEADER="$(mktemp -d)"
mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_KEYFILE"
mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_HEADER"
cp "$IMAGE_DETACHED_KEYFILE" "$TMPFS_DETACHED_KEYFILE/keyfile"
cp "$IMAGE_DETACHED_HEADER" "$TMPFS_DETACHED_HEADER/header"
udevadm settle --timeout=30
# Prepare our test crypttab
@ -177,6 +198,7 @@ detached_fail4 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,
detached_slot0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER
detached_slot1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=8
detached_slot_fail $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=0
detached_nofail $IMAGE_DETACHED $TMPFS_DETACHED_KEYFILE/keyfile headless=1,header=$TMPFS_DETACHED_HEADER/header,keyfile-offset=32,keyfile-size=16,nofail
EOF
# Temporarily drop luks.name=/luks.uuid= from the kernel command line, as it makes
@ -212,5 +234,6 @@ cryptsetup_start_and_check detached_store{0..2}
cryptsetup_start_and_check -f detached_fail{0..4}
cryptsetup_start_and_check detached_slot{0..1}
cryptsetup_start_and_check -f detached_slot_fail
cryptsetup_start_and_check -u detached_nofail
touch /testok