github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/pty/pty.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 pty provides basic pty support. 6 // It implments much of exec.Command 7 // but the Start() function starts two goroutines that relay the 8 // data for Stdin, Stdout, and Stdout such that proper kernel pty 9 // processing is done. We did not simply embed an exec.Command 10 // as we can no guarantee that we can implement all aspects of it 11 // for all time to come. 12 package pty 13 14 import ( 15 "fmt" 16 "io" 17 "os" 18 "os/exec" 19 "syscall" 20 21 "github.com/u-root/u-root/pkg/termios" 22 "golang.org/x/sys/unix" 23 ) 24 25 type Pty struct { 26 C *exec.Cmd 27 Ptm *os.File 28 Pts *os.File 29 Sname string 30 Kid int 31 TTY *termios.TTY 32 WS *unix.Winsize 33 Restorer *unix.Termios 34 } 35 36 func (p *Pty) Command(cmd string, args ...string) { 37 p.C = exec.Command(cmd, args...) 38 p.C.Stdin, p.C.Stdout, p.C.Stderr = p.Pts, p.Pts, p.Pts 39 p.C.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} 40 } 41 42 func (p *Pty) Start() error { 43 tty, err := termios.New() 44 if err != nil { 45 return err 46 } 47 48 if p.WS, err = tty.GetWinSize(); err != nil { 49 return err 50 } 51 52 if p.Restorer, err = tty.Raw(); err != nil { 53 return err 54 } 55 56 if err := p.C.Start(); err != nil { 57 tty.Set(p.Restorer) 58 return err 59 } 60 p.Kid = p.C.Process.Pid 61 62 // We make a good faith effort to set the 63 // WinSize of the Pts, but it's not a deal breaker 64 // if we can't do it. 65 if err := termios.SetWinSize(p.Pts.Fd(), p.WS); err != nil { 66 fmt.Fprintf(p.C.Stderr, "SetWinSize of Pts: %v", err) 67 } 68 69 return nil 70 } 71 72 func (p *Pty) Run() error { 73 if err := p.Start(); err != nil { 74 return err 75 } 76 77 go io.Copy(p.TTY, p.Ptm) 78 79 // The 1 byte for IO may seem weird, but ptys are for human interaction 80 // and, let's face it, we don't all type fast. 81 go func() { 82 var data [1]byte 83 for { 84 if _, err := p.TTY.Read(data[:]); err != nil { 85 return 86 } 87 // Log the error but it may be transient. 88 if _, err := p.Ptm.Write(data[:]); err != nil { 89 fmt.Fprintf(p.C.Stderr, "Error writing input to ptm: %v: give up\n", err) 90 } 91 } 92 }() 93 return p.Wait() 94 } 95 96 func (p *Pty) Wait() error { 97 defer p.TTY.Set(p.Restorer) 98 return p.C.Wait() 99 }