view sbin/fpkg @ 723:a97ec3f07bdb

farray.sh: REFACTOR: More flexible metadata retrieval. Using an array or alist variable name or token value (with prefix) is now supported in every function. This is possible because the value prefixes contain questin marks (?) which are not allowed in shell variable names. This again is a major precondition for recursive data structures (arrays/alists in arrays/alists).
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 05 Oct 2024 21:55:55 +0200
parents 7126823e4d70
children 53d05f470f4a
line wrap: on
line source

#!/bin/sh
# -*- indent-tabs-mode: nil; -*-
#:
#: A pkg frontend for common operations that also operates in all
#: running jails.
#:
#: :Author:    Franz Glasner
#: :Copyright: (c) 2019-2024 Franz Glasner.
#:             All rights reserved.
#: :License:   BSD 3-Clause "New" or "Revised" License.
#:             See LICENSE for details.
#:             If you cannot find LICENSE see
#:             <https://opensource.org/licenses/BSD-3-Clause>
#: :ID:        @(#)@@SIMPLEVERSIONTAG@@
#:

: # separator for shellcheck: no module-level directives below


# shellcheck disable=SC2034    # VERSION appears unused
VERSION='@@VERSION@@'

# shellcheck disable=SC2016    # no expansion
USAGE='
USAGE: fpkg [ OPTIONS] COMMAND [ COMMAND-OPTIONS ]

OPTIONS:

  -V    Print the program name and version number to stdout and exit

  -h    Print this help message to stdout and exit

COMMANDS:

  audit

    `pkg audit` on the local host and all running visible and
    compatible jails

  etcupdate-status

    Call `etcupdate status` on the local host and in all running visible
    jails

  update

    `pkg update` on the local host and all running visible and
    compatible jails

  upgrade

    `pkg upgrade` on the local host and all running visible and
    compatible jails

  check-upgrade
  upgrade-check

    `pkg upgrade -n` on the local host and all running visible and
    compatible jails

  check-fast-track
    Check packages installed from the LocalBSDPorts repository against
    the repositories `FreeBSD` and `LocalBSDPorts` on the local host
    and all visible and compatible jails

  config <name>

    Retrieve the value of a given configuration option on the local host
    and all running visible jails

  uversion

    Call `freebsd-version -u` on the local host and all running visible
    jails

  vv

   `pkg -vv` on the local host and all running visible jails

ENVIRONMENT:

  FPKG_AUDIT_FLAGS
                 Additional flags given to `pkg audit`
                 (Default: -Fr)

  FPKG_UPDATE_FLAGS
                 Additional flags given to `pkg update`
                 (Default: empty)

  FPKG_UPGRADE_FLAGS
                 Additional flags given to `pkg upgrade` and `pkg upgrade -n`
                 (Default: empty)

  FPKG_SIGN
                 Marker for the begin of an output group (local host or jail)
                 (Default: "===> ")

  FPKG_SKIPSIGN
                 Marker for the begin of a skipped output group
                 (Default: "----> ")

  All other environment variables that affect `pkg` are effective also.

  A "compatible jail" is a jail that'"'"'s "freebsd-version -u" is the same
  as the host'"'"'s.
'


_p_datadir='@@DATADIR@@'
[ "${_p_datadir#@@DATADIR}" = '@@' ] && _p_datadir="$(dirname "$0")"/../share/local-bsdtools
. "${_p_datadir}/common.subr"


#:
#: Configuration directory
#:
: ${CONFIGDIR:="@@ETCDIR@@"}

# shellcheck disable=SC1091        # not following...
[ -r "${CONFIGDIR}/pkgtools.conf" ] && . "${CONFIGDIR}/pkgtools.conf"

: ${FPKG_AUDIT_FLAGS="-Fr"}
: ${FPKG_UPDATE_FLAGS=}
: ${FPKG_UPGRADE_FLAGS=}
: ${FPKG_SIGN:='===> '}
: ${FPKG_SKIPSIGN:='----> '}

