changeset 658:260a81d769a6

Implemented "ftjail check-freebsd-update": thorough check for preconditions to a successful run of "ftjail freebsd-update". All checks that "ftjail freebsd-update" does are replicated in "ftjail check-freebsd-update".
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 28 Sep 2024 19:12:05 +0200
parents db22766df6a4
children 25de0d27e407
files docs/conf.py docs/man/index.rst docs/man/man8/ftjail-check-freebsd-update.rst docs/man/man8/ftjail-freebsd-update.rst docs/man/man8/ftjail.rst docs/man/man8/local-bsdtools.rst pkg-plist sbin/ftjail
diffstat 8 files changed, 265 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/docs/conf.py	Sat Sep 28 14:55:57 2024 +0200
+++ b/docs/conf.py	Sat Sep 28 19:12:05 2024 +0200
@@ -94,6 +94,7 @@
     ("man/man8/fpkg", "fpkg", "A frontend for some pkg(8) commands that also operate on running jails", [author], 8),
     ("man/man8/ftjail", "ftjail", "Management of Thin Jails", [author], 8),
     ("man/man8/ftjail-build-etcupdate-current-tmpl", "ftjail-build-etcupdate-current-tmpl", "Build a \"current\" tree suitable for the default and extract mode of \"etcupdate\"", [author], 8),
+    ("man/man8/ftjail-check-freebsd-update", "ftjail-check-freebsd-update", "Check preconditions to run freebsd-update for a Thin Jail successfully", [author], 8),
     ("man/man8/ftjail-copy-skel", "ftjail-copy-skel", "Recursively copy skeleton contents from the template tree into a jail-specific ZFS datasets", [author], 8),
     ("man/man8/ftjail-datasets-tmpl", "ftjail-datasets-tmpl", "Create ZFS datasets for new Thin Jails using base and skeleton", [author], 8),
     ("man/man8/ftjail-freebsd-update", "ftjail-freebsd-update", "A freebsd-update implementation for a Thin Jail", [author], 8),
--- a/docs/man/index.rst	Sat Sep 28 14:55:57 2024 +0200
+++ b/docs/man/index.rst	Sat Sep 28 19:12:05 2024 +0200
@@ -20,6 +20,7 @@
    man8/fwireguard
    man8/ftjail
    man8/ftjail-build-etcupdate-current-tmpl
+   man8/ftjail-check-freebsd-update   
    man8/ftjail-copy-skel
    man8/ftjail-datasets-tmpl
    man8/ftjail-freebsd-update
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/man/man8/ftjail-check-freebsd-update.rst	Sat Sep 28 19:12:05 2024 +0200
@@ -0,0 +1,63 @@
+.. -*- coding: utf-8; indent-tabs-mode: nil; -*-
+
+ftjail-check-freebsd-update
+===========================
+
+.. program:: ftjail freebsd-update
+
+
+Synopsis
+--------
+
+**ftjail check-freebsd-update** [**-k**] [**-o** `old-origin`] `directory` `new-origin` [`etcupdate-tarball`]
+
+
+Description
+-----------
+
+A thorough check for preconditions to run :manpage:`ftjail-freebsd-update(8)`
+for a Thin Jail successfully.
+
+It does all checks that :manpage:`ftjail-freebsd-update(8)` does but
+tries to collect all errors.
+
+It is intended that a successful run of :command:`ftjail check-freebsd-update`
+implies a successful run of :command:`ftjail freebsd-update` if the same
+arguments are given.
+
+
+Options
+-------
+
+.. option:: -k
+
+   Does nothing.
+   Implemented for compatibility to :manpage:`ftjail-freebsd-update(8)`.
+
+.. option:: -o <old-origin>
+
+   In addition to check that `directory` is a ZFS clone also check that
+   its origin is equal to `old-origin`.
+
+   Note that a check that `directory` is a ZFS clone with some origin
+   is done by default.
+
+
+Environment
+-----------
+
+All environment variables that affect :command:`zfs` are effective also.
+
+
+Files
+-----
+
+A unique temporary directory is created within :file:`/var/tmp`. All
+temporary files are created within this directory.
+
+
+See Also
+--------
+
+:manpage:`ftjail(8)`, :manpage:`ftjail-freebsd-update(8)`,
+:manpage:`freebsd-update(8)`, :manpage:`etcupdate(8)`
--- a/docs/man/man8/ftjail-freebsd-update.rst	Sat Sep 28 14:55:57 2024 +0200
+++ b/docs/man/man8/ftjail-freebsd-update.rst	Sat Sep 28 19:12:05 2024 +0200
@@ -20,6 +20,9 @@
 the fully re-mounted directory tree that is rooted at `directory`
 using `etcupdate-tarball` as the new "current".
 
+See also :manpage:`ftjail-check-freebsd-update(8)` for a tool that
+tries to thoroughly check the preconditions to run this tool successfully.
+
 
 Options
 -------
@@ -57,4 +60,5 @@
 See Also
 --------
 
