github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/ntpdate/ntpdate.go (about) 1 // Copyright 2021 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ntpdate 6 7 import ( 8 "bufio" 9 "fmt" 10 "os" 11 "strings" 12 "syscall" 13 "time" 14 15 "github.com/beevik/ntp" 16 17 "github.com/mvdan/u-root-coreutils/pkg/rtc" 18 ) 19 20 const DefaultNTPConfig = "/etc/ntp.conf" 21 22 var Debug = func(string, ...interface{}) {} 23 24 func parseServers(r *bufio.Reader) []string { 25 var uri []string 26 var l string 27 var err error 28 29 Debug("Reading config file") 30 for err == nil { 31 // This handles the case where the last line doesn't end in \n 32 l, err = r.ReadString('\n') 33 Debug("%v", l) 34 if w := strings.Fields(l); len(w) > 1 && w[0] == "server" { 35 // We look only for the server lines, we ignore options like iburst 36 // TODO(ganshun): figure out what options we want to support. 37 uri = append(uri, w[1]) 38 } 39 } 40 41 return uri 42 } 43 44 func getTime(servers []string) (time.Time, string, error) { 45 for _, s := range servers { 46 Debug("Getting time from %v", s) 47 t, err := ntp.Time(s) 48 if err == nil { 49 // Right now we return on the first valid time. 50 // We can implement better heuristics here. 51 Debug("Got time %v", t) 52 return t, s, nil 53 } 54 Debug("Error getting time from %s: %v", s, err) 55 } 56 57 return time.Time{}, "", fmt.Errorf("unable to get any time from servers %v", servers) 58 } 59 60 // SetTime sets system and optionally RTC time from NTP servers specified in sersers or the config file. 61 // If successful, returns the server used to set the time and the offset, in seconds. 62 func SetTime(servers []string, config string, fallback string, setRTC bool) (string, float64, error) { 63 return setTime(servers, config, fallback, setRTC, &realGetterSetter{}) 64 } 65 66 type timeGetterSetter interface { 67 GetTime(servers []string) (time.Time, string, error) 68 SetSystemTime(time.Time) error 69 SetRTCTime(time.Time) error 70 } 71 72 type realGetterSetter struct{} 73 74 func (*realGetterSetter) GetTime(servers []string) (time.Time, string, error) { 75 return getTime(servers) 76 } 77 78 func (*realGetterSetter) SetSystemTime(t time.Time) error { 79 tv := syscall.NsecToTimeval(t.UnixNano()) 80 return syscall.Settimeofday(&tv) 81 } 82 83 func (*realGetterSetter) SetRTCTime(t time.Time) error { 84 r, err := rtc.OpenRTC() 85 if err != nil { 86 return fmt.Errorf("unable to open RTC: %w", err) 87 } 88 defer r.Close() 89 return r.Set(t) 90 } 91 92 func setTime(servers []string, config string, fallback string, setRTC bool, gs timeGetterSetter) (string, float64, error) { 93 servers = servers[:] 94 95 if config != "" { 96 Debug("Reading NTP servers from config file: %v", config) 97 f, err := os.Open(config) 98 if err == nil { 99 defer f.Close() 100 configServers := parseServers(bufio.NewReader(f)) 101 Debug("Found %v servers", len(configServers)) 102 servers = append(servers, configServers...) 103 } else { 104 Debug("Unable to open config file: %v", err) 105 } 106 } 107 108 if len(servers) == 0 && len(fallback) != 0 { 109 Debug("No servers provided, falling back to %v", fallback) 110 servers = append(servers, fallback) 111 } 112 113 if len(servers) == 0 { 114 return "", 0, fmt.Errorf("no servers") 115 } 116 117 t, server, err := gs.GetTime(servers) 118 if err != nil { 119 return "", 0, fmt.Errorf("unable to get time: %w", err) 120 } 121 122 offset := time.Until(t).Seconds() 123 124 if err = gs.SetSystemTime(t); err != nil { 125 return "", 0, fmt.Errorf("unable to set system time: %w", err) 126 } 127 if setRTC { 128 Debug("Setting RTC time...") 129 if err = gs.SetRTCTime(t); err != nil { 130 return "", 0, fmt.Errorf("unable to set RTC time: %w", err) 131 } 132 } 133 134 return server, offset, nil 135 }