tailcfg,hostinfo: add Hostinfo.Machine and Hostinfo.GoArchVar

For detecting a non-ideal binary running on the current CPU.

And for helping detect the best Synology package to update to.

Updates #6995

Change-Id: I722f806675b60ce95364471b11c388150c0d4aea
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/7037/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent fd92fbd69e
commit 64547b2b86

@ -11,6 +11,7 @@ import (
"io" "io"
"os" "os"
"runtime" "runtime"
"runtime/debug"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -46,7 +47,9 @@ func New() *tailcfg.Hostinfo {
Desktop: desktop(), Desktop: desktop(),
Package: packageTypeCached(), Package: packageTypeCached(),
GoArch: runtime.GOARCH, GoArch: runtime.GOARCH,
GoArchVar: lazyGoArchVar.Get(),
GoVersion: runtime.Version(), GoVersion: runtime.Version(),
Machine: condCall(unameMachine),
DeviceModel: deviceModel(), DeviceModel: deviceModel(),
Cloud: string(cloudenv.Get()), Cloud: string(cloudenv.Get()),
NoLogsNoSupport: envknob.NoLogsNoSupport(), NoLogsNoSupport: envknob.NoLogsNoSupport(),
@ -60,6 +63,7 @@ var (
distroName func() string distroName func() string
distroVersion func() string distroVersion func() string
distroCodeName func() string distroCodeName func() string
unameMachine func() string
) )
func condCall[T any](fn func() T) T { func condCall[T any](fn func() T) T {
@ -72,6 +76,7 @@ func condCall[T any](fn func() T) T {
var ( var (
lazyInContainer = &lazyAtomicValue[opt.Bool]{f: ptr.To(inContainer)} lazyInContainer = &lazyAtomicValue[opt.Bool]{f: ptr.To(inContainer)}
lazyGoArchVar = &lazyAtomicValue[string]{f: ptr.To(goArchVar)}
) )
type lazyAtomicValue[T any] struct { type lazyAtomicValue[T any] struct {
@ -321,6 +326,27 @@ func inDockerDesktop() bool {
return false return false
} }
// goArchVar returns the GOARM or GOAMD64 etc value that the binary was built
// with.
func goArchVar() string {
bi, ok := debug.ReadBuildInfo()
if !ok {
return ""
}
// Look for GOARM, GOAMD64, GO386, etc. Note that the little-endian
// "le"-suffixed GOARCH values don't have their own environment variable.
//
// See https://pkg.go.dev/cmd/go#hdr-Environment_variables and the
// "Architecture-specific environment variables" section:
wantKey := "GO" + strings.ToUpper(strings.TrimSuffix(runtime.GOARCH, "le"))
for _, s := range bi.Settings {
if s.Key == wantKey {
return s.Value
}
}
return ""
}
type etcAptSrcResult struct { type etcAptSrcResult struct {
mod time.Time mod time.Time
disabled bool disabled bool

@ -0,0 +1,39 @@
// Copyright (c) 2023 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || freebsd || openbsd || darwin
package hostinfo
import (
"runtime"
"golang.org/x/sys/unix"
"tailscale.com/types/ptr"
)
func init() {
unameMachine = lazyUnameMachine.Get
}
var lazyUnameMachine = &lazyAtomicValue[string]{f: ptr.To(unameMachineUnix)}
func unameMachineUnix() string {
switch runtime.GOOS {
case "android":
// Don't call on Android for now. We're late in the 1.36 release cycle
// and don't want to test syscall filters on various Android versions to
// see what's permitted. Notably, the hostinfo_linux.go file has build
// tag !android, so maybe Uname is verboten.
return ""
case "ios":
// For similar reasons, don't call on iOS. There aren't many iOS devices
// and we know their CPU properties so calling this is only risk and no
// reward.
return ""
}
var un unix.Utsname
unix.Uname(&un)
return unix.ByteSliceToString(un.Machine[:])
}

@ -528,7 +528,9 @@ type Hostinfo struct {
ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user
NoLogsNoSupport bool `json:",omitempty"` // indicates that the user has opted out of sending logs and support NoLogsNoSupport bool `json:",omitempty"` // indicates that the user has opted out of sending logs and support
WireIngress bool `json:",omitempty"` // indicates that the node wants the option to receive ingress connections WireIngress bool `json:",omitempty"` // indicates that the node wants the option to receive ingress connections
GoArch string `json:",omitempty"` // the host's GOARCH value (of the running binary) Machine string `json:",omitempty"` // the current host's machine type (uname -m)
GoArch string `json:",omitempty"` // GOARCH value (of the built binary)
GoArchVar string `json:",omitempty"` // GOARM, GOAMD64, etc (of the built binary)
GoVersion string `json:",omitempty"` // Go version binary was built with GoVersion string `json:",omitempty"` // Go version binary was built with
RoutableIPs []netip.Prefix `json:",omitempty"` // set of IP ranges this client can route RoutableIPs []netip.Prefix `json:",omitempty"` // set of IP ranges this client can route
RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim

@ -137,7 +137,9 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct {
ShareeNode bool ShareeNode bool
NoLogsNoSupport bool NoLogsNoSupport bool
WireIngress bool WireIngress bool
Machine string
GoArch string GoArch string
GoArchVar string
GoVersion string GoVersion string
RoutableIPs []netip.Prefix RoutableIPs []netip.Prefix
RequestTags []string RequestTags []string

@ -50,7 +50,9 @@ func TestHostinfoEqual(t *testing.T) {
"ShareeNode", "ShareeNode",
"NoLogsNoSupport", "NoLogsNoSupport",
"WireIngress", "WireIngress",
"Machine",
"GoArch", "GoArch",
"GoArchVar",
"GoVersion", "GoVersion",
"RoutableIPs", "RoutableIPs",
"RequestTags", "RequestTags",

@ -276,7 +276,9 @@ func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp }
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode } func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport } func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport }
func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress } func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress }
func (v HostinfoView) Machine() string { return v.ж.Machine }
func (v HostinfoView) GoArch() string { return v.ж.GoArch } func (v HostinfoView) GoArch() string { return v.ж.GoArch }
func (v HostinfoView) GoArchVar() string { return v.ж.GoArchVar }
func (v HostinfoView) GoVersion() string { return v.ж.GoVersion } func (v HostinfoView) GoVersion() string { return v.ж.GoVersion }
func (v HostinfoView) RoutableIPs() views.IPPrefixSlice { func (v HostinfoView) RoutableIPs() views.IPPrefixSlice {
return views.IPPrefixSliceOf(v.ж.RoutableIPs) return views.IPPrefixSliceOf(v.ж.RoutableIPs)
@ -310,7 +312,9 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
ShareeNode bool ShareeNode bool
NoLogsNoSupport bool NoLogsNoSupport bool
WireIngress bool WireIngress bool
Machine string
GoArch string GoArch string
GoArchVar string
GoVersion string GoVersion string
RoutableIPs []netip.Prefix RoutableIPs []netip.Prefix
RequestTags []string RequestTags []string

Loading…
Cancel
Save