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