From 6c059494af363f8b17a40a9a3c07ae36992c3bca Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 13 Jun 2018 23:51:31 +0200 Subject: [PATCH] ntp: set real-time clock (hwclock) if present --- cmd/ntp/ntp.go | 29 +++++++++++++++++++++++++---- cmd/ntp/privdrop.go | 6 +++++- cmd/ntp/privdrop_dummy.go | 4 +++- cmd/ntp/rtc.go | 26 ++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 cmd/ntp/rtc.go diff --git a/cmd/ntp/ntp.go b/cmd/ntp/ntp.go index f3246bc..4eaa0cb 100644 --- a/cmd/ntp/ntp.go +++ b/cmd/ntp/ntp.go @@ -4,29 +4,50 @@ package main import ( "log" "math/rand" + "os" "syscall" "time" "github.com/beevik/ntp" ) -func set() error { +func set(rtc *os.File) error { r, err := ntp.Query("0.gokrazy.pool.ntp.org") if err != nil { return err } tv := syscall.NsecToTimeval(r.Time.UnixNano()) - return syscall.Settimeofday(&tv) + if err := syscall.Settimeofday(&tv); err != nil { + return err + } + + if rtc == nil { + return nil + } + return setRTC(rtc, r.Time.UTC()) } func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) - mustDropPrivileges() + var rtc *os.File + var err error + if os.Getenv("NTP_PRIVILEGES_DROPPED") == "1" { + if os.Getenv("NTP_RTC") == "1" { + rtc = os.NewFile(3, "/dev/rtc0") + } + } else { + rtc, err = os.Open("/dev/rtc0") + if err != nil && !os.IsNotExist(err) { + log.Fatal(err) + } + } + + mustDropPrivileges(rtc) for { - if err := set(); err != nil { + if err := set(rtc); err != nil { log.Fatalf("setting time failed: %v", err) } time.Sleep(1*time.Hour + time.Duration(rand.Int63n(250))*time.Millisecond) diff --git a/cmd/ntp/privdrop.go b/cmd/ntp/privdrop.go index 117ac70..9b35c12 100644 --- a/cmd/ntp/privdrop.go +++ b/cmd/ntp/privdrop.go @@ -46,7 +46,7 @@ func getCaps() (caps, error) { // mustDropPrivileges executes the program in a child process, dropping root // privileges, but retaining the CAP_SYS_TIME capability to change the system // clock. -func mustDropPrivileges() { +func mustDropPrivileges(rtc *os.File) { if os.Getenv("NTP_PRIVILEGES_DROPPED") == "1" { return } @@ -71,6 +71,10 @@ func mustDropPrivileges() { cmd := exec.Command(os.Args[0]) cmd.Env = append(os.Environ(), "NTP_PRIVILEGES_DROPPED=1") + if rtc != nil { + cmd.Env = append(cmd.Env, "NTP_RTC=1") + cmd.ExtraFiles = []*os.File{rtc} + } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.SysProcAttr = &syscall.SysProcAttr{ diff --git a/cmd/ntp/privdrop_dummy.go b/cmd/ntp/privdrop_dummy.go index c05d6c1..8267ea1 100644 --- a/cmd/ntp/privdrop_dummy.go +++ b/cmd/ntp/privdrop_dummy.go @@ -2,7 +2,9 @@ package main +import "os" + // mustDropPrivileges (on Go < 1.9) does nothing, as the AmbientCaps field in // syscall.SysProcAttr was only introduced in Go 1.9. -func mustDropPrivileges() { +func mustDropPrivileges(*os.File) { } diff --git a/cmd/ntp/rtc.go b/cmd/ntp/rtc.go new file mode 100644 index 0000000..7e1842d --- /dev/null +++ b/cmd/ntp/rtc.go @@ -0,0 +1,26 @@ +package main + +import ( + "log" + "os" + "time" + "unsafe" + + "golang.org/x/sys/unix" +) + +func setRTC(rtc *os.File, now time.Time) error { + c := unix.RTCTime{ + Sec: int32(now.Second()), + Min: int32(now.Minute()), + Hour: int32(now.Hour()), + Mday: int32(now.Day()), + Mon: int32(now.Month() - 1), + Year: int32(now.Year() - 1900), + } + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, rtc.Fd(), unix.RTC_SET_TIME, uintptr(unsafe.Pointer(&c))); errno != 0 { + return errno + } + log.Printf("RTC set to %+v", c) + return nil +}