libgps_core.c 13 KB

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