"physical-lock": Simplify scanning for lock devices

This CL simplifies the mechanism for discovering lock
devices by globbing the neighborhood.

Major changes
1) neighborhood-name under which a lock device's mounttable
is made visible is always of the form "lock-<lock name>", regardless of
whether the lock is claimed or unclaimed. Therefore discovering
lock devices simply requires a glob with the pattern "nh/lock-*".
This is a purely local operation at the clients. All locks objects
are accessible, by convention, under the name "nh/lock-<lock name>/lock.

2) The command for scanning lock objects globs over the neighborhood
in a loop in order to accomodate for any time lags in listening to MDNS
advertisments from the lock devices.

Minor change:
The command for scanning for lock objects is renamed from "find-locks"
to "scan".
Change-Id: Ie95b7dcffae7bc3a67de6b06b2e125a2359b09f4
diff --git a/go/src/v.io/x/lock/lock/main.go b/go/src/v.io/x/lock/lock/main.go
index 0ef63b2..2ea6e82 100644
--- a/go/src/v.io/x/lock/lock/main.go
+++ b/go/src/v.io/x/lock/lock/main.go
@@ -25,10 +25,11 @@
 )
 
 var (
-	cmdFindLocks = &cmdline.Command{
-		Runner: v23cmd.RunnerFunc(runFindLocks),
-		Name:   "findlocks",
-		Short:  "Find locks objects mounted in the neighborhood",
+	lockGlobPattern = path.Join("nh", locklib.LockNhPrefix+"*")
+	cmdScan         = &cmdline.Command{
+		Runner: v23cmd.RunnerFunc(runScan),
+		Name:   "scan",
+		Short:  "Scan the neighborhood for lock objects",
 		Long: `
 Globs over the neighborhood to find names of lock objects (both claimed
 and unclaimed).
@@ -87,67 +88,38 @@
 	}
 )
 
-func runFindLocks(ctx *context.T, env *cmdline.Env, args []string) error {
-	const (
-		unclaimedSuffix = "[UNCLAIMED]"
-		claimedSuffix   = "[CLAIMED]"
-	)
-	ctx, cancel := context.WithTimeout(ctx, time.Minute)
-	defer cancel()
+func runScan(ctx *context.T, env *cmdline.Env, args []string) error {
 	ctx, stop, err := withLocalNamespace(ctx)
 	if err != nil {
 		return err
 	}
 	defer stop()
-	ns := v23.GetNamespace(ctx)
 
-	// TODO(ataly): Currently we simply glob over the neighborhood with
-	// specific patterns to find claimed and unclaimed locks, and print
-	// all the names found. Longer term, we should print a name only after
-	// verifying that the object that the name resolves to exposes the
-	// appropricate Lock or UnclaimedLock interface and authenticates with
-	// blessings that are recognized by this client.
-
-	unclaimedCh, err := ns.Glob(ctx, path.Join("nh", locklib.UnclaimedLockNeighborhood, "*"))
-	if err != nil {
-		return err
-	}
-	claimedCh, err := ns.Glob(ctx, path.Join("nh", locklib.ClaimedLockNeighborhood, "*"))
-	if err != nil {
-		// TODO(ataly): We should drain unclaimedCh to avoid a gorouting leak.
-		return err
-	}
-	loop := true
-	for loop {
-		select {
-		case v, ok := <-unclaimedCh:
-			if !ok {
-				loop = false
-			} else {
-				printObjectName(v, unclaimedSuffix)
-			}
-		case v, ok := <-claimedCh:
-			if !ok {
-				loop = false
-			} else {
-				printObjectName(v, claimedSuffix)
-			}
+	locksFound := make(map[string]bool)
+	fmt.Println("Scanning for Locks...")
+	for {
+		ch, err := v23.GetNamespace(ctx).Glob(ctx, lockGlobPattern)
+		if err != nil {
+			return err
 		}
-	}
-	for v := range unclaimedCh {
-		printObjectName(v, unclaimedSuffix)
-	}
-	for v := range claimedCh {
-		printObjectName(v, claimedSuffix)
-	}
-	return nil
-}
+		for v := range ch {
+			switch entry := v.(type) {
+			case *naming.GlobReplyEntry:
+				if name, servers := entry.Value.Name, entry.Value.Servers; len(name) != 0 && !locksFound[name] && len(servers) != 0 {
+					epStr, _ := naming.SplitAddressName(servers[0].Server)
+					ep, err := v23.NewEndpoint(epStr)
+					if err != nil {
+						continue
+					}
 
-func printObjectName(glob naming.GlobReply, suffix string) {
-	switch v := glob.(type) {
-	case *naming.GlobReplyEntry:
-		if v.Value.Name != "" {
-			fmt.Println(v.Value.Name, suffix)
+					locksFound[name] = true
+					if bn := ep.BlessingNames(); len(bn) != 0 {
+						fmt.Printf("%v [owned by %v]\n", name, bn)
+					} else {
+						fmt.Printf("%v\n", name)
+					}
+				}
+			}
 		}
 	}
 }
@@ -157,17 +129,19 @@
 		return fmt.Errorf("requires exactly two arguments <lock>, <name>, provided %d", numargs)
 	}
 	lockname, name := args[0], args[1]
+	lockname = path.Join(lockname, locklib.LockSuffix)
 
-	ctx, cancel := context.WithTimeout(ctx, time.Minute)
-	defer cancel()
 	ctx, stop, err := withLocalNamespace(ctx)
 	if err != nil {
 		return err
 	}
 	defer stop()
 
-	// Skip server endpoint authorization since an unclaimed lock would have
-	// roots that will not be recognized by the claimer.
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
+	defer cancel()
+	// TODO(ataly): We should not skip server endpoint authorization while
+	// claiming locks but instead fetch the blessing root of the lock manufacturer
+	// from an authoritative source and then appropriately authenticate the server.
 	b, err := lock.UnclaimedLockClient(lockname).Claim(ctx, name, options.SkipServerEndpointAuthorization{})
 	if err != nil {
 		return err
@@ -197,15 +171,16 @@
 		return fmt.Errorf("requires exactly one arguments <lock>, provided %d", numargs)
 	}
 	lockname := args[0]
+	lockname = path.Join(lockname, locklib.LockSuffix)
 
-	ctx, cancel := context.WithTimeout(ctx, time.Minute)
-	defer cancel()
 	ctx, stop, err := withLocalNamespace(ctx)
 	if err != nil {
 		return err
 	}
 	defer stop()
 
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
+	defer cancel()
 	if status == lock.Locked {
 		err = lock.LockClient(lockname).Lock(ctx)
 	} else {
@@ -224,15 +199,16 @@
 		return fmt.Errorf("requires exactly one arguments <lock>, provided %d", numargs)
 	}
 	lockname := args[0]
+	lockname = path.Join(lockname, locklib.LockSuffix)
 
-	ctx, cancel := context.WithTimeout(ctx, time.Minute)
-	defer cancel()
 	ctx, stop, err := withLocalNamespace(ctx)
 	if err != nil {
 		return err
 	}
 	defer stop()
 
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
+	defer cancel()
 	status, err := lock.LockClient(lockname).Status(ctx)
 	if err != nil {
 		return err
@@ -285,7 +261,7 @@
 		Long: `
 Command lock claims and manages lock objects.
 `,
-		Children: []*cmdline.Command{cmdFindLocks, cmdClaim, cmdLock, cmdUnlock, cmdStatus},
+		Children: []*cmdline.Command{cmdScan, cmdClaim, cmdLock, cmdUnlock, cmdStatus},
 	}
 	cmdline.Main(root)
 }
diff --git a/go/src/v.io/x/lock/lockd/starter.go b/go/src/v.io/x/lock/lockd/starter.go
index cee86b3..f40f56e 100644
--- a/go/src/v.io/x/lock/lockd/starter.go
+++ b/go/src/v.io/x/lock/lockd/starter.go
@@ -6,6 +6,7 @@
 
 import (
 	"fmt"
+	"math/rand"
 
 	"v.io/v23"
 	"v.io/v23/context"
@@ -16,7 +17,7 @@
 	"v.io/x/lock/locklib"
 )
 
-const unclaimedLockSuffix = "unclaimed-lock-42"
+var unclaimedLockNhSuffix = "unclaimed-lock-" + fmt.Sprintf("%d", rand.Intn(1000000))
 
 // startServer checks whether the lock has been claimed and then appropriately
 // starts the server.
@@ -48,7 +49,7 @@
 	// Start a local mounttable where the unclaimed lock server would
 	// be mounted, and make this mounttable visible in the local
 	// neighborhood.
-	mtName, stopMT, err := locklib.StartMounttable(ctx, configDir, locklib.UnclaimedLockNeighborhood)
+	mtName, stopMT, err := locklib.StartMounttable(ctx, configDir, locklib.LockNhPrefix+unclaimedLockNhSuffix)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -75,8 +76,7 @@
 	}
 
 	claimed := make(chan struct{})
-	name := objectName(ctx, unclaimedLockSuffix)
-	if err := server.Serve(name, newUnclaimedLock(claimed, configDir), security.AllowEveryone()); err != nil {
+	if err := server.Serve(lockObjectName(ctx), newUnclaimedLock(claimed, configDir), security.AllowEveryone()); err != nil {
 		stopUnclaimedLock()
 		return nil, nil, err
 	}
@@ -87,10 +87,11 @@
 }
 
 func startLockServer(ctx *context.T, configDir string) (func(), error) {
+	lockNhSuffix := fmt.Sprintf("%v", v23.GetPrincipal(ctx).BlessingStore().Default())
 	// Start a local mounttable where the lock server would be
 	// mounted, and make this mounttable visible in the local
 	// neighborhood.
-	mtName, stopMT, err := locklib.StartMounttable(ctx, configDir, locklib.ClaimedLockNeighborhood)
+	mtName, stopMT, err := locklib.StartMounttable(ctx, configDir, locklib.LockNhPrefix+lockNhSuffix)
 	if err != nil {
 		return nil, err
 	}
@@ -116,8 +117,7 @@
 		return nil, err
 	}
 
-	name := objectName(ctx, fmt.Sprintf("%v", v23.GetPrincipal(ctx).BlessingStore().Default()))
-	if err := server.Serve(name, newLock(), security.DefaultAuthorizer()); err != nil {
+	if err := server.Serve(lockObjectName(ctx), newLock(), security.DefaultAuthorizer()); err != nil {
 		stopLock()
 		return nil, err
 	}
@@ -145,10 +145,10 @@
 	<-stop // Wait to be stopped
 }
 
-func objectName(ctx *context.T, suffix string) string {
+func lockObjectName(ctx *context.T) string {
 	nsroots := v23.GetNamespace(ctx).Roots()
 	if len(nsroots) == 0 {
 		return ""
 	}
-	return naming.Join(nsroots[0], suffix)
+	return naming.Join(nsroots[0], locklib.LockSuffix)
 }
diff --git a/go/src/v.io/x/lock/locklib/mounttable.go b/go/src/v.io/x/lock/locklib/mounttable.go
index 3028e31..5344ae7 100644
--- a/go/src/v.io/x/lock/locklib/mounttable.go
+++ b/go/src/v.io/x/lock/locklib/mounttable.go
@@ -23,12 +23,13 @@
 
 const (
 	permsFile = "mounttable.perms"
-	// UnclaimedLockNeighborhood is the name in the local neighborhood on which
-	// an unclaimed lock server's mounttable is made visible
-	UnclaimedLockNeighborhood = "unclaimed-locks"
-	// ClaimedLockNeighborhood is the name in the local neighborhood on which
-	// a claimed lock server's mounttable is made visible
-	ClaimedLockNeighborhood = "locks"
+	// LockSuffix is the name under which a lock server is mounted in its
+	// mounttable.
+	LockSuffix = "lock"
+	// LockNeighborhoodPrefix is a prefix of the name in the local
+	// neighborhood on which a lock server's mounttable is made
+	// visible.
+	LockNhPrefix = "lock-"
 )
 
 // StartMounttable starts a local mounttable server with an authorization
@@ -36,7 +37,7 @@
 // other operations to the principal specified by the provided context.
 //
 // The mounttable makes itself visible in the local neighborhood under the
-// provided 'nhName'.
+// name LockNeighborhoodPrefix + <nhName>.
 //
 // Returns the endpoint of the mounttable server and a callback to
 // be invoked to shutdown the mounttable server on success, or an error