// ntp is a minimal NTP client for gokrazy. package main import ( "encoding/json" "flag" "io/ioutil" "log" "math/rand" "net" "os" "os/signal" "path/filepath" "syscall" "time" "github.com/beevik/ntp" "github.com/rtr7/dhcp4" ) var server = "0.gokrazy.pool.ntp.org" var permDir = flag.String("perm", "/perm", "path to replace /perm") func set(rtc *os.File) error { r, err := ntp.Query(server) if err != nil { return err } tv := syscall.NsecToTimeval(r.Time.UnixNano()) if err := syscall.Settimeofday(&tv); err != nil { return err } log.Printf("clock set to %v using %v", r.Time, server) if rtc == nil { return nil } return setRTC(rtc, r.Time.UTC()) } func main() { flag.Parse() log.SetFlags(log.LstdFlags | log.Lshortfile) 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) go func() { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR1) for range ch { loadNTPServer() } }() loadNTPServer() for { 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) } } func loadNTPServer() { var lease dhcp4.Lease file, err := ioutil.ReadFile(filepath.Join(*permDir, "dhcp4/lease.json")) if err != nil { log.Println(err) return } err = json.Unmarshal(file, &lease) if err != nil { log.Println(err) return } if len(lease.DNS) > 0 && !lease.DNS[0].To4().Equal(net.IPv4zero) { server = lease.DNS[0].String() log.Printf("Setting ntp server to: %s", server) } }