aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch')
-rw-r--r--meta/recipes-core/systemd/systemd/0031-add-a-new-tool-for-creating-transient-mount-and-auto.patch1572
1 files changed, 1572 insertions, 0 deletions
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;
++}