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