github.com/darciopacifico/docker@v1.9.0-rc1/daemon/exec.go (about) 1 package daemon 2 3 import ( 4 "io" 5 "io/ioutil" 6 "strings" 7 "sync" 8 "time" 9 10 "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/daemon/execdriver" 12 derr "github.com/docker/docker/errors" 13 "github.com/docker/docker/pkg/broadcaster" 14 "github.com/docker/docker/pkg/ioutils" 15 "github.com/docker/docker/pkg/pools" 16 "github.com/docker/docker/pkg/stringid" 17 "github.com/docker/docker/pkg/stringutils" 18 "github.com/docker/docker/runconfig" 19 ) 20 21 // ExecConfig holds the configurations for execs. The Daemon keeps 22 // track of both running and finished execs so that they can be 23 // examined both during and after completion. 24 type ExecConfig struct { 25 sync.Mutex 26 ID string 27 Running bool 28 ExitCode int 29 ProcessConfig *execdriver.ProcessConfig 30 streamConfig 31 OpenStdin bool 32 OpenStderr bool 33 OpenStdout bool 34 Container *Container 35 canRemove bool 36 37 // waitStart will be closed immediately after the exec is really started. 38 waitStart chan struct{} 39 } 40 41 type execStore struct { 42 s map[string]*ExecConfig 43 sync.RWMutex 44 } 45 46 func newExecStore() *execStore { 47 return &execStore{s: make(map[string]*ExecConfig, 0)} 48 } 49 50 func (e *execStore) Add(id string, ExecConfig *ExecConfig) { 51 e.Lock() 52 e.s[id] = ExecConfig 53 e.Unlock() 54 } 55 56 func (e *execStore) Get(id string) *ExecConfig { 57 e.RLock() 58 res := e.s[id] 59 e.RUnlock() 60 return res 61 } 62 63 func (e *execStore) Delete(id string) { 64 e.Lock() 65 delete(e.s, id) 66 e.Unlock() 67 } 68 69 func (e *execStore) List() []string { 70 var IDs []string 71 e.RLock() 72 for id := range e.s { 73 IDs = append(IDs, id) 74 } 75 e.RUnlock() 76 return IDs 77 } 78 79 func (ExecConfig *ExecConfig) resize(h, w int) error { 80 select { 81 case <-ExecConfig.waitStart: 82 case <-time.After(time.Second): 83 return derr.ErrorCodeExecResize.WithArgs(ExecConfig.ID) 84 } 85 return ExecConfig.ProcessConfig.Terminal.Resize(h, w) 86 } 87 88 func (d *Daemon) registerExecCommand(ExecConfig *ExecConfig) { 89 // Storing execs in container in order to kill them gracefully whenever the container is stopped or removed. 90 ExecConfig.Container.execCommands.Add(ExecConfig.ID, ExecConfig) 91 // Storing execs in daemon for easy access via remote API. 92 d.execCommands.Add(ExecConfig.ID, ExecConfig) 93 } 94 95 // ExecExists looks up the exec instance and returns a bool if it exists or not. 96 // It will also return the error produced by `getExecConfig` 97 func (d *Daemon) ExecExists(name string) (bool, error) { 98 if _, err := d.getExecConfig(name); err != nil { 99 return false, err 100 } 101 return true, nil 102 } 103 104 // getExecConfig looks up the exec instance by name. If the container associated 105 // with the exec instance is stopped or paused, it will return an error. 106 func (d *Daemon) getExecConfig(name string) (*ExecConfig, error) { 107 ec := d.execCommands.Get(name) 108 109 // If the exec is found but its container is not in the daemon's list of 110 // containers then it must have been delete, in which case instead of 111 // saying the container isn't running, we should return a 404 so that 112 // the user sees the same error now that they will after the 113 // 5 minute clean-up loop is run which erases old/dead execs. 114 115 if ec != nil && d.containers.Get(ec.Container.ID) != nil { 116 if !ec.Container.IsRunning() { 117 return nil, derr.ErrorCodeContainerNotRunning.WithArgs(ec.Container.ID, ec.Container.State.String()) 118 } 119 if ec.Container.isPaused() { 120 return nil, derr.ErrorCodeExecPaused.WithArgs(ec.Container.ID) 121 } 122 return ec, nil 123 } 124 125 return nil, derr.ErrorCodeNoExecID.WithArgs(name) 126 } 127 128 func (d *Daemon) unregisterExecCommand(ExecConfig *ExecConfig) { 129 ExecConfig.Container.execCommands.Delete(ExecConfig.ID) 130 d.execCommands.Delete(ExecConfig.ID) 131 } 132 133 func (d *Daemon) getActiveContainer(name string) (*Container, error) { 134 container, err := d.Get(name) 135 if err != nil { 136 return nil, err 137 } 138 139 if !container.IsRunning() { 140 return nil, derr.ErrorCodeNotRunning.WithArgs(name) 141 } 142 if container.isPaused() { 143 return nil, derr.ErrorCodeExecPaused.WithArgs(name) 144 } 145 return container, nil 146 } 147 148 // ContainerExecCreate sets up an exec in a running container. 149 func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, error) { 150 // Not all drivers support Exec (LXC for example) 151 if err := checkExecSupport(d.execDriver.Name()); err != nil { 152 return "", err 153 } 154 155 container, err := d.getActiveContainer(config.Container) 156 if err != nil { 157 return "", err 158 } 159 160 cmd := stringutils.NewStrSlice(config.Cmd...) 161 entrypoint, args := d.getEntrypointAndArgs(stringutils.NewStrSlice(), cmd) 162 163 user := config.User 164 if len(user) == 0 { 165 user = container.Config.User 166 } 167 168 processConfig := &execdriver.ProcessConfig{ 169 Tty: config.Tty, 170 Entrypoint: entrypoint, 171 Arguments: args, 172 User: user, 173 Privileged: config.Privileged, 174 } 175 176 ExecConfig := &ExecConfig{ 177 ID: stringid.GenerateNonCryptoID(), 178 OpenStdin: config.AttachStdin, 179 OpenStdout: config.AttachStdout, 180 OpenStderr: config.AttachStderr, 181 streamConfig: streamConfig{}, 182 ProcessConfig: processConfig, 183 Container: container, 184 Running: false, 185 waitStart: make(chan struct{}), 186 } 187 188 d.registerExecCommand(ExecConfig) 189 190 container.logEvent("exec_create: " + ExecConfig.ProcessConfig.Entrypoint + " " + strings.Join(ExecConfig.ProcessConfig.Arguments, " ")) 191 192 return ExecConfig.ID, nil 193 } 194 195 // ContainerExecStart starts a previously set up exec instance. The 196 // std streams are set up. 197 func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error { 198 var ( 199 cStdin io.ReadCloser 200 cStdout, cStderr io.Writer 201 ) 202 203 ec, err := d.getExecConfig(name) 204 if err != nil { 205 return derr.ErrorCodeNoExecID.WithArgs(name) 206 } 207 208 ec.Lock() 209 if ec.Running { 210 ec.Unlock() 211 return derr.ErrorCodeExecRunning.WithArgs(ec.ID) 212 } 213 ec.Running = true 214 ec.Unlock() 215 216 logrus.Debugf("starting exec command %s in container %s", ec.ID, ec.Container.ID) 217 container := ec.Container 218 container.logEvent("exec_start: " + ec.ProcessConfig.Entrypoint + " " + strings.Join(ec.ProcessConfig.Arguments, " ")) 219 220 if ec.OpenStdin { 221 r, w := io.Pipe() 222 go func() { 223 defer w.Close() 224 defer logrus.Debugf("Closing buffered stdin pipe") 225 pools.Copy(w, stdin) 226 }() 227 cStdin = r 228 } 229 if ec.OpenStdout { 230 cStdout = stdout 231 } 232 if ec.OpenStderr { 233 cStderr = stderr 234 } 235 236 ec.streamConfig.stderr = new(broadcaster.Unbuffered) 237 ec.streamConfig.stdout = new(broadcaster.Unbuffered) 238 // Attach to stdin 239 if ec.OpenStdin { 240 ec.streamConfig.stdin, ec.streamConfig.stdinPipe = io.Pipe() 241 } else { 242 ec.streamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin 243 } 244 245 attachErr := attach(&ec.streamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr) 246 247 execErr := make(chan error) 248 249 // Note, the ExecConfig data will be removed when the container 250 // itself is deleted. This allows us to query it (for things like 251 // the exitStatus) even after the cmd is done running. 252 253 go func() { 254 execErr <- container.exec(ec) 255 }() 256 257 select { 258 case err := <-attachErr: 259 if err != nil { 260 return derr.ErrorCodeExecAttach.WithArgs(err) 261 } 262 return nil 263 case err := <-execErr: 264 if aErr := <-attachErr; aErr != nil && err == nil { 265 return derr.ErrorCodeExecAttach.WithArgs(aErr) 266 } 267 if err == nil { 268 return nil 269 } 270 271 // Maybe the container stopped while we were trying to exec 272 if !container.IsRunning() { 273 return derr.ErrorCodeExecContainerStopped 274 } 275 return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, container.ID, err) 276 } 277 } 278 279 // Exec calls the underlying exec driver to run 280 func (d *Daemon) Exec(c *Container, ExecConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) { 281 hooks := execdriver.Hooks{ 282 Start: startCallback, 283 } 284 exitStatus, err := d.execDriver.Exec(c.command, ExecConfig.ProcessConfig, pipes, hooks) 285 286 // On err, make sure we don't leave ExitCode at zero 287 if err != nil && exitStatus == 0 { 288 exitStatus = 128 289 } 290 291 ExecConfig.ExitCode = exitStatus 292 ExecConfig.Running = false 293 294 return exitStatus, err 295 } 296 297 // execCommandGC runs a ticker to clean up the daemon references 298 // of exec configs that are no longer part of the container. 299 func (d *Daemon) execCommandGC() { 300 for range time.Tick(5 * time.Minute) { 301 var ( 302 cleaned int 303 liveExecCommands = d.containerExecIds() 304 ) 305 for id, config := range d.execCommands.s { 306 if config.canRemove { 307 cleaned++ 308 d.execCommands.Delete(id) 309 } else { 310 if _, exists := liveExecCommands[id]; !exists { 311 config.canRemove = true 312 } 313 } 314 } 315 if cleaned > 0 { 316 logrus.Debugf("clean %d unused exec commands", cleaned) 317 } 318 } 319 } 320 321 // containerExecIds returns a list of all the current exec ids that are in use 322 // and running inside a container. 323 func (d *Daemon) containerExecIds() map[string]struct{} { 324 ids := map[string]struct{}{} 325 for _, c := range d.containers.List() { 326 for _, id := range c.execCommands.List() { 327 ids[id] = struct{}{} 328 } 329 } 330 return ids 331 }