Merge "v23: Added server option for lame duck timeout"
diff --git a/flow/message/.api b/flow/message/.api
index 69c0ff5..f292239 100644
--- a/flow/message/.api
+++ b/flow/message/.api
@@ -41,9 +41,11 @@
 pkg message, type Release struct
 pkg message, type Release struct, Counters map[uint64]uint64
 pkg message, type Setup struct
+pkg message, type Setup struct, Mtu uint64
 pkg message, type Setup struct, PeerLocalEndpoint naming.Endpoint
 pkg message, type Setup struct, PeerNaClPublicKey *[32]byte
 pkg message, type Setup struct, PeerRemoteEndpoint naming.Endpoint
+pkg message, type Setup struct, SharedTokens uint64
 pkg message, type Setup struct, Versions version.RPCVersionRange
 pkg message, type TearDown struct
 pkg message, type TearDown struct, Message string
diff --git a/flow/message/message.go b/flow/message/message.go
index 2aceebc..1ba0f70 100644
--- a/flow/message/message.go
+++ b/flow/message/message.go
@@ -99,6 +99,8 @@
 	peerNaClPublicKeyOption
 	peerRemoteEndpointOption
 	peerLocalEndpointOption
+	mtuOption
+	sharedTokensOption
 )
 
 // data flags.
@@ -125,11 +127,15 @@
 
 // Setup is the first message over the wire.  It negotiates protocol version
 // and encryption options for connection.
+// New fields to Setup must be added in order of creation. i.e. the order of the fields
+// should not be changed.
 type Setup struct {
 	Versions             version.RPCVersionRange
 	PeerNaClPublicKey    *[32]byte
 	PeerRemoteEndpoint   naming.Endpoint
 	PeerLocalEndpoint    naming.Endpoint
+	Mtu                  uint64
+	SharedTokens         uint64
 	uninterpretedOptions []option
 }
 
@@ -167,6 +173,12 @@
 		data = appendSetupOption(peerLocalEndpointOption,
 			[]byte(m.PeerLocalEndpoint.String()), data)
 	}
+	if m.Mtu != 0 {
+		data = appendSetupOption(mtuOption, writeVarUint64(m.Mtu, nil), data)
+	}
+	if m.SharedTokens != 0 {
+		data = appendSetupOption(sharedTokensOption, writeVarUint64(m.SharedTokens, nil), data)
+	}
 	for _, o := range m.uninterpretedOptions {
 		data = appendSetupOption(o.opt, o.payload, data)
 	}
@@ -203,6 +215,18 @@
 			m.PeerRemoteEndpoint, err = v23.NewEndpoint(string(payload))
 		case peerLocalEndpointOption:
 			m.PeerLocalEndpoint, err = v23.NewEndpoint(string(payload))
+		case mtuOption:
+			if mtu, _, valid := readVarUint64(ctx, payload); valid {
+				m.Mtu = mtu
+			} else {
+				return NewErrInvalidSetupOption(ctx, opt, field)
+			}
+		case sharedTokensOption:
+			if t, _, valid := readVarUint64(ctx, payload); valid {
+				m.SharedTokens = t
+			} else {
+				return NewErrInvalidSetupOption(ctx, opt, field)
+			}
 		default:
 			m.uninterpretedOptions = append(m.uninterpretedOptions, option{opt, payload})
 		}
diff --git a/flow/message/message_test.go b/flow/message/message_test.go
index 5a2307d..8b829b7 100644
--- a/flow/message/message_test.go
+++ b/flow/message/message_test.go
@@ -60,6 +60,11 @@
 			PeerRemoteEndpoint: ep1,
 			PeerLocalEndpoint:  ep2,
 		},
+		&message.Setup{
+			Versions:     version.RPCVersionRange{Min: 3, Max: 5},
+			Mtu:          1 << 16,
+			SharedTokens: 1 << 20,
+		},
 		&message.Setup{},
 	})
 }
