From 24cd26534fdc4ae94181e94d8125c15cf975d13d Mon Sep 17 00:00:00 2001 From: Tom DNetto Date: Thu, 7 Apr 2022 11:42:48 -0700 Subject: [PATCH] hostinfo, tailcfg: add desktop detection on Linux to hostinfo From the machines tab its hard to differenciate desktop Linux installs from server Linux installs. Transmitting this information should make this determination a lot easier. Due to the reality that tailscaled is likely a system process, the standard checks based on XDG_SESSION_TYPE or DISPLAY environment variables are not possible (those variables won't be set). Instead, we look for listening unix sockets that are typical of desktop installs. Signed-off-by: Tom DNetto --- hostinfo/hostinfo.go | 30 ++++++++++++++++++++++++++++++ tailcfg/tailcfg.go | 1 + tailcfg/tailcfg_clone.go | 1 + tailcfg/tailcfg_test.go | 2 +- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hostinfo/hostinfo.go b/hostinfo/hostinfo.go index 50ce6c896..9f859b347 100644 --- a/hostinfo/hostinfo.go +++ b/hostinfo/hostinfo.go @@ -17,11 +17,14 @@ "go4.org/mem" "tailscale.com/tailcfg" + "tailscale.com/types/opt" "tailscale.com/util/dnsname" "tailscale.com/util/lineread" "tailscale.com/version" ) +var started = time.Now() + // New returns a partially populated Hostinfo for the current host. func New() *tailcfg.Hostinfo { hostname, _ := os.Hostname() @@ -31,6 +34,7 @@ func New() *tailcfg.Hostinfo { Hostname: hostname, OS: version.OS(), OSVersion: GetOSVersion(), + Desktop: desktop(), Package: packageTypeCached(), GoArch: runtime.GOARCH, DeviceModel: deviceModel(), @@ -97,6 +101,7 @@ func GetEnvType() EnvType { var ( deviceModelAtomic atomic.Value // of string osVersionAtomic atomic.Value // of string + desktopAtomic atomic.Value // of opt.Bool packagingType atomic.Value // of string ) @@ -117,6 +122,31 @@ func deviceModel() string { return s } +func desktop() (ret opt.Bool) { + if runtime.GOOS != "linux" { + return opt.Bool("") + } + if v := desktopAtomic.Load(); v != nil { + v, _ := v.(opt.Bool) + return v + } + + seenDesktop := false + lineread.File("/proc/net/unix", func(line []byte) error { + seenDesktop = seenDesktop || mem.Contains(mem.B(line), mem.S(" @/tmp/dbus-")) + seenDesktop = seenDesktop || mem.Contains(mem.B(line), mem.S(".X11-unix")) + seenDesktop = seenDesktop || mem.Contains(mem.B(line), mem.S("/wayland-1")) + return nil + }) + ret.Set(seenDesktop) + + // Only cache after a minute - compositors might not have started yet. + if time.Since(started) > time.Minute { + desktopAtomic.Store(ret) + } + return ret +} + func getEnvType() EnvType { if inKnative() { return KNative diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 13a9a6b38..9e35102d1 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -460,6 +460,7 @@ type Hostinfo struct { BackendLogID string `json:",omitempty"` // logtail ID of backend instance OS string `json:",omitempty"` // operating system the client runs on (a version.OS value) OSVersion string `json:",omitempty"` // operating system version, with optional distro prefix ("Debian 10.4", "Windows 10 Pro 10.0.19041") + Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown) DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3") Hostname string `json:",omitempty"` // name of the host the client runs on diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index b1a3572ba..c94e8d5b4 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -117,6 +117,7 @@ func (src *Hostinfo) Clone() *Hostinfo { BackendLogID string OS string OSVersion string + Desktop opt.Bool Package string DeviceModel string Hostname string diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 6d580064b..e5135a407 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -28,7 +28,7 @@ func fieldsOf(t reflect.Type) (fields []string) { func TestHostinfoEqual(t *testing.T) { hiHandles := []string{ "IPNVersion", "FrontendLogID", "BackendLogID", - "OS", "OSVersion", "Package", "DeviceModel", "Hostname", + "OS", "OSVersion", "Desktop", "Package", "DeviceModel", "Hostname", "ShieldsUp", "ShareeNode", "GoArch", "RoutableIPs", "RequestTags",