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