github.com/reds/docker@v1.11.2-rc1/libcontainerd/container_linux.go (about) 1 package libcontainerd 2 3 import ( 4 "encoding/json" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "syscall" 10 "time" 11 12 "github.com/Sirupsen/logrus" 13 containerd "github.com/docker/containerd/api/grpc/types" 14 "github.com/docker/docker/restartmanager" 15 "github.com/opencontainers/specs/specs-go" 16 "golang.org/x/net/context" 17 ) 18 19 type container struct { 20 containerCommon 21 22 // Platform specific fields are below here. 23 pauseMonitor 24 oom bool 25 } 26 27 func (ctr *container) clean() error { 28 if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" { 29 return nil 30 } 31 if _, err := os.Lstat(ctr.dir); err != nil { 32 if os.IsNotExist(err) { 33 return nil 34 } 35 return err 36 } 37 38 if err := os.RemoveAll(ctr.dir); err != nil { 39 return err 40 } 41 return nil 42 } 43 44 // cleanProcess removes the fifos used by an additional process. 45 // Caller needs to lock container ID before calling this method. 46 func (ctr *container) cleanProcess(id string) { 47 if p, ok := ctr.processes[id]; ok { 48 for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} { 49 if err := os.Remove(p.fifo(i)); err != nil { 50 logrus.Warnf("failed to remove %v for process %v: %v", p.fifo(i), id, err) 51 } 52 } 53 } 54 delete(ctr.processes, id) 55 } 56 57 func (ctr *container) spec() (*specs.Spec, error) { 58 var spec specs.Spec 59 dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename)) 60 if err != nil { 61 return nil, err 62 } 63 if err := json.Unmarshal(dt, &spec); err != nil { 64 return nil, err 65 } 66 return &spec, nil 67 } 68 69 func (ctr *container) start() error { 70 spec, err := ctr.spec() 71 if err != nil { 72 return nil 73 } 74 iopipe, err := ctr.openFifos(spec.Process.Terminal) 75 if err != nil { 76 return err 77 } 78 79 r := &containerd.CreateContainerRequest{ 80 Id: ctr.containerID, 81 BundlePath: ctr.dir, 82 Stdin: ctr.fifo(syscall.Stdin), 83 Stdout: ctr.fifo(syscall.Stdout), 84 Stderr: ctr.fifo(syscall.Stderr), 85 // check to see if we are running in ramdisk to disable pivot root 86 NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", 87 } 88 ctr.client.appendContainer(ctr) 89 90 resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) 91 if err != nil { 92 ctr.closeFifos(iopipe) 93 return err 94 } 95 ctr.startedAt = time.Now() 96 97 if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil { 98 return err 99 } 100 ctr.systemPid = systemPid(resp.Container) 101 102 return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ 103 State: StateStart, 104 Pid: ctr.systemPid, 105 }) 106 } 107 108 func (ctr *container) newProcess(friendlyName string) *process { 109 return &process{ 110 dir: ctr.dir, 111 processCommon: processCommon{ 112 containerID: ctr.containerID, 113 friendlyName: friendlyName, 114 client: ctr.client, 115 }, 116 } 117 } 118 119 func (ctr *container) handleEvent(e *containerd.Event) error { 120 ctr.client.lock(ctr.containerID) 121 defer ctr.client.unlock(ctr.containerID) 122 switch e.Type { 123 case StateExit, StatePause, StateResume, StateOOM: 124 st := StateInfo{ 125 State: e.Type, 126 ExitCode: e.Status, 127 OOMKilled: e.Type == StateExit && ctr.oom, 128 } 129 if e.Type == StateOOM { 130 ctr.oom = true 131 } 132 if e.Type == StateExit && e.Pid != InitFriendlyName { 133 st.ProcessID = e.Pid 134 st.State = StateExitProcess 135 } 136 if st.State == StateExit && ctr.restartManager != nil { 137 restart, wait, err := ctr.restartManager.ShouldRestart(e.Status, false, time.Since(ctr.startedAt)) 138 if err != nil { 139 logrus.Warnf("container %s %v", ctr.containerID, err) 140 } else if restart { 141 st.State = StateRestart 142 ctr.restarting = true 143 ctr.client.deleteContainer(e.Id) 144 go func() { 145 err := <-wait 146 ctr.client.lock(ctr.containerID) 147 defer ctr.client.unlock(ctr.containerID) 148 ctr.restarting = false 149 if err != nil { 150 st.State = StateExit 151 ctr.clean() 152 ctr.client.q.append(e.Id, func() { 153 if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { 154 logrus.Error(err) 155 } 156 }) 157 if err != restartmanager.ErrRestartCanceled { 158 logrus.Error(err) 159 } 160 } else { 161 ctr.start() 162 } 163 }() 164 } 165 } 166 167 // Remove process from list if we have exited 168 // We need to do so here in case the Message Handler decides to restart it. 169 switch st.State { 170 case StateExit: 171 ctr.clean() 172 ctr.client.deleteContainer(e.Id) 173 case StateExitProcess: 174 ctr.cleanProcess(st.ProcessID) 175 } 176 ctr.client.q.append(e.Id, func() { 177 if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { 178 logrus.Error(err) 179 } 180 if e.Type == StatePause || e.Type == StateResume { 181 ctr.pauseMonitor.handle(e.Type) 182 } 183 if e.Type == StateExit { 184 if en := ctr.client.getExitNotifier(e.Id); en != nil { 185 en.close() 186 } 187 } 188 }) 189 190 default: 191 logrus.Debugf("event unhandled: %+v", e) 192 } 193 return nil 194 } 195 196 // discardFifos attempts to fully read the container fifos to unblock processes 197 // that may be blocked on the writer side. 198 func (ctr *container) discardFifos() { 199 for _, i := range []int{syscall.Stdout, syscall.Stderr} { 200 f := ctr.fifo(i) 201 c := make(chan struct{}) 202 go func() { 203 close(c) // this channel is used to not close the writer too early, before readonly open has been called. 204 io.Copy(ioutil.Discard, openReaderFromFifo(f)) 205 }() 206 <-c 207 closeReaderFifo(f) // avoid blocking permanently on open if there is no writer side 208 } 209 }