Merge "veyron/runtimes, veyron2/model: Share preferred protocols when client is changed with RuntimeX."
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index 09dc291..14ab572 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -200,7 +200,7 @@
 func TestExec(t *testing.T) {
 	sh, cleanup := newShell(t)
 	defer cleanup()
-	h, err := sh.Start(core.ExecCommand, nil, []string{"echo", "-n", "hello world"}...)
+	h, err := sh.Start(core.ExecCommand, nil, []string{"/bin/sh", "-c", "echo -n hello world"}...)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -216,7 +216,7 @@
 func TestExecWithEnv(t *testing.T) {
 	sh, cleanup := newShell(t)
 	defer cleanup()
-	h, err := sh.Start(core.ExecCommand, []string{"BLAH=hello world"}, "printenv", "BLAH")
+	h, err := sh.Start(core.ExecCommand, []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
index d0e5556..be41d0d 100644
--- a/lib/modules/core/exec.go
+++ b/lib/modules/core/exec.go
@@ -2,7 +2,7 @@
 
 import (
 	"io"
-	"os/exec"
+	"syscall"
 
 	"v.io/core/veyron/lib/modules"
 )
@@ -12,15 +12,9 @@
 }
 
 func execCommand(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	cmd := exec.Command(args[1], args[2:]...)
 	envSlice := []string{}
 	for key, value := range env {
 		envSlice = append(envSlice, key+"="+value)
 	}
-
-	cmd.Env = envSlice
-	cmd.Stdin = stdin
-	cmd.Stdout = stdout
-	cmd.Stderr = stderr
-	return cmd.Run()
+	return syscall.Exec(args[1], args[1:], envSlice)
 }
diff --git a/lib/testutil/integration/util.go b/lib/testutil/integration/util.go
index 12d2dc6..4836fce 100644
--- a/lib/testutil/integration/util.go
+++ b/lib/testutil/integration/util.go
@@ -85,8 +85,16 @@
 
 	// Wait waits for this invocation to finish. If either stdout or stderr
 	// is non-nil, any remaining unread output from those sources will be
-	// written to the corresponding writer.
-	Wait(stdout, stderr io.Writer)
+	// written to the corresponding writer. The returned error represents
+	// the exit status of the underlying command.
+	Wait(stdout, stderr io.Writer) error
+
+	// Wait waits for this invocation to finish. If either stdout or stderr
+	// is non-nil, any remaining unread output from those sources will be
+	// written to the corresponding writer. If the underlying command
+	// exited with anything but success (exit status 0), this function will
+	// cause the current test to fail.
+	WaitOrDie(stdout, stderr io.Writer)
 }
 
 type integrationTestEnvironment struct {
@@ -163,9 +171,13 @@
 	return readerToString(i.env.t, i.Stderr())
 }
 
-func (i *integrationTestBinaryInvocation) Wait(stdout, stderr io.Writer) {
-	if err := (*i.handle).Shutdown(stdout, stderr); err != nil {
-		i.env.t.Fatalf("Shutdown() failed: %v", err)
+func (i *integrationTestBinaryInvocation) Wait(stdout, stderr io.Writer) error {
+	return (*i.handle).Shutdown(stdout, stderr)
+}
+
+func (i *integrationTestBinaryInvocation) WaitOrDie(stdout, stderr io.Writer) {
+	if err := i.Wait(stdout, stderr); err != nil {
+		i.env.t.Fatalf("Wait() for pid %d failed: %v", (*i.handle).Pid(), err)
 	}
 }
 
diff --git a/services/identity/identityd/main.go b/services/identity/identityd/main.go
index 4a9f96b..10af65a 100644
--- a/services/identity/identityd/main.go
+++ b/services/identity/identityd/main.go
@@ -78,7 +78,7 @@
 	fmt.Fprintf(os.Stderr, `%s starts an HTTP server that brokers blessings after authenticating through OAuth.
 
 To generate TLS certificates so the HTTP server can use SSL:
-v23 go run $GOROOT/src/crypto/tls/generate_cert.go --host <IP address>
+go run $GOROOT/src/crypto/tls/generate_cert.go --host <IP address>
 
 To use Google as an OAuth provider the --google_config_* flags must be set to point to
 the a JSON file obtained after registering the application with the Google Developer Console
diff --git a/services/identity/identityd_test/main.go b/services/identity/identityd_test/main.go
index 6556f6e..4531b63 100644
--- a/services/identity/identityd_test/main.go
+++ b/services/identity/identityd_test/main.go
@@ -5,8 +5,11 @@
 	"flag"
 	"fmt"
 	"os"
+	"os/exec"
 	"time"
 
+	"v.io/core/veyron2/vlog"
+
 	"v.io/core/veyron/services/identity/auditor"
 	"v.io/core/veyron/services/identity/blesser"
 	"v.io/core/veyron/services/identity/caveats"
@@ -26,6 +29,14 @@
 	auditor, reader := auditor.NewMockBlessingAuditor()
 	revocationManager := revocation.NewMockRevocationManager()
 
+	// If no tlsconfig has been provided, write and use our own.
+	if flag.Lookup("tlsconfig").Value.String() == "" {
+		writeCertAndKey()
+		if err := flag.Set("tlsconfig", "./cert.pem,./key.pem"); err != nil {
+			vlog.Fatal(err)
+		}
+	}
+
 	server.NewIdentityServer(
 		oauth.NewMockOAuth(),
 		auditor,
@@ -36,17 +47,30 @@
 }
 
 func usage() {
-	fmt.Fprintf(os.Stderr, `%s starts an test version of the identityd server that
+	fmt.Fprintf(os.Stderr, `%s starts a test version of the identityd server that
 mocks out oauth, auditing, and revocation.
 
 To generate TLS certificates so the HTTP server can use SSL:
-v23 go run $GOROOT/src/crypto/tls/generate_cert.go --host <IP address>
+go run $GOROOT/src/crypto/tls/generate_cert.go --host <IP address>
 
 Flags:
 `, os.Args[0])
 	flag.PrintDefaults()
 }
 
+func writeCertAndKey() {
+	goroot := os.Getenv("GOROOT")
+	if goroot == "" {
+		vlog.Fatal("GOROOT not set")
+	}
+	generateCertFile := goroot + "/src/crypto/tls/generate_cert.go"
+	host := flag.Lookup("host").Value.String()
+	duration := 1 * time.Hour
+	if err := exec.Command("go", "run", generateCertFile, "--host", host, "--duration", duration.String()).Run(); err != nil {
+		vlog.Fatalf("Could not generate key and cert: %v", err)
+	}
+}
+
 func oauthBlesserGoogleParams(revocationManager revocation.RevocationManager) blesser.GoogleParams {
 	googleParams := blesser.GoogleParams{
 		BlessingDuration:  365 * 24 * time.Hour,
diff --git a/services/identity/test.sh b/services/identity/test.sh
index 67ccffa..5b30587 100755
--- a/services/identity/test.sh
+++ b/services/identity/test.sh
@@ -11,24 +11,6 @@
   PRINCIPAL_BIN="$(shell_test::build_go_binary 'v.io/core/veyron/tools/principal')"
 }
 
-# These certificatese were created with "generate_cert.go  --host=localhost --duration=87600h --ecdsa-curve=P256"
-CERT="-----BEGIN CERTIFICATE-----
-MIIBbTCCARSgAwIBAgIRANKYmC0v3pK+VohyJOdD1hgwCgYIKoZIzj0EAwIwEjEQ
-MA4GA1UEChMHQWNtZSBDbzAeFw0xNDExMjEyMjEwNTJaFw0yNDExMTgyMjEwNTJa
-MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASv
-heWcWcZT7d5Sm/uoWhBUJJPBSREN4qGzBV7yFYUFvHJ9mNaEcopo/6BopJRbvUmj
-CQMVDZVMm5Er/f8HgCngo0swSTAOBgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYI
-KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYI
-KoZIzj0EAwIDRwAwRAIgAkwh+mi5YlIxYzxzT7bQj/ZYU5pufxHt+F+a75gbm7AC
-IAI9+axCPawySY+UYvjO14hklsyy3LnSf1mNHyeGydMM
------END CERTIFICATE-----"
-
-KEY="-----BEGIN EC PRIVATE KEY-----
-MHcCAQEEIHxiR6vjOn1jF1KS0V//pXrulxss9PwUgV/7/QVeV2zCoAoGCCqGSM49
-AwEHoUQDQgAEr4XlnFnGU+3eUpv7qFoQVCSTwUkRDeKhswVe8hWFBbxyfZjWhHKK
-aP+gaKSUW71JowkDFQ2VTJuRK/3/B4Ap4A==
------END EC PRIVATE KEY-----"
-
 # runprincipal starts the principal tool, extracts the url and curls it, to avoid the
 # dependence the principal tool has on a browser.
 runprincipal() {
@@ -51,15 +33,11 @@
   cd "${WORKDIR}"
   build
 
-  # Setup the certificate files.
-  echo "${CERT}" > "${WORKDIR}/cert.pem"
-  echo "${KEY}" > "${WORKDIR}/key.pem"
-
   shell_test::setup_server_test || shell_test::fail "line ${LINENO} failed to setup server test"
   unset VEYRON_CREDENTIALS
 
   # Start the identityd server in test identity server.
-  shell_test::start_server "${IDENTITYD_BIN}" --host=localhost --tlsconfig="${WORKDIR}/cert.pem,${WORKDIR}/key.pem" -veyron.tcp.address=127.0.0.1:0
+  shell_test::start_server "${IDENTITYD_BIN}" --host=localhost -veyron.tcp.address=127.0.0.1:0
   echo Identityd Log File: $START_SERVER_LOG_FILE
   export VEYRON_CREDENTIALS="$(shell::tmp_dir)"
 
diff --git a/services/mgmt/binary/binaryd/testdata/integration_test.go b/services/mgmt/binary/binaryd/testdata/integration_test.go
index a70b1b0..87ff0f4 100644
--- a/services/mgmt/binary/binaryd/testdata/integration_test.go
+++ b/services/mgmt/binary/binaryd/testdata/integration_test.go
@@ -4,10 +4,11 @@
 	"bytes"
 	"fmt"
 	"io/ioutil"
+	"net/http"
 	"os"
 	"os/exec"
-	"path/filepath"
 	"strings"
+	"syscall"
 	"testing"
 
 	"v.io/core/veyron/lib/modules"
@@ -22,11 +23,6 @@
 	testutil.Init()
 }
 
-var binPkgs = []string{
-	"v.io/core/veyron/services/mgmt/binary/binaryd",
-	"v.io/core/veyron/tools/binary",
-}
-
 func checkFileType(t *testing.T, file, typeString string) {
 	var catOut bytes.Buffer
 	catCmd := exec.Command("cat", file+".__info")
@@ -40,89 +36,76 @@
 	}
 }
 
+func readFileOrDie(t *testing.T, path string) []byte {
+	result, err := ioutil.ReadFile(path)
+	if err != nil {
+		t.Fatalf("ReadFile(%q) failed: %v", path, err)
+	}
+	return result
+}
+
 func compareFiles(t *testing.T, f1, f2 string) {
-	var cmpOut bytes.Buffer
-	cmpCmd := exec.Command("cmp", f1, f2)
-	cmpCmd.Stdout = &cmpOut
-	cmpCmd.Stderr = &cmpOut
-	if err := cmpCmd.Run(); err != nil {
-		t.Fatalf("%q failed: %v\n%v", strings.Join(cmpCmd.Args, " "), err, cmpOut.String())
+	if !bytes.Equal(readFileOrDie(t, f1), readFileOrDie(t, f2)) {
+		t.Fatalf("the contents of %s and %s differ when they should not", f1, f2)
 	}
 }
 
-func deleteFile(t *testing.T, binDir, credentials, mt, name, suffix string) {
-	var deleteOut bytes.Buffer
+func deleteFile(env integration.TestEnvironment, clientBin integration.TestBinary, credentials, name, suffix string) {
 	deleteArgs := []string{
 		"-veyron.credentials=" + credentials,
-		"-veyron.namespace.root=" + mt,
+		"-veyron.namespace.root=" + env.RootMT(),
 		"delete", naming.Join(name, suffix),
 	}
-	deleteCmd := exec.Command(filepath.Join(binDir, "binary"), deleteArgs...)
-	deleteCmd.Stdout = &deleteOut
-	deleteCmd.Stderr = &deleteOut
-	if err := deleteCmd.Run(); err != nil {
-		t.Fatalf("%q failed: %v\n%v", strings.Join(deleteCmd.Args, " "), err, deleteOut.String())
-	}
+	clientBin.Start(deleteArgs...).WaitOrDie(nil, nil)
 }
 
-func downloadFile(t *testing.T, expectError bool, binDir, credentials, mt, name, path, suffix string) {
-	var downloadOut bytes.Buffer
+func downloadFile(t *testing.T, env integration.TestEnvironment, clientBin integration.TestBinary, expectError bool, credentials, name, path, suffix string) {
 	downloadArgs := []string{
 		"-veyron.credentials=" + credentials,
-		"-veyron.namespace.root=" + mt,
+		"-veyron.namespace.root=" + env.RootMT(),
 		"download", naming.Join(name, suffix), path,
 	}
-	downloadCmd := exec.Command(filepath.Join(binDir, "binary"), downloadArgs...)
-	downloadCmd.Stdout = &downloadOut
-	downloadCmd.Stderr = &downloadOut
-	err := downloadCmd.Run()
-	if err != nil && !expectError {
-		t.Fatalf("%q failed: %v\n%v", strings.Join(downloadCmd.Args, " "), err, downloadOut.String())
+	err := clientBin.Start(downloadArgs...).Wait(os.Stdout, os.Stderr)
+	if expectError && err == nil {
+		t.Fatalf("%s %q did not fail when it should", clientBin.Path(), strings.Join(downloadArgs, " "))
 	}
-	if err == nil && expectError {
-		t.Fatalf("%q did not fail when it should", strings.Join(downloadCmd.Args, " "))
+	if !expectError && err != nil {
+		t.Fatalf("%s %q failed: %v", clientBin.Path(), strings.Join(downloadArgs, " "), err)
 	}
 }
 
 func downloadURL(t *testing.T, path, rootURL, suffix string) {
-	var curlOut bytes.Buffer
-	curlCmd := exec.Command("curl", "-f", "-o", path, fmt.Sprintf("%v/%v", rootURL, suffix))
-	curlCmd.Stdout = &curlOut
-	curlCmd.Stderr = &curlOut
-	if err := curlCmd.Run(); err != nil {
-		t.Fatalf("%q failed: %v\n%v", strings.Join(curlCmd.Args, " "), err, curlOut.String())
+	url := fmt.Sprintf("http://%v/%v", rootURL, suffix)
+	resp, err := http.Get(url)
+	if err != nil {
+		t.Fatalf("Get(%q) failed: %v", url, err)
+	}
+	output, err := ioutil.ReadAll(resp.Body)
+	resp.Body.Close()
+	if err != nil {
+		t.Fatalf("ReadAll() failed: %v", err)
+	}
+	if err = ioutil.WriteFile(path, output, 0600); err != nil {
+		t.Fatalf("WriteFile() failed: %v", err)
 	}
 }
 
-func rootURL(t *testing.T, binDir, credentials, mt, name string) string {
-	var rootOut bytes.Buffer
+func rootURL(t *testing.T, env integration.TestEnvironment, clientBin integration.TestBinary, credentials, name string) string {
 	rootArgs := []string{
 		"-veyron.credentials=" + credentials,
-		"-veyron.namespace.root=" + mt,
+		"-veyron.namespace.root=" + env.RootMT(),
 		"url", name,
 	}
-	rootCmd := exec.Command(filepath.Join(binDir, "binary"), rootArgs...)
-	rootCmd.Stdout = &rootOut
-	rootCmd.Stderr = &rootOut
-	if err := rootCmd.Run(); err != nil {
-		t.Fatalf("%q failed: %v\n%v", strings.Join(rootCmd.Args, " "), err, rootOut.String())
-	}
-	return strings.TrimSpace(rootOut.String())
+	return strings.TrimSpace(clientBin.Start(rootArgs...).Output())
 }
 
-func uploadFile(t *testing.T, binDir, credentials, mt, name, path, suffix string) {
-	var uploadOut bytes.Buffer
+func uploadFile(t *testing.T, env integration.TestEnvironment, clientBin integration.TestBinary, credentials, name, path, suffix string) {
 	uploadArgs := []string{
 		"-veyron.credentials=" + credentials,
-		"-veyron.namespace.root=" + mt,
+		"-veyron.namespace.root=" + env.RootMT(),
 		"upload", naming.Join(name, suffix), path,
 	}
-	uploadCmd := exec.Command(filepath.Join(binDir, "binary"), uploadArgs...)
-	uploadCmd.Stdout = &uploadOut
-	uploadCmd.Stderr = &uploadOut
-	if err := uploadCmd.Run(); err != nil {
-		t.Fatalf("%q failed: %v\n%v", strings.Join(uploadCmd.Args, " "), err, uploadOut.String())
-	}
+	clientBin.Start(uploadArgs...).WaitOrDie(os.Stdout, os.Stderr)
 }
 
 func TestHelperProcess(t *testing.T) {
@@ -130,24 +113,12 @@
 }
 
 func TestBinaryRepositoryIntegration(t *testing.T) {
-	// Build the required binaries.
-	binDir, cleanup, err := integration.BuildPkgs(binPkgs)
-	if err != nil {
-		t.Fatalf("%v", err)
-	}
-	defer cleanup()
+	env := integration.NewTestEnvironment(t)
+	defer env.Cleanup()
 
-	// Start a root mount table.
-	shell, err := modules.NewShell(nil)
-	if err != nil {
-		t.Fatalf("NewShell() failed: %v", err)
-	}
-	defer shell.Cleanup(os.Stdin, os.Stderr)
-	handle, mt, err := integration.StartRootMT(shell)
-	if err != nil {
-		t.Fatalf("%v", err)
-	}
-	defer handle.CloseStdin()
+	// Build the required binaries.
+	binaryRepoBin := env.BuildGoPkg("v.io/core/veyron/services/mgmt/binary/binaryd")
+	clientBin := env.BuildGoPkg("v.io/core/veyron/tools/binary")
 
 	// Generate credentials.
 	serverCred, serverPrin := security.NewCredentials("server")
@@ -156,33 +127,25 @@
 	defer os.RemoveAll(clientCred)
 
 	// Start the build server.
-	binaryRepoBin := filepath.Join(binDir, "binaryd")
 	binaryRepoName := "test-binary-repository"
 	args := []string{
 		"-name=" + binaryRepoName,
 		"-http=127.0.0.1:0",
 		"-veyron.tcp.address=127.0.0.1:0",
 		"-veyron.credentials=" + serverCred,
-		"-veyron.namespace.root=" + mt,
+		"-veyron.namespace.root=" + env.RootMT(),
 	}
-	serverProcess, err := integration.StartServer(binaryRepoBin, args)
-	if err != nil {
-		t.Fatalf("%v", err)
-	}
-	defer serverProcess.Kill()
+
+	server := binaryRepoBin.Start(args...)
+	defer server.Kill(syscall.SIGTERM)
 
 	// Upload a random binary file.
-	binFile, err := ioutil.TempFile("", "")
-	if err != nil {
-		t.Fatalf("TempFile() failed: %v", err)
-	}
-	defer binFile.Close()
-	defer os.Remove(binFile.Name())
+	binFile := env.TempFile()
 	if _, err := binFile.Write(testutil.RandomBytes(16 * 1000 * 1000)); err != nil {
 		t.Fatalf("Write() failed: %v", err)
 	}
 	binSuffix := "test-binary"
-	uploadFile(t, binDir, clientCred, mt, binaryRepoName, binFile.Name(), binSuffix)
+	uploadFile(t, env, clientBin, clientCred, binaryRepoName, binFile.Name(), binSuffix)
 
 	// Upload a compressed version of the binary file.
 	tarFile := binFile.Name() + ".tar.gz"
@@ -195,13 +158,13 @@
 	}
 	defer os.Remove(tarFile)
 	tarSuffix := "test-compressed-file"
-	uploadFile(t, binDir, clientCred, mt, binaryRepoName, tarFile, tarSuffix)
+	uploadFile(t, env, clientBin, clientCred, binaryRepoName, tarFile, tarSuffix)
 
 	// Download the binary file and check that it matches the
 	// original one and that it has the right file type.
 	downloadedBinFile := binFile.Name() + "-downloaded"
 	defer os.Remove(downloadedBinFile)
-	downloadFile(t, false, binDir, clientCred, mt, binaryRepoName, downloadedBinFile, binSuffix)
+	downloadFile(t, env, clientBin, false, clientCred, binaryRepoName, downloadedBinFile, binSuffix)
 	compareFiles(t, binFile.Name(), downloadedBinFile)
 	checkFileType(t, downloadedBinFile, `{"Type":"application/octet-stream","Encoding":""}`)
 
@@ -210,13 +173,13 @@
 	// right file type.
 	downloadedTarFile := binFile.Name() + "-downloaded.tar.gz"
 	defer os.Remove(downloadedTarFile)
-	downloadFile(t, false, binDir, clientCred, mt, binaryRepoName, downloadedTarFile, tarSuffix)
+	downloadFile(t, env, clientBin, false, clientCred, binaryRepoName, downloadedTarFile, tarSuffix)
 	compareFiles(t, tarFile, downloadedTarFile)
 	checkFileType(t, downloadedTarFile, `{"Type":"application/x-tar","Encoding":"gzip"}`)
 
 	// Fetch the root URL of the HTTP server used by the binary
 	// repository to serve URLs.
-	root := rootURL(t, binDir, clientCred, mt, binaryRepoName)
+	root := rootURL(t, env, clientBin, clientCred, binaryRepoName)
 
 	// Download the binary file using the HTTP protocol and check
 	// that it matches the original one.
@@ -234,10 +197,10 @@
 	compareFiles(t, downloadedTarFile, downloadedTarFileURL)
 
 	// Delete the files.
-	deleteFile(t, binDir, clientCred, mt, binaryRepoName, binSuffix)
-	deleteFile(t, binDir, clientCred, mt, binaryRepoName, tarSuffix)
+	deleteFile(env, clientBin, clientCred, binaryRepoName, binSuffix)
+	deleteFile(env, clientBin, clientCred, binaryRepoName, tarSuffix)
 
 	// Check the files no longer exist.
-	downloadFile(t, true, binDir, clientCred, mt, binaryRepoName, downloadedBinFile, binSuffix)
-	downloadFile(t, true, binDir, clientCred, mt, binaryRepoName, downloadedTarFile, tarSuffix)
+	downloadFile(t, env, clientBin, true, clientCred, binaryRepoName, downloadedBinFile, binSuffix)
+	downloadFile(t, env, clientBin, true, clientCred, binaryRepoName, downloadedTarFile, tarSuffix)
 }
diff --git a/tools/debug/testdata/integration_test.go b/tools/debug/testdata/integration_test.go
index 46c4c1b..8216925 100644
--- a/tools/debug/testdata/integration_test.go
+++ b/tools/debug/testdata/integration_test.go
@@ -108,7 +108,7 @@
 	logName := filepath.Base(file.Name())
 	runCount := 12
 	for i := 0; i < runCount; i++ {
-		binary.Start("logs", "read", env.RootMT()+"/__debug/logs/"+logName).Wait(nil, nil)
+		binary.Start("logs", "read", env.RootMT()+"/__debug/logs/"+logName).WaitOrDie(nil, nil)
 	}
 	got := binary.Start("stats", "read", env.RootMT()+"/__debug/stats/ipc/server/routing-id/*/methods/ReadLog/latency-ms").Output()
 	want := fmt.Sprintf("Count: %d", runCount)
@@ -125,7 +125,7 @@
 	testLogData := "This is a test log file\n"
 	file := createTestLogFile(t, env, testLogData)
 	logName := filepath.Base(file.Name())
-	binary.Start("logs", "read", env.RootMT()+"/__debug/logs/"+logName).Wait(nil, nil)
+	binary.Start("logs", "read", env.RootMT()+"/__debug/logs/"+logName).WaitOrDie(nil, nil)
 
 	inv := binary.Start("stats", "watch", "-raw", env.RootMT()+"/__debug/stats/ipc/server/routing-id/*/methods/ReadLog/latency-ms")