#:
#: The official FreeBSD binary repository
#:
: ${FREEBSD_REPO:=FreeBSD}

#:
#: Local repository with ports with default OPTIONS (i.e. unchanged)
#: but newer than the packages in the "FreeBSD" repository.
#: Some sort of a fast-track repository.
#:
: ${LOCALBSDPORTS_REPO:=LocalBSDPorts}


#: Check whether the jail `_jail` has the same FreeBSD userland version
#: as the host the the current process runs.
#:
#: Args:
#:   $1 (str): the running jail (name or jail id) to check for
#:
#: Returns:
#:   int: 0 (truthy) if the userland versions match, 1 (falsy) otherwise
#:
has_same_userland_version() {
    local _jail _host_version _jail_version

    _jail="$1"

    _host_version="$(/bin/freebsd-version -u)" || exit 1
    _jail_version="$(jexec -l -- "${_jail}" /bin/freebsd-version -u)" || exit 1
    if [ "${_host_version%%-*}" = "${_jail_version%%-*}" ]; then
        return 0
    fi
    return 1
}


#:
#: Do a local `freebsd-version -u` and also for all running jails
#:
command_uversion() {
    local _jail _OLDIFS

    printf 'LOCALHOST: %s\n' "$(/bin/freebsd-version -u)"
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _jail in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '%s: %s\n' "${_jail}" "$(jexec -l -- "${_jail}" /bin/freebsd-version -u)"
    done
    IFS="$_OLDIFS"
}


#:
#: Do a local `pkg audit -Fr` and also for all running jails
#:
command_audit() {
    local _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg audit ${FPKG_AUDIT_FLAGS}
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" audit ${FPKG_AUDIT_FLAGS}
        else
            printf '%s%s\n' "${FPKG_SKIPSIGN}" "SKIPPED because of different userland"
        fi
    done
    IFS="$_OLDIFS"
}


#:
#: Do a local `pkg update` and also for all running jails
#:
command_update() {
    local _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg update ${FPKG_UPDATE_FLAGS}
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" update ${FPKG_UPDATE_FLAGS}
        else
            printf '%s%s\n' "${FPKG_SKIPSIGN}" "SKIPPED because of different userland"
        fi
    done
    IFS="$_OLDIFS"
}


#:
#: Do a local `pkg upgrade` and also for all running jails
#:
command_upgrade() {
    local _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg upgrade ${FPKG_UPGRADE_FLAGS}
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" upgrade ${FPKG_UPGRADE_FLAGS}
        else
            printf '%s%s\n' "${FPKG_SKIPSIGN}" "SKIPPED because of different userland"
        fi
    done
    IFS="$_OLDIFS"
}


#:
#: Do a local `pkg upgrade -n` and also for all running jails
#:
command_check_upgrade() {
    local _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg upgrade -n ${FPKG_UPGRADE_FLAGS}
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" upgrade -n ${FPKG_UPGRADE_FLAGS}
        else
            printf '%s%s\n' "${FPKG_SKIPSIGN}" "SKIPPED because of different userland"
        fi
    done
    IFS="$_OLDIFS"
}


#:
#: Check the fast-track repository versions against the canonical
#: FreeBSD repository versions.
#:
#: Input (Globals):
#:   FREEBSD_REPO:       the (canonical) FreeBSD repository name
#:   LOCALBSDPORTS_REPO: the fast-track repository name
#:
command_check_fasttrack() {
    local _name _repo _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg query '%n %R' |
        while read -r _name _repo; do
            if [ "${_repo}" = "${LOCALBSDPORTS_REPO}" ]; then
                printf '   %s\n' "${_name}"
                printf '      %-15s : %s\n' "${LOCALBSDPORTS_REPO}" "$(pkg version -U -r "${LOCALBSDPORTS_REPO}" -n "${_name}" -v)"
                printf '      %-15s : %s\n' "${FREEBSD_REPO}" "$(pkg version -U -r "${FREEBSD_REPO}" -n "${_name}" -v)"
            fi
        done
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" query '%n %R' |
                while IFS="$_OLDIFS" read -r _name _repo; do
                    if [ "${_repo}" = "${LOCALBSDPORTS_REPO}" ]; then
                        printf '   %s\n' "${_name}"
                        printf '      %s-15s : %s\n' "${LOCALBSDPORTS_REPO}" "$(pkg -j "${_j}" version -U -r "${LOCALBSDPORTS_REPO}" -n "${_name}" -v)"
                        printf '      %-15s : %s\n' "${FREEBSD_REPO}" "$(pkg -j "${_j}" version -U -r "${FREEBSD_REPO}" -n "${_name}" -v)"
            fi
                done
        else
            printf '%s%s\n' "${FPKG_SKIPSIGN}" "SKIPPED because of different userland"
        fi
    done
    IFS="$_OLDIFS"
}


