changeset 814:2310764e5c4e

fports: Implement a global "--jail/-j" option: execute the fports commands in a jail. The jail just needs to have an installed "pkg" command.
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 09 Nov 2024 00:47:05 +0100
parents 70e08ff3db45
children e2f262ec2bf4
files docs/man/man8/fports.rst sbin/fports share/local-bsdtools/common.subr share/local-bsdtools/ports.subr tests/ports.t
diffstat 5 files changed, 257 insertions(+), 106 deletions(-) [+]
line wrap: on
line diff
--- a/docs/man/man8/fports.rst	Fri Nov 08 12:57:59 2024 +0100
+++ b/docs/man/man8/fports.rst	Sat Nov 09 00:47:05 2024 +0100
@@ -13,9 +13,9 @@
 
 **fports**  **-V**\|\ **--version**
 
-**fports deptree** [**-l** `maxlevel`\|\ **--maxlevel**\=\ `maxlevel`] [**-r**\|\ **--reverse**] [**-t**\|\ **--list**\|\ **--transitive**] `package` ...
+**fports** [**-j** `jail`\|\ **--jail**\=\ `jail`] **deptree** [**-l** `maxlevel`\|\ **--maxlevel**\=\ `maxlevel`] [**-r**\|\ **--reverse**] [**-t**\|\ **--list**\|\ **--transitive**] `package` ...
 
-**fports detail**  [**-A**] [**-b**\|\ **--nofreebsd**\|\ **--no-freebsd**] [**-f**\ |\ **--filter-unused**] [**-n**\|\ **--noauto**\|\ **--no-auto**] [**-m**\|\ **--mapped**] [`package` ...]
+**fports** [**-j** `jail`\|\ **--jail**\=\ `jail`] **detail** [**-A**] [**-b**\|\ **--nofreebsd**\|\ **--no-freebsd**] [**-f**\ |\ **--filter-unused**] [**-n**\|\ **--noauto**\|\ **--no-auto**] [**-m**\|\ **--mapped**] [`package` ...]
 
 
 Description
@@ -33,6 +33,18 @@
 This tools is the successor of :manpage:`check-ports(8)`.
 
 
+Global Options
+--------------
+
+These global options are implemented for all subcommands:
+
+.. option:: -j <name_or_id>, --jail=<name_or_id>
+
+   The script will execute in the given jail `name or id`, where name
+   matches :command:`jls name` and id matches :command:`jls jid`.
+   See  :manpage:`jail(8)`  and :manpage:`jls(8)`.
+
+
 Subcommands
 -----------
 
@@ -47,7 +59,7 @@
    Print the program name and version number to stdout and exit.
 
 
-**fports deptree** [**-l** `maxlevel`\|\ **--maxlevel**\=\ `maxlevel`] [**-r**\|\ **--reverse**] [**-t**\|\ **--list**\|\ **--transitive**] `package` ...
+**fports** [**-j** `jail`\|\ **--jail**\=\ `jail`] **deptree** [**-l** `maxlevel`\|\ **--maxlevel**\=\ `maxlevel`] [**-r**\|\ **--reverse**] [**-t**\|\ **--list**\|\ **--transitive**] `package` ...
 
   Print a dependency tree for every given `package`. A package tree is
   a hierarchical list of packages that `packages` depends on.
@@ -71,7 +83,7 @@
      that depend on a given `package`.
 
 
-**fports detail**  [**-A**] [**-b**\|\ **--nofreebsd**\|\ **--no-freebsd**] [**-f**\ |\ **--filter-unused**] [**-n**\|\ **--noauto**\|\ **--no-auto**] [**-m**\|\ **--mapped**] [`package` ...]
+**fports** [**-j** `jail`\|\ **--jail**\=\ `jail`] **detail**  [**-A**] [**-b**\|\ **--nofreebsd**\|\ **--no-freebsd**] [**-f**\ |\ **--filter-unused**] [**-n**\|\ **--noauto**\|\ **--no-auto**] [**-m**\|\ **--mapped**] [`package` ...]
 
   Print the status of all given or selected packages in the most
   detail possible.
--- a/sbin/fports	Fri Nov 08 12:57:59 2024 +0100
+++ b/sbin/fports	Sat Nov 09 00:47:05 2024 +0100
@@ -23,8 +23,8 @@
 USAGE='
 USAGE: fports -h|--help
        fports -V|--version
