changeset 334:fdbb78c54ffb

Begin the "ftjail freebsd-update" command
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 02 Dec 2022 09:36:57 +0100
parents ebf6c8863d9b
children ed0975c76386
files docs/conf.py docs/man/index8.rst docs/man/man8/ftjail-freebsd-update.rst docs/man/man8/ftjail.rst pkg-plist sbin/ftjail
diffstat 6 files changed, 194 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/docs/conf.py	Thu Dec 01 09:18:39 2022 +0100
+++ b/docs/conf.py	Fri Dec 02 09:36:57 2022 +0100
@@ -87,6 +87,7 @@
     ("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-copy-skel", "ftjail-copy-skel", "Recursively copy skeleton contents", [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),
     ("man/man8/ftjail-interlink-tmpl", "ftjail-interlink-tmpl", "Create proper symlinks for \"skeleton\" style Thin Jails", [author], 8),
     ("man/man8/ftjail-mount-tmpl", "ftjail-mount-tmpl", "Canonically mount the RO base and the RW skeleton of a Thin Jail", [author], 8),
 ("man/man8/ftjail-populate-tmpl", "ftjail-populate-tmpl", "Populate a prepared directory structure with the contents of a FreeBSD base system", [author], 8),
--- a/docs/man/index8.rst	Thu Dec 01 09:18:39 2022 +0100
+++ b/docs/man/index8.rst	Fri Dec 02 09:36:57 2022 +0100
@@ -18,6 +18,7 @@
    man8/ftjail-build-etcupdate-current-tmpl
    man8/ftjail-copy-skel
    man8/ftjail-datasets-tmpl
+   man8/ftjail-freebsd-update
    man8/ftjail-interlink-tmpl
    man8/ftjail-mount-tmpl
    man8/ftjail-populate-tmpl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/man/man8/ftjail-freebsd-update.rst	Fri Dec 02 09:36:57 2022 +0100
@@ -0,0 +1,33 @@
+.. -*- coding: utf-8; indent-tabs-mode: nil; -*-
+
+ftjail-freebsd-update
+=====================
+
+Synopsis
+--------
+
+**ftjail freebsd-update** `directory`
+
+
+Description
+-----------
+
+A :manpage:`freebsd-update(8)` for a Thin Jail.
+
+
+Options
+-------
+
+.. program:: ftjail freebsd-update
+
+
+Environment
+-----------
+
+All environment variables that affect :command:`zfs` are effective also.
+
+
+See Also
+--------
+
+:manpage:`ftjail(8)`, :manpage:`freebsd-update(8)`, :manpage:`etcupdate(8)`
--- a/docs/man/man8/ftjail.rst	Thu Dec 01 09:18:39 2022 +0100
+++ b/docs/man/man8/ftjail.rst	Fri Dec 02 09:36:57 2022 +0100
@@ -42,6 +42,10 @@
 
     Create ZFS template datasets for new Thin Jails using base and skeleton
 
+:manpage:`ftjail-freebsd-update(8)`
+
+    A :manpage:`freebsd-update(8)` implementation for Thin Jails
+
 :manpage:`ftjail-mount-tmpl(8)`
 
     Canonically mount the RO base and the RW skeleton of a Thin Jail
--- a/pkg-plist	Thu Dec 01 09:18:39 2022 +0100
+++ b/pkg-plist	Fri Dec 02 09:36:57 2022 +0100
@@ -16,6 +16,7 @@
 %%DOCS%%man/man8/ftjail-build-etcupdate-current-tmpl.8.gz
 %%DOCS%%man/man8/ftjail-copy-skel.8.gz
 %%DOCS%%man/man8/ftjail-datasets-tmpl.8.gz
+%%DOCS%%man/man8/ftjail-freebsd-update.8.gz
 %%DOCS%%man/man8/ftjail-interlink-tmpl.8.gz
 %%DOCS%%man/man8/ftjail-mount-tmpl.8.gz
 %%DOCS%%man/man8/ftjail-populate-tmpl.8.gz
--- a/sbin/ftjail	Thu Dec 01 09:18:39 2022 +0100
+++ b/sbin/ftjail	Fri Dec 02 09:36:57 2022 +0100
@@ -103,6 +103,90 @@
 }
 
 
