github.com/demonoid81/containerd@v1.3.4/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 } 141 return errors.Wrapf(err, "unknown error after kill") 142 } 143 144 func newPidFile(bundle string) *pidFile { 145 return &pidFile{ 146 path: filepath.Join(bundle, InitPidFile), 147 } 148 } 149 150 func newExecPidFile(bundle, id string) *pidFile { 151 return &pidFile{ 152 path: filepath.Join(bundle, fmt.Sprintf("%s.pid", id)), 153 } 154 } 155 156 type pidFile struct { 157 path string 158 } 159 160 func (p *pidFile) Path() string { 161 return p.path 162 } 163 164 func (p *pidFile) Read() (int, error) { 165 return runc.ReadPidFile(p.path) 166 } 167 168 // waitTimeout handles waiting on a waitgroup with a specified timeout. 169 // this is commonly used for waiting on IO to finish after a process has exited 170 func waitTimeout(ctx context.Context, wg *sync.WaitGroup, timeout time.Duration) error { 171 ctx, cancel := context.WithTimeout(ctx, timeout) 172 defer cancel() 173 done := make(chan struct{}, 1) 174 go func() { 175 wg.Wait() 176 close(done) 177 }() 178 select { 179 case <-done: 180 return nil 181 case <-ctx.Done(): 182 return ctx.Err() 183 } 184 } 185 186 func stateName(v interface{}) string { 187 switch v.(type) { 188 case *runningState, *execRunningState: 189 return "running" 190 case *createdState, *execCreatedState, *createdCheckpointState: 191 return "created" 192 case *pausedState: 193 return "paused" 194 case *deletedState: 195 return "deleted" 196 case *stoppedState: 197 return "stopped" 198 } 199 panic(errors.Errorf("invalid state %v", v)) 200 }