...
1
2
3 package runhcs
4
5 import (
6 "bytes"
7 "context"
8 "errors"
9 "fmt"
10 "os"
11 "os/exec"
12 "path/filepath"
13 "strings"
14 "sync"
15 "sync/atomic"
16
17 irunhcs "github.com/Microsoft/hcsshim/internal/runhcs"
18 "github.com/containerd/go-runc"
19 )
20
21
22 type Format string
23
24 const (
25 none Format = ""
26
27 Text Format = "text"
28
29 JSON Format = "json"
30 )
31
32 var runhcsPath atomic.Value
33
34 func getCommandPath() string {
35 const command = "runhcs.exe"
36
37 pathi := runhcsPath.Load()
38 if pathi == nil {
39 path, err := exec.LookPath(command)
40 if err != nil {
41 if errors.Is(err, exec.ErrDot) {
42 err = nil
43 }
44 }
45 if err != nil {
46
47
48
49
50
51 if self, serr := os.Executable(); serr == nil {
52 testPath := filepath.Join(filepath.Dir(self), command)
53 if _, serr := os.Stat(testPath); serr == nil {
54 path = testPath
55 }
56 }
57 if path == "" {
58
59
60 path = command
61 }
62 runhcsPath.Store(path)
63 return path
64 }
65 apath, err := filepath.Abs(path)
66 if err != nil {
67
68
69 apath = path
70 }
71 runhcsPath.Store(apath)
72 return apath
73 }
74 return pathi.(string)
75 }
76
77 var bytesBufferPool = sync.Pool{
78 New: func() interface{} {
79 return bytes.NewBuffer(nil)
80 },
81 }
82
83 func getBuf() *bytes.Buffer {
84 return bytesBufferPool.Get().(*bytes.Buffer)
85 }
86
87 func putBuf(b *bytes.Buffer) {
88 b.Reset()
89 bytesBufferPool.Put(b)
90 }
91
92
93 type Runhcs struct {
94
95 Debug bool
96
97 Log string
98
99 LogFormat Format
100
101 Owner string
102
103 Root string
104 }
105
106 func (r *Runhcs) args() []string {
107 var out []string
108 if r.Debug {
109 out = append(out, "--debug")
110 }
111 if r.Log != "" {
112 if strings.HasPrefix(r.Log, irunhcs.SafePipePrefix) {
113 out = append(out, "--log", r.Log)
114 } else {
115 abs, err := filepath.Abs(r.Log)
116 if err == nil {
117 out = append(out, "--log", abs)
118 }
119 }
120 }
121 if r.LogFormat != none {
122 out = append(out, "--log-format", string(r.LogFormat))
123 }
124 if r.Owner != "" {
125 out = append(out, "--owner", r.Owner)
126 }
127 if r.Root != "" {
128 out = append(out, "--root", r.Root)
129 }
130 return out
131 }
132
133 func (r *Runhcs) command(context context.Context, args ...string) *exec.Cmd {
134 cmd := exec.CommandContext(context, getCommandPath(), append(r.args(), args...)...)
135 cmd.Env = os.Environ()
136 return cmd
137 }
138
139
140
141
142
143 func (r *Runhcs) runOrError(cmd *exec.Cmd) error {
144 if cmd.Stdout != nil || cmd.Stderr != nil {
145 ec, err := runc.Monitor.Start(cmd)
146 if err != nil {
147 return err
148 }
149 status, err := runc.Monitor.Wait(cmd, ec)
150 if err == nil && status != 0 {
151 err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0])
152 }
153 return err
154 }
155 data, err := cmdOutput(cmd, true)
156 if err != nil {
157 return fmt.Errorf("%s: %s", err, data)
158 }
159 return nil
160 }
161
162 func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, error) {
163 b := getBuf()
164 defer putBuf(b)
165
166 cmd.Stdout = b
167 if combined {
168 cmd.Stderr = b
169 }
170 ec, err := runc.Monitor.Start(cmd)
171 if err != nil {
172 return nil, err
173 }
174
175 status, err := runc.Monitor.Wait(cmd, ec)
176 if err == nil && status != 0 {
177 err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0])
178 }
179
180 return b.Bytes(), err
181 }
182
View as plain text