-       fports deptree [-l maxlevel|--maxlevel=maxlevel] [-r|--reverse] [-t|--list|--transitive] package...
-       fports detail [-A] [-b|--nofreebsd|--no-freebsd] [-f|--filter-unused] [-n|--noauto|--no-auto] [-m|--mapped] [package...]
+       fports [GLOBAL-OPTIONS] deptree [-l maxlevel|--maxlevel=maxlevel] [-r|--reverse] [-t|--list|--transitive] package...
+       fports [GLOBAL-OPTIONS] detail [-A] [-b|--nofreebsd|--no-freebsd] [-f|--filter-unused] [-n|--noauto|--no-auto] [-m|--mapped] [package...]
 
 GLOBAL OPTIONS:
 
@@ -32,6 +32,9 @@
 
   -h, --help     Print this help message to stdout and exit.
 
+  -j JAIL, --jail=JAIL
+                 Execute in given jail JAIL.
+
 '
 
 
@@ -66,12 +69,18 @@
 #:
 #: Implementation of the "deptree" command.
 #:
+#: Args:
+#:   $1 (str, null): The jail to attach to
+#:   $2...: the command's options and arguments
+#:
 command_deptree() {
-    local opt_reversed opt_maxlevel opt_flat
+    local opt_jail opt_reversed opt_maxlevel opt_flat
     # $@
 
     local opt
 
+    opt_jail="${1}"
+    shift
     opt_maxlevel=0
     opt_reversed=no
     opt_flat=no
@@ -94,10 +103,14 @@
     shift $((OPTIND-1))
     OPTIND=1
 
+    if [ -n "${opt_jail}" ]; then
+        can_call_command "${opt_jail}" || fatal "${EX_UNAVAILABLE}" "jail \`${opt_jail}' does not exist or attaching denied"
+    fi
+
     if checkyesno opt_reversed; then
-        _command_deptree_reversed "${opt_maxlevel}" "${opt_flat}" "$@"
+        _command_deptree_reversed "${opt_jail}" "${opt_maxlevel}" "${opt_flat}" "$@"
     else
-        _command_deptree_normal "${opt_maxlevel}" "${opt_flat}" "$@"
+        _command_deptree_normal "${opt_jail}" "${opt_maxlevel}" "${opt_flat}" "$@"
     fi
 }
 
