| /* libgps_core.c -- client interface library for the gpsd daemon |
| * |
| * Core portion of client library. Cals helpers to handle different eports. |
| * |
| * This file is Copyright (c) 2010 by the GPSD project |
| * BSD terms apply: see the file COPYING in the distribution root for details. |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| |
| #include "gpsd.h" |
| #include "libgps.h" |
| #include "gps_json.h" |
| #include "strfuncs.h" |
| |
| #ifdef LIBGPS_DEBUG |
| int libgps_debuglevel = 0; |
| |
| static FILE *debugfp; |
| |
| void gps_enable_debug(int level, FILE * fp) |
| /* control the level and destination of debug trace messages */ |
| { |
| libgps_debuglevel = level; |
| debugfp = fp; |
| #if defined(CLIENTDEBUG_ENABLE) && defined(SOCKET_EXPORT_ENABLE) |
| json_enable_debug(level - DEBUG_JSON, fp); |
| #endif |
| } |
| |
| void libgps_trace(int errlevel, const char *fmt, ...) |
| /* assemble command in printf(3) style */ |
| { |
| if (errlevel <= libgps_debuglevel) { |
| char buf[BUFSIZ]; |
| va_list ap; |
| |
| (void)strlcpy(buf, "libgps: ", sizeof(buf)); |
| va_start(ap, fmt); |
| str_vappendf(buf, sizeof(buf), fmt, ap); |
| va_end(ap); |
| |
| (void)fputs(buf, debugfp); |
| } |
| } |
| #endif /* LIBGPS_DEBUG */ |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| #define CONDITIONALLY_UNUSED |
| #else |
| #define CONDITIONALLY_UNUSED UNUSED |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| int gps_open(/*@null@*/const char *host, |
| /*@null@*/const char *port CONDITIONALLY_UNUSED, |
| /*@out@*/ struct gps_data_t *gpsdata) |
| { |
| int status = -1; |
| |
| /*@ -branchstate -compdef @*/ |
| if (!gpsdata) |
| return -1; |
| |
| #ifdef SHM_EXPORT_ENABLE |
| if (host != NULL && strcmp(host, GPSD_SHARED_MEMORY) == 0) { |
| status = gps_shm_open(gpsdata); |
| if (status == -1) |
| status = SHM_NOSHARED; |
| else if (status == -2) |
| status = SHM_NOATTACH; |
| } |
| #define USES_HOST |
| #endif /* SHM_EXPORT_ENABLE */ |
| |
| #ifdef DBUS_EXPORT_ENABLE |
| if (host != NULL && strcmp(host, GPSD_DBUS_EXPORT) == 0) { |
| /*@i@*/status = gps_dbus_open(gpsdata); |
| if (status != 0) |
| status = DBUS_FAILURE; |
| } |
| #define USES_HOST |
| #endif /* DBUS_EXPORT_ENABLE */ |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| if (status == -1) { |
| status = gps_sock_open(host, port, gpsdata); |
| } |
| #define USES_HOST |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| #ifndef USES_HOST |
| fprintf(stderr, "No methods available for connnecting to %s!\n", host); |
| #endif /* USES_HOST */ |
| #undef USES_HOST |
| |
| gpsdata->set = 0; |
| gpsdata->status = STATUS_NO_FIX; |
| gpsdata->satellites_used = 0; |
| gps_clear_fix(&(gpsdata->fix)); |
| gps_clear_dop(&(gpsdata->dop)); |
| |
| return status; |
| /*@ +branchstate +compdef @*/ |
| } |
| |
| #if defined(SHM_EXPORT_ENABLE) || defined(SOCKET_EXPORT_ENABLE) |
| #define CONDITIONALLY_UNUSED |
| #else |
| #define CONDITIONALLY_UNUSED UNUSED |
| #endif |
| |
| int gps_close(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED) |
| /* close a gpsd connection */ |
| { |
| int status = -1; |
| |
| libgps_debug_trace((DEBUG_CALLS, "gps_close()\n")); |
| |
| #ifdef SHM_EXPORT_ENABLE |
| if (BAD_SOCKET((intptr_t)(gpsdata->gps_fd))) { |
| gps_shm_close(gpsdata); |
| status = 0; |
| } |
| #endif /* SHM_EXPORT_ENABLE */ |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| if (status == -1) { |
| status = gps_sock_close(gpsdata); |
| } |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| return status; |
| } |
| |
| int gps_read(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED) |
| /* read from a gpsd connection */ |
| { |
| int status = -1; |
| |
| libgps_debug_trace((DEBUG_CALLS, "gps_read() begins\n")); |
| |
| /*@ -usedef -compdef -uniondef @*/ |
| #ifdef SHM_EXPORT_ENABLE |
| if (BAD_SOCKET((intptr_t)(gpsdata->gps_fd))) { |
| status = gps_shm_read(gpsdata); |
| } |
| #endif /* SHM_EXPORT_ENABLE */ |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| if (status == -1 && !BAD_SOCKET((intptr_t)(gpsdata->gps_fd))) { |
| status = gps_sock_read(gpsdata); |
| } |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| /*@ +usedef +compdef +uniondef @*/ |
| |
| /*@-usedef@*/ |
| libgps_debug_trace((DEBUG_CALLS, "gps_read() -> %d (%s)\n", |
| status, gps_maskdump(gpsdata->set))); |
| /*@+usedef@*/ |
| |
| return status; |
| } |
| |
| int gps_send(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED, const char *fmt CONDITIONALLY_UNUSED, ...) |
| /* send a command to the gpsd instance */ |
| { |
| int status = -1; |
| char buf[BUFSIZ]; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| (void)vsnprintf(buf, sizeof(buf) - 2, fmt, ap); |
| va_end(ap); |
| if (buf[strlen(buf) - 1] != '\n') |
| (void)strlcat(buf, "\n", sizeof(buf)); |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| status = gps_sock_send(gpsdata, buf); |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| return status; |
| } |
| |
| int gps_stream(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED, |
| unsigned int flags CONDITIONALLY_UNUSED, |
| /*@null@*/ void *d CONDITIONALLY_UNUSED) |
| { |
| int status = -1; |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| /* cppcheck-suppress redundantAssignment */ |
| status = gps_sock_stream(gpsdata, flags, d); |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| return status; |
| } |
| |
| const char /*@null observer@*/ *gps_data(const struct gps_data_t *gpsdata CONDITIONALLY_UNUSED) |
| /* return the contents of the client data buffer */ |
| { |
| /*@-dependenttrans -observertrans@*/ |
| const char *bufp = NULL; |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| bufp = gps_sock_data(gpsdata); |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| return bufp; |
| /*@+dependenttrans +observertrans@*/ |
| } |
| |
| bool gps_waiting(const struct gps_data_t *gpsdata CONDITIONALLY_UNUSED, int timeout CONDITIONALLY_UNUSED) |
| /* is there input waiting from the GPS? */ |
| { |
| /* this is bogus, but I can't think of a better solution yet */ |
| bool waiting = true; |
| |
| #ifdef SHM_EXPORT_ENABLE |
| if ((intptr_t)(gpsdata->gps_fd) == SHM_PSEUDO_FD) |
| waiting = gps_shm_waiting(gpsdata, timeout); |
| #endif /* SHM_EXPORT_ENABLE */ |
| |
| #ifdef SOCKET_EXPORT_ENABLE |
| // cppcheck-suppress pointerPositive |
| if ((intptr_t)(gpsdata->gps_fd) >= 0) |
| waiting = gps_sock_waiting(gpsdata, timeout); |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| |
| return waiting; |
| } |
| |
| int gps_mainloop(struct gps_data_t *gpsdata CONDITIONALLY_UNUSED, |
| int timeout CONDITIONALLY_UNUSED, |
| void (*hook)(struct gps_data_t *gpsdata) CONDITIONALLY_UNUSED) |
| { |
| int status = -1; |
| |
| libgps_debug_trace((DEBUG_CALLS, "gps_mainloop() begins\n")); |
| |
| /*@ -usedef -compdef -uniondef @*/ |
| #ifdef SHM_EXPORT_ENABLE |
| if ((intptr_t)(gpsdata->gps_fd) == SHM_PSEUDO_FD) |
| status = gps_shm_mainloop(gpsdata, timeout, hook); |
| #endif /* SHM_EXPORT_ENABLE */ |
| #ifdef DBUS_EXPORT_ENABLE |
| if ((intptr_t)(gpsdata->gps_fd) == DBUS_PSEUDO_FD) |
| status = gps_dbus_mainloop(gpsdata, timeout, hook); |
| #endif /* DBUS_EXPORT_ENABLE */ |
| #ifdef SOCKET_EXPORT_ENABLE |
| if ((intptr_t)(gpsdata->gps_fd) >= 0) |
| status = gps_sock_mainloop(gpsdata, timeout, hook); |
| #endif /* SOCKET_EXPORT_ENABLE */ |
| /*@ +usedef +compdef +uniondef @*/ |
| |
| libgps_debug_trace((DEBUG_CALLS, "gps_mainloop() -> %d (%s)\n", |
| status, gps_maskdump(gpsdata->set))); |
| |
| return status; |
| } |
| |
| extern const char /*@observer@*/ *gps_errstr(const int err) |
| { |
| /* |
| * We might add our own error codes in the future, e.g for |
| * protocol compatibility checks |
| */ |
| #ifndef USE_QT |
| #ifdef SHM_EXPORT_ENABLE |
| if (err == SHM_NOSHARED) |
| return "no shared-memory segment or daemon not running"; |
| else if (err == SHM_NOATTACH) |
| return "attach failed for unknown reason"; |
| #endif /* SHM_EXPORT_ENABLE */ |
| #ifdef DBUS_EXPORT_ENABLE |
| if (err == DBUS_FAILURE) |
| return "DBUS initialization failure"; |
| #endif /* DBUS_EXPORT_ENABLE */ |
| return netlib_errstr(err); |
| #else |
| static char buf[32]; |
| (void)snprintf(buf, sizeof(buf), "Qt error %d", err); |
| return buf; |
| #endif |
| } |
| |
| #ifdef LIBGPS_DEBUG |
| void libgps_dump_state(struct gps_data_t *collect) |
| { |
| /* no need to dump the entire state, this is a sanity check */ |
| #ifndef USE_QT |
| /* will fail on a 32-bit machine */ |
| (void)fprintf(debugfp, "flags: (0x%04x) %s\n", |
| (unsigned int)collect->set, gps_maskdump(collect->set)); |
| #endif |
| if (collect->set & ONLINE_SET) |
| (void)fprintf(debugfp, "ONLINE: %lf\n", collect->online); |
| if (collect->set & TIME_SET) |
| (void)fprintf(debugfp, "TIME: %lf\n", collect->fix.time); |
| if (collect->set & LATLON_SET) |
| (void)fprintf(debugfp, "LATLON: lat/lon: %lf %lf\n", |
| collect->fix.latitude, collect->fix.longitude); |
| if (collect->set & ALTITUDE_SET) |
| (void)fprintf(debugfp, "ALTITUDE: altitude: %lf U: climb: %lf\n", |
| collect->fix.altitude, collect->fix.climb); |
| if (collect->set & SPEED_SET) |
| (void)fprintf(debugfp, "SPEED: %lf\n", collect->fix.speed); |
| if (collect->set & TRACK_SET) |
| (void)fprintf(debugfp, "TRACK: track: %lf\n", collect->fix.track); |
| if (collect->set & CLIMB_SET) |
| (void)fprintf(debugfp, "CLIMB: climb: %lf\n", collect->fix.climb); |
| if (collect->set & STATUS_SET) { |
| const char *status_values[] = { "NO_FIX", "FIX", "DGPS_FIX" }; |
| (void)fprintf(debugfp, "STATUS: status: %d (%s)\n", |
| collect->status, status_values[collect->status]); |
| } |
| if (collect->set & MODE_SET) { |
| const char *mode_values[] = { "", "NO_FIX", "MODE_2D", "MODE_3D" }; |
| (void)fprintf(debugfp, "MODE: mode: %d (%s)\n", |
| collect->fix.mode, mode_values[collect->fix.mode]); |
| } |
| if (collect->set & DOP_SET) |
| (void)fprintf(debugfp, |
| "DOP: satellites %d, pdop=%lf, hdop=%lf, vdop=%lf\n", |
| collect->satellites_used, collect->dop.pdop, |
| collect->dop.hdop, collect->dop.vdop); |
| if (collect->set & VERSION_SET) |
| (void)fprintf(debugfp, "VERSION: release=%s rev=%s proto=%d.%d\n", |
| collect->version.release, |
| collect->version.rev, |
| collect->version.proto_major, |
| collect->version.proto_minor); |
| if (collect->set & POLICY_SET) |
| (void)fprintf(debugfp, |
| "POLICY: watcher=%s nmea=%s raw=%d scaled=%s timing=%s, split24=%s pps=%s, devpath=%s\n", |
| collect->policy.watcher ? "true" : "false", |
| collect->policy.nmea ? "true" : "false", |
| collect->policy.raw, |
| collect->policy.scaled ? "true" : "false", |
| collect->policy.timing ? "true" : "false", |
| collect->policy.split24 ? "true" : "false", |
| collect->policy.pps ? "true" : "false", |
| collect->policy.devpath); |
| if (collect->set & SATELLITE_SET) { |
| struct satellite_t *sp; |
| |
| (void)fprintf(debugfp, "SKY: satellites in view: %d\n", |
| collect->satellites_visible); |
| for (sp = collect->skyview; |
| sp < collect->skyview + collect->satellites_visible; |
| sp++) { |
| (void)fprintf(debugfp, " %2.2d: %2.2d %3.3d %3.0f %c\n", |
| sp->PRN, sp->elevation, |
| sp->azimuth, sp->ss, |
| sp->used ? 'Y' : 'N'); |
| } |
| } |
| if (collect->set & DEVICE_SET) |
| (void)fprintf(debugfp, "DEVICE: Device is '%s', driver is '%s'\n", |
| collect->dev.path, collect->dev.driver); |
| if (collect->set & DEVICELIST_SET) { |
| int i; |
| (void)fprintf(debugfp, "DEVICELIST:%d devices:\n", |
| collect->devices.ndevices); |
| for (i = 0; i < collect->devices.ndevices; i++) { |
| (void)fprintf(debugfp, "%d: path='%s' driver='%s'\n", |
| collect->devices.ndevices, |
| collect->devices.list[i].path, |
| collect->devices.list[i].driver); |
| } |
| } |
| |
| } |
| #endif /* LIBGPS_DEBUG */ |
| |
| // end |