github.com/gdevillele/moby@v1.13.0/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) 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 iopipe, err := ctr.openFifos(spec.Process.Terminal) 104 if err != nil { 105 return err 106 } 107 108 var stdinOnce sync.Once 109 110 // we need to delay stdin closure after container start or else "stdin close" 111 // event will be rejected by containerd. 112 // stdin closure happens in attachStdio 113 stdin := iopipe.Stdin 114 iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { 115 var err error 116 stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed 117 err = stdin.Close() 118 go func() { 119 select { 120 case <-ready: 121 case <-ctx.Done(): 122 } 123 select { 124 case <-ready: 125 if err := ctr.sendCloseStdin(); err != nil { 126 logrus.Warnf("failed to close stdin: %+v", err) 127 } 128 default: 129 } 130 }() 131 }) 132 return err 133 }) 134 135 r := &containerd.CreateContainerRequest{ 136 Id: ctr.containerID, 137 BundlePath: ctr.dir, 138 Stdin: ctr.fifo(syscall.Stdin), 139 Stdout: ctr.fifo(syscall.Stdout), 140 Stderr: ctr.fifo(syscall.Stderr), 141 Checkpoint: checkpoint, 142 CheckpointDir: checkpointDir, 143 // check to see if we are running in ramdisk to disable pivot root 144 NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", 145 Runtime: ctr.runtime, 146 RuntimeArgs: ctr.runtimeArgs, 147 } 148 ctr.client.appendContainer(ctr) 149 150 if err := attachStdio(*iopipe); err != nil { 151 ctr.closeFifos(iopipe) 152 return err 153 } 154 155 resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) 156 if err != nil { 157 ctr.closeFifos(iopipe) 158 return err 159 } 160 ctr.systemPid = systemPid(resp.Container) 161 close(ready) 162 163 return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ 164 CommonStateInfo: CommonStateInfo{ 165 State: StateStart, 166 Pid: ctr.systemPid, 167 }}) 168 } 169 170 func (ctr *container) newProcess(friendlyName string) *process { 171 return &process{ 172 dir: ctr.dir, 173 processCommon: processCommon{ 174 containerID: ctr.containerID, 175 friendlyName: friendlyName, 176 client: ctr.client, 177 }, 178 } 179 } 180 181 func (ctr *container) handleEvent(e *containerd.Event) error { 182 ctr.client.lock(ctr.containerID) 183 defer ctr.client.unlock(ctr.containerID) 184 switch e.Type { 185 case StateExit, StatePause, StateResume, StateOOM: 186 st := StateInfo{ 187 CommonStateInfo: CommonStateInfo{ 188 State: e.Type, 189 ExitCode: e.Status, 190 }, 191 OOMKilled: e.Type == StateExit && ctr.oom, 192 } 193 if e.Type == StateOOM { 194 ctr.oom = true 195 } 196 if e.Type == StateExit && e.Pid != InitFriendlyName { 197 st.ProcessID = e.Pid 198 st.State = StateExitProcess 199 } 200 201 // Remove process from list if we have exited 202 switch st.State { 203 case StateExit: 204 ctr.clean() 205 ctr.client.deleteContainer(e.Id) 206 case StateExitProcess: 207 ctr.cleanProcess(st.ProcessID) 208 } 209 ctr.client.q.append(e.Id, func() { 210 if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { 211 logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err) 212 } 213 if e.Type == StatePause || e.Type == StateResume { 214 ctr.pauseMonitor.handle(e.Type) 215 } 216 if e.Type == StateExit { 217 if en := ctr.client.getExitNotifier(e.Id); en != nil { 218 en.close() 219 } 220 } 221 }) 222 223 default: 224 logrus.Debugf("libcontainerd: event unhandled: %+v", e) 225 } 226 return nil 227 } 228 229 // discardFifos attempts to fully read the container fifos to unblock processes 230 // that may be blocked on the writer side. 231 func (ctr *container) discardFifos() { 232 ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) 233 for _, i := range []int{syscall.Stdout, syscall.Stderr} { 234 f, err := fifo.OpenFifo(ctx, ctr.fifo(i), syscall.O_RDONLY|syscall.O_NONBLOCK, 0) 235 if err != nil { 236 logrus.Warnf("error opening fifo %v for discarding: %+v", f, err) 237 continue 238 } 239 go func() { 240 io.Copy(ioutil.Discard, f) 241 }() 242 } 243 }