2017-02-01 18:26:00 +00:00
// +build windows
package host
import (
2018-10-19 18:33:23 +00:00
"context"
2017-02-01 18:26:00 +00:00
"fmt"
2018-10-19 18:33:23 +00:00
"math"
2017-02-01 18:26:00 +00:00
"strings"
2018-10-19 18:33:23 +00:00
"sync/atomic"
2017-02-03 00:11:54 +00:00
"syscall"
2017-02-01 18:26:00 +00:00
"time"
2017-02-03 00:11:54 +00:00
"unsafe"
2017-02-01 18:26:00 +00:00
"github.com/StackExchange/wmi"
"github.com/shirou/gopsutil/internal/common"
2020-10-07 18:52:50 +00:00
"github.com/shirou/gopsutil/process"
2018-10-19 18:33:23 +00:00
"golang.org/x/sys/windows"
2017-02-01 18:26:00 +00:00
)
var (
procGetSystemTimeAsFileTime = common . Modkernel32 . NewProc ( "GetSystemTimeAsFileTime" )
2018-10-19 18:33:23 +00:00
procGetTickCount32 = common . Modkernel32 . NewProc ( "GetTickCount" )
procGetTickCount64 = common . Modkernel32 . NewProc ( "GetTickCount64" )
2020-10-07 18:52:50 +00:00
procGetNativeSystemInfo = common . Modkernel32 . NewProc ( "GetNativeSystemInfo" )
2018-10-19 18:33:23 +00:00
procRtlGetVersion = common . ModNt . NewProc ( "RtlGetVersion" )
2017-02-01 18:26:00 +00:00
)
2018-10-19 18:33:23 +00:00
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_osversioninfoexw
type osVersionInfoExW struct {
dwOSVersionInfoSize uint32
dwMajorVersion uint32
dwMinorVersion uint32
dwBuildNumber uint32
dwPlatformId uint32
szCSDVersion [ 128 ] uint16
wServicePackMajor uint16
wServicePackMinor uint16
wSuiteMask uint16
wProductType uint8
wReserved uint8
}
2020-10-07 18:52:50 +00:00
type systemInfo struct {
wProcessorArchitecture uint16
wReserved uint16
dwPageSize uint32
lpMinimumApplicationAddress uintptr
lpMaximumApplicationAddress uintptr
dwActiveProcessorMask uintptr
dwNumberOfProcessors uint32
dwProcessorType uint32
dwAllocationGranularity uint32
wProcessorLevel uint16
wProcessorRevision uint16
}
2018-10-19 18:33:23 +00:00
type msAcpi_ThermalZoneTemperature struct {
Active bool
CriticalTripPoint uint32
CurrentTemperature uint32
InstanceName string
2017-02-01 18:26:00 +00:00
}
2020-10-07 18:52:50 +00:00
func HostIDWithContext ( ctx context . Context ) ( string , error ) {
2018-10-19 18:33:23 +00:00
// there has been reports of issues on 32bit using golang.org/x/sys/windows/registry, see https://github.com/shirou/gopsutil/pull/312#issuecomment-277422612
// for rationale of using windows.RegOpenKeyEx/RegQueryValueEx instead of registry.OpenKey/GetStringValue
var h windows . Handle
err := windows . RegOpenKeyEx ( windows . HKEY_LOCAL_MACHINE , windows . StringToUTF16Ptr ( ` SOFTWARE\Microsoft\Cryptography ` ) , 0 , windows . KEY_READ | windows . KEY_WOW64_64KEY , & h )
2017-02-03 00:11:54 +00:00
if err != nil {
return "" , err
}
2018-10-19 18:33:23 +00:00
defer windows . RegCloseKey ( h )
2017-02-03 00:11:54 +00:00
const windowsRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
const uuidLen = 36
var regBuf [ windowsRegBufLen ] uint16
bufLen := uint32 ( windowsRegBufLen )
var valType uint32
2018-10-19 18:33:23 +00:00
err = windows . RegQueryValueEx ( h , windows . StringToUTF16Ptr ( ` MachineGuid ` ) , nil , & valType , ( * byte ) ( unsafe . Pointer ( & regBuf [ 0 ] ) ) , & bufLen )
2017-02-03 00:11:54 +00:00
if err != nil {
return "" , err
}
2018-10-19 18:33:23 +00:00
hostID := windows . UTF16ToString ( regBuf [ : ] )
2017-02-03 00:11:54 +00:00
hostIDLen := len ( hostID )
if hostIDLen != uuidLen {
return "" , fmt . Errorf ( "HostID incorrect: %q\n" , hostID )
}
2020-10-07 18:52:50 +00:00
return strings . ToLower ( hostID ) , nil
2017-02-03 00:11:54 +00:00
}
2020-10-07 18:52:50 +00:00
func numProcs ( ctx context . Context ) ( uint64 , error ) {
procs , err := process . PidsWithContext ( ctx )
if err != nil {
return 0 , err
}
return uint64 ( len ( procs ) ) , nil
2017-02-01 18:26:00 +00:00
}
2018-10-19 18:33:23 +00:00
func UptimeWithContext ( ctx context . Context ) ( uint64 , error ) {
procGetTickCount := procGetTickCount64
err := procGetTickCount64 . Find ( )
if err != nil {
procGetTickCount = procGetTickCount32 // handle WinXP, but keep in mind that "the time will wrap around to zero if the system is run continuously for 49.7 days." from MSDN
2017-02-01 18:26:00 +00:00
}
2018-10-19 18:33:23 +00:00
r1 , _ , lastErr := syscall . Syscall ( procGetTickCount . Addr ( ) , 0 , 0 , 0 , 0 )
if lastErr != 0 {
return 0 , lastErr
}
return uint64 ( ( time . Duration ( r1 ) * time . Millisecond ) . Seconds ( ) ) , nil
2017-02-01 18:26:00 +00:00
}
2018-10-19 18:33:23 +00:00
// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64
func BootTimeWithContext ( ctx context . Context ) ( uint64 , error ) {
t := atomic . LoadUint64 ( & cachedBootTime )
if t != 0 {
return t , nil
2017-02-01 18:26:00 +00:00
}
up , err := Uptime ( )
if err != nil {
return 0 , err
}
2020-10-07 18:52:50 +00:00
t = timeSince ( up )
2018-10-19 18:33:23 +00:00
atomic . StoreUint64 ( & cachedBootTime , t )
return t , nil
2017-02-01 18:26:00 +00:00
}
2018-10-19 18:33:23 +00:00
func PlatformInformationWithContext ( ctx context . Context ) ( platform string , family string , version string , err error ) {
// GetVersionEx lies on Windows 8.1 and returns as Windows 8 if we don't declare compatibility in manifest
// RtlGetVersion bypasses this lying layer and returns the true Windows version
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlgetversion
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_osversioninfoexw
var osInfo osVersionInfoExW
osInfo . dwOSVersionInfoSize = uint32 ( unsafe . Sizeof ( osInfo ) )
ret , _ , err := procRtlGetVersion . Call ( uintptr ( unsafe . Pointer ( & osInfo ) ) )
if ret != 0 {
return
2017-02-01 18:26:00 +00:00
}
// Platform
2020-10-07 18:52:50 +00:00
var h windows . Handle // like HostIDWithContext(), we query the registry using the raw windows.RegOpenKeyEx/RegQueryValueEx
2018-10-19 18:33:23 +00:00
err = windows . RegOpenKeyEx ( windows . HKEY_LOCAL_MACHINE , windows . StringToUTF16Ptr ( ` SOFTWARE\Microsoft\Windows NT\CurrentVersion ` ) , 0 , windows . KEY_READ | windows . KEY_WOW64_64KEY , & h )
if err != nil {
return
}
defer windows . RegCloseKey ( h )
var bufLen uint32
var valType uint32
err = windows . RegQueryValueEx ( h , windows . StringToUTF16Ptr ( ` ProductName ` ) , nil , & valType , nil , & bufLen )
if err != nil {
return
}
regBuf := make ( [ ] uint16 , bufLen / 2 + 1 )
err = windows . RegQueryValueEx ( h , windows . StringToUTF16Ptr ( ` ProductName ` ) , nil , & valType , ( * byte ) ( unsafe . Pointer ( & regBuf [ 0 ] ) ) , & bufLen )
if err != nil {
return
}
platform = windows . UTF16ToString ( regBuf [ : ] )
if ! strings . HasPrefix ( platform , "Microsoft" ) {
platform = "Microsoft " + platform
}
err = windows . RegQueryValueEx ( h , windows . StringToUTF16Ptr ( ` CSDVersion ` ) , nil , & valType , nil , & bufLen ) // append Service Pack number, only on success
if err == nil { // don't return an error if only the Service Pack retrieval fails
regBuf = make ( [ ] uint16 , bufLen / 2 + 1 )
err = windows . RegQueryValueEx ( h , windows . StringToUTF16Ptr ( ` CSDVersion ` ) , nil , & valType , ( * byte ) ( unsafe . Pointer ( & regBuf [ 0 ] ) ) , & bufLen )
if err == nil {
platform += " " + windows . UTF16ToString ( regBuf [ : ] )
}
}
2017-02-01 18:26:00 +00:00
// PlatformFamily
2018-10-19 18:33:23 +00:00
switch osInfo . wProductType {
2017-02-01 18:26:00 +00:00
case 1 :
family = "Standalone Workstation"
case 2 :
family = "Server (Domain Controller)"
case 3 :
family = "Server"
}
// Platform Version
2018-10-19 18:33:23 +00:00
version = fmt . Sprintf ( "%d.%d.%d Build %d" , osInfo . dwMajorVersion , osInfo . dwMinorVersion , osInfo . dwBuildNumber , osInfo . dwBuildNumber )
2017-02-01 18:26:00 +00:00
2018-10-19 18:33:23 +00:00
return platform , family , version , nil
2017-02-01 18:26:00 +00:00
}
2018-10-19 18:33:23 +00:00
func UsersWithContext ( ctx context . Context ) ( [ ] UserStat , error ) {
2017-02-01 18:26:00 +00:00
var ret [ ] UserStat
2020-10-07 18:52:50 +00:00
return ret , common . ErrNotImplementedError
2018-10-19 18:33:23 +00:00
}
func SensorsTemperaturesWithContext ( ctx context . Context ) ( [ ] TemperatureStat , error ) {
var ret [ ] TemperatureStat
var dst [ ] msAcpi_ThermalZoneTemperature
q := wmi . CreateQuery ( & dst , "" )
if err := common . WMIQueryWithContext ( ctx , q , & dst , nil , "root/wmi" ) ; err != nil {
return ret , err
}
for _ , v := range dst {
ts := TemperatureStat {
SensorKey : v . InstanceName ,
Temperature : kelvinToCelsius ( v . CurrentTemperature , 2 ) ,
}
ret = append ( ret , ts )
}
return ret , nil
}
func kelvinToCelsius ( temp uint32 , n int ) float64 {
// wmi return temperature Kelvin * 10, so need to divide the result by 10,
// and then minus 273.15 to get °Celsius.
t := float64 ( temp / 10 ) - 273.15
n10 := math . Pow10 ( n )
return math . Trunc ( ( t + 0.5 / n10 ) * n10 ) / n10
}
func VirtualizationWithContext ( ctx context . Context ) ( string , string , error ) {
return "" , "" , common . ErrNotImplementedError
}
func KernelVersionWithContext ( ctx context . Context ) ( string , error ) {
2020-10-07 18:52:50 +00:00
_ , _ , version , err := PlatformInformationWithContext ( ctx )
2018-10-19 18:33:23 +00:00
return version , err
}
2020-10-07 18:52:50 +00:00
func KernelArch ( ) ( string , error ) {
var systemInfo systemInfo
procGetNativeSystemInfo . Call ( uintptr ( unsafe . Pointer ( & systemInfo ) ) )
const (
PROCESSOR_ARCHITECTURE_INTEL = 0
PROCESSOR_ARCHITECTURE_ARM = 5
PROCESSOR_ARCHITECTURE_ARM64 = 12
PROCESSOR_ARCHITECTURE_IA64 = 6
PROCESSOR_ARCHITECTURE_AMD64 = 9
)
switch systemInfo . wProcessorArchitecture {
case PROCESSOR_ARCHITECTURE_INTEL :
if systemInfo . wProcessorLevel < 3 {
return "i386" , nil
}
if systemInfo . wProcessorLevel > 6 {
return "i686" , nil
}
return fmt . Sprintf ( "i%d86" , systemInfo . wProcessorLevel ) , nil
case PROCESSOR_ARCHITECTURE_ARM :
return "arm" , nil
case PROCESSOR_ARCHITECTURE_ARM64 :
return "aarch64" , nil
case PROCESSOR_ARCHITECTURE_IA64 :
return "ia64" , nil
case PROCESSOR_ARCHITECTURE_AMD64 :
return "x86_64" , nil
}
return "" , nil
}