1 /****************************************************************************
2
3 NAME
4 libgps_shm.c - reader access to shared-memory export
5
6 DESCRIPTION
7 This is a very lightweight alternative to JSON-over-sockets. Clients
8 won't be able to filter by device, and won't get device activation/deactivation
9 notifications. But both client and daemon will avoid all the marshalling and
10 unmarshalling overhead.
11
12 PERMISSIONS
13 This file is Copyright (c) 2010-2018 by the GPSD project
14 SPDX-License-Identifier: BSD-2-clause
15
16 ***************************************************************************/
17
18 #include "gpsd_config.h"
19
20 #ifdef SHM_EXPORT_ENABLE
21
22 #include <stddef.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <sys/time.h>
27 #include <sys/ipc.h>
28 #include <sys/shm.h>
29
30 #include "gpsd.h"
31 #include "libgps.h"
32
33 struct privdata_t
34 {
35 void *shmseg;
36 int tick;
37 };
38
39
gps_shm_open(struct gps_data_t * gpsdata)40 int gps_shm_open(struct gps_data_t *gpsdata)
41 /* open a shared-memory connection to the daemon */
42 {
43 int shmid;
44
45 long shmkey = getenv("GPSD_SHM_KEY") ? strtol(getenv("GPSD_SHM_KEY"), NULL, 0) : GPSD_SHM_KEY;
46
47 libgps_debug_trace((DEBUG_CALLS, "gps_shm_open()\n"));
48
49 gpsdata->privdata = NULL;
50 shmid = shmget((key_t)shmkey, sizeof(struct gps_data_t), 0);
51 if (shmid == -1) {
52 /* daemon isn't running or failed to create shared segment */
53 return -1;
54 }
55 gpsdata->privdata = (void *)malloc(sizeof(struct privdata_t));
56 if (gpsdata->privdata == NULL)
57 return -1;
58
59 PRIVATE(gpsdata)->tick = 0;
60 PRIVATE(gpsdata)->shmseg = shmat(shmid, 0, 0);
61 if (PRIVATE(gpsdata)->shmseg == (void *) -1) {
62 /* attach failed for sume unknown reason */
63 free(gpsdata->privdata);
64 gpsdata->privdata = NULL;
65 return -2;
66 }
67 #ifndef USE_QT
68 gpsdata->gps_fd = SHM_PSEUDO_FD;
69 #else
70 gpsdata->gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
71 #endif /* USE_QT */
72 return 0;
73 }
74
75 /* check to see if new data has been written */
76 /* timeout is in uSec */
gps_shm_waiting(const struct gps_data_t * gpsdata,int timeout)77 bool gps_shm_waiting(const struct gps_data_t *gpsdata, int timeout)
78 {
79 volatile struct shmexport_t *shared =
80 (struct shmexport_t *)PRIVATE(gpsdata)->shmseg;
81 volatile bool newdata = false;
82 timespec_t endtime;
83
84 (void)clock_gettime(CLOCK_REALTIME, &endtime);
85 endtime.tv_sec += timeout / 1000000;
86 endtime.tv_nsec += (timeout % 1000000) * 1000;
87 TS_NORM(&endtime);
88
89 /* busy-waiting sucks, but there's not really an alternative */
90 for (;;) {
91 volatile int bookend1, bookend2;
92 timespec_t now;
93
94 memory_barrier();
95 bookend1 = shared->bookend1;
96 memory_barrier();
97 bookend2 = shared->bookend2;
98 memory_barrier();
99 if (bookend1 == bookend2 && bookend1 > PRIVATE(gpsdata)->tick) {
100 newdata = true;
101 break;
102 }
103 (void)clock_gettime(CLOCK_REALTIME, &now);
104 if (TS_GT(&now, &endtime)) {
105 break;
106 }
107 }
108
109 return newdata;
110 }
111
gps_shm_read(struct gps_data_t * gpsdata)112 int gps_shm_read(struct gps_data_t *gpsdata)
113 /* read an update from the shared-memory segment */
114 {
115 if (gpsdata->privdata == NULL)
116 return -1;
117 else
118 {
119 int before, after;
120 void *private_save = gpsdata->privdata;
121 volatile struct shmexport_t *shared = (struct shmexport_t *)PRIVATE(gpsdata)->shmseg;
122 struct gps_data_t noclobber;
123
124 /*
125 * Following block of instructions must not be reordered,
126 * otherwise havoc will ensue. The memory_barrier() call
127 * should prevent reordering of the data accesses.
128 *
129 * This is a simple optimistic-concurrency technique. We wrote
130 * the second bookend first, then the data, then the first bookend.
131 * Reader copies what it sees in normal order; that way, if we
132 * start to write the segment during the read, the second bookend will
133 * get clobbered first and the data can be detected as bad.
134 */
135 before = shared->bookend1;
136 memory_barrier();
137 (void)memcpy((void *)&noclobber,
138 (void *)&shared->gpsdata,
139 sizeof(struct gps_data_t));
140 memory_barrier();
141 after = shared->bookend2;
142
143 if (before != after)
144 return 0;
145 else {
146 (void)memcpy((void *)gpsdata,
147 (void *)&noclobber,
148 sizeof(struct gps_data_t));
149 gpsdata->privdata = private_save;
150 #ifndef USE_QT
151 gpsdata->gps_fd = SHM_PSEUDO_FD;
152 #else
153 gpsdata->gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
154 #endif /* USE_QT */
155 PRIVATE(gpsdata)->tick = after;
156 if ((gpsdata->set & REPORT_IS)!=0) {
157 gpsdata->set = STATUS_SET;
158 }
159 return (int)sizeof(struct gps_data_t);
160 }
161 }
162 }
163
gps_shm_close(struct gps_data_t * gpsdata)164 void gps_shm_close(struct gps_data_t *gpsdata)
165 {
166 if (PRIVATE(gpsdata)) {
167 if (PRIVATE(gpsdata)->shmseg != NULL)
168 (void)shmdt((const void *)PRIVATE(gpsdata)->shmseg);
169 free(PRIVATE(gpsdata));
170 gpsdata->privdata = NULL;
171 }
172 }
173
gps_shm_mainloop(struct gps_data_t * gpsdata,int timeout,void (* hook)(struct gps_data_t * gpsdata))174 int gps_shm_mainloop(struct gps_data_t *gpsdata, int timeout,
175 void (*hook)(struct gps_data_t *gpsdata))
176 /* run a shm main loop with a specified handler */
177 {
178 for (;;) {
179 if (!gps_shm_waiting(gpsdata, timeout)) {
180 return -1;
181 } else {
182 int status = gps_shm_read(gpsdata);
183
184 if (status == -1)
185 return -1;
186 if (status > 0)
187 (*hook)(gpsdata);
188 }
189 }
190 //return 0;
191 }
192
193 #endif /* SHM_EXPORT_ENABLE */
194
195 /* end */
196