#!/bin/sh # # SPDX-FileCopyrightText: 2021 - 2024 StorPool # SPDX-License-Identifier: BSD-2-Clause set -e usage() { cat <<'EOUSAGE' Usage: sp_variant command run [-N] category.command [arg...] sp_variant detect sp_variant features sp_variant repo add sp_variant show all sp_variant show current sp_variant show VARIANT EOUSAGE } detect_from_os_release() { if [ ! -r /etc/os-release ]; then return fi local output # Maybe we should provide a sample os-release file for testing? # shellcheck disable=SC1091,SC2153,SC2154 output="$(unset ID VERSION_ID; . /etc/os-release; printf -- '%s::%s\n' "$ID" "$VERSION_ID")" local os_id="${output%%::*}" version_id="${output#*::}" if [ -z "$os_id" ] || [ -z "$version_id" ]; then return fi {% for name, var in variants|dictvsort %} if [ "$os_id" = '{{ var.detect.os_id }}' ] && printf -- '%s\n' "$version_id" | grep -Eqe '{{ var.detect.os_version_regex.pattern|regexunx}}'; then printf -- '%s\n' '{{ name }}' return fi {% endfor %} } cmd_detect() { local res if [ "$#" -ne 0 ]; then usage 1>&2 exit 1 fi res="$(detect_from_os_release)" if [ -n "$res" ]; then printf -- '%s\n' "$res" return fi {% for name in order %} if [ -r '{{ variants[name].detect.filename }}' ] && grep -Eqe '{{ variants[name].detect.regex.pattern|regexunx }}' -- '{{ variants[name].detect.filename }}'; then printf -- '%s\n' '{{ name }}' return fi {% endfor %} echo "Could not detect the current host's build variant" 1>&2 exit 1 } {% for name, var in variants_json|dictvsort %} show_{{ name }}() { cat <<'EOVARIANT_JSON' {{ var }} EOVARIANT_JSON } {% endfor %} cmd_show_all() { if [ "$#" -ne 0 ]; then usage 1>&2 exit 1 fi cat <<'EOPROLOGUE' { "format": { "version": { "major": {{ format_version[0] }}, "minor": {{ format_version[1] }} } }, "order": [ {%- for name in order %} "{{ name }}"{% if loop.revindex > 1 %},{% endif %} {%- endfor %} ], "variants": { EOPROLOGUE {% for name in variants|vsort %} printf -- ' "%s": ' '{{ name }}' show_{{ name }} {% if loop.revindex > 1 %}echo ','{% endif %} {%- endfor %} cat <<'EOEPILOGUE' }, "version": "{{ version }}" } EOEPILOGUE } show_variant() { local variant="$1" cat <<'EOPROLOGUE' { "format": { "version": { "major": {{ format_version[0] }}, "minor": {{ format_version[1] }} } }, "variant": EOPROLOGUE eval "'show_$variant'" cat <<'EOEPILOGUE' , "version": "{{ version }}" } EOEPILOGUE } cmd_show_current() { if [ "$#" -ne 0 ]; then usage 1>&2 exit 1 fi local variant variant="$(cmd_detect)" [ -n "$variant" ] show_variant "$variant" } run_command() { local name="$1" noop="$2" command="$3" shift 3 local cmd_cat="${command%%.*}" cmd_item="${command#*.}" if [ "$command" = "$cmd_cat" ] || [ "$cmd_item" != "${cmd_item%.*}" ]; then echo "Invalid command specification '$command'" 1>&2 exit 1 fi case "$name" in {% for name, var in variants|dictvsort %} {{ name }}) case "$cmd_cat" in {% for cat_name in var.commands._fields|sort %} {{ cat_name }}) case "$cmd_item" in {% for cmd_name in var.commands|attr(cat_name)|attr("_fields")|sort %} {{ cmd_name }}) # The commands are quoted exactly as much as necessary. # shellcheck disable=SC2016 $noop {% for word in var.commands|attr(cat_name)|attr(cmd_name) %}'{{ word }}' {% endfor %} "$@" ;; {% endfor %} *) echo "Invalid command '$cmd_item' in the '$cmd_cat' category" 1>&2 exit 1 ;; esac ;; {% endfor %} *) echo "Invalid command category '$cmd_cat'" 1>&2 exit 1 ;; esac ;; {% endfor %} *) echo "Internal error: invalid variant '$name'" 1>&2 exit 1 ;; esac } copy_file() { local src="$1" dstdir="$2" if [ ! -f "$src" ]; then echo "Internal error: no $src file to copy to $dstdir" 1>&2 exit 1 fi if [ -z "$dstdir" ]; then echo "Internal error: no destination specified for $src" 1>&2 exit 1 fi if [ ! -d "$dstdir" ]; then echo "Internal error: no $dstdir directory" 1>&2 exit 1 fi local dst dst="$dstdir/$(basename -- "$src")" install -o root -g root -m 644 -- "$src" "$dst" } repo_add_extension() { local filename="$1" repotype="$2" local basename="${filename%.*}" local ext="${filename##*.}" local suffix='-invalid' case "$repotype" in {% for rtype, rdef in repotypes|dictvsort %} {{ rtype }}) suffix='{{ rdef.extension }}' ;; {% endfor %} *) echo "Invalid repository type '$repotype'" 1>&2 exit 1 ;; esac printf -- '%s%s.%s\n' "$basename" "$suffix" "$ext" } repo_add_yum() { local name="$1" vdir="$2" repotype="$3" yumdef="$4" keyring="$5" yum --disablerepo='storpool-*' install -q -y ca-certificates local yumbase repofile yumbase="$(basename -- "$yumdef")" repofile="$(repo_add_extension "$yumbase" "$repotype")" [ -n "$repofile" ] copy_file "$vdir/$repofile" /etc/yum.repos.d local keybase keybase="$(basename -- "$keyring")" copy_file "$vdir/$keybase" /etc/pki/rpm-gpg if [ -n "$(command -v rpmkeys || true)" ]; then rpmkeys --import "/etc/pki/rpm-gpg/$(basename -- "$keybase")" fi yum --disablerepo='*' --enablerepo="storpool-$repotype" clean metadata } repo_add_deb() { local name="$1" vdir="$2" repotype="$3" srcdef="$4" keyring="$5" packages="$6" # $packages is not quoted on purpose: there may be more than one. # shellcheck disable=SC2086 run_command "$name" '' package.install $packages local srcbase repofile srcbase="$(basename -- "$srcdef")" repofile="$(repo_add_extension "$srcbase" "$repotype")" [ -n "$repofile" ] copy_file "$vdir/$repofile" /etc/apt/sources.list.d local keybase keybase="$(basename -- "$keyring")" copy_file "$vdir/$keybase" /usr/share/keyrings apt-get update } cmd_repo_add() { local repodir repotype='contrib' repodir="$(dirname -- "$0")" local o while getopts 'd:t:' o; do case "$o" in d) repodir="$OPTARG" ;; t) repotype="$OPTARG" ;; *) usage 1>&2 exit 1 ;; esac done shift "$((OPTIND - 1))" if [ "$#" -ne 0 ]; then usage 1>&2 exit 1 fi local variant variant="$(cmd_detect)" local vdir="$repodir/$variant" if [ ! -d "$vdir" ]; then echo "No $vdir directory" 1>&2 exit 1 fi case "$variant" in {% for name, var in variants|dictvsort %} {{ name }}) {% if var.family == "debian" %} repo_add_deb '{{ name }}' "$vdir" "$repotype" '{{ var.repo.sources }}' '{{ var.repo.keyring }}' '{{ var.repo.req_packages|join(" ") }}' {% else %} repo_add_yum '{{ name }}' "$vdir" "$repotype" '{{ var.repo.yumdef }}' '{{ var.repo.keyring }}' {% endif %} ;; {% endfor %} *) echo "Internal error: '$variant' should be recognized at this point" 1>&2 exit 1 ;; esac install -o root -g root -m 755 -- "$0" /usr/sbin/sp_variant } cmd_command_run() { local noop='' while getopts 'N' o; do case "$o" in N) noop='echo' ;; *) usage 1>&2 exit 1 ;; esac done shift "$((OPTIND - 1))" local variant variant="$(cmd_detect)" [ -n "$variant" ] run_command "$variant" "$noop" "$@" } cmd_command() { case "$1" in run) shift cmd_command_run "$@" ;; *) usage 1>&2 exit 1 ;; esac } cmd_features() { echo 'Features: format={{ format_version[0] }}.{{ format_version[1] }} version={{ version }}' } case "$1" in command) shift cmd_command "$@" ;; detect) shift cmd_detect "$@" ;; features) shift cmd_features "$@" ;; repo) shift case "$1" in add) shift cmd_repo_add "$@" ;; *) usage 1>&2 exit 1 ;; esac ;; show) shift case "$1" in all) shift cmd_show_all "$@" ;; current) shift cmd_show_current "$@" ;; {% for name in variants|vsort %} {{ name }}) show_variant '{{ name }}' ;; {% endfor %} *) usage 1>&2 exit 1 ;; esac ;; *) usage 1>&2 exit 1 ;; esac