diff --git a/rpc/.api b/rpc/.api
index d2b6325..5e36f82 100644
--- a/rpc/.api
+++ b/rpc/.api
@@ -8,10 +8,6 @@
 pkg rpc, func NewGlobState(interface{}) *GlobState
 pkg rpc, func ReflectInvoker(interface{}) (Invoker, error)
 pkg rpc, func ReflectInvokerOrDie(interface{}) Invoker
-pkg rpc, func RegisterProtocol(string, DialerFunc, ResolverFunc, ListenerFunc, ...string) bool
-pkg rpc, func RegisterUnknownProtocol(string, DialerFunc, ResolverFunc, ListenerFunc) bool
-pkg rpc, func RegisteredProtocol(string) (DialerFunc, ResolverFunc, ListenerFunc, []string)
-pkg rpc, func RegisteredProtocols() []string
 pkg rpc, func TypeCheckMethods(interface{}) map[string]error
 pkg rpc, method (AddressChooserFunc) ChooseAddresses(string, []net.Addr) ([]net.Addr, error)
 pkg rpc, method (ListenSpec) Copy() ListenSpec
@@ -48,7 +44,6 @@
 pkg rpc, type ClientOpt interface, RPCClientOpt()
 pkg rpc, type Describer interface { Describe__ }
 pkg rpc, type Describer interface, Describe__() []InterfaceDesc
-pkg rpc, type DialerFunc func(ctx *context.T, protocol, address string, timeout time.Duration) (net.Conn, error)
 pkg rpc, type Dispatcher interface { Lookup }
 pkg rpc, type Dispatcher interface, Lookup(*context.T, string) (interface{}, security.Authorizer, error)
 pkg rpc, type EmbedDesc struct
@@ -96,7 +91,6 @@
 pkg rpc, type ListenSpec struct, Addrs ListenAddrs
 pkg rpc, type ListenSpec struct, Proxy string
 pkg rpc, type ListenSpec struct, embedded AddressChooser
-pkg rpc, type ListenerFunc func(ctx *context.T, protocol, address string) (net.Listener, error)
 pkg rpc, type MethodDesc struct
 pkg rpc, type MethodDesc struct, Doc string
 pkg rpc, type MethodDesc struct, InArgs []ArgDesc
@@ -123,7 +117,6 @@
 pkg rpc, type Request struct, NumPosArgs uint64
 pkg rpc, type Request struct, Suffix string
 pkg rpc, type Request struct, TraceRequest vtrace.Request
-pkg rpc, type ResolverFunc func(ctx *context.T, protocol, address string) (string, string, error)
 pkg rpc, type Response struct
 pkg rpc, type Response struct, AckBlessings bool
 pkg rpc, type Response struct, EndStreamResults bool
