veyron/lib/modules: provide a means to auto-register subprocesses.

Change-Id: I8f01f3da16bf223e7af1613d637742608c84cc86
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index b467fbc..96ed5d5 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -42,7 +42,6 @@
 
 import (
 	"flag"
-	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
@@ -70,18 +69,28 @@
 	help    string
 }
 
-type childRegistrar struct {
-	sync.Mutex
-	mains map[string]Main
+type childEntryPoint struct {
+	fn   Main
+	help string
 }
 
-var child = &childRegistrar{mains: make(map[string]Main)}
+type childRegistrar struct {
+	sync.Mutex
+	mains map[string]*childEntryPoint
+}
+
+var child = &childRegistrar{mains: make(map[string]*childEntryPoint)}
 
 // NewShell creates a new instance of Shell. If this new instance is
 // is a test and no identity has been configured in the environment
 // via VEYRON_IDENTITY then CreateAndUseNewID will be used to configure a new
 // ID for the shell and its children.
-func NewShell() *Shell {
+// NewShell takes optional regexp patterns that can be used to specify
+// subprocess commands that are implemented in the same binary as this shell
+// (i.e. have been registered using modules.RegisterChild) to be
+// automatically added to it. If the patterns fail to match any such command
+// then they have no effect.
+func NewShell(patterns ...string) *Shell {
 	// TODO(cnicolaou): should create a new identity if one doesn't
 	// already exist
 	sh := &Shell{
@@ -94,6 +103,9 @@
 			panic(err)
 		}
 	}
+	for _, pattern := range patterns {
+		child.addSubprocesses(sh, pattern)
+	}
 	return sh
 }
 
@@ -125,10 +137,14 @@
 // as a subprocess. In addition, the child process must call RegisterChild
 // using the same name used here and provide the function to be executed
 // in the child.
-func (sh *Shell) AddSubprocess(name string, help string) {
+func (sh *Shell) AddSubprocess(name, help string) {
 	if !child.hasCommand(name) {
 		vlog.Infof("Warning: %q is not registered with modules.Dispatcher", name)
 	}
+	sh.addSubprocess(name, help)
+}
+
+func (sh *Shell) addSubprocess(name string, help string) {
 	entryPoint := ShellEntryPoint + "=" + name
 	sh.mu.Lock()
 	sh.cmds[name] = &commandDesc{func() command { return newExecHandle(entryPoint) }, help}
@@ -168,15 +184,22 @@
 // Start starts the specified command, it returns a Handle which can be used
 // for interacting with that command. The Shell tracks all of the Handles
 // that it creates so that it can shut them down when asked to.
-func (sh *Shell) Start(command string, args ...string) (Handle, error) {
+// Commands may have already been registered with the Shell using AddFunction
+// or AddSubprocess, but if not, they will treated as subprocess commands
+// and an attempt made to run them. Such 'dynamically' started subprocess
+// commands are not remembered the by Shell and do not provide a 'help'
+// message etc; their handles are remembered and will be acted on by
+// the Cleanup method. If the non-registered subprocess command does not
+// exist then the Start command will return an error.
+func (sh *Shell) Start(name string, args ...string) (Handle, error) {
 	sh.mu.Lock()
-	cmd := sh.cmds[command]
-	if cmd == nil {
-		sh.mu.Unlock()
-		return nil, fmt.Errorf("command %q is not available", command)
-	}
-	expanded := append([]string{command}, sh.expand(args...)...)
+	cmd := sh.cmds[name]
 	sh.mu.Unlock()
+	if cmd == nil {
+		entryPoint := ShellEntryPoint + "=" + name
+		cmd = &commandDesc{func() command { return newExecHandle(entryPoint) }, ""}
+	}
+	expanded := append([]string{name}, sh.expand(args...)...)
 	h, err := cmd.factory().start(sh, expanded...)
 	if err != nil {
 		return nil, err