From 33d2e0eaf3b2f60529f4b10b348034becfdce526 Mon Sep 17 00:00:00 2001 From: Andreas Oberritter Date: Sat, 28 Jan 2017 21:49:24 +0100 Subject: systemd: backport transient mounts from 232 Signed-off-by: Andreas Oberritter --- ...-automount-implement-transient-automounts.patch | 98 ++ ...et_mount_parameters_fragment-consistently.patch | 114 ++ ...-drop-in-file-when-setting-transient-prop.patch | 29 + ...ify-loop-around-bus_append_unit_property_.patch | 136 ++ .../0030-run-various-minor-improvements.patch | 104 ++ ...ool-for-creating-transient-mount-and-auto.patch | 1572 ++++++++++++++++++++ meta/recipes-core/systemd/systemd_230.bb | 6 + 7 files changed, 2059 insertions(+) create mode 100644 meta/recipes-core/systemd/systemd/0026-automount-implement-transient-automounts.patch create mode 100644 meta/recipes-core/systemd/systemd/0027-mount-use-get_mount_parameters_fragment-consistently.patch create mode 100644 meta/recipes-core/systemd/systemd/0028-mount-write-drop-in-file-when-setting-transient-prop.patch create mode 100644 meta/recipes-core/systemd/systemd/0029-bus-util-unify-loop-around-bus_append_unit_property_.patch create mode 100644 meta/recipes-core/systemd/systemd/0030-run-various-minor-improvements.patch create mode 100644 meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch diff --git a/meta/recipes-core/systemd/systemd/0026-automount-implement-transient-automounts.patch b/meta/recipes-core/systemd/systemd/0026-automount-implement-transient-automounts.patch new file mode 100644 index 0000000000..11a8f075a2 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0026-automount-implement-transient-automounts.patch @@ -0,0 +1,98 @@ +From fb7177b2a03f7456c3ea5b2b2c3b51835eff70fd Mon Sep 17 00:00:00 2001 +From: Michael Olbrich +Date: Wed, 9 Dec 2015 15:46:46 +0100 +Subject: [PATCH] automount: implement transient automounts + +(cherry picked from commit afb14803b34a9f4abf5265365aebe9a3132344c2) +--- + src/core/automount.c | 3 +++ + src/core/dbus-automount.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ + src/core/dbus-automount.h | 2 ++ + 3 files changed, 59 insertions(+) + +diff --git a/src/core/automount.c b/src/core/automount.c +index a9d0d7a..b8d385d 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -1110,6 +1110,9 @@ const UnitVTable automount_vtable = { + .reset_failed = automount_reset_failed, + + .bus_vtable = bus_automount_vtable, ++ .bus_set_property = bus_automount_set_property, ++ ++ .can_transient = true, + + .shutdown = automount_shutdown, + .supported = automount_supported, +diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c +index b2806ad..26212b3 100644 +--- a/src/core/dbus-automount.c ++++ b/src/core/dbus-automount.c +@@ -32,3 +32,57 @@ const sd_bus_vtable bus_automount_vtable[] = { + SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END + }; ++ ++static int bus_automount_set_transient_property( ++ Automount *a, ++ const char *name, ++ sd_bus_message *message, ++ UnitSetPropertiesMode mode, ++ sd_bus_error *error) { ++ ++ int r; ++ ++ assert(a); ++ assert(name); ++ assert(message); ++ ++ if (streq(name, "TimeoutIdleUSec")) { ++ usec_t timeout_idle_usec; ++ r = sd_bus_message_read(message, "t", &timeout_idle_usec); ++ if (r < 0) ++ return r; ++ ++ if (mode != UNIT_CHECK) { ++ char time[FORMAT_TIMESPAN_MAX]; ++ ++ a->timeout_idle_usec = timeout_idle_usec; ++ unit_write_drop_in_format(UNIT(a), mode, name, "[Automount]\nTimeoutIdleSec=%s\n", ++ format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC)); ++ } ++ } else ++ return 0; ++ ++ return 1; ++} ++ ++int bus_automount_set_property( ++ Unit *u, ++ const char *name, ++ sd_bus_message *message, ++ UnitSetPropertiesMode mode, ++ sd_bus_error *error) { ++ ++ Automount *a = AUTOMOUNT(u); ++ int r = 0; ++ ++ assert(a); ++ assert(name); ++ assert(message); ++ ++ if (u->transient && u->load_state == UNIT_STUB) ++ /* This is a transient unit, let's load a little more */ ++ ++ r = bus_automount_set_transient_property(a, name, message, mode, error); ++ ++ return r; ++} +diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h +index 7b51eb9..f41adda 100644 +--- a/src/core/dbus-automount.h ++++ b/src/core/dbus-automount.h +@@ -21,3 +21,5 @@ + + + extern const sd_bus_vtable bus_automount_vtable[]; ++ ++int bus_automount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/meta/recipes-core/systemd/systemd/0027-mount-use-get_mount_parameters_fragment-consistently.patch b/meta/recipes-core/systemd/systemd/0027-mount-use-get_mount_parameters_fragment-consistently.patch new file mode 100644 index 0000000000..440e96ab46 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0027-mount-use-get_mount_parameters_fragment-consistently.patch @@ -0,0 +1,114 @@ +From 7f38735fa33f37ebe547b8849c1e5be8a1182693 Mon Sep 17 00:00:00 2001 +From: Michael Olbrich +Date: Wed, 9 Dec 2015 16:02:10 +0100 +Subject: [PATCH] mount: use get_mount_parameters_fragment() consistently + +There are multiple different checks, that all mean the same thing: +Is it a explicitly configured mount unit where actions need to be taken to +mount it, or is is just mirroring 'mountinfo': +'from_fragment' to set if fragment_path is not NULL, and +get_mount_parameters_fragment() just wraps that and returns fragment_path. + +Use get_mount_parameters_fragment() everywhere to be consistent. +This is just a cleanup without functional change. + +(cherry picked from commit b294b79fb0f9749afa53624c8f06b145ea2c1525) +--- + src/core/mount.c | 34 ++++++++++++++++------------------ + 1 file changed, 16 insertions(+), 18 deletions(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 665a60b..7b20892 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -482,6 +482,7 @@ static int mount_add_default_dependencies(Mount *m) { + + static int mount_verify(Mount *m) { + _cleanup_free_ char *e = NULL; ++ MountParameters *p; + int r; + + assert(m); +@@ -506,7 +507,8 @@ static int mount_verify(Mount *m) { + return -EINVAL; + } + +- if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { ++ p = get_mount_parameters_fragment(m); ++ if (p && !p->what) { + log_unit_error(UNIT(m), "What= setting is missing. Refusing."); + return -EBADMSG; + } +@@ -850,11 +852,6 @@ fail: + mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); + } + +-static int mount_get_opts(Mount *m, char **ret) { +- return fstab_filter_options(m->parameters_fragment.options, +- "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret); +-} +- + static void mount_enter_mounting(Mount *m) { + int r; + MountParameters *p; +@@ -877,19 +874,18 @@ static void mount_enter_mounting(Mount *m) { + if (p && mount_is_bind(p)) + (void) mkdir_p_label(p->what, m->directory_mode); + +- if (m->from_fragment) { ++ if (p) { + _cleanup_free_ char *opts = NULL; + +- r = mount_get_opts(m, &opts); ++ r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts); + if (r < 0) + goto fail; + +- r = exec_command_set(m->control_command, MOUNT_PATH, +- m->parameters_fragment.what, m->where, NULL); ++ r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL); + if (r >= 0 && m->sloppy_options) + r = exec_command_append(m->control_command, "-s", NULL); +- if (r >= 0 && m->parameters_fragment.fstype) +- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); ++ if (r >= 0 && p->fstype) ++ r = exec_command_append(m->control_command, "-t", p->fstype, NULL); + if (r >= 0 && !isempty(opts)) + r = exec_command_append(m->control_command, "-o", opts, NULL); + } else +@@ -915,27 +911,29 @@ fail: + + static void mount_enter_remounting(Mount *m) { + int r; ++ MountParameters *p; + + assert(m); + + m->control_command_id = MOUNT_EXEC_REMOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; + +- if (m->from_fragment) { ++ p = get_mount_parameters_fragment(m); ++ if (p) { + const char *o; + +- if (m->parameters_fragment.options) +- o = strjoina("remount,", m->parameters_fragment.options); ++ if (p->options) ++ o = strjoina("remount,", p->options); + else + o = "remount"; + + r = exec_command_set(m->control_command, MOUNT_PATH, +- m->parameters_fragment.what, m->where, ++ p->what, m->where, + "-o", o, NULL); + if (r >= 0 && m->sloppy_options) + r = exec_command_append(m->control_command, "-s", NULL); +- if (r >= 0 && m->parameters_fragment.fstype) +- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); ++ if (r >= 0 && p->fstype) ++ r = exec_command_append(m->control_command, "-t", p->fstype, NULL); + } else + r = -ENOENT; + diff --git a/meta/recipes-core/systemd/systemd/0028-mount-write-drop-in-file-when-setting-transient-prop.patch b/meta/recipes-core/systemd/systemd/0028-mount-write-drop-in-file-when-setting-transient-prop.patch new file mode 100644 index 0000000000..8580529060 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0028-mount-write-drop-in-file-when-setting-transient-prop.patch @@ -0,0 +1,29 @@ +From 26aed187f452644b833e061d28029b675ff32d04 Mon Sep 17 00:00:00 2001 +From: Michael Olbrich +Date: Wed, 9 Dec 2015 16:02:51 +0100 +Subject: [PATCH] mount: write drop-in file when setting transient properties + +This fixes transient mount units in general. With this change, +'from_fragment' is for transient mount units. As a result, the normal logic +for 'fragment' mount units vs. 'mountinfo' mount units works for transient +mount units as well. + +(cherry picked from commit 96f5957f1f0d98584bb463d73616a6755e02624b) +--- + src/core/dbus-mount.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c +index 935db7c..b4bbee0 100644 +--- a/src/core/dbus-mount.c ++++ b/src/core/dbus-mount.c +@@ -157,6 +157,9 @@ static int bus_mount_set_transient_property( + if (!p) + return -ENOMEM; + ++ unit_write_drop_in_format(UNIT(m), mode, name, "[Mount]\n%s=%s\n", ++ name, new_property); ++ + free(*property); + *property = p; + } diff --git a/meta/recipes-core/systemd/systemd/0029-bus-util-unify-loop-around-bus_append_unit_property_.patch b/meta/recipes-core/systemd/systemd/0029-bus-util-unify-loop-around-bus_append_unit_property_.patch new file mode 100644 index 0000000000..f4c03e7562 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0029-bus-util-unify-loop-around-bus_append_unit_property_.patch @@ -0,0 +1,136 @@ +From 76fdcb03aaa2fc8cd7483e628eef8a089afc1dfd Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Aug 2016 18:32:42 +0200 +Subject: [PATCH] bus-util: unify loop around + bus_append_unit_property_assignment() + +This is done exactly the same way a couple of times at various places, let's +unify this into one version. + +(cherry picked from commit 8673cf13c08998b50818346b703ad91fe5facfdf) +--- + src/nspawn/nspawn-register.c | 9 +++------ + src/run/run.c | 9 +++------ + src/shared/bus-unit-util.c | 15 +++++++++++++++ + src/shared/bus-unit-util.h | 1 + + src/systemctl/systemctl.c | 9 +++------ + 5 files changed, 25 insertions(+), 18 deletions(-) + +diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c +index 20103c5..3022184 100644 +--- a/src/nspawn/nspawn-register.c ++++ b/src/nspawn/nspawn-register.c +@@ -68,7 +68,6 @@ int register_machine( + local_ifindex > 0 ? 1 : 0, local_ifindex); + } else { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; +- char **i; + unsigned j; + + r = sd_bus_message_new_method_call( +@@ -164,11 +163,9 @@ int register_machine( + return bus_log_create_error(r); + } + +- STRV_FOREACH(i, properties) { +- r = bus_append_unit_property_assignment(m, *i); +- if (r < 0) +- return r; +- } ++ r = bus_append_unit_property_assignment_many(m, properties); ++ if (r < 0) ++ return r; + + r = sd_bus_message_close_container(m); + if (r < 0) +diff --git a/src/run/run.c b/src/run/run.c +index d6c9b6d..4b22cb7 100644 +--- a/src/run/run.c ++++ b/src/run/run.c +@@ -412,18 +412,15 @@ static int parse_argv(int argc, char *argv[]) { + } + + static int transient_unit_set_properties(sd_bus_message *m, char **properties) { +- char **i; + int r; + + r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); + if (r < 0) + return r; + +- STRV_FOREACH(i, properties) { +- r = bus_append_unit_property_assignment(m, *i); +- if (r < 0) +- return r; +- } ++ r = bus_append_unit_property_assignment_many(m, properties); ++ if (r < 0) ++ return r; + + return 0; + } +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index f6559cd..53cb293 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -542,6 +542,21 @@ finish: + return 0; + } + ++int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) { ++ char **i; ++ int r; ++ ++ assert(m); ++ ++ STRV_FOREACH(i, l) { ++ r = bus_append_unit_property_assignment(m, *i); ++ if (r < 0) ++ return r; ++ } ++ ++ return 0; ++} ++ + typedef struct BusWaitForJobs { + sd_bus *bus; + Set *jobs; +diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h +index c0c172f..d102ea1 100644 +--- a/src/shared/bus-unit-util.h ++++ b/src/shared/bus-unit-util.h +@@ -41,6 +41,7 @@ typedef struct UnitInfo { + int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); + + int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); ++int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l); + + typedef struct BusWaitForJobs BusWaitForJobs; + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index b943c68..4e2da68 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -4922,7 +4922,6 @@ static int set_property(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *n = NULL; + sd_bus *bus; +- char **i; + int r; + + polkit_agent_open_if_enabled(); +@@ -4953,11 +4952,9 @@ static int set_property(int argc, char *argv[], void *userdata) { + if (r < 0) + return bus_log_create_error(r); + +- STRV_FOREACH(i, strv_skip(argv, 2)) { +- r = bus_append_unit_property_assignment(m, *i); +- if (r < 0) +- return r; +- } ++ r = bus_append_unit_property_assignment_many(m, strv_skip(argv, 2)); ++ if (r < 0) ++ return r; + + r = sd_bus_message_close_container(m); + if (r < 0) diff --git a/meta/recipes-core/systemd/systemd/0030-run-various-minor-improvements.patch b/meta/recipes-core/systemd/systemd/0030-run-various-minor-improvements.patch new file mode 100644 index 0000000000..297bc2f708 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0030-run-various-minor-improvements.patch @@ -0,0 +1,104 @@ +From 8fd26d09a3ec01e09be6ba86b4c6390f6cff6165 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 6 Aug 2016 00:30:12 +0200 +Subject: [PATCH] run: various minor improvements + +Let's improve the --help text a bit, and other changes. + +(cherry picked from commit ad2c17019a2a25ace8d604b069124b361874139a) +--- + man/systemd-run.xml | 2 +- + src/run/run.c | 22 ++++++++++------------ + 2 files changed, 11 insertions(+), 13 deletions(-) + +diff --git a/man/systemd-run.xml b/man/systemd-run.xml +index 9c1a292..1a30c07 100644 +--- a/man/systemd-run.xml ++++ b/man/systemd-run.xml +@@ -45,7 +45,7 @@ + + + systemd-run +- Run programs in transient scope or service or timer units ++ Run programs in transient scope units, service units, or timer-scheduled service units + + + +diff --git a/src/run/run.c b/src/run/run.c +index 4b22cb7..f9a73f4 100644 +--- a/src/run/run.c ++++ b/src/run/run.c +@@ -83,9 +83,7 @@ static void polkit_agent_open_if_enabled(void) { + + static void help(void) { + printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" +- "Run the specified command in a transient scope or service or timer\n" +- "unit. If a timer option is specified and the unit specified with\n" +- "the --unit option exists, the command can be omitted.\n\n" ++ "Run the specified command in a transient scope or service.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" +@@ -94,7 +92,7 @@ static void help(void) { + " -M --machine=CONTAINER Operate on local container\n" + " --scope Run this as scope rather than service\n" + " --unit=UNIT Run under the specified unit name\n" +- " -p --property=NAME=VALUE Set unit property\n" ++ " -p --property=NAME=VALUE Set service or scope unit property\n" + " --description=TEXT Description for unit\n" + " --slice=SLICE Run in the specified slice\n" + " --no-block Do not wait until operation finished\n" +@@ -107,15 +105,15 @@ static void help(void) { + " -E --setenv=NAME=VALUE Set environment\n" + " -t --pty Run service on pseudo tty\n" + " -q --quiet Suppress information messages during runtime\n\n" +- "Timer options:\n\n" ++ "Timer options:\n" + " --on-active=SECONDS Run after SECONDS delay\n" + " --on-boot=SECONDS Run SECONDS after machine was booted up\n" + " --on-startup=SECONDS Run SECONDS after systemd activation\n" + " --on-unit-active=SECONDS Run SECONDS after the last activation\n" + " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n" + " --on-calendar=SPEC Realtime timer\n" +- " --timer-property=NAME=VALUE Set timer unit property\n", +- program_invocation_short_name); ++ " --timer-property=NAME=VALUE Set timer unit property\n" ++ , program_invocation_short_name); + } + + static bool with_timer(void) { +@@ -178,7 +176,7 @@ static int parse_argv(int argc, char *argv[]) { + { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, + { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, +- { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, ++ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + {}, + }; + +@@ -195,13 +193,13 @@ static int parse_argv(int argc, char *argv[]) { + help(); + return 0; + ++ case ARG_VERSION: ++ return version(); ++ + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + +- case ARG_VERSION: +- return version(); +- + case ARG_USER: + arg_user = true; + break; +@@ -1143,7 +1141,7 @@ static int start_transient_timer( + if (r < 0) + return bus_log_create_error(r); + +- if (argv[0]) { ++ if (!strv_isempty(argv)) { + r = sd_bus_message_open_container(m, 'r', "sa(sv)"); + if (r < 0) + return bus_log_create_error(r); diff --git a/meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch b/meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch new file mode 100644 index 0000000000..86e401b932 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch @@ -0,0 +1,1572 @@ +From 2e4f639b0f4aab11f2bbc173f26d90f7d75a5432 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 6 Aug 2016 00:32:46 +0200 +Subject: [PATCH] add a new tool for creating transient mount and automount + units +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds "systemd-mount" which is for transient mount and automount units what +"systemd-run" is for transient service, scope and timer units. + +The tool allows establishing mounts and automounts during runtime. It is very +similar to the usual /bin/mount commands, but can pull in additional +dependenices on access (for example, it pulls in fsck automatically), an take +benefit of the automount logic. + +This tool is particularly useful for mount removable file systems (such as USB +sticks), as the automount logic (together with automatic unmount-on-idle), as +well as automatic fsck on first access ensure that the removable file system +has a high chance to remain in a fully clean state even when it is unplugged +abruptly, and returns to a clean state on the next re-plug. + +This is a follow-up for #2471, as it adds a simple client-side for the +transient automount logic added in that PR. + +In later work it might make sense to invoke this tool automatically from udev +rules in order to implement a simpler and safer version of removable media +management á la udisks. + +(cherry picked from commit 450442cf93375af58161c1a9b973e3dfba60cba0) +--- + .gitignore | 1 + + Makefile-man.am | 2 + + Makefile.am | 8 + + man/standard-options.xml | 6 + + man/systemd-mount.xml | 295 ++++++++++++ + man/systemd-run.xml | 1 + + src/basic/mount-util.c | 1 - + src/mount/Makefile | 1 + + src/mount/mount-tool.c | 1112 ++++++++++++++++++++++++++++++++++++++++++++++ + 9 files changed, 1426 insertions(+), 1 deletion(-) + create mode 100644 man/systemd-mount.xml + create mode 120000 src/mount/Makefile + create mode 100644 src/mount/mount-tool.c + +diff --git a/.gitignore b/.gitignore +index 091b400..0afe09e 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -89,6 +89,7 @@ + /systemd-machine-id-setup + /systemd-machined + /systemd-modules-load ++/systemd-mount + /systemd-networkd + /systemd-networkd-wait-online + /systemd-notify +diff --git a/Makefile-man.am b/Makefile-man.am +index d5b328d..56a7384 100644 +--- a/Makefile-man.am ++++ b/Makefile-man.am +@@ -116,6 +116,7 @@ MANPAGES += \ + man/systemd-journald.service.8 \ + man/systemd-machine-id-commit.service.8 \ + man/systemd-machine-id-setup.1 \ ++ man/systemd-mount.1 \ + man/systemd-notify.1 \ + man/systemd-nspawn.1 \ + man/systemd-path.1 \ +@@ -2631,6 +2632,7 @@ EXTRA_DIST += \ + man/systemd-machine-id-setup.xml \ + man/systemd-machined.service.xml \ + man/systemd-modules-load.service.xml \ ++ man/systemd-mount.xml \ + man/systemd-networkd-wait-online.service.xml \ + man/systemd-networkd.service.xml \ + man/systemd-notify.xml \ +diff --git a/Makefile.am b/Makefile.am +index 305099a..fbdda0d 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -376,6 +376,7 @@ bin_PROGRAMS = \ + systemd-delta \ + systemd-analyze \ + systemd-run \ ++ systemd-mount \ + systemd-stdio-bridge \ + systemd-path + +@@ -3070,6 +3071,13 @@ systemd_run_LDADD = \ + libshared.la + + # ------------------------------------------------------------------------------ ++systemd_mount_SOURCES = \ ++ src/mount/mount-tool.c ++ ++systemd_mount_LDADD = \ ++ libshared.la ++ ++# ------------------------------------------------------------------------------ + systemd_stdio_bridge_SOURCES = \ + src/stdio-bridge/stdio-bridge.c + +diff --git a/man/standard-options.xml b/man/standard-options.xml +index f214463..f718451 100644 +--- a/man/standard-options.xml ++++ b/man/standard-options.xml +@@ -28,6 +28,12 @@ + + + ++ ++ ++ ++ Do not query the user for authentication for privileged operations. ++ ++ + + + +diff --git a/man/systemd-mount.xml b/man/systemd-mount.xml +new file mode 100644 +index 0000000..e6c0786 +--- /dev/null ++++ b/man/systemd-mount.xml +@@ -0,0 +1,295 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ systemd-mount ++ systemd ++ ++ ++ ++ Developer ++ Lennart ++ Poettering ++ lennart@poettering.net ++ ++ ++ ++ ++ ++ systemd-mount ++ 1 ++ ++ ++ ++ systemd-mount ++ Establish a mount or auto-mount point transiently ++ ++ ++ ++ ++ systemd-mount ++ OPTIONS ++ WHAT ++ WHERE ++ ++ ++ systemd-mount ++ OPTIONS ++ ++ ++ ++ ++ ++ Description ++ ++ systemd-mount may be used to create and start a transient .mount or ++ .automount unit of the file system WHAT on the mount point ++ WHERE. ++ ++ In many ways, systemd-mount is similar to the lower-level ++ mount8 command, however instead ++ of executing the mount operation directly and immediately, systemd-mount schedules it through ++ the service manager job queue, so that it may pull in further dependencies (such as parent mounts, or a file system ++ checker to execute a priori), and may make use of the auto-mounting logic. ++ ++ The command takes either one or two arguments. If only one argument is specified it should refer to a block ++ device containing a file system (e.g. /dev/sdb1), which is then probed for a label and other ++ metadata, and is mounted to a directory whose name is generated from the label. In this mode the block device must ++ exist at the time of invocation of the command, so that it may be probed. If the device is found to be a removable ++ block device (e.g. a USB stick) an automount point instead of a regular mount point is created (i.e. the ++ option is implied, see below). ++ ++ If two arguments are specified the first indicates the mount source (the WHAT) and ++ the second indicates the path to mount it on (the WHERE). In this mode no probing of the ++ source is attempted, and a backing device node doesn't have to exist yet. However, if this mode is combined with ++ , device node probing for additional metadata is enabled, and – much like in the ++ single-argument case discussed above – the specified device has to exist at the time of invocation of the ++ command. ++ ++ Use the command to show a terse table of all local, known block devices with file ++ systems that may be mounted with this command. ++ ++ ++ ++ Options ++ ++ The following options are understood: ++ ++ ++ ++ ++ ++ ++ ++ Do not synchronously wait for the requested operation to finish. If this is not specified, the job will ++ be verified, enqueued and systemd-mount will wait until the mount or automount unit's ++ start-up is completed. By passing this argument, it is only verified and enqueued. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Suppresses additional informational output while running. ++ ++ ++ ++ ++ ++ Enable probing of the mount source. This switch is implied if a single argument is specified on ++ the command line. If passed, additional metadata is read from the device to enhance the unit to create. For ++ example, a descriptive string for the transient units is generated from the file system label and device ++ model. Moreover if a removable block device (e.g. USB stick) is detected an automount unit instead of a regular ++ mount unit is created, with a short idle time-out, in order to ensure the file-system is placed in a clean ++ state quickly after each access. ++ ++ ++ ++ ++ ++ ++ Specifies the file system type to mount (e.g. vfat, ext4, ++ …). If omitted (or set to auto) the file system is determined automatically. ++ ++ ++ ++ ++ ++ ++ Additional mount options for the mount point. ++ ++ ++ ++ ++ ++ Takes a boolean argument, defaults to on. Controls whether to run a file system check ++ immediately before the mount operation. In the automount case (see below) the ++ check will be run the moment the first access to the device is made, which might slightly delay the ++ access. ++ ++ ++ ++ ++ ++ Provide a description for the mount or automount unit. See Description= in ++ systemd.unit5. ++ ++ ++ ++ ++ ++ ++ ++ Sets a unit property for the mount unit that is created. This takes an assignment in the same ++ format as systemctl1's ++ set-property command. ++ ++ ++ ++ ++ ++ ++ Takes a boolean argument. Controls whether to create an automount point or a regular mount ++ point. If true an automount point is created that is backed by the actual file system at the time of first ++ access. If false a plain mount point is created that is backed by the actual file system immediately. Automount ++ points have the benefit that the file system stays unmounted and hence in clean state until it is first ++ accessed. In automount mode the switch (see below) may be used to ensure ++ the mount point is unmounted automatically after the last access and an idle period passed. ++ ++ If this switch is not specified it defaults to false. If not specified and is ++ used (or only a single argument passed, which implies , see above), and the file ++ system block device is detected to be removable, it is set to true, in order to increase the chance that the ++ file system is in a fully clean state if the device is unplugged abruptly. ++ ++ ++ ++ ++ ++ Equivalent to . ++ ++ ++ ++ ++ ++ Takes a time value that controls the idle timeout in automount mode. If set to ++ infinity (the default) no automatic unmounts are done. Otherwise the file system backing the ++ automount point is detached after the last access and the idle timeout passed. See ++ systemd.time7 for details on ++ the time syntax supported. This option has no effect if only a regular mount is established, and automounting ++ is not used. ++ ++ Note that if is used (or only a single argument passed, which implies ++ , see above), and the file system block device is detected to be removable, ++ is implied. ++ ++ ++ ++ ++ ++ Similar to , but applies additional properties to the automount ++ unit created, instead of the mount unit. ++ ++ ++ ++ ++ ++ Takes a boolean argument, defaults to off. This option only has an effect in automount mode, ++ and controls whether the automount unit shall be bound to the backing device's lifetime. If enabled, the ++ automount point will be removed automatically when the backing device vanishes. If disabled the automount point ++ stays around, and subsequent accesses will block until backing device is replugged. This option has no effect ++ in case of non-device mounts, such as network or virtual file system mounts. ++ ++ Note that if is used (or only a single argument passed, which implies ++ , see above), and the file system block device is detected to be removable, this ++ option is implied. ++ ++ ++ ++ ++ ++ Instead of establishing a mount or automount point, print a terse list of block devices ++ containing file systems that may be mounted with systemd-mount, along with useful metadata ++ such as labels, etc. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Exit status ++ ++ On success, 0 is returned, a non-zero failure ++ code otherwise. ++ ++ ++ ++ The udev Database ++ ++ If is used, systemd-mount honours a couple of additional udev ++ properties of block devices: ++ ++ ++ ++ SYSTEMD_MOUNT_OPTIONS= ++ ++ The mount options to use, if is not used. ++ ++ ++ ++ SYSTEMD_MOUNT_WHERE= ++ ++ The file system path to place the mount point at, instead of the automatically generated ++ one. ++ ++ ++ ++ ++ ++ See Also ++ ++ systemd1, ++ mount8, ++ systemctl1, ++ systemd.unit5, ++ systemd.mount5, ++ systemd.automount5, ++ systemd-run1 ++ ++ ++ ++ +diff --git a/man/systemd-run.xml b/man/systemd-run.xml +index 1a30c07..56d585c 100644 +--- a/man/systemd-run.xml ++++ b/man/systemd-run.xml +@@ -452,6 +452,7 @@ There is a screen on: + systemd.exec5, + systemd.resource-control5, + systemd.timer5, ++ systemd-mount1, + machinectl1 + + +diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c +index ba69895..6192743 100644 +--- a/src/basic/mount-util.c ++++ b/src/basic/mount-util.c +@@ -75,7 +75,6 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id + return safe_atoi(p, mnt_id); + } + +- + int fd_is_mount_point(int fd, const char *filename, int flags) { + union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT; + int mount_id = -1, mount_id_parent = -1; +diff --git a/src/mount/Makefile b/src/mount/Makefile +new file mode 120000 +index 0000000..d0b0e8e +--- /dev/null ++++ b/src/mount/Makefile +@@ -0,0 +1 @@ ++../Makefile +\ No newline at end of file +diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c +new file mode 100644 +index 0000000..9076ba3 +--- /dev/null ++++ b/src/mount/mount-tool.c +@@ -0,0 +1,1112 @@ ++/*** ++ This file is part of systemd. ++ ++ Copyright 2016 Lennart Poettering ++ ++ systemd is free software; you can redistribute it and/or modify it ++ under the terms of the GNU Lesser General Public License as published by ++ the Free Software Foundation; either version 2.1 of the License, or ++ (at your option) any later version. ++ ++ systemd is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with systemd; If not, see . ++***/ ++ ++#include ++ ++#include "libudev.h" ++#include "sd-bus.h" ++ ++#include "bus-error.h" ++#include "bus-unit-util.h" ++#include "bus-util.h" ++#include "escape.h" ++#include "fstab-util.h" ++#include "pager.h" ++#include "parse-util.h" ++#include "path-util.h" ++#include "spawn-polkit-agent.h" ++#include "strv.h" ++#include "udev-util.h" ++#include "unit-name.h" ++#include "terminal-util.h" ++ ++enum { ++ ACTION_DEFAULT, ++ ACTION_MOUNT, ++ ACTION_AUTOMOUNT, ++ ACTION_LIST, ++} arg_action = ACTION_DEFAULT; ++ ++static bool arg_no_block = false; ++static bool arg_no_pager = false; ++static bool arg_ask_password = true; ++static bool arg_quiet = false; ++static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; ++static bool arg_user = false; ++static const char *arg_host = NULL; ++static bool arg_discover = false; ++static char *arg_mount_what = NULL; ++static char *arg_mount_where = NULL; ++static char *arg_mount_type = NULL; ++static char *arg_mount_options = NULL; ++static char *arg_description = NULL; ++static char **arg_property = NULL; ++static usec_t arg_timeout_idle = USEC_INFINITY; ++static bool arg_timeout_idle_set = false; ++static char **arg_automount_property = NULL; ++static int arg_bind_device = -1; ++static bool arg_fsck = true; ++ ++static void polkit_agent_open_if_enabled(void) { ++ ++ /* Open the polkit agent as a child process if necessary */ ++ if (!arg_ask_password) ++ return; ++ ++ if (arg_transport != BUS_TRANSPORT_LOCAL) ++ return; ++ ++ polkit_agent_open(); ++} ++ ++static void help(void) { ++ printf("%s [OPTIONS...] WHAT [WHERE]\n\n" ++ "Establish a mount or auto-mount point transiently.\n\n" ++ " -h --help Show this help\n" ++ " --version Show package version\n" ++ " --no-block Do not wait until operation finished\n" ++ " --no-pager Do not pipe output into a pager\n" ++ " --no-ask-password Do not prompt for password\n" ++ " -q --quiet Suppress information messages during runtime\n" ++ " --user Run as user unit\n" ++ " -H --host=[USER@]HOST Operate on remote host\n" ++ " -M --machine=CONTAINER Operate on local container\n" ++ " --discover Discover mount device metadata\n" ++ " -t --type=TYPE File system type\n" ++ " -o --options=OPTIONS Mount options\n" ++ " --fsck=no Don't run file system check before mount\n" ++ " --description=TEXT Description for unit\n" ++ " -p --property=NAME=VALUE Set mount unit property\n" ++ " -A --automount=BOOL Create an auto-mount point\n" ++ " --timeout-idle-sec=SEC Specify automount idle timeout\n" ++ " --automount-property=NAME=VALUE\n" ++ " Set automount unit property\n" ++ " --bind-device Bind automount unit to device\n" ++ " --list List mountable block devices\n" ++ , program_invocation_short_name); ++} ++ ++static int parse_argv(int argc, char *argv[]) { ++ ++ enum { ++ ARG_VERSION = 0x100, ++ ARG_NO_BLOCK, ++ ARG_NO_PAGER, ++ ARG_NO_ASK_PASSWORD, ++ ARG_USER, ++ ARG_SYSTEM, ++ ARG_DISCOVER, ++ ARG_MOUNT_TYPE, ++ ARG_MOUNT_OPTIONS, ++ ARG_FSCK, ++ ARG_DESCRIPTION, ++ ARG_TIMEOUT_IDLE, ++ ARG_AUTOMOUNT, ++ ARG_AUTOMOUNT_PROPERTY, ++ ARG_BIND_DEVICE, ++ ARG_LIST, ++ }; ++ ++ static const struct option options[] = { ++ { "help", no_argument, NULL, 'h' }, ++ { "version", no_argument, NULL, ARG_VERSION }, ++ { "no-block", no_argument, NULL, ARG_NO_BLOCK }, ++ { "no-pager", no_argument, NULL, ARG_NO_PAGER }, ++ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, ++ { "quiet", no_argument, NULL, 'q' }, ++ { "user", no_argument, NULL, ARG_USER }, ++ { "system", no_argument, NULL, ARG_SYSTEM }, ++ { "host", required_argument, NULL, 'H' }, ++ { "machine", required_argument, NULL, 'M' }, ++ { "discover", no_argument, NULL, ARG_DISCOVER }, ++ { "type", required_argument, NULL, 't' }, ++ { "options", required_argument, NULL, 'o' }, ++ { "description", required_argument, NULL, ARG_DESCRIPTION }, ++ { "property", required_argument, NULL, 'p' }, ++ { "automount", required_argument, NULL, ARG_AUTOMOUNT }, ++ { "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE }, ++ { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY }, ++ { "bind-device", no_argument, NULL, ARG_BIND_DEVICE }, ++ { "list", no_argument, NULL, ARG_LIST }, ++ {}, ++ }; ++ ++ int r, c; ++ ++ assert(argc >= 0); ++ assert(argv); ++ ++ while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:A", options, NULL)) >= 0) ++ ++ switch (c) { ++ ++ case 'h': ++ help(); ++ return 0; ++ ++ case ARG_VERSION: ++ return version(); ++ ++ case ARG_NO_BLOCK: ++ arg_no_block = true; ++ break; ++ ++ case ARG_NO_PAGER: ++ arg_no_pager = true; ++ break; ++ ++ case ARG_NO_ASK_PASSWORD: ++ arg_ask_password = false; ++ break; ++ ++ case 'q': ++ arg_quiet = true; ++ break; ++ ++ case ARG_USER: ++ arg_user = true; ++ break; ++ ++ case ARG_SYSTEM: ++ arg_user = false; ++ break; ++ ++ case 'H': ++ arg_transport = BUS_TRANSPORT_REMOTE; ++ arg_host = optarg; ++ break; ++ ++ case 'M': ++ arg_transport = BUS_TRANSPORT_MACHINE; ++ arg_host = optarg; ++ break; ++ ++ case ARG_DISCOVER: ++ arg_discover = true; ++ break; ++ ++ case 't': ++ if (free_and_strdup(&arg_mount_type, optarg) < 0) ++ return log_oom(); ++ break; ++ ++ case 'o': ++ if (free_and_strdup(&arg_mount_options, optarg) < 0) ++ return log_oom(); ++ break; ++ ++ case ARG_FSCK: ++ r = parse_boolean(optarg); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse --fsck= argument: %s", optarg); ++ ++ arg_fsck = r; ++ break; ++ ++ case ARG_DESCRIPTION: ++ if (free_and_strdup(&arg_description, optarg) < 0) ++ return log_oom(); ++ break; ++ ++ case 'p': ++ if (strv_extend(&arg_property, optarg) < 0) ++ return log_oom(); ++ ++ break; ++ ++ case 'A': ++ arg_action = ACTION_AUTOMOUNT; ++ break; ++ ++ case ARG_AUTOMOUNT: ++ r = parse_boolean(optarg); ++ if (r < 0) ++ return log_error_errno(r, "--automount= expects a valid boolean parameter: %s", optarg); ++ ++ arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT; ++ break; ++ ++ case ARG_TIMEOUT_IDLE: ++ r = parse_sec(optarg, &arg_timeout_idle); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse timeout: %s", optarg); ++ ++ break; ++ ++ case ARG_AUTOMOUNT_PROPERTY: ++ if (strv_extend(&arg_automount_property, optarg) < 0) ++ return log_oom(); ++ ++ break; ++ ++ case ARG_BIND_DEVICE: ++ arg_bind_device = true; ++ break; ++ ++ case ARG_LIST: ++ arg_action = ACTION_LIST; ++ break; ++ ++ case '?': ++ return -EINVAL; ++ ++ default: ++ assert_not_reached("Unhandled option"); ++ } ++ ++ if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) { ++ log_error("Execution in user context is not supported on non-local systems."); ++ return -EINVAL; ++ } ++ ++ if (arg_action == ACTION_LIST) { ++ if (optind < argc) { ++ log_error("Too many arguments."); ++ return -EINVAL; ++ } ++ ++ if (arg_transport != BUS_TRANSPORT_LOCAL) { ++ log_error("Listing devices only supported locally."); ++ return -EOPNOTSUPP; ++ } ++ } else { ++ if (optind >= argc) { ++ log_error("At least one argument required."); ++ return -EINVAL; ++ } ++ ++ if (argc > optind+2) { ++ log_error("At most two arguments required."); ++ return -EINVAL; ++ } ++ ++ arg_mount_what = fstab_node_to_udev_node(argv[optind]); ++ if (!arg_mount_what) ++ return log_oom(); ++ ++ if (argc > optind+1) { ++ r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where); ++ if (r < 0) ++ return log_error_errno(r, "Failed to make path absolute: %m"); ++ } else ++ arg_discover = true; ++ ++ if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL) { ++ log_error("Automatic mount location discovery is only supported locally."); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ return 1; ++} ++ ++static int transient_unit_set_properties(sd_bus_message *m, char **properties) { ++ int r; ++ ++ r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); ++ if (r < 0) ++ return r; ++ ++ if (arg_bind_device && is_device_path(arg_mount_what)) { ++ _cleanup_free_ char *device_unit = NULL; ++ ++ r = unit_name_from_path(arg_mount_what, ".device", &device_unit); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_append(m, "(sv)(sv)", ++ "After", "as", 1, device_unit, ++ "BindsTo", "as", 1, device_unit); ++ if (r < 0) ++ return r; ++ } ++ ++ r = bus_append_unit_property_assignment_many(m, properties); ++ if (r < 0) ++ return r; ++ ++ return 0; ++} ++ ++static int transient_mount_set_properties(sd_bus_message *m) { ++ int r; ++ ++ assert(m); ++ ++ r = transient_unit_set_properties(m, arg_property); ++ if (r < 0) ++ return r; ++ ++ if (arg_mount_what) { ++ r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what); ++ if (r < 0) ++ return r; ++ } ++ ++ if (arg_mount_type) { ++ r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type); ++ if (r < 0) ++ return r; ++ } ++ ++ if (arg_mount_options) { ++ r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options); ++ if (r < 0) ++ return r; ++ } ++ ++ if (arg_fsck) { ++ _cleanup_free_ char *fsck = NULL; ++ ++ r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_append(m, ++ "(sv)(sv)", ++ "Requires", "as", 1, fsck, ++ "After", "as", 1, fsck); ++ if (r < 0) ++ return r; ++ } ++ ++ return 0; ++} ++ ++static int transient_automount_set_properties(sd_bus_message *m) { ++ int r; ++ ++ assert(m); ++ ++ r = transient_unit_set_properties(m, arg_automount_property); ++ if (r < 0) ++ return r; ++ ++ if (arg_timeout_idle != USEC_INFINITY) { ++ r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle); ++ if (r < 0) ++ return r; ++ } ++ ++ return 0; ++} ++ ++static int start_transient_mount( ++ sd_bus *bus, ++ char **argv) { ++ ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; ++ _cleanup_free_ char *mount_unit = NULL; ++ int r; ++ ++ if (!arg_no_block) { ++ r = bus_wait_for_jobs_new(bus, &w); ++ if (r < 0) ++ return log_error_errno(r, "Could not watch jobs: %m"); ++ } ++ ++ r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit); ++ if (r < 0) ++ return log_error_errno(r, "Failed to make mount unit name: %m"); ++ ++ r = sd_bus_message_new_method_call( ++ bus, ++ &m, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "StartTransientUnit"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ /* Name and mode */ ++ r = sd_bus_message_append(m, "ss", mount_unit, "fail"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ /* Properties */ ++ r = sd_bus_message_open_container(m, 'a', "(sv)"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = transient_mount_set_properties(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_close_container(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ /* Auxiliary units */ ++ r = sd_bus_message_append(m, "a(sa(sv))", 0); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ polkit_agent_open_if_enabled(); ++ ++ r = sd_bus_call(bus, m, 0, &error, &reply); ++ if (r < 0) ++ return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r)); ++ ++ if (w) { ++ const char *object; ++ ++ r = sd_bus_message_read(reply, "o", &object); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ r = bus_wait_for_jobs_one(w, object, arg_quiet); ++ if (r < 0) ++ return r; ++ } ++ ++ if (!arg_quiet) ++ log_info("Started unit %s%s%s for mount point: %s%s%s", ++ ansi_highlight(), mount_unit, ansi_normal(), ++ ansi_highlight(), arg_mount_where, ansi_normal()); ++ ++ return 0; ++} ++ ++static int start_transient_automount( ++ sd_bus *bus, ++ char **argv) { ++ ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; ++ _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL; ++ int r; ++ ++ if (!arg_no_block) { ++ r = bus_wait_for_jobs_new(bus, &w); ++ if (r < 0) ++ return log_error_errno(r, "Could not watch jobs: %m"); ++ } ++ ++ r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit); ++ if (r < 0) ++ return log_error_errno(r, "Failed to make automount unit name: %m"); ++ ++ r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit); ++ if (r < 0) ++ return log_error_errno(r, "Failed to make mount unit name: %m"); ++ ++ r = sd_bus_message_new_method_call( ++ bus, ++ &m, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "StartTransientUnit"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ /* Name and mode */ ++ r = sd_bus_message_append(m, "ss", automount_unit, "fail"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ /* Properties */ ++ r = sd_bus_message_open_container(m, 'a', "(sv)"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = transient_automount_set_properties(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_close_container(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ /* Auxiliary units */ ++ r = sd_bus_message_open_container(m, 'a', "(sa(sv))"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_open_container(m, 'r', "sa(sv)"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_append(m, "s", mount_unit); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_open_container(m, 'a', "(sv)"); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = transient_mount_set_properties(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_close_container(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_close_container(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_close_container(m); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ polkit_agent_open_if_enabled(); ++ ++ r = sd_bus_call(bus, m, 0, &error, &reply); ++ if (r < 0) ++ return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r)); ++ ++ if (w) { ++ const char *object; ++ ++ r = sd_bus_message_read(reply, "o", &object); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ r = bus_wait_for_jobs_one(w, object, arg_quiet); ++ if (r < 0) ++ return r; ++ } ++ ++ if (!arg_quiet) ++ log_info("Started unit %s%s%s for mount point: %s%s%s", ++ ansi_highlight(), automount_unit, ansi_normal(), ++ ansi_highlight(), arg_mount_where, ansi_normal()); ++ ++ return 0; ++} ++ ++static int acquire_mount_type(struct udev_device *d) { ++ const char *v; ++ ++ assert(d); ++ ++ if (arg_mount_type) ++ return 0; ++ ++ v = udev_device_get_property_value(d, "ID_FS_TYPE"); ++ if (isempty(v)) ++ return 0; ++ ++ arg_mount_type = strdup(v); ++ if (!arg_mount_type) ++ return log_oom(); ++ ++ log_debug("Discovered type=%s", arg_mount_type); ++ return 1; ++} ++ ++static int acquire_mount_options(struct udev_device *d) { ++ const char *v; ++ ++ if (arg_mount_options) ++ return 0; ++ ++ v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS"); ++ if (isempty(v)) ++ return 0; ++ ++ arg_mount_options = strdup(v); ++ if (!arg_mount_options) ++ return log_oom(); ++ ++ log_debug("Discovered options=%s", arg_mount_options); ++ return 1; ++} ++ ++static const char *get_model(struct udev_device *d) { ++ const char *model; ++ ++ assert(d); ++ ++ model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE"); ++ if (model) ++ return model; ++ ++ return udev_device_get_property_value(d, "ID_MODEL"); ++} ++ ++static const char* get_label(struct udev_device *d) { ++ const char *label; ++ ++ assert(d); ++ ++ label = udev_device_get_property_value(d, "ID_FS_LABEL"); ++ if (label) ++ return label; ++ ++ return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME"); ++} ++ ++static int acquire_mount_where(struct udev_device *d) { ++ const char *v; ++ ++ if (arg_mount_where) ++ return 0; ++ ++ v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE"); ++ if (isempty(v)) { ++ _cleanup_free_ char *escaped = NULL; ++ const char *name; ++ ++ name = get_label(d); ++ if (!name) ++ name = get_model(d); ++ if (!name) { ++ const char *dn; ++ ++ dn = udev_device_get_devnode(d); ++ if (!dn) ++ return 0; ++ ++ name = basename(dn); ++ } ++ ++ escaped = xescape(name, "\\"); ++ if (!filename_is_valid(escaped)) ++ return 0; ++ ++ arg_mount_where = strjoin("/run/media/system/", escaped, NULL); ++ } else ++ arg_mount_where = strdup(v); ++ ++ if (!arg_mount_where) ++ return log_oom(); ++ ++ log_debug("Discovered where=%s", arg_mount_where); ++ return 1; ++} ++ ++static int acquire_description(struct udev_device *d) { ++ const char *model, *label; ++ ++ if (arg_description) ++ return 0; ++ ++ model = get_model(d); ++ ++ label = get_label(d); ++ if (!label) ++ label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER"); ++ ++ if (model && label) ++ arg_description = strjoin(model, " ", label, NULL); ++ else if (label) ++ arg_description = strdup(label); ++ else if (model) ++ arg_description = strdup(model); ++ else ++ return NULL; ++ ++ if (!arg_description) ++ return log_oom(); ++ ++ log_debug("Discovered description=%s", arg_description); ++ return 1; ++} ++ ++static int acquire_removable(struct udev_device *d) { ++ const char *v; ++ ++ /* Shortcut this if there's no reason to check it */ ++ if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0) ++ return 0; ++ ++ for (;;) { ++ v = udev_device_get_sysattr_value(d, "removable"); ++ if (v) ++ break; ++ ++ d = udev_device_get_parent(d); ++ if (!d) ++ return 0; ++ ++ if (!streq_ptr(udev_device_get_subsystem(d), "block")) ++ return 0; ++ } ++ ++ if (parse_boolean(v) <= 0) ++ return 0; ++ ++ log_debug("Discovered removable device."); ++ ++ if (arg_action == ACTION_DEFAULT) { ++ log_debug("Automatically turning on automount."); ++ arg_action = ACTION_AUTOMOUNT; ++ } ++ ++ if (!arg_timeout_idle_set) { ++ log_debug("Setting idle timeout to 1s."); ++ arg_timeout_idle = USEC_PER_SEC; ++ } ++ ++ if (arg_bind_device < 0) { ++ log_debug("Binding automount unit to device."); ++ arg_bind_device = true; ++ } ++ ++ return 1; ++} ++ ++static int discover_device(void) { ++ _cleanup_udev_device_unref_ struct udev_device *d = NULL; ++ _cleanup_udev_unref_ struct udev *udev = NULL; ++ struct stat st; ++ const char *v; ++ int r; ++ ++ if (!arg_discover) ++ return 0; ++ ++ if (!is_device_path(arg_mount_what)) { ++ log_error("Discovery only supported for block devices, don't know what to do."); ++ return -EINVAL; ++ } ++ ++ if (stat(arg_mount_what, &st) < 0) ++ return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what); ++ ++ if (!S_ISBLK(st.st_mode)) { ++ log_error("Path %s is not a block device, don't know what to do.", arg_mount_what); ++ return -ENOTBLK; ++ } ++ ++ udev = udev_new(); ++ if (!udev) ++ return log_oom(); ++ ++ d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); ++ if (!d) ++ return log_oom(); ++ ++ v = udev_device_get_property_value(d, "ID_FS_USAGE"); ++ if (!streq_ptr(v, "filesystem")) { ++ log_error("%s does not contain a file system.", arg_mount_what); ++ return -EINVAL; ++ } ++ ++ r = acquire_mount_type(d); ++ if (r < 0) ++ return r; ++ ++ r = acquire_mount_options(d); ++ if (r < 0) ++ return r; ++ ++ r = acquire_mount_where(d); ++ if (r < 0) ++ return r; ++ ++ r = acquire_description(d); ++ if (r < 0) ++ return r; ++ ++ r = acquire_removable(d); ++ if (r < 0) ++ return r; ++ ++ return 0; ++} ++ ++enum { ++ COLUMN_NODE, ++ COLUMN_PATH, ++ COLUMN_MODEL, ++ COLUMN_WWN, ++ COLUMN_FSTYPE, ++ COLUMN_LABEL, ++ COLUMN_UUID, ++ _COLUMN_MAX, ++}; ++ ++struct item { ++ char* columns[_COLUMN_MAX]; ++}; ++ ++static int compare_item(const void *a, const void *b) { ++ const struct item *x = a, *y = b; ++ ++ if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE]) ++ return 0; ++ if (!x->columns[COLUMN_NODE]) ++ return 1; ++ if (!y->columns[COLUMN_NODE]) ++ return -1; ++ ++ return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]); ++} ++ ++static int list_devices(void) { ++ ++ static const char * const titles[_COLUMN_MAX] = { ++ [COLUMN_NODE] = "NODE", ++ [COLUMN_PATH] = "PATH", ++ [COLUMN_MODEL] = "MODEL", ++ [COLUMN_WWN] = "WWN", ++ [COLUMN_FSTYPE] = "TYPE", ++ [COLUMN_LABEL] = "LABEL", ++ [COLUMN_UUID] = "UUID" ++ }; ++ ++ _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; ++ _cleanup_udev_unref_ struct udev *udev = NULL; ++ struct udev_list_entry *item = NULL, *first = NULL; ++ size_t n_allocated = 0, n = 0, i; ++ size_t column_width[_COLUMN_MAX]; ++ struct item *items = NULL; ++ unsigned c; ++ int r; ++ ++ for (c = 0; c < _COLUMN_MAX; c++) ++ column_width[c] = strlen(titles[c]); ++ ++ udev = udev_new(); ++ if (!udev) ++ return log_oom(); ++ ++ e = udev_enumerate_new(udev); ++ if (!e) ++ return log_oom(); ++ ++ r = udev_enumerate_add_match_subsystem(e, "block"); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add block match: %m"); ++ ++ r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem"); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add property match: %m"); ++ ++ r = udev_enumerate_scan_devices(e); ++ if (r < 0) ++ return log_error_errno(r, "Failed to scan devices: %m"); ++ ++ first = udev_enumerate_get_list_entry(e); ++ udev_list_entry_foreach(item, first) { ++ _cleanup_udev_device_unref_ struct udev_device *d; ++ struct item *j; ++ ++ d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); ++ if (!d) { ++ r = log_oom(); ++ goto finish; ++ } ++ ++ if (!GREEDY_REALLOC0(items, n_allocated, n+1)) { ++ r = log_oom(); ++ goto finish; ++ } ++ ++ j = items + n++; ++ ++ for (c = 0; c < _COLUMN_MAX; c++) { ++ const char *x; ++ size_t k; ++ ++ switch (c) { ++ ++ case COLUMN_NODE: ++ x = udev_device_get_devnode(d); ++ break; ++ ++ case COLUMN_PATH: ++ x = udev_device_get_property_value(d, "ID_PATH"); ++ break; ++ ++ case COLUMN_MODEL: ++ x = get_model(d); ++ break; ++ ++ case COLUMN_WWN: ++ x = udev_device_get_property_value(d, "ID_WWN"); ++ break; ++ ++ case COLUMN_FSTYPE: ++ x = udev_device_get_property_value(d, "ID_FS_TYPE"); ++ break; ++ ++ case COLUMN_LABEL: ++ x = get_label(d); ++ break; ++ ++ case COLUMN_UUID: ++ x = udev_device_get_property_value(d, "ID_FS_UUID"); ++ break; ++ } ++ ++ if (isempty(x)) ++ continue; ++ ++ j->columns[c] = strdup(x); ++ if (!j->columns[c]) { ++ r = log_oom(); ++ goto finish; ++ } ++ ++ k = strlen(x); ++ if (k > column_width[c]) ++ column_width[c] = k; ++ } ++ } ++ ++ if (n == 0) { ++ log_info("No devices found."); ++ goto finish; ++ } ++ ++ qsort_safe(items, n, sizeof(struct item), compare_item); ++ ++ pager_open(arg_no_pager, false); ++ ++ fputs(ansi_underline(), stdout); ++ for (c = 0; c < _COLUMN_MAX; c++) { ++ if (c > 0) ++ fputc(' ', stdout); ++ ++ printf("%-*s", (int) column_width[c], titles[c]); ++ } ++ fputs(ansi_normal(), stdout); ++ fputc('\n', stdout); ++ ++ for (i = 0; i < n; i++) { ++ for (c = 0; c < _COLUMN_MAX; c++) { ++ if (c > 0) ++ fputc(' ', stdout); ++ ++ printf("%-*s", (int) column_width[c], strna(items[i].columns[c])); ++ } ++ fputc('\n', stdout); ++ } ++ ++ r = 0; ++ ++finish: ++ for (i = 0; i < n; i++) ++ for (c = 0; c < _COLUMN_MAX; c++) ++ free(items[i].columns[c]); ++ ++ free(items); ++ return r; ++} ++ ++int main(int argc, char* argv[]) { ++ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; ++ int r; ++ ++ log_parse_environment(); ++ log_open(); ++ ++ r = parse_argv(argc, argv); ++ if (r <= 0) ++ goto finish; ++ ++ if (arg_action == ACTION_LIST) { ++ r = list_devices(); ++ goto finish; ++ } ++ ++ r = discover_device(); ++ if (r < 0) ++ goto finish; ++ if (!arg_mount_where) { ++ log_error("Can't figure out where to mount %s.", arg_mount_what); ++ r = -EINVAL; ++ goto finish; ++ } ++ ++ path_kill_slashes(arg_mount_where); ++ ++ if (path_equal(arg_mount_where, "/")) { ++ log_error("Refusing to operate on root directory."); ++ r = -EINVAL; ++ goto finish; ++ } ++ ++ if (!path_is_safe(arg_mount_where)) { ++ log_error("Path is contains unsafe components."); ++ r = -EINVAL; ++ goto finish; ++ } ++ ++ if (streq_ptr(arg_mount_type, "auto")) ++ arg_mount_type = mfree(arg_mount_type); ++ if (streq_ptr(arg_mount_options, "defaults")) ++ arg_mount_options = mfree(arg_mount_options); ++ ++ if (!is_device_path(arg_mount_what)) ++ arg_fsck = false; ++ ++ if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) { ++ r = fsck_exists(arg_mount_type); ++ if (r < 0) ++ log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type); ++ else if (r == 0) { ++ log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type); ++ arg_fsck = false; /* fsck doesn't exist, let's not attempt it */ ++ } ++ } ++ ++ r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); ++ if (r < 0) { ++ log_error_errno(r, "Failed to create bus connection: %m"); ++ goto finish; ++ } ++ ++ switch (arg_action) { ++ ++ case ACTION_MOUNT: ++ case ACTION_DEFAULT: ++ r = start_transient_mount(bus, argv + optind); ++ break; ++ ++ case ACTION_AUTOMOUNT: ++ r = start_transient_automount(bus, argv + optind); ++ break; ++ ++ default: ++ assert_not_reached("Unexpected action."); ++ } ++ ++finish: ++ bus = sd_bus_flush_close_unref(bus); ++ ++ pager_close(); ++ ++ free(arg_mount_what); ++ free(arg_mount_where); ++ free(arg_mount_type); ++ free(arg_mount_options); ++ free(arg_description); ++ strv_free(arg_property); ++ strv_free(arg_automount_property); ++ ++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} diff --git a/meta/recipes-core/systemd/systemd_230.bb b/meta/recipes-core/systemd/systemd_230.bb index b4140fca1e..7ad59d33df 100644 --- a/meta/recipes-core/systemd/systemd_230.bb +++ b/meta/recipes-core/systemd/systemd_230.bb @@ -38,6 +38,12 @@ SRC_URI += " \ file://0023-automount-handle-expire_tokens-when-the-mount-unit-c.patch \ file://0024-automount-don-t-cancel-mount-umount-request-on-reloa.patch \ file://0025-automount-make-sure-the-expire-event-is-restarted-af.patch \ + file://0026-automount-implement-transient-automounts.patch \ + file://0027-mount-use-get_mount_parameters_fragment-consistently.patch \ + file://0028-mount-write-drop-in-file-when-setting-transient-prop.patch \ + file://0029-bus-util-unify-loop-around-bus_append_unit_property_.patch \ + file://0030-run-various-minor-improvements.patch \ + file://0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch \ file://udev-re-enable-mount-propagation-for-udevd.patch \ file://CVE-2016-7795.patch \ " -- cgit 1.2.3-korg