osi-tools/osi-mk

180 lines
5.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# 2018 Luke Shumaker
declare -r NAME=osi-mk
declare -r VERSION=20180805
# Why is this different than mkosi[1]?
#
# - mkosi claims to be "legacy-free"--but they call everything that's
# not systemd "legacy"; that clearly won't do for creating OpenRC
# images.
#
# - 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
# first disecting the disk-label.
#
# [1]: https://github.com/systemd/mkosi
source "${BASH_SOURCE[0]%/*}/lib/osi.sh"
loaded_modules=()
load_module() {
local module
if ! [[ -f $1 ]]; then
error 2 'Module does not exist: %s' "$1"
fi
module="$(realpath -- "$1")"
if in_array "$module" "${loaded_modules[@]}"; then
return 0
fi
loaded_modules+=("$module")
source "$1"
}
osi-mk:directories() {
local arg_mountpoint=$1
local spec outside inside
for spec in "${arg_directories[@]}"; do
outside="${spec%:*}"
inside="${spec#"${outside}:"}"
mkdir -p -- "$(dirname -- "${arg_mountpoint}/${inside}")"
cp -rT "$outside" "${arg_mountpoint}/${inside}"
done
}
main() {
set -euE
local arg_mode=outside
local arg_mountpoint=
local arg_size=1G
local arg_directories=()
local arg_modules=()
local arg_packages=()
local args
if ! args="$(getopt -n "${0##*/}" -o "s:d:m:p:hV" -l "inside:,size:,directory:,module:,package:,help,version" -- "$@")"; then
arg_mode=error
else
eval "args=($args)"
set -- "${args[@]}"
while true; do
case "$1" in
--inside) shift; arg_mode=inside; arg_mountpoint=$1; shift;;
-s|--size) shift; arg_size=$1; shift;;
-d|--directory) shift; arg_directories+=("$1"); shift;;
-m|--module) shift; arg_modules+=("$1"); shift;;
-p|--package) shift; arg_packages+=("$1"); shift;;
-V|--version) shift; arg_mode=version;;
-h|--help) shift; arg_mode=usage;;
--) shift; break;;
*) error 1 'Internal error. The programmer writing this tool screwed up.';;
esac
done
case "$arg_mode" in
outside|inside)
if (( $# != 1 )); then
if (( $# == 0 )); then
error 0 "Expected 1 positional argument, got none"
else
error 0 "Expected 1 positional argument, got %d: %s" "$#" "${@@Q}"
fi
arg_mode=error
else
arg_file=$1
fi
;;
esac
fi
case "$arg_mode" in
error) print "Try '%q --help' for more information" "${0##*/}" >&2; return 2;;
version)
print "%s (notsystemd) %s" "$NAME" "$VERSION"
return 0
;;
usage)
print 'Usage: %s [OPTIONS]' "${0##*/} FILENAME.IMG"
print 'Make Operating System Image'
echo
print 'Create a mountable, bootable OS image.'
echo
print 'OPTIONS:'
# --inside is internal-only; undocumented
print ' -s SIZE, --size=SIZE set the size of the image (default: 1G)'
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 PACKAGE, --package=package include the given package (or group)'
echo
print ' -h, --help display this help'
print ' -V, --version output version information'
return 0
;;
# main code starts here
outside)
if [[ -e "$arg_file" ]]; then
error 1 'Image file already exists, refusing to overwrite: %s' "$arg_file"
fi
truncate --size="$arg_size" -- "$arg_file"
mkfs.btrfs -- "$arg_file"
arg_mountpoint=$(mktemp -dt -- "${0##*/}.XXXXXXXXXX")
# shellcheck disable=SC2064
trap "rmdir -- ${arg_mountpoint@Q}" EXIT
sudo -- ./osi-mount "$arg_file" "$arg_mountpoint" "${BASH_SOURCE[0]}" --inside="$arg_mountpoint" "${args[@]}"
;;
inside) # just keep reading...
needs_sudo
### Load modules ###
packages=(grub btrfs-progs "${arg_packages[@]}")
cache_packages=()
post_install=(50:osi-mk:directories)
for module in "${arg_modules[@]}"; do
load_module "$module"
done
cache_packages+=("${packages[@]}")
#### Base install ###
# Pre-fill the package cache
mkdir -p -- "$arg_mountpoint"/var/{cache/pacman/pkg,lib/pacman,log}
pacman -r "$arg_mountpoint" --config=/usr/share/pacman/defaults/pacman.conf.x86_64 \
-Syw --noconfirm -- "${cache_packages[@]}"
pacman -r "$arg_mountpoint" --config=/usr/share/pacman/defaults/pacman.conf.x86_64 \
-Sp --print-format='%l' -- "${cache_packages[@]}" \
| sed -n 's,^file://,,p' \
| xargs -d $'\n' -r cp -t "$arg_mountpoint/var/cache/pacman/pkg" --
# 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" "${packages[@]}"
genfstab -t uuid "$arg_mountpoint" > "${arg_mountpoint}/etc/fstab"
### post_install ###
while IFS=: read -r n fn; do
printf '[post_install:%s] %s\n' "$n" "$fn"
"$fn" "$arg_mountpoint"
done < <(printf '%s\n' "${post_install[@]}" | sort)
### Boot ###
cat <<-'EOT' >> "$arg_mountpoint/etc/default/grub"
GRUB_TIMEOUT=0
GRUB_DEFAULT=1
EOT
arch-chroot -- "$arg_mountpoint" sh -c \
'grub-install "$(awk '\''$2 == "/" { print $1 }'\'' </proc/mounts)"'
arch-chroot -- "$arg_mountpoint" grub-mkconfig -o /boot/grub/grub.cfg
;;
*) error 1 'Internal error. The programmer writing this tool screwed up.';;
esac
}
main "$@"