gpsmon.c 47 KB

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