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