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()
}