@@ -106,13 +119,14 @@
 #: Implementation of printing a "normal" dependency tree
 #:
 _command_deptree_normal() {
-    local maxlevel flat     # $@
+    local jail maxlevel flat     # $@
 
     local pkgdeps pkgqueue curdeps pkg n v flatdeps
 
-    maxlevel="${1}"
-    flat="${2}"
-    shift 2
+    jail="${1}"
+    maxlevel="${2}"
+    flat="${3}"
+    shift 3
 
     # shellcheck disable=SC2034    # pkgqueue seems unused
     pkgqueue=''
@@ -120,7 +134,7 @@
                             # resolution
 
     for pkg in "$@"; do
-        if ! "${PKG}" query '%n' "${pkg}" 1>/dev/null 2>/dev/null ; then
+        if ! call_pkg "${opt_jail}" query '%n' "${pkg}" 1>/dev/null 2>/dev/null ; then
             farray_release pkgqueue
             fatal "${EX_DATAERR}" "Package not found: ${pkg}"
         fi
@@ -137,7 +151,7 @@
                 farray_append curdeps "${n}=${v}"
                 farray_append pkgqueue "${n}"
             done <<EOF_01a8cebe-8659-4e32-87a4-bbce117e386b
-$(LC_ALL=C.UTF-8 "${PKG}" query '%dn %dv' "${pkg}")
+$(call_pkg "${opt_jail}" query '%dn %dv' "${pkg}")
 EOF_01a8cebe-8659-4e32-87a4-bbce117e386b
             falist_set pkgdeps "${pkg}" "${curdeps}"
             farray_release curdeps
@@ -149,12 +163,12 @@
     if checkyesno flat; then
         for pkg in "$@"; do
             _flatten_pkgdeps flatdeps "${pkgdeps}" "${pkg}"
-            _print_flatdeps '-->' "${pkg}" "$(LC_ALL=C.UTF-8 "${PKG}" query '%v' "${pkg}")" "${flatdeps}"
+            _print_flatdeps '-->' "${pkg}" "$(call_pkg "${opt_jail}" query '%v' "${pkg}")" "${flatdeps}"
             falist_release "${flatdeps}"
         done
     else
         for pkg in "$@"; do
-            _print_dependency_tree 0 "${maxlevel}" '-->' "${pkg}" "$(LC_ALL=C.UTF-8 "${PKG}" query '%v' "${pkg}")" "${pkgdeps}"
+            _print_dependency_tree 0 "${maxlevel}" '-->' "${pkg}" "$(call_pkg "${opt_jail}" query '%v' "${pkg}")" "${pkgdeps}"
         done
     fi
     falist_release pkgdeps
@@ -165,14 +179,15 @@
 #: Implementation of printing a reversed dependency tree
 #:
 _command_deptree_reversed() {
-    local maxlevel flat  # $@
+    local jail maxlevel flat  # $@
 
     local pkgdeps pkgqueue curdeps pkg n v flatdeps
 
-    maxlevel="${1}"
+    jail="${1}"
+    maxlevel="${2}"
     # shellcheck disable=SC2034    # appears unused
-    flat="${2}"
-    shift 2
+    flat="${3}"
+    shift 3
 
     # shellcheck disable=SC2034    # pkgqueue seems unused
     pkgqueue=''
@@ -180,7 +195,7 @@
                             # resolution
 
     for pkg in "$@"; do
-        if ! "${PKG}" query '%n' "${pkg}" 1>/dev/null 2>/dev/null ; then
+        if ! call_pkg "${opt_jail}" query '%n' "${pkg}" 1>/dev/null 2>/dev/null ; then
             farray_release pkgqueue
             fatal "${EX_DATAERR}" "Package not found: ${pkg}"
         fi
@@ -197,7 +212,7 @@
                 farray_append curdeps "${n}=${v}"
                 farray_append pkgqueue "${n}"
             done <<EOF_5079e996-c6d2-4e6d-825d-53183a64ab06
-$(LC_ALL=C.UTF-8 "${PKG}" query '%rn %rv' "${pkg}")
+$(call_pkg "${opt_jail}" query '%rn %rv' "${pkg}")
 EOF_5079e996-c6d2-4e6d-825d-53183a64ab06
             falist_set pkgdeps "${pkg}" "${curdeps}"
             farray_release curdeps
@@ -209,12 +224,12 @@
     if checkyesno flat; then
         for pkg in "$@"; do
             _flatten_pkgdeps flatdeps "${pkgdeps}" "${pkg}"
-            _print_flatdeps '<--' "${pkg}" "$(LC_ALL=C.UTF-8 "${PKG}" query '%v' "${pkg}")" "${flatdeps}"
+            _print_flatdeps '<--' "${pkg}" "$(call_pkg "${opt_jail}" query '%v' "${pkg}")" "${flatdeps}"
             falist_release "${flatdeps}"
         done
     else
         for pkg in "$@"; do
-            _print_dependency_tree 0 "${maxlevel}" '<--' "${pkg}" "$(LC_ALL=C.UTF-8 "${PKG}" query '%v' "${pkg}")" "${pkgdeps}"
+            _print_dependency_tree 0 "${maxlevel}" '<--' "${pkg}" "$(call_pkg "${opt_jail}" query '%v' "${pkg}")" "${pkgdeps}"
         done
     fi
     falist_release pkgdeps
@@ -343,8 +358,13 @@
 #:
 #: Implementation of the "detail" command.
 #:
+#: Args:
+#:   $1 (str, null): The jail to attach to
+#:   $2...: The command's options and arguments
+#:
 command_detail() {
-    local opt_nofreebsd  opt_filterunused opt_noauto opt_mapped opt_nofreebsd
+    local opt_jail opt_nofreebsd  opt_filterunused opt_noauto \
+          opt_mapped opt_nofreebsd
     # $@
 
     local package \
@@ -353,6 +373,8 @@
           repo title_printed indexfile _dummy opt acookie \
           pkglabel pkgdescr pkgversion mapped_package
 
+    opt_jail="${1}"
+    shift
     do_sort=no
     opt_filterunused=no
     opt_nofreebsd=no
@@ -394,24 +416,27 @@
     shift $((OPTIND-1))
     OPTIND=1
 
+    if [ -n "${opt_jail}" ]; then
+        can_call_command "${opt_jail}" || fatal "${EX_UNAVAILABLE}" "jail \`${opt_jail}' does not exist or attaching denied"
+    fi
+
     packages=''
     farray_create packages
     repositories=''
-    get_active_repositories repositories
+    get_active_repositories repositories "${opt_jail}"
     packagemapping=''
-    init_package_mapping packagemapping
-    falist_set packagemapping "uwsginl" "uwsgi"
-    indexfile="$(get_local_index_file)"
+    init_package_mapping packagemapping "${opt_jail}"
+    indexfile="$(get_local_index_file "${opt_jail}")"
 
     if checkyesno opt_nofreebsd; then
         while IFS='|' read -r package repo; do
             [ "${repo}" != 'FreeBSD' ] && farray_append packages "${package}"
         done <<EOF_1fa6f326-49e6-4b01-a7ea-52372d00df1e
-$(LC_ALL=C.UTF-8 "${PKG}" query '%n|%R')
+$(call_pkg "${opt_jail}" query '%n|%R')
 EOF_1fa6f326-49e6-4b01-a7ea-52372d00df1e
     fi
     if checkyesno opt_noauto; then
-        for package in $(LC_ALL=C.UTF-8 "${PKG}" query -e '%a = 0' '%n'); do
+        for package in $(call_pkg "${opt_jail}" query -e '%a = 0' '%n'); do
             farray_append packages "${package}"
         done
     fi
@@ -431,12 +456,12 @@
     prev_package=''    # to skip duplicate packages
     while farray_tryget package packages "${idx}"; do
         if [ "${prev_package}" != "${package}" ]; then
-            _package_max_detail "${package}" "${packagemapping}" "${repositories}" "${indexfile}" "${opt_filterunused}"
+            _package_max_detail "${package}" "${packagemapping}" "${repositories}" "${indexfile}" "${opt_filterunused}" "${opt_jail}"
             prev_package="${package}"
         fi
         idx=$((idx + 1))
     done
- 
+
     falist_release "${packagemapping}"
     farray_release "${repositories}"
     farray_release "${packages}"
@@ -455,9 +480,11 @@
 #:   $4 (str, null): The local index file if it exists
 #:   $5 (bool): Flag whether to suppress details for repositories that do
 #:              not contain the package `$1`
+#:   $6 (str, null): The jail where the packages live
 #:
 _package_max_detail() {
-    local package packagemapping repositories indexfile opt_filterunused
+    local package packagemapping repositories indexfile \
+          opt_filterunused opt_jail
 
     local instver instrepo repo title_printed _dummy \
           pkglabel pkgdescr pkgversion mapped_package
@@ -467,30 +494,31 @@
     repositories="${3}"
     indexfile="${4}"
     opt_filterunused="${5}"
+    opt_jail="${6}"
 
     # shellcheck disable=SC2034    # appears unused
     title_printed=no
     IFS='|' read -r instver instrepo <<EOF_e9bd7819-b4c5-4a86-b984-f5226db58cb1
-$(LC_ALL=C.UTF-8 "${PKG}" query '%v|%R' "${package}")
+$(call_pkg "${opt_jail}" query '%v|%R' "${package}")
 EOF_e9bd7819-b4c5-4a86-b984-f5226db58cb1
     print_title title_printed "${package}" "${instver}" "${instrepo}"
     if [ -n "${indexfile}" ]; then
         read -r _dummy pkglabel pkgdescr <<EOF_b1f225bd-d234-4a23-8a2a-40c2e5b7ff3c
-$(LC_ALL=C.UTF-8 "${PKG}" version -U -I -n "${package}" -v "${indexfile}")
+$(call_pkg "${opt_jail}" version -U -I -n "${package}" -v "${indexfile}")
 EOF_b1f225bd-d234-4a23-8a2a-40c2e5b7ff3c
-        pkgversion="$(parse_index_file_for_package_version "${indexfile}" "${package}")"
+        pkgversion="$(parse_index_file_for_package_version "${indexfile}" "${package}" "${opt_jail}")"
         _shall_print_package_detail_item "${pkglabel}" "${opt_filterunused}" && print_detail_item "INDEX" "${pkgversion}" "${pkglabel}" "${pkgdescr}"
     fi
-    farray_for_each repositories _package_repository_detail "${package}" "${opt_filterunused}" 0
+    farray_for_each repositories _package_repository_detail "${package}" "${opt_filterunused}" "${opt_jail}" 0
     mapped_package="$(get_package_mapping "${packagemapping}" "${package}")"
     if [ -n "${mapped_package}" ]; then
         printf '%18s %s\n' "--------------->" "${mapped_package}"
         if [ -n "${indexfile}" ]; then
-            pkgversion="$(parse_index_file_for_package_version "${indexfile}" "${mapped_package}")"
-            pkglabel="$(LC_ALL=C.UTF-8 "${PKG}" version --test-version "${instver}" "${pkgversion}")"
+            pkgversion="$(parse_index_file_for_package_version "${indexfile}" "${mapped_package}" "${opt_jail}")"
+            pkglabel="$(call_pkg "${opt_jail}" version --test-version "${instver}" "${pkgversion}")"
             print_detail_item "INDEX" "${pkgversion}" "${pkglabel}" '' 19
         fi
-        farray_for_each repositories _mapped_package_repository_detail "${mapped_package}" "{instver}" "${opt_filterunused}" 19
+        farray_for_each repositories _mapped_package_repository_detail "${mapped_package}" "{instver}" "${opt_filterunused}" "${opt_jail}" 19
     fi
 }
 
@@ -525,10 +553,12 @@
 #:   $3: The element value (i.e. repository name)
 #:   $4 (str): The (master) package name
 #:   $5 (flag): The global flat whether to filter repository printing
-#:   $6 (int, optional): The extra indent value to forward to called functions
+#:   $6 (str, null): The jail in which the package data live
+#:   $7 (int, optional): The extra indent value to forward to called functions
 #:
 _package_repository_detail() {
-    local repositories idx reponame package extraindent opt_filterunused
+    local repositories idx reponame package extraindent opt_filterunused \
+          opt_jail
 
     local _dummy \
           pkglabel pkgdescr pkgversion
@@ -539,12 +569,13 @@
     reponame="${3}"
     package="${4}"
     opt_filterunused="${5}"
-    extraindent="${6:-0}"
-    
+    opt_jail="${6}"
+    extraindent="${7:-0}"
+
     read -r _dummy  pkglabel pkgdescr <<EOF_19cf2d80-4eb9-4cda-bd4d-96b04e769206
-$(LC_ALL=C.UTF-8 "${PKG}" version -U -R -r "${reponame}" -n "${package}" -v)
+$(call_pkg "${opt_jail}" version -U -R -r "${reponame}" -n "${package}" -v)
 EOF_19cf2d80-4eb9-4cda-bd4d-96b04e769206
-    pkgversion="$(LC_ALL=C.UTF-8 "${PKG}" rquery -U -r "${reponame}" '%v' "${package}")"
+    pkgversion="$(call_pkg "${opt_jail}" rquery -U -r "${reponame}" '%v' "${package}")"
     _shall_print_package_detail_item "${pkglabel}" "${opt_filterunused}" && print_detail_item "${reponame}" "${pkgversion}" "${pkglabel}" "${pkgdescr}" "${extraindent}"
 }
 
@@ -560,11 +591,12 @@
 #:   $4 (str): The mapped package name
 #:   $5 (str): The parent package version
 #:   $6 (flag): The global flat whether to filter repository printing
-#:   $7 (int, optional): The extra indent value to forward to called functions
+#:   $7 (str, null): The jail where the package data live
+#:   $8 (int, optional): The extra indent value to forward to called functions
 #:
 _mapped_package_repository_detail() {
     local repositories idx reponame package parent_pkgversion \
-          opt_filterunused extraindent
+          opt_filterunused extraindent opt_jail
 
     local _dummy \
           pkglabel pkgversion
@@ -576,10 +608,11 @@
     package="${4}"
     parent_pkgversion="${5}"
     opt_filterunused="${6}"
-    extraindent="${7:-0}"
+    opt_jail="${7}"
+    extraindent="${8:-0}"
 
-    pkgversion="$(LC_ALL=C.UTF-8 "${PKG}" rquery -U -r "${reponame}" '%v' "${package}")"
-    pkglabel="$(LC_ALL=C.UTF-8 "${PKG}" version --test-version "${parent_pkgversion}" "${pkgversion}")"
+    pkgversion="$(call_pkg "${opt_jail}" rquery -U -r "${reponame}" '%v' "${package}")"
+    pkglabel="$(call_pkg "${opt_jail}" version --test-version "${parent_pkgversion}" "${pkgversion}")"
     _shall_print_package_detail_item "${pkglabel}" "${opt_filterunused}" && print_detail_item "${reponame}" "${pkgversion}" "${pkglabel}" '' "${extraindent}"
 }
 
@@ -659,8 +692,10 @@
 #
 # Global option handling
 #
-while getopts "Vh-:" _opt ; do
-    postprocess_getopts_for_long "Vh-:" _opt "version" "help" ""
+
+opt_jail=''
+while getopts "Vhj:-:" _opt ; do
+    postprocess_getopts_for_long "Vhj-:" _opt "version" "help" "jail=" ""
     case "${_opt}" in
         V|version)
             printf 'fports %s\n' '@@SIMPLEVERSIONSTR@@'
@@ -670,6 +705,9 @@
             echo "${USAGE}"
             exit 0
             ;;
+        j|jail)
+            opt_jail="${OPTARG}"
+            ;;
         \?)
             exit 2;
             ;;
