• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #!/bin/bash -e
2 
3 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6 
7 # This script installs Debian-derived distributions in a chroot environment.
8 # It can for example be used to have an accurate 32bit build and test
9 # environment when otherwise working on a 64bit machine.
10 # N. B. it is unlikely that this script will ever work on anything other than a
11 # Debian-derived system.
12 
13 usage() {
14   echo "usage: ${0##*/} [-m mirror] [-g group,...] [-s] [-c]"
15   echo "-g group,... groups that can use the chroot unauthenticated"
16   echo "             Default: 'admin' and current user's group ('$(id -gn)')"
17   echo "-m mirror    an alternate repository mirror for package downloads"
18   echo "-s           configure default deb-srcs"
19   echo "-c           always copy 64bit helper binaries to 32bit chroot"
20   echo "-h           this help message"
21 }
22 
23 process_opts() {
24   local OPTNAME OPTIND OPTERR OPTARG
25   while getopts ":g:m:sch" OPTNAME; do
26     case "$OPTNAME" in
27       g)
28         [ -n "${OPTARG}" ] &&
29           chroot_groups="${chroot_groups}${chroot_groups:+,}${OPTARG}"
30         ;;
31       m)
32         if [ -n "${mirror}" ]; then
33           echo "You can only specify exactly one mirror location"
34           usage
35           exit 1
36         fi
37         mirror="$OPTARG"
38         ;;
39       s)
40         add_srcs="y"
41         ;;
42       c)
43         copy_64="y"
44         ;;
45       h)
46         usage
47         exit 0
48         ;;
49       \:)
50         echo "'-$OPTARG' needs an argument."
51         usage
52         exit 1
53         ;;
54       *)
55         echo "invalid command-line option: $OPTARG"
56         usage
57         exit 1
58         ;;
59     esac
60   done
61 
62   if [ $# -ge ${OPTIND} ]; then
63     eval echo "Unexpected command line argument: \${${OPTIND}}"
64     usage
65     exit 1
66   fi
67 }
68 
69 
70 # Check that we are running as a regular user
71 [ "$(id -nu)" = root ] && {
72   echo "Run this script as a regular user and provide your \"sudo\""           \
73        "password if requested" >&2
74   exit 1
75 }
76 mkdir -p "$HOME/chroot/"
77 
78 process_opts "$@"
79 
80 # Error handler
81 trap 'exit 1' INT TERM QUIT
82 trap 'sudo apt-get clean; tput bel; echo; echo Failed' EXIT
83 
84 # Install any missing applications that this script relies on. If these packages
85 # are already installed, don't force another "apt-get install". That would
86 # prevent them from being auto-removed, if they ever become eligible for that.
87 # And as this script only needs the packages once, there is no good reason to
88 # introduce a hard dependency on things such as dchroot and debootstrap.
89 dep=
90 for i in dchroot debootstrap; do
91   [ -d /usr/share/doc/"$i" ] || dep="$dep $i"
92 done
93 [ -n "$dep" ] && sudo apt-get -y install $dep
94 sudo apt-get -y install schroot
95 
96 # Create directory for chroot
97 sudo mkdir -p /var/lib/chroot
98 
99 # Find chroot environments that can be installed with debootstrap
100 targets="$(cd /usr/share/debootstrap/scripts
101            ls | grep '^[a-z]*$')"
102 
103 # Ask user to pick one of the available targets
104 echo "The following targets are available to be installed in a chroot:"
105 j=1; for i in $targets; do
106   printf '%4d: %s\n' "$j" "$i"
107   j=$(($j+1))
108 done
109 while :; do
110   printf "Which target would you like to install: "
111   read n
112   [ "$n" -gt 0 -a "$n" -lt "$j" ] >&/dev/null && break
113 done
114 j=1; for i in $targets; do
115   [ "$j" -eq "$n" ] && { distname="$i"; break; }
116   j=$(($j+1))
117 done
118 
119 # On x86-64, ask whether the user wants to install x86-32 or x86-64
120 archflag=
121 arch=
122 if [ "$(uname -m)" = x86_64 ]; then
123   while :; do
124     echo "You are running a 64bit kernel. This allows you to install either a"
125     printf "32bit or a 64bit chroot environment. %s"                           \
126            "Which one do you want (32, 64) "
127     read arch
128     [ "${arch}" == 32 -o "${arch}" == 64 ] && break
129   done
130   [ "${arch}" == 32 ] && archflag="--arch i386" || archflag="--arch amd64"
131   arch="${arch}bit"
132 fi
133 target="${distname}${arch}"
134 
135 # Don't overwrite an existing installation
136 [ -d /var/lib/chroot/"${target}" ] && {
137   echo "This chroot already exists on your machine." >&2
138   echo "Delete /var/lib/chroot/${target} if you want to start over." >&2
139   exit 1
140 }
141 sudo mkdir -p /var/lib/chroot/"${target}"
142 
143 # Offer to include additional standard repositories for Ubuntu-based chroots.
144 alt_repos=
145 grep ubuntu.com /usr/share/debootstrap/scripts/"${distname}" >&/dev/null && {
146   while :; do
147     echo "Would you like to add ${distname}-updates and ${distname}-security "
148     echo -n "to the chroot's sources.list (y/n)? "
149     read alt_repos
150     case "${alt_repos}" in
151       y|Y)
152         alt_repos="y"
153         break
154       ;;
155       n|N)
156         break
157       ;;
158     esac
159   done
160 }
161 
162 # Remove stale entry from /etc/schroot/schroot.conf. Entries start
163 # with the target name in square brackets, followed by an arbitrary
164 # number of lines. The entry stops when either the end of file has
165 # been reached, or when the beginning of a new target is encountered.
166 # This means, we cannot easily match for a range of lines in
167 # "sed". Instead, we actually have to iterate over each line and check
168 # whether it is the beginning of a new entry.
169 sudo sed -ni '/^[[]'"${target%bit}"']$/,${:1;n;/^[[]/b2;b1;:2;p;n;b2};p'       \
170          /etc/schroot/schroot.conf
171 
172 # Download base system. This takes some time
173 if [ -z "${mirror}" ]; then
174  grep ubuntu.com /usr/share/debootstrap/scripts/"${distname}" >&/dev/null &&
175    mirror="http://archive.ubuntu.com/ubuntu" ||
176    mirror="http://ftp.us.debian.org/debian"
177 fi
178  sudo debootstrap ${archflag} "${distname}" /var/lib/chroot/"${target}"        \
179                   "$mirror"
180 
181 # Add new entry to /etc/schroot/schroot.conf
182 grep ubuntu.com /usr/share/debootstrap/scripts/"${distname}" >&/dev/null &&
183   brand="Ubuntu" || brand="Debian"
184 if [ -z "${chroot_groups}" ]; then
185   chroot_groups="admin,$(id -gn)"
186 fi
187 sudo sh -c 'cat >>/etc/schroot/schroot.conf' <<EOF
188 [${target%bit}]
189 description=${brand} ${distname} ${arch}
190 type=directory
191 directory=/var/lib/chroot/${target}
192 priority=3
193 users=root
194 groups=${chroot_groups}
195 root-groups=${chroot_groups}
196 personality=linux$([ "${arch}" != 64bit ] && echo 32)
197 script-config=script-${target}
198 
199 EOF
200 
201 # Set up a special directory that changes contents depending on the target
202 # that is executing.
203 sed '/^FSTAB=/s,/mount-defaults",/mount-'"${target}"'",'                       \
204          /etc/schroot/script-defaults |
205   sudo sh -c 'cat >/etc/schroot/script-'"${target}"
206 sudo cp /etc/schroot/mount-defaults /etc/schroot/mount-"${target}"
207 echo "$HOME/chroot/.${target} $HOME/chroot none rw,bind 0 0" |
208   sudo sh -c 'cat >>/etc/schroot/mount-'"${target}"
209 mkdir -p "$HOME/chroot/.${target}"
210 
211 # Install a helper script to launch commands in the chroot
212 sudo sh -c 'cat >/usr/local/bin/'"${target%bit}" <<EOF
213 #!/bin/bash
214 if [ \$# -eq 0 ]; then
215   exec schroot -c ${target%bit} -p
216 else
217   p="\$1"; shift
218   exec schroot -c ${target%bit} -p "\$p" -- "\$@"
219 fi
220 exit 1
221 EOF
222 sudo chown root:root /usr/local/bin/"${target%bit}"
223 sudo chmod 755 /usr/local/bin/"${target%bit}"
224 
225 # Add the standard Ubuntu update repositories if requested.
226 [ "${alt_repos}" = "y" -a \
227   -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
228 sudo sed -i '/^deb .* [^ -]\+ main$/p
229              s/^\(deb .* [^ -]\+\) main/\1-security main/
230              p
231              t1
232              d
233              :1;s/-security main/-updates main/
234              t
235              d' "/var/lib/chroot/${target}/etc/apt/sources.list"
236 
237 # Add a few more repositories to the chroot
238 [ "${add_srcs}" = "y" -a \
239   -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] &&
240 sudo sed -i 's/ main$/ main restricted universe multiverse/
241              p
242              t1
243              d
244           :1;s/^deb/deb-src/
245              t
246              d' "/var/lib/chroot/${target}/etc/apt/sources.list"
247 
248 # Update packages
249 sudo schroot -c "${target%bit}" -p -- /bin/sh -c '
250   apt-get update; apt-get -y dist-upgrade' || :
251 
252 # Install a couple of missing packages
253 for i in debian-keyring ubuntu-keyring locales sudo; do
254   [ -d "/var/lib/chroot/${target}/usr/share/doc/$i" ] ||
255     sudo schroot -c "${target%bit}" -p -- apt-get -y install "$i" || :
256 done
257 
258 # Configure locales
259 sudo schroot -c "${target%bit}" -p -- /bin/sh -c '
260   l='"${LANG:-en_US}"'; l="${l%%.*}"
261   [ -r /etc/locale.gen ] &&
262     sed -i "s/^# \($l\)/\1/" /etc/locale.gen
263   locale-gen $LANG en_US en_US.UTF-8' || :
264 
265 # Configure "sudo" package
266 sudo schroot -c "${target%bit}" -p -- /bin/sh -c '
267   egrep '"'^$(id -nu) '"' /etc/sudoers >/dev/null 2>&1 ||
268   echo '"'$(id -nu) ALL=(ALL) ALL'"' >>/etc/sudoers'
269 
270 # Install a few more commonly used packages
271 sudo schroot -c "${target%bit}" -p -- apt-get -y install                       \
272   autoconf automake1.9 dpkg-dev g++-multilib gcc-multilib gdb less libtool     \
273   strace
274 
275 # If running a 32bit environment on a 64bit machine, install a few binaries
276 # as 64bit. This is only done automatically if the chroot distro is the same as
277 # the host, otherwise there might be incompatibilities in build settings or
278 # runtime dependencies. The user can force it with the '-c' flag.
279 host_distro=$(grep DISTRIB_CODENAME /etc/lsb-release 2>/dev/null | \
280   cut -d "=" -f 2)
281 if [ "${copy_64}" = "y" -o \
282     "${host_distro}" = "${distname}" -a "${arch}" = 32bit ] && \
283     file /bin/bash 2>/dev/null | grep -q x86-64; then
284   readlinepkg=$(sudo schroot -c "${target%bit}" -p -- sh -c \
285     'apt-cache search "lib64readline.\$" | sort | tail -n 1 | cut -d " " -f 1')
286   sudo schroot -c "${target%bit}" -p -- apt-get -y install                     \
287     lib64expat1 lib64ncurses5 ${readlinepkg} lib64z1
288   dep=
289   for i in binutils gdb strace; do
290     [ -d /usr/share/doc/"$i" ] || dep="$dep $i"
291   done
292   [ -n "$dep" ] && sudo apt-get -y install $dep
293   sudo cp /usr/bin/gdb "/var/lib/chroot/${target}/usr/local/bin/"
294   sudo cp /usr/bin/ld "/var/lib/chroot/${target}/usr/local/bin/"
295   for i in libbfd libpython; do
296     lib="$({ ldd /usr/bin/ld; ldd /usr/bin/gdb; } |
297            grep "$i" | awk '{ print $3 }')"
298     if [ -n "$lib" -a -r "$lib" ]; then
299       sudo cp "$lib" "/var/lib/chroot/${target}/usr/lib64/"
300     fi
301   done
302   for lib in libssl libcrypt; do
303     sudo cp /usr/lib/$lib* "/var/lib/chroot/${target}/usr/lib64/" || :
304   done
305 fi
306 
307 # Clean up package files
308 sudo schroot -c "${target%bit}" -p -- apt-get clean
309 sudo apt-get clean
310 
311 # Let the user know what we did
312 trap '' INT TERM QUIT
313 trap '' EXIT
314 cat <<EOF
315 
316 
317 Successfully installed ${distname} ${arch}
318 
319 You can run programs inside of the chroot by invoking the "${target%bit}"
320 command.
321 
322 Your home directory is shared between the host and the chroot. But I configured
323 $HOME/chroot to be private to the chroot environment. You can use it
324 for files that need to differ between environments.
325 EOF
326