PPS-Client  1.4.0
Client for synchronizing the system clock to a GPS PPS source
pps-sntp.cpp
Go to the documentation of this file.
1 
22 #include "../client/pps-client.h"
23 #define ADDR_LEN 17
24 extern struct G g;
25 
29 static struct sntpLocalVars {
30  char *ntp_server[MAX_SERVERS];
31  int serverTimeDiff[MAX_SERVERS];
32  bool threadIsBusy[MAX_SERVERS];
33  pthread_t tid[MAX_SERVERS];
34  int numServers;
35  int timeCheckEnable;
36  bool allServersQueried;
37  unsigned int lastServerUpdate;
38 } f;
39 
40 const char* srv0 = "0.debian.pool.ntp.org";
41 const char* srv1 = "1.debian.pool.ntp.org";
42 const char* srv2 = "2.debian.pool.ntp.org";
43 const char* srv3 = "3.debian.pool.ntp.org";
44 
59 
60  if (tcp->buf != NULL){
61  delete[] tcp->buf;
62  tcp->buf = NULL;
63  }
64 
65  int buflen = 0;
66  buflen += strlen(srv0) + 1;
67  buflen += strlen(srv1) + 1;
68  buflen += strlen(srv2) + 1;
69  buflen += strlen(srv3) + 1;
70 
71  tcp->ntp_server = f.ntp_server;
72 
73  tcp->buf = new char[buflen];
74 
75  char *bufptr = tcp->buf;
76  strcpy(bufptr, srv0);
77  f.ntp_server[0] = bufptr;
78 
79  bufptr += strlen(srv0) + 1;
80  strcpy(bufptr, srv1);
81  f.ntp_server[1] = bufptr;
82 
83  bufptr += strlen(srv1) + 1;
84  strcpy(bufptr, srv2);
85  f.ntp_server[2] = bufptr;
86 
87  bufptr += strlen(srv2) + 1;
88  strcpy(bufptr, srv3);
89  f.ntp_server[3] = bufptr;
90 
91  for (int i = 0; i < MAX_SERVERS; i++){
92  f.serverTimeDiff[i] = 1000000;
93  f.threadIsBusy[i] = false;
94  }
95  return 4;
96 }
97 
98 void copyToLog(char *logbuf, const char* msg){
99  char timestamp[100];
100  time_t t = time(NULL);
101  struct tm *tmp = localtime(&t);
102  strftime(timestamp, STRBUF_SZ, "%F %H:%M:%S ", tmp);
103  strcat(logbuf, timestamp);
104  strcat(logbuf, msg);
105 }
106 
119 time_t getServerTime(const char *server, int id, char *strbuf, char *logbuf){
120  struct timeval startTime, returnTime;
121  struct stat stat_buf;
122  struct tm tm;
123  int rv;
124  char *end;
125  int fracSec;
126 
127  char num[2];
128  char buf[500];
129 
130  const char *filename = "/run/shm/sntp_out"; // Construct a filename string:
131  sprintf(num, "%d", id); // "/run/shm/sntp_outn" with n the id val.
132 
133  char *cmd = buf; // Construct a command string:
134  strcpy(cmd, "sntp "); // "sntp [server_name] > /run/shm/sntp_outn"
135  strcat(cmd, server);
136  strcat(cmd, " > ");
137  strcat(cmd, filename);
138  strcat(cmd, num);
139 
140  gettimeofday(&startTime, NULL);
141  rv = sysCommand(cmd); // Issue the command:
142  if (rv == -1){
143  return -1;
144  }
145  gettimeofday(&returnTime, NULL); // system() will block until sntp returns or times out.
146 
147  if (returnTime.tv_sec - startTime.tv_sec > 0){ // Took more than 1 second
148  // Reusing buf for constructing error message on exit.
149  sprintf(buf, "Skipped server %d. Took more than 1 second to respond.\n", id);
150  copyToLog(logbuf, buf);
151  return -1;
152  }
153 
154  char *fname = buf;
155  strcpy(fname, filename);
156  strcat(fname, num);
157  // Open the file: "/run/shm/sntp_out[n]"
158  int fd = open((const char *)fname, O_RDONLY);
159  if (fd == -1){
160  strcpy(buf, "ERROR: could not open \""); // Reusing buf for constructing error message on exit.
161  strcat(buf, fname);
162  strcat(buf, "\"\n");
163  copyToLog(logbuf, buf);
164  return -1;
165  }
166 
167  fstat(fd, &stat_buf);
168  int sz = stat_buf.st_size; // Get the size of file.
169 
170  if (sz < SNTP_MSG_SZ){
171  rv = read(fd, strbuf, sz); // Read the file.
172  if (rv == -1){
173  strcpy(buf, "ERROR: reading \""); // Reusing buf for constructing error message on exit.
174  strcat(buf, filename);
175  strcat(buf, "\" was interrupted.\n");
176  copyToLog(logbuf, buf);
177  return -1;
178  }
179  strbuf[sz] = '\0';
180  close(fd);
181  remove(fname);
182  }
183  else {
184  writeFileMsgToLogbuf(fname, logbuf);
185  close(fd);
186  return -1;
187  }
188 
189  int i;
190  for (i = 0; i < sz; i++){ // Locate the first
191  if (strbuf[i] == '\n'){ // line feed.
192  i += 1;
193  break;
194  }
195  }
196  char *pLine = strbuf + i;
197 
198  if (pLine[4] != '-' || pLine[7] != '-'){
199  // Reusing buf for constructing error message on exit.
200  sprintf(buf, "SNTP server %d returned an error message:\n", id);
201  strcat(buf, strbuf);
202  strcat(buf, "\n");
203  copyToLog(logbuf, buf);
204  return -1;
205  }
206 
207  end = strstr(pLine, "+/-"); // Chop string here if this is returned
208  if (end != NULL){ // by a server.
209  *end = '\0';
210  }
211 
212  int tz;
213  float delta; // The floor of this in integer seconds will be the
214  // whole second time correction returned by SNTP.
215  memset(&tm, 0, sizeof(struct tm));
216  // Start decoding.
217  // For example: 2016-02-01 16:28:54.146050 (+0500) -0.01507
218  sscanf(pLine, "%4d-%2d-%2d %2d:%2d:%2d.%6d (%5d) %f",
219  &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
220  &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
221  &fracSec, &tz, &delta);
222 
223  return (int)floor(delta); // Time correction to be made in whole seconds
224 }
225 
234 
235  int i = tcp->serverIndex;
236  char *strbuf = tcp->strbuf + i * STRBUF_SZ;
237  char *logbuf = tcp->logbuf + i * LOGBUF_SZ;
238 
239  logbuf[0] = '\0'; // Clear the logbuf.
240 
241  tcp->threadIsBusy[i] = true;
242 
243  char *ntp_server = tcp->ntp_server[i];
244 
245  time_t timeDiff = getServerTime(ntp_server, i, strbuf, logbuf);
246  if (timeDiff == -1){
247  tcp->serverTimeDiff[i] = 1000000; // Marker for no time returned
248  }
249  else {
250  tcp->serverTimeDiff[i] = timeDiff;
251  }
252 
253  tcp->threadIsBusy[i] = false;
254 }
255 
264  int diff[MAX_SERVERS];
265  int count[MAX_SERVERS];
266 
267  int nServersReporting = 0;
268 
269  memset(diff, 0, MAX_SERVERS * sizeof(int));
270  memset(count, 0, MAX_SERVERS * sizeof(int));
271 
272  for (int j = 0; j < f.numServers; j++){ // Construct a distribution of diffs
273  if (f.serverTimeDiff[j] != 1000000){ // Skip a server not returning a time
274  int k;
275  for (k = 0; k < f.numServers; k++){
276  if (f.serverTimeDiff[j] == diff[k]){ // Matched a value
277  count[k] += 1;
278  break;
279  }
280  }
281  if (k == f.numServers){ // No value match
282  for (int m = 0; m < f.numServers; m++){
283  if (count[m] == 0){
284  diff[m] = f.serverTimeDiff[j]; // Create a new diff value
285  count[m] = 1; // Set its count to 1
286  break;
287  }
288  }
289  }
290  nServersReporting += 1;
291  }
292  }
293 
294  int maxHits = 0;
295  int maxHitsIndex = 0;
296  // Get the diff having the max number of hits
297  for (int j = 0; j < f.numServers; j++){
298  if (count[j] > maxHits){
299  maxHits = count[j];
300  maxHitsIndex = j;
301  }
302  }
303  g.consensusTimeError = diff[maxHitsIndex];
304 
305  sprintf(g.msgbuf, "Number of servers responding: %d\n", nServersReporting);
306  bufferStatusMsg(g.msgbuf);
307 
308  for (int i = 0; i < MAX_SERVERS; i++){
309  f.serverTimeDiff[i] = 1000000;
310  }
311  return nServersReporting;
312 }
313 
321 void updateLog(char *buf, int numServers){
322 
323  char *logbuf;
324 
325  for (int i = 0; i < numServers; i++){
326  logbuf = buf + i * LOGBUF_SZ;
327 
328  if (strlen(logbuf) > 0){
329  writeToLogNoTimestamp(logbuf);
330  }
331  }
332 }
333 
342  int rv;
343 
344  if (f.allServersQueried){
345  if (g.queryCount == 0){
346  f.allServersQueried = false;
347 
349  updateLog(tcp->logbuf, f.numServers);
350  }
351  if (g.queryCount > 0){
352  g.queryCount -= 1;
353  }
354  }
355 
356  if (g.seq_num >= 0
357  && g.seq_num % CHECK_TIME == 0){ // Start a time check against the list of SNTP servers
358  // Refresh the server list once per day
359  if ((g.seq_num - f.lastServerUpdate) > SECS_PER_DAY
360  || g.seq_num == CHECK_TIME){ // and at the first CHECK_TIME
361 
362  f.numServers = allocNTPServerList(tcp);
363 
364  if (f.numServers == -1){
365  sprintf(g.logbuf, "Unable to allocate the SNTP servers!\n");
366  writeToLog(g.logbuf);
367  return;
368  }
369  f.lastServerUpdate = g.seq_num;
370  }
371 
372  f.timeCheckEnable = f.numServers;
373 
374  bufferStatusMsg("Starting a time check.\n");
375  }
376 
377  if (f.timeCheckEnable > 0){
378 
379  tcp->serverIndex = f.timeCheckEnable - 1;
380 
381  f.timeCheckEnable -= 1;
382 
383  int idx = tcp->serverIndex;
384 
385  if (idx == 0){
386  f.allServersQueried = true;
387  g.queryCount = 1;
388  }
389 
390  if (f.threadIsBusy[idx]){
391  sprintf(g.msgbuf, "Server %d is busy.\n", idx);
392  bufferStatusMsg(g.msgbuf);
393  }
394  else {
395  sprintf(g.msgbuf, "Requesting time from Server %d\n", idx);
396  bufferStatusMsg(g.msgbuf);
397 
398  rv = pthread_create(&((tcp->tid)[idx]), &(tcp->attr), (void* (*)(void*))&doTimeCheck, tcp);
399  if (rv != 0){
400  sprintf(g.logbuf, "Can't create thread : %s\n", strerror(errno));
401  writeToLog(g.logbuf);
402  }
403  }
404  }
405 }
406 
417  memset(&f, 0, sizeof(struct sntpLocalVars));
418 
419  tcp->tid = f.tid;
420  tcp->serverIndex = 0;
421  tcp->serverTimeDiff = f.serverTimeDiff;
422  tcp->strbuf = new char[STRBUF_SZ * MAX_SERVERS];
423  tcp->logbuf = new char[LOGBUF_SZ * MAX_SERVERS];
424  tcp->threadIsBusy = f.threadIsBusy;
425  tcp->buf = NULL;
426 
427  int rv = pthread_attr_init(&(tcp->attr));
428  if (rv != 0) {
429  sprintf(g.logbuf, "Can't init pthread_attr_t object: %s\n", strerror(errno));
430  writeToLog(g.logbuf);
431  return -1;
432  }
433 
434  rv = pthread_attr_setstacksize(&(tcp->attr), PTHREAD_STACK_REQUIRED);
435  if (rv != 0){
436  sprintf(g.logbuf, "Can't set pthread_attr_setstacksize(): %s\n", strerror(errno));
437  writeToLog(g.logbuf);
438  return -1;
439  }
440 
441  rv = pthread_attr_setdetachstate(&(tcp->attr), PTHREAD_CREATE_DETACHED);
442  if (rv != 0){
443  sprintf(g.logbuf, "Can't set pthread_attr_t object state: %s\n", strerror(errno));
444  writeToLog(g.logbuf);
445  return -1;
446  }
447 
448  return 0;
449 }
450 
457  pthread_attr_destroy(&(tcp->attr));
458  delete[] tcp->strbuf;
459  delete[] tcp->logbuf;
460  if (tcp->buf != NULL){
461  delete[] tcp->buf;
462  }
463 }
void writeToLog(char *logbuf)
Definition: pps-files.cpp:247
char * logbuf
Space for returned log messages.
Definition: pps-client.h:138
void doTimeCheck(timeCheckParams *tcp)
Definition: pps-sntp.cpp:233
#define PTHREAD_STACK_REQUIRED
Stack space requirements for threads.
Definition: pps-client.h:41
void freeSNTPThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:456
#define CHECK_TIME
Interval between Internet time checks (about 17 minutes)
Definition: pps-client.h:75
const char * srv3
Definition: pps-sntp.cpp:43
void makeSNTPTimeQuery(timeCheckParams *tcp)
Definition: pps-sntp.cpp:341
int serverIndex
Identifying index from the list of active SNTP servers.
Definition: pps-client.h:131
#define SNTP_MSG_SZ
Definition: pps-client.h:91
int sysCommand(const char *cmd)
Definition: pps-files.cpp:89
int writeFileMsgToLogbuf(const char *filename, char *logbuf)
Definition: pps-files.cpp:393
char * strbuf
Space for messages and query strings.
Definition: pps-client.h:137
int consensusTimeError
Consensus value of whole-second time corrections for DST or leap seconds from Internet SNTP servers...
Definition: pps-client.h:228
int * serverTimeDiff
Time difference between local time and server time.
Definition: pps-client.h:132
#define LOGBUF_SZ
Definition: pps-client.h:89
void updateLog(char *buf, int numServers)
Definition: pps-sntp.cpp:321
int getTimeConsensusAndCount(void)
Definition: pps-sntp.cpp:263
#define MAX_SERVERS
Maximum number of SNTP time servers to use.
Definition: pps-client.h:74
#define STRBUF_SZ
Definition: pps-client.h:88
const char * srv0
Definition: pps-sntp.cpp:40
struct G g
Declares the global variables defined in pps-client.h.
Definition: pps-client.cpp:53
const char * srv1
Definition: pps-sntp.cpp:41
pthread_attr_t attr
Thread attribute object.
Definition: pps-client.h:130
unsigned int seq_num
Advancing count of the number of PPS interrupt timings that have been received.
Definition: pps-client.h:155
int allocNTPServerList(timeCheckParams *tcp)
Definition: pps-sntp.cpp:58
time_t getServerTime(const char *server, int id, char *strbuf, char *logbuf)
Definition: pps-sntp.cpp:119
Struct for passing arguments to and from threads querying SNTP time servers or GPS receivers...
Definition: pps-client.h:128
const char * srv2
Definition: pps-sntp.cpp:42
#define SECS_PER_DAY
Definition: pps-client.h:47
bool * threadIsBusy
True while thread is waiting for or processing a time query.
Definition: pps-client.h:139
Struct for program-wide global variables showing those important to the controller.
Definition: pps-client.h:146
void writeToLogNoTimestamp(char *logbuf)
Definition: pps-files.cpp:215
int allocInitializeSNTPThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:416
void copyToLog(char *logbuf, const char *msg)
Definition: pps-sntp.cpp:98
char * buf
Space for the active SNTP server list.
Definition: pps-client.h:135
char ** ntp_server
The active SNTP server list when SNTP is used.
Definition: pps-client.h:133
void bufferStatusMsg(const char *msg)
Definition: pps-files.cpp:290
const char * num
Definition: pps-files.cpp:48
pthread_t * tid
Thread id.
Definition: pps-client.h:129