Merge "veyron2/options: Remove unused options and move no longer publicly needed options."
diff --git a/lib/modules/core/core.go b/lib/modules/core/core.go
index c2919fa..c937287 100644
--- a/lib/modules/core/core.go
+++ b/lib/modules/core/core.go
@@ -47,9 +47,6 @@
 // echoClient <name> <text>
 //    invoke <name>.Echo(<text>)
 //
-// exec <command> [args...]
-//    executes the given command with the given arguments
-//
 // proxyd <names>...
 //    runs a proxy server
 package core
@@ -67,5 +64,4 @@
 	ProxyServerCommand   = "proxyd"
 	WSPRCommand          = "wsprd"
 	TestIdentitydCommand = "test_identityd"
-	ExecCommand          = "exec"
 )
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index e5071c2..15a7c6a 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -202,7 +202,7 @@
 func TestExec(t *testing.T) {
 	sh, cleanup := newShell(t)
 	defer cleanup()
-	h, err := sh.Start(core.ExecCommand, nil, []string{"/bin/sh", "-c", "echo hello world"}...)
+	h, err := sh.StartExternalCommand(nil, []string{"/bin/sh", "-c", "echo hello world"}...)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -218,7 +218,7 @@
 func TestExecWithEnv(t *testing.T) {
 	sh, cleanup := newShell(t)
 	defer cleanup()
-	h, err := sh.Start(core.ExecCommand, []string{"BLAH=hello world"}, "/bin/sh", "-c", "printenv BLAH")
+	h, err := sh.StartExternalCommand([]string{"BLAH=hello world"}, "/bin/sh", "-c", "printenv BLAH")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/lib/modules/core/exec.go b/lib/modules/core/exec.go
deleted file mode 100644
index be41d0d..0000000
--- a/lib/modules/core/exec.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package core
-
-import (
-	"io"
-	"syscall"
-
-	"v.io/core/veyron/lib/modules"
-)
-
-func init() {
-	modules.RegisterChild(ExecCommand, "", execCommand)
-}
-
-func execCommand(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	envSlice := []string{}
-	for key, value := range env {
-		envSlice = append(envSlice, key+"="+value)
-	}
-	return syscall.Exec(args[1], args[1:], envSlice)
-}
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index b7e77c0..547ac0d 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -70,6 +70,10 @@
 	return &execHandle{name: name, entryPoint: shellEntryPoint + "=" + name, procErrCh: make(chan error, 1)}
 }
 
+func newExecHandleForExternalCommand(name string) command {
+	return &execHandle{name: name, procErrCh: make(chan error, 1)}
+}
+
 func (eh *execHandle) Stdout() io.Reader {
 	eh.mu.Lock()
 	defer eh.mu.Unlock()
@@ -114,8 +118,16 @@
 	eh.mu.Lock()
 	defer eh.mu.Unlock()
 	eh.sh = sh
-	newargs, newenv := eh.envelope(sh, env, args[1:]...)
-	cmd := exec.Command(os.Args[0], newargs[1:]...)
+	cmdPath := args[0]
+	newargs, newenv := args, env
+
+	// If an entry point is specified, use the envelope execution environment.
+	if eh.entryPoint != "" {
+		cmdPath = os.Args[0]
+		newargs, newenv = eh.envelope(sh, env, args[1:]...)
+	}
+
+	cmd := exec.Command(cmdPath, newargs[1:]...)
 	cmd.Env = newenv
 	stderr, err := newLogfile("stderr", eh.name)
 	if err != nil {
@@ -160,7 +172,6 @@
 		return nil, err
 	}
 	vlog.VI(1).Infof("Started: %q, pid %d", eh.name, cmd.Process.Pid)
-	err = handle.WaitForReady(sh.startTimeout)
 	go func() {
 		eh.procErrCh <- eh.handle.Wait(0)
 		// It's now safe to close eh.stdout, since Wait only returns
@@ -170,7 +181,7 @@
 		eh.stdout.Close()
 	}()
 
-	return eh, err
+	return eh, nil
 }
 
 func (eh *execHandle) Pid() int {
@@ -215,3 +226,7 @@
 
 	return procErr
 }
+
+func (eh *execHandle) WaitForReady(timeout time.Duration) error {
+	return eh.handle.WaitForReady(timeout)
+}
diff --git a/lib/modules/func.go b/lib/modules/func.go
index e7372e6..5c5bc4c 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -5,6 +5,7 @@
 	"io"
 	"os"
 	"sync"
+	"time"
 
 	"v.io/core/veyron2/vlog"
 )
