github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/pty/pty_linux.go (about) 1 // Copyright 2015-2020 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 // New returns a new Pty. 24 func New() (*Pty, error) { 25 tty, err := termios.New() 26 if err != nil { 27 return nil, err 28 } 29 restorer, err := tty.Get() 30 if err != nil { 31 return nil, err 32 } 33 34 ptm, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) 35 if err != nil { 36 return nil, err 37 } 38 39 if err := ptsunlock(ptm); err != nil { 40 return nil, err 41 } 42 43 sname, err := ptsname(ptm) 44 if err != nil { 45 return nil, err 46 } 47 48 // It can take a non-zero time for a pts to appear, it seems. 49 // Ten tries is reported to be far more than enough. 50 // We could consider something like inotify rather than polling? 51 for i := 0; i < 10; i++ { 52 _, err := os.Stat(sname) 53 if err == nil { 54 break 55 } 56 } 57 58 pts, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) 59 if err != nil { 60 return nil, err 61 } 62 return &Pty{Ptm: ptm, Pts: pts, Sname: sname, Kid: -1, TTY: tty, Restorer: restorer}, nil 63 } 64 65 func ptsname(f *os.File) (string, error) { 66 var n uintptr 67 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) 68 if err != 0 { 69 return "", err 70 } 71 return fmt.Sprintf("/dev/pts/%d", n), nil 72 } 73 74 func ptsunlock(f *os.File) error { 75 var u uintptr 76 // use TIOCSPTLCK with a zero valued arg to clear the slave pty lock 77 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) 78 if err != 0 { 79 return err 80 } 81 return nil 82 } 83 84 func sysLinux(p *Pty) { 85 p.C.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} 86 } 87 88 func init() { 89 sys = sysLinux 90 }