github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/pkg/process/utils.go (about) 1 // +build !windows 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 19 package process 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "io" 26 "os" 27 "path/filepath" 28 "strings" 29 "sync" 30 "sync/atomic" 31 "time" 32 33 "github.com/containerd/containerd/errdefs" 34 runc "github.com/containerd/go-runc" 35 "github.com/pkg/errors" 36 "golang.org/x/sys/unix" 37 ) 38 39 const ( 40 // RuncRoot is the path to the root runc state directory 41 RuncRoot = "/run/containerd/runc" 42 // InitPidFile name of the file that contains the init pid 43 InitPidFile = "init.pid" 44 ) 45 46 // safePid is a thread safe wrapper for pid. 47 type safePid struct { 48 sync.Mutex 49 pid int 50 } 51 52 func (s *safePid) get() int { 53 s.Lock() 54 defer s.Unlock() 55 return s.pid 56 } 57 58 type atomicBool int32 59 60 func (ab *atomicBool) set(b bool) { 61 if b { 62 atomic.StoreInt32((*int32)(ab), 1) 63 } else { 64 atomic.StoreInt32((*int32)(ab), 0) 65 } 66 } 67 68 func (ab *atomicBool) get() bool { 69 return atomic.LoadInt32((*int32)(ab)) == 1 70 } 71 72 // TODO(mlaventure): move to runc package? 73 func getLastRuntimeError(r *runc.Runc) (string, error) { 74 if r.Log == "" { 75 return "", nil 76 } 77 78 f, err := os.OpenFile(r.Log, os.O_RDONLY, 0400) 79 if err != nil { 80 return "", err 81 } 82 defer f.Close() 83 84 var ( 85 errMsg string 86 log struct { 87 Level string 88 Msg string 89 Time time.Time 90 } 91 ) 92 93 dec := json.NewDecoder(f) 94 for err = nil; err == nil; { 95 if err = dec.Decode(&log); err != nil && err != io.EOF { 96 return "", err 97 } 98 if log.Level == "error" { 99 errMsg = strings.TrimSpace(log.Msg) 100 } 101 } 102 103 return errMsg, nil 104 } 105 106 // criuError returns only the first line of the error message from criu 107 // it tries to add an invalid dump log location when returning the message 108 func criuError(err error) string { 109 parts := strings.Split(err.Error(), "\n") 110 return parts[0] 111 } 112 113 func copyFile(to, from string) error { 114 ff, err := os.Open(from) 115 if err != nil { 116 return err 117 } 118 defer ff.Close() 119 tt, err := os.Create(to) 120 if err != nil { 121 return err 122 } 123 defer tt.Close() 124 125 p := bufPool.Get().(*[]byte) 126 defer bufPool.Put(p) 127 _, err = io.CopyBuffer(tt, ff, *p) 128 return err 129 } 130 131 func checkKillError(err error) error { 132 if err == nil { 133 return nil 134 } 135 if strings.Contains(err.Error(), "os: process already finished") || 136 strings.Contains(err.Error(), "container not running") || 137 strings.Contains(strings.ToLower(err.Error()), "no such process") || 138 err == unix.ESRCH { 139 return errors.Wrapf(errdefs.ErrNotFound, "process already finished") 140 } else if strings.Contains(err.Error(), "does not exist") { 141 return errors.Wrapf(errdefs.ErrNotFound, "no such container") 142 } 143 return errors.Wrapf(err, "unknown error after kill") 144 } 145 146 func newPidFile(bundle string) *pidFile { 147 return &pidFile{ 148 path: filepath.Join(bundle, InitPidFile), 149 } 150 } 151 152 func newExecPidFile(bundle, id string) *pidFile { 153 return &pidFile{ 154 path: filepath.Join(bundle, fmt.Sprintf("%s.pid", id)), 155 } 156 } 157 158 type pidFile struct { 159 path string 160 } 161 162 func (p *pidFile) Path() string { 163 return p.path 164 } 165 166 func (p *pidFile) Read() (int, error) { 167 return runc.ReadPidFile(p.path) 168 } 169 170 // waitTimeout handles waiting on a waitgroup with a specified timeout. 171 // this is commonly used for waiting on IO to finish after a process has exited 172 func waitTimeout(ctx context.Context, wg *sync.WaitGroup, timeout time.Duration) error { 173 ctx, cancel := context.WithTimeout(ctx, timeout) 174 defer cancel() 175 done := make(chan struct{}, 1) 176 go func() { 177 wg.Wait() 178 close(done) 179 }() 180 select { 181 case <-done: 182 return nil 183 case <-ctx.Done(): 184 return ctx.Err() 185 } 186 } 187 188 func stateName(v interface{}) string { 189 switch v.(type) { 190 case *runningState, *execRunningState: 191 return "running" 192 case *createdState, *execCreatedState, *createdCheckpointState: 193 return "created" 194 case *pausedState: 195 return "paused" 196 case *deletedState: 197 return "deleted" 198 case *stoppedState: 199 return "stopped" 200 } 201 panic(errors.Errorf("invalid state %v", v)) 202 }