2014-07-12 21:43:54 +02:00
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
### CONFIGURATION
|
|
|
|
archlinux_mirror="https://mirrors.kernel.org/archlinux/"
|
2014-07-14 03:05:58 +02:00
|
|
|
preserve_home_directories=true
|
2014-07-12 21:43:54 +02:00
|
|
|
|
|
|
|
set -eu
|
|
|
|
set -o pipefail
|
|
|
|
shopt -s nullglob
|
2014-07-13 23:47:12 +02:00
|
|
|
shopt -s dotglob
|
2014-07-12 21:43:54 +02:00
|
|
|
|
2014-07-12 22:12:46 +02:00
|
|
|
export LC_ALL=C
|
|
|
|
export LANG=C
|
|
|
|
unset LANGUAGE
|
|
|
|
|
2014-07-12 21:43:54 +02:00
|
|
|
### VARIABLES
|
|
|
|
declare -A dependencies
|
|
|
|
dependencies[pacman]=x
|
|
|
|
|
|
|
|
log() {
|
|
|
|
echo "[$(date)]" "$@" >&2
|
|
|
|
}
|
|
|
|
|
|
|
|
clean_archroot() {
|
|
|
|
local file
|
|
|
|
local prompted=false
|
|
|
|
local lsfd
|
|
|
|
while read file <&${lsfd}; do
|
|
|
|
if [ "${file}" = "installer" ] || [ "${file}" = "packages" ]; then
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
if ! $prompted; then
|
|
|
|
log "Your /archroot directory contains a stale installation or other data."
|
|
|
|
log "Remove it?"
|
|
|
|
local response
|
|
|
|
read -p '(yes or [no]) ' response
|
|
|
|
if [ "${response}" = "yes" ]; then
|
|
|
|
prompted=true
|
|
|
|
else
|
|
|
|
break
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
rm -rf "/archroot/${file}"
|
|
|
|
done {lsfd}< <(ls /archroot)
|
|
|
|
}
|
|
|
|
|
|
|
|
initialize_coredb() {
|
|
|
|
log "Downloading package database ..."
|
|
|
|
wget "${archlinux_mirror}/core/os/x86_64/core.db"
|
|
|
|
log "Unpacking package database ..."
|
|
|
|
mkdir core
|
|
|
|
tar -zxf core.db -C core
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_version() {
|
|
|
|
echo "${1}" | grep -o '^[A-Za-z0-9_-]*'
|
|
|
|
}
|
|
|
|
|
|
|
|
get_package_directory() {
|
|
|
|
local dir pkg
|
|
|
|
for dir in core/${1}-*; do
|
|
|
|
if [ "$(get_package_value ${dir}/desc NAME)" = "${1}" ]; then
|
|
|
|
echo "${dir}"
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
for dir in core/*; do
|
|
|
|
while read pkg; do
|
|
|
|
pkg=$(remove_version "${pkg}")
|
|
|
|
if [ "${pkg}" = "${1}" ]; then
|
|
|
|
echo "${dir}"
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
done < <(get_package_array ${dir}/depends PROVIDES)
|
|
|
|
done
|
|
|
|
log "Package '${1}' not found."
|
|
|
|
}
|
|
|
|
|
|
|
|
get_package_value() {
|
|
|
|
local infofile=${1}
|
|
|
|
local infokey=${2}
|
|
|
|
get_package_array ${infofile} ${infokey} | (
|
|
|
|
local value
|
|
|
|
read value
|
|
|
|
echo "${value}"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
get_package_array() {
|
|
|
|
local infofile=${1}
|
|
|
|
local infokey=${2}
|
|
|
|
local line
|
|
|
|
while read line; do
|
|
|
|
if [ "${line}" = "%${infokey}%" ]; then
|
|
|
|
while read line; do
|
|
|
|
if [ -z "${line}" ]; then
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
echo "${line}"
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
done < ${infofile}
|
|
|
|
}
|
|
|
|
|
|
|
|
calculate_dependencies() {
|
|
|
|
log "Calculating dependencies ..."
|
|
|
|
local dirty=true
|
|
|
|
local pkg dir dep
|
|
|
|
while $dirty; do
|
|
|
|
dirty=false
|
|
|
|
for pkg in "${!dependencies[@]}"; do
|
|
|
|
dir=$(get_package_directory $pkg)
|
|
|
|
while read line; do
|
|
|
|
dep=$(remove_version "${line}")
|
|
|
|
if [ -z "${dependencies[$dep]:-}" ]; then
|
|
|
|
dependencies[$dep]=x
|
|
|
|
dirty=true
|
|
|
|
fi
|
|
|
|
done < <(get_package_array ${dir}/depends DEPENDS)
|
|
|
|
done
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
download_packages() {
|
|
|
|
log "Downloading packages ..."
|
|
|
|
mkdir -p /archroot/packages
|
|
|
|
local pkg dir filename sha256 localfn
|
|
|
|
for pkg in "${!dependencies[@]}"; do
|
|
|
|
dir=$(get_package_directory ${pkg})
|
|
|
|
filename=$(get_package_value ${dir}/desc FILENAME)
|
|
|
|
sha256=$(get_package_value ${dir}/desc SHA256SUM)
|
|
|
|
localfn=/archroot/packages/${filename}
|
|
|
|
if [ -e "${localfn}" ] && ( echo "${sha256} ${localfn}" | sha256sum -c ); then
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
wget "${archlinux_mirror}/core/os/x86_64/${filename}" -O "${localfn}"
|
|
|
|
if [ -e "${localfn}" ] && ( echo "${sha256} ${localfn}" | sha256sum -c ); then
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
log "Couldn't download package '${pkg}'."
|
|
|
|
false
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
extract_packages() {
|
|
|
|
log "Extracting packages ..."
|
2014-07-12 21:47:47 +02:00
|
|
|
local dir filename
|
|
|
|
for pkg in "${!dependencies[@]}"; do
|
|
|
|
dir=$(get_package_directory ${pkg})
|
|
|
|
filename=$(get_package_value ${dir}/desc FILENAME)
|
|
|
|
xz -dc /archroot/packages/${filename} | tar -C /archroot -xf -
|
|
|
|
done
|
2014-07-12 21:43:54 +02:00
|
|
|
}
|
|
|
|
|
2014-07-14 02:38:33 +02:00
|
|
|
mount_virtuals() {
|
2014-07-12 22:12:46 +02:00
|
|
|
log "Mounting virtual filesystems ..."
|
|
|
|
mount -t proc proc /archroot/proc
|
|
|
|
mount -t sysfs sys /archroot/sys
|
|
|
|
mount --bind /dev /archroot/dev
|
|
|
|
mount -t devpts pts /archroot/dev/pts
|
2014-07-14 02:38:33 +02:00
|
|
|
}
|
2014-07-12 22:12:46 +02:00
|
|
|
|
2014-07-14 02:38:33 +02:00
|
|
|
prebootstrap_configuration() {
|
|
|
|
log "Doing pre-bootstrap configuration ..."
|
2014-07-12 22:12:46 +02:00
|
|
|
rmdir /archroot/var/cache/pacman/pkg
|
|
|
|
ln -s ../../../packages /archroot/var/cache/pacman/pkg
|
|
|
|
chroot /archroot /usr/bin/update-ca-certificates --fresh
|
2014-07-14 02:38:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bootstrap_system() {
|
2014-07-12 22:12:46 +02:00
|
|
|
|
2014-07-12 22:28:07 +02:00
|
|
|
local shouldbootstrap=false isbootstrapped=false
|
|
|
|
while ! $isbootstrapped; do
|
|
|
|
if $shouldbootstrap; then
|
2014-07-14 02:38:33 +02:00
|
|
|
log "Bootstrapping system ..."
|
2014-07-12 22:28:07 +02:00
|
|
|
chroot /archroot pacman-key --init
|
|
|
|
chroot /archroot pacman-key --populate archlinux
|
2014-07-14 01:49:04 +02:00
|
|
|
chroot /archroot pacman -Sy --force --noconfirm base openssh kexec-tools
|
2014-07-12 22:28:07 +02:00
|
|
|
isbootstrapped=true
|
|
|
|
else
|
|
|
|
shouldbootstrap=true
|
|
|
|
fi
|
|
|
|
# config overwritten by pacman
|
|
|
|
rm -f /archroot/etc/resolv.conf.pacorig
|
|
|
|
cp /etc/resolv.conf /archroot/etc/resolv.conf
|
|
|
|
rm -f /archroot/etc/pacman.d/mirrorlist.pacorig
|
|
|
|
echo "Server = ${archlinux_mirror}"'/$repo/os/$arch' \
|
|
|
|
>> /archroot/etc/pacman.d/mirrorlist
|
|
|
|
done
|
2014-07-12 22:12:46 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-07-14 02:38:33 +02:00
|
|
|
postbootstrap_configuration() {
|
2014-07-14 02:47:19 +02:00
|
|
|
|
2014-07-14 02:38:33 +02:00
|
|
|
log "Doing post-bootstrap configuration ..."
|
2014-07-14 02:47:19 +02:00
|
|
|
|
|
|
|
# set up fstab
|
|
|
|
echo "LABEL=DOROOT / ext4 defaults 0 1" >> /archroot/etc/fstab
|
|
|
|
|
|
|
|
# set up shadow
|
|
|
|
(
|
|
|
|
umask 077
|
|
|
|
(
|
|
|
|
grep '^root:' /etc/shadow
|
|
|
|
grep -v '^root:' /archroot/etc/shadow
|
|
|
|
) > /archroot/etc/shadow.new
|
|
|
|
cat /archroot/etc/shadow.new > /archroot/etc/shadow
|
|
|
|
rm /archroot/etc/shadow.new
|
|
|
|
)
|
|
|
|
|
|
|
|
# set up network
|
|
|
|
local grepfd
|
|
|
|
local ipaddr netmask gateway prefixlen=24
|
|
|
|
local eni=/etc/network/interfaces
|
|
|
|
exec {grepfd}< <(
|
|
|
|
grep -o 'address [0-9.]\+' ${eni}
|
|
|
|
grep -o 'netmask [0-9.]\+' ${eni}
|
|
|
|
grep -o 'gateway [0-9.]\+' ${eni}
|
|
|
|
)
|
|
|
|
read ignored ipaddr <&${grepfd}
|
|
|
|
read ignored netmask <&${grepfd}
|
|
|
|
read ignored gateway <&${grepfd}
|
|
|
|
exec {grepfd}<&-
|
|
|
|
case ${netmask} in
|
|
|
|
255.255.255.0)
|
|
|
|
prefixlen=24
|
|
|
|
;;
|
|
|
|
255.255.240.0)
|
|
|
|
prefixlen=20
|
|
|
|
;;
|
|
|
|
255.255.0.0)
|
|
|
|
prefixlen=16
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
cat > /archroot/etc/systemd/network/internet.network <<EOF
|
|
|
|
[Match]
|
|
|
|
Name=eth0
|
|
|
|
|
|
|
|
[Network]
|
|
|
|
Address=${ipaddr}/${prefixlen}
|
|
|
|
Gateway=${gateway}
|
|
|
|
DNS=8.8.8.8
|
|
|
|
DNS=8.8.4.4
|
|
|
|
EOF
|
|
|
|
|
2014-07-14 03:00:58 +02:00
|
|
|
# copy over ssh keys
|
|
|
|
cp -p /etc/ssh/ssh_*_key{,.pub} /archroot/etc/ssh/
|
|
|
|
|
2014-07-14 03:05:58 +02:00
|
|
|
# optionally preserve home directories
|
|
|
|
if ${preserve_home_directories}; then
|
|
|
|
rm -rf /archroot/{home,root}
|
|
|
|
cp -al /{home,root} /archroot/
|
|
|
|
fi
|
|
|
|
|
2014-07-14 02:51:18 +02:00
|
|
|
# enable services
|
|
|
|
chroot /archroot systemctl enable systemd-networkd
|
|
|
|
chroot /archroot systemctl enable sshd
|
|
|
|
|
2014-07-14 04:39:38 +02:00
|
|
|
# install services
|
|
|
|
local unitdir=/archroot/etc/systemd/system
|
|
|
|
|
2014-07-14 04:50:27 +02:00
|
|
|
mkdir -p ${unitdir}/basic.target.wants
|
|
|
|
ln -s ../installer-cleanup.service ${unitdir}/basic.target.wants/
|
2014-07-14 04:39:38 +02:00
|
|
|
cat > ${unitdir}/installer-cleanup.service <<EOF
|
|
|
|
[Unit]
|
|
|
|
Description=Post-install cleanup
|
|
|
|
ConditionPathExists=/installer/script.sh
|
2014-07-14 04:50:27 +02:00
|
|
|
|
2014-07-14 04:39:38 +02:00
|
|
|
[Service]
|
|
|
|
Type=oneshot
|
|
|
|
ExecStart=/installer/script.sh
|
|
|
|
EOF
|
|
|
|
|
2014-07-14 02:38:33 +02:00
|
|
|
}
|
|
|
|
|
2014-07-12 21:43:54 +02:00
|
|
|
error_occurred() {
|
|
|
|
log "Error occurred. Exiting."
|
|
|
|
}
|
|
|
|
|
2014-07-12 22:12:46 +02:00
|
|
|
exit_cleanup() {
|
|
|
|
log "Cleaning up ..."
|
|
|
|
set +e
|
|
|
|
umount /archroot/dev/pts
|
|
|
|
umount /archroot/dev
|
|
|
|
umount /archroot/sys
|
|
|
|
umount /archroot/proc
|
|
|
|
}
|
|
|
|
|
2014-07-13 23:47:12 +02:00
|
|
|
installer_main() {
|
2014-07-12 21:43:54 +02:00
|
|
|
|
|
|
|
if [ "${EUID}" -ne 0 ] || [ "${UID}" -ne 0 ]; then
|
|
|
|
log "Script must be run as root. Exiting."
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
if ! grep -q '^7\.' /etc/debian_version; then
|
|
|
|
log "This script only supports Debian 7.x. Exiting."
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ "$(uname -m)" != "x86_64" ]; then
|
|
|
|
log "This script only targets 64-bit machines. Exiting."
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
trap error_occurred ERR
|
2014-07-12 22:12:46 +02:00
|
|
|
trap exit_cleanup EXIT
|
2014-07-12 21:43:54 +02:00
|
|
|
|
|
|
|
rm -rf /archroot/installer
|
|
|
|
mkdir -p /archroot/installer
|
|
|
|
cd /archroot/installer
|
|
|
|
|
|
|
|
clean_archroot
|
|
|
|
initialize_coredb
|
|
|
|
calculate_dependencies
|
|
|
|
download_packages
|
|
|
|
extract_packages
|
|
|
|
|
2014-07-14 02:38:33 +02:00
|
|
|
mount_virtuals
|
|
|
|
prebootstrap_configuration
|
|
|
|
bootstrap_system
|
|
|
|
postbootstrap_configuration
|
2014-07-12 22:12:46 +02:00
|
|
|
|
2014-07-13 23:47:12 +02:00
|
|
|
# prepare for transtiory_main
|
|
|
|
mv /sbin/init /sbin/init.original
|
|
|
|
cp "${script_path}" /sbin/init
|
|
|
|
reboot
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
transitory_main() {
|
|
|
|
|
|
|
|
if [ "${script_path}" = "/sbin/init" ]; then
|
|
|
|
# save script
|
|
|
|
mount -o remount,rw /
|
|
|
|
cp "${script_path}" /archroot/installer/script.sh
|
|
|
|
# restore init in case anything goes wrong
|
|
|
|
rm /sbin/init
|
|
|
|
mv /sbin/init.original /sbin/init
|
2014-07-14 00:56:52 +02:00
|
|
|
# unmount other filesystems
|
|
|
|
if ! [ -e /proc/mounts ]; then
|
|
|
|
mount -t proc proc /proc
|
|
|
|
fi
|
|
|
|
local device mountpoint fstype ignored
|
|
|
|
while IFS=" " read device mountpoint fstype ignored; do
|
|
|
|
if [ "${device}" == "${device/\//}" ] && [ "${fstype}" != "rootfs" ]; then
|
|
|
|
umount -l "${mountpoint}"
|
|
|
|
fi
|
|
|
|
done < <(tac /proc/mounts)
|
|
|
|
# mount real root
|
2014-07-13 23:47:12 +02:00
|
|
|
mkdir /archroot/realroot
|
|
|
|
mount --bind / /archroot/realroot
|
2014-07-14 00:56:52 +02:00
|
|
|
# chroot into archroot
|
2014-07-13 23:47:12 +02:00
|
|
|
exec chroot /archroot /installer/script.sh
|
|
|
|
elif [ "${script_path}" = "/installer/script.sh" ]; then
|
|
|
|
# now in archroot
|
|
|
|
local oldroot=/realroot/archroot/oldroot
|
|
|
|
mkdir ${oldroot}
|
|
|
|
# move old files into oldroot
|
2014-07-14 00:56:52 +02:00
|
|
|
log "Backing up old root ..."
|
2014-07-13 23:47:12 +02:00
|
|
|
local entry
|
|
|
|
for entry in /realroot/*; do
|
|
|
|
if [ "${entry}" != "/realroot/archroot" ]; then
|
|
|
|
mv "${entry}" ${oldroot}
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
# hardlink files into realroot
|
2014-07-14 00:56:52 +02:00
|
|
|
log "Populating new root ..."
|
2014-07-13 23:47:12 +02:00
|
|
|
cd /
|
|
|
|
mv ${oldroot} /realroot
|
|
|
|
for entry in /realroot/archroot/*; do
|
|
|
|
if [ "${entry}" != "/realroot/archroot/realroot" ]; then
|
|
|
|
cp -al "${entry}" /realroot
|
|
|
|
fi
|
|
|
|
done
|
2014-07-14 00:56:52 +02:00
|
|
|
# done!
|
|
|
|
log "Rebooting ..."
|
|
|
|
mount -t proc proc /proc
|
|
|
|
mount -o remount,ro /realroot
|
|
|
|
sync
|
|
|
|
umount /proc
|
|
|
|
reboot -f
|
2014-07-13 23:47:12 +02:00
|
|
|
else
|
|
|
|
log "Unknown state! You're own your own."
|
|
|
|
exec /bin/bash
|
|
|
|
fi
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-07-14 01:49:04 +02:00
|
|
|
postinstall_main() {
|
|
|
|
|
2014-07-14 04:39:38 +02:00
|
|
|
# remove cleanup service
|
|
|
|
local unitdir=/etc/systemd/system
|
|
|
|
rm -f ${unitdir}/installer-cleanup.service
|
2014-07-14 04:50:27 +02:00
|
|
|
rm -f ${unitdir}/basic.target.wants/installer-cleanup.service
|
2014-07-14 04:39:38 +02:00
|
|
|
|
2014-07-14 02:17:59 +02:00
|
|
|
# cleanup filesystem
|
|
|
|
rm -f /var/cache/pacman/pkg
|
|
|
|
mv /packages /var/cache/pacman/pkg
|
|
|
|
rm -f /.INSTALL /.MTREE /.PKGINFO
|
2014-07-14 03:05:58 +02:00
|
|
|
rm -rf /archroot
|
2014-07-14 02:17:59 +02:00
|
|
|
rm -rf /installer
|
|
|
|
|
2014-07-14 01:49:04 +02:00
|
|
|
}
|
|
|
|
|
2014-07-13 23:47:12 +02:00
|
|
|
canonicalize_path() {
|
|
|
|
local basename="$(basename "${1}")"
|
|
|
|
local dirname="$(dirname "${1}")"
|
|
|
|
(
|
|
|
|
cd "${dirname}"
|
|
|
|
echo "$(pwd -P)/${basename}"
|
|
|
|
)
|
2014-07-12 21:43:54 +02:00
|
|
|
}
|
|
|
|
|
2014-07-13 23:47:12 +02:00
|
|
|
script_path="$(canonicalize_path "${0}")"
|
|
|
|
if [ $$ -eq 1 ]; then
|
|
|
|
transitory_main "$@"
|
|
|
|
elif [ "${script_path}" = "/sbin/init" ]; then
|
|
|
|
exec /sbin/init.original "$@"
|
2014-07-14 02:27:09 +02:00
|
|
|
elif [ "${script_path}" = "/installer/script.sh" ]; then
|
|
|
|
postinstall_main "$@"
|
2014-07-13 23:47:12 +02:00
|
|
|
else
|
|
|
|
installer_main "$@"
|
|
|
|
fi
|