github.com/dolfly/pty@v1.2.1/pty_solaris.go (about) 1 //go:build solaris 2 //+build solaris 3 4 package pty 5 6 /* based on: 7 http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c 8 */ 9 10 import ( 11 "errors" 12 "os" 13 "strconv" 14 "syscall" 15 "unsafe" 16 ) 17 18 func open() (pty, tty *os.File, err error) { 19 ptmxfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY, 0) 20 if err != nil { 21 return nil, nil, err 22 } 23 p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx") 24 // In case of error after this point, make sure we close the ptmx fd. 25 defer func() { 26 if err != nil { 27 _ = p.Close() // Best effort. 28 } 29 }() 30 31 sname, err := ptsname(p) 32 if err != nil { 33 return nil, nil, err 34 } 35 36 if err := grantpt(p); err != nil { 37 return nil, nil, err 38 } 39 40 if err := unlockpt(p); err != nil { 41 return nil, nil, err 42 } 43 44 ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0) 45 if err != nil { 46 return nil, nil, err 47 } 48 t := os.NewFile(uintptr(ptsfd), sname) 49 50 // In case of error after this point, make sure we close the pts fd. 51 defer func() { 52 if err != nil { 53 _ = t.Close() // Best effort. 54 } 55 }() 56 57 // pushing terminal driver STREAMS modules as per pts(7) 58 for _, mod := range []string{"ptem", "ldterm", "ttcompat"} { 59 if err := streamsPush(t, mod); err != nil { 60 return nil, nil, err 61 } 62 } 63 64 return p, t, nil 65 } 66 67 func ptsname(f *os.File) (string, error) { 68 dev, err := ptsdev(f.Fd()) 69 if err != nil { 70 return "", err 71 } 72 fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10) 73 74 if err := syscall.Access(fn, 0); err != nil { 75 return "", err 76 } 77 return fn, nil 78 } 79 80 func unlockpt(f *os.File) error { 81 istr := strioctl{ 82 icCmd: UNLKPT, 83 icTimeout: 0, 84 icLen: 0, 85 icDP: nil, 86 } 87 return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))) 88 } 89 90 func minor(x uint64) uint64 { return x & 0377 } 91 92 func ptsdev(fd uintptr) (uint64, error) { 93 istr := strioctl{ 94 icCmd: ISPTM, 95 icTimeout: 0, 96 icLen: 0, 97 icDP: nil, 98 } 99 100 if err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil { 101 return 0, err 102 } 103 var status syscall.Stat_t 104 if err := syscall.Fstat(int(fd), &status); err != nil { 105 return 0, err 106 } 107 return uint64(minor(status.Rdev)), nil 108 } 109 110 type ptOwn struct { 111 rUID int32 112 rGID int32 113 } 114 115 func grantpt(f *os.File) error { 116 if _, err := ptsdev(f.Fd()); err != nil { 117 return err 118 } 119 pto := ptOwn{ 120 rUID: int32(os.Getuid()), 121 // XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty" 122 rGID: int32(os.Getgid()), 123 } 124 istr := strioctl{ 125 icCmd: OWNERPT, 126 icTimeout: 0, 127 icLen: int32(unsafe.Sizeof(strioctl{})), 128 icDP: unsafe.Pointer(&pto), 129 } 130 if err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))); err != nil { 131 return errors.New("access denied") 132 } 133 return nil 134 } 135 136 // streamsPush pushes STREAMS modules if not already done so. 137 func streamsPush(f *os.File, mod string) error { 138 buf := []byte(mod) 139 140 // XXX I_FIND is not returning an error when the module 141 // is already pushed even though truss reports a return 142 // value of 1. A bug in the Go Solaris syscall interface? 143 // XXX without this we are at risk of the issue 144 // https://www.illumos.org/issues/9042 145 // but since we are not using libc or XPG4.2, we should not be 146 // double-pushing modules 147 148 if err := ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil { 149 return nil 150 } 151 return ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0]))) 152 }