install-chroot.sh 28 KB

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