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