-
Notifications
You must be signed in to change notification settings - Fork 10
/
runtime_utils.go
145 lines (123 loc) · 3.42 KB
/
runtime_utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package embedshim
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/go-runc"
"golang.org/x/sys/unix"
)
const (
// runcRoot is the path to the root runc state directory
runcRoot = "/run/containerd/runc"
)
func newRuncRuntime(root, path, namespace, runtime, criu string, systemd bool) *runc.Runc {
if root == "" {
root = runcRoot
}
return &runc.Runc{
Command: runtime,
Log: filepath.Join(path, "log.json"),
LogFormat: runc.JSON,
// NOTE(fuweid):
//
// The CRI plugin will use runtime.LockOSThread to create
// the net namespace and that thread will be terminated because
// CRI plugin doesn't call the UnlockOSThread.
//
// Based on this, we can't use PdeathSignal: SIGKILL here.
Root: filepath.Join(root, namespace),
Criu: criu,
SystemdCgroup: systemd,
}
}
func getLastRuntimeError(r *runc.Runc) (string, error) {
if r.Log == "" {
return "", nil
}
f, err := os.OpenFile(r.Log, os.O_RDONLY, 0400)
if err != nil {
return "", err
}
defer f.Close()
var (
errMsg string
log struct {
Level string
Msg string
Time time.Time
}
)
dec := json.NewDecoder(f)
for err = nil; err == nil; {
if err = dec.Decode(&log); err != nil && err != io.EOF {
return "", err
}
if log.Level == "error" {
errMsg = strings.TrimSpace(log.Msg)
}
}
return errMsg, nil
}
func checkKillError(err error) error {
if err == nil {
return nil
}
if strings.Contains(err.Error(), "os: process already finished") ||
strings.Contains(err.Error(), "container not running") ||
strings.Contains(strings.ToLower(err.Error()), "no such process") ||
err == unix.ESRCH {
return fmt.Errorf("process already finished: %w", errdefs.ErrNotFound)
} else if strings.Contains(err.Error(), "does not exist") {
return fmt.Errorf("no such container: %w", errdefs.ErrNotFound)
}
return fmt.Errorf("unknown error after kill: %w", err)
}
// checkRuncInitAlive is to check the runc-init holding the exec.fifo in runc
// root dir, which is used to prevent from pid reuse.
func checkRuncInitAlive(init *initProcess) error {
var (
id = init.ID()
pid = init.pid
execFIFO = filepath.Join(init.runtime.Root, id, "exec.fifo")
procFDDir = filepath.Join("/proc", strconv.Itoa(pid), "fd")
)
fdInfos, err := os.ReadDir(procFDDir)
if err != nil {
return fmt.Errorf("failed to read %v: %w", procFDDir, err)
}
for _, fdInfo := range fdInfos {
fd, err := strconv.Atoi(fdInfo.Name())
if err != nil {
return err
}
if fd < 3 {
continue
}
realPath, err := os.Readlink(filepath.Join(procFDDir, fdInfo.Name()))
if err != nil {
return fmt.Errorf("failed to readlink: %w", err)
}
if realPath == execFIFO {
return nil
}
}
return fmt.Errorf("process %v maybe not valid runc-init", pid)
}