#! /bin/bash # # gentoostrap # Build a Gentoo system in /mnt # Copyright (C) 2020 Vintage Salt # # Distributed under terms of the MIT license. # set -e # Read-only set-once variables declare -r _name="$(basename -- "$0")" # Options declare -i _optchroot declare _optdest="/mnt" declare _optprofile="desktop" declare -i _opthelp declare -i _optverbose # Working variables declare -a _args declare _return # Helper functions log() { # Print a line to the terminal if _optverbose is greater than $2 # $2 defaults to 0 # loglevel 0: Daily-use messages # loglevel 1: Detailed but not quite debugging # loglevel 2: Definitely debugging [ -z "$1" ] && return 1 if (( _optverbose >= ${2:-0} )); then printf "%s\\n" "$1" fi } warn() { # Print a yellow line to the terminal, respecting _optverbose [ -z "$1" ] && return 1 if (( _optverbose >= ${2:-0} )); then if [ -t 1 ]; then printf "\\e[33m%s\\e[0m\\n" "$1" else printf "WARN: %s\\n" "$1" fi fi } error() { # Print a red line to the terminal, exit if $2 is specified [ -z "$1" ] && return 1 if [ -t 2 ]; then printf "\\e[31m%s\\e[0m\\n" "$1" 1>&2 else printf "ERROR: %s\\n" "$1" 1>&2 fi [ -z "$2" ] && return exit "${2:-1}" } has() { # Parse out all arguments and try to find them in path # If an argument cannot be found, set _return and fail for prog in "$@"; do if ! command -v "$prog" > /dev/null 2>&1; then _return="$prog" return 1 fi done return 0 } # Helper functions cleanup() { [ -z "$1" ] && warn "No argument passed to cleanup" rm -rf "$1" || warn "Failed to remove $1: $?" } # Core program functions printhelp() { cat << EOF Usage: $_name [OPTION]... -h Print this help text -c Perform the Gentoo half of installation. Must be invoked from a chroot -v Print more status messages. Stacks Copyright (c) 2020 rehashedsalt@cock.li Licensed under the MIT license EOF } build-gentoo() { # Build Gentoo # First, get a link to a stage3 tarball stage3="http://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-systemd/$( curl -s http://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-systemd/ \ | grep -oe 'stage3-amd64-systemd-20.*\.tar\.xz' \ | sed 's/^stage3.*">//g' \ | sort \ | uniq \ | head -n 1 )" stage3file="/tmp/tmp.$USER.$_name.stage3.tar.xz" stage3sig="/tmp/tmp.$USER.$_name.stage3.tar.xz.DIGESTS" for file in "$stage3file" "$stage3sig"; do touch "$file" chmod 0600 "$file" done # We grab the checksums first to verify the integrity of any tarball leftover from a previous run log "Downloading stage3 signature" curl -s "$stage3.DIGESTS" -o "$stage3sig" if ! [ -f "$stage3file" ]; then log "Downloading stage3" curl -s "$stage3" -o "$stage3file" elif ! grep "$stage3sig" -e "$(sha512sum "$stage3file" | awk '{print $1}')" > /dev/null 2>&1; then log "Checksum verification failed; downloading new stage3" curl -s "$stage3" -o "$stage3file" fi # At this point, we should have a new stage3 that matches our signature; die if not if ! grep "$stage3sig" -e "$(sha512sum "$stage3file" | awk '{print $1}')" > /dev/null 2>&1; then error "stage3 checksum verification failed" 50 fi log "Decompressing tarball; this will prompt for root privileges" sudo tar xf "$stage3file" -C "$_optdest" # Now we do some pre-chroot configuration pushd "$_optdest" > /dev/null 2>&1 # These is a very generic make.conf to get the system up and running # We use march of x86-64 to ensure compatibility if we're bootstrapping on a host other than the target cat <<-EOF > "$_optdest/etc/portage/make.conf" # ABI_X86 is used to control multilib support ABI_X86="64 32" COMMON_FLAGS="-march=x86-64 -mtune=generic -O2 -pipe" CFLAGS="\${COMMON_FLAGS}" CXXFLAGS="\${COMMON_FLAGS}" GENTOO_MIRRORS="http://distfiles.gentoo.org" MAKEOPTS="$(nproc)" USE="systemd -elogind -test" EOF # This is required on >=17.1 to unroll some circular dependencies cat <<-EOF > "$_optdest/etc/portage/package.use/gentoostrap" # These solve circular dependency issues dev-lang/python -sqlite sys-libs/ncurses -gpm EOF # Configure default ebuild repositories mkdir -p "$_optdest/etc/portage/repos.conf" cp "$_optdest/usr/share/portage/config/repos.conf" \ "$_optdest/etc/portage/repos.conf/gentoo.conf" # DNS cat <<-EOF > "$_optdest/etc/resolv.conf" nameserver 8.8.8.8 nameserver 8.8.4.4 EOF # Timezone cp /etc/timezone "$_optdest/etc/timezone" # Locale cat <<-EOF > "$_optdest/etc/locale.gen" en_US ISO-8859-1 en_US.UTF-8 UTF-8 EOF # Now we need to pivot into a chroot and finish configuration natively # Buuuut... since we're using systemd, we get to just spawn a systemd-nspawn container popd cp "$0" "$_optdest/gentoostrap.sh" sudo systemd-nspawn --directory="$_optdest" /gentoostrap.sh -c } build-gentoo-chroot() { # Build Gentoo, but from within the chroot environment log "Building Gentoo from within chroot" # Sync repositories log "Getting ebuild repositories" emerge-webrsync # Get the stable plasma/systemd profile and select it profile=$( eselect profile list \ | grep -ie 'stable' \ | grep -ie "$_optprofile" \ | head -n 1 \ | grep -oP '\[\K[^\]]+' ) || error "Error finding profile: $profile" 51 [ -z "$profile" ] && error "Could not find profile: $profile" 52 log "Setting profile $profile" eselect profile set "$profile" # BEHOLD! emerge -DNUua @world --jobs --quiet-build y --with-bdeps y # Configure timezone emerge --config sys-libs/timezone-data # Locales locale-gen eselect locale set en_US.utf8 env-update . /etc/profile # Build a kernel } # Main main() { # Parse out arguments while [ -n "$1" ]; do # Parse out flags while getopts ":chv" opt; do case $opt in c) _optchroot=1 ;; h) _opthelp=1 ;; v) _optverbose+=1 ;; :) error "Option requires argument: -$OPTARG" 2 ;; *) error "Invalid option: -$OPTARG" 2 ;; esac done # Store arguments shift $((OPTIND - 1)) if [ -n "$1" ]; then _args+=("$1") shift fi unset OPTIND done # Early hook for help [ -n "$_opthelp" ] && printhelp && exit 0 # Validate critical options # TODO: That # Validate core program dependencies log "Validating dependencies" 2 if ! has basename curl sha512sum systemd-nspawn tar; then error "Failed to find program: $_return" 1 fi # Do the do if [ -z "$_optchroot" ]; then build-gentoo else build-gentoo-chroot fi exit 0 } main "$@"