blob: b1376ffe8b881861c5fab5f5ed540a49eca20a3e [file] [log] [blame]
[email protected]47b16752010-08-31 05:01:151#!/bin/bash -e
2
Avi Drissman73a09d12022-09-08 20:33:383# Copyright 2012 The Chromium Authors
[email protected]47b16752010-08-31 05:01:154# 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
[email protected]757c2962012-03-15 19:05:1813# Older Debian based systems had both "admin" and "adm" groups, with "admin"
14# apparently being used in more places. Newer distributions have standardized
[email protected]d4da57be2014-07-16 17:53:2015# on just the "adm" group. Check /etc/group for the preferred name of the
[email protected]757c2962012-03-15 19:05:1816# administrator group.
17admin=$(grep '^admin:' /etc/group >&/dev/null && echo admin || echo adm)
18
[email protected]04aea5a2010-10-15 00:40:3919usage() {
[email protected]8eb22e92010-10-20 03:32:1720 echo "usage: ${0##*/} [-m mirror] [-g group,...] [-s] [-c]"
[email protected]757c2962012-03-15 19:05:1821 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"
[email protected]6a617002010-10-15 20:56:2224 echo "-g group,... groups that can use the chroot unauthenticated"
[email protected]757c2962012-03-15 19:05:1825 echo " Default: '${admin}' and current user's group ('$(id -gn)')"
26 echo "-l List all installed chroot environments"
[email protected]6a617002010-10-15 20:56:2227 echo "-m mirror an alternate repository mirror for package downloads"
[email protected]8eb22e92010-10-20 03:32:1728 echo "-s configure default deb-srcs"
29 echo "-c always copy 64bit helper binaries to 32bit chroot"
[email protected]6a617002010-10-15 20:56:2230 echo "-h this help message"
[email protected]04aea5a2010-10-15 00:40:3931}
32
33process_opts() {
34 local OPTNAME OPTIND OPTERR OPTARG
[email protected]757c2962012-03-15 19:05:1835 while getopts ":b:g:lm:sch" OPTNAME; do
[email protected]04aea5a2010-10-15 00:40:3936 case "$OPTNAME" in
[email protected]757c2962012-03-15 19:05:1837 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 ;;
[email protected]6a617002010-10-15 20:56:2251 g)
52 [ -n "${OPTARG}" ] &&
53 chroot_groups="${chroot_groups}${chroot_groups:+,}${OPTARG}"
54 ;;
[email protected]757c2962012-03-15 19:05:1855 l)
56 list_all_chroots
57 exit
58 ;;
[email protected]04aea5a2010-10-15 00:40:3959 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 ;;
[email protected]8eb22e92010-10-20 03:32:1767 s)
68 add_srcs="y"
69 ;;
70 c)
71 copy_64="y"
72 ;;
[email protected]04aea5a2010-10-15 00:40:3973 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
[email protected]757c2962012-03-15 19:05:1897list_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}
[email protected]04aea5a2010-10-15 00:40:39129
[email protected]47b16752010-08-31 05:01:15130# 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}
[email protected]47b16752010-08-31 05:01:15136
[email protected]04aea5a2010-10-15 00:40:39137process_opts "$@"
138
[email protected]757c2962012-03-15 19:05:18139echo "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
[email protected]47b16752010-08-31 05:01:15144# Error handler
[email protected]757c2962012-03-15 19:05:18145trap 'exit 1' INT TERM QUIT HUP
[email protected]47b16752010-08-31 05:01:15146trap '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=
[email protected]757c2962012-03-15 19:05:18154for i in dchroot debootstrap libwww-perl; do
[email protected]47b16752010-08-31 05:01:15155 [ -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
[email protected]757c2962012-03-15 19:05:18182echo
[email protected]47b16752010-08-31 05:01:15183
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"
[email protected]757c2962012-03-15 19:05:18197 echo
[email protected]47b16752010-08-31 05:01:15198fi
199target="${distname}${arch}"
200
[email protected]757c2962012-03-15 19:05:18201# Don't accidentally overwrite an existing installation
[email protected]47b16752010-08-31 05:01:15202[ -d /var/lib/chroot/"${target}" ] && {
[email protected]757c2962012-03-15 19:05:18203 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;;
[email protected]b11411c2012-03-21 22:09:41223 d|D) sudo rm -rf "/var/lib/chroot/${target}" \
224 "/usr/local/bin/${target%bit}" \
225 "/etc/schroot/mount-${target}" \
sbcfdfaf8f2015-06-29 18:18:17226 "/etc/schroot/script-${target}" \
227 "/etc/schroot/${target}"
[email protected]b11411c2012-03-21 22:09:41228 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;;
[email protected]757c2962012-03-15 19:05:18235 esac
236 done
237 echo
[email protected]47b16752010-08-31 05:01:15238}
239sudo mkdir -p /var/lib/chroot/"${target}"
240
[email protected]04aea5a2010-10-15 00:40:39241# Offer to include additional standard repositories for Ubuntu-based chroots.
242alt_repos=
[email protected]757c2962012-03-15 19:05:18243grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && {
[email protected]04aea5a2010-10-15 00:40:39244 while :; do
245 echo "Would you like to add ${distname}-updates and ${distname}-security "
[email protected]757c2962012-03-15 19:05:18246 printf "to the chroot's sources.list (y/n)? "
[email protected]04aea5a2010-10-15 00:40:39247 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
[email protected]757c2962012-03-15 19:05:18258 echo
[email protected]04aea5a2010-10-15 00:40:39259}
260
[email protected]757c2962012-03-15 19:05:18261# 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
[email protected]47b16752010-08-31 05:01:15327# 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
[email protected]04aea5a2010-10-15 00:40:39338if [ -z "${mirror}" ]; then
[email protected]757c2962012-03-15 19:05:18339 grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" &&
[email protected]04aea5a2010-10-15 00:40:39340 mirror="http://archive.ubuntu.com/ubuntu" ||
341 mirror="http://ftp.us.debian.org/debian"
342fi
[email protected]bb2c63a2012-05-09 18:31:56343
344sudo ${http_proxy:+http_proxy="${http_proxy}"} debootstrap ${archflag} \
345 "${distname}" "/var/lib/chroot/${target}" "$mirror"
[email protected]47b16752010-08-31 05:01:15346
347# Add new entry to /etc/schroot/schroot.conf
[email protected]757c2962012-03-15 19:05:18348grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" &&
[email protected]47b16752010-08-31 05:01:15349 brand="Ubuntu" || brand="Debian"
[email protected]6a617002010-10-15 20:56:22350if [ -z "${chroot_groups}" ]; then
[email protected]757c2962012-03-15 19:05:18351 chroot_groups="${admin},$(id -gn)"
[email protected]6a617002010-10-15 20:56:22352fi
sbcfdfaf8f2015-06-29 18:18:17353
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 &&
[email protected]757c2962012-03-15 19:05:18386 echo 'priority=3' || :)
sbcfdfaf8f2015-06-29 18:18:17387 sudo sh -c 'cat >>/etc/schroot/schroot.conf' <<EOF
[email protected]47b16752010-08-31 05:01:15388[${target%bit}]
389description=${brand} ${distname} ${arch}
390type=directory
391directory=/var/lib/chroot/${target}
[email protected]47b16752010-08-31 05:01:15392users=root
[email protected]6a617002010-10-15 20:56:22393groups=${chroot_groups}
394root-groups=${chroot_groups}
[email protected]47b16752010-08-31 05:01:15395personality=linux$([ "${arch}" != 64bit ] && echo 32)
396script-config=script-${target}
[email protected]757c2962012-03-15 19:05:18397${priority}
[email protected]47b16752010-08-31 05:01:15398
399EOF
400
sbcfdfaf8f2015-06-29 18:18:17401 # 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
[email protected]757c2962012-03-15 19:05:18410
411# Add the extra mount points that the user told us about
412[ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] &&
413 printf "${bind_mounts}" |
sbcfdfaf8f2015-06-29 18:18:17414 sudo sh -c 'cat >>'"${fstab}"
[email protected]757c2962012-03-15 19:05:18415
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 ] &&
sbcfdfaf8f2015-06-29 18:18:17420 ! grep -qs '^/media' "${fstab}"; then
[email protected]757c2962012-03-15 19:05:18421 echo '/media /media none rw,rbind 0 0' |
sbcfdfaf8f2015-06-29 18:18:17422 sudo sh -c 'cat >>'"${fstab}"
[email protected]757c2962012-03-15 19:05:18423fi
424
[email protected]d0debcc2012-12-21 02:58:34425# Share /dev/shm, /run and /run/shm.
sbcfdfaf8f2015-06-29 18:18:17426grep -qs '^/dev/shm' "${fstab}" ||
[email protected]757c2962012-03-15 19:05:18427 echo '/dev/shm /dev/shm none rw,bind 0 0' |
sbcfdfaf8f2015-06-29 18:18:17428 sudo sh -c 'cat >>'"${fstab}"
[email protected]d0debcc2012-12-21 02:58:34429if [ ! -d "/var/lib/chroot/${target}/run" ] &&
sbcfdfaf8f2015-06-29 18:18:17430 ! grep -qs '^/run' "${fstab}"; then
[email protected]d0debcc2012-12-21 02:58:34431 echo '/run /run none rw,bind 0 0' |
sbcfdfaf8f2015-06-29 18:18:17432 sudo sh -c 'cat >>'"${fstab}"
[email protected]d0debcc2012-12-21 02:58:34433fi
sbcfdfaf8f2015-06-29 18:18:17434if ! grep -qs '^/run/shm' "${fstab}"; then
[email protected]757c2962012-03-15 19:05:18435 { [ -d /run ] && echo '/run/shm /run/shm none rw,bind 0 0' ||
436 echo '/dev/shm /run/shm none rw,bind 0 0'; } |
sbcfdfaf8f2015-06-29 18:18:17437 sudo sh -c 'cat >>'"${fstab}"
[email protected]757c2962012-03-15 19:05:18438fi
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" |
sbcfdfaf8f2015-06-29 18:18:17445 sudo sh -c 'cat >>'"${target}"
[email protected]757c2962012-03-15 19:05:18446mkdir -p "${s}"
[email protected]47b16752010-08-31 05:01:15447
448# Install a helper script to launch commands in the chroot
[email protected]757c2962012-03-15 19:05:18449sudo sh -c 'cat >/usr/local/bin/'"${target%bit}" <<'EOF'
[email protected]47b16752010-08-31 05:01:15450#!/bin/bash
[email protected]757c2962012-03-15 19:05:18451
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
[email protected]b11411c2012-03-21 22:09:41499 sudo schroot -c "${s}" -e || rc=1
[email protected]757c2962012-03-15 19:05:18500 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
[email protected]6538c872013-07-23 05:36:07530# 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.
[email protected]757c2962012-03-15 19:05:18534session="$(schroot -c "${chroot}" -b)"
[email protected]6538c872013-07-23 05:36:07535export CHROOT_SESSION_ID="${session}"
[email protected]757c2962012-03-15 19:05:18536
537if [ $# -eq 0 ]; then
[email protected]6538c872013-07-23 05:36:07538 # Run an interactive shell session
[email protected]757c2962012-03-15 19:05:18539 schroot -c "${session}" -r -p
[email protected]47b16752010-08-31 05:01:15540else
[email protected]6538c872013-07-23 05:36:07541 # Run a command inside of the chroot environment
[email protected]757c2962012-03-15 19:05:18542 p="$1"; shift
543 schroot -c "${session}" -r -p "$p" -- "$@"
[email protected]47b16752010-08-31 05:01:15544fi
[email protected]757c2962012-03-15 19:05:18545rc=$?
546
[email protected]6538c872013-07-23 05:36:07547# Compute the inode of the root directory inside of the chroot environment.
[email protected]757c2962012-03-15 19:05:18548i=$(schroot -c "${session}" -r -p ls -- -id /proc/self/root/. |
549 awk '{ print $1 }') 2>/dev/null
[email protected]6538c872013-07-23 05:36:07550other_pids=
[email protected]757c2962012-03-15 19:05:18551while [ -n "$i" ]; do
[email protected]6538c872013-07-23 05:36:07552 # Identify processes by the inode number of their root directory. Then
553 # remove all processes that we know belong to other sessions. We use
Julien Brianceau96dfe4d82017-08-01 09:03:13554 # "sort | uniq -u" to do what amounts to a "set subtraction operation".
[email protected]6538c872013-07-23 05:36:07555 pids=$({ ls -id1 /proc/*/root/. 2>/dev/null |
[email protected]757c2962012-03-15 19:05:18556 sed -e 's,^[^0-9]*'$i'.*/\([1-9][0-9]*\)/.*$,\1,
557 t
[email protected]6538c872013-07-23 05:36:07558 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
[email protected]757c2962012-03-15 19:05:18580done
[email protected]6538c872013-07-23 05:36:07581# 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.
[email protected]757c2962012-03-15 19:05:18586schroot -c "${session}" -e
587exit $rc
[email protected]47b16752010-08-31 05:01:15588EOF
589sudo chown root:root /usr/local/bin/"${target%bit}"
590sudo chmod 755 /usr/local/bin/"${target%bit}"
591
[email protected]04aea5a2010-10-15 00:40:39592# Add the standard Ubuntu update repositories if requested.
[email protected]8eb22e92010-10-20 03:32:17593[ "${alt_repos}" = "y" -a \
594 -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
[email protected]04aea5a2010-10-15 00:40:39595sudo 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
[email protected]47b16752010-08-31 05:01:15604# Add a few more repositories to the chroot
[email protected]757c2962012-03-15 19:05:18605[ -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
[email protected]8eb22e92010-10-20 03:32:17620[ "${add_srcs}" = "y" -a \
621 -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
[email protected]757c2962012-03-15 19:05:18622sudo sed -i '/^deb[^-]/p
623 s/^deb\([^-]\)/deb-src\1/' \
624 "/var/lib/chroot/${target}/etc/apt/sources.list"
[email protected]47b16752010-08-31 05:01:15625
[email protected]bb2c63a2012-05-09 18:31:56626# 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
[email protected]47b16752010-08-31 05:01:15633# Update packages
[email protected]757c2962012-03-15 19:05:18634sudo "/usr/local/bin/${target%bit}" /bin/sh -c '
[email protected]47b16752010-08-31 05:01:15635 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" ] ||
[email protected]757c2962012-03-15 19:05:18640 sudo "/usr/local/bin/${target%bit}" apt-get -y install "$i" || :
[email protected]47b16752010-08-31 05:01:15641done
642
643# Configure locales
[email protected]757c2962012-03-15 19:05:18644sudo "/usr/local/bin/${target%bit}" /bin/sh -c '
[email protected]47b16752010-08-31 05:01:15645 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
[email protected]757c2962012-03-15 19:05:18650# 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/ ] &&
[email protected]e9216772012-12-22 00:51:00656 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'"
[email protected]757c2962012-03-15 19:05:18662}
663
[email protected]47b16752010-08-31 05:01:15664# Configure "sudo" package
[email protected]757c2962012-03-15 19:05:18665sudo "/usr/local/bin/${target%bit}" /bin/sh -c '
666 egrep -qs '"'^$(id -nu) '"' /etc/sudoers ||
[email protected]47b16752010-08-31 05:01:15667 echo '"'$(id -nu) ALL=(ALL) ALL'"' >>/etc/sudoers'
668
669# Install a few more commonly used packages
[email protected]757c2962012-03-15 19:05:18670sudo "/usr/local/bin/${target%bit}" apt-get -y install \
[email protected]47b16752010-08-31 05:01:15671 autoconf automake1.9 dpkg-dev g++-multilib gcc-multilib gdb less libtool \
yyanagisawa9061716a2014-10-08 06:25:40672 lsof strace
[email protected]47b16752010-08-31 05:01:15673
674# If running a 32bit environment on a 64bit machine, install a few binaries
[email protected]8eb22e92010-10-20 03:32:17675# 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.
[email protected]757c2962012-03-15 19:05:18678host_distro=$(grep -s DISTRIB_CODENAME /etc/lsb-release | \
[email protected]8eb22e92010-10-20 03:32:17679 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
[email protected]757c2962012-03-15 19:05:18683 readlinepkg=$(sudo "/usr/local/bin/${target%bit}" sh -c \
[email protected]42c58a42010-10-15 15:18:16684 'apt-cache search "lib64readline.\$" | sort | tail -n 1 | cut -d " " -f 1')
[email protected]757c2962012-03-15 19:05:18685 sudo "/usr/local/bin/${target%bit}" apt-get -y install \
yyanagisawadc73b4a2014-10-17 08:45:49686 lib64expat1 lib64ncurses5 ${readlinepkg} lib64z1 lib64stdc++6
[email protected]47b16752010-08-31 05:01:15687 dep=
[email protected]757c2962012-03-15 19:05:18688 for i in binutils gdb; do
[email protected]47b16752010-08-31 05:01:15689 [ -d /usr/share/doc/"$i" ] || dep="$dep $i"
690 done
691 [ -n "$dep" ] && sudo apt-get -y install $dep
[email protected]757c2962012-03-15 19:05:18692 sudo mkdir -p "/var/lib/chroot/${target}/usr/local/lib/amd64"
[email protected]47b16752010-08-31 05:01:15693 for i in libbfd libpython; do
694 lib="$({ ldd /usr/bin/ld; ldd /usr/bin/gdb; } |
[email protected]757c2962012-03-15 19:05:18695 grep -s "$i" | awk '{ print $3 }')"
[email protected]47b16752010-08-31 05:01:15696 if [ -n "$lib" -a -r "$lib" ]; then
[email protected]757c2962012-03-15 19:05:18697 sudo cp "$lib" "/var/lib/chroot/${target}/usr/local/lib/amd64"
[email protected]47b16752010-08-31 05:01:15698 fi
699 done
700 for lib in libssl libcrypt; do
[email protected]757c2962012-03-15 19:05:18701 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=
[email protected]d8185032012-10-17 16:47:59734 if ! sudo /usr/local/bin/"${target%bit}" \
735 sh -c "[ -x '${script}' ]" >&/dev/null; then
[email protected]757c2962012-03-15 19:05:18736 tmp_script="/tmp/${script##*/}"
737 cp "${script}" "${tmp_script}"
738 fi
739 # Some distributions automatically start an instance of the system-
[email protected]8a8b66f12012-10-17 16:45:29740 # 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.
sbcfdfaf8f2015-06-29 18:18:17744 sudo /usr/local/bin/"${target%bit}" sh -c "${script};
[email protected]757c2962012-03-15 19:05:18745 rc=$?;
[email protected]8a8b66f12012-10-17 16:45:29746 /etc/init.d/cron stop >/dev/null 2>&1 || :;
[email protected]b11411c2012-03-21 22:09:41747 /etc/init.d/rsyslog stop >/dev/null 2>&1 || :;
[email protected]757c2962012-03-15 19:05:18748 /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
[email protected]47b16752010-08-31 05:01:15841 done
842fi
843
844# Clean up package files
[email protected]ced8fddb2012-12-05 03:35:56845sudo schroot -c "${target%bit}" -p -- apt-get clean
[email protected]47b16752010-08-31 05:01:15846sudo apt-get clean
847
[email protected]757c2962012-03-15 19:05:18848trap '' INT TERM QUIT HUP
[email protected]47b16752010-08-31 05:01:15849trap '' EXIT
[email protected]757c2962012-03-15 19:05:18850
851# Let the user know what we did
[email protected]47b16752010-08-31 05:01:15852cat <<EOF
853
854
855Successfully installed ${distname} ${arch}
856
[email protected]d8185032012-10-17 16:47:59857You can run programs inside of the chroot by invoking the
858"/usr/local/bin/${target%bit}" command.
[email protected]47b16752010-08-31 05:01:15859
[email protected]757c2962012-03-15 19:05:18860This 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
[email protected]47b16752010-08-31 05:01:15882EOF