gpsmon.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
  1. /*
  2. * The generic GPS packet monitor.
  3. *
  4. * This file is Copyright 2010 by the GPSD project
  5. * SPDX-License-Identifier: BSD-2-clause
  6. */
  7. #include "../include/gpsd_config.h" /* must be before all includes */
  8. #include <assert.h>
  9. #include <ctype.h>
  10. #include <errno.h>
  11. #include <fcntl.h>
  12. #ifdef HAVE_GETOPT_LONG
  13. #include <getopt.h>
  14. #endif
  15. #include <math.h>
  16. #include <setjmp.h>
  17. #include <signal.h>
  18. #include <stdarg.h>
  19. #include <stdbool.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <sys/select.h>
  24. #include <sys/stat.h>
  25. #include <sys/types.h>
  26. #include <time.h>
  27. #include <unistd.h>
  28. #include "../include/compiler.h" // for FALLTHROUGH
  29. #include "../include/gpsdclient.h"
  30. #include "../include/gpsd.h"
  31. #include "../include/gps_json.h"
  32. #include "../include/gpsmon.h"
  33. #include "../include/strfuncs.h"
  34. #include "../include/timespec.h"
  35. #define BUFLEN 2048
  36. // needed under FreeBSD
  37. #ifndef HOST_NAME_MAX
  38. #define HOST_NAME_MAX 255
  39. #endif // HOST_NAME_MAX
  40. // external capability tables
  41. extern struct monitor_object_t nmea_mmt, sirf_mmt, ashtech_mmt;
  42. extern struct monitor_object_t garmin_mmt, garmin_bin_ser_mmt;
  43. extern struct monitor_object_t italk_mmt, ubx_mmt, superstar2_mmt;
  44. extern struct monitor_object_t fv18_mmt, gpsclock_mmt, mtk3301_mmt;
  45. extern struct monitor_object_t oncore_mmt, tnt_mmt, aivdm_mmt;
  46. extern const struct gps_type_t driver_nmea0183;
  47. // These are public
  48. struct gps_device_t session;
  49. WINDOW *devicewin;
  50. bool serial;
  51. // These are private
  52. static struct gps_context_t context;
  53. static bool curses_active;
  54. static WINDOW *statwin, *cmdwin;
  55. static WINDOW *packetwin;
  56. static FILE *logfile;
  57. static char *type_name = "Unknown device";
  58. static size_t promptlen = 0;
  59. static struct termios cooked, rare;
  60. static struct fixsource_t source;
  61. static char hostname[HOST_NAME_MAX];
  62. static struct timedelta_t time_offset;
  63. /* no methods, it's all device window */
  64. extern const struct gps_type_t driver_json_passthrough;
  65. const struct monitor_object_t json_mmt = {
  66. .initialize = NULL,
  67. .update = NULL,
  68. .command = NULL,
  69. .wrap = NULL,
  70. .min_y = 0, .min_x = 80, /* no need for a device window */
  71. .driver = &driver_json_passthrough,
  72. };
  73. static const struct monitor_object_t *monitor_objects[] = {
  74. &nmea_mmt,
  75. #if defined(GARMIN_ENABLE)
  76. &garmin_mmt,
  77. #endif // GARMIN_ENABLE
  78. #if defined(GARMIN_ENABLE) && defined(BINARY_ENABLE)
  79. &garmin_bin_ser_mmt,
  80. #endif // defined(GARMIN_ENABLE) && defined(BINARY_ENABLE)
  81. &ashtech_mmt,
  82. #ifdef FV18_ENABLE
  83. &fv18_mmt,
  84. #endif // FV18_ENABLE
  85. #ifdef GPSCLOCK_ENABLE
  86. &gpsclock_mmt,
  87. #endif // GPSCLOCK_ENABLE
  88. &mtk3301_mmt,
  89. #ifdef AIVDM_ENABLE
  90. &aivdm_mmt,
  91. #endif // AIVDM_ENABLE
  92. #if defined(SIRF_ENABLE) && defined(BINARY_ENABLE)
  93. &sirf_mmt,
  94. #endif // defined(SIRF_ENABLE) && defined(BINARY_ENABLE)
  95. #if defined(UBLOX_ENABLE) && defined(BINARY_ENABLE)
  96. &ubx_mmt,
  97. #endif // defined(UBLOX_ENABLE) && defined(BINARY_ENABLE)
  98. #if defined(ITRAX_ENABLE) && defined(BINARY_ENABLE)
  99. &italk_mmt,
  100. #endif // defined(ITALK_ENABLE) && defined(BINARY_ENABLE)
  101. #if defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE)
  102. &superstar2_mmt,
  103. #endif // defined(SUPERSTAR2_ENABLE) && defined(BINARY_ENABLE)
  104. #if defined(ONCORE_ENABLE) && defined(BINARY_ENABLE)
  105. &oncore_mmt,
  106. #endif // defined(ONCORE_ENABLE) && defined(BINARY_ENABLE)
  107. #ifdef TNT_ENABLE
  108. &tnt_mmt,
  109. #endif // TNT_ENABLE
  110. &json_mmt,
  111. NULL,
  112. };
  113. static const struct monitor_object_t **active;
  114. static const struct gps_type_t *fallback;
  115. static jmp_buf terminate;
  116. #define display (void)mvwprintw
  117. /* termination codes */
  118. #define TERM_SELECT_FAILED 1
  119. #define TERM_DRIVER_SWITCH 2
  120. #define TERM_EMPTY_READ 3
  121. #define TERM_READ_ERROR 4
  122. #define TERM_SIGNAL 5
  123. #define TERM_QUIT 6
  124. /* PPS monitoring */
  125. // FIXME: Lock what? Why? Where?
  126. static inline void report_lock(void)
  127. {
  128. // FIXME: gpsmon, a client, should not link to the gpsd server sources!
  129. gpsd_acquire_reporting_lock();
  130. }
  131. static inline void report_unlock(void)
  132. {
  133. // FIXME: gpsmon, a client, should not link to the gpsd server sources!
  134. gpsd_release_reporting_lock();
  135. }
  136. #define PPSBAR "-------------------------------------" \
  137. " PPS " \
  138. "-------------------------------------\n"
  139. /* Dummy conditional for *display* of (possibly remote) PPS events */
  140. #define PPS_DISPLAY_ENABLE 1
  141. /******************************************************************************
  142. *
  143. * Visualization helpers
  144. *
  145. ******************************************************************************/
  146. static void visibilize(char *buf2, size_t len2, const char *buf)
  147. /* string is mostly printable, dress up the nonprintables a bit */
  148. {
  149. const char *sp;
  150. buf2[0] = '\0';
  151. for (sp = buf; *sp != '\0' && strlen(buf2)+4 < len2; sp++)
  152. if (isprint((unsigned char) *sp) || (sp[0] == '\n' && sp[1] == '\0')
  153. || (sp[0] == '\r' && sp[2] == '\0'))
  154. (void)snprintf(buf2 + strlen(buf2), 2, "%c", *sp);
  155. else
  156. (void)snprintf(buf2 + strlen(buf2), 6, "\\x%02x",
  157. (unsigned)(*sp & 0xff));
  158. }
  159. static void cond_hexdump(char *buf2, size_t len2,
  160. const char *buf, size_t len)
  161. /* pass through visibilized if all printable, hexdump otherwise */
  162. {
  163. size_t i;
  164. bool printable = true;
  165. for (i = 0; i < len; i++)
  166. if (!isprint((unsigned char) buf[i]) && !isspace((unsigned char) buf[i]))
  167. printable = false;
  168. if (printable) {
  169. size_t j;
  170. for (i = j = 0; i < len && j < len2 - 1; i++)
  171. if (isprint((unsigned char) buf[i])) {
  172. buf2[j++] = buf[i];
  173. buf2[j] = '\0';
  174. }
  175. else {
  176. if (TEXTUAL_PACKET_TYPE(session.lexer.type)) {
  177. if (i == len - 1 && buf[i] == '\n')
  178. continue;
  179. if (i == len - 2 && buf[i] == '\r')
  180. continue;
  181. }
  182. (void)snprintf(&buf2[j], len2-strlen(buf2), "\\x%02x", (unsigned int)(buf[i] & 0xff));
  183. j = strlen(buf2);
  184. }
  185. } else {
  186. buf2[0] = '\0';
  187. for (i = 0; i < len; i++)
  188. str_appendf(buf2, len2, "%02x", (unsigned int)(buf[i] & 0xff));
  189. }
  190. }
  191. void toff_update(WINDOW *win, int y, int x)
  192. {
  193. assert(win != NULL);
  194. if (time_offset.real.tv_sec != 0)
  195. {
  196. /* NOTE: can not use double here due to precision requirements */
  197. struct timespec timedelta;
  198. int i, ymax, xmax;
  199. getmaxyx(win, ymax, xmax);
  200. assert(ymax > 0); /* squash a compiler warning */
  201. (void)wmove(win, y, x);
  202. /*
  203. * The magic number 18 shortening the field works because
  204. * we know we'll never see more than 5 digits of seconds
  205. * rather than 10 (because we don't print values of
  206. * 86400 seconds or greater in numerical form).
  207. */
  208. for (i = 0; i < 18 && x + i < xmax - 1; i++)
  209. (void)waddch(win, ' ');
  210. TS_SUB(&timedelta, &time_offset.clock, &time_offset.real);
  211. // (long long) for 32-bit CPU with 64-bit time_t
  212. if ( 86400 < llabs(timedelta.tv_sec) ) {
  213. /* more than one day off, overflow */
  214. /* need a bigger field to show it */
  215. (void)mvwaddstr(win, y, x, "> 1 day");
  216. } else {
  217. char buf[TIMESPEC_LEN];
  218. (void)mvwaddstr(win, y, x,
  219. timespec_str(&timedelta, buf, sizeof(buf)));
  220. }
  221. }
  222. }
  223. /* FIXME: Decouple this reporting from local PPS monitoring. */
  224. void pps_update(WINDOW *win, int y, int x)
  225. {
  226. struct timedelta_t ppstimes;
  227. assert(win != NULL);
  228. if (pps_thread_ppsout(&session.pps_thread, &ppstimes) > 0) {
  229. /* NOTE: can not use double here due to precision requirements */
  230. struct timespec timedelta;
  231. int i, ymax, xmax;
  232. getmaxyx(win, ymax, xmax);
  233. assert(ymax > 0); /* squash a compiler warning */
  234. (void)wmove(win, y, x);
  235. /* see toff_update() for explanation of the magic number */
  236. for (i = 0; i < 18 && x + i < xmax - 1; i++)
  237. (void)waddch(win, ' ');
  238. TS_SUB( &timedelta, &ppstimes.clock, &ppstimes.real);
  239. // (long long) for 32-bit CPU with 64-bit time_t
  240. if ( 86400 < llabs(timedelta.tv_sec) ) {
  241. /* more than one day off, overflow */
  242. /* need a bigger field to show it */
  243. (void)mvwaddstr(win, y, x, "> 1 day");
  244. } else {
  245. char buf[TIMESPEC_LEN];
  246. (void)mvwaddstr(win, y, x,
  247. timespec_str(&timedelta, buf, sizeof(buf)));
  248. }
  249. (void)wnoutrefresh(win);
  250. }
  251. }
  252. /******************************************************************************
  253. *
  254. * Curses I/O
  255. *
  256. ******************************************************************************/
  257. void monitor_fixframe(WINDOW * win)
  258. {
  259. int ymax, xmax, ycur, xcur;
  260. assert(win != NULL);
  261. getyx(win, ycur, xcur);
  262. getmaxyx(win, ymax, xmax);
  263. assert(xcur > -1 && ymax > 0); /* squash a compiler warning */
  264. (void)mvwaddch(win, ycur, xmax - 1, ACS_VLINE);
  265. }
  266. static void packet_dump(const char *buf, size_t buflen)
  267. {
  268. if (packetwin != NULL) {
  269. char buf2[MAX_PACKET_LENGTH * 2];
  270. cond_hexdump(buf2, buflen * 2, buf, buflen);
  271. (void)waddstr(packetwin, buf2);
  272. (void)waddch(packetwin, (chtype)'\n');
  273. }
  274. }
  275. static void monitor_dump_send(const char *buf, size_t len)
  276. {
  277. if (packetwin != NULL) {
  278. report_lock();
  279. (void)wattrset(packetwin, A_BOLD);
  280. (void)wprintw(packetwin, ">>>");
  281. packet_dump(buf, len);
  282. (void)wattrset(packetwin, A_NORMAL);
  283. report_unlock();
  284. }
  285. }
  286. /* log to the packet window if curses is up, otherwise stdout */
  287. static void gpsmon_report(const char *buf)
  288. {
  289. /* report locking is left to caller */
  290. if (!curses_active)
  291. (void)fputs(buf, stdout);
  292. else if (packetwin != NULL)
  293. (void)waddstr(packetwin, buf);
  294. if (logfile != NULL)
  295. (void)fputs(buf, logfile);
  296. }
  297. static void packet_vlog(char *buf, size_t len, const char *fmt, va_list ap)
  298. {
  299. char buf2[BUFSIZ];
  300. visibilize(buf2, sizeof(buf2), buf);
  301. report_lock();
  302. (void)vsnprintf(buf2 + strlen(buf2), len, fmt, ap);
  303. gpsmon_report(buf2);
  304. report_unlock();
  305. }
  306. static void announce_log(const char *fmt, ...)
  307. {
  308. char buf[BUFSIZ];
  309. va_list ap;
  310. va_start(ap, fmt);
  311. (void)vsnprintf(buf, sizeof(buf) - 5, fmt, ap);
  312. va_end(ap);
  313. if (packetwin != NULL) {
  314. report_lock();
  315. (void)wattrset(packetwin, A_BOLD);
  316. (void)wprintw(packetwin, ">>>");
  317. (void)waddstr(packetwin, buf);
  318. (void)wattrset(packetwin, A_NORMAL);
  319. (void)wprintw(packetwin, "\n");
  320. report_unlock();
  321. }
  322. if (logfile != NULL) {
  323. (void)fprintf(logfile, ">>>%s\n", buf);
  324. }
  325. }
  326. static void monitor_vcomplain(const char *fmt, va_list ap)
  327. {
  328. assert(cmdwin!=NULL);
  329. (void)wmove(cmdwin, 0, (int)promptlen);
  330. (void)wclrtoeol(cmdwin);
  331. (void)wattrset(cmdwin, A_BOLD);
  332. (void)vw_printw(cmdwin, (char *)fmt, ap);
  333. (void)wattrset(cmdwin, A_NORMAL);
  334. (void)wrefresh(cmdwin);
  335. (void)doupdate();
  336. (void)wgetch(cmdwin);
  337. (void)wmove(cmdwin, 0, (int)promptlen);
  338. (void)wclrtoeol(cmdwin);
  339. (void)wrefresh(cmdwin);
  340. (void)wmove(cmdwin, 0, (int)promptlen);
  341. (void)doupdate();
  342. }
  343. void monitor_complain(const char *fmt, ...)
  344. {
  345. va_list ap;
  346. va_start(ap, fmt);
  347. monitor_vcomplain(fmt, ap);
  348. va_end(ap);
  349. }
  350. void monitor_log(const char *fmt, ...)
  351. {
  352. if (packetwin != NULL) {
  353. va_list ap;
  354. report_lock();
  355. va_start(ap, fmt);
  356. (void)vw_printw(packetwin, (char *)fmt, ap);
  357. va_end(ap);
  358. report_unlock();
  359. }
  360. }
  361. static const char *promptgen(void)
  362. {
  363. static char buf[sizeof(session.gpsdata.dev.path) + HOST_NAME_MAX + 20];
  364. if (serial)
  365. (void)snprintf(buf, sizeof(buf),
  366. "%s:%s %u %u%c%u",
  367. hostname,
  368. session.gpsdata.dev.path,
  369. session.gpsdata.dev.baudrate,
  370. 9 - session.gpsdata.dev.stopbits,
  371. session.gpsdata.dev.parity,
  372. session.gpsdata.dev.stopbits);
  373. else {
  374. (void)strlcpy(buf, session.gpsdata.dev.path, sizeof(buf));
  375. if (source.device != NULL) {
  376. (void) strlcat(buf, ":", sizeof(buf));
  377. (void) strlcat(buf, source.device, sizeof(buf));
  378. }
  379. }
  380. return buf;
  381. }
  382. /* refresh the device-identification window */
  383. static void refresh_statwin(void)
  384. {
  385. if (NULL != session.device_type &&
  386. NULL != session.device_type->type_name) {
  387. type_name = session.device_type->type_name;
  388. } else {
  389. type_name = "Unknown device";
  390. }
  391. report_lock();
  392. (void)wclear(statwin);
  393. (void)wattrset(statwin, A_BOLD);
  394. (void)mvwaddstr(statwin, 0, 0, promptgen());
  395. (void)wattrset(statwin, A_NORMAL);
  396. (void)wnoutrefresh(statwin);
  397. report_unlock();
  398. }
  399. static void refresh_cmdwin(void)
  400. /* refresh the command window */
  401. {
  402. report_lock();
  403. (void)wmove(cmdwin, 0, 0);
  404. (void)wprintw(cmdwin, "%s", type_name);
  405. promptlen = strlen(type_name);
  406. if (fallback != NULL && strcmp(fallback->type_name, type_name) != 0) {
  407. (void)waddch(cmdwin, (chtype)' ');
  408. (void)waddch(cmdwin, (chtype)'(');
  409. (void)waddstr(cmdwin, fallback->type_name);
  410. (void)waddch(cmdwin, (chtype)')');
  411. promptlen += strlen(fallback->type_name) + 3;
  412. }
  413. (void)wprintw(cmdwin, "> ");
  414. promptlen += 2;
  415. (void)wclrtoeol(cmdwin);
  416. (void)wnoutrefresh(cmdwin);
  417. report_unlock();
  418. }
  419. static bool curses_init(void)
  420. {
  421. (void)initscr();
  422. (void)cbreak();
  423. (void)intrflush(stdscr, FALSE);
  424. (void)keypad(stdscr, true);
  425. (void)clearok(stdscr, true);
  426. (void)clear();
  427. (void)noecho();
  428. curses_active = true;
  429. #define CMDWINHEIGHT 1
  430. statwin = newwin(CMDWINHEIGHT, 30, 0, 0);
  431. cmdwin = newwin(CMDWINHEIGHT, 0, 0, 30);
  432. packetwin = newwin(0, 0, CMDWINHEIGHT, 0);
  433. if (statwin == NULL || cmdwin == NULL || packetwin == NULL)
  434. return false;
  435. (void)scrollok(packetwin, true);
  436. (void)wsetscrreg(packetwin, 0, LINES - CMDWINHEIGHT);
  437. (void)wmove(packetwin, 0, 0);
  438. refresh_statwin();
  439. refresh_cmdwin();
  440. return true;
  441. }
  442. static bool switch_type(const struct gps_type_t *devtype)
  443. {
  444. const struct monitor_object_t **trial, **newobject;
  445. newobject = NULL;
  446. for (trial = monitor_objects; *trial; trial++) {
  447. if (strcmp((*trial)->driver->type_name, devtype->type_name)==0) {
  448. newobject = trial;
  449. break;
  450. }
  451. }
  452. if (newobject) {
  453. if (LINES < (*newobject)->min_y + 1 || COLS < (*newobject)->min_x) {
  454. monitor_complain("%s requires %dx%d screen",
  455. (*newobject)->driver->type_name,
  456. (*newobject)->min_x, (*newobject)->min_y + 1);
  457. } else {
  458. int leftover;
  459. if (active != NULL) {
  460. if ((*active)->wrap != NULL)
  461. (*active)->wrap();
  462. (void)delwin(devicewin);
  463. }
  464. active = newobject;
  465. if (devicewin)
  466. delwin(devicewin);
  467. devicewin = newwin((*active)->min_y, (*active)->min_x, 1, 0);
  468. /* screen might have JSON on it from the init sequence */
  469. (void)clearok(stdscr, true);
  470. (void)clear();
  471. if ((devicewin == NULL) || ((*active)->initialize != NULL && !(*active)->initialize())) {
  472. monitor_complain("Internal initialization failure - screen "
  473. "must be at least 80x24. Aborting.");
  474. return false;
  475. }
  476. leftover = LINES - 1 - (*active)->min_y;
  477. report_lock();
  478. if (leftover <= 0) {
  479. if (packetwin != NULL)
  480. (void)delwin(packetwin);
  481. packetwin = NULL;
  482. } else if (packetwin == NULL) {
  483. packetwin = newwin(leftover, COLS, (*active)->min_y + 1, 0);
  484. (void)scrollok(packetwin, true);
  485. (void)wsetscrreg(packetwin, 0, leftover - 1);
  486. } else {
  487. (void)wresize(packetwin, leftover, COLS);
  488. (void)mvwin(packetwin, (*active)->min_y + 1, 0);
  489. (void)wsetscrreg(packetwin, 0, leftover - 1);
  490. }
  491. report_unlock();
  492. }
  493. return true;
  494. }
  495. monitor_complain("No monitor matches %s.", devtype->type_name);
  496. return false;
  497. }
  498. static void select_packet_monitor(struct gps_device_t *device)
  499. {
  500. static int last_type = BAD_PACKET;
  501. /*
  502. * Switch display types on packet receipt. Note, this *doesn't*
  503. * change the selection of the current device driver; that's done
  504. * within gpsd_multipoll() before this hook is called.
  505. */
  506. if (device->lexer.type != last_type) {
  507. const struct gps_type_t *active_type = device->device_type;
  508. if (NMEA_PACKET == device->lexer.type &&
  509. 0 != ((device->device_type->flags & DRIVER_STICKY))) {
  510. active_type = &driver_nmea0183;
  511. }
  512. if (!switch_type(active_type)) {
  513. longjmp(terminate, TERM_DRIVER_SWITCH);
  514. } else {
  515. refresh_statwin();
  516. refresh_cmdwin();
  517. }
  518. last_type = device->lexer.type;
  519. }
  520. if (NULL != active &&
  521. 0 < device->lexer.outbuflen &&
  522. NULL != (*active)->update) {
  523. (*active)->update();
  524. }
  525. if (NULL != devicewin) {
  526. (void)wnoutrefresh(devicewin);
  527. }
  528. }
  529. // Control-L character
  530. #define CTRL_L 0x0C
  531. // char-by-char nonblocking input, return accumulated command line on \n
  532. static char *curses_get_command(void)
  533. {
  534. static char input[80];
  535. static char line[80];
  536. int c;
  537. c = wgetch(cmdwin);
  538. if (CTRL_L == c) {
  539. // ^L is to repaint the screen
  540. (void)clearok(stdscr, true);
  541. if (NULL != active &&
  542. NULL != (*active)->initialize) {
  543. (void)(*active)->initialize();
  544. }
  545. } else if (c != '\r' && c != '\n') {
  546. size_t len = strlen(input);
  547. if ('\b' == c ||
  548. KEY_LEFT == c ||
  549. (int)erasechar() == c) {
  550. input[len--] = '\0';
  551. } else if (isprint(c)) {
  552. input[len] = (char)c;
  553. input[++len] = '\0';
  554. (void)waddch(cmdwin, (chtype)c);
  555. (void)wrefresh(cmdwin);
  556. (void)doupdate();
  557. }
  558. return NULL;
  559. }
  560. (void)wmove(cmdwin, 0, (int)promptlen);
  561. (void)wclrtoeol(cmdwin);
  562. (void)wrefresh(cmdwin);
  563. (void)doupdate();
  564. // user finished entering a command
  565. if (input[0] == '\0')
  566. return NULL;
  567. else {
  568. (void) strlcpy(line, input, sizeof(line));
  569. input[0] = '\0';
  570. }
  571. /* handle it in the currently selected monitor object if possible */
  572. if (serial && active != NULL && (*active)->command != NULL) {
  573. int status = (*active)->command(line);
  574. if (status == COMMAND_TERMINATE)
  575. longjmp(terminate, TERM_QUIT);
  576. else if (status == COMMAND_MATCH)
  577. return NULL;
  578. assert(status == COMMAND_UNKNOWN);
  579. }
  580. return line;
  581. }
  582. /******************************************************************************
  583. *
  584. * Mode-independent I/O
  585. *
  586. * Below this line, all calls to curses-dependent functions are guarded
  587. * by curses_active and have ttylike alternatives.
  588. *
  589. ******************************************************************************/
  590. static void packet_log(const char *fmt, ...)
  591. {
  592. char buf[BUFSIZ];
  593. va_list ap;
  594. buf[0] = '\0';
  595. va_start(ap, fmt);
  596. packet_vlog(buf, sizeof(buf), fmt, ap);
  597. va_end(ap);
  598. }
  599. static ssize_t gpsmon_serial_write(struct gps_device_t *session,
  600. const char *buf,
  601. const size_t len)
  602. /* pass low-level data to devices, echoing it to the log window */
  603. {
  604. monitor_dump_send((const char *)buf, len);
  605. return gpsd_serial_write(session, buf, len);
  606. }
  607. bool monitor_control_send( unsigned char *buf, size_t len)
  608. {
  609. if (!serial)
  610. return false;
  611. else {
  612. ssize_t st;
  613. context.readonly = false;
  614. st = session.device_type->control_send(&session, (char *)buf, len);
  615. context.readonly = true;
  616. return (st != -1);
  617. }
  618. }
  619. static bool monitor_raw_send( unsigned char *buf, size_t len)
  620. {
  621. ssize_t st = gpsd_write(&session, (char *)buf, len);
  622. return (st > 0 && (size_t) st == len);
  623. }
  624. static void complain(const char *fmt, ...)
  625. {
  626. va_list ap;
  627. va_start(ap, fmt);
  628. if (curses_active)
  629. monitor_vcomplain(fmt, ap);
  630. else {
  631. (void)vfprintf(stderr, fmt, ap);
  632. (void)fputc('\n', stderr);
  633. }
  634. va_end(ap);
  635. }
  636. /*****************************************************************************
  637. *
  638. * Main sequence
  639. *
  640. *****************************************************************************/
  641. static void gpsmon_hook(struct gps_device_t *device, gps_mask_t changed UNUSED)
  642. /* per-packet hook */
  643. {
  644. char buf[BUFSIZ];
  645. /* FIXME: If the following condition is false, the display is screwed up. */
  646. #if defined(SOCKET_EXPORT_ENABLE) && defined(PPS_DISPLAY_ENABLE)
  647. char ts_buf1[TIMESPEC_LEN];
  648. char ts_buf2[TIMESPEC_LEN];
  649. if (!serial && str_starts_with((char*)device->lexer.outbuffer,
  650. "{\"class\":\"TOFF\",")) {
  651. const char *end = NULL;
  652. int status = json_toff_read((const char *)device->lexer.outbuffer,
  653. &session.gpsdata,
  654. &end);
  655. if (status != 0) {
  656. complain("Ill-formed TOFF packet: %d (%s)", status,
  657. json_error_string(status));
  658. return;
  659. } else {
  660. if (!curses_active)
  661. (void)fprintf(stderr, "TOFF=%s real=%s\n",
  662. timespec_str(&session.gpsdata.toff.clock,
  663. ts_buf1, sizeof(ts_buf1)),
  664. timespec_str(&session.gpsdata.toff.real,
  665. ts_buf2, sizeof(ts_buf2)));
  666. time_offset = session.gpsdata.toff;
  667. return;
  668. }
  669. } else if (!serial && str_starts_with((char*)device->lexer.outbuffer, "{\"class\":\"PPS\",")) {
  670. const char *end = NULL;
  671. struct gps_data_t noclobber;
  672. int status = json_pps_read((const char *)device->lexer.outbuffer,
  673. &noclobber,
  674. &end);
  675. if (status != 0) {
  676. complain("Ill-formed PPS packet: %d (%s)", status,
  677. json_error_string(status));
  678. return;
  679. } else {
  680. struct timespec timedelta;
  681. char timedelta_str[TIMESPEC_LEN];
  682. TS_SUB( &timedelta, &noclobber.pps.clock, &noclobber.pps.real);
  683. timespec_str(&timedelta, timedelta_str, sizeof(timedelta_str));
  684. if (!curses_active) {
  685. char pps_clock_str[TIMESPEC_LEN];
  686. char pps_real_str[TIMESPEC_LEN];
  687. timespec_str(&noclobber.pps.clock, pps_clock_str,
  688. sizeof(pps_clock_str));
  689. timespec_str(&noclobber.pps.real, pps_real_str,
  690. sizeof(pps_real_str));
  691. (void)fprintf(stderr,
  692. "PPS=%.20s clock=%.20s offset=%.20s\n",
  693. pps_clock_str,
  694. pps_real_str,
  695. timedelta_str);
  696. }
  697. (void)snprintf(buf, sizeof(buf),
  698. "------------------- PPS offset: %.20s ------\n",
  699. timedelta_str);
  700. /* FIXME: Decouple this from the pps_thread code. */
  701. /*
  702. * In direct mode this would be a bad idea, but we're not actually
  703. * watching for handshake events on a spawned thread here.
  704. */
  705. /* coverity[missing_lock] */
  706. session.pps_thread.pps_out = noclobber.pps;
  707. /* coverity[missing_lock] */
  708. session.pps_thread.ppsout_count++;
  709. }
  710. }
  711. else
  712. #endif /* SOCKET_EXPORT_ENABLE && PPS_DISPLAY_ENABLE */
  713. {
  714. #ifdef __future__
  715. if (!serial)
  716. {
  717. if (device->lexer.type == JSON_PACKET)
  718. {
  719. const char *end = NULL;
  720. libgps_json_unpack((char *)device->lexer.outbuffer, &session.gpsdata, &end);
  721. }
  722. }
  723. #endif /* __future__ */
  724. if (curses_active)
  725. select_packet_monitor(device);
  726. (void)snprintf(buf, sizeof(buf), "(%d) ",
  727. (int)device->lexer.outbuflen);
  728. cond_hexdump(buf + strlen(buf), sizeof(buf) - strlen(buf),
  729. (char *)device->lexer.outbuffer,device->lexer.outbuflen);
  730. (void)strlcat(buf, "\n", sizeof(buf));
  731. }
  732. report_lock();
  733. if (!curses_active)
  734. (void)fputs(buf, stdout);
  735. else {
  736. if (packetwin != NULL) {
  737. (void)waddstr(packetwin, buf);
  738. (void)wnoutrefresh(packetwin);
  739. }
  740. (void)doupdate();
  741. }
  742. if (logfile != NULL && device->lexer.outbuflen > 0) {
  743. UNUSED size_t written_count = fwrite
  744. (device->lexer.outbuffer, sizeof(char),
  745. device->lexer.outbuflen, logfile);
  746. assert(written_count >= 1);
  747. }
  748. report_unlock();
  749. /* Update the last fix time seen for PPS if we've actually seen one,
  750. * and it is a new second. */
  751. if (0 >= device->newdata.time.tv_sec) {
  752. // "NTP: bad new time
  753. } else if (device->newdata.time.tv_sec <=
  754. device->pps_thread.fix_in.real.tv_sec) {
  755. // "NTP: Not a new time
  756. } else
  757. ntp_latch(device, &time_offset);
  758. }
  759. static bool do_command(const char *line)
  760. {
  761. unsigned int v;
  762. struct timespec delay;
  763. unsigned char buf[BUFLEN];
  764. const char *arg;
  765. if (isspace((unsigned char) line[1])) {
  766. for (arg = line + 2; *arg != '\0' && isspace((unsigned char) *arg); arg++)
  767. arg++;
  768. arg++;
  769. } else
  770. arg = line + 1;
  771. switch (line[0]) {
  772. case 'c': /* change cycle time */
  773. if (session.device_type == NULL)
  774. complain("No device defined yet");
  775. else if (!serial)
  776. complain("Only available in low-level mode.");
  777. else {
  778. double rate = strtod(arg, NULL);
  779. const struct gps_type_t *switcher = session.device_type;
  780. if (fallback != NULL && fallback->rate_switcher != NULL)
  781. switcher = fallback;
  782. if (switcher->rate_switcher != NULL) {
  783. /* *INDENT-OFF* */
  784. context.readonly = false;
  785. if (switcher->rate_switcher(&session, rate)) {
  786. announce_log("[Rate switcher called.]");
  787. } else
  788. complain("Rate not supported.");
  789. context.readonly = true;
  790. /* *INDENT-ON* */
  791. } else
  792. complain
  793. ("Device type %s has no rate switcher",
  794. switcher->type_name);
  795. }
  796. break;
  797. case 'i': /* start probing for subtype */
  798. if (session.device_type == NULL)
  799. complain("No GPS type detected.");
  800. else if (!serial)
  801. complain("Only available in low-level mode.");
  802. else {
  803. if (strcspn(line, "01") == strlen(line))
  804. context.readonly = !context.readonly;
  805. else
  806. context.readonly = (atoi(line + 1) == 0);
  807. announce_log("[probing %sabled]", context.readonly ? "dis" : "en");
  808. if (!context.readonly)
  809. /* magic - forces a reconfigure */
  810. session.lexer.counter = 0;
  811. }
  812. break;
  813. case 'l': /* open logfile */
  814. report_lock();
  815. if (logfile != NULL) {
  816. if (packetwin != NULL)
  817. (void)wprintw(packetwin,
  818. ">>> Logging off\n");
  819. (void)fclose(logfile);
  820. }
  821. if ((logfile = fopen(line + 1, "a")) != NULL)
  822. if (packetwin != NULL)
  823. (void)wprintw(packetwin,
  824. ">>> Logging to %s\n", line + 1);
  825. report_unlock();
  826. break;
  827. case 'n': /* change mode */
  828. /* if argument not specified, toggle */
  829. if (strcspn(line, "01") == strlen(line)) {
  830. /* *INDENT-OFF* */
  831. v = (unsigned int)TEXTUAL_PACKET_TYPE(
  832. session.lexer.type);
  833. /* *INDENT-ON* */
  834. } else
  835. v = (unsigned)atoi(line + 1);
  836. if (session.device_type == NULL)
  837. complain("No device defined yet");
  838. else if (!serial)
  839. complain("Only available in low-level mode.");
  840. else {
  841. const struct gps_type_t *switcher = session.device_type;
  842. if (fallback != NULL && fallback->mode_switcher != NULL)
  843. switcher = fallback;
  844. if (switcher->mode_switcher != NULL) {
  845. context.readonly = false;
  846. announce_log("[Mode switcher to mode %d]", v);
  847. switcher->mode_switcher(&session, (int)v);
  848. context.readonly = true;
  849. (void)tcdrain(session.gpsdata.gps_fd);
  850. /* wait 50,000 uSec */
  851. delay.tv_sec = 0;
  852. delay.tv_nsec = 50000000L;
  853. nanosleep(&delay, NULL);
  854. /*
  855. * Session device change will be set to NMEA when
  856. * gpsmon resyncs. So stash the current type to
  857. * be restored if we do 'n' from NMEA mode.
  858. */
  859. if (v == 0)
  860. fallback = switcher;
  861. } else
  862. complain
  863. ("Device type %s has no mode switcher",
  864. switcher->type_name);
  865. }
  866. break;
  867. case 'q': /* quit */
  868. return false;
  869. case 's': /* change speed */
  870. if (session.device_type == NULL)
  871. complain("No device defined yet");
  872. else if (!serial)
  873. complain("Only available in low-level mode.");
  874. else {
  875. speed_t speed;
  876. char parity = session.gpsdata.dev.parity;
  877. unsigned int stopbits =
  878. (unsigned int)session.gpsdata.dev.stopbits;
  879. char *modespec;
  880. const struct gps_type_t *switcher = session.device_type;
  881. if (fallback != NULL && fallback->speed_switcher != NULL)
  882. switcher = fallback;
  883. modespec = strchr(arg, ':');
  884. if (modespec != NULL) {
  885. if (strchr("78", *++modespec) == NULL) {
  886. complain
  887. ("No support for that word length.");
  888. break;
  889. }
  890. parity = *++modespec;
  891. if (strchr("NOE", parity) == NULL) {
  892. complain("What parity is '%c'?.",
  893. parity);
  894. break;
  895. }
  896. stopbits = (unsigned int)*++modespec;
  897. if (strchr("12", (char)stopbits) == NULL) {
  898. complain("Stop bits must be 1 or 2.");
  899. break;
  900. }
  901. stopbits = (unsigned int)(stopbits - '0');
  902. }
  903. speed = (unsigned)atoi(arg);
  904. /* *INDENT-OFF* */
  905. if (switcher->speed_switcher) {
  906. context.readonly = false;
  907. if (switcher->speed_switcher(&session, speed,
  908. parity, (int)
  909. stopbits)) {
  910. announce_log("[Speed switcher called.]");
  911. /*
  912. * See the comment attached to the 'DEVICE'
  913. * command in gpsd. Allow the control
  914. * string time to register at the GPS
  915. * before we do the baud rate switch,
  916. * which effectively trashes the UART's
  917. * buffer.
  918. */
  919. (void)tcdrain(session.gpsdata.gps_fd);
  920. /* wait 50,000 uSec */
  921. delay.tv_sec = 0;
  922. delay.tv_nsec = 50000000L;
  923. nanosleep(&delay, NULL);
  924. (void)gpsd_set_speed(&session, speed,
  925. parity, stopbits);
  926. } else
  927. complain
  928. ("Speed/mode combination not supported.");
  929. context.readonly = true;
  930. } else
  931. complain
  932. ("Device type %s has no speed switcher",
  933. switcher->type_name);
  934. /* *INDENT-ON* */
  935. if (curses_active)
  936. refresh_statwin();
  937. }
  938. break;
  939. case 't': /* force device type */
  940. if (!serial)
  941. complain("Only available in low-level mode.");
  942. else if (strlen(arg) > 0) {
  943. int matchcount = 0;
  944. const struct gps_type_t **dp, *forcetype = NULL;
  945. for (dp = gpsd_drivers; *dp; dp++) {
  946. if (strstr((*dp)->type_name, arg) != NULL) {
  947. forcetype = *dp;
  948. matchcount++;
  949. }
  950. }
  951. if (matchcount == 0) {
  952. complain
  953. ("No driver type matches '%s'.", arg);
  954. } else if (matchcount == 1) {
  955. assert(forcetype != NULL);
  956. /* *INDENT-OFF* */
  957. if (switch_type(forcetype))
  958. (void)gpsd_switch_driver(&session,
  959. forcetype->type_name);
  960. /* *INDENT-ON* */
  961. if (curses_active)
  962. refresh_cmdwin();
  963. } else {
  964. complain("Multiple driver type names match '%s'.", arg);
  965. }
  966. }
  967. break;
  968. case 'x': /* send control packet */
  969. if (session.device_type == NULL)
  970. complain("No device defined yet");
  971. else if (!serial)
  972. complain("Only available in low-level mode.");
  973. else {
  974. int st = gpsd_hexpack(arg, (char *)buf, strlen(arg));
  975. if (st < 0)
  976. complain("Invalid hex string (error %d)", st);
  977. else if (session.device_type->control_send == NULL)
  978. complain("Device type %s has no control-send method.",
  979. session.device_type->type_name);
  980. else if (!monitor_control_send(buf, (size_t)st))
  981. complain("Control send failed.");
  982. }
  983. break;
  984. case 'X': /* send raw packet */
  985. if (!serial)
  986. complain("Only available in low-level mode.");
  987. else {
  988. ssize_t len = (ssize_t) gpsd_hexpack(arg, (char *)buf, strlen(arg));
  989. if (len < 0)
  990. complain("Invalid hex string (error %lu)", (unsigned long)len);
  991. else if (!monitor_raw_send(buf, (size_t)len))
  992. complain("Raw send failed.");
  993. }
  994. break;
  995. default:
  996. complain("Unknown command '%c'", line[0]);
  997. break;
  998. }
  999. /* continue accepting commands */
  1000. return true;
  1001. }
  1002. static char *pps_report(volatile struct pps_thread_t *pps_thread UNUSED,
  1003. struct timedelta_t *td UNUSED) {
  1004. packet_log(PPSBAR);
  1005. return "gpsmon";
  1006. }
  1007. static jmp_buf assertbuf;
  1008. static void onsig(int sig UNUSED)
  1009. {
  1010. if (sig == SIGABRT)
  1011. longjmp(assertbuf, 1);
  1012. else
  1013. longjmp(terminate, TERM_SIGNAL);
  1014. }
  1015. #define WATCHRAW "?WATCH={\"raw\":2,\"pps\":true}\r\n"
  1016. #define WATCHRAWDEVICE "?WATCH={\"raw\":2,\"pps\":true,\"device\":\"%s\"}\r\n"
  1017. #define WATCHNMEA "?WATCH={\"nmea\":true,\"pps\":true}\r\n"
  1018. #define WATCHNMEADEVICE "?WATCH={\"nmea\":true,\"pps\":true,\"device\":\"%s\"}\r\n"
  1019. /* this placement avoids a compiler warning */
  1020. static const char *cmdline;
  1021. static void usage(void)
  1022. {
  1023. (void)fputs(
  1024. "usage: gpsmon [OPTIONS] [server[:port:[device]]]\n\n"
  1025. #ifdef HAVE_GETOPT_LONG
  1026. " --debug DEBUGLEVEL Set DEBUGLEVEL\n"
  1027. " --help Show this help, then exit\n"
  1028. " --list List known device types, then exit.\n"
  1029. " --logfile FILE Log to LOGFILE\n"
  1030. " --nocurses No curses. Data only.\n"
  1031. " --nmea Force NMEA mode.\n"
  1032. " --type TYPE Set receiver TYPE\n"
  1033. " --version Show version, then exit\n"
  1034. #endif
  1035. " -a No curses. Data only.\n"
  1036. " -? Show this help, then exit\n"
  1037. " -D DEBUGLEVEL Set DEBUGLEVEL\n"
  1038. " -h Show this help, then exit\n"
  1039. " -L List known device types, then exit.\n"
  1040. " -l FILE Log to LOGFILE\n"
  1041. " -n Force NMEA mode.\n"
  1042. " -t TYPE Set receiver TYPE\n"
  1043. " -V Show version, then exit\n",
  1044. stderr);
  1045. }
  1046. int main(int argc, char **argv)
  1047. {
  1048. int ch;
  1049. char *explanation;
  1050. int bailout = 0, matches = 0;
  1051. bool nmea = false;
  1052. fd_set all_fds;
  1053. fd_set rfds;
  1054. volatile socket_t maxfd = 0;
  1055. char inbuf[80];
  1056. volatile bool nocurses = false;
  1057. int activated = -1;
  1058. const char *optstring = "?aD:hLl:nt:V";
  1059. #ifdef HAVE_GETOPT_LONG
  1060. int option_index = 0;
  1061. static struct option long_options[] = {
  1062. {"debug", required_argument, NULL, 'D'},
  1063. {"help", no_argument, NULL, 'h'},
  1064. {"list", no_argument, NULL, 'L' },
  1065. {"logfile", required_argument, NULL, 'l'},
  1066. {"nmea", no_argument, NULL, 'n' },
  1067. {"nocurses", no_argument, NULL, 'a' },
  1068. {"type", required_argument, NULL, 't'},
  1069. {"version", no_argument, NULL, 'V' },
  1070. {NULL, 0, NULL, 0},
  1071. };
  1072. #endif
  1073. gethostname(hostname, sizeof(hostname)-1);
  1074. (void)putenv("TZ=UTC"); // for ctime()
  1075. gps_context_init(&context, "gpsmon"); // initialize the report mutex
  1076. context.serial_write = gpsmon_serial_write;
  1077. context.errout.report = gpsmon_report;
  1078. while (1) {
  1079. #ifdef HAVE_GETOPT_LONG
  1080. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  1081. #else
  1082. ch = getopt(argc, argv, optstring);
  1083. #endif
  1084. if (ch == -1) {
  1085. break;
  1086. }
  1087. switch (ch) {
  1088. case 'a':
  1089. nocurses = true;
  1090. break;
  1091. case 'D':
  1092. context.errout.debug = atoi(optarg);
  1093. json_enable_debug(context.errout.debug - 2, stderr);
  1094. break;
  1095. case 'L': /* list known device types */
  1096. (void)
  1097. fputs
  1098. ("General commands available per type. '+' "
  1099. "means there are private commands.\n",
  1100. stdout);
  1101. for (active = monitor_objects; *active; active++) {
  1102. (void)fputs("i l q ^S ^Q", stdout);
  1103. (void)fputc(' ', stdout);
  1104. if ((*active)->driver->mode_switcher != NULL)
  1105. (void)fputc('n', stdout);
  1106. else
  1107. (void)fputc(' ', stdout);
  1108. (void)fputc(' ', stdout);
  1109. if ((*active)->driver->speed_switcher != NULL)
  1110. (void)fputc('s', stdout);
  1111. else
  1112. (void)fputc(' ', stdout);
  1113. (void)fputc(' ', stdout);
  1114. if ((*active)->driver->rate_switcher != NULL)
  1115. (void)fputc('x', stdout);
  1116. else
  1117. (void)fputc(' ', stdout);
  1118. (void)fputc(' ', stdout);
  1119. if ((*active)->driver->control_send != NULL)
  1120. (void)fputc('x', stdout);
  1121. else
  1122. (void)fputc(' ', stdout);
  1123. (void)fputc(' ', stdout);
  1124. if ((*active)->command != NULL)
  1125. (void)fputc('+', stdout);
  1126. else
  1127. (void)fputc(' ', stdout);
  1128. (void)fputs("\t", stdout);
  1129. (void)fputs((*active)->driver->type_name, stdout);
  1130. (void)fputc('\n', stdout);
  1131. }
  1132. exit(EXIT_SUCCESS);
  1133. case 'l': /* enable logging at startup */
  1134. logfile = fopen(optarg, "w");
  1135. if (logfile == NULL) {
  1136. (void)fprintf(stderr, "Couldn't open logfile for writing.\n");
  1137. exit(EXIT_FAILURE);
  1138. }
  1139. break;
  1140. case 'n':
  1141. nmea = true;
  1142. break;
  1143. case 't':
  1144. fallback = NULL;
  1145. for (active = monitor_objects; *active; active++) {
  1146. if (str_starts_with((*active)->driver->type_name, optarg))
  1147. {
  1148. fallback = (*active)->driver;
  1149. matches++;
  1150. }
  1151. }
  1152. if (matches > 1) {
  1153. (void)fprintf(stderr, "-t option matched more than one driver.\n");
  1154. exit(EXIT_FAILURE);
  1155. }
  1156. else if (matches == 0) {
  1157. (void)fprintf(stderr, "-t option didn't match any driver.\n");
  1158. exit(EXIT_FAILURE);
  1159. }
  1160. active = NULL;
  1161. break;
  1162. case 'V':
  1163. (void)printf("%s: %s (revision %s)\n", argv[0], VERSION, REVISION);
  1164. exit(EXIT_SUCCESS);
  1165. case 'h':
  1166. FALLTHROUGH
  1167. case '?':
  1168. usage();
  1169. exit(EXIT_SUCCESS);
  1170. default:
  1171. usage();
  1172. exit(EXIT_FAILURE);
  1173. }
  1174. }
  1175. gpsd_time_init(&context, time(NULL));
  1176. gpsd_init(&session, &context, NULL);
  1177. /* Grok the server, port, and device. */
  1178. if (optind < argc) {
  1179. serial = str_starts_with(argv[optind], "/dev");
  1180. gpsd_source_spec(argv[optind], &source);
  1181. } else {
  1182. serial = false;
  1183. gpsd_source_spec(NULL, &source);
  1184. }
  1185. if (serial) {
  1186. if (NULL == source.device) {
  1187. /* this can happen with "gpsmon /dev:dd" */
  1188. (void) strlcpy(session.gpsdata.dev.path,
  1189. argv[optind],
  1190. sizeof(session.gpsdata.dev.path));
  1191. } else {
  1192. (void) strlcpy(session.gpsdata.dev.path,
  1193. source.device,
  1194. sizeof(session.gpsdata.dev.path));
  1195. }
  1196. } else {
  1197. if (strstr(source.server, "//") == 0)
  1198. (void) strlcpy(session.gpsdata.dev.path,
  1199. "tcp://",
  1200. sizeof(session.gpsdata.dev.path));
  1201. else
  1202. session.gpsdata.dev.path[0] = '\0';
  1203. str_appendf(session.gpsdata.dev.path, sizeof(session.gpsdata.dev.path),
  1204. "%s:%s", source.server, source.port);
  1205. }
  1206. activated = gpsd_activate(&session, O_PROBEONLY);
  1207. if ( 0 > activated ) {
  1208. if ( PLACEHOLDING_FD == activated ) {
  1209. (void)fputs("gpsmon:ERROR: PPS device unsupported\n", stderr);
  1210. }
  1211. exit(EXIT_FAILURE);
  1212. }
  1213. if (serial) {
  1214. // this guard suppresses a warning on Bluetooth devices
  1215. if (SOURCE_RS232 == session.sourcetype ||
  1216. SOURCE_ACM == session.sourcetype ||
  1217. SOURCE_USB == session.sourcetype ) {
  1218. session.pps_thread.report_hook = pps_report;
  1219. #ifdef MAGIC_HAT_ENABLE
  1220. /*
  1221. * The HAT kludge. If we're using the HAT GPS on a
  1222. * Raspberry Pi or a workalike like the ODROIDC2, and
  1223. * there is a static "first PPS", and we have access because
  1224. * we're root, assume we want to use KPPS.
  1225. */
  1226. if (0 == strcmp(session.pps_thread.devicename, MAGIC_HAT_GPS) ||
  1227. 0 == strcmp(session.pps_thread.devicename, MAGIC_LINK_GPS)) {
  1228. const char *first_pps = pps_get_first();
  1229. if (0 == access(first_pps, R_OK | W_OK)) {
  1230. session.pps_thread.devicename = first_pps;
  1231. }
  1232. }
  1233. #endif // MAGIC_HAT_ENABLE
  1234. pps_thread_activate(&session.pps_thread);
  1235. }
  1236. } else if (source.device != NULL) {
  1237. (void)gps_send(&session.gpsdata,
  1238. nmea ? WATCHNMEADEVICE : WATCHRAWDEVICE, source.device);
  1239. } else {
  1240. (void)gps_send(&session.gpsdata, nmea ? WATCHNMEA : WATCHRAW);
  1241. }
  1242. /*
  1243. * This is a monitoring utility. Disable autoprobing, because
  1244. * in some cases (e.g. SiRFs) there is no way to probe a chip
  1245. * type without flipping it to native mode.
  1246. */
  1247. context.readonly = true;
  1248. /* quit cleanly if an assertion fails */
  1249. (void)signal(SIGABRT, onsig);
  1250. if (setjmp(assertbuf) > 0) {
  1251. if (logfile)
  1252. (void)fclose(logfile);
  1253. if (curses_active)
  1254. (void)endwin();
  1255. (void)fputs("gpsmon: assertion failure, probable I/O error\n",
  1256. stderr);
  1257. exit(EXIT_FAILURE);
  1258. }
  1259. FD_ZERO(&all_fds);
  1260. #ifndef __clang_analyzer__
  1261. FD_SET(0, &all_fds); /* accept keystroke inputs */
  1262. #endif /* __clang_analyzer__ */
  1263. FD_SET(session.gpsdata.gps_fd, &all_fds);
  1264. if (session.gpsdata.gps_fd > maxfd)
  1265. maxfd = session.gpsdata.gps_fd;
  1266. if ((bailout = setjmp(terminate)) == 0) {
  1267. (void)signal(SIGQUIT, onsig);
  1268. (void)signal(SIGINT, onsig);
  1269. (void)signal(SIGTERM, onsig);
  1270. if (nocurses) {
  1271. (void)fputs("gpsmon: ", stdout);
  1272. (void)fputs(promptgen(), stdout);
  1273. (void)fputs("\n", stdout);
  1274. (void)tcgetattr(0, &cooked);
  1275. (void)tcgetattr(0, &rare);
  1276. rare.c_lflag &=~ (ICANON | ECHO);
  1277. rare.c_cc[VMIN] = (cc_t)1;
  1278. (void)tcflush(0, TCIFLUSH);
  1279. (void)tcsetattr(0, TCSANOW, &rare);
  1280. } else if (!curses_init()) {
  1281. goto quit;
  1282. }
  1283. for (;;) {
  1284. fd_set efds;
  1285. timespec_t ts_timeout = {2, 0}; // timeout for pselect()
  1286. switch(gpsd_await_data(&rfds, &efds, maxfd, &all_fds,
  1287. &context.errout, ts_timeout)) {
  1288. case AWAIT_GOT_INPUT:
  1289. FALLTHROUGH
  1290. case AWAIT_TIMEOUT:
  1291. break;
  1292. case AWAIT_NOT_READY:
  1293. // no recovery from bad fd is possible
  1294. if (FD_ISSET(session.gpsdata.gps_fd, &efds))
  1295. longjmp(terminate, TERM_SELECT_FAILED);
  1296. continue;
  1297. case AWAIT_FAILED:
  1298. longjmp(terminate, TERM_SELECT_FAILED);
  1299. break;
  1300. }
  1301. switch(gpsd_multipoll(FD_ISSET(session.gpsdata.gps_fd, &rfds),
  1302. &session, gpsmon_hook, 0)) {
  1303. case DEVICE_READY:
  1304. FD_SET(session.gpsdata.gps_fd, &all_fds);
  1305. break;
  1306. case DEVICE_UNREADY:
  1307. longjmp(terminate, TERM_EMPTY_READ);
  1308. break;
  1309. case DEVICE_ERROR:
  1310. longjmp(terminate, TERM_READ_ERROR);
  1311. break;
  1312. case DEVICE_EOF:
  1313. longjmp(terminate, TERM_QUIT);
  1314. break;
  1315. default:
  1316. break;
  1317. }
  1318. if (FD_ISSET(0, &rfds)) {
  1319. if (curses_active) {
  1320. cmdline = curses_get_command();
  1321. } else {
  1322. // coverity[string_null_argument]
  1323. ssize_t st = read(0, &inbuf, 1);
  1324. if (1 == st) {
  1325. report_lock();
  1326. (void)tcflush(0, TCIFLUSH);
  1327. (void)tcsetattr(0, TCSANOW, &cooked);
  1328. (void)fputs("gpsmon: ", stdout);
  1329. (void)fputs(promptgen(), stdout);
  1330. (void)fputs("> ", stdout);
  1331. (void)putchar(inbuf[0]);
  1332. cmdline = fgets(inbuf+1, sizeof(inbuf)-1, stdin);
  1333. if (cmdline) {
  1334. cmdline--;
  1335. }
  1336. report_unlock();
  1337. }
  1338. }
  1339. if (NULL != cmdline &&
  1340. !do_command(cmdline)) {
  1341. longjmp(terminate, TERM_QUIT);
  1342. }
  1343. if (!curses_active) {
  1344. (void)sleep(2);
  1345. report_lock();
  1346. (void)tcsetattr(0, TCSANOW, &rare);
  1347. report_unlock();
  1348. }
  1349. }
  1350. }
  1351. }
  1352. quit:
  1353. // we'll fall through to here on longjmp()
  1354. // Shut down PPS monitoring.
  1355. if (serial) {
  1356. (void)pps_thread_deactivate(&session.pps_thread);
  1357. }
  1358. gpsd_close(&session);
  1359. if (logfile) {
  1360. (void)fclose(logfile);
  1361. }
  1362. if (curses_active) {
  1363. (void)endwin();
  1364. } else {
  1365. (void)tcsetattr(0, TCSANOW, &cooked);
  1366. }
  1367. explanation = NULL;
  1368. switch (bailout) {
  1369. case TERM_SELECT_FAILED:
  1370. explanation = "I/O wait on device failed\n";
  1371. break;
  1372. case TERM_DRIVER_SWITCH:
  1373. explanation = "Driver type switch failed\n";
  1374. break;
  1375. case TERM_EMPTY_READ:
  1376. explanation = "Device went offline\n";
  1377. break;
  1378. case TERM_READ_ERROR:
  1379. explanation = "Read error from device\n";
  1380. break;
  1381. case TERM_SIGNAL:
  1382. FALLTHROUGH
  1383. case TERM_QUIT:
  1384. // normal exit, no message
  1385. break;
  1386. default:
  1387. explanation = "Unknown error, should never happen.\n";
  1388. break;
  1389. }
  1390. if (NULL != explanation) {
  1391. (void)fputs(explanation, stderr);
  1392. }
  1393. exit(EXIT_SUCCESS);
  1394. }
  1395. // gpsmon.c ends here
  1396. // vim: set expandtab shiftwidth=4