Skip to content
Permalink
Browse files

Systemd mount generator: add additional control properties

Adds additional properties to control the behaviour of the systemd
mount generator:
- org.open-zfs.systemd:requires:
  Space-separated list of units to require for this mount unit

- org.open-zfs.systemd:requires-mounts-for:
  Space-separated list of mounts to require by this mount unit

- org.open-zfs.systemd:before:
  Space-separated list of units to order after this mount unit

- org.open-zfs.systemd:after:
  Space-separated list of units to order before this mount unit

- org.open-zfs.systemd:wanted-by:
  Unit to add a dependency for this mount unit to
  (local-fs.target by default)

- org.open-zfs.systemd:nofail:
  Toggles between a wants and a requires dependency.

- org.open-zfs.systemd:ignore:
  Do not generate a mount unit for this dataset.

This extends the zfs-mount-generator(8) man page with the above
properties and a copyright header and also enhances the error messages
printed to the kernel log by the generator.

Signed-off-by: InsanePrawn <insane.prawny@gmail.com>
  • Loading branch information
InsanePrawn committed Jan 13, 2020
1 parent 07fccd3 commit a4e2069e15d41a4e7f8db2ffc561f60c985918dd
@@ -46,9 +46,14 @@ case "${ZEVENT_HISTORY_INTERNAL_NAME}" in
set|inherit)
# Only act if one of the tracked properties is altered.
case "${ZEVENT_HISTORY_INTERNAL_STR%%=*}" in
canmount|mountpoint|atime|relatime|devices|exec| \
readonly|setuid|nbmand|encroot|keylocation| \
org.open-zfs.systemd:noauto) ;;
canmount|mountpoint|atime|relatime|devices|exec|readonly| \
setuid|nbmand|encroot|keylocation| \
org.open-zfs.systemd:noauto|org.open-zfs.systemd:requires| \
org.open-zfs.systemd:requires-mounts-for| \
org.open-zfs.systemd:before|org.open-zfs.systemd:after| \
org.open-zfs.systemd:wanted-by|org.open-zfs.systemd:nofail| \
org.open-zfs.systemd:ignore \
) ;;
*) exit 0 ;;
esac
;;
@@ -62,8 +67,12 @@ esac
zed_lock zfs-list
trap abort_alter EXIT

PROPS="name,mountpoint,canmount,atime,relatime,devices,exec,readonly"
PROPS="${PROPS},setuid,nbmand,encroot,keylocation,org.open-zfs.systemd:noauto"
PROPS="name,mountpoint,canmount,atime,relatime,devices,exec,readonly\
,setuid,nbmand,encroot,keylocation,org.open-zfs.systemd:noauto\
,org.open-zfs.systemd:requires,org.open-zfs.systemd:requires-mounts-for\
,org.open-zfs.systemd:before,org.open-zfs.systemd:after\
,org.open-zfs.systemd:wanted-by,org.open-zfs.systemd:nofail\
,org.open-zfs.systemd:ignore"

"${ZFS}" list -H -t filesystem -o $PROPS -r "${ZEVENT_POOL}" > "${FSLIST_TMP}"

@@ -2,6 +2,7 @@

# zfs-mount-generator - generates systemd mount units for zfs
# Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
# Copyright (c) 2020 InsanePrawn <insane.prawny@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -56,11 +57,6 @@ else
do_fail "zero or three arguments required"
fi

# For datasets marked "auto", a dependency is created for local-fs.target. To
# avoid regressions, this dependency is reduced to "wants" rather than
# "requires". **THIS MAY CHANGE**
wants_dir="${dest_norm}/local-fs.target.wants/"
mkdir -p "${wants_dir}"