diff --git a/rpc/registry.go b/rpc/registry.go
deleted file mode 100644
index 2dd9d35..0000000
--- a/rpc/registry.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package rpc
-
-import (
-	"fmt"
-	"net"
-	"sync"
-	"time"
-
-	"v.io/v23/context"
-	"v.io/v23/naming"
-)
-
-// This file is DEPRECATED. If you wish to add new protocols please you the
-// v23/flow.RegisterProtocol methods.
-// TODO(suharshs): Remove this file and corresponding protocols once the transition
-// to the new rpc implementation is complete.
-
-// DialerFunc is the function used to create net.Conn objects given a
-// protocol-specific string representation of an address.
-type DialerFunc func(ctx *context.T, protocol, address string, timeout time.Duration) (net.Conn, error)
-
-// ResolverFunc is the function used for protocol-specific address normalization.
-// e.g. the TCP resolve performs DNS resolution.
-type ResolverFunc func(ctx *context.T, protocol, address string) (string, string, error)
-
-// ListenerFunc is the function used to create net.Listener objects given a
-// protocol-specific string representation of the address a server will listen on.
-type ListenerFunc func(ctx *context.T, protocol, address string) (net.Listener, error)
-
-// RegisterProtocol makes available a Dialer, Resolver, and Listener to RegisteredNetwork.
-// If the protocol represents other actual protocols, you need to specify all the
-// actual protocols. E.g, "wsh" represents "tcp4", "tcp6", "ws4", and "ws6".
-//
-// Implementations of the Manager interface are expected to use this registry
-// in order to expand the reach of the types of network protocols they can
-// handle.
-//
-// Successive calls to RegisterProtocol replace the contents of a previous
-// call to it and returns trues if a previous value was replaced, false otherwise.
-func RegisterProtocol(protocol string, dialer DialerFunc, resolver ResolverFunc, listener ListenerFunc, p ...string) bool {
-	// This is for handling the common case where protocol is a "singleton", to
-	// make it easier to specify.
-	if len(p) == 0 {
-		p = []string{protocol}
-	}
-	registryLock.Lock()
-	defer registryLock.Unlock()
-	_, present := registry[protocol]
-	registry[protocol] = registryEntry{dialer, resolver, listener, p}
-	return present
-}
-
-// RegisterUnknownProtocol registers a Dialer, Resolver, and Listener for endpoints with
-// no specified protocol.
-//
-// The desired protocol provided in the first argument will be passed to the
-// Dialer and Listener as the actual protocol to use when dialing or listening.
-//
-// The protocol itself must have already been registered before RegisterUnknownProtocol
-// is called, otherwise we'll panic.
-func RegisterUnknownProtocol(protocol string, dialer DialerFunc, resolver ResolverFunc, listener ListenerFunc) bool {
-	var p []string
-	registryLock.RLock()
-	r, present := registry[protocol]
-	if !present {
-		panic(fmt.Sprintf("%s not registered", protocol))
-	}
-	p = r.p
-	registryLock.RUnlock()
-	wrappedDialer := func(ctx *context.T, _, address string, timeout time.Duration) (net.Conn, error) {
-		return dialer(ctx, protocol, address, timeout)
-	}
-	wrappedResolver := func(ctx *context.T, _, address string) (string, string, error) {
-		return resolver(ctx, protocol, address)
-	}
-	wrappedListener := func(ctx *context.T, _, address string) (net.Listener, error) {
-		return listener(ctx, protocol, address)
-	}
-	return RegisterProtocol(naming.UnknownProtocol, wrappedDialer, wrappedResolver, wrappedListener, p...)
-}
-
-// RegisteredProtocol returns the Dialer, Resolver, and Listener registered with a
-// previous call to RegisterProtocol.
-func RegisteredProtocol(protocol string) (DialerFunc, ResolverFunc, ListenerFunc, []string) {
-	registryLock.RLock()
-	e := registry[protocol]
-	registryLock.RUnlock()
-	return e.d, e.r, e.l, e.p
-}
-
-// RegisteredProtocols returns the list of protocols that have been previously
-// registered using RegisterProtocol. The underlying implementation will
-// support additional protocols such as those supported by the native RPC stack.
-func RegisteredProtocols() []string {
-	registryLock.RLock()
-	defer registryLock.RUnlock()
-	p := make([]string, 0, len(registry))
-	for k, _ := range registry {
-		p = append(p, k)
-	}
-	return p
-}
-
-type registryEntry struct {
-	d DialerFunc
-	r ResolverFunc
-	l ListenerFunc
-	p []string
-}
-
-var (
-	registryLock sync.RWMutex
-	registry     = make(map[string]registryEntry)
-)
diff --git a/rpc/version/.api b/rpc/version/.api
index 636365d..836c50c 100644
--- a/rpc/version/.api
+++ b/rpc/version/.api
@@ -3,6 +3,7 @@
 pkg version, const RPCVersion11 RPCVersion
 pkg version, const RPCVersion12 RPCVersion
 pkg version, const RPCVersion13 RPCVersion
+pkg version, const RPCVersion14 RPCVersion
 pkg version, const UnknownRPCVersion RPCVersion
 pkg version, func CommonVersion(*context.T, RPCVersionRange, RPCVersionRange) (RPCVersion, error)
 pkg version, func NewErrNoCompatibleVersion(*context.T, uint64, uint64, uint64, uint64) error
