github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/domain/infra/abi/terminal/terminal.go (about) 1 // +build ABISupport 2 3 package terminal 4 5 import ( 6 "context" 7 "os" 8 "os/signal" 9 10 lsignal "github.com/containers/libpod/pkg/signal" 11 "github.com/docker/docker/pkg/term" 12 "github.com/pkg/errors" 13 "github.com/sirupsen/logrus" 14 "k8s.io/client-go/tools/remotecommand" 15 ) 16 17 // RawTtyFormatter ... 18 type RawTtyFormatter struct { 19 } 20 21 // getResize returns a TerminalSize command matching stdin's current 22 // size on success, and nil on errors. 23 func getResize() *remotecommand.TerminalSize { 24 winsize, err := term.GetWinsize(os.Stdin.Fd()) 25 if err != nil { 26 logrus.Warnf("Could not get terminal size %v", err) 27 return nil 28 } 29 return &remotecommand.TerminalSize{ 30 Width: winsize.Width, 31 Height: winsize.Height, 32 } 33 } 34 35 // Helper for prepareAttach - set up a goroutine to generate terminal resize events 36 func resizeTty(ctx context.Context, resize chan remotecommand.TerminalSize) { 37 sigchan := make(chan os.Signal, 1) 38 signal.Notify(sigchan, lsignal.SIGWINCH) 39 go func() { 40 defer close(resize) 41 // Update the terminal size immediately without waiting 42 // for a SIGWINCH to get the correct initial size. 43 resizeEvent := getResize() 44 for { 45 if resizeEvent == nil { 46 select { 47 case <-ctx.Done(): 48 return 49 case <-sigchan: 50 resizeEvent = getResize() 51 } 52 } else { 53 select { 54 case <-ctx.Done(): 55 return 56 case <-sigchan: 57 resizeEvent = getResize() 58 case resize <- *resizeEvent: 59 resizeEvent = nil 60 } 61 } 62 } 63 }() 64 } 65 66 func restoreTerminal(state *term.State) error { 67 logrus.SetFormatter(&logrus.TextFormatter{}) 68 return term.RestoreTerminal(os.Stdin.Fd(), state) 69 } 70 71 // Format ... 72 func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) { 73 textFormatter := logrus.TextFormatter{} 74 bytes, err := textFormatter.Format(entry) 75 76 if err == nil { 77 bytes = append(bytes, '\r') 78 } 79 80 return bytes, err 81 } 82 83 func handleTerminalAttach(ctx context.Context, resize chan remotecommand.TerminalSize) (context.CancelFunc, *term.State, error) { 84 logrus.Debugf("Handling terminal attach") 85 86 subCtx, cancel := context.WithCancel(ctx) 87 88 resizeTty(subCtx, resize) 89 90 oldTermState, err := term.SaveState(os.Stdin.Fd()) 91 if err != nil { 92 // allow caller to not have to do any cleaning up if we error here 93 cancel() 94 return nil, nil, errors.Wrapf(err, "unable to save terminal state") 95 } 96 97 logrus.SetFormatter(&RawTtyFormatter{}) 98 if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil { 99 return cancel, nil, err 100 } 101 102 return cancel, oldTermState, nil 103 }