#! /bin/sh

# $NetBSD: mksandbox,v 1.21 2026/01/28 20:37:38 lloyd Exp $

# Copyright (c) 2002,2012 Alistair Crooks <agc@NetBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# Usage: mksandbox [--mounthost=host] [--pkgsrc=dir]
#		   [--rodirs=dir1,...] [--rwdirs=dir1,...]
#		   [--src=srcdir] [--xsrc=xsrcdir]
#		   [--without-src] [--without-pkgsrc] [--without-xsrc]
#		   [--without-x] [--verbose] sandbox-dir
#
# A small shell script to set up a sandbox (usually for a pkgsrc bulk
# build), using null mounts.

pkgsrc=/usr/pkgsrc
src=/usr/src
xsrc=/usr/xsrc
rodirs=
rwdirs=
with_pkgsrc=yes
with_x=yes

kernel=""
sandboxWriteDirs=""
sandboxMountDirs="/bin /sbin /lib /libexec /usr/X11R7 /usr/bin /usr/games /usr/include /usr/lib /usr/libdata /usr/libexec /usr/lkm /usr/share /usr/sbin /var/mail"
sandboxEmptyDirs="/var/run /var/log /var/spool/lock /var/spool/mqueue"
sandboxEmptyFiles="/var/run/utmp /var/run/utmpx /var/log/wtmp /var/log/wtmpx /var/log/lastlog /var/log/lastlogx"

usage()
{
	echo "usage: mksandbox [--mounthost=host] [--rodirs=dir1,...] [--rwdirs=dir1,...]"
	echo "                 [--pkgsrc=dir] [--src=srcdir] [--xsrc=xsrcdir]"
	echo "                 [--without-src] [--without-pkgsrc] [--without-xsrc]"
	echo "		       [--without-x] [--verbose] sandbox-dir"
 	exit 1
}

err()
{
	echo "error: $1"
	exit 1
}

# by default, don't require src and xsrc to be available
need_src=no
need_xsrc=no

opsys=`uname -s`
case "$opsys" in
Darwin)
	bmakeprog=bmake
	chmodprog=/bin/chmod
	chownprog=/usr/sbin/chown
	cpprog=/bin/cp
	gtarprog=/usr/bin/gnutar
	idprog=/usr/bin/id
	kernel=/mach_kernel
	mkdirprog="/bin/mkdir -p"
	mountflags="-t nfs"
	mounthost="localhost"
	mountprog=/sbin/mount
	paxprog=/bin/pax
	sedprog=/usr/bin/sed
	sandboxEmptyDirs="$sandboxEmptyDirs /var/root /var/db"
	sandboxMountDirs="$sandboxMountDirs /usr/llvm-gcc-4.2 /usr/X11 /System/Library /Library"
	sandboxWriteDirs="$sandboxWriteDirs /Library/Server/Mail/Data"
	;;
DragonFly)
	bmakeprog=bmake
	chmodprog=/bin/chmod
	chownprog=/usr/sbin/chown
	cpprog=/bin/cp
	gtarprog=/usr/bin/tar
	idprog=/usr/bin/id
	mkdirprog="/bin/mkdir -p"
	mountflags="-t null"
	mountprog=/sbin/mount
	paxprog=/bin/pax
	sedprog=/usr/bin/sed
	sandboxEmptyDirs="$sandboxEmptyDirs /var/spool/dma"
	# Does /var/db exist on a basic install of DragonFly?
	# If so, delete the line above and uncomment the line below.
	# sandboxEmptyDirs="$sandboxEmptyDirs /var/spool/dma /var/db"
	;;
FreeBSD)
	bmakeprog=bmake
	chmodprog=/bin/chmod
	chownprog=/usr/sbin/chown
	cpprog=/bin/cp
	gtarprog=/usr/bin/tar
	idprog=/usr/bin/id
	mkdirprog="/bin/mkdir -p"
	mountflags="-t nullfs"
	mountprog=/sbin/mount
	paxprog=/bin/pax
	sedprog=/usr/bin/sed
	# Does /var/db exist on a basic install of FreeBSD?
	# If so, uncomment the line below.
	# sandboxEmptyDirs="$sandboxEmptyDirs /var/db"
	;;
