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