func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, execCmd []string) error { execConfig, err := parseExec(opts, container, execCmd) // just in case the ParseExec does not exit if container == "" || err != nil { return cli.StatusError{StatusCode: 1} } if opts.detachKeys != "" { dockerCli.ConfigFile().DetachKeys = opts.detachKeys } // Send client escape keys execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys ctx := context.Background() response, err := dockerCli.Client().ContainerExecCreate(ctx, container, *execConfig) if err != nil { return err } execID := response.ID if execID == "" { fmt.Fprintf(dockerCli.Out(), "exec ID empty") return nil } //Temp struct for execStart so that we don't need to transfer all the execConfig if !execConfig.Detach { if err := dockerCli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { return err } } else { execStartCheck := types.ExecStartCheck{ Detach: execConfig.Detach, Tty: execConfig.Tty, } if err := dockerCli.Client().ContainerExecStart(ctx, execID, execStartCheck); err != nil { return err } // For now don't print this - wait for when we support exec wait() // fmt.Fprintf(dockerCli.Out(), "%s\n", execID) return nil } // Interactive exec requested. var ( out, stderr io.Writer in io.ReadCloser errCh chan error ) if execConfig.AttachStdin { in = dockerCli.In() } if execConfig.AttachStdout { out = dockerCli.Out() } if execConfig.AttachStderr { if execConfig.Tty { stderr = dockerCli.Out() } else { stderr = dockerCli.Err() } } resp, err := dockerCli.Client().ContainerExecAttach(ctx, execID, *execConfig) if err != nil { return err } defer resp.Close() errCh = promise.Go(func() error { return dockerCli.HoldHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp) }) if execConfig.Tty && dockerCli.IsTerminalIn() { if err := dockerCli.MonitorTtySize(ctx, execID, true); err != nil { fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) } } if err := <-errCh; err != nil { logrus.Debugf("Error hijack: %s", err) return err } var status int if _, status, err = dockerCli.GetExecExitCode(ctx, execID); err != nil { return err } if status != 0 { return cli.StatusError{StatusCode: status} } return nil }