turbo-editors/turbo-corepublic Fork 0
v1.0.2
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.

🛟 Updated. 28d5985 · on v1.0.2 · k33g · 17h ago
pty_darwin.go · 94 lines · 2.6 KBGo Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//go:build darwin

package terminal

import (
	"bytes"
	"fmt"
	"os"
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
)

// openPTY allocates a pseudo-terminal and returns both of its ends.
//
// Darwin hands out the slave by name rather than by number: grant it, unlock
// it, then ask for the name. TIOCPTYGNAME writes into a buffer, and x/sys/unix
// exposes no helper for an ioctl of that shape — hence the one raw call below.
func openPTY() (master, slave *os.File, err error) {
	master, err = os.OpenFile("/dev/ptmx", os.O_RDWR|unix.O_NOCTTY, 0)
	if err != nil {
		return nil, nil, fmt.Errorf("terminal: opening /dev/ptmx: %w", err)
	}

	for _, step := range []struct {
		request uint
		what    string
	}{
		{unix.TIOCPTYGRANT, "granting"},
		{unix.TIOCPTYUNLK, "unlocking"},
	} {
		if err := unix.IoctlSetInt(int(master.Fd()), step.request, 0); err != nil {
			master.Close()
			return nil, nil, fmt.Errorf("terminal: %s the pseudo-terminal: %w", step.what, err)
		}
	}

	name, err := slaveName(master)
	if err != nil {
		master.Close()
		return nil, nil, err
	}

	slave, err = os.OpenFile(name, os.O_RDWR|unix.O_NOCTTY, 0)
	if err != nil {
		master.Close()
		return nil, nil, fmt.Errorf("terminal: opening the slave side: %w", err)
	}
	return master, slave, nil
}

// ptyNameLength is the size of the buffer TIOCPTYGNAME fills, as the request
// itself encodes.
const ptyNameLength = 128

// slaveName asks the kernel for the path of the slave device.
func slaveName(master *os.File) (string, error) {
	var buffer [ptyNameLength]byte

	_, _, errno := syscall.Syscall(
		syscall.SYS_IOCTL,
		master.Fd(),
		uintptr(unix.TIOCPTYGNAME),
		uintptr(unsafe.Pointer(&buffer[0])),
	)
	if errno != 0 {
		return "", fmt.Errorf("terminal: naming the pseudo-terminal: %w", errno)
	}

	// The kernel writes a C string, so the name ends at the first zero byte.
	if end := bytes.IndexByte(buffer[:], 0); end >= 0 {
		return string(buffer[:end]), nil
	}
	return string(buffer[:]), nil
}

// setWinsize tells the pseudo-terminal how big it is, which the kernel passes
// on to the shell as SIGWINCH.
func setWinsize(master *os.File, width, height int) error {
	return unix.IoctlSetWinsize(int(master.Fd()), unix.TIOCSWINSZ, &unix.Winsize{
		Row: uint16(height),
		Col: uint16(width),
	})
}

// childAttributes put the shell in a session of its own with the slave as its
// controlling terminal.
//
// Without this the shell has no controlling terminal, and job control — Ctrl-C,
// Ctrl-Z, running something in the background — does not work at all.
func childAttributes() *syscall.SysProcAttr {
	return &syscall.SysProcAttr{Setsid: true, Setctty: true}
}