refclock_shm.c revision 1.2 1 /* $NetBSD: refclock_shm.c,v 1.2 2013/12/28 03:20:14 christos Exp $ */
2
3 /*
4 * refclock_shm - clock driver for utc via shared memory
5 * - under construction -
6 * To add new modes: Extend or union the shmTime-struct. Do not
7 * extend/shrink size, because otherwise existing implementations
8 * will specify wrong size of shared memory-segment
9 * PB 18.3.97
10 */
11
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
15
16 #include "ntp_types.h"
17
18 #if defined(REFCLOCK) && defined(CLOCK_SHM)
19
20 #include "ntpd.h"
21 #undef fileno
22 #include "ntp_io.h"
23 #undef fileno
24 #include "ntp_refclock.h"
25 #undef fileno
26 #include "timespecops.h"
27 #undef fileno
28 #include "ntp_stdlib.h"
29
30 #undef fileno
31 #include <ctype.h>
32 #undef fileno
33
34 #ifndef SYS_WINNT
35 # include <sys/ipc.h>
36 # include <sys/shm.h>
37 # include <assert.h>
38 # include <unistd.h>
39 # include <stdio.h>
40 #endif
41
42 /*
43 * This driver supports a reference clock attached thru shared memory
44 */
45
46 /* Temp hack to simplify testing of the old mode. */
47 #define OLDWAY 0
48
49 /*
50 * SHM interface definitions
51 */
52 #define PRECISION (-1) /* precision assumed (0.5 s) */
53 #define REFID "SHM" /* reference ID */
54 #define DESCRIPTION "SHM/Shared memory interface"
55
56 #define NSAMPLES 3 /* stages of median filter */
57
58 /*
59 * Function prototypes
60 */
61 static int shm_start (int unit, struct peer *peer);
62 static void shm_shutdown (int unit, struct peer *peer);
63 static void shm_poll (int unit, struct peer *peer);
64 static void shm_timer (int unit, struct peer *peer);
65 int shm_peek (int unit, struct peer *peer);
66 void shm_clockstats (int unit, struct peer *peer);
67
68 /*
69 * Transfer vector
70 */
71 struct refclock refclock_shm = {
72 shm_start, /* start up driver */
73 shm_shutdown, /* shut down driver */
74 shm_poll, /* transmit poll message */
75 noentry, /* not used: control */
76 noentry, /* not used: init */
77 noentry, /* not used: buginfo */
78 shm_timer, /* once per second */
79 };
80
81 struct shmTime {
82 int mode; /* 0 - if valid is set:
83 * use values,
84 * clear valid
85 * 1 - if valid is set:
86 * if count before and after read of values is equal,
87 * use values
88 * clear valid
89 */
90 volatile int count;
91 time_t clockTimeStampSec;
92 int clockTimeStampUSec;
93 time_t receiveTimeStampSec;
94 int receiveTimeStampUSec;
95 int leap;
96 int precision;
97 int nsamples;
98 volatile int valid;
99 unsigned clockTimeStampNSec; /* Unsigned ns timestamps */
100 unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */
101 int dummy[8];
102 };
103
104 struct shmunit {
105 struct shmTime *shm; /* pointer to shared memory segment */
106
107 /* debugging/monitoring counters - reset when printed */
108 int ticks; /* number of attempts to read data*/
109 int good; /* number of valid samples */
110 int notready; /* number of peeks without data ready */
111 int bad; /* number of invalid samples */
112 int clash; /* number of access clashes while reading */
113 };
114
115
116 struct shmTime *getShmTime(int);
117
118 struct shmTime *getShmTime (int unit) {
119 #ifndef SYS_WINNT
120 int shmid=0;
121
122 /* 0x4e545030 is NTP0.
123 * Big units will give non-ascii but that's OK
124 * as long as everybody does it the same way.
125 */
126 shmid=shmget (0x4e545030 + unit, sizeof (struct shmTime),
127 IPC_CREAT | ((unit < 2) ? 0600 : 0666));
128 if (shmid == -1) { /* error */
129 msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit);
130 return 0;
131 }
132 else { /* no error */
133 struct shmTime *p = (struct shmTime *)shmat (shmid, 0, 0);
134 if (p == (struct shmTime *)-1) { /* error */
135 msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit);
136 return 0;
137 }
138 return p;
139 }
140 #else
141 char buf[10];
142 LPSECURITY_ATTRIBUTES psec = 0;
143 HANDLE shmid = 0;
144 SECURITY_DESCRIPTOR sd;
145 SECURITY_ATTRIBUTES sa;
146
147 snprintf(buf, sizeof(buf), "NTP%d", unit);
148 if (unit >= 2) { /* world access */
149 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
150 msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit);
151 return 0;
152 }
153 if (!SetSecurityDescriptorDacl(&sd, 1, 0, 0)) {
154 msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit);
155 return 0;
156 }
157 sa.nLength=sizeof (SECURITY_ATTRIBUTES);
158 sa.lpSecurityDescriptor = &sd;
159 sa.bInheritHandle = 0;
160 psec = &sa;
161 }
162 shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
163 0, sizeof (struct shmTime), buf);
164 if (!shmid) { /*error*/
165 char buf[1000];
166
167 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
168 0, GetLastError (), 0, buf, sizeof (buf), 0);
169 msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf);
170 return 0;
171 } else {
172 struct shmTime *p = (struct shmTime *) MapViewOfFile (shmid,
173 FILE_MAP_WRITE, 0, 0, sizeof (struct shmTime));
174 if (p == 0) { /*error*/
175 char buf[1000];
176
177 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
178 0, GetLastError (), 0, buf, sizeof (buf), 0);
179 msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf)
180 return 0;
181 }
182 return p;
183 }
184 #endif
185 }
186 /*
187 * shm_start - attach to shared memory
188 */
189 static int
190 shm_start(
191 int unit,
192 struct peer *peer
193 )
194 {
195 struct refclockproc *pp;
196 struct shmunit *up;
197
198 pp = peer->procptr;
199 pp->io.clock_recv = noentry;
200 pp->io.srcclock = peer;
201 pp->io.datalen = 0;
202 pp->io.fd = -1;
203
204 up = emalloc_zero(sizeof(*up));
205
206 up->shm = getShmTime(unit);
207
208 /*
209 * Initialize miscellaneous peer variables
210 */
211 memcpy((char *)&pp->refid, REFID, 4);
212 if (up->shm != 0) {
213 pp->unitptr = up;
214 up->shm->precision = PRECISION;
215 peer->precision = up->shm->precision;
216 up->shm->valid = 0;
217 up->shm->nsamples = NSAMPLES;
218 pp->clockdesc = DESCRIPTION;
219 return 1;
220 } else {
221 free(up);
222 return 0;
223 }
224 }
225
226
227 /*
228 * shm_shutdown - shut down the clock
229 */
230 static void
231 shm_shutdown(
232 int unit,
233 struct peer *peer
234 )
235 {
236 struct refclockproc *pp;
237 struct shmunit *up;
238
239 pp = peer->procptr;
240 up = pp->unitptr;
241
242 if (NULL == up)
243 return;
244 #ifndef SYS_WINNT
245 /* HMS: shmdt() wants char* or const void * */
246 (void) shmdt ((char *)up->shm);
247 #else
248 UnmapViewOfFile (up->shm);
249 #endif
250 free(up);
251 }
252
253
254 /*
255 * shm_timer - called every second
256 */
257 static void
258 shm_timer(int unit, struct peer *peer)
259 {
260 if (OLDWAY)
261 return;
262
263 shm_peek(unit, peer);
264 }
265
266
267 /*
268 * shm_poll - called by the transmit procedure
269 */
270 static void
271 shm_poll(
272 int unit,
273 struct peer *peer
274 )
275 {
276 struct refclockproc *pp;
277 int ok;
278
279 pp = peer->procptr;
280
281 if (OLDWAY) {
282 ok = shm_peek(unit, peer);
283 if (!ok)
284 return;
285 }
286
287 /*
288 * Process median filter samples. If none received, declare a
289 * timeout and keep going.
290 */
291 if (pp->coderecv == pp->codeproc) {
292 refclock_report(peer, CEVNT_TIMEOUT);
293 shm_clockstats(unit, peer);
294 return;
295 }
296 pp->lastref = pp->lastrec;
297 refclock_receive(peer);
298 shm_clockstats(unit, peer);
299 }
300
301 /*
302 * shm_peek - try to grab a sample
303 */
304 int shm_peek(
305 int unit,
306 struct peer *peer
307 )
308 {
309 struct refclockproc *pp;
310 struct shmunit *up;
311 struct shmTime *shm;
312
313 /*
314 * This is the main routine. It snatches the time from the shm
315 * board and tacks on a local timestamp.
316 */
317 pp = peer->procptr;
318 up = pp->unitptr;
319 up->ticks++;
320 if (up->shm == 0) {
321 /* try to map again - this may succeed if meanwhile some-
322 body has ipcrm'ed the old (unaccessible) shared mem segment */
323 up->shm = getShmTime(unit);
324 }
325 shm = up->shm;
326 if (shm == 0) {
327 refclock_report(peer, CEVNT_FAULT);
328 return 0;
329 }
330 if (shm->valid) {
331 struct timespec tvr;
332 struct timespec tvt;
333 struct tm *t;
334 char timestr[20]; /* "%Y-%m-%dT%H:%M:%S" + 1 */
335 int c;
336 int ok = 1;
337 unsigned cns_new, rns_new;
338 int cnt;
339
340 tvr.tv_sec = 0;
341 tvr.tv_nsec = 0;
342 tvt.tv_sec = 0;
343 tvt.tv_nsec = 0;
344 switch (shm->mode) {
345 case 0:
346 tvr.tv_sec = shm->receiveTimeStampSec;
347 tvr.tv_nsec = shm->receiveTimeStampUSec * 1000;
348 rns_new = shm->receiveTimeStampNSec;
349 tvt.tv_sec = shm->clockTimeStampSec;
350 tvt.tv_nsec = shm->clockTimeStampUSec * 1000;
351 cns_new = shm->clockTimeStampNSec;
352
353 /* Since these comparisons are between unsigned
354 ** variables they are always well defined, and any
355 ** (signed) underflow will turn into very large
356 ** unsigned values, well above the 1000 cutoff
357 */
358 if ( ((cns_new - (unsigned)tvt.tv_nsec) < 1000)
359 && ((rns_new - (unsigned)tvr.tv_nsec) < 1000)) {
360 tvt.tv_nsec = cns_new;
361 tvr.tv_nsec = rns_new;
362 }
363 // At this point tvr and tvt contains valid ns-level
364 // timestamps, possibly generated by extending the
365 // old us-level timestamps
366
367 break;
368
369 case 1:
370 cnt = shm->count;
371
372 tvr.tv_sec = shm->receiveTimeStampSec;
373 tvr.tv_nsec = shm->receiveTimeStampUSec * 1000;
374 rns_new = shm->receiveTimeStampNSec;
375 tvt.tv_sec = shm->clockTimeStampSec;
376 tvt.tv_nsec = shm->clockTimeStampUSec * 1000;
377 cns_new = shm->clockTimeStampNSec;
378 ok = (cnt == shm->count);
379
380 /* Since these comparisons are between unsigned
381 ** variables they are always well defined, and any
382 ** (signed) underflow will turn into very large
383 ** unsigned values, well above the 1000 cutoff
384 */
385 if ( ((cns_new - (unsigned)tvt.tv_nsec) < 1000)
386 && ((rns_new - (unsigned)tvr.tv_nsec) < 1000)) {
387 tvt.tv_nsec = cns_new;
388 tvr.tv_nsec = rns_new;
389 }
390 // At this point tvr and tvt contains valid ns-level
391 // timestamps, possibly generated by extending the
392 // old us-level timestamps
393
394 break;
395
396 default:
397 msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",shm->mode);
398 return 0;
399 }
400
401 /* XXX NetBSD has incompatible tv_sec */
402 t = gmtime((const time_t *)&tvt.tv_sec);
403
404 /* add ntpq -c cv timecode in ISO 8601 format */
405 strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", t);
406 c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
407 "%s.%09ldZ", timestr, (long)tvt.tv_nsec);
408 pp->lencode = ((size_t)c < sizeof(pp->a_lastcode))
409 ? c
410 : 0;
411
412 shm->valid = 0;
413 if (ok) {
414 pp->lastrec = tspec_stamp_to_lfp(tvr);
415 pp->polls++;
416 pp->day = t->tm_yday+1;
417 pp->hour = t->tm_hour;
418 pp->minute = t->tm_min;
419 pp->second = t->tm_sec;
420 pp->nsec = tvt.tv_nsec;
421 peer->precision = shm->precision;
422 pp->leap = shm->leap;
423 } else {
424 refclock_report(peer, CEVNT_FAULT);
425 msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
426 up->clash++;
427 return 0;
428 }
429 } else {
430 refclock_report(peer, CEVNT_TIMEOUT);
431 up->notready++;
432 return 0;
433 }
434 if (!refclock_process(pp)) {
435 refclock_report(peer, CEVNT_BADTIME);
436 up->bad++;
437 return 0;
438 }
439 up->good++;
440 return 1;
441 }
442
443 /*
444 * shm_clockstats - dump and reset counters
445 */
446 void shm_clockstats(
447 int unit,
448 struct peer *peer
449 )
450 {
451 struct refclockproc *pp;
452 struct shmunit *up;
453 char logbuf[256];
454
455 pp = peer->procptr;
456 up = pp->unitptr;
457
458 if (!(pp->sloppyclockflag & CLK_FLAG4))
459 return;
460
461 snprintf(logbuf, sizeof(logbuf), "%3d %3d %3d %3d %3d",
462 up->ticks, up->good, up->notready, up->bad, up->clash);
463 record_clock_stats(&peer->srcadr, logbuf);
464
465 up->ticks = up->good = up->notready = up->bad = up->clash = 0;
466
467 }
468
469 #else
470 NONEMPTY_TRANSLATION_UNIT
471 #endif /* REFCLOCK */
472