turbo-editors/turbo-corepublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

pty_darwin.go · 94 lines · 2.6 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 12h ago1//go:build darwin
2
3package terminal
4
5import (
6 "bytes"
7 "fmt"
8 "os"
9 "syscall"
10 "unsafe"
11
12 "golang.org/x/sys/unix"
13)
14
15// openPTY allocates a pseudo-terminal and returns both of its ends.
16//
17// Darwin hands out the slave by name rather than by number: grant it, unlock
18// it, then ask for the name. TIOCPTYGNAME writes into a buffer, and x/sys/unix
19// exposes no helper for an ioctl of that shape — hence the one raw call below.
20func openPTY() (master, slave *os.File, err error) {
21 master, err = os.OpenFile("/dev/ptmx", os.O_RDWR|unix.O_NOCTTY, 0)
22 if err != nil {
23 return nil, nil, fmt.Errorf("terminal: opening /dev/ptmx: %w", err)
24 }
25
26 for _, step := range []struct {
27 request uint
28 what string
29 }{
30 {unix.TIOCPTYGRANT, "granting"},
31 {unix.TIOCPTYUNLK, "unlocking"},
32 } {
33 if err := unix.IoctlSetInt(int(master.Fd()), step.request, 0); err != nil {
34 master.Close()
35 return nil, nil, fmt.Errorf("terminal: %s the pseudo-terminal: %w", step.what, err)
36 }
37 }
38
39 name, err := slaveName(master)
40 if err != nil {
41 master.Close()
42 return nil, nil, err
43 }
44
45 slave, err = os.OpenFile(name, os.O_RDWR|unix.O_NOCTTY, 0)
46 if err != nil {
47 master.Close()
48 return nil, nil, fmt.Errorf("terminal: opening the slave side: %w", err)
49 }
50 return master, slave, nil
51}
52
53// ptyNameLength is the size of the buffer TIOCPTYGNAME fills, as the request
54// itself encodes.
55const ptyNameLength = 128
56
57// slaveName asks the kernel for the path of the slave device.
58func slaveName(master *os.File) (string, error) {
59 var buffer [ptyNameLength]byte
60
61 _, _, errno := syscall.Syscall(
62 syscall.SYS_IOCTL,
63 master.Fd(),
64 uintptr(unix.TIOCPTYGNAME),
65 uintptr(unsafe.Pointer(&buffer[0])),
66 )
67 if errno != 0 {
68 return "", fmt.Errorf("terminal: naming the pseudo-terminal: %w", errno)
69 }
70
71 // The kernel writes a C string, so the name ends at the first zero byte.
72 if end := bytes.IndexByte(buffer[:], 0); end >= 0 {
73 return string(buffer[:end]), nil
74 }
75 return string(buffer[:]), nil
76}
77
78// setWinsize tells the pseudo-terminal how big it is, which the kernel passes
79// on to the shell as SIGWINCH.
80func setWinsize(master *os.File, width, height int) error {
81 return unix.IoctlSetWinsize(int(master.Fd()), unix.TIOCSWINSZ, &unix.Winsize{
82 Row: uint16(height),
83 Col: uint16(width),
84 })
85}
86
87// childAttributes put the shell in a session of its own with the slave as its
88// controlling terminal.
89//
90// Without this the shell has no controlling terminal, and job control — Ctrl-C,
91// Ctrl-Z, running something in the background — does not work at all.
92func childAttributes() *syscall.SysProcAttr {
93 return &syscall.SysProcAttr{Setsid: true, Setctty: true}
94}