github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/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 20 "github.com/u-root/u-root/pkg/termios" 21 "golang.org/x/sys/unix" 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.TTY 31 WS *unix.Winsize 32 Restorer *unix.Termios 33 } 34 35 func (p *Pty) Start() error { 36 tty, err := termios.New() 37 if err != nil { 38 return err 39 } 40 41 if p.WS, err = tty.GetWinSize(); err != nil { 42 return err 43 } 44 45 if p.Restorer, err = tty.Raw(); err != nil { 46 return err 47 } 48 49 if err := p.C.Start(); err != nil { 50 tty.Set(p.Restorer) 51 return err 52 } 53 p.Kid = p.C.Process.Pid 54 55 // We make a good faith effort to set the 56 // WinSize of the Pts, but it's not a deal breaker 57 // if we can't do it. 58 if err := termios.SetWinSize(p.Pts.Fd(), p.WS); err != nil { 59 fmt.Fprintf(p.C.Stderr, "SetWinSize of Pts: %v", err) 60 } 61 62 go io.Copy(p.C.Stdout, p.Ptm) 63 64 // The 1 byte for IO may seem weird, but ptys are for human interacxtion 65 // and, let's face it, we don't all type fast. 66 go func() { 67 var data [1]byte 68 for { 69 if _, err := p.C.Stdin.Read(data[:]); err != nil { 70 return 71 } 72 // TODO: should we really echo this? Or pass it to the 73 // shell? I think we need to echo it but not pass it 74 // on. 75 if data[0] == '\r' { 76 if _, err := p.C.Stdout.Write(data[:]); err != nil { 77 fmt.Fprintf(p.C.Stderr, "error on echo %v: %v", data, err) 78 } 79 data[0] = '\n' 80 } 81 // Log the error but it may be transient. 82 if _, err := p.Ptm.Write(data[:]); err != nil { 83 fmt.Fprintf(p.C.Stderr, "Error writing input to ptm: %v: give up\n", err) 84 } 85 } 86 }() 87 return nil 88 } 89 90 func (p *Pty) Run() error { 91 if err := p.Start(); err != nil { 92 return err 93 } 94 95 return p.Wait() 96 } 97 98 func (p *Pty) Wait() error { 99 defer p.TTY.Set(p.Restorer) 100 return p.C.Wait() 101 }