PageRenderTime 93ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/comm.c

https://github.com/ncsurobotics/libseawolf
C | 613 lines | 291 code | 105 blank | 217 comment | 54 complexity | 73da922a30b325feccd7ba4fa7b184c2 MD5 | raw file
  1. /**
  2. * \file
  3. * \brief Low-level communication
  4. */
  5. #include "seawolf.h"
  6. #include "seawolf/mem_pool.h"
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <pthread.h>
  10. #include <sys/socket.h>
  11. /**
  12. * \defgroup Comm Low-level communication
  13. * \ingroup Communications
  14. * \brief Provides support for sending and receiving messages directly to and from the hub
  15. * \{
  16. */
  17. /**
  18. * \cond Comm_Private
  19. * \internal
  20. */
  21. /**
  22. * Maximum number of consective errors allowed while attempting to receive data
  23. * from the hub before the application is terminated
  24. */
  25. #define MAX_RECEIVE_ERROR 5
  26. /**
  27. * The response set starts out with this many space and will grow by this ammount any time more room is needed
  28. */
  29. #define RESPONSE_SET_GROW 8
  30. /**
  31. * Maximum request ID that can be embedded in a message. 16-bits in the packed
  32. * message are reserved for the request ID allowing values up to this
  33. */
  34. #define MAX_REQUEST_ID ((uint32_t)0xffff)
  35. /** IP address of server to connect to */
  36. static char* comm_server = NULL;
  37. /** Port of server to connect to */
  38. static uint16_t comm_port = 31427;
  39. /** Password to authenticate using */
  40. static char* auth_password = NULL;
  41. /** The actual socket file descriptor */
  42. static int comm_socket;
  43. /** Task handle for thread that recieves incoming messages */
  44. static Task_Handle receive_thread;
  45. /** Component initialization status */
  46. static bool initialized = false;
  47. /** Signals that the current application has been disconnected from the hub */
  48. static bool hub_shutdown = false;
  49. /** Current size of the response set table */
  50. static size_t response_set_size = RESPONSE_SET_GROW;
  51. /** The response set table itself */
  52. static Comm_Message** response_set = NULL;
  53. /** Specifies whether a response is already pending for a given ID so as to not
  54. reissue that ID before a response is returned */
  55. static bool* response_pending = NULL;
  56. /** Response set mutex lock */
  57. static pthread_mutex_t response_set_lock = PTHREAD_MUTEX_INITIALIZER;
  58. /** New response available conditional */
  59. static pthread_cond_t new_response = PTHREAD_COND_INITIALIZER;
  60. static void Comm_authenticate(void);
  61. static Comm_PackedMessage* Comm_receivePackedMessage(void);
  62. static int Comm_receiveThread(void);
  63. /**
  64. * \endcond Comm_Private
  65. */
  66. /**
  67. * \brief Initialize the Comm component
  68. *
  69. * Initialize the Comm component by connecting a hub server at the configured
  70. * server and port and attempt to authenticate
  71. *
  72. * \private
  73. */
  74. void Comm_init(void) {
  75. struct sockaddr_in addr;
  76. if(comm_server == NULL) {
  77. Logging_log(CRITICAL, "No Comm_server address is set!");
  78. Seawolf_exitError();
  79. }
  80. /* Build connection address */
  81. addr.sin_family = AF_INET;
  82. addr.sin_addr.s_addr = inet_addr(comm_server);
  83. addr.sin_port = htons(comm_port);
  84. /* Create socket */
  85. comm_socket = socket(AF_INET, SOCK_STREAM, 0);
  86. if(comm_socket == -1) {
  87. Logging_log(CRITICAL, __Util_format("Unable to create socket: %s", strerror(errno)));
  88. Seawolf_exitError();
  89. }
  90. /* Connect socket */
  91. if(connect(comm_socket, (struct sockaddr*) &addr, sizeof(addr))) {
  92. Logging_log(CRITICAL, __Util_format("Unable to connect to Comm server: %s", strerror(errno)));
  93. Seawolf_exitError();
  94. }
  95. /* Prepare response set */
  96. response_set = calloc(response_set_size, sizeof(Comm_Message*));
  97. response_pending = calloc(response_set_size, sizeof(bool));
  98. /* Run receive thread */
  99. initialized = true;
  100. receive_thread = Task_background(&Comm_receiveThread);
  101. /* Authenticate */
  102. Comm_authenticate();
  103. }
  104. /**
  105. * \brief Perform authentication with the hub
  106. *
  107. * Authenticate with the hub server using the password specified by a call to
  108. * Comm_setPassword()
  109. */
  110. static void Comm_authenticate(void) {
  111. static char* namespace = "COMM";
  112. static char* command = "AUTH";
  113. if(auth_password) {
  114. Comm_Message* auth_message = Comm_Message_new(3);
  115. Comm_Message* response;
  116. auth_message->components[0] = namespace;
  117. auth_message->components[1] = command;
  118. auth_message->components[2] = auth_password;
  119. Comm_assignRequestID(auth_message);
  120. response = Comm_sendMessage(auth_message);
  121. if(response == NULL || strcmp(response->components[1], "SUCCESS") != 0) {
  122. Logging_log(CRITICAL, "Failed to authenticate with hub server!");
  123. } else {
  124. MemPool_free(auth_message->alloc);
  125. MemPool_free(response->alloc);
  126. return;
  127. }
  128. } else {
  129. Logging_log(CRITICAL, "No Comm_password set. Unable to connect to Comm server");
  130. }
  131. Seawolf_exitError();
  132. }
  133. /**
  134. * \brief Receive a packed message from the hub socket
  135. *
  136. * Receive a message from the hub and return at Comm_PackedMessage object
  137. * representing this received object
  138. *
  139. * \return A new Comm_PackedMessage object
  140. */
  141. static Comm_PackedMessage* Comm_receivePackedMessage(void) {
  142. Comm_PackedMessage* packed_message;
  143. uint16_t total_data_size;
  144. int n;
  145. n = recv(comm_socket, &total_data_size, sizeof(uint16_t), MSG_WAITALL|MSG_PEEK);
  146. if(n != sizeof(uint16_t)) {
  147. return NULL;
  148. }
  149. total_data_size = ntohs(total_data_size);
  150. packed_message = Comm_PackedMessage_new();
  151. packed_message->length = total_data_size + COMM_MESSAGE_PREFIX_LEN;
  152. packed_message->data = MemPool_reserve(packed_message->alloc, packed_message->length);
  153. n = recv(comm_socket, packed_message->data, packed_message->length, MSG_WAITALL);
  154. if(n != packed_message->length) {
  155. MemPool_free(packed_message->alloc);
  156. return NULL;
  157. }
  158. return packed_message;
  159. }
  160. /**
  161. * \brief Message receive loop
  162. *
  163. * Spawned by Comm_init() to receive incoming messages and process/queue them
  164. *
  165. * \return Returns 0 when shutting down (after a call to Comm_close())
  166. */
  167. static int Comm_receiveThread(void) {
  168. Comm_PackedMessage* packed_message;
  169. Comm_Message* message;
  170. unsigned short error_count = 0;
  171. while(initialized) {
  172. packed_message = Comm_receivePackedMessage();
  173. /* Receive error */
  174. if(packed_message == NULL) {
  175. if(Seawolf_closing()) {
  176. /* Library is closing and we've already been disconnected from
  177. the hub. Specify that the hub is gone and exit the main
  178. loop */
  179. hub_shutdown = true;
  180. break;
  181. }
  182. error_count++;
  183. if(error_count > MAX_RECEIVE_ERROR) {
  184. hub_shutdown = true;
  185. Logging_log(CRITICAL, "Excessive read errors (lost connection to hub), terminating!");
  186. Seawolf_exitError();
  187. /* It's possible for Seawolf_exitError to have been called
  188. elsewhere which presents a race condition. If
  189. Seawolf_exitError returns then ensure we exit the loop */
  190. break;
  191. }
  192. continue;
  193. }
  194. /* Received good packet, reset error count */
  195. error_count = 0;
  196. /* Unpack message */
  197. message = Comm_unpackMessage(packed_message);
  198. if(message->request_id != 0) {
  199. pthread_mutex_lock(&response_set_lock);
  200. response_set[message->request_id] = message;
  201. pthread_cond_broadcast(&new_response);
  202. pthread_mutex_unlock(&response_set_lock);
  203. } else if(strcmp(message->components[0], "NOTIFY") == 0) {
  204. /* Inbound notification */
  205. Notify_inputMessage(message);
  206. } else if(strcmp(message->components[0], "WATCH") == 0) {
  207. /* Inbound variable subscription udpdate */
  208. Var_inputMessage(message);
  209. } else if(strcmp(message->components[0], "COMM") == 0) {
  210. if(strcmp(message->components[1], "KICKING") == 0) {
  211. hub_shutdown = true;
  212. Logging_log(ERROR, __Util_format("I've been kicked: %s", message->components[2]));
  213. Seawolf_exitError();
  214. }
  215. MemPool_free(message->alloc);
  216. } else {
  217. /* Unknown, unsolicited message */
  218. MemPool_free(message->alloc);
  219. }
  220. }
  221. /* Wake up any stuck Comm_sendMessage call */
  222. pthread_mutex_lock(&response_set_lock);
  223. pthread_cond_broadcast(&new_response);
  224. pthread_mutex_unlock(&response_set_lock);
  225. return 0;
  226. }
  227. /**
  228. * \brief Send a message to the hub
  229. *
  230. * Send a message given as a Comm_Message to the connected hub after
  231. * packing. If a response is expected, block until the response is received and
  232. * return it.
  233. *
  234. * \param message A pointer to a Comm_Message representing the message to be
  235. * sent
  236. * \return If a response is expected, block until the response is available and
  237. * return the unpacked response. Otherwise, return NULL
  238. */
  239. Comm_Message* Comm_sendMessage(Comm_Message* message) {
  240. static pthread_mutex_t send_lock = PTHREAD_MUTEX_INITIALIZER;
  241. Comm_PackedMessage* packed_message;
  242. Comm_Message* response = NULL;
  243. int n;
  244. if(hub_shutdown) {
  245. return NULL;
  246. }
  247. /* Pack message */
  248. packed_message = Comm_packMessage(message);
  249. /* Send data */
  250. pthread_mutex_lock(&send_lock);
  251. n = send(comm_socket, packed_message->data, packed_message->length, 0);
  252. pthread_mutex_unlock(&send_lock);
  253. /* Send error */
  254. if(n < 0) {
  255. hub_shutdown = true;
  256. Logging_log(CRITICAL, "Unable to send message (lost connection to hub), terminating!");
  257. Seawolf_exitError();
  258. return NULL;
  259. }
  260. /* Expect a response and wait for it */
  261. if(message->request_id != 0) {
  262. pthread_mutex_lock(&response_set_lock);
  263. while(response_set[message->request_id] == NULL) {
  264. /* Woken up during shutdown. Return NULL */
  265. if(hub_shutdown) {
  266. return NULL;
  267. }
  268. pthread_cond_wait(&new_response, &response_set_lock);
  269. }
  270. response = response_set[message->request_id];
  271. response_pending[message->request_id] = false;
  272. response_set[message->request_id] = NULL;
  273. pthread_mutex_unlock(&response_set_lock);
  274. }
  275. return response;
  276. }
  277. /**
  278. * \brief Assign a ID for a request message
  279. *
  280. * If a message is to be sent and requires a responses than a request ID must be
  281. * assigned to the message
  282. *
  283. * \param message The message to assign an ID to
  284. */
  285. void Comm_assignRequestID(Comm_Message* message) {
  286. static uint32_t last_id = 1;
  287. pthread_mutex_lock(&response_set_lock);
  288. message->request_id = last_id;
  289. while(response_pending[message->request_id] == true) {
  290. message->request_id = (message->request_id % (response_set_size - 1)) + 1;
  291. /* Every available ID is taken, make space for more and make the
  292. response ID the next available one */
  293. if(message->request_id == last_id && response_set_size + RESPONSE_SET_GROW < MAX_REQUEST_ID) {
  294. last_id = response_set_size;
  295. message->request_id = last_id;
  296. response_set = realloc(response_set, sizeof(Comm_Message*) * (response_set_size + RESPONSE_SET_GROW));
  297. response_pending = realloc(response_pending, sizeof(bool) * (response_set_size + RESPONSE_SET_GROW));
  298. memset(response_set + response_set_size, 0, RESPONSE_SET_GROW * sizeof(Comm_Message*));
  299. memset(response_pending + response_set_size, 0, RESPONSE_SET_GROW * sizeof(bool));
  300. response_set_size += RESPONSE_SET_GROW;
  301. }
  302. }
  303. response_set[message->request_id] = NULL;
  304. response_pending[message->request_id] = true;
  305. pthread_mutex_unlock(&response_set_lock);
  306. }
  307. /**
  308. * \brief Pack a message
  309. *
  310. * Return a packed message constructed from the given message
  311. *
  312. * \param message The message to packe
  313. * \return The packed equivalent of message
  314. */
  315. Comm_PackedMessage* Comm_packMessage(Comm_Message* message) {
  316. Comm_PackedMessage* packed_message = Comm_PackedMessage_newWithAlloc(message->alloc);
  317. size_t total_data_length = 0;
  318. size_t* component_lengths = MemPool_reserve(message->alloc, sizeof(size_t) * message->count);
  319. char* buffer;
  320. int i;
  321. /* Add length of each message and space for a null terminator for each */
  322. for(i = 0; i < message->count; i++) {
  323. component_lengths[i] = strlen(message->components[i]) + 1;
  324. total_data_length += component_lengths[i];
  325. }
  326. /* Store message information */
  327. packed_message->length = total_data_length + COMM_MESSAGE_PREFIX_LEN;
  328. packed_message->data = MemPool_reserve(message->alloc, packed_message->length);
  329. /* Build packed message header */
  330. ((uint16_t*)packed_message->data)[0] = htons(total_data_length);
  331. ((uint16_t*)packed_message->data)[1] = htons(message->request_id);
  332. ((uint16_t*)packed_message->data)[2] = htons(message->count);
  333. /* Copy message components */
  334. buffer = packed_message->data + COMM_MESSAGE_PREFIX_LEN;
  335. for(i = 0; i < message->count; i++) {
  336. memcpy(buffer, message->components[i], component_lengths[i]);
  337. buffer += component_lengths[i];
  338. }
  339. return packed_message;
  340. }
  341. /**
  342. * \brief Unpack a message
  343. *
  344. * Unpack and return the given packed message. The returned message can be freed
  345. * with a call to Comm_Message_destroyUnpacked()
  346. *
  347. * \param packed_message A packed message to unpack
  348. * \return The unpacked message
  349. */
  350. Comm_Message* Comm_unpackMessage(Comm_PackedMessage* packed_message) {
  351. Comm_Message* message = Comm_Message_newWithAlloc(packed_message->alloc, 0);
  352. size_t data_length = ntohs(((uint16_t*)packed_message->data)[0]);
  353. /* Build message meta information */
  354. message->request_id = ntohs(((uint16_t*)packed_message->data)[1]);
  355. message->count = ntohs(((uint16_t*)packed_message->data)[2]);
  356. if(message->count == 0) {
  357. message->components = NULL;
  358. return message;
  359. }
  360. message->components = MemPool_reserve(message->alloc, sizeof(char*) * message->count);
  361. /* Extract components -- we allocate all the space to the first and use the
  362. rest of the elements as indexes */
  363. message->components[0] = MemPool_reserve(message->alloc, data_length);
  364. memcpy(message->components[0], packed_message->data + COMM_MESSAGE_PREFIX_LEN, data_length);
  365. /* Point the rest of the components into the space allocated to the first */
  366. for(int i = 1; i < message->count; i++) {
  367. message->components[i] = message->components[i-1] + strlen(message->components[i-1]) + 1;
  368. }
  369. return message;
  370. }
  371. /**
  372. * \brief Create a new message
  373. *
  374. * Create a new message with space for the given number of components. Space is
  375. * only allocated for the char pointers to the components, not to the
  376. * components themselves. Space for the components should be allocated and freed
  377. * separately.
  378. *
  379. * \param alloc The MemPool allocation to allocate space for this message from
  380. * \param component_count The number of components to make space for. If component_count is 0, no allocation is done
  381. * \return A new message
  382. */
  383. Comm_Message* Comm_Message_newWithAlloc(MemPool_Alloc* alloc, unsigned int component_count) {
  384. Comm_Message* message = MemPool_reserve(alloc, sizeof(Comm_Message));
  385. message->request_id = 0;
  386. message->count = component_count;
  387. message->components = NULL;
  388. message->alloc = alloc;
  389. if(component_count) {
  390. message->components = MemPool_reserve(alloc, sizeof(char*) * component_count);
  391. }
  392. return message;
  393. }
  394. /**
  395. * \brief Create a new message
  396. *
  397. * Allocate and return a new message using a new allocation
  398. *
  399. * \param component_count The number of components to make space for
  400. * \return The newly allocated message
  401. * \see Comm_Message_newWithAlloc
  402. */
  403. Comm_Message* Comm_Message_new(unsigned int component_count) {
  404. MemPool_Alloc* alloc = MemPool_alloc();
  405. return Comm_Message_newWithAlloc(alloc, component_count);
  406. }
  407. /**
  408. * \brief Create a new packed message object
  409. *
  410. * Return a new, emtpy packed message. No space is allocated to store data and
  411. * this should be allocated separately.
  412. *
  413. * \param alloc The MemPool allocation to allocate space for this message from
  414. * \return A new packed message object
  415. */
  416. Comm_PackedMessage* Comm_PackedMessage_newWithAlloc(MemPool_Alloc* alloc) {
  417. Comm_PackedMessage* packed_message = MemPool_reserve(alloc, sizeof(Comm_PackedMessage));
  418. packed_message->length = 0;
  419. packed_message->data = NULL;
  420. packed_message->alloc = alloc;
  421. return packed_message;
  422. }
  423. /**
  424. * \brief Create a new packed message object
  425. *
  426. * Return a new packed message object allocated from a new MemPool allocation
  427. *
  428. * \return A new packed message object
  429. * \see Comm_PackedMessage_newWithAlloc
  430. */
  431. Comm_PackedMessage* Comm_PackedMessage_new(void) {
  432. MemPool_Alloc* alloc = MemPool_alloc();
  433. return Comm_PackedMessage_newWithAlloc(alloc);
  434. }
  435. /**
  436. * \brief Set the hub password
  437. *
  438. * Set the password to use when authenticating with the hub
  439. *
  440. * \param password The password to authenticate with
  441. */
  442. void Comm_setPassword(const char* password) {
  443. auth_password = strdup(password);
  444. }
  445. /**
  446. * \brief Set the server to connect to
  447. *
  448. * Specify the server to connect to as an IP address given as a string
  449. *
  450. * \param server The IP address of the server to connect to given as a string
  451. */
  452. void Comm_setServer(const char* server) {
  453. comm_server = strdup(server);
  454. }
  455. /**
  456. * \brief Set the hub server port
  457. *
  458. * Specify the port to connect to when connceting to the hub
  459. *
  460. * \param port The port number to connect to
  461. */
  462. void Comm_setPort(uint16_t port) {
  463. comm_port = port;
  464. }
  465. /**
  466. * \brief Destroy a message
  467. *
  468. * Free all memory associated with a message and any packed message derived from
  469. * this message
  470. *
  471. * \param message The message to free
  472. */
  473. void Comm_Message_destroy(Comm_Message* message) {
  474. MemPool_free(message->alloc);
  475. }
  476. /**
  477. * \brief Close the Comm component
  478. *
  479. * Close the Comm component; close all connections, free memory, etc.
  480. *
  481. * \private
  482. */
  483. void Comm_close(void) {
  484. Comm_Message* message;
  485. /* This check is necessary if an error condition is reached in Comm_init */
  486. if(initialized) {
  487. if(!hub_shutdown) {
  488. message = Comm_Message_new(2);
  489. message->components[0] = MemPool_strdup(message->alloc, "COMM");
  490. message->components[1] = MemPool_strdup(message->alloc, "SHUTDOWN");
  491. Comm_assignRequestID(message);
  492. Comm_sendMessage(message);
  493. MemPool_free(message->alloc);
  494. }
  495. shutdown(comm_socket, SHUT_RDWR);
  496. Task_wait(receive_thread);
  497. free(response_set);
  498. free(response_pending);
  499. initialized = false;
  500. }
  501. if(comm_server) {
  502. free(comm_server);
  503. }
  504. if(auth_password) {
  505. free(auth_password);
  506. }
  507. }
  508. /** \} */