mirror of https://github.com/containers/podman.git
Merge pull request #13494 from n1hility/fix-tty
Fixes TTY & resizing on Mac and Windows
This commit is contained in:
commit
f0039bf281
|
@ -10,14 +10,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/podman/v4/libpod/define"
|
"github.com/containers/podman/v4/libpod/define"
|
||||||
"github.com/containers/podman/v4/pkg/bindings"
|
"github.com/containers/podman/v4/pkg/bindings"
|
||||||
sig "github.com/containers/podman/v4/pkg/signal"
|
|
||||||
"github.com/containers/podman/v4/utils"
|
"github.com/containers/podman/v4/utils"
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -94,7 +92,8 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
|
||||||
|
|
||||||
// Unless all requirements are met, don't use "stdin" is a terminal
|
// Unless all requirements are met, don't use "stdin" is a terminal
|
||||||
file, ok := stdin.(*os.File)
|
file, ok := stdin.(*os.File)
|
||||||
needTTY := ok && terminal.IsTerminal(int(file.Fd())) && ctnr.Config.Tty
|
outFile, outOk := stdout.(*os.File)
|
||||||
|
needTTY := ok && outOk && terminal.IsTerminal(int(file.Fd())) && ctnr.Config.Tty
|
||||||
if needTTY {
|
if needTTY {
|
||||||
state, err := setRawTerminal(file)
|
state, err := setRawTerminal(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -142,11 +141,10 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
|
||||||
|
|
||||||
if needTTY {
|
if needTTY {
|
||||||
winChange := make(chan os.Signal, 1)
|
winChange := make(chan os.Signal, 1)
|
||||||
signal.Notify(winChange, sig.SIGWINCH)
|
|
||||||
winCtx, winCancel := context.WithCancel(ctx)
|
winCtx, winCancel := context.WithCancel(ctx)
|
||||||
defer winCancel()
|
defer winCancel()
|
||||||
|
notifyWinChange(winCtx, winChange, file, outFile)
|
||||||
attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file)
|
attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file, outFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are attaching around a start, we need to "signal"
|
// If we are attaching around a start, we need to "signal"
|
||||||
|
@ -345,9 +343,9 @@ func (f *rawFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||||
|
|
||||||
// This is intended to not be run as a goroutine, handling resizing for a container
|
// This is intended to not be run as a goroutine, handling resizing for a container
|
||||||
// or exec session. It will call resize once and then starts a goroutine which calls resize on winChange
|
// or exec session. It will call resize once and then starts a goroutine which calls resize on winChange
|
||||||
func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File) {
|
func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File, outFile *os.File) {
|
||||||
resize := func() {
|
resize := func() {
|
||||||
w, h, err := terminal.GetSize(int(file.Fd()))
|
w, h, err := getTermSize(file, outFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("Failed to obtain TTY size: %v", err)
|
logrus.Warnf("Failed to obtain TTY size: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -379,7 +377,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i
|
||||||
|
|
||||||
// Configure the given terminal for raw mode
|
// Configure the given terminal for raw mode
|
||||||
func setRawTerminal(file *os.File) (*terminal.State, error) {
|
func setRawTerminal(file *os.File) (*terminal.State, error) {
|
||||||
state, err := terminal.MakeRaw(int(file.Fd()))
|
state, err := makeRawTerm(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -402,6 +400,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
|
||||||
// TODO: Make this configurable (can't use streams' InputStream as it's
|
// TODO: Make this configurable (can't use streams' InputStream as it's
|
||||||
// buffered)
|
// buffered)
|
||||||
terminalFile := os.Stdin
|
terminalFile := os.Stdin
|
||||||
|
terminalOutFile := os.Stdout
|
||||||
|
|
||||||
logrus.Debugf("Starting & Attaching to exec session ID %q", sessionID)
|
logrus.Debugf("Starting & Attaching to exec session ID %q", sessionID)
|
||||||
|
|
||||||
|
@ -447,7 +446,7 @@ 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()))
|
w, h, err := getTermSize(terminalFile, terminalOutFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("Failed to obtain TTY size: %v", err)
|
logrus.Warnf("Failed to obtain TTY size: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -490,11 +489,11 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
|
||||||
|
|
||||||
if needTTY {
|
if needTTY {
|
||||||
winChange := make(chan os.Signal, 1)
|
winChange := make(chan os.Signal, 1)
|
||||||
signal.Notify(winChange, sig.SIGWINCH)
|
|
||||||
winCtx, winCancel := context.WithCancel(ctx)
|
winCtx, winCancel := context.WithCancel(ctx)
|
||||||
defer winCancel()
|
defer winCancel()
|
||||||
|
|
||||||
attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile)
|
notifyWinChange(winCtx, winChange, terminalFile, terminalOutFile)
|
||||||
|
attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile, terminalOutFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.GetAttachInput() {
|
if options.GetAttachInput() {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
sig "github.com/containers/podman/v4/pkg/signal"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeRawTerm(stdin *os.File) (*terminal.State, error) {
|
||||||
|
return terminal.MakeRaw(int(stdin.Fd()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyWinChange(ctx context.Context, winChange chan os.Signal, stdin *os.File, stdout *os.File) {
|
||||||
|
signal.Notify(winChange, sig.SIGWINCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTermSize(stdin *os.File, stdout *os.File) (width, height int, err error) {
|
||||||
|
return terminal.GetSize(int(stdin.Fd()))
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sig "github.com/containers/podman/v4/pkg/signal"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeRawTerm(stdin *os.File) (*terminal.State, error) {
|
||||||
|
state, err := terminal.MakeRaw(int(stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt VT if supported (recent versions of Windows 10+)
|
||||||
|
var raw uint32
|
||||||
|
handle := windows.Handle(stdin.Fd())
|
||||||
|
if err := windows.GetConsoleMode(handle, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tryVT := raw | windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||||
|
|
||||||
|
if err := windows.SetConsoleMode(handle, tryVT); err != nil {
|
||||||
|
if err := windows.SetConsoleMode(handle, raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyWinChange(ctx context.Context, winChange chan os.Signal, stdin *os.File, stdout *os.File) {
|
||||||
|
// Simulate WINCH with polling
|
||||||
|
go func() {
|
||||||
|
var lastW int
|
||||||
|
var lastH int
|
||||||
|
|
||||||
|
d := time.Millisecond * 250
|
||||||
|
timer := time.NewTimer(d)
|
||||||
|
defer timer.Stop()
|
||||||
|
for ; ; timer.Reset(d) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-timer.C:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
w, h, err := terminal.GetSize(int(stdout.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if w != lastW || h != lastH {
|
||||||
|
winChange <- sig.SIGWINCH
|
||||||
|
lastW, lastH = w, h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTermSize(stdin *os.File, stdout *os.File) (width, height int, err error) {
|
||||||
|
return terminal.GetSize(int(stdout.Fd()))
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
// +build aix darwin dragonfly freebsd netbsd openbsd solaris zos
|
||||||
|
|
||||||
|
// Signal handling for Linux only.
|
||||||
|
package signal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sigrtmin = 34
|
||||||
|
sigrtmax = 64
|
||||||
|
|
||||||
|
SIGWINCH = syscall.SIGWINCH
|
||||||
|
)
|
||||||
|
|
||||||
|
// signalMap is a map of Linux signals.
|
||||||
|
// These constants are sourced from the Linux version of golang.org/x/sys/unix
|
||||||
|
// (I don't see much risk of this changing).
|
||||||
|
// This should work as long as Podman only runs containers on Linux, which seems
|
||||||
|
// a safe assumption for now.
|
||||||
|
var signalMap = map[string]syscall.Signal{
|
||||||
|
"ABRT": syscall.Signal(0x6),
|
||||||
|
"ALRM": syscall.Signal(0xe),
|
||||||
|
"BUS": syscall.Signal(0x7),
|
||||||
|
"CHLD": syscall.Signal(0x11),
|
||||||
|
"CLD": syscall.Signal(0x11),
|
||||||
|
"CONT": syscall.Signal(0x12),
|
||||||
|
"FPE": syscall.Signal(0x8),
|
||||||
|
"HUP": syscall.Signal(0x1),
|
||||||
|
"ILL": syscall.Signal(0x4),
|
||||||
|
"INT": syscall.Signal(0x2),
|
||||||
|
"IO": syscall.Signal(0x1d),
|
||||||
|
"IOT": syscall.Signal(0x6),
|
||||||
|
"KILL": syscall.Signal(0x9),
|
||||||
|
"PIPE": syscall.Signal(0xd),
|
||||||
|
"POLL": syscall.Signal(0x1d),
|
||||||
|
"PROF": syscall.Signal(0x1b),
|
||||||
|
"PWR": syscall.Signal(0x1e),
|
||||||
|
"QUIT": syscall.Signal(0x3),
|
||||||
|
"SEGV": syscall.Signal(0xb),
|
||||||
|
"STKFLT": syscall.Signal(0x10),
|
||||||
|
"STOP": syscall.Signal(0x13),
|
||||||
|
"SYS": syscall.Signal(0x1f),
|
||||||
|
"TERM": syscall.Signal(0xf),
|
||||||
|
"TRAP": syscall.Signal(0x5),
|
||||||
|
"TSTP": syscall.Signal(0x14),
|
||||||
|
"TTIN": syscall.Signal(0x15),
|
||||||
|
"TTOU": syscall.Signal(0x16),
|
||||||
|
"URG": syscall.Signal(0x17),
|
||||||
|
"USR1": syscall.Signal(0xa),
|
||||||
|
"USR2": syscall.Signal(0xc),
|
||||||
|
"VTALRM": syscall.Signal(0x1a),
|
||||||
|
"WINCH": syscall.Signal(0x1c),
|
||||||
|
"XCPU": syscall.Signal(0x18),
|
||||||
|
"XFSZ": syscall.Signal(0x19),
|
||||||
|
"RTMIN": sigrtmin,
|
||||||
|
"RTMIN+1": sigrtmin + 1,
|
||||||
|
"RTMIN+2": sigrtmin + 2,
|
||||||
|
"RTMIN+3": sigrtmin + 3,
|
||||||
|
"RTMIN+4": sigrtmin + 4,
|
||||||
|
"RTMIN+5": sigrtmin + 5,
|
||||||
|
"RTMIN+6": sigrtmin + 6,
|
||||||
|
"RTMIN+7": sigrtmin + 7,
|
||||||
|
"RTMIN+8": sigrtmin + 8,
|
||||||
|
"RTMIN+9": sigrtmin + 9,
|
||||||
|
"RTMIN+10": sigrtmin + 10,
|
||||||
|
"RTMIN+11": sigrtmin + 11,
|
||||||
|
"RTMIN+12": sigrtmin + 12,
|
||||||
|
"RTMIN+13": sigrtmin + 13,
|
||||||
|
"RTMIN+14": sigrtmin + 14,
|
||||||
|
"RTMIN+15": sigrtmin + 15,
|
||||||
|
"RTMAX-14": sigrtmax - 14,
|
||||||
|
"RTMAX-13": sigrtmax - 13,
|
||||||
|
"RTMAX-12": sigrtmax - 12,
|
||||||
|
"RTMAX-11": sigrtmax - 11,
|
||||||
|
"RTMAX-10": sigrtmax - 10,
|
||||||
|
"RTMAX-9": sigrtmax - 9,
|
||||||
|
"RTMAX-8": sigrtmax - 8,
|
||||||
|
"RTMAX-7": sigrtmax - 7,
|
||||||
|
"RTMAX-6": sigrtmax - 6,
|
||||||
|
"RTMAX-5": sigrtmax - 5,
|
||||||
|
"RTMAX-4": sigrtmax - 4,
|
||||||
|
"RTMAX-3": sigrtmax - 3,
|
||||||
|
"RTMAX-2": sigrtmax - 2,
|
||||||
|
"RTMAX-1": sigrtmax - 1,
|
||||||
|
"RTMAX": sigrtmax,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CatchAll catches all signals and relays them to the specified channel.
|
||||||
|
func CatchAll(sigc chan os.Signal) {
|
||||||
|
panic("Unsupported on non-linux platforms")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopCatch stops catching the signals and closes the specified channel.
|
||||||
|
func StopCatch(sigc chan os.Signal) {
|
||||||
|
panic("Unsupported on non-linux platforms")
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// +build !linux
|
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
|
||||||
|
|
||||||
// Signal handling for Linux only.
|
// Signal handling for Linux only.
|
||||||
package signal
|
package signal
|
||||||
|
|
Loading…
Reference in New Issue