github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/termios/sgtty_unix.go (about)

     1  // Copyright 2015-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build !plan9,!windows
     6  
     7  package termios
     8  
     9  import (
    10  	"fmt"
    11  	"log"
    12  	"reflect"
    13  	"sort"
    14  	"strconv"
    15  
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  // GTTY returns the TTY struct for a given fd. It is like a New in
    20  // many packages but the name GTTY is a tradition.
    21  func GTTY(fd int) (*TTY, error) {
    22  	term, err := unix.IoctlGetTermios(fd, unix.TCGETS)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	w, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  
    31  	var t = TTY{Opts: make(map[string]bool), CC: make(map[string]uint8)}
    32  	for n, b := range boolFields {
    33  		val := uint32(reflect.ValueOf(term).Elem().Field(b.word).Uint()) & b.mask
    34  		t.Opts[n] = val != 0
    35  	}
    36  
    37  	for n, c := range cc {
    38  		t.CC[n] = term.Cc[c]
    39  	}
    40  
    41  	// back in the day, you could have different i and o speeds.
    42  	// since about 1975, this has not been a thing. It's still in POSIX
    43  	// evidently. WTF?
    44  	t.Ispeed = int(term.Ispeed)
    45  	t.Ospeed = int(term.Ospeed)
    46  	t.Row = int(w.Row)
    47  	t.Col = int(w.Col)
    48  
    49  	return &t, nil
    50  }
    51  
    52  // STTY uses a TTY * to set TTY settings on an fd.
    53  // It returns a new TTY struct for the fd after the changes are made,
    54  // and an error. It does not change the original TTY struct.
    55  func (t *TTY) STTY(fd int) (*TTY, error) {
    56  	// Get a unix.Termios which we can partially fill in.
    57  	term, err := unix.IoctlGetTermios(fd, unix.TCGETS)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	for n, b := range boolFields {
    63  		set := t.Opts[n]
    64  		i := reflect.ValueOf(term).Elem().Field(b.word).Uint()
    65  		if set {
    66  			i |= uint64(b.mask)
    67  		} else {
    68  			i &= ^uint64(b.mask)
    69  		}
    70  		reflect.ValueOf(term).Elem().Field(b.word).SetUint(i)
    71  	}
    72  
    73  	for n, c := range cc {
    74  		term.Cc[c] = t.CC[n]
    75  	}
    76  
    77  	term.Ispeed = uint32(t.Ispeed)
    78  	term.Ospeed = uint32(t.Ospeed)
    79  
    80  	if err := unix.IoctlSetTermios(fd, unix.TCSETS, term); err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	w := &unix.Winsize{Row: uint16(t.Row), Col: uint16(t.Col)}
    85  	if err := unix.IoctlSetWinsize(fd, unix.TIOCSWINSZ, w); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	return GTTY(fd)
    90  }
    91  
    92  // String will stringify a TTY, including printing out the options all in the same order.
    93  func (t *TTY) String() string {
    94  	s := fmt.Sprintf("speed:%v ", t.Ispeed)
    95  	s += fmt.Sprintf("rows:%d cols:%d", t.Row, t.Col)
    96  	var opts []string
    97  	for n, c := range t.CC {
    98  		opts = append(opts, fmt.Sprintf("%v:%#02x", n, c))
    99  	}
   100  
   101  	for n, set := range t.Opts {
   102  		if set {
   103  			opts = append(opts, fmt.Sprintf("%v:1", n))
   104  		} else {
   105  			opts = append(opts, fmt.Sprintf("%v:0", n))
   106  		}
   107  	}
   108  	sort.Strings(opts)
   109  	for _, v := range opts {
   110  		s += fmt.Sprintf(" %s", v)
   111  	}
   112  	return s
   113  }
   114  
   115  func intarg(s []string) int {
   116  	if len(s) < 2 {
   117  		log.Fatalf("%s requires an arg", s[0])
   118  	}
   119  	i, err := strconv.Atoi(s[1])
   120  	if err != nil {
   121  		log.Fatalf("%s is not a number", s)
   122  	}
   123  	return i
   124  }
   125  
   126  // SetOpts sets opts in a TTY given an array of key-value pairs and
   127  // booleans. The arguments are a variety of key-value pairs and booleans.
   128  // booleans are cleared if the first char is a -, set otherwise.
   129  func (t *TTY) SetOpts(opts []string) error {
   130  	for i := 0; i < len(opts); i++ {
   131  		o := opts[i]
   132  		switch o {
   133  		case "row":
   134  			t.Row = intarg(opts[i:])
   135  			i++
   136  			continue
   137  		case "col":
   138  			t.Col = intarg(opts[i:])
   139  			i++
   140  			continue
   141  		case "speed":
   142  			t.Ispeed = intarg(opts[i:])
   143  			i++
   144  			continue
   145  		}
   146  
   147  		// see if it's one of the control char options.
   148  		if _, ok := cc[opts[i]]; ok {
   149  			t.CC[opts[i]] = uint8(intarg(opts[i:]))
   150  			i++
   151  			continue
   152  		}
   153  
   154  		// At this point, it has to be one of the boolean ones
   155  		// or we're done here.
   156  		set := true
   157  		if o[0] == '~' {
   158  			set = false
   159  			o = o[1:]
   160  		}
   161  		if _, ok := boolFields[o]; !ok {
   162  			log.Fatalf("%s: unknown option", o)
   163  		}
   164  
   165  		t.Opts[o] = set
   166  	}
   167  	return nil
   168  }
   169  
   170  // Raw sets a TTY into raw more, returning a TTY struct
   171  func Raw(fd int) (*TTY, error) {
   172  	t, err := GTTY(fd)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	t.SetOpts([]string{"~ignbrk", "~brkint", "~parmrk", "~istrip", "~inlcr", "~igncr", "~icrnl", "~ixon", "~opost", "~echo", "~echonl", "~icanon", "~isig", "~iexten", "~parenb" /*"cs8", */, "min", "1", "time", "0"})
   178  
   179  	return t.STTY(fd)
   180  }