File: pty_solaris.go

package info (click to toggle)
golang-pty 1.1.6-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 244 kB
  • sloc: sh: 140; makefile: 3
file content (139 lines) | stat: -rw-r--r-- 3,180 bytes parent folder | download | duplicates (3)
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
package pty

/* based on:
http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c
*/

import (
	"errors"
	"golang.org/x/sys/unix"
	"os"
	"strconv"
	"syscall"
	"unsafe"
)

const NODEV = ^uint64(0)

func open() (pty, tty *os.File, err error) {
	masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|unix.O_NOCTTY, 0)
	//masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC|unix.O_NOCTTY, 0)
	if err != nil {
		return nil, nil, err
	}
	p := os.NewFile(uintptr(masterfd), "/dev/ptmx")

	sname, err := ptsname(p)
	if err != nil {
		return nil, nil, err
	}

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

	err = unlockpt(p)
	if err != nil {
		return nil, nil, err
	}

	slavefd, err := syscall.Open(sname, os.O_RDWR|unix.O_NOCTTY, 0)
	if err != nil {
		return nil, nil, err
	}
	t := os.NewFile(uintptr(slavefd), sname)

	// pushing terminal driver STREAMS modules as per pts(7)
	for _, mod := range([]string{"ptem", "ldterm", "ttcompat"}) {
		err = streams_push(t, mod)
		if err != nil {
			return nil, nil, err
		}
	}
	
	return p, t, nil
}

func minor(x uint64) uint64 {
	return x & 0377
}

func ptsdev(fd uintptr) uint64 {
	istr := strioctl{ISPTM, 0, 0, nil}
	err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr)))
	if err != nil {
		return NODEV
	}
	var status unix.Stat_t
	err = unix.Fstat(int(fd), &status)
	if err != nil {
		return NODEV
	}
	return uint64(minor(status.Rdev))
}

func ptsname(f *os.File) (string, error) {
	dev := ptsdev(f.Fd())
	if dev == NODEV {
		return "", errors.New("not a master pty")
	}
	fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10)
	// access(2) creates the slave device (if the pty exists)
	// F_OK == 0 (unistd.h)
	err := unix.Access(fn, 0)
	if err != nil {
		return "", err
	}
	return fn, nil
}

type pt_own struct {
	pto_ruid int32
	pto_rgid int32
}

func grantpt(f *os.File) error {
	if ptsdev(f.Fd()) == NODEV {
		return errors.New("not a master pty")
	}
	var pto pt_own
	pto.pto_ruid = int32(os.Getuid())
	// XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
	pto.pto_rgid = int32(os.Getgid())
	var istr strioctl
	istr.ic_cmd = OWNERPT
	istr.ic_timout = 0
	istr.ic_len = int32(unsafe.Sizeof(istr))
	istr.ic_dp = unsafe.Pointer(&pto)
	err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
	if err != nil {
		return errors.New("access denied")
	}
	return nil
}

func unlockpt(f *os.File) error {
	istr := strioctl{UNLKPT, 0, 0, nil}
	return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
}

// push STREAMS modules if not already done so
func streams_push(f *os.File, mod string) error {
	var err 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
	
	err = ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0])))
	if err != nil {
		return nil
	}
	err = ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
	return err
}