From 8b8658bd72e4e27b6ba86de60894bd67115b7ac0 Mon Sep 17 00:00:00 2001 From: Steve Hill Date: Thu, 10 Nov 2016 11:54:08 -0500 Subject: [PATCH] Add scripts to setup 32-bit chroots for building. --- install/centos/build_env.sh | 2 + install/centos/run_in_chroot.sh | 50 +++++++++++++ install/centos/setup_chroot.sh | 120 ++++++++++++++++++++++++++++++++ install/install_from_source.sh | 10 +++ install/run_in_chroot.sh | 1 + install/setup_chroot.sh | 1 + install/shell_utils.sh | 66 ++++++++++++++++++ install/ubuntu/build_env.sh | 3 + install/ubuntu/run_in_chroot.sh | 7 ++ install/ubuntu/setup_chroot.sh | 82 ++++++++++++++++++++++ 10 files changed, 342 insertions(+) create mode 100644 install/centos/run_in_chroot.sh create mode 100644 install/centos/setup_chroot.sh create mode 100755 install/install_from_source.sh create mode 120000 install/run_in_chroot.sh create mode 120000 install/setup_chroot.sh create mode 100644 install/ubuntu/run_in_chroot.sh create mode 100644 install/ubuntu/setup_chroot.sh diff --git a/install/centos/build_env.sh b/install/centos/build_env.sh index a93125cff..3e7db7ecc 100644 --- a/install/centos/build_env.sh +++ b/install/centos/build_env.sh @@ -17,6 +17,8 @@ if [ -f "$devtoolset_enable" ]; then fi +export CHROOT_DIR=/var/chroot/centos_i386 + export SSL_CERT_DIR=/etc/pki/tls/certs export SSL_CERT_FILE=/etc/pki/tls/cert.pem diff --git a/install/centos/run_in_chroot.sh b/install/centos/run_in_chroot.sh new file mode 100644 index 000000000..de81d8548 --- /dev/null +++ b/install/centos/run_in_chroot.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2016 Google Inc. All Rights Reserved. +# Author: cheesy@google.com (Steve Hill) +# +# This script emulates the behavior of schroot, which: +# - Invokes chroot(2) as root (schroot is setuid). +# - setuids back to the uid it was run as. +# - chdirs to the directory it was run from. +# - execs the command supplied as arguments, or starts an interactive shell. +# +# Trying to write a single command that can be passed to sudo that will do the +# su, chdir, exec combination without breaking arg tokenisation is nigh +# impossible. Instead, once the chrooting and setuiding (via sudo) has been +# taken care of, the script execs itself with --chroot_done. This takes care +# of the chdir and exec. + +# This comes from build_env.sh. +if [ -z "${CHROOT_DIR:-}" ]; then + echo "This must be run via os_redirector.sh!" >&2 + exit 1 +fi + +# When we re-invoke the script, it's called with: +# --chroot_done [CMD] +# It then chdirs to and invokes CMD or an interactive $SHELL +if [ "${1-}" = "--chroot_done" ]; then + if [ $# -lt 2 ]; then + echo "Do not run this directly with --chroot_done" >&2 + exit 1 + fi + + cd "$2" + shift 2 + + if [ $# -eq 0 ]; then + set -- "$SHELL" -l + fi + eval exec "$@" + exit 1 # NOTREACHED +fi + +# We need the absolute path to re-exec the script after the chroot. +this_script="$0" +if [[ "$this_script" != /* ]]; then + this_script="$PWD/$this_script" +fi + +# Note that here $0 is expected to be a symlink to os_redirector.sh. +exec setarch i386 sudo /usr/sbin/chroot "$CHROOT_DIR" sudo -u "$USER" -i -- \ + "$this_script" --chroot_done "$PWD" "$@" diff --git a/install/centos/setup_chroot.sh b/install/centos/setup_chroot.sh new file mode 100644 index 000000000..ea23ae4c8 --- /dev/null +++ b/install/centos/setup_chroot.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# Copyright 2016 Google Inc. All Rights Reserved. +# Author: cheesy@google.com (Steve Hill) +# +# Setup a 32-bit chroot for CentOS. + +# This comes from build_env.sh. +if [ -z "${CHROOT_DIR:-}" ]; then + echo "This must be run via os_redirector.sh!" >&2 + exit 1 +fi + +if [ -d "$CHROOT_DIR" ]; then + "$install_dir/run_in_chroot.sh" /bin/true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + echo chroot already setup, nothing to do. + exit 0 + else + echo "$CHROOT_DIR exists but doesn't seem to be setup correctly." >&2 + echo "You're going to have to clean up manually." >&2 + exit 1 + fi +fi + +if [ "$UID" != 0 ]; then + echo "This script needs to run as root, re-running with sudo" + exec sudo $0 "$@" + exit 1 # NOTREACHED +fi + +# TODO(cheesy): The release_rpm stuff below is not especially robust, but +# scraping the site is a pain. If this is a problem we can fix it later. + +centos_version="$(lsb_release -rs)" +git_pkg= +if version_compare "$centos_version" -lt 6; then + # CentOS 5 + release_rpm_url=http://mirror.centos.org/centos/5/os/i386/CentOS/centos-release-5-11.el5.centos.i386.rpm +elif version_compare "$centos_version" -lt 7; then + # CentOS 6 + release_rpm_url=http://mirror.centos.org/centos/6/os/i386/Packages/centos-release-6-8.el6.centos.12.3.i686.rpm + # TODO(cheesy): Once gclient is gone, we may be able to use the git rpm. +else + # CentOS 7 + release_rpm_url=http://mirror.centos.org/altarch/7/os/i386/Packages/centos-release-7-2.1511.el7.centos.2.9.i686.rpm + git_pkg=git +fi + +release_rpm="$(basename "$release_rpm_url")" +wget -O "$release_rpm" "$release_rpm_url" + +mkdir -p "$CHROOT_DIR/var/lib/rpm" + +# Required to run the chroot in 32-bit mode. +yum -y install setarch + +function cleanup_etc_rpm_platform() { + if [ -s /etc/rpm/platform.real ]; then + mv -f /etc/rpm/platform.real /etc/rpm/platform + elif [ -f /etc/rpm/platform.real ]; then + # Only do this if platform.real exists, otherwise we could delete a + # perfectly valid file. + rm -f /etc/rpm/platform /etc/rpm/platform.real + fi +} + +# To force install a different architecture, we must put a fake arch into +# /etc/rpm/platform. Older CentOSes will have the file, newer may not. Either +# way, it's important that we don't leave the fake one lying around. +trap 'cleanup_etc_rpm_platform' EXIT +if [ -e /etc/rpm/platform ]; then + mv /etc/rpm/platform /etc/rpm/platform.real +else + touch /etc/rpm/platform.real +fi + +echo i686-redhat-linux > /etc/rpm/platform +rpm --root="$CHROOT_DIR" --rebuilddb +rpm --root="$CHROOT_DIR" --nodeps -i "$release_rpm" + +yum -y --installroot="$CHROOT_DIR" update +# redhat-lsb and sudo are required for run_in_chroot.sh +yum -y --installroot="$CHROOT_DIR" install yum sudo redhat-lsb + +cleanup_etc_rpm_platform +trap - EXIT + +for x in passwd shadow group gshadow hosts sudoers resolv.conf; do + ln -f "/etc/$x" "$CHROOT_DIR/etc/$x" +done + +cp -p /etc/yum.repos.d/* "$CHROOT_DIR/etc/yum.repos.d/" +# These don't work for 32-bit. +if version_compare "$centos_version" -ge 6; then + rm -f "$CHROOT_DIR/etc/yum.repos.d"/CentOS-SCLo-scl[.-]* +fi + +for dir in /proc /sys /dev /selinux /etc/selinux /home; do + [ -d "$dir" ] || continue + chroot_dir="${CHROOT_DIR}$dir" + if [ ! -d "$chroot_dir" ]; then + mkdir -p "$chroot_dir" + chown --reference "$dir" "$chroot_dir" + chmod --reference "$dir" "$chroot_dir" + fi + echo "$dir $chroot_dir none bind 0 0" >> /etc/fstab +done + +echo "none $CHROOT_DIR/dev/shm tmpfs defaults 0 0" >> /etc/fstab + +mount -a + +# The previous yum install above probably did all the updates, +# but it doesn't hurt to ask. +install/run_in_chroot.sh yum -y update +install/run_in_chroot.sh yum -y install which redhat-lsb curl wget $git_pkg + +if [ -z "$git_pkg" ]; then + install/run_in_chroot.sh install/install_from_source.sh git +fi diff --git a/install/install_from_source.sh b/install/install_from_source.sh new file mode 100755 index 000000000..5a460b014 --- /dev/null +++ b/install/install_from_source.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Copyright 2016 Google Inc. All Rights Reserved. +# Author: cheesy@google.com (Steve Hill) +# +# Trivial wrapper for shell function that installs a package from source +# tarball. + +source "$(dirname "$BASH_SOURCE")/build_env.sh" || exit 1 + +install_from_src "$@" diff --git a/install/run_in_chroot.sh b/install/run_in_chroot.sh new file mode 120000 index 000000000..ede6c5b83 --- /dev/null +++ b/install/run_in_chroot.sh @@ -0,0 +1 @@ +os_redirector.sh \ No newline at end of file diff --git a/install/setup_chroot.sh b/install/setup_chroot.sh new file mode 120000 index 000000000..ede6c5b83 --- /dev/null +++ b/install/setup_chroot.sh @@ -0,0 +1 @@ +os_redirector.sh \ No newline at end of file diff --git a/install/shell_utils.sh b/install/shell_utils.sh index 0ecc673db..97b5a2df5 100644 --- a/install/shell_utils.sh +++ b/install/shell_utils.sh @@ -83,3 +83,69 @@ function run_with_log() { fi return $rc } + +# Compare version numbers in dotted notation. +# Usage: version_compare +# For instance: +# if version_compare $version -lt 4.2; then echo "Too old!"; fi +# +function version_compare() { + if [ $# -ne 3 ]; then + echo "Usage: version_compare " >&2 + exit 1 + fi + + local a=$1 + local comparator=$2 + local b=$3 + + if [[ "$a" == *[^.0-9]* ]]; then + echo "Non-numeric version: $a" >&2 + exit 1 + fi + + if [[ "$b" == *[^.0-9]* ]]; then + echo "Non-numeric version: $b" >&2 + exit 1 + fi + + # The computed difference. 0 means a == b, -1 means a < b, 1 means a > b. + local difference=0 + + while [ $difference -eq 0 ]; do + if [ -z "$a" -a -z "$b" ]; then + break + elif [ -z "$a" ]; then + # a="" and b != "", therefore a < b + difference=-1 + break + elif [ -z "$b" ]; then + # a != "" and b="", therefore a > b + difference=1 + break + fi + + # $a is N[.N.N]. Extract the first N from the beginning into $a_tok + local a_tok="${a%%.*}" + # Make $a any remaining N.N. + a="${a#*.}" + [ "$a" = "$a_tok" ] && a="" # Happens when there are no dots in $a + + # Same for $b + local b_tok="${b%%.*}" + b="${b#*.}" + [ "$b" = "$b_tok" ] && b="" + + # Now do the integer comparison between a and b. + if [ "$a_tok" -lt "$b_tok" ]; then + difference=-1 + elif [ "$b_tok" -gt "$b_tok" ]; then + difference=1 + fi + done + + # Now do the actual comparison. We use the supplied comparator (say -le) to + # compare the computed difference with zero. This will return the expected + # result. + [ "$difference" "$comparator" 0 ] +} diff --git a/install/ubuntu/build_env.sh b/install/ubuntu/build_env.sh index fc75f43e3..6ab0ad832 100644 --- a/install/ubuntu/build_env.sh +++ b/install/ubuntu/build_env.sh @@ -14,3 +14,6 @@ if [ -x "${compiler_path}/bin/gcc" ]; then fi export PATH=$HOME/bin:/usr/local/bin:$PATH + +# precise_i386, for instance. +CHROOT_NAME="$(lsb_release -cs)_i386" diff --git a/install/ubuntu/run_in_chroot.sh b/install/ubuntu/run_in_chroot.sh new file mode 100644 index 000000000..3a4d60754 --- /dev/null +++ b/install/ubuntu/run_in_chroot.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# Copyright 2016 Google Inc. All Rights Reserved. +# Author: cheesy@google.com (Steve Hill) +# +# Run a single command in a chroot via schroot. + +exec schroot -c "$CHROOT_NAME" -- "$@" diff --git a/install/ubuntu/setup_chroot.sh b/install/ubuntu/setup_chroot.sh new file mode 100644 index 000000000..b06a98154 --- /dev/null +++ b/install/ubuntu/setup_chroot.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Copyright 2016 Google Inc. All Rights Reserved. +# Author: cheesy@google.com (Steve Hill) +# +# Setup a 32-bit chroot for Ubuntu. + +install_dir="$(dirname "${BASH_SOURCE[0]}")/.." + +chroot_dir="/var/chroot/$CHROOT_NAME" + +if [ -d "$chroot_dir" ]; then + "$install_dir/run_in_chroot.sh" /bin/true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + echo chroot already setup, nothing to do. + exit 0 + else + echo "$chroot_dir exists but doesn't seem to be setup correctly." >&2 + echo "You're going to have to clean up manually." >&2 + exit 1 + fi +fi + +if [ "$UID" -ne 0 ]; then + echo This script needs to run as root. Re-execing via sudo. + exec sudo $0 "$@" + exit 1 # NOTREACHED +fi + +apt-get -y update +# We have to install ssl-cert in the host because /etc/group is copied +# into the chroot. +apt-get -y install debootstrap dchroot ssl-cert + +distro_name="$(lsb_release -cs)" + +# Create the initial chroot. +debootstrap --variant=buildd --arch i386 \ + "$distro_name" "$chroot_dir" http://archive.ubuntu.com/ubuntu/ + +# Stop daemons from starting in the chroot. Do this before updating any pkgs! +# https://major.io/2016/05/05/preventing-ubuntu-16-04-starting-daemons-package-installed/ +cat > "$chroot_dir/usr/sbin/policy-rc.d" << EOF +#!/bin/sh +# Prevent all daemons from starting. +# Created by $(basename $0) for mod_pagespeed. +exit 101 +EOF +chmod +x "$chroot_dir/usr/sbin/policy-rc.d" + +# Configure schroot + +cat >> /etc/schroot/schroot.conf << EOF +[$CHROOT_NAME] +description=Ubuntu $distro_name for i386 +directory=$chroot_dir +type=directory +personality=linux32 +preserve-environment=true +root-groups=sudo +groups=sudo +EOF + +cat >> /etc/schroot/default/fstab << EOF +# Don't add /dev/shm, weird things will happen. See: +# https://bugs.launchpad.net/ubuntu/+source/schroot/+bug/1438942/comments/5 +none /run/shm tmpfs rw,nosuid,nodev,noexec 0 0 +EOF + +cat >> /etc/schroot/default/copyfiles << EOF +/etc/apt/sources.list +EOF + +# schroot is now functional, so we can use run_in_chroot.sh to complete setup +# of the chroot. + +"$install_dir/run_in_chroot.sh" apt-get -y update +"$install_dir/run_in_chroot.sh" apt-get -y upgrade +"$install_dir/run_in_chroot.sh" apt-get -y install locales sudo lsb-release +"$install_dir/run_in_chroot.sh" locale-gen en_US.UTF-8 + +# This must be done after we install sudo or dpkg gets cranky. +echo /etc/sudoers >> /etc/schroot/default/copyfiles