commhub.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /*
  2. * Copyright (c) 2019 Clementine Computing LLC.
  3. *
  4. * This file is part of PopuFare.
  5. *
  6. * PopuFare is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * PopuFare is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <poll.h>
  24. #include <errno.h>
  25. #include <string.h>
  26. #include <sys/un.h>
  27. #include <sys/time.h>
  28. #include <sys/socket.h>
  29. #include "../common/common_defs.h"
  30. #include "commhub.h"
  31. // This function gets the number of microseconds since epoch for storing in message headers and
  32. // comparison to determine how timely a message is.
  33. //
  34. long long int get_usec_time() {
  35. struct timeval tv;
  36. // Get the number of seconds (and remainder microseconds) since epoch.
  37. //
  38. gettimeofday(&tv,NULL);
  39. // Return those in a single sane 64 bit integer containing the total number
  40. // of microseconds since epoch by multiplying tv_sec by a million and summing
  41. // the two values
  42. //
  43. return (long long int)tv.tv_usec + (1000000 * (long long int)tv.tv_sec);
  44. }
  45. // This function does a non-blocking poll to determine if the message passing socket is
  46. // ready for input or output (depending on
  47. // since it is much more efficient to block on the entire list of your I/O file descriptors
  48. // and service any that cause you to unblock.
  49. //
  50. // Returns: MSG_NO_ACTION = no message
  51. // MSG_ERROR = fatal error (passed fd is no good)
  52. //
  53. // If the socket is ready, the return value will be
  54. // whatever combination of events specified in mask (MSG_SEND, MSG_RECV)
  55. // that is actually ready.
  56. //
  57. int message_socket_status(int message_socket_fd, int mask) {
  58. struct pollfd fds[1];
  59. int retval;
  60. fds[0].fd = message_socket_fd;
  61. do {
  62. fds[0].events = mask;
  63. retval = poll(fds,1,0); //poll the 1 file descriptor, returning immediately.
  64. if (retval < 0) {
  65. // if we were interrupted by a signal, try again...
  66. //
  67. if(errno == EINTR) {
  68. continue;
  69. }
  70. // otherwise poll() has failed, which is BAD news.
  71. //
  72. return MSG_ERROR;
  73. }
  74. if (retval == 0) {
  75. return MSG_NO_ACTION;
  76. }
  77. // If poll tells us that the fd is invalid or has encountered a serious error, return -1.
  78. //
  79. if(fds[0].revents & (POLLERR | POLLNVAL)) {
  80. return MSG_ERROR;
  81. }
  82. // If any of the events supplied in mask have occurred, report them.
  83. //
  84. if(fds[0].revents & mask) {
  85. return fds[0].revents & mask;
  86. }
  87. // if there are no more message to drain (see above) and a hangup has occurred,
  88. // let the user know the socket is done for.
  89. if (fds[0].revents & POLLHUP) {
  90. return MSG_ERROR;
  91. }
  92. // Otherwise, by process of elimination we have no message and no error condition to report
  93. return MSG_NO_ACTION;
  94. } while(1);
  95. }
  96. // this function will pack a mailbox address and a payload into the supplied message structure and
  97. // set the correct timestamp.
  98. //
  99. int prepare_message(struct message_record *target, char *mailbox, void *payload, unsigned int payload_len) {
  100. if (target == NULL) {
  101. return -1;
  102. }
  103. if (mailbox == NULL) {
  104. return -1;
  105. }
  106. if (payload == NULL) {
  107. return -1;
  108. }
  109. if (payload_len > MAX_PAYLOAD_LENGTH) {
  110. return -1;
  111. }
  112. memset(target, 0, sizeof(struct message_record));
  113. strncpy(target->header.mailbox_name, mailbox, MAILBOX_NAME_MAX);
  114. target->header.mailbox_name[MAILBOX_NAME_MAX] = '\0';
  115. target->header.usec_time = get_usec_time();
  116. target->header.payload_length = payload_len;
  117. target->header.sender = getpid();
  118. target->header.from_fd = 0;
  119. memmove(target->payload, payload, payload_len);
  120. return 0;
  121. }
  122. // the send_message function will transmit a message and its header (trimmed to the specified payload length).
  123. // zero will be returned on success, -1 on failure.
  124. //
  125. int send_message(int message_socket_fd, struct message_record *message) {
  126. int txlen;
  127. int retval;
  128. if (message == NULL) {
  129. return -1;
  130. }
  131. // If we are trying to send a message that is longer than allowed, throw an error!
  132. //
  133. if (message->header.payload_length > MAX_PAYLOAD_LENGTH) {
  134. return -1;
  135. }
  136. // Calculate the total message length (header + payload) so that we don't send
  137. // any extra padding that we don't need.
  138. txlen = sizeof(struct message_header_record) + message->header.payload_length;
  139. do {
  140. retval = send(message_socket_fd, message, txlen, MSG_EOR);
  141. // if we were interrupted by a signal, try again.
  142. //
  143. if ( (retval == -1) && (errno == EINTR) ) {
  144. continue;
  145. }
  146. // if we tried to send a message and it got truncated, return failure.
  147. //
  148. if (retval != txlen) {
  149. return -1;
  150. }
  151. return 0;
  152. } while(1);
  153. }
  154. // the get_message function will receive a message from the opposite end of the socket and place it in the
  155. // supplied message structure incliding its header. -1 is returned on failure, 0 on success.
  156. //
  157. int get_message(int message_socket_fd, struct message_record *message) {
  158. int retval;
  159. if (message == NULL) {
  160. return -1;
  161. }
  162. // clear our buffer of any garbage before using it to read in the message
  163. //
  164. memset(message, 0, sizeof(struct message_record));
  165. do {
  166. // try and receive our message
  167. //
  168. retval = recv(message_socket_fd, message, sizeof(struct message_record), 0);
  169. // if we were interrupted by a signal, try again.
  170. //
  171. if ( (retval == -1) && (errno == EINTR) ) {
  172. continue;
  173. }
  174. // zero is a special case for recv which indicates that the other side has shut down the connection.
  175. if (retval == 0) {
  176. return -1;
  177. }
  178. // if the received message size does not add up (between header and payload)
  179. //
  180. if (retval != (sizeof(struct message_header_record) + message->header.payload_length)) {
  181. // throw an error!
  182. //
  183. return -1;
  184. }
  185. // Record which IPC socket we got this from (for things like PING replies)
  186. message->header.from_fd = message_socket_fd;
  187. // otherwise, return success
  188. //
  189. return 0;
  190. } while(1);
  191. }
  192. // This function connects to the communication hub and returns the file descriptor of the connection.
  193. // If the connection failed, -1 is returned. The progname parameter (if not NULL) will be used to
  194. // register a module name (snagged from argv[0]) with the server for ease of debugging.
  195. //
  196. int connect_to_message_server(char *progname) {
  197. int fd;
  198. int retval;
  199. int len;
  200. char *message_text;
  201. struct sockaddr_un addr;
  202. struct message_record msg;
  203. fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
  204. if (fd < 0) {
  205. return -1;
  206. }
  207. // construct our address structure (pointing to the agreed upon server address)
  208. // to pass to connect.
  209. addr.sun_family = AF_UNIX;
  210. strncpy(addr.sun_path, COMMHUB_ADDRESS, sizeof(addr.sun_path) - 1);
  211. addr.sun_path[sizeof(addr.sun_path) - 1]='\0';
  212. len=strlen(COMMHUB_ADDRESS) + sizeof(addr.sun_family);
  213. // attempt to connect to said server
  214. //
  215. retval = connect(fd,(struct sockaddr *)&addr,len);
  216. if (retval) {
  217. fprintf(stderr, "Cannot connect to IPC hub at %s\n", COMMHUB_ADDRESS);
  218. close(fd);
  219. return -1;
  220. }
  221. // if we have been passed a module name to register as, use that, otherwise default to "ANONYMOUS"
  222. //
  223. if (progname) {
  224. message_text = progname;
  225. }
  226. else {
  227. message_text = "ANONYMOUS";
  228. }
  229. len = strlen(message_text);
  230. // if for some inconceivable reason our module name is longer than the server allows, truncate it.
  231. //
  232. if (len > MAX_MODULE_NAME_LENGTH) {
  233. len = MAX_MODULE_NAME_LENGTH;
  234. }
  235. // prepare and send our registation message
  236. //
  237. prepare_message(&msg, MAILBOX_HELLO, message_text, len);
  238. send_message(fd, &msg);
  239. return fd;
  240. }
  241. //---------------------------------------------------------------------
  242. // Read the `state_info_t` from the on disk text file.
  243. // Format is field (in caps) followed by the field value
  244. // separated by a single space.
  245. //
  246. // return:
  247. // 0 on success
  248. // non-zero on error
  249. //
  250. int get_state_info(state_info_t *_state) {
  251. FILE *fp;
  252. int ch = 1,
  253. input_idx=0,
  254. n=0,
  255. field_len=0;
  256. char buffer[LINE_BUFFER_SIZE],
  257. *chp;
  258. if (access(STATE_INFO_FILE, R_OK)!=0) {
  259. return -1;
  260. }
  261. memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
  262. if ((fp = fopen(STATE_INFO_FILE, "r")) == NULL) {
  263. return -1;
  264. }
  265. while (!feof(fp)) {
  266. ch = fgetc(fp);
  267. if ((ch == '\n') || (ch == EOF)) {
  268. // ignore blank lines and comment lines
  269. // (begining with '#')
  270. //
  271. if (input_idx == 0) { continue; }
  272. if (buffer[0] == '#') {
  273. buffer[0] = '\0';
  274. input_idx = 0;
  275. continue;
  276. }
  277. chp = strchr(buffer, ' ');
  278. if (chp != NULL) {
  279. chp++;
  280. n = chp - buffer;
  281. if (strncmp(buffer, "LAT ", n)==0) {
  282. _state->lat = atof(chp);
  283. }
  284. else if (strncmp(buffer, "LON ", n)==0) {
  285. _state->lon = atof(chp);
  286. }
  287. else if (strncmp(buffer, "HEADING ", n)==0) {
  288. _state->heading = atof(chp);
  289. }
  290. else if (strncmp(buffer, "VELOCITY ", n)==0) {
  291. _state->velocity = atof(chp);
  292. }
  293. else if (strncmp(buffer, "NUM_SATS ", n)==0) {
  294. _state->num_sats = atoi(chp);
  295. }
  296. else if (strncmp(buffer, "GPS_GOOD ", n)==0) {
  297. _state->gps_good = atoi(chp);
  298. }
  299. else if (strncmp(buffer, "STAMP ", n)==0) {
  300. _state->stamp = (time_t)atoi(chp);
  301. }
  302. else if (strncmp(buffer, "GPSTAMP ", n)==0) {
  303. _state->gpstime = (time_t)atoi(chp);
  304. }
  305. else if (strncmp(buffer, "PADDLE ", n)==0) {
  306. _state->paddle = atoi(chp);
  307. }
  308. else if (strncmp(buffer, "ROUTE ", n)==0) {
  309. _state->route = atoi(chp);
  310. }
  311. else if (strncmp(buffer, "TRIP ", n)==0) {
  312. _state->trip = atoi(chp);
  313. }
  314. else if (strncmp(buffer, "STOP ", n)==0) {
  315. _state->stop = atoi(chp);
  316. }
  317. else if (strncmp(buffer, "STOPNAME ", n)==0) {
  318. field_len = input_idx - n;
  319. if (field_len >= STATE_INFO_FIELD_SIZE) {
  320. field_len = STATE_INFO_FIELD_SIZE-1;
  321. }
  322. memcpy(_state->stopname, chp, field_len);
  323. _state->stopname[field_len] = '\0';
  324. }
  325. else if (strncmp(buffer, "LOGGED_IN_DRIVER ", n)==0) {
  326. _state->logged_in_driver = atoi(chp);
  327. }
  328. else if (strncmp(buffer, "DRIVER_NAME ", n)==0) {
  329. field_len = input_idx - n;
  330. if (field_len >= STATE_INFO_FIELD_SIZE) {
  331. field_len = STATE_INFO_FIELD_SIZE-1;
  332. }
  333. memcpy(_state->driver_name, chp, input_idx - n + 1);
  334. _state->driver_name[field_len] = '\0';
  335. }
  336. else if (strncmp(buffer, "EQUIP_NUM ", n)==0) {
  337. _state->equip_num = atoi(chp);
  338. }
  339. }
  340. buffer[0] = '\0';
  341. input_idx = 0;
  342. continue;
  343. }
  344. buffer[input_idx] = ch;
  345. input_idx++;
  346. if (input_idx >= LINE_BUFFER_SIZE) {
  347. input_idx = LINE_BUFFER_SIZE-1;
  348. }
  349. buffer[input_idx] = '\0';
  350. }
  351. fclose(fp);
  352. return 0;
  353. }
  354. // Save `state_info_t` on disk.
  355. // Format it field name (in caps) separated by
  356. // a single space followed by the field value.
  357. //
  358. // Constructs the text state file as a temporary file
  359. // (specified by STATE_INFO_TEMPFILE) then moves it
  360. // after it's finished.
  361. //
  362. // return:
  363. // 0 on success
  364. // non-zero on error
  365. //
  366. int save_state_info(state_info_t *_state) {
  367. FILE *fp;
  368. if ((fp = fopen(STATE_INFO_TEMPFILE, "w")) == NULL) {
  369. return -1;
  370. }
  371. fprintf(fp, "# updated %lli\n", get_usec_time());
  372. fprintf(fp, "# %s\n", _state->comment);
  373. fprintf(fp, "LAT %f\n", _state->lat);
  374. fprintf(fp, "LON %f\n", _state->lon);
  375. fprintf(fp, "HEADING %f\n", _state->heading);
  376. fprintf(fp, "VELOCITY %f\n", _state->velocity);
  377. fprintf(fp, "NUM_SATS %i\n", _state->num_sats);
  378. fprintf(fp, "GPS_GOOD %i\n", _state->gps_good);
  379. fprintf(fp, "STAMP %i\n", (int)(_state->stamp));
  380. fprintf(fp, "GPSTAMP %i\n", (int)(_state->gpstime));
  381. fprintf(fp, "PADDLE %i\n", _state->paddle);
  382. fprintf(fp, "ROUTE %i\n", _state->route);
  383. fprintf(fp, "TRIP %i\n", _state->trip);
  384. fprintf(fp, "STOP %i\n", _state->stop);
  385. fprintf(fp, "STOPNAME %s\n", _state->stopname);
  386. fprintf(fp, "LOGGED_IN_DRIVER %i\n", _state->logged_in_driver);
  387. fprintf(fp, "DRIVER_NAME %s\n", _state->driver_name);
  388. fprintf(fp, "EQUIP_NUM %i\n", _state->equip_num);
  389. fclose(fp);
  390. return rename(STATE_INFO_TEMPFILE, STATE_INFO_FILE);
  391. }
  392. // print out the state information
  393. int print_state_info(state_info_t *_state) {
  394. FILE *fp = stdout;
  395. fprintf(fp, "LAT %f\n", _state->lat);
  396. fprintf(fp, "LON %f\n", _state->lon);
  397. fprintf(fp, "HEADING %f\n", _state->heading);
  398. fprintf(fp, "VELOCITY %f\n", _state->velocity);
  399. fprintf(fp, "NUM_SATS %i\n", _state->num_sats);
  400. fprintf(fp, "GPS_GOOD %i\n", _state->gps_good);
  401. fprintf(fp, "STAMP %i\n", (int)(_state->stamp));
  402. fprintf(fp, "GPSTAMP %i\n", (int)(_state->gpstime));
  403. fprintf(fp, "PADDLE %i\n", _state->paddle);
  404. fprintf(fp, "ROUTE %i\n", _state->route);
  405. fprintf(fp, "TRIP %i\n", _state->trip);
  406. fprintf(fp, "STOP %i\n", _state->stop);
  407. fprintf(fp, "STOPNAME %s\n", _state->stopname);
  408. fprintf(fp, "LOGGED_IN_DRIVER %i\n", _state->logged_in_driver);
  409. fprintf(fp, "DRIVER_NAME %s\n", _state->driver_name);
  410. fprintf(fp, "EQUIP_NUM %i\n", _state->equip_num);
  411. return 0;
  412. }