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