@@ -693,9 +731,9 @@
     '')
         fatal 2 "no command given";;
     deptree)
-        command_deptree "$@";;
+        command_deptree "${opt_jail}" "$@";;
     detail)
-        command_detail "$@";;
+        command_detail "${opt_jail}" "$@";;
     *)
         fatal 2 "unknown command \`${command}'";;
 esac
--- a/share/local-bsdtools/common.subr	Fri Nov 08 12:57:59 2024 +0100
+++ b/share/local-bsdtools/common.subr	Sat Nov 09 00:47:05 2024 +0100
@@ -471,6 +471,85 @@
 
 
 #:
+#: Run a command on the host or in a jail.
+#:
+#: Args:
+#:   $1 (str, null): The jail in which to execute the command in $2...
+#:                   If `$1` is null then the command is executed on the
+#:                   current host.
+#:   $@: The command and its args
+#:
+#: Returns:
+#:   int: Whatever the called command exists with
+#:
+call_command() {
+    local _jail      # $@
+
+    _jail="${1}"
+    shift
+    if [ -z "${_jail}" ]; then
+	LC_ALL=C.UTF-8 "$@"
+    else
+	LC_ALL=C.UTF-8 /usr/sbin/jexec "${_jail}" "$@"
+    fi
+}
+
+
+#:
+#: Test whether a jail exists and the current user is allowed to run a
+#: command on the in given jail or if the jail exists.
+#:
+#: Args:
+#:   $1 (str): The jail to attach to temporarily
+#:
+#: Returns:
+#:   int: 0 if the attach was successful, 1 otherwise
+#:
+#: Works by executing :command:`/bin/hostname` in given jail using
+#: :command:`jexec`
+#:
+can_call_command() {
+    local _jail      # $@
+
+    _jail="${1}"
+    shift
+    if [ -n "${_jail}" ]; then
+	LC_ALL=C.UTF-8 /usr/sbin/jexec "${_jail}" /bin/hostname >/dev/null 2>/dev/null || return 1
+    fi
+    return 0
+}
+
+
+#:
+#: A special form of `call_command` that eventually calls
+#: :command:`pkg -j JAIL` directly.
+#:
+#: Args:
+#:   $1 (str, null): The jail in which to execute the command in $2...
+#:                   If `$1` is null then the command is executed on the
+#:                   current host.
+#:   $@: The args to :command:`pkg`
+#:
+#: Input (Globals):
+#:   - PKG: The path to the :command:`pkg` commant to call into.
+#:
+#: Returns:
+#:   int: Whatever the called command exists with
+#:
+call_pkg() {
+    local jail    # $@
+
+    jail="${1}"
+    shift
+    if [ -z "${jail}" ]; then
+	LC_ALL=C.UTF-8 "${PKG}" "$@"
+    else
+	LC_ALL=C.UTF-8 "${PKG}" -j "${jail}" "$@"
+    fi
+}
+
+
+#:
 #: Determine some important dataset properties that are set locally
 #:
 #: Args:
