github.com/insomniacslk/u-root@v0.0.0-20200717035308-96b791510d76/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 ) 23 24 type Pty struct { 25 C *exec.Cmd 26 Ptm *os.File 27 Pts *os.File 28 Sname string 29 Kid int 30 TTY *termios.TTYIO 31 WS *termios.Winsize 32 Restorer *termios.Termios 33 } 34 35 func (p *Pty) Command(cmd string, args ...string) { 36 p.C = exec.Command(cmd, args...) 37 p.C.Stdin, p.C.Stdout, p.C.Stderr = p.Pts, p.Pts, p.Pts 38 p.C.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} 39 } 40 41 func (p *Pty) Start() error { 42 tty, err := termios.New() 43 if err != nil { 44 return err 45 } 46 47 if p.WS, err = tty.GetWinSize(); err != nil { 48 return err 49 } 50 51 if p.Restorer, err = tty.Raw(); err != nil { 52 return err 53 } 54 55 if err := p.C.Start(); err != nil { 56 tty.Set(p.Restorer) 57 return err 58 } 59 p.Kid = p.C.Process.Pid 60 61 // We make a good faith effort to set the 62 // WinSize of the Pts, but it's not a deal breaker 63 // if we can't do it. 64 if err := termios.SetWinSize(p.Pts.Fd(), p.WS); err != nil { 65 fmt.Fprintf(p.C.Stderr, "SetWinSize of Pts: %v", err) 66 } 67 68 return nil 69 } 70 71 func (p *Pty) Run() error { 72 if err := p.Start(); err != nil { 73 return err 74 } 75 76 go io.Copy(p.TTY, p.Ptm) 77 78 // The 1 byte for IO may seem weird, but ptys are for human interaction 79 // and, let's face it, we don't all type fast. 80 go func() { 81 var data [1]byte 82 for { 83 if _, err := p.TTY.Read(data[:]); err != nil { 84 return 85 } 86 // Log the error but it may be transient. 87 if _, err := p.Ptm.Write(data[:]); err != nil { 88 fmt.Fprintf(p.C.Stderr, "Error writing input to ptm: %v: give up\n", err) 89 } 90 } 91 }() 92 return p.Wait() 93 } 94 95 func (p *Pty) Wait() error { 96 defer p.TTY.Set(p.Restorer) 97 return p.C.Wait() 98 }