Skip to content

Commit edfbf75

Browse files
authored
Merge pull request #201 from MacMalainey/master
add z/OS support
2 parents 2cde18b + 7c00df3 commit edfbf75

File tree

5 files changed

+181
-3
lines changed

5 files changed

+181
-3
lines changed

fd_helper_other_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build !zos
2+
// +build !zos
3+
4+
package pty
5+
6+
import (
7+
"os"
8+
"testing"
9+
)
10+
11+
func getNonBlockingFile(t *testing.T, file *os.File, path string) *os.File {
12+
t.Helper()
13+
return file
14+
}

fd_helper_zos_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//go:build zos
2+
// +build zos
3+
4+
package pty
5+
6+
import (
7+
"os"
8+
"testing"
9+
)
10+
11+
func getNonBlockingFile(t *testing.T, file *os.File, path string) *os.File {
12+
t.Helper()
13+
// z/OS doesn't open a pollable FD - fix that here
14+
if _, err := fcntl(uintptr(file.Fd()), F_SETFL, O_NONBLOCK); err != nil {
15+
t.Fatalf("Error: zos-nonblock: %s.\n", err)
16+
}
17+
nf := os.NewFile(file.Fd(), path)
18+
t.Cleanup(func() { _ = nf.Close() })
19+
return nf
20+
}

io_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ func prepare(t *testing.T) (ptmx *os.File, done func()) {
106106
if err != nil {
107107
t.Fatalf("Error: open: %s.\n", err)
108108
}
109-
t.Cleanup(func() { _ = ptmx.Close() })
109+
_ptmx := ptmx
110+
t.Cleanup(func() { _ = _ptmx.Close() })
110111
t.Cleanup(func() { _ = pts.Close() })
111112

113+
ptmx = getNonBlockingFile(t, ptmx, "/dev/ptmx")
114+
112115
ctx, done := context.WithCancel(context.Background())
113116
t.Cleanup(done)
114117
go func() {

pty_unsupported.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris
2-
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris
1+
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris && !zos
2+
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris,!zos
33

44
package pty
55

pty_zos.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//go:build zos
2+
// +build zos
3+
4+
package pty
5+
6+
import (
7+
"os"
8+
"runtime"
9+
"syscall"
10+
"unsafe"
11+
)
12+
13+
const (
14+
SYS_UNLOCKPT = 0x37B
15+
SYS_GRANTPT = 0x37A
16+
SYS_POSIX_OPENPT = 0xC66
17+
SYS_FCNTL = 0x18C
18+
SYS___PTSNAME_A = 0x718
19+
20+
SETCVTON = 1
21+
22+
O_NONBLOCK = 0x04
23+
24+
F_SETFL = 4
25+
F_CONTROL_CVT = 13
26+
)
27+
28+
type f_cnvrt struct {
29+
Cvtcmd int32
30+
Pccsid int16
31+
Fccsid int16
32+
}
33+
34+
func open() (pty, tty *os.File, err error) {
35+
ptmxfd, err := openpt(os.O_RDWR | syscall.O_NOCTTY)
36+
if err != nil {
37+
return nil, nil, err
38+
}
39+
40+
// Needed for z/OS so that the characters are not garbled if ptyp* is untagged
41+
cvtreq := f_cnvrt{Cvtcmd: SETCVTON, Pccsid: 0, Fccsid: 1047}
42+
if _, err = fcntl(uintptr(ptmxfd), F_CONTROL_CVT, uintptr(unsafe.Pointer(&cvtreq))); err != nil {
43+
return nil, nil, err
44+
}
45+
46+
p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
47+
if p == nil {
48+
return nil, nil, err
49+
}
50+
51+
// In case of error after this point, make sure we close the ptmx fd.
52+
defer func() {
53+
if err != nil {
54+
_ = p.Close() // Best effort.
55+
}
56+
}()
57+
58+
sname, err := ptsname(ptmxfd)
59+
if err != nil {
60+
return nil, nil, err
61+
}
62+
63+
_, err = grantpt(ptmxfd)
64+
if err != nil {
65+
return nil, nil, err
66+
}
67+
68+
if _, err = unlockpt(ptmxfd); err != nil {
69+
return nil, nil, err
70+
}
71+
72+
ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
73+
if err != nil {
74+
return nil, nil, err
75+
}
76+
77+
if _, err = fcntl(uintptr(ptsfd), F_CONTROL_CVT, uintptr(unsafe.Pointer(&cvtreq))); err != nil {
78+
return nil, nil, err
79+
}
80+
81+
t := os.NewFile(uintptr(ptsfd), sname)
82+
if err != nil {
83+
return nil, nil, err
84+
}
85+
86+
return p, t, nil
87+
}
88+
89+
func openpt(oflag int) (fd int, err error) {
90+
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_POSIX_OPENPT<<4, uintptr(oflag))
91+
fd = int(r0)
92+
if e1 != 0 {
93+
err = syscall.Errno(e1)
94+
}
95+
return
96+
}
97+
98+
func fcntl(fd uintptr, cmd int, arg uintptr) (val int, err error) {
99+
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_FCNTL<<4, uintptr(fd), uintptr(cmd), arg)
100+
val = int(r0)
101+
if e1 != 0 {
102+
err = syscall.Errno(e1)
103+
}
104+
return
105+
}
106+
107+
func ptsname(fd int) (name string, err error) {
108+
r0, _, e1 := runtime.CallLeFuncWithPtrReturn(runtime.GetZosLibVec()+SYS___PTSNAME_A<<4, uintptr(fd))
109+
name = u2s(unsafe.Pointer(r0))
110+
if e1 != 0 {
111+
err = syscall.Errno(e1)
112+
}
113+
return
114+
}
115+
116+
func grantpt(fildes int) (rc int, err error) {
117+
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_GRANTPT<<4, uintptr(fildes))
118+
rc = int(r0)
119+
if e1 != 0 {
120+
err = syscall.Errno(e1)
121+
}
122+
return
123+
}
124+
125+
func unlockpt(fildes int) (rc int, err error) {
126+
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_UNLOCKPT<<4, uintptr(fildes))
127+
rc = int(r0)
128+
if e1 != 0 {
129+
err = syscall.Errno(e1)
130+
}
131+
return
132+
}
133+
134+
func u2s(cstr unsafe.Pointer) string {
135+
str := (*[1024]uint8)(cstr)
136+
i := 0
137+
for str[i] != 0 {
138+
i++
139+
}
140+
return string(str[:i])
141+
}

0 commit comments

Comments
 (0)