kvm/internal/timesync/rtc_linux.go

106 lines
2.1 KiB
Go

//go:build linux
package timesync
import (
"fmt"
"os"
"time"
"golang.org/x/sys/unix"
)
func TimetoRtcTime(t time.Time) unix.RTCTime {
return unix.RTCTime{
Sec: int32(t.Second()),
Min: int32(t.Minute()),
Hour: int32(t.Hour()),
Mday: int32(t.Day()),
Mon: int32(t.Month() - 1),
Year: int32(t.Year() - 1900),
Wday: int32(0),
Yday: int32(0),
Isdst: int32(0),
}
}
func RtcTimetoTime(t unix.RTCTime) time.Time {
return time.Date(
int(t.Year)+1900,
time.Month(t.Mon+1),
int(t.Mday),
int(t.Hour),
int(t.Min),
int(t.Sec),
0,
time.UTC,
)
}
func (t *TimeSync) getRtcDevice() (*os.File, error) {
if t.rtcDevice == nil {
file, err := os.OpenFile(t.rtcDevicePath, os.O_RDWR, 0666)
if err != nil {
return nil, err
}
t.rtcDevice = file
}
return t.rtcDevice, nil
}
func (t *TimeSync) getRtcDeviceFd() (int, error) {
device, err := t.getRtcDevice()
if err != nil {
return 0, err
}
return int(device.Fd()), nil
}
// Read implements Read for the Linux RTC
func (t *TimeSync) readRtcTime() (time.Time, error) {
fd, err := t.getRtcDeviceFd()
if err != nil {
return time.Time{}, fmt.Errorf("failed to get RTC device fd: %w", err)
}
rtcTime, err := unix.IoctlGetRTCTime(fd)
if err != nil {
return time.Time{}, fmt.Errorf("failed to get RTC time: %w", err)
}
date := RtcTimetoTime(*rtcTime)
return date, nil
}
// Set implements Set for the Linux RTC
// ...
// It might be not accurate as the time consumed by the system call is not taken into account
// but it's good enough for our purposes
func (t *TimeSync) setRtcTime(tu time.Time) error {
rt := TimetoRtcTime(tu)
fd, err := t.getRtcDeviceFd()
if err != nil {
return fmt.Errorf("failed to get RTC device fd: %w", err)
}
currentRtcTime, err := t.readRtcTime()
if err != nil {
return fmt.Errorf("failed to read RTC time: %w", err)
}
t.l.Info().
Interface("rtc_time", tu).
Str("offset", tu.Sub(currentRtcTime).String()).
Msg("set rtc time")
if err := unix.IoctlSetRTCTime(fd, &rt); err != nil {
return fmt.Errorf("failed to set RTC time: %w", err)
}
metricRTCUpdateCount.Inc()
return nil
}