tailscale/hostinfo/hostinfo_windows.go

97 lines
2.3 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package hostinfo
import (
"fmt"
"os"
"path/filepath"
"strings"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"tailscale.com/types/ptr"
"tailscale.com/util/winutil"
"tailscale.com/util/winutil/winenv"
)
func init() {
distroName = lazyDistroName.Get
osVersion = lazyOSVersion.Get
packageType = lazyPackageType.Get
}
var (
lazyDistroName = &lazyAtomicValue[string]{f: ptr.To(distroNameWindows)}
lazyOSVersion = &lazyAtomicValue[string]{f: ptr.To(osVersionWindows)}
lazyPackageType = &lazyAtomicValue[string]{f: ptr.To(packageTypeWindows)}
)
func distroNameWindows() string {
if winenv.IsWindowsServer() {
return "Server"
}
return ""
}
func osVersionWindows() string {
major, minor, build := windows.RtlGetNtVersionNumbers()
s := fmt.Sprintf("%d.%d.%d", major, minor, build)
// Windows 11 still uses 10 as its major number internally
if major == 10 {
if ubr, err := getUBR(); err == nil {
s += fmt.Sprintf(".%d", ubr)
}
}
return s // "10.0.19041.388", ideally
}
// getUBR obtains a fourth version field, the "Update Build Revision",
// from the registry. This field is only available beginning with Windows 10.
func getUBR() (uint32, error) {
key, err := registry.OpenKey(registry.LOCAL_MACHINE,
`SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE|registry.WOW64_64KEY)
if err != nil {
return 0, err
}
defer key.Close()
val, valType, err := key.GetIntegerValue("UBR")
if err != nil {
return 0, err
}
if valType != registry.DWORD {
return 0, registry.ErrUnexpectedType
}
return uint32(val), nil
}
func packageTypeWindows() string {
if _, err := os.Stat(`C:\ProgramData\chocolatey\lib\tailscale`); err == nil {
return "choco"
}
msiSentinel, _ := winutil.GetRegInteger("MSI")
if msiSentinel == 1 {
return "msi"
}
exe, err := os.Executable()
if err != nil {
return ""
}
home, _ := os.UserHomeDir()
if strings.HasPrefix(exe, filepath.Join(home, "scoop", "apps", "tailscale")) {
return "scoop"
}
dir := filepath.Dir(exe)
nsisUninstaller := filepath.Join(dir, "Uninstall-Tailscale.exe")
_, err = os.Stat(nsisUninstaller)
if err == nil {
return "nsis"
}
// Atypical. Not worth trying to detect. Likely open
// source tailscaled or a developer running by hand.
return ""
}