func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { err := httputils.ParseForm(r) if err != nil { return err } containerName := vars["name"] _, upgrade := r.Header["Upgrade"] keys := []byte{} detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{ Hijacker: w.(http.Hijacker), Upgrade: upgrade, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, } return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig) }
func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { err := httputils.ParseForm(r) if err != nil { return err } containerName := vars["name"] _, upgrade := r.Header["Upgrade"] keys := []byte{} detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } hijacker, ok := w.(http.Hijacker) if !ok { return derr.ErrorCodeNoHijackConnection.WithArgs(containerName) } setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { conn, _, err := hijacker.Hijack() if err != nil { return nil, nil, nil, err } // set raw mode conn.Write([]byte{}) if upgrade { fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") } else { fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") } closer := func() error { httputils.CloseStreams(conn) return nil } return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil } attachConfig := &backend.ContainerAttachConfig{ GetStreams: setupStreams, UseStdin: httputils.BoolValue(r, "stdin"), UseStdout: httputils.BoolValue(r, "stdout"), UseStderr: httputils.BoolValue(r, "stderr"), Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, MuxStreams: true, } return s.backend.ContainerAttach(containerName, attachConfig) }
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] var keys []byte var err error detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } done := make(chan struct{}) started := make(chan struct{}) setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { wsChan := make(chan *websocket.Conn) h := func(conn *websocket.Conn) { wsChan <- conn <-done } srv := websocket.Server{Handler: h, Handshake: nil} go func() { close(started) srv.ServeHTTP(w, r) }() conn := <-wsChan return conn, conn, conn, nil } attachConfig := &backend.ContainerAttachConfig{ GetStreams: setupStreams, Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, UseStdin: true, UseStdout: true, UseStderr: true, MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr } err = s.backend.ContainerAttach(containerName, attachConfig) close(done) select { case <-started: logrus.Errorf("Error attaching websocket: %s", err) return nil default: } return err }
// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig. func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error { keys := []byte{} var err error if c.DetachKeys != "" { keys, err = term.ToBytes(c.DetachKeys) if err != nil { return fmt.Errorf("Invalid escape keys (%s) provided", c.DetachKeys) } } container, err := daemon.GetContainer(prefixOrName) if err != nil { return err } if container.IsPaused() { err := fmt.Errorf("Container %s is paused. Unpause the container before attach", prefixOrName) return errors.NewRequestConflictError(err) } inStream, outStream, errStream, err := c.GetStreams() if err != nil { return err } defer inStream.Close() if !container.Config.Tty && c.MuxStreams { errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr) outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } var stdin io.ReadCloser var stdout, stderr io.Writer if c.UseStdin { stdin = inStream } if c.UseStdout { stdout = outStream } if c.UseStderr { stderr = errStream } if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, keys); err != nil { fmt.Fprintf(outStream, "Error attaching: %s\n", err) } return nil }
// ContainerExecCreate sets up an exec in a running container. func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (string, error) { container, err := d.getActiveContainer(name) if err != nil { return "", err } cmd := strslice.StrSlice(config.Cmd) entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmd) keys := []byte{} if config.DetachKeys != "" { keys, err = term.ToBytes(config.DetachKeys) if err != nil { err = fmt.Errorf("Invalid escape keys (%s) provided", config.DetachKeys) return "", err } } execConfig := exec.NewConfig() execConfig.OpenStdin = config.AttachStdin execConfig.OpenStdout = config.AttachStdout execConfig.OpenStderr = config.AttachStderr execConfig.ContainerID = container.ID execConfig.DetachKeys = keys execConfig.Entrypoint = entrypoint execConfig.Args = args execConfig.Tty = config.Tty execConfig.Privileged = config.Privileged execConfig.User = config.User execConfig.Env = []string{ "PATH=" + system.DefaultPathEnv, } if config.Tty { execConfig.Env = append(execConfig.Env, "TERM=xterm") } execConfig.Env = utils.ReplaceOrAppendEnvValues(execConfig.Env, container.Config.Env) if len(execConfig.User) == 0 { execConfig.User = container.Config.User } d.registerExecCommand(container, execConfig) d.LogContainerEvent(container, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")) return execConfig.ID, nil }
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } containerName := vars["name"] if !s.backend.Exists(containerName) { return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) } var keys []byte var err error detachKeys := r.FormValue("detachKeys") if detachKeys != "" { keys, err = term.ToBytes(detachKeys) if err != nil { logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys) } } h := websocket.Handler(func(ws *websocket.Conn) { defer ws.Close() wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{ InStream: ws, OutStream: ws, ErrStream: ws, Logs: httputils.BoolValue(r, "logs"), Stream: httputils.BoolValue(r, "stream"), DetachKeys: keys, } if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil { logrus.Errorf("Error attaching websocket: %s", utils.GetErrorMessage(err)) } }) ws := websocket.Server{Handler: h, Handshake: nil} ws.ServeHTTP(w, r) return nil }
// ContainerExecCreate sets up an exec in a running container. func (d *Daemon) ContainerExecCreate(config *types.ExecConfig) (string, error) { container, err := d.getActiveContainer(config.Container) if err != nil { return "", err } cmd := strslice.New(config.Cmd...) entrypoint, args := d.getEntrypointAndArgs(strslice.New(), cmd) keys := []byte{} if config.DetachKeys != "" { keys, err = term.ToBytes(config.DetachKeys) if err != nil { logrus.Warnf("Wrong escape keys provided (%s, error: %s) using default : ctrl-p ctrl-q", config.DetachKeys, err.Error()) } } processConfig := &execdriver.ProcessConfig{ CommonProcessConfig: execdriver.CommonProcessConfig{ Tty: config.Tty, Entrypoint: entrypoint, Arguments: args, }, } setPlatformSpecificExecProcessConfig(config, container, processConfig) execConfig := exec.NewConfig() execConfig.OpenStdin = config.AttachStdin execConfig.OpenStdout = config.AttachStdout execConfig.OpenStderr = config.AttachStderr execConfig.ProcessConfig = processConfig execConfig.ContainerID = container.ID execConfig.DetachKeys = keys d.registerExecCommand(container, execConfig) d.LogContainerEvent(container, "exec_create: "+execConfig.ProcessConfig.Entrypoint+" "+strings.Join(execConfig.ProcessConfig.Arguments, " ")) return execConfig.ID, nil }
// ContainerExecCreate sets up an exec in a running container. func (d *Daemon) ContainerExecCreate(config *types.ExecConfig) (string, error) { container, err := d.getActiveContainer(config.Container) if err != nil { return "", err } cmd := strslice.StrSlice(config.Cmd) entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmd) keys := []byte{} if config.DetachKeys != "" { keys, err = term.ToBytes(config.DetachKeys) if err != nil { logrus.Warnf("Wrong escape keys provided (%s, error: %s) using default : ctrl-p ctrl-q", config.DetachKeys, err.Error()) } } execConfig := exec.NewConfig() execConfig.OpenStdin = config.AttachStdin execConfig.OpenStdout = config.AttachStdout execConfig.OpenStderr = config.AttachStderr execConfig.ContainerID = container.ID execConfig.DetachKeys = keys execConfig.Entrypoint = entrypoint execConfig.Args = args execConfig.Tty = config.Tty execConfig.Privileged = config.Privileged execConfig.User = config.User if len(execConfig.User) == 0 { execConfig.User = container.Config.User } d.registerExecCommand(container, execConfig) d.LogContainerEvent(container, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")) return execConfig.ID, nil }