Merge "veyron/runtimes/google/ipc: Fix race in choosing an error to return when the flow closes."
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.go b/lib/modules/core/core.go
index 98a6656..ccb14dd 100644
--- a/lib/modules/core/core.go
+++ b/lib/modules/core/core.go
@@ -87,7 +87,7 @@
 	prints the current time`)
 	shell.AddFunction(NamespaceCacheCommand, namespaceCache, `on|off
 	turns the namespace cache on or off`)
-	shell.AddFunction(MountCommand, mountServer, `<mountpoint> <server> <ttl>
+	shell.AddFunction(MountCommand, mountServer, `<mountpoint> <server> <ttl> [M][R]
 	invokes namespace.Mount(<mountpoint>, <server>, <ttl>)`)
 	shell.AddSubprocess(EchoClientCommand, `<name> <message>...
 	invokes name.Echo(message)`)
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..e70aa02 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,28 +42,39 @@
 }
 
 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]
+	var opts []naming.MountOpt
+	for _, arg := range args[4:] {
+		for _, c := range arg {
+			switch c {
+			case 'R':
+				opts = append(opts, naming.ReplaceMountOpt(true))
+			case 'M':
+				opts = append(opts, naming.ServesMountTableOpt(true))
+			}
+		}
+	}
+	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)
 	}
 	ns := rt.R().Namespace()
-	if err := ns.Mount(rt.R().NewContext(), mp, server, ttl); err != nil {
+	if err := ns.Mount(rt.R().NewContext(), mp, server, ttl, opts...); err != nil {
 		return err
 	}
-	fmt.Fprintf(stdout, "Mount(%s, %s, %s)\n", mp, server, ttl)
+	fmt.Fprintf(stdout, "Mount(%s, %s, %s, %v)\n", mp, server, ttl, opts)
 	return nil
 }
 
 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/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index f4e177b..6f8539f 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -159,28 +159,29 @@
 	return false
 }
 
