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