Coverage Report

Created: 2026-04-12 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/libgps/libgps_shm.c
Line
Count
Source
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 by the GPSD project
14
   SPDX-License-Identifier: BSD-2-clause
15
16
***************************************************************************/
17
18
#include "../include/gpsd_config.h"
19
20
#ifdef SHM_EXPORT_ENABLE
21
22
#include <errno.h>
23
#include <stddef.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <sys/ipc.h>
27
#include <sys/shm.h>
28
#include <sys/time.h>
29
30
#include "../include/gpsd.h"
31
#include "../include/libgps.h"
32
33
34
/* open a shared-memory connection to the daemon
35
 *
36
 * Return: 0 == OK
37
 *        less than zero is an error
38
 *         -1 shmget() error
39
 *         -2 shmat() error
40
 *         -3 calloc() error
41
 */
42
int gps_shm_open(struct gps_data_t *gpsdata)
43
0
{
44
0
    int shmid;
45
46
0
    const char *shmkey_s = getenv("GPSD_SHM_KEY");;
47
0
    long shmkey = GPSD_SHM_KEY;
48
0
    if (NULL != shmkey_s) {
49
0
        long tmp = strtol(shmkey_s, NULL, 0);
50
0
        if (0 < tmp) {
51
0
            shmkey = tmp;
52
0
        }
53
0
    }
54
55
0
    libgps_debug_trace(DEBUG_CALLS, "%s",  "libgps: gps_shm_open()\n");
56
57
0
    gpsdata->privdata = NULL;
58
0
    shmid = shmget((key_t)shmkey, sizeof(struct gps_data_t), 0);
59
0
    if (-1 == shmid) {
60
        // daemon isn't running or failed to create shared segment
61
0
        libgps_debug_trace(DEBUG_CALLS, "libgps: gps_shm_open(x%lx) %s(%d)\n",
62
0
                            (unsigned long)shmkey, strerror(errno),  errno);
63
0
        return -1;
64
0
    }
65
0
    gpsdata->privdata =
66
0
        (struct privdata_t *)calloc(1, sizeof(struct privdata_t));
67
0
    if (NULL == gpsdata->privdata) {
68
0
        libgps_debug_trace(DEBUG_CALLS, "libgps: calloc() %s(%d)\n",
69
0
                            strerror(errno),  errno);
70
0
        return -3;
71
0
    }
72
73
0
    PRIVATE(gpsdata)->shmseg = shmat(shmid, 0, 0);
74
0
    if ((void *)-1 == PRIVATE(gpsdata)->shmseg) {
75
        // attach failed for sume unknown reason
76
0
        libgps_debug_trace(DEBUG_CALLS, "libgps: shmat() %s(%d)\n",
77
0
                            strerror(errno),  errno);
78
0
        free(gpsdata->privdata);
79
0
        gpsdata->privdata = NULL;
80
0
        return -2;
81
0
    }
82
0
    gpsdata->gps_fd = (gps_fd_t)SHM_PSEUDO_FD;
83
0
    return 0;
84
0
}
85
86
/* check to see if new data has been written
87
 * timeout is in uSec */
