#!/bin/bash # SPDX-License-Identifier: GPL-2.0-only # # Assumptions: # 1) User has partitioned, formatted, and mounted partitions on /mnt # 2) Network is functional # 3) Arguments passed to the script are valid pacman targets # 4) A valid mirror appears in /etc/pacman.d/mirrorlist # shopt -s extglob hostcache=0 copykeyring=1 initkeyring=0 copymirrorlist=1 pacman_args=() pacmode=-Sy unshare=0 copyconf=0 pacman_config=/etc/pacman.conf m4_include(common) usage() { cat < Use an alternate config file for pacman -c Use the package cache on the host, rather than the target -D Skip pacman dependency checks -G Avoid copying the host's pacman keyring to the target -i Prompt for package confirmation when needed (run interactively) -K Initialize an empty pacman keyring in the target (implies '-G') -M Avoid copying the host's mirrorlist to the target -N Run in unshare mode as a regular user -P Copy the host's pacman config to the target -U Use pacman -U to install packages -h Print this help message pacstrap installs packages to the specified new root directory. If no packages are given, pacstrap defaults to the "base" group. EOF } pacstrap() { (( EUID == 0 )) || die 'This script must be run with root privileges' # create obligatory directories msg 'Creating install root at %s' "$newroot" # shellcheck disable=SC2174 # permissions are perfectly fine here mkdir -m 0755 -p "$newroot"/var/{cache/pacman/pkg,lib/pacman,log} "$newroot"/{dev,run,etc/pacman.d} # shellcheck disable=SC2174 # permissions are perfectly fine here mkdir -m 1777 -p "$newroot"/tmp # shellcheck disable=SC2174 # permissions are perfectly fine here mkdir -m 0555 -p "$newroot"/{sys,proc} # mount API filesystems $setup "$newroot" || die "failed to setup chroot %s" "$newroot" if [[ ! -d $newroot/etc/pacman.d/gnupg ]]; then if (( initkeyring )); then pacman-key --gpgdir "$newroot"/etc/pacman.d/gnupg --init elif (( copykeyring )) && [[ -d /etc/pacman.d/gnupg ]]; then # if there's a keyring on the host, copy it into the new root cp -a --no-preserve=ownership /etc/pacman.d/gnupg "$newroot/etc/pacman.d/" fi fi msg 'Installing packages to %s' "$newroot" if ! $pid_unshare pacman -r "$newroot" "${pacman_args[@]}"; then die 'Failed to install packages to new root' fi if (( copymirrorlist )); then # install the host's mirrorlist onto the new root cp -a /etc/pacman.d/mirrorlist "$newroot/etc/pacman.d/" fi if (( copyconf )); then cp -a "$pacman_config" "$newroot/etc/pacman.conf" fi } if [[ -z $1 || $1 = @(-h|--help) ]]; then usage exit $(( $# ? 0 : 1 )) fi while getopts ':C:cDGiKMNPU' flag; do case $flag in C) pacman_config=$OPTARG ;; D) pacman_args+=(-dd) ;; c) hostcache=1 ;; i) interactive=1 ;; G) copykeyring=0 ;; K) initkeyring=1 ;; M) copymirrorlist=0 ;; N) unshare=1 ;; P) copyconf=1 ;; U) pacmode=-U ;; :) die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG" ;; ?) die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG" ;; esac done shift $(( OPTIND - 1 )) (( $# )) || die "No root directory specified" newroot=$1 shift [[ -d $newroot ]] || die "%s is not a directory" "$newroot" tmpfile="$(mktemp -t pacman.conf.XXXX)" cp "$pacman_config" "$tmpfile" sed -i 's/^DownloadUser/#&/' "$tmpfile" pacman_config="$tmpfile" pacman_args+=("$pacmode" "${@:-base}" --config="$pacman_config" --disable-sandbox) if (( ! hostcache )); then pacman_args+=(--cachedir="$newroot/var/cache/pacman/pkg") fi if (( ! interactive )); then pacman_args+=(--noconfirm) fi if (( unshare )); then setup=unshare_setup $mount_unshare bash -c "$(declare_all); pacstrap" else setup=chroot_setup pacstrap fi # TODO: There is a trap check on exit. Need to rework the trap handling with # hook-ins/callbacks to remove aux files rm "$tmpfile"