github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/pkg/pty/pty_linux.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 "os" 17 "os/exec" 18 "syscall" 19 "unsafe" 20 21 "github.com/u-root/u-root/pkg/termios" 22 ) 23 24 // pty support. We used to import github.com/kr/pty but what we need is not that complex. 25 // Thanks to keith rarick for these functions. 26 func New(cmd string, args ...string) (*Pty, error) { 27 tty, err := termios.New() 28 if err != nil { 29 return nil, err 30 } 31 restorer, err := tty.Get() 32 if err != nil { 33 return nil, err 34 } 35 ptm, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) 36 if err != nil { 37 return nil, err 38 } 39 40 if err := ptsunlock(ptm); err != nil { 41 return nil, err 42 } 43 44 sname, err := ptsname(ptm) 45 if err != nil { 46 return nil, err 47 } 48 49 // It can take a non-zero time for a pts to appear, it seems. 50 // Ten tries is reported to be far more than enough. 51 for i := 0; i < 10; i++ { 52 _, err := os.Stat(sname) 53 if err == nil { 54 break 55 } 56 } 57 pts, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) 58 if err != nil { 59 return nil, err 60 } 61 c := exec.Command(cmd, args...) 62 c.Stdin, c.Stdout, c.Stderr = pts, pts, pts 63 c.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} 64 return &Pty{Ptm: ptm, Pts: pts, Sname: sname, Kid: -1, C: c, TTY: tty, Restorer: restorer}, nil 65 } 66 67 func ptsname(f *os.File) (string, error) { 68 var n uintptr 69 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) 70 if err != 0 { 71 return "", err 72 } 73 return fmt.Sprintf("/dev/pts/%d", n), nil 74 } 75 76 func ptsunlock(f *os.File) error { 77 var u uintptr 78 // use TIOCSPTLCK with a zero valued arg to clear the slave pty lock 79 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) 80 if err != 0 { 81 return err 82 } 83 return nil 84 }