@@ -143,3 +144,7 @@
 	fh.mu.Unlock()
 	return funcErr
 }
+
+func (fh *functionHandle) WaitForReady(time.Duration) error {
+	return nil
+}
diff --git a/lib/modules/registry.go b/lib/modules/registry.go
index 7b01a1e..8331f9c 100644
--- a/lib/modules/registry.go
+++ b/lib/modules/registry.go
@@ -39,7 +39,7 @@
 	return r.cmds[name]
 }
 
-// RegisterChild adds a new command to the registery that will be run
+// RegisterChild adds a new command to the registry that will be run
 // as a subprocess. It must be called before Dispatch or DispatchInTest is
 // called, typically from an init function.
 func RegisterChild(name, help string, main Main) {
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index c80aae7..c6cd898 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -37,6 +37,7 @@
 package modules
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -213,6 +214,14 @@
 	return registry.help(command)
 }
 
+func (sh *Shell) StartExternalCommand(env []string, args ...string) (Handle, error) {
+	if len(args) == 0 {
+		return nil, errors.New("no arguments specified to StartExternalCommand")
+	}
+	c := newExecHandleForExternalCommand(args[0])
+	return sh.startCommand(c, env, args...)
+}
+
 // Start starts the specified command, it returns a Handle which can be
 // used for interacting with that command.
 //
@@ -236,6 +245,26 @@
 // Commands must have already been registered using RegisterFunction
 // or RegisterChild.
 func (sh *Shell) Start(name string, env []string, args ...string) (Handle, error) {
+	cmd := registry.getCommand(name)
+	if cmd == nil {
+		return nil, fmt.Errorf("%s: not registered", name)
+	}
+	expanded := append([]string{name}, sh.expand(args...)...)
+	c := cmd.factory()
+	h, err := sh.startCommand(c, env, expanded...)
+	if err != nil {
+		// If the error is a timeout, then h can be used to recover
+		// any output from the process.
+		return h, err
+	}
+
+	if err := h.WaitForReady(sh.waitTimeout); err != nil {
+		return h, err
+	}
+	return h, nil
+}
+
+func (sh *Shell) startCommand(c command, env []string, args ...string) (Handle, error) {
 	cenv, err := sh.setupCommandEnv(env)
 	if err != nil {
 		return nil, err
@@ -244,16 +273,10 @@
 	if err != nil {
 		return nil, err
 	}
-	cmd := registry.getCommand(name)
-	if cmd == nil {
-		return nil, fmt.Errorf("%s: not registered", name)
-	}
-	expanded := append([]string{name}, sh.expand(args...)...)
-	h, err := cmd.factory().start(sh, p, cenv, expanded...)
+
+	h, err := c.start(sh, p, cenv, args...)
 	if err != nil {
-		// If the error is a timeout, then h can be used to recover
-		// any output from the process.
-		return h, err
+		return nil, err
 	}
 	sh.mu.Lock()
 	sh.handles[h] = struct{}{}
@@ -459,6 +482,11 @@
 
 	// Pid returns the pid of the process running the command
 	Pid() int
+
+	// WaitForReady waits until the child process signals to us that it is
+	// ready. If this does not occur within the given timeout duration, a
+	// timeout error is returned.
+	WaitForReady(timeout time.Duration) error
 }
 
 // command is used to abstract the implementations of inprocess and subprocess
diff --git a/lib/testutil/integration/util.go b/lib/testutil/integration/util.go
index 46a2a6b..208fad1 100644
--- a/lib/testutil/integration/util.go
+++ b/lib/testutil/integration/util.go
@@ -262,9 +262,9 @@
 		locationString = fmt.Sprintf("(requested at %s:%d) ", filepath.Base(file), line)
 	}
 	b.env.t.Logf("%sstarting %s %s", locationString, b.Path(), strings.Join(args, " "))
