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