Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
//go:build !ios
package version
import ( "bytes" "encoding/hex" "errors" "io" "os" "path" "runtime" "runtime/debug" "strings")
// CmdName returns either the base name of the current binary// using os.Executable. If os.Executable fails (it shouldn't), then// "cmd" is returned.func CmdName() string { // On non-Windows, the modinfo embedded in the running binary is // authoritative and avoids re-reading the executable from disk. // Windows needs the executable-name-based GUI override in cmdName, // so it still takes the slower path. if runtime.GOOS != "windows" { if info, ok := debug.ReadBuildInfo(); ok && info.Path != "" { return path.Base(info.Path) } } e, err := os.Executable() if err != nil { return "cmd" } return cmdName(e)}
func cmdName(exe string) string { // fallbackName, the lowercase basename of the executable, is what we return if // we can't find the Go module metadata embedded in the file. fallbackName := prepExeNameForCmp(exe, runtime.GOARCH)
var ret string info, err := findModuleInfo(exe) if err != nil { return fallbackName } // v is like: // "path\ttailscale.com/cmd/tailscale\nmod\ttailscale.com\t(devel)\t\ndep\tgithub.com/apenwarr/fixconsole\tv0.0.0-20191012055117-5a9f6489cc29\th1:muXWUcay7DDy1/hEQWrYlBy+g0EuwT70sBHg65SeUc4=\ndep\tgithub.... for line := range strings.SplitSeq(info, "\n") { if goPkg, ok := strings.CutPrefix(line, "path\t"); ok { // like "tailscale.com/cmd/tailscale" ret = path.Base(goPkg) // goPkg is always forward slashes; use path, not filepath break } } if runtime.GOOS == "windows" && strings.HasPrefix(ret, "gui") && checkPreppedExeNameForGUI(fallbackName) { // The GUI binary for internal build system packaging reasons // has a path of "tailscale.io/win/gui". // Ignore that name and use fallbackName instead. return fallbackName } if ret == "" { return fallbackName } return ret}
// findModuleInfo returns the Go module info from the executable file.func findModuleInfo(file string) (s string, err error) { f, err := os.Open(file) if err != nil { return "", err } defer f.Close() // Scan through f until we find infoStart. buf := make([]byte, 65536) start, err := findOffset(f, buf, infoStart) if err != nil { return "", err } start += int64(len(infoStart)) // Seek to the end of infoStart and scan for infoEnd. _, err = f.Seek(start, io.SeekStart) if err != nil { return "", err } end, err := findOffset(f, buf, infoEnd) if err != nil { return "", err } length := end - start // As of Aug 2021, tailscaled's mod info was about 2k. if length > int64(len(buf)) { return "", errors.New("mod info too large") } // We have located modinfo. Read it into buf. buf = buf[:length] _, err = f.Seek(start, io.SeekStart) if err != nil { return "", err } _, err = io.ReadFull(f, buf) if err != nil { return "", err } return string(buf), nil}
// findOffset finds the absolute offset of needle in f,// starting at f's current read position,// using temporary buffer buf.func findOffset(f *os.File, buf, needle []byte) (int64, error) { for { // Fill buf and look within it. n, err := f.Read(buf) if err != nil { return -1, err } i := bytes.Index(buf[:n], needle) if i < 0 { // Not found. Rewind a little bit in case we happened to end halfway through needle. rewind, err := f.Seek(int64(-len(needle)), io.SeekCurrent) if err != nil { return -1, err } // If we're at EOF and rewound exactly len(needle) bytes, return io.EOF. _, err = f.ReadAt(buf[:1], rewind+int64(len(needle))) if err == io.EOF { return -1, err } continue } // Found! Figure out exactly where. cur, err := f.Seek(0, io.SeekCurrent) if err != nil { return -1, err } return cur - int64(n) + int64(i), nil }}
// These constants are taken from rsc.io/goversion.
var ( infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6") infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2"))