blob: 105d0d574cb771a7a08c07dbc5b053e668c7338a [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package exec
2
3import (
Jiri Simsac199bc12014-05-30 12:52:24 -07004 "encoding/binary"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07005 "errors"
Jiri Simsac199bc12014-05-30 12:52:24 -07006 "io"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07007 "os"
Jiri Simsa84059da2014-06-02 17:22:05 -07008 "sync"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07009)
10
11var (
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -070012 ErrNoVersion = errors.New(VersionVariable + " environment variable missing")
13 ErrUnsupportedVersion = errors.New("Unsupported version of veyron/lib/exec request by " + VersionVariable + " environment variable")
Jiri Simsa5293dcb2014-05-10 09:56:38 -070014)
15
16type ChildHandle struct {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070017 // Config is passed down from the parent.
Cosmos Nicolaou486d3492014-09-30 22:21:20 -070018 Config Config
Jiri Simsac199bc12014-05-30 12:52:24 -070019 // Secret is a secret passed to the child by its parent via a
20 // trusted channel.
21 Secret string
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070022 // statusPipe is a pipe that is used to notify the parent that the child
23 // process has started successfully. It is meant to be invoked by the
24 // veyron framework to notify the parent that the child process has
25 // successfully started.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070026 statusPipe *os.File
27}
28
Jiri Simsa84059da2014-06-02 17:22:05 -070029var (
30 childHandle *ChildHandle
31 childHandleErr error
32 once sync.Once
33)
34
Jiri Simsac199bc12014-05-30 12:52:24 -070035// fileOffset accounts for the file descriptors that are always passed
36// to the child by the parent: stderr, stdin, stdout, data read, and
37// status write. Any extra files added by the client will follow
38// fileOffset.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070039const fileOffset = 5
40
Jiri Simsa84059da2014-06-02 17:22:05 -070041// GetChildHandle returns a ChildHandle that can be used to signal
42// that the child is 'ready' (by calling SetReady) to its parent or to
43// retrieve data securely passed to this process by its parent. For
44// instance, a secret intended to create a secure communication
45// channels and or authentication.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070046//
Jiri Simsac199bc12014-05-30 12:52:24 -070047// If the child is relying on exec.Cmd.ExtraFiles then its first file
48// descriptor will not be 3, but will be offset by extra files added
49// by the framework. The developer should use the NewExtraFile method
50// to robustly get their extra files with the correct offset applied.
Jiri Simsa84059da2014-06-02 17:22:05 -070051func GetChildHandle() (*ChildHandle, error) {
52 once.Do(func() {
53 childHandle, childHandleErr = createChildHandle()
54 })
55 return childHandle, childHandleErr
Jiri Simsa5293dcb2014-05-10 09:56:38 -070056}
57
58// SetReady writes a 'ready' status to its parent.
59func (c *ChildHandle) SetReady() error {
60 _, err := c.statusPipe.Write([]byte(readyStatus))
61 c.statusPipe.Close()
62 return err
63}
64
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -070065// SetFailed writes a 'failed' status to its parent.
66func (c *ChildHandle) SetFailed(oerr error) error {
67 _, err := c.statusPipe.Write([]byte(failedStatus + oerr.Error()))
68 c.statusPipe.Close()
69 return err
70}
71
Jiri Simsa5293dcb2014-05-10 09:56:38 -070072// NewExtraFile creates a new file handle for the i-th file descriptor after
73// discounting stdout, stderr, stdin and the files reserved by the framework for
74// its own purposes.
75func (c *ChildHandle) NewExtraFile(i uintptr, name string) *os.File {
76 return os.NewFile(i+fileOffset, name)
77}
Jiri Simsac199bc12014-05-30 12:52:24 -070078
Jiri Simsa84059da2014-06-02 17:22:05 -070079func createChildHandle() (*ChildHandle, error) {
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -070080 switch os.Getenv(VersionVariable) {
Jiri Simsa84059da2014-06-02 17:22:05 -070081 case "":
82 return nil, ErrNoVersion
83 case version1:
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -070084 os.Setenv(VersionVariable, "")
Jiri Simsa84059da2014-06-02 17:22:05 -070085 // TODO(cnicolaou): need to use major.minor.build format for
86 // version #s.
87 default:
88 return nil, ErrUnsupportedVersion
89 }
90 dataPipe := os.NewFile(3, "data_rd")
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070091 serializedConfig, err := decodeString(dataPipe)
Jiri Simsa84059da2014-06-02 17:22:05 -070092 if err != nil {
93 return nil, err
94 }
Cosmos Nicolaou486d3492014-09-30 22:21:20 -070095 cfg := NewConfig()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -070096 if err := cfg.MergeFrom(serializedConfig); err != nil {
97 return nil, err
98 }
Jiri Simsa84059da2014-06-02 17:22:05 -070099 secret, err := decodeString(dataPipe)
100 if err != nil {
101 return nil, err
102 }
103 childHandle = &ChildHandle{
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700104 Config: cfg,
105 Secret: secret,
106 statusPipe: os.NewFile(4, "status_wr"),
Jiri Simsa84059da2014-06-02 17:22:05 -0700107 }
108 return childHandle, nil
109}
110
111func decodeString(r io.Reader) (string, error) {
Jiri Simsac199bc12014-05-30 12:52:24 -0700112 var l int64 = 0
113 if err := binary.Read(r, binary.BigEndian, &l); err != nil {
114 return "", err
115 }
116 var data []byte = make([]byte, l)
117 if n, err := r.Read(data); err != nil || int64(n) != l {
118 if err != nil {
119 return "", err
120 } else {
121 return "", errors.New("partial read")
122 }
123 }
124 return string(data), nil
125}