+func getRetryTimeoutOpt(opts []ipc.CallOpt) (time.Duration, bool) {
+	for _, o := range opts {
+		if r, ok := o.(veyron2.RetryTimeoutOpt); ok {
+			return time.Duration(r), true
+		}
+	}
+	return 0, false
+}
+
 func (c *client) StartCall(ctx context.T, name, method string, args []interface{}, opts ...ipc.CallOpt) (ipc.Call, error) {
 	defer vlog.LogCall()()
-	var retry = true
+	// Context specified deadline.
 	deadline, hasDeadline := ctx.Deadline()
 	if !hasDeadline {
-		// If no deadline is set, use the default
+		// Default deadline.
 		deadline = time.Now().Add(defaultCallTimeout)
 	}
-	for _, o := range opts {
-		r, ok := o.(veyron2.RetryTimeoutOpt)
-		if !ok {
-			continue
-		}
-		if r == 0 {
-			retry = false
-		} else {
-			deadline = time.Now().Add(time.Duration(r))
-		}
-		break
+	if r, ok := getRetryTimeoutOpt(opts); ok {
+		// Caller specified deadline.
+		deadline = time.Now().Add(time.Duration(r))
 	}
 	var lastErr verror.E
-	for retries := 0; deadline.After(time.Now()); retries++ {
+	for retries := 0; ; retries++ {
 		if retries != 0 {
 			if !backoff(retries, deadline) {
 				break
@@ -191,23 +192,37 @@
 			return call, nil
 		}
 		lastErr = err
-		if !retry || !retriable(err) {
+		if deadline.After(time.Now()) || !retriable(err) {
 			break
 		}
 	}
 	return nil, lastErr
 }
 
+func getNoResolveOpt(opts []ipc.CallOpt) bool {
+	for _, o := range opts {
+		if r, ok := o.(veyron2.NoResolveOpt); ok {
+			return bool(r)
+		}
+	}
+	return false
+}
+
 // startCall ensures StartCall always returns verror.E.
 func (c *client) startCall(ctx context.T, name, method string, args []interface{}, opts ...ipc.CallOpt) (ipc.Call, verror.E) {
 	if ctx == nil {
 		return nil, verror.BadArgf("ipc: %s.%s called with nil context", name, method)
 	}
 	ctx, _ = vtrace.WithNewSpan(ctx, fmt.Sprintf("Client Call: %s.%s", name, method))
-
-	servers, err := c.ns.Resolve(ctx, name)
-	if err != nil {
-		return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
+	// Resolve name unless told not to.
+	var servers []string
+	if getNoResolveOpt(opts) {
+		servers = []string{name}
+	} else {
+		var err error
+		if servers, err = c.ns.Resolve(ctx, name); err != nil {
+			return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
+		}
 	}
 	// Try all servers, and if none of them are authorized for the call then return the error of the last server
 	// that was tried.
diff --git a/runtimes/google/ipc/debug_test.go b/runtimes/google/ipc/debug_test.go
index 6bcfc69..887bdec 100644
--- a/runtimes/google/ipc/debug_test.go
+++ b/runtimes/google/ipc/debug_test.go
@@ -5,6 +5,7 @@
 	"sort"
 	"testing"
 
+	"veyron.io/veyron/veyron2"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/services/mounttable/types"
@@ -37,7 +38,7 @@
 	ctx := testContext()
 	// Call the Foo method on ""
 	{
-		addr := naming.JoinAddressName(ep.String(), "//")
+		addr := naming.JoinAddressName(ep.String(), "")
 		call, err := client.StartCall(ctx, addr, "Foo", nil)
 		if err != nil {
 			t.Fatalf("client.StartCall failed: %v", err)
@@ -52,8 +53,8 @@
 	}
 	// Call Glob on __debug
 	{
-		addr := naming.JoinAddressName(ep.String(), "//__debug")
-		call, err := client.StartCall(ctx, addr, "Glob", []interface{}{"*"})
+		addr := naming.JoinAddressName(ep.String(), "__debug")
+		call, err := client.StartCall(ctx, addr, "Glob", []interface{}{"*"}, veyron2.NoResolveOpt(true))
 		if err != nil {
 			t.Fatalf("client.StartCall failed: %v", err)
 		}
@@ -82,8 +83,8 @@
 	{
 		foo := stats.NewString("testing/foo")
 		foo.Set("The quick brown fox jumps over the lazy dog")
-		addr := naming.JoinAddressName(ep.String(), "//__debug/stats/testing/foo")
-		call, err := client.StartCall(ctx, addr, "Value", nil)
+		addr := naming.JoinAddressName(ep.String(), "__debug/stats/testing/foo")
+		call, err := client.StartCall(ctx, addr, "Value", nil, veyron2.NoResolveOpt(true))
 		if err != nil {
 			t.Fatalf("client.StartCall failed: %v", err)
 		}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 1174d02..5c4b391 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -941,8 +941,8 @@
 	defer publisher.WaitForStop()
 	defer publisher.Stop()
 	publisher.AddName("incompatible")
-	publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@")
-	publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@")
+	publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@", false)
+	publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@", false)
 
 	_, err := b.client.StartCall(testContext(), "incompatible/suffix", "Echo", []interface{}{"foo"})
 	if !strings.Contains(err.Error(), version.NoCompatibleVersionErr.Error()) {
@@ -950,7 +950,7 @@
 	}
 
 	// Now add a server with a compatible endpoint and try again.
-	publisher.AddServer("/" + b.ep.String())
+	publisher.AddServer("/"+b.ep.String(), false)
 	publisher.AddName("incompatible")
 
 	call, err := b.client.StartCall(testContext(), "incompatible/suffix", "Echo", []interface{}{"foo"})
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 3188aeb..7665b83 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -204,7 +204,7 @@
 		}(ln, ep)
 	}
 	s.Unlock()
-	s.publisher.AddServer(s.publishEP(ep))
+	s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
 	return ep, nil
 }
 
@@ -340,9 +340,9 @@
 			s.proxyListenLoop(ln, ep, proxy)
 			s.active.Done()
 		}(pln, pep, listenSpec.Proxy)
-		s.publisher.AddServer(s.publishEP(pep))
+		s.publisher.AddServer(s.publishEP(pep), s.servesMountTable)
 	} else {
-		s.publisher.AddServer(s.publishEP(ep))
+		s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
 	}
 	s.Unlock()
 	return ep, nil
@@ -393,7 +393,7 @@
 			}
 		}
 		// (3) reconnected, publish new address
-		s.publisher.AddServer(s.publishEP(ep))
+		s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
 		s.Lock()
 		s.listeners[ln] = nil
 		s.Unlock()
@@ -459,7 +459,7 @@
 			switch setting.Name() {
 			case ipc.NewAddrsSetting:
 				vlog.Infof("Added some addresses: %q", v)
-				s.applyChange(dhcpl, v, publisher.AddServer)
+				s.applyChange(dhcpl, v, func(name string) { publisher.AddServer(name, s.servesMountTable) })
 			case ipc.RmAddrsSetting:
 				vlog.Infof("Removed some addresses: %q", v)
 				s.applyChange(dhcpl, v, publisher.RemoveServer)
diff --git a/runtimes/google/lib/publisher/publisher.go b/runtimes/google/lib/publisher/publisher.go
index 25db23f..83468e2 100644
--- a/runtimes/google/lib/publisher/publisher.go
+++ b/runtimes/google/lib/publisher/publisher.go
@@ -16,7 +16,7 @@
 // Publisher manages the publishing of servers in mounttable.
 type Publisher interface {
 	// AddServer adds a new server to be mounted.
-	AddServer(server string)
+	AddServer(server string, ServesMountTable bool)
 	// RemoveServer removes a server from the list of mounts.
 	RemoveServer(server string)
 	// AddName adds a new name for all servers to be mounted as.
@@ -49,6 +49,7 @@
 
 type addServerCmd struct {
 	server string        // server to add
+	mt     bool          // true if server serves a mount table
 	done   chan struct{} // closed when the cmd is done
 }
 
@@ -87,9 +88,9 @@
 	}
 }
 
-func (p *publisher) AddServer(server string) {
+func (p *publisher) AddServer(server string, mt bool) {
 	done := make(chan struct{})
-	if p.sendCmd(addServerCmd{server, done}) {
+	if p.sendCmd(addServerCmd{server, mt, done}) {
 		<-done
 	}
 }
@@ -161,7 +162,7 @@
 				vlog.VI(2).Info("ipc pub: exit runLoop")
 				return
 			case addServerCmd:
-				state.addServer(tcmd.server)
+				state.addServer(tcmd.server, tcmd.mt)
 				close(tcmd.done)
 			case removeServerCmd:
 				state.removeServer(tcmd.server)
@@ -192,6 +193,7 @@
 	deadline time.Time                 // deadline for the next sync call
 	names    []string                  // names that have been added
 	servers  map[string]bool           // servers that have been added
+	servesMT map[string]bool           // true if server is a mount table server
 	mounts   map[mountKey]*mountStatus // map each (name,server) to its status
 }
 
@@ -214,6 +216,7 @@
 		period:   period,
 		deadline: time.Now().Add(period),
 		servers:  make(map[string]bool),
+		servesMT: make(map[string]bool),
 		mounts:   make(map[mountKey]*mountStatus),
 	}
 }
@@ -234,19 +237,20 @@
 	for server, _ := range ps.servers {
 		status := new(mountStatus)
 		ps.mounts[mountKey{name, server}] = status
-		ps.mount(name, server, status)
+		ps.mount(name, server, status, ps.servesMT[server])
 	}
 }
 
