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  }