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