diff options
-rw-r--r-- | man/org.freedesktop.systemd1.xml | 10 | ||||
-rw-r--r-- | man/systemd.mount.xml | 16 | ||||
-rw-r--r-- | src/core/dbus-mount.c | 25 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.in | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 45 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/mount.c | 44 | ||||
-rw-r--r-- | src/core/mount.h | 2 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 19 | ||||
-rwxr-xr-x | test/units/TEST-74-AUX-UTILS.mount.sh | 7 |
10 files changed, 161 insertions, 9 deletions
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 1fc925fe4a..0664d026f1 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -7050,6 +7050,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { readonly s Result = '...'; readonly u UID = ...; readonly u GID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as GracefulOptions = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") readonly a(sasbttttuii) ExecMount = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") @@ -7654,6 +7656,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { <!--property GID is not documented!--> + <!--property GracefulOptions is not documented!--> + <!--property ExecUnmount is not documented!--> <!--property ExecRemount is not documented!--> @@ -8200,6 +8204,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { <variablelist class="dbus-property" generated="True" extra-ref="GID"/> + <variablelist class="dbus-property" generated="True" extra-ref="GracefulOptions"/> + <variablelist class="dbus-property" generated="True" extra-ref="ExecMount"/> <variablelist class="dbus-property" generated="True" extra-ref="ExecUnmount"/> @@ -12455,7 +12461,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ <varname>ManagedOOMMemoryPressureDurationUSec</varname>, <varname>ProtectControlGroupsEx</varname>, and <varname>PrivatePIDs</varname> were added in version 257.</para> - <para><varname>ProtectHostnameEx</varname> and <function>RemoveSubgroup()</function> was added in version 258.</para> + <para><varname>ProtectHostnameEx</varname>, + <function>RemoveSubgroup()</function>, and + <varname>GracefulOptions</varname> were added in version 258.</para> </refsect2> <refsect2> <title>Swap Unit Objects</title> diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 3043e90499..991c1f8506 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -650,6 +650,22 @@ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. </para></listitem> </varlistentry> + + <varlistentry> + <term><varname>GracefulOptions=</varname></term> + + <listitem><para>Additional mount options that shall be appended to <varname>Options=</varname> if + supported by the kernel. This may be used to configure mount options that are optional and only + enabled on kernels that support them. Note that this is supported only for native kernel mount + options (i.e. explicitly not for mount options implemented in userspace, such as those processed by + <command>/usr/bin/mount</command> itself, by FUSE or by mount helpers such as + <command>mount.nfs</command>).</para> + + <para>May be specified multiple times. If specified multiple times, all listed, supported mount + options are combined. If an empty string is assigned, the list is reset.</para> + + <xi:include href="version-info.xml" xpointer="v258"/></listitem> + </varlistentry> </variablelist> <xi:include href="systemd.service.xml" xpointer="shared-unit-options" /> diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index d59aa06a11..855300d025 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -75,6 +75,7 @@ const sd_bus_vtable bus_mount_vtable[] = { SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("GracefulOptions", "as", NULL, offsetof(Mount, graceful_options), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), @@ -150,6 +151,30 @@ static int bus_mount_set_transient_property( if (streq(name, "ReadWriteOnly")) return bus_set_transient_bool(u, name, &m->read_write_only, message, flags, error); + if (streq(name, "GracefulOptions")) { + _cleanup_strv_free_ char **add = NULL; + r = sd_bus_message_read_strv(message, &add); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + + if (strv_isempty(add)) { + m->graceful_options = strv_free(m->graceful_options); + unit_write_settingf(u, flags, name, "GracefulOptions="); + } else { + r = strv_extend_strv(&m->graceful_options, add, /* filter_duplicates= */ false); + if (r < 0) + return r; + + STRV_FOREACH(a, add) + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "GracefulOptions=%s", *a); + } + } + + return 1; + } + return 0; } diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 04a560110e..1033d08103 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -548,6 +548,7 @@ Mount.SloppyOptions, config_parse_bool, Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount) Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount) Mount.ReadWriteOnly, config_parse_bool, 0, offsetof(Mount, read_write_only) +Mount.GracefulOptions, config_parse_mount_graceful_options, 0, offsetof(Mount, graceful_options) {{ EXEC_CONTEXT_CONFIG_ITEMS('Mount') }} {{ CGROUP_CONTEXT_CONFIG_ITEMS('Mount') }} {{ KILL_CONTEXT_CONFIG_ITEMS('Mount') }} diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index c8fff60108..fe60f27259 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -6123,6 +6123,51 @@ int config_parse_mount_node( return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, path, data, userdata); } +int config_parse_mount_graceful_options( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + const Unit *u = ASSERT_PTR(userdata); + char ***sv = ASSERT_PTR(data); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + *sv = strv_free(*sv); + return 1; + } + + _cleanup_free_ char *resolved = NULL; + r = unit_full_printf(u, rvalue, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); + return 0; + } + + _cleanup_strv_free_ char **strv = NULL; + + r = strv_split_full(&strv, resolved, ",", EXTRACT_RETAIN_ESCAPE|EXTRACT_UNESCAPE_SEPARATORS); + if (r < 0) + return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); + + r = strv_extend_strv_consume(sv, TAKE_PTR(strv), /* filter_duplicates = */ false); + if (r < 0) + return log_oom(); + + return 1; +} + static int merge_by_names(Unit *u, Set *names, const char *id) { char *k; int r; diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 881ce152d5..28275af8d8 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -165,6 +165,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_open_file); CONFIG_PARSER_PROTOTYPE(config_parse_memory_pressure_watch); CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set); CONFIG_PARSER_PROTOTYPE(config_parse_mount_node); +CONFIG_PARSER_PROTOTYPE(config_parse_mount_graceful_options); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/core/mount.c b/src/core/mount.c index 4b47092c1d..073e9c7193 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -231,6 +231,8 @@ static void mount_done(Unit *u) { mount_unwatch_control_pid(m); m->timer_event_source = sd_event_source_disable_unref(m->timer_event_source); + + m->graceful_options = strv_free(m->graceful_options); } static int update_parameters_proc_self_mountinfo( @@ -1079,6 +1081,44 @@ fail: mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false); } +static int mount_append_graceful_options(Mount *m, const MountParameters *p, char **opts) { + int r; + + assert(m); + assert(p); + assert(opts); + + if (strv_isempty(m->graceful_options)) + return 0; + + if (!p->fstype) { + log_unit_warning(UNIT(m), "GracefulOptions= used but file system type not known, suppressing all graceful options."); + return 0; + } + + STRV_FOREACH(o, m->graceful_options) { + _cleanup_free_ char *k = NULL, *v = NULL; + + r = split_pair(*o, "=", &k, &v); + if (r < 0 && r != -EINVAL) /* EINVAL → not a key/value pair */ + return r; + + r = mount_option_supported(p->fstype, k ?: *o, v); + if (r < 0) + log_unit_warning_errno(UNIT(m), r, "GracefulOptions=%s specified, but cannot determine availability, suppressing.", *o); + else if (r == 0) + log_unit_info(UNIT(m), "GracefulOptions=%s specified, but option is not available, suppressing.", *o); + else { + log_unit_debug(UNIT(m), "GracefulOptions=%s specified and supported, appending to mount option string.", *o); + + if (!strextend_with_separator(opts, ",", *o)) + return -ENOMEM; + } + } + + return 0; +} + static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParameters *p) { int r; @@ -1113,6 +1153,10 @@ static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParamete if (r < 0) return r; + r = mount_append_graceful_options(m, p, &opts); + if (r < 0) + return r; + if (!isempty(opts)) { r = exec_command_append(c, "-o", opts, NULL); if (r < 0) diff --git a/src/core/mount.h b/src/core/mount.h index 9a8b6bb4c0..28cc7785d8 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -88,6 +88,8 @@ struct Mount { sd_event_source *timer_event_source; unsigned n_retry_umount; + + char **graceful_options; }; extern const UnitVTable mount_vtable; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 1b6c6f0566..ed73950355 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -147,7 +147,7 @@ static int bus_append_string(sd_bus_message *m, const char *field, const char *e return 1; } -static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) { +static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, const char *separator, ExtractFlags flags) { const char *p; int r; @@ -170,7 +170,7 @@ static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, for (p = eq;;) { _cleanup_free_ char *word = NULL; - r = extract_first_word(&p, &word, NULL, flags); + r = extract_first_word(&p, &word, separator, flags); if (r == 0) break; if (r == -ENOMEM) @@ -613,12 +613,12 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons return bus_append_cg_blkio_weight_parse(m, field, eq); if (streq(field, "DisableControllers")) - return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE); + return bus_append_strv(m, "DisableControllers", eq, /* separator= */ NULL, EXTRACT_UNQUOTE); if (streq(field, "Delegate")) { r = parse_boolean(eq); if (r < 0) - return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE); + return bus_append_strv(m, "DelegateControllers", eq, /* separator= */ NULL, EXTRACT_UNQUOTE); r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r); if (r < 0) @@ -1116,7 +1116,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "ConfigurationDirectory", "SupplementaryGroups", "SystemCallArchitectures")) - return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); + return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE); if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) @@ -1175,7 +1175,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) - return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE); + return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE); if (streq(field, "EnvironmentFile")) { if (isempty(eq)) @@ -2333,6 +2333,9 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const "ReadwriteOnly")) return bus_append_parse_boolean(m, field, eq); + if (streq(field, "GracefulOptions")) + return bus_append_strv(m, field, eq, /* separator= */ ",", 0); + return 0; } @@ -2615,7 +2618,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons return bus_append_string(m, field, eq); if (streq(field, "Symlinks")) - return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); + return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE); if (streq(field, "SocketProtocol")) return bus_append_parse_ip_protocol(m, field, eq); @@ -2749,7 +2752,7 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const "RequiresMountsFor", "WantsMountsFor", "Markers")) - return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE); + return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE); t = condition_type_from_string(field); if (t >= 0) diff --git a/test/units/TEST-74-AUX-UTILS.mount.sh b/test/units/TEST-74-AUX-UTILS.mount.sh index 89a391d48e..0700b43063 100755 --- a/test/units/TEST-74-AUX-UTILS.mount.sh +++ b/test/units/TEST-74-AUX-UTILS.mount.sh @@ -185,3 +185,10 @@ systemctl status "$WORK_DIR/mnt" touch "$WORK_DIR/mnt/hello" [[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]] systemd-umount LABEL=owner-vfat + +# Mkae sure that graceful mount options work +GRACEFULTEST="/tmp/graceful/$RANDOM" +systemd-mount --tmpfs -p GracefulOptions=idefinitelydontexist,nr_inodes=4711,idonexisteither "$GRACEFULTEST" +findmnt -n -o options "$GRACEFULTEST" +findmnt -n -o options "$GRACEFULTEST" | grep -q nr_inodes=4711 +umount "$GRACEFULTEST" |