Fix resize race with podman exec -it
When starting a process with `podman exec -it` the terminal is resized after the process is started. To fix this allow exec start to accept the terminal height and width as parameter and let it resize right before the process is started. Fixes #10560 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
parent
e2f51eeb06
commit
666f555aa5
|
@ -277,9 +277,10 @@ func (c *Container) ExecStart(sessionID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecStartAndAttach starts and attaches to an exec session in a container.
|
// ExecStartAndAttach starts and attaches to an exec session in a container.
|
||||||
|
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
|
||||||
// TODO: Should we include detach keys in the signature to allow override?
|
// TODO: Should we include detach keys in the signature to allow override?
|
||||||
// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
|
// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
|
||||||
func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams) error {
|
func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *define.TerminalSize) error {
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
@ -310,7 +311,7 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pid, attachChan, err := c.ociRuntime.ExecContainer(c, session.ID(), opts, streams)
|
pid, attachChan, err := c.ociRuntime.ExecContainer(c, session.ID(), opts, streams, newSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -373,7 +374,9 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecHTTPStartAndAttach starts and performs an HTTP attach to an exec session.
|
// ExecHTTPStartAndAttach starts and performs an HTTP attach to an exec session.
|
||||||
func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool) error {
|
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
|
||||||
|
func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w http.ResponseWriter,
|
||||||
|
streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, newSize *define.TerminalSize) error {
|
||||||
// TODO: How do we combine streams with the default streams set in the exec session?
|
// TODO: How do we combine streams with the default streams set in the exec session?
|
||||||
|
|
||||||
// Ensure that we don't leak a goroutine here
|
// Ensure that we don't leak a goroutine here
|
||||||
|
@ -431,7 +434,7 @@ func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w
|
||||||
close(holdConnOpen)
|
close(holdConnOpen)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
pid, attachChan, err := c.ociRuntime.ExecContainerHTTP(c, session.ID(), execOpts, r, w, streams, cancel, hijackDone, holdConnOpen)
|
pid, attachChan, err := c.ociRuntime.ExecContainerHTTP(c, session.ID(), execOpts, r, w, streams, cancel, hijackDone, holdConnOpen, newSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.State = define.ExecStateStopped
|
session.State = define.ExecStateStopped
|
||||||
session.ExitCode = define.TranslateExecErrorToExitCode(define.ExecErrorCodeGeneric, err)
|
session.ExitCode = define.TranslateExecErrorToExitCode(define.ExecErrorCodeGeneric, err)
|
||||||
|
@ -719,7 +722,10 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
|
||||||
// API there.
|
// API there.
|
||||||
// TODO: Refactor so this is closed here, before we remove the exec
|
// TODO: Refactor so this is closed here, before we remove the exec
|
||||||
// session.
|
// session.
|
||||||
|
var size *define.TerminalSize
|
||||||
if resize != nil {
|
if resize != nil {
|
||||||
|
s := <-resize
|
||||||
|
size = &s
|
||||||
go func() {
|
go func() {
|
||||||
logrus.Debugf("Sending resize events to exec session %s", sessionID)
|
logrus.Debugf("Sending resize events to exec session %s", sessionID)
|
||||||
for resizeRequest := range resize {
|
for resizeRequest := range resize {
|
||||||
|
@ -737,7 +743,7 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ExecStartAndAttach(sessionID, streams); err != nil {
|
if err := c.ExecStartAndAttach(sessionID, streams, size); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,13 +72,16 @@ type OCIRuntime interface {
|
||||||
// has completed, as one might expect. The attach session will remain
|
// has completed, as one might expect. The attach session will remain
|
||||||
// running, in a goroutine that will return via the chan error in the
|
// running, in a goroutine that will return via the chan error in the
|
||||||
// return signature.
|
// return signature.
|
||||||
ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams) (int, chan error, error)
|
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
|
||||||
|
ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error)
|
||||||
// ExecContainerHTTP executes a command in a running container and
|
// ExecContainerHTTP executes a command in a running container and
|
||||||
// attaches its standard streams to a provided hijacked HTTP session.
|
// attaches its standard streams to a provided hijacked HTTP session.
|
||||||
// Maintains the same invariants as ExecContainer (returns on session
|
// Maintains the same invariants as ExecContainer (returns on session
|
||||||
// start, with a goroutine running in the background to handle attach).
|
// start, with a goroutine running in the background to handle attach).
|
||||||
// The HTTP attach itself maintains the same invariants as HTTPAttach.
|
// The HTTP attach itself maintains the same invariants as HTTPAttach.
|
||||||
ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (int, chan error, error)
|
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
|
||||||
|
ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, r *http.Request, w http.ResponseWriter,
|
||||||
|
streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error)
|
||||||
// ExecContainerDetached executes a command in a running container, but
|
// ExecContainerDetached executes a command in a running container, but
|
||||||
// does not attach to it. Returns the PID of the exec session and an
|
// does not attach to it. Returns the PID of the exec session and an
|
||||||
// error (if starting the exec session failed)
|
// error (if starting the exec session failed)
|
||||||
|
|
|
@ -94,17 +94,18 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-
|
||||||
// this ensures attachToExec gets all of the output of the called process
|
// this ensures attachToExec gets all of the output of the called process
|
||||||
// conmon will then send the exit code of the exec process, or an error in the exec session
|
// conmon will then send the exit code of the exec process, or an error in the exec session
|
||||||
// startFd must be the input side of the fd.
|
// startFd must be the input side of the fd.
|
||||||
|
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
|
||||||
// conmon will wait to start the exec session until the parent process has setup the console socket.
|
// conmon will wait to start the exec session until the parent process has setup the console socket.
|
||||||
// Once attachToExec successfully attaches to the console socket, the child conmon process responsible for calling runtime exec
|
// Once attachToExec successfully attaches to the console socket, the child conmon process responsible for calling runtime exec
|
||||||
// will read from the output side of start fd, thus learning to start the child process.
|
// will read from the output side of start fd, thus learning to start the child process.
|
||||||
// Thus, the order goes as follow:
|
// Thus, the order goes as follow:
|
||||||
// 1. conmon parent process sets up its console socket. sends on attachFd
|
// 1. conmon parent process sets up its console socket. sends on attachFd
|
||||||
// 2. attachToExec attaches to the console socket after reading on attachFd
|
// 2. attachToExec attaches to the console socket after reading on attachFd and resizes the tty
|
||||||
// 3. child waits on startFd for attachToExec to attach to said console socket
|
// 3. child waits on startFd for attachToExec to attach to said console socket
|
||||||
// 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go
|
// 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go
|
||||||
// 5. child receives on startFd, runs the runtime exec command
|
// 5. child receives on startFd, runs the runtime exec command
|
||||||
// attachToExec is responsible for closing startFd and attachFd
|
// attachToExec is responsible for closing startFd and attachFd
|
||||||
func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
|
func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File, newSize *define.TerminalSize) error {
|
||||||
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
|
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
|
||||||
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
||||||
}
|
}
|
||||||
|
@ -137,6 +138,14 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resize before we start the container process
|
||||||
|
if newSize != nil {
|
||||||
|
err = c.ociRuntime.ExecAttachResize(c, sessionID, *newSize)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn("resize failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2: then attach
|
// 2: then attach
|
||||||
conn, err := openUnixSocket(sockPath)
|
conn, err := openUnixSocket(sockPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecContainer executes a command in a running container
|
// ExecContainer executes a command in a running container
|
||||||
func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams) (int, chan error, error) {
|
func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) {
|
||||||
if options == nil {
|
if options == nil {
|
||||||
return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer")
|
return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer")
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options
|
||||||
attachChan := make(chan error)
|
attachChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
// attachToExec is responsible for closing pipes
|
// attachToExec is responsible for closing pipes
|
||||||
attachChan <- c.attachToExec(streams, options.DetachKeys, sessionID, pipes.startPipe, pipes.attachPipe)
|
attachChan <- c.attachToExec(streams, options.DetachKeys, sessionID, pipes.startPipe, pipes.attachPipe, newSize)
|
||||||
close(attachChan)
|
close(attachChan)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -83,7 +83,8 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options
|
||||||
|
|
||||||
// ExecContainerHTTP executes a new command in an existing container and
|
// ExecContainerHTTP executes a new command in an existing container and
|
||||||
// forwards its standard streams over an attach
|
// forwards its standard streams over an attach
|
||||||
func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (int, chan error, error) {
|
func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter,
|
||||||
|
streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) {
|
||||||
if streams != nil {
|
if streams != nil {
|
||||||
if !streams.Stdin && !streams.Stdout && !streams.Stderr {
|
if !streams.Stdin && !streams.Stdout && !streams.Stderr {
|
||||||
return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
||||||
|
@ -133,7 +134,7 @@ func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, o
|
||||||
conmonPipeDataChan := make(chan conmonPipeData)
|
conmonPipeDataChan := make(chan conmonPipeData)
|
||||||
go func() {
|
go func() {
|
||||||
// attachToExec is responsible for closing pipes
|
// attachToExec is responsible for closing pipes
|
||||||
attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog)
|
attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog, newSize)
|
||||||
close(attachChan)
|
close(attachChan)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -486,7 +487,7 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to a container over HTTP
|
// Attach to a container over HTTP
|
||||||
func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string) (deferredErr error) {
|
func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string, newSize *define.TerminalSize) (deferredErr error) {
|
||||||
// NOTE: As you may notice, the attach code is quite complex.
|
// NOTE: As you may notice, the attach code is quite complex.
|
||||||
// Many things happen concurrently and yet are interdependent.
|
// Many things happen concurrently and yet are interdependent.
|
||||||
// If you ever change this function, make sure to write to the
|
// If you ever change this function, make sure to write to the
|
||||||
|
@ -524,6 +525,14 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resize before we start the container process
|
||||||
|
if newSize != nil {
|
||||||
|
err = c.ociRuntime.ExecAttachResize(c, sessionID, *newSize)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn("resize failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2: then attach
|
// 2: then attach
|
||||||
conn, err := openUnixSocket(sockPath)
|
conn, err := openUnixSocket(sockPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -119,12 +119,13 @@ func (r *MissingRuntime) AttachResize(ctr *Container, newSize define.TerminalSiz
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecContainer is not available as the runtime is missing
|
// ExecContainer is not available as the runtime is missing
|
||||||
func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams) (int, chan error, error) {
|
func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) {
|
||||||
return -1, nil, r.printError()
|
return -1, nil, r.printError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecContainerHTTP is not available as the runtime is missing
|
// ExecContainerHTTP is not available as the runtime is missing
|
||||||
func (r *MissingRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (int, chan error, error) {
|
func (r *MissingRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter,
|
||||||
|
streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) {
|
||||||
return -1, nil, r.printError()
|
return -1, nil, r.printError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,8 +178,16 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
logrus.Error(errors.Wrapf(e, "error attaching to container %s exec session %s", sessionCtr.ID(), sessionID))
|
logrus.Error(errors.Wrapf(e, "error attaching to container %s exec session %s", sessionCtr.ID(), sessionID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var size *define.TerminalSize
|
||||||
|
if bodyParams.Tty && (bodyParams.Height > 0 || bodyParams.Width > 0) {
|
||||||
|
size = &define.TerminalSize{
|
||||||
|
Height: bodyParams.Height,
|
||||||
|
Width: bodyParams.Width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hijackChan := make(chan bool, 1)
|
hijackChan := make(chan bool, 1)
|
||||||
err = sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan)
|
err = sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan, size)
|
||||||
|
|
||||||
if <-hijackChan {
|
if <-hijackChan {
|
||||||
// If connection was Hijacked, we have to signal it's being closed
|
// If connection was Hijacked, we have to signal it's being closed
|
||||||
|
|
|
@ -73,7 +73,7 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := ctnr.ExecResize(name, sz); err != nil {
|
if err := ctnr.ExecResize(name, sz); err != nil {
|
||||||
if errors.Cause(err) != define.ErrCtrStateInvalid || !query.IgnoreNotRunning {
|
if errors.Cause(err) != define.ErrExecSessionStateInvalid || !query.IgnoreNotRunning {
|
||||||
utils.InternalServerError(w, errors.Wrapf(err, "cannot resize session"))
|
utils.InternalServerError(w, errors.Wrapf(err, "cannot resize session"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,8 +157,10 @@ type ExecCreateResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecStartConfig struct {
|
type ExecStartConfig struct {
|
||||||
Detach bool `json:"Detach"`
|
Detach bool `json:"Detach"`
|
||||||
Tty bool `json:"Tty"`
|
Tty bool `json:"Tty"`
|
||||||
|
Height uint16 `json:"h"`
|
||||||
|
Width uint16 `json:"w"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) {
|
func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) {
|
||||||
|
|
|
@ -269,10 +269,16 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
|
||||||
// properties:
|
// properties:
|
||||||
// Detach:
|
// Detach:
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// description: Detach from the command. Not presently supported.
|
// description: Detach from the command.
|
||||||
// Tty:
|
// Tty:
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// description: Allocate a pseudo-TTY. Presently ignored.
|
// description: Allocate a pseudo-TTY.
|
||||||
|
// h:
|
||||||
|
// type: integer
|
||||||
|
// description: Height of the TTY session in characters. Tty must be set to true to use it.
|
||||||
|
// w:
|
||||||
|
// type: integer
|
||||||
|
// description: Width of the TTY session in characters. Tty must be set to true to use it.
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// responses:
|
// responses:
|
||||||
|
|
|
@ -343,7 +343,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i
|
||||||
resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w))
|
resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w))
|
||||||
}
|
}
|
||||||
if resizeErr != nil {
|
if resizeErr != nil {
|
||||||
logrus.Warnf("failed to resize TTY: %v", resizeErr)
|
logrus.Infof("failed to resize TTY: %v", resizeErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,6 +408,17 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
|
||||||
// If we are in TTY mode, we need to set raw mode for the terminal.
|
// If we are in TTY mode, we need to set raw mode for the terminal.
|
||||||
// TODO: Share all of this with Attach() for containers.
|
// TODO: Share all of this with Attach() for containers.
|
||||||
needTTY := terminalFile != nil && terminal.IsTerminal(int(terminalFile.Fd())) && isTerm
|
needTTY := terminalFile != nil && terminal.IsTerminal(int(terminalFile.Fd())) && isTerm
|
||||||
|
|
||||||
|
body := struct {
|
||||||
|
Detach bool `json:"Detach"`
|
||||||
|
TTY bool `json:"Tty"`
|
||||||
|
Height uint16 `json:"h"`
|
||||||
|
Width uint16 `json:"w"`
|
||||||
|
}{
|
||||||
|
Detach: false,
|
||||||
|
TTY: needTTY,
|
||||||
|
}
|
||||||
|
|
||||||
if needTTY {
|
if needTTY {
|
||||||
state, err := setRawTerminal(terminalFile)
|
state, err := setRawTerminal(terminalFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -419,13 +430,14 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
|
||||||
}
|
}
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||||
}()
|
}()
|
||||||
|
w, h, err := terminal.GetSize(int(terminalFile.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("failed to obtain TTY size: %v", err)
|
||||||
|
}
|
||||||
|
body.Width = uint16(w)
|
||||||
|
body.Height = uint16(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := struct {
|
|
||||||
Detach bool `json:"Detach"`
|
|
||||||
}{
|
|
||||||
Detach: false,
|
|
||||||
}
|
|
||||||
bodyJSON, err := json.Marshal(body)
|
bodyJSON, err := json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -15,12 +15,13 @@ import (
|
||||||
|
|
||||||
// ExecAttachCtr execs and attaches to a container
|
// ExecAttachCtr execs and attaches to a container
|
||||||
func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) {
|
func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) {
|
||||||
resize := make(chan define.TerminalSize)
|
var resize chan define.TerminalSize
|
||||||
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||||
|
|
||||||
// Check if we are attached to a terminal. If we are, generate resize
|
// Check if we are attached to a terminal. If we are, generate resize
|
||||||
// events, and set the terminal to raw mode
|
// events, and set the terminal to raw mode
|
||||||
if haveTerminal && execConfig.Terminal {
|
if haveTerminal && execConfig.Terminal {
|
||||||
|
resize = make(chan define.TerminalSize)
|
||||||
cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
|
cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -32,7 +33,6 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctr.Exec(execConfig, streams, resize)
|
return ctr.Exec(execConfig, streams, resize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,16 @@ function teardown() {
|
||||||
|
|
||||||
# ...and make sure stty under podman reads that.
|
# ...and make sure stty under podman reads that.
|
||||||
run_podman run -it --name mystty $IMAGE stty size <$PODMAN_TEST_PTY
|
run_podman run -it --name mystty $IMAGE stty size <$PODMAN_TEST_PTY
|
||||||
is "$output" "$rows $cols" "stty under podman reads the correct dimensions"
|
is "$output" "$rows $cols" "stty under podman run reads the correct dimensions"
|
||||||
|
|
||||||
|
run_podman rm -f mystty
|
||||||
|
|
||||||
|
# check that the same works for podman exec
|
||||||
|
run_podman run -d --name mystty $IMAGE top
|
||||||
|
run_podman exec -it mystty stty size <$PODMAN_TEST_PTY
|
||||||
|
is "$output" "$rows $cols" "stty under podman exec reads the correct dimensions"
|
||||||
|
|
||||||
|
run_podman rm -f mystty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue