1
2
3 package main
4
5 import (
6 "context"
7 "encoding/json"
8 "fmt"
9 "os"
10 "path/filepath"
11 "strings"
12
13 runhcsopts "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
14 "github.com/Microsoft/hcsshim/internal/extendedtask"
15 "github.com/Microsoft/hcsshim/internal/oci"
16 "github.com/Microsoft/hcsshim/internal/shimdiag"
17 containerd_v1_types "github.com/containerd/containerd/api/types/task"
18 "github.com/containerd/containerd/errdefs"
19 "github.com/containerd/containerd/mount"
20 "github.com/containerd/containerd/runtime/v2/task"
21 "github.com/containerd/typeurl"
22 google_protobuf1 "github.com/gogo/protobuf/types"
23 "github.com/opencontainers/runtime-spec/specs-go"
24 "github.com/pkg/errors"
25 )
26
27 var empty = &google_protobuf1.Empty{}
28
29
30
31
32
33
34 func (s *service) getPod() (shimPod, error) {
35 raw := s.taskOrPod.Load()
36 if raw == nil {
37 return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "task with id: '%s' must be created first", s.tid)
38 }
39 return raw.(shimPod), nil
40 }
41
42
43
44
45
46 func (s *service) getTask(tid string) (shimTask, error) {
47 raw := s.taskOrPod.Load()
48 if raw == nil {
49 return nil, errors.Wrapf(errdefs.ErrNotFound, "task with id: '%s' not found", tid)
50 }
51 if s.isSandbox {
52 p := raw.(shimPod)
53 return p.GetTask(tid)
54 }
55
56 if s.tid != tid {
57 return nil, errors.Wrapf(errdefs.ErrNotFound, "task with id: '%s' not found", tid)
58 }
59 return raw.(shimTask), nil
60 }
61
62 func (s *service) stateInternal(ctx context.Context, req *task.StateRequest) (*task.StateResponse, error) {
63 t, err := s.getTask(req.ID)
64 if err != nil {
65 return nil, err
66 }
67 e, err := t.GetExec(req.ExecID)
68 if err != nil {
69 return nil, err
70 }
71 return e.Status(), nil
72 }
73
74 func (s *service) createInternal(ctx context.Context, req *task.CreateTaskRequest) (*task.CreateTaskResponse, error) {
75 setupDebuggerEvent()
76
77 shimOpts := &runhcsopts.Options{}
78 if req.Options != nil {
79 v, err := typeurl.UnmarshalAny(req.Options)
80 if err != nil {
81 return nil, err
82 }
83 shimOpts = v.(*runhcsopts.Options)
84 }
85
86 var spec specs.Spec
87 f, err := os.Open(filepath.Join(req.Bundle, "config.json"))
88 if err != nil {
89 return nil, err
90 }
91 if err := json.NewDecoder(f).Decode(&spec); err != nil {
92 f.Close()
93 return nil, err
94 }
95 f.Close()
96
97 spec = oci.UpdateSpecFromOptions(spec, shimOpts)
98
99 err = oci.ProcessAnnotations(ctx, &spec)
100
101
102 if err != nil {
103 return nil, errors.Wrap(err, "unable to process OCI Spec annotations")
104 }
105
106
107
108
109 if shimOpts.SandboxIsolation == runhcsopts.Options_HYPERVISOR {
110 if spec.Windows == nil {
111 spec.Windows = &specs.Windows{}
112 }
113 if spec.Windows.HyperV == nil {
114 spec.Windows.HyperV = &specs.WindowsHyperV{}
115 }
116 }
117
118 if len(req.Rootfs) == 0 {
119
120
121
122 if spec.Windows == nil || len(spec.Windows.LayerFolders) < 2 {
123 return nil, errors.Wrap(errdefs.ErrFailedPrecondition, "no Windows.LayerFolders found in oci spec")
124 }
125 } else if len(req.Rootfs) != 1 {
126 return nil, errors.Wrap(errdefs.ErrFailedPrecondition, "Rootfs does not contain exactly 1 mount for the root file system")
127 } else {
128 m := req.Rootfs[0]
129 if m.Type != "windows-layer" && m.Type != "lcow-layer" {
130 return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "unsupported mount type '%s'", m.Type)
131 }
132
133
134
135
136
137 var parentLayerPaths []string
138 for _, option := range m.Options {
139 if strings.HasPrefix(option, mount.ParentLayerPathsFlag) {
140 err := json.Unmarshal([]byte(option[len(mount.ParentLayerPathsFlag):]), &parentLayerPaths)
141 if err != nil {
142 return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "failed to unmarshal parent layer paths from mount: %v", err)
143 }
144 }
145 }
146
147
148 if spec.Windows.HyperV == nil {
149 if spec.Root == nil {
150 spec.Root = &specs.Root{}
151 }
152 }
153
154
155 spec.Windows.LayerFolders = append(spec.Windows.LayerFolders, parentLayerPaths...)
156
157 spec.Windows.LayerFolders = append(spec.Windows.LayerFolders, m.Source)
158 }
159
160 if req.Terminal && req.Stderr != "" {
161 return nil, errors.Wrap(errdefs.ErrFailedPrecondition, "if using terminal, stderr must be empty")
162 }
163
164 resp := &task.CreateTaskResponse{}
165 s.cl.Lock()
166 if s.isSandbox {
167 pod, err := s.getPod()
168 if err == nil {
169
170 s.cl.Unlock()
171 t, err := pod.CreateTask(ctx, req, &spec)
172 if err != nil {
173 return nil, err
174 }
175 e, _ := t.GetExec("")
176 resp.Pid = uint32(e.Pid())
177 return resp, nil
178 }
179 pod, err = createPod(ctx, s.events, req, &spec)
180 if err != nil {
181 s.cl.Unlock()
182 return nil, err
183 }
184 t, _ := pod.GetTask(req.ID)
185 e, _ := t.GetExec("")
186 resp.Pid = uint32(e.Pid())
187 s.taskOrPod.Store(pod)
188 } else {
189 t, err := newHcsStandaloneTask(ctx, s.events, req, &spec)
190 if err != nil {
191 s.cl.Unlock()
192 return nil, err
193 }
194 e, _ := t.GetExec("")
195 resp.Pid = uint32(e.Pid())
196 s.taskOrPod.Store(t)
197 }
198 s.cl.Unlock()
199 return resp, nil
200 }
201
202 func (s *service) startInternal(ctx context.Context, req *task.StartRequest) (*task.StartResponse, error) {
203 t, err := s.getTask(req.ID)
204 if err != nil {
205 return nil, err
206 }
207 e, err := t.GetExec(req.ExecID)
208 if err != nil {
209 return nil, err
210 }
211 err = e.Start(ctx)
212 if err != nil {
213 return nil, err
214 }
215 return &task.StartResponse{
216 Pid: uint32(e.Pid()),
217 }, nil
218 }
219
220 func (s *service) deleteInternal(ctx context.Context, req *task.DeleteRequest) (*task.DeleteResponse, error) {
221 t, err := s.getTask(req.ID)
222 if err != nil {
223 return nil, err
224 }
225
226 pid, exitStatus, exitedAt, err := t.DeleteExec(ctx, req.ExecID)
227 if err != nil {
228 return nil, err
229 }
230
231
232 if s.isSandbox && req.ExecID == "" {
233 p, err := s.getPod()
234 if err != nil {
235 return nil, errors.Wrapf(err, "could not get pod %q to delete task %q", s.tid, req.ID)
236 }
237 err = p.DeleteTask(ctx, req.ID)
238 if err != nil {
239 return nil, fmt.Errorf("could not delete task %q in pod %q: %w", req.ID, s.tid, err)
240 }
241 }
242
243
244 return &task.DeleteResponse{
245 Pid: uint32(pid),
246 ExitStatus: exitStatus,
247 ExitedAt: exitedAt,
248 }, nil
249 }
250
251 func (s *service) pidsInternal(ctx context.Context, req *task.PidsRequest) (*task.PidsResponse, error) {
252 t, err := s.getTask(req.ID)
253 if err != nil {
254 return nil, err
255 }
256 pids, err := t.Pids(ctx)
257 if err != nil {
258 return nil, err
259 }
260 processes := make([]*containerd_v1_types.ProcessInfo, len(pids))
261 for i, p := range pids {
262 a, err := typeurl.MarshalAny(&p)
263 if err != nil {
264 return nil, errors.Wrapf(err, "failed to marshal ProcessDetails for process: %s, task: %s", p.ExecID, req.ID)
265 }
266 proc := &containerd_v1_types.ProcessInfo{
267 Pid: p.ProcessID,
268 Info: a,
269 }
270 processes[i] = proc
271 }
272 return &task.PidsResponse{
273 Processes: processes,
274 }, nil
275 }
276
277 func (s *service) pauseInternal(ctx context.Context, req *task.PauseRequest) (*google_protobuf1.Empty, error) {
278
286 return nil, errdefs.ErrNotImplemented
287 }
288
289 func (s *service) resumeInternal(ctx context.Context, req *task.ResumeRequest) (*google_protobuf1.Empty, error) {
290
298 return nil, errdefs.ErrNotImplemented
299 }
300
301 func (s *service) checkpointInternal(ctx context.Context, req *task.CheckpointTaskRequest) (*google_protobuf1.Empty, error) {
302 return nil, errdefs.ErrNotImplemented
303 }
304
305 func (s *service) killInternal(ctx context.Context, req *task.KillRequest) (*google_protobuf1.Empty, error) {
306 if s.isSandbox {
307 pod, err := s.getPod()
308 if err != nil {
309 return nil, errors.Wrapf(errdefs.ErrNotFound, "%v: task with id: '%s' not found", err, req.ID)
310 }
311
312 err = pod.KillTask(ctx, req.ID, req.ExecID, req.Signal, req.All)
313 if err != nil {
314 return nil, err
315 }
316 return empty, nil
317 }
318 t, err := s.getTask(req.ID)
319 if err != nil {
320 return nil, err
321 }
322
323 err = t.KillExec(ctx, req.ExecID, req.Signal, req.All)
324 if err != nil {
325 return nil, err
326 }
327 return empty, nil
328 }
329
330 func (s *service) execInternal(ctx context.Context, req *task.ExecProcessRequest) (*google_protobuf1.Empty, error) {
331 t, err := s.getTask(req.ID)
332 if err != nil {
333 return nil, err
334 }
335 if req.Terminal && req.Stderr != "" {
336 return nil, errors.Wrap(errdefs.ErrFailedPrecondition, "if using terminal, stderr must be empty")
337 }
338 var spec specs.Process
339 if err := json.Unmarshal(req.Spec.Value, &spec); err != nil {
340 return nil, errors.Wrap(err, "request.Spec was not oci process")
341 }
342 err = t.CreateExec(ctx, req, &spec)
343 if err != nil {
344 return nil, err
345 }
346 return empty, nil
347 }
348
349 func (s *service) diagExecInHostInternal(ctx context.Context, req *shimdiag.ExecProcessRequest) (*shimdiag.ExecProcessResponse, error) {
350 if req.Terminal && req.Stderr != "" {
351 return nil, errors.Wrap(errdefs.ErrFailedPrecondition, "if using terminal, stderr must be empty")
352 }
353 t, err := s.getTask(s.tid)
354 if err != nil {
355 return nil, err
356 }
357 ec, err := t.ExecInHost(ctx, req)
358 if err != nil {
359 return nil, err
360 }
361 return &shimdiag.ExecProcessResponse{ExitCode: int32(ec)}, nil
362 }
363
364 func (s *service) diagShareInternal(ctx context.Context, req *shimdiag.ShareRequest) (*shimdiag.ShareResponse, error) {
365 t, err := s.getTask(s.tid)
366 if err != nil {
367 return nil, err
368 }
369 if err := t.Share(ctx, req); err != nil {
370 return nil, err
371 }
372 return &shimdiag.ShareResponse{}, nil
373 }
374
375 func (s *service) diagListExecs(task shimTask) ([]*shimdiag.Exec, error) {
376 var sdExecs []*shimdiag.Exec
377 execs, err := task.ListExecs()
378 if err != nil {
379 return nil, err
380 }
381 for _, exec := range execs {
382 sdExecs = append(sdExecs, &shimdiag.Exec{ID: exec.ID(), State: string(exec.State())})
383 }
384 return sdExecs, nil
385 }
386
387 func (s *service) diagTasksInternal(ctx context.Context, req *shimdiag.TasksRequest) (_ *shimdiag.TasksResponse, err error) {
388 raw := s.taskOrPod.Load()
389 if raw == nil {
390 return nil, errors.Wrapf(errdefs.ErrNotFound, "task with id: '%s' not found", s.tid)
391 }
392
393 resp := &shimdiag.TasksResponse{}
394 if s.isSandbox {
395 p, ok := raw.(shimPod)
396 if !ok {
397 return nil, errors.New("failed to convert task to pod")
398 }
399
400 tasks, err := p.ListTasks()
401 if err != nil {
402 return nil, err
403 }
404
405 for _, task := range tasks {
406 t := &shimdiag.Task{ID: task.ID()}
407 if req.Execs {
408 t.Execs, err = s.diagListExecs(task)
409 if err != nil {
410 return nil, err
411 }
412 }
413 resp.Tasks = append(resp.Tasks, t)
414 }
415 return resp, nil
416 }
417
418 t, ok := raw.(shimTask)
419 if !ok {
420 return nil, errors.New("failed to convert task to 'shimTask'")
421 }
422
423 task := &shimdiag.Task{ID: t.ID()}
424 if req.Execs {
425 task.Execs, err = s.diagListExecs(t)
426 if err != nil {
427 return nil, err
428 }
429 }
430
431 resp.Tasks = []*shimdiag.Task{task}
432 return resp, nil
433 }
434
435 func (s *service) resizePtyInternal(ctx context.Context, req *task.ResizePtyRequest) (*google_protobuf1.Empty, error) {
436 t, err := s.getTask(req.ID)
437 if err != nil {
438 return nil, err
439 }
440 e, err := t.GetExec(req.ExecID)
441 if err != nil {
442 return nil, err
443 }
444 err = e.ResizePty(ctx, req.Width, req.Height)
445 if err != nil {
446 return nil, err
447 }
448 return empty, nil
449 }
450
451 func (s *service) closeIOInternal(ctx context.Context, req *task.CloseIORequest) (*google_protobuf1.Empty, error) {
452 t, err := s.getTask(req.ID)
453 if err != nil {
454 return nil, err
455 }
456 e, err := t.GetExec(req.ExecID)
457 if err != nil {
458 return nil, err
459 }
460 err = e.CloseIO(ctx, req.Stdin)
461 if err != nil {
462 return nil, err
463 }
464 return empty, nil
465 }
466
467 func (s *service) updateInternal(ctx context.Context, req *task.UpdateTaskRequest) (*google_protobuf1.Empty, error) {
468 if req.Resources == nil {
469 return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "resources cannot be empty, updating container %s resources failed", req.ID)
470 }
471 t, err := s.getTask(req.ID)
472 if err != nil {
473 return nil, err
474 }
475 if err := t.Update(ctx, req); err != nil {
476 return nil, err
477 }
478 return empty, nil
479 }
480
481 func (s *service) waitInternal(ctx context.Context, req *task.WaitRequest) (*task.WaitResponse, error) {
482 t, err := s.getTask(req.ID)
483 if err != nil {
484 return nil, err
485 }
486 var state *task.StateResponse
487 if req.ExecID != "" {
488 e, err := t.GetExec(req.ExecID)
489 if err != nil {
490 return nil, err
491 }
492 state = e.Wait()
493 } else {
494 state = t.Wait()
495 }
496 return &task.WaitResponse{
497 ExitStatus: state.ExitStatus,
498 ExitedAt: state.ExitedAt,
499 }, nil
500 }
501
502 func (s *service) statsInternal(ctx context.Context, req *task.StatsRequest) (*task.StatsResponse, error) {
503 t, err := s.getTask(req.ID)
504 if err != nil {
505 return nil, err
506 }
507 stats, err := t.Stats(ctx)
508 if err != nil {
509 return nil, err
510 }
511 any, err := typeurl.MarshalAny(stats)
512 if err != nil {
513 return nil, errors.Wrapf(err, "failed to marshal Statistics for task: %s", req.ID)
514 }
515 return &task.StatsResponse{Stats: any}, nil
516 }
517
518 func (s *service) connectInternal(ctx context.Context, req *task.ConnectRequest) (*task.ConnectResponse, error) {
519
520 pid := uint32(os.Getpid())
521 return &task.ConnectResponse{
522 ShimPid: pid,
523 TaskPid: pid,
524 }, nil
525 }
526
527 func (s *service) shutdownInternal(ctx context.Context, req *task.ShutdownRequest) (*google_protobuf1.Empty, error) {
528
529
530 if req.ID != s.tid {
531 return empty, nil
532 }
533
534 s.shutdownOnce.Do(func() {
535
536
537 s.gracefulShutdown = !req.Now
538 close(s.shutdown)
539 })
540
541 return empty, nil
542 }
543
544 func (s *service) computeProcessorInfoInternal(ctx context.Context, req *extendedtask.ComputeProcessorInfoRequest) (*extendedtask.ComputeProcessorInfoResponse, error) {
545 t, err := s.getTask(req.ID)
546 if err != nil {
547 return nil, err
548 }
549 info, err := t.ProcessorInfo(ctx)
550 if err != nil {
551 return nil, err
552 }
553 return &extendedtask.ComputeProcessorInfoResponse{
554 Count: info.count,
555 }, nil
556 }
557
View as plain text