gpsmon.c 49 KB

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