blob: e4eac10b5259482a4a32292b2e5e510b1fb0628c [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Todd Wang8c4e5cc2015-04-09 11:30:52 -07005// Package modules implements a mechanism for running commonly used services as
6// subprocesses, and client functionality for accessing those services. Such
Todd Wang95873902015-05-22 14:21:30 -07007// services and functions are collectively called 'programs' and are managed by
Todd Wang8c4e5cc2015-04-09 11:30:52 -07008// a 'Registry'. The Shell is analagous to the UNIX shell and maintains a key,
9// value store of environment variables and config settings that are accessible
Todd Wang95873902015-05-22 14:21:30 -070010// to the programs that it hosts. Simple variable expansion is supported.
Cosmos Nicolaou62613842014-08-25 21:57:37 -070011//
Todd Wang95873902015-05-22 14:21:30 -070012// Three types of 'programs' may be invoked via a Shell:
Cosmos Nicolaou62613842014-08-25 21:57:37 -070013//
Todd Wang95873902015-05-22 14:21:30 -070014// 1) Functions of type Main as subprocesses via fork/exec.
15// 2) Arbitrary non-Vanadium programs available on the underlying operating
Todd Wang5507c832015-05-15 22:59:23 -070016// system such as '/bin/cp', 'bash' etc.
Todd Wang95873902015-05-22 14:21:30 -070017// 3) Arbitrary Vanadium programs available on the underlying operating system
Todd Wang5507c832015-05-15 22:59:23 -070018// such as precompiled Vanadium services.
Cosmos Nicolaou62613842014-08-25 21:57:37 -070019//
Todd Wang5507c832015-05-15 22:59:23 -070020// The first type requires that the function to be executed is compiled into the
21// binary executing the calls to the Shell. These functions are registered with
22// a single, per-process, registry.
Cosmos Nicolaou9afe1202014-09-19 13:45:57 -070023//
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070024// The second two types allow for arbitrary binaries to be executed. The
Todd Wang95873902015-05-22 14:21:30 -070025// distinction between a Vanadium and non-Vanadium program is that the Vanadium
26// program implements the protocol used by v.io/x/ref/lib/exec package to
Todd Wang5507c832015-05-15 22:59:23 -070027// synchronise between the parent and child processes and to share information
28// such as the ConfigKey key,value store supported by the Shell, a shared
29// secret, shared file descriptors etc.
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -070030//
31// When the exec protocol is not used the only form of communication with the
32// child processes are environment variables and command line flags and any
33// shared file descriptors explicitly created by the parent process and expected
Todd Wang5507c832015-05-15 22:59:23 -070034// by the child process; the Start method will not create any additional file
35// descriptors.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070036//
37// The registry provides the following functions:
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070038//
Todd Wang95873902015-05-22 14:21:30 -070039// Register: registers a Main function to be executed in a subprocess,
40// the returned Program is typically assigned to a global variable.
41// Dispatch: must be called in the child process to lookup and invoke the
42// requested function. Typically called from TestMain.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070043//
Nicolas Lacasse0f07c602015-09-24 16:57:06 -070044// The jiri tool can automate generation of TestMain. Adding the comment below
Todd Wang95873902015-05-22 14:21:30 -070045// to a test file will generate the appropriate code.
Todd Wang5507c832015-05-15 22:59:23 -070046//
Nicolas Lacasse0f07c602015-09-24 16:57:06 -070047// //go:generate jiri test generate .
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070048//
Nicolas Lacasse0f07c602015-09-24 16:57:06 -070049// Use 'jiri test generate --help' to get a complete explanation.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070050//
Todd Wang95873902015-05-22 14:21:30 -070051// In all cases programs are started by invoking the StartWithOpts method on the
52// Shell with the name of the program to run. An instance of the Handle
Todd Wang5507c832015-05-15 22:59:23 -070053// interface is returned which can be used to interact with the function or
54// subprocess, and in particular to read/write data from/to it using io channels
55// that follow the stdin, stdout, stderr convention. The StartOpts struct is
56// used to control the detailed behaviour of each such invocation. Various
57// helper functions are provided both for creating appropriate instances of
58// StartOpts and for common uses of StartWithOpts.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070059//
Todd Wang5507c832015-05-15 22:59:23 -070060// Each successful call to StartWithOpts returns a handle representing the
Todd Wang95873902015-05-22 14:21:30 -070061// running program. This handle can be used to gain access to that program's
Todd Wang5507c832015-05-15 22:59:23 -070062// stdin, stdout, stderr and to request or synchronize with its termination via
63// the Shutdown method. The Shutdown method can optionally be used to read any
Todd Wang95873902015-05-22 14:21:30 -070064// remaining output from the programs stdout and stderr. The Shell maintains a
Todd Wang5507c832015-05-15 22:59:23 -070065// record of all such handles and will call Shutdown on them in LIFO order when
66// the Shell's Cleanup method is called.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070067//
Todd Wang95873902015-05-22 14:21:30 -070068// A simple protocol must be followed by all programs, in particular, they
69// should wait for their stdin stream to be closed before exiting. The caller
70// can then coordinate with any program by writing to that stdin stream and
71// reading responses from the stdout stream, and it can close stdin when it's
72// ready for the program to exit using the CloseStdin method on the program's
73// handle. Any binary or script that follows this protocol can be used as well.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070074//
Todd Wang5507c832015-05-15 22:59:23 -070075// By default, every Shell created by NewShell starts a security agent to manage
76// principals for child processes. These default credentials can be overridden
77// by passing a nil context to NewShell then specifying VeyronCredentials in the
78// environment provided as a parameter to the StartWithOpts method. It is also
79// possible to specify custom credentials via StartOpts.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070080//
Todd Wang95873902015-05-22 14:21:30 -070081// Interacting with Programs
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070082//
83// Handle.Stdout(), Stdin(), Stderr():
Todd Wang5507c832015-05-15 22:59:23 -070084//
85// StartWithOpts returns a Handle which can be used to interact with the running
Todd Wang95873902015-05-22 14:21:30 -070086// program. In particular, its Stdin() and Stdout() methods give access to the
Todd Wang5507c832015-05-15 22:59:23 -070087// running process' corresponding stdin and stdout and hence can be used to
88// communicate with it. Stderr is handled differently and is configured so that
89// the child's stderr is written to a log file rather than a pipe. This is in
90// order to maximise the liklihood of capturing stderr output from a crashed
91// child process.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -070092//
93// Handle.Shutdown(stdout, stderr io.Writer):
Todd Wang5507c832015-05-15 22:59:23 -070094//
Todd Wang95873902015-05-22 14:21:30 -070095// The Shutdown method is used to gracefully shutdown a program and to
Todd Wang5507c832015-05-15 22:59:23 -070096// synchronise with its termination. In particular, Shutdown can be used to read
Todd Wang95873902015-05-22 14:21:30 -070097// any unread output from the program's stdout and stderr. Note that since
Todd Wang5507c832015-05-15 22:59:23 -070098// Stderr is buffered to a file, Shutdown is able to return the entire contents
99// of that file. This is useful for debugging misbehaving/crashing child
100// processes.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700101//
102// Shell.Cleanup(stdout, stderr io.Writer):
Todd Wang5507c832015-05-15 22:59:23 -0700103//
104// The Shell keeps track of all Handles that it has issued and in particular if
105// Shutdown (or Forget) have not been called, it will call Shutdown for each
Todd Wang95873902015-05-22 14:21:30 -0700106// such Handle in LIFO order. This ensures that all programs will be Shutdown
Todd Wang5507c832015-05-15 22:59:23 -0700107// even if the developer does not explicitly take care to do so for every
108// invocation.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700109//
110// Pipes:
Todd Wang5507c832015-05-15 22:59:23 -0700111//
Todd Wang95873902015-05-22 14:21:30 -0700112// StartWithOpts allows the caller to pass an io.Reader to the program
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700113// (StartOpts.Stdin) for it to read from, rather than creating a new pipe
Todd Wang95873902015-05-22 14:21:30 -0700114// internally. This makes it possible to connect the output of one program to
Todd Wang5507c832015-05-15 22:59:23 -0700115// the input of another directly.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700116//
117// Command Line Arguments:
Todd Wang5507c832015-05-15 22:59:23 -0700118//
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700119// The arguments passed in calls to Start are appended to any system required
120// ones (e.g. for propagating test timeouts, verbosity etc) and the child
Todd Wang95873902015-05-22 14:21:30 -0700121// process will call the program with the result of flag.Args(). In this way the
Todd Wang5507c832015-05-15 22:59:23 -0700122// caller can provide flags used by libraries in the child process as well as
Todd Wang95873902015-05-22 14:21:30 -0700123// those specific to the program and the program will only receive the args
Todd Wang5507c832015-05-15 22:59:23 -0700124// specific to it. The usual "--" convention can be used to override this
125// default behaviour.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700126//
127// Caveats:
128//
Todd Wang95873902015-05-22 14:21:30 -0700129// Handle.Shutdown assumes that the child program/process will terminate when
Todd Wang5507c832015-05-15 22:59:23 -0700130// its stdin stream is closed. This assumption is unlikely to be valid for
Todd Wang95873902015-05-22 14:21:30 -0700131// 'external' programs (e.g. /bin/cp) and in these cases Kill or some other
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700132// application specific mechanism will need to be used.
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700133package modules
134
135import (
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700136 "errors"
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800137 "fmt"
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700138 "io"
Cosmos Nicolaou9afe1202014-09-19 13:45:57 -0700139 "io/ioutil"
140 "os"
Ryan Brown04384432015-08-27 16:08:32 -0700141 "path/filepath"
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700142 "sync"
Ryan Browna08a2212015-01-15 15:40:10 -0800143 "syscall"
Cosmos Nicolaoue5b41502014-10-29 22:55:09 -0700144 "time"
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700145
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700146 "v.io/x/lib/envvar"
147
Jiri Simsa6ac95222015-02-23 16:11:49 -0800148 "v.io/v23"
149 "v.io/v23/context"
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700150 "v.io/v23/logging"
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700151 "v.io/v23/security"
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700152
Todd Wang8123b5e2015-05-14 18:44:43 -0700153 "v.io/x/ref"
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700154 "v.io/x/ref/internal/logger"
Jiri Simsaffceefa2015-02-28 11:03:34 -0800155 "v.io/x/ref/lib/exec"
Ryan Brown04384432015-08-27 16:08:32 -0700156 "v.io/x/ref/services/agent"
Todd Wang88509682015-04-10 10:28:24 -0700157 "v.io/x/ref/services/agent/agentlib"
Todd Wangb3511492015-04-07 23:32:34 -0700158 "v.io/x/ref/services/agent/keymgr"
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700159 "v.io/x/ref/test/expect"
Ankur9f957942014-11-24 16:34:18 -0800160)
161
162const (
163 shellBlessingExtension = "test-shell"
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700164
165 defaultStartTimeout = time.Minute
166 defaultShutdownTimeout = time.Minute
167 defaultExpectTimeout = time.Minute
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700168)
169
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700170var defaultStartOpts = StartOpts{
171 StartTimeout: defaultStartTimeout,
172 ShutdownTimeout: defaultShutdownTimeout,
173 ExpectTimeout: defaultExpectTimeout,
174 ExecProtocol: true,
175}
176
Todd Wang95873902015-05-22 14:21:30 -0700177// Shell represents the context within which programs are run.
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700178type Shell struct {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700179 mu sync.Mutex
180 env map[string]string
Todd Wang5507c832015-05-15 22:59:23 -0700181 handles map[*execHandle]struct{}
182 lifoHandles []*execHandle
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700183 defaultStartOpts StartOpts
Ryan Browna08a2212015-01-15 15:40:10 -0800184 // tmpCredDir is the temporary directory created by this
185 // shell. This must be removed when the shell is cleaned up.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700186 tempCredDir string
187 config exec.Config
188 principal security.Principal
Ryan Brown04384432015-08-27 16:08:32 -0700189 agent agent.KeyManager
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700190 ctx *context.T
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700191 logger logging.Logger
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700192 sessionVerbosity bool
193 cancelCtx func()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700194}
195
Ryan Browna08a2212015-01-15 15:40:10 -0800196// NewShell creates a new instance of Shell.
Ankur50ab9882015-02-17 12:17:17 -0800197//
Ryan Browna08a2212015-01-15 15:40:10 -0800198// If ctx is non-nil, the shell will manage Principals for child processes.
Ankur50ab9882015-02-17 12:17:17 -0800199//
200// If p is non-nil, any child process created has its principal blessed
201// by the default blessings of 'p', Else any child process created has its
202// principal blessed by the default blessings of ctx's principal.
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700203//
204// If verbosity is true additional debugging info will be displayed,
205// in particular by the Shutdown.
206//
207// If t is non-nil, then the expect Session created for every invocation
208// will be constructed with that value of t unless overridden by a
209// StartOpts provided to that invocation. Providing a non-nil value of
210// t enables expect.Session to call t.Error, Errorf and Log.
211func NewShell(ctx *context.T, p security.Principal, verbosity bool, t expect.Testing) (*Shell, error) {
Cosmos Nicolaou9afe1202014-09-19 13:45:57 -0700212 sh := &Shell{
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700213 env: make(map[string]string),
Todd Wang5507c832015-05-15 22:59:23 -0700214 handles: make(map[*execHandle]struct{}),
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700215 config: exec.NewConfig(),
216 defaultStartOpts: defaultStartOpts,
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700217 sessionVerbosity: verbosity,
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700218 }
Cosmos Nicolaou9e909842015-03-17 11:58:59 -0700219 sh.defaultStartOpts = sh.defaultStartOpts.WithSessions(t, time.Minute)
Ryan Browna08a2212015-01-15 15:40:10 -0800220 if ctx == nil {
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700221 sh.logger = logger.Global()
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800222 return sh, nil
Cosmos Nicolaou9afe1202014-09-19 13:45:57 -0700223 }
Ryan Browna08a2212015-01-15 15:40:10 -0800224 var err error
225 ctx, sh.cancelCtx = context.WithCancel(ctx)
Todd Wangad492042015-04-17 15:58:40 -0700226 if ctx, err = v23.WithNewStreamManager(ctx); err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800227 return nil, err
228 }
229 sh.ctx = ctx
Cosmos Nicolaoue3b19322015-06-18 16:05:08 -0700230 sh.logger = ctx
Ryan Browna08a2212015-01-15 15:40:10 -0800231
Bogdan Caprita44c7f982015-09-14 14:39:05 -0700232 if sh.tempCredDir, err = ioutil.TempDir("", "sh_creds"); err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800233 return nil, err
234 }
Ryan Brown04384432015-08-27 16:08:32 -0700235 if sh.agent, err = keymgr.NewLocalAgent(sh.tempCredDir, nil); err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800236 return nil, err
237 }
Ryan Browna08a2212015-01-15 15:40:10 -0800238 sh.principal = p
Ankur50ab9882015-02-17 12:17:17 -0800239 if sh.principal == nil {
Jiri Simsa6ac95222015-02-23 16:11:49 -0800240 sh.principal = v23.GetPrincipal(ctx)
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800241 }
Cosmos Nicolaou344cc4a2014-11-26 15:38:43 -0800242 return sh, nil
Cosmos Nicolaou9afe1202014-09-19 13:45:57 -0700243}
244
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700245// DefaultStartOpts returns the current StartOpts stored with the Shell.
246func (sh *Shell) DefaultStartOpts() StartOpts {
247 return sh.defaultStartOpts
248}
249
250// SetDefaultStartOpts sets the default StartOpts stored with the Shell.
251func (sh *Shell) SetDefaultStartOpts(opts StartOpts) {
252 sh.defaultStartOpts = opts
253}
254
Ryan Brown61d69382015-02-25 11:13:39 -0800255// CustomCredentials encapsulates a Principal which can be shared with
256// one or more processes run by a Shell.
257type CustomCredentials struct {
Ryan Brown04384432015-08-27 16:08:32 -0700258 p security.Principal
259 path string
Ryan Brown61d69382015-02-25 11:13:39 -0800260}
Ankur50ab9882015-02-17 12:17:17 -0800261
Ryan Brown61d69382015-02-25 11:13:39 -0800262// Principal returns the Principal.
263func (c *CustomCredentials) Principal() security.Principal {
264 return c.p
265}
266
Ryan Brown04384432015-08-27 16:08:32 -0700267// Path returns the path to the credential's agent.
268// Typically you would pass this to a child process in EnvAgentPath.
269func (c *CustomCredentials) Path() string {
270 return c.path
Ryan Brown61d69382015-02-25 11:13:39 -0800271}
272
273func dup(conn *os.File) (int, error) {
Bogdan Capritabb37c542015-01-22 10:21:57 -0800274 syscall.ForkLock.RLock()
Ryan Browna08a2212015-01-15 15:40:10 -0800275 fd, err := syscall.Dup(int(conn.Fd()))
Ankur9f957942014-11-24 16:34:18 -0800276 if err != nil {
Bogdan Capritabb37c542015-01-22 10:21:57 -0800277 syscall.ForkLock.RUnlock()
Ryan Brown61d69382015-02-25 11:13:39 -0800278 return -1, err
Ryan Browna08a2212015-01-15 15:40:10 -0800279 }
280 syscall.CloseOnExec(fd)
Bogdan Capritabb37c542015-01-22 10:21:57 -0800281 syscall.ForkLock.RUnlock()
Ryan Brown61d69382015-02-25 11:13:39 -0800282 return fd, nil
283}
284
Bogdan Caprita44c7f982015-09-14 14:39:05 -0700285// NewCustomCredentials creates a new Principal for StartWithOpts.
Ryan Brown61d69382015-02-25 11:13:39 -0800286// Returns nil if the shell is not managing principals.
287func (sh *Shell) NewCustomCredentials() (cred *CustomCredentials, err error) {
288 // Create child principal.
289 if sh.ctx == nil {
290 return nil, nil
291 }
Ryan Brown04384432015-08-27 16:08:32 -0700292 id, err := sh.agent.NewPrincipal(true)
Ryan Brown61d69382015-02-25 11:13:39 -0800293 if err != nil {
294 return nil, err
295 }
Bogdan Caprita44c7f982015-09-14 14:39:05 -0700296 dir, err := ioutil.TempDir(sh.tempCredDir, "a")
Ryan Brown61d69382015-02-25 11:13:39 -0800297 if err != nil {
298 return nil, err
299 }
Ryan Brown04384432015-08-27 16:08:32 -0700300 path := filepath.Join(dir, "sock")
301 if err := sh.agent.ServePrincipal(id, path); err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800302 return nil, err
Ankur9f957942014-11-24 16:34:18 -0800303 }
Ryan Brown04384432015-08-27 16:08:32 -0700304 p, err := agentlib.NewAgentPrincipalX(path)
Ryan Brown8b59ca32015-08-24 21:50:09 +0000305 if err != nil {
Ryan Brown8b59ca32015-08-24 21:50:09 +0000306 return nil, err
307 }
Ryan Brown04384432015-08-27 16:08:32 -0700308 return &CustomCredentials{p, path}, nil
Ryan Brown61d69382015-02-25 11:13:39 -0800309}
310
Asim Shankar34fb2492015-03-12 10:25:46 -0700311// NewChildCredentials creates a new principal, served via the security agent
312// whose blessings are an extension of this shell's principal (with the
313// provided caveats).
314//
315// All processes started by this shell will recognize the credentials created
316// by this call.
317//
318// Returns nil if the shell is not managing principals.
319//
320// Since the Shell type is intended for tests, it is not required to provide
321// caveats. In production scenarios though, one must think long and hard
322// before blessing anothing principal without any caveats.
Arup Mukherjeed8048282015-06-26 23:18:44 -0700323func (sh *Shell) NewChildCredentials(extension string, caveats ...security.Caveat) (*CustomCredentials, error) {
Ryan Brown61d69382015-02-25 11:13:39 -0800324 creds, err := sh.NewCustomCredentials()
325 if creds == nil {
326 return nil, err
327 }
Arup Mukherjeed8048282015-06-26 23:18:44 -0700328 return sh.AddToChildCredentials(creds, extension, caveats...)
329}
330
331func (sh *Shell) AddToChildCredentials(creds *CustomCredentials, extension string, caveats ...security.Caveat) (*CustomCredentials, error) {
Asim Shankar34fb2492015-03-12 10:25:46 -0700332 parent := sh.principal
333 child := creds.p
334 if len(caveats) == 0 {
335 caveats = []security.Caveat{security.UnconstrainedUse()}
336 }
Ankur50ab9882015-02-17 12:17:17 -0800337
338 // Bless the child principal with blessings derived from the default blessings
339 // of shell's principal.
Asim Shankar34fb2492015-03-12 10:25:46 -0700340 blessings, err := parent.Bless(child.PublicKey(), parent.BlessingStore().Default(), extension, caveats[0], caveats[1:]...)
Ankur9f957942014-11-24 16:34:18 -0800341 if err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800342 return nil, err
Ankur9f957942014-11-24 16:34:18 -0800343 }
Arup Mukherjeed8048282015-06-26 23:18:44 -0700344
345 union, err := security.UnionOfBlessings(child.BlessingStore().Default(), blessings)
346 if err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800347 return nil, err
Ankur9f957942014-11-24 16:34:18 -0800348 }
Arup Mukherjeed8048282015-06-26 23:18:44 -0700349
350 if err := child.BlessingStore().SetDefault(union); err != nil {
351 return nil, err
352 }
353 if _, err := child.BlessingStore().Set(union, security.AllPrincipals); err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800354 return nil, err
Ankur9f957942014-11-24 16:34:18 -0800355 }
Asim Shankar34fb2492015-03-12 10:25:46 -0700356 if err := child.AddToRoots(blessings); err != nil {
Ryan Browna08a2212015-01-15 15:40:10 -0800357 return nil, err
Ankur9f957942014-11-24 16:34:18 -0800358 }
359
Ryan Brown61d69382015-02-25 11:13:39 -0800360 return creds, nil
Ankur9f957942014-11-24 16:34:18 -0800361}
362
Todd Wang95873902015-05-22 14:21:30 -0700363// Env represents the environment for Main functions.
364type Env struct {
365 Stdin io.Reader
366 Stdout io.Writer
367 Stderr io.Writer
368 Vars map[string]string // Environment variables
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700369}
370
Todd Wang95873902015-05-22 14:21:30 -0700371// EnvFromOS returns a new Env based on the underlying OS.
372func EnvFromOS() *Env {
373 return &Env{
374 Stdin: os.Stdin,
375 Stdout: os.Stdout,
376 Stderr: os.Stderr,
377 Vars: envvar.SliceToMap(os.Environ()),
378 }
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700379}
380
Todd Wang95873902015-05-22 14:21:30 -0700381// Main describes the entry-point function type for registered programs.
382type Main func(env *Env, args ...string) error
383
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700384// Start is shorthand for StartWithOpts(sh.DefaultStartOpts(), ...)
Todd Wang95873902015-05-22 14:21:30 -0700385func (sh *Shell) Start(env []string, prog Program, args ...string) (Handle, error) {
386 return sh.StartWithOpts(sh.DefaultStartOpts(), env, prog, args...)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700387}
388
389// StartOpts represents the options that can be passed to the
390// StartWithOpts method.
391type StartOpts struct {
392 // Error is set when creating/intializing instances of StartOpts
393 // via one of the factory methods and returned when StartWithOpts
394 // is called. This allows usage of of the form:
395 //
396 // err := sh.StartWithOpts(sh.DefaultStartOpts()...)
397 //
398 // as opposed to:
399 //
400 // opts, err := sh.DefaultStartOpts(....)
401 // if err != nil {
402 // panic(...)
403 // }
404 // sh.StartWithOpts(opts, ....)
405 Error error
406
407 // Stdin, if non-nil, will be used as the stdin for the child process.
408 // If this option is set, then the Stdin() method on the returned Handle
409 // will return nil. The client of this API maintains ownership of stdin
410 // and must close it, i.e. the shell will not do so.
411 Stdin io.Reader
412 // Credentials, if non-nil, will be used as the credentials for the
413 // child process. If the creds are nil or the shell is not managing
414 // principals, the credentials are ignored.
415 Credentials *CustomCredentials
416 // ExecProtocol indicates whether the child process is expected to
417 // implement the v.io/x.ref/lib/exec parent/child protocol.
Todd Wang95873902015-05-22 14:21:30 -0700418 // It should be set to false when running non-vanadium programs
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700419 // (e.g. /bin/cp).
420 ExecProtocol bool
Todd Wang95873902015-05-22 14:21:30 -0700421 // External indicates if the program is an external process rather than
422 // a Main function.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700423 External bool
424 // StartTimeout specifies the amount of time to wait for the
425 // child process to signal its correct intialization for Vanadium
426 // processes that implement the exec parent/child protocol. It has no
427 // effect if External is set to true.
428 StartTimeout time.Duration
429 // ShutdownTimeout specifics the amount of time to wait for the child
430 // process to exit when the Shutdown method is called on that
431 // child's handle.
432 ShutdownTimeout time.Duration
433 // ExpectTesting is used when creating an instance of expect.Session
434 // to embed in Handle.
435 ExpectTesting expect.Testing
436 // ExpectTimeout is the timeout to use with expect.Session.
437 ExpectTimeout time.Duration
438}
439
440// DefaultStartOpts returns an instance of Startops with the current default
441// values. The defaults have values for timeouts, no credentials
442// (StartWithOpts will then create credentials each time it is called),
443// and with ExecProtocol set to true.
444// This is expected to be the common use case.
445func DefaultStartOpts() StartOpts {
446 return defaultStartOpts
447}
448
449// WithCustomCredentials returns an instance of StartOpts with the specified
Asim Shankar34fb2492015-03-12 10:25:46 -0700450// credentials.
451//
452// All other options are set to the current defaults.
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700453func (opts StartOpts) WithCustomCredentials(creds *CustomCredentials) StartOpts {
454 opts.Credentials = creds
455 return opts
456}
457
458// WithSessions returns a copy of opts with the specified expect.Testing and
459// associated timeout.
460func (opts StartOpts) WithSessions(t expect.Testing, timeout time.Duration) StartOpts {
461 opts.ExpectTesting = t
462 opts.ExpectTimeout = timeout
463 return opts
464}
465
466// WithStdin returns a copy of opts with the specified Stdin io.Reader.
467func (opts StartOpts) WithStdin(stdin io.Reader) StartOpts {
468 opts.Stdin = stdin
469 return opts
470}
471
Todd Wang95873902015-05-22 14:21:30 -0700472// NoExecProgram returns a copy of opts with the External option
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700473// enabled and ExecProtocol disabled.
Todd Wang95873902015-05-22 14:21:30 -0700474func (opts StartOpts) NoExecProgram() StartOpts {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700475 opts.External = true
476 opts.ExecProtocol = false
477 return opts
478}
479
Todd Wang95873902015-05-22 14:21:30 -0700480// ExternalProgram returns a copy of StartOpts with the
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700481// External option enabled.
Todd Wang95873902015-05-22 14:21:30 -0700482func (opts StartOpts) ExternalProgram() StartOpts {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700483 opts.External = true
484 return opts
485}
486
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700487var (
Todd Wang95873902015-05-22 14:21:30 -0700488 ErrNotRegistered = errors.New("program not registered")
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700489 ErrNoExecAndCustomCreds = errors.New("ExecProtocol set to false but this invocation is attempting to use custome credentials")
490)
491
Todd Wang95873902015-05-22 14:21:30 -0700492// StartWithOpts starts the specified program according to the supplied
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700493// StartOpts and returns a Handle which can be used for interacting with
Todd Wang95873902015-05-22 14:21:30 -0700494// that program.
Ankur9f957942014-11-24 16:34:18 -0800495//
Todd Wang95873902015-05-22 14:21:30 -0700496// The environment variables for the program are set by merging variables
Ankur9f957942014-11-24 16:34:18 -0800497// from the OS environment, those in this Shell and those provided as a
498// parameter to it. In general, it prefers values from its parameter over
499// those from the Shell, over those from the OS. However, the VeyronCredentials
Ryan Brown10a4ade2015-02-10 13:17:18 -0800500// and agent FdEnvVar variables will never use the value from the Shell or OS.
Ankur9f957942014-11-24 16:34:18 -0800501//
Todd Wang95873902015-05-22 14:21:30 -0700502// If the shell is managing principals, the program is configured to
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700503// connect to the shell's agent. Custom credentials may be specified
504// via StartOpts. If the shell is not managing principals, set
Ryan Brown10a4ade2015-02-10 13:17:18 -0800505// the VeyronCredentials environment variable in the 'env' parameter.
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700506//
507// The Shell tracks all of the Handles that it creates so that it can shut
Cosmos Nicolaoue5b41502014-10-29 22:55:09 -0700508// them down when asked to. The returned Handle may be non-nil even when an
509// error is returned, in which case it may be used to retrieve any output
Todd Wang95873902015-05-22 14:21:30 -0700510// from the failed program.
Cosmos Nicolaou612ad382014-10-29 19:41:35 -0700511//
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700512// StartWithOpts will return a valid handle for errors that occur during the
513// child processes startup process. It is thus possible to call Shutdown
514// to obtain the error output. Handle will be nil if the error is due to
515// some other reason, such as failure to create pipes/files before starting
516// the child process. A common use will therefore be:
Ryan Brown61d69382015-02-25 11:13:39 -0800517//
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700518// h, err := sh.Start(env, "/bin/echo", "hello")
519// if err != nil {
520// if h != nil {
521// h.Shutdown(nil,os.Stderr)
522// }
523// t.Fatal(err)
524// }
Todd Wang95873902015-05-22 14:21:30 -0700525func (sh *Shell) StartWithOpts(opts StartOpts, env []string, prog Program, args ...string) (Handle, error) {
Todd Wang3bb46f52015-05-14 22:01:18 -0700526 var err error
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700527 if opts.Error != nil {
528 return nil, opts.Error
529 }
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700530
Todd Wang95873902015-05-22 14:21:30 -0700531 var info *programInfo
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700532 if opts.External {
Todd Wang95873902015-05-22 14:21:30 -0700533 info = registry.getExternalProgram(prog)
534 } else if info = registry.getProgram(prog); info == nil {
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700535 return nil, ErrNotRegistered
James Ring9d9489d2015-01-27 15:48:07 -0800536 }
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700537
538 if !opts.ExecProtocol && opts.Credentials != nil {
539 return nil, ErrNoExecAndCustomCreds
540 }
541
542 if sh.ctx != nil && opts.ExecProtocol && opts.Credentials == nil {
Asim Shankar34fb2492015-03-12 10:25:46 -0700543 opts.Credentials, err = sh.NewChildCredentials("child")
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700544 if err != nil {
545 return nil, err
546 }
547 }
Ryan Brown61d69382015-02-25 11:13:39 -0800548
Ryan Brown04384432015-08-27 16:08:32 -0700549 var agentPath string
Cosmos Nicolaou52e9a222015-03-16 21:57:16 -0700550 if opts.Credentials != nil {
Ryan Brown04384432015-08-27 16:08:32 -0700551 agentPath = opts.Credentials.Path()
Ryan Browna08a2212015-01-15 15:40:10 -0800552 }
James Ring9d9489d2015-01-27 15:48:07 -0800553
Todd Wang95873902015-05-22 14:21:30 -0700554 handle := info.factory()
Ryan Brown04384432015-08-27 16:08:32 -0700555 h, err := handle.start(sh, agentPath, &opts, sh.setupProgramEnv(env), sh.expand(args))
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700556 if err != nil {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700557 return h, err
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700558 }
559 sh.mu.Lock()
560 sh.handles[h] = struct{}{}
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700561 sh.lifoHandles = append(sh.lifoHandles, h)
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700562 sh.mu.Unlock()
563 return h, nil
564}
565
Todd Wang95873902015-05-22 14:21:30 -0700566// ProgramEnvelope returns the command line and environment that would be used
Todd Wang5507c832015-05-15 22:59:23 -0700567// for running the subprocess if it were started with the specifed arguments.
Todd Wang95873902015-05-22 14:21:30 -0700568func (sh *Shell) ProgramEnvelope(env []string, prog Program, args ...string) ([]string, []string) {
569 info := registry.getProgram(prog)
570 if info == nil {
Cosmos Nicolaoud4f00562014-11-17 20:35:48 -0800571 return []string{}, []string{}
572 }
Todd Wang95873902015-05-22 14:21:30 -0700573 return info.factory().envelope(sh, sh.setupProgramEnv(env), sh.expand(args))
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700574}
575
576// Forget tells the Shell to stop tracking the supplied Handle. This is
577// generally used when the application wants to control the order that
Todd Wang95873902015-05-22 14:21:30 -0700578// programs are shutdown in.
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700579func (sh *Shell) Forget(h Handle) {
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700580 sh.mu.Lock()
Todd Wang5507c832015-05-15 22:59:23 -0700581 if handle, ok := h.(*execHandle); ok {
582 delete(sh.handles, handle)
583 }
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700584 sh.mu.Unlock()
585}
586
Todd Wang5507c832015-05-15 22:59:23 -0700587func (sh *Shell) expand(args []string) []string {
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700588 exp := []string{}
589 for _, a := range args {
590 if len(a) > 0 && a[0] == '$' {
591 if v, present := sh.env[a[1:]]; present {
592 exp = append(exp, v)
593 continue
594 }
595 }
596 exp = append(exp, a)
597 }
598 return exp
599}
600
601// GetVar returns the variable associated with the specified key
602// and an indication of whether it is defined or not.
603func (sh *Shell) GetVar(key string) (string, bool) {
604 sh.mu.Lock()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700605 v, present := sh.env[key]
Todd Wang3bb46f52015-05-14 22:01:18 -0700606 sh.mu.Unlock()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700607 return v, present
608}
609
610// SetVar sets the value to be associated with key.
611func (sh *Shell) SetVar(key, value string) {
612 sh.mu.Lock()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700613 // TODO(cnicolaou): expand value
614 sh.env[key] = value
Todd Wang3bb46f52015-05-14 22:01:18 -0700615 sh.mu.Unlock()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700616}
617
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700618// ClearVar removes the speficied variable from the Shell's environment
619func (sh *Shell) ClearVar(key string) {
620 sh.mu.Lock()
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700621 delete(sh.env, key)
Todd Wang3bb46f52015-05-14 22:01:18 -0700622 sh.mu.Unlock()
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700623}
624
Jiri Simsa37893392014-11-07 10:55:45 -0800625// GetConfigKey returns the value associated with the specified key in
626// the Shell's config and an indication of whether it is defined or
627// not.
628func (sh *Shell) GetConfigKey(key string) (string, bool) {
629 v, err := sh.config.Get(key)
630 return v, err == nil
631}
632
633// SetConfigKey sets the value of the specified key in the Shell's
634// config.
635func (sh *Shell) SetConfigKey(key, value string) {
636 sh.config.Set(key, value)
637}
638
639// ClearConfigKey removes the speficied key from the Shell's config.
640func (sh *Shell) ClearConfigKey(key string) {
641 sh.config.Clear(key)
642}
643
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700644// Env returns the entire set of environment variables associated with this
645// Shell as a string slice.
646func (sh *Shell) Env() []string {
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700647 sh.mu.Lock()
Todd Wang3bb46f52015-05-14 22:01:18 -0700648 vars := envvar.MapToSlice(sh.env)
649 sh.mu.Unlock()
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700650 return vars
651}
652
653// Cleanup calls Shutdown on all of the Handles currently being tracked
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700654// by the Shell and writes to stdout and stderr as per the Shutdown
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700655// method in the Handle interface. Cleanup returns the error from the
656// last Shutdown that returned a non-nil error. The order that the
657// Shutdown routines are executed is not defined.
658func (sh *Shell) Cleanup(stdout, stderr io.Writer) error {
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700659 sh.mu.Lock()
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700660 verbose := sh.sessionVerbosity
661 sh.mu.Unlock()
662
663 writeMsg := func(format string, args ...interface{}) {
664 if !verbose {
665 return
666 }
667 if stderr != nil {
668 fmt.Fprintf(stderr, format, args...)
669 }
670 }
671
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -0700672 writeMsg("---- Shell Cleanup ----\n")
673 defer writeMsg("---- Shell Cleanup Complete ----\n")
674
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700675 sh.mu.Lock()
Todd Wang5507c832015-05-15 22:59:23 -0700676 handles := make([]*execHandle, 0, len(sh.lifoHandles))
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700677 for _, h := range sh.lifoHandles {
678 if _, present := sh.handles[h]; present {
679 handles = append(handles, h)
680 }
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700681 }
Todd Wang5507c832015-05-15 22:59:23 -0700682 sh.handles = make(map[*execHandle]struct{})
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700683 sh.lifoHandles = nil
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700684 sh.mu.Unlock()
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700685 var err error
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700686 for i := len(handles); i > 0; i-- {
687 h := handles[i-1]
Todd Wang95873902015-05-22 14:21:30 -0700688 writeMsg("---- Cleanup calling Shutdown on program %q\n", h.desc)
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700689 cerr := h.Shutdown(stdout, stderr)
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700690 if cerr != nil {
691 err = cerr
692 }
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700693 fn := func() string {
694 if cerr == nil {
695 return ": done"
696 } else {
697 return ": error: " + err.Error()
698 }
699 }
Todd Wang95873902015-05-22 14:21:30 -0700700 writeMsg("---- Shutdown on program %q%s\n", h.desc, fn())
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700701 }
Ankur9f957942014-11-24 16:34:18 -0800702
Ryan Browna08a2212015-01-15 15:40:10 -0800703 if sh.cancelCtx != nil {
Cosmos Nicolaou9fb10342015-04-12 19:37:24 -0700704 writeMsg("---- Cleanup calling cancelCtx ----\n")
Ryan Browna08a2212015-01-15 15:40:10 -0800705 // Note(ribrdb, caprita): This will shutdown the agents. If there
706 // were errors shutting down it is possible there could be child
707 // processes still running, and stopping the agent may cause
708 // additional failures.
709 sh.cancelCtx()
Cosmos Nicolaou9afe1202014-09-19 13:45:57 -0700710 }
Ryan Browna08a2212015-01-15 15:40:10 -0800711 os.RemoveAll(sh.tempCredDir)
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700712 return err
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700713}
714
Todd Wang95873902015-05-22 14:21:30 -0700715func (sh *Shell) setupProgramEnv(env []string) []string {
Todd Wang3bb46f52015-05-14 22:01:18 -0700716 osmap := envvar.SliceToMap(os.Environ())
717 evmap := envvar.SliceToMap(env)
Ankur9f957942014-11-24 16:34:18 -0800718
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700719 sh.mu.Lock()
Cosmos Nicolaou2cb05fb2014-10-23 13:37:32 -0700720 defer sh.mu.Unlock()
Todd Wang3bb46f52015-05-14 22:01:18 -0700721 m1 := envvar.MergeMaps(osmap, sh.env)
Ankur9f957942014-11-24 16:34:18 -0800722 // Clear any VeyronCredentials directory in m1 as we never
723 // want the child to directly use the directory specified
724 // by the shell's VeyronCredentials.
Todd Wang8123b5e2015-05-14 18:44:43 -0700725 delete(m1, ref.EnvCredentials)
726 delete(m1, ref.EnvAgentEndpoint)
Ryan Brown04384432015-08-27 16:08:32 -0700727 delete(m1, ref.EnvAgentPath)
Ankur9f957942014-11-24 16:34:18 -0800728
Todd Wang3bb46f52015-05-14 22:01:18 -0700729 m2 := envvar.MergeMaps(m1, evmap)
730 return envvar.MapToSlice(m2)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700731}
732
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -0700733// ExpectSession is a subset of v.io/x/ref/tests/expect.Session's methods
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700734// that are embedded in Handle.
735type ExpectSession interface {
736 Expect(expected string)
737 ExpectEOF() error
738 ExpectRE(pattern string, n int) [][]string
739 ExpectSetEventuallyRE(expected ...string) [][]string
740 ExpectSetRE(expected ...string) [][]string
741 ExpectVar(name string) string
742 Expectf(format string, args ...interface{})
743 ReadAll() (string, error)
744 ReadLine() string
745 SetVerbosity(bool)
746 Failed() bool
747 Error() error
748}
749
Todd Wang95873902015-05-22 14:21:30 -0700750// Handle represents a running program.
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700751type Handle interface {
Cosmos Nicolaou42a17362015-03-10 16:40:18 -0700752 ExpectSession
753
Todd Wang95873902015-05-22 14:21:30 -0700754 // Stdout returns a reader to the running program's stdout stream.
Cosmos Nicolaou9ca249d2014-09-18 15:07:12 -0700755 Stdout() io.Reader
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700756
Todd Wang95873902015-05-22 14:21:30 -0700757 // Stderr returns a reader to the running program's stderr
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700758 // stream.
759 Stderr() io.Reader
760
Todd Wang95873902015-05-22 14:21:30 -0700761 // Stdin returns a writer to the running program's stdin. The
762 // convention is for programs to wait for stdin to be closed before
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700763 // they exit, thus the caller should close stdin when it wants the
Todd Wang95873902015-05-22 14:21:30 -0700764 // program to exit cleanly.
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700765 Stdin() io.Writer
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700766
Cosmos Nicolaou66afced2014-09-15 22:12:43 -0700767 // CloseStdin closes stdin in a manner that avoids a data race
768 // between any current readers on it.
769 CloseStdin()
770
Todd Wang95873902015-05-22 14:21:30 -0700771 // Shutdown closes the Stdin for the program and then reads output
772 // from the program's stdout until it encounters EOF, waits for
773 // the program to complete and then reads all of its stderr output.
Cosmos Nicolaoubbae3882014-10-02 22:58:19 -0700774 // The stdout and stderr contents are written to the corresponding
775 // io.Writers if they are non-nil, otherwise the content is discarded.
776 Shutdown(stdout, stderr io.Writer) error
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700777
Todd Wang95873902015-05-22 14:21:30 -0700778 // Pid returns the pid of the process running the program
Cosmos Nicolaoucc581722014-10-07 12:45:39 -0700779 Pid() int
Cosmos Nicolaou62613842014-08-25 21:57:37 -0700780}