xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_shm.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
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