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