--- a/share/local-bsdtools/ports.subr	Fri Nov 08 12:57:59 2024 +0100
+++ b/share/local-bsdtools/ports.subr	Sat Nov 09 00:47:05 2024 +0100
@@ -68,6 +68,8 @@
 #:
 #: Args:
 #:   $1 (str): The variable where to create the alist
+#:   $2 (str, null): The jail from which to retrieve the package mapping
+#:                   configuration
 #:
 #: Input (Globals):
 #:   PACKAGE_MAPPING: See also :manpage:`package-mapping.conf(5)`.
@@ -88,14 +90,15 @@
 #:   fmg-nextcloud-twofactor_totp-php71 nextcloud-twofactor_totp-php71
 #:
 init_package_mapping() {
-    local _mapping
+    local _mapping _jail
 
     local _iname _mapped_package
 
     falist_create "$1" || return
     _mapping="$1"
+    _jail="$2"
 
-    if [ -r "${PACKAGE_MAPPING}" ] ; then
+    if call_command "${_jail}" /bin/test -r "${PACKAGE_MAPPING}" ; then
         while IFS=$' \t' read -r _iname _mapped_package ; do
             case "${_iname}" in
                 '')
@@ -111,7 +114,9 @@
                     fi
                     ;;
             esac
-        done < "${PACKAGE_MAPPING}"
+        done <<EOF_6969d915-b864-47bb-a0f9-70519b1be0e2
+$(call_command "${_jail}" /bin/cat "${PACKAGE_MAPPING}")
+EOF_6969d915-b864-47bb-a0f9-70519b1be0e2
     fi
     return 0
 }
