Hello misc@,

I put together a script, which builds OpenBSD inside a chroot.
Since it took me quite some time to figure out a couple of pitfalls (see
below) I thought I'd just share it. Perhaps somebody finds it usefull
and/or can _please_ give me feedback.

READ THE SCRIPT BEFORE USING IT!

Why build OpenBSD inside a chroot?
Mail-server's CPU is idle all the time and I don't have money/room to
put up a dedicated build-host (especially for -stable). I do not want to
kill my mail-server if something breaks during the build, thus I had the
idea to chroot the build process.

Thanks to get great quality of OpenBSD it pretty much worked as
documented in the FAQ and release(8).

First of all my partition layout for /chroot_build:
/dev/sd0g on /chroot_build type ffs (local, noatime, softdep)
/dev/sd0m on /chroot_build/usr/XF4 type ffs (local, noatime, softdep)
/dev/sd0n on /chroot_build/usr/Xbld type ffs (local, noatime, softdep)
/dev/sd0o on /chroot_build/usr/obj type ffs (local, noatime, softdep)
/dev/sd0p on /chroot_build/usr/ports type ffs (local, noatime, softdep)
/dev/sd0k on /chroot_build/usr/src type ffs (local, noatime, softdep)

You _can_ use only one partition for /chroot_build, but using multiple
ones speeds up the process (see newfs/rm -rf part of script).

The one dirty hack left inside the script:
One can't use MAKEDEV (mknod) inside a chroot, thus I had to
"communicate" with an outside process to run MAKEDEV at the appropriate
time. What I came up with: make nc listen on localhost in side a
subshell, once it receives data it will terminate and the commands
inside the subshell will be run.
It is _dirty_! Could somebody _please_ give a hint on a better way,
which isn't more complicated.
IMHO it isn't a security problem because the worst case scenary is a
local user "triggering" the MAKEDEV, which will result in a broken
build, but nothing worse.

Patch needed for dirty hack (see patch_file in setup_chroot_build.sh):
#-------------- patch-usr_src_distrib_i386_common_list ------------------------#
--- /usr/src/distrib/i386/common/list.orig      Tue Nov 21 23:29:16 2006
+++ /usr/src/distrib/i386/common/list   Tue Nov 21 23:30:23 2006
@@ -53,8 +53,9 @@
 LINK   instbin                                 usr/mdec/installboot
 
 # copy the MAKEDEV script and make some devices
+# this does NOT work inside chroot, thus spezial care is taken.
 SCRIPT ${DESTDIR}/dev/MAKEDEV                  dev/MAKEDEV
-SPECIAL        cd dev; sh MAKEDEV ramdisk
+SPECIAL        echo "MAKEDEV" | nc 127.0.0.1 12345; sleep 10
 
 # we need the contents of /usr/mdec
 COPY   ${DESTDIR}/usr/mdec/biosboot            usr/mdec/biosboot
#------------------------------------------------------------------------------#


Create /chroot_build (with partitions) and run:
#----------------------- setup_chroot_build.sh --------------------------------#
#!/bin/sh

# Setup chroot environment to build -current/-stable in,
# see build_chrooted.sh.

# Directory holding chrooted OpenBSD.
# WARNING: Partition containing chroot musn't be mounted with nodev!
chroot_dir="/chroot_build"

# Directory holding install sets (vers/arch will be appended)
# e.g. sets should be in /home/ahb/4.0/i386/
install_sets="/home/ahb"

# Directory containing custom patches (applied after cvs update)
patch_file="/home/ahb/patch-usr_src_distrib_i386_common_list"

#----------------------- DO NOT EDIT BELOW ------------------------------------#
# Display string on stderr then exit false
# Usage: die "<string>"
die()
{
        echo "$1" >&2
        exit 1
}

# Private variables
install_sets="${install_sets}/`uname -r`/`uname -m`/"

echo "Remember to create ${chroot_dir} (including setting up its partitions)"
wait_time 5

