From 9c9afa2abaa73c73bcb0af55b42394c54d65d710 Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Wed, 30 Aug 2017 23:55:22 -0400 Subject: rename file --- src/selfstrap | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100755 src/selfstrap (limited to 'src') diff --git a/src/selfstrap b/src/selfstrap new file mode 100755 index 0000000..165a921 --- /dev/null +++ b/src/selfstrap @@ -0,0 +1,376 @@ +#!/bin/bash +debian_mirror=http://httpredir.debian.org/debian + +if [ $# = 1 ]; then + rootfs=$1 +else + echo "Usage: $0 " >&2 + exit 1 +fi + +die() { printf 'Error: %s\n' "$*"; exit 1; } + +am_root() { [ "$(id -u)" = 0 ]; } + +current_debian_codename() +{ + # lsb_release -cs + sed -ne 's/^VERSION=.* (\(.*\)).*/\1/p' /etc/os-release +} + +sanity_checks() +{ + am_root || die 'you are not root' + rootfs=$(realpath "$rootfs") || die 'realpath failed' + [ "$rootfs" ] || die 'no $rootfs' + [ -d "$rootfs" ] || mkdir "$rootfs" || die 'could not mkdir($rootfs)' +} + +generate_apt_config() +{ + APT_CONFIG=$(mktemp) || exit + chmod 644 "$APT_CONFIG" + cat > "$APT_CONFIG" </dev/null || DEBUG_DPKG=10013 + set -- -o DPkg::options::arg0=--debug="${DEBUG_DPKG}" "$@" + fi + # This is the important one: + set -- -o DPkg::options::arg1=--root="$rootfs" "$@" + set -- -o DPkg::options::arg2=--force-unsafe-io "$@" + + # Use the calling system for these. This is an optimization. + set -- -o Dir::Cache::archives=/var/cache/apt/archives "$@" + set -- -o Dir::Etc::Trusted=/etc/apt/trusted.gpg "$@" + set -- -o Dir::Etc::TrustedParts=/etc/apt/trusted.gpg.d "$@" + set -- -o Dir::State::Lists=/var/lib/apt/lists "$@" + + # Avoid deleting lists on the calling system. + set -- -o APT::Get::List-Cleanup=false "$@" + + [ "$VERBOSE" ] && printf '+ apt-get %s\n' "$visible_args" >&2 || true + + apt-get "$@" +} + +idem() { if [ ! -e "${!#}" ]; then "$@"; fi; } +idem_mknod() { if [ ! -e "$3" ]; then mknod "$@"; fi; } + +# This function is copied (with modifications) from debootstrap. +install_devices() +{ + local TARGET="$rootfs" + [ "$TARGET" -a -d "$TARGET" ] || die 'no $TARGET' + idem mkdir "$TARGET"/dev + # The list of devices that can be created in a container comes from + # src/core/cgroup.c in the systemd source tree. + idem_mknod -m 666 "$TARGET"/dev/null c 1 3 + idem_mknod -m 666 "$TARGET"/dev/zero c 1 5 + idem_mknod -m 666 "$TARGET"/dev/full c 1 7 + idem_mknod -m 666 "$TARGET"/dev/random c 1 8 + idem_mknod -m 666 "$TARGET"/dev/urandom c 1 9 + idem_mknod -m 666 "$TARGET"/dev/tty c 5 0 + idem mkdir "$TARGET"/dev/pts/ + idem mkdir "$TARGET"/dev/shm/ + # Inside a container, we might not be allowed to create /dev/ptmx. + # If not, do the next best thing. + if ! idem_mknod -m 666 "$TARGET"/dev/ptmx c 5 2; then + idem ln -s pts/ptmx "$TARGET"/dev/ptmx + fi + idem ln -s /proc/self/fd "$TARGET"/dev/fd + idem ln -s /proc/self/fd/0 "$TARGET"/dev/stdin + idem ln -s /proc/self/fd/1 "$TARGET"/dev/stdout + idem ln -s /proc/self/fd/2 "$TARGET"/dev/stderr +} + +mount_virtfs() +{ + local TARGET="$1" + [ "$TARGET" ] || die 'no $TARGET' + [ -d "$TARGET"/proc ] || mkdir "$TARGET"/proc + [ -d "$TARGET"/sys ] || mkdir "$TARGET"/sys + mount -t proc proc "$TARGET"/proc + mount -t sysfs sysfs "$TARGET"/sys +} + +umount_virtfs() +{ + local TARGET="$1" fail= + [ "$TARGET" ] || die 'no $TARGET' + umount "$TARGET"/proc || fail=y + umount "$TARGET"/sys || fail=y + [ ! "$fail" ] +} + +write_lines_once() +{ + local output="$1" + shift + [ -e "$output" ] || printf '%s\n' "$@" > "$output" +} + +populate_rootfs() +{ + [ "$rootfs" ] || die 'no $rootfs' + set -B + mkdir -p \ + "$rootfs"/etc/apt/{preferences.d,apt.conf.d,trusted.gpg.d,sources.list.d} \ + "$rootfs"/var/{lib/{apt/lists/partial,dpkg/{info,parts,triggers,alternatives,updates}},cache/apt} \ + "$rootfs"/var/log/apt + + touch "$rootfs"/var/lib/dpkg/status + + write_lines_once "$rootfs"/var/lib/dpkg/arch "$rootfs_arch" + write_lines_once "$rootfs"/etc/apt/sources.list \ + "deb $debian_mirror $release main contrib non-free" \ + "deb http://security.debian.org $release/updates main contrib non-free" + install_devices +} + +parse_apt_noact_line() +{ + set -- $* + action=$1 + package=$2 + version=${3#\(} + shift 3 + + while [ "$1" ]; do + case "$1" in + *\)) + arch=${1%\)} + arch=${arch#\[} + arch=${arch%\]} + return + ;; + *) shift ;; + esac + done + return 1 +} + +dpkg_extract_with_info() +{ + local deb="$1" TARGET="$2" multiarch="$3" command PKG + PKG=${deb##*/} + PKG=${PKG%%_*} + PKG=$PKG$multiarch + + command=$(cat <<'EOF' +if [ "$TAR_FILENAME" = ./control ]; then + (sed "/^Package:/a Status: install ok installed"; echo) >> "$TARGET"/var/lib/dpkg/status +else + f=$TARGET/var/lib/dpkg/info/$PKG.${TAR_FILENAME#./} + cat > "$f" + chmod $TAR_MODE "$f" +fi +EOF +) + (export PKG TARGET; dpkg --ctrl-tarfile "$deb" | tar -x --to-command "$command") + dpkg --fsys-tarfile "$deb" | + (cd "$TARGET" && tar -xv) | + sed 's?^\.??; s?^/$?/.?; s?/$??' > "$TARGET/var/lib/dpkg/info/$PKG.list" +} + +apt_run_inst() +{ + apt_get -s -yqq install "$@" | dpkg_inst_from_apt +} + +dpkg_inst_from_apt() +{ + while read line; do + parse_apt_noact_line "$line" || die "unexpected output from apt-get: $line" + export LC_ALL=C + export DEBIAN_FRONTEND=noninteractive + export DPKG_MAINTSCRIPT_PACKAGE="$package" DPKG_MAINTSCRIPT_ARCH="$arch" + is_multiarch_same "$package" && multiarch=":$arch" || multiarch= + case "$action" in + Inst) + export DPKG_MAINTSCRIPT_NAME=preinst + preinst=/var/lib/dpkg/info/${package}${multiarch}.preinst + if [ -x "$rootfs"/"$preinst" ]; then + (set -x; chroot "$rootfs" "$preinst" install) + fi + ;; + Conf) + export DPKG_MAINTSCRIPT_NAME=postinst + postinst=/var/lib/dpkg/info/${package}${multiarch}.postinst + if [ -x "$rootfs"/"$postinst" ]; then + (set -x; chroot "$rootfs" "$postinst" configure) + fi + ;; + Remv) ;; + *) die "impossible" ;; + esac + done +} + +apt_extract() +{ + apt_get -d -yqq install "$@" + actions=$(mktemp) || die 'mktemp failed' + apt_get -s -yqq install "$@" | tee "$actions" | dpkg_extract_from_apt + + if [ "$EXTRACT_DPKG_INFO" ]; then + install_etc_passwd + dpkg_inst_from_apt < "$actions" + fi + + rm "$actions" +} + +dpkg_extract_from_apt() +{ + while read line; do + + parse_apt_noact_line "$line" || die "unexpected output from apt-get: $line" + + deb=/var/cache/apt/archives/${package}_${version//:/%3a}_${arch}.deb + [ -f "$deb" ] || { + echo "line=$line" >&2 + printf '%s\n' "$action" "$package" "$version" "$arch" >&2 + die "unexpected output from apt-get" + } + case "$action" in + Inst) + printf 'Extracting %s\n' "${deb##*/}" >&2 + + if [ "$EXTRACT_DPKG_INFO" ]; then + is_multiarch_same "$package" && multiarch=":$arch" || multiarch= + dpkg_extract_with_info "$deb" "$rootfs" "$multiarch" + else + dpkg-deb --extract "$deb" "$rootfs" + fi + ;; + Conf) ;; + Remv) ;; + *) die "impossible" ;; + esac + done +} + +install_etc_passwd() +{ + [ -e "$rootfs"/etc/passwd ] || cp "$rootfs"/usr/share/base-passwd/passwd.master "$rootfs"/etc/passwd + [ -e "$rootfs"/etc/group ] || cp "$rootfs"/usr/share/base-passwd/group.master "$rootfs"/etc/group +} + +main_packages_file() +{ + local uri_part="${debian_mirror#*/}" + while [ "${uri_part:0:1}" = / ]; do + uri_part=${uri_part#?} + done + uri_part=${uri_part//\//_} + printf '/var/lib/apt/lists/%s_dists_%s_main_binary-%s_Packages\n' "$uri_part" "$release" "$rootfs_arch" +} + +required_packages() +{ + perl -00 -ne \ + '/^Priority: required/m || next; /^Package: (.*)$/m && print "$1\n"' \ + "$(main_packages_file)" | sort -u +} + +declare -A is_multiarch_same +multicheck() +{ + local m + if [ ! "$multichecked" ]; then + for m in $(multiarch_same_packages); do + is_multiarch_same[$m]=y + done + fi + multichecked=y +} + +multiarch_same_packages() +{ + perl -00 -ne \ + '/^Multi-Arch: same/mi || next; /^Package: (.*)$/m && print "$1\n"' \ + "$(main_packages_file)" | sort -u +} + +is_multiarch_same() +{ + multicheck + [ "${is_multiarch_same[$1]}" ] +} + +set -e + +rootfs_arch=$(dpkg-architecture -q DEB_HOST_ARCH) || die 'dpkg-architecture failed' +release=$(current_debian_codename) && [ "$release" ] || die 'could not determine Debian release name' + +if [ "$FAST_MODE" ]; then + SKIP_INSTALL=y + EXTRACT_DPKG_INFO=y + RUN_INST_SCRIPTS=y +fi + +# Set things up so apt-get update works. +sanity_checks +generate_apt_config +populate_rootfs + + +# Initial apt-get update will determine what we shall install. +[ "$SKIP_UPDATE" ] || apt_get update +packages=$(required_packages) && [ "$packages" ] || die 'failed to determine list of required packages' +extra_packages='apt debian-archive-keyring locales' + + +# Some files need to be present before 'apt-get install' can install anything. +# In particular: +# +# 1. binaries used by dpkg 'inst' scripts. +# 2. /etc/passwd and /etc/group so that 'chown' works + +# Rather than fuss about (1), extract everything from all packages. + +# Note: apt_extract() runs preinst and postinst scripts itself when +# $EXTRACT_DPKG_INFO is true. +apt_extract $packages + +if [ "$SKIP_INSTALL" ]; then + apt_extract $extra_packages +else + export LC_ALL=C + export DEBIAN_FRONTEND=noninteractive + + install_etc_passwd # This handles (2). + if [ "$FIX_BROKEN" ]; then + dpkg --root="$rootfs" --configure -a + fi + apt_get install -y $packages $extra_packages +fi -- cgit v1.2.3