-func (ps *pubState) addServer(server string) {
+func (ps *pubState) addServer(server string, servesMT bool) {
 	// Each non-dup server that is added causes new mounts to be created for all
 	// existing names.
 	if !ps.servers[server] {
 		ps.servers[server] = true
+		ps.servers[server] = servesMT
 		for _, name := range ps.names {
 			status := new(mountStatus)
 			ps.mounts[mountKey{name, server}] = status
-			ps.mount(name, server, status)
+			ps.mount(name, server, status, servesMT)
 		}
 	}
 }
@@ -263,13 +267,13 @@
 	}
 }
 
-func (ps *pubState) mount(name, server string, status *mountStatus) {
+func (ps *pubState) mount(name, server string, status *mountStatus, servesMT bool) {
 	// Always mount with ttl = period + slack, regardless of whether this is
 	// triggered by a newly added server or name, or by sync.  The next call
 	// to sync will occur within the next period, and refresh all mounts.
 	ttl := ps.period + mountTTLSlack
 	status.lastMount = time.Now()
-	status.lastMountErr = ps.ns.Mount(ps.ctx, name, server, ttl)
+	status.lastMountErr = ps.ns.Mount(ps.ctx, name, server, ttl, naming.ServesMountTableOpt(servesMT))
 	if status.lastMountErr != nil {
 		vlog.Errorf("ipc pub: couldn't mount(%v, %v, %v): %v", name, server, ttl, status.lastMountErr)
 	} else {
@@ -284,7 +288,7 @@
 			// Desired state is "unmounted", failed at previous attempt. Retry.
 			ps.unmount(key.name, key.server, status)
 		} else {
-			ps.mount(key.name, key.server, status)
+			ps.mount(key.name, key.server, status, ps.servesMT[key.server])
 		}
 	}
 }
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index 49cc811..35d5c01 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -5,13 +5,15 @@
 
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/services/mounttable/types"
 	"veyron.io/veyron/veyron2/vlog"
 )
 
 // mountIntoMountTable mounts a single server into a single mount table.
