summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pkg/bindings/containers/attach.go23
-rw-r--r--pkg/bindings/containers/term_unix.go25
-rw-r--r--pkg/bindings/containers/term_windows.go69
-rw-r--r--pkg/signal/signal_unix.go99
-rw-r--r--pkg/signal/signal_unsupported.go2
5 files changed, 205 insertions, 13 deletions
diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go
index c6d434c87..f410606e4 100644
--- a/pkg/bindings/containers/attach.go
+++ b/pkg/bindings/containers/attach.go
@@ -10,14 +10,12 @@ import (
"net/http"
"net/url"
"os"
- "os/signal"
"reflect"
"strconv"
"time"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/bindings"
- sig "github.com/containers/podman/v4/pkg/signal"
"github.com/containers/podman/v4/utils"
"github.com/moby/term"
"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
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 {
state, err := setRawTerminal(file)
if err != nil {
@@ -142,11 +141,10 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
if needTTY {
winChange := make(chan os.Signal, 1)
- signal.Notify(winChange, sig.SIGWINCH)
winCtx, winCancel := context.WithCancel(ctx)
defer winCancel()
-
- attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file)
+ notifyWinChange(winCtx, winChange, file, outFile)
+ attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file, outFile)
}
// 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
// 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() {
- w, h, err := terminal.GetSize(int(file.Fd()))
+ w, h, err := getTermSize(file, outFile)
if err != nil {
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
func setRawTerminal(file *os.File) (*terminal.State, error) {
- state, err := terminal.MakeRaw(int(file.Fd()))
+ state, err := makeRawTerm(file)
if err != nil {
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
// buffered)
terminalFile := os.Stdin
+ terminalOutFile := os.Stdout
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{})
}()
- w, h, err := terminal.GetSize(int(terminalFile.Fd()))
+ w, h, err := getTermSize(terminalFile, terminalOutFile)
if err != nil {
logrus.Warnf("Failed to obtain TTY size: %v", err)
}
@@ -490,11 +489,11 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
if needTTY {
winChange := make(chan os.Signal, 1)
- signal.Notify(winChange, sig.SIGWINCH)
winCtx, winCancel := context.WithCancel(ctx)
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() {
diff --git a/pkg/bindings/containers/term_unix.go b/pkg/bindings/containers/term_unix.go
new file mode 100644
index 000000000..2c976393f
--- /dev/null
+++ b/pkg/bindings/containers/term_unix.go
@@ -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()))
+}
diff --git a/pkg/bindings/containers/term_windows.go b/pkg/bindings/containers/term_windows.go
new file mode 100644
index 000000000..11d4bd50d
--- /dev/null
+++ b/pkg/bindings/containers/term_windows.go
@@ -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()))
+}
diff --git a/pkg/signal/signal_unix.go b/pkg/signal/signal_unix.go
new file mode 100644
index 000000000..f35abddc1
--- /dev/null
+++ b/pkg/signal/signal_unix.go
@@ -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")
+}
diff --git a/pkg/signal/signal_unsupported.go b/pkg/signal/signal_unsupported.go
index 9d1733c02..45946f142 100644
--- a/pkg/signal/signal_unsupported.go
+++ b/pkg/signal/signal_unsupported.go
@@ -1,4 +1,4 @@
-// +build !linux
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
// Signal handling for Linux only.
package signal