1 /* gpsdctl.c -- communicate with the control socket of a gpsd instance
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 <fcntl.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <syslog.h>
17 #include <sys/socket.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 
21 #include "gpsd.h"
22 
23 #define DEFAULT_GPSD_TEST_SOCKET	"/tmp/gpsd.sock"
24 
25 static char *control_socket = DEFAULT_GPSD_SOCKET;
26 static char *gpsd_options = "";
27 
gpsd_control(char * action,char * argument)28 static int gpsd_control(char *action, char *argument)
29 /* pass a command to gpsd; start the daemon if not already running */
30 {
31     int connect = -1;
32     char buf[512];
33     int status;
34 
35     (void)syslog(LOG_ERR, "gpsd_control(action=%s, arg=%s)", action, argument);
36     if (access(control_socket, F_OK) == 0 &&
37 	    (connect = netlib_localsocket(control_socket, SOCK_STREAM)) >= 0)
38 	syslog(LOG_INFO, "reached a running gpsd");
39     else if (strcmp(action, "add") == 0) {
40 	(void)snprintf(buf, sizeof(buf),
41 		       "gpsd %s -F %s", gpsd_options, control_socket);
42 	(void)syslog(LOG_NOTICE, "launching %s", buf);
43 	if (system(buf) != 0) {
44 	    (void)syslog(LOG_ERR, "launch of gpsd failed");
45 	    return -1;
46 	}
47 	if (access(control_socket, F_OK) == 0)
48 	    connect = netlib_localsocket(control_socket, SOCK_STREAM);
49     }
50     if (connect < 0) {
51 	syslog(LOG_ERR, "can't reach gpsd");
52 	return -1;
53     }
54     /*
55      * We've got a live connection to the gpsd control socket.  No
56      * need to parse the response, because gpsd will lock on to the
57      * device if it's really a GPS and ignore it if it's not.
58      *
59      * The only other place in the code that knows about the format of
60      * the add and remove commands is the handle_control() function in
61      * gpsd.c. Be careful about keeping them in sync, or hotplugging
62      * will have mysterious failures.
63      */
64     if (strcmp(action, "add") == 0) {
65 	/*
66 	 * Force the group-read & group-write bits on, so gpsd will still be
67          * able to use this device after dropping root privileges.
68 	 */
69 	struct stat sb;
70 
71 	/* coverity[toctou] */
72 	if (stat(argument, &sb) != 1)
73 	    (void)chmod(argument, sb.st_mode | S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
74 	(void)snprintf(buf, sizeof(buf), "+%s\r\n", argument);
75 	status = (int)write(connect, buf, strlen(buf));
76 	ignore_return(read(connect, buf, 12));
77     } else if (strcmp(action, "remove") == 0) {
78 	(void)snprintf(buf, sizeof(buf), "-%s\r\n", argument);
79 	status = (int)write(connect, buf, strlen(buf));
80 	ignore_return(read(connect, buf, 12));
81     } else {
82 	(void)syslog(LOG_ERR, "unknown action \"%s\"", action);
83 	status = -1;
84     }
85     (void)close(connect);
86     //syslog(LOG_DEBUG, "gpsd_control ends");
87     return status;
88 }
89 
main(int argc,char * argv[])90 int main(int argc, char *argv[])
91 {
92     openlog("gpsdctl", 0, LOG_DAEMON);
93     if (argc != 3) {
94 	(void)syslog(LOG_ERR, "requires action and argument (%d)", argc);
95 	exit(EXIT_FAILURE);
96     } else {
97 	char *sockenv = getenv("GPSD_SOCKET");
98 	char *optenv = getenv("GPSD_OPTIONS");
99 
100 	if (sockenv != NULL)
101 	    control_socket = sockenv;
102 	else if (geteuid() != 0)
103 	    control_socket = DEFAULT_GPSD_TEST_SOCKET;
104 	if (optenv != NULL)
105 	    gpsd_options = optenv;
106 
107 	/* coverity[string_size] */
108 	if (gpsd_control(argv[1], argv[2]) < 0)
109 	    exit(EXIT_FAILURE);
110 	else
111 	    exit(EXIT_SUCCESS);
112     }
113 }
114