@@ -124,12 +129,16 @@
 #: The output of :command:`pkg -vv` is parsed to get all the configured
 #: and enabled repositories.
 #:
+#: Args:
+#:   $1 (str, null, optional): The jail from where to read the repositoriy
+#:                             configuration
+#:
 #: Output (stdout):
 #:   Each configured and enabled repository name is printed on a single
 #:   line.
 #:
 get_configured_pkg_repository_names() {
-    # no args
+    # $1
 
     local _line _status _repository_name _repository_enabled _key _value _rest
 
@@ -165,9 +174,9 @@
                     *)
                         _key=''
                         _value=''
-                        IFS=$' \t:' read -r _key _value _rest || true <<EOF73d43dccec1a496f98cb519be160f549
+                        IFS=$' \t:' read -r _key _value _rest || true <<EOF_73d43dccec1a496f98cb519be160f549
 ${_line}
-EOF73d43dccec1a496f98cb519be160f549
+EOF_73d43dccec1a496f98cb519be160f549
                         if [ "${_key}" = "enabled" ]; then
                             case "${_value}" in
                                 'yes,'|yes)
@@ -182,9 +191,9 @@
             *)
                 fatal "${EX_SOFTWARE}" "unhandled format of \`pkg -vv'"
         esac
-    done <<EOF7c6ea1b0ce544021a7813757c7003392
-$(LC_ALL=C.UTF-8 "${PKG}" -vv)
-EOF7c6ea1b0ce544021a7813757c7003392
+    done <<EOF_7c6ea1b0ce544021a7813757c7003392
+$(call_pkg "${1-}" -vv)
+EOF_7c6ea1b0ce544021a7813757c7003392
 }
 
 
