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  }