osi-tools/osi-mk

602 lines
20 KiB
Plaintext
Raw Normal View History

2018-07-25 19:23:05 +00:00
#!/usr/bin/env bash
2018-08-18 18:42:42 +00:00
# Copyright (C) 2018 Luke Shumaker
# Copyright (C) 2023 Umorpha Systems
2018-08-18 18:42:42 +00:00
# SPDX-License-Identifier: AGPL-3.0-or-later
2018-08-06 01:56:44 +00:00
declare -r NAME=osi-mk
2023-10-23 22:19:22 +00:00
declare -r VERSION=20231023
2018-07-25 19:23:05 +00:00
# Why is this different than mkosi[1]?
#
# - mkosi claims to be "legacy-free"--but they call everything that's
2018-08-06 00:09:42 +00:00
# not systemd "legacy"; that clearly won't do for creating OpenRC
# images.
2018-07-25 19:23:05 +00:00
#
# - mkosi claims to be "legacy-free", only supporting GPT
# disk-labels--but btrfs can be booted directly, without a separate
# disk-label, or a separate ESP partition. To a btrfs disk, GPT/ESP
# is legacy.
#
# - Using a raw btrfs disk means that it can easily be mounted without
2023-10-25 11:04:49 +00:00
# first dissecting the disk-label.
2018-07-25 19:23:05 +00:00
#
# [1]: https://github.com/systemd/mkosi
2018-08-12 21:53:08 +00:00
set -euE -o pipefail
source "$(dirname -- "${BASH_SOURCE[0]}")/lib/osi.sh"
2018-08-06 01:49:45 +00:00
loaded_modules=()
2018-08-06 04:02:37 +00:00
load_module() {
2018-08-06 01:49:45 +00:00
local module
2018-08-06 04:02:37 +00:00
if ! [[ -f $1 ]]; then
error $EXIT_INVALIDARGUMENT 'Module does not exist: %s' "$1"
2018-08-06 04:02:37 +00:00
fi
2018-08-06 01:49:45 +00:00
module="$(realpath -- "$1")"
if in_array "$module" "${loaded_modules[@]}"; then
2018-08-06 04:02:37 +00:00
return 0
2018-08-06 01:49:45 +00:00
fi
2018-08-06 04:02:37 +00:00
loaded_modules+=("$module")
# shellcheck source=/dev/null
2018-08-06 04:02:37 +00:00
source "$1"
2018-08-06 01:49:45 +00:00
}
2018-08-12 21:53:08 +00:00
osi-mk:genfstab() {
local arg_mountpoint=$1
2023-10-30 21:17:34 +00:00
{
# This header mimics the stock fstat from 'filesystem'.
echo '# Static information about the filesystems.'
echo '# See fstab(5) for details.'
echo
{
# This doesn't quite mimic the header from 'filesystem'
#
# - I swapped "file system" for "device", for clarity,
# and so that it's one word for `column -t`.
# - I swapped "pass" for "fsck", for clarity.
# - I removed the space after the "#", so it works with
# `column -t`.
echo '#<device> <mountpoint> <type> <options> <dump> <fsck>'
2023-10-30 21:14:27 +00:00
case "${arg_conf[format]}" in
erofs)
echo "UUID=${arg_conf[fsuuid]}" / erofs defaults 0 1
;;
*)
genfstab -t uuid "$arg_mountpoint" | grep '^[#]' | awk '$3 != "swap"'
;;
esac
2023-10-30 21:17:34 +00:00
} | column -t
} >"${arg_mountpoint}/etc/fstab"
2018-08-12 21:53:08 +00:00
}
2018-08-06 01:56:44 +00:00
osi-mk:directories() {
2018-08-06 01:49:45 +00:00
local arg_mountpoint=$1
local spec outside inside
2018-08-06 04:02:37 +00:00
for spec in "${arg_directories[@]}"; do
2018-08-06 01:49:45 +00:00
outside="${spec%:*}"
inside="${spec#"${outside}:"}"
2018-08-13 01:03:56 +00:00
# TODO: maybe rsync would be better?
2018-08-06 04:02:37 +00:00
mkdir -p -- "$(dirname -- "${arg_mountpoint}/${inside}")"
2018-08-13 05:16:36 +00:00
print 'Copying %q to %q:%q' "$outside" "$arg_file" "$inside"
2023-10-29 07:07:28 +00:00
cp -aT --no-preserve=ownership -- "$outside" "${arg_mountpoint}/${inside}"
2018-08-06 01:49:45 +00:00
done
}
2023-10-28 22:32:47 +00:00
osi-mk:hostname() {
local arg_mountpoint=$1
echo "${arg_conf[hostname]}" >"$arg_mountpoint/etc/hostname"
}
2023-10-25 17:54:35 +00:00
osi-mk:mkinitcpio() {
local arg_mountpoint=$1
print 'Configuring mkinitcpio to include all drivers'
mkdir -p -- "$arg_mountpoint/etc/mkinitcpio.conf.d"
cat >"$arg_mountpoint/etc/mkinitcpio.conf.d/osi-mk.conf" <<'EOF'
2023-10-25 17:54:35 +00:00
#!/hint/bash
# Copyright (C) 2023 Umorpha Systems
# SPDX-License-Identifier: AGPL-3.0-or-later
# Remove 'autodetect' from HOOKS; include all drivers.
for ((i=0; i<${#HOOKS[@]}; i++)); do
if [[ ${HOOKS[i]} == autodetect ]]; then
HOOKS=("${HOOKS[@]:0:i}" "${HOOKS[@]:i+1}")
2023-10-25 17:54:35 +00:00
fi
done
EOF
}
2018-08-12 21:53:08 +00:00
osi-mk:grub-install() {
local arg_mountpoint=$1
# shellcheck disable=SC2016
2018-08-12 21:53:08 +00:00
arch-chroot -- "$arg_mountpoint" sh -c \
2023-10-25 17:54:35 +00:00
'grub-install --target=i386-pc "$(awk '\''$2 == "/" { print $1 }'\'' </proc/mounts)"'
}
osi-mk:grub-mkconfig() {
local arg_mountpoint=$1
2018-08-12 21:53:08 +00:00
arch-chroot -- "$arg_mountpoint" grub-mkconfig -o /boot/grub/grub.cfg
}
2023-11-05 07:25:59 +00:00
osi-mk:source-date-epoch() {
local arg_mountpoint=$1
# Backdate package install dates to 1 second before SOURCE_DATE_EPOCH.
local file
for file in "$arg_mountpoint"/var/lib/pacman/local/*/desc; do
awk -v maxdate="$((SOURCE_DATE_EPOCH-1))" '
BEGIN{
x=0
}
$0 == "%INSTALLDATE%" {
x=2
}
{
if (x == 1 && $0 > maxdate) {
print maxdate
} else {
print
}
x--
}
' <"$file" >"$file.tmp"
mv -T -- "$file.tmp" "$file"
touch --date="@$((SOURCE_DATE_EPOCH-1))" -- "$file"
done
# Backdate file timestamps to SOURCE_DATE_EPOCH.
case "${arg_conf[format]}" in
raw_btrfs)
# Ugg, it's lame that `find` can't find files newer than a
# timestamp without using a temporary file.
touch --date="@$SOURCE_DATE_EPOCH" -- "$arg_mountpoint/.source_date_epoch"
find "$arg_mountpoint" -xdev -depth \
-newer "$arg_mountpoint/.source_date_epoch" \
-exec touch --no-dereference --date="@$SOURCE_DATE_EPOCH" -- {} +
rm -f -- "$arg_mountpoint/.source_date_epoch"
touch --date="@$SOURCE_DATE_EPOCH" -- "$arg_mountpoint/"
;;
erofs)
# Nothing to do; mkfs.erofs supports SOURCE_DATE_EPOCH.
:
;;
esac
}
2018-07-25 19:23:05 +00:00
main() {
local arg_orig=("$@")
2018-07-25 19:23:05 +00:00
local arg_mode=outside
local arg_mountpoint=
2018-08-12 02:35:52 +00:00
local arg_edit=false
local arg_edit_base=
2018-08-06 01:49:45 +00:00
local arg_directories=()
2018-08-06 00:09:42 +00:00
local arg_modules=()
2018-08-06 01:49:45 +00:00
local arg_packages=()
local arg_package_files=()
2023-10-30 21:14:27 +00:00
declare -A arg_conf=(
[format]=raw_btrfs
[bootloader]=grub
[genfstab]=true
2023-10-30 21:14:27 +00:00
)
2023-11-05 07:25:59 +00:00
if [[ "${SOURCE_DATE_EPOCH:-}" =~ ^([0-9]+|auto)$ ]]; then
arg_conf[SOURCE_DATE_EPOCH]="$SOURCE_DATE_EPOCH"
arg_orig=(--conf=SOURCE_DATE_EPOCH="${arg_conf[SOURCE_DATE_EPOCH]}" "${arg_orig[@]}")
fi
2018-08-06 01:49:45 +00:00
2018-07-25 19:23:05 +00:00
local args
2023-10-30 21:14:27 +00:00
if ! args="$(getopt -n "${0##*/}" -o "s:e::d:m:p:P:C:hV" -l "inside::,edit::,directory:,module:,package:,package-file:,conf:,help,version" -- "$@")"; then
2018-07-25 19:23:05 +00:00
arg_mode=error
else
eval "set -- $args"
2018-07-25 19:23:05 +00:00
while true; do
case "$1" in
--inside) shift; arg_mode=inside; arg_mountpoint=$1; shift;;
-e|--edit) shift; arg_edit=true; arg_edit_base=$1; shift;;
2018-08-06 01:49:45 +00:00
2018-08-06 04:02:37 +00:00
-d|--directory) shift; arg_directories+=("$1"); shift;;
2018-08-06 00:09:42 +00:00
-m|--module) shift; arg_modules+=("$1"); shift;;
2018-08-06 04:02:37 +00:00
-p|--package) shift; arg_packages+=("$1"); shift;;
-P|--package-file) shift; arg_package_files+=("$1"); shift;;
-C|--conf) shift; arg_conf["${1%%=*}"]="${1#*=}"; shift;;
2018-08-06 01:49:45 +00:00
-V|--version) shift; arg_mode=version;;
-h|--help) shift; arg_mode=usage;;
2018-07-25 19:23:05 +00:00
--) shift; break;;
*) error $EXIT_FAILURE 'Internal error. The programmer writing this tool screwed up.';;
2018-07-25 19:23:05 +00:00
esac
done
2018-08-06 00:09:42 +00:00
case "$arg_mode" in
outside|inside)
if (( $# != 1 )); then
if (( $# == 0 )); then
error 0 "Expected 1 positional argument, got none"
else
2018-08-14 20:17:31 +00:00
error 0 "Expected 1 positional argument, got %d: %s" "$#" "${*@Q}"
2018-08-06 00:09:42 +00:00
fi
arg_mode=error
else
arg_file=$1
fi
2018-08-12 22:15:43 +00:00
for module in "${arg_modules[@]}"; do
if ! [[ -f $module ]]; then
error 0 'Module does not exist: %s' "$module"
arg_mode=error
fi
done
for dirspec in "${arg_directories[@]}"; do
if ! [[ -d "${dirspec%:*}" ]]; then
error 0 'Directory does not exist: %s' "${dirspec%:*}"
arg_mode=error
fi
done
for package_file in "${arg_package_files[@]}"; do
if ! [[ -f "$package_file" ]]; then
error 0 'Package file does not exist: %s' "$package_file"
arg_mode=error
fi
done
if [[ $arg_mode = outside ]]; then
if [[ ( $arg_edit = false || -n $arg_edit_base ) && -e $arg_file ]]; then
error $EXIT_INVALIDARGUMENT 'Image file already exists, refusing to overwrite: %s' "$arg_file"
fi
if $arg_edit; then
if ! [[ -f ${arg_edit_base:-$arg_file} ]]; then
error $EXIT_INVALIDARGUMENT 'Image must already exist to --edit: %s' "${arg_edit_base:-$arg_file}"
fi
fi
2023-10-30 21:14:27 +00:00
case "${arg_conf[format]}" in
raw_btrfs)
if [[ -z ${arg_conf[size]:-} ]]; then
error $EXIT_INVALIDARGUMENT 'Must specify --conf=size= when creating a new image'
fi
;;
erofs)
if $arg_edit; then
error $EXIT_INVALIDARGUMENT '--conf=format=erofs does not support --edit'
fi
if [[ ${arg_conf[bootloader]} != none ]]; then
error $EXIT_INVALIDARGUMENT '--conf=format=erofs requires --conf=bootloader=none'
fi
;;
*)
error $EXIT_INVALIDARGUMENT 'Unrecognized --conf=format= value: %q' "${arg_conf[format]}"
;;
esac
case "${arg_conf[bootloader]}" in
grub) :;;
none) :;;
*)
error $EXIT_INVALIDARGUMENT 'Unrecognized --conf=bootloader= value: %q' "${arg_conf[bootloader]}"
;;
esac
case "${arg_conf[genfstab],,}" in
1|t|true|y|yes) :;;
0|f|false|n|no) :;;
*)
error $EXIT_INVALIDARGUMENT 'Unrecognized --conf=genfstab= value: %q' "${arg_conf[genfstab]}"
;;
esac
2023-11-05 07:25:59 +00:00
if ! [[ "${arg_conf[SOURCE_DATE_EPOCH]:-}" =~ ^([0-9]+|auto|)$ ]]; then
ferror $EXIT_INVALIDARGUMENT 'Invalid --conf=SOURCE_DATE_EPOCH= value: %q' "${arg_conf[SOURCE_DATE_EPOCH]}"
fi
fi
2018-08-06 00:09:42 +00:00
;;
esac
2018-07-25 19:23:05 +00:00
fi
case "$arg_mode" in
error)
print "Try '%q --help' for more information" "${0##*/}" >&2
return $EXIT_INVALIDARGUMENT
;;
2018-07-25 19:23:05 +00:00
version)
print "%s (osi-tools) %s" "$NAME" "$VERSION"
return $EXIT_SUCCESS
2018-07-25 19:23:05 +00:00
;;
usage)
2018-08-12 20:35:56 +00:00
print 'Usage: %s [OPTIONS] FILENAME.img' "${0##*/}"
print 'Operating System Image: Make'
2018-07-25 19:23:05 +00:00
echo
print 'Create a mountable, bootable OS image.'
echo
print 'OPTIONS:'
# 000000000011111111112222222222333333333344444444445555555555666666666677777777778
# 012345678901234567890123456789012345678901234567890123456789012345678901234567890
print ' -s SIZE, --size=SIZE set the size of the image'
print ' -e[BASE.img], --edit[=BASE.img] edit an existing image'
2018-07-25 19:23:05 +00:00
# --inside is internal-only; undocumented
2018-08-06 01:49:45 +00:00
echo
print ' -d OUTSIDE:INSIDE, --directory=OUTSIDE:INSIDE include the given directory'
print ' -m MOD.sh, --module=MOD.sh include the given module'
print ' -p PKGNAME, --package=PKGNAME include the given package (or group)'
print ' -P PKG.pkg.tar.xz, --package-file=PKG.pkg.tar.xz include the given package file'
print ' -C KEY=VAL, --conf=KEY=VAL set set a config option'
2018-07-25 19:23:05 +00:00
echo
print ' -h, --help display this help'
print ' -V, --version output version information'
return $EXIT_SUCCESS
2018-07-25 19:23:05 +00:00
;;
# main code starts here
2023-10-25 11:04:49 +00:00
outside) # the part that runs as a normal user without the image mounted
2023-10-30 21:14:27 +00:00
mount_dev="$arg_file"
mount_opt=''
gprintf -v prefix '%s [format]' "$NAME"
{
2023-10-30 21:14:27 +00:00
case "${arg_conf[format]}" in
raw_btrfs)
if $arg_edit; then
if [[ -n $arg_edit_base ]]; then
cp -T --reflink -- "$arg_edit_base" "$arg_file"
2023-10-30 21:14:27 +00:00
fi
if [[ -n ${arg_conf[fsuuid]:-} ]]; then
# Explicit new FSUUID.
btrfstune -f -U "${arg_conf[fsuuid]:-}" "$arg_file"
elif [[ -n $arg_edit_base ]]; then
# Random new FSUUID.
btrfstune -fu "$arg_file"
fi
2023-10-30 21:14:27 +00:00
if [[ -n ${arg_conf[size]:-} ]]; then
# Calculate sizes in exact bytes now, to avoid any discrepancies
# between truncate(1) and btrfs-filesystem(8).
# Use truncate(1) to do this. It's a little gross,
# but because TMPDIR is likely on tmpfs, it's time-cheap,
# and because of sparse files, it's space-cheap.
tmpfile=$(mktemp -t -- "${0##*/}.XXXXXXXXXX")
trap "rm -- ${tmpfile@Q}" EXIT
2023-10-30 21:14:27 +00:00
case "${arg_conf[size]}" in
'+'*|'-'*|'<'*|'>'*|'/'*|'%'*)
2023-10-30 21:14:27 +00:00
truncate --reference="$arg_file" --size="${arg_conf[size]}" -- "$tmpfile";;
*)
2023-10-30 21:14:27 +00:00
truncate --size="${arg_conf[size]}" -- "$tmpfile";;
esac
old_size=$(stat --format='%s' -- "$arg_file")
new_size=$(stat --format='%s' -- "$tmpfile")
rm -- "$tmpfile"
trap - EXIT
2018-08-16 05:19:05 +00:00
# Do the resize
arg_mountpoint=$(mktemp -dt -- "${0##*/}.XXXXXXXXXX")
trap "rmdir -- ${arg_mountpoint@Q}" EXIT
if (( new_size > old_size )); then
truncate --size="$new_size" -- "$arg_file"
sudo -- "$(dirname -- "${BASH_SOURCE[0]}")/osi-mount" --root -- "$arg_file" "$arg_mountpoint" btrfs filesystem resize max "$arg_mountpoint"
elif (( new_size < old_size )); then
sudo -- "$(dirname -- "${BASH_SOURCE[0]}")/osi-mount" --root -- "$arg_file" "$arg_mountpoint" btrfs filesystem resize "$new_size" "$arg_mountpoint"
truncate --size="$new_size" -- "$arg_file"
fi
rmdir -- "$arg_mountpoint"
trap - EXIT
fi
else
2023-10-30 21:14:27 +00:00
truncate --size="${arg_conf[size]}" -- "$arg_file"
mkfs.btrfs ${arg_conf[fsuuid]:+"--uuid=${arg_conf[fsuuid]}"} -- "$arg_file"
fi
;;
2023-10-30 21:14:27 +00:00
erofs)
if $arg_edit; then
error 2 '--conf=format=erofs does not support --edit'
fi
if [[ -z "${arg_conf[fsuuid]:-}" ]]; then
arg_conf[fsuuid]=$(uuidgen)
arg_orig=(--conf=fsuuid="${arg_conf[fsuuid]}" "${arg_orig[@]}")
fi
mount_dev=$(mktemp -dt -- "${0##*/}.XXXXXXXXXX")
# Make our own tmpfs because $TMPDIR might be mounted
# with 'noexec' or something. Also, so we can clean it
# up without a `sudo rm -rf`.
sudo mount -t tmpfs tmpfs:osi-mk "$mount_dev"
trap "sudo umount ${mount_dev@Q}; rmdir -- ${mount_dev@Q}" EXIT
mount_opt=bind
;;
esac
2023-10-30 21:14:27 +00:00
} >& >(sed "s|^|${prefix} |")
2018-07-25 19:23:05 +00:00
arg_mountpoint=$(mktemp -dt -- "${0##*/}.XXXXXXXXXX")
2023-10-30 21:14:27 +00:00
r=0
sudo -- "$(dirname -- "${BASH_SOURCE[0]}")/osi-mount" --root \
--rwdir=/var/cache/pacman/pkg \
--rwdir=/etc/pacman.d/gnupg \
2023-10-30 21:14:27 +00:00
--options="$mount_opt" \
-- \
"$mount_dev" "$arg_mountpoint" \
"${BASH_SOURCE[0]}" --inside="$arg_mountpoint" \
"${arg_orig[@]}" || r=$?
rmdir -- "$arg_mountpoint"
if (( r != 0 )); then
return $r
fi
2023-11-04 06:55:45 +00:00
gprintf -v prefix '%s [format]' "$NAME"
{
case "${arg_conf[format]}" in
erofs)
2023-11-05 07:25:59 +00:00
if [[ "${arg_conf[SOURCE_DATE_EPOCH]:-}" == auto ]]; then
local file
for file in "$mount_dev"/var/lib/pacman/local/*/desc; do
arg_conf[SOURCE_DATE_EPOCH]="$(sed -n '/^%INSTALLDATE%$/{n;p;}' -- "$file")"
break
done
fi
if [[ -n "${arg_conf[SOURCE_DATE_EPOCH]:-}" ]]; then
export SOURCE_DATE_EPOCH="${arg_conf[SOURCE_DATE_EPOCH]}"
fi
2023-11-04 06:55:45 +00:00
tmpdir=$(mktemp -dt -- "${0##*/}.XXXXXXXXXX")
trap "sudo umount ${mount_dev@Q}; rmdir -- ${mount_dev@Q}; rm -f -- ${tmpdir@Q}/erofs.img; rmdir -- ${tmpdir@Q}" EXIT
2023-11-05 07:25:59 +00:00
sudo --preserve-env=SOURCE_DATE_EPOCH mkfs.erofs \
2023-11-04 06:55:45 +00:00
-U"${arg_conf[fsuuid]}" \
${arg_conf[erofs_compression]:+"-z${arg_conf[erofs_compression]}"} \
"$tmpdir/erofs.img" "$mount_dev"
cp -T "$tmpdir/erofs.img" "$arg_file"
;;
esac
} >& >(sed "s|^|${prefix} |")
print '%s Done' "$NAME"
2018-07-25 19:23:05 +00:00
;;
2023-10-25 11:04:49 +00:00
inside) # the part that runs as root with the image mounted
2018-08-11 18:57:24 +00:00
needs_sudo
2018-07-26 20:35:32 +00:00
2018-08-06 01:49:45 +00:00
### Load modules ###
packages=("${arg_packages[@]}")
2023-10-25 18:09:28 +00:00
pre_install=()
2018-08-12 21:53:08 +00:00
post_install=(
50:osi-mk:directories
)
2023-10-30 21:14:27 +00:00
case "${arg_conf[genfstab],,}" in
1|t|true|y|yes)
if ! $arg_edit || [[ -n $arg_edit_base ]] || [[ -n "${arg_conf[fsuuid]:-}" ]]; then
post_install+=(10:osi-mk:genfstab)
fi
;;
esac
2023-10-30 21:14:27 +00:00
case "${arg_conf[format]}" in
raw_btrfs)
packages+=(btrfs-progs)
;;
erofs)
packages+=(erofs-utils)
;;
esac
case "${arg_conf[bootloader]}" in
grub)
packages+=(grub)
arg_directories+=("$(dirname -- "${BASH_SOURCE[0]}")/osi-mk.d/grub-hack":usr/bin)
2023-11-05 07:25:59 +00:00
post_install+=(88:osi-mk:grub-mkconfig) # before 89:osi-mk:source-date-epoch
2023-10-30 21:14:27 +00:00
if ! $arg_edit; then
2023-11-05 07:25:59 +00:00
post_install+=(87:osi-mk:grub-install) # before '88:osi-mk:grub-mkconfig'
2023-10-30 21:14:27 +00:00
fi
;;
esac
case "${arg_conf[initramfs]:-}" in
mkinitcpio)
pre_install+=(10:osi-mk:mkinitcpio)
2023-10-30 21:14:27 +00:00
;;
esac
2023-10-28 22:32:47 +00:00
if [[ -n "${arg_conf[hostname]:-}" ]]; then
post_install+=(10:osi-mk:hostname)
2023-10-28 22:32:47 +00:00
fi
2023-11-05 07:25:59 +00:00
if [[ -n "${arg_conf[SOURCE_DATE_EPOCH]:-}" ]]; then
post_install+=(89:osi-mk:source-date-epoch)
fi
2023-10-30 21:14:27 +00:00
2018-08-06 00:09:42 +00:00
for module in "${arg_modules[@]}"; do
2018-08-06 04:02:37 +00:00
load_module "$module"
2018-08-06 00:09:42 +00:00
done
cache_packages+=("${packages[@]}")
2018-08-06 01:49:45 +00:00
### Download ###
gprintf -v prefix '%s [download:repos]' "$NAME"
2018-08-12 21:53:08 +00:00
{
# Download syncdbs to the image
mkdir -p -- "$arg_mountpoint"/var/{lib/pacman,log}
pacman -r "$arg_mountpoint" --config=/usr/share/pacman/defaults/pacman.conf.x86_64 \
-Sy --noconfirm
} |& sed "s|^|${prefix} |"
gprintf -v prefix '%s [download:check-config]' "$NAME"
{
# Validate that the user specified an answer to
# every question that pacman asks.
opt_fail=false
while read -r -a options; do
name="${options[0]%:}"
options=("${options[@]:1}")
base=${name%%[<>=]*}
if in_array "${arg_conf["$base"]:-}" "${options[@]}" "${options[@]#*/}"; then
print 'inserting package %s=%s' "$name" "${arg_conf["$base"]}"
packages+=("${arg_conf["$base"]}")
cache_packages+=("${arg_conf["$base"]}")
else
error 0 "must set option '%q' to one of [%s]" "$base" "${options[*]}"
opt_fail=true
fi
done < <("$(dirname -- "${BASH_SOURCE[0]}")/pacman-choices" \
-r "$arg_mountpoint" --config=/usr/share/pacman/defaults/pacman.conf.x86_64 \
--oneline -- "${cache_packages[@]}")
if [[ $opt_fail == true ]]; then
exit $EXIT_NOTCONFIGURED
fi
} >& >(sed "s|^|${prefix} |")
gprintf -v prefix '%s [download:packages]' "$NAME"
{
if (( ${#cache_packages[@]} > 0 )); then # this check is important for --edit
# Download needed packages to the host cache
2018-08-12 22:20:59 +00:00
pacman -r "$arg_mountpoint" --config=/usr/share/pacman/defaults/pacman.conf.x86_64 \
-Syw --noconfirm -- "${cache_packages[@]}"
2018-08-12 22:25:10 +00:00
# Copy needed packages from host cache to image cache
mkdir -p -- "$arg_mountpoint"/var/cache/pacman/pkg
2018-08-12 22:20:59 +00:00
pacman -r "$arg_mountpoint" --config=/usr/share/pacman/defaults/pacman.conf.x86_64 \
-Sp --print-format='%l' -- "${cache_packages[@]}" \
| sed -En 's,^file://(.*),\1\n\1.sig,p' \
| xargs -d $'\n' -r cp -t "$arg_mountpoint/var/cache/pacman/pkg" --
2018-08-12 22:20:59 +00:00
fi
} |& sed "s|^|${prefix} |"
2023-11-05 07:25:59 +00:00
if [[ "${arg_conf[SOURCE_DATE_EPOCH]:-}" =~ ^[0-9]+$ ]]; then
print '%s SOURCE_DATE_EPOCH=%s' "$NAME" "${arg_conf[SOURCE_DATE_EPOCH]}"
export SOURCE_DATE_EPOCH="${arg_conf[SOURCE_DATE_EPOCH]}"
fi
2023-10-25 18:09:28 +00:00
### pre_install ###
while IFS=: read -r n fn; do
printf -v prefix '%s [pre_install:%s:%s]' "$NAME" "$n" "$fn"
{
print Begin
"$fn" "$arg_mountpoint"
print End
} |& sed "s|^|${prefix} |"
done < <(printf '%s\n' "${pre_install[@]}" | sort)
### Install ###
gprintf -v prefix '%s [install]' "$NAME"
{
if (( ${#packages[@]} > 0 )); then # this check is important for --edit
2018-08-12 22:20:59 +00:00
# The --hookdir bit is to hack around https://bugs.archlinux.org/task/49347
pacstrap -M -C /usr/share/pacman/defaults/pacman.conf.x86_64 -- "$arg_mountpoint" \
--hookdir="$arg_mountpoint/etc/pacman.d/hooks" \
--needed \
"${packages[@]}"
fi
if (( ${#arg_package_files[@]} > 0 )); then
dir="$(mktemp -d -- "$arg_mountpoint/tmp/package-files.XXXXXXXXXX")"
trap "rm -rf -- ${dir@Q}" EXIT
cp -t "$dir" -- "${arg_package_files[@]}"
arch-chroot -- "$arg_mountpoint" pacman -U --noconfirm -- "${arg_package_files[@]/#*\//"/tmp/${dir##*/tmp/}"}"
rm -rf -- "$dir"
trap - EXIT
fi
2018-08-12 22:05:29 +00:00
} |& sed "s|^|${prefix} |"
2018-07-26 20:35:32 +00:00
2023-11-05 07:25:59 +00:00
if [[ "${arg_conf[SOURCE_DATE_EPOCH]:-}" == auto ]]; then
# set it to 1 second after the last timestamp from any
# package (that extra second is so that post-install
# things can be timestamped after the package files).
arg_conf[SOURCE_DATE_EPOCH]=$(($({
zcat "$arg_mountpoint"/var/lib/pacman/local/*/mtree | grep -o 'time=[0-9]*' | cut -d= -f2
cat /var/lib/pacman/local/*/desc | sed -n '/^%BUILDDATE%$/{n;p;}'
} | sort -n | tail -n1) + 1))
fi
if [[ -n "${arg_conf[SOURCE_DATE_EPOCH]:-}" ]]; then
print '%s SOURCE_DATE_EPOCH=%s' "$NAME" "${arg_conf[SOURCE_DATE_EPOCH]}"
export SOURCE_DATE_EPOCH="${arg_conf[SOURCE_DATE_EPOCH]}"
fi
2018-08-06 01:49:45 +00:00
### post_install ###
while IFS=: read -r n fn; do
printf -v prefix '%s [post_install:%s:%s]' "$NAME" "$n" "$fn"
2018-08-12 21:53:08 +00:00
{
2018-08-12 22:05:29 +00:00
print Begin
2018-08-12 21:53:08 +00:00
"$fn" "$arg_mountpoint"
2018-08-12 22:05:29 +00:00
print End
2018-08-12 21:53:08 +00:00
} |& sed "s|^|${prefix} |"
2018-08-06 01:49:45 +00:00
done < <(printf '%s\n' "${post_install[@]}" | sort)
2018-07-25 19:23:05 +00:00
;;
*) error $EXIT_FAILURE 'Internal error. The programmer writing this tool screwed up.';;
2018-07-25 19:23:05 +00:00
esac
}
main "$@"