@@ -194,9 +203,11 @@
 #: Args:
 #:   $1 (str): The variable name where to store the array with the
 #:             repository names. It must be released by the caller.
+#:   $2 (str, null): The jail from where to read the repository
+#:                   configurations
 #:
 get_active_repositories() {
-    # $1
+    # $1 $2
 
     local repo
 
@@ -204,7 +215,7 @@
     while IFS='' read -r repo; do
         farray_append "${1}" "${repo}"
     done <<EOF_b4e5385c-4c37-413b-b6f2-7909ac9eaa86
-$(get_configured_pkg_repository_names)
+$(get_configured_pkg_repository_names "${2}")
 EOF_b4e5385c-4c37-413b-b6f2-7909ac9eaa86
 }
 
@@ -240,7 +251,7 @@
             #farray_append _test "${_pkgversion}"
             #[ $_idx -gt 5000 ] && break
         done <<EOF_pkg_9b5d20d4-805e-484e-9afb-ecc62e75f7cc
-$(LC_ALL=C.UTF-8 "${PKG}" rquery -U -r "${_reponame}" $'%n\t%v')
+$(LC_ALL=C.UTF-8 "${PKG}" rquery -U -r "${_reponame}" '%n\t%v')
 EOF_pkg_9b5d20d4-805e-484e-9afb-ecc62e75f7cc
         falist_set "${_allrepos}" "${_reponame}" "${_repodb}"
         falist_release _repodb
@@ -257,31 +268,34 @@
 #: The check follows largely the conventions of :manpage:`pkg-version(8)`
 #: where to check for a local index.
 #:
+#: Args:
+#:   $1 (str, null): The jail in which to determine a local index file
+#:
 #: Returns:
 #:   int: 0 if the index is available, 1 otherwise
 #:
 is_local_index_file_available() {
-    # no args
+    # $1
 
     local versrc indexdir indexfile
 
-    versrc="$(LC_ALL=C.UTF-8 "${PKG}" config VERSION_SOURCE)" || fatal "${EX_UNAVAILABLE}" "cannot get VERSION_SOURCE configuration value"
-    indexfile="$(LC_ALL=C.UTF-8 "${PKG}" config INDEXFILE)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXFILE configuration value"
+    versrc="$(call_pkg "${1}" config VERSION_SOURCE)" || fatal "${EX_UNAVAILABLE}" "cannot get VERSION_SOURCE configuration value"
+    indexfile="$(call_pkg "${1}" config INDEXFILE)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXFILE configuration value"
     case "${versrc}" in
         ''|R)
             # XXX FIXME: handle R ("remote")  really like default?
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
-            [ -r "${indexdir}/${indexfile}" ] && return 0
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
-            [ -r "${indexdir}/${indexfile}" ]
+            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
+            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" && return 0
+            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
+            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}"
             ;;
         I)
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
-            [ -r "${indexdir}/${indexfile}" ]
+            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
+            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}"
             ;;
         P)
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
-            [ -r "${indexdir}/${indexfile}" ]
+            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
+            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}"
             ;;
         *)
             fatal "${EX_SOFTWARE}" "unhandled value in VERSION_SOURCE: ${versrc}";;
@@ -292,40 +306,43 @@
 #:
 #: Like `is_local_index_file_available` but also print the found index file.
 #:
