PPS-Client  2.0.0
Client for synchronizing the system clock to a GPS PPS source
pps-sntp.cpp
Go to the documentation of this file.
1 
6 /*
7  * Copyright (C) 2016-2020 Raymond S. Connell
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include "../client/pps-client.h"
25 #define ADDR_LEN 17
26 extern struct G g;
27 extern struct ppsFiles f;
28 
32 static struct nistLocalVars {
33  bool hasStarted;
34  int serverTimeDiff[MAX_SERVERS];
35  bool threadIsBusy[MAX_SERVERS];
36  pthread_t tid[MAX_SERVERS];
37  int numServers;
38  int timeCheckEnable;
39  bool allServersQueried;
40  bool gotError;
41 } n;
42 
43 void copyToLog(char *logbuf, const char* msg){
44  char timestamp[100];
45  time_t t = time(NULL);
46  struct tm *tmp = localtime(&t);
47  strftime(timestamp, STRBUF_SZ-1, "%F %H:%M:%S ", tmp);
48  strcat(logbuf, timestamp);
49  strcat(logbuf, msg);
50 }
51 
80 int getNISTTime(int id, char *strbuf, char *logbuf, time_t *timeDiff, char *nistTime_file){
81  struct timeval startTime, returnTime;
82  struct stat stat_buf;
83  char num[2];
84  char buf[500];
85 
86  sprintf(num, "%d", id); // "/run/shm/nist_outn" with n the id val.
87 
88  char *cmd = buf; // Construct a command string:
89  sprintf(cmd, "udp-time-client -u%d ", id); // "udp-time-client -uxx > /run/shm/nist_outn"
90  strcat(cmd, " > ");
91  strcat(cmd, nistTime_file);
92  strcat(cmd, num);
93 
94  gettimeofday(&startTime, NULL);
95  int rv = sysCommand(cmd); // Issue the command:
96  if (rv == -1){
97  return -1;
98  }
99  gettimeofday(&returnTime, NULL); // sysCommand() will block until udp-time-client returns or times out.
100 
101  if (returnTime.tv_sec - startTime.tv_sec > 0){ // Took more than 1 second
102  // Reusing buf for constructing error message on exit.
103 // sprintf(buf, "Skipped server %d. Unable to retrieve in the same second.\n", id);
104 // copyToLog(logbuf, buf);
105  return -1;
106  }
107 
108  char *fname = buf;
109  strcpy(fname, nistTime_file);
110  strcat(fname, num);
111  // Open the file: "/run/shm/nist_out[n]"
112  int fd = open((const char *)fname, O_RDONLY);
113  if (fd == -1){
114  strcpy(buf, "ERROR: could not open \""); // Reusing buf for constructing error message on exit.
115  strcat(buf, fname);
116  strcat(buf, "\"\n");
117  copyToLog(logbuf, buf);
118  return -1;
119  }
120 
121  fstat(fd, &stat_buf);
122  int sz = stat_buf.st_size; // Get the size of file.
123 
124  if (sz < NIST_MSG_SZ){
125  rv = read(fd, strbuf, sz); // Read the file.
126  if (rv == -1){
127  strcpy(buf, "ERROR: reading \""); // Reusing buf for constructing error message on exit.
128  strcat(buf, nistTime_file);
129  strcat(buf, "\" was interrupted.\n");
130  copyToLog(logbuf, buf);
131  return -1;
132  }
133  strbuf[sz] = '\0';
134  close(fd);
135  remove(fname);
136  }
137  else {
138  writeFileMsgToLogbuf(fname, logbuf);
139  close(fd);
140  return -1;
141  }
142 
143  char *pNum = strpbrk(strbuf, "-0123456789");
144  time_t delta;
145  if (pNum == strbuf){ // strbuf contains the time difference in whole seconds
146  sscanf(strbuf, "%ld\n", &delta); // between UDP server n and the local clock time.
147  *timeDiff = -delta;
148 // if (delta != 0){
149 // sprintf(strbuf, "getNISTTime(): Says that time is behind by %ld secs\n", *timeDiff);
150 // copyToLog(logbuf, strbuf);
151 // }
152  return 0;
153  }
154  else { // strbuf contains an error message
155  copyToLog(logbuf, strbuf);
156  return -1;
157  }
158 }
159 
168 
169  int i = tcp->serverIndex;
170  char *strbuf = tcp->strbuf + i * STRBUF_SZ;
171  char *logbuf = tcp->logbuf + i * LOGBUF_SZ;
172 
173  logbuf[0] = '\0'; // Clear the logbuf.
174 
175  tcp->threadIsBusy[i] = true;
176 
177  time_t timeDiff;
178  int r = getNISTTime(i+1, strbuf, logbuf, &timeDiff, tcp->nistTime_file);
179  if (r == -1){
180  tcp->serverTimeDiff[i] = 1000000; // Marker for no time returned
181  }
182  else {
183  tcp->serverTimeDiff[i] = timeDiff;
184  }
185 
186  tcp->threadIsBusy[i] = false;
187 }
188 
197  int timeDiff[MAX_SERVERS];
198  int count[MAX_SERVERS];
199 
200  int nServersReporting = 0;
201 
202  memset(timeDiff, 0, MAX_SERVERS * sizeof(int));
203  memset(count, 0, MAX_SERVERS * sizeof(int));
204 
205  for (int j = 0; j < n.numServers; j++){ // Construct a distribution of diffs
206  if (n.serverTimeDiff[j] != 1000000){ // Skip a server not returning a time
207  int k;
208  for (k = 0; k < n.numServers; k++){
209  if (n.serverTimeDiff[j] == timeDiff[k]){ // Matched a value
210  count[k] += 1;
211  break;
212  }
213  }
214  if (k == n.numServers){ // No value match
215  for (int m = 0; m < n.numServers; m++){
216  if (count[m] == 0){
217  timeDiff[m] = n.serverTimeDiff[j]; // Create a new timeDiff value
218  count[m] = 1; // Set its count to 1
219  break;
220  }
221  }
222  }
223  nServersReporting += 1;
224  }
225  }
226 
227  int maxHits = 0;
228  int maxHitsIndex = 0;
229  // Get the timeDiff having the max number of hits
230  for (int j = 0; j < n.numServers; j++){
231  if (count[j] > maxHits){
232  maxHits = count[j];
233  maxHitsIndex = j;
234  }
235  }
236 
237  g.consensusTimeError = timeDiff[maxHitsIndex];
238 
239  if (g.consensusTimeError != 0){
240  if (maxHits >= 3 && n.gotError == false){
241 
242  sprintf(g.msgbuf, "getTimeConsensusAndCount(): Time is behind by %d seconds.\n", timeDiff[maxHitsIndex]);
243  bufferStatusMsg(g.msgbuf);
244  n.gotError = true;
245 
246  }
247  else if (n.gotError == true){
248 
249  sprintf(g.msgbuf, "getTimeConsensusAndCount(): Waiting for controller to become active to correct the time error.\n");
250  bufferStatusMsg(g.msgbuf);
251  }
252  else {
253  sprintf(g.msgbuf, "getTimeConsensusAndCount(): Number of servers responding: %d\n", nServersReporting);
254  bufferStatusMsg(g.msgbuf);
255  }
256  }
257  else {
258  n.gotError = false;
259 
260  sprintf(g.msgbuf, "getTimeConsensusAndCount(): Number of servers responding: %d\n", nServersReporting);
261  bufferStatusMsg(g.msgbuf);
262  }
263 
264  for (int i = 0; i < MAX_SERVERS; i++){
265  n.serverTimeDiff[i] = 1000000;
266  }
267  return nServersReporting;
268 }
269 
277 void updateLog(char *buf, int numServers){
278 
279  char *logbuf;
280 
281  for (int i = 0; i < numServers; i++){
282  logbuf = buf + i * LOGBUF_SZ;
283 
284  if (strlen(logbuf) > 0){
285  writeToLogNoTimestamp(logbuf);
286  }
287  }
288 }
289 
301  int rv;
302 
303  if (n.allServersQueried){
304  if (g.queryWait == false){
305  n.allServersQueried = false;
306 
308  updateLog(tcp->logbuf, n.numServers);
309  }
310  if (g.queryWait == true){ // This delays getting the time consensus by one second
311  g.queryWait = false; // to allow the last thread enough time to report back.
312  }
313  }
314 
315  if (n.hasStarted == false && // Start a time check against the list of NIST servers
316  (g.activeCount == 1 || g.activeCount % CHECK_TIME == 0)){
317  n.hasStarted = true;
318 
319  n.numServers = MAX_SERVERS;
320 
321  for (int i = 0; i < MAX_SERVERS; i++){
322  n.serverTimeDiff[i] = 1000000;
323  n.threadIsBusy[i] = false;
324  }
325 
326  n.timeCheckEnable = n.numServers; // Initialize the server list
327 
328  bufferStatusMsg("Starting a time check.\n");
329  }
330 
331  if (n.timeCheckEnable > 0){ // As long as is true dispatch a thread to query a server
332  // starting at the highest thread index.
333  tcp->serverIndex = n.timeCheckEnable - 1;
334 
335  n.timeCheckEnable -= 1; // In next second, dispatch thread with next lower index
336 
337  int idx = tcp->serverIndex;
338 
339  if (idx == 0){
340  n.allServersQueried = true;
341  n.hasStarted = false;
342  g.queryWait = true;
343  }
344 
345  if (n.threadIsBusy[idx]){
346  sprintf(g.msgbuf, "Server %d is busy.\n", idx);
347  bufferStatusMsg(g.msgbuf);
348  }
349  else {
350  sprintf(g.msgbuf, "Requesting time from Server %d\n", idx);
351  bufferStatusMsg(g.msgbuf);
352 
353  rv = pthread_create(&((tcp->tid)[idx]), &(tcp->attr), (void* (*)(void*))&doTimeCheck, tcp);
354  if (rv != 0){
355  sprintf(g.logbuf, "Can't create thread : %s\n", strerror(errno));
356  writeToLog(g.logbuf, "makeNISTTimeQuery()");
357  }
358  }
359  }
360 }
361 
372  memset(&n, 0, sizeof(struct nistLocalVars));
373 
374  tcp->tid = n.tid;
375  tcp->serverIndex = 0;
376  tcp->serverTimeDiff = n.serverTimeDiff;
377  tcp->strbuf = new char[STRBUF_SZ * MAX_SERVERS];
378  tcp->logbuf = new char[LOGBUF_SZ * MAX_SERVERS];
379  tcp->threadIsBusy = n.threadIsBusy;
380  tcp->buf = NULL;
381  tcp->nistTime_file = f.nistTime_file;
382 
383  int rv = pthread_attr_init(&(tcp->attr));
384  if (rv != 0) {
385  sprintf(g.logbuf, "Can't init pthread_attr_t object: %s\n", strerror(errno));
386  writeToLog(g.logbuf, "allocInitializeNISTThreads()");
387  return -1;
388  }
389 
390  rv = pthread_attr_setstacksize(&(tcp->attr), PTHREAD_STACK_REQUIRED);
391  if (rv != 0){
392  sprintf(g.logbuf, "Can't set pthread_attr_setstacksize(): %s\n", strerror(errno));
393  writeToLog(g.logbuf, "allocInitializeNISTThreads()");
394  return -1;
395  }
396 
397  rv = pthread_attr_setdetachstate(&(tcp->attr), PTHREAD_CREATE_DETACHED);
398  if (rv != 0){
399  sprintf(g.logbuf, "Can't set pthread_attr_t object state: %s\n", strerror(errno));
400  writeToLog(g.logbuf, "allocInitializeNISTThreads()");
401  return -1;
402  }
403 
404  return 0;
405 }
406 
413  pthread_attr_destroy(&(tcp->attr));
414  delete[] tcp->strbuf;
415  delete[] tcp->logbuf;
416  if (tcp->buf != NULL){
417  delete[] tcp->buf;
418  }
419 }
timeCheckParams::logbuf
char * logbuf
Space for returned log messages.
Definition: pps-client.h:156
timeCheckParams
Definition: pps-client.h:146
copyToLog
void copyToLog(char *logbuf, const char *msg)
Definition: pps-sntp.cpp:43
f
struct ppsFiles f
PPS-Client internal files.
Definition: pps-files.cpp:63
G::activeCount
unsigned int activeCount
Advancing count of active (not skipped) controller cycles once G.isControlling is "true".
Definition: pps-client.h:181
doTimeCheck
void doTimeCheck(timeCheckParams *tcp)
Definition: pps-sntp.cpp:167
timeCheckParams::serverIndex
int serverIndex
Identifying index from the list of active NIST servers.
Definition: pps-client.h:149
updateLog
void updateLog(char *buf, int numServers)
Definition: pps-sntp.cpp:277
sysCommand
int sysCommand(const char *cmd)
Definition: pps-files.cpp:193
freeNISTThreads
void freeNISTThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:412
timeCheckParams::attr
pthread_attr_t attr
Thread attribute object.
Definition: pps-client.h:148
writeToLog
void writeToLog(char *logbuf, const char *location)
Definition: pps-files.cpp:364
allocInitializeNISTThreads
int allocInitializeNISTThreads(timeCheckParams *tcp)
Definition: pps-sntp.cpp:371
timeCheckParams::tid
pthread_t * tid
Thread id.
Definition: pps-client.h:147
g
struct G g
Declares the global variables defined in pps-client.h.
Definition: pps-client.cpp:55
num
const char * num
Definition: pps-files.cpp:47
G::consensusTimeError
int consensusTimeError
Consensus value of whole-second time corrections for DST or leap seconds from Internet NIST servers.
Definition: pps-client.h:240
makeNISTTimeQuery
void makeNISTTimeQuery(timeCheckParams *tcp)
Definition: pps-sntp.cpp:300
writeFileMsgToLogbuf
int writeFileMsgToLogbuf(const char *filename, char *logbuf)
Definition: pps-files.cpp:750
timeCheckParams::threadIsBusy
bool * threadIsBusy
True while thread is waiting for or processing a time query.
Definition: pps-client.h:157
timeCheckParams::buf
char * buf
Space for the active NIST server list.
Definition: pps-client.h:153
bufferStatusMsg
void bufferStatusMsg(const char *msg)
Definition: pps-files.cpp:407
CHECK_TIME
#define CHECK_TIME
Interval between Internet time checks (about 17 minutes)
Definition: pps-client.h:79
getTimeConsensusAndCount
int getTimeConsensusAndCount(void)
Definition: pps-sntp.cpp:196
LOGBUF_SZ
#define LOGBUF_SZ
Definition: pps-client.h:96
STRBUF_SZ
#define STRBUF_SZ
Definition: pps-client.h:95
MAX_SERVERS
#define MAX_SERVERS
Maximum number of NIST time servers to use.
Definition: pps-client.h:78
timeCheckParams::strbuf
char * strbuf
Space for messages and query strings.
Definition: pps-client.h:155
nistTime_file
const char * nistTime_file
Definition: pps-files.cpp:41
getNISTTime
int getNISTTime(int id, char *strbuf, char *logbuf, time_t *timeDiff, char *nistTime_file)
Definition: pps-sntp.cpp:80
writeToLogNoTimestamp
void writeToLogNoTimestamp(char *logbuf)
Definition: pps-files.cpp:332
timeCheckParams::nistTime_file
char * nistTime_file
Definition: pps-client.h:161
G
Struct for program-wide global variables showing those important to the controller.
Definition: pps-client.h:167
PTHREAD_STACK_REQUIRED
#define PTHREAD_STACK_REQUIRED
Stack space requirements for threads.
Definition: pps-client.h:62
NIST_MSG_SZ
#define NIST_MSG_SZ
Definition: pps-client.h:98
timeCheckParams::serverTimeDiff
int * serverTimeDiff
Time difference between local time and server time.
Definition: pps-client.h:150