/src/ntp-dev/ntpd/refclock_shm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * refclock_shm - clock driver for utc via shared memory |
3 | | * - under construction - |
4 | | * To add new modes: Extend or union the shmTime-struct. Do not |
5 | | * extend/shrink size, because otherwise existing implementations |
6 | | * will specify wrong size of shared memory-segment |
7 | | * PB 18.3.97 |
8 | | */ |
9 | | |
10 | | #ifdef HAVE_CONFIG_H |
11 | | # include <config.h> |
12 | | #endif |
13 | | |
14 | | #include "ntp_types.h" |
15 | | |
16 | | #if defined(REFCLOCK) && defined(CLOCK_SHM) |
17 | | |
18 | | #include "ntpd.h" |
19 | | #undef fileno |
20 | | #include "ntp_io.h" |
21 | | #undef fileno |
22 | | #include "ntp_refclock.h" |
23 | | #undef fileno |
24 | | #include "timespecops.h" |
25 | | #undef fileno |
26 | | #include "ntp_stdlib.h" |
27 | | #include "ntp_assert.h" |
28 | | |
29 | | #undef fileno |
30 | | #include <ctype.h> |
31 | | #undef fileno |
32 | | |
33 | | #ifndef SYS_WINNT |
34 | | # include <sys/ipc.h> |
35 | | # include <sys/shm.h> |
36 | | # include <assert.h> |
37 | | # include <unistd.h> |
38 | | # include <stdio.h> |
39 | | #endif |
40 | | |
41 | | #ifdef HAVE_STDATOMIC_H |
42 | | # include <stdatomic.h> |
43 | | #endif /* HAVE_STDATOMIC_H */ |
44 | | |
45 | | /* |
46 | | * This driver supports a reference clock attached thru shared memory |
47 | | */ |
48 | | |
49 | | /* |
50 | | * SHM interface definitions |
51 | | */ |
52 | 0 | #define PRECISION (-1) /* precision assumed (0.5 s) */ |
53 | 0 | #define REFID "SHM" /* reference ID */ |
54 | 0 | #define DESCRIPTION "SHM/Shared memory interface" |
55 | | |
56 | 0 | #define NSAMPLES 3 /* stages of median filter */ |
57 | | |
58 | | /* |
59 | | * Mode flags |
60 | | */ |
61 | 0 | #define SHM_MODE_PRIVATE 0x0001 |
62 | | |
63 | | /* |
64 | | * Function prototypes |
65 | | */ |
66 | | static int shm_start (int unit, struct peer *peer); |
67 | | static void shm_shutdown (int unit, struct peer *peer); |
68 | | static void shm_poll (int unit, struct peer *peer); |
69 | | static void shm_timer (int unit, struct peer *peer); |
70 | | static void shm_clockstats (int unit, struct peer *peer); |
71 | | static void shm_control (int unit, const struct refclockstat * in_st, |
72 | | struct refclockstat * out_st, struct peer *peer); |
73 | | |
74 | | /* |
75 | | * Transfer vector |
76 | | */ |
77 | | struct refclock refclock_shm = { |
78 | | shm_start, /* start up driver */ |
79 | | shm_shutdown, /* shut down driver */ |
80 | | shm_poll, /* transmit poll message */ |
81 | | shm_control, /* control settings */ |
82 | | noentry, /* not used: init */ |
83 | | noentry, /* not used: buginfo */ |
84 | | shm_timer, /* once per second */ |
85 | | }; |
86 | | |
87 | | struct shmTime { |
88 | | int mode; /* 0 - if valid is set: |
89 | | * use values, |
90 | | * clear valid |
91 | | * 1 - if valid is set: |
92 | | * if count before and after read of values is equal, |
93 | | * use values |
94 | | * clear valid |
95 | | */ |
96 | | volatile int count; |
97 | | time_t clockTimeStampSec; |
98 | | int clockTimeStampUSec; |
99 | | time_t receiveTimeStampSec; |
100 | | int receiveTimeStampUSec; |
101 | | int leap; |
102 | | int precision; |
103 | | int nsamples; |
104 | | volatile int valid; |
105 | | unsigned clockTimeStampNSec; /* Unsigned ns timestamps */ |
106 | | unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */ |
107 | | int dummy[8]; |
108 | | }; |
109 | | |
110 | | struct shmunit { |
111 | | struct shmTime *shm; /* pointer to shared memory segment */ |
112 | | int forall; /* access for all UIDs? */ |
113 | | |
114 | | /* debugging/monitoring counters - reset when printed */ |
115 | | int ticks; /* number of attempts to read data*/ |
116 | | int good; /* number of valid samples */ |
117 | | int notready; /* number of peeks without data ready */ |
118 | | int bad; /* number of invalid samples */ |
119 | | int clash; /* number of access clashes while reading */ |
120 | | |
121 | | time_t max_delta; /* difference limit */ |
122 | | time_t max_delay; /* age/stale limit */ |
123 | | }; |
124 | | |
125 | | |
126 | | static struct shmTime* |
127 | | getShmTime( |
128 | | int unit, |
129 | | int/*BOOL*/ forall |
130 | | ) |
131 | 0 | { |
132 | 0 | struct shmTime *p = NULL; |
133 | |
|
134 | 0 | #ifndef SYS_WINNT |
135 | |
|
136 | 0 | int shmid; |
137 | | |
138 | | /* 0x4e545030 is NTP0. |
139 | | * Big units will give non-ascii but that's OK |
140 | | * as long as everybody does it the same way. |
141 | | */ |
142 | 0 | shmid=shmget(0x4e545030 + unit, sizeof (struct shmTime), |
143 | 0 | IPC_CREAT | (forall ? 0666 : 0600)); |
144 | 0 | if (shmid == -1) { /* error */ |
145 | 0 | msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit); |
146 | 0 | return NULL; |
147 | 0 | } |
148 | 0 | p = (struct shmTime *)shmat (shmid, 0, 0); |
149 | 0 | if (p == (struct shmTime *)-1) { /* error */ |
150 | 0 | msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit); |
151 | 0 | return NULL; |
152 | 0 | } |
153 | | |
154 | 0 | return p; |
155 | | #else |
156 | | |
157 | | static const char * nspref[2] = { "Local", "Global" }; |
158 | | char buf[20]; |
159 | | LPSECURITY_ATTRIBUTES psec = 0; |
160 | | HANDLE shmid = 0; |
161 | | SECURITY_DESCRIPTOR sd; |
162 | | SECURITY_ATTRIBUTES sa; |
163 | | unsigned int numch; |
164 | | |
165 | | numch = snprintf(buf, sizeof(buf), "%s\\NTP%d", |
166 | | nspref[forall != 0], (unit & 0xFF)); |
167 | | if (numch >= sizeof(buf)) { |
168 | | msyslog(LOG_ERR, "SHM name too long (unit %d)", unit); |
169 | | return NULL; |
170 | | } |
171 | | if (forall) { /* world access */ |
172 | | if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { |
173 | | msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit); |
174 | | return NULL; |
175 | | } |
176 | | if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) { |
177 | | msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit); |
178 | | return NULL; |
179 | | } |
180 | | sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
181 | | sa.lpSecurityDescriptor = &sd; |
182 | | sa.bInheritHandle = FALSE; |
183 | | psec = &sa; |
184 | | } |
185 | | shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE, |
186 | | 0, sizeof (struct shmTime), buf); |
187 | | if (shmid == NULL) { /*error*/ |
188 | | char buf[1000]; |
189 | | FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, |
190 | | 0, GetLastError (), 0, buf, sizeof (buf), 0); |
191 | | msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf); |
192 | | return NULL; |
193 | | } |
194 | | p = (struct shmTime *)MapViewOfFile(shmid, FILE_MAP_WRITE, 0, 0, |
195 | | sizeof (struct shmTime)); |
196 | | if (p == NULL) { /*error*/ |
197 | | char buf[1000]; |
198 | | FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, |
199 | | 0, GetLastError (), 0, buf, sizeof (buf), 0); |
200 | | msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf); |
201 | | return NULL; |
202 | | } |
203 | | |
204 | | return p; |
205 | | #endif |
206 | | |
207 | | /* NOTREACHED */ |
208 | 0 | ENSURE(!"getShmTime(): Not reached."); |
209 | 0 | } |
210 | | |
211 | | |
212 | | /* |
213 | | * shm_start - attach to shared memory |
214 | | */ |
215 | | static int |
216 | | shm_start( |
217 | | int unit, |
218 | | struct peer *peer |
219 | | ) |
220 | 0 | { |
221 | 0 | struct refclockproc * const pp = peer->procptr; |
222 | 0 | struct shmunit * const up = emalloc_zero(sizeof(*up)); |
223 | |
|
224 | 0 | pp->io.clock_recv = noentry; |
225 | 0 | pp->io.srcclock = peer; |
226 | 0 | pp->io.datalen = 0; |
227 | 0 | pp->io.fd = -1; |
228 | |
|
229 | 0 | up->forall = (unit >= 2) && !(peer->ttl & SHM_MODE_PRIVATE); |
230 | |
|
231 | 0 | up->shm = getShmTime(unit, up->forall); |
232 | | |
233 | | /* |
234 | | * Initialize miscellaneous peer variables |
235 | | */ |
236 | 0 | memcpy((char *)&pp->refid, REFID, 4); |
237 | 0 | if (up->shm != 0) { |
238 | 0 | pp->unitptr = up; |
239 | 0 | up->shm->precision = PRECISION; |
240 | 0 | peer->precision = up->shm->precision; |
241 | 0 | up->shm->valid = 0; |
242 | 0 | up->shm->nsamples = NSAMPLES; |
243 | 0 | pp->clockdesc = DESCRIPTION; |
244 | | /* items to be changed later in 'shm_control()': */ |
245 | 0 | up->max_delay = 5; |
246 | 0 | up->max_delta = 4*3600; |
247 | 0 | return 1; |
248 | 0 | } else { |
249 | 0 | free(up); |
250 | 0 | pp->unitptr = NULL; |
251 | 0 | return 0; |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | |
256 | | /* |
257 | | * shm_control - configure flag1/time2 params |
258 | | * |
259 | | * These are not yet available during 'shm_start', so we have to do any |
260 | | * pre-computations we want to avoid during regular poll/timer callbacks |
261 | | * in this callback. |
262 | | */ |
263 | | static void |
264 | | shm_control( |
265 | | int unit, |
266 | | const struct refclockstat * in_st, |
267 | | struct refclockstat * out_st, |
268 | | struct peer * peer |
269 | | ) |
270 | 0 | { |
271 | 0 | struct refclockproc * const pp = peer->procptr; |
272 | 0 | struct shmunit * const up = pp->unitptr; |
273 | |
|
274 | 0 | UNUSED_ARG(unit); |
275 | 0 | UNUSED_ARG(in_st); |
276 | 0 | UNUSED_ARG(out_st); |
277 | 0 | if (NULL == up) |
278 | 0 | return; |
279 | 0 | if (pp->sloppyclockflag & CLK_FLAG1) |
280 | 0 | up->max_delta = 0; |
281 | 0 | else if (pp->fudgetime2 < 1. || pp->fudgetime2 > 86400.) |
282 | 0 | up->max_delta = 4*3600; |
283 | 0 | else |
284 | 0 | up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5); |
285 | 0 | } |
286 | | |
287 | | |
288 | | /* |
289 | | * shm_shutdown - shut down the clock |
290 | | */ |
291 | | static void |
292 | | shm_shutdown( |
293 | | int unit, |
294 | | struct peer *peer |
295 | | ) |
296 | 0 | { |
297 | 0 | struct refclockproc * const pp = peer->procptr; |
298 | 0 | struct shmunit * const up = pp->unitptr; |
299 | |
|
300 | 0 | UNUSED_ARG(unit); |
301 | 0 | if (NULL == up) |
302 | 0 | return; |
303 | 0 | #ifndef SYS_WINNT |
304 | | |
305 | | /* HMS: shmdt() wants char* or const void * */ |
306 | 0 | (void)shmdt((char *)up->shm); |
307 | |
|
308 | | #else |
309 | | |
310 | | UnmapViewOfFile(up->shm); |
311 | | |
312 | | #endif |
313 | 0 | free(up); |
314 | 0 | } |
315 | | |
316 | | |
317 | | /* |
318 | | * shm_poll - called by the transmit procedure |
319 | | */ |
320 | | static void |
321 | | shm_poll( |
322 | | int unit, |
323 | | struct peer *peer |
324 | | ) |
325 | 0 | { |
326 | 0 | struct refclockproc * const pp = peer->procptr; |
327 | 0 | struct shmunit * const up = pp->unitptr; |
328 | 0 | int major_error; |
329 | |
|
330 | 0 | pp->polls++; |
331 | | |
332 | | /* get dominant reason if we have no samples at all */ |
333 | 0 | major_error = max(up->notready, up->bad); |
334 | 0 | major_error = max(major_error, up->clash); |
335 | | |
336 | | /* |
337 | | * Process median filter samples. If none received, see what |
338 | | * happened, tell the core and keep going. |
339 | | */ |
340 | 0 | if (pp->coderecv != pp->codeproc) { |
341 | | /* have some samples, everything OK */ |
342 | 0 | pp->lastref = pp->lastrec; |
343 | 0 | refclock_report(peer, CEVNT_NOMINAL); |
344 | 0 | refclock_receive(peer); |
345 | 0 | } else if (NULL == up->shm) { /* is this possible at all? */ |
346 | | /* we're out of business without SHM access */ |
347 | 0 | refclock_report(peer, CEVNT_FAULT); |
348 | 0 | } else if (major_error == up->clash) { |
349 | | /* too many collisions is like a bad signal */ |
350 | 0 | refclock_report(peer, CEVNT_PROP); |
351 | 0 | } else if (major_error == up->bad) { |
352 | | /* too much stale/bad/garbled data */ |
353 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
354 | 0 | } else { |
355 | | /* in any other case assume it's just a timeout */ |
356 | 0 | refclock_report(peer, CEVNT_TIMEOUT); |
357 | 0 | } |
358 | | /* shm_clockstats() clears the tallies, so it must be last... */ |
359 | 0 | shm_clockstats(unit, peer); |
360 | 0 | } |
361 | | |
362 | | |
363 | | enum segstat_t { |
364 | | OK, NO_SEGMENT, NOT_READY, BAD_MODE, CLASH |
365 | | }; |
366 | | |
367 | | struct shm_stat_t { |
368 | | int status; |
369 | | int mode; |
370 | | struct timespec tvc, tvr, tvt; |
371 | | int precision; |
372 | | int leap; |
373 | | }; |
374 | | |
375 | | static inline void memory_barrier(void) |
376 | 0 | { |
377 | | #ifdef HAVE_ATOMIC_THREAD_FENCE |
378 | | atomic_thread_fence(memory_order_seq_cst); |
379 | | #endif /* HAVE_ATOMIC_THREAD_FENCE */ |
380 | 0 | } |
381 | | |
382 | | static enum segstat_t shm_query(volatile struct shmTime *shm_in, struct shm_stat_t *shm_stat) |
383 | | /* try to grab a sample from the specified SHM segment */ |
384 | 0 | { |
385 | 0 | struct shmTime shmcopy; |
386 | 0 | volatile struct shmTime *shm = shm_in; |
387 | 0 | volatile int cnt; |
388 | |
|
389 | 0 | unsigned int cns_new, rns_new; |
390 | | |
391 | | /* |
392 | | * This is the main routine. It snatches the time from the shm |
393 | | * board and tacks on a local timestamp. |
394 | | */ |
395 | 0 | if (shm == NULL) { |
396 | 0 | shm_stat->status = NO_SEGMENT; |
397 | 0 | return NO_SEGMENT; |
398 | 0 | } |
399 | | |
400 | | /*@-type@*//* splint is confused about struct timespec */ |
401 | 0 | shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0; |
402 | 0 | { |
403 | 0 | time_t now; |
404 | |
|
405 | 0 | time(&now); |
406 | 0 | shm_stat->tvc.tv_sec = now; |
407 | 0 | } |
408 | | |
409 | | /* relying on word access to be atomic here */ |
410 | 0 | if (shm->valid == 0) { |
411 | 0 | shm_stat->status = NOT_READY; |
412 | 0 | return NOT_READY; |
413 | 0 | } |
414 | | |
415 | 0 | cnt = shm->count; |
416 | | |
417 | | /* |
418 | | * This is proof against concurrency issues if either |
419 | | * (a) the memory_barrier() call works on this host, or |
420 | | * (b) memset compiles to an uninterruptible single-instruction bitblt. |
421 | | */ |
422 | 0 | memory_barrier(); |
423 | 0 | memcpy(&shmcopy, (void*)(uintptr_t)shm, sizeof(struct shmTime)); |
424 | 0 | shm->valid = 0; |
425 | 0 | memory_barrier(); |
426 | | |
427 | | /* |
428 | | * Clash detection in case neither (a) nor (b) was true. |
429 | | * Not supported in mode 0, and word access to the count field |
430 | | * must be atomic for this to work. |
431 | | */ |
432 | 0 | if (shmcopy.mode > 0 && cnt != shm->count) { |
433 | 0 | shm_stat->status = CLASH; |
434 | 0 | return shm_stat->status; |
435 | 0 | } |
436 | | |
437 | 0 | shm_stat->status = OK; |
438 | 0 | shm_stat->mode = shmcopy.mode; |
439 | |
|
440 | 0 | switch (shmcopy.mode) { |
441 | 0 | case 0: |
442 | 0 | shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; |
443 | 0 | shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; |
444 | 0 | rns_new = shmcopy.receiveTimeStampNSec; |
445 | 0 | shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; |
446 | 0 | shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; |
447 | 0 | cns_new = shmcopy.clockTimeStampNSec; |
448 | | |
449 | | /* Since the following comparisons are between unsigned |
450 | | ** variables they are always well defined, and any |
451 | | ** (signed) underflow will turn into very large unsigned |
452 | | ** values, well above the 1000 cutoff. |
453 | | ** |
454 | | ** Note: The usecs *must* be a *truncated* |
455 | | ** representation of the nsecs. This code will fail for |
456 | | ** *rounded* usecs, and the logic to deal with |
457 | | ** wrap-arounds in the presence of rounded values is |
458 | | ** much more convoluted. |
459 | | */ |
460 | 0 | if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) |
461 | 0 | && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { |
462 | 0 | shm_stat->tvt.tv_nsec = cns_new; |
463 | 0 | shm_stat->tvr.tv_nsec = rns_new; |
464 | 0 | } |
465 | | /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level |
466 | | ** timestamps, possibly generated by extending the old |
467 | | ** us-level timestamps |
468 | | */ |
469 | 0 | break; |
470 | | |
471 | 0 | case 1: |
472 | |
|
473 | 0 | shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; |
474 | 0 | shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; |
475 | 0 | rns_new = shmcopy.receiveTimeStampNSec; |
476 | 0 | shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; |
477 | 0 | shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; |
478 | 0 | cns_new = shmcopy.clockTimeStampNSec; |
479 | | |
480 | | /* See the case above for an explanation of the |
481 | | ** following test. |
482 | | */ |
483 | 0 | if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) |
484 | 0 | && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { |
485 | 0 | shm_stat->tvt.tv_nsec = cns_new; |
486 | 0 | shm_stat->tvr.tv_nsec = rns_new; |
487 | 0 | } |
488 | | /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level |
489 | | ** timestamps, possibly generated by extending the old |
490 | | ** us-level timestamps |
491 | | */ |
492 | 0 | break; |
493 | | |
494 | 0 | default: |
495 | 0 | shm_stat->status = BAD_MODE; |
496 | 0 | break; |
497 | 0 | } |
498 | | /*@-type@*/ |
499 | | |
500 | | /* |
501 | | * leap field is not a leap offset but a leap notification code. |
502 | | * The values are magic numbers used by NTP and set by GPSD, if at all, in |
503 | | * the subframe code. |
504 | | */ |
505 | 0 | shm_stat->leap = shmcopy.leap; |
506 | 0 | shm_stat->precision = shmcopy.precision; |
507 | |
|
508 | 0 | return shm_stat->status; |
509 | 0 | } |
510 | | |
511 | | /* |
512 | | * shm_timer - called once every second. |
513 | | * |
514 | | * This tries to grab a sample from the SHM segment, filtering bad ones |
515 | | */ |
516 | | static void |
517 | | shm_timer( |
518 | | int unit, |
519 | | struct peer *peer |
520 | | ) |
521 | 0 | { |
522 | 0 | struct refclockproc * const pp = peer->procptr; |
523 | 0 | struct shmunit * const up = pp->unitptr; |
524 | |
|
525 | 0 | volatile struct shmTime *shm; |
526 | |
|
527 | 0 | l_fp tsrcv; |
528 | 0 | l_fp tsref; |
529 | 0 | int c; |
530 | | |
531 | | /* for formatting 'a_lastcode': */ |
532 | 0 | struct calendar cd; |
533 | 0 | time_t tt; |
534 | 0 | vint64 ts; |
535 | |
|
536 | 0 | enum segstat_t status; |
537 | 0 | struct shm_stat_t shm_stat; |
538 | |
|
539 | 0 | up->ticks++; |
540 | 0 | if ((shm = up->shm) == NULL) { |
541 | | /* try to map again - this may succeed if meanwhile some- |
542 | | body has ipcrm'ed the old (unaccessible) shared mem segment */ |
543 | 0 | shm = up->shm = getShmTime(unit, up->forall); |
544 | 0 | if (shm == NULL) { |
545 | 0 | DPRINTF(1, ("%s: no SHM segment\n", |
546 | 0 | refnumtoa(&peer->srcadr))); |
547 | 0 | return; |
548 | 0 | } |
549 | 0 | } |
550 | | |
551 | | /* query the segment, atomically */ |
552 | 0 | status = shm_query(shm, &shm_stat); |
553 | |
|
554 | 0 | switch (status) { |
555 | 0 | case OK: |
556 | 0 | DPRINTF(2, ("%s: SHM type %d sample\n", |
557 | 0 | refnumtoa(&peer->srcadr), shm_stat.mode)); |
558 | 0 | break; |
559 | 0 | case NO_SEGMENT: |
560 | | /* should never happen, but is harmless */ |
561 | 0 | return; |
562 | 0 | case NOT_READY: |
563 | 0 | DPRINTF(1, ("%s: SHM not ready\n",refnumtoa(&peer->srcadr))); |
564 | 0 | up->notready++; |
565 | 0 | return; |
566 | 0 | case BAD_MODE: |
567 | 0 | DPRINTF(1, ("%s: SHM type blooper, mode=%d\n", |
568 | 0 | refnumtoa(&peer->srcadr), shm->mode)); |
569 | 0 | up->bad++; |
570 | 0 | msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d", |
571 | 0 | shm->mode); |
572 | 0 | return; |
573 | 0 | case CLASH: |
574 | 0 | DPRINTF(1, ("%s: type 1 access clash\n", |
575 | 0 | refnumtoa(&peer->srcadr))); |
576 | 0 | msyslog (LOG_NOTICE, "SHM: access clash in shared memory"); |
577 | 0 | up->clash++; |
578 | 0 | return; |
579 | 0 | default: |
580 | 0 | DPRINTF(1, ("%s: internal error, unknown SHM fetch status\n", |
581 | 0 | refnumtoa(&peer->srcadr))); |
582 | 0 | msyslog (LOG_NOTICE, "internal error, unknown SHM fetch status"); |
583 | 0 | up->bad++; |
584 | 0 | return; |
585 | 0 | } |
586 | | |
587 | | |
588 | | /* format the last time code in human-readable form into |
589 | | * 'pp->a_lastcode'. Someone claimed: "NetBSD has incompatible |
590 | | * tv_sec". I can't find a base for this claim, but we can work |
591 | | * around that potential problem. BTW, simply casting a pointer |
592 | | * is a receipe for disaster on some architectures. |
593 | | */ |
594 | 0 | tt = (time_t)shm_stat.tvt.tv_sec; |
595 | 0 | ts = time_to_vint64(&tt); |
596 | 0 | ntpcal_time_to_date(&cd, &ts); |
597 | | |
598 | | /* add ntpq -c cv timecode in ISO 8601 format */ |
599 | 0 | c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), |
600 | 0 | "%04u-%02u-%02uT%02u:%02u:%02u.%09ldZ", |
601 | 0 | cd.year, cd.month, cd.monthday, |
602 | 0 | cd.hour, cd.minute, cd.second, |
603 | 0 | (long)shm_stat.tvt.tv_nsec); |
604 | 0 | pp->lencode = (c > 0 && (size_t)c < sizeof(pp->a_lastcode)) ? c : 0; |
605 | | |
606 | | /* check 1: age control of local time stamp */ |
607 | 0 | tt = shm_stat.tvc.tv_sec - shm_stat.tvr.tv_sec; |
608 | 0 | if (tt < 0 || tt > up->max_delay) { |
609 | 0 | DPRINTF(1, ("%s:SHM stale/bad receive time, delay=%llds\n", |
610 | 0 | refnumtoa(&peer->srcadr), (long long)tt)); |
611 | 0 | up->bad++; |
612 | 0 | msyslog (LOG_ERR, "SHM: stale/bad receive time, delay=%llds", |
613 | 0 | (long long)tt); |
614 | 0 | return; |
615 | 0 | } |
616 | | |
617 | | /* check 2: delta check */ |
618 | 0 | tt = shm_stat.tvr.tv_sec - shm_stat.tvt.tv_sec - (shm_stat.tvr.tv_nsec < shm_stat.tvt.tv_nsec); |
619 | 0 | if (tt < 0) |
620 | 0 | tt = -tt; |
621 | 0 | if (up->max_delta > 0 && tt > up->max_delta) { |
622 | 0 | DPRINTF(1, ("%s: SHM diff limit exceeded, delta=%llds\n", |
623 | 0 | refnumtoa(&peer->srcadr), (long long)tt)); |
624 | 0 | up->bad++; |
625 | 0 | msyslog (LOG_ERR, "SHM: difference limit exceeded, delta=%llds\n", |
626 | 0 | (long long)tt); |
627 | 0 | return; |
628 | 0 | } |
629 | | |
630 | | /* if we really made it to this point... we're winners! */ |
631 | 0 | DPRINTF(2, ("%s: SHM feeding data\n", |
632 | 0 | refnumtoa(&peer->srcadr))); |
633 | 0 | tsrcv = tspec_stamp_to_lfp(shm_stat.tvr); |
634 | 0 | tsref = tspec_stamp_to_lfp(shm_stat.tvt); |
635 | 0 | pp->leap = shm_stat.leap; |
636 | 0 | peer->precision = shm_stat.precision; |
637 | 0 | refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1); |
638 | 0 | up->good++; |
639 | 0 | } |
640 | | |
641 | | /* |
642 | | * shm_clockstats - dump and reset counters |
643 | | */ |
644 | | static void shm_clockstats( |
645 | | int unit, |
646 | | struct peer *peer |
647 | | ) |
648 | 0 | { |
649 | 0 | struct refclockproc * const pp = peer->procptr; |
650 | 0 | struct shmunit * const up = pp->unitptr; |
651 | |
|
652 | 0 | UNUSED_ARG(unit); |
653 | 0 | if (pp->sloppyclockflag & CLK_FLAG4) { |
654 | 0 | mprintf_clock_stats( |
655 | 0 | &peer->srcadr, "%3d %3d %3d %3d %3d", |
656 | 0 | up->ticks, up->good, up->notready, |
657 | 0 | up->bad, up->clash); |
658 | 0 | } |
659 | 0 | up->ticks = up->good = up->notready = up->bad = up->clash = 0; |
660 | 0 | } |
661 | | |
662 | | #else |
663 | | NONEMPTY_TRANSLATION_UNIT |
664 | | #endif /* REFCLOCK */ |