github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/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 "syscall" 18 "unsafe" 19 20 "github.com/u-root/u-root/pkg/termios" 21 ) 22 23 // pty support. We used to import github.com/kr/pty but what we need is not that complex. 24 // Thanks to keith rarick for these functions. 25 func New() (*Pty, error) { 26 tty, err := termios.New() 27 if err != nil { 28 return nil, err 29 } 30 restorer, err := tty.Get() 31 if err != nil { 32 return nil, err 33 } 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 // We could consider something like inotify rather than polling? 52 for i := 0; i < 10; i++ { 53 _, err := os.Stat(sname) 54 if err == nil { 55 break 56 } 57 } 58 59 pts, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) 60 if err != nil { 61 return nil, err 62 } 63 return &Pty{Ptm: ptm, Pts: pts, Sname: sname, Kid: -1, TTY: tty, Restorer: restorer}, nil 64 } 65 66 func ptsname(f *os.File) (string, error) { 67 var n uintptr 68 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) 69 if err != 0 { 70 return "", err 71 } 72 return fmt.Sprintf("/dev/pts/%d", n), nil 73 } 74 75 func ptsunlock(f *os.File) error { 76 var u uintptr 77 // use TIOCSPTLCK with a zero valued arg to clear the slave pty lock 78 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) 79 if err != 0 { 80 return err 81 } 82 return nil 83 }