1
2
3 package host
4
5 import (
6 "context"
7 "fmt"
8 "math"
9 "strconv"
10 "strings"
11 "sync/atomic"
12 "syscall"
13 "time"
14 "unsafe"
15
16 "github.com/shirou/gopsutil/internal/common"
17 "github.com/shirou/gopsutil/process"
18 "github.com/yusufpapurcu/wmi"
19 "golang.org/x/sys/windows"
20 )
21
22 var (
23 procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime")
24 procGetTickCount32 = common.Modkernel32.NewProc("GetTickCount")
25 procGetTickCount64 = common.Modkernel32.NewProc("GetTickCount64")
26 procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
27 procRtlGetVersion = common.ModNt.NewProc("RtlGetVersion")
28 )
29
30
31 type osVersionInfoExW struct {
32 dwOSVersionInfoSize uint32
33 dwMajorVersion uint32
34 dwMinorVersion uint32
35 dwBuildNumber uint32
36 dwPlatformId uint32
37 szCSDVersion [128]uint16
38 wServicePackMajor uint16
39 wServicePackMinor uint16
40 wSuiteMask uint16
41 wProductType uint8
42 wReserved uint8
43 }
44
45 type systemInfo struct {
46 wProcessorArchitecture uint16
47 wReserved uint16
48 dwPageSize uint32
49 lpMinimumApplicationAddress uintptr
50 lpMaximumApplicationAddress uintptr
51 dwActiveProcessorMask uintptr
52 dwNumberOfProcessors uint32
53 dwProcessorType uint32
54 dwAllocationGranularity uint32
55 wProcessorLevel uint16
56 wProcessorRevision uint16
57 }
58
59 type msAcpi_ThermalZoneTemperature struct {
60 Active bool
61 CriticalTripPoint uint32
62 CurrentTemperature uint32
63 InstanceName string
64 }
65
66 func HostIDWithContext(ctx context.Context) (string, error) {
67
68
69 var h windows.Handle
70 err := windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
71 if err != nil {
72 return "", err
73 }
74 defer windows.RegCloseKey(h)
75
76 const windowsRegBufLen = 74
77 const uuidLen = 36
78
79 var regBuf [windowsRegBufLen]uint16
80 bufLen := uint32(windowsRegBufLen)
81 var valType uint32
82 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
83 if err != nil {
84 return "", err
85 }
86
87 hostID := windows.UTF16ToString(regBuf[:])
88 hostIDLen := len(hostID)
89 if hostIDLen != uuidLen {
90 return "", fmt.Errorf("HostID incorrect: %q\n", hostID)
91 }
92
93 return strings.ToLower(hostID), nil
94 }
95
96 func numProcs(ctx context.Context) (uint64, error) {
97 procs, err := process.PidsWithContext(ctx)
98 if err != nil {
99 return 0, err
100 }
101 return uint64(len(procs)), nil
102 }
103
104 func UptimeWithContext(ctx context.Context) (uint64, error) {
105 procGetTickCount := procGetTickCount64
106 err := procGetTickCount64.Find()
107 if err != nil {
108 procGetTickCount = procGetTickCount32
109 }
110 r1, _, lastErr := syscall.Syscall(procGetTickCount.Addr(), 0, 0, 0, 0)
111 if lastErr != 0 {
112 return 0, lastErr
113 }
114 return uint64((time.Duration(r1) * time.Millisecond).Seconds()), nil
115 }
116
117
118 var cachedBootTime uint64
119
120 func BootTimeWithContext(ctx context.Context) (uint64, error) {
121 t := atomic.LoadUint64(&cachedBootTime)
122 if t != 0 {
123 return t, nil
124 }
125 up, err := Uptime()
126 if err != nil {
127 return 0, err
128 }
129 t = timeSince(up)
130 atomic.StoreUint64(&cachedBootTime, t)
131 return t, nil
132 }
133
134 func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
135
136
137
138
139 var osInfo osVersionInfoExW
140 osInfo.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osInfo))
141 ret, _, err := procRtlGetVersion.Call(uintptr(unsafe.Pointer(&osInfo)))
142 if ret != 0 {
143 return
144 }
145
146
147 var h windows.Handle
148 err = windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Windows NT\CurrentVersion`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
149 if err != nil {
150 return
151 }
152 defer windows.RegCloseKey(h)
153 var bufLen uint32
154 var valType uint32
155 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`ProductName`), nil, &valType, nil, &bufLen)
156 if err != nil {
157 return
158 }
159 regBuf := make([]uint16, bufLen/2+1)
160 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`ProductName`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
161 if err != nil {
162 return
163 }
164 platform = windows.UTF16ToString(regBuf[:])
165 if strings.Contains(platform, "Windows 10") {
166 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CurrentBuildNumber`), nil, &valType, nil, &bufLen)
167 if err == nil {
168 regBuf = make([]uint16, bufLen/2+1)
169 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CurrentBuildNumber`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
170 if err == nil {
171 buildNumberStr := windows.UTF16ToString(regBuf[:])
172 if buildNumber, err := strconv.Atoi(buildNumberStr); err == nil && buildNumber >= 22000 {
173 platform = strings.Replace(platform, "Windows 10", "Windows 11", 1)
174 }
175 }
176 }
177 }
178 if !strings.HasPrefix(platform, "Microsoft") {
179 platform = "Microsoft " + platform
180 }
181 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CSDVersion`), nil, &valType, nil, &bufLen)
182 if err == nil {
183 regBuf = make([]uint16, bufLen/2+1)
184 err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CSDVersion`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
185 if err == nil {
186 platform += " " + windows.UTF16ToString(regBuf[:])
187 }
188 }
189
190
191 switch osInfo.wProductType {
192 case 1:
193 family = "Standalone Workstation"
194 case 2:
195 family = "Server (Domain Controller)"
196 case 3:
197 family = "Server"
198 }
199
200
201 version = fmt.Sprintf("%d.%d.%d Build %d", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.dwBuildNumber)
202
203 return platform, family, version, nil
204 }
205
206 func UsersWithContext(ctx context.Context) ([]UserStat, error) {
207 var ret []UserStat
208
209 return ret, common.ErrNotImplementedError
210 }
211
212 func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
213 var ret []TemperatureStat
214 var dst []msAcpi_ThermalZoneTemperature
215 q := wmi.CreateQuery(&dst, "")
216 if err := common.WMIQueryWithContext(ctx, q, &dst, nil, "root/wmi"); err != nil {
217 return ret, err
218 }
219
220 for _, v := range dst {
221 ts := TemperatureStat{
222 SensorKey: v.InstanceName,
223 Temperature: kelvinToCelsius(v.CurrentTemperature, 2),
224 }
225 ret = append(ret, ts)
226 }
227
228 return ret, nil
229 }
230
231 func kelvinToCelsius(temp uint32, n int) float64 {
232
233
234 t := float64(temp/10) - 273.15
235 n10 := math.Pow10(n)
236 return math.Trunc((t+0.5/n10)*n10) / n10
237 }
238
239 func VirtualizationWithContext(ctx context.Context) (string, string, error) {
240 return "", "", common.ErrNotImplementedError
241 }
242
243 func KernelVersionWithContext(ctx context.Context) (string, error) {
244 _, _, version, err := PlatformInformationWithContext(ctx)
245 return version, err
246 }
247
248 func KernelArch() (string, error) {
249 var systemInfo systemInfo
250 procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
251
252 const (
253 PROCESSOR_ARCHITECTURE_INTEL = 0
254 PROCESSOR_ARCHITECTURE_ARM = 5
255 PROCESSOR_ARCHITECTURE_ARM64 = 12
256 PROCESSOR_ARCHITECTURE_IA64 = 6
257 PROCESSOR_ARCHITECTURE_AMD64 = 9
258 )
259 switch systemInfo.wProcessorArchitecture {
260 case PROCESSOR_ARCHITECTURE_INTEL:
261 if systemInfo.wProcessorLevel < 3 {
262 return "i386", nil
263 }
264 if systemInfo.wProcessorLevel > 6 {
265 return "i686", nil
266 }
267 return fmt.Sprintf("i%d86", systemInfo.wProcessorLevel), nil
268 case PROCESSOR_ARCHITECTURE_ARM:
269 return "arm", nil
270 case PROCESSOR_ARCHITECTURE_ARM64:
271 return "aarch64", nil
272 case PROCESSOR_ARCHITECTURE_IA64:
273 return "ia64", nil
274 case PROCESSOR_ARCHITECTURE_AMD64:
275 return "x86_64", nil
276 }
277 return "", nil
278 }
279
View as plain text