shmexport.c 3.6 KB

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