Skip to content

Commit

Permalink
Merge pull request #201 from MacMalainey/master
Browse files Browse the repository at this point in the history
add z/OS support
  • Loading branch information
creack authored Oct 31, 2024
2 parents 2cde18b + 7c00df3 commit edfbf75
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 3 deletions.
14 changes: 14 additions & 0 deletions fd_helper_other_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:build !zos
// +build !zos

package pty

import (
"os"
"testing"
)

func getNonBlockingFile(t *testing.T, file *os.File, path string) *os.File {
t.Helper()
return file
}
20 changes: 20 additions & 0 deletions fd_helper_zos_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//go:build zos
// +build zos

package pty

import (
"os"
"testing"
)

func getNonBlockingFile(t *testing.T, file *os.File, path string) *os.File {
t.Helper()
// z/OS doesn't open a pollable FD - fix that here
if _, err := fcntl(uintptr(file.Fd()), F_SETFL, O_NONBLOCK); err != nil {
t.Fatalf("Error: zos-nonblock: %s.\n", err)
}
nf := os.NewFile(file.Fd(), path)
t.Cleanup(func() { _ = nf.Close() })
return nf
}
5 changes: 4 additions & 1 deletion io_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,12 @@ func prepare(t *testing.T) (ptmx *os.File, done func()) {
if err != nil {
t.Fatalf("Error: open: %s.\n", err)
}
t.Cleanup(func() { _ = ptmx.Close() })
_ptmx := ptmx
t.Cleanup(func() { _ = _ptmx.Close() })
t.Cleanup(func() { _ = pts.Close() })

ptmx = getNonBlockingFile(t, ptmx, "/dev/ptmx")

ctx, done := context.WithCancel(context.Background())
t.Cleanup(done)
go func() {
Expand Down
4 changes: 2 additions & 2 deletions pty_unsupported.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris && !zos
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris,!zos

package pty

Expand Down
141 changes: 141 additions & 0 deletions pty_zos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//go:build zos
// +build zos

package pty

import (
"os"
"runtime"
"syscall"
"unsafe"
)

const (
SYS_UNLOCKPT = 0x37B
SYS_GRANTPT = 0x37A
SYS_POSIX_OPENPT = 0xC66
SYS_FCNTL = 0x18C
SYS___PTSNAME_A = 0x718

SETCVTON = 1

O_NONBLOCK = 0x04

F_SETFL = 4
F_CONTROL_CVT = 13
)

type f_cnvrt struct {
Cvtcmd int32
Pccsid int16
Fccsid int16
}

func open() (pty, tty *os.File, err error) {
ptmxfd, err := openpt(os.O_RDWR | syscall.O_NOCTTY)
if err != nil {
return nil, nil, err
}

// Needed for z/OS so that the characters are not garbled if ptyp* is untagged
cvtreq := f_cnvrt{Cvtcmd: SETCVTON, Pccsid: 0, Fccsid: 1047}
if _, err = fcntl(uintptr(ptmxfd), F_CONTROL_CVT, uintptr(unsafe.Pointer(&cvtreq))); err != nil {
return nil, nil, err
}

p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
if p == nil {
return nil, nil, err
}

// 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(ptmxfd)
if err != nil {
return nil, nil, err
}

_, err = grantpt(ptmxfd)
if err != nil {
return nil, nil, err
}

if _, err = unlockpt(ptmxfd); 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
}

if _, err = fcntl(uintptr(ptsfd), F_CONTROL_CVT, uintptr(unsafe.Pointer(&cvtreq))); err != nil {
return nil, nil, err
}

t := os.NewFile(uintptr(ptsfd), sname)
if err != nil {
return nil, nil, err
}

return p, t, nil
}

func openpt(oflag int) (fd int, err error) {
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_POSIX_OPENPT<<4, uintptr(oflag))
fd = int(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}

func fcntl(fd uintptr, cmd int, arg uintptr) (val int, err error) {
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_FCNTL<<4, uintptr(fd), uintptr(cmd), arg)
val = int(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}

func ptsname(fd int) (name string, err error) {
r0, _, e1 := runtime.CallLeFuncWithPtrReturn(runtime.GetZosLibVec()+SYS___PTSNAME_A<<4, uintptr(fd))
name = u2s(unsafe.Pointer(r0))
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}

func grantpt(fildes int) (rc int, err error) {
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_GRANTPT<<4, uintptr(fildes))
rc = int(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}

func unlockpt(fildes int) (rc int, err error) {
r0, _, e1 := runtime.CallLeFuncWithErr(runtime.GetZosLibVec()+SYS_UNLOCKPT<<4, uintptr(fildes))
rc = int(r0)
if e1 != 0 {
err = syscall.Errno(e1)
}
return
}

func u2s(cstr unsafe.Pointer) string {
str := (*[1024]uint8)(cstr)
i := 0
for str[i] != 0 {
i++
}
return string(str[:i])
}

0 comments on commit edfbf75

Please sign in to comment.