veyron/{lib/exec,runtimes/google}: adding the veyron exec library
SetReady() invocation to runtime initialization, renaming
NewChildHandle() to GetChildHandle() and making it idempotent.

Change-Id: I9f83b1a043e0086c7850d34e7fcf45f0cde21182
diff --git a/lib/exec/child.go b/lib/exec/child.go
index f200751..2353664 100644
--- a/lib/exec/child.go
+++ b/lib/exec/child.go
@@ -5,6 +5,7 @@
 	"errors"
 	"io"
 	"os"
+	"sync"
 )
 
 var (
@@ -33,52 +34,33 @@
 	statusPipe *os.File
 }
 
+var (
+	childHandle    *ChildHandle
+	childHandleErr error
+	once           sync.Once
+)
+
 // fileOffset accounts for the file descriptors that are always passed
 // to the child by the parent: stderr, stdin, stdout, data read, and
 // status write. Any extra files added by the client will follow
 // fileOffset.
 const fileOffset = 5
 
-// NewChildHandle creates a new ChildHandle that can be used to signal
-// that the child is 'ready' (by calling SetReady) to its parent. The
-// value of the ChildHandle's Secret securely passed to it by the
-// parent; this is intended for subsequent use to create a secure
-// communication channels and or authentication.
+// GetChildHandle returns a ChildHandle that can be used to signal
+// that the child is 'ready' (by calling SetReady) to its parent or to
+// retrieve data securely passed to this process by its parent. For
+// instance, a secret intended to create a secure communication
+// channels and or authentication.
 //
 // If the child is relying on exec.Cmd.ExtraFiles then its first file
 // descriptor will not be 3, but will be offset by extra files added
 // by the framework. The developer should use the NewExtraFile method
 // to robustly get their extra files with the correct offset applied.
-func NewChildHandle() (*ChildHandle, error) {
-	switch os.Getenv(versionVariable) {
-	case "":
-		return nil, ErrNoVersion
-	case version1:
-		// TODO(cnicolaou): need to use major.minor.build format for
-		// version #s.
-	default:
-		return nil, ErrUnsupportedVersion
-	}
-	dataPipe := os.NewFile(3, "data_rd")
-	endpoint, err := readData(dataPipe)
-	if err != nil {
-		return nil, err
-	}
-	id, err := readData(dataPipe)
-	if err != nil {
-		return nil, err
-	}
-	secret, err := readData(dataPipe)
-	if err != nil {
-		return nil, err
-	}
-	c := &ChildHandle{
-		Endpoint:   endpoint,
-		ID:         id,
-		Secret:     secret,
-		statusPipe: os.NewFile(4, "status_wr"),
-	}
-	return c, nil
+func GetChildHandle() (*ChildHandle, error) {
+	once.Do(func() {
+		childHandle, childHandleErr = createChildHandle()
+	})
+	return childHandle, childHandleErr
 }
 
 // SetReady writes a 'ready' status to its parent.
@@ -95,7 +77,39 @@
 	return os.NewFile(i+fileOffset, name)
 }
 
-func readData(r io.Reader) (string, error) {
+func createChildHandle() (*ChildHandle, error) {
+	switch os.Getenv(versionVariable) {
+	case "":
+		return nil, ErrNoVersion
+	case version1:
+		// TODO(cnicolaou): need to use major.minor.build format for
+		// version #s.
+	default:
+		return nil, ErrUnsupportedVersion
+	}
+	dataPipe := os.NewFile(3, "data_rd")
+	endpoint, err := decodeString(dataPipe)
+	if err != nil {
+		return nil, err
+	}
+	id, err := decodeString(dataPipe)
+	if err != nil {
+		return nil, err
+	}
+	secret, err := decodeString(dataPipe)
+	if err != nil {
+		return nil, err
+	}
+	childHandle = &ChildHandle{
+		Endpoint:   endpoint,
+		ID:         id,
+		Secret:     secret,
+		statusPipe: os.NewFile(4, "status_wr"),
+	}
+	return childHandle, nil
+}
+
+func decodeString(r io.Reader) (string, error) {
 	var l int64 = 0
 	if err := binary.Read(r, binary.BigEndian, &l); err != nil {
 		return "", err
diff --git a/lib/exec/example_test.go b/lib/exec/example_test.go
index e5eeb5a..c66adcc 100644
--- a/lib/exec/example_test.go
+++ b/lib/exec/example_test.go
@@ -7,7 +7,7 @@
 )
 
 func ExampleChildHandle() {
-	ch, _ := NewChildHandle()
+	ch, _ := GetChildHandle()
 	// Initalize the app/service, access the secret shared with the
 	// child by its parent
 	_ = ch.Secret
diff --git a/lib/exec/exec_test.go b/lib/exec/exec_test.go
index 4da8b5b..2e4c143 100644
--- a/lib/exec/exec_test.go
+++ b/lib/exec/exec_test.go
@@ -155,7 +155,7 @@
 
 func TestNoVersion(t *testing.T) {
 	// Make sure that Init correctly tests for the presence of VEXEC_VERSION
-	_, err := vexec.NewChildHandle()
+	_, err := vexec.GetChildHandle()
 	if err != vexec.ErrNoVersion {
 		t.Errorf("Should be missing Version")
 	}
@@ -424,13 +424,13 @@
 
 	switch cmd {
 	case "testNeverReady":
-		_, err := vexec.NewChildHandle()
+		_, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal(os.Stderr, "%s\n", err)
 		}
 		fmt.Fprintf(os.Stderr, "never ready")
 	case "testTooSlowToReady":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal(os.Stderr, "%s\n", err)
 		}
@@ -455,14 +455,14 @@
 		}
 		os.Exit(0)
 	case "testReady":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal(os.Stderr, "%s", err)
 		}
 		ch.SetReady()
 		fmt.Fprintf(os.Stderr, ".")
 	case "testReadySlow":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal(os.Stderr, "%s", err)
 		}
