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