forked from creack/pty
-
Notifications
You must be signed in to change notification settings - Fork 2
/
pty_solaris.go
152 lines (130 loc) · 3.27 KB
/
pty_solaris.go
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//go:build solaris
// +build solaris
package pty
/* based on:
http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c
*/
import (
"errors"
"os"
"strconv"
"syscall"
"unsafe"
)
func open() (pty, tty *os.File, err error) {
ptmxfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
// In case of error after this point, make sure we close the ptmx fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
if err := grantpt(p); err != nil {
return nil, nil, err
}
if err := unlockpt(p); err != nil {
return nil, nil, err
}
ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
t := os.NewFile(uintptr(ptsfd), sname)
// In case of error after this point, make sure we close the pts fd.
defer func() {
if err != nil {
_ = t.Close() // Best effort.
}
}()
// pushing terminal driver STREAMS modules as per pts(7)
for _, mod := range []string{"ptem", "ldterm", "ttcompat"} {
if err := streamsPush(t, mod); err != nil {
return nil, nil, err
}
}
return p, t, nil
}
func ptsname(f *os.File) (string, error) {
dev, err := ptsdev(f.Fd())
if err != nil {
return "", err
}
fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10)
if err := syscall.Access(fn, 0); err != nil {
return "", err
}
return fn, nil
}
func unlockpt(f *os.File) error {
istr := strioctl{
icCmd: UNLKPT,
icTimeout: 0,
icLen: 0,
icDP: nil,
}
return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
}
func minor(x uint64) uint64 { return x & 0377 }
func ptsdev(fd uintptr) (uint64, error) {
istr := strioctl{
icCmd: ISPTM,
icTimeout: 0,
icLen: 0,
icDP: nil,
}
if err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
return 0, err
}
var status syscall.Stat_t
if err := syscall.Fstat(int(fd), &status); err != nil {
return 0, err
}
return uint64(minor(status.Rdev)), nil
}
type ptOwn struct {
rUID int32
rGID int32
}
func grantpt(f *os.File) error {
if _, err := ptsdev(f.Fd()); err != nil {
return err
}
pto := ptOwn{
rUID: int32(os.Getuid()),
// XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
rGID: int32(os.Getgid()),
}
istr := strioctl{
icCmd: OWNERPT,
icTimeout: 0,
icLen: int32(unsafe.Sizeof(strioctl{})),
icDP: unsafe.Pointer(&pto),
}
if err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
return errors.New("access denied")
}
return nil
}
// streamsPush pushes STREAMS modules if not already done so.
func streamsPush(f *os.File, mod string) error {
buf := []byte(mod)
// XXX I_FIND is not returning an error when the module
// is already pushed even though truss reports a return
// value of 1. A bug in the Go Solaris syscall interface?
// XXX without this we are at risk of the issue
// https://www.illumos.org/issues/9042
// but since we are not using libc or XPG4.2, we should not be
// double-pushing modules
if err := ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
return nil
}
return ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
}