github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/api/server/router/container/exec.go (about) 1 package container // import "github.com/docker/docker/api/server/router/container" 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net/http" 10 "strconv" 11 12 "github.com/docker/docker/api/server/httputils" 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/versions" 15 "github.com/docker/docker/errdefs" 16 "github.com/docker/docker/pkg/stdcopy" 17 "github.com/sirupsen/logrus" 18 ) 19 20 func (s *containerRouter) getExecByID(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 21 eConfig, err := s.backend.ContainerExecInspect(vars["id"]) 22 if err != nil { 23 return err 24 } 25 26 return httputils.WriteJSON(w, http.StatusOK, eConfig) 27 } 28 29 type execCommandError struct{} 30 31 func (execCommandError) Error() string { 32 return "No exec command specified" 33 } 34 35 func (execCommandError) InvalidParameter() {} 36 37 func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 38 if err := httputils.ParseForm(r); err != nil { 39 return err 40 } 41 if err := httputils.CheckForJSON(r); err != nil { 42 return err 43 } 44 name := vars["name"] 45 46 execConfig := &types.ExecConfig{} 47 if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil { 48 if err == io.EOF { 49 return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) 50 } 51 return errdefs.InvalidParameter(err) 52 } 53 54 if len(execConfig.Cmd) == 0 { 55 return execCommandError{} 56 } 57 58 // Register an instance of Exec in container. 59 id, err := s.backend.ContainerExecCreate(name, execConfig) 60 if err != nil { 61 logrus.Errorf("Error setting up exec command in container %s: %v", name, err) 62 return err 63 } 64 65 return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ 66 ID: id, 67 }) 68 } 69 70 // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. 71 func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 72 if err := httputils.ParseForm(r); err != nil { 73 return err 74 } 75 76 version := httputils.VersionFromContext(ctx) 77 if versions.GreaterThan(version, "1.21") { 78 if err := httputils.CheckForJSON(r); err != nil { 79 return err 80 } 81 } 82 83 var ( 84 execName = vars["name"] 85 stdin, inStream io.ReadCloser 86 stdout, stderr, outStream io.Writer 87 ) 88 89 execStartCheck := &types.ExecStartCheck{} 90 if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil { 91 if err == io.EOF { 92 return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) 93 } 94 return errdefs.InvalidParameter(err) 95 } 96 97 if exists, err := s.backend.ExecExists(execName); !exists { 98 return err 99 } 100 101 if !execStartCheck.Detach { 102 var err error 103 // Setting up the streaming http interface. 104 inStream, outStream, err = httputils.HijackConnection(w) 105 if err != nil { 106 return err 107 } 108 defer httputils.CloseStreams(inStream, outStream) 109 110 if _, ok := r.Header["Upgrade"]; ok { 111 fmt.Fprint(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n") 112 } else { 113 fmt.Fprint(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n") 114 } 115 116 // copy headers that were removed as part of hijack 117 if err := w.Header().WriteSubset(outStream, nil); err != nil { 118 return err 119 } 120 fmt.Fprint(outStream, "\r\n") 121 122 stdin = inStream 123 stdout = outStream 124 if !execStartCheck.Tty { 125 stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) 126 stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) 127 } 128 } 129 130 // Now run the user process in container. 131 // Maybe we should we pass ctx here if we're not detaching? 132 if err := s.backend.ContainerExecStart(context.Background(), execName, stdin, stdout, stderr); err != nil { 133 if execStartCheck.Detach { 134 return err 135 } 136 stdout.Write([]byte(err.Error() + "\r\n")) 137 logrus.Errorf("Error running exec %s in container: %v", execName, err) 138 } 139 return nil 140 } 141 142 func (s *containerRouter) postContainerExecResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 143 if err := httputils.ParseForm(r); err != nil { 144 return err 145 } 146 height, err := strconv.Atoi(r.Form.Get("h")) 147 if err != nil { 148 return errdefs.InvalidParameter(err) 149 } 150 width, err := strconv.Atoi(r.Form.Get("w")) 151 if err != nil { 152 return errdefs.InvalidParameter(err) 153 } 154 155 return s.backend.ContainerExecResize(vars["name"], height, width) 156 }