You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
5.0KB

  1. /****************************************************************************
  2. NAME
  3. libgps_shm.c - reader access to shared-memory export
  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 <stddef.h>
  16. #include <string.h>
  17. #include <errno.h>
  18. #include <stdlib.h>
  19. #include <sys/time.h>
  20. #include <sys/ipc.h>
  21. #include <sys/shm.h>
  22. #include "gpsd.h"
  23. #include "libgps.h"
  24. struct privdata_t
  25. {
  26. void *shmseg;
  27. int tick;
  28. };
  29. int gps_shm_open(struct gps_data_t *gpsdata)
  30. /* open a shared-memory connection to the daemon */
  31. {
  32. int shmid;
  33. long shmkey = getenv("GPSD_SHM_KEY") ? strtol(getenv("GPSD_SHM_KEY"), NULL, 0) : GPSD_SHM_KEY;
  34. libgps_debug_trace((DEBUG_CALLS, "gps_shm_open()\n"));
  35. gpsdata->privdata = NULL;
  36. shmid = shmget((key_t)shmkey, sizeof(struct gps_data_t), 0);
  37. if (shmid == -1) {
  38. /* daemon isn't running or failed to create shared segment */
  39. return -1;
  40. }
  41. gpsdata->privdata = (void *)malloc(sizeof(struct privdata_t));
  42. if (gpsdata->privdata == NULL)
  43. return -1;
  44. PRIVATE(gpsdata)->tick = 0;
  45. PRIVATE(gpsdata)->shmseg = shmat(shmid, 0, 0);
  46. if (PRIVATE(gpsdata)->shmseg == (void *) -1) {
  47. /* attach failed for sume unknown reason */
  48. free(gpsdata->privdata);
  49. gpsdata->privdata = NULL;
  50. return -2;
  51. }
  52. #ifndef USE_QT
  53. gpsdata->gps_fd = SHM_PSEUDO_FD;
  54. #else
  55. gpsdata->gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
  56. #endif /* USE_QT */
  57. return 0;
  58. }
  59. /* check to see if new data has been written */
  60. /* timeout is in uSec */
  61. bool gps_shm_waiting(const struct gps_data_t *gpsdata, int timeout)
  62. {
  63. volatile struct shmexport_t *shared =
  64. (struct shmexport_t *)PRIVATE(gpsdata)->shmseg;
  65. volatile bool newdata = false;
  66. timespec_t endtime;
  67. (void)clock_gettime(CLOCK_REALTIME, &endtime);
  68. endtime.tv_sec += timeout / 1000000;
  69. endtime.tv_nsec += (timeout % 1000000) * 1000;
  70. TS_NORM(&endtime);
  71. /* busy-waiting sucks, but there's not really an alternative */
  72. for (;;) {
  73. volatile int bookend1, bookend2;
  74. timespec_t now;
  75. memory_barrier();
  76. bookend1 = shared->bookend1;
  77. memory_barrier();
  78. bookend2 = shared->bookend2;
  79. memory_barrier();
  80. if (bookend1 == bookend2 && bookend1 > PRIVATE(gpsdata)->tick) {
  81. newdata = true;
  82. break;
  83. }
  84. (void)clock_gettime(CLOCK_REALTIME, &now);
  85. if (TS_GT(&now, &endtime)) {
  86. break;
  87. }
  88. }
  89. return newdata;
  90. }
  91. int gps_shm_read(struct gps_data_t *gpsdata)
  92. /* read an update from the shared-memory segment */
  93. {
  94. if (gpsdata->privdata == NULL)
  95. return -1;
  96. else
  97. {
  98. int before, after;
  99. void *private_save = gpsdata->privdata;
  100. volatile struct shmexport_t *shared = (struct shmexport_t *)PRIVATE(gpsdata)->shmseg;
  101. struct gps_data_t noclobber;
  102. /*
  103. * Following block of instructions must not be reordered,
  104. * otherwise havoc will ensue. The memory_barrier() call
  105. * should prevent reordering of the data accesses.
  106. *
  107. * This is a simple optimistic-concurrency technique. We wrote
  108. * the second bookend first, then the data, then the first bookend.
  109. * Reader copies what it sees in normal order; that way, if we
  110. * start to write the segment during the read, the second bookend will
  111. * get clobbered first and the data can be detected as bad.
  112. */
  113. before = shared->bookend1;
  114. memory_barrier();
  115. (void)memcpy((void *)&noclobber,
  116. (void *)&shared->gpsdata,
  117. sizeof(struct gps_data_t));
  118. memory_barrier();
  119. after = shared->bookend2;
  120. if (before != after)
  121. return 0;
  122. else {
  123. (void)memcpy((void *)gpsdata,
  124. (void *)&noclobber,
  125. sizeof(struct gps_data_t));
  126. gpsdata->privdata = private_save;
  127. #ifndef USE_QT
  128. gpsdata->gps_fd = SHM_PSEUDO_FD;
  129. #else
  130. gpsdata->gps_fd = (void *)(intptr_t)SHM_PSEUDO_FD;
  131. #endif /* USE_QT */
  132. PRIVATE(gpsdata)->tick = after;
  133. if ((gpsdata->set & REPORT_IS)!=0) {
  134. gpsdata->set = STATUS_SET;
  135. }
  136. return (int)sizeof(struct gps_data_t);
  137. }
  138. }
  139. }
  140. void gps_shm_close(struct gps_data_t *gpsdata)
  141. {
  142. if (PRIVATE(gpsdata)) {
  143. if (PRIVATE(gpsdata)->shmseg != NULL)
  144. (void)shmdt((const void *)PRIVATE(gpsdata)->shmseg);
  145. free(PRIVATE(gpsdata));
  146. gpsdata->privdata = NULL;
  147. }
  148. }
  149. int gps_shm_mainloop(struct gps_data_t *gpsdata, int timeout,
  150. void (*hook)(struct gps_data_t *gpsdata))
  151. /* run a shm main loop with a specified handler */
  152. {
  153. for (;;) {
  154. if (!gps_shm_waiting(gpsdata, timeout)) {
  155. return -1;
  156. } else {
  157. int status = gps_shm_read(gpsdata);
  158. if (status == -1)
  159. return -1;
  160. if (status > 0)
  161. (*hook)(gpsdata);
  162. }
  163. }
  164. //return 0;
  165. }
  166. #endif /* SHM_EXPORT_ENABLE */
  167. /* end */