github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/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  	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  	for i := 0; i < 10; i++ {
    51  		_, err := os.Stat(sname)
    52  		if err == nil {
    53  			break
    54  		}
    55  	}
    56  	pts, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return &Pty{Ptm: ptm, Pts: pts, Sname: sname, Kid: -1, TTY: tty, Restorer: restorer}, nil
    61  }
    62  
    63  func ptsname(f *os.File) (string, error) {
    64  	var n uintptr
    65  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
    66  	if err != 0 {
    67  		return "", err
    68  	}
    69  	return fmt.Sprintf("/dev/pts/%d", n), nil
    70  }
    71  
    72  func ptsunlock(f *os.File) error {
    73  	var u uintptr
    74  	// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
    75  	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
    76  	if err != 0 {
    77  		return err
    78  	}
    79  	return nil
    80  }