Home | History | Annotate | Download | only in build
      1 #!/bin/bash -e
      2 
      3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 # This script installs Debian-derived distributions in a chroot environment.
      8 # It can for example be used to have an accurate 32bit build and test
      9 # environment when otherwise working on a 64bit machine.
     10 # N. B. it is unlikely that this script will ever work on anything other than a
     11 # Debian-derived system.
     12 
     13 # Older Debian based systems had both "admin" and "adm" groups, with "admin"
     14 # apparently being used in more places. Newer distributions have standardized
     15 # on just the "adm" group. Check /etc/group for the preferred name of the
     16 # administrator group.
     17 admin=$(grep '^admin:' /etc/group >&/dev/null && echo admin || echo adm)
     18 
     19 usage() {
     20   echo "usage: ${0##*/} [-m mirror] [-g group,...] [-s] [-c]"
     21   echo "-b dir       additional directories that should be bind mounted,"
     22   echo '             or "NONE".'
     23   echo "             Default: if local filesystems present, ask user for help"
     24   echo "-g group,... groups that can use the chroot unauthenticated"
     25   echo "             Default: '${admin}' and current user's group ('$(id -gn)')"
     26   echo "-l           List all installed chroot environments"
     27   echo "-m mirror    an alternate repository mirror for package downloads"
     28   echo "-s           configure default deb-srcs"
     29   echo "-c           always copy 64bit helper binaries to 32bit chroot"
     30   echo "-h           this help message"
     31 }
     32 
     33 process_opts() {
     34   local OPTNAME OPTIND OPTERR OPTARG
     35   while getopts ":b:g:lm:sch" OPTNAME; do
     36     case "$OPTNAME" in
     37       b)
     38         if [ "${OPTARG}" = "NONE" -a -z "${bind_mounts}" ]; then
     39           bind_mounts="${OPTARG}"
     40         else
     41           if [ "${bind_mounts}" = "NONE" -o "${OPTARG}" = "${OPTARG#/}" -o \
     42                ! -d "${OPTARG}" ]; then
     43             echo "Invalid -b option(s)"
     44             usage
     45             exit 1
     46           fi
     47           bind_mounts="${bind_mounts}
     48 ${OPTARG} ${OPTARG} none rw,bind 0 0"
     49         fi
     50         ;;
     51       g)
     52         [ -n "${OPTARG}" ] &&
     53           chroot_groups="${chroot_groups}${chroot_groups:+,}${OPTARG}"
     54         ;;
     55       l)
     56         list_all_chroots
     57         exit
     58         ;;
     59       m)
     60         if [ -n "${mirror}" ]; then
     61           echo "You can only specify exactly one mirror location"
     62           usage
     63           exit 1
     64         fi
     65         mirror="$OPTARG"
     66         ;;
     67       s)
     68         add_srcs="y"
     69         ;;
     70       c)
     71         copy_64="y"
     72         ;;
     73       h)
     74         usage
     75         exit 0
     76         ;;
     77       \:)
     78         echo "'-$OPTARG' needs an argument."
     79         usage
     80         exit 1
     81         ;;
     82       *)
     83         echo "invalid command-line option: $OPTARG"
     84         usage
     85         exit 1
     86         ;;
     87     esac
     88   done
     89 
     90   if [ $# -ge ${OPTIND} ]; then
     91     eval echo "Unexpected command line argument: \${${OPTIND}}"
     92     usage
     93     exit 1
     94   fi
     95 }
     96 
     97 list_all_chroots() {
     98   for i in /var/lib/chroot/*; do
     99     i="${i##*/}"
    100     [ "${i}" = "*" ] && continue
    101     [ -x "/usr/local/bin/${i%bit}" ] || continue
    102     grep -qs "^\[${i%bit}\]\$" /etc/schroot/schroot.conf || continue
    103     [ -r "/etc/schroot/script-${i}" -a \
    104       -r "/etc/schroot/mount-${i}" ] || continue
    105     echo "${i%bit}"
    106   done
    107 }
    108 
    109 getkey() {
    110   (
    111     trap 'stty echo -iuclc icanon 2>/dev/null' EXIT INT TERM QUIT HUP
    112     stty -echo iuclc -icanon 2>/dev/null
    113     dd count=1 bs=1 2>/dev/null
    114   )
    115 }
    116 
    117 chr() {
    118   printf "\\$(printf '%03o' "$1")"
    119 }
    120 
    121 ord() {
    122   printf '%d' $(printf '%c' "$1" | od -tu1 -An)
    123 }
    124 
    125 is_network_drive() {
    126   stat -c %T -f "$1/" 2>/dev/null |
    127     egrep -qs '^nfs|cifs|smbfs'
    128 }
    129 
    130 # Check that we are running as a regular user
    131 [ "$(id -nu)" = root ] && {
    132   echo "Run this script as a regular user and provide your \"sudo\""           \
    133        "password if requested" >&2
    134   exit 1
    135 }
    136 
    137 process_opts "$@"
    138 
    139 echo "This script will help you through the process of installing a"
    140 echo "Debian or Ubuntu distribution in a chroot environment. You will"
    141 echo "have to provide your \"sudo\" password when requested."
    142 echo
    143 
    144 # Error handler
    145 trap 'exit 1' INT TERM QUIT HUP
    146 trap 'sudo apt-get clean; tput bel; echo; echo Failed' EXIT
    147 
    148 # Install any missing applications that this script relies on. If these packages
    149 # are already installed, don't force another "apt-get install". That would
    150 # prevent them from being auto-removed, if they ever become eligible for that.
    151 # And as this script only needs the packages once, there is no good reason to
    152 # introduce a hard dependency on things such as dchroot and debootstrap.
    153 dep=
    154 for i in dchroot debootstrap libwww-perl; do
    155   [ -d /usr/share/doc/"$i" ] || dep="$dep $i"
    156 done
    157 [ -n "$dep" ] && sudo apt-get -y install $dep
    158 sudo apt-get -y install schroot
    159 
    160 # Create directory for chroot
    161 sudo mkdir -p /var/lib/chroot
    162 
    163 # Find chroot environments that can be installed with debootstrap
    164 targets="$(cd /usr/share/debootstrap/scripts
    165            ls | grep '^[a-z]*$')"
    166 
    167 # Ask user to pick one of the available targets
    168 echo "The following targets are available to be installed in a chroot:"
    169 j=1; for i in $targets; do
    170   printf '%4d: %s\n' "$j" "$i"
    171   j=$(($j+1))
    172 done
    173 while :; do
    174   printf "Which target would you like to install: "
    175   read n
    176   [ "$n" -gt 0 -a "$n" -lt "$j" ] >&/dev/null && break
    177 done
    178 j=1; for i in $targets; do
    179   [ "$j" -eq "$n" ] && { distname="$i"; break; }
    180   j=$(($j+1))
    181 done
    182 echo
    183 
    184 # On x86-64, ask whether the user wants to install x86-32 or x86-64
    185 archflag=
    186 arch=
    187 if [ "$(uname -m)" = x86_64 ]; then
    188   while :; do
    189     echo "You are running a 64bit kernel. This allows you to install either a"
    190     printf "32bit or a 64bit chroot environment. %s"                           \
    191            "Which one do you want (32, 64) "
    192     read arch
    193     [ "${arch}" == 32 -o "${arch}" == 64 ] && break
    194   done
    195   [ "${arch}" == 32 ] && archflag="--arch i386" || archflag="--arch amd64"
    196   arch="${arch}bit"
    197   echo
    198 fi
    199 target="${distname}${arch}"
    200 
    201 # Don't accidentally overwrite an existing installation
    202 [ -d /var/lib/chroot/"${target}" ] && {
    203   while :; do
    204     echo "This chroot already exists on your machine."
    205     if schroot -l --all-sessions 2>&1 |
    206        sed 's/^session://' |
    207        grep -qs "^${target%bit}-"; then
    208       echo "And it appears to be in active use. Terminate all programs that"
    209       echo "are currently using the chroot environment and then re-run this"
    210       echo "script."
    211       echo "If you still get an error message, you might have stale mounts"
    212       echo "that you forgot to delete. You can always clean up mounts by"
    213       echo "executing \"${target%bit} -c\"."
    214       exit 1
    215     fi
    216     echo "I can abort installation, I can overwrite the existing chroot,"
    217     echo "or I can delete the old one and then exit. What would you like to"
    218     printf "do (a/o/d)? "
    219     read choice
    220     case "${choice}" in
    221       a|A) exit 1;;
    222       o|O) sudo rm -rf "/var/lib/chroot/${target}"; break;;
    223       d|D) sudo rm -rf "/var/lib/chroot/${target}"      \
    224                        "/usr/local/bin/${target%bit}"   \
    225                        "/etc/schroot/mount-${target}"   \
    226                        "/etc/schroot/script-${target}"
    227            sudo sed -ni '/^[[]'"${target%bit}"']$/,${
    228                          :1;n;/^[[]/b2;b1;:2;p;n;b2};p' \
    229                        "/etc/schroot/schroot.conf"
    230            trap '' INT TERM QUIT HUP
    231            trap '' EXIT
    232            echo "Deleted!"
    233            exit 0;;
    234     esac
    235   done
    236   echo
    237 }
    238 sudo mkdir -p /var/lib/chroot/"${target}"
    239 
    240 # Offer to include additional standard repositories for Ubuntu-based chroots.
    241 alt_repos=
    242 grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && {
    243   while :; do
    244     echo "Would you like to add ${distname}-updates and ${distname}-security "
    245     printf "to the chroot's sources.list (y/n)? "
    246     read alt_repos
    247     case "${alt_repos}" in
    248       y|Y)
    249         alt_repos="y"
    250         break
    251       ;;
    252       n|N)
    253         break
    254       ;;
    255     esac
    256   done
    257   echo
    258 }
    259 
    260 # Check for non-standard file system mount points and ask the user whether
    261 # they should be imported into the chroot environment
    262 # We limit to the first 26 mount points that much some basic heuristics,
    263 # because a) that allows us to enumerate choices with a single character,
    264 # and b) if we find more than 26 mount points, then these are probably
    265 # false-positives and something is very unusual about the system's
    266 # configuration. No need to spam the user with even more information that
    267 # is likely completely irrelevant.
    268 if [ -z "${bind_mounts}" ]; then
    269   mounts="$(awk '$2 != "/" && $2 !~ "^/boot" && $2 !~ "^/home" &&
    270                  $2 !~ "^/media" && $2 !~ "^/run" &&
    271                  ($3 ~ "ext[2-4]" || $3 == "reiserfs" || $3 == "btrfs" ||
    272                  $3 == "xfs" || $3 == "jfs" || $3 == "u?msdos" ||
    273                  $3 == "v?fat" || $3 == "hfs" || $3 == "ntfs" ||
    274                  $3 ~ "nfs[4-9]?" || $3 == "smbfs" || $3 == "cifs") {
    275                    print $2
    276                  }' /proc/mounts |
    277             head -n26)"
    278   if [ -n "${mounts}" ]; then
    279     echo "You appear to have non-standard mount points that you"
    280     echo "might want to import into the chroot environment:"
    281     echo
    282     sel=
    283     while :; do
    284       # Print a menu, listing all non-default mounts of local or network
    285       # file systems.
    286       j=1; for m in ${mounts}; do
    287         c="$(printf $(printf '\\%03o' $((64+$j))))"
    288         echo "$sel" | grep -qs $c &&
    289           state="mounted in chroot" || state="$(tput el)"
    290         printf "   $c) %-40s${state}\n" "$m"
    291         j=$(($j+1))
    292       done
    293       # Allow user to interactively (de-)select any of the entries
    294       echo
    295       printf "Select mount points that you want to be included or press %s" \
    296              "SPACE to continue"
    297       c="$(getkey | tr a-z A-Z)"
    298       [ "$c" == " " ] && { echo; echo; break; }
    299       if [ -z "$c" ] ||
    300          [ "$c" '<' 'A' -o $(ord "$c") -gt $((64 + $(ord "$j"))) ]; then
    301           # Invalid input, ring the console bell
    302           tput bel
    303       else
    304         # Toggle the selection for the given entry
    305         if echo "$sel" | grep -qs $c; then
    306           sel="$(printf "$sel" | sed "s/$c//")"
    307         else
    308           sel="$sel$c"
    309         fi
    310       fi
    311       # Reposition cursor to the top of the list of entries
    312       tput cuu $(($j + 1))
    313       echo
    314     done
    315   fi
    316   j=1; for m in ${mounts}; do
    317     c="$(chr $(($j + 64)))"
    318     if echo "$sel" | grep -qs $c; then
    319       bind_mounts="${bind_mounts}$m $m none rw,bind 0 0
    320 "
    321     fi
    322     j=$(($j+1))
    323   done
    324 fi
    325 
    326 # Remove stale entry from /etc/schroot/schroot.conf. Entries start
    327 # with the target name in square brackets, followed by an arbitrary
    328 # number of lines. The entry stops when either the end of file has
    329 # been reached, or when the beginning of a new target is encountered.
    330 # This means, we cannot easily match for a range of lines in
    331 # "sed". Instead, we actually have to iterate over each line and check
    332 # whether it is the beginning of a new entry.
    333 sudo sed -ni '/^[[]'"${target%bit}"']$/,${:1;n;/^[[]/b2;b1;:2;p;n;b2};p'       \
    334          /etc/schroot/schroot.conf
    335 
    336 # Download base system. This takes some time
    337 if [ -z "${mirror}" ]; then
    338  grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" &&
    339    mirror="http://archive.ubuntu.com/ubuntu" ||
    340    mirror="http://ftp.us.debian.org/debian"
    341 fi
    342 
    343 sudo ${http_proxy:+http_proxy="${http_proxy}"} debootstrap ${archflag} \
    344     "${distname}" "/var/lib/chroot/${target}"  "$mirror"
    345 
    346 # Add new entry to /etc/schroot/schroot.conf
    347 grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" &&
    348   brand="Ubuntu" || brand="Debian"
    349 if [ -z "${chroot_groups}" ]; then
    350   chroot_groups="${admin},$(id -gn)"
    351 fi
    352 # Older versions of schroot wanted a "priority=" line, whereas recent
    353 # versions deprecate "priority=" and warn if they see it. We don't have
    354 # a good feature test, but scanning for the string "priority=" in the
    355 # existing "schroot.conf" file is a good indication of what to do.
    356 priority=$(grep -qs 'priority=' /etc/schroot/schroot.conf &&
    357            echo 'priority=3' || :)
    358 sudo sh -c 'cat >>/etc/schroot/schroot.conf' <<EOF
    359 [${target%bit}]
    360 description=${brand} ${distname} ${arch}
    361 type=directory
    362 directory=/var/lib/chroot/${target}
    363 users=root
    364 groups=${chroot_groups}
    365 root-groups=${chroot_groups}
    366 personality=linux$([ "${arch}" != 64bit ] && echo 32)
    367 script-config=script-${target}
    368 ${priority}
    369 
    370 EOF
    371 
    372 # Set up a list of mount points that is specific to this
    373 # chroot environment.
    374 sed '/^FSTAB=/s,"[^"]*","/etc/schroot/mount-'"${target}"'",' \
    375          /etc/schroot/script-defaults |
    376   sudo sh -c 'cat >/etc/schroot/script-'"${target}"
    377 sed '\,^/home[/[:space:]],s/\([,[:space:]]\)bind[[:space:]]/\1rbind /' \
    378   /etc/schroot/mount-defaults |
    379   sudo sh -c 'cat > /etc/schroot/mount-'"${target}"
    380 
    381 # Add the extra mount points that the user told us about
    382 [ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] &&
    383   printf "${bind_mounts}" |
    384     sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
    385 
    386 # If this system has a "/media" mountpoint, import it into the chroot
    387 # environment. Most modern distributions use this mount point to
    388 # automatically mount devices such as CDROMs, USB sticks, etc...
    389 if [ -d /media ] &&
    390    ! grep -qs '^/media' /etc/schroot/mount-"${target}"; then
    391   echo '/media /media none rw,rbind 0 0' |
    392     sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
    393 fi
    394 
    395 # Share /dev/shm, /run and /run/shm.
    396 grep -qs '^/dev/shm' /etc/schroot/mount-"${target}" ||
    397   echo '/dev/shm /dev/shm none rw,bind 0 0' |
    398     sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
    399 if [ ! -d "/var/lib/chroot/${target}/run" ] &&
    400    ! grep -qs '^/run' /etc/schroot/mount-"${target}"; then
    401   echo '/run /run none rw,bind 0 0' |
    402     sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
    403 fi
    404 if ! grep -qs '^/run/shm' /etc/schroot/mount-"${target}"; then
    405   { [ -d /run ] && echo '/run/shm /run/shm none rw,bind 0 0' ||
    406                    echo '/dev/shm /run/shm none rw,bind 0 0'; } |
    407     sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
    408 fi
    409 
    410 # Set up a special directory that changes contents depending on the target
    411 # that is executing.
    412 d="$(readlink -f "${HOME}/chroot" 2>/dev/null || echo "${HOME}/chroot")"
    413 s="${d}/.${target}"
    414 echo "${s} ${d} none rw,bind 0 0" |
    415   sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
    416 mkdir -p "${s}"
    417 
    418 # Install a helper script to launch commands in the chroot
    419 sudo sh -c 'cat >/usr/local/bin/'"${target%bit}" <<'EOF'
    420 #!/bin/bash
    421 
    422 chroot="${0##*/}"
    423 
    424 wrap() {
    425   # Word-wrap the text passed-in on stdin. Optionally, on continuation lines
    426   # insert the same number of spaces as the number of characters in the
    427   # parameter(s) passed to this function.
    428   # If the "fold" program cannot be found, or if the actual width of the
    429   # terminal cannot be determined, this function doesn't attempt to do any
    430   # wrapping.
    431   local f="$(type -P fold)"
    432   [ -z "${f}" ] && { cat; return; }
    433   local c="$(stty -a </dev/tty 2>/dev/null |
    434              sed 's/.*columns[[:space:]]*\([0-9]*\).*/\1/;t;d')"
    435   [ -z "${c}" ] && { cat; return; }
    436   local i="$(echo "$*"|sed 's/./ /g')"
    437   local j="$(printf %s "${i}"|wc -c)"
    438   if [ "${c}" -gt "${j}" ]; then
    439     dd bs=1 count="${j}" 2>/dev/null
    440     "${f}" -sw "$((${c}-${j}))" | sed '2,$s/^/'"${i}"'/'
    441   else
    442     "${f}" -sw "${c}"
    443   fi
    444 }
    445 
    446 help() {
    447   echo "Usage ${0##*/} [-h|--help] [-c|--clean] [-C|--clean-all] [-l|--list] [--] args" | wrap "Usage ${0##*/} "
    448   echo "  help:      print this message"                                                | wrap "             "
    449   echo "  list:      list all known chroot environments"                                | wrap "             "
    450   echo "  clean:     remove all old chroot sessions for \"${chroot}\""                  | wrap "             "
    451   echo "  clean-all: remove all old chroot sessions for all environments"               | wrap "             "
    452   exit 0
    453 }
    454 
    455 clean() {
    456   local s t rc
    457   rc=0
    458   for s in $(schroot -l --all-sessions); do
    459     if [ -n "$1" ]; then
    460       t="${s#session:}"
    461       [ "${t#${chroot}-}" == "${t}" ] && continue
    462     fi
    463     if ls -l /proc/*/{cwd,fd} 2>/dev/null |
    464        fgrep -qs "/var/lib/schroot/mount/${t}"; then
    465       echo "Session \"${t}\" still has active users, not cleaning up" | wrap
    466       rc=1
    467       continue
    468     fi
    469     sudo schroot -c "${s}" -e || rc=1
    470   done
    471   exit ${rc}
    472 }
    473 
    474 list() {
    475   for e in $(schroot -l); do
    476     e="${e#chroot:}"
    477     [ -x "/usr/local/bin/${e}" ] || continue
    478     if schroot -l --all-sessions 2>/dev/null |
    479        sed 's/^session://' |
    480        grep -qs "^${e}-"; then
    481       echo "${e} is currently active"
    482     else
    483       echo "${e}"
    484     fi
    485   done
    486   exit 0
    487 }
    488 
    489 while [ "$#" -ne 0 ]; do
    490   case "$1" in
    491     --)             shift; break;;
    492     -h|--help)      shift; help;;
    493     -l|--list)      shift; list;;
    494     -c|--clean)     shift; clean "${chroot}";;
    495     -C|--clean-all) shift; clean;;
    496     *)              break;;
    497   esac
    498 done
    499 
    500 # Start a new chroot session and keep track of the session id. We inject this
    501 # id into all processes that run inside the chroot. Unless they go out of their
    502 # way to clear their environment, we can then later identify our child and
    503 # grand-child processes by scanning their environment.
    504 session="$(schroot -c "${chroot}" -b)"
    505 export CHROOT_SESSION_ID="${session}"
    506 
    507 if [ $# -eq 0 ]; then
    508   # Run an interactive shell session
    509   schroot -c "${session}" -r -p
    510 else
    511   # Run a command inside of the chroot environment
    512   p="$1"; shift
    513   schroot -c "${session}" -r -p "$p" -- "$@"
    514 fi
    515 rc=$?
    516 
    517 # Compute the inode of the root directory inside of the chroot environment.
    518 i=$(schroot -c "${session}" -r -p ls -- -id /proc/self/root/. |
    519      awk '{ print $1 }') 2>/dev/null
    520 other_pids=
    521 while [ -n "$i" ]; do
    522   # Identify processes by the inode number of their root directory. Then
    523   # remove all processes that we know belong to other sessions. We use
    524   # "sort | uniq -u" to do what amounts to a "set substraction operation".
    525   pids=$({ ls -id1 /proc/*/root/. 2>/dev/null |
    526          sed -e 's,^[^0-9]*'$i'.*/\([1-9][0-9]*\)/.*$,\1,
    527                  t
    528                  d';
    529          echo "${other_pids}";
    530          echo "${other_pids}"; } | sort | uniq -u) >/dev/null 2>&1
    531   # Kill all processes that are still left running in the session. This is
    532   # typically an assortment of daemon processes that were started
    533   # automatically. They result in us being unable to tear down the session
    534   # cleanly.
    535   [ -z "${pids}" ] && break
    536   for j in $pids; do
    537     # Unfortunately, the way that schroot sets up sessions has the
    538     # side-effect of being unable to tell one session apart from another.
    539     # This can result in us attempting to kill processes in other sessions.
    540     # We make a best-effort to avoid doing so.
    541     k="$( ( xargs -0 -n1 </proc/$j/environ ) 2>/dev/null |
    542          sed 's/^CHROOT_SESSION_ID=/x/;t1;d;:1;q')"
    543     if [ -n "${k}" -a "${k#x}" != "${session}" ]; then
    544       other_pids="${other_pids}
    545 ${j}"
    546       continue
    547     fi
    548     kill -9 $pids
    549   done
    550 done
    551 # End the chroot session. This should clean up all temporary files. But if we
    552 # earlier failed to terminate all (daemon) processes inside of the session,
    553 # deleting the session could fail. When that happens, the user has to manually
    554 # clean up the stale files by invoking us with "--clean" after having killed
    555 # all running processes.
    556 schroot -c "${session}" -e
    557 exit $rc
    558 EOF
    559 sudo chown root:root /usr/local/bin/"${target%bit}"
    560 sudo chmod 755 /usr/local/bin/"${target%bit}"
    561 
    562 # Add the standard Ubuntu update repositories if requested.
    563 [ "${alt_repos}" = "y" -a \
    564   -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
    565 sudo sed -i '/^deb .* [^ -]\+ main$/p
    566              s/^\(deb .* [^ -]\+\) main/\1-security main/
    567              p
    568              t1
    569              d
    570              :1;s/-security main/-updates main/
    571              t
    572              d' "/var/lib/chroot/${target}/etc/apt/sources.list"
    573 
    574 # Add a few more repositories to the chroot
    575 [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
    576 sudo sed -i 's/ main$/ main restricted universe multiverse/' \
    577          "/var/lib/chroot/${target}/etc/apt/sources.list"
    578 
    579 # Add the Ubuntu "partner" repository, if available
    580 if [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
    581    HEAD "http://archive.canonical.com/ubuntu/dists/${distname}/partner" \
    582    >&/dev/null; then
    583   sudo sh -c '
    584     echo "deb http://archive.canonical.com/ubuntu" \
    585          "'"${distname}"' partner" \
    586       >>"/var/lib/chroot/'"${target}"'/etc/apt/sources.list"'
    587 fi
    588 
    589 # Add source repositories, if the user requested we do so
    590 [ "${add_srcs}" = "y" -a \
    591   -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
    592 sudo sed -i '/^deb[^-]/p
    593              s/^deb\([^-]\)/deb-src\1/' \
    594          "/var/lib/chroot/${target}/etc/apt/sources.list"
    595 
    596 # Set apt proxy if host has set http_proxy
    597 if [ -n "${http_proxy}" ]; then
    598   sudo sh -c '
    599     echo "Acquire::http::proxy \"'"${http_proxy}"'\";" \
    600         >>"/var/lib/chroot/'"${target}"'/etc/apt/apt.conf"'
    601 fi
    602 
    603 # Update packages
    604 sudo "/usr/local/bin/${target%bit}" /bin/sh -c '
    605   apt-get update; apt-get -y dist-upgrade' || :
    606 
    607 # Install a couple of missing packages
    608 for i in debian-keyring ubuntu-keyring locales sudo; do
    609   [ -d "/var/lib/chroot/${target}/usr/share/doc/$i" ] ||
    610     sudo "/usr/local/bin/${target%bit}" apt-get -y install "$i" || :
    611 done
    612 
    613 # Configure locales
    614 sudo "/usr/local/bin/${target%bit}" /bin/sh -c '
    615   l='"${LANG:-en_US}"'; l="${l%%.*}"
    616   [ -r /etc/locale.gen ] &&
    617     sed -i "s/^# \($l\)/\1/" /etc/locale.gen
    618   locale-gen $LANG en_US en_US.UTF-8' || :
    619 
    620 # Enable multi-arch support, if available
    621 sudo "/usr/local/bin/${target%bit}" dpkg --assert-multi-arch >&/dev/null &&
    622   [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && {
    623   sudo sed -i 's/ / [arch=amd64,i386] /' \
    624               "/var/lib/chroot/${target}/etc/apt/sources.list"
    625   [ -d /var/lib/chroot/${target}/etc/dpkg/dpkg.cfg.d/ ] &&
    626   sudo "/usr/local/bin/${target%bit}" dpkg --add-architecture \
    627       $([ "${arch}" = "32bit" ] && echo amd64 || echo i386) >&/dev/null ||
    628     echo foreign-architecture \
    629         $([ "${arch}" = "32bit" ] && echo amd64 || echo i386) |
    630       sudo sh -c \
    631         "cat >'/var/lib/chroot/${target}/etc/dpkg/dpkg.cfg.d/multiarch'"
    632 }
    633 
    634 # Configure "sudo" package
    635 sudo "/usr/local/bin/${target%bit}" /bin/sh -c '
    636   egrep -qs '"'^$(id -nu) '"' /etc/sudoers ||
    637   echo '"'$(id -nu) ALL=(ALL) ALL'"' >>/etc/sudoers'
    638 
    639 # Install a few more commonly used packages
    640 sudo "/usr/local/bin/${target%bit}" apt-get -y install                         \
    641   autoconf automake1.9 dpkg-dev g++-multilib gcc-multilib gdb less libtool     \
    642   strace
    643 
    644 # If running a 32bit environment on a 64bit machine, install a few binaries
    645 # as 64bit. This is only done automatically if the chroot distro is the same as
    646 # the host, otherwise there might be incompatibilities in build settings or
    647 # runtime dependencies. The user can force it with the '-c' flag.
    648 host_distro=$(grep -s DISTRIB_CODENAME /etc/lsb-release | \
    649   cut -d "=" -f 2)
    650 if [ "${copy_64}" = "y" -o \
    651     "${host_distro}" = "${distname}" -a "${arch}" = 32bit ] && \
    652     file /bin/bash 2>/dev/null | grep -q x86-64; then
    653   readlinepkg=$(sudo "/usr/local/bin/${target%bit}" sh -c \
    654     'apt-cache search "lib64readline.\$" | sort | tail -n 1 | cut -d " " -f 1')
    655   sudo "/usr/local/bin/${target%bit}" apt-get -y install                       \
    656     lib64expat1 lib64ncurses5 ${readlinepkg} lib64z1
    657   dep=
    658   for i in binutils gdb; do
    659     [ -d /usr/share/doc/"$i" ] || dep="$dep $i"
    660   done
    661   [ -n "$dep" ] && sudo apt-get -y install $dep
    662   sudo mkdir -p "/var/lib/chroot/${target}/usr/local/lib/amd64"
    663   for i in libbfd libpython; do
    664     lib="$({ ldd /usr/bin/ld; ldd /usr/bin/gdb; } |
    665            grep -s "$i" | awk '{ print $3 }')"
    666     if [ -n "$lib" -a -r "$lib" ]; then
    667       sudo cp "$lib" "/var/lib/chroot/${target}/usr/local/lib/amd64"
    668     fi
    669   done
    670   for lib in libssl libcrypt; do
    671     for path in /usr/lib /usr/lib/x86_64-linux-gnu; do
    672       sudo cp $path/$lib* \
    673               "/var/lib/chroot/${target}/usr/local/lib/amd64/" >&/dev/null || :
    674     done
    675   done
    676   for i in gdb ld; do
    677     sudo cp /usr/bin/$i "/var/lib/chroot/${target}/usr/local/lib/amd64/"
    678     sudo sh -c "cat >'/var/lib/chroot/${target}/usr/local/bin/$i'" <<EOF
    679 #!/bin/sh
    680 exec /lib64/ld-linux-x86-64.so.2 --library-path /usr/local/lib/amd64 \
    681   /usr/local/lib/amd64/$i "\$@"
    682 EOF
    683     sudo chmod 755 "/var/lib/chroot/${target}/usr/local/bin/$i"
    684   done
    685 fi
    686 
    687 
    688 # If the install-build-deps.sh script can be found, offer to run it now
    689 script="$(dirname $(readlink -f "$0"))/install-build-deps.sh"
    690 if [ -x "${script}" ]; then
    691   while :; do
    692     echo
    693     echo "If you plan on building Chrome inside of the new chroot environment,"
    694     echo "you now have to install the build dependencies. Do you want me to"
    695     printf "start the script that does this for you (y/n)? "
    696     read install_deps
    697     case "${install_deps}" in
    698       y|Y)
    699         echo
    700         # We prefer running the script in-place, but this might not be
    701         # possible, if it lives on a network filesystem that denies
    702         # access to root.
    703         tmp_script=
    704         if ! sudo /usr/local/bin/"${target%bit}" \
    705             sh -c "[ -x '${script}' ]" >&/dev/null; then
    706           tmp_script="/tmp/${script##*/}"
    707           cp "${script}" "${tmp_script}"
    708         fi
    709         # Some distributions automatically start an instance of the system-
    710         # wide dbus daemon, cron daemon or of the logging daemon, when
    711         # installing the Chrome build depencies. This prevents the chroot
    712         # session from being closed.  So, we always try to shut down any running
    713         # instance of dbus and rsyslog.
    714         sudo /usr/local/bin/"${target%bit}" sh -c "${script} --no-lib32;
    715               rc=$?;
    716               /etc/init.d/cron stop >/dev/null 2>&1 || :;
    717               /etc/init.d/rsyslog stop >/dev/null 2>&1 || :;
    718               /etc/init.d/dbus stop >/dev/null 2>&1 || :;
    719               exit $rc"
    720         rc=$?
    721         [ -n "${tmp_script}" ] && rm -f "${tmp_script}"
    722         [ $rc -ne 0 ] && exit $rc
    723         break
    724       ;;
    725       n|N)
    726         break
    727       ;;
    728     esac
    729   done
    730   echo
    731 fi
    732 
    733 # Check whether ~/chroot is on a (slow) network file system and offer to
    734 # relocate it. Also offer relocation, if the user appears to have multiple
    735 # spindles (as indicated by "${bind_mount}" being non-empty).
    736 # We only offer this option, if it doesn't look as if a chroot environment
    737 # is currently active. Otherwise, relocation is unlikely to work and it
    738 # can be difficult for the user to recover from the failed attempt to relocate
    739 # the ~/chroot directory.
    740 # We don't aim to solve this problem for every configuration,
    741 # but try to help with the common cases. For more advanced configuration
    742 # options, the user can always manually adjust things.
    743 mkdir -p "${HOME}/chroot/"
    744 if [ ! -h "${HOME}/chroot" ] &&
    745    ! egrep -qs '^[^[:space:]]*/chroot' /etc/fstab &&
    746    { [ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] ||
    747      is_network_drive "${HOME}/chroot"; } &&
    748    ! egrep -qs '/var/lib/[^/]*chroot/.*/chroot' /proc/mounts; then
    749   echo "${HOME}/chroot is currently located on the same device as your"
    750   echo "home directory."
    751   echo "This might not be what you want. Do you want me to move it somewhere"
    752   echo "else?"
    753   # If the computer has multiple spindles, many users configure all or part of
    754   # the secondary hard disk to be writable by the primary user of this machine.
    755   # Make some reasonable effort to detect this type of configuration and
    756   # then offer a good location for where to put the ~/chroot directory.
    757   suggest=
    758   for i in $(echo "${bind_mounts}"|cut -d ' ' -f 1); do
    759     if [ -d "$i" -a -w "$i" -a \( ! -a "$i/chroot" -o -w "$i/chroot/." \) ] &&
    760        ! is_network_drive "$i"; then
    761       suggest="$i"
    762     else
    763       for j in "$i/"*; do
    764         if [ -d "$j" -a -w "$j" -a \
    765              \( ! -a "$j/chroot" -o -w "$j/chroot/." \) ] &&
    766            ! is_network_drive "$j"; then
    767           suggest="$j"
    768         else
    769           for k in "$j/"*; do
    770             if [ -d "$k" -a -w "$k" -a \
    771                  \( ! -a "$k/chroot" -o -w "$k/chroot/." \) ] &&
    772                ! is_network_drive "$k"; then
    773               suggest="$k"
    774               break
    775             fi
    776           done
    777         fi
    778         [ -n "${suggest}" ] && break
    779       done
    780     fi
    781     [ -n "${suggest}" ] && break
    782   done
    783   def_suggest="${HOME}"
    784   if [ -n "${suggest}" ]; then
    785     # For home directories that reside on network drives, make our suggestion
    786     # the default option. For home directories that reside on a local drive,
    787     # require that the user manually enters the new location.
    788     if is_network_drive "${HOME}"; then
    789       def_suggest="${suggest}"
    790     else
    791       echo "A good location would probably be in \"${suggest}\""
    792     fi
    793   fi
    794   while :; do
    795     printf "Physical location [${def_suggest}]: "
    796     read dir
    797     [ -z "${dir}" ] && dir="${def_suggest}"
    798     [ "${dir%%/}" == "${HOME%%/}" ] && break
    799     if ! [ -d "${dir}" -a -w "${dir}" ] ||
    800        [ -a "${dir}/chroot" -a ! -w "${dir}/chroot/." ]; then
    801       echo "Cannot write to ${dir}/chroot. Please try again"
    802     else
    803       mv "${HOME}/chroot" "${dir}/chroot"
    804       ln -s "${dir}/chroot" "${HOME}/chroot"
    805       for i in $(list_all_chroots); do
    806         sudo "$i" mkdir -p "${dir}/chroot"
    807       done
    808       sudo sed -i "s,${HOME}/chroot,${dir}/chroot,g" /etc/schroot/mount-*
    809       break
    810     fi
    811   done
    812 fi
    813 
    814 # Clean up package files
    815 sudo schroot -c "${target%bit}" -p -- apt-get clean
    816 sudo apt-get clean
    817 
    818 trap '' INT TERM QUIT HUP
    819 trap '' EXIT
    820 
    821 # Let the user know what we did
    822 cat <<EOF
    823 
    824 
    825 Successfully installed ${distname} ${arch}
    826 
    827 You can run programs inside of the chroot by invoking the
    828 "/usr/local/bin/${target%bit}" command.
    829 
    830 This command can be used with arguments, in order to just run a single
    831 program inside of the chroot environment (e.g. "${target%bit} make chrome")
    832 or without arguments, in order to run an interactive shell session inside
    833 of the chroot environment.
    834 
    835 If you need to run things as "root", you can use "sudo" (e.g. try
    836 "sudo ${target%bit} apt-get update").
    837 
    838 Your home directory is shared between the host and the chroot. But I
    839 configured "${HOME}/chroot" to be private to the chroot environment.
    840 You can use it for files that need to differ between environments. This
    841 would be a good place to store binaries that you have built from your
    842 source files.
    843 
    844 For Chrome, this probably means you want to make your "out" directory a
    845 symbolic link that points somewhere inside of "${HOME}/chroot".
    846 
    847 You still need to run "gclient runhooks" whenever you switch from building
    848 outside of the chroot to inside of the chroot. But you will find that you
    849 don't have to repeatedly erase and then completely rebuild all your object
    850 and binary files.
    851 
    852 EOF
    853