Devuan fork of gpsd
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gpsmon.c 40 KiB

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