diff --git a/.gitignore b/.gitignore index 8974921..f84ef26 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /dist/*.* /dist/schema /dist/runtime +/dist/bin diff --git a/dist/src/bash.bashrc b/dist/src/bash.bashrc index 807d4b5..a787cea 100644 --- a/dist/src/bash.bashrc +++ b/dist/src/bash.bashrc @@ -20,3 +20,5 @@ case ${TERM} in esac [ -r /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion + +[[ $FAKECHROOT ]] && cd $HOME diff --git a/dist/src/pacwrap-key b/dist/src/pacwrap-key new file mode 100755 index 0000000..d4c410d --- /dev/null +++ b/dist/src/pacwrap-key @@ -0,0 +1,806 @@ +#!/usr/bin/bash +# +# pacwrap-key - manages pacwrap's keyring +# Based on pacman-key, +# which itself was derived from apt-key. +# +# Copyright (C) 2023-2024 Xavier R.M. +# Copyright (C) 2010-2021 Pacman Development Team +# SPDX-License-Identifier: GPL-3.0-only +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# TODO: Provide our own localization +export TEXTDOMAIN='pacwrap-bash' +export TEXTDOMAINDIR='/usr/share/locale' + +declare -r VERSION="version_string_placeholder" + +[[ ! $PACWRAP_DATA_DIR ]] && PACWRAP_DATA_DIR="$HOME/.local/share/pacwrap" + +PACWRAP_KEYRING_DIR="$PACWRAP_DATA_DIR/pacman/gnupg" +TARGETS=() +KEYSERVER='' +USE_COLOR='y' +VERBOSE=0 +COLOR_SET=0 +RESET= +BOLD= +BLUE= +GREEN= +RED= +YELLOW= +HEADER= +HEADER_RESET= +TAB=$' ' +SUB_HEADER=$TAB +SUB_TEXT=$TAB$TAB +CHILD_HEADER=$SUB_TEXT +CHILD_SUBTEXT=$TAB$TAB$TAB + +[[ $FAKECHROOT ]] && PACWRAP_KEYRING_DIR="/etc/pacman.d/gnupg"; + +main() { + parse_params $@ + colorize + check + + case $SWITCH in + init) initialize ${TARGETS[@]};; + a*) add_keys ${TARGETS[@]};; + d*) delete_keys ${TARGETS[@]};; + e*) edit_keys ${TARGETS[@]};; + E*) export_keys ${TARGETS[@]};; + f*) finger_keys ${TARGETS[@]};; + it*) import_trustdb ${TARGETS[@]};; + i*) import ${TARGETS[@]};; + ls*) list_sigs ${TARGETS[@]};; + lk*) list_keys ${TARGETS[@]};; + p*) populate_keyring ${TARGETS[@]};; + rk*) receive_keys ${TARGETS[@]};; + uk*) refresh_keys ${TARGETS[@]};; + v*) verify_sig ${TARGETS[@]};; + h) man_key;; + V) version;; + DB) ;; + *) invalid_option $SWITCH;; + esac + + [[ $SWITCH == *DB* ]] && updatedb +} + +parse_params() { + if [[ -z $@ ]]; then + invalid_option + fi + + local pos=0 + local count=0 + + while (( $# )); do case $1 in + --nocolor) count=0; USE_COLOR='n'; shift; continue;; + --verbose) count=0; VERBOSE=1; shift; continue;; + --keyserver) shift; count=0 KEYSERVER=$1; shift; continue;; + --import) SWITCH="iDB";; + --import-trustdb) SWITCH="itDB" ;; + --init) SWITCH="init";; + --list-sigs) SWITCH="ls";; + --lsign-key) SWITCH="lskDB";; + --populate) SWITCH+="pkDB";; + --edit-key) SWITCH="e";; + --refresh-keys) SWITCH="uk";; + -u|--updatedb) SWITCH+="DB";; + -a|--add) SWITCH="aDB";; + -d|--delete) SWITCH="dDB";; + -e|--export) SWITCH="EDB";; + -f|--finger) SWITCH="f";; + -l|--list-keys) SWITCH="lk";; + -r|--recv-keys) SWITCH="rkDB";; + -v|--verify) SWITCH="v";; + -h|--help*) SWITCH="h";; + -V|--version) SWITCH="V";; + -*) invalid_option $1;; + *) TARGETS+=($1); shift; continue; + esac + + if [[ $count > 0 ]]; then + invalid_option $1 + fi + + pos+=1; count+=1 + shift; done +} + +invalid_option() { + colorize + + if [[ -z $@ ]]; then + error "$(gettext "Operation not specified")" + else + error "$(gettext "Invalid option '%s'")" "$@" + fi + + echo "$(gettext "Try 'pacwrap-key -h' for more information on valid operational parameters.")" + exit 1 +} + +man_key() { + +cat << _USAGE +${HEADER}NAME$HEADER_RESET +${TAB}pacwrap-key + +${HEADER}USAGE$HEADER_RESET +${TAB}pacwrap-key [${BOLD}OPERATIONS$RESET] [${BOLD}TARGETS$RESET] + +${HEADER}SYNOPSIS$HEADER_RESET +${TAB}Manage pacwrap's gnupg trust database. This utility is only to be used with pacwrap and its associated containers. + +${HEADER}OPERATIONS$HEADER_RESET +${SUB_HEADER}-a, --add$RESET +${SUB_TEXT}Add the specified keys (empty for stdin) + +${SUB_HEADER}-d, --delete$RESET +${SUB_TEXT}REmove the specified keys from the keyring. + +${SUB_HEADER}-e, --export$RESET +${SUB_TEXT}Export the specified or all keyring ids. + +${SUB_HEADER}-f, --finger$RESET +${SUB_TEXT}List fingerprint for specified or all keyring ids. + +${SUB_HEADER}-l, --list-keys$RESET +${SUB_TEXT}List the specified or all keyring ids. + +${SUB_HEADER}-r, --recv-keys$RESET +${SUB_TEXT}Fetch the specified keyrings by id from the default or specified keyserver. + +${SUB_HEADER}-u, --updatedb$RESET +${SUB_TEXT}Update the trustdb of pacwrap on this local, standard privileged user. + +${SUB_HEADER}--init$RESET +${SUB_TEXT}Initialize the gnupg keyring for use with pacwrap. + +${SUB_HEADER}--populate$RESET +${SUB_TEXT}Populate the keyring with default signatures located typically in: +${SUB_TEXT}'/usr/share/pacman/keyrings/' + +${SUB_HEADER}--list-sigs$RESET +${SUB_TEXT}List populated keys alongside their signatures. + +${SUB_HEADER}--lsign-key$RESET +${SUB_TEXT}Locally sign the specified keyring id. + +${SUB_HEADER}--import$RESET +${SUB_TEXT}Locally sign the specified keyring id + +${SUB_HEADER}--import-trustdb$RESET +${SUB_TEXT}Locally sign the specified keyring id + +${SUB_HEADER}--keyserver $RESET[${BOLD}keyserver$RESET]$BOLD$RESET +${SUB_TEXT}Specify an alternate keyserver + +${SUB_HEADER}-h, --help$RESET +${SUB_TEXT}Invokes a printout of this manual to ${BOLD}STDOUT$RESET. + +${SUB_HEADER}-v, --version$RESET +${SUB_TEXT}Invokes a printout of version and copyright information to ${BOLD}STDOUT$RESET. + +${HEADER}ENVIRONMENT VARIABLES$HEADER_RESET +${SUB_HEADER}PACWRAP_DATA_DIR$RESET +${SUB_TEXT}Overrides the default XDG Directory Specification compliant data directory. Set this$RESET +${SUB_TEXT}environment variable to change the relative target directory for the keystore.$RESET + +${HEADER}COPYRIGHT$HEADER_RESET +${TAB}Copyright (C) 2023-2024 Xavier R.M. +${TAB}Copyright (C) 2010-2021 Pacman Development Team + +${TAB}This program may be freely redistributed under +${TAB}the terms of the GNU General Public License v3. + +_USAGE +} + +check() { + if ! type -p gpg >/dev/null; then + error_fatal "$(gettext "Cannot find the %s binary required for all %s operations.")" "gpg" "pacwrap-key" + fi + + if [[ $UID == 0 ]] && [[ $PACWRAP_KEYRING_DIR == $PACWRAP_DATA_DIR* ]]; then + error_fatal "$(gettext "Keyring stores held by %s cannot be operated upon as root.")" "pacwrap" + fi + + if [[ -d $PACWRAP_KEYRING_DIR ]] && [[ ! -w "$PACWRAP_KEYRING_DIR" ]]; then + error_fatal "$(gettext "Insufficient permissions to operate on designated keystore.")" "pacwrap" + fi + + if [[ ! -d $PACWRAP_DATA_DIR ]] && [[ ! $FAKECHROOT ]]; then + if [[ ! -w "$PACWRAP_KEYRING_DIR" ]]; then + error_fatal "$(gettext "Insufficient permissions to operate on designated keystore.")" + else + error_fatal "$(gettext "'%s': pacwrap data directory not found.")" $PACWRAP_DATA_DIR + fi + fi + + if [[ ! -d "$PACWRAP_KEYRING_DIR" ]] && [[ ! $FAKECHROOT ]]; then + mkdir -p "$PACWRAP_KEYRING_DIR" + check_keyring + else + check_keyring + fi + + GPG_PACWRAP=(gpg --homedir "${PACWRAP_KEYRING_DIR}" --no-permission-warning) + + if [[ -n ${KEYSERVER} ]]; then + GPG_PACWRAP+=(--keyserver "${KEYSERVER}") + fi +} + +colorize() { + [[ $COLOR_SET == 1 ]] && return + + if [[ ! -z $COLORTERM ]] && [[ $TERM != "dummy" ]] && [[ $USE_COLOR != 'n' ]] && [[ -t 2 ]]; then + RESET="" + BOLD="" + BLUE="${BOLD}" + GREEN="${BOLD}" + RED="${BOLD}" + YELLOW="${BOLD}" + fi + + HEADER=$BOLD + HEADER_RESET=$RESET + SUB_HEADER=$TAB$BOLD + SUB_TEXT=$TAB$TAB + CHILD_HEADER=$SUB_TEXT$BOLD + CHILD_SUBTEXT=$TAB$TAB$TAB + + readonly RESET BOLD BLUE GREEN RED YELLOW \ + HEADER HEADER_RESET \ + SUB_HEADER SUBTEXT \ + CHILD_HEADER CHILD_SUBTEXT; COLOR_SET=1 +} + +msg() { + (( QUIET )) && return + local mesg=$1; shift + printf "${GREEN}->${RESET}${BOLD} ${mesg}${RESET}\n" "$@" +} + +msg2() { + (( QUIET )) && return + local mesg=$1; shift + printf "${GREEN} ✓ ${RESET}${mesg}\n" "$@" +} + +ask() { + local mesg=$1; shift + printf "${BLUE}::${RESET}${BOLD} ${mesg}${RESET}" "$@" +} + +warning() { + local mesg=$1; shift + printf "${YELLOW}$(gettext "warning:")${RESET} ${mesg}\n" "$@" >&2 +} + +error() { + local mesg=$1; shift + printf "${RED}$(gettext "error:")${RESET} ${mesg}\n" "$@" >&2 +} + +error_fatal() { + local mesg=$1; shift + printf "${RED}$(gettext "error:")${RESET} ${mesg}\n" "$@" >&2 + exit 1 +} + +usage() { + pacwrap "$@" + exit 0 +} + +version() { + cat << ENDOF +${VERSION} +Copyright (C) 2023-2024 Xavier R.M. +Copyright (C) 2010-2021 Pacman Development Team + +Website: https://pacwrap.sapphirus.org/ +Github: https://github.com/pacwrap/pacwrap + +This program may be freely redstributed under the +terms of the GNU General Public License V3 only. + +ENDOF + exit 0 +} + +key_lookup_from_name() { + local ids + + mapfile -t ids < \ + <("${GPG_PACWRAP[@]}" --search-keys --batch --with-colons "$1" 2>/dev/null | + awk -F: '$1 == "pub" { print $2 }') + + # only return success on non-ambiguous lookup + case ${#ids[*]} in + 0) + error "$(gettext "Failed to lookup key by name:") %s" "$name" + return 1 + ;; + 1) + printf '%s' "${ids[0]}" + return 0 + ;; + *) + error "$(gettext "Key name is ambiguous:") %s" "$name" + return 1 + ;; + esac +} + +generate_master_key() { + # Generate the master key, which will be in both pubring and secring + msg "$(gettext "Generating pacwrap master key...")" + + "${GPG_PACWRAP[@]}" --gen-key --batch </dev/null; then + printf '%s\n' "$*" >> "$conffile" + fi +} + +check_keyids_exist() { + local ret=0 + for key in "$@"; do + # Verify if the key exists in pacman's keyring + if ! "${GPG_PACWRAP[@]}" --list-keys "$key" &>/dev/null ; then + error "$(gettext "The key identified by %s could not be found locally.")" "$key" + ret=1 + fi + done + if (( ret )); then + exit 1 + fi +} + +key_is_lsigned() { + secret_key=$("${GPG_PACWRAP[@]}" --with-colons --list-secret-key --quiet | awk -F : 'NR==1 {print $5}') + while IFS=: read -r type valid _ _ sign_key _; do + if [[ $type != "sig" || $valid != "!" ]]; then + continue + fi + if [[ "$sign_key" == "$secret_key" ]]; then + return 0 + fi + done < <("${GPG_PACWRAP[@]}" --with-colons --check-signatures --quiet "$1") + return 1 +} + +key_is_revoked() { + while IFS=: read -r type _ _ _ _ _ _ _ _ _ _ flags _; do + if [[ $type != "pub" ]]; then + continue + fi + if [[ $flags == *"D"* ]]; then + return 0 + fi + done < <("${GPG_PACWRAP[@]}" --with-colons --list-key --quiet "$1") + return 1 +} + +initialize() { + local conffile keyserv + # Check for simple existence rather than for a directory as someone + # may want to use a symlink here + [[ -e ${PACWRAP_KEYRING_DIR} ]] || mkdir -p -m 755 "${PACMAN_KEYRING_DIR}" + + # keyring files + [[ -f ${PACWRAP_KEYRING_DIR}/pubring.gpg ]] || touch ${PACWRAP_KEYRING_DIR}/pubring.gpg + [[ -f ${PACWRAP_KEYRING_DIR}/secring.gpg ]] || touch ${PACWRAP_KEYRING_DIR}/secring.gpg + [[ -f ${PACWRAP_KEYRING_DIR}/trustdb.gpg ]] || "${GPG_PACWRAP[@]}" --update-trustdb + chmod 644 ${PACWRAP_KEYRING_DIR}/{pubring,trustdb}.gpg + chmod 600 ${PACWRAP_KEYRING_DIR}/secring.gpg + + # gpg.conf + conffile="${PACWRAP_KEYRING_DIR}/gpg.conf" + [[ -f $conffile ]] || touch "$conffile" + chmod 644 "$conffile" + add_gpg_conf_option "$conffile" 'no-greeting' + add_gpg_conf_option "$conffile" 'no-permission-warning' + add_gpg_conf_option "$conffile" 'lock-never' + add_gpg_conf_option "$conffile" 'keyserver-options' 'timeout=10' + add_gpg_conf_option "$conffile" 'keyserver-options' 'import-clean' + + local gpg_ver=$(gpg --version | awk '{print $3; exit}') + if (( $(vercmp "$gpg_ver" 2.2.17) >= 0 )); then + add_gpg_conf_option "$conffile" 'keyserver-options' 'no-self-sigs-only' + fi + + # gpg-agent.conf + agent_conffile="${PACWRAP_KEYRING_DIR}/gpg-agent.conf" + [[ -f $agent_conffile ]] || touch "$agent_conffile" + chmod 644 "$agent_conffile" + add_gpg_conf_option "$agent_conffile" 'disable-scdaemon' + + # set up a private signing key (if none available) + if [[ $(secret_keys_available) -lt 1 ]]; then + generate_master_key + UPDATEDB=1 + fi +} + +populate_keyring() { + local KEYRING_IMPORT_DIR='/usr/share/pacman/keyrings' + + local keyring KEYRINGIDS=("$@") + local ret=0 + if (( ${#KEYRINGIDS[*]} == 0 )); then + # get list of all available keyrings + shopt -s nullglob + KEYRINGIDS=("$KEYRING_IMPORT_DIR"/*.gpg) + shopt -u nullglob + KEYRINGIDS=("${KEYRINGIDS[@]##*/}") + KEYRINGIDS=("${KEYRINGIDS[@]%.gpg}") + if (( ${#KEYRINGIDS[*]} == 0 )); then + error "$(gettext "No keyring files exist in %s.")" "$KEYRING_IMPORT_DIR" + ret=1 + fi + else + # verify listed keyrings exist + for keyring in "${KEYRINGIDS[@]}"; do + if [[ ! -f "$KEYRING_IMPORT_DIR/$keyring.gpg" ]]; then + error "$(gettext "The keyring file %s does not exist.")" "$KEYRING_IMPORT_DIR/$keyring.gpg" + ret=1 + fi + done + fi + + if (( ret )); then + exit 1 + fi + + # Variable used for iterating on keyrings + local keys key_id + + # Add keys from requested keyrings + for keyring in "${KEYRINGIDS[@]}"; do + msg "$(gettext "Appending keys from %s.gpg...")" "$keyring" + "${GPG_PACWRAP[@]}" --quiet --import "${KEYRING_IMPORT_DIR}/${keyring}.gpg" + done + + # Read the trusted key IDs to an array. Because this is an ownertrust + # file, we know we have the full 40 hex digit fingerprint values. + # Format of ownertrust dump file: + # 40CHARFINGERPRINTXXXXXXXXXXXXXXXXXXXXXXX:6: + # 40CHARFINGERPRINTXXXXXXXXXXXXXXXXXXXXXXX:5: + local -A trusted_ids + for keyring in "${KEYRINGIDS[@]}"; do + if [[ -s "${KEYRING_IMPORT_DIR}/${keyring}-trusted" ]]; then + while IFS=: read key_id _; do + # skip blank lines, comments; these are valid in this file + [[ -z $key_id || ${key_id:0:1} = \# ]] && continue + + if key_is_lsigned "$key_id" ; then + continue + fi + + # Mark this key to be lsigned + trusted_ids[$key_id]=$keyring + done < "${KEYRING_IMPORT_DIR}/${keyring}-trusted" + fi + done + + local -A revoked_ids + for keyring in "${KEYRINGIDS[@]}"; do + if [[ -s $KEYRING_IMPORT_DIR/$keyring-revoked ]]; then + while read -r key_id; do + if key_is_revoked "$key_id" ; then + continue + fi + + revoked_ids["$key_id"]=1 + done <"$KEYRING_IMPORT_DIR/$keyring-revoked" + fi + done + + if (( ${#trusted_ids[@]} > 0 )); then + msg "$(gettext "Locally signing trusted keys in keyring...")" + lsign_keys "${!trusted_ids[@]}" + msg "$(gettext "Importing owner trust values...")" + for keyring in "${KEYRINGIDS[@]}"; do + if [[ -s "${KEYRING_IMPORT_DIR}/${keyring}-trusted" ]]; then + "${GPG_PACWRAP[@]}" --import-ownertrust "${KEYRING_IMPORT_DIR}/${keyring}-trusted" + fi + done + fi + + if (( ${#revoked_ids[@]} > 0 )); then + local key_count=0 + msg "$(gettext "Disabling revoked keys in keyring...")" + for key_id in "${!revoked_ids[@]}"; do + if (( VERBOSE )); then + msg2 "$(gettext "Disabling key %s...")" "${key_id}" + fi + printf 'disable\nquit\n' | LANG=C "${GPG_PACWRAP[@]}" --command-fd 0 --no-auto-check-trustdb --quiet --batch --edit-key "${key_id}" 2>/dev/null + key_count=$((key_count+1)) + done + if (( key_count )); then + msg2 "$(gettext "Disabled %s keys.")" "${key_count}" + fi + fi +} + +add_keys() { + if ! "${GPG_PACWRAP[@]}" --quiet --batch --import "$@" ; then + error_fatal "$(gettext "A specified keyfile could not be added to the keyring.")" + fi +} + +delete_keys() { + check_keyids_exist "$@" + if ! "${GPG_PACWRAP[@]}" --quiet --batch --delete-key --yes "$@" ; then + error_fatal "$(gettext "A specified key could not be removed from the keyring.")" + fi +} + +edit_keys() { + check_keyids_exist "$@" + local ret=0 + for key in "$@"; do + if ! "${GPG_PACWRAP[@]}" --edit-key "$key" ; then + error "$(gettext "The key identified by %s could not be edited.")" "$key" + ret=1 + fi + done + if (( ret )); then + exit 1 + fi +} + +export_keys() { + check_keyids_exist "$@" + if ! "${GPG_PACWRAP[@]}" --armor --export "$@" ; then + error_fatal "$(gettext "A specified key could not be exported from the keyring.")" + fi +} + +finger_keys() { + check_keyids_exist + if ! "${GPG_PACWRAP[@]}" --batch --fingerprint "$@" ; then + error_fatal "$(gettext "The fingerprint of a specified key could not be determined.")" + fi +} + +import_trustdb() { + local importdir + local ret=0 + for importdir in "$@"; do + + if [[ -f "${importdir}/trustdb.gpg" ]]; then + gpg --homedir "${importdir}" --export-ownertrust | \ + "${GPG_PACWRAP[@]}" --import-ownertrust - + if (( PIPESTATUS )); then + error "$(gettext "%s could not be imported.")" "${importdir}/trustdb.gpg" + ret=1 + fi + else + error "$(gettext "File %s does not exist and could not be imported.")" "${importdir}/trustdb.gpg" + ret=1 + fi + done + if (( ret )); then + exit 1 + fi +} + +import() { + local importdir + local ret=0 + for importdir in "$@"; do + if [[ -f "${importdir}/pubring.gpg" ]]; then + if ! "${GPG_PACWRAP[@]}" --quiet --batch --import "${importdir}/pubring.gpg" ; then + error "$(gettext "%s could not be imported.")" "${importdir}/pubring.gpg" + ret=1 + fi + else + error "$(gettext "File %s does not exist and could not be imported.")" "${importdir}/pubring.gpg" + ret=1 + fi + done + if (( ret )); then + exit 1 + fi +} + +list_keys() { + check_keyids_exist + if ! "${GPG_PACWRAP[@]}" --batch --list-keys "$@" ; then + error_fatal "$(gettext "A specified key could not be listed.")" + fi +} + +list_sigs() { + check_keyids_exist + if ! "${GPG_PACWRAP[@]}" --batch --list-sigs "$@" ; then + error_fatal "$(gettext "A specified signature could not be listed.")" + fi +} + +lsign_keys() { + check_keyids_exist + + local ret=0 + local key_count=0 + for key_id in "$@"; do + if (( VERBOSE )); then + msg2 "$(gettext "Locally signing key %s...")" "${key_id}" + fi + # we cannot use --yes here as gpg would still ask for confirmation if a key has more than one uid + printf 'y\ny\n' | LANG=C "${GPG_PACWRAP[@]}" --command-fd 0 --quiet --batch --lsign-key "${key_id}" 2>/dev/null + if (( PIPESTATUS[1] )); then + error "$(gettext "%s could not be locally signed.")" "${key_id}" + ret=1 + fi + key_count=$((key_count+1)) + done + + if (( ret )); then + exit 1 + fi + if (( key_count )); then + msg2 "$(gettext "Locally signed %s keys.")" "${key_count}" + fi +} + +receive_keys() { + local ret=0 name id keyids emails + + # if the key is not a hex ID, do a lookup + for name; do + if [[ $name = ?(0x)+([0-9a-fA-F]) ]]; then + keyids+=("$name") + elif [[ $name = *@*.* ]]; then + emails+=("$name") + elif id=$(key_lookup_from_name "$name"); then + keyids+=("$id") + fi + done + + (( ${#keyids[*]}+${#emails[*]} > 0 )) || exit 1 + + if (( ${#emails[*]} > 0 )) && \ + ! "${GPG_PACWRAP[@]}" --auto-key-locate clear,nodefault,wkd,keyserver \ + --locate-key "${emails[@]}" ; then + error "$(gettext "Remote key not fetched correctly from WKD or keyserver.")" + ret=1 + fi + + if (( ${#keyids[*]} > 0 )) && ! "${GPG_PACWRAP[@]}" --recv-keys "${keyids[@]}" ; then + error "$(gettext "Remote key not fetched correctly from keyserver.")" + ret=1 + fi + + exit $ret +} + +check_keyring() { + if [[ ! -d "$PACWRAP_KEYRING_DIR" ]]; then + error_fatal "$(gettext "Keyring store not found in pacwrap data directory.")" "gpg" "pacwrap-key" + fi +} + +refresh_keys() { + local ret=0 ids masterkey emails + + check_keyids_exist "$@" + + # don't try to refresh the user's local masterkey + masterkey="$("${GPG_PACWRAP[@]}" --list-keys --with-colons pacman@localhost | + awk -F: '$1 == "pub" { print $5 }')" + + mapfile -t ids < \ + <("${GPG_PACWRAP[@]}" --list-keys --with-colons "$@" | + awk -F: '$1 == "pub" { print $5 }' | grep -vx "$masterkey") + + for id in "${ids[@]}"; do + mapfile -t emails < \ + <("${GPG_PACWRAP[@]}" --list-keys --list-options show-only-fpr-mbox "$id" | + awk '{print $2 }') + + # first try looking up the key in a WKD (only works by email address) + for email in "${emails[@]}"; do + "${GPG_PACWRAP[@]}" --locate-external-keys "$email" && break + done + + # if no key was found, fall back to using the keyservers (with the key fingerprint instead) + if (( $? )) && ! "${GPG_PACWRAP[@]}" --refresh-keys "$id"; then + error "$(gettext "Could not update key: %s")" "$id" + ret=1 + fi + done + + exit $ret +} + +verify_sig() { + local ret=0 sig=$1 file=$2 + if [[ -z $file && -f ${sig%.*} ]]; then + file=${sig%.*} + fi + if [[ -n $file ]]; then + local files=("$sig" "$file") + msg "Checking %s... (detached)" "$sig" + else + local files=("$sig") + msg "Checking %s... (embedded)" "$sig" + fi + if grep -q 'BEGIN PGP SIGNATURE' "$sig"; then + error_fatal "$(gettext "Cannot use armored signatures for packages: %s")" "$sig" + fi + + "${GPG_PACWRAP[@]}" --status-fd 1 --verify "${files[@]}" | grep -qE '^\[GNUPG:\] TRUST_(FULLY|ULTIMATE).*$' + + # return error if GnuPG fails to verify the signature + if [[ "${PIPESTATUS[0]}" -ne 0 ]]; then + error "$(gettext "The signature verification for %s failed.")" "$sig" + ret=1 + fi + + # return error if the signature is not trusted fully or ultimately + if [[ "${PIPESTATUS[1]}" -ne 0 ]]; then + error "$(gettext "The signature %s is not trusted.")" "$sig" + ret=1 + fi + + exit $ret +} + +updatedb() { + msg "$(gettext "Updating trust database...")" + if ! "${GPG_PACWRAP[@]}" --batch --check-trustdb ; then + error_fatal "$(gettext "Trust database could not be updated.")" + fi +} + +# PROGRAM START +if ! type gettext &>/dev/null; then + gettext() { + echo "$@" + } +fi + +main $@ diff --git a/dist/tools/clean.sh b/dist/tools/clean.sh index 2f17024..4416707 100755 --- a/dist/tools/clean.sh +++ b/dist/tools/clean.sh @@ -5,57 +5,60 @@ # Copyright (C) 2023-2024 Xavier R.M. # sapphirus(at)azorium(dot)net # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, with only version 3 of the License. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . -if ! [[ -z $COLORTERM ]] || [[ $TERM == "dummy" ]]; then - BOLD=$(tput bold) - GREEN=$(tput setaf 2) - RED=$(tput setaf 1) - RESET=$(tput sgr0) -fi +if [[ ! -d "$PWD/dist/tools/" ]]; then echo "This script may only be executed via the workspace root directory."; exit 2; fi +if [[ ! -f ./dist/tools/common.sh ]]; then echo "Common script is missing. Ensure the source tree is intact."; exit 2; fi -DIST_RUNTIME="./dist/runtime" -DIST_BASE="./dist/pacwrap-base-dist" -DIST_SCHEMA="./dist/schema" +source ./dist/tools/common.sh + +DIST_BIN="$PWD/dist/bin" +DIST_RUNTIME="$PWD/dist/runtime" +DIST_SCHEMA="$PWD/dist/schema" runtime() { if [[ -d "$DIST_RUNTIME" ]]; then rm -r "$DIST_RUNTIME" mkdir -p "$DIST_RUNTIME" - echo "$BOLD$GREEN Cleaned$RESET container runtime" - fi + cleaned "container runtime" + fi } filesystem() { if [[ -d "$DIST_SCHEMA" ]]; then rm -r "$DIST_SCHEMA" mkdir -p "$DIST_SCHEMA" - echo "$BOLD$GREEN Cleaned$RESET container schema" - fi + cleaned "container schema" + fi } -invalid() { - echo $BOLD$RED"error:$RESET Invalid parameter '$1'" +bin() { + if [[ -d "$DIST_BIN" ]]; then + rm -r "$DIST_BIN" + mkdir -p "$DIST_BIN" + cleaned "bin artifacts" + fi } main() { for var in "$@"; do case $var in - schema) filesystem;; - runtime) runtime;; - all) filesystem + schema) filesystem;; + runtime) runtime;; + bin) bin;; + all) bin + filesystem runtime;; - *) invalid $var;; + *) error_fatal "Invalid parameter '$1'";; esac; done } diff --git a/dist/tools/common.sh b/dist/tools/common.sh new file mode 100755 index 0000000..e560aa6 --- /dev/null +++ b/dist/tools/common.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# +# pacwrap - common.sh +# +# Copyright (C) 2023-2024 Xavier R.M. +# sapphirus(at)azorium(dot)net +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +[[ ! -z $COMMON_SCRIPT ]] && return + +if [[ -t 2 ]] && [[ ! -z $COLORTERM ]] && [[ $TERM != "dummy" ]]; then + BOLD="" + RED="" + GREEN="" + RESET="" +fi + +error_fatal() { + echo $BOLD$RED"error:$RESET $@"; + exit 1 +} + +error() { + echo $BOLD$RED"error:$RESET $@"; +} + +packaged() { + echo "$GREEN$BOLD Packaged$RESET $@" +} + +cleaned() { + echo "$BOLD$GREEN Cleaned$RESET $@" +} + +validate_args() { + [[ -z "$1" ]] && error_fatal "release target not specified." + + case $1 in + release);; + debug) ;; + *) error_fatal "release target $1 is invalid.";; + esac +} + +COMMON_SCRIPT=1; readonly COMMON_SCRIPT BOLD RED GREEN RESET diff --git a/dist/tools/key.sh b/dist/tools/key.sh new file mode 100755 index 0000000..0b653a1 --- /dev/null +++ b/dist/tools/key.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# +# pacwrap - key.sh +# +# This script packages pacwrap-key and defines version information within the script +# +# Copyright (C) 2023-2024 Xavier R.M. +# sapphirus(at)azorium(dot)net# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if [[ ! -d "$PWD/dist/tools/" ]]; then echo "This script may only be executed via the workspace root directory."; exit 2; fi +if [[ ! -f ./dist/tools/common.sh ]]; then echo "Common script is missing. Ensure the source tree is intact."; exit 2; fi + +source ./dist/tools/common.sh + +# +# Environment variables +# +DIST_BIN="./dist/bin" +DIST_SRC="./dist/src/pacwrap-key" +DIST_PKG="./dist/bin/pacwrap-key" + +# +# Main function +# +main() { + validate_args $1 + prepare_and_validate + package $1 + packaged "pacwrap-key [$1]" +} + +# +# Validate and prepare staging environment +# +prepare_and_validate() { + [[ ! -f "$DIST_SRC" ]] && error_fatal "'$DIST_SRC': file not found" + + clean + mkdir -p $DIST_BIN +} + +# +# Clean build artifacts +# +clean() { + if [[ -f "$DIST_PKG" ]]; then + rm $DIST_PKG + cleaned "pacwrap-key" + fi +} + +# +# Populate version string and package script +# +package() { + local version_string=$(version_string $1 | head -n1 | sed -e 's/[]\/$*.^[]/\\&/g') + local placeholder="version_string_placeholder" + + sed -e "s/$placeholder/$version_string/g" < $DIST_SRC > $DIST_PKG +} + + +version_string() { + local git=$(git rev-parse --short HEAD) + local release= + local dev= + + case $1 in + release) release="RELEASE" + date=$(git log -1 --date=format:%d/%m/%Y --format=%ad);; + debug) release="DEV" + date=$(date +'%d/%m/%Y %T');; + esac + + eval $(cat pacwrap/Cargo.toml | grep version | head -n1 | sed -e "s/version = /local version=/g") + echo "$version-$git-$release ($date)" +} + +main $@ diff --git a/dist/tools/package.sh b/dist/tools/package.sh new file mode 100755 index 0000000..2797555 --- /dev/null +++ b/dist/tools/package.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# pacwrap - package.sh +# +# This script calls upon various binaries to build resources and package artifacts +# +# Copyright (C) 2023-2024 Xavier R.M. +# sapphirus(at)azorium(dot)net +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if [[ ! -d "$PWD/dist/tools/" ]]; then echo "This script may only be executed via the workspace root directory."; exit 2; fi +if [[ ! -f ./dist/tools/common.sh ]]; then echo "Common script is missing. Ensure the source tree is intact."; exit 2; fi + +source ./dist/tools/common.sh + +validate_args $1 +./target/$1/pacwrap --help=all --format=man > ./dist/bin/pacwrap.1; if [[ $? != 0 ]]; then error_fatal "Generation of manpage failed."; fi +./target/$1/pacwrap --help=pacwrap.yml --format=man > ./dist/bin/pacwrap.yml.2; if [[ $? != 0 ]]; then error_fatal "Generation of manpage failed"; fi +./dist/tools/runtime.sh $1; if [[ $? != 0 ]]; then error_fatal "Build of container runtime failed."; fi diff --git a/dist/tools/prepare.sh b/dist/tools/prepare.sh new file mode 100755 index 0000000..dd75145 --- /dev/null +++ b/dist/tools/prepare.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# pacwrap - prepare.sh +# +# This script calls upon various binaries to build resources and package artifacts +# +# Copyright (C) 2023-2024 Xavier R.M. +# sapphirus(at)azorium(dot)net# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if [[ ! -d "$PWD/dist/tools/" ]]; then echo "This script may only be executed via the workspace root directory."; exit 2; fi +if [[ ! -f ./dist/tools/common.sh ]]; then echo "Common script is missing. Ensure the source tree is intact."; exit 2; fi + +source ./dist/tools/common.sh + +validate_args $1; if [[ $? != 0 ]]; then error_fatal "Argument validation failed."; fi +./dist/tools/key.sh $1; if [[ $? != 0 ]]; then error_fatal "Packaging of pacwrap-key failed."; fi +./dist/tools/schema.sh $1; if [[ $? != 0 ]]; then error_fatal "Build of container schema failed."; fi diff --git a/dist/tools/runtime.sh b/dist/tools/runtime.sh index 205b514..f7709f1 100755 --- a/dist/tools/runtime.sh +++ b/dist/tools/runtime.sh @@ -8,25 +8,22 @@ # Copyright (C) 2023-2024 Xavier R.M. # sapphirus(at)azorium(dot)net # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, with only version 3 of the License. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . -if ! [[ -z $COLORTERM ]] || [[ $TERM == "dummy" ]]; then - BOLD=$(tput bold) - RED=$(tput setaf 1) - GREEN=$(tput setaf 2) - RESET=$(tput sgr0) -fi +if [[ ! -d "$PWD/dist/tools/" ]]; then echo "This script may only be executed via the workspace root directory."; exit 2; fi +if [[ ! -f ./dist/tools/common.sh ]]; then echo "Common script is missing. Ensure the source tree is intact."; exit 2; fi + +source ./dist/tools/common.sh # # Environment variables @@ -36,6 +33,7 @@ BIN_DIR="/bin" ETC_DIR="/etc" DEST_DIR="$PWD/dist/runtime" DIST_SRC="$PWD/dist/src" +DIST_BIN="$PWD/dist/bin" FAKEROOT="/libfakeroot" FAKEROOT_DIR="/usr/lib/libfakeroot" FAKEROOT_SRC="$FAKEROOT_DIR/libfakeroot.so" @@ -54,43 +52,50 @@ BIN_UTILS="bash busybox faked fakeroot find gpg grep getopt sed" # Array of coreutils to include within the runtime environment # COREUTILS="cat chgrp chmod chown chroot cp cut dd df dir du head id install link ln ls mkdir mktemp mv pathchk pwd readlink realpath rm rmdir shred sort split stat sum tail tee touch tr truncate tsort unlink wc" +# +# Array of binaries to derive library paths +# +LIB_BINS="bash ls gpg grep" # -# Copy libraries +# Main function # -# $@: takes an array of system library paths -# -copy_libs() { - for path in ${@}; do - ldd $path | sed -e "s/.*=> //g;s/ (.*)//g;s/\t.*//g" | xargs cp -Lt $DEST_DIR$LIB_DIR - done -} - -# -# Copy binaries -# -# $@: takes an array of system binaries located in /usr/bin -# -copy_bins() { - for bin in ${@}; do - cp /usr/bin/$bin $DEST_DIR$BIN_DIR/$bin - - # Remove debuglink section, to ensure the Arch Build System doesn't complain - objcopy --remove-section=.gnu_debuglink $DEST_DIR$BIN_DIR/$bin 2>/dev/null - done +main() { + validate_args $1 + prepare_and_validate $1 + populate_lib + populate_bin + populate_etc + busybox_links + packaged "container runtime [$1]" } # # Validate and prepare staging environment # prepare_and_validate() { - clean + local agent="./target/$1/pacwrap-agent" + + if [[ ! -f "$agent" ]]; then + error_fatal "agent binary not built." + fi + + BIN_PATHS=("$agent") + + clean mkdir -p $DEST_DIR$LIB_DIR$FAKEROOT$FAKECHROOT $DEST_DIR$BIN_DIR $DEST_DIR$ETC_DIR - if [[ ! -d "$DEST_DIR$LIB_DIR" ]] || [[ ! -d $DEST_DIR$BIN_DIR ]]; then - echo $BOLD$RED"error:$RESET '$DEST_DIR': directory not found."; - exit 1 + if [[ ! -d "$DEST_DIR$LIB_DIR" ]] || [[ ! -d $DEST_DIR$BIN_DIR ]]; then + error_fatal "'$DEST_DIR': directory not found." fi + + for bin in $LIB_BINS; do + local path=$(type -P $bin) + + [[ -z $path ]] && error_fatal "'$bin' dependency not fulfilled" + + BIN_PATHS+=("$path") + done } # @@ -100,7 +105,7 @@ clean() { if [[ -d "$DEST_DIR" ]]; then rm -r "$DEST_DIR" mkdir -p "$DEST_DIR" - echo "$BOLD$GREEN Cleaned$RESET container runtime" + cleaned "container runtime" fi } @@ -108,7 +113,7 @@ clean() { # Populate libraries for container runtime # populate_lib() { - copy_libs ./target/$1/pacwrap-agent /usr/bin/gpg /usr/bin/bash /usr/bin/ls /usr/bin/grep + copy_libs ${BIN_PATHS[@]} cp -L $FAKEROOT_SRC $FAKEROOT_DEST cp -L $FAKECHROOT_SRC $FAKECHROOT_DEST ln -s .$FAKEROOT/libfakeroot.so $DEST_DIR$LIB_DIR/libfakeroot.so @@ -124,7 +129,7 @@ populate_lib() { # Populate binaries for container runtime # populate_bin() { - cp ./target/$1/pacwrap-agent $DEST_DIR$BIN_DIR/agent + cp ${BIN_PATHS[0]} $DEST_DIR$BIN_DIR/agent copy_bins $BIN_UTILS $COREUTILS ln -s bash $DEST_DIR$BIN_DIR/sh ln -s ld-linux-x86-64.so.2 $DEST_DIR$BIN_DIR/ld-linux.so.2 @@ -151,28 +156,28 @@ busybox_links() { } # -# Main function +# Copy libraries # -main() { - if [[ -z $1 ]]; then - echo $BOLD$RED"error:$RESET target not specified."; - exit 1 - fi +# $@: takes an array of system library paths +# +copy_libs() { + for path in ${@}; do + ldd $path | sed -e "s/.*=> //g;s/ (.*)//g;s/\t.*//g" | xargs cp -Lt $DEST_DIR$LIB_DIR + done +} - case $1 in - release);; - debug);; - *) echo $BOLD$RED"error:$RESET target $1 is invalid."; - exit 1;; - esac +# +# Copy binaries +# +# $@: takes an array of system binaries located in /usr/bin +# +copy_bins() { + for bin in ${@}; do + cp $(type -P $bin) $DEST_DIR$BIN_DIR/$bin - prepare_and_validate - populate_lib $1 - populate_bin $1 - populate_etc - busybox_links - - echo "$GREEN$BOLD Packaged$RESET container runtime [$1]" + # Remove debuglink section, to ensure the Arch Build System doesn't complain + objcopy --remove-section=.gnu_debuglink $DEST_DIR$BIN_DIR/$bin 2>/dev/null + done } main $@ diff --git a/dist/tools/schema.sh b/dist/tools/schema.sh index 7bfa8e3..b55f5f0 100755 --- a/dist/tools/schema.sh +++ b/dist/tools/schema.sh @@ -8,53 +8,58 @@ # Copyright (C) 2023-2024 Xavier R.M. # sapphirus(at)azorium(dot)net # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, with only version 3 of the License. # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, with only version 3 of the License. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -if ! [[ -z $COLORTERM ]] || [[ $TERM == "dummy" ]]; then - BOLD=$(tput bold) - RED=$(tput setaf 1) - GREEN=$(tput setaf 2) - RESET=$(tput sgr0) -fi +if [[ ! -d "$PWD/dist/tools/" ]]; then echo "This script may only be executed via the workspace root directory."; exit 2; fi +if [[ ! -f ./dist/tools/common.sh ]]; then echo "Common script is missing. Ensure the source tree is intact."; exit 2; fi + +source ./dist/tools/common.sh # # Environment variables # USR_DIR="/usr" ETC_DIR="/etc" -DEST_DIR="./dist/schema" -DIST_SRC="./dist/src" +DEST_DIR="$PWD/dist/schema" +DIST_SRC="$PWD/dist/src" +DIST_BIN="$PWD/dist/bin" + +# +# Main function +# +main() { + prepare_and_validate + populate_usr + populate_etc + create_archive $1 + packaged "container schema [$1]" +} # # Validate and prepare staging environment # prepare_and_validate() { clean - mkdir -p $DEST_DIR$USR_DIR $DEST_DIR$ETC_DIR + mkdir -p $DEST_DIR$USR_DIR $DEST_DIR$ETC_DIR $DIST_BIN if [[ ! -d "$DEST_DIR$LIB_DIR" ]] || [[ ! -d $DEST_DIR$BIN_DIR ]]; then - echo $BOLD$RED"error:$RESET '$DEST_DIR': directory not found."; - exit 1 + error_fatal "'$DEST_DIR': directory not found." fi -} -# -# Populate container skeleton archive -# -create_archive() { - cd $DEST_DIR - tar acf ../filesystem.tar.zst * + if [[ ! -d "$DIST_SRC" ]]; then + error_fatal "'$DIST_SRC': directory not found." + fi } # @@ -64,10 +69,18 @@ clean() { if [[ -d "$DEST_DIR" ]]; then rm -r "$DEST_DIR" mkdir -p "$DEST_DIR" - echo "$BOLD$GREEN Cleaned$RESET container schema" + cleaned "container schema" fi } +# +# Populate container skeleton archive +# +create_archive() { + cd $DEST_DIR + tar acf ../bin/filesystem.tar.zst * +} + # # Populate usr for container filesystem # @@ -75,17 +88,18 @@ populate_usr() { mkdir -p "${DEST_DIR}/usr/share/libalpm/hooks/" \ "${DEST_DIR}/usr/share/libalpm/scripts/" \ "${DEST_DIR}/usr/local/bin" \ - "${DEST_DIR}/usr/lib/" + "${DEST_DIR}/usr/lib/" ln -s /usr/lib/flatpak-xdg-utils/xdg-open "${DEST_DIR}/usr/local/bin/" ln -s /usr/lib/flatpak-xdg-utils/xdg-email "${DEST_DIR}/usr/local/bin/" - install -Dm 644 "$DIST_SRC/0-pacwrap-dist.hook" "${DEST_DIR}/usr/share/libalpm/hooks/0-pacwrap-dist.hook" - install -Dm 644 "$DIST_SRC/42-trust-permission.hook" "${DEST_DIR}/usr/share/libalpm/hooks/42-trust-permission.hook" - # TODO: Perhaps identify ourselves as our own distribution of Arch Linux? - # install -Dm 644 "$DIST_SRC/os-release" "${DEST_DIR}/usr/lib/os-release" - install -Dm 755 "$DIST_SRC/pacwrap-dist" "${DEST_DIR}/usr/share/libalpm/scripts/pacwrap-dist" + install -Dm 644 "$DIST_SRC/0-pacwrap-dist.hook" "${DEST_DIR}/usr/share/libalpm/hooks/0-pacwrap-dist.hook" + install -Dm 644 "$DIST_SRC/42-trust-permission.hook" "${DEST_DIR}/usr/share/libalpm/hooks/42-trust-permission.hook" + # TODO: Perhaps identify ourselves as our own distribution of Arch Linux? + # install -Dm 644 "$DIST_SRC/os-release" "${DEST_DIR}/usr/lib/os-release" + install -Dm 755 "$DIST_SRC/pacwrap-dist" "${DEST_DIR}/usr/share/libalpm/scripts/pacwrap-dist" + install -Dm 755 "$DIST_BIN/pacwrap-key" "${DEST_DIR}/usr/bin/pacwrap-key" } # @@ -113,28 +127,4 @@ populate_etc() { cp "$DIST_SRC/bash.bashrc" "$DEST_DIR$ETC_DIR" } -# -# Main function -# -main() { - if [[ -z $1 ]]; then - echo $BOLD$RED"error:$RESET target not specified."; - exit 1 - fi - - case $1 in - release);; - debug);; - *) echo $BOLD$RED"error:$RESET target $1 is invalid."; - exit 1;; - esac - - prepare_and_validate - populate_usr - populate_etc - create_archive $1 - - echo "$GREEN$BOLD Packaged$RESET container schema [$1]" -} - main $@ diff --git a/pacwrap-core/src/constants.rs b/pacwrap-core/src/constants.rs index cfb6a2d..b53afd5 100644 --- a/pacwrap-core/src/constants.rs +++ b/pacwrap-core/src/constants.rs @@ -33,7 +33,7 @@ pub static PROCESS_SLEEP_DURATION: Duration = Duration::from_millis(250); pub const BWRAP_EXECUTABLE: &str = "bwrap"; pub const DBUS_PROXY_EXECUTABLE: &str = "xdg-dbus-proxy"; pub const DEFAULT_PATH: &str = "/usr/local/bin:/bin:/usr/bin/"; -pub const PACMAN_KEY_SCRIPT: &str = "pacman-key"; +pub const PACMAN_KEY_SCRIPT: &str = "pacwrap-key"; pub const RUNTIME_DIRECTORY: &str = "/usr/share/pacwrap/runtime"; pub const RUNTIME_TLS_STORE: &str = "/etc/ca-certificates/extracted/tls-ca-bundle.pem"; diff --git a/pacwrap-core/src/exec.rs b/pacwrap-core/src/exec.rs index 4a2eadd..4021bf0 100644 --- a/pacwrap-core/src/exec.rs +++ b/pacwrap-core/src/exec.rs @@ -181,7 +181,6 @@ pub fn fakeroot_container(exec_type: ExecutionType, trap: Option, ins: } else { process.arg("--hostname").arg("FakeChroot") .arg("--ro-bind").arg("/etc/resolv.conf").arg("/mnt/fs/etc/resolv.conf") - .arg("--ro-bind").arg(&format!("{}/etc/bash.bashrc",*DIST_IMG)).arg("/mnt/fs/etc/bash.bashrc") .arg("--bind").arg(ins.vars().pacman_gnupg()).arg("/mnt/fs/etc/pacman.d/gnupg") .arg("--bind").arg(ins.vars().pacman_cache()).arg("/mnt/fs/var/cache/pacman/pkg") .arg("--setenv").arg("EUID").arg("0") @@ -264,12 +263,9 @@ pub fn transaction_agent(ins: &ContainerHandle, params: TransactionParameters, m } } -pub fn pacman_key(path: &str, cmd: Vec<&str>) -> Result<()> { +pub fn pacwrap_key(cmd: Vec<&str>) -> Result<()> { match Command::new(PACMAN_KEY_SCRIPT) .stderr(Stdio::null()) - .env("EUID", "0") - .arg("--gpgdir") - .arg(path) .args(cmd) .spawn() { diff --git a/pacwrap-core/src/sync.rs b/pacwrap-core/src/sync.rs index 79c46c1..c780b0e 100644 --- a/pacwrap-core/src/sync.rs +++ b/pacwrap-core/src/sync.rs @@ -33,7 +33,7 @@ use crate::{ err, error, error::*, - exec::pacman_key, + exec::pacwrap_key, sync::event::download::{self, DownloadEvent}, ErrorKind, }; @@ -174,8 +174,6 @@ fn alpm_handle(insvars: &ContainerVariables, db_path: String, remotes: &AlpmConf handle } -//TODO: Port pacman-key to Rust - pub fn instantiate_trust() -> Result<()> { let path = &format!("{}/pacman/gnupg/", *DATA_DIR); @@ -193,8 +191,8 @@ pub fn instantiate_trust() -> Result<()> { err!(ErrorKind::IOError(path.into(), error.kind()))? } - pacman_key(path, vec!["--init"])?; - pacman_key(path, vec!["--populate"]) + pacwrap_key(vec!["--init"])?; + pacwrap_key(vec!["--populate"]) } fn register_remote(mut handle: Alpm, config: &AlpmConfigData) -> Alpm { diff --git a/pacwrap-core/src/sync/transaction/aggregator.rs b/pacwrap-core/src/sync/transaction/aggregator.rs index 52468c3..044911f 100644 --- a/pacwrap-core/src/sync/transaction/aggregator.rs +++ b/pacwrap-core/src/sync/transaction/aggregator.rs @@ -196,8 +196,7 @@ impl<'a> TransactionAggregator<'a> { } pub fn keyring_update(&mut self, inshandle: &ContainerHandle) -> Result<()> { - fakeroot_container(NonInteractive, None, inshandle, vec!["/usr/bin/pacman-key", "--populate", "archlinux"])?; - fakeroot_container(NonInteractive, None, inshandle, vec!["/usr/bin/pacman-key", "--updatedb"])?; + fakeroot_container(NonInteractive, None, inshandle, vec!["/usr/bin/pacwrap-key", "--populate", "archlinux"])?; self.keyring = true; Ok(()) } diff --git a/pacwrap/build.rs b/pacwrap/build.rs index 525274d..c8a46d7 100644 --- a/pacwrap/build.rs +++ b/pacwrap/build.rs @@ -58,7 +58,7 @@ fn main() { panic!("Unsupported build target. Please refer to the build documentation for further information.") } else if built && (!Path::new("../dist/").exists() || !Path::new("../dist/tools/").exists()) { panic!("Distribution directory is missing. Please refer to the build documentation for further information.") - } else if built && !Path::new("../dist/filesystem.tar.zst").exists() { + } else if built && !Path::new("../dist/bin/filesystem.tar.zst").exists() { panic!("Container fileystem schema is missing. Please refer to the build documentation for further information.") } @@ -72,6 +72,6 @@ fn main() { println!("cargo:rustc-env=PACWRAP_BUILD={}", release(debug)); if built { - schema::serialize_path("../dist/filesystem.tar.zst", "../dist/filesystem.dat"); + schema::serialize_path("../dist/bin/filesystem.tar.zst", "../dist/bin/filesystem.dat"); } }