#:
#: The `pkg config name` command for the host and all running jails
#:
#: Args:
#:   $1 (str): the configuration option to retrieve to
#:
#: Output (stdout):
#:   The value of the configuration option
#:
command_config() {
    local _name _j _OLDIFS

    _name="$1"

    if [ -z "${_name}" ]; then
        echo "Usage: fpkg config <name>" >&2
        return 1
    fi
    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg config "${_name}"
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        # This prints the value on the *host* also
        #pkg -j "${_j}" config "${_name}"
        # with jexec it can be run on all jails
        LC_ALL=C.UTF-8 /usr/sbin/jexec -- "${_j}" pkg config "${_name}"
    done
    IFS="$_OLDIFS"
}


#:
#: The `pkg -vv` command for the host and all running compatible jails
#:
command_vv() {
    local _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    pkg -vv
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" -vv
        else
            printf '%s%s\n' "${FPKG_SKIPSIGN}" "SKIPPED because of different userland"
        fi
    done
    IFS="$_OLDIFS"
}

#:
#: Call `etcupdate status` for the host and all running jails
#:
command_etcupdate_status() {
    local _j _OLDIFS

    printf '%sLOCALHOST\n' "${FPKG_SIGN}"
    /usr/sbin/etcupdate status
    _OLDIFS="$IFS"
    IFS=$'\n'
    for _j in $(/usr/sbin/jls name | LC_ALL=C.UTF-8 /usr/bin/sort); do
        printf '\n%sJAIL: %s\n' "${FPKG_SIGN}" "${_j}"
        LC_ALL=C.UTF-8 /usr/sbin/jexec -- "${_j}" /usr/sbin/etcupdate status
    done
    IFS="$_OLDIFS"
}


#
# Global option handling
#
while getopts "Vh" _opt ; do
    case ${_opt} in
        V)
            printf 'fpkg %s\n' '@@SIMPLEVERSIONSTR@@'
            exit 0
            ;;
        h)
            echo "${USAGE}"
            exit 0
            ;;
        \?)
            exit 2;
            ;;
        *)
            fatal 2 "option handling failed"
            ;;
    esac
done

#
# Reset the Shell's option handling system to prepare for handling
# command-local options.
#
shift $((OPTIND-1))
OPTIND=1

command="$1"
shift

test -n "$command" || fatal 2 "no command given"

case "${command}" in
    audit)
        command_audit "$@"
        ;;
    etcupdate-status|etcupdate_status|status-etcupdate|status_etcupdate)
        command_etcupdate_status "$@"
        ;;
    update)
        command_update "$@"
        ;;
    upgrade)
        command_upgrade "$@"
        ;;
    check-upgrade|check_upgrade|upgrade-check|upgrade_check)
        command_check_upgrade "$@"
        ;;
    check-fast-track|check-fasttrack|check_fast_track|check_fasttrack)
        command_check_fasttrack "$@"
        ;;
    config)
        command_config "$@"
        ;;
    uversion)
        command_uversion "$@"
        ;;
    vv)
        command_vv "$@"
        ;;
    *)
        fatal 2 "unknown command \`${command}'"
        ;;
esac