+#:
+#: Search for a running jail where it's "path" points to a given location
+#:
+#: Args:
+#:   $1: the location to search for
+#:
+#: Output (stdout):
+#:   The name if the jail with a "path" that is equal to the input param.
+#:   Nothing if a jail is not found.
+#:
+#: Return:
+#:   - 0: if a running jail is found
+#:   - 1: error
+#:   - 2: no running jail found
+#:   - 3: jail found but currently dying
+#:
+_get_jail_from_path() {
+  local _location
+
+  local _name _path _dying
+
+  _location="${1-}"
+  [ -z "${_location}" ] && { echo "ERROR: no mountpoint given" 1>&2; return 1; }
+
+
+  jls -d name path dying \
+  | {
+      while IFS=' '$'\t' read -r _name _path _dying ; do
+        if [ "${_path}" = "${_location}" ]; then
+          if [ "${_dying}" != "false" ]; then
+            echo "Jail \`${_name}' is currently dying" 1>&2
+            return 3
+          fi
+          echo "${_name}"
+          return 0
+        fi
+      done
+      return 2
+  }
+}
+
+
+#:
+#: Search for mounts and sub-mounts at a given directory.
+#:
+#: The output is sorted by the mountpoint.
+#:
+#: Args:
+#:   $1: the directory where to start for mounts and sub-mounts
+#:
+#: Output (stdout):
+#:   The sorted list (lines) of mounts in :manpage:`fstab(5)` format.
+#:   This list may be empty.
+#:
+#: Exit:
+#:   1: on fatal errors (usage et al.)
+#:
+#: Important:
+#:   The input directory **must** be an absolute path.
+#:
+_get_mounts_at_directory() {
+    local _directory
+
+    local _fstab
+
+    _directory=${1-}
+    case "${_directory}" in
+        /*)
+            :
+            ;;
+        '')
+            echo "ERROR: no directory given" 1>&2;
+            exit 1;
+            ;;
+        *)
+            echo "ERROR: directory must be an absolute path" 1>&2;
+            exit 1;
+            ;;
+    esac
+    _fstab="$(mount -p | grep -E '\s'"${_directory}" | sort -k3)"
+    echo "${_fstab}"
+}
+
+
 #
 # PARENT-BASE NAME DRY-RUN
 #
@@ -787,6 +871,73 @@
 }
 
 
+#:
+#: Implement the "freebsd-update" command for a thin jail
+#:
+command_freebsd_update() {
+    local _directory
+
+    local _res _jailname _dir_mounts _dir_fn_fstab _dir_basename
+    local _root_dataset _root_mountpoint _root_type _root_options
+    local _dummy
+    local _root_readonly
+
+    _directory="${1-}"
+
+    [ -z "${_directory}" ] && { echo "ERROR: no directory given" 1>&2; return 2; }
+    [ -d "${_directory}" ] || { echo "ERROR: directory \`${_directory}' does not exist" 1>&2; exit 1; }
+
+    _dir_basename="$(basename ${_directory})"
+
+    set +e
+    _jailname=$(_get_jail_from_path "${_directory}")ยด
+    _res=$?
+    set -e
+    if [ ${_res} -ne 2 ] ; then
+        if [ ${_res} -ne 0 ] ; then
+            exit ${_res}
+        else
+            echo "ERROR: Please stop the \`${_jailname}' jail" >&2
+            exit 1
+        fi
+    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.
+    # Also check that it is a clone proper.
+    #
+    IFS=' '$'\t' read -r _root_dataset _root_mountpoint _root_type _root_options _dummy <<EOF4tHGCSS
+${_dir_mounts}
+EOF4tHGCSS
+    [ "${_root_mountpoint}" != "${_directory}" ] && { echo "ERROR: found root mountpoint does not match given directory" 1>&2; exit 1; }
+    [ "${_root_type}" != "zfs" ] && { echo "ERROR: root mountpoint is not from a ZFS dataset" 1>&2; exit 1; }
+    _root_readonly="$(zfs list -H -o readonly "${_root_dataset}")"
+    [ "${_root_readonly}" != "on" ] &&  { echo "ERROR: the root dataset is not mounted read-only" 1>&2; exit 1; }
+    # XXX TBD: Check that it is a proper clone
+    
+    #
+    # XXX FIXME: should we check that _root_options equals "ro" or
+    #            start with "ro,"
+    #
+    _dir_fn_fstab="$(env TMPDIR=/var/tmp mktemp -t ftjail-fstab.${_dir_basename})"
+    echo -n "${_dir_mounts}" >>"${_dir_fn_fstab}"
+
+    # Unmount in reverse order: unmount can do it for us
+    umount -a -F "${_dir_fn_fstab}" -v || exit 1
+
+    #
+    # XXX TBD: Hooks to create some new top-level dirs (/srv /proc et
+    #          al.)  if needed: clone RW, mount, make the dirs,
+    #          umount, make the clone RO and continue "normally" by
+    #          completely mounting the stored fstab.
+    # 
+}
+
+
 #
 # Global option handling
 #
@@ -851,6 +1002,9 @@
         echo "ERROR: use \`fjail configure' instead" 1>&2;
         exit 2
         ;;
+    freebsd-update)
+        command_freebsd_update "$@"
+        ;;
     *)
         echo "ERROR: unknown command \`${command}'" 1>&2
         exit 2