github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/daemon/execdriver/native/driver.go (about) 1 // +build linux,cgo 2 3 package native 4 5 import ( 6 "fmt" 7 "io" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "sync" 12 "syscall" 13 "time" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/docker/daemon/execdriver" 17 "github.com/docker/docker/pkg/reexec" 18 sysinfo "github.com/docker/docker/pkg/system" 19 "github.com/docker/docker/pkg/term" 20 "github.com/docker/libcontainer" 21 "github.com/docker/libcontainer/apparmor" 22 "github.com/docker/libcontainer/cgroups/systemd" 23 "github.com/docker/libcontainer/configs" 24 "github.com/docker/libcontainer/system" 25 "github.com/docker/libcontainer/utils" 26 ) 27 28 const ( 29 DriverName = "native" 30 Version = "0.2" 31 ) 32 33 type driver struct { 34 root string 35 initPath string 36 activeContainers map[string]libcontainer.Container 37 machineMemory int64 38 factory libcontainer.Factory 39 sync.Mutex 40 } 41 42 func NewDriver(root, initPath string) (*driver, error) { 43 meminfo, err := sysinfo.ReadMemInfo() 44 if err != nil { 45 return nil, err 46 } 47 48 if err := os.MkdirAll(root, 0700); err != nil { 49 return nil, err 50 } 51 // native driver root is at docker_root/execdriver/native. Put apparmor at docker_root 52 if err := apparmor.InstallDefaultProfile(); err != nil { 53 return nil, err 54 } 55 cgm := libcontainer.Cgroupfs 56 if systemd.UseSystemd() { 57 cgm = libcontainer.SystemdCgroups 58 } 59 60 f, err := libcontainer.New( 61 root, 62 cgm, 63 libcontainer.InitPath(reexec.Self(), DriverName), 64 ) 65 if err != nil { 66 return nil, err 67 } 68 69 return &driver{ 70 root: root, 71 initPath: initPath, 72 activeContainers: make(map[string]libcontainer.Container), 73 machineMemory: meminfo.MemTotal, 74 factory: f, 75 }, nil 76 } 77 78 type execOutput struct { 79 exitCode int 80 err error 81 } 82 83 func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) { 84 // take the Command and populate the libcontainer.Config from it 85 container, err := d.createContainer(c) 86 if err != nil { 87 return execdriver.ExitStatus{ExitCode: -1}, err 88 } 89 90 p := &libcontainer.Process{ 91 Args: append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...), 92 Env: c.ProcessConfig.Env, 93 Cwd: c.WorkingDir, 94 User: c.ProcessConfig.User, 95 } 96 97 if err := setupPipes(container, &c.ProcessConfig, p, pipes); err != nil { 98 return execdriver.ExitStatus{ExitCode: -1}, err 99 } 100 101 cont, err := d.factory.Create(c.ID, container) 102 if err != nil { 103 return execdriver.ExitStatus{ExitCode: -1}, err 104 } 105 d.Lock() 106 d.activeContainers[c.ID] = cont 107 d.Unlock() 108 defer func() { 109 cont.Destroy() 110 d.cleanContainer(c.ID) 111 }() 112 113 if err := cont.Start(p); err != nil { 114 return execdriver.ExitStatus{ExitCode: -1}, err 115 } 116 117 if startCallback != nil { 118 pid, err := p.Pid() 119 if err != nil { 120 p.Signal(os.Kill) 121 p.Wait() 122 return execdriver.ExitStatus{ExitCode: -1}, err 123 } 124 startCallback(&c.ProcessConfig, pid) 125 } 126 127 oom := notifyOnOOM(cont) 128 waitF := p.Wait 129 if nss := cont.Config().Namespaces; !nss.Contains(configs.NEWPID) { 130 // we need such hack for tracking processes with inherited fds, 131 // because cmd.Wait() waiting for all streams to be copied 132 waitF = waitInPIDHost(p, cont) 133 } 134 ps, err := waitF() 135 if err != nil { 136 execErr, ok := err.(*exec.ExitError) 137 if !ok { 138 return execdriver.ExitStatus{ExitCode: -1}, err 139 } 140 ps = execErr.ProcessState 141 } 142 cont.Destroy() 143 _, oomKill := <-oom 144 return execdriver.ExitStatus{ExitCode: utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), OOMKilled: oomKill}, nil 145 } 146 147 // notifyOnOOM returns a channel that signals if the container received an OOM notification 148 // for any process. If it is unable to subscribe to OOM notifications then a closed 149 // channel is returned as it will be non-blocking and return the correct result when read. 150 func notifyOnOOM(container libcontainer.Container) <-chan struct{} { 151 oom, err := container.NotifyOOM() 152 if err != nil { 153 logrus.Warnf("Your kernel does not support OOM notifications: %s", err) 154 c := make(chan struct{}) 155 close(c) 156 return c 157 } 158 return oom 159 } 160 161 func killCgroupProcs(c libcontainer.Container) { 162 var procs []*os.Process 163 if err := c.Pause(); err != nil { 164 logrus.Warn(err) 165 } 166 pids, err := c.Processes() 167 if err != nil { 168 // don't care about childs if we can't get them, this is mostly because cgroup already deleted 169 logrus.Warnf("Failed to get processes from container %s: %v", c.ID(), err) 170 } 171 for _, pid := range pids { 172 if p, err := os.FindProcess(pid); err == nil { 173 procs = append(procs, p) 174 if err := p.Kill(); err != nil { 175 logrus.Warn(err) 176 } 177 } 178 } 179 if err := c.Resume(); err != nil { 180 logrus.Warn(err) 181 } 182 for _, p := range procs { 183 if _, err := p.Wait(); err != nil { 184 logrus.Warn(err) 185 } 186 } 187 } 188 189 func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*os.ProcessState, error) { 190 return func() (*os.ProcessState, error) { 191 pid, err := p.Pid() 192 if err != nil { 193 return nil, err 194 } 195 196 process, err := os.FindProcess(pid) 197 s, err := process.Wait() 198 if err != nil { 199 execErr, ok := err.(*exec.ExitError) 200 if !ok { 201 return s, err 202 } 203 s = execErr.ProcessState 204 } 205 killCgroupProcs(c) 206 p.Wait() 207 return s, err 208 } 209 } 210 211 func (d *driver) Kill(c *execdriver.Command, sig int) error { 212 active := d.activeContainers[c.ID] 213 if active == nil { 214 return fmt.Errorf("active container for %s does not exist", c.ID) 215 } 216 state, err := active.State() 217 if err != nil { 218 return err 219 } 220 return syscall.Kill(state.InitProcessPid, syscall.Signal(sig)) 221 } 222 223 func (d *driver) Pause(c *execdriver.Command) error { 224 active := d.activeContainers[c.ID] 225 if active == nil { 226 return fmt.Errorf("active container for %s does not exist", c.ID) 227 } 228 return active.Pause() 229 } 230 231 func (d *driver) Unpause(c *execdriver.Command) error { 232 active := d.activeContainers[c.ID] 233 if active == nil { 234 return fmt.Errorf("active container for %s does not exist", c.ID) 235 } 236 return active.Resume() 237 } 238 239 func (d *driver) Terminate(c *execdriver.Command) error { 240 defer d.cleanContainer(c.ID) 241 container, err := d.factory.Load(c.ID) 242 if err != nil { 243 return err 244 } 245 defer container.Destroy() 246 state, err := container.State() 247 if err != nil { 248 return err 249 } 250 pid := state.InitProcessPid 251 currentStartTime, err := system.GetProcessStartTime(pid) 252 if err != nil { 253 return err 254 } 255 if state.InitProcessStartTime == currentStartTime { 256 err = syscall.Kill(pid, 9) 257 syscall.Wait4(pid, nil, 0, nil) 258 } 259 return err 260 } 261 262 func (d *driver) Info(id string) execdriver.Info { 263 return &info{ 264 ID: id, 265 driver: d, 266 } 267 } 268 269 func (d *driver) Name() string { 270 return fmt.Sprintf("%s-%s", DriverName, Version) 271 } 272 273 func (d *driver) GetPidsForContainer(id string) ([]int, error) { 274 d.Lock() 275 active := d.activeContainers[id] 276 d.Unlock() 277 278 if active == nil { 279 return nil, fmt.Errorf("active container for %s does not exist", id) 280 } 281 return active.Processes() 282 } 283 284 func (d *driver) cleanContainer(id string) error { 285 d.Lock() 286 delete(d.activeContainers, id) 287 d.Unlock() 288 return os.RemoveAll(filepath.Join(d.root, id)) 289 } 290 291 func (d *driver) createContainerRoot(id string) error { 292 return os.MkdirAll(filepath.Join(d.root, id), 0655) 293 } 294 295 func (d *driver) Clean(id string) error { 296 return os.RemoveAll(filepath.Join(d.root, id)) 297 } 298 299 func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) { 300 c := d.activeContainers[id] 301 if c == nil { 302 return nil, execdriver.ErrNotRunning 303 } 304 now := time.Now() 305 stats, err := c.Stats() 306 if err != nil { 307 return nil, err 308 } 309 memoryLimit := c.Config().Cgroups.Memory 310 // if the container does not have any memory limit specified set the 311 // limit to the machines memory 312 if memoryLimit == 0 { 313 memoryLimit = d.machineMemory 314 } 315 return &execdriver.ResourceStats{ 316 Stats: stats, 317 Read: now, 318 MemoryLimit: memoryLimit, 319 }, nil 320 } 321 322 type TtyConsole struct { 323 console libcontainer.Console 324 } 325 326 func NewTtyConsole(console libcontainer.Console, pipes *execdriver.Pipes, rootuid int) (*TtyConsole, error) { 327 tty := &TtyConsole{ 328 console: console, 329 } 330 331 if err := tty.AttachPipes(pipes); err != nil { 332 tty.Close() 333 return nil, err 334 } 335 336 return tty, nil 337 } 338 339 func (t *TtyConsole) Master() libcontainer.Console { 340 return t.console 341 } 342 343 func (t *TtyConsole) Resize(h, w int) error { 344 return term.SetWinsize(t.console.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) 345 } 346 347 func (t *TtyConsole) AttachPipes(pipes *execdriver.Pipes) error { 348 go func() { 349 if wb, ok := pipes.Stdout.(interface { 350 CloseWriters() error 351 }); ok { 352 defer wb.CloseWriters() 353 } 354 355 io.Copy(pipes.Stdout, t.console) 356 }() 357 358 if pipes.Stdin != nil { 359 go func() { 360 io.Copy(t.console, pipes.Stdin) 361 362 pipes.Stdin.Close() 363 }() 364 } 365 366 return nil 367 } 368 369 func (t *TtyConsole) Close() error { 370 return t.console.Close() 371 } 372 373 func setupPipes(container *configs.Config, processConfig *execdriver.ProcessConfig, p *libcontainer.Process, pipes *execdriver.Pipes) error { 374 var term execdriver.Terminal 375 var err error 376 377 if processConfig.Tty { 378 rootuid, err := container.HostUID() 379 if err != nil { 380 return err 381 } 382 cons, err := p.NewConsole(rootuid) 383 if err != nil { 384 return err 385 } 386 term, err = NewTtyConsole(cons, pipes, rootuid) 387 } else { 388 p.Stdout = pipes.Stdout 389 p.Stderr = pipes.Stderr 390 r, w, err := os.Pipe() 391 if err != nil { 392 return err 393 } 394 if pipes.Stdin != nil { 395 go func() { 396 io.Copy(w, pipes.Stdin) 397 w.Close() 398 }() 399 p.Stdin = r 400 } 401 term = &execdriver.StdConsole{} 402 } 403 if err != nil { 404 return err 405 } 406 processConfig.Terminal = term 407 return nil 408 }