veyron/mgmt/lib/exec: move this into veyron/lib/exec and merge veyron/lib/config with it.

Change-Id: I8c5b2d1f1a4a9d3c48b6e76e1fe5e43c93416c0d
diff --git a/lib/exec/child.go b/lib/exec/child.go
new file mode 100644
index 0000000..72055e4
--- /dev/null
+++ b/lib/exec/child.go
@@ -0,0 +1,117 @@
+package exec
+
+import (
+	"encoding/binary"
+	"errors"
+	"io"
+	"os"
+	"sync"
+)
+
+var (
+	ErrNoVersion          = errors.New(versionVariable + " environment variable missing")
+	ErrUnsupportedVersion = errors.New("Unsupported version of veyron/lib/exec request by " + versionVariable + " environment variable")
+)
+
+type ChildHandle struct {
+	// Config is passed down from the parent.
+	Config Config
+	// 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. It is meant to be invoked by the
+	// veyron framework to notify the parent that the child process has
+	// successfully started.
+	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
+
+// 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 GetChildHandle() (*ChildHandle, error) {
+	once.Do(func() {
+		childHandle, childHandleErr = createChildHandle()
+	})
+	return childHandle, childHandleErr
+}
+
+// SetReady writes a 'ready' status to its parent.
+func (c *ChildHandle) SetReady() error {
+	_, err := c.statusPipe.Write([]byte(readyStatus))
+	c.statusPipe.Close()
+	return err
+}
+
+// NewExtraFile creates a new file handle for the i-th file descriptor after
+// discounting stdout, stderr, stdin and the files reserved by the framework for
+// its own purposes.
+func (c *ChildHandle) NewExtraFile(i uintptr, name string) *os.File {
+	return os.NewFile(i+fileOffset, name)
+}
+
+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")
+	serializedConfig, err := decodeString(dataPipe)
+	if err != nil {
+		return nil, err
+	}
+	cfg := NewConfig()
+	if err := cfg.MergeFrom(serializedConfig); err != nil {
+		return nil, err
+	}
+	secret, err := decodeString(dataPipe)
+	if err != nil {
+		return nil, err
+	}
+	childHandle = &ChildHandle{
+		Config:     cfg,
+		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
+	}
+	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
+}