github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/spotty/spotty_nix.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  //go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
     5  // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
     6  
     7  package spotty
     8  
     9  import (
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"syscall"
    14  )
    15  
    16  func sameFile(ss1 *syscall.Stat_t, fi os.FileInfo) bool {
    17  	if ss2, ok := fi.Sys().(*syscall.Stat_t); ok {
    18  		return ss1.Dev == ss2.Dev && ss1.Rdev == ss2.Rdev && ss1.Ino == ss2.Ino
    19  	}
    20  	return false
    21  }
    22  
    23  // Discover which named TTY we have open as FD=0. Will return empty string if nothing
    24  // was found, and a non-nil error if there was a problem. Will noop on Windows.
    25  func Discover() (string, error) {
    26  	var sstat syscall.Stat_t
    27  	if err := syscall.Fstat(0, &sstat); err != nil {
    28  		return "", err
    29  	}
    30  	res, err := findFileIn(&sstat, "/dev", regexp.MustCompile(`^tty[A-Za-z0-9]+$`))
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  	if len(res) > 0 {
    35  		return res, nil
    36  	}
    37  	res, err = findFileIn(&sstat, "/dev/pts", regexp.MustCompile(`^[0-9]+$`))
    38  	return res, err
    39  }
    40  
    41  func findFileIn(ss *syscall.Stat_t, dir string, re *regexp.Regexp) (string, error) {
    42  	v, err := os.ReadDir(dir)
    43  	if err != nil {
    44  		if _, ok := err.(*os.PathError); ok {
    45  			return "", nil
    46  		}
    47  		return "", err
    48  	}
    49  	for _, fi := range v {
    50  		if !re.MatchString(fi.Name()) {
    51  			continue
    52  		}
    53  		info, err := fi.Info()
    54  		if err != nil {
    55  			return "", err
    56  		}
    57  		if sameFile(ss, info) {
    58  			return filepath.Join(dir, fi.Name()), nil
    59  		}
    60  	}
    61  	return "", nil
    62  }