xref: /netbsd/external/bsd/ntp/dist/ntpd/refclock_shm.c (revision 6550d01e)
1 /*	$NetBSD: refclock_shm.c,v 1.1.1.1 2009/12/13 16:56:03 kardel 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 #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 "ntp_unixtime.h"
25 #undef fileno
26 #include "ntp_stdlib.h"
27 
28 #undef fileno
29 #include <ctype.h>
30 #undef fileno
31 
32 #ifndef SYS_WINNT
33 # include <sys/ipc.h>
34 # include <sys/shm.h>
35 # include <assert.h>
36 # include <unistd.h>
37 # include <stdio.h>
38 #endif
39 
40 /*
41  * This driver supports a reference clock attached thru shared memory
42  */
43 
44 /* Temp hack to simplify testing of the old mode. */
45 #define OLDWAY 0
46 
47 /*
48  * SHM interface definitions
49  */
50 #define PRECISION       (-1)    /* precision assumed (0.5 s) */
51 #define REFID           "SHM"   /* reference ID */
52 #define DESCRIPTION     "SHM/Shared memory interface"
53 
54 #define NSAMPLES        3       /* stages of median filter */
55 
56 /*
57  * Function prototypes
58  */
59 static  int     shm_start       (int unit, struct peer *peer);
60 static  void    shm_shutdown    (int unit, struct peer *peer);
61 static  void    shm_poll        (int unit, struct peer *peer);
62 static  void    shm_timer       (int unit, struct peer *peer);
63 	int	shm_peek	(int unit, struct peer *peer);
64 	void	shm_clockstats  (int unit, struct peer *peer);
65 
66 /*
67  * Transfer vector
68  */
69 struct  refclock refclock_shm = {
70 	shm_start,              /* start up driver */
71 	shm_shutdown,           /* shut down driver */
72 	shm_poll,		/* transmit poll message */
73 	noentry,		/* not used: control */
74 	noentry,		/* not used: init */
75 	noentry,		/* not used: buginfo */
76 	shm_timer,              /* once per second */
77 };
78 
79 struct shmTime {
80 	int    mode; /* 0 - if valid set
81 		      *       use values,
82 		      *       clear valid
83 		      * 1 - if valid set
84 		      *       if count before and after read of values is equal,
85 		      *         use values
86 		      *       clear valid
87 		      */
88 	int    count;
89 	time_t clockTimeStampSec;
90 	int    clockTimeStampUSec;
91 	time_t receiveTimeStampSec;
92 	int    receiveTimeStampUSec;
93 	int    leap;
94 	int    precision;
95 	int    nsamples;
96 	int    valid;
97 	int    dummy[10];
98 };
99 
100 struct shmunit {
101 	struct shmTime *shm;	/* pointer to shared memory segment */
102 
103 	/* debugging/monitoring counters - reset when printed */
104 	int ticks;		/* number of attempts to read data*/
105 	int good;		/* number of valid samples */
106 	int notready;		/* number of peeks without data ready */
107 	int bad;		/* number of invalid samples */
108 	int clash;		/* number of access clashes while reading */
109 };
110 
111 
112 struct shmTime *getShmTime(int);
113 
114 struct shmTime *getShmTime (int unit) {
115 #ifndef SYS_WINNT
116 	int shmid=0;
117 
118 	/* 0x4e545030 is NTP0.
119 	 * Big units will give non-ascii but that's OK
120 	 * as long as everybody does it the same way.
121 	 */
122 	shmid=shmget (0x4e545030+unit, sizeof (struct shmTime),
123 		      IPC_CREAT|(unit<2?0600:0666));
124 	if (shmid==-1) { /*error */
125 		msyslog(LOG_ERR,"SHM shmget (unit %d): %s",unit,strerror(errno));
126 		return 0;
127 	}
128 	else { /* no error  */
129 		struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0);
130 		if ((int)(long)p==-1) { /* error */
131 			msyslog(LOG_ERR,"SHM shmat (unit %d): %s",unit,strerror(errno));
132 			return 0;
133 		}
134 		return p;
135 	}
136 #else
137 	char buf[10];
138 	LPSECURITY_ATTRIBUTES psec=0;
139 	HANDLE shmid=0;
140 	SECURITY_DESCRIPTOR sd;
141 	SECURITY_ATTRIBUTES sa;
142 	sprintf (buf,"NTP%d",unit);
143 	if (unit>=2) { /* world access */
144 		if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
145 			msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m",unit);
146 			return 0;
147 		}
148 		if (!SetSecurityDescriptorDacl(&sd,1,0,0)) {
149 			msyslog(LOG_ERR,"SHM SetSecurityDescriptorDacl (unit %d): %m",unit);
150 			return 0;
151 		}
152 		sa.nLength=sizeof (SECURITY_ATTRIBUTES);
153 		sa.lpSecurityDescriptor=&sd;
154 		sa.bInheritHandle=0;
155 		psec=&sa;
156 	}
157 	shmid=CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
158 				 0, sizeof (struct shmTime),buf);
159 	if (!shmid) { /*error*/
160 		char buf[1000];
161 		FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
162 			       0, GetLastError (), 0, buf, sizeof (buf), 0);
163 		msyslog(LOG_ERR,"SHM CreateFileMapping (unit %d): %s",unit,buf);
164 		return 0;
165 	}
166 	else {
167 		struct shmTime *p=(struct shmTime *) MapViewOfFile (shmid,
168 								    FILE_MAP_WRITE, 0, 0, sizeof (struct shmTime));
169 		if (p==0) { /*error*/
170 			char buf[1000];
171 			FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
172 				       0, GetLastError (), 0, buf, sizeof (buf), 0);
173 			msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s",unit,buf);
174 			return 0;
175 		}
176 		return p;
177 	}
178 #endif
179 }
180 /*
181  * shm_start - attach to shared memory
182  */
183 static int
184 shm_start(
185 	int unit,
186 	struct peer *peer
187 	)
188 {
189 	struct refclockproc *pp;
190 	struct shmunit *up;
191 
192 	pp = peer->procptr;
193 	pp->io.clock_recv = noentry;
194 	pp->io.srcclock = (caddr_t)peer;
195 	pp->io.datalen = 0;
196 	pp->io.fd = -1;
197 
198 	up = (struct shmunit *) emalloc(sizeof(*up));
199 	if (up == NULL)
200 		return (FALSE);
201 	memset((char *)up, 0, sizeof(*up));
202 	pp->unitptr = (caddr_t)up;
203 
204 	up->shm = getShmTime(unit);
205 
206 	/*
207 	 * Initialize miscellaneous peer variables
208 	 */
209 	memcpy((char *)&pp->refid, REFID, 4);
210 	if (up->shm != 0) {
211 		up->shm->precision = PRECISION;
212 		peer->precision = up->shm->precision;
213 		up->shm->valid=0;
214 		up->shm->nsamples=NSAMPLES;
215 		pp->clockdesc = DESCRIPTION;
216 		return (1);
217 	}
218 	else {
219 		return 0;
220 	}
221 }
222 
223 
224 /*
225  * shm_shutdown - shut down the clock
226  */
227 static void
228 shm_shutdown(
229 	int unit,
230 	struct peer *peer
231 	)
232 {
233 	struct refclockproc *pp;
234 	struct shmunit *up;
235 
236 	pp = peer->procptr;
237 	up = (struct shmunit *)pp->unitptr;
238 #ifndef SYS_WINNT
239 	/* HMS: shmdt()wants char* or const void * */
240 	(void) shmdt ((char *)up->shm);
241 #else
242 	UnmapViewOfFile (up->shm);
243 #endif
244 }
245 
246 
247 /*
248  * shm_timer - called every second
249  */
250 static  void
251 shm_timer(int unit, struct peer *peer)
252 {
253 	if (OLDWAY)
254 		return;
255 
256 	shm_peek(unit, peer);
257 }
258 
259 
260 /*
261  * shm_poll - called by the transmit procedure
262  */
263 static void
264 shm_poll(
265 	int unit,
266 	struct peer *peer
267 	)
268 {
269 	struct refclockproc *pp;
270 	int ok;
271 
272 	pp = peer->procptr;
273 
274 	if (OLDWAY) {
275 		ok = shm_peek(unit, peer);
276 		if (!ok) return;
277 	}
278 
279         /*
280          * Process median filter samples. If none received, declare a
281          * timeout and keep going.
282          */
283         if (pp->coderecv == pp->codeproc) {
284                 refclock_report(peer, CEVNT_TIMEOUT);
285 		shm_clockstats(unit, peer);
286                 return;
287         }
288 	pp->lastref = pp->lastrec;
289 	refclock_receive(peer);
290 	shm_clockstats(unit, peer);
291 }
292 
293 /*
294  * shm_peek - try to grab a sample
295  */
296 int shm_peek(
297 	int unit,
298 	struct peer *peer
299 	)
300 {
301 	struct refclockproc *pp;
302 	struct shmunit *up;
303 	struct shmTime *shm;
304 
305 	/*
306 	 * This is the main routine. It snatches the time from the shm
307 	 * board and tacks on a local timestamp.
308 	 */
309 	pp = peer->procptr;
310 	up = (struct shmunit*)pp->unitptr;
311 	up->ticks++;
312 	if (up->shm == 0) {
313 		/* try to map again - this may succeed if meanwhile some-
314 		body has ipcrm'ed the old (unaccessible) shared mem segment */
315 		up->shm = getShmTime(unit);
316 	}
317 	shm = up->shm;
318 	if (shm == 0) {
319 		refclock_report(peer, CEVNT_FAULT);
320 		return(0);
321 	}
322 	if (shm->valid) {
323 		struct timeval tvr;
324 		struct timeval tvt;
325 		struct tm *t;
326 		int ok=1;
327 		tvr.tv_sec = 0;
328 		tvr.tv_usec = 0;
329 		tvt.tv_sec = 0;
330 		tvt.tv_usec = 0;
331 		switch (shm->mode) {
332 		    case 0: {
333 			    tvr.tv_sec=shm->receiveTimeStampSec;
334 			    tvr.tv_usec=shm->receiveTimeStampUSec;
335 			    tvt.tv_sec=shm->clockTimeStampSec;
336 			    tvt.tv_usec=shm->clockTimeStampUSec;
337 		    }
338 		    break;
339 		    case 1: {
340 			    int cnt=shm->count;
341 			    tvr.tv_sec=shm->receiveTimeStampSec;
342 			    tvr.tv_usec=shm->receiveTimeStampUSec;
343 			    tvt.tv_sec=shm->clockTimeStampSec;
344 			    tvt.tv_usec=shm->clockTimeStampUSec;
345 			    ok=(cnt==shm->count);
346 		    }
347 		    break;
348 		    default:
349 			msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",shm->mode);
350 		}
351 		shm->valid=0;
352 		if (ok) {
353 			time_t help;	/* XXX NetBSD has incompatible tv_sec */
354 
355 			TVTOTS(&tvr,&pp->lastrec);
356 			pp->lastrec.l_ui += JAN_1970;
357 			/* pp->lasttime = current_time; */
358 			pp->polls++;
359 			help = tvt.tv_sec;
360 			t = gmtime (&help);
361 			pp->day=t->tm_yday+1;
362 			pp->hour=t->tm_hour;
363 			pp->minute=t->tm_min;
364 			pp->second=t->tm_sec;
365 			pp->nsec=tvt.tv_usec * 1000;
366 			peer->precision=shm->precision;
367 			pp->leap=shm->leap;
368 		}
369 		else {
370 			refclock_report(peer, CEVNT_FAULT);
371 			msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
372 			up->clash++;
373 			return(0);
374 		}
375 	}
376 	else {
377 		refclock_report(peer, CEVNT_TIMEOUT);
378 		up->notready++;
379 		return(0);
380 	}
381 	if (!refclock_process(pp)) {
382 		refclock_report(peer, CEVNT_BADTIME);
383 		up->bad++;
384 		return(0);
385 	}
386 	up->good++;
387 	return(1);
388 }
389 
390 /*
391  * shm_clockstats - dump and reset counters
392  */
393 void shm_clockstats(
394 	int unit,
395 	struct peer *peer
396 	)
397 {
398 	struct refclockproc *pp;
399 	struct shmunit *up;
400 	char logbuf[256];
401 
402 	pp = peer->procptr;
403 	up = (struct shmunit*)pp->unitptr;
404 
405 	if (!(pp->sloppyclockflag & CLK_FLAG4)) return;
406 
407         snprintf(logbuf, sizeof(logbuf), "%3d %3d %3d %3d %3d",
408                 up->ticks, up->good, up->notready, up->bad, up->clash);
409         record_clock_stats(&peer->srcadr, logbuf);
410 
411 	up->ticks = up->good = up->notready =up->bad = up->clash = 0;
412 
413 }
414 
415 #else
416 int refclock_shm_bs;
417 #endif /* REFCLOCK */
418