Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 1 | package exec |
| 2 | |
| 3 | import ( |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 4 | "encoding/binary" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 5 | "errors" |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 6 | "io" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 7 | "os" |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 8 | "sync" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 9 | ) |
| 10 | |
| 11 | var ( |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 12 | ErrNoVersion = errors.New(VersionVariable + " environment variable missing") |
| 13 | ErrUnsupportedVersion = errors.New("Unsupported version of veyron/lib/exec request by " + VersionVariable + " environment variable") |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 14 | ) |
| 15 | |
| 16 | type ChildHandle struct { |
Bogdan Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 17 | // Config is passed down from the parent. |
Cosmos Nicolaou | 486d349 | 2014-09-30 22:21:20 -0700 | [diff] [blame] | 18 | Config Config |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 19 | // Secret is a secret passed to the child by its parent via a |
| 20 | // trusted channel. |
| 21 | Secret string |
Bogdan Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 22 | // 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 Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 26 | statusPipe *os.File |
| 27 | } |
| 28 | |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 29 | var ( |
| 30 | childHandle *ChildHandle |
| 31 | childHandleErr error |
| 32 | once sync.Once |
| 33 | ) |
| 34 | |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 35 | // 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 Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 39 | const fileOffset = 5 |
| 40 | |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 41 | // 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 Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 46 | // |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 47 | // 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 Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 51 | func GetChildHandle() (*ChildHandle, error) { |
| 52 | once.Do(func() { |
| 53 | childHandle, childHandleErr = createChildHandle() |
| 54 | }) |
| 55 | return childHandle, childHandleErr |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | // SetReady writes a 'ready' status to its parent. |
| 59 | func (c *ChildHandle) SetReady() error { |
| 60 | _, err := c.statusPipe.Write([]byte(readyStatus)) |
| 61 | c.statusPipe.Close() |
| 62 | return err |
| 63 | } |
| 64 | |
Cosmos Nicolaou | 1c18c1c | 2014-10-08 16:37:10 -0700 | [diff] [blame] | 65 | // SetFailed writes a 'failed' status to its parent. |
| 66 | func (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 Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 72 | // 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. |
| 75 | func (c *ChildHandle) NewExtraFile(i uintptr, name string) *os.File { |
| 76 | return os.NewFile(i+fileOffset, name) |
| 77 | } |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 78 | |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 79 | func createChildHandle() (*ChildHandle, error) { |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 80 | switch os.Getenv(VersionVariable) { |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 81 | case "": |
| 82 | return nil, ErrNoVersion |
| 83 | case version1: |
Cosmos Nicolaou | e664f3f | 2014-10-20 17:40:05 -0700 | [diff] [blame] | 84 | os.Setenv(VersionVariable, "") |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 85 | // 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 Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 91 | serializedConfig, err := decodeString(dataPipe) |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 92 | if err != nil { |
| 93 | return nil, err |
| 94 | } |
Cosmos Nicolaou | 486d349 | 2014-09-30 22:21:20 -0700 | [diff] [blame] | 95 | cfg := NewConfig() |
Bogdan Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 96 | if err := cfg.MergeFrom(serializedConfig); err != nil { |
| 97 | return nil, err |
| 98 | } |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 99 | secret, err := decodeString(dataPipe) |
| 100 | if err != nil { |
| 101 | return nil, err |
| 102 | } |
| 103 | childHandle = &ChildHandle{ |
Bogdan Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 104 | Config: cfg, |
| 105 | Secret: secret, |
| 106 | statusPipe: os.NewFile(4, "status_wr"), |
Jiri Simsa | 84059da | 2014-06-02 17:22:05 -0700 | [diff] [blame] | 107 | } |
| 108 | return childHandle, nil |
| 109 | } |
| 110 | |
| 111 | func decodeString(r io.Reader) (string, error) { |
Jiri Simsa | c199bc1 | 2014-05-30 12:52:24 -0700 | [diff] [blame] | 112 | 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 | } |