github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/abi/terminal/terminal.go (about)

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