vkernel manager

Aggelos Economopoulos aoiko at cc.ece.ntua.gr
Fri Feb 8 23:07:36 PST 2008


Working with vkernels involves a lot of vnconfig, mount, installworld etc
commands and there's always the posibility that you'll try to write to
a filesystem that is in use by a running vkernel. Some months ago I was
using a set of scripts to help with managing vkernel instances and
diskimages. I took some time to convert them to a single script and expand
them a bit (probably adding some bugs in the process). I suppose other
people have their own scripts; if so, please share them.

You use the script like this:

vkernmgr init -m /mnt/vkern -s 1024 -w /usr/src -k /path/to/vkernel test
[creates and formats an 1G disk image, installs base system from /usr/src]

vkernmgr run test [extra vkernel flags, if any]
[ensures no vn device is configured for access to the diskimage, runs
vkernel]

vkernmgr iw test
[ensures no vkernel is using the diskimage, installs world from /usr/src]

vkernmgr {start,stop}vn test
vkernmgr {,u}mount test
vkernmgr halt test
vkernmgr kill test
[These do the obvious; stopvn may need to run umount, mount may need to run
startvn]

Hopefully this will be useful to someone.

#!/bin/sh

# FIXME: vndev busy on newfs sometimes? Then stuck for good, too.
# FIXME: need better error checking
# FIXME: awk will break on paths with spaces
# TODO: ensure no other configuration that has the same disk image is running
#       in startvn, run
# TODO: pass on args to vkernel
# TODO: network setup

loadconf()
{
	vkernrc=default

	if ! test -z $1 ; then
		vkernrc=$1
	fi

	name="$vkernrc"
	. ~/.vkernels/$vkernrc
}

usage()
{
	echo "usage:"
	exit 2
}

is_running()
{
	if test ! -f "/var/run/vkernel_${name}.pid"; then
	    return 1
	fi

	pid=`cat /var/run/vkernel_${name}.pid`

	kill -0 "$pid"
	if test "$?" -ne "0"; then
	    echo "warning: stale pid file /var/run/vkernel_${name}.pid"
	    return 1
	fi
	return 0
}

getvn()
{
	awkcmd=`printf '/covering .* on .*$/ { if ($3 == \"%s\") print substr($1, 1, length($1) - 1)}' $diskimage`
	vndev=`vnconfig -l | awk "$awkcmd"`
}

cmd_stopvn()
{
	getvn
	if test -z "$vndev"; then
		return 0
	fi

	cmd_umount
	vnconfig -u "$vndev"
}

cmd_startvn()
{
	is_running
	if test "$?" -eq "0"; then
	    echo "A virtual kernel is using this image file, sorry"
	    exit 2
	fi
	# see if there already exists a vn device for this disk image
	getvn

	if test ! -z "$vndev"; then
		return 0
	fi

	# get free vn device
	if test -z "$vndev"; then
		vndev=`vnconfig -l | awk -F':' '/.*: not in use$/ {print $1 ; exit 0}'`
	fi

	vnconfig -c -s labels "$vndev" "$diskimage"
}

is_mounted()
{
	for i in `/sbin/mount | awk '/.* on .*/ {print $3}'`; do
		if test "$mountpoint" = "$i"; then
			return 0
		fi
	done
	return 1
}

cmd_mount()
{
	cmd_startvn
	is_mounted
	if test "$?" -eq "0"; then
		return 0
	fi

	/sbin/mount /dev/${vndev}s0a $mountpoint
}

cmd_umount()
{
	is_mounted
	if test "$?" -eq "0"; then
		/sbin/umount $mountpoint
	fi
}

cmd_iw()
{
	cmd_mount
	(cd $world && make DESTDIR=$mountpoint installworld)
}

