diff --git a/README b/README index 4d73677..ad67374 100644 --- a/README +++ b/README @@ -9,20 +9,26 @@ virtual machine image creation ------------------------------ To create a new virtual machine image, run - $> sudo ./create.sh -The creation is influenced by the following environment variables: + $> ./pvmbootstrap.sh [path to image] [arch] - ARCH - the target architecture of the image. default: armv7h +where arch is one of the supported parabola arches, which are currently: - SIZE - the size of the root image. default: 64GiB + official: [ i686, x86_64, armv7h ] + unofficial: [ ppc64le, riscv64 ] - MIRROR - the mirror used to pacstrap the image, anything valid in a `Server =` - line can go here. - default: https://redirector.parabola.nu/\$repo/os/\$arch} +the script will attempt to bootstrap a virtual machine of the selected +archituecture in the output file specified. If the output file already exists, +the script will emit a warning and ask for confirmation to proceed. -The created images are stored in the build/ directory. +the creation can be influenced by providing one or more of the following +arguments to pvmbootstrap.sh: + -s size -- set the size of the created VM image (default: 64G) + -M mirror -- set the mirror to fetch packages from + (default: https://repo.parabola.nu/$repo/os/$arch) + +the created machine images should be bootable using pvmboot.sh virtual machine boot -------------------- diff --git a/create.sh b/create.sh deleted file mode 100755 index efefe59..0000000 --- a/create.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - ############################################################################## - # parabola-imagebuilder # - # # - # Copyright (C) 2017, 2018 Andreas Grapentin # - # # - # 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, either version 3 of the License, or # - # (at your option) any later version. # - # # - # 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 . # - ############################################################################## - -# target options -export ARCH="${ARCH:-armv7h}" -export SIZE="${SIZE:-64G}" -export MIRROR="${MIRROR:-https://redirector.parabola.nu/\$repo/os/\$arch}" - -# common directories -startdir="$(pwd)" -export TOPBUILDDIR="$startdir"/build -export TOPSRCDIR="$startdir"/src -mkdir -p "$TOPBUILDDIR" -chown "$SUDO_USER" "$TOPBUILDDIR" - -# shellcheck source=src/shared/common.sh -. "$TOPSRCDIR"/shared/common.sh - -# sanity checks -if [ "$(id -u)" -ne 0 ]; then - die -e "$ERROR_INVOCATION" "must be root" -fi - -# shellcheck source=src/qemu.sh -. "$TOPSRCDIR"/qemu.sh - -qemu_make_image "$TOPBUILDDIR/parabola-$ARCH.img" "$SIZE" \ - || die "failed to prepare qemu base image" - -msg "all done." diff --git a/src/pvmboot.sh b/src/pvmboot.sh index 7196e5f..d6fbb27 100644 --- a/src/pvmboot.sh +++ b/src/pvmboot.sh @@ -50,7 +50,8 @@ pvm_mount() { workdir="$(mktemp -d -t pvm-XXXXXXXXXX)" || return loopdev="$(sudo losetup -fLP --show "$1")" || return - sudo mount "$loopdev"p1 "$workdir" || return + sudo mount "$loopdev"p1 "$workdir" \ + || sudo mount "$loopdev"p2 "$workdir" || return } pvm_umount() { @@ -106,7 +107,7 @@ pvm_native_arch() { setarch "$arch" /bin/true 2>/dev/null || return } -pvm_build_qemu_args() { +pvm_guess_qemu_args() { # if we're not running on X / wayland, disable graphics if [ -z "$DISPLAY" ]; then qemu_args+=(-nographic); fi @@ -117,8 +118,7 @@ pvm_build_qemu_args() { case "$2" in i386|x86_64|ppc64) qemu_args+=(-m 1G "$1") - if [ -z "$DISPLAY" ]; then qemu_args+=(-append "console=ttyS0"); fi - # unmount the drive early + # unmount the unneeded virtual drive early pvm_umount ;; arm) qemu_args+=( @@ -128,22 +128,23 @@ pvm_build_qemu_args() { -kernel "$workdir"/vmlinuz-linux-libre -dtb "$workdir"/dtbs/linux-libre/vexpress-v2p-ca9.dtb -initrd "$workdir"/initramfs-linux-libre.img - -append " rw root=/dev/mmcblk0p3" - -drive "if=sd,driver=raw,cache=writeback,file=$1") - if [ -z "$DISPLAY" ]; then qemu_args+=(-append " console=ttyAMA0"); fi ;; + -append "console=tty0 console=ttyAMA0 rw root=/dev/mmcblk0p3" + -drive "if=sd,driver=raw,cache=writeback,file=$1") ;; riscv64) qemu_args+=( -machine virt -m 1G -kernel "$workdir"/bbl - -append " rw root=/dev/vda" + -append "rw root=/dev/vda" -drive "file=${loopdev}p3,format=raw,id=hd0" -device "virtio-blk-device,drive=hd0" -object "rng-random,filename=/dev/urandom,id=rng0" -device "virtio-rng-device,rng=rng0" -device "virtio-net-device,netdev=usernet" -netdev "user,id=usernet") - if [ -z "$DISPLAY" ]; then qemu_args+=(-append " console=ttyS0"); fi ;; + if [ -z "$DISPLAY" ]; then + qemu_args+=(-append "console=ttyS0 rw root=/dev/vda"); + fi ;; *) error "%s: unable to determine default qemu args" "$1" return "$EXIT_FAILURE" ;; @@ -152,7 +153,7 @@ pvm_build_qemu_args() { main() { if [ "$(id -u)" -eq 0 ]; then - error "This program must be run as regular user" + error "This program must be run as a regular user" exit "$EXIT_NOPERMISSION" fi @@ -187,7 +188,7 @@ main() { fi local qemu_args=() - pvm_build_qemu_args "$imagefile" "$arch" || exit + pvm_guess_qemu_args "$imagefile" "$arch" || exit qemu_args+=("$@") (set -x; qemu-system-"$arch" "${qemu_args[@]}") diff --git a/src/pvmbootstrap.sh b/src/pvmbootstrap.sh new file mode 100644 index 0000000..ab6a52c --- /dev/null +++ b/src/pvmbootstrap.sh @@ -0,0 +1,266 @@ +#!/bin/bash +############################################################################### +# parabola-vmbootstrap -- create and start parabola virtual machines # +# # +# Copyright (C) 2017 - 2019 Andreas Grapentin # +# # +# 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, either version 3 of the License, or # +# (at your option) any later version. # +# # +# 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 . # +############################################################################### + +# shellcheck source=/usr/lib/libretools/messages.sh +. "$(librelib messages)" + +usage() { + print "usage: %s [-h] [-s size] [-M mirror] filename arch" "${0##*/}" + echo + echo "this script is developed as part of parabola-vmbootstrap." +} + +pvm_native_arch() { + local arch + case "$1" in + arm*) arch=armv7l;; + *) arch="$1";; + esac + + setarch "$arch" /bin/true 2>/dev/null || return +} + +pvm_bootstrap() { + msg "%s: starting image creation for %s" "$file" "$arch" + + qemu-img create -f raw "$file" "$size" || return + + trap 'pvm_cleanup' INT TERM RETURN + + local workdir loopdev + workdir="$(mktemp -d -t pvm-rootfs-XXXXXXXXXX)" || return + loopdev="$(sudo losetup -fLP --show "$file")" || return + + sudo dd if=/dev/zero of="$loopdev" bs=1M count=8 || return + + # partition and mount + case "$arch" in + i686|x86_64) + sudo parted -s "$loopdev" \ + mklabel gpt \ + mkpart primary 1MiB 2Mib \ + set 1 bios_grub on \ + mkpart primary ext2 2MiB 514MiB \ + mkpart primary linux-swap 514MiB 4610MiB \ + mkpart primary ext4 4610MiB 100% || return + + sudo partprobe "$loopdev" + + sudo mkfs.ext2 "$loopdev"p2 || return + sudo mkswap "$loopdev"p3 || return + sudo mkfs.ext4 "$loopdev"p4 || return + + sudo mount "$loopdev"p4 "$workdir" || return + sudo mkdir -p "$workdir"/boot || return + sudo mount "$loopdev"p2 "$workdir"/boot || return + ;; + ppc64le|riscv64) + sudo parted -s "$loopdev" \ + mklabel gpt \ + mkpart primary ext2 1MiB 513MiB \ + set 1 boot on \ + mkpart primary linux-swap 513MiB 4609MiB \ + mkpart primary ext4 4609MiB 100% || return + + sudo partprobe "$loopdev" + + sudo mkfs.ext2 "$loopdev"p1 || return + sudo mkswap "$loopdev"p2 || return + sudo mkfs.ext4 "$loopdev"p3 || return + + sudo mount "$loopdev"p3 "$workdir" || return + sudo mkdir -p "$workdir"/boot || return + sudo mount "$loopdev"p1 "$workdir"/boot || return + ;; + armv7h) + sudo parted -s "$loopdev" \ + mklabel gpt \ + mkpart ESP fat32 1MiB 513MiB \ + set 1 boot on \ + mkpart primary linux-swap 513MiB 4609MiB \ + mkpart primary ext4 4609MiB 100% || return + + sudo partprobe "$loopdev" + + sudo mkfs.vfat -F 32 "$loopdev"p1 || return + sudo mkswap "$loopdev"p2 || return + sudo mkfs.ext4 "$loopdev"p3 || return + + sudo mount "$loopdev"p3 "$workdir" || return + sudo mkdir -p "$workdir"/boot || return + sudo mount "$loopdev"p1 "$workdir"/boot || return + ;; + esac + + # setup qemu-user-static + if ! pvm_native_arch "$arch"; then + # target arch can't execute natively, pacstrap is going to need help by qemu + local qemu_arch + case "$arch" in + armv7h) qemu_arch=arm ;; + *) qemu_arch="$arch" ;; + esac + + if [[ -z $(sudo grep -l -F \ + -e "interpreter /usr/bin/qemu-$qemu_arch-" \ + -r -- /proc/sys/fs/binfmt_misc 2>/dev/null \ + | xargs -r sudo grep -xF 'enabled') ]] + then + error "%s: missing qemu-user-static for %s" "$file" "$arch" + return "$EXIT_FAILURE" + fi + + sudo mkdir -p "$workdir"/usr/bin + sudo cp -v "/usr/bin/qemu-$qemu_arch-"* "$workdir"/usr/bin || return + fi + + # pacstrap + local pacconf + pacconf="$(mktemp -t pvm-pacconf-XXXXXXXXXX)" || return + cat > "$pacconf" << EOF +[options] +Architecture = $arch +[libre] +Server = $mirror +[core] +Server = $mirror +[extra] +Server = $mirror +[community] +Server = $mirror +EOF + + local pkg=(base) + + case "$arch" in + i686|x86_64) pkg+=(grub) ;; + esac + + sudo pacstrap -GMcd -C "$pacconf" "$workdir" "${pkg[@]}" || return + + # finalize + case "$arch" in + i686|x86_64) + # create an fstab + sudo swapoff --all + sudo swapon "$loopdev"p3 + genfstab -U "$workdir" | sudo tee "$workdir"/etc/fstab + sudo swapoff "$loopdev"p3 + sudo swapon --all + + # install grub to the VM + sudo sed -i 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0"/' \ + "$workdir"/etc/default/grub || return + sudo arch-chroot "$workdir" grub-install --target=i386-pc "$loopdev" || return + sudo arch-chroot "$workdir" grub-mkconfig -o /boot/grub/grub.cfg || return + + # regenerate the chroot-botched initcpio + sudo cp "$workdir"/etc/mkinitcpio.d/linux-libre.preset{,.backup} || return + echo "default_options=\"-S autodetect\"" \ + | sudo tee -a "$workdir"/etc/mkinitcpio.d/linux-libre.preset || return + sudo arch-chroot "$workdir" mkinitcpio -p linux-libre || return + sudo mv "$workdir"/etc/mkinitcpio.d/linux-libre.preset{.backup,} || return + ;; + armv7h) + # create an fstab + sudo swapoff --all + sudo swapon "$loopdev"p2 + genfstab -U "$workdir" | sudo tee "$workdir"/etc/fstab + sudo swapoff "$loopdev"p2 + sudo swapon --all + ;; + riscv64) + # FIXME: for the time being, use fedora bbl to boot + sudo wget https://fedorapeople.org/groups/risc-v/disk-images/bbl \ + -O "$workdir"/boot/bbl || return + ;; + esac + + pvm_cleanup +} + +pvm_cleanup() { + trap - INT TERM RETURN + + [ -n "$pacconf" ] && rm -f "$pacconf" + unset pacconf + if [ -n "$workdir" ]; then + sudo rm -f "$workdir"/usr/bin/qemu-* + sudo umount -R "$workdir" + rmdir "$workdir" + fi + unset workdir + [ -n "$loopdev" ] && sudo losetup -d "$loopdev" + unset loopdev +} + +main() { + if [ "$(id -u)" -eq 0 ]; then + error "This program must be run as a regular user" + exit "$EXIT_NOPERMISSION" + fi + + local size="64G" + local mirror="https://repo.parabola.nu/\$repo/os/\$arch" + + # parse options + while getopts 'hs:M:' arg; do + case "$arg" in + h) usage; return "$EXIT_SUCCESS";; + s) size="$OPTARG";; + M) mirror="$OPTARG";; + *) usage >&2; exit "$EXIT_INVALIDARGUMENT";; + esac + done + local shiftlen=$(( OPTIND - 1 )) + shift $shiftlen + if [ "$#" -ne 2 ]; then usage >&2; exit "$EXIT_INVALIDARGUMENT"; fi + + local file="$1" + local arch="$2" + + # determine if the target arch is supported + case "$arch" in + i686|x86_64|armv7h) ;; + ppc64le|riscv64) + warning "%s: arch %s is experimental" "$file" "$arch";; + *) + error "%s: arch %s is unsupported" "$file" "$arch" + exit "$EXIT_INVALIDARGUMENT";; + esac + + if [ -e "$file" ]; then + warning "%s: file exists. Continue? [y/N]" "$file" + read -p " " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit "$EXIT_FAILURE" + fi + rm -f "$file" || exit + fi + + if ! pvm_bootstrap; then + error "%s: bootstrap failed" "$file" + exit "$EXIT_FAILURE" + fi + msg "%s: bootstrap complete" "$file" +} + +main "$@" diff --git a/src/qemu.sh b/src/qemu.sh deleted file mode 100644 index da4b16b..0000000 --- a/src/qemu.sh +++ /dev/null @@ -1,202 +0,0 @@ -#!/bin/bash - ############################################################################## - # parabola-imagebuilder # - # # - # Copyright (C) 2018 Andreas Grapentin # - # # - # 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, either version 3 of the License, or # - # (at your option) any later version. # - # # - # 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 . # - ############################################################################## - -qemu_img_partition_and_mount_for_armv7h() { - parted -s "$1" \ - mklabel gpt \ - mkpart ESP fat32 1MiB 513MiB \ - set 1 boot on \ - mkpart primary linux-swap 513MiB 4609MiB \ - mkpart primary ext4 4609MiB 100% || return - - check_exe -r mkfs.vfat mkfs.ext4 - - mkfs.vfat -F 32 "${1}p1" - mkswap "${1}p2" - mkfs.ext4 "${1}p3" - - mkdir -p "$2" - mount "${1}p3" "$2" || return - trap_add "umount -R $2" INT TERM EXIT - mkdir -p "$2"/boot - mount "${1}p1" "$2"/boot || return -} - -qemu_img_partition_and_mount_for_riscv64() { - qemu_img_partition_and_mount_for_x86_64 "$@" -} - -qemu_img_partition_and_mount_for_powerpc64le() { - qemu_img_partition_and_mount_for_x86_64 "$@" -} - -qemu_img_partition_and_mount_for_i686() { - qemu_img_partition_and_mount_for_x86_64 "$@" -} - -qemu_img_partition_and_mount_for_x86_64() { - parted -s "$1" \ - mklabel gpt \ - mkpart primary ext2 1MiB 513MiB \ - set 1 boot on \ - mkpart primary linux-swap 513MiB 4609MiB \ - mkpart primary ext4 4609MiB 100% || return - - check_exe mkfs.ext2 mkfs.ext4 - - mkfs.ext2 "${1}p1" - mkswap "${1}p2" - mkfs.ext4 "${1}p3" - - mkdir -p "$2" - mount "${1}p3" "$2" || return - trap_add "umount -R $2" INT TERM EXIT - mkdir -p "$2"/boot - mount "${1}p1" "$2"/boot || return -} - -qemu_img_losetup() { - echo -n "checking for free loop device ... " - loopdev=$(losetup -f --show "$1") || loopdev=no - echo "$loopdev" - partprobe "$loopdev" - - [ "x$loopdev" == "xno" ] && return "$ERROR_MISSING" - - trap_add "qemu_img_lorelease $loopdev" INT TERM EXIT -} - -qemu_img_lorelease() { - losetup -d "$1" -} - -qemu_arch_is_foreign() { - # borrowed from /usr/bin/librechroot - local setarch - case "$1" in - arm*) setarch=armv7l ;; - *) setarch="$1" ;; - esac - - echo -n "checking if arch '$1' is foreign ... " - local need_qemu=no - setarch "$setarch" /bin/true 2>/dev/null || need_qemu=yes - echo "$need_qemu" - - [ "x$need_qemu" == "xyes" ] || return -} - -qemu_setup_user_static() { - local interpreter - case "$ARCH" in - armv7h) interpreter=/usr/bin/qemu-arm- ;; - powerpc64le) interpreter=/usr/bin/qemu-ppc64le- ;; - *) interpreter=/usr/bin/qemu-"$ARCH"- ;; - esac - - if qemu_arch_is_foreign "$ARCH"; then - # target arch can't execute natively, pacstrap is going to need help by qemu - if [[ -z $(grep -l -F \ - -e "interpreter $interpreter" \ - -r -- /proc/sys/fs/binfmt_misc 2>/dev/null \ - | xargs -r grep -xF 'enabled') ]] - then - error "unable to continue - need qemu-user-static for $ARCH" - return "$ERROR_MISSING" - fi - - mkdir -p "$1"/usr/bin - cp -v "$interpreter"* "$1"/usr/bin || return - trap_add "qemu_cleanup_user_static $1" - fi -} - -qemu_cleanup_user_static() { - rm -f "$1"/usr/bin/qemu-* -} - -qemu_img_finalize_for_armv7h() { - true -} - -qemu_img_finalize_for_riscv64() { - # for the time being, use fedora bbl to boot - wget https://fedorapeople.org/groups/risc-v/disk-images/bbl \ - -O "$1"/boot/bbl -} - -qemu_img_finalize_for_powerpc64le() { - true -} - -qemu_img_finalize_for_i686() { - true -} - -qemu_img_finalize_for_x86_64() { - true -} - -qemu_make_image() { - msg "preparing parabola qemu image for $ARCH" - - # skip, if already exists - check_file "$1" && return - - check_exe -r parted - - # write to preliminary file - local tmpfile="$1.part" - rm -f "$tmpfile" - - # create an empty image - qemu-img create -f raw "$tmpfile" "$2" || return - - # create a minimal pacman.conf - cat > "$TOPBUILDDIR/pacman.conf.$ARCH" << EOF -[options] -Architecture = $ARCH -[libre] -Server = $MIRROR -[core] -Server = $MIRROR -[extra] -Server = $MIRROR -[community] -Server = $MIRROR -EOF - - # setup the image (in a subshell for trap management) - ( - loopdev='' - qemu_img_losetup "$tmpfile" || return - - dd if=/dev/zero of="$loopdev" bs=1M count=8 || return - "qemu_img_partition_and_mount_for_$ARCH" "$loopdev" "$TOPBUILDDIR"/mnt || return - - qemu_setup_user_static "$TOPBUILDDIR"/mnt || return - - pacstrap -GMcd -C "$TOPBUILDDIR/pacman.conf.$ARCH" "$TOPBUILDDIR"/mnt || return - - "qemu_img_finalize_for_$ARCH" "$TOPBUILDDIR"/mnt || return - ) || return - - mv "$tmpfile" "$1" -} diff --git a/src/shared/checks.sh b/src/shared/checks.sh deleted file mode 100644 index 38a94dc..0000000 --- a/src/shared/checks.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash - ############################################################################## - # parabola-imagebuilder # - # # - # Copyright (C) 2018 Andreas Grapentin # - # # - # 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, either version 3 of the License, or # - # (at your option) any later version. # - # # - # 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 . # - ############################################################################## - -check_exe() { - local OPTIND o r= - while getopts "r" o; do - case "$o" in - r) r=yes ;; - *) die -e "$ERROR_INVOCATION" "Usage: ${FUNCNAME[0]} [-r] program ..." ;; - esac - done - shift $((OPTIND-1)) - - local v res=0 - for v in "$@"; do - echo -n "checking for $v in \$PATH ... " - - local have_exe=yes - type -p "$v" >/dev/null || have_exe=no - echo $have_exe - - if [ "x$have_exe" != "xyes" ]; then - [ "x$r" == "xyes" ] && die -e "$ERROR_MISSING" "missing $v in \$PATH" - res="$ERROR_MISSING" - fi - done - - return "$res" -} - -check_file() { - local OPTIND o r= - while getopts "r" o; do - case "$o" in - r) r=yes ;; - *) die -e "$ERROR_INVOCATION" "Usage: ${FUNCNAME[0]} [-r] file ..." ;; - esac - done - shift $((OPTIND-1)) - - local v res=0 - for v in "$@"; do - echo -n "checking for $v ... " - - local have_file=yes - [ -f "$v" ] || have_file=no - echo $have_file - - if [ "x$have_file" != "xyes" ]; then - [ "x$r" == "xyes" ] && die -e "$ERROR_MISSING" "missing $v in filesystem" - res="$ERROR_MISSING" - fi - done - - return "$res" -} - -check_gpgkey() { - local OPTIND o r= - while getopts "r" o; do - case "$o" in - r) r=yes ;; - *) die -e "$ERROR_INVOCATION" "Usage: ${FUNCNAME[0]} [-r] key" ;; - esac - done - shift $((OPTIND-1)) - - local v res=0 - for v in "$@"; do - echo -n "checking for key $v ... " - - local have_key=yes - sudo -u "$SUDO_USER" gpg --list-keys "$v" &>/dev/null || have_key=no - echo $have_key - - if [ "x$have_key" != "xyes" ]; then - [ "x$r" == "xyes" ] && die -e "$ERROR_MISSING" "missing $v in keyring" - res="$ERROR_MISSING" - fi - done - - return "$res" -} diff --git a/src/shared/common.sh b/src/shared/common.sh deleted file mode 100644 index 62dfb15..0000000 --- a/src/shared/common.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - ############################################################################## - # parabola-imagebuilder # - # # - # Copyright (C) 2018 Andreas Grapentin # - # # - # 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, either version 3 of the License, or # - # (at your option) any later version. # - # # - # 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 . # - ############################################################################## - -# shellcheck source=src/shared/feedback.sh -. "$TOPSRCDIR"/shared/feedback.sh -# shellcheck source=src/shared/checks.sh -. "$TOPSRCDIR"/shared/checks.sh - -retry() { - local OPTIND o n=5 s=60 - while getopts "n:s:" o; do - case "$o" in - n) n="$OPTARG" ;; - s) s="$OPTARG" ;; - *) die -e $ERROR_INVOCATION "Usage: ${FUNCNAME[0]} [-n tries] [-s delay] cmd ..." ;; - esac - done - shift $((OPTIND-1)) - - for _ in $(seq "$((n - 1))"); do - "$@" && return 0 - sleep "$s" - done - "$@" || return -} - -# appends a command to a trap -# source: https://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal -# -# - 1st arg: code to add -# - remaining args: names of traps to modify -# -trap_add() { - trap_add_cmd=$1; shift || fatal "${FUNCNAME[0]} usage error" - for trap_add_name in "$@"; do - trap -- "$( - # helper fn to get existing trap command from output - # of trap -p - extract_trap_cmd() { printf '%s\n' "$3"; } - # print the new trap command - printf '%s\n' "${trap_add_cmd}" - # print existing trap command with newline - eval "extract_trap_cmd $(trap -p "${trap_add_name}")" - )" "${trap_add_name}" \ - || fatal "unable to add to trap ${trap_add_name}" - done -} -# set the trace attribute for the above function. this is -# required to modify DEBUG or RETURN traps because functions don't -# inherit them unless the trace attribute is set -declare -f -t trap_add diff --git a/src/shared/feedback.sh b/src/shared/feedback.sh deleted file mode 100644 index ff050e7..0000000 --- a/src/shared/feedback.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - ############################################################################## - # parabola-imagebuilder # - # # - # Copyright (C) 2018 Andreas Grapentin # - # # - # 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, either version 3 of the License, or # - # (at your option) any later version. # - # # - # 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 . # - ############################################################################## - -# error codes -export ERROR_UNSPECIFIED=1 -export ERROR_INVOCATION=2 -export ERROR_MISSING=3 -export ERROR_BUILDFAIL=4 -export ERROR_KEYFAIL=5 - -# messaging functions -msg() { - echo "$(tput bold)$(tput setf 2)==>$(tput setf 7) $*$(tput sgr0)"; -} - -error() { - echo "$(tput bold)$(tput setf 4)==> ERROR:$(tput setf 7) $*$(tput sgr0)" 1>&2 -} - -die() { - local OPTIND o e="$ERROR_UNSPECIFIED" - while getopts "e:" o; do - case "$o" in - e) e="$OPTARG" ;; - *) die -e "$ERROR_INVOCATION" "Usage: ${FUNCNAME[0]} [-e status] msg ..." ;; - esac - done - shift $((OPTIND-1)) - - error "$@" - trap - ERR - exit "$e" -} -trap 'die "unknown error"' ERR