]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_shm.c
Restore packaging subdir to enable running unmodified configure script.
[FreeBSD/FreeBSD.git] / contrib / ntp / ntpd / refclock_shm.c
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
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 /*
45  * SHM interface definitions
46  */
47 #define PRECISION       (-1)    /* precision assumed (0.5 s) */
48 #define REFID           "SHM"   /* reference ID */
49 #define DESCRIPTION     "SHM/Shared memory interface"
50
51 #define NSAMPLES        3       /* stages of median filter */
52
53 /*
54  * Mode flags
55  */
56 #define SHM_MODE_PRIVATE 0x0001
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 static  void    shm_clockstats  (int unit, struct peer *peer);
66 static  void    shm_control     (int unit, const struct refclockstat * in_st,
67                                  struct refclockstat * out_st, struct peer *peer);
68
69 /*
70  * Transfer vector
71  */
72 struct  refclock refclock_shm = {
73         shm_start,              /* start up driver */
74         shm_shutdown,           /* shut down driver */
75         shm_poll,               /* transmit poll message */
76         shm_control,            /* control settings */
77         noentry,                /* not used: init */
78         noentry,                /* not used: buginfo */
79         shm_timer,              /* once per second */
80 };
81
82 struct shmTime {
83         int    mode; /* 0 - if valid is set:
84                       *       use values,
85                       *       clear valid
86                       * 1 - if valid is set:
87                       *       if count before and after read of values is equal,
88                       *         use values
89                       *       clear valid
90                       */
91         volatile int    count;
92         time_t          clockTimeStampSec;
93         int             clockTimeStampUSec;
94         time_t          receiveTimeStampSec;
95         int             receiveTimeStampUSec;
96         int             leap;
97         int             precision;
98         int             nsamples;
99         volatile int    valid;
100         unsigned        clockTimeStampNSec;     /* Unsigned ns timestamps */
101         unsigned        receiveTimeStampNSec;   /* Unsigned ns timestamps */
102         int             dummy[8];
103 };
104
105 struct shmunit {
106         struct shmTime *shm;    /* pointer to shared memory segment */
107         int forall;             /* access for all UIDs? */
108
109         /* debugging/monitoring counters - reset when printed */
110         int ticks;              /* number of attempts to read data*/
111         int good;               /* number of valid samples */
112         int notready;           /* number of peeks without data ready */
113         int bad;                /* number of invalid samples */
114         int clash;              /* number of access clashes while reading */
115
116         time_t max_delta;       /* difference limit */
117         time_t max_delay;       /* age/stale limit */
118 };
119
120 static struct shmTime*
121 getShmTime(
122         int unit,
123         int/*BOOL*/ forall
124         )
125 {
126         struct shmTime *p = NULL;
127
128 #ifndef SYS_WINNT
129
130         int shmid;
131
132         /* 0x4e545030 is NTP0.
133          * Big units will give non-ascii but that's OK
134          * as long as everybody does it the same way.
135          */
136         shmid=shmget(0x4e545030 + unit, sizeof (struct shmTime),
137                       IPC_CREAT | (forall ? 0666 : 0600));
138         if (shmid == -1) { /* error */
139                 msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit);
140                 return NULL;
141         }
142         p = (struct shmTime *)shmat (shmid, 0, 0);
143         if (p == (struct shmTime *)-1) { /* error */
144                 msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit);
145                 return NULL;
146         }
147         return p;
148
149 #else
150
151         static const char * nspref[2] = { "Local", "Global" };
152         char buf[20];
153         LPSECURITY_ATTRIBUTES psec = 0;
154         HANDLE shmid = 0;
155         SECURITY_DESCRIPTOR sd;
156         SECURITY_ATTRIBUTES sa;
157         unsigned int numch;
158
159         numch = snprintf(buf, sizeof(buf), "%s\\NTP%d",
160                          nspref[forall != 0], (unit & 0xFF));
161         if (numch >= sizeof(buf)) {
162                 msyslog(LOG_ERR, "SHM name too long (unit %d)", unit);
163                 return NULL;
164         }
165         if (forall) { /* world access */
166                 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
167                         msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit);
168                         return NULL;
169                 }
170                 if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) {
171                         msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit);
172                         return NULL;
173                 }
174                 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
175                 sa.lpSecurityDescriptor = &sd;
176                 sa.bInheritHandle = FALSE;
177                 psec = &sa;
178         }
179         shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
180                                    0, sizeof (struct shmTime), buf);
181         if (shmid == NULL) { /*error*/
182                 char buf[1000];         
183                 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
184                                0, GetLastError (), 0, buf, sizeof (buf), 0);
185                 msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf);
186                 return NULL;
187         }
188         p = (struct shmTime *)MapViewOfFile(shmid, FILE_MAP_WRITE, 0, 0,
189                                             sizeof (struct shmTime));
190         if (p == NULL) { /*error*/
191                 char buf[1000];         
192                 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
193                                0, GetLastError (), 0, buf, sizeof (buf), 0);
194                 msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf);
195                 return NULL;
196         }
197
198 #endif
199
200         return p;
201 }
202 /*
203  * shm_start - attach to shared memory
204  */
205 static int
206 shm_start(
207         int unit,
208         struct peer *peer
209         )
210 {
211         struct refclockproc * const pp = peer->procptr;
212         struct shmunit *      const up = emalloc_zero(sizeof(*up));
213
214         pp->io.clock_recv = noentry;
215         pp->io.srcclock = peer;
216         pp->io.datalen = 0;
217         pp->io.fd = -1;
218
219         up->forall = (unit >= 2) && !(peer->ttl & SHM_MODE_PRIVATE);
220
221         up->shm = getShmTime(unit, up->forall);
222
223         /*
224          * Initialize miscellaneous peer variables
225          */
226         memcpy((char *)&pp->refid, REFID, 4);
227         if (up->shm != 0) {
228                 pp->unitptr = up;
229                 up->shm->precision = PRECISION;
230                 peer->precision = up->shm->precision;
231                 up->shm->valid = 0;
232                 up->shm->nsamples = NSAMPLES;
233                 pp->clockdesc = DESCRIPTION;
234                 /* items to be changed later in 'shm_control()': */
235                 up->max_delay = 5;
236                 up->max_delta = 4*3600;
237                 return 1;
238         } else {
239                 free(up);
240                 pp->unitptr = NULL;
241                 return 0;
242         }
243 }
244
245
246 /*
247  * shm_control - configure flag1/time2 params
248  *
249  * These are not yet available during 'shm_start', so we have to do any
250  * pre-computations we want to avoid during regular poll/timer callbacks
251  * in this callback.
252  */
253 static void
254 shm_control(
255         int                         unit,
256         const struct refclockstat * in_st,
257         struct refclockstat       * out_st,
258         struct peer               * peer
259         )
260 {
261         struct refclockproc * const pp = peer->procptr;
262         struct shmunit *      const up = pp->unitptr;
263
264         UNUSED_ARG(unit);
265         UNUSED_ARG(in_st);
266         UNUSED_ARG(out_st);
267         if (NULL == up)
268                 return;
269         if (pp->sloppyclockflag & CLK_FLAG1)
270                 up->max_delta = 0;
271         else if (pp->fudgetime2 < 1. || pp->fudgetime2 > 86400.)
272                 up->max_delta = 4*3600;
273         else
274                 up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5);
275 }
276
277
278 /*
279  * shm_shutdown - shut down the clock
280  */
281 static void
282 shm_shutdown(
283         int unit,
284         struct peer *peer
285         )
286 {
287         struct refclockproc * const pp = peer->procptr;
288         struct shmunit *      const up = pp->unitptr;
289
290         UNUSED_ARG(unit);
291         if (NULL == up)
292                 return;
293 #ifndef SYS_WINNT
294
295         /* HMS: shmdt() wants char* or const void * */
296         (void)shmdt((char *)up->shm);
297
298 #else
299
300         UnmapViewOfFile(up->shm);
301
302 #endif
303         free(up);
304 }
305
306
307 /*
308  * shm_poll - called by the transmit procedure
309  */
310 static void
311 shm_poll(
312         int unit,
313         struct peer *peer
314         )
315 {
316         struct refclockproc * const pp = peer->procptr;
317         struct shmunit *      const up = pp->unitptr;
318         int major_error;
319
320         pp->polls++;
321
322         /* get dominant reason if we have no samples at all */
323         major_error = max(up->notready, up->bad);
324         major_error = max(major_error, up->clash);
325
326         /*
327          * Process median filter samples. If none received, see what
328          * happened, tell the core and keep going.
329          */
330         if (pp->coderecv != pp->codeproc) {
331                 /* have some samples, everything OK */
332                 pp->lastref = pp->lastrec;
333                 refclock_receive(peer);
334         } else if (NULL == up->shm) { /* is this possible at all? */
335                 /* we're out of business without SHM access */
336                 refclock_report(peer, CEVNT_FAULT);
337         } else if (major_error == up->clash) {
338                 /* too many collisions is like a bad signal */
339                 refclock_report(peer, CEVNT_PROP);
340         } else if (major_error == up->bad) {
341                 /* too much stale/bad/garbled data */
342                 refclock_report(peer, CEVNT_BADREPLY);
343         } else {
344                 /* in any other case assume it's just a timeout */
345                 refclock_report(peer, CEVNT_TIMEOUT);
346         }
347         /* shm_clockstats() clears the tallies, so it must be last... */
348         shm_clockstats(unit, peer);
349 }
350
351 /*
352  * shm_timer - called onece every second.
353  *
354  * This tries to grab a sample from the SHM segment
355  */
356 static void
357 shm_timer(
358         int unit,
359         struct peer *peer
360         )
361 {
362         struct refclockproc * const pp = peer->procptr;
363         struct shmunit *      const up = pp->unitptr;
364
365         /* access order is important for lock-free SHM access; we
366         ** enforce order by treating the whole structure volatile.
367         **
368         ** IMPORTANT NOTE: This does not protect from reordering on CPU
369         ** level, and it does nothing for cache consistency and
370         ** visibility of changes by other cores. We need atomic and/or
371         ** fence instructions for that.
372         */
373         volatile struct shmTime *shm;
374
375         struct timespec tvr;
376         struct timespec tvt;
377         l_fp tsrcv;
378         l_fp tsref;
379         unsigned int c;
380         unsigned int cns_new, rns_new;
381         int cnt;
382
383         /* for formatting 'a_lastcode': */
384         struct calendar cd;
385         time_t tt, now;
386         vint64 ts;
387
388         /*
389          * This is the main routine. It snatches the time from the shm
390          * board and tacks on a local timestamp.
391          */
392         up->ticks++;
393         if ((shm = up->shm) == NULL) {
394                 /* try to map again - this may succeed if meanwhile some-
395                 body has ipcrm'ed the old (unaccessible) shared mem segment */
396                 shm = up->shm = getShmTime(unit, up->forall);
397                 if (shm == NULL) {
398                         DPRINTF(1, ("%s: no SHM segment\n",
399                                     refnumtoa(&peer->srcadr)));
400                         return;
401                 }
402         }
403         if ( ! shm->valid) {
404                 DPRINTF(1, ("%s: SHM not ready\n",
405                             refnumtoa(&peer->srcadr)));
406                 up->notready++;
407                 return;
408         }
409
410         switch (shm->mode) {
411         case 0:
412                 tvr.tv_sec      = shm->receiveTimeStampSec;
413                 tvr.tv_nsec     = shm->receiveTimeStampUSec * 1000;
414                 rns_new         = shm->receiveTimeStampNSec;
415                 tvt.tv_sec      = shm->clockTimeStampSec;
416                 tvt.tv_nsec     = shm->clockTimeStampUSec * 1000;
417                 cns_new         = shm->clockTimeStampNSec;
418
419                 /* Since the following comparisons are between unsigned
420                 ** variables they are always well defined, and any
421                 ** (signed) underflow will turn into very large unsigned
422                 ** values, well above the 1000 cutoff.
423                 **
424                 ** Note: The usecs *must* be a *truncated*
425                 ** representation of the nsecs. This code will fail for
426                 ** *rounded* usecs, and the logic to deal with
427                 ** wrap-arounds in the presence of rounded values is
428                 ** much more convoluted.
429                 */
430                 if (   ((cns_new - (unsigned)tvt.tv_nsec) < 1000)
431                     && ((rns_new - (unsigned)tvr.tv_nsec) < 1000)) {
432                         tvt.tv_nsec = cns_new;
433                         tvr.tv_nsec = rns_new;
434                 }
435                 /* At this point tvr and tvt contains valid ns-level
436                 ** timestamps, possibly generated by extending the old
437                 ** us-level timestamps
438                 */
439                 DPRINTF(2, ("%s: SHM type 0 sample\n",
440                             refnumtoa(&peer->srcadr)));
441                 break;
442
443         case 1:
444                 cnt = shm->count;
445
446                 tvr.tv_sec      = shm->receiveTimeStampSec;
447                 tvr.tv_nsec     = shm->receiveTimeStampUSec * 1000;
448                 rns_new         = shm->receiveTimeStampNSec;
449                 tvt.tv_sec      = shm->clockTimeStampSec;
450                 tvt.tv_nsec     = shm->clockTimeStampUSec * 1000;
451                 cns_new         = shm->clockTimeStampNSec;
452                 if (cnt != shm->count) {
453                         DPRINTF(1, ("%s: type 1 access clash\n",
454                                     refnumtoa(&peer->srcadr)));
455                         msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
456                         up->clash++;
457                         return;
458                 }
459                 
460                 /* See the case above for an explanation of the
461                 ** following test.
462                 */
463                 if (   ((cns_new - (unsigned)tvt.tv_nsec) < 1000)
464                     && ((rns_new - (unsigned)tvr.tv_nsec) < 1000)) {
465                         tvt.tv_nsec = cns_new;
466                         tvr.tv_nsec = rns_new;
467                 }
468                 /* At this point tvr and tvt contains valid ns-level
469                 ** timestamps, possibly generated by extending the old
470                 ** us-level timestamps
471                 */
472                 DPRINTF(2, ("%s: SHM type 1 sample\n",
473                             refnumtoa(&peer->srcadr)));
474                 break;
475
476         default:
477                 DPRINTF(1, ("%s: SHM type blooper, mode=%d\n",
478                             refnumtoa(&peer->srcadr), shm->mode));
479                 up->bad++;
480                 msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",
481                          shm->mode);
482                 return;
483         }
484         shm->valid = 0;
485
486         /* format the last time code in human-readable form into
487          * 'pp->a_lastcode'. Someone claimed: "NetBSD has incompatible
488          * tv_sec". I can't find a base for this claim, but we can work
489          * around that potential problem. BTW, simply casting a pointer
490          * is a receipe for disaster on some architectures.
491          */
492         tt = (time_t)tvt.tv_sec;
493         ts = time_to_vint64(&tt);
494         ntpcal_time_to_date(&cd, &ts);
495                 
496         /* add ntpq -c cv timecode in ISO 8601 format */
497         c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
498                      "%04u-%02u-%02uT%02u:%02u:%02u.%09ldZ",
499                      cd.year, cd.month, cd.monthday,
500                      cd.hour, cd.minute, cd.second,
501                      (long)tvt.tv_nsec);
502         pp->lencode = (c < sizeof(pp->a_lastcode)) ? c : 0;
503
504         /* check 1: age control of local time stamp */
505         time(&now);
506         tt = now - tvr.tv_sec;
507         if (tt < 0 || tt > up->max_delay) {
508                 DPRINTF(1, ("%s:SHM stale/bad receive time, delay=%llds\n",
509                             refnumtoa(&peer->srcadr), (long long)tt));
510                 up->bad++;
511                 msyslog (LOG_ERR, "SHM: stale/bad receive time, delay=%llds",
512                          (long long)tt);
513                 return;
514         }
515
516         /* check 2: delta check */
517         tt = tvr.tv_sec - tvt.tv_sec - (tvr.tv_nsec < tvt.tv_nsec);
518         if (tt < 0)
519                 tt = -tt;
520         if (up->max_delta > 0 && tt > up->max_delta) {
521                 DPRINTF(1, ("%s: SHM diff limit exceeded, delta=%llds\n",
522                             refnumtoa(&peer->srcadr), (long long)tt));
523                 up->bad++;
524                 msyslog (LOG_ERR, "SHM: difference limit exceeded, delta=%llds\n",
525                          (long long)tt);
526                 return;
527         }
528
529         /* if we really made it to this point... we're winners! */
530         DPRINTF(2, ("%s: SHM feeding data\n",
531                     refnumtoa(&peer->srcadr)));
532         tsrcv = tspec_stamp_to_lfp(tvr);
533         tsref = tspec_stamp_to_lfp(tvt);
534         pp->leap = shm->leap;
535         peer->precision = shm->precision;
536         refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1);
537         up->good++;
538 }
539
540 /*
541  * shm_clockstats - dump and reset counters
542  */
543 static void shm_clockstats(
544         int unit,
545         struct peer *peer
546         )
547 {
548         struct refclockproc * const pp = peer->procptr;
549         struct shmunit *      const up = pp->unitptr;
550
551         UNUSED_ARG(unit);
552         if (pp->sloppyclockflag & CLK_FLAG4) {
553                 mprintf_clock_stats(
554                         &peer->srcadr, "%3d %3d %3d %3d %3d",
555                         up->ticks, up->good, up->notready,
556                         up->bad, up->clash);
557         }
558         up->ticks = up->good = up->notready = up->bad = up->clash = 0;
559 }
560
561 #else
562 NONEMPTY_TRANSLATION_UNIT
563 #endif /* REFCLOCK */