-	handle, err := b.env.shell.Start("exec", b.envVars, append([]string{b.Path()}, args...)...)
+	handle, err := b.env.shell.StartExternalCommand(b.envVars, append([]string{b.Path()}, args...)...)
 	if err != nil {
-		b.env.t.Fatalf("Start(%v, %v) failed: %v", b.Path(), strings.Join(args, ", "), err)
+		b.env.t.Fatalf("StartExternalCommand(%v, %v) failed: %v", b.Path(), strings.Join(args, ", "), err)
 	}
 	b.env.t.Logf("started PID %d\n", handle.Pid())
 	return &integrationTestBinaryInvocation{
diff --git a/tools/mgmt/device/acl_fmt.go b/tools/mgmt/device/impl/acl_fmt.go
similarity index 99%
rename from tools/mgmt/device/acl_fmt.go
rename to tools/mgmt/device/impl/acl_fmt.go
index 24091bc..05a6b05 100644
--- a/tools/mgmt/device/acl_fmt.go
+++ b/tools/mgmt/device/impl/acl_fmt.go
@@ -1,4 +1,4 @@
-package main
+package impl
 
 import (
 	"fmt"
diff --git a/tools/mgmt/device/acl_impl.go b/tools/mgmt/device/impl/acl_impl.go
similarity index 99%
rename from tools/mgmt/device/acl_impl.go
rename to tools/mgmt/device/impl/acl_impl.go
index e39f68e..dfa0cab 100644
--- a/tools/mgmt/device/acl_impl.go
+++ b/tools/mgmt/device/impl/acl_impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
 
 // Commands to get/set ACLs.
 
diff --git a/tools/mgmt/device/acl_test.go b/tools/mgmt/device/impl/acl_test.go
similarity index 98%
rename from tools/mgmt/device/acl_test.go
rename to tools/mgmt/device/impl/acl_test.go
index a146e82..d195afa 100644
--- a/tools/mgmt/device/acl_test.go
+++ b/tools/mgmt/device/impl/acl_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
 
 import (
 	"bytes"
@@ -10,6 +10,8 @@
 	"v.io/core/veyron2/security"
 	"v.io/core/veyron2/services/security/access"
 	verror "v.io/core/veyron2/verror2"
+
+	"v.io/core/veyron/tools/mgmt/device/impl"
 )
 
 const pkgPath = "v.io/core/veyron/tools/mgmt/device/main"
@@ -31,7 +33,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := endpoint.Name()
@@ -80,7 +82,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := endpoint.Name()
diff --git a/tools/mgmt/device/associate_impl.go b/tools/mgmt/device/impl/associate_impl.go
similarity index 99%
rename from tools/mgmt/device/associate_impl.go
rename to tools/mgmt/device/impl/associate_impl.go
index 1f21191..08d68f8 100644
--- a/tools/mgmt/device/associate_impl.go
+++ b/tools/mgmt/device/impl/associate_impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
 
 import (
 	"fmt"
diff --git a/tools/mgmt/device/devicemanager_mock_test.go b/tools/mgmt/device/impl/devicemanager_mock_test.go
similarity index 99%
rename from tools/mgmt/device/devicemanager_mock_test.go
rename to tools/mgmt/device/impl/devicemanager_mock_test.go
index 7501383..3777043 100644
--- a/tools/mgmt/device/devicemanager_mock_test.go
+++ b/tools/mgmt/device/impl/devicemanager_mock_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
 
 import (
 	"log"
diff --git a/tools/mgmt/device/impl.go b/tools/mgmt/device/impl/impl.go
similarity index 92%
rename from tools/mgmt/device/impl.go
rename to tools/mgmt/device/impl/impl.go
index b62fb96..3a60782 100644
--- a/tools/mgmt/device/impl.go
+++ b/tools/mgmt/device/impl/impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
 
 import (
 	"encoding/json"
@@ -135,14 +135,3 @@
 	}
 	return nil
 }
-
-func root() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "device",
-		Short: "Tool for interacting with the veyron device manager",
-		Long: `
-The device tool facilitates interaction with the veyron device manager.
-`,
-		Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdDescribe, cmdClaim, cmdStop, cmdSuspend, cmdResume, aclRoot()},
-	}
-}
diff --git a/tools/mgmt/device/impl_test.go b/tools/mgmt/device/impl/impl_test.go
similarity index 96%
rename from tools/mgmt/device/impl_test.go
rename to tools/mgmt/device/impl/impl_test.go
index 065574c..dc53e15 100644
--- a/tools/mgmt/device/impl_test.go
+++ b/tools/mgmt/device/impl/impl_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
 
 import (
 	"bytes"
@@ -8,22 +8,12 @@
 	"strings"
 	"testing"
 
-	tsecurity "v.io/core/veyron/lib/testutil/security"
-
-	"v.io/core/veyron2"
 	"v.io/core/veyron2/naming"
 	"v.io/core/veyron2/services/mgmt/device"
 	verror "v.io/core/veyron2/verror2"
-)
 
