1
2
3
4 package bridge
5
6 import (
7 "context"
8 "encoding/json"
9 "syscall"
10 "time"
11
12 "github.com/pkg/errors"
13 "go.opencensus.io/trace"
14 "golang.org/x/sys/unix"
15
16 "github.com/Microsoft/hcsshim/internal/guest/commonutils"
17 "github.com/Microsoft/hcsshim/internal/guest/gcserr"
18 "github.com/Microsoft/hcsshim/internal/guest/prot"
19 "github.com/Microsoft/hcsshim/internal/guest/runtime/hcsv2"
20 "github.com/Microsoft/hcsshim/internal/guest/stdio"
21 "github.com/Microsoft/hcsshim/internal/log"
22 "github.com/Microsoft/hcsshim/internal/oc"
23 "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
24 )
25
26
27 var capabilities = prot.GcsCapabilities{
28 SendHostCreateMessage: false,
29 SendHostStartMessage: false,
30 HVSocketConfigOnStartup: false,
31 SupportedSchemaVersions: []prot.SchemaVersion{
32 {
33 Major: 2,
34 Minor: 1,
35 },
36 },
37 RuntimeOsType: prot.OsTypeLinux,
38 GuestDefinedCapabilities: prot.GcsGuestCapabilities{
39 NamespaceAddRequestSupported: true,
40 SignalProcessSupported: true,
41 DumpStacksSupported: true,
42 DeleteContainerStateSupported: true,
43 },
44 }
45
46
47
48 func (b *Bridge) negotiateProtocolV2(r *Request) (_ RequestResponse, err error) {
49 _, span := oc.StartSpan(r.Context, "opengcs::bridge::negotiateProtocolV2")
50 defer span.End()
51 defer func() { oc.SetSpanStatus(span, err) }()
52 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
53
54 var request prot.NegotiateProtocol
55 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
56 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
57 }
58
59 if request.MaximumVersion < uint32(prot.PvV4) || uint32(prot.PvMax) < request.MinimumVersion {
60 return nil, gcserr.NewHresultError(gcserr.HrVmcomputeUnsupportedProtocolVersion)
61 }
62
63 min := func(x, y uint32) uint32 {
64 if x < y {
65 return x
66 }
67 return y
68 }
69
70 major := min(uint32(prot.PvMax), request.MaximumVersion)
71
72
73 b.protVer = prot.ProtocolVersion(major)
74
75 return &prot.NegotiateProtocolResponse{
76 Version: major,
77 Capabilities: capabilities,
78 }, nil
79 }
80
81
82
83
84 func (b *Bridge) createContainerV2(r *Request) (_ RequestResponse, err error) {
85 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::createContainerV2")
86 defer span.End()
87 defer func() { oc.SetSpanStatus(span, err) }()
88 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
89
90 var request prot.ContainerCreate
91 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
92 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
93 }
94
95 var settingsV2 prot.VMHostedContainerSettingsV2
96 if err := commonutils.UnmarshalJSONWithHresult([]byte(request.ContainerConfig), &settingsV2); err != nil {
97 return nil, errors.Wrapf(err, "failed to unmarshal JSON for ContainerConfig \"%s\"", request.ContainerConfig)
98 }
99
100 if settingsV2.SchemaVersion.Cmp(prot.SchemaVersion{Major: 2, Minor: 1}) < 0 {
101 return nil, gcserr.WrapHresult(
102 errors.Errorf("invalid schema version: %v", settingsV2.SchemaVersion),
103 gcserr.HrVmcomputeInvalidJSON)
104 }
105
106 c, err := b.hostState.CreateContainer(ctx, request.ContainerID, &settingsV2)
107 if err != nil {
108 return nil, err
109 }
110 waitFn := func() prot.NotificationType {
111 return c.Wait()
112 }
113
114 go func() {
115 nt := waitFn()
116 notification := &prot.ContainerNotification{
117 MessageBase: prot.MessageBase{
118 ContainerID: request.ContainerID,
119 ActivityID: request.ActivityID,
120 },
121 Type: nt,
122 Operation: prot.AoNone,
123 Result: 0,
124 ResultInfo: "",
125 }
126 b.PublishNotification(notification)
127 }()
128
129 return &prot.ContainerCreateResponse{}, nil
130 }
131
132
133
134
135
136
137 func (b *Bridge) startContainerV2(r *Request) (_ RequestResponse, err error) {
138 _, span := oc.StartSpan(r.Context, "opengcs::bridge::startContainerV2")
139 defer span.End()
140 defer func() { oc.SetSpanStatus(span, err) }()
141 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
142
143
144
145 var request prot.MessageBase
146 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
147 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
148 }
149
150 return &prot.MessageResponseBase{}, nil
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 func (b *Bridge) execProcessV2(r *Request) (_ RequestResponse, err error) {
169 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::execProcessV2")
170 defer span.End()
171 defer func() { oc.SetSpanStatus(span, err) }()
172 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
173
174 var request prot.ContainerExecuteProcess
175 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
176 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
177 }
178
179
180
181 var params prot.ProcessParameters
182 if err := commonutils.UnmarshalJSONWithHresult([]byte(request.Settings.ProcessParameters), ¶ms); err != nil {
183 return nil, errors.Wrapf(err, "failed to unmarshal JSON for ProcessParameters \"%s\"", request.Settings.ProcessParameters)
184 }
185
186 var conSettings stdio.ConnectionSettings
187 if params.CreateStdInPipe {
188 conSettings.StdIn = &request.Settings.VsockStdioRelaySettings.StdIn
189 }
190 if params.CreateStdOutPipe {
191 conSettings.StdOut = &request.Settings.VsockStdioRelaySettings.StdOut
192 }
193 if params.CreateStdErrPipe {
194 conSettings.StdErr = &request.Settings.VsockStdioRelaySettings.StdErr
195 }
196
197 pid, err := b.hostState.ExecProcess(ctx, request.ContainerID, params, conSettings)
198
199 if err != nil {
200 return nil, err
201 }
202 log.G(ctx).WithField("pid", pid).Debug("created process pid")
203 return &prot.ContainerExecuteProcessResponse{
204 ProcessID: uint32(pid),
205 }, nil
206 }
207
208
209
210
211
212
213 func (b *Bridge) killContainerV2(r *Request) (RequestResponse, error) {
214 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::killContainerV2")
215 defer span.End()
216 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
217
218 return b.signalContainerShutdownV2(ctx, span, r, false)
219 }
220
221
222
223
224
225
226 func (b *Bridge) shutdownContainerV2(r *Request) (RequestResponse, error) {
227 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::shutdownContainerV2")
228 defer span.End()
229 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
230
231 return b.signalContainerShutdownV2(ctx, span, r, true)
232 }
233
234
235
236
237 func (b *Bridge) signalContainerShutdownV2(ctx context.Context, span *trace.Span, r *Request, graceful bool) (_ RequestResponse, err error) {
238 defer func() { oc.SetSpanStatus(span, err) }()
239 span.AddAttributes(
240 trace.StringAttribute("cid", r.ContainerID),
241 trace.BoolAttribute("graceful", graceful),
242 )
243
244 var request prot.MessageBase
245 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
246 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
247 }
248
249
250 if request.ContainerID == hcsv2.UVMContainerID {
251
252
253 b.quitChan <- true
254 b.hostState.Shutdown()
255 } else {
256 err = b.hostState.ShutdownContainer(ctx, request.ContainerID, graceful)
257 if err != nil {
258 return nil, err
259 }
260 }
261
262 return &prot.MessageResponseBase{}, nil
263 }
264
265 func (b *Bridge) signalProcessV2(r *Request) (_ RequestResponse, err error) {
266 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::signalProcessV2")
267 defer span.End()
268 defer func() { oc.SetSpanStatus(span, err) }()
269 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
270
271 var request prot.ContainerSignalProcess
272 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
273 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
274 }
275
276 span.AddAttributes(
277 trace.Int64Attribute("pid", int64(request.ProcessID)),
278 trace.Int64Attribute("signal", int64(request.Options.Signal)))
279
280 var signal syscall.Signal
281 if request.Options.Signal == 0 {
282 signal = unix.SIGKILL
283 } else {
284 signal = syscall.Signal(request.Options.Signal)
285 }
286
287 if err := b.hostState.SignalContainerProcess(ctx, request.ContainerID, request.ProcessID, signal); err != nil {
288 return nil, err
289 }
290
291 return &prot.MessageResponseBase{}, nil
292 }
293
294 func (b *Bridge) getPropertiesV2(r *Request) (_ RequestResponse, err error) {
295 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::getPropertiesV2")
296 defer span.End()
297 defer func() { oc.SetSpanStatus(span, err) }()
298 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
299
300 var request prot.ContainerGetProperties
301 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
302 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
303 }
304
305 var query prot.PropertyQuery
306 if len(request.Query) != 0 {
307 if err := json.Unmarshal([]byte(request.Query), &query); err != nil {
308 e := gcserr.WrapHresult(err, gcserr.HrVmcomputeInvalidJSON)
309 return nil, errors.Wrapf(e, "The query could not be unmarshaled: '%s'", query)
310 }
311 }
312
313 if request.ContainerID == hcsv2.UVMContainerID {
314 return nil, errors.New("getPropertiesV2 is not supported against the UVM")
315 }
316
317 properties, err := b.hostState.GetProperties(ctx, request.ContainerID, query)
318 if err != nil {
319 return nil, err
320 }
321
322 propertyJSON := []byte("{}")
323 if properties != nil {
324 var err error
325 propertyJSON, err = json.Marshal(properties)
326 if err != nil {
327 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%+v\"", properties)
328 }
329 }
330
331 return &prot.ContainerGetPropertiesResponse{
332 Properties: string(propertyJSON),
333 }, nil
334 }
335
336 func (b *Bridge) waitOnProcessV2(r *Request) (_ RequestResponse, err error) {
337 _, span := oc.StartSpan(r.Context, "opengcs::bridge::waitOnProcessV2")
338 defer span.End()
339 defer func() { oc.SetSpanStatus(span, err) }()
340 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
341
342 var request prot.ContainerWaitForProcess
343 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
344 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
345 }
346
347 span.AddAttributes(
348 trace.Int64Attribute("pid", int64(request.ProcessID)),
349 trace.Int64Attribute("timeout-ms", int64(request.TimeoutInMs)))
350
351 var exitCodeChan <-chan int
352 var doneChan chan<- bool
353
354 if request.ContainerID == hcsv2.UVMContainerID {
355 p, err := b.hostState.GetExternalProcess(int(request.ProcessID))
356 if err != nil {
357 return nil, err
358 }
359 exitCodeChan, doneChan = p.Wait()
360 } else {
361 c, err := b.hostState.GetCreatedContainer(request.ContainerID)
362 if err != nil {
363 return nil, err
364 }
365 p, err := c.GetProcess(request.ProcessID)
366 if err != nil {
367 return nil, err
368 }
369 exitCodeChan, doneChan = p.Wait()
370 }
371
372
373 defer close(doneChan)
374
375 var tc <-chan time.Time
376 if request.TimeoutInMs != prot.InfiniteWaitTimeout {
377 t := time.NewTimer(time.Duration(request.TimeoutInMs) * time.Millisecond)
378 defer t.Stop()
379 tc = t.C
380 }
381 select {
382 case exitCode := <-exitCodeChan:
383 return &prot.ContainerWaitForProcessResponse{
384 ExitCode: uint32(exitCode),
385 }, nil
386 case <-tc:
387 return nil, gcserr.NewHresultError(gcserr.HvVmcomputeTimeout)
388 }
389 }
390
391 func (b *Bridge) resizeConsoleV2(r *Request) (_ RequestResponse, err error) {
392 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::resizeConsoleV2")
393 defer span.End()
394 defer func() { oc.SetSpanStatus(span, err) }()
395 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
396
397 var request prot.ContainerResizeConsole
398 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
399 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
400 }
401
402 span.AddAttributes(
403 trace.Int64Attribute("pid", int64(request.ProcessID)),
404 trace.Int64Attribute("height", int64(request.Height)),
405 trace.Int64Attribute("width", int64(request.Width)))
406
407 c, err := b.hostState.GetCreatedContainer(request.ContainerID)
408 if err != nil {
409 return nil, err
410 }
411
412 p, err := c.GetProcess(request.ProcessID)
413 if err != nil {
414 return nil, err
415 }
416
417 err = p.ResizeConsole(ctx, request.Height, request.Width)
418 if err != nil {
419 return nil, err
420 }
421
422 return &prot.MessageResponseBase{}, nil
423 }
424
425 func (b *Bridge) modifySettingsV2(r *Request) (_ RequestResponse, err error) {
426 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::modifySettingsV2")
427 defer span.End()
428 defer func() { oc.SetSpanStatus(span, err) }()
429 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
430
431 request, err := prot.UnmarshalContainerModifySettings(r.Message)
432 if err != nil {
433 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
434 }
435
436 err = b.hostState.ModifySettings(ctx, request.ContainerID, request.Request.(*guestrequest.ModificationRequest))
437 if err != nil {
438 return nil, err
439 }
440
441 return &prot.MessageResponseBase{}, nil
442 }
443
444 func (b *Bridge) dumpStacksV2(r *Request) (_ RequestResponse, err error) {
445 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::dumpStacksV2")
446 defer span.End()
447 defer func() { oc.SetSpanStatus(span, err) }()
448
449 stacks, err := b.hostState.GetStacks(ctx)
450 if err != nil {
451 return nil, err
452 }
453 return &prot.DumpStacksResponse{
454 GuestStacks: stacks,
455 }, nil
456 }
457
458 func (b *Bridge) deleteContainerStateV2(r *Request) (_ RequestResponse, err error) {
459 ctx, span := oc.StartSpan(r.Context, "opengcs::bridge::deleteContainerStateV2")
460 defer span.End()
461 defer func() { oc.SetSpanStatus(span, err) }()
462
463 span.AddAttributes(trace.StringAttribute("cid", r.ContainerID))
464
465 var request prot.MessageBase
466 if err := commonutils.UnmarshalJSONWithHresult(r.Message, &request); err != nil {
467 return nil, errors.Wrapf(err, "failed to unmarshal JSON in message \"%s\"", r.Message)
468 }
469
470 c, err := b.hostState.GetCreatedContainer(request.ContainerID)
471 if err != nil {
472 return nil, err
473 }
474
475 defer b.hostState.RemoveContainer(request.ContainerID)
476
477 if err := c.Delete(ctx); err != nil {
478 return nil, err
479 }
480
481 return &prot.MessageResponseBase{}, nil
482 }
483
View as plain text