diff --git a/rpc/version/version.go b/rpc/version/version.go
index 01e27e8..c1b6d5a 100644
--- a/rpc/version/version.go
+++ b/rpc/version/version.go
@@ -43,6 +43,10 @@
 
 	// RPCVersion13 adds error messages in responses from proxies.
 	RPCVersion13
+
+	// RPCVersion14 adds the setup message to the channel binding during
+	// connection setup.
+	RPCVersion14
 )
 
 // RPCVersionRange allows you to optionally specify a range of versions to
diff --git a/security/ecdsa_go.go b/security/ecdsa_go.go
index 30ed8d8..bff1875 100644
--- a/security/ecdsa_go.go
+++ b/security/ecdsa_go.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !cgo noopenssl !linux,!openssl android !amd64,!openssl
+// +build go1.6 !cgo noopenssl !linux,!openssl android !amd64,!openssl
 
 // See comments in ecdsa_openssl.go for an explanation of the choice of
 // build tags.
diff --git a/security/ecdsa_openssl.go b/security/ecdsa_openssl.go
index e2914bc..e9573d7 100644
--- a/security/ecdsa_openssl.go
+++ b/security/ecdsa_openssl.go
@@ -2,21 +2,23 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !android,linux,amd64,cgo,!noopenssl openssl,cgo
+// +build !go1.6,!android,linux,amd64,cgo,!noopenssl openssl,cgo
 
 // The purpose of this file is to improve performance, as demonstrated by
 // benchmarks when linked against openssl-1.0.1f (with further improvements in
 // openssl-1.0.2, at the time this comment was written).
 //   go test -bench ECDSA v.io/v23/security
 //
-// See https://go-review.googlesource.com/#/c/8968/ for why the Go standard
-// library (as of Go 1.5) does not have performance on par with OpenSSL.
+// With Go 1.6, the Go standard library will have performance on-par with
+// OpenSSL for amd64 (see https://go-review.googlesource.com/#/c/8968/).
+// Prior to that however, using OpenSSL (via cgo) provides a significant
+// performance improvement.
 //
 // By default (without an explicit build tag), this is disabled for darwin
 // since OpenSSL has been marked deprecated on OS X since version 10.7.  The
 // last openssl release on OS X was version 0.9.8 and compiling this file will
 // show these deprecation warnings.  Those sensitive to performance are
-// encouraged to compile a later version of OpenSSL and set the build tag.
+// encouraged to move to Go 1.6.
 //
 // Currently, this file is disabled for linux/arm as a temporary hack.  In
 // practice, linux/arm binaries are often cross-compiled on a linux/amd64 host.
diff --git a/security/ecdsa_openssl_test.go b/security/ecdsa_openssl_test.go
index 70408f6..cba249b 100644
--- a/security/ecdsa_openssl_test.go
+++ b/security/ecdsa_openssl_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !android,linux,amd64,cgo,!noopenssl openssl,cgo
+// +build !go1.6,!android,linux,amd64,cgo,!noopenssl openssl,cgo
 
 package security
 
