1 /* libgps_core.c -- client interface library for the gpsd daemon
2  *
3  * Core portion of client library.  Cals helpers to handle different eports.
4  *
5  * This file is Copyright (c) 2010-2018 by the GPSD project
6  * SPDX-License-Identifier: BSD-2-clause
7  */
8 
9 #include "gpsd_config.h"  /* must be before all includes */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <stdarg.h>
19 
20 #include "gpsd.h"
21 #include "libgps.h"
22 #include "gps_json.h"
23 #include "strfuncs.h"
24 
25 #ifdef LIBGPS_DEBUG
26 int libgps_debuglevel = 0;
27 
28 static FILE *debugfp;
29 
gps_enable_debug(int level,FILE * fp)30 void gps_enable_debug(int level, FILE * fp)
31 /* control the level and destination of debug trace messages */
32 {
33     libgps_debuglevel = level;
34     debugfp = fp;
35 #if defined(CLIENTDEBUG_ENABLE) && defined(SOCKET_EXPORT_ENABLE)
36     json_enable_debug(level - DEBUG_JSON, fp);
37 #endif
38 }
39 
libgps_trace(int errlevel,const char * fmt,...)40 void libgps_trace(int errlevel, const char *fmt, ...)
41 /* assemble command in printf(3) style */
42 {
43     if (errlevel <= libgps_debuglevel) {
44 	char buf[BUFSIZ];
45 	va_list ap;
46 
47 	(void)strlcpy(buf, "libgps: ", sizeof(buf));
48 	va_start(ap, fmt);
49 	str_vappendf(buf, sizeof(buf), fmt, ap);
50 	va_end(ap);
51 
52 	(void)fputs(buf, debugfp);
53     }
54 }
55 #else
56 // Functions defined as so to furfil the API but otherwise do nothing when built with debug capability turned off
gps_enable_debug(int level UNUSED,FILE * fp UNUSED)57 void gps_enable_debug(int level UNUSED, FILE * fp UNUSED) {}
libgps_trace(int errlevel UNUSED,const char * fmt UNUSED,...)58 void libgps_trace(int errlevel UNUSED, const char *fmt UNUSED, ...){}
59 #endif /* LIBGPS_DEBUG */
60 
61 #ifdef SOCKET_EXPORT_ENABLE
62 #define CONDITIONALLY_UNUSED
63 #else
64 #define CONDITIONALLY_UNUSED UNUSED
65 #endif /* SOCKET_EXPORT_ENABLE */
66 
gps_open(const char * host,const char * port CONDITIONALLY_UNUSED,struct gps_data_t * gpsdata)67 int gps_open(const char *host,
68 	     const char *port CONDITIONALLY_UNUSED,
69 	     struct gps_data_t *gpsdata)
70 {
71     int status = -1;
72 
73     if (!gpsdata)
74 	return -1;
75 
76 #ifdef SHM_EXPORT_ENABLE
77     if (host != NULL && strcmp(host, GPSD_SHARED_MEMORY) == 0) {
78 	status = gps_shm_open(gpsdata);
79 	if (status == -1)
80 	    status = SHM_NOSHARED;
81 	else if (status == -2)
82 	    status = SHM_NOATTACH;
83     }
84 #define USES_HOST
85 #endif /* SHM_EXPORT_ENABLE */
86 
87 #ifdef DBUS_EXPORT_ENABLE
88     if (host != NULL && strcmp(host, GPSD_DBUS_EXPORT) == 0) {
89 	status = gps_dbus_open(gpsdata);
90 	if (status != 0)
91 	    status = DBUS_FAILURE;
92     }
93 #define USES_HOST
94 #endif /* DBUS_EXPORT_ENABLE */
95 
96 #ifdef SOCKET_EXPORT_ENABLE
97     if (status == -1) {
98         status = gps_sock_open(host, port, gpsdata);
99     }
100 #define USES_HOST
101 #endif /* SOCKET_EXPORT_ENABLE */
102 
103 #ifndef USES_HOST
104     (void)fprintf(stderr,
105                   "No methods available for connnecting to %s!\n",
106                   host);
107 #endif /* USES_HOST */
108 #undef USES_HOST
109 
110     gpsdata->set = 0;
111     gpsdata->status = STATUS_NO_FIX;
112     gpsdata->satellites_used = 0;
113     gps_clear_att(&(gpsdata->attitude));
114     gps_clear_dop(&(gpsdata->dop));
115     gps_clear_fix(&(gpsdata->fix));
116 
117     return status;
118 }
119 
120 #if defined(SHM_EXPORT_ENABLE) || defined(SOCKET_EXPORT_ENABLE)
121 #define CONDITIONALLY_UNUSED
122 #else
123 #define CONDITIONALLY_UNUSED	UNUSED
124 #endif
125 
gps_close(struct gps_data_t * gpsdata CONDITIONALLY_UNUSED)126 int gps_close(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED)
127 /* close a gpsd connection */
128 {
129     int status = -1;
130 
131     libgps_debug_trace((DEBUG_CALLS, "gps_close()\n"));
132 
133 #ifdef SHM_EXPORT_ENABLE
134     if (BAD_SOCKET((intptr_t)(gpsdata->gps_fd))) {
135 	gps_shm_close(gpsdata);
136 	status = 0;
137     }
138 #endif /* SHM_EXPORT_ENABLE */
139 
140 #ifdef SOCKET_EXPORT_ENABLE
141     if (status == -1) {
142         status = gps_sock_close(gpsdata);
143     }
144 #endif /* SOCKET_EXPORT_ENABLE */
145 
146 	return status;
147 }
148 
149 /* read from a gpsd connection
150  *
151  * parameters:
152  *    gps_data_t *gpsdata   -- structure for GPS data
153  *    char *message         -- NULL, or optional buffer for received JSON
154  *    int message_len       -- zero, or sizeof(message)
155  */
gps_read(struct gps_data_t * gpsdata CONDITIONALLY_UNUSED,char * message,int message_len)156 int gps_read(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED,
157              char *message, int message_len)
158 {
159     int status = -1;
160 
161     libgps_debug_trace((DEBUG_CALLS, "gps_read() begins\n"));
162     if ((NULL != message) && (0 < message_len)) {
163         /* be sure message is zero length */
164         /* we do not memset() as this is time critical input path */
165         *message = '\0';
166     }
167 
168 #ifdef SHM_EXPORT_ENABLE
169     if (BAD_SOCKET((intptr_t)(gpsdata->gps_fd))) {
170 	status = gps_shm_read(gpsdata);
171     }
172 #endif /* SHM_EXPORT_ENABLE */
173 
174 #ifdef SOCKET_EXPORT_ENABLE
175     if (status == -1 && !BAD_SOCKET((intptr_t)(gpsdata->gps_fd))) {
176         status = gps_sock_read(gpsdata, message, message_len);
177     }
178 #endif /* SOCKET_EXPORT_ENABLE */
179 
180     libgps_debug_trace((DEBUG_CALLS, "gps_read() -> %d (%s)\n",
181 			status, gps_maskdump(gpsdata->set)));
182 
183     return status;
184 }
185 
gps_send(struct gps_data_t * gpsdata CONDITIONALLY_UNUSED,const char * fmt CONDITIONALLY_UNUSED,...)186 int gps_send(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED, const char *fmt CONDITIONALLY_UNUSED, ...)
187 /* send a command to the gpsd instance */
188 {
189     int status = -1;
190     char buf[BUFSIZ];
191     va_list ap;
192 
193     va_start(ap, fmt);
194     (void)vsnprintf(buf, sizeof(buf) - 2, fmt, ap);
195     va_end(ap);
196     if (buf[strlen(buf) - 1] != '\n')
197 	(void)strlcat(buf, "\n", sizeof(buf));
198 
199 #ifdef SOCKET_EXPORT_ENABLE
200     status = gps_sock_send(gpsdata, buf);
201 #endif /* SOCKET_EXPORT_ENABLE */
202 
203     return status;
204 }
205 
gps_stream(struct gps_data_t * gpsdata CONDITIONALLY_UNUSED,unsigned int flags CONDITIONALLY_UNUSED,void * d CONDITIONALLY_UNUSED)206 int gps_stream(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED,
207 	unsigned int flags CONDITIONALLY_UNUSED,
208 	void *d CONDITIONALLY_UNUSED)
209 {
210     int status = -1;
211 
212 #ifdef SOCKET_EXPORT_ENABLE
213     /* cppcheck-suppress redundantAssignment */
214     status = gps_sock_stream(gpsdata, flags, d);
215 #endif /* SOCKET_EXPORT_ENABLE */
216 
217     return status;
218 }
219 
gps_data(const struct gps_data_t * gpsdata CONDITIONALLY_UNUSED)220 const char *gps_data(const struct gps_data_t *gpsdata CONDITIONALLY_UNUSED)
221 /* return the contents of the client data buffer */
222 {
223     const char *bufp = NULL;
224 
225 #ifdef SOCKET_EXPORT_ENABLE
226     bufp = gps_sock_data(gpsdata);
227 #endif /* SOCKET_EXPORT_ENABLE */
228 
229     return bufp;
230 }
231 
gps_waiting(const struct gps_data_t * gpsdata CONDITIONALLY_UNUSED,int timeout CONDITIONALLY_UNUSED)232 bool gps_waiting(const struct gps_data_t *gpsdata CONDITIONALLY_UNUSED, int timeout CONDITIONALLY_UNUSED)
233 /* is there input waiting from the GPS? */
234 /* timeout is in uSec */
235 {
236     /* this is bogus, but I can't think of a better solution yet */
237     bool waiting = true;
238 
239 #ifdef SHM_EXPORT_ENABLE
240     if ((intptr_t)(gpsdata->gps_fd) == SHM_PSEUDO_FD)
241 	waiting = gps_shm_waiting(gpsdata, timeout);
242 #endif /* SHM_EXPORT_ENABLE */
243 
244 #ifdef SOCKET_EXPORT_ENABLE
245     // cppcheck-suppress pointerPositive
246     if ((intptr_t)(gpsdata->gps_fd) >= 0)
247 	waiting = gps_sock_waiting(gpsdata, timeout);
248 #endif /* SOCKET_EXPORT_ENABLE */
249 
250     return waiting;
251 }
252 
gps_mainloop(struct gps_data_t * gpsdata CONDITIONALLY_UNUSED,int timeout CONDITIONALLY_UNUSED,void (* hook)(struct gps_data_t * gpsdata)CONDITIONALLY_UNUSED)253 int gps_mainloop(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED,
254 		 int timeout CONDITIONALLY_UNUSED,
255 		 void (*hook)(struct gps_data_t *gpsdata) CONDITIONALLY_UNUSED)
256 {
257     int status = -1;
258 
259     libgps_debug_trace((DEBUG_CALLS, "gps_mainloop() begins\n"));
260 
261 #ifdef SHM_EXPORT_ENABLE
262     if ((intptr_t)(gpsdata->gps_fd) == SHM_PSEUDO_FD)
263 	status = gps_shm_mainloop(gpsdata, timeout, hook);
264 #endif /* SHM_EXPORT_ENABLE */
265 #ifdef DBUS_EXPORT_ENABLE
266     if ((intptr_t)(gpsdata->gps_fd) == DBUS_PSEUDO_FD)
267 	status = gps_dbus_mainloop(gpsdata, timeout, hook);
268 #endif /* DBUS_EXPORT_ENABLE */
269 #ifdef SOCKET_EXPORT_ENABLE
270     if ((intptr_t)(gpsdata->gps_fd) >= 0)
271 	status = gps_sock_mainloop(gpsdata, timeout, hook);
272 #endif /* SOCKET_EXPORT_ENABLE */
273 
274     libgps_debug_trace((DEBUG_CALLS, "gps_mainloop() -> %d (%s)\n",
275 			status, gps_maskdump(gpsdata->set)));
276 
277     return status;
278 }
279 
gps_errstr(const int err)280 extern const char *gps_errstr(const int err)
281 {
282     /*
283      * We might add our own error codes in the future, e.g for
284      * protocol compatibility checks
285      */
286 #ifndef USE_QT
287 #ifdef SHM_EXPORT_ENABLE
288     if (err == SHM_NOSHARED)
289 	return "no shared-memory segment or daemon not running";
290     else if (err == SHM_NOATTACH)
291 	return "attach failed for unknown reason";
292 #endif /* SHM_EXPORT_ENABLE */
293 #ifdef DBUS_EXPORT_ENABLE
294     if (err == DBUS_FAILURE)
295 	return "DBUS initialization failure";
296 #endif /* DBUS_EXPORT_ENABLE */
297     return netlib_errstr(err);
298 #else
299     static char buf[32];
300     (void)snprintf(buf, sizeof(buf), "Qt error %d", err);
301     return buf;
302 #endif
303 }
304 
305 #ifdef LIBGPS_DEBUG
libgps_dump_state(struct gps_data_t * collect)306 void libgps_dump_state(struct gps_data_t *collect)
307 {
308     char ts_buf[TIMESPEC_LEN];
309 
310     /* no need to dump the entire state, this is a sanity check */
311 #ifndef USE_QT
312     (void)fprintf(debugfp, "flags: (0x%04x) %s\n",
313 		  (unsigned int)collect->set, gps_maskdump(collect->set));
314 #endif
315     if (collect->set & ONLINE_SET)
316 	(void)fprintf(debugfp, "ONLINE: %s\n",
317                       timespec_str(&collect->online, ts_buf, sizeof(ts_buf)));
318     if (collect->set & TIME_SET)
319 	(void)fprintf(debugfp, "TIME: %s\n",
320                      timespec_str(&collect->fix.time, ts_buf, sizeof(ts_buf)));
321     /* NOTE: %.7f needed for cm level accurate GPS */
322     if (collect->set & LATLON_SET)
323 	(void)fprintf(debugfp, "LATLON: lat/lon: %.7lf %.7lf\n",
324 		      collect->fix.latitude, collect->fix.longitude);
325     if (collect->set & ALTITUDE_SET)
326 	(void)fprintf(debugfp, "ALTITUDE: altHAE: %lf  U: climb: %lf\n",
327 		      collect->fix.altHAE, collect->fix.climb);
328     if (collect->set & SPEED_SET)
329 	(void)fprintf(debugfp, "SPEED: %lf\n", collect->fix.speed);
330     if (collect->set & TRACK_SET)
331 	(void)fprintf(debugfp, "TRACK: track: %lf\n", collect->fix.track);
332     if (collect->set & MAGNETIC_TRACK_SET)
333         (void)fprintf(debugfp, "MAGNETIC_TRACK: magtrack: %lf\n",
334                       collect->fix.magnetic_track);
335     if (collect->set & CLIMB_SET)
336 	(void)fprintf(debugfp, "CLIMB: climb: %lf\n", collect->fix.climb);
337     if (collect->set & STATUS_SET) {
338 	const char *status_values[] = { "NO_FIX", "FIX", "DGPS_FIX" };
339 	(void)fprintf(debugfp, "STATUS: status: %d (%s)\n",
340 		      collect->status, status_values[collect->status]);
341     }
342     if (collect->set & MODE_SET) {
343 	const char *mode_values[] = { "", "NO_FIX", "MODE_2D", "MODE_3D" };
344 	(void)fprintf(debugfp, "MODE: mode: %d (%s)\n",
345 		      collect->fix.mode, mode_values[collect->fix.mode]);
346     }
347     if (collect->set & DOP_SET)
348 	(void)fprintf(debugfp,
349 		      "DOP: satellites %d, pdop=%lf, hdop=%lf, vdop=%lf\n",
350 		      collect->satellites_used, collect->dop.pdop,
351 		      collect->dop.hdop, collect->dop.vdop);
352     if (collect->set & VERSION_SET)
353 	(void)fprintf(debugfp, "VERSION: release=%s rev=%s proto=%d.%d\n",
354 		      collect->version.release,
355 		      collect->version.rev,
356 		      collect->version.proto_major,
357 		      collect->version.proto_minor);
358     if (collect->set & POLICY_SET)
359 	(void)fprintf(debugfp,
360 		      "POLICY: watcher=%s nmea=%s raw=%d scaled=%s timing=%s, split24=%s pps=%s, devpath=%s\n",
361 		      collect->policy.watcher ? "true" : "false",
362 		      collect->policy.nmea ? "true" : "false",
363 		      collect->policy.raw,
364 		      collect->policy.scaled ? "true" : "false",
365 		      collect->policy.timing ? "true" : "false",
366 		      collect->policy.split24 ? "true" : "false",
367 		      collect->policy.pps ? "true" : "false",
368 		      collect->policy.devpath);
369     if (collect->set & SATELLITE_SET) {
370 	struct satellite_t *sp;
371 
372 	(void)fprintf(debugfp, "SKY: satellites in view: %d\n",
373 		      collect->satellites_visible);
374 	for (sp = collect->skyview;
375 	     sp < collect->skyview + collect->satellites_visible;
376 	     sp++) {
377 	    (void)fprintf(debugfp, "  %2.2d: %4.1f %5.1f %3.0f %c\n",
378 			  sp->PRN, sp->elevation,
379 			  sp->azimuth, sp->ss,
380 			  sp->used ? 'Y' : 'N');
381 	}
382     }
383     if (collect->set & RAW_SET)
384 	(void)fprintf(debugfp, "RAW: got raw data\n");
385     if (collect->set & DEVICE_SET)
386 	(void)fprintf(debugfp, "DEVICE: Device is '%s', driver is '%s'\n",
387 		      collect->dev.path, collect->dev.driver);
388     if (collect->set & DEVICELIST_SET) {
389 	int i;
390 	(void)fprintf(debugfp, "DEVICELIST:%d devices:\n",
391 		      collect->devices.ndevices);
392 	for (i = 0; i < collect->devices.ndevices; i++) {
393 	    (void)fprintf(debugfp, "%d: path='%s' driver='%s'\n",
394 			  collect->devices.ndevices,
395 			  collect->devices.list[i].path,
396 			  collect->devices.list[i].driver);
397 	}
398     }
399 
400 }
401 #endif /* LIBGPS_DEBUG */
402 
403 // end
404