aboutsummaryrefslogtreecommitdiffstats
path: root/etc/systemd
diff options
context:
space:
mode:
authorInsanePrawn <[email protected]>2020-02-12 18:01:15 +0100
committerBrian Behlendorf <[email protected]>2020-02-14 15:32:55 -0800
commitecbbdac799e0fd33f9d8b5fd6315008e3b4c9a50 (patch)
tree1559ec1a2271277cb8a104ab4b9097b06a80fd9d /etc/systemd
parent9d2f3b7f94b62939de3fb50c8d9a2d78477f8627 (diff)
Systemd mount generator: Generate noauto units; add control properties
This commit refactors the systemd mount generators and makes the following major changes: - The generator now generates units for datasets marked canmount=noauto, too. These units are NOT WantedBy local-fs.target. If there are multiple noauto datasets for a path, no noauto unit will be created. Datasets with canmount=on are prioritized. - Introduces handling of new user properties which are now included in the zfs-list.cache files: - org.openzfs.systemd:requires: List of units to require for this mount unit - org.openzfs.systemd:requires-mounts-for: List of mounts to require by this mount unit - org.openzfs.systemd:before: List of units to order after this mount unit - org.openzfs.systemd:after: List of units to order before this mount unit - org.openzfs.systemd:wanted-by: List of units to add a Wants dependency on this mount unit to - org.openzfs.systemd:required-by: List of units to add a Requires dependency on this mount unit to - org.openzfs.systemd:nofail: Toggles between a wants and a requires dependency. - org.openzfs.systemd:ignore: Do not generate a mount unit for this dataset. Consult the updated man page for detailed documentation. - Restructures and extends the zfs-mount-generator(8) man page with the above properties, information on unit ordering and a license header. Reviewed-by: Richard Laager <[email protected]> Reviewed-by: Antonio Russo <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: InsanePrawn <[email protected]> Closes #9649
Diffstat (limited to 'etc/systemd')
-rwxr-xr-xetc/systemd/system-generators/zfs-mount-generator.in217
1 files changed, 191 insertions, 26 deletions
diff --git a/etc/systemd/system-generators/zfs-mount-generator.in b/etc/systemd/system-generators/zfs-mount-generator.in
index 411b3f955..bb735112d 100755
--- a/etc/systemd/system-generators/zfs-mount-generator.in
+++ b/etc/systemd/system-generators/zfs-mount-generator.in
@@ -2,6 +2,7 @@
# zfs-mount-generator - generates systemd mount units for zfs
# Copyright (c) 2017 Antonio Russo <[email protected]>
+# Copyright (c) 2020 InsanePrawn <[email protected]>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -33,6 +34,35 @@ do_fail() {
exit 1
}
+# test if $1 is in space-separated list $2
+is_known() {
+ query="$1"
+ IFS=' '
+ # protect against special characters
+ set -f
+ for element in $2 ; do
+ if [ "$query" = "$element" ] ; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+# create dependency on unit file $1
+# of type $2, i.e. "wants" or "requires"
+# in the target units from space-separated list $3
+create_dependencies() {
+ unitfile="$1"
+ suffix="$2"
+ # protect against special characters
+ set -f
+ for target in $3 ; do
+ target_dir="${dest_norm}/${target}.${suffix}/"
+ mkdir -p "${target_dir}"
+ ln -s "../${unitfile}" "${target_dir}"
+ done
+}
+
# see systemd.generator
if [ $# -eq 0 ] ; then
dest_norm="/tmp"
@@ -42,11 +72,6 @@ else
do_fail "zero or three arguments required"
fi
-# For ZFSs 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**
-req_dir="${dest_norm}/local-fs.target.wants/"
-mkdir -p "${req_dir}"
# All needed information about each ZFS is available from
# zfs list -H -t filesystem -o <properties>
@@ -74,18 +99,58 @@ process_line() {
p_nbmand="${10}"
p_encroot="${11}"
p_keyloc="${12}"
+ p_systemd_requires="${13}"
+ p_systemd_requiresmountsfor="${14}"
+ p_systemd_before="${15}"
+ p_systemd_after="${16}"
+ p_systemd_wantedby="${17}"
+ p_systemd_requiredby="${18}"
+ p_systemd_nofail="${19}"
+ p_systemd_ignore="${20}"
# Minimal pre-requisites to mount a ZFS dataset
+ # By ordering before zfs-mount.service, we avoid race conditions.
+ after="zfs-import.target"
+ before="zfs-mount.service"
wants="zfs-import.target"
+ requires=""
+ requiredmounts=""
+ wantedby=""
+ requiredby=""
+ noauto="off"
+
+ 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
# 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://}'"
+ if [ -n "${requiredmounts}" ] ; then
+ keymountdep="${requiredmounts} '${p_keyloc#file://}'"
+ else
+ keymountdep="RequiresMountsFor='${p_keyloc#file://}'"
+ fi
keyloadcmd="@sbindir@/zfs load-key '${dataset}'"
elif [ "${p_keyloc}" = "prompt" ] ; then
keyloadcmd="\
@@ -121,8 +186,10 @@ SourcePath=${cachefile}
Documentation=man:zfs-mount-generator(8)
DefaultDependencies=no
Wants=${wants}
-After=${wants}
-${pathdep}
+After=${after}
+Before=${before}
+${requires}
+${keymountdep}
[Service]
Type=oneshot
@@ -130,23 +197,35 @@ RemainAfterExit=yes
ExecStart=${keyloadcmd}
ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}"
fi
- # Update the dependencies for the mount file to require the
+ # Update the dependencies for the mount file to want the
# key-loading unit.
wants="${wants} ${keyloadunit}"
+ after="${after} ${keyloadunit}"
fi
# Prepare the .mount unit
+ # skip generation of the mount unit if org.openzfs.systemd: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.openzfs.systemd:ignore for ${dataset}"
+ fi
+ fi
+
# Check for canmount=off .
if [ "${p_canmount}" = "off" ] ; then
return
elif [ "${p_canmount}" = "noauto" ] ; then
- # Don't let a noauto marked mountpoint block an "auto" marked mountpoint
- return
+ noauto="on"
elif [ "${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.
@@ -155,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.
@@ -233,15 +312,91 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
"${dataset}" >/dev/kmsg
fi
- # If the mountpoint has already been created, give it precedence.
+ if [ -n "${p_systemd_wantedby}" ] && \
+ [ "${p_systemd_wantedby}" != "-" ] ; then
+ noauto="on"
+ if [ "${p_systemd_wantedby}" = "none" ] ; then
+ wantedby=""
+ else
+ wantedby="${p_systemd_wantedby}"
+ before="${before} ${wantedby}"
+ fi
+ fi
+
+ if [ -n "${p_systemd_requiredby}" ] && \
+ [ "${p_systemd_requiredby}" != "-" ] ; then
+ noauto="on"
+ if [ "${p_systemd_requiredby}" = "none" ] ; then
+ requiredby=""
+ else
+ requiredby="${p_systemd_requiredby}"
+ before="${before} ${requiredby}"
+ fi
+ fi
+
+ # For datasets with canmount=on, a dependency is created for
+ # 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**
+ # noauto=on disables this behavior completely.
+ if [ "${noauto}" != "on" ] ; then
+ if [ "${p_systemd_nofail}" = "off" ] ; then
+ requiredby="local-fs.target"
+ before="${before} local-fs.target"
+ else
+ wantedby="local-fs.target"
+ if [ "${p_systemd_nofail}" != "on" ] ; then
+ before="${before} local-fs.target"
+ fi
+ fi
+ fi
+
+ # Handle existing files:
+ # 1. We never overwrite existing files, although we may delete
+ # files if we're sure they were created by us. (see 5.)
+ # 2. We handle files differently based on canmount. Units with canmount=on
+ # always have precedence over noauto. This is enforced by the sort pipe
+ # in the loop around this function.
+ # It is important to use $p_canmount and not $noauto here, since we
+ # sort by canmount while other properties also modify $noauto, e.g.
+ # org.openzfs.systemd:wanted-by.
+ # 3. If no unit file exists for a noauto dataset, we create one.
+ # 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 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
+ # for the same mountpoint. In such cases, we remove the file for safety.
+ # To avoid further noauto datasets creating a file for this path again,
+ # we leave the file name in the tracking variable.
if [ -e "${dest_norm}/${mountfile}" ] ; then
- printf 'zfs-mount-generator: %s already exists\n' "${mountfile}" \
- >/dev/kmsg
+ if is_known "$mountfile" "$noauto_files" ; then
+ # if it's in $noauto_files, we must be noauto too. See 2.
+ printf 'zfs-mount-generator: removing duplicate noauto %s\n' \
+ "${mountfile}" >/dev/kmsg
+ # See 5.
+ rm "${dest_norm}/${mountfile}"
+ else
+ # don't log for canmount=noauto
+ if [ "${p_canmount}" = "on" ] ; then
+ printf 'zfs-mount-generator: %s already exists. Skipping.\n' \
+ "${mountfile}" >/dev/kmsg
+ fi
+ fi
+ # file exists; Skip current dataset.
return
+ else
+ if is_known "${mountfile}" "${noauto_files}" ; then
+ # See 4.
+ return
+ elif [ "${p_canmount}" = "noauto" ] ; then
+ noauto_files="${mountfile} ${noauto_files}"
+ fi
fi
# Create the .mount unit file.
- # By ordering before zfs-mount.service, we avoid race conditions.
#
# (Do not use `<<EOF`-style here-documents for this, see warning above)
#
@@ -251,9 +406,12 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
[Unit]
SourcePath=${cachefile}
Documentation=man:zfs-mount-generator(8)
-Before=local-fs.target zfs-mount.service
-After=${wants}
+
+Before=${before}
+After=${after}
Wants=${wants}
+${requires}
+${requiredmounts}
[Mount]
Where=${p_mountpoint}
@@ -261,13 +419,20 @@ What=${dataset}
Type=zfs
Options=defaults${opts},zfsutil" > "${dest_norm}/${mountfile}"
- # Finally, create the appropriate dependency
- ln -s "../${mountfile}" "${req_dir}"
+ # Finally, create the appropriate dependencies
+ create_dependencies "${mountfile}" "wants" "$wantedby"
+ create_dependencies "${mountfile}" "requires" "$requiredby"
+
}
-# Feed each line into process_line
for cachefile in "${FSLIST}/"* ; do
- while read -r fs ; do
- process_line "${fs}"
- done < "${cachefile}"
+ # Sort cachefile's lines by canmount, "on" before "noauto"
+ # and feed each line into process_line
+ sort -t "$(printf '\t')" -k 3 -r "${cachefile}" | \
+ ( # subshell is necessary for `sort|while read` and $noauto_files
+ noauto_files=""
+ while read -r fs ; do
+ process_line "${fs}"
+ done
+ )
done