#!/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 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:        @(#)@@PKGORIGIN@@ $HGid$

'

VERSION="@@VERSION@@"

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 jails

  update

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

  upgrade

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

  upgrade-check

    `pkg upgrade -n` 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.
'

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

test -r "${CONFIGDIR}/tools.conf" && . "${CONFIGDIR}/tools.conf"

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


has_same_userland_version() {
    : 'Check whether the jail `_jail` has the same FreeBSD userland version
    as the host the the current process runs.

    Args:
        _jail: the running jail to check for

    Returns:
        0 if the userland versions match, 1 otherwise

    '
    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
}


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

    '
    echo "${FPKG_SIGN}LOCALHOST"
    pkg audit ${FPKG_AUDIT_FLAGS}
    for _j in $(jls -N | awk '{if(NR>1)print $1}' | sort); do
        echo ""
        echo "${FPKG_SIGN}JAIL: ${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" audit ${FPKG_AUDIT_FLAGS}
        else
            echo "${FPKG_SKIPSIGN}SKIPPED because of different userland"
        fi
    done
}


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

    '
    echo "${FPKG_SIGN}LOCALHOST"
    pkg update ${FPKG_UPDATE_FLAGS}
    for _j in $(jls -N | awk '{if(NR>1)print $1}' | sort); do
        echo ""
        echo "${FPKG_SIGN}JAIL: ${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" update ${FPKG_UPDATE_FLAGS}
        else
            echo "${FPKG_SKIPSIGN}SKIPPED because of different userland"
        fi
    done
}


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

    '
    echo "${FPKG_SIGN}LOCALHOST"
    pkg upgrade ${FPKG_UPGRADE_FLAGS}
    for _j in $(jls -N | awk '{if(NR>1)print $1}' | sort); do
        echo ""
        echo "${FPKG_SIGN}JAIL: ${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" upgrade ${FPKG_UPGRADE_FLAGS}
        else
            echo "${FPKG_SKIPSIGN}SKIPPED because of different userland"
        fi
    done
}


command_upgrade_check() {
    : 'Do a local `pkg upgrade -n` and also for all running jails

    '
    echo "${FPKG_SIGN}LOCALHOST"
    pkg upgrade -n ${FPKG_UPGRADE_FLAGS}
    for _j in $(jls -N | awk '{if(NR>1)print $1}' | sort); do
        echo ""
        echo "${FPKG_SIGN}JAIL: ${_j}"
        if has_same_userland_version "${_j}"; then
            pkg -j "${_j}" upgrade -n ${FPKG_UPGRADE_FLAGS}
        else
            echo "${FPKG_SKIPSIGN}SKIPPED because of different userland"
        fi
    done
}


#
# Global option handling
#
while getopts "Vh" _opt ; do
    case ${_opt} in
        V)
            echo "fpkg v${VERSION} (rv:@@HGREVISION@@)"
            exit 0
            ;;
        h)
            echo "${USAGE}"
            exit 0
            ;;
        \?)
            exit 2;
            ;;
        *)
            echo "ERROR: option handling failed" >&2
            exit 2
            ;;
    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" || { echo "ERROR: no command given" >&2; exit 2; }

case "${command}" in
    audit)
        command_audit "$@"
        ;;
    update)
        command_update "$@"
        ;;
    upgrade)
        command_upgrade "$@"
        ;;
    upgrade-check|upgrade_check)
        command_upgrade_check "$@"
        ;;
    *)
        echo "ERROR: unknown command \`${command}'" >&2
        exit 2;
        ;;
esac
