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 <rlaager@wiktel.com> Reviewed-by: Antonio Russo <antonio.e.russo@gmail.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: InsanePrawn <insane.prawny@gmail.com> Closes #9649
This commit is contained in:
parent
9d2f3b7f94
commit
ecbbdac799
|
@ -46,8 +46,13 @@ case "${ZEVENT_HISTORY_INTERNAL_NAME}" in
|
||||||
set|inherit)
|
set|inherit)
|
||||||
# Only act if one of the tracked properties is altered.
|
# Only act if one of the tracked properties is altered.
|
||||||
case "${ZEVENT_HISTORY_INTERNAL_STR%%=*}" in
|
case "${ZEVENT_HISTORY_INTERNAL_STR%%=*}" in
|
||||||
canmount|mountpoint|atime|relatime|devices|exec| \
|
canmount|mountpoint|atime|relatime|devices|exec|readonly| \
|
||||||
readonly|setuid|nbmand|encroot|keylocation) ;;
|
setuid|nbmand|encroot|keylocation|org.openzfs.systemd:requires| \
|
||||||
|
org.openzfs.systemd:requires-mounts-for| \
|
||||||
|
org.openzfs.systemd:before|org.openzfs.systemd:after| \
|
||||||
|
org.openzfs.systemd:wanted-by|org.openzfs.systemd:required-by| \
|
||||||
|
org.openzfs.systemd:nofail|org.openzfs.systemd:ignore \
|
||||||
|
) ;;
|
||||||
*) exit 0 ;;
|
*) exit 0 ;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
@ -61,8 +66,12 @@ esac
|
||||||
zed_lock zfs-list
|
zed_lock zfs-list
|
||||||
trap abort_alter EXIT
|
trap abort_alter EXIT
|
||||||
|
|
||||||
PROPS="name,mountpoint,canmount,atime,relatime,devices,exec,readonly"
|
PROPS="name,mountpoint,canmount,atime,relatime,devices,exec\
|
||||||
PROPS="${PROPS},setuid,nbmand,encroot,keylocation"
|
,readonly,setuid,nbmand,encroot,keylocation\
|
||||||
|
,org.openzfs.systemd:requires,org.openzfs.systemd:requires-mounts-for\
|
||||||
|
,org.openzfs.systemd:before,org.openzfs.systemd:after\
|
||||||
|
,org.openzfs.systemd:wanted-by,org.openzfs.systemd:required-by\
|
||||||
|
,org.openzfs.systemd:nofail,org.openzfs.systemd:ignore"
|
||||||
|
|
||||||
"${ZFS}" list -H -t filesystem -o $PROPS -r "${ZEVENT_POOL}" > "${FSLIST_TMP}"
|
"${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
|
# zfs-mount-generator - generates systemd mount units for zfs
|
||||||
# Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
|
# 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
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
# a copy of this software and associated documentation files (the
|
# a copy of this software and associated documentation files (the
|
||||||
|
@ -33,6 +34,35 @@ do_fail() {
|
||||||
exit 1
|
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
|
# see systemd.generator
|
||||||
if [ $# -eq 0 ] ; then
|
if [ $# -eq 0 ] ; then
|
||||||
dest_norm="/tmp"
|
dest_norm="/tmp"
|
||||||
|
@ -42,11 +72,6 @@ else
|
||||||
do_fail "zero or three arguments required"
|
do_fail "zero or three arguments required"
|
||||||
fi
|
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
|
# All needed information about each ZFS is available from
|
||||||
# zfs list -H -t filesystem -o <properties>
|
# zfs list -H -t filesystem -o <properties>
|
||||||
|
@ -74,18 +99,58 @@ process_line() {
|
||||||
p_nbmand="${10}"
|
p_nbmand="${10}"
|
||||||
p_encroot="${11}"
|
p_encroot="${11}"
|
||||||
p_keyloc="${12}"
|
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
|
# 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"
|
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
|
# Handle encryption
|
||||||
if [ -n "${p_encroot}" ] &&
|
if [ -n "${p_encroot}" ] &&
|
||||||
[ "${p_encroot}" != "-" ] ; then
|
[ "${p_encroot}" != "-" ] ; then
|
||||||
keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
|
keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
|
||||||
if [ "${p_encroot}" = "${dataset}" ] ; then
|
if [ "${p_encroot}" = "${dataset}" ] ; then
|
||||||
pathdep=""
|
keymountdep=""
|
||||||
if [ "${p_keyloc%%://*}" = "file" ] ; then
|
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}'"
|
keyloadcmd="@sbindir@/zfs load-key '${dataset}'"
|
||||||
elif [ "${p_keyloc}" = "prompt" ] ; then
|
elif [ "${p_keyloc}" = "prompt" ] ; then
|
||||||
keyloadcmd="\
|
keyloadcmd="\
|
||||||
|
@ -121,8 +186,10 @@ SourcePath=${cachefile}
|
||||||
Documentation=man:zfs-mount-generator(8)
|
Documentation=man:zfs-mount-generator(8)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Wants=${wants}
|
Wants=${wants}
|
||||||
After=${wants}
|
After=${after}
|
||||||
${pathdep}
|
Before=${before}
|
||||||
|
${requires}
|
||||||
|
${keymountdep}
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -130,23 +197,35 @@ RemainAfterExit=yes
|
||||||
ExecStart=${keyloadcmd}
|
ExecStart=${keyloadcmd}
|
||||||
ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}"
|
ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}"
|
||||||
fi
|
fi
|
||||||
# Update the dependencies for the mount file to require the
|
# Update the dependencies for the mount file to want the
|
||||||
# key-loading unit.
|
# key-loading unit.
|
||||||
wants="${wants} ${keyloadunit}"
|
wants="${wants} ${keyloadunit}"
|
||||||
|
after="${after} ${keyloadunit}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prepare the .mount unit
|
# 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 .
|
# Check for canmount=off .
|
||||||
if [ "${p_canmount}" = "off" ] ; then
|
if [ "${p_canmount}" = "off" ] ; then
|
||||||
return
|
return
|
||||||
elif [ "${p_canmount}" = "noauto" ] ; then
|
elif [ "${p_canmount}" = "noauto" ] ; then
|
||||||
# Don't let a noauto marked mountpoint block an "auto" marked mountpoint
|
noauto="on"
|
||||||
return
|
|
||||||
elif [ "${p_canmount}" = "on" ] ; then
|
elif [ "${p_canmount}" = "on" ] ; then
|
||||||
: # This is OK
|
: # This is OK
|
||||||
else
|
else
|
||||||
do_fail "invalid canmount"
|
do_fail "invalid canmount for ${dataset}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for legacy and blank mountpoints.
|
# Check for legacy and blank mountpoints.
|
||||||
|
@ -155,7 +234,7 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
|
||||||
elif [ "${p_mountpoint}" = "none" ] ; then
|
elif [ "${p_mountpoint}" = "none" ] ; then
|
||||||
return
|
return
|
||||||
elif [ "${p_mountpoint%"${p_mountpoint#?}"}" != "/" ] ; then
|
elif [ "${p_mountpoint%"${p_mountpoint#?}"}" != "/" ] ; then
|
||||||
do_fail "invalid mountpoint $*"
|
do_fail "invalid mountpoint for ${dataset}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape the mountpoint per systemd policy.
|
# Escape the mountpoint per systemd policy.
|
||||||
|
@ -233,15 +312,91 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
|
||||||
"${dataset}" >/dev/kmsg
|
"${dataset}" >/dev/kmsg
|
||||||
fi
|
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
|
if [ -e "${dest_norm}/${mountfile}" ] ; then
|
||||||
printf 'zfs-mount-generator: %s already exists\n' "${mountfile}" \
|
if is_known "$mountfile" "$noauto_files" ; then
|
||||||
>/dev/kmsg
|
# 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
|
return
|
||||||
|
else
|
||||||
|
if is_known "${mountfile}" "${noauto_files}" ; then
|
||||||
|
# See 4.
|
||||||
|
return
|
||||||
|
elif [ "${p_canmount}" = "noauto" ] ; then
|
||||||
|
noauto_files="${mountfile} ${noauto_files}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the .mount unit file.
|
# 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)
|
# (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]
|
[Unit]
|
||||||
SourcePath=${cachefile}
|
SourcePath=${cachefile}
|
||||||
Documentation=man:zfs-mount-generator(8)
|
Documentation=man:zfs-mount-generator(8)
|
||||||
Before=local-fs.target zfs-mount.service
|
|
||||||
After=${wants}
|
Before=${before}
|
||||||
|
After=${after}
|
||||||
Wants=${wants}
|
Wants=${wants}
|
||||||
|
${requires}
|
||||||
|
${requiredmounts}
|
||||||
|
|
||||||
[Mount]
|
[Mount]
|
||||||
Where=${p_mountpoint}
|
Where=${p_mountpoint}
|
||||||
|
@ -261,13 +419,20 @@ What=${dataset}
|
||||||
Type=zfs
|
Type=zfs
|
||||||
Options=defaults${opts},zfsutil" > "${dest_norm}/${mountfile}"
|
Options=defaults${opts},zfsutil" > "${dest_norm}/${mountfile}"
|
||||||
|
|
||||||
# Finally, create the appropriate dependency
|
# Finally, create the appropriate dependencies
|
||||||
ln -s "../${mountfile}" "${req_dir}"
|
create_dependencies "${mountfile}" "wants" "$wantedby"
|
||||||
|
create_dependencies "${mountfile}" "requires" "$requiredby"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Feed each line into process_line
|
|
||||||
for cachefile in "${FSLIST}/"* ; do
|
for cachefile in "${FSLIST}/"* ; do
|
||||||
|
# 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
|
while read -r fs ; do
|
||||||
process_line "${fs}"
|
process_line "${fs}"
|
||||||
done < "${cachefile}"
|
done
|
||||||
|
)
|
||||||
done
|
done
|
||||||
|
|
|
@ -89,6 +89,7 @@ EXTRA_DIST = \
|
||||||
|
|
||||||
$(nodist_man_MANS): %: %.in
|
$(nodist_man_MANS): %: %.in
|
||||||
-$(SED) -e 's,@zfsexecdir\@,$(zfsexecdir),g' \
|
-$(SED) -e 's,@zfsexecdir\@,$(zfsexecdir),g' \
|
||||||
|
-e 's,@systemdgeneratordir\@,$(systemdgeneratordir),g' \
|
||||||
-e 's,@runstatedir\@,$(runstatedir),g' \
|
-e 's,@runstatedir\@,$(runstatedir),g' \
|
||||||
-e 's,@sysconfdir\@,$(sysconfdir),g' \
|
-e 's,@sysconfdir\@,$(sysconfdir),g' \
|
||||||
$< >'$@'
|
$< >'$@'
|
||||||
|
|
|
@ -1,8 +1,33 @@
|
||||||
.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>
|
||||||
|
.\"
|
||||||
|
.\" Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
.\" a copy of this software and associated documentation files (the
|
||||||
|
.\" "Software"), to deal in the Software without restriction, including
|
||||||
|
.\" without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
.\" distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
.\" permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
.\" the following conditions:
|
||||||
|
.\"
|
||||||
|
.\" The above copyright notice and this permission notice shall be
|
||||||
|
.\" included in all copies or substantial portions of the Software.
|
||||||
|
.\"
|
||||||
|
.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
.\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
.TH "ZFS\-MOUNT\-GENERATOR" "8" "2020-01-19" "ZFS" "zfs-mount-generator" "\""
|
||||||
|
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
zfs\-mount\-generator \- generates systemd mount units for ZFS
|
zfs\-mount\-generator \- generates systemd mount units for ZFS
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B /lib/systemd/system-generators/zfs\-mount\-generator
|
.B @systemdgeneratordir@/zfs\-mount\-generator
|
||||||
.sp
|
.sp
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
zfs\-mount\-generator implements the \fBGenerators Specification\fP
|
zfs\-mount\-generator implements the \fBGenerators Specification\fP
|
||||||
|
@ -11,22 +36,50 @@ of
|
||||||
and is called during early boot to generate
|
and is called during early boot to generate
|
||||||
.BR systemd.mount (5)
|
.BR systemd.mount (5)
|
||||||
units for automatically mounted datasets. Mount ordering and dependencies
|
units for automatically mounted datasets. Mount ordering and dependencies
|
||||||
are created for all tracked pools (see below). If a dataset has
|
are created for all tracked pools (see below).
|
||||||
.BR canmount=on
|
|
||||||
and
|
|
||||||
.BR mountpoint
|
|
||||||
set, the
|
|
||||||
.BR auto
|
|
||||||
mount option will be set, and a dependency for
|
|
||||||
.BR local-fs.target
|
|
||||||
on the mount will be created.
|
|
||||||
|
|
||||||
Because zfs pools may not be available very early in the boot process,
|
.SS ENCRYPTION KEYS
|
||||||
information on ZFS mountpoints must be stored separately. The output
|
If the dataset is an encryption root, a service that loads the associated key (either from file or through a
|
||||||
of the command
|
.BR systemd\-ask\-password (1)
|
||||||
|
prompt) will be created. This service
|
||||||
|
. BR RequiresMountsFor
|
||||||
|
the path of the key (if file-based) and also copies the mount unit's
|
||||||
|
.BR After ,
|
||||||
|
.BR Before
|
||||||
|
and
|
||||||
|
.BR Requires .
|
||||||
|
All mount units of encrypted datasets add the key\-load service for their encryption root to their
|
||||||
|
.BR Wants
|
||||||
|
and
|
||||||
|
.BR After .
|
||||||
|
The service will not be
|
||||||
|
.BR Want ed
|
||||||
|
or
|
||||||
|
.BR Require d
|
||||||
|
by
|
||||||
|
.BR local-fs.target
|
||||||
|
directly, and so will only be started manually or as a dependency of a started mount unit.
|
||||||
|
|
||||||
|
.SS UNIT ORDERING AND DEPENDENCIES
|
||||||
|
mount unit's
|
||||||
|
.BR Before
|
||||||
|
\->
|
||||||
|
key\-load service (if any)
|
||||||
|
\->
|
||||||
|
mount unit
|
||||||
|
\->
|
||||||
|
mount unit's
|
||||||
|
.BR After
|
||||||
|
|
||||||
|
It is worth nothing that when a mount unit is activated, it activates all available mount units for parent paths to its mountpoint, i.e. activating the mount unit for /tmp/foo/1/2/3 automatically activates all available mount units for /tmp, /tmp/foo, /tmp/foo/1, and /tmp/foo/1/2. This is true for any combination of mount units from any sources, not just ZFS.
|
||||||
|
|
||||||
|
.SS CACHE FILE
|
||||||
|
Because ZFS pools may not be available very early in the boot process,
|
||||||
|
information on ZFS mountpoints must be stored separately. The output of the command
|
||||||
.PP
|
.PP
|
||||||
.RS 4
|
.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.openzfs.systemd:requires,org.openzfs.systemd:requires-mounts-for,org.openzfs.systemd:before,org.openzfs.systemd:after,org.openzfs.systemd:wanted-by,org.openzfs.systemd:required-by,org.openzfs.systemd:nofail,org.openzfs.systemd:ignore
|
||||||
|
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
for datasets that should be mounted by systemd, should be kept
|
for datasets that should be mounted by systemd, should be kept
|
||||||
|
@ -45,6 +98,98 @@ history_event-zfs-list-cacher.sh .
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
.sp
|
.sp
|
||||||
|
.SS PROPERTIES
|
||||||
|
The behavior of the generator script can be influenced by the following dataset properties:
|
||||||
|
.sp
|
||||||
|
.TP 4
|
||||||
|
.BR canmount = on | off | noauto
|
||||||
|
If a dataset has
|
||||||
|
.BR mountpoint
|
||||||
|
set and
|
||||||
|
.BR canmount
|
||||||
|
is not
|
||||||
|
.BR off ,
|
||||||
|
a mount unit will be generated.
|
||||||
|
Additionally, if
|
||||||
|
.BR canmount
|
||||||
|
is
|
||||||
|
.BR on ,
|
||||||
|
.BR local-fs.target
|
||||||
|
will gain a dependency on the mount unit.
|
||||||
|
|
||||||
|
This behavior is equal to the
|
||||||
|
.BR auto
|
||||||
|
and
|
||||||
|
.BR noauto
|
||||||
|
legacy mount options, see
|
||||||
|
.BR systemd.mount (5).
|
||||||
|
|
||||||
|
Encryption roots always generate a key-load service, even for
|
||||||
|
.BR canmount=off .
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:requires\-mounts\-for = \fIpath\fR...
|
||||||
|
Space\-separated list of mountpoints to require to be mounted for this mount unit
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:before = \fIunit\fR...
|
||||||
|
The mount unit and associated key\-load service will be ordered before this space\-separated list of units.
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:after = \fIunit\fR...
|
||||||
|
The mount unit and associated key\-load service will be ordered after this space\-separated list of units.
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:wanted\-by = \fIunit\fR...
|
||||||
|
Space-separated list of units that will gain a
|
||||||
|
.BR Wants
|
||||||
|
dependency on this mount unit.
|
||||||
|
Setting this property implies
|
||||||
|
.BR noauto .
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:required\-by = \fIunit\fR...
|
||||||
|
Space-separated list of units that will gain a
|
||||||
|
.BR Requires
|
||||||
|
dependency on this mount unit.
|
||||||
|
Setting this property implies
|
||||||
|
.BR noauto .
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:nofail = unset | on | off
|
||||||
|
Toggles between a
|
||||||
|
.BR Wants
|
||||||
|
and
|
||||||
|
.BR Requires
|
||||||
|
type of dependency between the mount unit and
|
||||||
|
.BR local-fs.target ,
|
||||||
|
if
|
||||||
|
.BR noauto
|
||||||
|
isn't set or implied.
|
||||||
|
|
||||||
|
.BR on :
|
||||||
|
Mount will be
|
||||||
|
.BR WantedBy
|
||||||
|
local-fs.target
|
||||||
|
|
||||||
|
.BR off :
|
||||||
|
Mount will be
|
||||||
|
.BR Before
|
||||||
|
and
|
||||||
|
.BR RequiredBy
|
||||||
|
local-fs.target
|
||||||
|
|
||||||
|
.BR unset :
|
||||||
|
Mount will be
|
||||||
|
.BR Before
|
||||||
|
and
|
||||||
|
.BR WantedBy
|
||||||
|
local-fs.target
|
||||||
|
.TP 4
|
||||||
|
.BR org.openzfs.systemd:ignore = on | off
|
||||||
|
If set to
|
||||||
|
.BR on ,
|
||||||
|
do not generate a mount unit for this dataset.
|
||||||
|
|
||||||
|
.RE
|
||||||
|
See also
|
||||||
|
.BR systemd.mount (5)
|
||||||
|
|
||||||
|
.PP
|
||||||
.SH EXAMPLE
|
.SH EXAMPLE
|
||||||
To begin, enable tracking for the pool:
|
To begin, enable tracking for the pool:
|
||||||
.PP
|
.PP
|
||||||
|
@ -63,7 +208,9 @@ systemctl enable zfs-zed.service
|
||||||
systemctl restart zfs-zed.service
|
systemctl restart zfs-zed.service
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.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
|
.PP
|
||||||
.RS 4
|
.RS 4
|
||||||
zfs set canmount=on
|
zfs set canmount=on
|
||||||
|
@ -71,6 +218,24 @@ zfs set canmount=on
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
This forces an update to the stale cache file.
|
This forces an update to the stale cache file.
|
||||||
|
|
||||||
|
To test the generator output, run
|
||||||
|
.PP
|
||||||
|
.RS 4
|
||||||
|
@systemdgeneratordir@/zfs-mount-generator /tmp/zfs-mount-generator . .
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
This will generate units and dependencies in
|
||||||
|
.I /tmp/zfs-mount-generator
|
||||||
|
for you to inspect them. The second and third argument are ignored.
|
||||||
|
|
||||||
|
If you're satisfied with the generated units, instruct systemd to re-run all generators:
|
||||||
|
.PP
|
||||||
|
.RS 4
|
||||||
|
systemctl daemon-reload
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
|
||||||
.sp
|
.sp
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.BR zfs (5)
|
.BR zfs (5)
|
||||||
|
|
Loading…
Reference in New Issue