michael@0: #!/bin/bash -e michael@0: michael@0: # Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: # This script installs Debian-derived distributions in a chroot environment. michael@0: # It can for example be used to have an accurate 32bit build and test michael@0: # environment when otherwise working on a 64bit machine. michael@0: # N. B. it is unlikely that this script will ever work on anything other than a michael@0: # Debian-derived system. michael@0: michael@0: # Older Debian based systems had both "admin" and "adm" groups, with "admin" michael@0: # apparently being used in more places. Newer distributions have standardized michael@0: # on just the "adm" group. Check /etc/group for the prefered name of the michael@0: # administrator group. michael@0: admin=$(grep '^admin:' /etc/group >&/dev/null && echo admin || echo adm) michael@0: michael@0: usage() { michael@0: echo "usage: ${0##*/} [-m mirror] [-g group,...] [-s] [-c]" michael@0: echo "-b dir additional directories that should be bind mounted," michael@0: echo ' or "NONE".' michael@0: echo " Default: if local filesystems present, ask user for help" michael@0: echo "-g group,... groups that can use the chroot unauthenticated" michael@0: echo " Default: '${admin}' and current user's group ('$(id -gn)')" michael@0: echo "-l List all installed chroot environments" michael@0: echo "-m mirror an alternate repository mirror for package downloads" michael@0: echo "-s configure default deb-srcs" michael@0: echo "-c always copy 64bit helper binaries to 32bit chroot" michael@0: echo "-h this help message" michael@0: } michael@0: michael@0: process_opts() { michael@0: local OPTNAME OPTIND OPTERR OPTARG michael@0: while getopts ":b:g:lm:sch" OPTNAME; do michael@0: case "$OPTNAME" in michael@0: b) michael@0: if [ "${OPTARG}" = "NONE" -a -z "${bind_mounts}" ]; then michael@0: bind_mounts="${OPTARG}" michael@0: else michael@0: if [ "${bind_mounts}" = "NONE" -o "${OPTARG}" = "${OPTARG#/}" -o \ michael@0: ! -d "${OPTARG}" ]; then michael@0: echo "Invalid -b option(s)" michael@0: usage michael@0: exit 1 michael@0: fi michael@0: bind_mounts="${bind_mounts} michael@0: ${OPTARG} ${OPTARG} none rw,bind 0 0" michael@0: fi michael@0: ;; michael@0: g) michael@0: [ -n "${OPTARG}" ] && michael@0: chroot_groups="${chroot_groups}${chroot_groups:+,}${OPTARG}" michael@0: ;; michael@0: l) michael@0: list_all_chroots michael@0: exit michael@0: ;; michael@0: m) michael@0: if [ -n "${mirror}" ]; then michael@0: echo "You can only specify exactly one mirror location" michael@0: usage michael@0: exit 1 michael@0: fi michael@0: mirror="$OPTARG" michael@0: ;; michael@0: s) michael@0: add_srcs="y" michael@0: ;; michael@0: c) michael@0: copy_64="y" michael@0: ;; michael@0: h) michael@0: usage michael@0: exit 0 michael@0: ;; michael@0: \:) michael@0: echo "'-$OPTARG' needs an argument." michael@0: usage michael@0: exit 1 michael@0: ;; michael@0: *) michael@0: echo "invalid command-line option: $OPTARG" michael@0: usage michael@0: exit 1 michael@0: ;; michael@0: esac michael@0: done michael@0: michael@0: if [ $# -ge ${OPTIND} ]; then michael@0: eval echo "Unexpected command line argument: \${${OPTIND}}" michael@0: usage michael@0: exit 1 michael@0: fi michael@0: } michael@0: michael@0: list_all_chroots() { michael@0: for i in /var/lib/chroot/*; do michael@0: i="${i##*/}" michael@0: [ "${i}" = "*" ] && continue michael@0: [ -x "/usr/local/bin/${i%bit}" ] || continue michael@0: grep -qs "^\[${i%bit}\]\$" /etc/schroot/schroot.conf || continue michael@0: [ -r "/etc/schroot/script-${i}" -a \ michael@0: -r "/etc/schroot/mount-${i}" ] || continue michael@0: echo "${i%bit}" michael@0: done michael@0: } michael@0: michael@0: getkey() { michael@0: ( michael@0: trap 'stty echo -iuclc icanon 2>/dev/null' EXIT INT TERM QUIT HUP michael@0: stty -echo iuclc -icanon 2>/dev/null michael@0: dd count=1 bs=1 2>/dev/null michael@0: ) michael@0: } michael@0: michael@0: chr() { michael@0: printf "\\$(printf '%03o' "$1")" michael@0: } michael@0: michael@0: ord() { michael@0: printf '%d' $(printf '%c' "$1" | od -tu1 -An) michael@0: } michael@0: michael@0: is_network_drive() { michael@0: stat -c %T -f "$1/" 2>/dev/null | michael@0: egrep -qs '^nfs|cifs|smbfs' michael@0: } michael@0: michael@0: # Check that we are running as a regular user michael@0: [ "$(id -nu)" = root ] && { michael@0: echo "Run this script as a regular user and provide your \"sudo\"" \ michael@0: "password if requested" >&2 michael@0: exit 1 michael@0: } michael@0: michael@0: process_opts "$@" michael@0: michael@0: echo "This script will help you through the process of installing a" michael@0: echo "Debian or Ubuntu distribution in a chroot environment. You will" michael@0: echo "have to provide your \"sudo\" password when requested." michael@0: echo michael@0: michael@0: # Error handler michael@0: trap 'exit 1' INT TERM QUIT HUP michael@0: trap 'sudo apt-get clean; tput bel; echo; echo Failed' EXIT michael@0: michael@0: # Install any missing applications that this script relies on. If these packages michael@0: # are already installed, don't force another "apt-get install". That would michael@0: # prevent them from being auto-removed, if they ever become eligible for that. michael@0: # And as this script only needs the packages once, there is no good reason to michael@0: # introduce a hard dependency on things such as dchroot and debootstrap. michael@0: dep= michael@0: for i in dchroot debootstrap libwww-perl; do michael@0: [ -d /usr/share/doc/"$i" ] || dep="$dep $i" michael@0: done michael@0: [ -n "$dep" ] && sudo apt-get -y install $dep michael@0: sudo apt-get -y install schroot michael@0: michael@0: # Create directory for chroot michael@0: sudo mkdir -p /var/lib/chroot michael@0: michael@0: # Find chroot environments that can be installed with debootstrap michael@0: targets="$(cd /usr/share/debootstrap/scripts michael@0: ls | grep '^[a-z]*$')" michael@0: michael@0: # Ask user to pick one of the available targets michael@0: echo "The following targets are available to be installed in a chroot:" michael@0: j=1; for i in $targets; do michael@0: printf '%4d: %s\n' "$j" "$i" michael@0: j=$(($j+1)) michael@0: done michael@0: while :; do michael@0: printf "Which target would you like to install: " michael@0: read n michael@0: [ "$n" -gt 0 -a "$n" -lt "$j" ] >&/dev/null && break michael@0: done michael@0: j=1; for i in $targets; do michael@0: [ "$j" -eq "$n" ] && { distname="$i"; break; } michael@0: j=$(($j+1)) michael@0: done michael@0: echo michael@0: michael@0: # On x86-64, ask whether the user wants to install x86-32 or x86-64 michael@0: archflag= michael@0: arch= michael@0: if [ "$(uname -m)" = x86_64 ]; then michael@0: while :; do michael@0: echo "You are running a 64bit kernel. This allows you to install either a" michael@0: printf "32bit or a 64bit chroot environment. %s" \ michael@0: "Which one do you want (32, 64) " michael@0: read arch michael@0: [ "${arch}" == 32 -o "${arch}" == 64 ] && break michael@0: done michael@0: [ "${arch}" == 32 ] && archflag="--arch i386" || archflag="--arch amd64" michael@0: arch="${arch}bit" michael@0: echo michael@0: fi michael@0: target="${distname}${arch}" michael@0: michael@0: # Don't accidentally overwrite an existing installation michael@0: [ -d /var/lib/chroot/"${target}" ] && { michael@0: while :; do michael@0: echo "This chroot already exists on your machine." michael@0: if schroot -l --all-sessions 2>&1 | michael@0: sed 's/^session://' | michael@0: grep -qs "^${target%bit}-"; then michael@0: echo "And it appears to be in active use. Terminate all programs that" michael@0: echo "are currently using the chroot environment and then re-run this" michael@0: echo "script." michael@0: echo "If you still get an error message, you might have stale mounts" michael@0: echo "that you forgot to delete. You can always clean up mounts by" michael@0: echo "executing \"${target%bit} -c\"." michael@0: exit 1 michael@0: fi michael@0: echo "I can abort installation, I can overwrite the existing chroot," michael@0: echo "or I can delete the old one and then exit. What would you like to" michael@0: printf "do (a/o/d)? " michael@0: read choice michael@0: case "${choice}" in michael@0: a|A) exit 1;; michael@0: o|O) sudo rm -rf "/var/lib/chroot/${target}"; break;; michael@0: d|D) sudo rm -rf "/var/lib/chroot/${target}" \ michael@0: "/usr/local/bin/${target%bit}" \ michael@0: "/etc/schroot/mount-${target}" \ michael@0: "/etc/schroot/script-${target}" michael@0: sudo sed -ni '/^[[]'"${target%bit}"']$/,${ michael@0: :1;n;/^[[]/b2;b1;:2;p;n;b2};p' \ michael@0: "/etc/schroot/schroot.conf" michael@0: trap '' INT TERM QUIT HUP michael@0: trap '' EXIT michael@0: echo "Deleted!" michael@0: exit 0;; michael@0: esac michael@0: done michael@0: echo michael@0: } michael@0: sudo mkdir -p /var/lib/chroot/"${target}" michael@0: michael@0: # Offer to include additional standard repositories for Ubuntu-based chroots. michael@0: alt_repos= michael@0: grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && { michael@0: while :; do michael@0: echo "Would you like to add ${distname}-updates and ${distname}-security " michael@0: printf "to the chroot's sources.list (y/n)? " michael@0: read alt_repos michael@0: case "${alt_repos}" in michael@0: y|Y) michael@0: alt_repos="y" michael@0: break michael@0: ;; michael@0: n|N) michael@0: break michael@0: ;; michael@0: esac michael@0: done michael@0: echo michael@0: } michael@0: michael@0: # Check for non-standard file system mount points and ask the user whether michael@0: # they should be imported into the chroot environment michael@0: # We limit to the first 26 mount points that much some basic heuristics, michael@0: # because a) that allows us to enumerate choices with a single character, michael@0: # and b) if we find more than 26 mount points, then these are probably michael@0: # false-positives and something is very unusual about the system's michael@0: # configuration. No need to spam the user with even more information that michael@0: # is likely completely irrelevant. michael@0: if [ -z "${bind_mounts}" ]; then michael@0: mounts="$(awk '$2 != "/" && $2 !~ "^/boot" && $2 !~ "^/home" && michael@0: $2 !~ "^/media" && $2 !~ "^/run" && michael@0: ($3 ~ "ext[2-4]" || $3 == "reiserfs" || $3 == "btrfs" || michael@0: $3 == "xfs" || $3 == "jfs" || $3 == "u?msdos" || michael@0: $3 == "v?fat" || $3 == "hfs" || $3 == "ntfs" || michael@0: $3 ~ "nfs[4-9]?" || $3 == "smbfs" || $3 == "cifs") { michael@0: print $2 michael@0: }' /proc/mounts | michael@0: head -n26)" michael@0: if [ -n "${mounts}" ]; then michael@0: echo "You appear to have non-standard mount points that you" michael@0: echo "might want to import into the chroot environment:" michael@0: echo michael@0: sel= michael@0: while :; do michael@0: # Print a menu, listing all non-default mounts of local or network michael@0: # file systems. michael@0: j=1; for m in ${mounts}; do michael@0: c="$(printf $(printf '\\%03o' $((64+$j))))" michael@0: echo "$sel" | grep -qs $c && michael@0: state="mounted in chroot" || state="$(tput el)" michael@0: printf " $c) %-40s${state}\n" "$m" michael@0: j=$(($j+1)) michael@0: done michael@0: # Allow user to interactively (de-)select any of the entries michael@0: echo michael@0: printf "Select mount points that you want to be included or press %s" \ michael@0: "SPACE to continue" michael@0: c="$(getkey | tr a-z A-Z)" michael@0: [ "$c" == " " ] && { echo; echo; break; } michael@0: if [ -z "$c" ] || michael@0: [ "$c" '<' 'A' -o $(ord "$c") -gt $((64 + $(ord "$j"))) ]; then michael@0: # Invalid input, ring the console bell michael@0: tput bel michael@0: else michael@0: # Toggle the selection for the given entry michael@0: if echo "$sel" | grep -qs $c; then michael@0: sel="$(printf "$sel" | sed "s/$c//")" michael@0: else michael@0: sel="$sel$c" michael@0: fi michael@0: fi michael@0: # Reposition cursor to the top of the list of entries michael@0: tput cuu $(($j + 1)) michael@0: echo michael@0: done michael@0: fi michael@0: j=1; for m in ${mounts}; do michael@0: c="$(chr $(($j + 64)))" michael@0: if echo "$sel" | grep -qs $c; then michael@0: bind_mounts="${bind_mounts}$m $m none rw,bind 0 0 michael@0: " michael@0: fi michael@0: j=$(($j+1)) michael@0: done michael@0: fi michael@0: michael@0: # Remove stale entry from /etc/schroot/schroot.conf. Entries start michael@0: # with the target name in square brackets, followed by an arbitrary michael@0: # number of lines. The entry stops when either the end of file has michael@0: # been reached, or when the beginning of a new target is encountered. michael@0: # This means, we cannot easily match for a range of lines in michael@0: # "sed". Instead, we actually have to iterate over each line and check michael@0: # whether it is the beginning of a new entry. michael@0: sudo sed -ni '/^[[]'"${target%bit}"']$/,${:1;n;/^[[]/b2;b1;:2;p;n;b2};p' \ michael@0: /etc/schroot/schroot.conf michael@0: michael@0: # Download base system. This takes some time michael@0: if [ -z "${mirror}" ]; then michael@0: grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && michael@0: mirror="http://archive.ubuntu.com/ubuntu" || michael@0: mirror="http://ftp.us.debian.org/debian" michael@0: fi michael@0: michael@0: sudo ${http_proxy:+http_proxy="${http_proxy}"} debootstrap ${archflag} \ michael@0: "${distname}" "/var/lib/chroot/${target}" "$mirror" michael@0: michael@0: # Add new entry to /etc/schroot/schroot.conf michael@0: grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && michael@0: brand="Ubuntu" || brand="Debian" michael@0: if [ -z "${chroot_groups}" ]; then michael@0: chroot_groups="${admin},$(id -gn)" michael@0: fi michael@0: # Older versions of schroot wanted a "priority=" line, whereas recent michael@0: # versions deprecate "priority=" and warn if they see it. We don't have michael@0: # a good feature test, but scanning for the string "priority=" in the michael@0: # existing "schroot.conf" file is a good indication of what to do. michael@0: priority=$(grep -qs 'priority=' /etc/schroot/schroot.conf && michael@0: echo 'priority=3' || :) michael@0: sudo sh -c 'cat >>/etc/schroot/schroot.conf' </etc/schroot/script-'"${target}" michael@0: sed '\,^/home[/[:space:]],s/\([,[:space:]]\)bind[[:space:]]/\1rbind /' \ michael@0: /etc/schroot/mount-defaults | michael@0: sudo sh -c 'cat > /etc/schroot/mount-'"${target}" michael@0: michael@0: # Add the extra mount points that the user told us about michael@0: [ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] && michael@0: printf "${bind_mounts}" | michael@0: sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" michael@0: michael@0: # If this system has a "/media" mountpoint, import it into the chroot michael@0: # environment. Most modern distributions use this mount point to michael@0: # automatically mount devices such as CDROMs, USB sticks, etc... michael@0: if [ -d /media ] && michael@0: ! grep -qs '^/media' /etc/schroot/mount-"${target}"; then michael@0: echo '/media /media none rw,rbind 0 0' | michael@0: sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" michael@0: fi michael@0: michael@0: # Share /dev/shm and possibly /run/shm michael@0: grep -qs '^/dev/shm' /etc/schroot/mount-"${target}" || michael@0: echo '/dev/shm /dev/shm none rw,bind 0 0' | michael@0: sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" michael@0: if [ -d "/var/lib/chroot/${target}/run" ] && michael@0: ! grep -qs '^/run/shm' /etc/schroot/mount-"${target}"; then michael@0: { [ -d /run ] && echo '/run/shm /run/shm none rw,bind 0 0' || michael@0: echo '/dev/shm /run/shm none rw,bind 0 0'; } | michael@0: sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" michael@0: fi michael@0: michael@0: # Set up a special directory that changes contents depending on the target michael@0: # that is executing. michael@0: d="$(readlink -f "${HOME}/chroot" 2>/dev/null || echo "${HOME}/chroot")" michael@0: s="${d}/.${target}" michael@0: echo "${s} ${d} none rw,bind 0 0" | michael@0: sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" michael@0: mkdir -p "${s}" michael@0: michael@0: # Install a helper script to launch commands in the chroot michael@0: sudo sh -c 'cat >/usr/local/bin/'"${target%bit}" <<'EOF' michael@0: #!/bin/bash michael@0: michael@0: chroot="${0##*/}" michael@0: michael@0: wrap() { michael@0: # Word-wrap the text passed-in on stdin. Optionally, on continuation lines michael@0: # insert the same number of spaces as the number of characters in the michael@0: # parameter(s) passed to this function. michael@0: # If the "fold" program cannot be found, or if the actual width of the michael@0: # terminal cannot be determined, this function doesn't attempt to do any michael@0: # wrapping. michael@0: local f="$(type -P fold)" michael@0: [ -z "${f}" ] && { cat; return; } michael@0: local c="$(stty -a /dev/null | michael@0: sed 's/.*columns[[:space:]]*\([0-9]*\).*/\1/;t;d')" michael@0: [ -z "${c}" ] && { cat; return; } michael@0: local i="$(echo "$*"|sed 's/./ /g')" michael@0: local j="$(printf %s "${i}"|wc -c)" michael@0: if [ "${c}" -gt "${j}" ]; then michael@0: dd bs=1 count="${j}" 2>/dev/null michael@0: "${f}" -sw "$((${c}-${j}))" | sed '2,$s/^/'"${i}"'/' michael@0: else michael@0: "${f}" -sw "${c}" michael@0: fi michael@0: } michael@0: michael@0: help() { michael@0: echo "Usage ${0##*/} [-h|--help] [-c|--clean] [-C|--clean-all] [-l|--list] [--] args" | wrap "Usage ${0##*/} " michael@0: echo " help: print this message" | wrap " " michael@0: echo " list: list all known chroot environments" | wrap " " michael@0: echo " clean: remove all old chroot sessions for \"${chroot}\"" | wrap " " michael@0: echo " clean-all: remove all old chroot sessions for all environments" | wrap " " michael@0: exit 0 michael@0: } michael@0: michael@0: clean() { michael@0: local s t rc michael@0: rc=0 michael@0: for s in $(schroot -l --all-sessions); do michael@0: if [ -n "$1" ]; then michael@0: t="${s#session:}" michael@0: [ "${t#${chroot}-}" == "${t}" ] && continue michael@0: fi michael@0: if ls -l /proc/*/{cwd,fd} 2>/dev/null | michael@0: fgrep -qs "/var/lib/schroot/mount/${t}"; then michael@0: echo "Session \"${t}\" still has active users, not cleaning up" | wrap michael@0: rc=1 michael@0: continue michael@0: fi michael@0: sudo schroot -c "${s}" -e || rc=1 michael@0: done michael@0: exit ${rc} michael@0: } michael@0: michael@0: list() { michael@0: for e in $(schroot -l); do michael@0: e="${e#chroot:}" michael@0: [ -x "/usr/local/bin/${e}" ] || continue michael@0: if schroot -l --all-sessions 2>/dev/null | michael@0: sed 's/^session://' | michael@0: grep -qs "^${e}-"; then michael@0: echo "${e} is currently active" michael@0: else michael@0: echo "${e}" michael@0: fi michael@0: done michael@0: exit 0 michael@0: } michael@0: michael@0: while [ "$#" -ne 0 ]; do michael@0: case "$1" in michael@0: --) shift; break;; michael@0: -h|--help) shift; help;; michael@0: -l|--list) shift; list;; michael@0: -c|--clean) shift; clean "${chroot}";; michael@0: -C|--clean-all) shift; clean;; michael@0: *) break;; michael@0: esac michael@0: done michael@0: michael@0: session="$(schroot -c "${chroot}" -b)" michael@0: michael@0: if [ $# -eq 0 ]; then michael@0: schroot -c "${session}" -r -p michael@0: else michael@0: p="$1"; shift michael@0: schroot -c "${session}" -r -p "$p" -- "$@" michael@0: fi michael@0: rc=$? michael@0: michael@0: i=$(schroot -c "${session}" -r -p ls -- -id /proc/self/root/. | michael@0: awk '{ print $1 }') 2>/dev/null michael@0: while [ -n "$i" ]; do michael@0: pids=$(ls -id1 /proc/*/root/. 2>/dev/null | michael@0: sed -e 's,^[^0-9]*'$i'.*/\([1-9][0-9]*\)/.*$,\1, michael@0: t michael@0: d') >/dev/null 2>&1 michael@0: [ -z "$pids" ] && break michael@0: kill -9 $pids michael@0: done michael@0: schroot -c "${session}" -e michael@0: exit $rc michael@0: EOF michael@0: sudo chown root:root /usr/local/bin/"${target%bit}" michael@0: sudo chmod 755 /usr/local/bin/"${target%bit}" michael@0: michael@0: # Add the standard Ubuntu update repositories if requested. michael@0: [ "${alt_repos}" = "y" -a \ michael@0: -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && michael@0: sudo sed -i '/^deb .* [^ -]\+ main$/p michael@0: s/^\(deb .* [^ -]\+\) main/\1-security main/ michael@0: p michael@0: t1 michael@0: d michael@0: :1;s/-security main/-updates main/ michael@0: t michael@0: d' "/var/lib/chroot/${target}/etc/apt/sources.list" michael@0: michael@0: # Add a few more repositories to the chroot michael@0: [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && michael@0: sudo sed -i 's/ main$/ main restricted universe multiverse/' \ michael@0: "/var/lib/chroot/${target}/etc/apt/sources.list" michael@0: michael@0: # Add the Ubuntu "partner" repository, if available michael@0: if [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && michael@0: HEAD "http://archive.canonical.com/ubuntu/dists/${distname}/partner" \ michael@0: >&/dev/null; then michael@0: sudo sh -c ' michael@0: echo "deb http://archive.canonical.com/ubuntu" \ michael@0: "'"${distname}"' partner" \ michael@0: >>"/var/lib/chroot/'"${target}"'/etc/apt/sources.list"' michael@0: fi michael@0: michael@0: # Add source repositories, if the user requested we do so michael@0: [ "${add_srcs}" = "y" -a \ michael@0: -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && michael@0: sudo sed -i '/^deb[^-]/p michael@0: s/^deb\([^-]\)/deb-src\1/' \ michael@0: "/var/lib/chroot/${target}/etc/apt/sources.list" michael@0: michael@0: # Set apt proxy if host has set http_proxy michael@0: if [ -n "${http_proxy}" ]; then michael@0: sudo sh -c ' michael@0: echo "Acquire::http::proxy \"'"${http_proxy}"'\";" \ michael@0: >>"/var/lib/chroot/'"${target}"'/etc/apt/apt.conf"' michael@0: fi michael@0: michael@0: # Update packages michael@0: sudo "/usr/local/bin/${target%bit}" /bin/sh -c ' michael@0: apt-get update; apt-get -y dist-upgrade' || : michael@0: michael@0: # Install a couple of missing packages michael@0: for i in debian-keyring ubuntu-keyring locales sudo; do michael@0: [ -d "/var/lib/chroot/${target}/usr/share/doc/$i" ] || michael@0: sudo "/usr/local/bin/${target%bit}" apt-get -y install "$i" || : michael@0: done michael@0: michael@0: # Configure locales michael@0: sudo "/usr/local/bin/${target%bit}" /bin/sh -c ' michael@0: l='"${LANG:-en_US}"'; l="${l%%.*}" michael@0: [ -r /etc/locale.gen ] && michael@0: sed -i "s/^# \($l\)/\1/" /etc/locale.gen michael@0: locale-gen $LANG en_US en_US.UTF-8' || : michael@0: michael@0: # Enable multi-arch support, if available michael@0: sudo "/usr/local/bin/${target%bit}" dpkg --assert-multi-arch >&/dev/null && michael@0: [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && { michael@0: sudo sed -i 's/ / [arch=amd64,i386] /' \ michael@0: "/var/lib/chroot/${target}/etc/apt/sources.list" michael@0: [ -d /var/lib/chroot/${target}/etc/dpkg/dpkg.cfg.d/ ] && michael@0: echo foreign-architecture \ michael@0: $([ "${arch}" = "32bit" ] && echo amd64 || echo i386) | michael@0: sudo sh -c "cat >'/var/lib/chroot/${target}/etc/dpkg/dpkg.cfg.d/multiarch'" michael@0: } michael@0: michael@0: # Configure "sudo" package michael@0: sudo "/usr/local/bin/${target%bit}" /bin/sh -c ' michael@0: egrep -qs '"'^$(id -nu) '"' /etc/sudoers || michael@0: echo '"'$(id -nu) ALL=(ALL) ALL'"' >>/etc/sudoers' michael@0: michael@0: # Install a few more commonly used packages michael@0: sudo "/usr/local/bin/${target%bit}" apt-get -y install \ michael@0: autoconf automake1.9 dpkg-dev g++-multilib gcc-multilib gdb less libtool \ michael@0: strace michael@0: michael@0: # If running a 32bit environment on a 64bit machine, install a few binaries michael@0: # as 64bit. This is only done automatically if the chroot distro is the same as michael@0: # the host, otherwise there might be incompatibilities in build settings or michael@0: # runtime dependencies. The user can force it with the '-c' flag. michael@0: host_distro=$(grep -s DISTRIB_CODENAME /etc/lsb-release | \ michael@0: cut -d "=" -f 2) michael@0: if [ "${copy_64}" = "y" -o \ michael@0: "${host_distro}" = "${distname}" -a "${arch}" = 32bit ] && \ michael@0: file /bin/bash 2>/dev/null | grep -q x86-64; then michael@0: readlinepkg=$(sudo "/usr/local/bin/${target%bit}" sh -c \ michael@0: 'apt-cache search "lib64readline.\$" | sort | tail -n 1 | cut -d " " -f 1') michael@0: sudo "/usr/local/bin/${target%bit}" apt-get -y install \ michael@0: lib64expat1 lib64ncurses5 ${readlinepkg} lib64z1 michael@0: dep= michael@0: for i in binutils gdb; do michael@0: [ -d /usr/share/doc/"$i" ] || dep="$dep $i" michael@0: done michael@0: [ -n "$dep" ] && sudo apt-get -y install $dep michael@0: sudo mkdir -p "/var/lib/chroot/${target}/usr/local/lib/amd64" michael@0: for i in libbfd libpython; do michael@0: lib="$({ ldd /usr/bin/ld; ldd /usr/bin/gdb; } | michael@0: grep -s "$i" | awk '{ print $3 }')" michael@0: if [ -n "$lib" -a -r "$lib" ]; then michael@0: sudo cp "$lib" "/var/lib/chroot/${target}/usr/local/lib/amd64" michael@0: fi michael@0: done michael@0: for lib in libssl libcrypt; do michael@0: for path in /usr/lib /usr/lib/x86_64-linux-gnu; do michael@0: sudo cp $path/$lib* \ michael@0: "/var/lib/chroot/${target}/usr/local/lib/amd64/" >&/dev/null || : michael@0: done michael@0: done michael@0: for i in gdb ld; do michael@0: sudo cp /usr/bin/$i "/var/lib/chroot/${target}/usr/local/lib/amd64/" michael@0: sudo sh -c "cat >'/var/lib/chroot/${target}/usr/local/bin/$i'" <&/dev/null; then michael@0: tmp_script="/tmp/${script##*/}" michael@0: cp "${script}" "${tmp_script}" michael@0: fi michael@0: # Some distributions automatically start an instance of the system- michael@0: # wide dbus daemon, cron daemon or of the logging daemon, when michael@0: # installing the Chrome build depencies. This prevents the chroot michael@0: # session from being closed. So, we always try to shut down any running michael@0: # instance of dbus and rsyslog. michael@0: sudo /usr/local/bin/"${target%bit}" sh -c "${script} --no-lib32; michael@0: rc=$?; michael@0: /etc/init.d/cron stop >/dev/null 2>&1 || :; michael@0: /etc/init.d/rsyslog stop >/dev/null 2>&1 || :; michael@0: /etc/init.d/dbus stop >/dev/null 2>&1 || :; michael@0: exit $rc" michael@0: rc=$? michael@0: [ -n "${tmp_script}" ] && rm -f "${tmp_script}" michael@0: [ $rc -ne 0 ] && exit $rc michael@0: break michael@0: ;; michael@0: n|N) michael@0: break michael@0: ;; michael@0: esac michael@0: done michael@0: echo michael@0: fi michael@0: michael@0: # Check whether ~/chroot is on a (slow) network file system and offer to michael@0: # relocate it. Also offer relocation, if the user appears to have multiple michael@0: # spindles (as indicated by "${bind_mount}" being non-empty). michael@0: # We only offer this option, if it doesn't look as if a chroot environment michael@0: # is currently active. Otherwise, relocation is unlikely to work and it michael@0: # can be difficult for the user to recover from the failed attempt to relocate michael@0: # the ~/chroot directory. michael@0: # We don't aim to solve this problem for every configuration, michael@0: # but try to help with the common cases. For more advanced configuration michael@0: # options, the user can always manually adjust things. michael@0: mkdir -p "${HOME}/chroot/" michael@0: if [ ! -h "${HOME}/chroot" ] && michael@0: ! egrep -qs '^[^[:space:]]*/chroot' /etc/fstab && michael@0: { [ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] || michael@0: is_network_drive "${HOME}/chroot"; } && michael@0: ! egrep -qs '/var/lib/[^/]*chroot/.*/chroot' /proc/mounts; then michael@0: echo "${HOME}/chroot is currently located on the same device as your" michael@0: echo "home directory." michael@0: echo "This might not be what you want. Do you want me to move it somewhere" michael@0: echo "else?" michael@0: # If the computer has multiple spindles, many users configure all or part of michael@0: # the secondary hard disk to be writable by the primary user of this machine. michael@0: # Make some reasonable effort to detect this type of configuration and michael@0: # then offer a good location for where to put the ~/chroot directory. michael@0: suggest= michael@0: for i in $(echo "${bind_mounts}"|cut -d ' ' -f 1); do michael@0: if [ -d "$i" -a -w "$i" -a \( ! -a "$i/chroot" -o -w "$i/chroot/." \) ] && michael@0: ! is_network_drive "$i"; then michael@0: suggest="$i" michael@0: else michael@0: for j in "$i/"*; do michael@0: if [ -d "$j" -a -w "$j" -a \ michael@0: \( ! -a "$j/chroot" -o -w "$j/chroot/." \) ] && michael@0: ! is_network_drive "$j"; then michael@0: suggest="$j" michael@0: else michael@0: for k in "$j/"*; do michael@0: if [ -d "$k" -a -w "$k" -a \ michael@0: \( ! -a "$k/chroot" -o -w "$k/chroot/." \) ] && michael@0: ! is_network_drive "$k"; then michael@0: suggest="$k" michael@0: break michael@0: fi michael@0: done michael@0: fi michael@0: [ -n "${suggest}" ] && break michael@0: done michael@0: fi michael@0: [ -n "${suggest}" ] && break michael@0: done michael@0: def_suggest="${HOME}" michael@0: if [ -n "${suggest}" ]; then michael@0: # For home directories that reside on network drives, make our suggestion michael@0: # the default option. For home directories that reside on a local drive, michael@0: # require that the user manually enters the new location. michael@0: if is_network_drive "${HOME}"; then michael@0: def_suggest="${suggest}" michael@0: else michael@0: echo "A good location would probably be in \"${suggest}\"" michael@0: fi michael@0: fi michael@0: while :; do michael@0: printf "Physical location [${def_suggest}]: " michael@0: read dir michael@0: [ -z "${dir}" ] && dir="${def_suggest}" michael@0: [ "${dir%%/}" == "${HOME%%/}" ] && break michael@0: if ! [ -d "${dir}" -a -w "${dir}" ] || michael@0: [ -a "${dir}/chroot" -a ! -w "${dir}/chroot/." ]; then michael@0: echo "Cannot write to ${dir}/chroot. Please try again" michael@0: else michael@0: mv "${HOME}/chroot" "${dir}/chroot" michael@0: ln -s "${dir}/chroot" "${HOME}/chroot" michael@0: for i in $(list_all_chroots); do michael@0: sudo "$i" mkdir -p "${dir}/chroot" michael@0: done michael@0: sudo sed -i "s,${HOME}/chroot,${dir}/chroot,g" /etc/schroot/mount-* michael@0: break michael@0: fi michael@0: done michael@0: fi michael@0: michael@0: # Clean up package files michael@0: sudo schroot -c /usr/local/bin/"${target%bit}" -p -- apt-get clean michael@0: sudo apt-get clean michael@0: michael@0: trap '' INT TERM QUIT HUP michael@0: trap '' EXIT michael@0: michael@0: # Let the user know what we did michael@0: cat <