123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 | package pty
import (
"bytes"
"fmt"
"os"
"strings"
"syscall"
"unsafe"
)
// getTermios and setTermios are the ioctls that get and set a
// terminal's settings (see KeyMode).
const getTermios, setTermios = syscall.TIOCGETA, syscall.TIOCSETA
// Open opens a new pseudo-terminal: the master, which the opener reads and
// writes, and the slave, the terminal a command gets.
func Open() (master, slave *os.File, err error) {
m, err := os.OpenFile("/dev/ptmx", os.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, nil, err
}
defer func() {
if err != nil {
m.Close()
}
}()
if err := ioctl(m, syscall.TIOCPTYGRANT, nil); err != nil {
return nil, nil, fmt.Errorf("granting the pty: %w", err)
}
if err := ioctl(m, syscall.TIOCPTYUNLK, nil); err != nil {
return nil, nil, fmt.Errorf("unlocking the pty: %w", err)
}
var buf [128]byte
if err := ioctl(m, syscall.TIOCPTYGNAME, unsafe.Pointer(&buf[0])); err != nil {
return nil, nil, fmt.Errorf("naming the pty: %w", err)
}
name, _, _ := bytes.Cut(buf[:], []byte{0})
// The sandbox profile names the device, so it must be one.
if !strings.HasPrefix(string(name), "/dev/tty") || strings.ContainsAny(string(name), "\"\\\n") {
return nil, nil, fmt.Errorf("naming the pty: unexpected name %q", name)
}
s, err := os.OpenFile(string(name), os.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, nil, err
}
return m, s, nil
}
|