1package hcsshim 2 3import ( 4 "context" 5 "io" 6 "sync" 7 "time" 8 9 "github.com/Microsoft/hcsshim/internal/hcs" 10) 11 12// ContainerError is an error encountered in HCS 13type process struct { 14 p *hcs.Process 15 waitOnce sync.Once 16 waitCh chan struct{} 17 waitErr error 18} 19 20// Pid returns the process ID of the process within the container. 21func (process *process) Pid() int { 22 return process.p.Pid() 23} 24 25// Kill signals the process to terminate but does not wait for it to finish terminating. 26func (process *process) Kill() error { 27 found, err := process.p.Kill(context.Background()) 28 if err != nil { 29 return convertProcessError(err, process) 30 } 31 if !found { 32 return &ProcessError{Process: process, Err: ErrElementNotFound, Operation: "hcsshim::Process::Kill"} 33 } 34 return nil 35} 36 37// Wait waits for the process to exit. 38func (process *process) Wait() error { 39 return convertProcessError(process.p.Wait(), process) 40} 41 42// WaitTimeout waits for the process to exit or the duration to elapse. It returns 43// false if timeout occurs. 44func (process *process) WaitTimeout(timeout time.Duration) error { 45 process.waitOnce.Do(func() { 46 process.waitCh = make(chan struct{}) 47 go func() { 48 process.waitErr = process.Wait() 49 close(process.waitCh) 50 }() 51 }) 52 t := time.NewTimer(timeout) 53 defer t.Stop() 54 select { 55 case <-t.C: 56 return &ProcessError{Process: process, Err: ErrTimeout, Operation: "hcsshim::Process::Wait"} 57 case <-process.waitCh: 58 return process.waitErr 59 } 60} 61 62// ExitCode returns the exit code of the process. The process must have 63// already terminated. 64func (process *process) ExitCode() (int, error) { 65 code, err := process.p.ExitCode() 66 if err != nil { 67 err = convertProcessError(err, process) 68 } 69 return code, err 70} 71 72// ResizeConsole resizes the console of the process. 73func (process *process) ResizeConsole(width, height uint16) error { 74 return convertProcessError(process.p.ResizeConsole(context.Background(), width, height), process) 75} 76 77// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing 78// these pipes does not close the underlying pipes; it should be possible to 79// call this multiple times to get multiple interfaces. 80func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { 81 stdin, stdout, stderr, err := process.p.StdioLegacy() 82 if err != nil { 83 err = convertProcessError(err, process) 84 } 85 return stdin, stdout, stderr, err 86} 87 88// CloseStdin closes the write side of the stdin pipe so that the process is 89// notified on the read side that there is no more data in stdin. 90func (process *process) CloseStdin() error { 91 return convertProcessError(process.p.CloseStdin(context.Background()), process) 92} 93 94// Close cleans up any state associated with the process but does not kill 95// or wait on it. 96func (process *process) Close() error { 97 return convertProcessError(process.p.Close(), process) 98} 99