github.com/v2fly/tools@v0.100.0/internal/lsp/lsprpc/autostart_posix.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 7 8 package lsprpc 9 10 import ( 11 "crypto/sha256" 12 "errors" 13 "fmt" 14 exec "golang.org/x/sys/execabs" 15 "log" 16 "os" 17 "os/user" 18 "path/filepath" 19 "strconv" 20 "syscall" 21 22 "golang.org/x/xerrors" 23 ) 24 25 func init() { 26 startRemote = startRemotePosix 27 autoNetworkAddress = autoNetworkAddressPosix 28 verifyRemoteOwnership = verifyRemoteOwnershipPosix 29 } 30 31 func startRemotePosix(goplsPath string, args ...string) error { 32 cmd := exec.Command(goplsPath, args...) 33 cmd.SysProcAttr = &syscall.SysProcAttr{ 34 Setsid: true, 35 } 36 if err := cmd.Start(); err != nil { 37 return xerrors.Errorf("starting remote gopls: %w", err) 38 } 39 return nil 40 } 41 42 // autoNetworkAddress resolves an id on the 'auto' pseduo-network to a 43 // real network and address. On unix, this uses unix domain sockets. 44 func autoNetworkAddressPosix(goplsPath, id string) (network string, address string) { 45 // Especially when doing local development or testing, it's important that 46 // the remote gopls instance we connect to is running the same binary as our 47 // forwarder. So we encode a short hash of the binary path into the daemon 48 // socket name. If possible, we also include the buildid in this hash, to 49 // account for long-running processes where the binary has been subsequently 50 // rebuilt. 51 h := sha256.New() 52 cmd := exec.Command("go", "tool", "buildid", goplsPath) 53 cmd.Stdout = h 54 var pathHash []byte 55 if err := cmd.Run(); err == nil { 56 pathHash = h.Sum(nil) 57 } else { 58 log.Printf("error getting current buildid: %v", err) 59 sum := sha256.Sum256([]byte(goplsPath)) 60 pathHash = sum[:] 61 } 62 shortHash := fmt.Sprintf("%x", pathHash)[:6] 63 user := os.Getenv("USER") 64 if user == "" { 65 user = "shared" 66 } 67 basename := filepath.Base(goplsPath) 68 idComponent := "" 69 if id != "" { 70 idComponent = "-" + id 71 } 72 runtimeDir := os.TempDir() 73 if xdg := os.Getenv("XDG_RUNTIME_DIR"); xdg != "" { 74 runtimeDir = xdg 75 } 76 return "unix", filepath.Join(runtimeDir, fmt.Sprintf("%s-%s-daemon.%s%s", basename, shortHash, user, idComponent)) 77 } 78 79 func verifyRemoteOwnershipPosix(network, address string) (bool, error) { 80 if network != "unix" { 81 return true, nil 82 } 83 fi, err := os.Stat(address) 84 if err != nil { 85 if os.IsNotExist(err) { 86 return true, nil 87 } 88 return false, xerrors.Errorf("checking socket owner: %w", err) 89 } 90 stat, ok := fi.Sys().(*syscall.Stat_t) 91 if !ok { 92 return false, errors.New("fi.Sys() is not a Stat_t") 93 } 94 user, err := user.Current() 95 if err != nil { 96 return false, xerrors.Errorf("checking current user: %w", err) 97 } 98 uid, err := strconv.ParseUint(user.Uid, 10, 32) 99 if err != nil { 100 return false, xerrors.Errorf("parsing current UID: %w", err) 101 } 102 return stat.Uid == uint32(uid), nil 103 }