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