--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2014 Martin Lucina. 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 OR CONTRIBUTORS 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.
+#
+
+#
+# xr: rumprun-xen stack "driver" script for running application stacks
+#
+
+err() {
+ echo xr: error: "$@" 1>&2
+ exit 1
+}
+
+usage() {
+ cat <<EOM
+usage: xr COMMAND [ args ]
+
+ supported COMMANDs:
+
+ run [ -ipd ] [ -b BLKSPEC ] [ -n NETSPEC ] [ -- ] APP [ args ]
+ APP is the rumprun-xen application to start
+ args will be passed to the application command line
+ -b BLKSPEC configures a block device as hostpath:mountpoint
+ -n NETSPEC configures a network interface as type:method
+ -i attaches to domain console on startup
+ -p creates the domain but leaves it paused
+ -d destroys the domain on poweroff
+
+ When run without -i the local domain id is printed to standard output
+EOM
+ exit 1
+}
+
+# Detect the filesystem type of the image at $1.
+detect_fstype() {
+ ftype=$(file -b $1)
+ case $ftype in
+ "Unix Fast File system"*)
+ echo ffs
+ return 0
+ ;;
+ "ISO 9660"*)
+ echo cd9660
+ return 0
+ ;;
+ *)
+ echo unknown
+ return 1
+ ;;
+ esac
+}
+
+# xr run: Generate configuration and run application stack.
+xr_run() {
+ conf=/tmp/xr.conf.$$
+ xenstore=/tmp/xr.store.$$
+ >${conf}
+ >${xenstore}
+ OPTIND=1
+ nindex=0
+ bindex=0
+ conf_disk=
+ conf_vif=
+ opt_pause=
+ opt_interactive=
+ opt_destroy='on_poweroff="preserve"'
+ while getopts "n:b:pid" opt; do
+ case "$opt" in
+ # -n: NETSPEC: type:method
+ n)
+ iftype=${OPTARG%:*}
+ ifmethod=${OPTARG#*:}
+ [ "$iftype" != "inet" ] && usage
+ [ "$ifmethod" != "dhcp" ] && usage
+ conf_vif="${conf_vif}'',"
+ echo net/${nindex}/type inet >>${xenstore}
+ echo net/${nindex}/method dhcp >>${xenstore}
+ nindex=$(expr $nindex + 1)
+ ;;
+ # -b: BLKSPEC: hostpath:mountpoint
+ b)
+ image=${OPTARG%:*}
+ mountpoint=${OPTARG#*:}
+ [ -n "$image" ] || usage
+ [ -n "$mountpoint" ] || usage
+ [ -f "$image" ] || err File $image does not exist
+ fstype=$(detect_fstype $image)
+ [ $? -ne 0 ] && err File $image: unknown fstype
+ vdev=hd$(echo $bindex | tr '[0-9]' '[a-j]')
+ conf_disk="${conf_disk}'file:$image,$vdev,rw',"
+ echo blk/${bindex}/type etfs >>${xenstore}
+ echo blk/${bindex}/fstype $fstype >>${xenstore}
+ echo blk/${bindex}/mountpoint $mountpoint\
+ >>${xenstore}
+ bindex=$(expr $bindex + 1)
+ ;;
+ # -p: Leave the domain paused after creation.
+ p)
+ opt_pause=1
+ ;;
+ # -i: Attach to domain console on startup.
+ i)
+ opt_interactive=1
+ ;;
+ # -d: Destroy domain on poweroff instead of preserving it.
+ d)
+ opt_destroy='on_poweroff="destroy"'
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ done
+ # Clean up vif and disk, xl does not like trailing commas.
+ conf_vif=$(echo ${conf_vif} | sed -e s/,\$//)
+ [ -n ${conf_vif} ] && conf_vif="vif=[${conf_vif}]"
+ conf_disk=$(echo ${conf_disk} | sed -e s/,\$//)
+ [ -n ${conf_disk} ] && conf_disk="disk=[${conf_disk}]"
+ # Remaining arguments belong to the application.
+ shift $((OPTIND-1))
+ [ "$1" = "--" ] && shift
+ name=rumprun-$(basename $1)
+ app=$(realpath $1)
+ shift
+ # Generate xl configuration file.
+ cat <<EOM >/tmp/xr.conf.$$
+kernel="${app}"
+name="${name}"
+vcpus=1
+memory=16
+on_crash="preserve"
+${opt_destroy}
+extra="$@"
+${conf_vif}
+${conf_disk}
+EOM
+ # Create the domain and leave it paused so that we can get its domid.
+ if ! xl create -p ${conf} >/dev/null; then
+ err xl create failed
+ fi
+ rm ${conf}
+ domid=$(xl domid ${name})
+ # Write provisioning information for domain to xenstore.
+ prefix=/local/domain/${domid}/rumprun
+ cat ${xenstore} | while read line; do
+ xenstore-write ${prefix}/${line}
+ done
+ rm ${xenstore}
+ # Go go go!
+ [ -z "$opt_pause" ] && xl unpause ${domid}
+ if [ -n "$opt_interactive" ]; then
+ exec xl console $domid
+ else
+ echo ${domid}
+ fi
+}
+
+if [ $# -lt 2 ]; then
+ usage
+fi
+if [ $(id -u) -ne 0 ]; then
+ err Must be root
+fi
+
+cmd="$1"
+shift
+case "$cmd" in
+ run)
+ xr_run "$@"
+ ;;
+ *)
+ usage
+ ;;
+esac
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Martin Lucina. 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 OR CONTRIBUTORS 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.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+#include <rump/netconfig.h>
+
+#include <mini-os/os.h>
+#include <mini-os/mm.h>
+#include <mini-os/xenbus.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <isofs/cd9660/cd9660_mount.h>
+
+#include "rumpconfig.h"
+
+static int
+xs_read_netconfig(const char *if_index, char **type, char **method)
+{
+ char *if_type = NULL;
+ char *if_method = NULL;
+ char buf[128];
+ char *xberr = NULL;
+ xenbus_transaction_t txn;
+ int xbretry = 0;
+
+ xberr = xenbus_transaction_start(&txn);
+ if (xberr) {
+ warnx("rumprun_config: xenbus_transaction_start() failed: %s",
+ xberr);
+ return 1;
+ }
+ snprintf(buf, sizeof buf, "rumprun/net/%s/type", if_index);
+ xberr = xenbus_read(txn, buf, &if_type);
+ if (xberr) {
+ warnx("rumprun_config: xenif%s: read %s failed: %s",
+ if_index, buf, xberr);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ return 1;
+ }
+ if (strcmp(if_type, "inet") != 0) {
+ warnx("rumprun_config: xenif%s: unknown type '%s'",
+ if_index, if_type);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ free(if_type);
+ return 1;
+ }
+ snprintf(buf, sizeof buf, "rumprun/net/%s/method", if_index);
+ xberr = xenbus_read(txn, buf, &if_method);
+ if (xberr) {
+ warnx("rumprun_config: xenif%s: read %s failed: %s",
+ if_index, buf, xberr);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ free(if_type);
+ return 1;
+ }
+ if (strcmp(if_method, "dhcp") != 0) {
+ warnx("rumprun_config: xenif%s: unknown method '%s'",
+ if_index, if_method);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ free(if_type);
+ free(if_method);
+ return 1;
+ }
+ xberr = xenbus_transaction_end(txn, 0, &xbretry);
+ if (xberr) {
+ warnx("rumprun_config: xenbus_transaction_end() failed: %s",
+ xberr);
+ free(if_type);
+ free(if_method);
+ return 1;
+ }
+ *type = if_type;
+ *method = if_method;
+ return 0;
+}
+
+static void
+rumprun_config_net(const char *if_index)
+{
+ char *if_type = NULL;
+ char *if_method = NULL;
+ char buf[128];
+ int rv;
+
+ rv = xs_read_netconfig(if_index, &if_type, &if_method);
+ if (rv != 0)
+ return;
+
+ printf("rumprun_config: configuring xenif%s as %s with %s\n",
+ if_index, if_type, if_method);
+ snprintf(buf, sizeof buf, "xenif%s", if_index);
+ if ((rv = rump_pub_netconfig_ifcreate(buf)) != 0) {
+ warnx("rumprun_config: creating %s failed: %d\n", buf, rv);
+ goto out;
+ }
+ if ((rv = rump_pub_netconfig_dhcp_ipv4_oneshot(buf)) != 0) {
+ printf("rumprun_config: dhcp for %s failed: %d\n", buf, rv);
+ goto out;
+ }
+
+out:
+ free(if_type);
+ free(if_method);
+}
+
+static void
+rumprun_deconfig_net(const char *if_index)
+{
+ char *if_type = NULL;
+ char *if_method = NULL;
+ int rv;
+
+ rv = xs_read_netconfig(if_index, &if_type, &if_method);
+ if (rv != 0)
+ return;
+
+ printf("rumprun_config: (not yet) deconfiguring xenif%s\n", if_index);
+#if 0 /* XXX causes dhcpcd from brlib to fall over */
+ snprintf(buf, sizeof buf, "xenif%s", if_index);
+ if ((rv = rump_pub_netconfig_ifdown(buf)) != 0) {
+ warnx("rumprun_config: ifdown %s failed: %d\n", buf, rv);
+ return;
+ }
+ if ((rv = rump_pub_netconfig_ifdestroy(buf)) != 0) {
+ printf("rumprun_config: ifdestroy %s failed: %d\n", buf, rv);
+ return;
+ }
+#endif
+ free(if_type);
+ free(if_method);
+}
+
+static int
+xs_read_blkconfig(const char *blk_index, char **type, char **mountpoint,
+ char **fstype)
+{
+ char *blk_type = NULL;
+ char *blk_mountpoint = NULL;
+ char *blk_fstype = NULL;
+ char buf[128];
+ char *xberr = NULL;
+ xenbus_transaction_t txn;
+ int xbretry = 0;
+
+ xberr = xenbus_transaction_start(&txn);
+ if (xberr) {
+ warnx("rumprun_config: xenbus_transaction_start() failed: %s",
+ xberr);
+ return 1;
+ }
+ snprintf(buf, sizeof buf, "rumprun/blk/%s/type", blk_index);
+ xberr = xenbus_read(txn, buf, &blk_type);
+ if (xberr) {
+ warnx("rumprun_config: xenblk%s: read %s failed: %s",
+ blk_index, buf, xberr);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ return 1;
+ }
+ if (strcmp(blk_type, "etfs") != 0) {
+ warnx("rumprun_config: xenblk%s: unknown type '%s'",
+ blk_index, blk_type);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ free(blk_type);
+ return 1;
+ }
+ snprintf(buf, sizeof buf, "rumprun/blk/%s/mountpoint", blk_index);
+ xberr = xenbus_read(txn, buf, &blk_mountpoint);
+ if (xberr) {
+ warnx("rumprun_config: xenblk%s: read %s failed: %s",
+ blk_index, buf, xberr);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ free(blk_type);
+ return 1;
+ }
+ snprintf(buf, sizeof buf, "rumprun/blk/%s/fstype", blk_index);
+ xberr = xenbus_read(txn, buf, &blk_fstype);
+ if (xberr) {
+ warnx("rumprun_config: xenblk%s: read %s failed: %s",
+ blk_index, buf, xberr);
+ xenbus_transaction_end(txn, 0, &xbretry);
+ free(blk_type);
+ free(blk_mountpoint);
+ return 1;
+ }
+ xberr = xenbus_transaction_end(txn, 0, &xbretry);
+ if (xberr) {
+ warnx("rumprun_config: xenbus_transaction_end() failed: %s",
+ xberr);
+ free(blk_type);
+ free(blk_mountpoint);
+ free(blk_fstype);
+ return 1;
+ }
+ *type = blk_type;
+ *mountpoint = blk_mountpoint;
+ *fstype = blk_fstype;
+ return 0;
+}
+
+static void
+rumprun_config_blk(const char *blk_index)
+{
+ char *blk_type = NULL;
+ char *blk_mountpoint = NULL;
+ char *blk_fstype = NULL;
+ int rv;
+ char key[32],
+ hostpath[32];
+
+ rv = xs_read_blkconfig(blk_index, &blk_type, &blk_mountpoint,
+ &blk_fstype);
+ if (rv != 0)
+ return;
+ if ((strcmp(blk_fstype, "ffs") != 0) &&
+ (strcmp(blk_fstype, "cd9660") != 0)) {
+ warnx("rumprun_config: xenblk%s: unsupported fstype %s\n",
+ blk_index, blk_fstype);
+ goto out;
+ }
+
+ printf("rumprun_config: mounting xenblk%s on %s as %s\n",
+ blk_index, blk_mountpoint, blk_fstype);
+ if ((rv = mkdir(blk_mountpoint, 0777)) != 0) {
+ warnx("rumprun_config: mkdir failed: %d", errno);
+ goto out;
+ }
+ snprintf(key, sizeof key, "/dev/xenblk%s", blk_index);
+ snprintf(hostpath, sizeof hostpath, "blk%s", blk_index);
+ if ((rv = rump_pub_etfs_register(key, hostpath, RUMP_ETFS_BLK)) != 0) {
+ warnx("rumprun_config: etfs_register failed: %d", rv);
+ goto out;
+ }
+ if (strcmp(blk_fstype, "ffs") == 0) {
+ struct ufs_args mntargs = { .fspec = key };
+ if (mount(MOUNT_FFS, blk_mountpoint, 0, &mntargs, sizeof(mntargs)) != 0) {
+ warn("rumprun_config: mount_ffs failed");
+ goto out;
+ }
+ }
+ else if(strcmp(blk_fstype, "cd9660") == 0) {
+ struct iso_args mntargs = { .fspec = key };
+ if (mount(MOUNT_CD9660, blk_mountpoint, MNT_RDONLY, &mntargs, sizeof(mntargs)) != 0) {
+ warn("rumprun_config: mount_cd9660 failed");
+ goto out;
+ }
+ }
+
+out:
+ free(blk_type);
+ free(blk_mountpoint);
+ free(blk_fstype);
+}
+
+static void
+rumprun_deconfig_blk(const char *blk_index)
+{
+ char *blk_type = NULL;
+ char *blk_mountpoint = NULL;
+ char *blk_fstype = NULL;
+ char key[32];
+ int rv;
+
+ rv = xs_read_blkconfig(blk_index, &blk_type, &blk_mountpoint,
+ &blk_fstype);
+ if (rv != 0)
+ return;
+
+ printf("rumprun_config: unmounting xenblk%s from %s\n",
+ blk_index, blk_mountpoint);
+ if (unmount(blk_mountpoint, 0) != 0) {
+ warnx("rumprun_config: unmount failed: %d", errno);
+ /* Continue anyway, the initial mount may have failed. */
+ }
+ snprintf(key, sizeof key, "/dev/xenblk%s", blk_index);
+ if ((rv = rump_pub_etfs_remove(key)) != 0) {
+ warnx("rumprun_config: etfs_remove failed: %d", rv);
+ goto out;
+ }
+
+out:
+ free(blk_type);
+ free(blk_mountpoint);
+ free(blk_fstype);
+}
+
+void
+_rumprun_config(void)
+{
+ char *err = NULL;
+ xenbus_transaction_t txn;
+ char **netdevices = NULL,
+ **blkdevices = NULL;
+ int retry = 0,
+ i;
+
+ err = xenbus_transaction_start(&txn);
+ if (err) {
+ warnx("rumprun_config: xenbus_transaction_start() failed: %s",
+ err);
+ goto out_err;
+ }
+ err = xenbus_ls(txn, "rumprun/net", &netdevices);
+ if (err && strcmp(err, "ENOENT") != 0) {
+ warnx("rumprun_config: xenbus_ls(rumprun/net) failed: %s", err);
+ xenbus_transaction_end(txn, 0, &retry);
+ goto out_err;
+ }
+ err = xenbus_ls(txn, "rumprun/blk", &blkdevices);
+ if (err && strcmp(err, "ENOENT") != 0) {
+ warnx("rumprun_config: xenbus_ls(rumprun/blk) failed: %s", err);
+ xenbus_transaction_end(txn, 0, &retry);
+ goto out_err;
+ }
+ err = xenbus_transaction_end(txn, 0, &retry);
+ if (err) {
+ warnx("rumprun_config: xenbus_transaction_end() failed: %s",
+ err);
+ goto out_err;
+ }
+ if (netdevices) {
+ for(i = 0; netdevices[i]; i++) {
+ rumprun_config_net(netdevices[i]);
+ free(netdevices[i]);
+ }
+ free(netdevices);
+ }
+ if (blkdevices) {
+ for(i = 0; blkdevices[i]; i++) {
+ rumprun_config_blk(blkdevices[i]);
+ free(blkdevices[i]);
+ }
+ free(blkdevices);
+ }
+ return;
+
+out_err:
+ if (netdevices) {
+ for(i = 0; netdevices[i]; i++)
+ free(netdevices[i]);
+ free(netdevices);
+ }
+ if (blkdevices) {
+ for(i = 0; blkdevices[i]; i++)
+ free(blkdevices[i]);
+ free(blkdevices);
+ }
+}
+
+void
+_rumprun_deconfig(void)
+{
+ char *err = NULL;
+ xenbus_transaction_t txn;
+ char **netdevices = NULL,
+ **blkdevices = NULL;
+ int retry = 0,
+ i;
+
+ err = xenbus_transaction_start(&txn);
+ if (err) {
+ warnx("rumprun_config: xenbus_transaction_start() failed: %s",
+ err);
+ goto out_err;
+ }
+ err = xenbus_ls(txn, "rumprun/net", &netdevices);
+ if (err && strcmp(err, "ENOENT") != 0) {
+ warnx("rumprun_config: xenbus_ls(rumprun/net) failed: %s", err);
+ xenbus_transaction_end(txn, 0, &retry);
+ goto out_err;
+ }
+ err = xenbus_ls(txn, "rumprun/blk", &blkdevices);
+ if (err && strcmp(err, "ENOENT") != 0) {
+ warnx("rumprun_config: xenbus_ls(rumprun/blk) failed: %s", err);
+ xenbus_transaction_end(txn, 0, &retry);
+ goto out_err;
+ }
+ err = xenbus_transaction_end(txn, 0, &retry);
+ if (err) {
+ warnx("rumprun_config: xenbus_transaction_end() failed: %s",
+ err);
+ goto out_err;
+ }
+ if (netdevices) {
+ for(i = 0; netdevices[i]; i++) {
+ rumprun_deconfig_net(netdevices[i]);
+ free(netdevices[i]);
+ }
+ free(netdevices);
+ }
+ if (blkdevices) {
+ for(i = 0; blkdevices[i]; i++) {
+ rumprun_deconfig_blk(blkdevices[i]);
+ free(blkdevices[i]);
+ }
+ free(blkdevices);
+ }
+ return;
+
+out_err:
+ if (netdevices) {
+ for(i = 0; netdevices[i]; i++)
+ free(netdevices[i]);
+ free(netdevices);
+ }
+ if (blkdevices) {
+ for(i = 0; blkdevices[i]; i++)
+ free(blkdevices[i]);
+ free(blkdevices);
+ }
+}