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