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 }