1 /****************************************************************************
2
3 NAME
4 shmexport.c - shared-memory export from the daemon
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 <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 "gpsd.h"
31 #include "libgps.h" /* for SHM_PSEUDO_FD */
32
33
shm_acquire(struct gps_context_t * context)34 bool shm_acquire(struct gps_context_t *context)
35 /* initialize the shared-memory segment to be used for export */
36 {
37 long shmkey = getenv("GPSD_SHM_KEY") ? strtol(getenv("GPSD_SHM_KEY"), NULL, 0) : GPSD_SHM_KEY;
38
39 int shmid = shmget((key_t)shmkey, sizeof(struct shmexport_t), (int)(IPC_CREAT|0666));
40 if (shmid == -1) {
41 GPSD_LOG(LOG_ERROR, &context->errout,
42 "shmget(0x%lx, %zd, 0666) for SHM export failed: %s\n",
43 shmkey,
44 sizeof(struct shmexport_t),
45 strerror(errno));
46 return false;
47 } else
48 GPSD_LOG(LOG_PROG, &context->errout,
49 "shmget(0x%lx, %zd, 0666) for SHM export succeeded\n",
50 shmkey,
51 sizeof(struct shmexport_t));
52
53 context->shmexport = (void *)shmat(shmid, 0, 0);
54 if ((int)(long)context->shmexport == -1) {
55 GPSD_LOG(LOG_ERROR, &context->errout,
56 "shmat failed: %s\n", strerror(errno));
57 context->shmexport = NULL;
58 return false;
59 }
60 context->shmid = shmid;
61
62 GPSD_LOG(LOG_PROG, &context->errout,
63 "shmat() for SHM export succeeded, segment %d\n", shmid);
64 return true;
65 }
66
shm_release(struct gps_context_t * context)67 void shm_release(struct gps_context_t *context)
68 /* release the shared-memory segment used for export */
69 {
70 if (context->shmexport == NULL)
71 return;
72
73 /* Mark shmid to go away when no longer used
74 * Having it linger forever is bad, and when the size enlarges
75 * it can no longer be opened
76 */
77 if (shmctl(context->shmid, IPC_RMID, NULL) == -1) {
78 GPSD_LOG(LOG_WARN, &context->errout,
79 "shmctl for IPC_RMID failed, errno = %d (%s)\n",
80 errno, strerror(errno));
81 }
82 (void)shmdt((const void *)context->shmexport);
83 }
84
shm_update(struct gps_context_t * context,struct gps_data_t * gpsdata)85 void shm_update(struct gps_context_t *context, struct gps_data_t *gpsdata)
86 /* export an update to all listeners */
87 {
88 if (context->shmexport != NULL)
89 {
90 static int tick;
91 volatile struct shmexport_t *shared = (struct shmexport_t *)context->shmexport;
92
93 ++tick;
94 /*
95 * Following block of instructions must not be reordered, otherwise
96 * havoc will ensue.
97 *
98 * This is a simple optimistic-concurrency technique. We write
99 * the second bookend first, then the data, then the first bookend.
100 * Reader copies what it sees in normal order; that way, if we
101 * start to write the segment during the read, the second bookend will
102 * get clobbered first and the data can be detected as bad.
103 *
104 * Of course many architectures, like Intel, make no guarantees
105 * about the actual memory read or write order into RAM, so this
106 * is partly wishful thinking. Thus the need for the memory_barriers()
107 * to enforce the required order.
108 */
109 shared->bookend2 = tick;
110 memory_barrier();
111 shared->gpsdata = *gpsdata;
112 memory_barrier();
113 #ifndef USE_QT
114 shared->gpsdata.gps_fd = SHM_PSEUDO_FD;
115 #else
116 shared->gpsdata.gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
117 #endif /* USE_QT */
118 memory_barrier();
119 shared->bookend1 = tick;
120 }
121 }
122
123
124 #endif /* SHM_EXPORT_ENABLE */
125
126 /* end */
127