Coverage Report

Created: 2025-07-12 06:36

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