mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 05:56:37 +00:00
Merge pull request #702 from benridley/dev_cs_collector
Replace WMI in cs and os collectors
This commit is contained in:
@@ -3,10 +3,9 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/prometheus-community/windows_exporter/headers/sysinfoapi"
|
||||
"github.com/prometheus-community/windows_exporter/log"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
@@ -60,51 +59,47 @@ func (c *CSCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// Win32_ComputerSystem docs:
|
||||
// - https://msdn.microsoft.com/en-us/library/aa394102
|
||||
type Win32_ComputerSystem struct {
|
||||
NumberOfLogicalProcessors uint32
|
||||
TotalPhysicalMemory uint64
|
||||
DNSHostname string
|
||||
Domain string
|
||||
Workgroup *string
|
||||
}
|
||||
|
||||
func (c *CSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
||||
var dst []Win32_ComputerSystem
|
||||
q := queryAll(&dst)
|
||||
if err := wmi.Query(q, &dst); err != nil {
|
||||
// Get systeminfo for number of processors
|
||||
systemInfo := sysinfoapi.GetSystemInfo()
|
||||
|
||||
// Get memory status for physical memory
|
||||
mem, err := sysinfoapi.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(dst) == 0 {
|
||||
return nil, errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.LogicalProcessors,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].NumberOfLogicalProcessors),
|
||||
float64(systemInfo.NumberOfProcessors),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.PhysicalMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].TotalPhysicalMemory),
|
||||
float64(mem.TotalPhys),
|
||||
)
|
||||
|
||||
var fqdn string
|
||||
if dst[0].Workgroup == nil || dst[0].Domain != *dst[0].Workgroup {
|
||||
fqdn = dst[0].DNSHostname + "." + dst[0].Domain
|
||||
} else {
|
||||
fqdn = dst[0].DNSHostname
|
||||
hostname, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSHostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domain, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fqdn, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSFullyQualified)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Hostname,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
dst[0].DNSHostname,
|
||||
dst[0].Domain,
|
||||
hostname,
|
||||
domain,
|
||||
fqdn,
|
||||
)
|
||||
|
||||
|
||||
23
collector/cs_test.go
Normal file
23
collector/cs_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func BenchmarkCsCollect(b *testing.B) {
|
||||
c, err := NewCSCollector()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
metrics := make(chan prometheus.Metric)
|
||||
go func() {
|
||||
for {
|
||||
<-metrics
|
||||
}
|
||||
}()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Collect(&ScrapeContext{}, metrics)
|
||||
}
|
||||
}
|
||||
131
collector/os.go
131
collector/os.go
@@ -3,16 +3,21 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/prometheus-community/windows_exporter/log"
|
||||
"github.com/prometheus-community/windows_exporter/headers/netapi32"
|
||||
"github.com/prometheus-community/windows_exporter/headers/psapi"
|
||||
"github.com/prometheus-community/windows_exporter/headers/sysinfoapi"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/log"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerCollector("os", NewOSCollector)
|
||||
registerCollector("os", NewOSCollector, "Paging File")
|
||||
}
|
||||
|
||||
// A OSCollector is a Prometheus collector for WMI metrics
|
||||
@@ -32,6 +37,12 @@ type OSCollector struct {
|
||||
Timezone *prometheus.Desc
|
||||
}
|
||||
|
||||
type pagingFileCounter struct {
|
||||
Name string
|
||||
Usage float64 `perflib:"% Usage"`
|
||||
UsagePeak float64 `perflib:"% Usage Peak"`
|
||||
}
|
||||
|
||||
// NewOSCollector ...
|
||||
func NewOSCollector() (Collector, error) {
|
||||
const subsystem = "os"
|
||||
@@ -121,7 +132,7 @@ func NewOSCollector() (Collector, error) {
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *OSCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||
if desc, err := c.collect(ch); err != nil {
|
||||
if desc, err := c.collect(ctx, ch); err != nil {
|
||||
log.Error("failed collecting os metrics:", desc, err)
|
||||
return err
|
||||
}
|
||||
@@ -146,41 +157,102 @@ type Win32_OperatingSystem struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
func (c *OSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
||||
var dst []Win32_OperatingSystem
|
||||
q := queryAll(&dst)
|
||||
if err := wmi.Query(q, &dst); err != nil {
|
||||
func (c *OSCollector) collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
||||
nwgi, err := netapi32.GetWorkstationInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return nil, errors.New("WMI query returned empty result set")
|
||||
gmse, err := sysinfoapi.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentTime := time.Now()
|
||||
timezoneName, _ := currentTime.Zone()
|
||||
|
||||
// Get total allocation of paging files across all disks.
|
||||
memManKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management`, registry.QUERY_VALUE)
|
||||
defer memManKey.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pagingFiles, _, err := memManKey.GetStringsValue("ExistingPageFiles")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get build number and product name from registry
|
||||
ntKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
defer ntKey.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pn, _, err := ntKey.GetStringValue("ProductName")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bn, _, err := ntKey.GetStringValue("CurrentBuildNumber")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fsipf float64 = 0
|
||||
for _, pagingFile := range pagingFiles {
|
||||
fileString := strings.ReplaceAll(pagingFile, `\??\`, "")
|
||||
file, err := os.Stat(fileString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fsipf += float64(file.Size())
|
||||
}
|
||||
|
||||
gpi, err := psapi.GetPerformanceInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pfc = make([]pagingFileCounter, 0)
|
||||
if err := unmarshalObject(ctx.perfObjects["Paging File"], &pfc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get current page file usage.
|
||||
var pfbRaw float64 = 0
|
||||
for _, pageFile := range pfc {
|
||||
if strings.Contains(strings.ToLower(pageFile.Name), "_total") {
|
||||
continue
|
||||
}
|
||||
pfbRaw += pageFile.Usage
|
||||
}
|
||||
|
||||
// Subtract from total page file allocation on disk.
|
||||
pfb := fsipf - (pfbRaw * float64(gpi.PageSize))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.OSInformation,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
dst[0].Caption,
|
||||
dst[0].Version,
|
||||
fmt.Sprintf("Microsoft %s", pn), // Caption
|
||||
fmt.Sprintf("%d.%d.%s", nwgi.VersionMajor, nwgi.VersionMinor, bn), // Version
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.PhysicalMemoryFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].FreePhysicalMemory*1024), // KiB -> bytes
|
||||
float64(gmse.AvailPhys),
|
||||
)
|
||||
|
||||
time := dst[0].LocalDateTime
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Time,
|
||||
prometheus.GaugeValue,
|
||||
float64(time.Unix()),
|
||||
float64(currentTime.Unix()),
|
||||
)
|
||||
|
||||
timezoneName, _ := time.Zone()
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Timezone,
|
||||
prometheus.GaugeValue,
|
||||
@@ -191,55 +263,58 @@ func (c *OSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, er
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.PagingFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].FreeSpaceInPagingFiles*1024), // KiB -> bytes
|
||||
pfb,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.VirtualMemoryFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].FreeVirtualMemory*1024), // KiB -> bytes
|
||||
float64(gmse.AvailPageFile),
|
||||
)
|
||||
|
||||
// Windows has no defined limit, and is based off available resources. This currently isn't calculated by WMI and is set to default value.
|
||||
// https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-processes-and-threads/ba-p/723824
|
||||
// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-operatingsystem
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ProcessesLimit,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].MaxNumberOfProcesses),
|
||||
float64(4294967295),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ProcessMemoryLimitBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].MaxProcessMemorySize*1024), // KiB -> bytes
|
||||
float64(gmse.TotalVirtual),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Processes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].NumberOfProcesses),
|
||||
float64(gpi.ProcessCount),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Users,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].NumberOfUsers),
|
||||
float64(nwgi.LoggedOnUsers),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.PagingLimitBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].SizeStoredInPagingFiles*1024), // KiB -> bytes
|
||||
float64(fsipf),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.VirtualMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].TotalVirtualMemorySize*1024), // KiB -> bytes
|
||||
float64(gmse.TotalPageFile),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.VisibleMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(dst[0].TotalVisibleMemorySize*1024), // KiB -> bytes
|
||||
float64(gmse.TotalPhys),
|
||||
)
|
||||
|
||||
return nil, nil
|
||||
|
||||
24
collector/os_test.go
Normal file
24
collector/os_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func BenchmarkOsCollect(b *testing.B) {
|
||||
o, err := NewOSCollector()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
metrics := make(chan prometheus.Metric)
|
||||
go func() {
|
||||
for {
|
||||
<-metrics
|
||||
}
|
||||
}()
|
||||
s, err := PrepareScrapeContext([]string{"os"})
|
||||
for i := 0; i < b.N; i++ {
|
||||
o.Collect(s, metrics)
|
||||
}
|
||||
}
|
||||
108
headers/netapi32/netapi32.go
Normal file
108
headers/netapi32/netapi32.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package netapi32
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// WKSTAInfo102 is a wrapper of WKSTA_Info_102
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/ns-lmwksta-wksta_info_102
|
||||
type wKSTAInfo102 struct {
|
||||
wki102_platform_id uint32
|
||||
wki102_computername *uint16
|
||||
wki102_langroup *uint16
|
||||
wki102_ver_major uint32
|
||||
wki102_ver_minor uint32
|
||||
wki102_lanroot *uint16
|
||||
wki102_logged_on_users uint32
|
||||
}
|
||||
|
||||
// WorkstationInfo is an idiomatic wrapper of WKSTAInfo102
|
||||
type WorkstationInfo struct {
|
||||
PlatformId uint32
|
||||
ComputerName string
|
||||
LanGroup string
|
||||
VersionMajor uint32
|
||||
VersionMinor uint32
|
||||
LanRoot string
|
||||
LoggedOnUsers uint32
|
||||
}
|
||||
|
||||
var (
|
||||
netapi32 = windows.NewLazySystemDLL("netapi32")
|
||||
procNetWkstaGetInfo = netapi32.NewProc("NetWkstaGetInfo")
|
||||
procNetApiBufferFree = netapi32.NewProc("NetApiBufferFree")
|
||||
)
|
||||
|
||||
// NetApiStatus is a map of Network Management Error Codes.
|
||||
// https://docs.microsoft.com/en-gb/windows/win32/netmgmt/network-management-error-codes?redirectedfrom=MSDN
|
||||
var NetApiStatus = map[uint32]string{
|
||||
// Success
|
||||
0: "NERR_Success",
|
||||
// This computer name is invalid.
|
||||
2351: "NERR_InvalidComputer",
|
||||
// This operation is only allowed on the primary domain controller of the domain.
|
||||
2226: "NERR_NotPrimary",
|
||||
/// This operation is not allowed on this special group.
|
||||
2234: "NERR_SpeGroupOp",
|
||||
/// This operation is not allowed on the last administrative account.
|
||||
2452: "NERR_LastAdmin",
|
||||
/// The password parameter is invalid.
|
||||
2203: "NERR_BadPassword",
|
||||
/// The password does not meet the password policy requirements.
|
||||
/// Check the minimum password length, password complexity and password history requirements.
|
||||
2245: "NERR_PasswordTooShort",
|
||||
/// The user name could not be found.
|
||||
2221: "NERR_UserNotFound",
|
||||
// Errors
|
||||
5: "ERROR_ACCESS_DENIED",
|
||||
8: "ERROR_NOT_ENOUGH_MEMORY",
|
||||
87: "ERROR_INVALID_PARAMETER",
|
||||
123: "ERROR_INVALID_NAME",
|
||||
124: "ERROR_INVALID_LEVEL",
|
||||
234: "ERROR_MORE_DATA",
|
||||
1219: "ERROR_SESSION_CREDENTIAL_CONFLICT",
|
||||
}
|
||||
|
||||
// NetApiBufferFree frees the memory other network management functions use internally to return information.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/lmapibuf/nf-lmapibuf-netapibufferfree
|
||||
func netApiBufferFree(buffer *wKSTAInfo102) {
|
||||
procNetApiBufferFree.Call(uintptr(unsafe.Pointer(buffer)))
|
||||
}
|
||||
|
||||
// NetWkstaGetInfo returns information about the configuration of a workstation.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstagetinfo
|
||||
func netWkstaGetInfo() (wKSTAInfo102, uint32, error) {
|
||||
var lpwi *wKSTAInfo102
|
||||
pLevel := uintptr(102)
|
||||
|
||||
r1, _, _ := procNetWkstaGetInfo.Call(0, pLevel, uintptr(unsafe.Pointer(&lpwi)))
|
||||
defer netApiBufferFree(lpwi)
|
||||
|
||||
if ret := *(*uint32)(unsafe.Pointer(&r1)); ret != 0 {
|
||||
return wKSTAInfo102{}, ret, errors.New(NetApiStatus[ret])
|
||||
}
|
||||
|
||||
deref := *lpwi
|
||||
return deref, 0, nil
|
||||
}
|
||||
|
||||
// GetWorkstationInfo is an idiomatic wrapper for netWkstaGetInfo
|
||||
func GetWorkstationInfo() (WorkstationInfo, error) {
|
||||
info, _, err := netWkstaGetInfo()
|
||||
if err != nil {
|
||||
return WorkstationInfo{}, err
|
||||
}
|
||||
workstationInfo := WorkstationInfo{
|
||||
PlatformId: info.wki102_platform_id,
|
||||
ComputerName: windows.UTF16PtrToString(info.wki102_computername),
|
||||
LanGroup: windows.UTF16PtrToString(info.wki102_langroup),
|
||||
VersionMajor: info.wki102_ver_major,
|
||||
VersionMinor: info.wki102_ver_minor,
|
||||
LanRoot: windows.UTF16PtrToString(info.wki102_lanroot),
|
||||
LoggedOnUsers: info.wki102_logged_on_users,
|
||||
}
|
||||
return workstationInfo, nil
|
||||
}
|
||||
45
headers/psapi/psapi.go
Normal file
45
headers/psapi/psapi.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package psapi
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// PerformanceInformation is a wrapper of the PERFORMANCE_INFORMATION struct.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-performance_information
|
||||
type PerformanceInformation struct {
|
||||
cb uint32
|
||||
CommitTotal uint
|
||||
CommitLimit uint
|
||||
CommitPeak uint
|
||||
PhysicalTotal uint
|
||||
PhysicalAvailable uint
|
||||
SystemCache uint
|
||||
KernelTotal uint
|
||||
KernelPaged uint
|
||||
KernelNonpaged uint
|
||||
PageSize uint
|
||||
HandleCount uint32
|
||||
ProcessCount uint32
|
||||
ThreadCount uint32
|
||||
}
|
||||
|
||||
var (
|
||||
psapi = windows.NewLazySystemDLL("psapi.dll")
|
||||
procGetPerformanceInfo = psapi.NewProc("GetPerformanceInfo")
|
||||
)
|
||||
|
||||
// GetPerformanceInfo returns the dereferenced version of GetLPPerformanceInfo.
|
||||
func GetPerformanceInfo() (PerformanceInformation, error) {
|
||||
var lppi PerformanceInformation
|
||||
size := (uint32)(unsafe.Sizeof(lppi))
|
||||
lppi.cb = size
|
||||
r1, _, err := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&lppi)), uintptr(size))
|
||||
|
||||
if ret := *(*bool)(unsafe.Pointer(&r1)); !ret {
|
||||
return PerformanceInformation{}, err
|
||||
}
|
||||
|
||||
return lppi, nil
|
||||
}
|
||||
164
headers/sysinfoapi/sysinfoapi.go
Normal file
164
headers/sysinfoapi/sysinfoapi.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package sysinfoapi
|
||||
|
||||
import (
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// MemoryStatusEx is a wrapper for MEMORYSTATUSEX
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex
|
||||
type memoryStatusEx struct {
|
||||
dwLength uint32
|
||||
DwMemoryLoad uint32
|
||||
UllTotalPhys uint64
|
||||
UllAvailPhys uint64
|
||||
UllTotalPageFile uint64
|
||||
UllAvailPageFile uint64
|
||||
UllTotalVirtual uint64
|
||||
UllAvailVirtual uint64
|
||||
UllAvailExtendedVirtual uint64
|
||||
}
|
||||
|
||||
// MemoryStatus is an idiomatic wrapper for MemoryStatusEx
|
||||
type MemoryStatus struct {
|
||||
MemoryLoad uint32
|
||||
TotalPhys uint64
|
||||
AvailPhys uint64
|
||||
TotalPageFile uint64
|
||||
AvailPageFile uint64
|
||||
TotalVirtual uint64
|
||||
AvailVirtual uint64
|
||||
AvailExtendedVirtual uint64
|
||||
}
|
||||
|
||||
// wProcessorArchitecture is a wrapper for the union found in LP_SYSTEM_INFO
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
|
||||
type wProcessorArchitecture struct {
|
||||
WReserved uint16
|
||||
WProcessorArchitecture uint16
|
||||
}
|
||||
|
||||
// ProcessorArchitecture is an idiomatic wrapper for wProcessorArchitecture
|
||||
type ProcessorArchitecture uint16
|
||||
|
||||
// Idiomatic values for wProcessorArchitecture
|
||||
const (
|
||||
AMD64 ProcessorArchitecture = 9
|
||||
ARM = 5
|
||||
ARM64 = 12
|
||||
IA64 = 6
|
||||
INTEL = 0
|
||||
UNKNOWN = 0xffff
|
||||
)
|
||||
|
||||
// LpSystemInfo is a wrapper for LPSYSTEM_INFO
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
|
||||
type lpSystemInfo struct {
|
||||
Arch wProcessorArchitecture
|
||||
DwPageSize uint32
|
||||
LpMinimumApplicationAddress uintptr
|
||||
LpMaximumApplicationAddress uintptr
|
||||
DwActiveProcessorMask uint32
|
||||
DwNumberOfProcessors uint32
|
||||
DwProcessorType uint32
|
||||
DwAllocationGranularity uint32
|
||||
WProcessorLevel uint16
|
||||
WProcessorRevision uint16
|
||||
}
|
||||
|
||||
// SystemInfo is an idiomatic wrapper for LpSystemInfo
|
||||
type SystemInfo struct {
|
||||
Arch ProcessorArchitecture
|
||||
PageSize uint32
|
||||
MinimumApplicationAddress uintptr
|
||||
MaximumApplicationAddress uintptr
|
||||
ActiveProcessorMask uint32
|
||||
NumberOfProcessors uint32
|
||||
ProcessorType uint32
|
||||
AllocationGranularity uint32
|
||||
ProcessorLevel uint16
|
||||
ProcessorRevision uint16
|
||||
}
|
||||
|
||||
// WinComputerNameFormat is a wrapper for COMPUTER_NAME_FORMAT
|
||||
type WinComputerNameFormat int
|
||||
|
||||
// Definitions for WinComputerNameFormat constants
|
||||
const (
|
||||
ComputerNameNetBIOS WinComputerNameFormat = iota
|
||||
ComputerNameDNSHostname
|
||||
ComputerNameDNSDomain
|
||||
ComputerNameDNSFullyQualified
|
||||
ComputerNamePhysicalNetBIOS
|
||||
ComputerNamePhysicalDNSHostname
|
||||
ComputerNamePhysicalDNSDomain
|
||||
ComputerNamePhysicalDNSFullyQualified
|
||||
ComputerNameMax
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
procGetSystemInfo = kernel32.NewProc("GetSystemInfo")
|
||||
procGlobalMemoryStatusEx = kernel32.NewProc("GlobalMemoryStatusEx")
|
||||
procGetComputerNameExW = kernel32.NewProc("GetComputerNameExW")
|
||||
)
|
||||
|
||||
// GlobalMemoryStatusEx retrieves information about the system's current usage of both physical and virtual memory.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex
|
||||
func GlobalMemoryStatusEx() (MemoryStatus, error) {
|
||||
var mse memoryStatusEx
|
||||
mse.dwLength = (uint32)(unsafe.Sizeof(mse))
|
||||
r1, _, err := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&mse)))
|
||||
|
||||
if ret := *(*bool)(unsafe.Pointer(&r1)); ret == false {
|
||||
return MemoryStatus{}, err
|
||||
}
|
||||
|
||||
return MemoryStatus{
|
||||
MemoryLoad: mse.DwMemoryLoad,
|
||||
TotalPhys: mse.UllTotalPhys,
|
||||
AvailPhys: mse.UllAvailPhys,
|
||||
TotalPageFile: mse.UllTotalPageFile,
|
||||
AvailPageFile: mse.UllAvailPageFile,
|
||||
TotalVirtual: mse.UllTotalVirtual,
|
||||
AvailVirtual: mse.UllAvailVirtual,
|
||||
AvailExtendedVirtual: mse.UllAvailExtendedVirtual,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSystemInfo is an idiomatic wrapper for the GetSystemInfo function from sysinfoapi
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo
|
||||
func GetSystemInfo() SystemInfo {
|
||||
var info lpSystemInfo
|
||||
procGetSystemInfo.Call(uintptr(unsafe.Pointer(&info)))
|
||||
return SystemInfo{
|
||||
Arch: ProcessorArchitecture(info.Arch.WProcessorArchitecture),
|
||||
PageSize: info.DwPageSize,
|
||||
MinimumApplicationAddress: info.LpMinimumApplicationAddress,
|
||||
MaximumApplicationAddress: info.LpMinimumApplicationAddress,
|
||||
ActiveProcessorMask: info.DwActiveProcessorMask,
|
||||
NumberOfProcessors: info.DwNumberOfProcessors,
|
||||
ProcessorType: info.DwProcessorType,
|
||||
AllocationGranularity: info.DwAllocationGranularity,
|
||||
ProcessorLevel: info.WProcessorLevel,
|
||||
ProcessorRevision: info.WProcessorRevision,
|
||||
}
|
||||
}
|
||||
|
||||
// GetComputerName wraps the GetComputerNameW function in a more Go-like way
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw
|
||||
func GetComputerName(f WinComputerNameFormat) (string, error) {
|
||||
// 1kb buffer to accept computer name. This should be more than enough as the maximum size
|
||||
// returned is the max length of a DNS name, which this author believes is 253 characters.
|
||||
size := 1024
|
||||
var buffer [1024]uint16
|
||||
r1, _, err := procGetComputerNameExW.Call(uintptr(f), uintptr(unsafe.Pointer(&buffer)), uintptr(unsafe.Pointer(&size)))
|
||||
if r1 == 0 {
|
||||
return "", err
|
||||
}
|
||||
bytes := buffer[0:size]
|
||||
out := utf16.Decode(bytes)
|
||||
return string(out), nil
|
||||
}
|
||||
Reference in New Issue
Block a user