github.com/amylindburg/docker@v1.7.0/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 113 // Not all drivers support Exec (LXC for example) 114 if err := checkExecSupport(d.execDriver.Name()); err != nil { 115 return "", err 116 } 117 118 container, err := d.getActiveContainer(config.Container) 119 if err != nil { 120 return "", err 121 } 122 123 cmd := runconfig.NewCommand(config.Cmd...) 124 entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd) 125 126 processConfig := execdriver.ProcessConfig{ 127 Tty: config.Tty, 128 Entrypoint: entrypoint, 129 Arguments: args, 130 User: config.User, 131 } 132 133 execConfig := &execConfig{ 134 ID: stringid.GenerateRandomID(), 135 OpenStdin: config.AttachStdin, 136 OpenStdout: config.AttachStdout, 137 OpenStderr: config.AttachStderr, 138 StreamConfig: StreamConfig{}, 139 ProcessConfig: processConfig, 140 Container: container, 141 Running: false, 142 } 143 144 container.LogEvent("exec_create: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " ")) 145 146 d.registerExecCommand(execConfig) 147 148 return execConfig.ID, nil 149 150 } 151 152 func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error { 153 154 var ( 155 cStdin io.ReadCloser 156 cStdout, cStderr io.Writer 157 ) 158 159 execConfig, err := d.getExecConfig(execName) 160 if err != nil { 161 return err 162 } 163 164 func() { 165 execConfig.Lock() 166 defer execConfig.Unlock() 167 if execConfig.Running { 168 err = fmt.Errorf("Error: Exec command %s is already running", execName) 169 } 170 execConfig.Running = true 171 }() 172 if err != nil { 173 return err 174 } 175 176 logrus.Debugf("starting exec command %s in container %s", execConfig.ID, execConfig.Container.ID) 177 container := execConfig.Container 178 179 container.LogEvent("exec_start: " + execConfig.ProcessConfig.Entrypoint + " " + strings.Join(execConfig.ProcessConfig.Arguments, " ")) 180 181 if execConfig.OpenStdin { 182 r, w := io.Pipe() 183 go func() { 184 defer w.Close() 185 defer logrus.Debugf("Closing buffered stdin pipe") 186 io.Copy(w, stdin) 187 }() 188 cStdin = r 189 } 190 if execConfig.OpenStdout { 191 cStdout = stdout 192 } 193 if execConfig.OpenStderr { 194 cStderr = stderr 195 } 196 197 execConfig.StreamConfig.stderr = broadcastwriter.New() 198 execConfig.StreamConfig.stdout = broadcastwriter.New() 199 // Attach to stdin 200 if execConfig.OpenStdin { 201 execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdinPipe = io.Pipe() 202 } else { 203 execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin 204 } 205 206 attachErr := attach(&execConfig.StreamConfig, execConfig.OpenStdin, true, execConfig.ProcessConfig.Tty, cStdin, cStdout, cStderr) 207 208 execErr := make(chan error) 209 210 // Note, the execConfig data will be removed when the container 211 // itself is deleted. This allows us to query it (for things like 212 // the exitStatus) even after the cmd is done running. 213 214 go func() { 215 if err := container.Exec(execConfig); err != nil { 216 execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err) 217 } 218 }() 219 220 select { 221 case err := <-attachErr: 222 if err != nil { 223 return fmt.Errorf("attach failed with error: %s", err) 224 } 225 break 226 case err := <-execErr: 227 return err 228 } 229 230 return nil 231 } 232 233 func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { 234 exitStatus, err := d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback) 235 236 // On err, make sure we don't leave ExitCode at zero 237 if err != nil && exitStatus == 0 { 238 exitStatus = 128 239 } 240 241 execConfig.ExitCode = exitStatus 242 execConfig.Running = false 243 244 return exitStatus, err 245 }