@@ -481,7 +481,7 @@
 		ch.SetReady()
 		fmt.Fprintf(os.Stderr, "..")
 	case "testSuccess", "testError":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal(os.Stderr, "%s\n", err)
 		}
@@ -499,28 +499,28 @@
 		r := <-rc
 		os.Exit(r)
 	case "testEndpoint":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatalf("%v", err)
 		} else {
 			fmt.Fprintf(os.Stderr, "%s", ch.Endpoint)
 		}
 	case "testID":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatalf("%s", err)
 		} else {
 			fmt.Fprintf(os.Stderr, "%s", ch.ID)
 		}
 	case "testSecret":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatalf("%s", err)
 		} else {
 			fmt.Fprintf(os.Stderr, "%s", ch.Secret)
 		}
 	case "testExtraFiles":
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal("error.... %s\n", err)
 		}
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index 2a77924..b2789fc 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -137,17 +137,17 @@
 		return err
 	}
 	// Pass data to the child using a pipe.
-	if err := writeData(dataWrite, p.endpoint); err != nil {
+	if err := encodeString(dataWrite, p.endpoint); err != nil {
 		p.statusWrite.Close()
 		p.statusRead.Close()
 		return err
 	}
-	if err := writeData(dataWrite, p.id); err != nil {
+	if err := encodeString(dataWrite, p.id); err != nil {
 		p.statusWrite.Close()
 		p.statusRead.Close()
 		return err
 	}
-	if err := writeData(dataWrite, p.secret); err != nil {
+	if err := encodeString(dataWrite, p.secret); err != nil {
 		p.statusWrite.Close()
 		p.statusRead.Close()
 		return err
@@ -250,7 +250,7 @@
 	return p.c.Wait()
 }
 
-func writeData(w io.Writer, data string) error {
+func encodeString(w io.Writer, data string) error {
 	l := len(data)
 	if err := binary.Write(w, binary.BigEndian, int64(l)); err != nil {
 		return err
diff --git a/lib/testutil/blackbox/subprocess.go b/lib/testutil/blackbox/subprocess.go
index 06b9c51..116b51f 100644
--- a/lib/testutil/blackbox/subprocess.go
+++ b/lib/testutil/blackbox/subprocess.go
@@ -145,7 +145,7 @@
 		// If this process was started using the veyron exec library
 		// we have to use NewExtraFile below to get at the correct
 		// offset for the fd.
-		ch, err := vexec.NewChildHandle()
+		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			vlog.Fatalf("Failed to init child handle: %v", err)
 		}
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index 6122930..19d4871 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -6,14 +6,15 @@
 	"os"
 	"sync"
 
+	"veyron/lib/exec"
+	imounttable "veyron/runtimes/google/naming/mounttable"
+
 	"veyron2"
 	"veyron2/ipc"
 	"veyron2/ipc/stream"
 	"veyron2/naming"
 	"veyron2/product"
 	"veyron2/vlog"
-
-	imounttable "veyron/runtimes/google/naming/mounttable"
 )
 
 type vrt struct {
@@ -117,6 +118,19 @@
 		return err
 	}
 
+	handle, err := exec.GetChildHandle()
+	switch err {
+	case exec.ErrNoVersion:
+		// The process has not been started through the veyron exec
+		// library. No further action is needed.
+	case nil:
+		// The process has been started through the veyron exec
+		// library. Signal the parent the the child is ready.
+		handle.SetReady()
+	default:
+		return err
+	}
+
 	vlog.VI(2).Infof("rt.Init done")
 	return nil
 }
diff --git a/services/mgmt/node/noded/main.go b/services/mgmt/node/noded/main.go
index 76dce1e..3d878a3 100644
--- a/services/mgmt/node/noded/main.go
+++ b/services/mgmt/node/noded/main.go
@@ -3,7 +3,6 @@
 import (
 	"flag"
 
-	"veyron/lib/exec"
 	"veyron/lib/signals"
 	vflag "veyron/security/flag"
 	"veyron/services/mgmt/node/impl"
@@ -47,21 +46,6 @@
 			vlog.Fatalf("Publish(%v) failed: %v", name, err)
 		}
 	}
-	// This should really move into the runtime so that every process
-	// benefits from it, in particular we should use it to securely
-	// pick security credentials from the parent.
-	handle, err := exec.NewChildHandle()
-	switch err {
-	case nil:
-		// Node manager was started by self-update, notify the parent
-		// process that you are ready.
-		handle.SetReady()
-	case exec.ErrNoVersion:
-		// Node manager was not started by self-update, no action is
-		// needed.
-	default:
-		vlog.Fatalf("NewChildHandle() failed: %v", err)
-	}
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals()
 }