veyron/lib/modules: allow Cleanup/Shutdown to access stderr and stdout.

Change-Id: I3447e4908720339ffc0d8f82cc3a77764ce02532
diff --git a/lib/expect/expect.go b/lib/expect/expect.go
index c36bec4..8f0bb72 100644
--- a/lib/expect/expect.go
+++ b/lib/expect/expect.go
@@ -100,16 +100,20 @@
 	s.verbose = v
 }
 
-func (s *Session) log(format string, args ...interface{}) {
+func (s *Session) log(err error, format string, args ...interface{}) {
 	if !s.verbose {
 		return
 	}
 	_, path, line, _ := runtime.Caller(2)
+	errstr := ""
+	if err != nil {
+		errstr = err.Error() + ": "
+	}
 	loc := fmt.Sprintf("%s:%d", filepath.Base(path), line)
 	o := strings.TrimRight(fmt.Sprintf(format, args...), "\n\t ")
-	vlog.VI(2).Infof("%s: %s", loc, o)
+	vlog.VI(2).Infof("%s: %s%s", loc, errstr, o)
 	if s.t == nil {
-		fmt.Fprint(os.Stderr, loc, o)
+		fmt.Fprintf(os.Stderr, "%s: %s%s\n", loc, errstr, o)
 		return
 	}
 	s.t.Log(loc, o)
@@ -181,7 +185,7 @@
 		return
 	}
 	line, err := s.read(readLine)
-	s.log("Expect: %v: %s", err, line)
+	s.log(err, "Expect: %s", line)
 	if err != nil {
 		s.error(err)
 		return
@@ -217,7 +221,7 @@
 		return [][]string{}
 	}
 	l, m, err := s.expectRE(pattern, n)
-	s.log("ExpectRE: %v: %s", err, l)
+	s.log(err, "ExpectVar: %s", l)
 	if err != nil {
 		s.error(err)
 		return [][]string{}
@@ -235,7 +239,7 @@
 		return ""
 	}
 	l, m, err := s.expectRE(name+"=(.*)", 1)
-	s.log("ExpectVar: %v: %s", err, l)
+	s.log(err, "ExpectVar: %s", l)
 	if err != nil {
 		s.error(err)
 		return ""
@@ -255,7 +259,7 @@
 		return ""
 	}
 	l, err := s.read(readLine)
-	s.log("Readline: %v: %s", err, l)
+	s.log(err, "Readline: %s", l)
 	if err != nil {
 		s.error(err)
 	}
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index 6d1731d..a9094bb 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -21,7 +21,7 @@
 
 func TestCommands(t *testing.T) {
 	shell := core.NewShell()
-	defer shell.Cleanup(os.Stderr)
+	defer shell.Cleanup(nil, os.Stderr)
 	for _, c := range []string{core.RootMTCommand, core.MTCommand} {
 		if len(shell.Help(c)) == 0 {
 			t.Fatalf("missing command %q", c)
@@ -37,9 +37,9 @@
 	sh := core.NewShell()
 	return sh, func() {
 		if testing.Verbose() {
-			sh.Cleanup(os.Stderr)
+			sh.Cleanup(os.Stderr, os.Stderr)
 		} else {
-			sh.Cleanup(nil)
+			sh.Cleanup(nil, nil)
 		}
 	}
 }
@@ -56,7 +56,6 @@
 	s.ExpectVar("MT_ADDR")
 	s.ExpectVar("PID")
 	root.CloseStdin()
-	s.Expect("PASS")
 }
 
 func startMountTables(t *testing.T, sh *modules.Shell, mountPoints ...string) (map[string]string, error) {
@@ -235,7 +234,7 @@
 	if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
-	if err = resolver.Shutdown(nil); err != nil {
+	if err = resolver.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 
@@ -252,7 +251,7 @@
 	if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
 		t.Fatalf("got %v, want %v", got, want)
 	}
-	if err := resolver.Shutdown(nil); err != nil {
+	if err := resolver.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 
@@ -261,7 +260,7 @@
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	if err := nsroots.Shutdown(nil); err != nil {
+	if err := nsroots.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 
@@ -276,16 +275,11 @@
 	if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
 		t.Fatalf("got %v, want %v", got, want)
 	}
-	if err := resolver.Shutdown(nil); err != nil {
+	if err := resolver.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 }
 
 func TestHelperProcess(t *testing.T) {
-	if !modules.IsTestHelperProcess() {
-		return
-	}
-	if err := modules.Dispatch(); err != nil {
-		t.Fatalf("failed: %v", err)
-	}
+	modules.DispatchInTest()
 }
diff --git a/lib/modules/core/echo.go b/lib/modules/core/echo.go
index f3c3fce..459e7fe 100644
--- a/lib/modules/core/echo.go
+++ b/lib/modules/core/echo.go
@@ -37,10 +37,10 @@
 }
 
 func echoServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) != 2 {
+	if len(args) != 3 {
 		return fmt.Errorf("wrong # args")
 	}
-	id, mp := args[0], args[1]
+	id, mp := args[1], args[2]
 	disp := &treeDispatcher{id: id}
 	server, err := rt.R().NewServer()
 	if err != nil {
@@ -62,11 +62,11 @@
 }
 
 func echoClient(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) < 2 {
+	if len(args) < 3 {
 		return fmt.Errorf("wrong # args")
 	}
-	name := args[0]
-	args = args[1:]
+	name := args[1]
+	args = args[2:]
 	client := rt.R().Client()
 	for _, a := range args {
 		ctxt := rt.R().NewContext()
diff --git a/lib/modules/core/misc.go b/lib/modules/core/misc.go
index 033fd09..47f6ba9 100644
--- a/lib/modules/core/misc.go
+++ b/lib/modules/core/misc.go
@@ -13,9 +13,9 @@
 
 func sleep(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	d := time.Second
-	if len(args) > 0 {
+	if len(args) > 1 {
 		var err error
-		if d, err = time.ParseDuration(args[0]); err != nil {
+		if d, err = time.ParseDuration(args[1]); err != nil {
 			return err
 		}
 	}
@@ -42,10 +42,10 @@
 }
 
 func mountServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) != 3 {
+	if len(args) != 4 {
 		return fmt.Errorf("wrong # args")
 	}
-	mp, server, ttlstr := args[0], args[1], args[2]
+	mp, server, ttlstr := args[1], args[2], args[3]
 	ttl, err := time.ParseDuration(ttlstr)
 	if err != nil {
 		return fmt.Errorf("failed to parse time from %q", ttlstr)
@@ -59,11 +59,11 @@
 }
 
 func namespaceCache(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) != 1 {
+	if len(args) != 2 {
 		return fmt.Errorf("wrong # args")
 	}
 	disable := true
-	switch args[0] {
+	switch args[1] {
 	case "on":
 		disable = false
 	case "off":
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index d21d2c0..b013862 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -23,14 +23,14 @@
 }
 
 func mountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) != 1 {
+	if len(args) != 2 {
 		return fmt.Errorf("expected exactly one argument: <mount point>")
 	}
 	return runMT(false, stdin, stdout, stderr, env, args...)
 }
 
 func rootMountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) != 0 {
+	if len(args) != 1 {
 		return fmt.Errorf("expected no arguments")
 	}
 	return runMT(true, stdin, stdout, stderr, env, args...)
@@ -44,7 +44,7 @@
 	}
 	mp := ""
 	if !root {
-		mp = args[0]
+		mp = args[1]
 	}
 	mt, err := mounttable.NewMountTable("")
 	if err != nil {
@@ -67,6 +67,7 @@
 
 func ls(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	details := false
+	args = args[1:] // skip over comamnd name
 	if len(args) > 0 && args[0] == "-l" {
 		details = true
 		args = args[1:]
@@ -106,10 +107,10 @@
 type resolver func(ctx context.T, name string) (names []string, err error)
 
 func resolve(fn resolver, stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	if len(args) != 1 {
+	if len(args) != 2 {
 		return fmt.Errorf("wrong # args")
 	}
-	name := args[0]
+	name := args[1]
 	servers, err := fn(rt.R().NewContext(), name)
 	if err != nil {
 		fmt.Fprintf(stdout, "RN=0\n")
@@ -132,5 +133,8 @@
 
 func setNamespaceRoots(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	ns := rt.R().Namespace()
-	return ns.SetRoots(args...)
+	if len(args) < 2 {
+		return fmt.Errorf("wrong # args")
+	}
+	return ns.SetRoots(args[1:]...)
 }
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index b2a12da..5be34de 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -1,11 +1,9 @@
 package modules
 
 import (
-	"bufio"
 	"flag"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"strings"
@@ -137,7 +135,8 @@
 	newargs := append(testFlags(), args...)
 	cmd := exec.Command(os.Args[0], newargs...)
 	cmd.Env = append(sh.mergeOSEnvSlice(), eh.entryPoint)
-	stderr, err := ioutil.TempFile("", "__modules__"+strings.TrimLeft(eh.entryPoint, "-\n\t "))
+	fname := strings.TrimPrefix(eh.entryPoint, ShellEntryPoint+"=")
+	stderr, err := newLogfile(strings.TrimLeft(fname, "-\n\t "))
 	if err != nil {
 		return nil, err
 	}
@@ -164,28 +163,37 @@
 	return eh, err
 }
 
-func (eh *execHandle) Shutdown(output io.Writer) error {
+func (eh *execHandle) Shutdown(stdout, stderr io.Writer) error {
 	eh.mu.Lock()
 	defer eh.mu.Unlock()
 	eh.stdin.Close()
+	logFile := eh.stderr.Name()
 	defer eh.sh.forget(eh)
-	if eh.stderr != nil {
-		defer func() {
-			eh.stderr.Close()
-			os.Remove(eh.stderr.Name())
-		}()
-		if output == nil {
-			return eh.cmd.Wait()
-		}
-		if _, err := eh.stderr.Seek(0, 0); err != nil {
-			return eh.cmd.Wait()
-		}
-		scanner := bufio.NewScanner(eh.stderr)
-		for scanner.Scan() {
-			fmt.Fprintf(output, "%s\n", scanner.Text())
-		}
+
+	defer func() {
+		os.Remove(logFile)
+	}()
+
+	if stdout == nil && stderr == nil {
+		return eh.cmd.Wait()
 	}
-	return eh.cmd.Wait()
+	// Read from stdin before waiting for the child process to ensure
+	// that we get to read all of its output.
+	readTo(eh.stdout, stdout)
+
+	procErr := eh.cmd.Wait()
+
+	// Stderr is buffered to a file, so we can safely read it after we
+	// wait for the process.
+	eh.stderr.Close()
+	stderrFile, err := os.Open(logFile)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "failed to open %q: %s", logFile, err)
+		return procErr
+	}
+	readTo(stderrFile, stderr)
+	stderrFile.Close()
+	return procErr
 }
 
 const ShellEntryPoint = "VEYRON_SHELL_HELPER_PROCESS_ENTRY_POINT"
@@ -196,7 +204,25 @@
 	child.mains[name] = main
 }
 
+// DispatchInTest will execute the requested subproccess command from within
+// a unit test run as a subprocess.
+func DispatchInTest() {
+	if !IsTestHelperProcess() {
+		return
+	}
+	if err := child.dispatch(); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed: %s\n", err)
+		os.Exit(1)
+	}
+	os.Exit(0)
+}
+
+// Dispatch will execute the request subprocess command from a within a
+// a subprocess that is not a unit test.
 func Dispatch() error {
+	if IsTestHelperProcess() {
+		return fmt.Errorf("use DispatchInTest in unittests")
+	}
 	return child.dispatch()
 }
 
diff --git a/lib/modules/func.go b/lib/modules/func.go
index f44127b..6273c64 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -1,7 +1,6 @@
 package modules
 
 import (
-	"bufio"
 	"fmt"
 	"io"
 	"os"
@@ -12,12 +11,13 @@
 	r, w *os.File
 }
 type functionHandle struct {
-	mu                    sync.Mutex
-	main                  Main
-	stdin, stderr, stdout pipe
-	err                   error
-	sh                    *Shell
-	wg                    sync.WaitGroup
+	mu            sync.Mutex
+	main          Main
+	stdin, stdout pipe
+	stderr        *os.File
+	err           error
+	sh            *Shell
+	wg            sync.WaitGroup
 }
 
 func newFunctionHandle(main Main) command {
@@ -33,7 +33,7 @@
 func (fh *functionHandle) Stderr() io.Reader {
 	fh.mu.Lock()
 	defer fh.mu.Unlock()
-	return fh.stderr.r
+	return os.NewFile(fh.stderr.Fd(), "stderr")
 }
 
 func (fh *functionHandle) Stdin() io.Writer {
@@ -52,19 +52,24 @@
 	fh.mu.Lock()
 	defer fh.mu.Unlock()
 	fh.sh = sh
-	for _, p := range []*pipe{&fh.stdin, &fh.stdout, &fh.stderr} {
+	for _, p := range []*pipe{&fh.stdin, &fh.stdout} {
 		var err error
 		if p.r, p.w, err = os.Pipe(); err != nil {
 			return nil, err
 		}
 	}
+	stderr, err := newLogfile(args[0])
+	if err != nil {
+		return nil, err
+	}
+	fh.stderr = stderr
 	fh.wg.Add(1)
 
 	go func() {
 		fh.mu.Lock()
 		stdin := fh.stdin.r
 		stdout := fh.stdout.w
-		stderr := fh.stderr.w
+		stderr := fh.stderr
 		main := fh.main
 		fh.mu.Unlock()
 
@@ -76,7 +81,6 @@
 		fh.mu.Lock()
 		fh.stdin.r.Close()
 		fh.stdout.w.Close()
-		fh.stderr.w.Close()
 		fh.err = err
 		fh.mu.Unlock()
 		fh.wg.Done()
@@ -84,26 +88,34 @@
 	return fh, nil
 }
 
-func (fh *functionHandle) Shutdown(output io.Writer) error {
+func (fh *functionHandle) Shutdown(stdout_w, stderr_w io.Writer) error {
 	fh.mu.Lock()
 	fh.stdin.w.Close()
-	stderr := fh.stderr.r
+	stdout := fh.stdout.r
+	stderr := fh.stderr
 	fh.mu.Unlock()
 
-	if output != nil {
-		scanner := bufio.NewScanner(stderr)
-		for scanner.Scan() {
-			l := scanner.Text()
-			fmt.Fprintf(output, "%s\n", l)
-		}
+	// Read stdout until EOF to ensure that we read all of it.
+	readTo(stdout, stdout_w)
+	fh.wg.Wait()
+
+	fh.mu.Lock()
+	funcErr := fh.err
+	fh.mu.Unlock()
+
+	// Safe to close stderr now.
+	stderr.Close()
+	stderr, err := os.Open(stderr.Name())
+	if err == nil {
+		readTo(stderr, stderr_w)
+		stderr.Close()
+	} else {
+		fmt.Fprintf(os.Stderr, "failed to open %q: %s", stderr.Name(), err)
 	}
 
-	fh.wg.Wait()
 	fh.mu.Lock()
 	fh.stdout.r.Close()
-	fh.stderr.r.Close()
-	err := fh.err
 	fh.sh.forget(fh)
 	fh.mu.Unlock()
-	return err
+	return funcErr
 }
diff --git a/lib/modules/modules_internal_test.go b/lib/modules/modules_internal_test.go
index 000819b..c5e686e 100644
--- a/lib/modules/modules_internal_test.go
+++ b/lib/modules/modules_internal_test.go
@@ -43,7 +43,7 @@
 	assertNumHandles(t, sh, 2)
 
 	for i, h := range []Handle{hs, hf} {
-		if got := h.Shutdown(nil); got != nil {
+		if got := h.Shutdown(nil, nil); got != nil {
 			t.Errorf("%d: got %q, want %q", i, got, nil)
 		}
 	}
@@ -53,6 +53,6 @@
 	hf, _ = sh.Start("echof", "c")
 	assertNumHandles(t, sh, 2)
 
-	sh.Cleanup(nil)
+	sh.Cleanup(nil, nil)
 	assertNumHandles(t, sh, 0)
 }
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index cd7db53..7320eb6 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -2,9 +2,9 @@
 
 import (
 	"bufio"
+	"bytes"
 	"fmt"
 	"io"
-	"os"
 	"testing"
 	"time"
 
@@ -13,9 +13,31 @@
 
 func init() {
 	modules.RegisterChild("envtest", PrintEnv)
+	modules.RegisterChild("echo", Echo)
 	modules.RegisterChild("errortest", ErrorMain)
 }
 
+func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	for _, a := range args {
+		fmt.Fprintf(stdout, "stdout: %s\n", a)
+		fmt.Fprintf(stderr, "stderr: %s\n", a)
+	}
+	return nil
+}
+
+func Print(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	for _, a := range args {
+		if v := env[a]; len(v) > 0 {
+			fmt.Fprintf(stdout, a+"="+v+"\n")
+		} else {
+			fmt.Fprintf(stderr, "missing %s\n", a)
+		}
+	}
+	modules.WaitForEOF(stdin)
+	fmt.Fprintf(stdout, "done\n")
+	return nil
+}
+
 func PrintEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	for _, a := range args {
 		if v := env[a]; len(v) > 0 {
@@ -53,7 +75,14 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer func() {
-		sh.Cleanup(os.Stderr)
+		var stdout, stderr bytes.Buffer
+		sh.Cleanup(&stdout, &stderr)
+		if len(stdout.String()) != 0 {
+			t.Errorf("unexpected stdout: %q", stdout.String())
+		}
+		if len(stderr.String()) != 0 {
+			t.Errorf("unexpected stderr: %q", stderr.String())
+		}
 	}()
 	scanner := bufio.NewScanner(h.Stdout())
 	if !waitForInput(scanner) {
@@ -71,13 +100,14 @@
 	if got, want := scanner.Text(), "done"; got != want {
 		t.Errorf("got %q, want %q", got, want)
 	}
-	if err := h.Shutdown(nil); err != nil {
+	if err := h.Shutdown(nil, nil); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 }
 
 func TestChild(t *testing.T) {
 	sh := modules.NewShell()
+	defer sh.Cleanup(nil, nil)
 	key, val := "simpleVar", "foo & bar"
 	sh.SetVar(key, val)
 	sh.AddSubprocess("envtest", "envtest: <variables to print>...")
@@ -86,6 +116,7 @@
 
 func TestFunction(t *testing.T) {
 	sh := modules.NewShell()
+	defer sh.Cleanup(nil, nil)
 	key, val := "simpleVar", "foo & bar & baz"
 	sh.SetVar(key, val)
 	sh.AddFunction("envtest", PrintEnv, "envtest: <variables to print>...")
@@ -94,35 +125,67 @@
 
 func TestErrorChild(t *testing.T) {
 	sh := modules.NewShell()
+	defer sh.Cleanup(nil, nil)
 	sh.AddSubprocess("errortest", "")
 	h, err := sh.Start("errortest")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	if got, want := h.Shutdown(nil), "exit status 1"; got == nil || got.Error() != want {
+	if got, want := h.Shutdown(nil, nil), "exit status 1"; got == nil || got.Error() != want {
 		t.Errorf("got %q, want %q", got, want)
 	}
 }
 
+func testShutdown(t *testing.T, sh *modules.Shell) {
+	result := ""
+	args := []string{"a", "b c", "ddd"}
+	if _, err := sh.Start("echo", args...); err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	var stdoutBuf bytes.Buffer
+	var stderrBuf bytes.Buffer
+	sh.Cleanup(&stdoutBuf, &stderrBuf)
+	stdoutOutput, stderrOutput := "stdout: echo\n", "stderr: echo\n"
+	for _, a := range args {
+		stdoutOutput += fmt.Sprintf("stdout: %s\n", a)
+		stderrOutput += fmt.Sprintf("stderr: %s\n", a)
+	}
+	if got, want := stdoutBuf.String(), stdoutOutput+result; got != want {
+		t.Errorf("got %q want %q", got, want)
+	}
+	if got, want := stderrBuf.String(), stderrOutput; got != want {
+		t.Errorf("got %q want %q", got, want)
+	}
+
+}
+
+func TestShutdownSubprocess(t *testing.T) {
+	sh := modules.NewShell()
+	sh.AddSubprocess("echo", "[args]*")
+	testShutdown(t, sh)
+}
+
+func TestShutdownFunction(t *testing.T) {
+	sh := modules.NewShell()
+	sh.AddFunction("echo", Echo, "[args]*")
+	testShutdown(t, sh)
+}
+
 func TestErrorFunc(t *testing.T) {
 	sh := modules.NewShell()
+	defer sh.Cleanup(nil, nil)
 	sh.AddFunction("errortest", ErrorMain, "")
 	h, err := sh.Start("errortest")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	if got, want := h.Shutdown(nil), "an error"; got != nil && got.Error() != want {
+	if got, want := h.Shutdown(nil, nil), "an error"; got != nil && got.Error() != want {
 		t.Errorf("got %q, want %q", got, want)
 	}
 }
 
 func TestHelperProcess(t *testing.T) {
-	if !modules.IsTestHelperProcess() {
-		return
-	}
-	if err := modules.Dispatch(); err != nil {
-		t.Fatalf("failed: %v", err)
-	}
+	modules.DispatchInTest()
 }
 
 // TODO(cnicolaou): more complete tests for environment variables,
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 561e3b2..26c3869 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -175,7 +175,7 @@
 		sh.mu.Unlock()
 		return nil, fmt.Errorf("command %q is not available", command)
 	}
-	expanded := sh.expand(args...)
+	expanded := append([]string{command}, sh.expand(args...)...)
 	sh.mu.Unlock()
 	h, err := cmd.factory().start(sh, expanded...)
 	if err != nil {
@@ -238,10 +238,9 @@
 }
 
 // Cleanup calls Shutdown on all of the Handles currently being tracked
-// by the Shell. Any buffered output from the command's stderr stream
-// will be written to the supplied io.Writer. If the io.Writer is nil
-// then any such output is lost.
-func (sh *Shell) Cleanup(output io.Writer) {
+// by the Shell and writes to stdout and stderr as per the Shutdown
+// method in the Handle interface.
+func (sh *Shell) Cleanup(stdout, stderr io.Writer) {
 	sh.mu.Lock()
 	handles := make(map[Handle]struct{})
 	for k, v := range sh.handles {
@@ -250,7 +249,7 @@
 	sh.handles = make(map[Handle]struct{})
 	sh.mu.Unlock()
 	for k, _ := range handles {
-		k.Shutdown(output)
+		k.Shutdown(stdout, stderr)
 	}
 	if len(sh.idfile) > 0 {
 		os.Remove(sh.idfile)
@@ -277,10 +276,11 @@
 	CloseStdin()
 
 	// Shutdown closes the Stdin for the command and then reads output
-	// from the command's stdout until it encounters EOF and writes that
-	// output to the supplied io.Writer. It returns any error returned by
-	// the command.
-	Shutdown(io.Writer) error
+	// from the command's stdout until it encounters EOF, waits for
+	// the command to complete and then reads all of its stderr output.
+	// The stdout and stderr contents are written to the corresponding
+	// io.Writers if they are non-nil, otherwise the content is discarded.
+	Shutdown(stdout, stderr io.Writer) error
 }
 
 // command is used to abstract the implementations of inprocess and subprocess
diff --git a/lib/modules/util.go b/lib/modules/util.go
new file mode 100644
index 0000000..21fc4c2
--- /dev/null
+++ b/lib/modules/util.go
@@ -0,0 +1,27 @@
+package modules
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+)
+
+func newLogfile(prefix string) (*os.File, error) {
+	f, err := ioutil.TempFile("", "__modules__"+prefix)
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}
+
+func readTo(r io.Reader, w io.Writer) {
+	if w == nil {
+		return
+	}
+	scanner := bufio.NewScanner(r)
+	for scanner.Scan() {
+		fmt.Fprintf(w, "%s\n", scanner.Text())
+	}
+}
diff --git a/tools/naming/simulator/commands.go b/tools/naming/simulator/commands.go
index 3ec00dc..b11a3da 100644
--- a/tools/naming/simulator/commands.go
+++ b/tools/naming/simulator/commands.go
@@ -105,7 +105,7 @@
 
 func readStderr(state *cmdState) (string, error) {
 	var b bytes.Buffer
-	if err := state.Handle.Shutdown(&b); err != nil && err != io.EOF {
+	if err := state.Handle.Shutdown(nil, &b); err != nil && err != io.EOF {
 		return b.String(), err
 	}
 	return b.String(), nil
@@ -182,7 +182,7 @@
 func quit(sh *modules.Shell, _ *cmdState, args ...string) (string, error) {
 	r := ""
 	for k, h := range handles {
-		if err := h.Handle.Shutdown(os.Stdout); err != nil {
+		if err := h.Handle.Shutdown(os.Stdout, os.Stdout); err != nil {
 			r += fmt.Sprintf("%s: %v\n", k, err)
 		} else {
 			r += fmt.Sprintf("%s: ok\n", k)
diff --git a/tools/naming/simulator/driver.go b/tools/naming/simulator/driver.go
index b9c91ba..60028b5 100644
--- a/tools/naming/simulator/driver.go
+++ b/tools/naming/simulator/driver.go
@@ -100,7 +100,6 @@
 	if os.Getenv(modules.ShellEntryPoint) != "" {
 		// Subprocess, run the requested command.
 		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stdout, "failed: %v\n", err)
 			fmt.Fprintf(os.Stderr, "failed: %v\n", err)
 			return
 		}
@@ -108,7 +107,7 @@
 	}
 
 	shell := modules.NewShell()
-	defer shell.Cleanup(os.Stderr)
+	defer shell.Cleanup(os.Stderr, os.Stderr)
 	if os.Getenv("VEYRON_IDENTITY") == "" {
 		shell.CreateAndUseNewID()
 	}
diff --git a/tools/naming/simulator/t.scr b/tools/naming/simulator/t.scr
deleted file mode 100644
index adf0571..0000000
--- a/tools/naming/simulator/t.scr
+++ /dev/null
@@ -1,5 +0,0 @@
-mount a b
-set w=$_
-read $w
-read $w
-wait $w
diff --git a/tools/naming/simulator/test.sh b/tools/naming/simulator/test.sh
new file mode 100755
index 0000000..040625c
--- /dev/null
+++ b/tools/naming/simulator/test.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Test the simulator command-line tool.
+#
+
+source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
+
+main() {
+  # Build binaries.
+  cd "${TMPDIR}"
+  local -r PKG=veyron.io/veyron/veyron/tools/naming/simulator
+  veyron go build "${PKG}" || shell_test::fail "line ${LINENO}: failed to build simulator"
+
+  local -r DIR=$(shell::go_package_dir "${PKG}")
+  local file
+  for file in ${DIR}/*.scr; do
+    echo $file
+    ./simulator < $file > /dev/null 2>&1 || shell_test::fail "failed for $file"
+  done
+  shell_test::pass
+}
+
+main "$@"