+#: Args:
+#:   $1 (str, null): The jail in which to determine a local index file
+#:
 #: Output (stdout):
 #:   The found index file
 #:
 get_local_index_file() {
-    # no args
+    # $1
 
     local versrc indexdir indexfile
 
-    versrc="$(LC_ALL=C.UTF-8 "${PKG}" config VERSION_SOURCE)" || fatal "${EX_UNAVAILABLE}" "cannot get VERSION_SOURCE configuration value"
-    indexfile="$(LC_ALL=C.UTF-8 "${PKG}" config INDEXFILE)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXFILE configuration value"
+    versrc="$(call_pkg "${1}" config VERSION_SOURCE)" || fatal "${EX_UNAVAILABLE}" "cannot get VERSION_SOURCE configuration value"
+    indexfile="$(call_pkg "${1}" config INDEXFILE)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXFILE configuration value"
     case "${versrc}" in
         ''|R)
             # XXX FIXME: handle R ("remote")  really like default?
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
-            if [ -r "${indexdir}/${indexfile}" ]; then
+            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
+            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                 printf '%s' "${indexdir}/${indexfile}"
                 return 0
             fi
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
-            if [ -r "${indexdir}/${indexfile}" ]; then
+            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
+            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                 printf '%s' "${indexdir}/${indexfile}"
                 return 0
             fi
             ;;
         I)
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
-            if [ -r "${indexdir}/${indexfile}" ]; then
+            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
+            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                 printf '%s' "${indexdir}/${indexfile}"
                 return 0
             fi
             ;;
         P)
-            indexdir="$(LC_ALL=C.UTF-8 "${PKG}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
-            if [ -r "${indexdir}/${indexfile}" ]; then
+            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
+            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                 printf '%s' "${indexdir}/${indexfile}"
                 return 0
             fi
@@ -344,6 +361,7 @@
 #: Args:
 #:   $1 (str): An existing index file
 #:   $2 (str): The package to search for
+#:   $3 (str, null): The jail where `$1` lives
 #:
 #: Output (stdout):
 #:   The package version if found, or a null string
@@ -352,11 +370,15 @@
 #:   int: 0 if a matching package is found, 1 otherwise
 #:
 parse_index_file_for_package_version() {
-    # $1 $2
+    # $1 $2 $3
+
+    local pkgspec _rest _pathprefix
 
-    local pkgspec _rest
-
-    [ -r "${1}" ] || fatal "${EX_SOFTWARE}" "given index file \`${1}' is not readable (or does not exist)"
+    _pathprefix=''
+    if [ -n "${3}" ] ; then
+        _pathprefix="$(LC_ALL=C.UTF-8 /usr/sbin/jls -j "${3}" path)"
+    fi
+    [ -r "${_pathprefix}${1}" ] || fatal "${EX_SOFTWARE}" "given index file \`${1}' is not readable (or does not exist)"
 
     # Using grep to pre-select with BREs is at least one magnitude slower
     while IFS='|' read -r pkgspec _rest; do
@@ -364,6 +386,6 @@
             printf '%s' "${pkgspec##*-}"
             return 0
         fi
-    done <"${1}"
+    done <"${_pathprefix}${1}"
     return 1
 }
--- a/tests/ports.t	Fri Nov 08 12:57:59 2024 +0100
+++ b/tests/ports.t	Sat Nov 09 00:47:05 2024 +0100
@@ -22,7 +22,7 @@
 init_package_mapping
 --------------------
 
-  $ init_package_mapping PMAPPING
+  $ init_package_mapping PMAPPING ""
 
 
 get_package_mapping
@@ -64,29 +64,29 @@
 
 Assumes fag's standard: PORTSDIR=/home/fag/ports
 
-  $ is_local_index_file_available
-  $ PORTSDIR=/nonexisting is_local_index_file_available
+  $ is_local_index_file_available ""
+  $ PORTSDIR=/nonexisting is_local_index_file_available ""
   [1]
   $ export VERSION_SOURCE=I
-  $ is_local_index_file_available
+  $ is_local_index_file_available ""
   [1]
   $ export VERSION_SOURCE=P
-  $ is_local_index_file_available
+  $ is_local_index_file_available ""
   $ unset VERSION_SOURCE
 
-  $ get_local_index_file
+  $ get_local_index_file ""
   /home/fag/ports/INDEX-[0-9]+ \(no-eol\) (re)
-  $ PORTSDIR=/nonexisting get_local_index_file
+  $ PORTSDIR=/nonexisting get_local_index_file ""
   [1]
   $ export VERSION_SOURCE=I
-  $ get_local_index_file
+  $ get_local_index_file ""
   [1]
   $ export VERSION_SOURCE=P
-  $ get_local_index_file
+  $ get_local_index_file ""
   /home/fag/ports/INDEX-[0-9]+ \(no-eol\) (re)
   $ unset VERSION_SOURCE
 
-  $ _pkgversion="$(parse_index_file_for_package_version "$(get_local_index_file)" 'pkg')"
+  $ _pkgversion="$(parse_index_file_for_package_version "$(get_local_index_file "")" 'pkg' '')"
   $ [ -n "${_pkgversion}" ]
   $ printf '%s' "${_pkgversion}"
   \d+\.\d+(\.\d+) \(no-eol\) (re)