diff sbin/ftjail @ 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 8dbd11726ee5
children a423bc0e2a3f
line wrap: on
line diff
--- 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