From 3859fe2f521390503bac26855b232ac2129a4332 Mon Sep 17 00:00:00 2001 From: Ivan Andreev Date: Mon, 5 Apr 2021 21:53:09 +0300 Subject: [PATCH] cmd/version: print os/version, kernel and bitness (#5204) Related to #5121 Note: OpenBSD is stub yet. This will be fixed after upstream PR gets resolved https://github.com/shirou/gopsutil/pull/993 --- cmd/cmd.go | 11 +++ cmd/version/version.go | 9 +- go.mod | 1 + go.sum | 11 +++ lib/buildinfo/osversion.go | 34 ++++++++ lib/buildinfo/osversion_openbsd.go | 13 +++ lib/buildinfo/osversion_windows.go | 131 +++++++++++++++++++++++++++++ 7 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 lib/buildinfo/osversion.go create mode 100644 lib/buildinfo/osversion_openbsd.go create mode 100644 lib/buildinfo/osversion_windows.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 637b91b39..4d0c053b7 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -75,8 +75,19 @@ const ( // ShowVersion prints the version to stdout func ShowVersion() { + osVersion, osKernel := buildinfo.GetOSVersion() + if osVersion == "" { + osVersion = "unknown" + } + if osKernel == "" { + osKernel = "unknown" + } + linking, tagString := buildinfo.GetLinkingAndTags() + fmt.Printf("rclone %s\n", fs.Version) + fmt.Printf("- os/version: %s\n", osVersion) + fmt.Printf("- os/kernel: %s\n", osKernel) fmt.Printf("- os/type: %s\n", runtime.GOOS) fmt.Printf("- os/arch: %s\n", runtime.GOARCH) fmt.Printf("- go/version: %s\n", runtime.Version()) diff --git a/cmd/version/version.go b/cmd/version/version.go index 0ccfe8e3f..1f72b178a 100644 --- a/cmd/version/version.go +++ b/cmd/version/version.go @@ -29,13 +29,16 @@ var commandDefinition = &cobra.Command{ Use: "version", Short: `Show the version number.`, Long: ` -Show the rclone version number, the go version, the build target OS and -architecture, build tags and the type of executable (static or dynamic). +Show the rclone version number, the go version, the build target +OS and architecture, the runtime OS and kernel version and bitness, +build tags and the type of executable (static or dynamic). For example: $ rclone version - rclone v1.54 + rclone v1.55.0 + - os/version: ubuntu 18.04 (64 bit) + - os/kernel: 4.15.0-136-generic (x86_64) - os/type: linux - os/arch: amd64 - go/version: go1.16 diff --git a/go.mod b/go.mod index cd69b1e47..1e56ccd3d 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sevlyar/go-daemon v0.1.5 + github.com/shirou/gopsutil/v3 v3.21.3 github.com/sirupsen/logrus v1.7.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cobra v1.1.1 diff --git a/go.sum b/go.sum index af5d8682c..cc9093cb8 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 h1:1TrMV1HmBApBbM+Hy7RCKZD6UlYWYIPPfoeXomG7+zE= github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -209,6 +211,8 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -558,6 +562,8 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk= github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE= +github.com/shirou/gopsutil/v3 v3.21.3 h1:wgcdAHZS2H6qy4JFewVTtqfiYxFzCeEJod/mLztdPG8= +github.com/shirou/gopsutil/v3 v3.21.3/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -610,6 +616,10 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/t3rm1n4l/go-mega v0.0.0-20200416171014-ffad7fcb44b8 h1:IGJQmLBLYBdAknj21W3JsVof0yjEXfy1Q0K3YZebDOg= github.com/t3rm1n4l/go-mega v0.0.0-20200416171014-ffad7fcb44b8/go.mod h1:XWL4vDyd3JKmJx+hZWUVgCNmmhZ2dTBcaNDcxH465s0= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M= +github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= +github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc= +github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= @@ -848,6 +858,7 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112091331-59c308dcf3cc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210313110737-8e9fff1a3a18 h1:jxr7/dEo+rR29uEBoLSWJ1tRHCFAMwFbGUU9nRqzpds= golang.org/x/sys v0.0.0-20210313110737-8e9fff1a3a18/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= diff --git a/lib/buildinfo/osversion.go b/lib/buildinfo/osversion.go new file mode 100644 index 000000000..d229bea18 --- /dev/null +++ b/lib/buildinfo/osversion.go @@ -0,0 +1,34 @@ +// +build !openbsd,!windows + +package buildinfo + +import ( + "strings" + + "github.com/shirou/gopsutil/v3/host" +) + +// GetOSVersion returns OS version, kernel and bitness +func GetOSVersion() (osVersion, osKernel string) { + if platform, _, version, err := host.PlatformInformation(); err == nil && platform != "" { + osVersion = platform + if version != "" { + osVersion += " " + version + } + } + + if version, err := host.KernelVersion(); err == nil && version != "" { + osKernel = version + } + + if arch, err := host.KernelArch(); err == nil && arch != "" { + if strings.HasSuffix(arch, "64") && osVersion != "" { + osVersion += " (64 bit)" + } + if osKernel != "" { + osKernel += " (" + arch + ")" + } + } + + return +} diff --git a/lib/buildinfo/osversion_openbsd.go b/lib/buildinfo/osversion_openbsd.go new file mode 100644 index 000000000..a834e5e60 --- /dev/null +++ b/lib/buildinfo/osversion_openbsd.go @@ -0,0 +1,13 @@ +// +build openbsd + +package buildinfo + +// gopsutil v3.21.3 fails to build on openbsd: +// Error: .../go/pkg/mod/github.com/tklauser/go-sysconf@v0.3.4/sysconf_openbsd.go:22:28: undefined: unix.RLIMIT_NPROC +// Error: .../go/pkg/mod/github.com/shirou/gopsutil/v3@v3.21.3/process/process.go:163:15: undefined: pidsWithContext +// and so on... + +// GetOSVersion returns OS version, kernel and bitness +func GetOSVersion() (osVersion, osKernel string) { + return "OpenBSD", "" +} diff --git a/lib/buildinfo/osversion_windows.go b/lib/buildinfo/osversion_windows.go new file mode 100644 index 000000000..7976aa485 --- /dev/null +++ b/lib/buildinfo/osversion_windows.go @@ -0,0 +1,131 @@ +// +build !openbsd !windows + +package buildinfo + +import ( + "fmt" + "regexp" + "strings" + "unsafe" + + "github.com/shirou/gopsutil/v3/host" + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" +) + +// GetOSVersion returns OS version, kernel and bitness +// On Windows it performs additional output enhancements. +func GetOSVersion() (osVersion, osKernel string) { + if platform, _, version, err := host.PlatformInformation(); err == nil && platform != "" { + osVersion = platform + if version != "" { + osVersion += " " + version + } + } + + if version, err := host.KernelVersion(); err == nil && version != "" { + osKernel = version + + // Prevent duplication of output on Windows + if strings.Contains(osVersion, osKernel) { + deduped := strings.TrimSpace(strings.Replace(osVersion, osKernel, "", 1)) + if deduped != "" { + osVersion = deduped + } + } + + // Simplify kernel output: `RELEASE.BUILD Build BUILD` -> `RELEASE.BUILD` + match := regexp.MustCompile(`^([\d\.]+?\.)(\d+) Build (\d+)$`).FindStringSubmatch(osKernel) + if len(match) == 4 && match[2] == match[3] { + osKernel = match[1] + match[2] + } + } + + friendlyName := getRegistryVersionString("ReleaseId") + if osVersion != "" && friendlyName != "" { + osVersion += " " + friendlyName + } + + updateRevision := getRegistryVersionInt("UBR") + if osKernel != "" && updateRevision != 0 { + osKernel += fmt.Sprintf(".%d", updateRevision) + } + + if arch, err := host.KernelArch(); err == nil && arch != "" { + if strings.HasSuffix(arch, "64") && osVersion != "" { + osVersion += " (64 bit)" + } + if osKernel != "" { + osKernel += " (" + arch + ")" + } + } + + return +} + +var regVersionKeyUTF16 = windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Windows NT\CurrentVersion`) + +func getRegistryVersionString(name string) string { + var ( + err error + handle windows.Handle + bufLen uint32 + valType uint32 + ) + + err = windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, regVersionKeyUTF16, 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &handle) + if err != nil { + return "" + } + defer func() { + _ = windows.RegCloseKey(handle) + }() + + nameUTF16 := windows.StringToUTF16Ptr(name) + err = windows.RegQueryValueEx(handle, nameUTF16, nil, &valType, nil, &bufLen) + if err != nil { + return "" + } + + regBuf := make([]uint16, bufLen/2+1) + err = windows.RegQueryValueEx(handle, nameUTF16, nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen) + if err != nil { + return "" + } + + return windows.UTF16ToString(regBuf[:]) +} + +func getRegistryVersionInt(name string) int { + var ( + err error + handle windows.Handle + bufLen uint32 + valType uint32 + ) + + err = windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, regVersionKeyUTF16, 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &handle) + if err != nil { + return 0 + } + defer func() { + _ = windows.RegCloseKey(handle) + }() + + nameUTF16 := windows.StringToUTF16Ptr(name) + err = windows.RegQueryValueEx(handle, nameUTF16, nil, &valType, nil, &bufLen) + if err != nil { + return 0 + } + + if valType != registry.DWORD || bufLen != 4 { + return 0 + } + var val32 uint32 + err = windows.RegQueryValueEx(handle, nameUTF16, nil, &valType, (*byte)(unsafe.Pointer(&val32)), &bufLen) + if err != nil { + return 0 + } + + return int(val32) +}