88
bool gps_shm_waiting(const struct gps_data_t *gpsdata, int timeout)
89
0
{
90
0
    volatile struct shmexport_t *shared =
91
0
            (struct shmexport_t *)PRIVATE(gpsdata)->shmseg;
92
0
    volatile bool newdata = false;
93
0
    timespec_t endtime;
94
95
0
    (void)clock_gettime(CLOCK_REALTIME, &endtime);
96
0
    endtime.tv_sec += timeout / 1000000;
97
0
    endtime.tv_nsec += (timeout % 1000000) * 1000;
98
0
    TS_NORM(&endtime);
99
100
    // busy-waiting sucks, but there's not really an alternative
101
0
    for (;;) {
102
0
        volatile int bookend1, bookend2;
103
0
        timespec_t now;
104
105
0
        memory_barrier();
106
0
        bookend1 = shared->bookend1;
107
0
        memory_barrier();
108
0
        bookend2 = shared->bookend2;
109
0
        memory_barrier();
110
0
        if (bookend1 == bookend2 && bookend1 > PRIVATE(gpsdata)->tick) {
111
0
            newdata = true;
112
0
            break;
113
0
        }
114
0
        (void)clock_gettime(CLOCK_REALTIME, &now);
115
0
        if (TS_GT(&now, &endtime)) {
116
0
            break;
117
0
        }
118
0
    }
119
120
0
    return newdata;
121
0
}
122
123
// read an update from the shared-memory segment
124
int gps_shm_read(struct gps_data_t *gpsdata)
125
0
{
126
0
    if (NULL == gpsdata->privdata) {
127
0
        return -1;
128
0
    } else {
129
0
        int before1, before2, after1, after2;
130
0
        struct privdata_t *private_save = gpsdata->privdata;
131
0
        struct shmexport_t *shared =
132
0
            (struct shmexport_t *)PRIVATE(gpsdata)->shmseg;
133
0
        struct gps_data_t noclobber;
134
135
        /*
136
         * Following block of instructions must not be reordered,
137
         * otherwise havoc will ensue.  The memory_barrier() call
138
         * should prevent reordering of the data accesses.
139
         * for those lucky enough to have a working memory_barrier()
140
         *
141
         * bookends are volatile, so that should force
142
         * them to be read in order.
143
         *
144
         * This is a simple optimistic-concurrency technique.  We wrote
145
         * the second bookend first, then the data, then the first bookend.
146
         * Reader copies what it sees in normal order; that way, if we
147
         * start to write the segment during the read, the second bookend will
148
         * get clobbered first and the data can be detected as bad.
149
         *
150
         * Excwpt with mutil-treading and CPU caches, order is iffy...
151
         */
152
0
        before1 = shared->bookend1;
153
0
        before2 = shared->bookend2;
154
0
        memory_barrier();
155
        // memcpy() and (volatile) don't play well together.
156
0
        (void)memcpy((void *)&noclobber,
157
0
                     (void *)&shared->gpsdata,
158
0
                     sizeof(struct gps_data_t));
159
0
        memory_barrier();
160
0
        after1 = shared->bookend1;
161
0
        after2 = shared->bookend2;
162
163
0
        if (before1 != after1 ||
164
0
            before1 != after2 ||
165
0
            before1 != before2) {
166
            // bookend mismatch, throw away the data
167
            // FIXME: retry?
168
0
            return 0;
169
0
        } else {
170
0
            (void)memcpy((void *)gpsdata,
171
0
                         (void *)&noclobber,
172
0
                         sizeof(struct gps_data_t));
173
0
            gpsdata->privdata = private_save;
174
0
            gpsdata->gps_fd = (gps_fd_t)SHM_PSEUDO_FD;
175
0
            PRIVATE(gpsdata)->tick = after2;
176
0
            if (0 != (gpsdata->set & REPORT_IS)) {
177
0
                gpsdata->set |= STATUS_SET;
178
0
            }
179
0
            return (int)sizeof(struct gps_data_t);
180
0
        }
181
0
    }
182
0
}
183
184
void gps_shm_close(struct gps_data_t *gpsdata)
185
0
{
186
0
    if (PRIVATE(gpsdata)) {
187
0
        if (NULL != PRIVATE(gpsdata)->shmseg) {
188
0
            (void)shmdt((const void *)PRIVATE(gpsdata)->shmseg);
189
0
        }
190
0
        free(PRIVATE(gpsdata));
191
0
        gpsdata->privdata = NULL;
192
0
    }
193
0
}
194
195
/* run a shm main loop with a specified handler
196
 *
197
 * Returns: -1 on timeout
198
 *          -2 on error
199
 * FIXME: read error should return different than timeout
200
 */
201
int gps_shm_mainloop(struct gps_data_t *gpsdata, int timeout,
202
                     void (*hook)(struct gps_data_t *gpsdata))
203
0
{
204
205
0
    for (;;) {
206
0
        int status;
207
208
0
        if (!gps_shm_waiting(gpsdata, timeout)) {
209
0
            return -1;
210
0
        }
211
0
        status = gps_shm_read(gpsdata);
212
213
0
        if (-1 == status) {
214
0
            break;
215
0
        }
216
0
        if (0 < status) {
217
0
            (*hook)(gpsdata);
218
0
        }
219
0
    }
220
0
    return -2;
221
0
}
222
223
#endif  // SHM_EXPORT_ENABLE
224
225
// vim: set expandtab shiftwidth=4