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