github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/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 err = fmt.Errorf("Invalid escape keys (%s) provided", config.DetachKeys) 105 return "", err 106 } 107 } 108 109 execConfig := exec.NewConfig() 110 execConfig.OpenStdin = config.AttachStdin 111 execConfig.OpenStdout = config.AttachStdout 112 execConfig.OpenStderr = config.AttachStderr 113 execConfig.ContainerID = container.ID 114 execConfig.DetachKeys = keys 115 execConfig.Entrypoint = entrypoint 116 execConfig.Args = args 117 execConfig.Tty = config.Tty 118 execConfig.Privileged = config.Privileged 119 execConfig.User = config.User 120 if len(execConfig.User) == 0 { 121 execConfig.User = container.Config.User 122 } 123 124 d.registerExecCommand(container, execConfig) 125 126 d.LogContainerEvent(container, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")) 127 128 return execConfig.ID, nil 129 } 130 131 // ContainerExecStart starts a previously set up exec instance. The 132 // std streams are set up. 133 func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) (err error) { 134 var ( 135 cStdin io.ReadCloser 136 cStdout, cStderr io.Writer 137 ) 138 139 ec, err := d.getExecConfig(name) 140 if err != nil { 141 return errExecNotFound(name) 142 } 143 144 ec.Lock() 145 if ec.ExitCode != nil { 146 ec.Unlock() 147 err := fmt.Errorf("Error: Exec command %s has already run", ec.ID) 148 return errors.NewRequestConflictError(err) 149 } 150 151 if ec.Running { 152 ec.Unlock() 153 return fmt.Errorf("Error: Exec command %s is already running", ec.ID) 154 } 155 ec.Running = true 156 defer func() { 157 if err != nil { 158 ec.Running = false 159 exitCode := 126 160 ec.ExitCode = &exitCode 161 } 162 }() 163 ec.Unlock() 164 165 c := d.containers.Get(ec.ContainerID) 166 logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID) 167 d.LogContainerEvent(c, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " ")) 168 169 if ec.OpenStdin && stdin != nil { 170 r, w := io.Pipe() 171 go func() { 172 defer w.Close() 173 defer logrus.Debugf("Closing buffered stdin pipe") 174 pools.Copy(w, stdin) 175 }() 176 cStdin = r 177 } 178 if ec.OpenStdout { 179 cStdout = stdout 180 } 181 if ec.OpenStderr { 182 cStderr = stderr 183 } 184 185 if ec.OpenStdin { 186 ec.NewInputPipes() 187 } else { 188 ec.NewNopInputPipe() 189 } 190 191 p := libcontainerd.Process{ 192 Args: append([]string{ec.Entrypoint}, ec.Args...), 193 Terminal: ec.Tty, 194 } 195 196 if err := execSetPlatformOpt(c, ec, &p); err != nil { 197 return nil 198 } 199 200 attachErr := container.AttachStreams(context.Background(), ec.StreamConfig, ec.OpenStdin, true, ec.Tty, cStdin, cStdout, cStderr, ec.DetachKeys) 201 202 if err := d.containerd.AddProcess(c.ID, name, p); err != nil { 203 return err 204 } 205 206 err = <-attachErr 207 if err != nil { 208 return fmt.Errorf("attach failed with error: %v", err) 209 } 210 return nil 211 } 212 213 // execCommandGC runs a ticker to clean up the daemon references 214 // of exec configs that are no longer part of the container. 215 func (d *Daemon) execCommandGC() { 216 for range time.Tick(5 * time.Minute) { 217 var ( 218 cleaned int 219 liveExecCommands = d.containerExecIds() 220 ) 221 for id, config := range d.execCommands.Commands() { 222 if config.CanRemove { 223 cleaned++ 224 d.execCommands.Delete(id) 225 } else { 226 if _, exists := liveExecCommands[id]; !exists { 227 config.CanRemove = true 228 } 229 } 230 } 231 if cleaned > 0 { 232 logrus.Debugf("clean %d unused exec commands", cleaned) 233 } 234 } 235 } 236 237 // containerExecIds returns a list of all the current exec ids that are in use 238 // and running inside a container. 239 func (d *Daemon) containerExecIds() map[string]struct{} { 240 ids := map[string]struct{}{} 241 for _, c := range d.containers.List() { 242 for _, id := range c.ExecCommands.List() { 243 ids[id] = struct{}{} 244 } 245 } 246 return ids 247 }