github.com/akerouanton/docker@v1.11.0-rc3/daemon/exec.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 "time" 8 9 "golang.org/x/net/context" 10 11 "github.com/Sirupsen/logrus" 12 "github.com/docker/docker/container" 13 "github.com/docker/docker/daemon/exec" 14 "github.com/docker/docker/errors" 15 "github.com/docker/docker/libcontainerd" 16 "github.com/docker/docker/pkg/pools" 17 "github.com/docker/docker/pkg/term" 18 "github.com/docker/engine-api/types" 19 "github.com/docker/engine-api/types/strslice" 20 ) 21 22 func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) { 23 // Storing execs in container in order to kill them gracefully whenever the container is stopped or removed. 24 container.ExecCommands.Add(config.ID, config) 25 // Storing execs in daemon for easy access via remote API. 26 d.execCommands.Add(config.ID, config) 27 } 28 29 // ExecExists looks up the exec instance and returns a bool if it exists or not. 30 // It will also return the error produced by `getConfig` 31 func (d *Daemon) ExecExists(name string) (bool, error) { 32 if _, err := d.getExecConfig(name); err != nil { 33 return false, err 34 } 35 return true, nil 36 } 37 38 // getExecConfig looks up the exec instance by name. If the container associated 39 // with the exec instance is stopped or paused, it will return an error. 40 func (d *Daemon) getExecConfig(name string) (*exec.Config, error) { 41 ec := d.execCommands.Get(name) 42 43 // If the exec is found but its container is not in the daemon's list of 44 // containers then it must have been deleted, in which case instead of 45 // saying the container isn't running, we should return a 404 so that 46 // the user sees the same error now that they will after the 47 // 5 minute clean-up loop is run which erases old/dead execs. 48 49 if ec != nil { 50 if container := d.containers.Get(ec.ContainerID); container != nil { 51 if !container.IsRunning() { 52 return nil, fmt.Errorf("Container %s is not running: %s", container.ID, container.State.String()) 53 } 54 if container.IsPaused() { 55 return nil, errExecPaused(container.ID) 56 } 57 if container.IsRestarting() { 58 return nil, errContainerIsRestarting(container.ID) 59 } 60 return ec, nil 61 } 62 } 63 64 return nil, errExecNotFound(name) 65 } 66 67 func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) { 68 container.ExecCommands.Delete(execConfig.ID) 69 d.execCommands.Delete(execConfig.ID) 70 } 71 72 func (d *Daemon) getActiveContainer(name string) (*container.Container, error) { 73 container, err := d.GetContainer(name) 74 if err != nil { 75 return nil, err 76 } 77 78 if !container.IsRunning() { 79 return nil, errNotRunning{container.ID} 80 } 81 if container.IsPaused() { 82 return nil, errExecPaused(name) 83 } 84 if container.IsRestarting() { 85 return nil, errContainerIsRestarting(container.ID) 86 } 87 return container, nil 88 } 89 90 // ContainerExecCreate sets up an exec in a running container. 91 func (d *Daemon) ContainerExecCreate(config *types.ExecConfig) (string, error) { 92 container, err := d.getActiveContainer(config.Container) 93 if err != nil { 94 return "", err 95 } 96 97 cmd := strslice.StrSlice(config.Cmd) 98 entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmd) 99 100 keys := []byte{} 101 if config.DetachKeys != "" { 102 keys, err = term.ToBytes(config.DetachKeys) 103 if err != nil { 104 logrus.Warnf("Wrong escape keys provided (%s, error: %s) using default : ctrl-p ctrl-q", config.DetachKeys, err.Error()) 105 } 106 } 107 108 execConfig := exec.NewConfig() 109 execConfig.OpenStdin = config.AttachStdin 110 execConfig.OpenStdout = config.AttachStdout 111 execConfig.OpenStderr = config.AttachStderr 112 execConfig.ContainerID = container.ID 113 execConfig.DetachKeys = keys 114 execConfig.Entrypoint = entrypoint 115 execConfig.Args = args 116 execConfig.Tty = config.Tty 117 execConfig.Privileged = config.Privileged 118 execConfig.User = config.User 119 if len(execConfig.User) == 0 { 120 execConfig.User = container.Config.User 121 } 122 123 d.registerExecCommand(container, execConfig) 124 125 d.LogContainerEvent(container, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")) 126 127 return execConfig.ID, nil 128 } 129 130 // ContainerExecStart starts a previously set up exec instance. The 131 // std streams are set up. 132 func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) (err error) { 133 var ( 134 cStdin io.ReadCloser 135 cStdout, cStderr io.Writer 136 ) 137 138 ec, err := d.getExecConfig(name) 139 if err != nil { 140 return errExecNotFound(name) 141 } 142 143 ec.Lock() 144 if ec.ExitCode != nil { 145 ec.Unlock() 146 err := fmt.Errorf("Error: Exec command %s has already run", ec.ID) 147 return errors.NewRequestConflictError(err) 148 } 149 150 if ec.Running { 151 ec.Unlock() 152 return fmt.Errorf("Error: Exec command %s is already running", ec.ID) 153 } 154 ec.Running = true 155 defer func() { 156 if err != nil { 157 ec.Running = false 158 exitCode := 126 159 ec.ExitCode = &exitCode 160 } 161 }() 162 ec.Unlock() 163 164 c := d.containers.Get(ec.ContainerID) 165 logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID) 166 d.LogContainerEvent(c, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " ")) 167 168 if ec.OpenStdin && stdin != nil { 169 r, w := io.Pipe() 170 go func() { 171 defer w.Close() 172 defer logrus.Debugf("Closing buffered stdin pipe") 173 pools.Copy(w, stdin) 174 }() 175 cStdin = r 176 } 177 if ec.OpenStdout { 178 cStdout = stdout 179 } 180 if ec.OpenStderr { 181 cStderr = stderr 182 } 183 184 if ec.OpenStdin { 185 ec.NewInputPipes() 186 } else { 187 ec.NewNopInputPipe() 188 } 189 190 p := libcontainerd.Process{ 191 Args: append([]string{ec.Entrypoint}, ec.Args...), 192 Terminal: ec.Tty, 193 } 194 195 if err := execSetPlatformOpt(c, ec, &p); err != nil { 196 return nil 197 } 198 199 attachErr := container.AttachStreams(context.Background(), ec.StreamConfig, ec.OpenStdin, true, ec.Tty, cStdin, cStdout, cStderr, ec.DetachKeys) 200 201 if err := d.containerd.AddProcess(c.ID, name, p); err != nil { 202 return err 203 } 204 205 err = <-attachErr 206 if err != nil { 207 return fmt.Errorf("attach failed with error: %v", err) 208 } 209 return nil 210 } 211 212 // execCommandGC runs a ticker to clean up the daemon references 213 // of exec configs that are no longer part of the container. 214 func (d *Daemon) execCommandGC() { 215 for range time.Tick(5 * time.Minute) { 216 var ( 217 cleaned int 218 liveExecCommands = d.containerExecIds() 219 ) 220 for id, config := range d.execCommands.Commands() { 221 if config.CanRemove { 222 cleaned++ 223 d.execCommands.Delete(id) 224 } else { 225 if _, exists := liveExecCommands[id]; !exists { 226 config.CanRemove = true 227 } 228 } 229 } 230 if cleaned > 0 { 231 logrus.Debugf("clean %d unused exec commands", cleaned) 232 } 233 } 234 } 235 236 // containerExecIds returns a list of all the current exec ids that are in use 237 // and running inside a container. 238 func (d *Daemon) containerExecIds() map[string]struct{} { 239 ids := map[string]struct{}{} 240 for _, c := range d.containers.List() { 241 for _, id := range c.ExecCommands.List() { 242 ids[id] = struct{}{} 243 } 244 } 245 return ids 246 }