veyron/lib/exec: adding support for passing callback endpoint and
nonce to the child process

Change-Id: Ia161ac70bac47f4f3d06de792a21391825ea2dc3
diff --git a/lib/exec/child.go b/lib/exec/child.go
index 676beb2..f200751 100644
--- a/lib/exec/child.go
+++ b/lib/exec/child.go
@@ -1,54 +1,81 @@
 package exec
 
 import (
+	"encoding/binary"
 	"errors"
+	"io"
 	"os"
 )
 
 var (
 	ErrNoVersion          = errors.New(versionVariable + " environment variable missing")
-	ErrUnsupportedVersion = errors.New("Unsupported version of veyron/runtimes/google/lib/exec request by " + versionVariable + " environment variable")
+	ErrUnsupportedVersion = errors.New("Unsupported version of veyron/lib/exec request by " + versionVariable + " environment variable")
 )
 
 type ChildHandle struct {
-	// A Secret passed to the child by its parent via a trusted channel
-	Secret     string
+	// Endpoint is a callback endpoint that can be use to notify the
+	// parent that the child has started up successfully via the
+	// Callback() RPC.
+	Endpoint string
+	// ID is a callback ID that can be used by a parent to identify this
+	// child when the child invokes the Callback() RPC using the
+	// callback endpoint.
+	ID string
+	// Secret is a secret passed to the child by its parent via a
+	// trusted channel.
+	Secret string
+	// statusPipe is a pipe that is used to notify the parent that the
+	// child process has started successfully. Unlike the Callback()
+	// RPC, which is to be invoked by the application to notify the
+	// parent that the application is "ready", the statusPipe is to be
+	// invoked by the veyron framework to notify the parent that the
+	// child process has successfully started.
 	statusPipe *os.File
 }
 
-// fileOffset accounts for the file descriptors that are always passed to the
-// child by the parent: stderr, stdin, stdout, token read, and status write.
-// Any extra files added by the client will follow fileOffset.
+// 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.
+// 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.
 //
-// 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.
+// 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
+		// TODO(cnicolaou): need to use major.minor.build format for
+		// version #s.
 	default:
 		return nil, ErrUnsupportedVersion
 	}
-	tokenPipe := os.NewFile(3, "token_rd")
-
-	buf := make([]byte, MaxSecretSize)
-	n, err := tokenPipe.Read(buf)
+	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{
-		Secret:     string(buf[:n]),
+		Endpoint:   endpoint,
+		ID:         id,
+		Secret:     secret,
 		statusPipe: os.NewFile(4, "status_wr"),
 	}
 	return c, nil
@@ -67,3 +94,19 @@
 func (c *ChildHandle) NewExtraFile(i uintptr, name string) *os.File {
 	return os.NewFile(i+fileOffset, name)
 }
+
+func readData(r io.Reader) (string, error) {
+	var l int64 = 0
+	if err := binary.Read(r, binary.BigEndian, &l); err != nil {
+		return "", err
+	}
+	var data []byte = make([]byte, l)
+	if n, err := r.Read(data); err != nil || int64(n) != l {
+		if err != nil {
+			return "", err
+		} else {
+			return "", errors.New("partial read")
+		}
+	}
+	return string(data), nil
+}