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