gpsctl.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. /* gpsctl.c -- tweak the control settings on a GPS
  2. *
  3. * This file is Copyright 2010 by the GPSD project
  4. * SPDX-License-Identifier: BSD-2-clause
  5. *
  6. */
  7. #include "include/gpsd_config.h" // must be before all includes
  8. #include <assert.h>
  9. #include <errno.h>
  10. #ifdef HAVE_GETOPT_LONG
  11. #include <getopt.h>
  12. #endif
  13. #include <signal.h>
  14. #include <stdarg.h>
  15. #include <stdbool.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h> // for strlcat() and strlcpy()
  19. #include <sys/select.h>
  20. #include <sys/time.h>
  21. #include <time.h>
  22. #include <unistd.h> // for _exit()
  23. #include "include/gpsd.h"
  24. #ifdef SHM_EXPORT_ENABLE
  25. #include <sys/ipc.h>
  26. #include <sys/shm.h>
  27. #endif // SHM_EXPORT_ENABLE
  28. #define HIGH_LEVEL_TIMEOUT 8
  29. static int debuglevel;
  30. static bool explicit_timeout = false;
  31. static unsigned int timeout = 0; // no timeout
  32. static struct gps_context_t context;
  33. static bool hunting = true;
  34. /*
  35. * Set this as high or higher than the maximum number of subtype
  36. * probes in drivers.c.
  37. */
  38. #define REDIRECT_SNIFF 15
  39. // allow the device to settle after a control operation
  40. static void settle(struct gps_device_t *session)
  41. {
  42. struct timespec delay;
  43. /*
  44. * See the 'deep black magic' comment in serial.c:set_serial().
  45. */
  46. (void)tcdrain(session->gpsdata.gps_fd);
  47. // wait 50,000 uSec
  48. delay.tv_sec = 0;
  49. delay.tv_nsec = 50000000L;
  50. nanosleep(&delay, NULL);
  51. (void)tcdrain(session->gpsdata.gps_fd);
  52. }
  53. /*
  54. * Allows any response other than ERROR. Use it for queries where a
  55. * failure return (due to, for example, a missing driver method) is
  56. * immediate, but successful responses have unpredictable lag.
  57. */
  58. #define NON_ERROR 0 // must be distinct from any gps_mask_t value
  59. // ship a command and wait on an expected response type
  60. static bool gps_query(struct gps_data_t *gpsdata,
  61. gps_mask_t expect,
  62. const int timeout,
  63. const char *fmt, ... )
  64. {
  65. static fd_set rfds;
  66. char buf[BUFSIZ];
  67. va_list ap;
  68. time_t starttime;
  69. struct timespec tv;
  70. sigset_t oldset, blockset;
  71. (void)sigemptyset(&blockset);
  72. (void)sigaddset(&blockset, SIGHUP);
  73. (void)sigaddset(&blockset, SIGINT);
  74. (void)sigaddset(&blockset, SIGTERM);
  75. (void)sigaddset(&blockset, SIGQUIT);
  76. (void)sigprocmask(SIG_BLOCK, &blockset, &oldset);
  77. va_start(ap, fmt);
  78. (void)vsnprintf(buf, sizeof(buf)-2, fmt, ap);
  79. va_end(ap);
  80. // codacy does not like strlen()
  81. if ('\n' != buf[strnlen(buf, sizeof(buf) - 1) - 1]) {
  82. (void)strlcat(buf, "\n", sizeof(buf));
  83. }
  84. if (0 <= write(gpsdata->gps_fd, buf, strlen(buf))) {
  85. GPSD_LOG(LOG_ERROR, &context.errout, "gps_query(), write failed\n");
  86. return false;
  87. }
  88. GPSD_LOG(LOG_PROG, &context.errout, "gps_query(), wrote, %s\n", buf);
  89. FD_ZERO(&rfds);
  90. starttime = time(NULL);
  91. for (;;) {
  92. FD_CLR(gpsdata->gps_fd, &rfds);
  93. GPSD_LOG(LOG_PROG, &context.errout, "waiting...\n");
  94. tv.tv_sec = 2;
  95. tv.tv_nsec = 0;
  96. // (socket_t) to pacify codacy.
  97. if (-1 == pselect((socket_t)gpsdata->gps_fd + 1, &rfds, NULL, NULL,
  98. &tv, &oldset)) {
  99. if (EINTR == errno ||
  100. !FD_ISSET(gpsdata->gps_fd, &rfds)) {
  101. continue;
  102. }
  103. GPSD_LOG(LOG_ERROR, &context.errout, "select %s(%d)\n",
  104. strerror(errno), errno);
  105. exit(EXIT_FAILURE);
  106. }
  107. GPSD_LOG(LOG_PROG, &context.errout, "reading...\n");
  108. (void)gps_read(gpsdata, NULL, 0);
  109. if (ERROR_SET & gpsdata->set) {
  110. GPSD_LOG(LOG_ERROR, &context.errout, "error '%s'\n",
  111. gpsdata->error);
  112. return false;
  113. }
  114. if ((NON_ERROR == expect) ||
  115. 0 != (expect & gpsdata->set)) {
  116. return true;
  117. } else if (0 < timeout &&
  118. (time(NULL) - starttime > timeout)) {
  119. GPSD_LOG(LOG_ERROR, &context.errout,
  120. "timed out after %d seconds\n",
  121. timeout);
  122. return false;
  123. }
  124. }
  125. return false;
  126. }
  127. static void onsig(int sig)
  128. {
  129. // CWE-479: Signal Handler Use of a Non-reentrant Function
  130. // See: The C Standard, 7.14.1.1, paragraph 5 [ISO/IEC 9899:2011]
  131. // Can't log in a signal handler. Can't even call exit().
  132. if (sig == SIGALRM) {
  133. _exit(EXIT_FAILURE);
  134. } else {
  135. _exit(EXIT_SUCCESS);
  136. }
  137. }
  138. // full ID of the device for reports, including subtype
  139. static char *gpsd_id(struct gps_device_t *session)
  140. {
  141. static char buf[128];
  142. if (NULL == session ||
  143. NULL == session->device_type ||
  144. NULL == session->device_type->type_name) {
  145. return "unknown,";
  146. }
  147. (void)strlcpy(buf, session->device_type->type_name, sizeof(buf));
  148. if ('\0' != session->subtype[0]) {
  149. (void)strlcat(buf, " ", sizeof(buf));
  150. (void)strlcat(buf, session->subtype, sizeof(buf));
  151. }
  152. return (buf);
  153. }
  154. // recognize when we've achieved sync
  155. static void ctlhook(struct gps_device_t *device UNUSED,
  156. gps_mask_t changed UNUSED)
  157. {
  158. static int packet_counter = 0;
  159. /*
  160. * If it's NMEA, go back around enough times for the type probes to
  161. * reveal any secret identity (like SiRF or UBX) the chip might have.
  162. * If it's not, getting more packets might fetch subtype information.
  163. */
  164. if (REDIRECT_SNIFF <= packet_counter++) {
  165. hunting = false;
  166. (void)alarm(0);
  167. }
  168. }
  169. static void usage(void)
  170. {
  171. (void)printf("usage: gpsctl [OPTIONS] [device]\n\n"
  172. #ifdef HAVE_GETOPT_LONG
  173. " --binary Switch device to native binary mode.\n"
  174. " --debug DEBUGLEVEL Set debug level to DEBUGLEVEL.\n"
  175. " --direct Force direct access to the device.\n"
  176. " --echo Echo specified control string with wrapper.\n"
  177. " --help Show this help, then exit\n"
  178. " --list List known device types and exit.\n"
  179. " --nmea Switch device to NMEA mode.\n"
  180. " --rate RATE Change receiver cyclte time to RATE.\n"
  181. " --reset Force reset to default mode.\n"
  182. #ifdef SHM_EXPORT_ENABLE
  183. " --rmshm Remove the SHM export segment and exit.\n"
  184. #endif // SHM_EXPORT_ENABLE
  185. " --ship CONTROL Ship specified control string.\n"
  186. " --speed SPEED Set device speed to SPEED.\n"
  187. " --timeout TIMEOUT Set the timeout on packet recognition.\n"
  188. " --type DEVTYPE Force the device type.\n"
  189. " --version Show version, then exit\n"
  190. #endif // HAVE_GETOPT_LONG
  191. " -? Show this help, then exit\n"
  192. " -b Switch device to native binary mode.\n"
  193. " -c RATE Change receiver cyclte time to RATE.\n"
  194. " -D DEBUGLEVEL Set debug level to DEBUGLEVEL.\n"
  195. " -e Echo specified control string with wrapper.\n"
  196. " -f Force direct access to the device.\n"
  197. " -h Show this help, then exit\n"
  198. " -l List known device types and exit.\n"
  199. " -n Switch device to NMEA mode.\n"
  200. #ifdef SHM_EXPORT_ENABLE
  201. " -R Remove the SHM export segment and exit.\n"
  202. #endif // SHM_EXPORT_ENABLE
  203. " -r Force reset to default mode.\n"
  204. " -s SPEED Set device speed to SPEED.\n"
  205. " -t DEVTYPE Force the device type.\n"
  206. " -T TIMEOUT Set the timeout on packet recognition.\n"
  207. " -V Show version, then exit\n"
  208. " -x CONTROL Ship specified control string.\n");
  209. }
  210. int main(int argc, char **argv)
  211. {
  212. int ch, status;
  213. char *device = NULL, *devtype = NULL;
  214. char *speed = NULL, *control = NULL, *rate = NULL;
  215. bool to_binary = false, to_nmea = false, reset = false;
  216. bool control_stdout = false;
  217. bool lowlevel=false, echo=false;
  218. struct gps_data_t gpsdata;
  219. const struct gps_type_t *forcetype = NULL;
  220. const struct gps_type_t **dp;
  221. char cooked[BUFSIZ];
  222. ssize_t cooklen = 0;
  223. const char *optstring = "?bec:fhlnrs:t:x:D:RT:V";
  224. #ifdef HAVE_GETOPT_LONG
  225. int option_index = 0;
  226. static struct option long_options[] = {
  227. {"binary", no_argument, NULL, 'b'},
  228. {"debug", required_argument, NULL, 'D'},
  229. {"direct", no_argument, NULL, 'f'},
  230. {"echo", no_argument, NULL, 'e'},
  231. {"help", no_argument, NULL, 'h'},
  232. {"list", no_argument, NULL, 'l'},
  233. {"nmea", no_argument, NULL, 'n'},
  234. {"rate", required_argument, NULL, 'c'},
  235. #ifdef SHM_EXPORT_ENABLE
  236. {"rmshm", required_argument, NULL, 'R'},
  237. #endif
  238. {"ship", required_argument, NULL, 'x'},
  239. {"speed", required_argument, NULL, 's'},
  240. {"timeout", required_argument, NULL, 'T'},
  241. {"type", required_argument, NULL, 't'},
  242. {"version", no_argument, NULL, 'V' },
  243. {NULL, 0, NULL, 0},
  244. };
  245. #endif
  246. // We need this before any logging happens (for report_mutex)
  247. gps_context_init(&context, "gpsctl");
  248. while (1) {
  249. #ifdef HAVE_GETOPT_LONG
  250. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  251. #else
  252. ch = getopt(argc, argv, optstring);
  253. #endif
  254. if (-1 == ch) {
  255. break;
  256. }
  257. switch (ch) {
  258. case 'b': // switch to vendor binary mode
  259. to_binary = true;
  260. break;
  261. case 'c':
  262. rate = optarg;
  263. break;
  264. case 'D': // set debugging level
  265. debuglevel = atoi(optarg);
  266. gps_enable_debug(debuglevel, stderr);
  267. break;
  268. case 'e': // echo specified control string with wrapper
  269. lowlevel = true;
  270. control_stdout = true; /* Prevent message going to stdout */
  271. echo = true;
  272. break;
  273. case 'f': // force direct access to the device
  274. lowlevel = true;
  275. break;
  276. case 'l': // list known device types
  277. for (dp = gpsd_drivers; *dp; dp++) {
  278. if ((*dp)->mode_switcher != NULL)
  279. (void)fputs("-[bn]\t", stdout);
  280. else
  281. (void)fputc('\t', stdout);
  282. if ((*dp)->speed_switcher != NULL)
  283. (void)fputs("-s\t", stdout);
  284. else
  285. (void)fputc('\t', stdout);
  286. if ((*dp)->rate_switcher != NULL)
  287. (void)fputs("-c\t", stdout);
  288. else
  289. (void)fputc('\t', stdout);
  290. if ((*dp)->control_send != NULL)
  291. (void)fputs("-x\t", stdout);
  292. else
  293. (void)fputc('\t', stdout);
  294. (void)puts((*dp)->type_name);
  295. }
  296. exit(EXIT_SUCCESS);
  297. case 'n': /* switch to NMEA mode */
  298. to_nmea = true;
  299. break;
  300. #ifdef SHM_EXPORT_ENABLE
  301. case 'R': /* remove the SHM export segment */
  302. status = shmget(getenv("GPSD_SHM_KEY") ?
  303. (key_t)strtol(getenv("GPSD_SHM_KEY"), NULL, 0) :
  304. (key_t)GPSD_SHM_KEY, 0, 0);
  305. if (status == -1) {
  306. GPSD_LOG(LOG_WARN, &context.errout,
  307. "GPSD SHM segment does not exist.\n");
  308. exit(1);
  309. } else {
  310. status = shmctl(status, IPC_RMID, NULL);
  311. if (status == -1) {
  312. GPSD_LOG(LOG_ERROR, &context.errout,
  313. "shmctl failed, errno = %d (%s)\n",
  314. errno, strerror(errno));
  315. exit(1);
  316. }
  317. }
  318. exit(0);
  319. #endif /* SHM_EXPORT_ENABLE */
  320. case 'r': /* force-switch to default mode */
  321. reset = true;
  322. lowlevel = false; /* so we'll abort if the daemon is running */
  323. break;
  324. case 's': /* change output baud rate */
  325. speed = optarg;
  326. break;
  327. case 'T': /* set the timeout on packet recognition */
  328. timeout = (unsigned)atoi(optarg);
  329. explicit_timeout = true;
  330. break;
  331. case 't': /* force the device type */
  332. devtype = optarg;
  333. /* experimental kluge */
  334. if (strcmp(devtype, "u-blox") == 0)
  335. timeout = 2;
  336. break;
  337. case 'x': /* ship specified control string */
  338. control = optarg;
  339. lowlevel = true;
  340. if ((cooklen = hex_escapes(cooked, control)) <= 0) {
  341. GPSD_LOG(LOG_ERROR, &context.errout,
  342. "invalid escape string (error %d)\n", (int)cooklen);
  343. exit(EXIT_FAILURE);
  344. }
  345. break;
  346. case 'V':
  347. (void)fprintf(stderr, "%s: version %s (revision %s)\n",
  348. argv[0], VERSION, REVISION);
  349. exit(EXIT_SUCCESS);
  350. case '?':
  351. FALLTHROUGH
  352. case 'h':
  353. usage();
  354. exit(EXIT_SUCCESS);
  355. default:
  356. usage();
  357. exit(EXIT_FAILURE);
  358. }
  359. }
  360. if (optind < argc)
  361. device = argv[optind];
  362. if (devtype != NULL) {
  363. int matchcount = 0;
  364. for (dp = gpsd_drivers; *dp; dp++) {
  365. if (strstr((*dp)->type_name, devtype) != NULL) {
  366. forcetype = *dp;
  367. matchcount++;
  368. }
  369. }
  370. if (matchcount == 0)
  371. GPSD_LOG(LOG_ERROR, &context.errout,
  372. "no driver type name matches '%s'.\n", devtype);
  373. else if (matchcount == 1) {
  374. assert(forcetype != NULL);
  375. GPSD_LOG( LOG_PROG,&context.errout,
  376. "%s driver selected.\n", forcetype->type_name);
  377. } else {
  378. forcetype = NULL;
  379. GPSD_LOG(LOG_ERROR, &context.errout,
  380. "%d driver type names match '%s'.\n",
  381. matchcount, devtype);
  382. }
  383. }
  384. if (((int)to_nmea + (int)to_binary + (int)reset) > 1) {
  385. GPSD_LOG(LOG_ERROR, &context.errout, "make up your mind, would you?\n");
  386. exit(EXIT_SUCCESS);
  387. }
  388. (void) signal(SIGINT, onsig);
  389. (void) signal(SIGTERM, onsig);
  390. (void) signal(SIGQUIT, onsig);
  391. if (!lowlevel) {
  392. /* Try to open the stream to gpsd. */
  393. if (gps_open(NULL, NULL, &gpsdata) != 0) {
  394. GPSD_LOG(LOG_ERROR, &context.errout,
  395. "no gpsd running or network error: %s.\n",
  396. gps_errstr(errno));
  397. lowlevel = true;
  398. }
  399. }
  400. if (!lowlevel) {
  401. int i, devcount;
  402. if (!explicit_timeout)
  403. timeout = HIGH_LEVEL_TIMEOUT;
  404. /* what devices have we available? */
  405. if (!gps_query(&gpsdata, DEVICELIST_SET, (int)timeout,
  406. "?DEVICES;\r\n")) {
  407. GPSD_LOG(LOG_ERROR, &context.errout,
  408. "no DEVICES response received.\n");
  409. (void)gps_close(&gpsdata);
  410. exit(EXIT_FAILURE);
  411. }
  412. if (gpsdata.devices.ndevices == 0) {
  413. GPSD_LOG(LOG_ERROR, &context.errout, "no devices connected.\n");
  414. (void)gps_close(&gpsdata);
  415. exit(EXIT_FAILURE);
  416. } else if (gpsdata.devices.ndevices > 1 && device == NULL) {
  417. GPSD_LOG(LOG_ERROR, &context.errout,
  418. "multiple devices and no device specified.\n");
  419. (void)gps_close(&gpsdata);
  420. exit(EXIT_FAILURE);
  421. }
  422. GPSD_LOG(LOG_PROG, &context.errout,
  423. "%d device(s) found.\n",gpsdata.devices.ndevices);
  424. /* try to mine the devicelist return for the data we want */
  425. if (gpsdata.devices.ndevices == 1 && device == NULL) {
  426. device = gpsdata.dev.path;
  427. i = 0;
  428. } else {
  429. assert(device != NULL);
  430. for (i = 0; i < gpsdata.devices.ndevices; i++)
  431. if (strcmp(device, gpsdata.devices.list[i].path) == 0) {
  432. goto devicelist_entry_matches;
  433. }
  434. GPSD_LOG(LOG_ERROR, &context.errout,
  435. "specified device not found in device list.\n");
  436. (void)gps_close(&gpsdata);
  437. exit(EXIT_FAILURE);
  438. devicelist_entry_matches:;
  439. }
  440. gpsdata.dev = gpsdata.devices.list[i];
  441. devcount = gpsdata.devices.ndevices;
  442. /* if the device has not identified, watch it until it does so */
  443. if (gpsdata.dev.driver[0] == '\0') {
  444. if (gps_stream(&gpsdata, WATCH_ENABLE|WATCH_JSON, NULL) == -1) {
  445. GPSD_LOG(LOG_ERROR, &context.errout, "stream set failed.\n");
  446. (void)gps_close(&gpsdata);
  447. exit(EXIT_FAILURE);
  448. }
  449. while (devcount > 0) {
  450. /* Wait for input data */
  451. if (!gps_waiting(&gpsdata, timeout * 1000000)) {
  452. GPSD_LOG(LOG_ERROR, &context.errout,
  453. "timed out waiting for device\n");
  454. (void)gps_close(&gpsdata);
  455. exit(EXIT_FAILURE);
  456. }
  457. errno = 0;
  458. if (gps_read(&gpsdata, NULL, 0) == -1) {
  459. GPSD_LOG(LOG_ERROR, &context.errout, "data read failed.\n");
  460. (void)gps_close(&gpsdata);
  461. exit(EXIT_FAILURE);
  462. }
  463. if (gpsdata.set & DEVICE_SET) {
  464. --devcount;
  465. assert(gpsdata.dev.path[0]!='\0' &&
  466. gpsdata.dev.driver[0]!='\0');
  467. if (strcmp(gpsdata.dev.path, device) == 0) {
  468. goto matching_device_seen;
  469. }
  470. }
  471. }
  472. GPSD_LOG(LOG_ERROR, &context.errout, "data read failed.\n");
  473. (void)gps_close(&gpsdata);
  474. exit(EXIT_FAILURE);
  475. matching_device_seen:;
  476. }
  477. /* sanity check */
  478. if (gpsdata.dev.driver[0] == '\0') {
  479. GPSD_LOG(LOG_SHOUT, &context.errout,
  480. "%s can't be identified.\n",
  481. gpsdata.dev.path);
  482. (void)gps_close(&gpsdata);
  483. exit(EXIT_SUCCESS);
  484. }
  485. /* if no control operation was specified, just ID the device */
  486. if (speed==NULL && rate == NULL && !to_nmea && !to_binary && !reset) {
  487. (void)printf("%s identified as a %s",
  488. gpsdata.dev.path, gpsdata.dev.driver);
  489. if (gpsdata.dev.subtype[0] != '\0') {
  490. (void)fputc(' ', stdout);
  491. (void)fputs(gpsdata.dev.subtype, stdout);
  492. }
  493. if (gpsdata.dev.baudrate > 0)
  494. (void)printf(" at %u baud", gpsdata.dev.baudrate);
  495. (void)fputc('.', stdout);
  496. (void)fputc('\n', stdout);
  497. }
  498. status = 0;
  499. if (reset)
  500. {
  501. GPSD_LOG(LOG_PROG, &context.errout,
  502. "cannot reset with gpsd running.\n");
  503. exit(EXIT_SUCCESS);
  504. }
  505. /*
  506. * We used to wait on DEVICE_SET here. That doesn't work
  507. * anymore because when the demon generates its response it
  508. * sets the mode bit in the response from the current packet
  509. * type, which may not have changed (probably will not have
  510. * changed) even though the command to switch modes has been
  511. * sent and will shortly take effect.
  512. */
  513. if (to_nmea) {
  514. if (!gps_query(&gpsdata, NON_ERROR, (int)timeout,
  515. "?DEVICE={\"path\":\"%s\",\"native\":0}\r\n",
  516. device)) {
  517. GPSD_LOG(LOG_ERROR, &context.errout,
  518. "%s mode change to NMEA failed\n",
  519. gpsdata.dev.path);
  520. status = 1;
  521. } else
  522. GPSD_LOG(LOG_PROG, &context.errout,
  523. "%s mode change succeeded\n", gpsdata.dev.path);
  524. }
  525. else if (to_binary) {
  526. if (!gps_query(&gpsdata, NON_ERROR, (int)timeout,
  527. "?DEVICE={\"path\":\"%s\",\"native\":1}\r\n",
  528. device)) {
  529. GPSD_LOG(LOG_ERROR, &context.errout,
  530. "%s mode change to native mode failed\n",
  531. gpsdata.dev.path);
  532. status = 1;
  533. } else
  534. GPSD_LOG(LOG_PROG, &context.errout,
  535. "%s mode change succeeded\n",
  536. gpsdata.dev.path);
  537. }
  538. if (speed != NULL) {
  539. char parity = 'N';
  540. char stopbits = '1';
  541. if (strchr(speed, ':') == NULL)
  542. (void)gps_query(&gpsdata,
  543. DEVICE_SET, (int)timeout,
  544. "?DEVICE={\"path\":\"%s\",\"bps\":%s}\r\n",
  545. device, speed);
  546. else {
  547. char *modespec = strchr(speed, ':');
  548. status = 0;
  549. if (modespec!=NULL) {
  550. *modespec = '\0';
  551. if (strchr("78", *++modespec) == NULL) {
  552. GPSD_LOG(LOG_ERROR, &context.errout,
  553. "No support for that word length.\n");
  554. status = 1;
  555. }
  556. parity = *++modespec;
  557. if (strchr("NOE", parity) == NULL) {
  558. GPSD_LOG(LOG_ERROR, &context.errout,
  559. "What parity is '%c'?\n", parity);
  560. status = 1;
  561. }
  562. stopbits = *++modespec;
  563. if (strchr("12", stopbits) == NULL) {
  564. GPSD_LOG(LOG_ERROR, &context.errout,
  565. "Stop bits must be 1 or 2.\n");
  566. status = 1;
  567. }
  568. }
  569. if (0 == status)
  570. (void)gps_query(&gpsdata,
  571. DEVICE_SET, (int)timeout,
  572. "?DEVICE={\"path\":\"%s\",\"bps\":%s,"
  573. "\"parity\":\"%c\",\"stopbits\":%c}\r\n",
  574. device, speed, parity, stopbits);
  575. }
  576. if (atoi(speed) != (int)gpsdata.dev.baudrate) {
  577. GPSD_LOG(LOG_ERROR, &context.errout,
  578. "%s driver won't support %s%c%c\n",
  579. gpsdata.dev.path,
  580. speed, parity, stopbits);
  581. status = 1;
  582. } else
  583. GPSD_LOG(LOG_PROG, &context.errout,
  584. "%s change to %s%c%c succeeded\n",
  585. gpsdata.dev.path,
  586. speed, parity, stopbits);
  587. }
  588. if (NULL != rate) {
  589. (void)gps_query(&gpsdata,
  590. DEVICE_SET, (int)timeout,
  591. "?DEVICE={\"path\":\"%s\",\"cycle\":%s}\r\n",
  592. device, rate);
  593. }
  594. (void)gps_close(&gpsdata);
  595. exit(status);
  596. } else if (reset) {
  597. // hard reset will go through lower-level operations
  598. // FIXME: missing speeds, should come from a header.
  599. const int speeds[] = {4800, 9600, 19200, 38400, 57600, 115200, 230400,
  600. 460800, 921600};
  601. static struct gps_device_t session; // zero this too
  602. int i;
  603. if (NULL == device ||
  604. NULL == forcetype) {
  605. GPSD_LOG(LOG_ERROR, &context.errout,
  606. "device and type must be specified for the "
  607. "reset operation.\n");
  608. exit(EXIT_FAILURE);
  609. }
  610. context.errout.debug = debuglevel;
  611. session.context = &context;
  612. gpsd_tty_init(&session);
  613. (void)strlcpy(session.gpsdata.dev.path, device,
  614. sizeof(session.gpsdata.dev.path));
  615. session.device_type = forcetype;
  616. (void)gpsd_open(&session);
  617. (void)gpsd_set_raw(&session);
  618. (void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
  619. (void)tcdrain(session.gpsdata.gps_fd);
  620. for(i = 0; i < (int)(sizeof(speeds) / sizeof(speeds[0])); i++) {
  621. (void)gpsd_set_speed(&session, speeds[i], 'N', 1);
  622. (void)session.device_type->speed_switcher(&session, 4800, 'N', 1);
  623. (void)tcdrain(session.gpsdata.gps_fd);
  624. }
  625. gpsd_set_speed(&session, 4800, 'N', 1);
  626. for (i = 0; i < 3; i++) {
  627. if (session.device_type->mode_switcher) {
  628. session.device_type->mode_switcher(&session, MODE_NMEA);
  629. }
  630. }
  631. gpsd_wrap(&session);
  632. exit(EXIT_SUCCESS);
  633. } else {
  634. // access to the daemon failed, use the low-level facilities
  635. static struct gps_device_t session; // zero this too
  636. fd_set all_fds;
  637. fd_set rfds;
  638. /*
  639. * Unless the user explicitly requested it, always run to end of
  640. * hunt rather than timing out. Otherwise we can easily get messages
  641. * that spuriously look like failure at high baud rates.
  642. */
  643. gps_context_init(&context, "gpsctl");
  644. context.errout.debug = debuglevel;
  645. session.context = &context; // in case gps_init isn't called
  646. if (echo) {
  647. context.readonly = true;
  648. }
  649. if (timeout > 0) {
  650. (void) alarm(timeout);
  651. (void) signal(SIGALRM, onsig);
  652. }
  653. /*
  654. * Unless the user has forced a type and only wants to see the
  655. * string (not send it) we now need to try to open the device
  656. * and find out what is actually there.
  657. */
  658. if (NULL == forcetype ||
  659. !echo) {
  660. socket_t maxfd = 0;
  661. int activated = -1;
  662. if (device == NULL) {
  663. GPSD_LOG(LOG_ERROR, &context.errout,
  664. "device must be specified for low-level access.\n");
  665. exit(EXIT_FAILURE);
  666. }
  667. gpsd_init(&session, &context, device);
  668. activated = gpsd_activate(&session, O_PROBEONLY);
  669. if ( 0 > activated ) {
  670. if ( PLACEHOLDING_FD == activated ) {
  671. (void)printf("%s identified as a %s.\n",
  672. device, gpsd_id(&session));
  673. exit(EXIT_SUCCESS);
  674. }
  675. GPSD_LOG(LOG_ERROR, &context.errout,
  676. "initial GPS device %s open failed\n",
  677. device);
  678. exit(EXIT_FAILURE);
  679. }
  680. GPSD_LOG(LOG_INF, &context.errout,
  681. "device %s activated\n", session.gpsdata.dev.path);
  682. FD_SET(session.gpsdata.gps_fd, &all_fds);
  683. if (session.gpsdata.gps_fd > maxfd) {
  684. maxfd = session.gpsdata.gps_fd;
  685. }
  686. // initialize the GPS context's time fields
  687. gpsd_time_init(&context, time(NULL));
  688. // grab packets until we time out, get sync, or fail sync
  689. for (hunting = true; hunting; ) {
  690. fd_set efds;
  691. timespec_t ts_timeout = {2, 0}; // timeout for pselect()
  692. switch(gpsd_await_data(&rfds, &efds, maxfd, &all_fds,
  693. &context.errout, ts_timeout)) {
  694. case AWAIT_GOT_INPUT:
  695. FALLTHROUGH
  696. case AWAIT_TIMEOUT:
  697. break;
  698. case AWAIT_NOT_READY:
  699. /* no recovery from bad fd is possible */
  700. if (FD_ISSET(session.gpsdata.gps_fd, &efds))
  701. exit(EXIT_FAILURE);
  702. continue;
  703. case AWAIT_FAILED:
  704. exit(EXIT_FAILURE);
  705. }
  706. switch(gpsd_multipoll(FD_ISSET(session.gpsdata.gps_fd, &rfds),
  707. &session, ctlhook, 0))
  708. {
  709. case DEVICE_READY:
  710. FD_SET(session.gpsdata.gps_fd, &all_fds);
  711. break;
  712. case DEVICE_UNREADY:
  713. FD_CLR(session.gpsdata.gps_fd, &all_fds);
  714. break;
  715. case DEVICE_ERROR:
  716. /* this is where a failure to sync lands */
  717. GPSD_LOG(LOG_WARN, &context.errout,
  718. "device error, bailing out.\n");
  719. exit(EXIT_FAILURE);
  720. case DEVICE_EOF:
  721. GPSD_LOG(LOG_WARN, &context.errout,
  722. "device signed off, bailing out.\n");
  723. exit(EXIT_SUCCESS);
  724. default:
  725. break;
  726. }
  727. }
  728. GPSD_LOG(LOG_PROG, &context.errout,
  729. "%s looks like a %s at %d.\n",
  730. device, gpsd_id(&session),
  731. session.gpsdata.dev.baudrate);
  732. if (forcetype!=NULL &&
  733. strcmp("NMEA0183", session.device_type->type_name) != 0 &&
  734. strcmp(forcetype->type_name,
  735. session.device_type->type_name) != 0) {
  736. GPSD_LOG(LOG_ERROR, &context.errout,
  737. "'%s' doesn't match non-generic type '%s' "
  738. "of selected device.\n",
  739. forcetype->type_name,
  740. session.device_type->type_name);
  741. }
  742. }
  743. if(!control_stdout)
  744. (void)printf("%s identified as a %s at %u baud.\n",
  745. device, gpsd_id(&session),
  746. session.gpsdata.dev.baudrate);
  747. /* if no control operation was specified, we're done */
  748. if (speed==NULL && !to_nmea && !to_binary && control==NULL)
  749. exit(EXIT_SUCCESS);
  750. /* maybe user wants to see the packet rather than send it */
  751. if (echo)
  752. session.gpsdata.gps_fd = fileno(stdout);
  753. /* control op specified; maybe we forced the type */
  754. if (forcetype != NULL)
  755. (void)gpsd_switch_driver(&session, forcetype->type_name);
  756. /* now perform the actual control function */
  757. status = 0;
  758. if (to_nmea || to_binary) {
  759. bool write_enable = context.readonly;
  760. context.readonly = false;
  761. if (session.device_type->mode_switcher == NULL) {
  762. GPSD_LOG(LOG_SHOUT, &context.errout,
  763. "%s devices have no mode switch.\n",
  764. session.device_type->type_name);
  765. status = 1;
  766. } else {
  767. int target_mode = to_nmea ? MODE_NMEA : MODE_BINARY;
  768. GPSD_LOG(LOG_SHOUT, &context.errout,
  769. "switching to mode %s.\n",
  770. to_nmea ? "NMEA" : "BINARY");
  771. session.device_type->mode_switcher(&session, target_mode);
  772. settle(&session);
  773. }
  774. context.readonly = write_enable;
  775. }
  776. if (speed) {
  777. char parity = echo ? 'N': session.gpsdata.dev.parity;
  778. int stopbits = echo ? 1 : session.gpsdata.dev.stopbits;
  779. char *modespec;
  780. modespec = strchr(speed, ':');
  781. status = 0;
  782. if (modespec!=NULL) {
  783. *modespec = '\0';
  784. if (strchr("78", *++modespec) == NULL) {
  785. GPSD_LOG(LOG_ERROR, &context.errout,
  786. "No support for that word lengths.\n");
  787. status = 1;
  788. }
  789. parity = *++modespec;
  790. if (strchr("NOE", parity) == NULL) {
  791. GPSD_LOG(LOG_ERROR, &context.errout,
  792. "What parity is '%c'?\n", parity);
  793. status = 1;
  794. }
  795. stopbits = *++modespec;
  796. if (strchr("12", parity) == NULL) {
  797. GPSD_LOG(LOG_ERROR, &context.errout,
  798. "Stop bits must be 1 or 2.\n");
  799. status = 1;
  800. }
  801. stopbits = (int)(stopbits-'0');
  802. }
  803. if (status == 0) {
  804. if (session.device_type->speed_switcher == NULL) {
  805. GPSD_LOG(LOG_ERROR, &context.errout,
  806. "%s devices have no speed switch.\n",
  807. session.device_type->type_name);
  808. status = 1;
  809. }
  810. else if (session.device_type->speed_switcher(&session,
  811. (speed_t)atoi(speed),
  812. parity,
  813. stopbits)) {
  814. settle(&session);
  815. GPSD_LOG(LOG_PROG, &context.errout,
  816. "%s change to %s%c%d succeeded\n",
  817. session.gpsdata.dev.path,
  818. speed, parity, stopbits);
  819. } else {
  820. GPSD_LOG(LOG_ERROR, &context.errout,
  821. "%s driver won't support %s%c%d.\n",
  822. session.gpsdata.dev.path,
  823. speed, parity, stopbits);
  824. status = 1;
  825. }
  826. }
  827. }
  828. if (rate) {
  829. bool write_enable = context.readonly;
  830. context.readonly = false;
  831. if (session.device_type->rate_switcher == NULL) {
  832. GPSD_LOG(LOG_ERROR, &context.errout,
  833. "%s devices have no rate switcher.\n",
  834. session.device_type->type_name);
  835. status = 1;
  836. } else {
  837. double rate_dbl = strtod(rate, NULL);
  838. if (!session.device_type->rate_switcher(&session, rate_dbl)) {
  839. GPSD_LOG(LOG_ERROR, &context.errout,
  840. "rate switch failed.\n");
  841. status = 1;
  842. }
  843. settle(&session);
  844. }
  845. context.readonly = write_enable;
  846. }
  847. if (control) {
  848. bool write_enable = context.readonly;
  849. context.readonly = false;
  850. if (session.device_type->control_send == NULL) {
  851. GPSD_LOG(LOG_ERROR, &context.errout,
  852. "%s devices have no control sender.\n",
  853. session.device_type->type_name);
  854. status = 1;
  855. } else {
  856. if (session.device_type->control_send(&session,
  857. cooked,
  858. (size_t)cooklen) == -1) {
  859. GPSD_LOG(LOG_ERROR, &context.errout,
  860. "control transmission failed.\n");
  861. status = 1;
  862. }
  863. settle(&session);
  864. }
  865. context.readonly = write_enable;
  866. }
  867. exit(status);
  868. }
  869. }
  870. // end
  871. // vim: set expandtab shiftwidth=4