diff --git a/syncbase/featuretests/cr_v23_test.go b/syncbase/featuretests/cr_v23_test.go
index e5613f0..b7f84b1 100644
--- a/syncbase/featuretests/cr_v23_test.go
+++ b/syncbase/featuretests/cr_v23_test.go
@@ -33,6 +33,10 @@
 // TODO(jlodhia): Add more rules based on value type and combination of key
 // prefix and value type once its implemented.
 func TestV23CRRuleConfig(t *testing.T) {
+	// TODO(jlodhia): Re-enable test after following issue is resolved.
+	// https://github.com/vanadium/issues/issues/1027
+	t.Skip()
+
 	v23test.SkipUnlessRunningIntegrationTests(t)
 	sh := v23test.NewShell(t, v23test.Opts{})
 	defer sh.Cleanup()
@@ -153,6 +157,10 @@
 // 2) 5 rows written as a single batch on both syncbases resulting into a
 //    single conflict for the batch.
 func TestV23CRAppResolved(t *testing.T) {
+	// TODO(jlodhia): Re-enable test after following issue is resolved.
+	// https://github.com/vanadium/issues/issues/1027
+	t.Skip()
+
 	v23test.SkipUnlessRunningIntegrationTests(t)
 	sh := v23test.NewShell(t, v23test.Opts{})
 	defer sh.Cleanup()
diff --git a/syncbase/featuretests/mgmt_v23_test.go b/syncbase/featuretests/mgmt_v23_test.go
deleted file mode 100644
index 1732142..0000000
--- a/syncbase/featuretests/mgmt_v23_test.go
+++ /dev/null
@@ -1,334 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package featuretests_test
-
-// TODO(ivanpi): Port and reenable.
-
-// +build ignore
-
-import (
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"math/rand"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strings"
-	"time"
-
-	_ "v.io/x/ref/runtime/factories/generic"
-	"v.io/x/ref/test/v23tests"
-)
-
-var (
-	hostname   string
-	errTimeout = errors.New("timeout")
-)
-
-func init() {
-	name, err := os.Hostname()
-	if err != nil {
-		panic(fmt.Sprintf("Hostname() failed: %v", err))
-	}
-	hostname = name
-}
-
-// V23TestDeviceManager publishes and runs syncbased using the device manager
-// and verifies that the running instance of syncbased has appropriate
-// blessings.
-func V23TestDeviceManager(i *v23tests.T) {
-	defer fmt.Fprintf(os.Stderr, "--------------- SHUTDOWN ---------------\n")
-	var (
-		workDir       = i.NewTempDir("")
-		binStagingDir = mkSubdir(i, workDir, "bin")
-		dmInstallDir  = filepath.Join(workDir, "dm")
-
-		// Most vanadium command-line utilities will be run by a
-		// principal that has "root:u:alice" as its blessing.
-		// (Where "root" comes from i.Principal().BlessingStore().Default()).
-		// Create those credentials and options to use to setup the
-		// binaries with them.
-		aliceCreds, _ = i.Shell().NewChildCredentials("u:alice")
-		aliceOpts     = i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(aliceCreds)
-
-		// Build all the command-line tools and set them up to run as alice.
-		// applicationd/binaryd servers will be run by alice too.
-		// TODO: applicationd/binaryd should run as a separate "service" role, as
-		// alice is just a user.
-		namespaceBin    = i.BuildV23Pkg("v.io/x/ref/cmd/namespace").WithStartOpts(aliceOpts)
-		deviceBin       = i.BuildV23Pkg("v.io/x/ref/services/device/device").WithStartOpts(aliceOpts)
-		binarydBin      = i.BuildV23Pkg("v.io/x/ref/services/binary/binaryd").WithStartOpts(aliceOpts)
-		applicationdBin = i.BuildV23Pkg("v.io/x/ref/services/application/applicationd").WithStartOpts(aliceOpts)
-		syncbasedBin    = i.BuildV23Pkg("v.io/x/ref/services/syncbase/syncbased")
-
-		// The devicex script is not provided with any credentials, it
-		// will generate its own.  This means that on "devicex start"
-		// the device will have no useful credentials and until "device
-		// claim" is invoked (as alice), it will just sit around
-		// waiting to be claimed.
-		//
-		// Other binaries, like applicationd and binaryd will be run by alice.
-		deviceScriptPath = filepath.Join(os.Getenv("JIRI_ROOT"), "release", "go", "src", "v.io", "x", "ref", "services", "device", "devicex")
-		deviceScript     = i.BinaryFromPath(deviceScriptPath).WithEnv("V23_DEVICE_DIR=" + dmInstallDir)
-
-		mtName = "devices/" + hostname // Name under which the device manager will publish itself.
-	)
-
-	// We also need some tools running with different sets of credentials...
-
-	// Administration tasks will be performed with a blessing that represents a corporate
-	// administrator (which is usually a role account)
-	adminCreds, err := i.Shell().NewChildCredentials("r:admin")
-	if err != nil {
-		i.Fatalf("generating admin creds: %v", err)
-	}
-	adminOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(adminCreds)
-	adminDeviceBin := deviceBin.WithStartOpts(adminOpts)
-	debugBin := i.BuildV23Pkg("v.io/x/ref/services/debug/debug").WithStartOpts(adminOpts)
-
-	// A special set of credentials will be used to give two blessings to the device manager
-	// when claiming it -- one blessing will be from the corporate administrator role who owns
-	// the machine, and the other will be a manufacturer blessing. (This is a hack until
-	// there's a way to separately supply a manufacturer blessing. Eventually, the claim
-	// would really be done by the administrator, and the administrator's blessing would get
-	// added to the manufacturer's blessing, which would already be present.)
-	claimCreds, err := i.Shell().AddToChildCredentials(adminCreds, "m:orange:zphone5:ime-i007")
-	if err != nil {
-		i.Fatalf("adding the mfr blessing to admin creds: %v", err)
-	}
-	claimOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(claimCreds)
-	claimDeviceBin := deviceBin.WithStartOpts(claimOpts)
-
-	// Another set of credentials be used to represent the application publisher, who
-	// signs and pushes binaries
-	pubCreds, err := i.Shell().NewChildCredentials("a:rovio")
-	if err != nil {
-		i.Fatalf("generating publisher creds: %v", err)
-	}
-	pubOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(pubCreds)
-	pubDeviceBin := deviceBin.WithStartOpts(pubOpts)
-	applicationBin := i.BuildV23Pkg("v.io/x/ref/services/application/application").WithStartOpts(pubOpts)
-
-	v23tests.RunRootMT(i, "--v23.tcp.address=127.0.0.1:0")
-	buildAndCopyBinaries(
-		i,
-		binStagingDir,
-		"v.io/x/ref/services/device/deviced",
-		"v.io/x/ref/services/agent/agentd",
-		"v.io/x/ref/services/device/suidhelper",
-		"v.io/x/ref/services/device/inithelper")
-
-	appDName := "applications"
-	devicedAppName := filepath.Join(appDName, "deviced", "test")
-
-	deviceScript.Start(
-		"install",
-		binStagingDir,
-		"--single_user",
-		"--origin="+devicedAppName,
-		"--",
-		"--v23.tcp.address=127.0.0.1:0",
-		"--neighborhood-name="+fmt.Sprintf("%s-%d-%d", hostname, os.Getpid(), rand.Int()),
-	).WaitOrDie(os.Stdout, os.Stderr)
-	deviceScript.Start("start").WaitOrDie(os.Stdout, os.Stderr)
-	// Grab the endpoint for the claimable service from the device manager's
-	// log.
-	dmLog := filepath.Join(dmInstallDir, "dmroot/device-manager/logs/deviced.INFO")
-	var claimableEP string
-	expiry := time.Now().Add(30 * time.Second)
-	for {
-		if time.Now().After(expiry) {
-			i.Fatalf("Timed out looking for claimable endpoint in %v", dmLog)
-		}
-		startLog, err := ioutil.ReadFile(dmLog)
-		if err != nil {
-			i.Logf("Couldn't read log %v: %v", dmLog, err)
-			time.Sleep(time.Second)
-			continue
-		}
-		re := regexp.MustCompile(`Unclaimed device manager endpoint: (.*)`)
-		matches := re.FindSubmatch(startLog)
-		if len(matches) == 0 {
-			i.Logf("Couldn't find match in %v [%s]", dmLog, startLog)
-			time.Sleep(time.Second)
-			continue
-		}
-		if len(matches) < 2 {
-			i.Fatalf("Wrong match in %v (%d) %v", dmLog, len(matches), string(matches[0]))
-		}
-		claimableEP = string(matches[len(matches)-1])
-		break
-	}
-	// Claim the device as "root:u:alice:myworkstation".
-	claimDeviceBin.Start("claim", claimableEP, "myworkstation").WaitOrDie(os.Stdout, os.Stderr)
-
-	resolve := func(name string) string {
-		resolver := func() (interface{}, error) {
-			// Use Start, rather than Run, since it's ok for 'namespace resolve'
-			// to fail with 'name doesn't exist'
-			inv := namespaceBin.Start("resolve", name)
-			// Cleanup after ourselves to avoid leaving a ton of invocations
-			// lying around which obscure logging output.
-			defer inv.Wait(nil, os.Stderr)
-			if r := strings.TrimRight(inv.Output(), "\n"); len(r) > 0 {
-				return r, nil
-			}
-			return nil, nil
-		}
-		return i.WaitFor(resolver, 100*time.Millisecond, time.Minute).(string)
-	}
-
-	// Wait for the device manager to publish its mount table entry.
-	resolve(mtName)
-	adminDeviceBin.Run("acl", "set", mtName+"/devmgr/device", "root:u:alice", "Read,Resolve,Write")
-
-	// Verify the device's default blessing is as expected.
-	mfrBlessing := "root:m:orange:zphone5:ime-i007:myworkstation"
-	ownerBlessing := "root:r:admin:myworkstation"
-	inv := debugBin.Start("stats", "read", mtName+"/devmgr/__debug/stats/security/principal/*/blessingstore/*")
-	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+" + mfrBlessing + "," + ownerBlessing)
-	inv.Wait(nil, os.Stderr)
-
-	// Get the device's profile, which should be set to non-empty string
-	inv = adminDeviceBin.Start("describe", mtName+"/devmgr/device")
-
-	parts := inv.ExpectRE(`{Profiles:map\[(.*):{}\]}`, 1)
-	inv.Wait(nil, os.Stderr)
-	expectOneMatch := func(parts [][]string) string {
-		if len(parts) != 1 || len(parts[0]) != 2 {
-			loc := v23tests.Caller(1)
-			i.Fatalf("%s: failed to match profile: %#v", loc, parts)
-		}
-		return parts[0][1]
-	}
-	deviceProfile := expectOneMatch(parts)
-	if len(deviceProfile) == 0 {
-		i.Fatalf("failed to get profile")
-	}
-
-	// Start a binaryd server that will serve the binary for the test
-	// application to be installed on the device.
-	binarydName := "binaries"
-	binarydBin.Start(
-		"--name=binaries",
-		"--root-dir="+filepath.Join(workDir, "binstore"),
-		"--v23.tcp.address=127.0.0.1:0",
-		"--http=127.0.0.1:0")
-	// Allow publishers to update binaries
-	deviceBin.Run("acl", "set", binarydName, "root:a", "Write")
-
-	// Start an applicationd server that will serve the application
-	// envelope for the test application to be installed on the device.
-	applicationdBin.Start(
-		"--name="+appDName,
-		"--store="+mkSubdir(i, workDir, "appstore"),
-		"--v23.tcp.address=127.0.0.1:0",
-	)
-	// Allow publishers to create and update envelopes
-	deviceBin.Run("acl", "set", appDName, "root:a", "Read,Write,Resolve")
-
-	syncbasedName := appDName + "/syncbased"
-	syncbasedBinName := binarydName + "/syncbased"
-	syncbasedEnvelopeFilename := filepath.Join(workDir, "syncbased.envelope")
-	syncbasedEnvelope := fmt.Sprintf("{\"Title\":\"syncbased\","+
-		"\"Args\":[\"-v=0\", \"--name=syncbased\", \"--root-dir=%s\", \"--v23.tcp.address=127.0.0.1:0\"]}",
-		mkSubdir(i, workDir, "syncbased"))
-	ioutil.WriteFile(syncbasedEnvelopeFilename, []byte(syncbasedEnvelope), 0666)
-	defer os.Remove(syncbasedEnvelopeFilename)
-
-	output := applicationBin.Run("put", syncbasedName+"/0", deviceProfile, syncbasedEnvelopeFilename)
-	if got, want := output, fmt.Sprintf("Application envelope added for profile %s.", deviceProfile); got != want {
-		i.Fatalf("got %q, want %q", got, want)
-	}
-
-	// Publish the app.
-	pubDeviceBin.Start("publish", "-from", filepath.Dir(syncbasedBin.Path()), "-readers", "root:r:admin", "syncbased").WaitOrDie(os.Stdout, os.Stderr)
-	if got := namespaceBin.Run("glob", syncbasedBinName); len(got) == 0 {
-		i.Fatalf("glob failed for %q", syncbasedBinName)
-	}
-
-	// Install the app on the device.
-	inv = deviceBin.Start("install", mtName+"/devmgr/apps", syncbasedName)
-	installationName := inv.ReadLine()
-	inv.WaitOrDie(os.Stdout, os.Stderr)
-	if installationName == "" {
-		i.Fatalf("got empty installation name from install")
-	}
-
-	// Verify that the installation shows up when globbing the device manager.
-	output = namespaceBin.Run("glob", mtName+"/devmgr/apps/syncbased/*")
-	if got, want := output, installationName; got != want {
-		i.Fatalf("got %q, want %q", got, want)
-	}
-
-	// Start an instance of the app, granting it blessing extension syncbased.
-	inv = deviceBin.Start("instantiate", installationName, "syncbased")
-	instanceName := inv.ReadLine()
-	inv.WaitOrDie(os.Stdout, os.Stderr)
-	if instanceName == "" {
-		i.Fatalf("got empty instance name from new")
-	}
-	deviceBin.Start("run", instanceName).WaitOrDie(os.Stdout, os.Stderr)
-
-	resolve(mtName + "/syncbased")
-
-	// Verify that the instance shows up when globbing the device manager.
-	output = namespaceBin.Run("glob", mtName+"/devmgr/apps/syncbased/*/*")
-	if got, want := output, instanceName; got != want {
-		i.Fatalf("got %q, want %q", got, want)
-	}
-
-	// Verify the app's blessings. We check the default blessing, as well as the
-	// "..." blessing, which should be the default blessing plus a publisher blessing.
-	userBlessing := "root:u:alice:syncbased"
-	pubBlessing := "root:a:rovio:apps:published:syncbased"
-	// Just to remind:
-	// mfrBlessing   = "root:m:orange:zphone5:ime-i007:myworkstation"
-	// ownerBlessing = "root:r:admin:myworkstation"
-	appBlessing := mfrBlessing + ":a:" + pubBlessing + "," + ownerBlessing + ":a:" + pubBlessing
-	inv = debugBin.Start("stats", "read", instanceName+"/stats/security/principal/*/blessingstore/*")
-	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+"+userBlessing+"$", "[.][.][.][ ]+"+userBlessing+","+appBlessing)
-	inv.Wait(nil, os.Stderr)
-
-	// Kill and delete the instance.
-	deviceBin.Run("kill", instanceName)
-	deviceBin.Run("delete", instanceName)
-
-	// Shut down the device manager.
-	deviceScript.Run("stop")
-
-	// Wait for the mounttable entry to go away.
-	resolveGone := func(name string) string {
-		resolver := func() (interface{}, error) {
-			inv := namespaceBin.Start("resolve", name)
-			defer inv.Wait(nil, os.Stderr)
-			if r := strings.TrimRight(inv.Output(), "\n"); len(r) == 0 {
-				return r, nil
-			}
-			return nil, nil
-		}
-		return i.WaitFor(resolver, 100*time.Millisecond, time.Minute).(string)
-	}
-	resolveGone(mtName)
-	deviceScript.Run("uninstall")
-}
-
-func buildAndCopyBinaries(i *v23tests.T, destinationDir string, packages ...string) {
-	var args []string
-	for _, pkg := range packages {
-		args = append(args, i.BuildGoPkg(pkg).Path())
-	}
-	args = append(args, destinationDir)
-	i.BinaryFromPath("/bin/cp").Start(args...).WaitOrDie(os.Stdout, os.Stderr)
-}
-
-func mkSubdir(i *v23tests.T, parent, child string) string {
-	dir := filepath.Join(parent, child)
-	if err := os.Mkdir(dir, 0755); err != nil {
-		i.Fatalf("failed to create %q: %v", dir, err)
-	}
-	return dir
-}