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