Linux)
	bmakeprog=bmake
	chmodprog=/bin/chmod
	chownprog=/bin/chown
	cpprog=/bin/cp
	gtarprog=/bin/tar
	idprog=/usr/bin/id
	mkdirprog="/bin/mkdir -p"
	mountflags="--bind"
	if [ -f /bin/mount ]; then
		mountprog=/bin/mount
	else
		mountprog=/sbin/mount
	fi
	paxprog=""
	sedprog=/bin/sed
	sandboxEmptyDirs="$sandboxEmptyDirs /var/lib"
	sandboxMountDirs="$sandboxMountDirs /lib64 /usr/lib64 /usr/kerberos"
	sandboxWriteDirs="$sandboxWriteDirs /proc"
	;;
NetBSD)
	bmakeprog=make
	chmodprog=/bin/chmod
	chownprog=/usr/sbin/chown
	cpprog=/bin/cp
	gtarprog=/usr/bin/tar
	idprog=/usr/bin/id
	kernel=/netbsd
	mkdirprog="/bin/mkdir -p"
	mountflags="-t null"
	mountprog=/sbin/mount
	paxprog=/bin/pax
	sedprog=/usr/bin/sed
	need_src=yes
	need_xsrc=yes
	sandboxEmptyDirs="$sandboxEmptyDirs /var/db"
	;;
SunOS)
	bmakeprog=bmake
	chmodprog=/usr/bin/chmod
	chownprog=/usr/bin/chown
	cpprog=/usr/bin/cp
	gtarprog=""
	idprog=/usr/xpg4/bin/id
	mkdirprog="/usr/bin/mkdir -p"
	mountflags="-F lofs"
	mountprog=/sbin/mount
	paxprog=/bin/pax
	sedprog=/usr/xpg4/bin/sed
	sandboxMountDirs="/bin /sbin /kernel /lib /proc /opt/SUNWspro /usr/X11R6 /usr/5bin /usr/bin /usr/ccs /usr/dt /usr/games /usr/include /usr/lib /usr/openwin /usr/share /usr/sbin /usr/sadm /usr/sfw /usr/ucb /usr/ucblib /usr/xpg4 /var/mail /var/sadm"
	sandboxEmptyDirs="$sandboxEmptyDirs /usr/tmp /var/tmp"
	# Does /var/adm exist on a basic install of Solaris?
	# If so, delete the line above and uncomment the line below.
	# sandboxEmptyDirs="$sandboxEmptyDirs /usr/tmp /var/tmp /var/adm"
	;;
*)
	echo "Unknown Operating System ($opsys) - good luck"
	bmakeprog=bmake
	chmodprog=chmod
	chownprog=chown
	cpprog=cp
	gtarprog="tar"
	idprog="id"
	mkdirprog="mkdir -p"
	mountflags="-t null"
	mountprog=mount
	paxprog=pax
	sedprog=sed
	;;
esac

while [ $# -gt 0 ]; do
	case "$1" in
	--mounthost=*)		mounthost=`echo $1 | $sedprog -e 's|^--mounthost=||'` ;;
	--pkgsrc=*)		pkgsrc=`echo $1 | $sedprog -e 's|^--pkgsrc=||'` ;;
	--src=*)		src=`echo $1 | $sedprog -e 's|^--src=||'` ;;
	--xsrc=*)		xsrc=`echo $1 | $sedprog -e 's|^--xsrc=||'` ;;
	--rodirs=*)		rodirs=`echo $1 | $sedprog -e 's|^--rodirs=||'` ;;
	--rwdirs=*)		rwdirs=`echo $1 | $sedprog -e 's|^--rwdirs=||'` ;;
	--without-pkgsrc)	with_pkgsrc=no ;;
	--without-src)		need_src=no ;;
	--without-xsrc)		need_xsrc=no ;;
	--without-x)		with_x=no ;;
	--verbose)		set -x ;;
	-*)			usage ;;
	*)			break ;;
	esac
	shift
