arm_api.c 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2009, 2010, 2012, 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 arm/arm_api.c
  19. * @brief API for accessing the ARM service
  20. * @author Christian Grothoff
  21. * @author LRN
  22. */
  23. #include "platform.h"
  24. #include "gnunet_util_lib.h"
  25. #include "gnunet_arm_service.h"
  26. #include "gnunet_protocols.h"
  27. #include "arm.h"
  28. #define LOG(kind,...) GNUNET_log_from (kind, "arm-api",__VA_ARGS__)
  29. /**
  30. * Handle for interacting with ARM.
  31. */
  32. struct GNUNET_ARM_Handle
  33. {
  34. /**
  35. * Our control connection to the ARM service.
  36. */
  37. struct GNUNET_CLIENT_Connection *client;
  38. /**
  39. * The configuration that we are using.
  40. */
  41. struct GNUNET_CONFIGURATION_Handle *cfg;
  42. /**
  43. * Handle for our current transmission request.
  44. */
  45. struct GNUNET_CLIENT_TransmitHandle *cth;
  46. /**
  47. * Head of doubly-linked list of pending requests.
  48. */
  49. struct ARMControlMessage *control_pending_head;
  50. /**
  51. * Tail of doubly-linked list of pending requests.
  52. */
  53. struct ARMControlMessage *control_pending_tail;
  54. /**
  55. * Head of doubly-linked list of sent requests.
  56. */
  57. struct ARMControlMessage *control_sent_head;
  58. /**
  59. * Tail of doubly-linked list of sent requests.
  60. */
  61. struct ARMControlMessage *control_sent_tail;
  62. /**
  63. * Callback to invoke on connection/disconnection.
  64. */
  65. GNUNET_ARM_ConnectionStatusCallback conn_status;
  66. /**
  67. * Closure for conn_status.
  68. */
  69. void *conn_status_cls;
  70. /**
  71. * ARM control message for the 'arm_termination_handler'
  72. * with the continuation to call once the ARM shutdown is done.
  73. */
  74. struct ARMControlMessage *thm;
  75. /**
  76. * ID of the reconnect task (if any).
  77. */
  78. GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
  79. /**
  80. * Current delay we use for re-trying to connect to core.
  81. */
  82. struct GNUNET_TIME_Relative retry_backoff;
  83. /**
  84. * Counter for request identifiers
  85. */
  86. uint64_t request_id_counter;
  87. /**
  88. * Are we currently disconnected and hence unable to send?
  89. */
  90. unsigned char currently_down;
  91. /**
  92. * GNUNET_YES if we're running a service test.
  93. */
  94. unsigned char service_test_is_active;
  95. };
  96. /**
  97. * Entry in a doubly-linked list of control messages to be transmitted
  98. * to the arm service.
  99. *
  100. * The actual message is allocated at the end of this struct.
  101. */
  102. struct ARMControlMessage
  103. {
  104. /**
  105. * This is a doubly-linked list.
  106. */
  107. struct ARMControlMessage *next;
  108. /**
  109. * This is a doubly-linked list.
  110. */
  111. struct ARMControlMessage *prev;
  112. /**
  113. * ARM handle.
  114. */
  115. struct GNUNET_ARM_Handle *h;
  116. /**
  117. * Message to send.
  118. */
  119. struct GNUNET_ARM_Message *msg;
  120. /**
  121. * Callback for service state change requests.
  122. */
  123. GNUNET_ARM_ResultCallback result_cont;
  124. /**
  125. * Callback for service list requests.
  126. */
  127. GNUNET_ARM_ServiceListCallback list_cont;
  128. /**
  129. * Closure for 'result_cont' or 'list_cont'.
  130. */
  131. void *cont_cls;
  132. /**
  133. * Timeout for the operation.
  134. */
  135. struct GNUNET_TIME_Absolute timeout;
  136. /**
  137. * Task to run when request times out.
  138. */
  139. GNUNET_SCHEDULER_TaskIdentifier timeout_task_id;
  140. /**
  141. * Flags for passing std descriptors to ARM (when starting ARM).
  142. */
  143. enum GNUNET_OS_InheritStdioFlags std_inheritance;
  144. /**
  145. * Type of the request expressed as a message type (start, stop or list).
  146. */
  147. uint16_t type;
  148. };
  149. /**
  150. * Connect to arm.
  151. *
  152. * @param h arm handle
  153. * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  154. */
  155. static int
  156. reconnect_arm (struct GNUNET_ARM_Handle *h);
  157. /**
  158. * Check the list of pending requests, send the next
  159. * one to the arm.
  160. *
  161. * @param h arm handle
  162. * @param ignore_currently_down transmit message even if not initialized?
  163. */
  164. static void
  165. trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down);
  166. /**
  167. * Task scheduled to try to re-connect to arm.
  168. *
  169. * @param cls the 'struct GNUNET_ARM_Handle'
  170. * @param tc task context
  171. */
  172. static void
  173. reconnect_arm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  174. {
  175. struct GNUNET_ARM_Handle *h = cls;
  176. h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
  177. LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to ARM service after delay\n");
  178. reconnect_arm (h);
  179. }
  180. /**
  181. * Close down any existing connection to the ARM service and
  182. * try re-establishing it later.
  183. *
  184. * @param h our handle
  185. */
  186. static void
  187. reconnect_arm_later (struct GNUNET_ARM_Handle *h)
  188. {
  189. if (GNUNET_NO != h->currently_down)
  190. return;
  191. if (NULL != h->cth)
  192. {
  193. GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
  194. h->cth = NULL;
  195. }
  196. if (NULL != h->client)
  197. {
  198. GNUNET_CLIENT_disconnect (h->client);
  199. h->client = NULL;
  200. }
  201. h->currently_down = GNUNET_YES;
  202. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
  203. h->reconnect_task =
  204. GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h);
  205. /* Don't clear pending messages on disconnection, deliver them later
  206. clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED);
  207. GNUNET_assert (NULL == h->control_pending_head);
  208. */
  209. h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
  210. if (NULL != h->conn_status)
  211. h->conn_status (h->conn_status_cls, GNUNET_NO);
  212. }
  213. /**
  214. * Find a control message by its unique ID.
  215. *
  216. * @param h ARM handle
  217. * @param id unique message ID to use for the lookup
  218. * @return NULL if not found
  219. */
  220. static struct ARMControlMessage *
  221. find_cm_by_id (struct GNUNET_ARM_Handle *h, uint64_t id)
  222. {
  223. struct ARMControlMessage *result;
  224. for (result = h->control_sent_head; result; result = result->next)
  225. if (id == result->msg->request_id)
  226. return result;
  227. return NULL;
  228. }
  229. /**
  230. * Handler for ARM 'termination' reply (failure to receive).
  231. *
  232. * @param cls our "struct GNUNET_ARM_Handle"
  233. * @param msg expected to be NULL
  234. */
  235. static void
  236. arm_termination_handler (void *cls, const struct GNUNET_MessageHeader *msg)
  237. {
  238. struct GNUNET_ARM_Handle *h = cls;
  239. struct ARMControlMessage *cm;
  240. if (NULL != msg)
  241. {
  242. GNUNET_break (0);
  243. GNUNET_CLIENT_receive (h->client, &arm_termination_handler, h,
  244. GNUNET_TIME_UNIT_FOREVER_REL);
  245. return;
  246. }
  247. cm = h->thm;
  248. h->thm = NULL;
  249. h->currently_down = GNUNET_YES;
  250. GNUNET_CLIENT_disconnect (h->client);
  251. h->client = NULL;
  252. if (NULL != cm->result_cont)
  253. cm->result_cont (cm->cont_cls,
  254. GNUNET_ARM_REQUEST_SENT_OK,
  255. (const char *) &cm->msg[1],
  256. GNUNET_ARM_RESULT_STOPPED);
  257. GNUNET_free (cm->msg);
  258. GNUNET_free (cm);
  259. }
  260. /**
  261. * Handler for ARM replies.
  262. *
  263. * @param cls our `struct GNUNET_ARM_Handle`
  264. * @param msg the message received from the arm service
  265. */
  266. static void
  267. client_notify_handler (void *cls,
  268. const struct GNUNET_MessageHeader *msg)
  269. {
  270. struct GNUNET_ARM_Handle *h = cls;
  271. const struct GNUNET_ARM_Message *arm_msg;
  272. const struct GNUNET_ARM_ResultMessage *res;
  273. const struct GNUNET_ARM_ListResultMessage *lres;
  274. struct ARMControlMessage *cm;
  275. const char **list;
  276. const char *pos;
  277. uint64_t id;
  278. enum GNUNET_ARM_Result result;
  279. uint16_t size_check;
  280. uint16_t rcount;
  281. uint16_t msize;
  282. unsigned char fail;
  283. list = NULL;
  284. rcount = 0;
  285. if (NULL == msg)
  286. {
  287. LOG (GNUNET_ERROR_TYPE_INFO,
  288. _("Client was disconnected from arm service, trying to reconnect.\n"));
  289. reconnect_arm_later (h);
  290. return;
  291. }
  292. msize = ntohs (msg->size);
  293. LOG (GNUNET_ERROR_TYPE_DEBUG,
  294. "Processing message of type %u and size %u from arm service\n",
  295. ntohs (msg->type), msize);
  296. if (msize < sizeof (struct GNUNET_ARM_Message))
  297. {
  298. GNUNET_break (0);
  299. reconnect_arm_later (h);
  300. return;
  301. }
  302. arm_msg = (const struct GNUNET_ARM_Message *) msg;
  303. GNUNET_break (0 == ntohl (arm_msg->reserved));
  304. id = GNUNET_ntohll (arm_msg->request_id);
  305. cm = find_cm_by_id (h, id);
  306. if (NULL == cm)
  307. {
  308. LOG (GNUNET_ERROR_TYPE_DEBUG,
  309. "Message with unknown id %llu\n",
  310. id);
  311. return;
  312. }
  313. fail = GNUNET_NO;
  314. switch (ntohs (msg->type))
  315. {
  316. case GNUNET_MESSAGE_TYPE_ARM_RESULT:
  317. if (msize < sizeof (struct GNUNET_ARM_ResultMessage))
  318. {
  319. GNUNET_assert (0);
  320. fail = GNUNET_YES;
  321. }
  322. break;
  323. case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
  324. if (msize < sizeof (struct GNUNET_ARM_ListResultMessage))
  325. {
  326. GNUNET_break (0);
  327. fail = GNUNET_YES;
  328. break;
  329. }
  330. size_check = 0;
  331. lres = (const struct GNUNET_ARM_ListResultMessage *) msg;
  332. rcount = ntohs (lres->count);
  333. {
  334. unsigned int i;
  335. list = GNUNET_malloc (sizeof (const char *) * rcount);
  336. pos = (const char *)&lres[1];
  337. for (i = 0; i < rcount; i++)
  338. {
  339. const char *end = memchr (pos, 0, msize - size_check);
  340. if (NULL == end)
  341. {
  342. GNUNET_break (0);
  343. fail = GNUNET_YES;
  344. break;
  345. }
  346. list[i] = pos;
  347. size_check += (end - pos) + 1;
  348. pos = end + 1;
  349. }
  350. if (GNUNET_YES == fail)
  351. {
  352. GNUNET_free (list);
  353. list = NULL;
  354. }
  355. }
  356. break;
  357. default:
  358. fail = GNUNET_YES;
  359. break;
  360. }
  361. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
  362. GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
  363. GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
  364. h->control_sent_tail, cm);
  365. if (GNUNET_YES == fail)
  366. {
  367. reconnect_arm_later (h);
  368. GNUNET_free (cm->msg);
  369. GNUNET_free (cm);
  370. return;
  371. }
  372. if ( (GNUNET_MESSAGE_TYPE_ARM_RESULT == ntohs (msg->type)) &&
  373. (0 == strcasecmp ((const char *) &cm->msg[1],
  374. "arm")) &&
  375. (NULL != (res = (const struct GNUNET_ARM_ResultMessage *) msg)) &&
  376. (GNUNET_ARM_RESULT_STOPPING == ntohl (res->result)) )
  377. {
  378. /* special case: if we are stopping 'gnunet-service-arm', we do not just
  379. wait for the result message, but also wait for the service to close
  380. the connection (and then we have to close our client handle as well);
  381. this is done by installing a different receive handler, waiting for
  382. the connection to go down */
  383. if (NULL != h->thm)
  384. {
  385. GNUNET_break (0);
  386. cm->result_cont (h->thm->cont_cls,
  387. GNUNET_ARM_REQUEST_SENT_OK,
  388. (const char *) &h->thm->msg[1],
  389. GNUNET_ARM_RESULT_IS_NOT_KNOWN);
  390. GNUNET_free (h->thm->msg);
  391. GNUNET_free (h->thm);
  392. }
  393. h->thm = cm;
  394. GNUNET_CLIENT_receive (h->client, &arm_termination_handler, h,
  395. GNUNET_TIME_UNIT_FOREVER_REL);
  396. return;
  397. }
  398. GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
  399. GNUNET_TIME_UNIT_FOREVER_REL);
  400. switch (ntohs (msg->type))
  401. {
  402. case GNUNET_MESSAGE_TYPE_ARM_RESULT:
  403. res = (const struct GNUNET_ARM_ResultMessage *) msg;
  404. LOG (GNUNET_ERROR_TYPE_DEBUG,
  405. "Received response from ARM for service `%s': %u\n",
  406. (const char *) &cm->msg[1], ntohs (msg->type));
  407. result = (enum GNUNET_ARM_Result) ntohl (res->result);
  408. if (NULL != cm->result_cont)
  409. cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK,
  410. (const char *) &cm->msg[1], result);
  411. break;
  412. case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
  413. if (NULL != cm->list_cont)
  414. cm->list_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, rcount,
  415. list);
  416. GNUNET_free_non_null (list);
  417. break;
  418. }
  419. GNUNET_free (cm->msg);
  420. GNUNET_free (cm);
  421. }
  422. /**
  423. * Transmit the next message to the arm service.
  424. *
  425. * @param cls closure with the `struct GNUNET_ARM_Handle`
  426. * @param size number of bytes available in @a buf
  427. * @param buf where the callee should write the message
  428. * @return number of bytes written to @a buf
  429. */
  430. static size_t
  431. transmit_arm_message (void *cls, size_t size, void *buf)
  432. {
  433. struct GNUNET_ARM_Handle *h = cls;
  434. struct ARMControlMessage *cm;
  435. struct GNUNET_ARM_Message *arm_msg;
  436. uint64_t request_id;
  437. int notify_connection;
  438. uint16_t msize;
  439. notify_connection = GNUNET_NO;
  440. LOG (GNUNET_ERROR_TYPE_DEBUG,
  441. "transmit_arm_message is running with %p buffer of size %lu. ARM is known to be %s\n",
  442. buf, size, h->currently_down ? "unconnected" : "connected");
  443. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
  444. h->cth = NULL;
  445. if ((GNUNET_YES == h->currently_down) && (NULL != buf))
  446. {
  447. h->currently_down = GNUNET_NO;
  448. notify_connection = GNUNET_YES;
  449. h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
  450. GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
  451. GNUNET_TIME_UNIT_FOREVER_REL);
  452. }
  453. if (NULL == buf)
  454. {
  455. LOG (GNUNET_ERROR_TYPE_DEBUG,
  456. "Transmission failed, initiating reconnect\n");
  457. reconnect_arm_later (h);
  458. return 0;
  459. }
  460. if (NULL == (cm = h->control_pending_head))
  461. {
  462. LOG (GNUNET_ERROR_TYPE_DEBUG,
  463. "Queue is empty, not sending anything\n");
  464. msize = 0;
  465. goto end;
  466. }
  467. GNUNET_assert (NULL != cm->msg);
  468. msize = ntohs (cm->msg->header.size);
  469. if (size < msize)
  470. {
  471. LOG (GNUNET_ERROR_TYPE_DEBUG,
  472. "Request is too big (%u < %u), not sending it\n", size, msize);
  473. trigger_next_request (h, GNUNET_NO);
  474. msize = 0;
  475. goto end;
  476. }
  477. arm_msg = cm->msg;
  478. if (0 == h->request_id_counter)
  479. h->request_id_counter++;
  480. request_id = h->request_id_counter++;
  481. LOG (GNUNET_ERROR_TYPE_DEBUG,
  482. "Transmitting control message with %u bytes of type %u to arm with id %llu\n",
  483. (unsigned int) msize, (unsigned int) ntohs (cm->msg->header.type), request_id);
  484. arm_msg->reserved = htonl (0);
  485. arm_msg->request_id = GNUNET_htonll (request_id);
  486. memcpy (buf, cm->msg, msize);
  487. /* Otherwise we won't be able to find it later! */
  488. arm_msg->request_id = request_id;
  489. GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
  490. h->control_pending_tail, cm);
  491. GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head,
  492. h->control_sent_tail, cm);
  493. /* Don't free msg, keep it around (kind of wasteful, but then we don't
  494. * really have many messages to handle, and it'll be freed when it times
  495. * out anyway.
  496. */
  497. trigger_next_request (h, GNUNET_NO);
  498. end:
  499. if ((GNUNET_YES == notify_connection) && (NULL != h->conn_status))
  500. h->conn_status (h->conn_status_cls, GNUNET_YES);
  501. return msize;
  502. }
  503. /**
  504. * Check the list of pending requests, send the next
  505. * one to the arm.
  506. *
  507. * @param h arm handle
  508. * @param ignore_currently_down transmit message even if not initialized?
  509. */
  510. static void
  511. trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down)
  512. {
  513. uint16_t msize;
  514. msize = sizeof (struct GNUNET_MessageHeader);
  515. if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO))
  516. {
  517. LOG (GNUNET_ERROR_TYPE_DEBUG,
  518. "ARM connection down, not processing queue\n");
  519. return;
  520. }
  521. if (NULL != h->cth)
  522. {
  523. LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n");
  524. return;
  525. }
  526. if (NULL != h->control_pending_head)
  527. msize =
  528. ntohs (h->control_pending_head->msg->header.size);
  529. else if (GNUNET_NO == ignore_currently_down)
  530. {
  531. LOG (GNUNET_ERROR_TYPE_DEBUG,
  532. "Request queue empty, not processing queue\n");
  533. return; /* no pending message */
  534. }
  535. h->cth =
  536. GNUNET_CLIENT_notify_transmit_ready (h->client, msize,
  537. GNUNET_TIME_UNIT_FOREVER_REL,
  538. GNUNET_NO, &transmit_arm_message, h);
  539. }
  540. /**
  541. * Connect to arm.
  542. *
  543. * @param h arm handle
  544. * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  545. */
  546. static int
  547. reconnect_arm (struct GNUNET_ARM_Handle *h)
  548. {
  549. GNUNET_assert (NULL == h->client);
  550. GNUNET_assert (GNUNET_YES == h->currently_down);
  551. h->client = GNUNET_CLIENT_connect ("arm", h->cfg);
  552. if (NULL == h->client)
  553. {
  554. LOG (GNUNET_ERROR_TYPE_DEBUG,
  555. "arm_api, GNUNET_CLIENT_connect returned NULL\n");
  556. if (NULL != h->conn_status)
  557. h->conn_status (h->conn_status_cls, GNUNET_SYSERR);
  558. return GNUNET_SYSERR;
  559. }
  560. LOG (GNUNET_ERROR_TYPE_DEBUG,
  561. "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
  562. trigger_next_request (h, GNUNET_YES);
  563. return GNUNET_OK;
  564. }
  565. /**
  566. * Set up a context for communicating with ARM, then
  567. * start connecting to the ARM service using that context.
  568. *
  569. * @param cfg configuration to use (needed to contact ARM;
  570. * the ARM service may internally use a different
  571. * configuration to determine how to start the service).
  572. * @param conn_status will be called when connecting/disconnecting
  573. * @param cls closure for conn_status
  574. * @return context to use for further ARM operations, NULL on error.
  575. */
  576. struct GNUNET_ARM_Handle *
  577. GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
  578. GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls)
  579. {
  580. struct GNUNET_ARM_Handle *h;
  581. h = GNUNET_new (struct GNUNET_ARM_Handle);
  582. h->cfg = GNUNET_CONFIGURATION_dup (cfg);
  583. h->currently_down = GNUNET_YES;
  584. h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
  585. h->conn_status = conn_status;
  586. h->conn_status_cls = cls;
  587. if (GNUNET_OK != reconnect_arm (h))
  588. {
  589. GNUNET_free (h);
  590. return NULL;
  591. }
  592. return h;
  593. }
  594. /**
  595. * Disconnect from the ARM service (if connected) and destroy the context.
  596. *
  597. * @param h the handle that was being used
  598. */
  599. void
  600. GNUNET_ARM_disconnect_and_free (struct GNUNET_ARM_Handle *h)
  601. {
  602. struct ARMControlMessage *cm;
  603. LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
  604. if (NULL != h->cth)
  605. {
  606. GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
  607. h->cth = NULL;
  608. }
  609. while ((NULL != (cm = h->control_pending_head))
  610. || (NULL != (cm = h->control_sent_head)) )
  611. {
  612. if (NULL != h->control_pending_head)
  613. GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
  614. h->control_pending_tail, cm);
  615. else
  616. GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
  617. h->control_sent_tail, cm);
  618. GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
  619. GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
  620. if (NULL != cm->result_cont)
  621. cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_DISCONNECTED,
  622. NULL, 0);
  623. /* FIXME: What about list callback? */
  624. GNUNET_free_non_null (cm->msg);
  625. GNUNET_free (cm);
  626. }
  627. if (NULL != h->client)
  628. {
  629. GNUNET_CLIENT_disconnect (h->client);
  630. h->client = NULL;
  631. }
  632. if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
  633. {
  634. GNUNET_SCHEDULER_cancel (h->reconnect_task);
  635. h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
  636. }
  637. if (GNUNET_NO == h->service_test_is_active)
  638. {
  639. GNUNET_CONFIGURATION_destroy (h->cfg);
  640. GNUNET_free (h);
  641. }
  642. }
  643. /**
  644. * Message timed out. Remove it from the queue.
  645. *
  646. * @param cls the message (struct ARMControlMessage *)
  647. * @param tc task context
  648. */
  649. static void
  650. control_message_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  651. {
  652. struct ARMControlMessage *cm = cls;
  653. struct GNUNET_ARM_Message *arm_msg;
  654. LOG (GNUNET_ERROR_TYPE_DEBUG,
  655. "Control message timed out\n");
  656. arm_msg = cm->msg;
  657. if ((NULL == arm_msg) || (0 == arm_msg->request_id))
  658. {
  659. GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head,
  660. cm->h->control_pending_tail, cm);
  661. }
  662. else
  663. {
  664. GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head,
  665. cm->h->control_sent_tail, cm);
  666. }
  667. if (NULL != cm->result_cont)
  668. cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_TIMEOUT, NULL, 0);
  669. else if (NULL != cm->list_cont)
  670. cm->list_cont (cm->cont_cls, GNUNET_ARM_REQUEST_TIMEOUT, 0, NULL);
  671. GNUNET_free_non_null (cm->msg);
  672. GNUNET_free (cm);
  673. }
  674. /**
  675. * A client specifically requested starting of ARM itself.
  676. * This function is called with information about whether
  677. * or not ARM is running; if it is, report success. If
  678. * it is not, start the ARM process.
  679. *
  680. * @param cls the context for the request that we will report on (struct ARMControlMessage *)
  681. * @param result GNUNET_YES if ARM is running
  682. */
  683. static void
  684. arm_service_report (void *cls,
  685. int result)
  686. {
  687. struct ARMControlMessage *cm = cls;
  688. struct GNUNET_ARM_Handle *h;
  689. struct GNUNET_OS_Process *proc;
  690. unsigned char test_is_active;
  691. char *cbinary;
  692. char *binary;
  693. char *quotedbinary;
  694. char *config;
  695. char *loprefix;
  696. char *lopostfix;
  697. test_is_active = cm->h->service_test_is_active;
  698. if ((GNUNET_YES == test_is_active) &&
  699. (GNUNET_YES == result))
  700. {
  701. LOG (GNUNET_ERROR_TYPE_DEBUG,
  702. "Looks like `%s' is already running.\n",
  703. "gnunet-service-arm");
  704. /* arm is running! */
  705. if (cm->result_cont)
  706. cm->result_cont (cm->cont_cls,
  707. GNUNET_ARM_REQUEST_SENT_OK, "arm",
  708. GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
  709. }
  710. if (GNUNET_NO == test_is_active)
  711. {
  712. /* User disconnected & destroyed ARM handle in the middle of
  713. * the service test, so we kept the handle around until now.
  714. */
  715. GNUNET_CONFIGURATION_destroy (cm->h->cfg);
  716. GNUNET_free (cm->h);
  717. }
  718. if ((GNUNET_YES == result) ||
  719. (GNUNET_NO == test_is_active))
  720. {
  721. GNUNET_free (cm);
  722. return;
  723. }
  724. cm->h->service_test_is_active = GNUNET_NO;
  725. LOG (GNUNET_ERROR_TYPE_DEBUG,
  726. "Looks like `%s' is not running, will start it.\n",
  727. "gnunet-service-arm");
  728. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
  729. cm->h->cfg, "arm", "PREFIX", &loprefix))
  730. loprefix = GNUNET_strdup ("");
  731. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
  732. cm->h->cfg, "arm", "OPTIONS", &lopostfix))
  733. lopostfix = GNUNET_strdup ("");
  734. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
  735. cm->h->cfg, "arm", "BINARY", &cbinary))
  736. {
  737. GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY");
  738. if (cm->result_cont)
  739. cm->result_cont (cm->cont_cls,
  740. GNUNET_ARM_REQUEST_SENT_OK, "arm",
  741. GNUNET_ARM_RESULT_IS_NOT_KNOWN);
  742. GNUNET_free (cm);
  743. GNUNET_free (loprefix);
  744. GNUNET_free (lopostfix);
  745. return;
  746. }
  747. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (
  748. cm->h->cfg, "arm", "CONFIG", &config))
  749. config = NULL;
  750. binary = GNUNET_OS_get_libexec_binary_path (cbinary);
  751. GNUNET_asprintf (&quotedbinary,
  752. "\"%s\"",
  753. binary);
  754. GNUNET_free (cbinary);
  755. if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (
  756. cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
  757. (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (
  758. cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
  759. (GNUNET_NO == GNUNET_CONFIGURATION_have_value (
  760. cm->h->cfg, "TESTING", "HOSTFILE")))
  761. {
  762. /* Means we are ONLY running locally */
  763. /* we're clearly running a test, don't daemonize */
  764. if (NULL == config)
  765. proc = GNUNET_OS_start_process_s (GNUNET_NO, cm->std_inheritance,
  766. NULL, loprefix, quotedbinary,
  767. /* no daemonization! */
  768. lopostfix, NULL);
  769. else
  770. proc = GNUNET_OS_start_process_s (GNUNET_NO, cm->std_inheritance,
  771. NULL, loprefix, quotedbinary, "-c", config,
  772. /* no daemonization! */
  773. lopostfix, NULL);
  774. }
  775. else
  776. {
  777. if (NULL == config)
  778. proc = GNUNET_OS_start_process_s (GNUNET_NO, cm->std_inheritance,
  779. NULL, loprefix, quotedbinary,
  780. "-d", lopostfix, NULL);
  781. else
  782. proc = GNUNET_OS_start_process_s (GNUNET_NO, cm->std_inheritance,
  783. NULL, loprefix, quotedbinary, "-c",
  784. config,
  785. "-d", lopostfix, NULL);
  786. }
  787. GNUNET_free (binary);
  788. GNUNET_free (quotedbinary);
  789. GNUNET_free_non_null (config);
  790. GNUNET_free (loprefix);
  791. GNUNET_free (lopostfix);
  792. if (NULL == proc)
  793. {
  794. if (cm->result_cont)
  795. cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, "arm",
  796. GNUNET_ARM_RESULT_START_FAILED);
  797. GNUNET_free (cm);
  798. return;
  799. }
  800. if (cm->result_cont)
  801. cm->result_cont (cm->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, "arm",
  802. GNUNET_ARM_RESULT_STARTING);
  803. GNUNET_OS_process_destroy (proc);
  804. h = cm->h;
  805. GNUNET_free (cm);
  806. reconnect_arm (h);
  807. }
  808. /**
  809. * Start or stop a service.
  810. *
  811. * @param h handle to ARM
  812. * @param service_name name of the service
  813. * @param timeout how long to wait before failing for good
  814. * @param cb callback to invoke when service is ready
  815. * @param cb_cls closure for callback
  816. * @param type type of the request
  817. */
  818. static void
  819. change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
  820. struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cb,
  821. void *cb_cls, uint16_t type)
  822. {
  823. struct ARMControlMessage *cm;
  824. size_t slen;
  825. struct GNUNET_ARM_Message *msg;
  826. slen = strlen (service_name) + 1;
  827. if (slen + sizeof (struct GNUNET_ARM_Message) >=
  828. GNUNET_SERVER_MAX_MESSAGE_SIZE)
  829. {
  830. GNUNET_break (0);
  831. if (cb != NULL)
  832. cb (cb_cls, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0);
  833. return;
  834. }
  835. LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n",
  836. (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination",
  837. service_name);
  838. cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
  839. cm->h = h;
  840. cm->result_cont = cb;
  841. cm->cont_cls = cb_cls;
  842. cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
  843. memcpy (&cm[1], service_name, slen);
  844. msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen);
  845. msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen);
  846. msg->header.type = htons (type);
  847. msg->reserved = htonl (0);
  848. memcpy (&msg[1], service_name, slen);
  849. cm->msg = msg;
  850. LOG (GNUNET_ERROR_TYPE_DEBUG,
  851. "Inserting a control message into the queue. Timeout is %s\n",
  852. GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (cm->timeout),
  853. GNUNET_NO));
  854. GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
  855. h->control_pending_tail, cm);
  856. cm->timeout_task_id =
  857. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
  858. (cm->timeout), &control_message_timeout, cm);
  859. trigger_next_request (h, GNUNET_NO);
  860. }
  861. /**
  862. * Request for a service to be started.
  863. *
  864. * @param h handle to ARM
  865. * @param service_name name of the service
  866. * @param std_inheritance inheritance of std streams
  867. * @param timeout how long to wait before failing for good
  868. * @param cont callback to invoke after request is sent or not sent
  869. * @param cont_cls closure for callback
  870. */
  871. void
  872. GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
  873. const char *service_name,
  874. enum GNUNET_OS_InheritStdioFlags std_inheritance,
  875. struct GNUNET_TIME_Relative timeout,
  876. GNUNET_ARM_ResultCallback cont,
  877. void *cont_cls)
  878. {
  879. struct ARMControlMessage *cm;
  880. size_t slen;
  881. LOG (GNUNET_ERROR_TYPE_DEBUG,
  882. "Asked to start service `%s' within %s\n", service_name,
  883. GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
  884. if (0 == strcasecmp ("arm", service_name))
  885. {
  886. /* Possible cases:
  887. * 1) We're connected to ARM already. Invoke the callback immediately.
  888. * 2) We're not connected to ARM.
  889. * Cancel any reconnection attempts temporarily, then perform
  890. * a service test.
  891. */
  892. if (GNUNET_NO == h->currently_down)
  893. {
  894. LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n");
  895. if (NULL != cont)
  896. cont (cont_cls, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
  897. }
  898. else if (GNUNET_NO == h->service_test_is_active)
  899. {
  900. if (NULL != h->cth)
  901. {
  902. GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
  903. h->cth = NULL;
  904. }
  905. if (NULL != h->client)
  906. {
  907. GNUNET_CLIENT_disconnect (h->client);
  908. h->client = NULL;
  909. }
  910. if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
  911. {
  912. GNUNET_SCHEDULER_cancel (h->reconnect_task);
  913. h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
  914. }
  915. LOG (GNUNET_ERROR_TYPE_DEBUG,
  916. "Not connected to ARM, will do a service test\n");
  917. slen = strlen ("arm") + 1;
  918. cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
  919. cm->h = h;
  920. cm->result_cont = cont;
  921. cm->cont_cls = cont_cls;
  922. cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
  923. cm->std_inheritance = std_inheritance;
  924. memcpy (&cm[1], service_name, slen);
  925. h->service_test_is_active = GNUNET_YES;
  926. GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
  927. cm);
  928. }
  929. else
  930. {
  931. /* Service test is already running - tell user to chill out and try
  932. * again later.
  933. */
  934. LOG (GNUNET_ERROR_TYPE_DEBUG, "Service test is already in progress, we're busy\n");
  935. if (NULL != cont)
  936. cont (cont_cls, GNUNET_ARM_REQUEST_BUSY, NULL, 0);
  937. }
  938. return;
  939. }
  940. change_service (h, service_name, timeout, cont, cont_cls,
  941. GNUNET_MESSAGE_TYPE_ARM_START);
  942. }
  943. /**
  944. * Request a service to be stopped.
  945. * Stopping arm itself will not invalidate its handle, and
  946. * ARM API will try to restore connection to the ARM service,
  947. * even if ARM connection was lost because you asked for ARM to be stopped.
  948. * Call GNUNET_ARM_disconnect_and_free () to free the handle and prevent
  949. * further connection attempts.
  950. *
  951. * @param h handle to ARM
  952. * @param service_name name of the service
  953. * @param timeout how long to wait before failing for good
  954. * @param cont callback to invoke after request is sent or is not sent
  955. * @param cont_cls closure for callback
  956. */
  957. void
  958. GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
  959. const char *service_name,
  960. struct GNUNET_TIME_Relative timeout,
  961. GNUNET_ARM_ResultCallback cont,
  962. void *cont_cls)
  963. {
  964. LOG (GNUNET_ERROR_TYPE_DEBUG,
  965. "Stopping service `%s' within %s\n",
  966. service_name,
  967. GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
  968. change_service (h, service_name, timeout, cont, cont_cls,
  969. GNUNET_MESSAGE_TYPE_ARM_STOP);
  970. }
  971. /**
  972. * Request a list of running services.
  973. *
  974. * @param h handle to ARM
  975. * @param timeout how long to wait before failing for good
  976. * @param cont callback to invoke after request is sent or is not sent
  977. * @param cont_cls closure for callback
  978. */
  979. void
  980. GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
  981. struct GNUNET_TIME_Relative timeout,
  982. GNUNET_ARM_ServiceListCallback cont,
  983. void *cont_cls)
  984. {
  985. struct ARMControlMessage *cm;
  986. struct GNUNET_ARM_Message *msg;
  987. LOG (GNUNET_ERROR_TYPE_DEBUG,
  988. "Requesting LIST from ARM service with timeout: %s\n",
  989. GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES));
  990. cm = GNUNET_new (struct ARMControlMessage);
  991. cm->h = h;
  992. cm->list_cont = cont;
  993. cm->cont_cls = cont_cls;
  994. cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
  995. msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message));
  996. msg->header.size = htons (sizeof (struct GNUNET_ARM_Message));
  997. msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
  998. msg->reserved = htonl (0);
  999. cm->msg = msg;
  1000. GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
  1001. h->control_pending_tail, cm);
  1002. cm->timeout_task_id =
  1003. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
  1004. (cm->timeout), &control_message_timeout, cm);
  1005. trigger_next_request (h, GNUNET_NO);
  1006. }
  1007. /* end of arm_api.c */