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