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