123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- /****************************************************************************
- NAME
- shmexport.c - shared-memory export from the daemon
- DESCRIPTION
- This is a very lightweight alternative to JSON-over-sockets. Clients
- won't be able to filter by device, and won't get device activation/deactivation
- notifications. But both client and daemon will avoid all the marshalling and
- unmarshalling overhead.
- PERMISSIONS
- This file is Copyright 2010 by the GPSD project
- SPDX-License-Identifier: BSD-2-clause
- ***************************************************************************/
- #include "gpsd_config.h"
- #ifdef SHM_EXPORT_ENABLE
- #include <errno.h>
- #include <stddef.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/time.h>
- #include "gpsd.h"
- #include "libgps.h" /* for SHM_PSEUDO_FD */
- bool shm_acquire(struct gps_context_t *context)
- /* initialize the shared-memory segment to be used for export */
- {
- long shmkey = getenv("GPSD_SHM_KEY") ? \
- strtol(getenv("GPSD_SHM_KEY"), NULL, 0) : GPSD_SHM_KEY;
- int shmid = shmget((key_t)shmkey, sizeof(struct shmexport_t),
- (int)(IPC_CREAT|0666));
- if (shmid == -1) {
- GPSD_LOG(LOG_ERROR, &context->errout,
- "shmget(0x%lx, %zd, 0666) for SHM export failed: %s\n",
- shmkey,
- sizeof(struct shmexport_t),
- strerror(errno));
- return false;
- } else
- GPSD_LOG(LOG_PROG, &context->errout,
- "shmget(0x%lx, %zd, 0666) for SHM export succeeded\n",
- shmkey,
- sizeof(struct shmexport_t));
- context->shmexport = (void *)shmat(shmid, 0, 0);
- if ((int)(long)context->shmexport == -1) {
- GPSD_LOG(LOG_ERROR, &context->errout,
- "shmat failed: %s\n", strerror(errno));
- context->shmexport = NULL;
- return false;
- }
- context->shmid = shmid;
- GPSD_LOG(LOG_PROG, &context->errout,
- "shmat() for SHM export succeeded, segment %d\n", shmid);
- return true;
- }
- /* release the shared-memory segment used for export */
- void shm_release(struct gps_context_t *context)
- {
- if (context->shmexport == NULL)
- return;
- /* Mark shmid to go away when no longer used
- * Having it linger forever is bad, and when the size enlarges
- * it can no longer be opened
- */
- if (shmctl(context->shmid, IPC_RMID, NULL) == -1) {
- GPSD_LOG(LOG_WARN, &context->errout,
- "shmctl for IPC_RMID failed, errno = %d (%s)\n",
- errno, strerror(errno));
- }
- (void)shmdt((const void *)context->shmexport);
- }
- /* export an update to all listeners */
- void shm_update(struct gps_context_t *context, struct gps_data_t *gpsdata)
- {
- if (context->shmexport != NULL)
- {
- static int tick;
- volatile struct shmexport_t *shared = \
- (struct shmexport_t *)context->shmexport;
- ++tick;
- /*
- * Following block of instructions must not be reordered, otherwise
- * havoc will ensue.
- *
- * This is a simple optimistic-concurrency technique. We write
- * the second bookend first, then the data, then the first bookend.
- * Reader copies what it sees in normal order; that way, if we
- * start to write the segment during the read, the second bookend will
- * get clobbered first and the data can be detected as bad.
- *
- * Of course many architectures, like Intel, make no guarantees
- * about the actual memory read or write order into RAM, so this
- * is partly wishful thinking. Thus the need for the memory_barriers()
- * to enforce the required order.
- */
- shared->bookend2 = tick;
- memory_barrier();
- shared->gpsdata = *gpsdata;
- memory_barrier();
- #ifndef USE_QT
- shared->gpsdata.gps_fd = SHM_PSEUDO_FD;
- #else
- shared->gpsdata.gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
- #endif /* USE_QT */
- memory_barrier();
- shared->bookend1 = tick;
- }
- }
- #endif /* SHM_EXPORT_ENABLE */
- /* end */
- // vim: set expandtab shiftwidth=4
|