2
0

client_manager.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2013 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file util/client_manager.c
  19. * @brief Client manager; higher level client API with transmission queue
  20. * and message handler registration.
  21. * @author Gabor X Toth
  22. */
  23. #include <inttypes.h>
  24. #include "platform.h"
  25. #include "gnunet_util_lib.h"
  26. #define LOG(kind,...) GNUNET_log_from (kind, "util-client-mgr", __VA_ARGS__)
  27. /**
  28. * List of arrays of message handlers.
  29. */
  30. struct HandlersListItem
  31. {
  32. struct HandlersListItem *prev;
  33. struct HandlersListItem *next;
  34. /**
  35. * NULL-terminated array of handlers.
  36. */
  37. const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
  38. };
  39. struct MessageQueueItem
  40. {
  41. struct MessageQueueItem *prev;
  42. struct MessageQueueItem *next;
  43. struct GNUNET_MessageHeader *msg;
  44. };
  45. struct GNUNET_CLIENT_MANAGER_Connection
  46. {
  47. /**
  48. * Configuration to use.
  49. */
  50. const struct GNUNET_CONFIGURATION_Handle *cfg;
  51. /**
  52. * Client connection to service.
  53. */
  54. struct GNUNET_CLIENT_Connection *client;
  55. /**
  56. * Currently pending transmission request, or NULL for none.
  57. */
  58. struct GNUNET_CLIENT_TransmitHandle *client_tmit;
  59. /**
  60. * Service name to connect to.
  61. */
  62. const char *service_name;
  63. /**
  64. * Head of messages to transmit to the service.
  65. */
  66. struct MessageQueueItem *tmit_head;
  67. /**
  68. * Tail of messages to transmit to the service.
  69. */
  70. struct MessageQueueItem *tmit_tail;
  71. /**
  72. * Message handlers.
  73. */
  74. const struct GNUNET_CLIENT_MANAGER_MessageHandler *handlers;
  75. /**
  76. * Disconnect callback.
  77. */
  78. void (*disconnect_cb)(void *);
  79. /**
  80. * Disconnect closure.
  81. */
  82. void *disconnect_cls;
  83. /**
  84. * User context value.
  85. * @see GNUNET_CLIENT_MANAGER_set_user_context()
  86. * @see GNUNET_CLIENT_MANAGER_get_user_context()
  87. */
  88. void *user_ctx;
  89. /**
  90. * Last size given when user context was initialized.
  91. * Used for sanity check.
  92. */
  93. size_t user_ctx_size;
  94. /**
  95. * Task doing exponential back-off trying to reconnect.
  96. */
  97. GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
  98. /**
  99. * Time for next connect retry.
  100. */
  101. struct GNUNET_TIME_Relative reconnect_delay;
  102. /**
  103. * Are we currently polling for incoming messages?
  104. */
  105. uint8_t in_receive;
  106. /**
  107. * #GNUNET_YES if GNUNET_CLIENT_MANAGER_disconnect() was called
  108. * and we're transmitting the last messages from the queue.
  109. */
  110. uint8_t is_disconnecting;
  111. };
  112. /**
  113. * Handle received messages from the service.
  114. */
  115. static void
  116. recv_message (void *cls, const struct GNUNET_MessageHeader *msg)
  117. {
  118. struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
  119. uint16_t type = 0, size = 0;
  120. if (NULL != msg)
  121. {
  122. type = ntohs (msg->type);
  123. size = ntohs (msg->size);
  124. /* FIXME: decrease reconnect_delay gradually after a successful reconnection */
  125. }
  126. size_t i = 0;
  127. while (NULL != mgr->handlers[i].callback)
  128. {
  129. const struct GNUNET_CLIENT_MANAGER_MessageHandler *mh = &mgr->handlers[i];
  130. if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
  131. {
  132. if (0 != mh->expected_size
  133. && ((GNUNET_NO == mh->is_variable_size && size != mh->expected_size)
  134. || (GNUNET_YES == mh->is_variable_size && size < mh->expected_size)))
  135. {
  136. LOG (GNUNET_ERROR_TYPE_ERROR,
  137. "Expected %u bytes for message of type %u, got %u.\n",
  138. mh->expected_size, type, size);
  139. GNUNET_break_op (0);
  140. GNUNET_CLIENT_disconnect (mgr->client);
  141. mgr->client = NULL;
  142. recv_message (mgr, NULL);
  143. break;
  144. }
  145. mh->callback (mh->callback_cls, mgr, msg);
  146. }
  147. i++;
  148. }
  149. if (NULL != mgr->client)
  150. {
  151. GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
  152. GNUNET_TIME_UNIT_FOREVER_REL);
  153. }
  154. }
  155. /**
  156. * Schedule transmission of the next message from our queue.
  157. *
  158. * @param mgr Client manager connection.
  159. */
  160. static void
  161. transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr);
  162. static void
  163. schedule_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  164. {
  165. struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
  166. GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
  167. mgr->disconnect_cb, mgr->disconnect_cls);
  168. }
  169. /**
  170. * Transmit next message to service.
  171. *
  172. * @param cls
  173. * struct GNUNET_CLIENT_MANAGER_Connection
  174. * @param size
  175. * Number of bytes available in @a buf.
  176. * @param buf
  177. * Where to copy the message.
  178. *
  179. * @return Number of bytes copied to @a buf.
  180. */
  181. static size_t
  182. send_next_message (void *cls, size_t buf_size, void *buf)
  183. {
  184. LOG (GNUNET_ERROR_TYPE_DEBUG, "send_next_message()\n");
  185. struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
  186. if (NULL == buf)
  187. {
  188. /* disconnected */
  189. recv_message (mgr, NULL);
  190. return 0;
  191. }
  192. struct MessageQueueItem *mqi = mgr->tmit_head;
  193. if (NULL == mqi)
  194. return 0;
  195. uint16_t size = ntohs (mqi->msg->size);
  196. mgr->client_tmit = NULL;
  197. GNUNET_assert (size <= buf_size);
  198. memcpy (buf, mqi->msg, size);
  199. GNUNET_CONTAINER_DLL_remove (mgr->tmit_head, mgr->tmit_tail, mqi);
  200. GNUNET_free (mqi->msg);
  201. GNUNET_free (mqi);
  202. if (NULL != mgr->tmit_head)
  203. {
  204. transmit_next (mgr);
  205. }
  206. else if (GNUNET_YES == mgr->is_disconnecting)
  207. {
  208. GNUNET_SCHEDULER_add_now (&schedule_disconnect, mgr);
  209. return size;
  210. }
  211. if (GNUNET_NO == mgr->in_receive)
  212. {
  213. mgr->in_receive = GNUNET_YES;
  214. GNUNET_CLIENT_receive (mgr->client, &recv_message, mgr,
  215. GNUNET_TIME_UNIT_FOREVER_REL);
  216. }
  217. return size;
  218. }
  219. /**
  220. * Schedule transmission of the next message from our queue.
  221. *
  222. * @param mgr Client manager connection.
  223. */
  224. static void
  225. transmit_next (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
  226. {
  227. LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_next()\n");
  228. if (NULL != mgr->client_tmit || NULL == mgr->client)
  229. return;
  230. if (NULL == mgr->tmit_head)
  231. {
  232. if (GNUNET_YES == mgr->is_disconnecting)
  233. GNUNET_CLIENT_MANAGER_disconnect (mgr, GNUNET_NO,
  234. mgr->disconnect_cb, mgr->disconnect_cls);
  235. return;
  236. }
  237. mgr->client_tmit
  238. = GNUNET_CLIENT_notify_transmit_ready (mgr->client,
  239. ntohs (mgr->tmit_head->msg->size),
  240. GNUNET_TIME_UNIT_FOREVER_REL,
  241. GNUNET_NO,
  242. &send_next_message,
  243. mgr);
  244. }
  245. /**
  246. * Try again to connect to the service.
  247. *
  248. * @param cls
  249. * Channel handle.
  250. * @param tc
  251. * Scheduler context.
  252. */
  253. static void
  254. schedule_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  255. {
  256. struct GNUNET_CLIENT_MANAGER_Connection *mgr = cls;
  257. mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
  258. LOG (GNUNET_ERROR_TYPE_DEBUG,
  259. "Connecting to %s service.\n", mgr->service_name);
  260. GNUNET_assert (NULL == mgr->client);
  261. mgr->client = GNUNET_CLIENT_connect (mgr->service_name, mgr->cfg);
  262. GNUNET_assert (NULL != mgr->client);
  263. transmit_next (mgr);
  264. }
  265. /**
  266. * Connect to service.
  267. *
  268. * @param cfg
  269. * Configuration to use.
  270. * @param service_name
  271. * Service name to connect to.
  272. * @param handlers
  273. * Message handlers.
  274. *
  275. * @return Client manager connection handle.
  276. */
  277. struct GNUNET_CLIENT_MANAGER_Connection *
  278. GNUNET_CLIENT_MANAGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
  279. const char *service_name,
  280. const struct
  281. GNUNET_CLIENT_MANAGER_MessageHandler *handlers)
  282. {
  283. struct GNUNET_CLIENT_MANAGER_Connection *
  284. mgr = GNUNET_malloc (sizeof (*mgr));
  285. mgr->cfg = cfg;
  286. mgr->service_name = service_name;
  287. mgr->handlers = handlers;
  288. mgr->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
  289. mgr->reconnect_task = GNUNET_SCHEDULER_add_now (&schedule_reconnect, mgr);
  290. return mgr;
  291. }
  292. /**
  293. * Disconnect from the service.
  294. *
  295. * @param mgr
  296. * Client manager connection.
  297. * @param transmit_queue
  298. * Transmit pending messages in queue before disconnecting.
  299. * @param disconnect_cb
  300. * Function called after disconnected from the service.
  301. * @param cls
  302. * Closure for @a disconnect_cb.
  303. */
  304. void
  305. GNUNET_CLIENT_MANAGER_disconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
  306. int transmit_queue,
  307. GNUNET_ContinuationCallback disconnect_cb,
  308. void *cls)
  309. {
  310. LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting (%d)\n", transmit_queue);
  311. mgr->disconnect_cb = disconnect_cb;
  312. mgr->disconnect_cls = cls;
  313. if (NULL != mgr->tmit_head)
  314. {
  315. if (GNUNET_YES == transmit_queue)
  316. {
  317. mgr->is_disconnecting = GNUNET_YES;
  318. transmit_next (mgr);
  319. return;
  320. }
  321. else
  322. {
  323. LOG (GNUNET_ERROR_TYPE_DEBUG,
  324. "Disconnecting while there are still messages "
  325. "in the transmission queue.\n");
  326. GNUNET_CLIENT_MANAGER_drop_queue (mgr);
  327. }
  328. }
  329. if (mgr->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
  330. {
  331. GNUNET_SCHEDULER_cancel (mgr->reconnect_task);
  332. mgr->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
  333. }
  334. if (NULL != mgr->client_tmit)
  335. {
  336. GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
  337. mgr->client_tmit = NULL;
  338. }
  339. if (NULL != mgr->client)
  340. {
  341. GNUNET_CLIENT_disconnect (mgr->client);
  342. mgr->client = NULL;
  343. }
  344. if (NULL != mgr->disconnect_cb)
  345. mgr->disconnect_cb (mgr->disconnect_cls);
  346. GNUNET_free (mgr);
  347. LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnected.\n");
  348. }
  349. /**
  350. * Reschedule connect to the service using exponential back-off.
  351. *
  352. * @param mgr
  353. * Client manager connection.
  354. */
  355. void
  356. GNUNET_CLIENT_MANAGER_reconnect (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
  357. {
  358. if (GNUNET_SCHEDULER_NO_TASK != mgr->reconnect_task)
  359. return;
  360. if (NULL != mgr->client_tmit)
  361. {
  362. GNUNET_CLIENT_notify_transmit_ready_cancel (mgr->client_tmit);
  363. mgr->client_tmit = NULL;
  364. }
  365. if (NULL != mgr->client)
  366. {
  367. GNUNET_CLIENT_disconnect (mgr->client);
  368. mgr->client = NULL;
  369. }
  370. mgr->in_receive = GNUNET_NO;
  371. LOG (GNUNET_ERROR_TYPE_DEBUG,
  372. "Scheduling task to reconnect to service in %s.\n",
  373. GNUNET_STRINGS_relative_time_to_string (mgr->reconnect_delay, GNUNET_YES));
  374. mgr->reconnect_task =
  375. GNUNET_SCHEDULER_add_delayed (mgr->reconnect_delay, &schedule_reconnect, mgr);
  376. mgr->reconnect_delay = GNUNET_TIME_STD_BACKOFF (mgr->reconnect_delay);
  377. }
  378. /**
  379. * Add a message to the end of the transmission queue.
  380. *
  381. * @param mgr
  382. * Client manager connection.
  383. * @param msg
  384. * Message to transmit, should be allocated with GNUNET_malloc() or
  385. * GNUNET_new(), as it is freed with GNUNET_free() after transmission.
  386. */
  387. void
  388. GNUNET_CLIENT_MANAGER_transmit (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
  389. struct GNUNET_MessageHeader *msg)
  390. {
  391. struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
  392. mqi->msg = msg;
  393. GNUNET_CONTAINER_DLL_insert_tail (mgr->tmit_head, mgr->tmit_tail, mqi);
  394. transmit_next (mgr);
  395. }
  396. /**
  397. * Add a message to the beginning of the transmission queue.
  398. *
  399. * @param mgr
  400. * Client manager connection.
  401. * @param msg
  402. * Message to transmit, should be allocated with GNUNET_malloc() or
  403. * GNUNET_new(), as it is freed with GNUNET_free() after transmission.
  404. */
  405. void
  406. GNUNET_CLIENT_MANAGER_transmit_now (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
  407. struct GNUNET_MessageHeader *msg)
  408. {
  409. struct MessageQueueItem *mqi = GNUNET_malloc (sizeof (*mqi));
  410. mqi->msg = msg;
  411. GNUNET_CONTAINER_DLL_insert (mgr->tmit_head, mgr->tmit_tail, mqi);
  412. transmit_next (mgr);
  413. }
  414. /**
  415. * Drop all queued messages.
  416. *
  417. * @param mgr
  418. * Client manager connection.
  419. */
  420. void
  421. GNUNET_CLIENT_MANAGER_drop_queue (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
  422. {
  423. struct MessageQueueItem *cur, *next = mgr->tmit_head;
  424. while (NULL != next)
  425. {
  426. cur = next;
  427. next = cur->next;
  428. GNUNET_free (cur->msg);
  429. GNUNET_free (cur);
  430. }
  431. }
  432. /**
  433. * Obtain client connection handle.
  434. *
  435. * @param mgr
  436. * Client manager connection.
  437. *
  438. * @return Client connection handle.
  439. */
  440. struct GNUNET_CLIENT_Connection *
  441. GNUNET_CLIENT_MANAGER_get_client (struct GNUNET_CLIENT_MANAGER_Connection *mgr)
  442. {
  443. return mgr->client;
  444. }
  445. /**
  446. * Return user context associated with the given client.
  447. * Note: you should probably use the macro (call without the underscore).
  448. *
  449. * @param mgr
  450. * Client manager connection.
  451. * @param size
  452. * Number of bytes in user context struct (for verification only).
  453. *
  454. * @return User context.
  455. */
  456. void *
  457. GNUNET_CLIENT_MANAGER_get_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
  458. size_t size)
  459. {
  460. if ((0 == mgr->user_ctx_size) &&
  461. (NULL == mgr->user_ctx))
  462. return NULL; /* never set */
  463. GNUNET_assert (size == mgr->user_ctx_size);
  464. return mgr->user_ctx;
  465. }
  466. /**
  467. * Set user context to be associated with the given client.
  468. * Note: you should probably use the macro (call without the underscore).
  469. *
  470. * @param mgr
  471. * Client manager connection.
  472. * @param ctx
  473. * User context.
  474. * @param size
  475. * Number of bytes in user context struct (for verification only).
  476. */
  477. void
  478. GNUNET_CLIENT_MANAGER_set_user_context_ (struct GNUNET_CLIENT_MANAGER_Connection *mgr,
  479. void *ctx,
  480. size_t size)
  481. {
  482. if (NULL == ctx)
  483. {
  484. mgr->user_ctx_size = 0;
  485. mgr->user_ctx = ctx;
  486. return;
  487. }
  488. mgr->user_ctx_size = size;
  489. mgr->user_ctx = ctx;
  490. }