shmexport.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /****************************************************************************
  2. NAME
  3. shmexport.c - shared-memory export from the daemon
  4. DESCRIPTION
  5. This is a very lightweight alternative to JSON-over-sockets. Clients
  6. won't be able to filter by device, and won't get device activation/deactivation
  7. notifications. But both client and daemon will avoid all the marshalling and
  8. unmarshalling overhead.
  9. PERMISSIONS
  10. This file is Copyright 2010 by the GPSD project
  11. SPDX-License-Identifier: BSD-2-clause
  12. ***************************************************************************/
  13. #include "gpsd_config.h"
  14. #ifdef SHM_EXPORT_ENABLE
  15. #include <errno.h>
  16. #include <stddef.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <sys/ipc.h>
  20. #include <sys/shm.h>
  21. #include <sys/time.h>
  22. #include "gpsd.h"
  23. #include "libgps.h" /* for SHM_PSEUDO_FD */
  24. bool shm_acquire(struct gps_context_t *context)
  25. /* initialize the shared-memory segment to be used for export */
  26. {
  27. long shmkey = getenv("GPSD_SHM_KEY") ? \
  28. strtol(getenv("GPSD_SHM_KEY"), NULL, 0) : GPSD_SHM_KEY;
  29. int shmid = shmget((key_t)shmkey, sizeof(struct shmexport_t),
  30. (int)(IPC_CREAT|0666));
  31. if (shmid == -1) {
  32. GPSD_LOG(LOG_ERROR, &context->errout,
  33. "shmget(0x%lx, %zd, 0666) for SHM export failed: %s\n",
  34. shmkey,
  35. sizeof(struct shmexport_t),
  36. strerror(errno));
  37. return false;
  38. } else
  39. GPSD_LOG(LOG_PROG, &context->errout,
  40. "shmget(0x%lx, %zd, 0666) for SHM export succeeded\n",
  41. shmkey,
  42. sizeof(struct shmexport_t));
  43. context->shmexport = (void *)shmat(shmid, 0, 0);
  44. if ((int)(long)context->shmexport == -1) {
  45. GPSD_LOG(LOG_ERROR, &context->errout,
  46. "shmat failed: %s\n", strerror(errno));
  47. context->shmexport = NULL;
  48. return false;
  49. }
  50. context->shmid = shmid;
  51. GPSD_LOG(LOG_PROG, &context->errout,
  52. "shmat() for SHM export succeeded, segment %d\n", shmid);
  53. return true;
  54. }
  55. /* release the shared-memory segment used for export */
  56. void shm_release(struct gps_context_t *context)
  57. {
  58. if (context->shmexport == NULL)
  59. return;
  60. /* Mark shmid to go away when no longer used
  61. * Having it linger forever is bad, and when the size enlarges
  62. * it can no longer be opened
  63. */
  64. if (shmctl(context->shmid, IPC_RMID, NULL) == -1) {
  65. GPSD_LOG(LOG_WARN, &context->errout,
  66. "shmctl for IPC_RMID failed, errno = %d (%s)\n",
  67. errno, strerror(errno));
  68. }
  69. (void)shmdt((const void *)context->shmexport);
  70. }
  71. /* export an update to all listeners */
  72. void shm_update(struct gps_context_t *context, struct gps_data_t *gpsdata)
  73. {
  74. if (context->shmexport != NULL)
  75. {
  76. static int tick;
  77. volatile struct shmexport_t *shared = \
  78. (struct shmexport_t *)context->shmexport;
  79. ++tick;
  80. /*
  81. * Following block of instructions must not be reordered, otherwise
  82. * havoc will ensue.
  83. *
  84. * This is a simple optimistic-concurrency technique. We write
  85. * the second bookend first, then the data, then the first bookend.
  86. * Reader copies what it sees in normal order; that way, if we
  87. * start to write the segment during the read, the second bookend will
  88. * get clobbered first and the data can be detected as bad.
  89. *
  90. * Of course many architectures, like Intel, make no guarantees
  91. * about the actual memory read or write order into RAM, so this
  92. * is partly wishful thinking. Thus the need for the memory_barriers()
  93. * to enforce the required order.
  94. */
  95. shared->bookend2 = tick;
  96. memory_barrier();
  97. shared->gpsdata = *gpsdata;
  98. memory_barrier();
  99. #ifndef USE_QT
  100. shared->gpsdata.gps_fd = SHM_PSEUDO_FD;
  101. #else
  102. shared->gpsdata.gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
  103. #endif /* USE_QT */
  104. memory_barrier();
  105. shared->bookend1 = tick;
  106. }
  107. }
  108. #endif /* SHM_EXPORT_ENABLE */
  109. /* end */
  110. // vim: set expandtab shiftwidth=4