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