aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Oberritter <obi@opendreambox.org>2017-01-28 21:49:24 +0100
committerAndreas Oberritter <obi@opendreambox.org>2017-06-26 22:57:20 +0200
commit33d2e0eaf3b2f60529f4b10b348034becfdce526 (patch)
treeb7cf40b427f0586fd73ae5ffa175221e639cbe42
parent368889351d09fe2387311b621405753e3f771d76 (diff)
downloadopenembedded-core-contrib-33d2e0eaf3b2f60529f4b10b348034becfdce526.tar.gz
systemd: backport transient mounts from 232
Signed-off-by: Andreas Oberritter <obi@opendreambox.org>
-rw-r--r--meta/recipes-core/systemd/systemd/0026-automount-implement-transient-automounts.patch98
-rw-r--r--meta/recipes-core/systemd/systemd/0027-mount-use-get_mount_parameters_fragment-consistently.patch114
-rw-r--r--meta/recipes-core/systemd/systemd/0028-mount-write-drop-in-file-when-setting-transient-prop.patch29
-rw-r--r--meta/recipes-core/systemd/systemd/0029-bus-util-unify-loop-around-bus_append_unit_property_.patch136
-rw-r--r--meta/recipes-core/systemd/systemd/0030-run-various-minor-improvements.patch104
-rw-r--r--meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch1572
-rw-r--r--meta/recipes-core/systemd/systemd_230.bb6
7 files changed, 2059 insertions, 0 deletions
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 <m.olbrich@pengutronix.de>
+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 <m.olbrich@pengutronix.de>
+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 <m.olbrich@pengutronix.de>
+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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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 @@
+
+ <refnamediv>
+ <refname>systemd-run</refname>
+- <refpurpose>Run programs in transient scope or service or timer units</refpurpose>
++ <refpurpose>Run programs in transient scope units, service units, or timer-scheduled service units</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+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 <lennart@poettering.net>
+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 @@
+ </listitem>
+ </varlistentry>
+
++ <varlistentry id='no-ask-password'>
++ <term><option>--no-ask-password</option></term>
++
++ <listitem><para>Do not query the user for authentication for privileged operations.</para></listitem>
++ </varlistentry>
++
+ <varlistentry id='no-legend'>
+ <term><option>--no-legend</option></term>
+
+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 @@
++<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
++"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
++
++<!--
++ 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 <http://www.gnu.org/licenses/>.
++-->
++
++<refentry id="systemd-mount"
++ xmlns:xi="http://www.w3.org/2001/XInclude">
++
++ <refentryinfo>
++ <title>systemd-mount</title>
++ <productname>systemd</productname>
++
++ <authorgroup>
++ <author>
++ <contrib>Developer</contrib>
++ <firstname>Lennart</firstname>
++ <surname>Poettering</surname>
++ <email>lennart@poettering.net</email>
++ </author>
++ </authorgroup>
++ </refentryinfo>
++
++ <refmeta>
++ <refentrytitle>systemd-mount</refentrytitle>
++ <manvolnum>1</manvolnum>
++ </refmeta>
++
++ <refnamediv>
++ <refname>systemd-mount</refname>
++ <refpurpose>Establish a mount or auto-mount point transiently</refpurpose>
++ </refnamediv>
++
++ <refsynopsisdiv>
++ <cmdsynopsis>
++ <command>systemd-mount</command>
++ <arg choice="opt" rep="repeat"><replaceable>OPTIONS</replaceable></arg>
++ <arg choice="plain"><replaceable>WHAT</replaceable></arg>
++ <arg choice="opt"><replaceable>WHERE</replaceable></arg>
++ </cmdsynopsis>
++ <cmdsynopsis>
++ <command>systemd-mount</command>
++ <arg choice="opt" rep="repeat"><replaceable>OPTIONS</replaceable></arg>
++ <arg choice="plain"><option>--list</option></arg>
++ </cmdsynopsis>
++ </refsynopsisdiv>
++
++ <refsect1>
++ <title>Description</title>
++
++ <para><command>systemd-mount</command> may be used to create and start a transient <filename>.mount</filename> or
++ <filename>.automount</filename> unit of the file system <replaceable>WHAT</replaceable> on the mount point
++ <replaceable>WHERE</replaceable>.</para>
++
++ <para>In many ways, <command>systemd-mount</command> is similar to the lower-level
++ <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> command, however instead
++ of executing the mount operation directly and immediately, <command>systemd-mount</command> 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.</para>
++
++ <para>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. <literal>/dev/sdb1</literal>), 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>--automount=</option> option is implied, see below).</para>
++
++ <para>If two arguments are specified the first indicates the mount source (the <replaceable>WHAT</replaceable>) and
++ the second indicates the path to mount it on (the <replaceable>WHERE</replaceable>). 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
++ <option>--discover</option>, 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.</para>
++
++ <para>Use the <option>--list</option> command to show a terse table of all local, known block devices with file
++ systems that may be mounted with this command.</para>
++ </refsect1>
++
++ <refsect1>
++ <title>Options</title>
++
++ <para>The following options are understood:</para>
++
++ <variablelist>
++
++ <varlistentry>
++ <term><option>--no-block</option></term>
++
++ <listitem>
++ <para>Do not synchronously wait for the requested operation to finish. If this is not specified, the job will
++ be verified, enqueued and <command>systemd-mount</command> will wait until the mount or automount unit's
++ start-up is completed. By passing this argument, it is only verified and enqueued.</para>
++ </listitem>
++ </varlistentry>
++
++ <xi:include href="standard-options.xml" xpointer="no-pager"/>
++ <xi:include href="standard-options.xml" xpointer="no-ask-password"/>
++
++ <varlistentry>
++ <term><option>--quiet</option></term>
++ <term><option>-q</option></term>
++
++ <listitem><para>Suppresses additional informational output while running.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--discover</option></term>
++
++ <listitem><para>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.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--type=</option></term>
++ <term><option>-t</option></term>
++
++ <listitem><para>Specifies the file system type to mount (e.g. <literal>vfat</literal>, <literal>ext4</literal>,
++ …). If omitted (or set to <literal>auto</literal>) the file system is determined automatically.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--options=</option></term>
++ <term><option>-o</option></term>
++
++ <listitem><para>Additional mount options for the mount point.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--fsck=</option></term>
++
++ <listitem><para>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 <option>--automount=</option> below) the
++ check will be run the moment the first access to the device is made, which might slightly delay the
++ access.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--description=</option></term>
++
++ <listitem><para>Provide a description for the mount or automount unit. See <varname>Description=</varname> in
++ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
++ </para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--property=</option></term>
++ <term><option>-p</option></term>
++
++ <listitem><para>Sets a unit property for the mount unit that is created. This takes an assignment in the same
++ format as <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
++ <command>set-property</command> command.</para>
++ </listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--automount=</option></term>
++
++ <listitem><para>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 <option>--timeout-idle-sec=</option> switch (see below) may be used to ensure
++ the mount point is unmounted automatically after the last access and an idle period passed.</para>
++
++ <para>If this switch is not specified it defaults to false. If not specified and <option>--discover</option> is
++ used (or only a single argument passed, which implies <option>--discover</option>, 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.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>-A</option></term>
++
++ <listitem><para>Equivalent to <option>--automount=yes</option>.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--timeout-idle-sec=</option></term>
++
++ <listitem><para>Takes a time value that controls the idle timeout in automount mode. If set to
++ <literal>infinity</literal> (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
++ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details on
++ the time syntax supported. This option has no effect if only a regular mount is established, and automounting
++ is not used.</para>
++
++ <para>Note that if <option>--discover</option> is used (or only a single argument passed, which implies
++ <option>--discover</option>, see above), and the file system block device is detected to be removable,
++ <option>--timeout-idle-sec=1s</option> is implied.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--automount-property=</option></term>
++
++ <listitem><para>Similar to <option>--property=</option>, but applies additional properties to the automount
++ unit created, instead of the mount unit.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--bind-device=</option></term>
++
++ <listitem><para>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.</para>
++
++ <para>Note that if <option>--discover</option> is used (or only a single argument passed, which implies
++ <option>--discover</option>, see above), and the file system block device is detected to be removable, this
++ option is implied.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><option>--list</option></term>
++
++ <listitem><para>Instead of establishing a mount or automount point, print a terse list of block devices
++ containing file systems that may be mounted with <literal>systemd-mount</literal>, along with useful metadata
++ such as labels, etc.</para></listitem>
++ </varlistentry>
++
++ <xi:include href="user-system-options.xml" xpointer="user" />
++ <xi:include href="user-system-options.xml" xpointer="system" />
++ <xi:include href="user-system-options.xml" xpointer="host" />
++ <xi:include href="user-system-options.xml" xpointer="machine" />
++
++ <xi:include href="standard-options.xml" xpointer="help" />
++ <xi:include href="standard-options.xml" xpointer="version" />
++ </variablelist>
++
++ </refsect1>
++
++ <refsect1>
++ <title>Exit status</title>
++
++ <para>On success, 0 is returned, a non-zero failure
++ code otherwise.</para>
++ </refsect1>
++
++ <refsect1>
++ <title>The udev Database</title>
++
++ <para>If <option>--discover</option> is used, <command>systemd-mount</command> honours a couple of additional udev
++ properties of block devices:</para>
++
++ <variablelist class='udev-directives'>
++ <varlistentry>
++ <term><varname>SYSTEMD_MOUNT_OPTIONS=</varname></term>
++
++ <listitem><para>The mount options to use, if <option>--options=</option> is not used.</para></listitem>
++ </varlistentry>
++
++ <varlistentry>
++ <term><varname>SYSTEMD_MOUNT_WHERE=</varname></term>
++
++ <listitem><para>The file system path to place the mount point at, instead of the automatically generated
++ one.</para></listitem>
++ </varlistentry>
++ </variablelist>
++ </refsect1>
++
++ <refsect1>
++ <title>See Also</title>
++ <para>
++ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd.automount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
++ </para>
++ </refsect1>
++
++</refentry>
+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:
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+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 <http://www.gnu.org/licenses/>.
++***/
++
++#include <getopt.h>
++
++#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 \
"