mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-12 11:55:42 +00:00
We were closing resize both on the receiver and the sender side. This was racy as the sender might have written to a closed channel. If the container could not be created, the attach exited immediately causing the channel to be closed before the write from resizeTty. Change the logic to close only from the senderSide and add another channel to notify the resizeTty goroutine when the container exited. Closes: https://github.com/projectatomic/libpod/issues/785 Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com> Closes: #787 Approved by: mheon
173 lines
4.2 KiB
Go
173 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
gosignal "os/signal"
|
|
|
|
"github.com/docker/docker/pkg/signal"
|
|
"github.com/docker/docker/pkg/term"
|
|
"github.com/pkg/errors"
|
|
"github.com/projectatomic/libpod/libpod"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
"k8s.io/client-go/tools/remotecommand"
|
|
)
|
|
|
|
// Attach to a container
|
|
func attachCtr(ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool) error {
|
|
resize := make(chan remotecommand.TerminalSize)
|
|
|
|
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
|
|
|
// Check if we are attached to a terminal. If we are, generate resize
|
|
// events, and set the terminal to raw mode
|
|
if haveTerminal && ctr.Spec().Process.Terminal {
|
|
logrus.Debugf("Handling terminal attach")
|
|
|
|
resizeTerminate := make(chan interface{})
|
|
defer func() {
|
|
resizeTerminate <- true
|
|
close(resizeTerminate)
|
|
}()
|
|
resizeTty(resize, resizeTerminate)
|
|
|
|
oldTermState, err := term.SaveState(os.Stdin.Fd())
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to save terminal state")
|
|
}
|
|
|
|
term.SetRawTerminal(os.Stdin.Fd())
|
|
|
|
defer term.RestoreTerminal(os.Stdin.Fd(), oldTermState)
|
|
}
|
|
|
|
streams := new(libpod.AttachStreams)
|
|
streams.OutputStream = stdout
|
|
streams.ErrorStream = stderr
|
|
streams.InputStream = stdin
|
|
streams.AttachOutput = true
|
|
streams.AttachError = true
|
|
streams.AttachInput = true
|
|
|
|
if stdout == nil {
|
|
logrus.Debugf("Not attaching to stdout")
|
|
streams.AttachOutput = false
|
|
}
|
|
if stderr == nil {
|
|
logrus.Debugf("Not attaching to stderr")
|
|
streams.AttachError = false
|
|
}
|
|
if stdin == nil {
|
|
logrus.Debugf("Not attaching to stdin")
|
|
streams.AttachInput = false
|
|
}
|
|
|
|
if sigProxy {
|
|
ProxySignals(ctr)
|
|
}
|
|
|
|
return ctr.Attach(streams, detachKeys, resize)
|
|
}
|
|
|
|
// Start and attach to a container
|
|
func startAttachCtr(ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool) error {
|
|
resize := make(chan remotecommand.TerminalSize)
|
|
|
|
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
|
|
|
// Check if we are attached to a terminal. If we are, generate resize
|
|
// events, and set the terminal to raw mode
|
|
if haveTerminal && ctr.Spec().Process.Terminal {
|
|
logrus.Debugf("Handling terminal attach")
|
|
|
|
resizeTerminate := make(chan interface{})
|
|
defer func() {
|
|
resizeTerminate <- true
|
|
close(resizeTerminate)
|
|
}()
|
|
resizeTty(resize, resizeTerminate)
|
|
|
|
oldTermState, err := term.SaveState(os.Stdin.Fd())
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to save terminal state")
|
|
}
|
|
|
|
term.SetRawTerminal(os.Stdin.Fd())
|
|
|
|
defer term.RestoreTerminal(os.Stdin.Fd(), oldTermState)
|
|
}
|
|
|
|
streams := new(libpod.AttachStreams)
|
|
streams.OutputStream = stdout
|
|
streams.ErrorStream = stderr
|
|
streams.InputStream = stdin
|
|
streams.AttachOutput = true
|
|
streams.AttachError = true
|
|
streams.AttachInput = true
|
|
|
|
if stdout == nil {
|
|
logrus.Debugf("Not attaching to stdout")
|
|
streams.AttachOutput = false
|
|
}
|
|
if stderr == nil {
|
|
logrus.Debugf("Not attaching to stderr")
|
|
streams.AttachError = false
|
|
}
|
|
if stdin == nil {
|
|
logrus.Debugf("Not attaching to stdin")
|
|
streams.AttachInput = false
|
|
}
|
|
|
|
attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if sigProxy {
|
|
ProxySignals(ctr)
|
|
}
|
|
|
|
if stdout == nil && stderr == nil {
|
|
fmt.Printf("%s\n", ctr.ID())
|
|
}
|
|
|
|
err = <-attachChan
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper for prepareAttach - set up a goroutine to generate terminal resize events
|
|
func resizeTty(resize chan remotecommand.TerminalSize, resizeTerminate chan interface{}) {
|
|
sigchan := make(chan os.Signal, 1)
|
|
gosignal.Notify(sigchan, signal.SIGWINCH)
|
|
sendUpdate := func() {
|
|
winsize, err := term.GetWinsize(os.Stdin.Fd())
|
|
if err != nil {
|
|
logrus.Warnf("Could not get terminal size %v", err)
|
|
return
|
|
}
|
|
resize <- remotecommand.TerminalSize{
|
|
Width: winsize.Width,
|
|
Height: winsize.Height,
|
|
}
|
|
}
|
|
go func() {
|
|
defer close(resize)
|
|
// Update the terminal size immediately without waiting
|
|
// for a SIGWINCH to get the correct initial size.
|
|
sendUpdate()
|
|
for {
|
|
select {
|
|
case <-resizeTerminate:
|
|
return
|
|
|
|
case <-sigchan:
|
|
sendUpdate()
|
|
}
|
|
}
|
|
}()
|
|
}
|