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