//go:build linux package hostcollect import ( "math" "syscall" "github.com/itworx/pulse/internal/host" ) // Bits of the adjtimex status word, defined here because the standard syscall package // does not export them on every architecture. const ( staUnsync = 0x0040 // clock is not synchronised to a reference staNano = 0x2000 // offset and precision are in nanoseconds, not microseconds timeError = 5 // adjtimex state: the clock is not synchronised ) // readClockSync asks the kernel about clock discipline with adjtimex(2) in read mode. // // A zeroed Timex has Modes == 0, which makes the call a pure read: it adjusts nothing. // Docker's default seccomp profile still gates adjtimex behind CAP_SYS_TIME, which the // agent drops, so the expected in-container result is EPERM. The caller turns that into // a warning; see Collector.clock. func readClockSync() (host.RawTime, error) { timex := syscall.Timex{} state, err := syscall.Adjtimex(&timex) if err != nil { return host.RawTime{}, err } divisor := 1e6 if timex.Status&staNano != 0 { divisor = 1e9 } offset := math.Abs(float64(timex.Offset)) / divisor if math.IsNaN(offset) || math.IsInf(offset, 0) { offset = 0 } return host.RawTime{ Synchronized: timex.Status&staUnsync == 0 && state != timeError, // The kernel exposes no stratum; that belongs to the NTP daemon, which the // agent deliberately does not talk to. OffsetSeconds: offset, }, nil }