commhub.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. long long int get_usec_time()
  34. {
  35. struct timeval tv;
  36. //Get the number of seconds (and remainder microseconds) since epoch.
  37. gettimeofday(&tv,NULL);
  38. //Return those in a single sane 64 bit integer containing the total number
  39. //of microseconds since epoch by multiplying tv_sec by a million and summing
  40. //the two values
  41. return (long long int)tv.tv_usec + (1000000 * (long long int)tv.tv_sec);
  42. }
  43. //This function does a non-blocking poll to determine if the message passing socket is
  44. //ready for input or output (depending on
  45. //since it is much more efficient to block on the entire list of your I/O file descriptors
  46. //and service any that cause you to unblock.
  47. //
  48. // Returns: MSG_NO_ACTION = no message
  49. // MSG_ERROR = fatal error (passed fd is no good)
  50. //
  51. // If the socket is ready, the return value will be
  52. // whatever combination of events specified in mask (MSG_SEND, MSG_RECV)
  53. // that is actually ready.
  54. //
  55. int message_socket_status(int message_socket_fd, int mask)
  56. {
  57. struct pollfd fds[1];
  58. int retval;
  59. fds[0].fd = message_socket_fd;
  60. do
  61. {
  62. fds[0].events = mask;
  63. retval = poll(fds,1,0); //poll the 1 file descriptor, returning immediately.
  64. if(retval < 0)
  65. {
  66. //if we were interrupted by a signal, try again...
  67. if(errno == EINTR)
  68. continue;
  69. //otherwise poll() has failed, which is BAD news.
  70. return MSG_ERROR;
  71. }
  72. if(retval == 0)
  73. {
  74. return MSG_NO_ACTION;
  75. }
  76. //If poll tells us that the fd is invalid or has encountered a serious error, return -1.
  77. if(fds[0].revents & (POLLERR | POLLNVAL))
  78. {
  79. return MSG_ERROR;
  80. }
  81. //If any of the events supplied in mask have occurred, report them.
  82. if(fds[0].revents & mask)
  83. {
  84. return fds[0].revents & mask;
  85. }
  86. //if there are no more message to drain (see above) and a hangup has occurred,
  87. //let the user know the socket is done for.
  88. if(fds[0].revents & POLLHUP)
  89. {
  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. int prepare_message(struct message_record *target, char *mailbox, void *payload, unsigned int payload_len)
  99. {
  100. if(target == NULL)
  101. return -1;
  102. if(mailbox == NULL)
  103. return -1;
  104. if(payload == NULL)
  105. return -1;
  106. if(payload_len > MAX_PAYLOAD_LENGTH)
  107. return -1;
  108. memset(target, 0, sizeof(struct message_record));
  109. strncpy(target->header.mailbox_name, mailbox, MAILBOX_NAME_MAX);
  110. target->header.mailbox_name[MAILBOX_NAME_MAX] = '\0';
  111. target->header.usec_time = get_usec_time();
  112. target->header.payload_length = payload_len;
  113. target->header.sender = getpid();
  114. target->header.from_fd = 0;
  115. memmove(target->payload, payload, payload_len);
  116. return 0;
  117. }
  118. //the send_message function will transmit a message and its header (trimmed to the specified payload length).
  119. //zero will be returned on success, -1 on failure.
  120. int send_message(int message_socket_fd, struct message_record *message)
  121. {
  122. int txlen;
  123. int retval;
  124. if(message == NULL)
  125. return -1;
  126. //If we are trying to send a message that is longer than allowed, throw an error!
  127. if(message->header.payload_length > MAX_PAYLOAD_LENGTH)
  128. return -1;
  129. //Calculate the total message length (header + payload) so that we don't send
  130. //any extra padding that we don't need.
  131. txlen = sizeof(struct message_header_record) + message->header.payload_length;
  132. do
  133. {
  134. retval = send(message_socket_fd, message, txlen, MSG_EOR);
  135. //if we were interrupted by a signal, try again.
  136. if( (retval == -1) && (errno == EINTR) )
  137. {
  138. continue;
  139. }
  140. //if we tried to send a message and it got truncated, return failure.
  141. if(retval != txlen)
  142. {
  143. return -1;
  144. }
  145. return 0;
  146. } while(1);
  147. }
  148. //the get_message function will receive a message from the opposite end of the socket and place it in the
  149. //supplied message structure incliding its header. -1 is returned on failure, 0 on success.
  150. int get_message(int message_socket_fd, struct message_record *message)
  151. {
  152. int retval;
  153. if(message == NULL)
  154. return -1;
  155. //clear our buffer of any garbage before using it to read in the message
  156. memset(message, 0, sizeof(struct message_record));
  157. do
  158. {
  159. //try and receive our message
  160. retval = recv(message_socket_fd, message, sizeof(struct message_record), 0);
  161. //if we were interrupted by a signal, try again.
  162. if( (retval == -1) && (errno == EINTR) )
  163. {
  164. continue;
  165. }
  166. //zero is a special case for recv which indicates that the other side has shut down the connection.
  167. if(retval == 0)
  168. {
  169. return -1;
  170. }
  171. //if the received message size does not add up (between header and payload)
  172. if(retval != (sizeof(struct message_header_record) + message->header.payload_length))
  173. {
  174. return -1; //throw an error!
  175. }
  176. //Record which IPC socket we got this from (for things like PING replies)
  177. message->header.from_fd = message_socket_fd;
  178. return 0; //otherwise, return success
  179. } while(1);
  180. }
  181. //This function connects to the communication hub and returns the file descriptor of the connection.
  182. //If the connection failed, -1 is returned. The progname parameter (if not NULL) will be used to
  183. //register a module name (snagged from argv[0]) with the server for ease of debugging.
  184. int connect_to_message_server(char *progname)
  185. {
  186. int fd;
  187. int retval;
  188. int len;
  189. char *message_text;
  190. struct sockaddr_un addr;
  191. struct message_record msg;
  192. fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
  193. if(fd < 0)
  194. return -1;
  195. //construct our address structure (pointing to the agreed upon server address)
  196. //to pass to connect.
  197. addr.sun_family = AF_UNIX;
  198. strncpy(addr.sun_path, COMMHUB_ADDRESS, sizeof(addr.sun_path) - 1);
  199. addr.sun_path[sizeof(addr.sun_path) - 1]='\0';
  200. len=strlen(COMMHUB_ADDRESS) + sizeof(addr.sun_family);
  201. //attempt to connect to said server
  202. retval = connect(fd,(struct sockaddr *)&addr,len);
  203. if(retval)
  204. {
  205. fprintf(stderr, "Cannot connect to IPC hub at %s\n", COMMHUB_ADDRESS);
  206. close(fd);
  207. return -1;
  208. }
  209. //if we have been passed a module name to register as, use that, otherwise default to "ANONYMOUS"
  210. if(progname)
  211. {
  212. message_text = progname;
  213. }
  214. else
  215. {
  216. message_text = "ANONYMOUS";
  217. }
  218. len = strlen(message_text);
  219. //if for some inconceivable reason our module name is longer than the server allows, truncate it.
  220. if(len > MAX_MODULE_NAME_LENGTH)
  221. {
  222. len = MAX_MODULE_NAME_LENGTH;
  223. }
  224. //prepare and send our registation message
  225. prepare_message(&msg, MAILBOX_HELLO, message_text, len);
  226. send_message(fd, &msg);
  227. return fd;
  228. }
  229. //---------------------------------------------------------------------