view sbin/fpkg @ 749:e8eb5e7ceb37

farray.sh: Implement binary lexicographical search in "farray_binsearch()". While there disable SC3012 in shellcheck because FreeBSD's /bin/sh supports lexicographical comparisons in "test".
author Franz Glasner <fzglas.hg@dom66.de>
date Thu, 10 Oct 2024 13:27:50 +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