test -d ${chroot_dir} || mkdir ${chroot_dir} || return 1

echo -n 'Extracting install sets: '
for file in `find ${install_sets} -name '*tgz'`
do
        tar pxzf $file -C ${chroot_dir} || return 1
done
echo 'done'

echo -n 'Copying kernels: '
for file in `find ${install_sets} -name 'bsd*'`
do
        cp $file ${chroot_dir} || return 1
done
echo 'done'

# Create necessary directories (mount points)
test -d ${chroot_dir}/usr/ports || mkdir ${chroot_dir}/usr/ports || return 1
test -d ${chroot_dir}/usr/XF4 || mkdir ${chroot_dir}/usr/XF4 || return 1
test -d ${chroot_dir}/usr/Xbld || mkdir ${chroot_dir}/usr/Xbld || return 1
test -d ${chroot_dir}/patches || mkdir ${chroot_dir}/patches || return 1
test -d ${chroot_dir}/root/.ssh || mkdir ${chroot_dir}/root/.ssh || return 1

echo -n "Creating necessary ${chroot_dir}/etc/fstab entries: "
chroot_dir_s=$(echo $chroot_dir | sed 's#^/##')
grep "${chroot_dir_s}" /etc/fstab                               \
    | sed -e 's#'${chroot_dir_s}'##' -e 's#//#/#'               \
    > ${chroot_dir}/etc/fstab || return 1
echo 'done'

echo -n 'Copying misc files: '
# Copy /etc files
for file in resolv.conf
do
        echo -n "$file "
        cp -pR /etc/$file ${chroot_dir}/etc/
done

# Copy patches
cp $patch_file ${chroot_dir}/patches/

# known_hosts for CVS checkout
cat >${chroot_dir}/root/.ssh/known_hosts << _END_KNOWN_HOSTS
anoncvs1.de.openbsd.org,131.188.40.91 ssh-rsa 
AAAAB3NzaC1yc2EAAAABIwAAAIEA3aqCYtHuUCuXKehui2e383CH5VoqYEbILHtZHLHZT/dcyOo/R4562wyD8fh/N4/w9FjM60rfZbZKseXWrc8nuHXGZE5/C1osUNCRfCcMIYqNQBXbb9UR+QWwUNmwYRg6siWmiE+L6MCddJOeXK2ZE4LdU0UYlOjACxiNq8/3tR0=
anoncvs2.de.openbsd.org,194.45.27.107 ssh-rsa 
AAAAB3NzaC1yc2EAAAABIwAAAIEAtv2eaMczWA2dDktE3I4cET7iWqdEST5KENz9xRe8CRzJjIJpbv6L4xw32wEj3FqLYE4hJmkOdfsg3UwC4DBBGV9//Zu7onzZ2G47yTw+Yrv5yK53ZJ7fKbrQlmgMzSzQ+OkRgLmeI2U+xhCV+VtVmKj4z8fmZruRV4YjN+cGqEc=
_END_KNOWN_HOSTS
echo 'DONE'