-:manpage:`ftjail(8)`, :manpage:`freebsd-update(8)`, :manpage:`etcupdate(8)`
+:manpage:`ftjail(8)`, :manpage:`ftjail-check-freebsd-update(8)`,
+:manpage:`freebsd-update(8)`, :manpage:`etcupdate(8)`
--- a/docs/man/man8/ftjail.rst	Sat Sep 28 14:55:57 2024 +0200
+++ b/docs/man/man8/ftjail.rst	Sat Sep 28 19:12:05 2024 +0200
@@ -38,6 +38,11 @@
     Build a "current" tree suitable for the default and extract mode
     of \"etcupdate\"
 
+:manpage:`ftjail-check-freebsd-update(8)`
+
+    Check for preconditions to run :manpage:`ftjail-freebsd-update(8)`
+    for a Thin Jail successfully
+
 :manpage:`ftjail-copy-skel(8)`
 
     Recursively copy template skeleton contents into jail-specific datasets
--- a/docs/man/man8/local-bsdtools.rst	Sat Sep 28 14:55:57 2024 +0200
+++ b/docs/man/man8/local-bsdtools.rst	Sat Sep 28 19:12:05 2024 +0200
@@ -55,6 +55,7 @@
 - :manpage:`ftjail(8)`
 
   * :manpage:`ftjail-build-etcupdate-current-tmpl(8)`
+  * :manpage:`ftjail-check-freebsd-update(8)`    
   * :manpage:`ftjail-copy-skel(8)`
   * :manpage:`ftjail-datasets-tmpl(8)`
   * :manpage:`ftjail-freebsd-update(8)`
--- a/pkg-plist	Sat Sep 28 14:55:57 2024 +0200
+++ b/pkg-plist	Sat Sep 28 19:12:05 2024 +0200
@@ -27,6 +27,7 @@
 %%DOCS%%share/man/man8/fpkg.8.gz
 %%DOCS%%share/man/man8/ftjail.8.gz
 %%DOCS%%share/man/man8/ftjail-build-etcupdate-current-tmpl.8.gz
+%%DOCS%%share/man/man8/ftjail-check-freebsd-update.8.gz
 %%DOCS%%share/man/man8/ftjail-copy-skel.8.gz
 %%DOCS%%share/man/man8/ftjail-datasets-tmpl.8.gz
 %%DOCS%%share/man/man8/ftjail-freebsd-update.8.gz
--- a/sbin/ftjail	Sat Sep 28 14:55:57 2024 +0200
+++ b/sbin/ftjail	Sat Sep 28 19:12:05 2024 +0200
@@ -61,6 +61,7 @@
 _p_datadir='@@DATADIR@@'
 [ "${_p_datadir#@@DATADIR}" = '@@' ] && _p_datadir="$(dirname "$0")"/../share/local-bsdtools
 . "${_p_datadir}/common.subr"
+. "${_p_datadir}/farray.sh"
 
 
 # Reset to standard umask
@@ -858,6 +859,189 @@
 
 
 #:
