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