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