-func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration) error {
+func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration, flags types.MountFlag) error {
 	ctx, _ = ctx.WithTimeout(callTimeout)
-	call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds())})
+	call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags})
 	if err != nil {
 		return err
 	}
@@ -34,8 +36,24 @@
 	return err
 }
 
-func (ns *namespace) Mount(ctx context.T, name, server string, ttl time.Duration) error {
+func (ns *namespace) Mount(ctx context.T, name, server string, ttl time.Duration, opts ...naming.MountOpt) error {
 	defer vlog.LogCall()()
+
+	var flags types.MountFlag
+	for _, o := range opts {
+		// NB: used a switch since we'll be adding more options.
+		switch v := o.(type) {
+		case naming.ReplaceMountOpt:
+			if v {
+				flags |= types.MountFlag(types.Replace)
+			}
+		case naming.ServesMountTableOpt:
+			if v {
+				flags |= types.MountFlag(types.MT)
+			}
+		}
+	}
+
 	// Resolve to all the mount tables implementing name.
 	mtServers, err := ns.ResolveToMountTable(ctx, name)
 	if err != nil {
@@ -45,7 +63,7 @@
 	c := make(chan error, len(mtServers))
 	for _, mt := range mtServers {
 		go func() {
-			c <- mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl)
+			c <- mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl, flags)
 		}()
 	}
 	// Return error if any mounts failed, since otherwise we'll get
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index 68b0bfe..58c914c 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -25,7 +25,7 @@
 	mounts map[string][]string
 }
 
