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