1/*
2   Copyright The containerd Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17package v2
18
19import (
20	"context"
21
22	tasktypes "github.com/containerd/containerd/api/types/task"
23	"github.com/containerd/containerd/errdefs"
24	"github.com/containerd/containerd/runtime"
25	"github.com/containerd/containerd/runtime/v2/task"
26	"github.com/containerd/ttrpc"
27	"github.com/pkg/errors"
28)
29
30type process struct {
31	id   string
32	shim *shim
33}
34
35func (p *process) ID() string {
36	return p.id
37}
38
39func (p *process) Kill(ctx context.Context, signal uint32, _ bool) error {
40	_, err := p.shim.task.Kill(ctx, &task.KillRequest{
41		Signal: signal,
42		ID:     p.shim.ID(),
43		ExecID: p.id,
44	})
45	if err != nil {
46		return errdefs.FromGRPC(err)
47	}
48	return nil
49}
50
51func (p *process) State(ctx context.Context) (runtime.State, error) {
52	response, err := p.shim.task.State(ctx, &task.StateRequest{
53		ID:     p.shim.ID(),
54		ExecID: p.id,
55	})
56	if err != nil {
57		if !errors.Is(err, ttrpc.ErrClosed) {
58			return runtime.State{}, errdefs.FromGRPC(err)
59		}
60		return runtime.State{}, errdefs.ErrNotFound
61	}
62	var status runtime.Status
63	switch response.Status {
64	case tasktypes.StatusCreated:
65		status = runtime.CreatedStatus
66	case tasktypes.StatusRunning:
67		status = runtime.RunningStatus
68	case tasktypes.StatusStopped:
69		status = runtime.StoppedStatus
70	case tasktypes.StatusPaused:
71		status = runtime.PausedStatus
72	case tasktypes.StatusPausing:
73		status = runtime.PausingStatus
74	}
75	return runtime.State{
76		Pid:        response.Pid,
77		Status:     status,
78		Stdin:      response.Stdin,
79		Stdout:     response.Stdout,
80		Stderr:     response.Stderr,
81		Terminal:   response.Terminal,
82		ExitStatus: response.ExitStatus,
83		ExitedAt:   response.ExitedAt,
84	}, nil
85}
86
87// ResizePty changes the side of the process's PTY to the provided width and height
88func (p *process) ResizePty(ctx context.Context, size runtime.ConsoleSize) error {
89	_, err := p.shim.task.ResizePty(ctx, &task.ResizePtyRequest{
90		ID:     p.shim.ID(),
91		ExecID: p.id,
92		Width:  size.Width,
93		Height: size.Height,
94	})
95	if err != nil {
96		return errdefs.FromGRPC(err)
97	}
98	return nil
99}
100
101// CloseIO closes the provided IO pipe for the process
102func (p *process) CloseIO(ctx context.Context) error {
103	_, err := p.shim.task.CloseIO(ctx, &task.CloseIORequest{
104		ID:     p.shim.ID(),
105		ExecID: p.id,
106		Stdin:  true,
107	})
108	if err != nil {
109		return errdefs.FromGRPC(err)
110	}
111	return nil
112}
113
114// Start the process
115func (p *process) Start(ctx context.Context) error {
116	_, err := p.shim.task.Start(ctx, &task.StartRequest{
117		ID:     p.shim.ID(),
118		ExecID: p.id,
119	})
120	if err != nil {
121		return errdefs.FromGRPC(err)
122	}
123	return nil
124}
125
126// Wait on the process to exit and return the exit status and timestamp
127func (p *process) Wait(ctx context.Context) (*runtime.Exit, error) {
128	response, err := p.shim.task.Wait(ctx, &task.WaitRequest{
129		ID:     p.shim.ID(),
130		ExecID: p.id,
131	})
132	if err != nil {
133		return nil, errdefs.FromGRPC(err)
134	}
135	return &runtime.Exit{
136		Timestamp: response.ExitedAt,
137		Status:    response.ExitStatus,
138	}, nil
139}
140
141func (p *process) Delete(ctx context.Context) (*runtime.Exit, error) {
142	response, err := p.shim.task.Delete(ctx, &task.DeleteRequest{
143		ID:     p.shim.ID(),
144		ExecID: p.id,
145	})
146	if err != nil {
147		return nil, errdefs.FromGRPC(err)
148	}
149	return &runtime.Exit{
150		Status:    response.ExitStatus,
151		Timestamp: response.ExitedAt,
152		Pid:       response.Pid,
153	}, nil
154}
155