-func (ns *namespace) Mount(ctx context.T, name, server string, _ time.Duration) error {
+func (ns *namespace) Mount(ctx context.T, name, server string, _ time.Duration, _ ...naming.MountOpt) error {
 	defer vlog.LogCall()()
 	ns.Lock()
 	defer ns.Unlock()
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index 85936ec..2a1376d 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -47,6 +47,7 @@
 // point can be sent to any of these servers.
 type mount struct {
 	servers *serverList
+	mt      bool
 }
 
 // node is a single point in the tree representing the mount table.
@@ -225,8 +226,16 @@
 	return n.mount.servers.copyToSlice(), slashSlashJoin(elems), nil
 }
 
+func hasMTFlag(flags types.MountFlag) bool {
+	return (flags & types.MT) == types.MT
+}
+
+func hasReplaceFlag(flags types.MountFlag) bool {
+	return (flags & types.Replace) == types.Replace
+}
+
 // Mount a server onto the name in the receiver.
-func (ms *mountContext) Mount(context ipc.ServerContext, server string, ttlsecs uint32) error {
+func (ms *mountContext) Mount(context ipc.ServerContext, server string, ttlsecs uint32, flags types.MountFlag) error {
 	mt := ms.mt
 	if ttlsecs == 0 {
 		ttlsecs = 10 * 365 * 24 * 60 * 60 // a really long time
@@ -246,13 +255,17 @@
 	if n == nil {
 		return naming.ErrNoSuchName
 	}
+	if hasReplaceFlag(flags) {
+		n.mount = nil
+	}
 	if n.mount == nil {
-		n.mount = &mount{
-			servers: NewServerList(),
+		n.mount = &mount{servers: NewServerList(), mt: hasMTFlag(flags)}
+	} else {
+		if hasMTFlag(flags) != n.mount.mt {
+			return fmt.Errorf("MT doesn't match")
 		}
 	}
-	m := n.mount
-	m.servers.add(server, time.Duration(ttlsecs)*time.Second)
+	n.mount.servers.add(server, time.Duration(ttlsecs)*time.Second)
 	return nil
 }
 
diff --git a/services/mounttable/lib/mounttable_test.go b/services/mounttable/lib/mounttable_test.go
index d830415..c773312 100644
--- a/services/mounttable/lib/mounttable_test.go
+++ b/services/mounttable/lib/mounttable_test.go
@@ -53,7 +53,7 @@
 	return c
 }
 
-func (stupidNS) Mount(context.T, string, string, time.Duration) error {
+func (stupidNS) Mount(context.T, string, string, time.Duration, ...naming.MountOpt) error {
 	return errors.New("unimplemented")
 }
 
@@ -124,7 +124,7 @@
 	if err != nil {
 		boom(t, "Failed to BindMountTable: %s", err)
 	}
-	if err := mtpt.Mount(rt.R().NewContext(), service, uint32(ttlSecs), veyron2.RetryTimeoutOpt(0)); err != nil {
+	if err := mtpt.Mount(rt.R().NewContext(), service, uint32(ttlSecs), 0, veyron2.RetryTimeoutOpt(0)); err != nil {
 		if shouldSucceed {
 			boom(t, "Failed to Mount %s onto %s: %s", service, name, err)
 		}
diff --git a/services/mounttable/lib/neighborhood.go b/services/mounttable/lib/neighborhood.go
index 6b6df4d..0381352 100644
--- a/services/mounttable/lib/neighborhood.go
+++ b/services/mounttable/lib/neighborhood.go
@@ -221,7 +221,7 @@
 }
 
 // Mount not implemented.
-func (*neighborhoodService) Mount(_ ipc.ServerContext, server string, ttlsecs uint32) error {
+func (*neighborhoodService) Mount(_ ipc.ServerContext, server string, ttlsecs uint32, opts types.MountFlag) error {
 	return errors.New("this server does not implement Mount")
 }
 
@@ -247,7 +247,7 @@
 			if ok, _, _ := g.MatchInitialSegment(k); !ok {
 				continue
 			}
-			if err := sender.Send(types.MountEntry{Name: k, Servers: n}); err != nil {
+			if err := sender.Send(types.MountEntry{Name: k, Servers: n, MT: true}); err != nil {
 				return err
 			}
 		}
@@ -257,7 +257,7 @@
 		if neighbor == nil {
 			return naming.ErrNoSuchName
 		}
-		return sender.Send(types.MountEntry{Name: "", Servers: neighbor})
+		return sender.Send(types.MountEntry{Name: "", Servers: neighbor, MT: true})
 	default:
 		return naming.ErrNoSuchName
 	}
diff --git a/services/proxy/proxyd/main.go b/services/proxy/proxyd/main.go
index ce9859b..903db8b 100644
--- a/services/proxy/proxyd/main.go
+++ b/services/proxy/proxyd/main.go
@@ -46,7 +46,7 @@
 		publisher := publisher.New(r.NewContext(), r.Namespace(), time.Minute)
 		defer publisher.WaitForStop()
 		defer publisher.Stop()
-		publisher.AddServer(naming.JoinAddressName(proxy.Endpoint().String(), "//"))
+		publisher.AddServer(naming.JoinAddressName(proxy.Endpoint().String(), "//"), false)
 		publisher.AddName(*name)
 	}
 
diff --git a/tools/mounttable/impl/impl.go b/tools/mounttable/impl/impl.go
index 0bec3fe..fb39d1a 100644
--- a/tools/mounttable/impl/impl.go
+++ b/tools/mounttable/impl/impl.go
@@ -99,7 +99,7 @@
 	if err != nil {
 		return fmt.Errorf("TTL parse error: %v", err)
 	}
-	err = c.Mount(ctx, args[1], uint32(ttl.Seconds()))
+	err = c.Mount(ctx, args[1], uint32(ttl.Seconds()), 0)
 	if err != nil {
 		return err
 	}
diff --git a/tools/mounttable/impl/impl_test.go b/tools/mounttable/impl/impl_test.go
index 77a5a12..b4f96c7 100644
--- a/tools/mounttable/impl/impl_test.go
+++ b/tools/mounttable/impl/impl_test.go
@@ -25,12 +25,12 @@
 func (s *server) Glob(_ ipc.ServerContext, pattern string, stream mounttable.GlobbableServiceGlobStream) error {
 	vlog.VI(2).Infof("Glob() was called. suffix=%v pattern=%q", s.suffix, pattern)
 	sender := stream.SendStream()
-	sender.Send(types.MountEntry{"name1", []types.MountedServer{{"server1", 123}}})
-	sender.Send(types.MountEntry{"name2", []types.MountedServer{{"server2", 456}, {"server3", 789}}})
+	sender.Send(types.MountEntry{"name1", []types.MountedServer{{"server1", 123}}, false})
+	sender.Send(types.MountEntry{"name2", []types.MountedServer{{"server2", 456}, {"server3", 789}}, false})
 	return nil
 }
 
-func (s *server) Mount(_ ipc.ServerContext, server string, ttl uint32) error {
+func (s *server) Mount(_ ipc.ServerContext, server string, ttl uint32, flags types.MountFlag) error {
 	vlog.VI(2).Infof("Mount() was called. suffix=%v server=%q ttl=%d", s.suffix, server, ttl)
 	return nil
 }
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 "$@"