# All needed information about each ZFS is available from
# zfs list -H -t filesystem -o <properties>
@@ -89,18 +85,76 @@ process_line() {
p_encroot="${11}"
p_keyloc="${12}"
p_systemd_noauto="${13}"
p_systemd_requires="${14}"
p_systemd_requiresmountsfor="${15}"
p_systemd_before="${16}"
p_systemd_after="${17}"
p_systemd_wantedby="${18}"
p_systemd_nofail="${19}"
p_systemd_ignore="${20}"

# Minimal pre-requisites to mount a ZFS dataset
wantedby="local-fs.target"
after="zfs-import.target"
before="zfs-mount.service"
wants="zfs-import.target"
requires=""
requiredmounts=""

if [ -n "${p_systemd_wantedby}" ] && \
[ "${p_systemd_wantedby}" != "-" ] ; then
if [ "${p_systemd_wantedby}" = "none" ] ; then
p_systemd_noauto="on"
wantedby=""
else
wantedby="${p_systemd_wantedby}"
fi
fi

if [ -n "${p_systemd_after}" ] && \
[ "${p_systemd_after}" != "-" ] ; then
after="${p_systemd_after} ${after}"
fi

if [ -n "${p_systemd_before}" ] && \
[ "${p_systemd_before}" != "-" ] ; then
before="${p_systemd_before} ${before}"
fi

if [ -n "${p_systemd_requires}" ] && \
[ "${p_systemd_requires}" != "-" ] ; then
requires="Requires=${p_systemd_requires}"
fi

if [ -n "${p_systemd_requiresmountsfor}" ] && \
[ "${p_systemd_requiresmountsfor}" != "-" ] ; then
requiredmounts="RequiresMountsFor=${p_systemd_requiresmountsfor}"
fi

# For datasets marked "auto", a dependency is created for $wantedby
# (local-fs.target by default). To avoid regressions, this dependency
# is reduced to "wants" rather than "requires" when nofail is not "off".
# **THIS MAY CHANGE**
if [ -n "${wantedby}" ] ; then
if [ "${p_systemd_nofail}" = "on" ] ; then
wants_dir="${dest_norm}/${wantedby}.requires/"
else
if [ "${p_systemd_nofail}" != "off" ] \
&& [ "${p_systemd_nofail}" != "-" ] ; then
do_fail "invalid org.open-zfs.systemd:nofail for ${dataset}"
fi
wants_dir="${dest_norm}/${wantedby}.wants/"
fi
mkdir -p "${wants_dir}"
fi
# Handle encryption
if [ -n "${p_encroot}" ] &&
[ "${p_encroot}" != "-" ] ; then
keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
if [ "${p_encroot}" = "${dataset}" ] ; then
pathdep=""
keymountdep=""
if [ "${p_keyloc%%://*}" = "file" ] ; then
pathdep="RequiresMountsFor='${p_keyloc#file://}'"
keymountdep="RequiresMountsFor='${p_keyloc#file://}'"
keyloadcmd="@sbindir@/zfs load-key '${dataset}'"
elif [ "${p_keyloc}" = "prompt" ] ; then
keyloadcmd="\
@@ -136,8 +190,8 @@ SourcePath=${cachefile}
Documentation=man:zfs-mount-generator(8)
DefaultDependencies=no
Wants=${wants}
After=${wants}
${pathdep}
After=${after}
${keymountdep}
[Service]
Type=oneshot
@@ -148,17 +202,30 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
# Update the dependencies for the mount file to require the
# key-loading unit.
wants="${wants} ${keyloadunit}"
after="${after} ${keyloadunit}"
fi

# Prepare the .mount unit

# skip generation of the mount unit if org.open-zfs.sytemd:ignore is "on"
if [ -n "${p_systemd_ignore}" ] ; then
if [ "${p_systemd_ignore}" = "on" ] ; then
return
elif [ "${p_systemd_ignore}" = "-" ] \
|| [ "${p_systemd_ignore}" = "off" ] ; then
: # This is OK
else
do_fail "invalid org.open-zfs.systemd:ignore for ${dataset}"
fi
fi

# Check for canmount=off .
if [ "${p_canmount}" = "off" ] ; then
return
elif [ "${p_canmount}" = "noauto" ] || [ "${p_canmount}" = "on" ] ; then
: # This is OK
else
do_fail "invalid canmount"
do_fail "invalid canmount for ${dataset}"
fi

# Check for legacy and blank mountpoints.
@@ -167,7 +234,7 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
elif [ "${p_mountpoint}" = "none" ] ; then
return
elif [ "${p_mountpoint%"${p_mountpoint#?}"}" != "/" ] ; then
do_fail "invalid mountpoint $*"
do_fail "invalid mountpoint for ${dataset}"
fi

# Escape the mountpoint per systemd policy.
@@ -251,10 +318,10 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
# 2. Units with canmount=on always have precedence over noauto.
# This is enforced by the sort pipe in the loop around this function.
# 3. If no unit file exists for a noauto dataset, we create one.
# Additionally, we use a $noauto_files to track the unit file names
# Additionally, we use $noauto_files to track the unit file names
# (which are the systemd-escaped mountpoints) of all (exclusively)
# noauto datasets that had a file created.
# 4. If the file to be created is not in the tracking variaable,
# 4. If the file to be created is found in the tracking variable,
# we do NOT create it.
# 5. If a file exists for a noauto dataset, we check whether the file
# name is in the variable. If it is, we have multiple noauto datasets
@@ -292,9 +359,9 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
#
# (Do not use `<<EOF`-style here-documents for this, see warning above)
#
before="zfs-mount.service"
if [ "${p_canmount}" = "on" ] && [ "${p_systemd_noauto}" != "on" ] ; then
before="${before} local-fs.target"
if [ "${p_canmount}" = "on" ] && [ "${p_systemd_noauto}" != "on" ] && \
[ "${p_systemd_nofail}" != "on" ] && [ -n "${wantedby}" ] ; then
before="${before} ${wantedby}"
fi
echo \
"# Automatically generated by zfs-mount-generator
@@ -304,8 +371,10 @@ SourcePath=${cachefile}
Documentation=man:zfs-mount-generator(8)
Before=${before}
After=${wants}
After=${after}
Wants=${wants}
${requires}
${requiredmounts}
[Mount]
Where=${p_mountpoint}
@@ -314,7 +383,8 @@ Type=zfs
Options=defaults${opts},zfsutil" > "${dest_norm}/${mountfile}"

# Finally, create the appropriate dependency
if [ "${p_canmount}" = "on" ] && [ "${p_systemd_noauto}" != "on" ] ; then
if [ -n "${wantedby}" ] && [ "${p_canmount}" = "on" ] && \
[ "${p_systemd_noauto}" != "on" ] ; then
ln -s "../${mountfile}" "${wants_dir}"
fi
}
@@ -1,4 +1,11 @@
.TH "ZFS\-MOUNT\-GENERATOR" "8" "ZFS" "zfs-mount-generator" "\""
.\"
.\" Copyright 2018 Antonio Russo <antonio.e.russo@gmail.com>
.\" Copyright 2019 Kjeld Schouten-Lebbing <kjeld@schouten-lebbing.nl>
.\" Copyright 2020 InsanePrawn <insane.prawny@gmail.com>
.\"

.TH "ZFS\-MOUNT\-GENERATOR" "8" "2020-01-14" "ZFS" "zfs-mount-generator" "\""

.SH "NAME"
zfs\-mount\-generator \- generates systemd mount units for ZFS
.SH SYNOPSIS
@@ -12,10 +19,19 @@ and is called during early boot to generate
.BR systemd.mount (5)
units for automatically mounted datasets. Mount ordering and dependencies
are created for all tracked pools (see below). If a dataset has
.BR canmount=on
and
.BR mountpoint
set, the
set and
.BR canmount
is either
.BR on
or
.BR noauto ,
a mount unit will be generated.
If
.BR canmount
is
.BR on ,
the
.BR auto
mount option will be set, and a dependency for
.BR local-fs.target
@@ -26,7 +42,8 @@ information on ZFS mountpoints must be stored separately. The output
of the command
.PP
.RS 4
zfs list -H -o name,mountpoint,canmount,atime,relatime,devices,exec,readonly,setuid,nbmand,encroot,keylocation
zfs list -H -o name,mountpoint,canmount,atime,relatime,devices,exec,readonly,setuid,nbmand,encroot,keylocation,org.open-zfs.systemd:noauto,org.open-zfs.systemd:requires,org.open-zfs.systemd:requires-mounts-for,org.open-zfs.systemd:before,org.open-zfs.systemd:after,org.open-zfs.systemd:wanted-by,org.open-zfs.systemd:nofail,org.open-zfs.systemd:ignore

.RE
.PP
for datasets that should be mounted by systemd, should be kept
@@ -45,6 +62,51 @@ history_event-zfs-list-cacher.sh .
.RE
.PP
.sp
.SS PROPERTIES
The behavior of the generator script can be influenced by setting special properties on datasets:
.sp
.TP 4
.BR org.open\-zfs.systemd:noauto
Create and treat the mount unit as if it had
.BR canmount=noauto
set, so no dependencies on the unit are created and it only gets activated on demand.
.TP 4
.BR org.open\-zfs.systemd:requires\-mounts\-for
Space\-separated list of mountpoints to require to be mounted for this mount unit
.TP 4
.BR org.open\-zfs.systemd:before
Mount unit will be ordered before this space\-separated list of units
.TP 4
.BR org.open\-zfs.systemd:after
Mount unit will be ordered after this space\-separated list of units
.TP 4
.BR org.open\-zfs.systemd:wanted\-by
The specified unit will gain a dependency on this mount unit.
Defaults to
.BR local\-fs.target
if unset.
Setting this property to
.BR none
will result in similar behavior to
.BR noauto .
.TP 4
.BR org.open\-zfs.systemd:nofail
Toggles between a
.BR wants
and
.BR requires
type of dependency between the mount unit and its
.BR wanted-by
target.
.TP 4
.BR org.open\-zfs.systemd:ignore
Do not generate a mount unit for this dataset.

.RE
See also
.BR systemd.mount (5)

.PP
.SH EXAMPLE
To begin, enable tracking for the pool:
.PP
@@ -63,7 +125,9 @@ systemctl enable zfs-zed.service
systemctl restart zfs-zed.service
.RE
.PP
Force the running of the ZEDLET by setting canmount=on for at least one dataset in the pool:
Force the running of the ZEDLET by setting a monitored property, e.g.
.BR canmount
for at least one dataset in the pool:
.PP
.RS 4
zfs set canmount=on

0 comments on commit a4e2069

Please sign in to comment.
You can’t perform that action at this time.