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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
| #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <getopt.h>
#include <errno.h>
#define NTP_PORT 123
#define NTP_PACKET_SIZE 48
#define TIMEOUT 5 // seconds
// NTP time starts at 1900, UNIX time at 1970
#define NTP_UNIX_DELTA 2208988800UL
// NTP packet structure
typedef struct {
uint8_t li_vn_mode; // leap indicator, version and mode
uint8_t stratum; // stratum level
uint8_t poll; // poll interval
int8_t precision; // precision
uint32_t rootdelay; // root delay
uint32_t rootdisp; // root dispersion
uint32_t refid; // reference ID
uint32_t reftimesec; // reference timestamp seconds
uint32_t reftimefrac; // reference timestamp fraction
uint32_t origtimesec; // originate timestamp seconds
uint32_t origtimefrac; // originate timestamp fraction
uint32_t rcvtimesec; // receive timestamp seconds
uint32_t rcvtimefrac; // receive timestamp fraction
uint32_t txtimesec; // transmit timestamp seconds
uint32_t txtimefrac; // transmit timestamp fraction
} ntp_packet;
// Structure to store debug information for later output
typedef struct {
int valid;
char server_ip[64];
ntp_packet packet;
struct timeval before_set;
double offset;
int sync_result;
int hwclock_result;
} debug_info_t;
// Global variables
char **server_list = NULL;
int server_count = 0;
int debug_mode = 0;
debug_info_t *debug_logs = NULL;
int debug_log_count = 0;
void cleanup() {
if (server_list) {
for (int i = 0; i < server_count; i++) {
if (server_list[i]) {
free(server_list[i]);
}
}
free(server_list);
}
if (debug_logs) {
free(debug_logs);
}
}
void parse_server_list(const char *env_servers) {
if (!env_servers) {
fprintf(stderr, "Environment variable ntpdate__list not set\n");
exit(1);
}
// Count spaces to determine number of servers
int count = 1;
for (int i = 0; env_servers[i]; i++) {
if (env_servers[i] == ' ') {
count++;
}
}
// Allocate memory for server_list
server_list = malloc(sizeof(char*) * count);
if (!server_list) {
perror("Failed to allocate memory");
exit(1);
}
// Copy server addresses
char *env_copy = strdup(env_servers);
if (!env_copy) {
perror("Failed to duplicate string");
free(server_list);
exit(1);
}
char *token = strtok(env_copy, " ");
server_count = 0;
while (token) {
server_list[server_count] = strdup(token);
if (!server_list[server_count]) {
perror("Failed to duplicate server address");
free(env_copy);
cleanup();
exit(1);
}
server_count++;
token = strtok(NULL, " ");
}
free(env_copy);
// Allocate debug logs array based on server count
if (debug_mode) {
debug_logs = malloc(sizeof(debug_info_t) * server_count);
if (!debug_logs) {
perror("Failed to allocate memory for debug logs");
exit(1);
}
memset(debug_logs, 0, sizeof(debug_info_t) * server_count);
printf("Server list loaded (%d servers):\n", server_count);
for (int i = 0; i < server_count; i++) {
printf(" - %s\n", server_list[i]);
}
printf("\n");
}
}
void init_ntp_packet(ntp_packet *packet) {
memset(packet, 0, sizeof(ntp_packet));
// Set the first byte:
// LI = 0 (no warning)
// VN = 4 (NTP version 4)
// Mode = 3 (client mode)
packet->li_vn_mode = 0x1B; // 00 011 011
}
// Get a human-readable string from a time_t
char* get_time_str(time_t t) {
static char buffer[64];
struct tm *tm_info = localtime(&t);
strftime(buffer, sizeof(buffer), "%a %b %d %H:%M:%S %Y", tm_info);
return buffer;
}
// Calculate offset between local time and NTP time
double calculate_offset(ntp_packet *packet, struct timeval *current_time) {
time_t ntp_time_sec = packet->txtimesec - NTP_UNIX_DELTA;
double ntp_time_usec = (double)packet->txtimefrac / 4294967296.0 * 1000000.0;
double ntp_time = (double)ntp_time_sec + (ntp_time_usec / 1000000.0);
double current = (double)current_time->tv_sec + (current_time->tv_usec / 1000000.0);
return ntp_time - current;
}
int sync_with_ntp_server(const char *server_ip, int debug_index) {
int sockfd;
struct sockaddr_in server_addr;
ntp_packet packet;
struct timeval before_set;
int result = 0;
// Create UDP socket
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
return 0;
}
// Set socket timeout
struct timeval timeout;
timeout.tv_sec = TIMEOUT;
timeout.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
close(sockfd);
return 0;
}
// Initialize server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(NTP_PORT);
if (inet_aton(server_ip, &server_addr.sin_addr) == 0) {
close(sockfd);
return 0;
}
// Initialize NTP packet
init_ntp_packet(&packet);
// Send NTP packet
if (sendto(sockfd, &packet, sizeof(ntp_packet), 0,
(struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
close(sockfd);
return 0;
}
// Receive response
socklen_t addr_len = sizeof(server_addr);
if (recvfrom(sockfd, &packet, sizeof(ntp_packet), 0,
(struct sockaddr*)&server_addr, &addr_len) < 0) {
close(sockfd);
return 0;
}
// Convert from network byte order to host byte order for all fields
packet.rootdelay = ntohl(packet.rootdelay);
packet.rootdisp = ntohl(packet.rootdisp);
packet.refid = ntohl(packet.refid);
packet.reftimesec = ntohl(packet.reftimesec);
packet.reftimefrac = ntohl(packet.reftimefrac);
packet.origtimesec = ntohl(packet.origtimesec);
packet.origtimefrac = ntohl(packet.origtimefrac);
packet.rcvtimesec = ntohl(packet.rcvtimesec);
packet.rcvtimefrac = ntohl(packet.rcvtimefrac);
packet.txtimesec = ntohl(packet.txtimesec);
packet.txtimefrac = ntohl(packet.txtimefrac);
// Check if we received a valid timestamp
if (packet.txtimesec == 0) {
close(sockfd);
return 0;
}
// Get current time before setting it
gettimeofday(&before_set, NULL);
// Calculate offset between local time and NTP time - do this before any prints
double offset = calculate_offset(&packet, &before_set);
// Convert NTP time to Unix time
time_t ntp_time = packet.txtimesec - NTP_UNIX_DELTA;
// Set system time
struct timeval tv;
tv.tv_sec = ntp_time;
// Convert fractional part to microseconds (2^32 fraction -> microseconds)
tv.tv_usec = (int)((double)packet.txtimefrac / 4294967296.0 * 1000000.0);
if (settimeofday(&tv, NULL) < 0) {
close(sockfd);
return 0;
}
result = 1;
close(sockfd);
// Store debug info for later if in debug mode
if (debug_mode && debug_index >= 0 && debug_index < server_count) {
debug_info_t *info = &debug_logs[debug_index];
info->valid = 1;
strncpy(info->server_ip, server_ip, sizeof(info->server_ip) - 1);
memcpy(&info->packet, &packet, sizeof(ntp_packet));
memcpy(&info->before_set, &before_set, sizeof(struct timeval));
info->offset = offset;
info->sync_result = result;
}
return result;
}
void sync_to_hwclock() {
int result = system("hwclock -w --localtime");
// Store result for debug output later
if (debug_mode) {
for (int i = 0; i < server_count; i++) {
if (debug_logs[i].valid) {
debug_logs[i].hwclock_result = result;
}
}
}
}
// Print all the stored debug information
void print_debug_logs() {
if (!debug_mode || !debug_logs) return;
printf("\n===== Debug Log Summary =====\n");
int success_count = 0;
for (int i = 0; i < server_count; i++) {
debug_info_t *info = &debug_logs[i];
if (!info->valid) continue;
printf("\nServer: %s\n", info->server_ip);
printf(" Sync Result: %s\n", info->sync_result ? "SUCCESS" : "FAILED");
if (info->sync_result) {
success_count++;
// Print packet details
ntp_packet *packet = &info->packet;
printf(" NTP Packet Details:\n");
printf(" - LI/VN/Mode: 0x%02x\n", packet->li_vn_mode);
printf(" - Stratum: %u\n", packet->stratum);
printf(" - Poll: %u\n", packet->poll);
printf(" - Precision: %d\n", packet->precision);
printf(" - Root Delay: 0x%08x\n", packet->rootdelay);
printf(" - Root Dispersion: 0x%08x\n", packet->rootdisp);
printf(" - Reference ID: 0x%08x\n", packet->refid);
printf(" - Reference Timestamp: %u.%u sec\n", packet->reftimesec, packet->reftimefrac);
printf(" - Originate Timestamp: %u.%u sec\n", packet->origtimesec, packet->origtimefrac);
printf(" - Receive Timestamp: %u.%u sec\n", packet->rcvtimesec, packet->rcvtimefrac);
printf(" - Transmit Timestamp: %u.%u sec\n", packet->txtimesec, packet->txtimefrac);
// Converted values
time_t unix_time = packet->txtimesec - NTP_UNIX_DELTA;
printf(" - NTP Time (seconds): %u\n", packet->txtimesec);
printf(" - Unix Time (seconds): %ld\n", (long)unix_time);
printf(" - Human-readable time: %s\n", get_time_str(unix_time));
double frac = (double)packet->txtimefrac / 4294967296.0; // 2^32
printf(" - Fractional part: %.9f (%u/2^32)\n", frac, packet->txtimefrac);
printf(" - Microseconds: %ld\n", (long)(frac * 1000000.0));
printf(" Time Offset Calculation:\n");
printf(" - Before system time: %s.%06ld\n",
get_time_str(info->before_set.tv_sec), (long)info->before_set.tv_usec);
printf(" - NTP time: %s.%06ld\n",
get_time_str(unix_time), (long)(frac * 1000000.0));
printf(" - Offset: %.6f seconds\n", info->offset);
printf(" Hardware Clock Update: %s\n",
info->hwclock_result == 0 ? "SUCCESS" : "FAILED");
}
}
printf("\nSummary: Successfully synced with %d/%d NTP servers\n", success_count, server_count);
// Reset debug logs for next cycle
memset(debug_logs, 0, sizeof(debug_info_t) * server_count);
}
void perform_time_sync() {
int success_count = 0;
// Try all servers in the list
for (int i = 0; i < server_count; i++) {
int result = sync_with_ntp_server(server_list[i], i);
if (result) {
success_count++;
}
}
if (success_count > 0) {
// Try to update hardware clock
sync_to_hwclock();
}
// Print all debug logs after time sync is complete
if (debug_mode) {
print_debug_logs();
}
}
void print_usage(const char *program_name) {
printf("Usage: %s [OPTIONS]\n", program_name);
printf("Options:\n");
printf(" -d, --debug Enable debug output\n");
printf(" -h, --help Display this help message\n");
}
int main(int argc, char *argv[]) {
// Handle command line arguments
struct option long_options[] = {
{"debug", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
int option;
while ((option = getopt_long(argc, argv, "dh", long_options, NULL)) != -1) {
switch (option) {
case 'd':
debug_mode = 1;
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
print_usage(argv[0]);
return 1;
}
}
// Get server list from environment
const char *env_servers = getenv("ntpdate__list");
parse_server_list(env_servers);
// Register cleanup function
atexit(cleanup);
if (debug_mode) {
printf("Starting NTP time sync daemon...\n");
}
// Main loop - sync every hour
while (1) {
if (debug_mode) {
time_t now = time(NULL);
printf("\n===== Performing time sync at %s =====\n", get_time_str(now));
}
perform_time_sync();
if (debug_mode) {
printf("\nSleeping for 3600 seconds until next sync...\n");
}
sleep(3600); // Sleep for 1 hour
}
return 0;
}
|