gpsdctl.c 4.5 KB

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