github.com/wulonghui/docker@v1.8.0-rc2/daemon/exec.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/Sirupsen/logrus" 12 "github.com/docker/docker/daemon/execdriver" 13 "github.com/docker/docker/pkg/broadcastwriter" 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/runconfig" 18 ) 19 20 type execConfig struct { 21 sync.Mutex 22 ID string 23 Running bool 24 ExitCode int 25 ProcessConfig *execdriver.ProcessConfig 26 StreamConfig 27 OpenStdin bool 28 OpenStderr bool 29 OpenStdout bool 30 Container *Container 31 canRemove bool 32 } 33 34 type execStore struct { 35 s map[string]*execConfig 36 sync.RWMutex 37 } 38 39 func newExecStore() *execStore { 40 return &execStore{s: make(map[string]*execConfig, 0)} 41 } 42 43 func (e *execStore) Add(id string, execConfig *execConfig) { 44 e.Lock() 45 e.s[id] = execConfig 46 e.Unlock() 47 } 48 49 func (e *execStore) Get(id string) *execConfig { 50 e.RLock() 51 res := e.s[id] 52 e.RUnlock() 53 return res 54 } 55 56 func (e *execStore) Delete(id string) { 57 e.Lock() 58 delete(e.s, id) 59 e.Unlock() 60 } 61 62 func (e *execStore) List() []string { 63 var IDs []string 64 e.RLock() 65 for id := range e.s { 66 IDs = append(IDs, id) 67 } 68 e.RUnlock() 69 return IDs 70 } 71 72 func (execConfig *execConfig) Resize(h, w int) error { 73 return execConfig.ProcessConfig.Terminal.Resize(h, w) 74 } 75 76 func (d *Daemon) registerExecCommand(execConfig *execConfig) { 77 // Storing execs in container in order to kill them gracefully whenever the container is stopped or removed. 78 execConfig.Container.execCommands.Add(execConfig.ID, execConfig) 79 // Storing execs in daemon for easy access via remote API. 80 d.execCommands.Add(execConfig.ID, execConfig) 81 } 82 83 func (d *Daemon) getExecConfig(name string) (*execConfig, error) { 84 if execConfig := d.execCommands.Get(name); execConfig != nil { 85 if !execConfig.Container.IsRunning() { 86 return nil, fmt.Errorf("Container %s is not running", execConfig.Container.ID) 87 } 88 return execConfig, nil 89 } 90 91 return nil, fmt.Errorf("No such exec instance '%s' found in daemon", name) 92 } 93 94 func (d *Daemon) unregisterExecCommand(execConfig *execConfig) { 95 execConfig.Container.execCommands.Delete(execConfig.ID) 96 d.execCommands.Delete(execConfig.ID) 97 } 98 99 func (d *Daemon) getActiveContainer(name string) (*Container, error) { 100 container, err := d.Get(name) 101 if err != nil { 102 return nil, err 103 } 104 105 if !container.IsRunning() { 106 return nil, fmt.Errorf("Container %s is not running", name) 107 } 108 if container.IsPaused() { 109 return nil, fmt.Errorf("Container %s is paused, unpause the container before exec", name) 110 } 111 return container, nil 112 } 113 114 func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, error) { 115 // Not all drivers support Exec (LXC for example) 116 if err := checkExecSupport(d.execDriver.Name()); err != nil { 117 return "", err 118 } 119 120 container, err := d.getActiveContainer(config.Container) 121 if err != nil { 122 return "", err 123 } 124 125 cmd := runconfig.NewCommand(config.Cmd...) 126 entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd) 127 128 user := config.User 129 if len(user) == 0 { 130 user = container.Config.User 131 } 132 133 processConfig := &execdriver.ProcessConfig{ 134 Tty: config.Tty, 135 Entrypoint: entrypoint, 136 Arguments: args, 137 User: user, 138 } 139 140 execConfig := &execConfig{ 141 ID: stringid.GenerateRandomID(), 142 OpenStdin: config.AttachStdin, 143 OpenStdout: config.AttachStdout, 144 OpenStderr: config.AttachStderr, 145 StreamConfig: StreamConfig{}, 146 ProcessConfig: processConfig, 147 Container: container, 148 Running: false, 149 } 150 151 d.registerExecCommand(execConfig) 152 153 container.LogEvent("exec_create: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " ")) 154 155 return execConfig.ID, nil 156 157 } 158 159 func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error { 160 161 var ( 162 cStdin io.ReadCloser 163 cStdout, cStderr io.Writer 164 ) 165 166 execConfig, err := d.getExecConfig(execName) 167 if err != nil { 168 return err 169 } 170 171 func() { 172 execConfig.Lock() 173 defer execConfig.Unlock() 174 if execConfig.Running { 175 err = fmt.Errorf("Error: Exec command %s is already running", execName) 176 } 177 execConfig.Running = true 178 }() 179 if err != nil { 180 return err 181 } 182 183 logrus.Debugf("starting exec command %s in container %s", execConfig.ID, execConfig.Container.ID) 184 container := execConfig.Container 185 186 container.LogEvent("exec_start: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " ")) 187 188 if execConfig.OpenStdin { 189 r, w := io.Pipe() 190 go func() { 191 defer w.Close() 192 defer logrus.Debugf("Closing buffered stdin pipe") 193 pools.Copy(w, stdin) 194 }() 195 cStdin = r 196 } 197 if execConfig.OpenStdout { 198 cStdout = stdout 199 } 200 if execConfig.OpenStderr { 201 cStderr = stderr 202 } 203 204 execConfig.StreamConfig.stderr = broadcastwriter.New() 205 execConfig.StreamConfig.stdout = broadcastwriter.New() 206 // Attach to stdin 207 if execConfig.OpenStdin { 208 execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdinPipe = io.Pipe() 209 } else { 210 execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin 211 } 212 213 attachErr := attach(&execConfig.StreamConfig, execConfig.OpenStdin, true, execConfig.ProcessConfig.Tty, cStdin, cStdout, cStderr) 214 215 execErr := make(chan error) 216 217 // Note, the execConfig data will be removed when the container 218 // itself is deleted. This allows us to query it (for things like 219 // the exitStatus) even after the cmd is done running. 220 221 go func() { 222 if err := container.Exec(execConfig); err != nil { 223 execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err) 224 } 225 }() 226 select { 227 case err := <-attachErr: 228 if err != nil { 229 return fmt.Errorf("attach failed with error: %s", err) 230 } 231 break 232 case err := <-execErr: 233 return err 234 } 235 236 return nil 237 } 238 239 func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { 240 exitStatus, err := d.execDriver.Exec(c.command, execConfig.ProcessConfig, pipes, startCallback) 241 242 // On err, make sure we don't leave ExitCode at zero 243 if err != nil && exitStatus == 0 { 244 exitStatus = 128 245 } 246 247 execConfig.ExitCode = exitStatus 248 execConfig.Running = false 249 250 return exitStatus, err 251 } 252 253 // execCommandGC runs a ticker to clean up the daemon references 254 // of exec configs that are no longer part of the container. 255 func (d *Daemon) execCommandGC() { 256 for range time.Tick(5 * time.Minute) { 257 var ( 258 cleaned int 259 liveExecCommands = d.containerExecIds() 260 ) 261 for id, config := range d.execCommands.s { 262 if config.canRemove { 263 cleaned++ 264 d.execCommands.Delete(id) 265 } else { 266 if _, exists := liveExecCommands[id]; !exists { 267 config.canRemove = true 268 } 269 } 270 } 271 if cleaned > 0 { 272 logrus.Debugf("clean %d unused exec commands", cleaned) 273 } 274 } 275 } 276 277 // containerExecIds returns a list of all the current exec ids that are in use 278 // and running inside a container. 279 func (d *Daemon) containerExecIds() map[string]struct{} { 280 ids := map[string]struct{}{} 281 for _, c := range d.containers.List() { 282 for _, id := range c.execCommands.List() { 283 ids[id] = struct{}{} 284 } 285 } 286 return ids 287 }