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  }