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