forked from richardcochran/linuxptp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lstab.c
263 lines (229 loc) · 6.15 KB
/
lstab.c
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/**
* @file lstab.c
* @note Copyright (C) 2012 Richard Cochran <[email protected]>
* @note SPDX-License-Identifier: GPL-2.0+
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "lstab.h"
/*
* Keep a history of the TAI - UTC offset in a lookup table.
*
* Each entry gives the NTP time when a new TAI offset came into
* effect. This is always the second immediately after a leap second.
*
* The size of the table is the number of entries from the NIST table,
* plus room for two hundred more entries to be added at run time.
* Since there can be at most two leap seconds per year, this allows
* for at least one hundred years.
*
* Information about leap seconds is published by the IERS in the
* bulletin:
* https://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat
*
* The table data is available from
* https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list
*
* Backup source:
* https://data.iana.org/time-zones/tzdb/leapseconds
*
* When updating this table, do not forget to set N_HISTORICAL_LEAPS
* and the expiration date.
*/
#define BASE_TAI_OFFSET 10
#define N_HISTORICAL_LEAPS 28
#define N_LEAPS (N_HISTORICAL_LEAPS + 200)
#define NTP_UTC_OFFSET 2208988800ULL
struct epoch_marker {
int offset; /* TAI - UTC offset of epoch */
uint64_t ntp; /* NTP time of epoch */
uint64_t tai; /* TAI time of epoch */
uint64_t utc; /* UTC time of epoch */
};
struct lstab {
struct epoch_marker lstab[N_LEAPS];
uint64_t expiration_utc;
const char *leapfile;
time_t lsfile_mtime;
int length;
};
static const uint64_t expiration_date_ntp = 3928521600ULL; /* 24 June 2024 */
static const uint64_t offset_table[N_LEAPS * 2] = {
2272060800ULL, 10, /* 1 Jan 1972 */
2287785600ULL, 11, /* 1 Jul 1972 */
2303683200ULL, 12, /* 1 Jan 1973 */
2335219200ULL, 13, /* 1 Jan 1974 */
2366755200ULL, 14, /* 1 Jan 1975 */
2398291200ULL, 15, /* 1 Jan 1976 */
2429913600ULL, 16, /* 1 Jan 1977 */
2461449600ULL, 17, /* 1 Jan 1978 */
2492985600ULL, 18, /* 1 Jan 1979 */
2524521600ULL, 19, /* 1 Jan 1980 */
2571782400ULL, 20, /* 1 Jul 1981 */
2603318400ULL, 21, /* 1 Jul 1982 */
2634854400ULL, 22, /* 1 Jul 1983 */
2698012800ULL, 23, /* 1 Jul 1985 */
2776982400ULL, 24, /* 1 Jan 1988 */
2840140800ULL, 25, /* 1 Jan 1990 */
2871676800ULL, 26, /* 1 Jan 1991 */
2918937600ULL, 27, /* 1 Jul 1992 */
2950473600ULL, 28, /* 1 Jul 1993 */
2982009600ULL, 29, /* 1 Jul 1994 */
3029443200ULL, 30, /* 1 Jan 1996 */
3076704000ULL, 31, /* 1 Jul 1997 */
3124137600ULL, 32, /* 1 Jan 1999 */
3345062400ULL, 33, /* 1 Jan 2006 */
3439756800ULL, 34, /* 1 Jan 2009 */
3550089600ULL, 35, /* 1 Jul 2012 */
3644697600ULL, 36, /* 1 Jul 2015 */
3692217600ULL, 37, /* 1 Jan 2017 */
};
static void epoch_marker_init(struct epoch_marker *ls, uint64_t val, int offset)
{
ls->ntp = val;
ls->utc = val - NTP_UTC_OFFSET;
ls->tai = val - NTP_UTC_OFFSET + offset;
ls->offset = offset;
}
static void lstab_init(struct lstab *lstab)
{
struct epoch_marker *ls;
uint64_t offset, val;
int i;
for (i = 0; i < N_HISTORICAL_LEAPS; i++) {
ls = lstab->lstab + i;
val = offset_table[2 * i];
offset = offset_table[2 * i + 1];
epoch_marker_init(ls, val, offset);
}
lstab->expiration_utc = expiration_date_ntp - NTP_UTC_OFFSET;
lstab->length = i;
}
void lstab_print(struct lstab *lstab, FILE *fp)
{
int i, len = lstab->length;
fprintf(fp, "%3s%12s%12s%12s%4s\n", "idx", "NTP", "TAI", "UTC", "OFF");
for (i = 0; i < len; i++) {
fprintf(fp, "%3d" "%12" PRIu64 "%12" PRIu64 "%12" PRIu64 "%4d\n", i,
lstab->lstab[i].ntp, lstab->lstab[i].tai,
lstab->lstab[i].utc, lstab->lstab[i].offset);
}
}
static int lstab_read(struct lstab *lstab, const char *name)
{
uint64_t expiration, val;
struct epoch_marker *ls;
int index = 0, offset;
char buf[1024];
FILE *fp;
fp = fopen(name, "r");
if (!fp) {
fprintf(stderr, "failed to open '%s' for reading: %m\n", name);
return -1;
}
while (index < N_LEAPS) {
if (!fgets(buf, sizeof(buf), fp)) {
break;
}
if (1 == sscanf(buf, "#@ %" PRIu64, &expiration)) {
lstab->expiration_utc = expiration - NTP_UTC_OFFSET;
continue;
}
if (2 == sscanf(buf, "%" PRIu64 " %d", &val, &offset)) {
ls = lstab->lstab + index;
epoch_marker_init(ls, val, offset);
index++;
}
}
fclose(fp);
if (!lstab->expiration_utc) {
fprintf(stderr, "missing expiration date in '%s'\n", name);
return -1;
}
lstab->length = index;
return 0;
}
struct lstab *lstab_create(const char *filename)
{
struct lstab *lstab = calloc(1, sizeof(*lstab));
struct stat statbuf;
int err;
if (!lstab) {
return NULL;
}
if (filename && filename[0]) {
if (lstab_read(lstab, filename)) {
free(lstab);
return NULL;
}
lstab->leapfile = filename;
err = stat(lstab->leapfile, &statbuf);
if (err) {
fprintf(stderr, "file status failed on %s: %m",
lstab->leapfile);
free(lstab);
return NULL;
}
lstab->lsfile_mtime = statbuf.st_mtim.tv_sec;
} else {
lstab_init(lstab);
}
return lstab;
}
int update_leapsecond_table(struct lstab *lstab)
{
struct stat statbuf;
int err;
if (!lstab->leapfile) {
return 0;
}
err = stat(lstab->leapfile, &statbuf);
if (err) {
fprintf(stderr, "file status failed on %s: %m",
lstab->leapfile);
return -1;
}
if (lstab->lsfile_mtime == statbuf.st_mtim.tv_sec) {
return 0;
}
printf("updating leap seconds file\n");
if (lstab_read(lstab, lstab->leapfile)) {
lstab->length = 0;
return -1;
}
lstab->lsfile_mtime = statbuf.st_mtim.tv_sec;
return 0;
}
void lstab_destroy(struct lstab *lstab)
{
free(lstab);
}
enum lstab_result lstab_utc2tai(struct lstab *lstab, uint64_t utctime,
int *tai_offset)
{
int epoch = -1, index, next;
if (update_leapsecond_table(lstab)) {
fprintf(stderr, "Failed to update leap seconds table");
return LSTAB_UNKNOWN;
}
for (index = lstab->length - 1; index > -1; index--) {
if (utctime >= lstab->lstab[index].utc) {
epoch = index;
break;
}
}
if (epoch == -1) {
return LSTAB_UNKNOWN;
}
*tai_offset = lstab->lstab[epoch].offset;
next = epoch + 1;
if (next < lstab->length && utctime == lstab->lstab[next].utc - 1) {
return LSTAB_AMBIGUOUS;
}
if (utctime > lstab->expiration_utc) {
return LSTAB_EXPIRED;
}
return LSTAB_OK;
}