1//go:build !windows
2// +build !windows
3
4package pty
5
6import (
7 "context"
8 "errors"
9 "os"
10 "os/exec"
11
12 "github.com/creack/pty"
13 "golang.org/x/sys/unix"
14)
15
16type unixPty struct {
17 master, slave *os.File
18 closed bool
19}
20
21var _ Pty = &unixPty{}
22
23// Close implements Pty.
24func (p *unixPty) Close() error {
25 if p.closed {
26 return nil
27 }
28 defer func() {
29 p.closed = true
30 }()
31 return errors.Join(p.master.Close(), p.slave.Close())
32}
33
34// Command implements Pty.
35func (p *unixPty) Command(name string, args ...string) *Cmd {
36 return p.CommandContext(nil, name, args...) // nolint:staticcheck
37}
38
39// CommandContext implements Pty.
40func (p *unixPty) CommandContext(ctx context.Context, name string, args ...string) *Cmd {
41 cmd := exec.Command(name, args...)
42 if ctx != nil {
43 cmd = exec.CommandContext(ctx, name, args...)
44 }
45 c := &Cmd{
46 ctx: ctx,
47 pty: p,
48 sys: cmd,
49 Path: name,
50 Args: append([]string{name}, args...),
51 }
52 return c
53}
54
55// Name implements Pty.
56func (p *unixPty) Name() string {
57 return p.slave.Name()
58}
59
60// Read implements Pty.
61func (p *unixPty) Read(b []byte) (n int, err error) {
62 return p.master.Read(b)
63}
64
65func (p *unixPty) Control(f func(fd uintptr)) error {
66 conn, err := p.master.SyscallConn()
67 if err != nil {
68 return err
69 }
70 return conn.Control(f)
71}
72
73// Resize implements Pty.
74func (p *unixPty) Resize(rows int, cols int) error {
75 var ctrlErr error
76 if err := p.Control(func(fd uintptr) {
77 ctrlErr = unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &unix.Winsize{
78 Row: uint16(rows),
79 Col: uint16(cols),
80 })
81 }); err != nil {
82 return err
83 }
84
85 return ctrlErr
86}
87
88// Write implements Pty.
89func (p *unixPty) Write(b []byte) (n int, err error) {
90 return p.master.Write(b)
91}
92
93func newPty() (Pty, error) {
94 master, slave, err := pty.Open()
95 if err != nil {
96 return nil, err
97 }
98
99 return &unixPty{
100 master: master,
101 slave: slave,
102 }, nil
103}
104
105func (c *Cmd) start() error {
106 cmd, ok := c.sys.(*exec.Cmd)
107 if !ok {
108 return ErrInvalidCommand
109 }
110 pty, ok := c.pty.(*unixPty)
111 if !ok {
112 return ErrInvalidCommand
113 }
114
115 cmd.Stdin = pty.slave
116 cmd.Stdout = pty.slave
117 cmd.Stderr = pty.slave
118 cmd.SysProcAttr = &unix.SysProcAttr{
119 Setsid: true,
120 Setctty: true,
121 }
122 if err := cmd.Start(); err != nil {
123 return err
124 }
125
126 c.Process = cmd.Process
127 return nil
128}
129
130func (c *Cmd) wait() error {
131 cmd, ok := c.sys.(*exec.Cmd)
132 if !ok {
133 return ErrInvalidCommand
134 }
135 err := cmd.Wait()
136 c.ProcessState = cmd.ProcessState
137 return err
138}