done

if [ $# -ne 1 ]; then
	usage
fi

if [ "$with_x" = "no" ]; then
	need_xsrc=no
fi

if [ `$idprog -u` -ne 0 ]; then
	err "You must be root to run this script."
fi

if [ -n "$mounthost" ]; then
	mounthost="$mounthost:"
fi

if [ ! -d $pkgsrc -a "$with_pkgsrc" = "yes" ]; then
	err "pkgsrc directory $pkgsrc does not exist."
fi

if [ ! -d $src -a "$need_src" = "yes" ]; then
	err "source directory $src does not exist."
fi

if [ ! -d $xsrc -a "$need_xsrc" = "yes" ]; then
	err "xsrc directory $xsrc does not exist."
fi

sandbox=$1
sandbox_script="$sandbox/sandbox"

if [ "$with_pkgsrc" = "yes" ]; then
	packages=`(cd $pkgsrc/pkgtools/lintpkgsrc; $bmakeprog show-var VARNAME=PACKAGES)`
	distfiles=`(cd $pkgsrc/pkgtools/lintpkgsrc; $bmakeprog show-var VARNAME=DISTDIR)`
	localbase=`(cd $pkgsrc/pkgtools/lintpkgsrc; $bmakeprog show-var VARNAME=LOCALBASE)`
	pkg_dbdir=`(cd $pkgsrc/pkgtools/lintpkgsrc; $bmakeprog show-var VARNAME=PKG_DBDIR)`
	localpatches=`(cd $pkgsrc/pkgtools/lintpkgsrc; $bmakeprog show-var VARNAME=LOCALPATCHES)`

	test -d "$localpatches" || echo "WARNING: LOCALPATCHES directory does not exist - ignoring"
fi

$mkdirprog $sandbox
sandbox="`(cd $sandbox && pwd)`"
cat > $sandbox_script <<EOS
#! /bin/sh -
mounthost=$mounthost
opsys=$opsys
sandbox=$sandbox

r3() {
	_R=
	while [ \$# -ge 3 ]
	do
		_R="\$1 \$2 \$3 \$_R"
		shift; shift; shift
	done
	echo "\$_R"
}

fses="\\
EOS

if [ -n "$kernel" -a -f "$kernel" ]; then
	echo "Copying the kernel"
	$cpprog $kernel $sandbox
fi

if [ "$with_pkgsrc" = "yes" ]; then
	echo "Checking package hierarchy in $localbase and package database in $pkg_dbdir exist"
	$mkdirprog $sandbox/$localbase $sandbox/$pkg_dbdir
fi

echo "Make and populate $sandbox/dev"
$mkdirprog $sandbox/dev

case "$opsys" in
Darwin|DragonFly|FreeBSD)
	$mountprog -t devfs devfs $sandbox/dev
	;;
SunOS)
	$mountprog -F lofs -r /dev $sandbox/dev
	$mountprog -F fd fd $sandbox/dev/fd
	;;
Linux)
	$mountprog $mountflags /dev $sandbox/dev
	$mountprog -t tmpfs tmpfs $sandbox/dev/shm
	;;
*)
	$cpprog /dev/MAKEDEV* $sandbox/dev
	(cd $sandbox/dev; ./MAKEDEV all)
esac

echo "Make and populate $sandbox/etc"
$mkdirprog $sandbox/etc
case "$paxprog" in
"")	(cd /etc; $gtarprog -cf - . | (cd $sandbox/etc; $gtarprog xf - )) ;;
*)	(cd /etc; $paxprog -rwpe . $sandbox/etc) ;;
esac
rm -f $sandbox/etc/localtime

case "$opsys" in
SunOS)
	$cpprog /etc/TIMEZONE $sandbox/etc/TIMEZONE
	;;
*)
	$cpprog /usr/share/zoneinfo/GMT $sandbox/etc/localtime
	;;
esac

