Repository navigation
Conversation
Add a w32time source which reads the status of the Windows Time service directly from w32time.dll (W32TimeQueryStatus) instead of spawning w32tm.exe. It needs no administrative rights and provides the same data as w32tm /query /status /verbose: phase offset, stratum, source, clock state, last synchronization result and the time of the last successful synchronization. On 64 bit windows the new source is preferred in the automatic source detection. w32tm.exe is kept as a fallback and remains selectable with source=w32tm. 32 bit builds cannot use it: the 386 structure layout does not match the dll and on 64 bit windows there is no w32time.dll for a 32 bit process, so they continue to use w32tm. The new source adds the filter attributes leap_indicator, state, state_name, last_sync_result, last_sync_result_name, root_delay, root_dispersion and last_sync, which are only set for w32time. The existing metrics (offset, stratum) are unchanged. A failed query returns UNKNOWN instead of CRITICAL, so a stopped time service does not show up as a clock problem. A successful query which reports a state other than Sync still returns CRITICAL, as before. The implementation is a port of the windows_exporter W32Time support (prometheus-community/windows_exporter#2813). check_ntp_offset_windows.go carries the Apache-2.0 header and the origin, check_ntp_offset_other.go is the stub for the other platforms. The structure layout was verified against w32tm /query /status on windows. The source detection was changed from a sequence of if blocks to an ordered list to make adding the new source simpler. Unit tests cover the captured W32TIME_STATUS_INFO buffer and the entry formatting, the live windows test skips unless the service is running and synchronized. Changes and the generated check documentation are updated.
|
|
| | jitter | jitter of the clock in milliseconds | | ||
| | offset | Time offset to the NTP server in milliseconds. SNClient adds this value as a metric. | | ||
| | offset_seconds | Time offset to the NTP server in seconds. SNClient does not add this value as a metric. SNClient converts thresholds from 'offset_seconds' to 'offset' without a message. | | ||
| | Attribute | Description | |
There was a problem hiding this comment.
some of these attributes are only present in w32time.dll backend.
it is way more verbose than some other tools like chrony. i included all of them here, what is not needed can be trimmed.
| return nil | ||
| } | ||
|
|
||
| // ntpSource is a provider of time data, tried in the order of ntpSources |
There was a problem hiding this comment.
refactored this to a struct to avoid golangci warnings
| } | ||
|
|
||
| // w32timeStatus is the status of the local Windows Time service. It is filled | ||
| // by queryW32TimeStatus, a port of the windows_exporter (Apache-2.0, Copyright |
There was a problem hiding this comment.
needed to add license details here, its apache 2
|
|
||
| // On 386 there is no w32time.dll in a 32 bit process and the structure layout | ||
| // differs, so automatic detection has to use w32tm. | ||
| if runtime.GOOS == "windows" && runtime.GOARCH == "386" { |
There was a problem hiding this comment.
like it says in the PR description, this method likely does not work on a 32 bit system.
Need to find its own dll and test it, the structs could be packed differently.
Do not want to commit it without testing, so return an error here.
| | last_sync_result_name | name of the last synchronization result. Only set with source w32time. | | ||
| | root_delay | total round-trip delay to the primary time source in milliseconds. Only set with source w32time. | | ||
| | root_dispersion | root dispersion, the maximum error relative to the primary time source in milliseconds. Only set with source w32time. | | ||
| | last_sync | time of the last successful synchronization in RFC 3339 format. Only set with source w32time. | |
There was a problem hiding this comment.
this can be translated to another format if needed. cannot remember if snclient has a preferred time format
There was a problem hiding this comment.
default format is unix timestamps (unit: UDate) then there are formatters to convert this in any user supplied format.
There was a problem hiding this comment.
done, its now an unix timestamp with milisecond precision
| // recorded by the windows_exporter project (Apache-2.0, Copyright The | ||
| // Prometheus Authors) and is reused to test the ported parser. | ||
| const statusSample = "" + | ||
| "78000000 00000000 01000000 06000000" + |
There was a problem hiding this comment.
this binary data comes from prometheus tests.
testing to this detail might be too excessive to put in snclient, so I can remove this if desired.
AI GENERATED
Summary
Adds a
w32timesource tocheck_ntp_offsetthat reads the Windows Time statusfrom
w32time.dll(W32TimeQueryStatus) instead of spawningw32tm.exe. It ispreferred on 64-bit Windows;
w32tm.exestays as fallback and can still beselected with
source=w32tm.Why
w32tmoutput; the DLL returnstyped values and needs no admin rights.
windows_exporter(PR #2813); no new dependency or package.What changed
w32timesource: Windows implementation inpkg/snclient/check_ntp_offset_windows.go, stub incheck_ntp_offset_other.go, shared types and entry formatting incheck_ntp_offset.go.unchanged.
w32time:leap_indicatorstatestate_nameUnset,Hold,Sync,Spike)last_sync_resultlast_sync_result_nameSuccess,NoData, …)root_delayroot_dispersionlast_syncI can trim those if needed @sni . I know that other providers are not as detailed, but just like check_drivesize I get a bit more information when using DLLs directly.
Behaviour
offset,stratum, plusjitterwhere available);w32timehas no jitter, likew32tm.unsupported layout), so a collection error is not reported as a clock problem.
Syncreturns CRITICAL, as before.sourceattribute changes fromw32tmtow32time.32-bit Windows
w32timeis unavailable there (386 layout mismatch; no 32-bitw32time.dllon64-bit Windows).
autousesw32tm, andsource=w32timereturns UNKNOWN.Need a VM to test/fix this out, but did not do it yet. Basic 32bit compiled snclient is not enough, I need the 32bit version of the DLL that I can only get in a 32 bit windows install. If it is important, I can test it separately @sni
Verification
Compared field by field with
w32tm /query /status /verboseon Windows 11 24H2amd64 while the clock was 2.08s fast: phase offset (2.0826758s, same sign), poll
interval, root delay/dispersion, precision, stratum, leap indicator, source,
state, last sync result and last successful sync all matched. The phase offset
matched again after a resync (0.0008642s).
go test ./pkg/snclient -run 'TestCheckNTPOffset|TestParseW32TimeStatus'covers the captured
W32TIME_STATUS_INFObuffer, entry formatting and statehandling; the live Windows test skips unless the service is running and in
Sync.go vetpasses for windows/linux/darwin/freebsd; 386 and amd64 builds compile;make docswas run.make golangci,make citest.Review notes
sync.Once; a missing DLL or export falls backcleanly to the next source.
parseW32TimeStatusrejects the 386 layout instead of reading shifted fields.