-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathntp.go
143 lines (127 loc) · 3.24 KB
/
ntp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package kvm
import (
"errors"
"fmt"
"log"
"net/http"
"os/exec"
"time"
"github.com/beevik/ntp"
)
const (
timeSyncRetryStep = 5 * time.Second
timeSyncRetryMaxInt = 1 * time.Minute
timeSyncWaitNetChkInt = 100 * time.Millisecond
timeSyncWaitNetUpInt = 3 * time.Second
timeSyncInterval = 1 * time.Hour
timeSyncTimeout = 2 * time.Second
)
var (
timeSynced = false
timeSyncRetryInterval = 0 * time.Second
defaultNTPServers = []string{
"time.cloudflare.com",
"time.apple.com",
}
)
func TimeSyncLoop() {
for {
if !networkState.checked {
time.Sleep(timeSyncWaitNetChkInt)
continue
}
if !networkState.Up {
log.Printf("Waiting for network to come up")
time.Sleep(timeSyncWaitNetUpInt)
continue
}
log.Printf("Syncing system time")
start := time.Now()
err := SyncSystemTime()
if err != nil {
log.Printf("Failed to sync system time: %v", err)
// retry after a delay
timeSyncRetryInterval += timeSyncRetryStep
time.Sleep(timeSyncRetryInterval)
// reset the retry interval if it exceeds the max interval
if timeSyncRetryInterval > timeSyncRetryMaxInt {
timeSyncRetryInterval = 0
}
continue
}
log.Printf("Time sync successful, now is: %v, time taken: %v", time.Now(), time.Since(start))
timeSynced = true
time.Sleep(timeSyncInterval) // after the first sync is done
}
}
func SyncSystemTime() (err error) {
now, err := queryNetworkTime()
if err != nil {
return fmt.Errorf("failed to query network time: %w", err)
}
err = setSystemTime(*now)
if err != nil {
return fmt.Errorf("failed to set system time: %w", err)
}
return nil
}
func queryNetworkTime() (*time.Time, error) {
ntpServers, err := getNTPServersFromDHCPInfo()
if err != nil {
log.Printf("failed to get NTP servers from DHCP info: %v\n", err)
}
if ntpServers == nil {
ntpServers = defaultNTPServers
log.Printf("Using default NTP servers: %v\n", ntpServers)
} else {
log.Printf("Using NTP servers from DHCP: %v\n", ntpServers)
}
for _, server := range ntpServers {
now, err := queryNtpServer(server, timeSyncTimeout)
if err == nil {
log.Printf("NTP server [%s] returned time: %v\n", server, now)
return now, nil
}
}
httpUrls := []string{
"http://apple.com",
"http://cloudflare.com",
}
for _, url := range httpUrls {
now, err := queryHttpTime(url, timeSyncTimeout)
if err == nil {
return now, nil
}
}
return nil, errors.New("failed to query network time")
}
func queryNtpServer(server string, timeout time.Duration) (now *time.Time, err error) {
resp, err := ntp.QueryWithOptions(server, ntp.QueryOptions{Timeout: timeout})
if err != nil {
return nil, err
}
return &resp.Time, nil
}
func queryHttpTime(url string, timeout time.Duration) (*time.Time, error) {
client := http.Client{
Timeout: timeout,
}
resp, err := client.Head(url)
if err != nil {
return nil, err
}
dateStr := resp.Header.Get("Date")
now, err := time.Parse(time.RFC1123, dateStr)
if err != nil {
return nil, err
}
return &now, nil
}
func setSystemTime(now time.Time) error {
nowStr := now.Format("2006-01-02 15:04:05")
output, err := exec.Command("date", "-s", nowStr).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to run date -s: %w, %s", err, string(output))
}
return nil
}