1
2
3 package main
4
5 import (
6 gcontext "context"
7 "encoding/json"
8 "fmt"
9 "io"
10 "net"
11 "os"
12 "time"
13
14 winio "github.com/Microsoft/go-winio"
15 "github.com/Microsoft/hcsshim/internal/appargs"
16 cmdpkg "github.com/Microsoft/hcsshim/internal/cmd"
17 "github.com/Microsoft/hcsshim/internal/hcs"
18 "github.com/Microsoft/hcsshim/internal/runhcs"
19 specs "github.com/opencontainers/runtime-spec/specs-go"
20 "github.com/sirupsen/logrus"
21 "github.com/urfave/cli"
22 )
23
24 func newFile(context *cli.Context, param string) *os.File {
25 fd := uintptr(context.Int(param))
26 if fd == 0 {
27 return nil
28 }
29 return os.NewFile(fd, "")
30 }
31
32 var shimCommand = cli.Command{
33 Name: "shim",
34 Usage: `launch the process and proxy stdio (do not call it outside of runhcs)`,
35 Hidden: true,
36 Flags: []cli.Flag{
37 &cli.IntFlag{Name: "stdin", Hidden: true},
38 &cli.IntFlag{Name: "stdout", Hidden: true},
39 &cli.IntFlag{Name: "stderr", Hidden: true},
40 &cli.BoolFlag{Name: "exec", Hidden: true},
41 cli.StringFlag{Name: "log-pipe", Hidden: true},
42 },
43 Before: appargs.Validate(argID),
44 Action: func(context *cli.Context) error {
45 logPipe := context.String("log-pipe")
46 if logPipe != "" {
47 lpc, err := winio.DialPipe(logPipe, nil)
48 if err != nil {
49 return err
50 }
51 defer lpc.Close()
52 logrus.SetOutput(lpc)
53 } else {
54 logrus.SetOutput(os.Stderr)
55 }
56 fatalWriter.Writer = os.Stdout
57
58 id := context.Args().First()
59 c, err := getContainer(id, true)
60 if err != nil {
61 return err
62 }
63 defer c.Close()
64
65
66 containerExitCh := make(chan struct{})
67 var containerExitErr error
68 go func() {
69 containerExitErr = c.hc.Wait()
70 close(containerExitCh)
71 }()
72
73
74 stdin := newFile(context, "stdin")
75 stdout := newFile(context, "stdout")
76 stderr := newFile(context, "stderr")
77
78 exec := context.Bool("exec")
79 terminateOnFailure := false
80
81 errorOut := io.WriteCloser(os.Stdout)
82
83 var spec *specs.Process
84
85 if exec {
86
87 specj, err := io.ReadAll(os.Stdin)
88 if err != nil {
89 return err
90 }
91 os.Stdin.Close()
92
93 spec = new(specs.Process)
94 err = json.Unmarshal(specj, spec)
95 if err != nil {
96 return err
97 }
98
99 } else {
100
101 os.Stdin.Close()
102
103
104 l, err := winio.ListenPipe(c.ShimPipePath(), nil)
105 if err != nil {
106 return err
107 }
108
109
110
111 _, _ = errorOut.Write(runhcs.ShimSuccess)
112 errorOut.Close()
113 fatalWriter.Writer = io.Discard
114
115
116 defer func() {
117 _ = stateKey.Set(id, keyShimPid, 0)
118 }()
119
120 defer func() {
121 if terminateOnFailure {
122 _ = c.hc.Terminate(gcontext.Background())
123 <-containerExitCh
124 }
125 }()
126 terminateOnFailure = true
127
128
129
130 var pipe net.Conn
131 pipeCh := make(chan error)
132 go func() {
133 var err error
134 pipe, err = l.Accept()
135 pipeCh <- err
136 }()
137
138 select {
139 case err = <-pipeCh:
140 if err != nil {
141 return err
142 }
143 case <-containerExitCh:
144 err = containerExitErr
145 if err != nil {
146 return err
147 }
148 return cli.NewExitError("", 1)
149 }
150
151
152 errorOut = pipe
153 fatalWriter.Writer = pipe
154
155
156 spec = c.Spec.Process
157 }
158
159
160 cmd := &cmdpkg.Cmd{
161 Host: c.hc,
162 Stdin: stdin,
163 Stdout: stdout,
164 Stderr: stderr,
165 }
166 if c.Spec.Linux == nil || exec {
167 cmd.Spec = spec
168 }
169
170 err = cmd.Start()
171 if err != nil {
172 return err
173 }
174 pid := cmd.Process.Pid()
175 if !exec {
176 err = stateKey.Set(c.ID, keyInitPid, pid)
177 if err != nil {
178 stdin.Close()
179 _, _ = cmd.Process.Kill(gcontext.Background())
180 _ = cmd.Wait()
181 return err
182 }
183 }
184
185
186 err = stateKey.Set(c.ID, fmt.Sprintf(keyPidMapFmt, os.Getpid()), pid)
187
188 if err != nil {
189 stdin.Close()
190 _, _ = cmd.Process.Kill(gcontext.Background())
191 _ = cmd.Wait()
192 return err
193 }
194 defer func() {
195
196 _ = stateKey.Clear(c.ID, fmt.Sprintf(keyPidMapFmt, os.Getpid()))
197 }()
198
199 terminateOnFailure = false
200
201
202
203 _, _ = errorOut.Write(runhcs.ShimSuccess)
204 errorOut.Close()
205 fatalWriter.Writer = io.Discard
206
207 _ = cmd.Wait()
208 code := cmd.ExitState.ExitCode()
209 if !exec {
210
211
212 const shutdownTimeout = time.Minute * 5
213 err := c.hc.Shutdown(gcontext.Background())
214 if err != nil {
215 select {
216 case <-containerExitCh:
217 err = containerExitErr
218 case <-time.After(shutdownTimeout):
219 err = hcs.ErrTimeout
220 }
221 }
222
223 if err != nil {
224 _ = c.hc.Terminate(gcontext.Background())
225 }
226 <-containerExitCh
227 }
228
229 return cli.NewExitError("", code)
230 },
231 }
232
View as plain text