turbo-editors/turbo-corepublic Fork 0
28d59854361aeda8541d853093e732126f3d7bff
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 28d59854361aeda8541d853093e732126f3d7bff · k33g · 18h ago
session_unix.go · 79 lines · 2.2 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
//go:build linux || darwin

package terminal

import (
	"os"
	"os/exec"
	"path/filepath"
)

// unixChild is a shell behind a Unix pseudo-terminal: the master end this side
// holds, and the process that has the slave as its controlling terminal.
type unixChild struct {
	master  *os.File
	command *exec.Cmd
}

// startChild opens a pseudo-terminal and starts the shell in it.
func startChild(options Options) (child, error) {
	master, slave, err := openPTY()
	if err != nil {
		return nil, err
	}
	// The child keeps the slave as its own standard input, output and error;
	// this process has no further use for it once the child has started.
	defer slave.Close()

	command := exec.Command(shellOrDefault(options.Shell), options.Args...)
	command.Dir = options.Dir
	command.Env = environment(options.Env)
	command.Stdin, command.Stdout, command.Stderr = slave, slave, slave
	command.SysProcAttr = childAttributes()

	if err := command.Start(); err != nil {
		master.Close()
		return nil, err
	}

	c := &unixChild{master: master, command: command}
	if err := c.Resize(max(options.Width, 1), max(options.Height, 1)); err != nil {
		c.Close()
		return nil, err
	}
	return c, nil
}

// defaultShell returns the user's own shell, or a last-resort fallback that
// exists on every Unix.
func defaultShell() string {
	if fromEnvironment := os.Getenv("SHELL"); fromEnvironment != "" {
		return fromEnvironment
	}
	return "/bin/sh"
}

func (c *unixChild) Read(b []byte) (int, error)  { return c.master.Read(b) }
func (c *unixChild) Write(b []byte) (int, error) { return c.master.Write(b) }

// Resize sets the pseudo-terminal's size, which the kernel passes on to the
// shell as SIGWINCH.
func (c *unixChild) Resize(width, height int) error {
	return setWinsize(c.master, width, height)
}

// Close closes the master — the shell sees its input end — then kills and
// reaps the process, so that closing cannot hang on a shell that ignores the
// hint.
func (c *unixChild) Close() error {
	closeErr := c.master.Close()

	if c.command.Process != nil {
		_ = c.command.Process.Kill()
		_, _ = c.command.Process.Wait()
	}
	return closeErr
}

// Command returns the shell's name without its path.
func (c *unixChild) Command() string { return filepath.Base(c.command.Path) }