libgps_dbus.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * This file is Copyright 2010 by the GPSD project
  3. * SPDX-License-Identifier: BSD-2-clause
  4. */
  5. #include "../include/gpsd_config.h" // must be before all includes
  6. #if defined(DBUS_EXPORT_ENABLE)
  7. #include <dbus/dbus.h>
  8. #include <errno.h>
  9. #include <libgen.h>
  10. #include <math.h>
  11. #include <signal.h>
  12. #include <stdbool.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <syslog.h>
  17. #include <time.h>
  18. #include <unistd.h>
  19. #include "../include/gps.h"
  20. #include "../include/libgps.h"
  21. #include "../include/os_compat.h"
  22. #include "../include/timespec.h"
  23. struct privdata_t
  24. {
  25. void (*handler)(struct gps_data_t *);
  26. };
  27. /*
  28. * Unpleasant that we have to declare a static context pointer here - means
  29. * you can't have multiple DBUS sessions open (not that this matters
  30. * much in practice). The problem is the DBUS API lacks some hook
  31. * arguments that it ought to have.
  32. */
  33. static struct gps_data_t *share_gpsdata;
  34. static DBusConnection *connection;
  35. static DBusHandlerResult handle_gps_fix(DBusMessage * message)
  36. {
  37. DBusError error;
  38. const char *gpsd_devname = NULL;
  39. double fix_time;
  40. dbus_error_init(&error);
  41. dbus_message_get_args(message,
  42. &error,
  43. DBUS_TYPE_DOUBLE, &fix_time,
  44. DBUS_TYPE_INT32, &share_gpsdata->fix.mode,
  45. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.ept,
  46. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.latitude,
  47. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.longitude,
  48. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.eph,
  49. /* The dbus doc does not seem to specify
  50. * altHAE or altMSL */
  51. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.altHAE,
  52. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.epv,
  53. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.track,
  54. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.epd,
  55. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.speed,
  56. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.eps,
  57. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.climb,
  58. DBUS_TYPE_DOUBLE, &share_gpsdata->fix.epc,
  59. DBUS_TYPE_STRING, &gpsd_devname, DBUS_TYPE_INVALID);
  60. // convert time as double back to timespec_t, potential loss of precision.
  61. DTOTS(&share_gpsdata->fix.time, fix_time);
  62. if (MODE_NO_FIX < share_gpsdata->fix.mode) {
  63. share_gpsdata->fix.status = STATUS_GPS;
  64. } else {
  65. share_gpsdata->fix.status = STATUS_UNK;
  66. }
  67. dbus_error_free(&error);
  68. PRIVATE(share_gpsdata)->handler(share_gpsdata);
  69. return DBUS_HANDLER_RESULT_HANDLED;
  70. }
  71. /*
  72. * Message dispatching function
  73. *
  74. */
  75. static DBusHandlerResult signal_handler(DBusConnection * connection UNUSED,
  76. DBusMessage * message,
  77. void *user_data UNUSED)
  78. {
  79. if (dbus_message_is_signal(message, "org.gpsd", "fix"))
  80. return handle_gps_fix(message);
  81. /*
  82. * ignore all other messages
  83. */
  84. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  85. }
  86. int gps_dbus_open(struct gps_data_t *gpsdata)
  87. {
  88. DBusError error;
  89. gpsdata->privdata = (void *)malloc(sizeof(struct privdata_t));
  90. if (NULL == gpsdata->privdata) {
  91. return -1;
  92. }
  93. dbus_error_init(&error);
  94. connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
  95. if (dbus_error_is_set(&error)) {
  96. syslog(LOG_CRIT, "%s: %s", error.name, error.message);
  97. dbus_error_free(&error);
  98. return 3;
  99. }
  100. dbus_bus_add_match(connection, "type='signal'", &error);
  101. if (dbus_error_is_set(&error)) {
  102. syslog(LOG_CRIT, "unable to add match for signals %s: %s", error.name,
  103. error.message);
  104. dbus_error_free(&error);
  105. return 4;
  106. }
  107. if (!dbus_connection_add_filter
  108. (connection, (DBusHandleMessageFunction) signal_handler, NULL,
  109. NULL)) {
  110. syslog(LOG_CRIT, "unable to register filter with the connection");
  111. return 5;
  112. }
  113. #ifndef USE_QT
  114. gpsdata->gps_fd = DBUS_PSEUDO_FD;
  115. #else
  116. gpsdata->gps_fd = (void *)(intptr_t)DBUS_PSEUDO_FD;
  117. #endif // USE_QT
  118. share_gpsdata = gpsdata;
  119. return 0;
  120. }
  121. /* run a DBUS main loop with a specified handler
  122. *
  123. * timeout is in micro seconds
  124. *
  125. * Returns: -1 on timeout
  126. * -2 on error or disconnect
  127. * FIXME: read error should return different than timeout
  128. */
  129. int gps_dbus_mainloop(struct gps_data_t *gpsdata,
  130. int timeout,
  131. void (*hook)(struct gps_data_t *))
  132. {
  133. struct timespec ts_from, ts_to;
  134. double d_timeout;
  135. d_timeout = (double)timeout / 1000000; // timeout in seconds
  136. share_gpsdata = gpsdata;
  137. PRIVATE(share_gpsdata)->handler = (void (*)(struct gps_data_t *))hook;
  138. for (;;) {
  139. bool status;
  140. double diff;
  141. if (0 != clock_gettime(CLOCK_REALTIME, &ts_from)) {
  142. return -2;
  143. }
  144. status = dbus_connection_read_write_dispatch(connection,
  145. (int)(timeout/1000));
  146. if (FALSE == status) {
  147. // lost connection
  148. break;
  149. }
  150. if (0 != clock_gettime(CLOCK_REALTIME, &ts_to)) {
  151. return -2;
  152. }
  153. diff = TS_SUB_D(&ts_to, &ts_from);
  154. if (d_timeout <= diff) {
  155. // timeout
  156. return -1;
  157. }
  158. }
  159. return -2;
  160. }
  161. #endif // defined(DBUS_EXPORT_ENABLE)
  162. // vim: set expandtab shiftwidth=4