gpsctl.c 25 KB

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