1 /* ntpshmread.c -- monitor the inner end of an ntpshmwrite.connection
2 *
3 * This file is Copyright (c) 2010-2018 by the GPSD project
4 * SPDX-License-Identifier: BSD-2-clause
5 *
6 * Some of this was swiped from the NTPD distribution.
7 */
8
9 #include "gpsd_config.h" /* must be before all includes */
10
11 #include <string.h>
12 #include <stdbool.h>
13 #include <math.h>
14 #include <errno.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <assert.h>
18 #include <stdio.h>
19 #include <stdint.h>
20 #include <unistd.h>
21
22 #include "ntpshm.h"
23 #include "compiler.h"
24
shm_get(const int unit,const bool create,const bool forall)25 struct shmTime *shm_get(const int unit, const bool create, const bool forall)
26 /* initialize a SHM segment */
27 {
28 struct shmTime *p = NULL;
29 int shmid;
30
31 /*
32 * Big units will give non-ASCII but that's OK
33 * as long as everybody does it the same way.
34 */
35 shmid = shmget((key_t)(NTPD_BASE + unit), sizeof(struct shmTime),
36 (create ? IPC_CREAT : 0) | (forall ? 0666 : 0600));
37 if (shmid == -1) { /* error */
38 return NULL;
39 }
40 p = (struct shmTime *)shmat (shmid, 0, 0);
41 if (p == (struct shmTime *)-1) { /* error */
42 return NULL;
43 }
44 return p;
45 }
46
ntp_name(const int unit)47 char *ntp_name(const int unit)
48 /* return the name of a specified segment */
49 {
50 static char name[5] = "NTP\0";
51
52 name[3] = (char)('0' + unit);
53
54 return name;
55 }
56
ntp_read(struct shmTime * shm_in,struct shm_stat_t * shm_stat,const bool consume)57 enum segstat_t ntp_read(struct shmTime *shm_in, struct shm_stat_t *shm_stat, const bool consume)
58 /* try to grab a sample from the specified SHM segment */
59 {
60 volatile struct shmTime shmcopy, *shm = shm_in;
61 volatile int cnt;
62
63 unsigned int cns_new, rns_new;
64
65 if (shm == NULL) {
66 shm_stat->status = NO_SEGMENT;
67 return NO_SEGMENT;
68 }
69
70 shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0;
71
72 /* relying on word access to be atomic here */
73 if (shm->valid == 0) {
74 shm_stat->status = NOT_READY;
75 return NOT_READY;
76 }
77
78 cnt = shm->count;
79
80 /*
81 * This is proof against concurrency issues if either (a) the
82 * memory_barrier() call works on this host, or (b) memset
83 * compiles to an uninterruptible single-instruction bitblt (this
84 * will probably cease to be true if the structure exceeds your VM
85 * page size).
86 */
87 memory_barrier();
88 memcpy((void *)&shmcopy, (void *)shm, sizeof(struct shmTime));
89
90 /*
91 * An update consumer such as ntpd should zero the valid flag at this point.
92 * A program snooping the updates to collect statistics should not, lest
93 * it make the data unavailable for consumers.
94 */
95 if (consume)
96 shm->valid = 0;
97 memory_barrier();
98
99 /*
100 * Clash detection in case neither (a) nor (b) was true.
101 * Not supported in mode 0, and word access to the count field
102 * must be atomic for this to work.
103 */
104 if (shmcopy.mode > 0 && cnt != shm->count) {
105 shm_stat->status = CLASH;
106 return shm_stat->status;
107 }
108
109 shm_stat->status = OK;
110
111 switch (shmcopy.mode) {
112 case 0:
113 shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec;
114 shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000;
115 rns_new = shmcopy.receiveTimeStampNSec;
116 shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec;
117 shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000;
118 cns_new = shmcopy.clockTimeStampNSec;
119
120 /* Since the following comparisons are between unsigned
121 ** variables they are always well defined, and any
122 ** (signed) underflow will turn into very large unsigned
123 ** values, well above the 1000 cutoff.
124 **
125 ** Note: The usecs *must* be a *truncated*
126 ** representation of the nsecs. This code will fail for
127 ** *rounded* usecs, and the logic to deal with
128 ** wrap-arounds in the presence of rounded values is
129 ** much more convoluted.
130 */
131 if (((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
132 && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
133 shm_stat->tvt.tv_nsec = cns_new;
134 shm_stat->tvr.tv_nsec = rns_new;
135 }
136 /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level
137 ** timestamps, possibly generated by extending the old
138 ** us-level timestamps
139 */
140 break;
141
142 case 1:
143 shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec;
144 shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000;
145 rns_new = shmcopy.receiveTimeStampNSec;
146 shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec;
147 shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000;
148 cns_new = shmcopy.clockTimeStampNSec;
149
150 /* See the case above for an explanation of the
151 ** following test.
152 */
153 if (((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
154 && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
155 shm_stat->tvt.tv_nsec = cns_new;
156 shm_stat->tvr.tv_nsec = rns_new;
157 }
158 /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level
159 ** timestamps, possibly generated by extending the old
160 ** us-level timestamps
161 */
162 break;
163
164 default:
165 shm_stat->status = BAD_MODE;
166 break;
167 }
168
169 /*
170 * leap field is not a leap offset but a leap notification code.
171 * The values are magic numbers used by NTP and set by GPSD, if at all, in
172 * the subframe code.
173 */
174 shm_stat->leap = shmcopy.leap;
175 shm_stat->precision = shmcopy.precision;
176
177 return shm_stat->status;
178 }
179
180 /* end */
181