gitee.com/bomy/docker.git@v1.13.1/libcontainerd/container_unix.go (about) 1 // +build linux solaris 2 3 package libcontainerd 4 5 import ( 6 "encoding/json" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "sync" 12 "syscall" 13 "time" 14 15 "github.com/Sirupsen/logrus" 16 containerd "github.com/docker/containerd/api/grpc/types" 17 "github.com/docker/docker/pkg/ioutils" 18 specs "github.com/opencontainers/runtime-spec/specs-go" 19 "github.com/tonistiigi/fifo" 20 "golang.org/x/net/context" 21 ) 22 23 type container struct { 24 containerCommon 25 26 // Platform specific fields are below here. 27 pauseMonitor 28 oom bool 29 runtime string 30 runtimeArgs []string 31 } 32 33 type runtime struct { 34 path string 35 args []string 36 } 37 38 // WithRuntime sets the runtime to be used for the created container 39 func WithRuntime(path string, args []string) CreateOption { 40 return runtime{path, args} 41 } 42 43 func (rt runtime) Apply(p interface{}) error { 44 if pr, ok := p.(*container); ok { 45 pr.runtime = rt.path 46 pr.runtimeArgs = rt.args 47 } 48 return nil 49 } 50 51 func (ctr *container) clean() error { 52 if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" { 53 return nil 54 } 55 if _, err := os.Lstat(ctr.dir); err != nil { 56 if os.IsNotExist(err) { 57 return nil 58 } 59 return err 60 } 61 62 if err := os.RemoveAll(ctr.dir); err != nil { 63 return err 64 } 65 return nil 66 } 67 68 // cleanProcess removes the fifos used by an additional process. 69 // Caller needs to lock container ID before calling this method. 70 func (ctr *container) cleanProcess(id string) { 71 if p, ok := ctr.processes[id]; ok { 72 for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} { 73 if err := os.Remove(p.fifo(i)); err != nil && !os.IsNotExist(err) { 74 logrus.Warnf("libcontainerd: failed to remove %v for process %v: %v", p.fifo(i), id, err) 75 } 76 } 77 } 78 delete(ctr.processes, id) 79 } 80 81 func (ctr *container) spec() (*specs.Spec, error) { 82 var spec specs.Spec 83 dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename)) 84 if err != nil { 85 return nil, err 86 } 87 if err := json.Unmarshal(dt, &spec); err != nil { 88 return nil, err 89 } 90 return &spec, nil 91 } 92 93 func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio StdioCallback) (err error) { 94 spec, err := ctr.spec() 95 if err != nil { 96 return nil 97 } 98 99 ctx, cancel := context.WithCancel(context.Background()) 100 defer cancel() 101 ready := make(chan struct{}) 102 103 fifoCtx, cancel := context.WithCancel(context.Background()) 104 defer func() { 105 if err != nil { 106 cancel() 107 } 108 }() 109 110 iopipe, err := ctr.openFifos(fifoCtx, spec.Process.Terminal) 111 if err != nil { 112 return err 113 } 114 115 var stdinOnce sync.Once 116 117 // we need to delay stdin closure after container start or else "stdin close" 118 // event will be rejected by containerd. 119 // stdin closure happens in attachStdio 120 stdin := iopipe.Stdin 121 iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { 122 var err error 123 stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed 124 err = stdin.Close() 125 go func() { 126 select { 127 case <-ready: 128 case <-ctx.Done(): 129 } 130 select { 131 case <-ready: 132 if err := ctr.sendCloseStdin(); err != nil { 133 logrus.Warnf("failed to close stdin: %+v", err) 134 } 135 default: 136 } 137 }() 138 }) 139 return err 140 }) 141 142 r := &containerd.CreateContainerRequest{ 143 Id: ctr.containerID, 144 BundlePath: ctr.dir, 145 Stdin: ctr.fifo(syscall.Stdin), 146 Stdout: ctr.fifo(syscall.Stdout), 147 Stderr: ctr.fifo(syscall.Stderr), 148 Checkpoint: checkpoint, 149 CheckpointDir: checkpointDir, 150 // check to see if we are running in ramdisk to disable pivot root 151 NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", 152 Runtime: ctr.runtime, 153 RuntimeArgs: ctr.runtimeArgs, 154 } 155 ctr.client.appendContainer(ctr) 156 157 if err := attachStdio(*iopipe); err != nil { 158 ctr.closeFifos(iopipe) 159 return err 160 } 161 162 resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) 163 if err != nil { 164 ctr.closeFifos(iopipe) 165 return err 166 } 167 ctr.systemPid = systemPid(resp.Container) 168 close(ready) 169 170 return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ 171 CommonStateInfo: CommonStateInfo{ 172 State: StateStart, 173 Pid: ctr.systemPid, 174 }}) 175 } 176 177 func (ctr *container) newProcess(friendlyName string) *process { 178 return &process{ 179 dir: ctr.dir, 180 processCommon: processCommon{ 181 containerID: ctr.containerID, 182 friendlyName: friendlyName, 183 client: ctr.client, 184 }, 185 } 186 } 187 188 func (ctr *container) handleEvent(e *containerd.Event) error { 189 ctr.client.lock(ctr.containerID) 190 defer ctr.client.unlock(ctr.containerID) 191 switch e.Type { 192 case StateExit, StatePause, StateResume, StateOOM: 193 st := StateInfo{ 194 CommonStateInfo: CommonStateInfo{ 195 State: e.Type, 196 ExitCode: e.Status, 197 }, 198 OOMKilled: e.Type == StateExit && ctr.oom, 199 } 200 if e.Type == StateOOM { 201 ctr.oom = true 202 } 203 if e.Type == StateExit && e.Pid != InitFriendlyName { 204 st.ProcessID = e.Pid 205 st.State = StateExitProcess 206 } 207 208 // Remove process from list if we have exited 209 switch st.State { 210 case StateExit: 211 ctr.clean() 212 ctr.client.deleteContainer(e.Id) 213 case StateExitProcess: 214 ctr.cleanProcess(st.ProcessID) 215 } 216 ctr.client.q.append(e.Id, func() { 217 if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { 218 logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err) 219 } 220 if e.Type == StatePause || e.Type == StateResume { 221 ctr.pauseMonitor.handle(e.Type) 222 } 223 if e.Type == StateExit { 224 if en := ctr.client.getExitNotifier(e.Id); en != nil { 225 en.close() 226 } 227 } 228 }) 229 230 default: 231 logrus.Debugf("libcontainerd: event unhandled: %+v", e) 232 } 233 return nil 234 } 235 236 // discardFifos attempts to fully read the container fifos to unblock processes 237 // that may be blocked on the writer side. 238 func (ctr *container) discardFifos() { 239 ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) 240 for _, i := range []int{syscall.Stdout, syscall.Stderr} { 241 f, err := fifo.OpenFifo(ctx, ctr.fifo(i), syscall.O_RDONLY|syscall.O_NONBLOCK, 0) 242 if err != nil { 243 logrus.Warnf("error opening fifo %v for discarding: %+v", f, err) 244 continue 245 } 246 go func() { 247 io.Copy(ioutil.Discard, f) 248 }() 249 } 250 }