+#: Callback for _print_check_errors
+#:
+_print_check_error() {
+    printf '%s CHECK: %s\n' '-' "$3" 1>&2
+    return 0
+}
+
+
+#:
+#: Print all the errors to stderr
+#:
+_print_check_errors() {
+    farray_istrue "$1" || return 0
+    echo "There are ERRORs to be resolved before \`freebsd-update' can be run:" 1>&2
+    farray_for_each "$1" _print_check_error
+}
+
+
+#:
+#: Callback for _print_check_warnings
+#:
+_print_check_warning() {
+    printf '%s WARNING: %s\n' '-' "$3" 1>&2
+    return 0
+}
+
+
+#:
+#: Print all the warnings to stderr
+#:
+_print_check_warnings() {
+    farray_istrue "$1" || return 0
+    echo "There are WARNINGs to be considered before \`freebsd-update' should be run:" 1>&2
+    farray_for_each "$1" _print_check_warning
+}
+
+
+#:
+#: Implement the "check-freebsd-update" command for a thin jail
+#:
+command_check_freebsd_update() {
+    local _directory _new_origin _etcupdate_tarball
+    local _opt_keep _opt_old_origin
+
+    local _errors _warnings _rc
+    local _directory _new_origin _etcupdate_tarball
+    local _dir_basename _dir_mounts _jailname _tmp _line _log_sock
+    local _root_dataset _root_mountpoint _root_type _root_options
+    local _mnt_device _mnt_mountpoint _mnt_type _mnt_options
+
+    _rc=0
+
+    _warnings=''
+    farray_create _warnings XXX
+    _errors=''
+    farray_create _errors
+
+    _opt_keep="no"
+    _opt_old_origin=""
+    while getopts "ko:" _opt ; do
+        case "${_opt}" in
+            k)
+                _opt_keep="yes"
+                ;;
+            o)
+                _opt_old_origin="$OPTARG"
+                ;;
+            \?|:)
+                return 2;
+                ;;
+        esac
+    done
+    shift $((OPTIND-1))
+    OPTIND=1
+
+    _directory="${1-}"
+    _new_origin="${2-}"
+    _etcupdate_tarball="${3-}"
+
+    if [ -z "${_directory}" ]; then
+        farray_append _errors "no directory given"
+    else
+        [ -d "${_directory}" ] || farray_append _errors "directory \`${_directory}' does not exist"
+    fi
+    if [ -z "${_new_origin}" ]; then
+        farray_append _errors "no new origin given"
+    else
+        zfs list -H -o name -t snapshot "${_new_origin}" >/dev/null 2>/dev/null || farray_append  _errors "ZFS dataset snapshot for the new origin \`${_new_origin}' does not exist"
+    fi
+    if [ -n "${_etcupdate_tarball}" ]; then
+        [ -r "${_etcupdate_tarball}" ] || farray_append _errors "given etcupdate tarball does not exist and/or is not readable"
+    fi
+
+    if [ -n "${_directory}" ]; then
+
+        _dir_basename="$(basename "${_directory}")"
+
+        set +e
+        _jailname="$(_get_jail_from_path "${_directory}")"
+        _tmp=$?
+        set -e
+        case ${_tmp} in
+            0)
+                farray_append _errors "Jail \`${_jailname}' is running. Please stop it."
+                ;;
+            1)
+                farray_append _errors "Cannot determine jail name"
+                ;;
+            3)
+                true
+                ;;
+            2)
+                farray_append _errors "Jail \`${_jailname}' is currently yet dying. Please wait."
+                ;;
+            *)
+                farray_append _errors "UNHANDLED RETURN VALUE from _get_jail_from_path()"
+                ;;
+        esac
+
+        #
+        # Check whether additional log sockets are opened at their default
+        # locations. Because they hinder proper unmounting of filesystems.
+        #
+        for _log_sock in /var/run/log /var/run/logpriv ; do
+            if [ -S "${_directory}${_log_sock}" ]; then
+                farray_append _errors "log socket is open at \`${_directory}${_log_sock}'"
+            fi
+        done
+
+        # Check whether there are any open files or VM mappings  within the jail.
+        if ! _check_no_open_files_from_all_proc "${_directory}" ; then
+            farray_append _errors "There are open files or memory mappings within the jail"
+        fi
+
+        _dir_mounts="$(_get_mounts_at_directory "${_directory}")"
+
+        #
+        # Check preconditions thoroughly!
+        #
+        # Check that the first item/line is a read-only ZFS mount directly
+        # at the given directory. This must also be its configured
+        # mountpoint in ZFS.
+        # Also check that it is a clone proper.
+        #
+        IFS=$'\t' read -r _root_dataset _root_mountpoint _root_type _root_options _line <<EOF4tHGCSSf5d7d9cf
+${_dir_mounts}
+EOF4tHGCSSf5d7d9cf
+        [ "${_root_mountpoint}" != "${_directory}" ] && farray_append _errors "found root mountpoint does not match given directory"
+        [ "${_root_type}" != "zfs" ] && farray_append _errors "root mountpoint is not from a ZFS dataset"
+        _root_readonly="$(zfs get -H -o value readonly "${_root_dataset}")"
+        [ "${_root_readonly}" != "on" ] &&  farray_append _errors "the root dataset is not mounted read-only"
+        _root_origin="$(zfs get -H -o value origin "${_root_dataset}")"
+        if [ -n "${_opt_old_origin}" ]; then
+            [ "${_opt_old_origin}" != "${_root_origin}" ] && farray_append _errors "origin mismatch"
+        else
+            [ "${_root_origin}" = '-' ] &&  farray_append _errors "the root dataset is not a ZFS clone"
+        fi
+        #
+        # Check for open files on all the mounted filesystems
+        #
+        while IFS=$'\t' read -r _mnt_device _mnt_mountpoint _mnt_type _mnt_options _line; do
+            if ! _check_no_open_files_on_filesystem "${_mnt_mountpoint}" ; then
+                farray_append _errors "There are open files or memory mapping on file system \`${_mnt_mountpoint}'"
+            fi
+        done <<EOF4tHGCAASL775f9f320205
+${_dir_mounts}
+EOF4tHGCAASL775f9f320205
+    fi
+
+    if farray_istrue _errors; then
+        _print_check_errors _errors
+        _rc=1
+    fi
+    # Warnings do not influence the return code
+    _print_check_warnings _warnings
+
+    farray_destroy _errors
+    farray_destroy _warnings
+    return ${_rc}
+}
+
+
+#:
 #: Implement the "freebsd-update" command for a thin jail
 #:
 #: .. note:: FreeBSD's :command:`etcupdate` also executes
@@ -1160,11 +1344,13 @@
         echo "ERROR: use \`fjail configure' instead" 1>&2;
         exit 2
         ;;
+    check-freebsd-update)
+        command_check_freebsd_update "$@"
+        ;;
     freebsd-update)
         command_freebsd_update "$@"
         ;;
     *)
-        echo "ERROR: unknown command \`${command}'" 1>&2
-        exit 2
+        fatal 2 "unknown command \`${command}'"
         ;;
 esac