-func initTest() (shutdown veyron2.Shutdown) {
-	var err error
-	gctx, shutdown = veyron2.Init()
-	if gctx, err = veyron2.SetPrincipal(gctx, tsecurity.NewPrincipal("test-blessing")); err != nil {
-		panic(err)
-	}
-	return shutdown
-}
+	"v.io/core/veyron/tools/mgmt/device/impl"
+)
 
 func TestListCommand(t *testing.T) {
 	shutdown := initTest()
@@ -37,7 +27,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -92,7 +82,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := naming.JoinAddressName(endpoint.String(), "/myapp/1")
@@ -145,7 +135,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -185,7 +175,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -278,7 +268,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -358,7 +348,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	appName := naming.JoinAddressName(endpoint.String(), "")
diff --git a/tools/mgmt/device/instance_impl.go b/tools/mgmt/device/impl/instance_impl.go
similarity index 99%
rename from tools/mgmt/device/instance_impl.go
rename to tools/mgmt/device/impl/instance_impl.go
index b7a1fc4..3210ad4 100644
--- a/tools/mgmt/device/instance_impl.go
+++ b/tools/mgmt/device/impl/instance_impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
 
 // Commands to modify instance.
 
diff --git a/tools/mgmt/device/instance_impl_test.go b/tools/mgmt/device/impl/instance_impl_test.go
similarity index 97%
rename from tools/mgmt/device/instance_impl_test.go
rename to tools/mgmt/device/impl/instance_impl_test.go
index 10f6702..08daed4 100644
--- a/tools/mgmt/device/instance_impl_test.go
+++ b/tools/mgmt/device/impl/instance_impl_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
 
 import (
 	"bytes"
@@ -8,6 +8,8 @@
 
 	"v.io/core/veyron2/naming"
 	verror "v.io/core/veyron2/verror2"
+
+	"v.io/core/veyron/tools/mgmt/device/impl"
 )
 
 func TestStopCommand(t *testing.T) {
@@ -22,7 +24,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	appName := naming.JoinAddressName(endpoint.String(), "")
@@ -97,7 +99,7 @@
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
+	cmd := impl.Root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	appName := naming.JoinAddressName(endpoint.String(), "")
diff --git a/tools/mgmt/device/mock_test.go b/tools/mgmt/device/impl/mock_test.go
similarity index 97%
rename from tools/mgmt/device/mock_test.go
rename to tools/mgmt/device/impl/mock_test.go
index 936fc45..f08b44c 100644
--- a/tools/mgmt/device/mock_test.go
+++ b/tools/mgmt/device/impl/mock_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
 
 import (
 	"fmt"
diff --git a/tools/mgmt/device/impl/root.go b/tools/mgmt/device/impl/root.go
new file mode 100644
index 0000000..9228247
--- /dev/null
+++ b/tools/mgmt/device/impl/root.go
@@ -0,0 +1,24 @@
+package impl
+
+import (
+	"v.io/core/veyron2/context"
+
+	"v.io/lib/cmdline"
+)
+
+var gctx *context.T
+
+func SetGlobalContext(ctx *context.T) {
+	gctx = ctx
+}
+
+func Root() *cmdline.Command {
+	return &cmdline.Command{
+		Name:  "device",
+		Short: "Tool for interacting with the veyron device manager",
+		Long: `
+The device tool facilitates interaction with the veyron device manager.
+`,
+		Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdDescribe, cmdClaim, cmdStop, cmdSuspend, cmdResume, aclRoot()},
+	}
+}
diff --git a/tools/mgmt/device/impl/util_test.go b/tools/mgmt/device/impl/util_test.go
new file mode 100644
index 0000000..e108e49
--- /dev/null
+++ b/tools/mgmt/device/impl/util_test.go
@@ -0,0 +1,27 @@
+package impl_test
+
+import (
+	"v.io/core/veyron2"
+	"v.io/core/veyron2/context"
+
+	"v.io/core/veyron/lib/testutil/security"
+	_ "v.io/core/veyron/profiles"
+	"v.io/core/veyron/tools/mgmt/device/impl"
+)
+
+var gctx *context.T
+
+func initTest() veyron2.Shutdown {
+	ctx, shutdown := veyron2.Init()
+	var err error
+	if ctx, err = veyron2.SetPrincipal(ctx, security.NewPrincipal("test-blessing")); err != nil {
+		panic(err)
+	}
+	gctx = ctx
+	impl.SetGlobalContext(gctx)
+	return func() {
+		shutdown()
+		impl.SetGlobalContext(nil)
+		gctx = nil
+	}
+}
diff --git a/tools/mgmt/device/main.go b/tools/mgmt/device/main.go
index 65f7187..fc24f06 100644
--- a/tools/mgmt/device/main.go
+++ b/tools/mgmt/device/main.go
@@ -7,17 +7,15 @@
 	"os"
 
 	"v.io/core/veyron2"
-	"v.io/core/veyron2/context"
 
 	_ "v.io/core/veyron/profiles"
+	"v.io/core/veyron/tools/mgmt/device/impl"
 )
 
-var gctx *context.T
-
 func main() {
-	var shutdown veyron2.Shutdown
-	gctx, shutdown = veyron2.Init()
-	exitCode := root().Main()
+	gctx, shutdown := veyron2.Init()
+	impl.SetGlobalContext(gctx)
+	exitCode := impl.Root().Main()
 	shutdown()
 	os.Exit(exitCode)
 }
diff --git a/tools/mgmt/test.sh b/tools/mgmt/test.sh
index c42fa1d..0628c47 100755
--- a/tools/mgmt/test.sh
+++ b/tools/mgmt/test.sh
@@ -1,8 +1,29 @@
 #!/bin/bash
 
 # Test the device manager and related services and tools.
+#
+#
+# By default, this script tests the device manager in a fashion amenable
+# to automatic testing: the --single_user is passed to the device
+# manager so that all device manager components run as the same user and
+# no user input (such as an agent pass phrase) is needed.
+#
+# When this script is invoked with the --with_suid <user> flag, it
+# installs the device manager in its more secure multi-account
+# configuration where the device manager runs under the account of the
+# invoker and test apps will be executed as <user>. This mode will
+# require root permisisons to install and may require configuring an
+# agent passphrase.
+#
+# For exanple:
+#
+#   ./test.sh --with_suid vanaguest
+#
+# to test a device manager with multi-account support enabled for app
+# account vanaguest.
+#
 
-source "$(go list -f {{.Dir}} v.io/core/shell/lib)/shell_test.sh"
+ source "$(go list -f {{.Dir}} v.io/core/shell/lib)/shell_test.sh"
 
 # Run the test under the security agent.
 shell_test::enable_agent "$@"
@@ -83,6 +104,12 @@
 }
 
 main() {
+  local -r WITH_SUID="${1:-no}"
+   if [[ "${WITH_SUID}" == "--with_suid" ]]; then
+    local -r SUID_USER="$2"
+    SUDO_USER="root"
+  fi
+
   cd "${WORKDIR}"
   build
 
@@ -90,14 +117,17 @@
   cp "${AGENTD_BIN}" "${SUIDHELPER_BIN}" "${INITHELPER_BIN}" "${DEVICEMANAGER_BIN}" "${BIN_STAGING_DIR}"
   shell_test::setup_server_test
 
-  # TODO(caprita): Expose an option to turn --single_user off, so we can run
-  # test.sh by hand and exercise the code that requires root privileges.
-
   # Install and start device manager.
   DM_INSTALL_DIR=$(shell::tmp_dir)
 
   export VANADIUM_DEVICE_DIR="${DM_INSTALL_DIR}/dm"
-  "${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --single_user -- --veyron.tcp.address=127.0.0.1:0
+
+  if [[ "${WITH_SUID}" == "--with_suid" ]]; then
+    "${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --veyron.tcp.address=127.0.0.1:0
+  else
+      "${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --single_user -- --veyron.tcp.address=127.0.0.1:0
+  fi
+
   "${VRUN}" "${DEVICE_SCRIPT}" start
   local -r DM_NAME=$(hostname)
   DM_EP=$(wait_for_mountentry "${NAMESPACE_BIN}" 5 "${DM_NAME}")
@@ -119,6 +149,12 @@
   # Claim the device as "alice/myworkstation".
   "${DEVICE_BIN}" claim "${DM_NAME}/device" myworkstation
 
+  if [[ "${WITH_SUID}" == "--with_suid" ]]; then
+    "${DEVICE_BIN}" associate add "${DM_NAME}/device"   "${SUID_USER}"  "alice"
+     shell_test::assert_eq   "$("${DEVICE_BIN}" associate list "${DM_NAME}/device")" \
+       "alice ${SUID_USER}" "${LINENO}"
+  fi
+
   # Verify the device's default blessing is as expected.
   shell_test::assert_eq "$("${DEBUG_BIN}" stats read "${DM_NAME}/__debug/stats/security/principal/blessingstore" | head -1 | sed -e 's/^.*Default blessings: '//)" \
     "alice/myworkstation" "${LINENO}"
@@ -173,6 +209,8 @@
   # Verify that the instance shows up when globbing the device manager.
   shell_test::assert_eq "$("${NAMESPACE_BIN}" glob "${DM_NAME}/apps/BINARYD/*/*")" "${INSTANCE_NAME}" "${LINENO}"
 
+  # TODO(rjkroege): Verify that the app is actually running as ${SUID_USER}
+
   # Verify the app's default blessing.
   shell_test::assert_eq "$("${DEBUG_BIN}" stats read "${INSTANCE_NAME}/stats/security/principal/blessingstore" | head -1 | sed -e 's/^.*Default blessings: '//)" \
     "alice/myapp/BINARYD" "${LINENO}"