echo "Make empty dirs upon which to mount the null mounts"
for d in $sandboxMountDirs; do
	if [ -L $d ]; then
		$cpprog -P $d $sandbox$d
		continue
	fi
	test -d $d || continue;
	case $d in
	*X11*)	test "$with_x" = "yes" || continue ;;
	esac
	$mkdirprog $sandbox$d;
	$mountprog $mountflags -r $mounthost$d $sandbox$d;
	case "$opsys" in
	Linux)	$mountprog $mountflags -o remount,bind,ro $mounthost$d $sandbox$d ;;
	esac
	echo "$mounthost$d $d ro \\" >> $sandbox_script
done
for d in $sandboxWriteDirs; do
	test -d $d || continue;
	$mkdirprog $sandbox$d;
	$mountprog $mountflags $mounthost$d $sandbox$d;
	echo "$mounthost$d $d rw \\" >> $sandbox_script
done

echo "Making /tmp in $sandbox"
$mkdirprog $sandbox/tmp $sandbox/var/tmp
$chmodprog 1777 $sandbox/tmp $sandbox/var/tmp

$idprog games >/dev/null 2>&1
if [ $? -eq 0 ]; then
	echo "Making /var/games in $sandbox"
	$mkdirprog $sandbox/var/games
	$chownprog games:games $sandbox/var/games
	$chmodprog 2775 $sandbox/var/games
fi

for d in $sandboxEmptyDirs; do
	test -d $d || continue
	echo "Making $d in $sandbox"
	$mkdirprog $sandbox$d
done

for f in $sandboxEmptyFiles; do
	test -f $f || continue
	echo "Making $f in $sandbox"
	$cpprog /dev/null $sandbox$f
done

if [ "$need_src" = "yes" ]; then
	echo "Mount $src from $sandbox"
	$mkdirprog $sandbox/usr/src
	$mountprog $mountflags -r $mounthost$src $sandbox/usr/src
	echo "$mounthost$src /usr/src ro \\" >> $sandbox_script
fi

if [ "$with_pkgsrc" = "yes" ]; then
	echo "Mount $pkgsrc from $sandbox"
	$mkdirprog $sandbox/usr/pkgsrc
	$mountprog $mountflags $mounthost$pkgsrc $sandbox/usr/pkgsrc
	echo "$mounthost$pkgsrc /usr/pkgsrc rw \\" >> $sandbox_script

	echo "Mounting $packages and $distfiles from $sandbox"
	$mkdirprog $sandbox/$packages $sandbox/$distfiles
	$mkdirprog $packages $distfiles
	$mountprog $mountflags $mounthost$packages $sandbox/$packages
	$mountprog $mountflags $mounthost$distfiles $sandbox/$distfiles
	echo "$mounthost$packages $packages rw \\" >> $sandbox_script
	echo "$mounthost$distfiles $distfiles rw \\" >> $sandbox_script

	if [ -n "$localpatches" ] && [ -d "$localpatches" ]; then
		echo "Mounting $localpatches from $sandbox"
		$mkdirprog $sandbox/$localpatches
		$mountprog $mountflags $mounthost$localpatches $sandbox/$localpatches
		echo "$mounthost$localpatches $localpatches rw \\" >> $sandbox_script
	fi
fi

if [ "$need_xsrc" = "yes" ]; then
	echo "Mount $xsrc from $sandbox"
	$mkdirprog $sandbox/usr/xsrc
	$mountprog $mountflags -r $mounthost$xsrc $sandbox/usr/xsrc
	echo "$mounthost$xsrc /usr/xsrc ro \\" >> $sandbox_script
fi

if [ -n "$rodirs" ]; then
	for dir in `echo $rodirs | $sedprog -e 's/,/ /g'`; do
		echo "Mount $dir from $sandbox"
		$mkdirprog $sandbox$dir
		$mountprog $mountflags -r $mounthost$dir $sandbox$dir
		case "$opsys" in
		Linux)	$mountprog $mountflags -o remount,bind,ro $mounthost$dir $sandbox$dir ;;
		esac
		echo "$mounthost$dir $dir ro \\" >> $sandbox_script
	done
