x.ref/lib/exec: add an option to allow bypassing the parent/child protocol.

Change-Id: I4c12c4d80d64021a6bf0671f19b4468455c7bad1
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index 0c31dcf..ddb3d5b 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -21,15 +21,17 @@
 )
 
 var (
-	ErrAuthTimeout    = errors.New("timeout in auth handshake")
-	ErrTimeout        = errors.New("timeout waiting for child")
-	ErrSecretTooLarge = errors.New("secret is too large")
+	ErrAuthTimeout      = errors.New("timeout in auth handshake")
+	ErrTimeout          = errors.New("timeout waiting for child")
+	ErrSecretTooLarge   = errors.New("secret is too large")
+	ErrNotUsingProtocol = errors.New("not using parent/child exec protocol")
 )
 
 // A ParentHandle is the Parent process' means of managing a single child.
 type ParentHandle struct {
 	c           *exec.Cmd
 	config      Config
+	protocol    bool // true if we're using the parent/child protocol.
 	secret      string
 	statusRead  *os.File
 	statusWrite *os.File
@@ -71,11 +73,19 @@
 // ExecParentHandleOpt makes TimeKeeperOpt an instance of ParentHandleOpt.
 func (TimeKeeperOpt) ExecParentHandleOpt() {}
 
+// UseExecProtocolOpt can be used to control whether parent/child handshake
+// protocol is used. WaitForReady will return immediately with an error if
+// this option is set to false.
+type UseExecProtocolOpt bool
+
+func (UseExecProtocolOpt) ExecParentHandleOpt() {}
+
 // NewParentHandle creates a ParentHandle for the child process represented by
 // an instance of exec.Cmd.
 func NewParentHandle(c *exec.Cmd, opts ...ParentHandleOpt) *ParentHandle {
 	cfg, secret := NewConfig(), ""
 	tk := timekeeper.RealTime()
+	protocol := true
 	for _, opt := range opts {
 		switch v := opt.(type) {
 		case ConfigOpt:
@@ -84,15 +94,18 @@
 			secret = string(v)
 		case TimeKeeperOpt:
 			tk = v
+		case UseExecProtocolOpt:
+			protocol = bool(v)
 		default:
 			vlog.Errorf("Unrecognized parent option: %v", v)
 		}
 	}
 	return &ParentHandle{
-		c:      c,
-		config: cfg,
-		secret: secret,
-		tk:     tk,
+		c:        c,
+		protocol: protocol,
+		config:   cfg,
+		secret:   secret,
+		tk:       tk,
 	}
 }
 
@@ -109,6 +122,11 @@
 		}
 		nenv = append(nenv, e)
 	}
+
+	if !p.protocol {
+		return p.c.Start()
+	}
+
 	p.c.Env = append(nenv, consts.ExecVersionVariable+"="+version1)
 
 	// Create anonymous pipe for communicating data between the child
@@ -161,6 +179,7 @@
 		return err
 	}
 	return nil
+
 }
 
 // copy is like io.Copy, but it also treats the receipt of the special eofChar
@@ -209,6 +228,9 @@
 
 // WaitForReady will wait for the child process to become ready.
 func (p *ParentHandle) WaitForReady(timeout time.Duration) error {
+	if !p.protocol {
+		return ErrNotUsingProtocol
+	}
 	// An invariant of WaitForReady is that both statusWrite and statusRead
 	// get closed before WaitForStatus returns (statusRead gets closed by
 	// waitForStatus).