cmd_init()
{
	args=`getopt m:s:w:k: $*`

	if test "0" -ne "$?"; then
	    usage
	fi
	set -- $args

	for i; do
	    case "$i"
		in
		-m)
		mountpoint="$2"
		shift; shift;;

		-s)
		size=$2
		shift; shift;;

		-w)
		world=$2
		shift; shift;;

		-k)
		kernel=$2
		shift; shift;;

		--)
		shift; break;;
		esac
	done
	name=$1
	if test -z "$name" -o -z "$world" -o -z "$mountpoint" -o -z "$kernel"; then
	    usage
	fi

	printf "[$name]\n"
	printf "mountpoint=%s\n" $mountpoint
	printf "world=%s\n" $world
	printf "kernel=%s\n" $kernel

	configfile="$HOME/.vkernels/$name"

	if test -f "$configfile"; then
	    echo "vkernel config \"$name\" already exists" 1>&2
	    exit 2
	fi

	diskimage="/var/vkernel/$name.rootimg"

	if test ! -d "$HOME/.vkernels"; then
	    mkdir "$HOME/.vkernels"
	    if test "$?" -eq "0"; then
		echo "can't create $HOME/.vkernels"
		exit 1
	    fi
	fi
	printf 'diskimage=%s\n' "$diskimage" >> $configfile
	printf 'memory=%s\n' "64m" >> $configfile
	printf 'mountpoint=%s\n' "$mountpoint" >> $configfile
	printf 'world=%s\n' "$world" >> $configfile
	printf 'kernel=%s\n' "$kernel" >> $configfile
	if test ! -z "$kernelconfig"; then
	    printf 'kernelconfig=%s' "$kernelconfig" >> $configfile
	fi

	dd if=/dev/zero of=$diskimage bs=1m count=$size
	
	cmd_startvn		# sets vndev

	disklabel -r -w ${vndev}s0 auto
	partsize=`disklabel ${vndev}s0 | awk '/^  c:.*/ {print $2}'`
	(disklabel ${vndev}s0 ; echo "  a: $partsize 0 4.2BSD")  | disklabel -R ${vndev}s0 /dev/stdin
	newfs ${vndev}s0a

	cmd_iw

	(cd $world/etc && make DESTDIR=$mountpoint distribution)

	printf "/dev/%ss0a\t/\tufs\trw\t1\t1\n" >> $mountpoint/etc/fstab
	printf "proc\t/proc\tprocfs\trw\t0\t0\n" >> $mountpoint/etc/fstab

	# fugly
	cat /etc/ttys | awk '// {if ($1 == "console") print "console \"/usr/libexec/getty Pc\"         cons25  on  secure"; else if ($2 == "\"/usr/libexec/getty") print $1, $2, $3, $4, "off", $6; else print $0}' > $mountpoint/etc/ttys
}

cmd_run()
{
	cmd_stopvn

	is_running
	if test "$?" -eq "0"; then
	    echo "The vkernel is already running"
	    exit 2
	fi
	for i in $*; do
	    echo $i
	done
	$kernel -p /var/run/vkernel_${name}.pid -m $memory -r $diskimage $*
}

cmd_halt()
{
    if test ! -f "/var/run/vkernel_${name}.pid"; then
	echo "Not running"
	exit 2
    fi
    kill `cat /var/run/vkernel_${name}.pid`
}

cmd_kill()
{
    if test ! -f "/var/run/vkernel_${name}.pid"; then
	echo "Not running"
	exit 2
    fi
    kill -9 `cat /var/run/vkernel_${name}.pid`
    rm -f /var/run/vkernel_${name}.pid
}

cmd="$1"

if test -z "$cmd"; then
	usage
fi

case "$cmd" in
	startvn|stopvn|iw|mount|umount|run|halt|kill)
		shift
		loadconf $*
		shift		# get rid of config name
		# while mount is a reasonable name, defining e.g. mount()
		# shadows the mount command, which may lead to silly
		# bugs. FIXME: kill eval
		eval "cmd_${cmd}" $*
		break
		;;
	init)
		shift
		cmd_init $*
		break
		;;
	*)
		printf "unsupported command: %s\n" $cmd
		usage
esac

exit 0





More information about the Kernel mailing list