Home | History | Annotate | Line # | Download | only in ntpd
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