fi

if [ -n "$rwdirs" ]; then
	for dir in `echo $rwdirs | $sedprog -e 's/,/ /g'`; do
		echo "Mount $dir from $sandbox"
		$mkdirprog $sandbox$dir
		$mountprog $mountflags $mounthost$dir $sandbox$dir
		echo "$mounthost$dir $dir rw \\" >> $sandbox_script
	done
fi

date > $sandbox/.sandbox_mounted

cat >> $sandbox_script <<EOS
"
case x\$1 in
xmount)
	if [ -s \$sandbox/.sandbox_mounted ]; then
		echo \$sandbox already mounted && exit 1
	fi
	set dummy \$fses
	shift
	while [ \$# -ge 3 ]; do
		fs=\$1; shift
		mntpoint=\$1; shift
		rwro=\$1; shift
		case \$rwro in
			ro) $mountprog $mountflags -r \$fs \$sandbox/\$mntpoint || exit 1 ;;
			rw) $mountprog $mountflags \$fs \$sandbox/\$mntpoint || exit 1 ;;
		esac
	done
	case \$opsys in
	Darwin|DragonFly|FreeBSD)
		$mountprog -t devfs devfs \$sandbox/dev
		;;
	Linux)
		$mountprog $mountflags /dev \$sandbox/dev
		$mountprog -t tmpfs tmpfs \$sandbox/dev/shm
		;;
	SunOS)
		$mountprog -F lofs -r /dev \$sandbox/dev
		$mountprog -F fd fd \$sandbox/dev/fd
		;;
	esac
	date > \$sandbox/.sandbox_mounted
	;;
xumount)
	if [ ! -s \$sandbox/.sandbox_mounted ]; then
		echo \$sandbox not mounted && exit 1
	fi
	set dummy \`r3 \$fses\`
	shift
	while [ \$# -ge 3 ]; do
		fs=\$1; shift
		mntpoint=\$1; shift
		dummy=\$1; shift
		umount \$sandbox/\$mntpoint
	done
	case \$opsys in
	Linux)
		umount \$sandbox/dev/shm
		umount \$sandbox/dev
		;;
	Darwin|DragonFly|FreeBSD)
		umount \$sandbox/dev
		;;
	SunOS)
		umount \$sandbox/dev/fd
		umount \$sandbox/dev
		;;
	esac
	rm -f \$sandbox/.sandbox_mounted
	;;
xchroot)
	if [ ! -s \$sandbox/.sandbox_mounted ]; then
		echo \$sandbox not mounted && exit 1
	fi
	case x\$2 in
	x)	rootshell=/bin/ksh ;;
	*)	rootshell="\$2" ;;
	esac
	script="\$sandbox/tmp/script.\$\$"
	echo "#!/bin/sh" > \$script
	echo "ENV=/etc/shrc \$rootshell" >> \$script
	chmod +x \$script
	ENV=/etc/shrc chroot \$sandbox /tmp/\`basename \$script\`
	rm -f \$script
	;;
xismounted)
	if [ -s \$sandbox/.sandbox_mounted ]; then
		echo \$sandbox is mounted && exit 0
	else
		echo \$sandbox is not mounted && exit 1
	fi
	;;
*)
	if [ \$# -eq 0 ]; then
		set dummy /bin/sh
		shift
	fi
	chroot \$sandbox "\$@"
	;;
esac
EOS

chmod +x $sandbox_script

case $opsys in
Darwin)
	$cpprog /var/run/resolv.conf $sandbox/var/run/resolv.conf
	;;
Linux)
	if [ -f /run/resolvconf/resolv.conf ]; then
		$mkdirprog $sandbox/run/resolvconf
		$cpprog /run/resolvconf/resolv.conf $sandbox/run/resolvconf/resolv.conf
	fi
	;;
SunOS)
	$cpprog /etc/mnttab $sandbox/etc/mnttab
	;;
*)
esac

echo "Sandbox creation is now complete"

exit 0
