github.com/fcwu/docker@v1.4.2-0.20150115145920-2a69ca89f0df/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 17 log "github.com/Sirupsen/logrus" 18 "github.com/docker/docker/daemon/execdriver" 19 "github.com/docker/docker/pkg/term" 20 "github.com/docker/libcontainer" 21 "github.com/docker/libcontainer/apparmor" 22 "github.com/docker/libcontainer/cgroups/fs" 23 "github.com/docker/libcontainer/cgroups/systemd" 24 consolepkg "github.com/docker/libcontainer/console" 25 "github.com/docker/libcontainer/namespaces" 26 _ "github.com/docker/libcontainer/namespaces/nsenter" 27 "github.com/docker/libcontainer/system" 28 ) 29 30 const ( 31 DriverName = "native" 32 Version = "0.2" 33 ) 34 35 type activeContainer struct { 36 container *libcontainer.Config 37 cmd *exec.Cmd 38 } 39 40 type driver struct { 41 root string 42 initPath string 43 activeContainers map[string]*activeContainer 44 sync.Mutex 45 } 46 47 func NewDriver(root, initPath string) (*driver, error) { 48 if err := os.MkdirAll(root, 0700); err != nil { 49 return nil, err 50 } 51 52 // native driver root is at docker_root/execdriver/native. Put apparmor at docker_root 53 if err := apparmor.InstallDefaultProfile(); err != nil { 54 return nil, err 55 } 56 57 return &driver{ 58 root: root, 59 initPath: initPath, 60 activeContainers: make(map[string]*activeContainer), 61 }, nil 62 } 63 64 type execOutput struct { 65 exitCode int 66 err error 67 } 68 69 func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) { 70 // take the Command and populate the libcontainer.Config from it 71 container, err := d.createContainer(c) 72 if err != nil { 73 return execdriver.ExitStatus{ExitCode: -1}, err 74 } 75 76 var term execdriver.Terminal 77 78 if c.ProcessConfig.Tty { 79 term, err = NewTtyConsole(&c.ProcessConfig, pipes) 80 } else { 81 term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes) 82 } 83 if err != nil { 84 return execdriver.ExitStatus{ExitCode: -1}, err 85 } 86 c.ProcessConfig.Terminal = term 87 88 d.Lock() 89 d.activeContainers[c.ID] = &activeContainer{ 90 container: container, 91 cmd: &c.ProcessConfig.Cmd, 92 } 93 d.Unlock() 94 95 var ( 96 dataPath = filepath.Join(d.root, c.ID) 97 args = append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...) 98 ) 99 100 if err := d.createContainerRoot(c.ID); err != nil { 101 return execdriver.ExitStatus{ExitCode: -1}, err 102 } 103 defer d.cleanContainer(c.ID) 104 105 if err := d.writeContainerFile(container, c.ID); err != nil { 106 return execdriver.ExitStatus{ExitCode: -1}, err 107 } 108 109 execOutputChan := make(chan execOutput, 1) 110 waitForStart := make(chan struct{}) 111 112 go func() { 113 exitCode, err := namespaces.Exec(container, c.ProcessConfig.Stdin, c.ProcessConfig.Stdout, c.ProcessConfig.Stderr, c.ProcessConfig.Console, dataPath, args, func(container *libcontainer.Config, console, dataPath, init string, child *os.File, args []string) *exec.Cmd { 114 c.ProcessConfig.Path = d.initPath 115 c.ProcessConfig.Args = append([]string{ 116 DriverName, 117 "-console", console, 118 "-pipe", "3", 119 "-root", filepath.Join(d.root, c.ID), 120 "--", 121 }, args...) 122 123 // set this to nil so that when we set the clone flags anything else is reset 124 c.ProcessConfig.SysProcAttr = &syscall.SysProcAttr{ 125 Cloneflags: uintptr(namespaces.GetNamespaceFlags(container.Namespaces)), 126 } 127 c.ProcessConfig.ExtraFiles = []*os.File{child} 128 129 c.ProcessConfig.Env = container.Env 130 c.ProcessConfig.Dir = container.RootFs 131 132 return &c.ProcessConfig.Cmd 133 }, func() { 134 close(waitForStart) 135 if startCallback != nil { 136 c.ContainerPid = c.ProcessConfig.Process.Pid 137 startCallback(&c.ProcessConfig, c.ContainerPid) 138 } 139 }) 140 execOutputChan <- execOutput{exitCode, err} 141 }() 142 143 select { 144 case execOutput := <-execOutputChan: 145 return execdriver.ExitStatus{ExitCode: execOutput.exitCode}, execOutput.err 146 case <-waitForStart: 147 break 148 } 149 150 oomKill := false 151 state, err := libcontainer.GetState(filepath.Join(d.root, c.ID)) 152 if err == nil { 153 oomKillNotification, err := libcontainer.NotifyOnOOM(state) 154 if err == nil { 155 _, oomKill = <-oomKillNotification 156 } else { 157 log.Warnf("WARNING: Your kernel does not support OOM notifications: %s", err) 158 } 159 } else { 160 log.Warnf("Failed to get container state, oom notify will not work: %s", err) 161 } 162 // wait for the container to exit. 163 execOutput := <-execOutputChan 164 165 return execdriver.ExitStatus{ExitCode: execOutput.exitCode, OOMKilled: oomKill}, execOutput.err 166 } 167 168 func (d *driver) Kill(p *execdriver.Command, sig int) error { 169 return syscall.Kill(p.ProcessConfig.Process.Pid, syscall.Signal(sig)) 170 } 171 172 func (d *driver) Pause(c *execdriver.Command) error { 173 active := d.activeContainers[c.ID] 174 if active == nil { 175 return fmt.Errorf("active container for %s does not exist", c.ID) 176 } 177 active.container.Cgroups.Freezer = "FROZEN" 178 if systemd.UseSystemd() { 179 return systemd.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) 180 } 181 return fs.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) 182 } 183 184 func (d *driver) Unpause(c *execdriver.Command) error { 185 active := d.activeContainers[c.ID] 186 if active == nil { 187 return fmt.Errorf("active container for %s does not exist", c.ID) 188 } 189 active.container.Cgroups.Freezer = "THAWED" 190 if systemd.UseSystemd() { 191 return systemd.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) 192 } 193 return fs.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) 194 } 195 196 func (d *driver) Terminate(p *execdriver.Command) error { 197 // lets check the start time for the process 198 state, err := libcontainer.GetState(filepath.Join(d.root, p.ID)) 199 if err != nil { 200 if !os.IsNotExist(err) { 201 return err 202 } 203 // TODO: Remove this part for version 1.2.0 204 // This is added only to ensure smooth upgrades from pre 1.1.0 to 1.1.0 205 data, err := ioutil.ReadFile(filepath.Join(d.root, p.ID, "start")) 206 if err != nil { 207 // if we don't have the data on disk then we can assume the process is gone 208 // because this is only removed after we know the process has stopped 209 if os.IsNotExist(err) { 210 return nil 211 } 212 return err 213 } 214 state = &libcontainer.State{InitStartTime: string(data)} 215 } 216 217 currentStartTime, err := system.GetProcessStartTime(p.ProcessConfig.Process.Pid) 218 if err != nil { 219 return err 220 } 221 222 if state.InitStartTime == currentStartTime { 223 err = syscall.Kill(p.ProcessConfig.Process.Pid, 9) 224 syscall.Wait4(p.ProcessConfig.Process.Pid, nil, 0, nil) 225 } 226 d.cleanContainer(p.ID) 227 228 return err 229 230 } 231 232 func (d *driver) Info(id string) execdriver.Info { 233 return &info{ 234 ID: id, 235 driver: d, 236 } 237 } 238 239 func (d *driver) Name() string { 240 return fmt.Sprintf("%s-%s", DriverName, Version) 241 } 242 243 func (d *driver) GetPidsForContainer(id string) ([]int, error) { 244 d.Lock() 245 active := d.activeContainers[id] 246 d.Unlock() 247 248 if active == nil { 249 return nil, fmt.Errorf("active container for %s does not exist", id) 250 } 251 c := active.container.Cgroups 252 253 if systemd.UseSystemd() { 254 return systemd.GetPids(c) 255 } 256 return fs.GetPids(c) 257 } 258 259 func (d *driver) writeContainerFile(container *libcontainer.Config, id string) error { 260 data, err := json.Marshal(container) 261 if err != nil { 262 return err 263 } 264 return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655) 265 } 266 267 func (d *driver) cleanContainer(id string) error { 268 d.Lock() 269 delete(d.activeContainers, id) 270 d.Unlock() 271 return os.RemoveAll(filepath.Join(d.root, id, "container.json")) 272 } 273 274 func (d *driver) createContainerRoot(id string) error { 275 return os.MkdirAll(filepath.Join(d.root, id), 0655) 276 } 277 278 func (d *driver) Clean(id string) error { 279 return os.RemoveAll(filepath.Join(d.root, id)) 280 } 281 282 func getEnv(key string, env []string) string { 283 for _, pair := range env { 284 parts := strings.Split(pair, "=") 285 if parts[0] == key { 286 return parts[1] 287 } 288 } 289 return "" 290 } 291 292 type TtyConsole struct { 293 MasterPty *os.File 294 } 295 296 func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) { 297 ptyMaster, console, err := consolepkg.CreateMasterAndConsole() 298 if err != nil { 299 return nil, err 300 } 301 302 tty := &TtyConsole{ 303 MasterPty: ptyMaster, 304 } 305 306 if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil { 307 tty.Close() 308 return nil, err 309 } 310 311 processConfig.Console = console 312 313 return tty, nil 314 } 315 316 func (t *TtyConsole) Master() *os.File { 317 return t.MasterPty 318 } 319 320 func (t *TtyConsole) Resize(h, w int) error { 321 return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) 322 } 323 324 func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error { 325 go func() { 326 if wb, ok := pipes.Stdout.(interface { 327 CloseWriters() error 328 }); ok { 329 defer wb.CloseWriters() 330 } 331 332 io.Copy(pipes.Stdout, t.MasterPty) 333 }() 334 335 if pipes.Stdin != nil { 336 go func() { 337 io.Copy(t.MasterPty, pipes.Stdin) 338 339 pipes.Stdin.Close() 340 }() 341 } 342 343 return nil 344 } 345 346 func (t *TtyConsole) Close() error { 347 return t.MasterPty.Close() 348 }