1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
#!/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 <<EOF
usage: ${0##*/} [options] root [packages...]
Options:
-C <config> 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'
db_dir="/var/lib/pacman/"
gpg_dir="/etc/pacman.d/gnupg/"
log_dir="/var/log/"
# These can contain multiple paths. Ensure they are arrays
cache_dirs=("/var/cache/pacman/pkg/")
# Ensure we only override dirs if we copy the config
if (( copyconf )); then
db_dir="$(pacman-conf -c "$pacman_config" DBPath)"
gpg_dir="$(pacman-conf -c "$pacman_config" GPGDir)"
log_dir="$(dirname "$(pacman-conf -c "$pacman_config" LogFile)")"
mapfile -t cache_dirs < <( pacman-conf -c "$pacman_config" CacheDir )
fi
# create obligatory directories
msg 'Creating install root at %s' "$newroot"
# shellcheck disable=SC2174 # permissions are perfectly fine here
mkdir -m 0755 -p "$newroot/$db_dir" \
"$newroot/$log_dir" \
"$newroot"/etc/pacman.d
for path in "${cache_dirs[@]}"; do
# shellcheck disable=SC2174 # permissions are perfectly fine here
mkdir -m 0755 -p "$newroot/$path"
done
# shellcheck disable=SC2174 # permissions are perfectly fine here
mkdir -m 0555 -p "$newroot"/{run,dev,sys,proc}
# shellcheck disable=SC2174 # permissions are perfectly fine here
mkdir -m 1777 -p "$newroot"/tmp
if (( ! hostcache )); then
for path in "${cache_dirs[@]}"; do
pacman_args+=(--cachedir="$newroot/$path")
done
fi
# mount API filesystems
chroot_init
$setup "$newroot" || die "failed to setup chroot %s" "$newroot"
if [[ ! -d $newroot/$gpg_dir ]]; then
if (( initkeyring )); then
pacman-key --gpgdir "$newroot/$gpg_dir" --init
elif (( copykeyring )) && [[ -d $gpg_dir ]]; then
# if there's a keyring on the host, copy it into the new root
cp -a --no-preserve=ownership "$gpg_dir" "$newroot/$gpg_dir"
fi
fi
msg 'Installing packages to %s' "$newroot"
if ! $pid_unshare pacman -b "$newroot/$db_dir" -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_args+=("$pacmode" "${@:-base}" --config="$tmpfile" --disable-sandbox)
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"
|