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