echo -n 'Creating devices: '
cd ${chroot_dir}/dev && ./MAKEDEV all || \
    (cp -pR /dev/* ${chroot_dir}/dev/ 2>/dev/null; echo -n ' COPY ONLY ')
echo 'done'
#------------------------------------------------------------------------------#


Copy this into your chroot-directory (/chroot_build) and make chmod +x:
#----------------------- build_chrooted.sh ------------------------------------#
#!/bin/sh

# This builts OpenBSD inside a chroot and is RUN INSIDE $chroot_dir!
# See setup_chroot_build.sh on how to setup the chroot.
# Script assumes stuff which might break your neck!

# IMPORTANT:
# Location of chroot (this script is run inside)
chroot_dir="/chroot_build"
export CVSROOT="[EMAIL PROTECTED]:/cvs"

#----------------------- DO NOT EDIT BELOW ------------------------------------#
# Private variables
export BASEDIR="/usr"   # BASE for DESTDIR & RELEASEDIR
quiet="n"
cvs_method="checkout"

# Display string on stderr then exit false
# Usage: die "<string>"
die()
{
        echo "$1" >&2
        exit 1
}

# Only echo if $quiet is NOT 'y'
echoq()
{
        if [ x"$quiet" != xy ] ; then
                echo "$1"
        fi
}

usage()
{
        echo "`basename $0` [-u] [-q] (-s|-c)"
        echo "-c    Build -current"
        echo "-s    Build -stable"
        echo "-u    Update source (default is to checkout/extract)"
        echo "-q    Be quiet (no output besides errors)"

        exit 1
}

# Only 1 or 2 arguments
if [ $# -lt 1 -o $# -gt 3 ] ; then
        usage
fi

while [ x"$1" != x"" ]
do
        case $1 in
        -s)
                cvs_flags="-rOPENBSD_`uname -r | tr '.' '_'`"
                shift
                ;;
        -c)
                cvs_flags=""
                shift
                ;;
        -u)
                cvs_method="update"
                shift
                ;;
        -q)
                quiet="y"
                shift
                ;;
        *)
                usage
        esac
done


# Sane umask
umask 022

# Link needed for [u]mount
test -e ${chroot_dir} || ln -s / ${chroot_dir}

if [ "$cvs_method" = "checkout" ] ; then
# Reformat ASSUMING /usr/{XF4,Xbld,ports,src,obj} each is a seperate partition,
# otherwise rm -rf the folder (which takes WAY longer).
echoq 'Cleaning source/build directories'
for mnt in ports src XF4 Xbld obj
do
        wd=$(mount | grep ${chroot_dir}/usr/${mnt} | sed 's#^/dev/\([a-z0-9]*\) 
.*#\1#')
        if [ x"${wd}" == x"" ] ; then
                rm -rf /usr/${mnt}
        else
                /sbin/umount /dev/$wd &&                        \
                    /sbin/newfs $wd &&                          \
                    /sbin/mount -w -o softdep,noatime /dev/$wd
        fi
done

# Extract sources form *.tar.gz or get them by CVS
echoq 'Fetching/extracting sources'
for src in ports src XF4
do
        if [ ! -r /usr/${src}.tar.gz -o "$src_only" == "y" ] ; then
                echoq "CVS checkout from ${CVSROOT} for /usr/${src}"
                cd /usr && \
                    cvs -d ${CVSROOT} checkout \
                    -R ${cvs_flags} -P ${src} || \
                    die "Error CVS checkout of $src"
                tar cpzf ${src}.tar.gz ${src} || \
                    die "Error creating tar archive of $src"
        else
                echoq "Extracting /usr/${src}.tar.gz"
                cd /usr && \
                    tar xpzf ${src}.tar.gz || \
                    die "Error extracting tar archive $src"
        fi
done
fi

if [ "$cvs_method" = "update" ] ; then
        # Update sources from CVS
        echoq 'Updating sources'
        for src in ports src XF4
        do
                        echoq "CVS update from ${CVSROOT} for /usr/${src}"
                        cd /usr && \
                            cvs -d ${CVSROOT} update \
                            -R ${cvs_flags} -P ${src} || \
                            die "Error CVS update of $src"
                        tar cpzf ${src}.tar.gz ${src} || \
                            die "Error creating tar archive of $src"
        done
fi

# Apply custom patches
for file in `find /patches -type f`
do
        patch -p0 < $file || die "Error patch $file FAILED"
done

# Compile GENERIC kernel
echoq "Compiling new GENERIC kernel"
cd /usr/src/sys/arch/`uname -m`/conf && config GENERIC && \
cd ../compile/GENERIC && \
make clean && \
make depend && \
make && \
make install || die "Error compiling GENERIC"

# Compile GENERIC.MP kernel
echoq "Compiling new GENERIC.MP kernel"
cd /usr/src/sys/arch/`uname -m`/conf && config GENERIC.MP && \
cd ../compile/GENERIC.MP && \
make clean && \
make depend && \
make && \
make install || die "Error compiling GENERIC.MP"

# Compile base system
echoq "Compiling base system"
cd /usr/src && make cleandir && make obj && \
cd /usr/src/etc && env DESTDIR=/ make distrib-dirs && \
cd /usr/src && make build || \
die "Error building base system"

# Add new devices, WILL HAVE ERRORS and not necessary!
# echoq "Creating new devices"
# cd /dev && sh MAKEDEV all 2>/dev/null

# Compile X
echoq "Compiling X"
cd /usr/ports/x11/tk/8.4/ && make install && \
cd /usr/Xbld && lndir ../XF4 && make build || \
die "Error building X"

# Create base release files
echoq "Creating install sets"
cd /usr/src/distrib/crunch && make obj depend all install
        # continue even if failed because it might just BE installed
export DESTDIR="${BASEDIR}/dest"
export RELEASEDIR="${BASEDIR}/release"
rm -rf ${BASEDIR}/dest && mkdir -p ${BASEDIR}/dest && \
rm -rf ${BASEDIR}/release && mkdir -p ${BASEDIR}/release && \
cd /usr/src/etc && nice make release && \
cd /usr/src/distrib/sets && sh checkflist || die "Error creating release"

# Create X release files
echoq "Creating X install sets"
export DESTDIR="${BASEDIR}/destXF4"
rm -rf ${BASEDIR}/destXF4 && mkdir -p ${BASEDIR}/destXF4 && \
cd /usr/Xbld && nice make release || die "Error creating X sets"

# Umount partitions mounted from inside chroot, they can't be unmounted from
# outside chroot!
cd /            # Otherwise we can't umount everything
for mnt in ports src XF4 Xbld obj
do
        wd=$(mount | grep /usr/${mnt} | sed 's#^/dev/\([a-z0-9]*\) .*#\1#')
        if [ x"${wd}" != x"" ] ; then
                /sbin/umount /dev/$wd
        fi
done

echoq "DONE! Your install sets are located in ${chroot_dir}/${RELEASEDIR}"
#------------------------------------------------------------------------------#


Put this into root's crontab (or just run the _exact_ command part
inside [k]sh shell):
#----------------------- crontab.root -----------------------------------------#
BUILD_DIR="/chroot_build"
BUILD_LOG="/home/ahb/build_chroot.log"
#minute hour    mday    month   wday    command
# Build OpenBSD -stable chrooted inside BUILD_DIR, copy release sets onto NFS 
server
# Next is ONE very LONG line
#40     1       *       *       0       ((echo ""; echo "") >>$BUILD_LOG; for i 
in A B C CD; do (nc -l 127.0.0.1 12345 >>$BUILD_LOG 2>&1; cd $BUILD_DIR/mnt/dev 
&& sh MAKEDEV ramdisk >>$BUILD_LOG 2>&1)& done; /usr/sbin/chroot $BUILD_DIR 
/build_chrooted.sh -q -s >>$BUILD_LOG 2>&1 || echo "ERROR building -stable 
inside $BUILD_DIR"; mount -a; sleep 1; cp $BUILD_DIR/usr/release/* 
/mnt/nfs/temp/OpenBSD/stable/; chown ahb:users /mnt/nfs/temp/OpenBSD/stable/*; 
chmod 644 /mnt/nfs/temp/OpenBSD/stable/*; kill `ps waux | grep 
'nc.*127.0.0.1.*1234[5]' | awk '{print $2}' | tr '\n' ' '` >>$BUILD_LOG 2>&1)
#------------------------------------------------------------------------------#

I hope somebody will save time because of this :)
Hopefully all needed parts are included, if something is missing, tell me.

Regards,
ahb

Reply via email to