arm_api.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2009, 2010, 2012, 2013, 2016 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your 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. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file arm/arm_api.c
  18. * @brief API for accessing the ARM service
  19. * @author Christian Grothoff
  20. * @author LRN
  21. */
  22. #include "platform.h"
  23. #include "gnunet_util_lib.h"
  24. #include "gnunet_arm_service.h"
  25. #include "gnunet_protocols.h"
  26. #include "arm.h"
  27. #define LOG(kind, ...) GNUNET_log_from (kind, "arm-api", __VA_ARGS__)
  28. /**
  29. * Entry in a doubly-linked list of operations awaiting for replies
  30. * (in-order) from the ARM service.
  31. */
  32. struct GNUNET_ARM_Operation
  33. {
  34. /**
  35. * This is a doubly-linked list.
  36. */
  37. struct GNUNET_ARM_Operation *next;
  38. /**
  39. * This is a doubly-linked list.
  40. */
  41. struct GNUNET_ARM_Operation *prev;
  42. /**
  43. * ARM handle.
  44. */
  45. struct GNUNET_ARM_Handle *h;
  46. /**
  47. * Callback for service state change requests.
  48. */
  49. GNUNET_ARM_ResultCallback result_cont;
  50. /**
  51. * Callback for service list requests.
  52. */
  53. GNUNET_ARM_ServiceListCallback list_cont;
  54. /**
  55. * Closure for @e result_cont or @e list_cont.
  56. */
  57. void *cont_cls;
  58. /**
  59. * Task for async completion.
  60. */
  61. struct GNUNET_SCHEDULER_Task *async;
  62. /**
  63. * Unique ID for the request.
  64. */
  65. uint64_t id;
  66. /**
  67. * Result of this operation for #notify_starting().
  68. */
  69. enum GNUNET_ARM_Result starting_ret;
  70. /**
  71. * Is this an operation to stop the ARM service?
  72. */
  73. int is_arm_stop;
  74. };
  75. /**
  76. * Handle for interacting with ARM.
  77. */
  78. struct GNUNET_ARM_Handle
  79. {
  80. /**
  81. * Our connection to the ARM service.
  82. */
  83. struct GNUNET_MQ_Handle *mq;
  84. /**
  85. * The configuration that we are using.
  86. */
  87. const struct GNUNET_CONFIGURATION_Handle *cfg;
  88. /**
  89. * Head of doubly-linked list of pending operations.
  90. */
  91. struct GNUNET_ARM_Operation *operation_pending_head;
  92. /**
  93. * Tail of doubly-linked list of pending operations.
  94. */
  95. struct GNUNET_ARM_Operation *operation_pending_tail;
  96. /**
  97. * Callback to invoke on connection/disconnection.
  98. */
  99. GNUNET_ARM_ConnectionStatusCallback conn_status;
  100. /**
  101. * Closure for @e conn_status.
  102. */
  103. void *conn_status_cls;
  104. /**
  105. * ARM operation where the goal is to wait for ARM shutdown to
  106. * complete. This operation is special in that it waits for an
  107. * error on the @e mq. So we complete it by calling the
  108. * continuation in the #mq_error_handler(). Note that the operation
  109. * is no longer in the @e operation_pending_head DLL once it is
  110. * referenced from this field.
  111. */
  112. struct GNUNET_ARM_Operation *thm;
  113. /**
  114. * ID of the reconnect task (if any).
  115. */
  116. struct GNUNET_SCHEDULER_Task *reconnect_task;
  117. /**
  118. * Current delay we use for re-trying to connect to core.
  119. */
  120. struct GNUNET_TIME_Relative retry_backoff;
  121. /**
  122. * Counter for request identifiers. They are used to match replies
  123. * from ARM to operations in the @e operation_pending_head DLL.
  124. */
  125. uint64_t request_id_counter;
  126. /**
  127. * Have we detected that ARM is up?
  128. */
  129. int currently_up;
  130. };
  131. /**
  132. * Connect to arm.
  133. *
  134. * @param h arm handle
  135. * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
  136. */
  137. static int
  138. reconnect_arm (struct GNUNET_ARM_Handle *h);
  139. /**
  140. * Task scheduled to try to re-connect to arm.
  141. *
  142. * @param cls the `struct GNUNET_ARM_Handle`
  143. */
  144. static void
  145. reconnect_arm_task (void *cls)
  146. {
  147. struct GNUNET_ARM_Handle *h = cls;
  148. h->reconnect_task = NULL;
  149. reconnect_arm (h);
  150. }
  151. /**
  152. * Close down any existing connection to the ARM service and
  153. * try re-establishing it later.
  154. *
  155. * @param h our handle
  156. */
  157. static void
  158. reconnect_arm_later (struct GNUNET_ARM_Handle *h)
  159. {
  160. struct GNUNET_ARM_Operation *op;
  161. if (NULL != h->mq)
  162. {
  163. GNUNET_MQ_destroy (h->mq);
  164. h->mq = NULL;
  165. }
  166. h->currently_up = GNUNET_NO;
  167. GNUNET_assert (NULL == h->reconnect_task);
  168. h->reconnect_task =
  169. GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h);
  170. while (NULL != (op = h->operation_pending_head))
  171. {
  172. if (NULL != op->result_cont)
  173. op->result_cont (op->cont_cls, GNUNET_ARM_REQUEST_DISCONNECTED, 0);
  174. if (NULL != op->list_cont)
  175. op->list_cont (op->cont_cls, GNUNET_ARM_REQUEST_DISCONNECTED, 0, NULL);
  176. GNUNET_ARM_operation_cancel (op);
  177. }
  178. GNUNET_assert (NULL == h->operation_pending_head);
  179. h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
  180. if (NULL != h->conn_status)
  181. h->conn_status (h->conn_status_cls, GNUNET_NO);
  182. }
  183. /**
  184. * Find a control message by its unique ID.
  185. *
  186. * @param h ARM handle
  187. * @param id unique message ID to use for the lookup
  188. * @return NULL if not found
  189. */
  190. static struct GNUNET_ARM_Operation *
  191. find_op_by_id (struct GNUNET_ARM_Handle *h, uint64_t id)
  192. {
  193. struct GNUNET_ARM_Operation *result;
  194. for (result = h->operation_pending_head; NULL != result;
  195. result = result->next)
  196. if (id == result->id)
  197. return result;
  198. return NULL;
  199. }
  200. /**
  201. * Handler for ARM replies.
  202. *
  203. * @param cls our `struct GNUNET_ARM_Handle`
  204. * @param res the message received from the arm service
  205. */
  206. static void
  207. handle_arm_result (void *cls, const struct GNUNET_ARM_ResultMessage *res)
  208. {
  209. struct GNUNET_ARM_Handle *h = cls;
  210. struct GNUNET_ARM_Operation *op;
  211. uint64_t id;
  212. enum GNUNET_ARM_Result result;
  213. GNUNET_ARM_ResultCallback result_cont;
  214. void *result_cont_cls;
  215. id = GNUNET_ntohll (res->arm_msg.request_id);
  216. op = find_op_by_id (h, id);
  217. if (NULL == op)
  218. {
  219. LOG (GNUNET_ERROR_TYPE_DEBUG,
  220. "Message with unknown id %llu\n",
  221. (unsigned long long) id);
  222. return;
  223. }
  224. result = (enum GNUNET_ARM_Result) ntohl (res->result);
  225. if ((GNUNET_YES == op->is_arm_stop) && (GNUNET_ARM_RESULT_STOPPING == result))
  226. {
  227. /* special case: if we are stopping 'gnunet-service-arm', we do not just
  228. wait for the result message, but also wait for the service to close
  229. the connection (and then we have to close our client handle as well);
  230. this is done by installing a different receive handler, waiting for
  231. the connection to go down */
  232. if (NULL != h->thm)
  233. {
  234. GNUNET_break (0);
  235. op->result_cont (h->thm->cont_cls,
  236. GNUNET_ARM_REQUEST_SENT_OK,
  237. GNUNET_ARM_RESULT_IS_NOT_KNOWN);
  238. GNUNET_free (h->thm);
  239. }
  240. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  241. h->operation_pending_tail,
  242. op);
  243. h->thm = op;
  244. return;
  245. }
  246. result_cont = op->result_cont;
  247. result_cont_cls = op->cont_cls;
  248. GNUNET_ARM_operation_cancel (op);
  249. if (NULL != result_cont)
  250. result_cont (result_cont_cls, GNUNET_ARM_REQUEST_SENT_OK, result);
  251. }
  252. /**
  253. * Checked that list result message is well-formed.
  254. *
  255. * @param cls our `struct GNUNET_ARM_Handle`
  256. * @param lres the message received from the arm service
  257. * @return #GNUNET_OK if message is well-formed
  258. */
  259. static int
  260. check_arm_list_result (void *cls,
  261. const struct GNUNET_ARM_ListResultMessage *lres)
  262. {
  263. const char *pos = (const char *) &lres[1];
  264. uint16_t rcount = ntohs (lres->count);
  265. uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof (*lres);
  266. uint16_t size_check;
  267. (void) cls;
  268. size_check = 0;
  269. for (unsigned int i = 0; i < rcount; i++)
  270. {
  271. const char *end = memchr (pos, 0, msize - size_check);
  272. if (NULL == end)
  273. {
  274. GNUNET_break (0);
  275. return GNUNET_SYSERR;
  276. }
  277. size_check += (end - pos) + 1;
  278. pos = end + 1;
  279. }
  280. return GNUNET_OK;
  281. }
  282. /**
  283. * Handler for ARM list replies.
  284. *
  285. * @param cls our `struct GNUNET_ARM_Handle`
  286. * @param lres the message received from the arm service
  287. */
  288. static void
  289. handle_arm_list_result (void *cls,
  290. const struct GNUNET_ARM_ListResultMessage *lres)
  291. {
  292. struct GNUNET_ARM_Handle *h = cls;
  293. uint16_t rcount = ntohs (lres->count);
  294. const char *list[rcount];
  295. const char *pos = (const char *) &lres[1];
  296. uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof (*lres);
  297. struct GNUNET_ARM_Operation *op;
  298. uint16_t size_check;
  299. uint64_t id;
  300. id = GNUNET_ntohll (lres->arm_msg.request_id);
  301. op = find_op_by_id (h, id);
  302. if (NULL == op)
  303. {
  304. LOG (GNUNET_ERROR_TYPE_DEBUG,
  305. "Message with unknown id %llu\n",
  306. (unsigned long long) id);
  307. return;
  308. }
  309. size_check = 0;
  310. for (unsigned int i = 0; i < rcount; i++)
  311. {
  312. const char *end = memchr (pos, 0, msize - size_check);
  313. /* Assert, as this was already checked in #check_arm_list_result() */
  314. GNUNET_assert (NULL != end);
  315. list[i] = pos;
  316. size_check += (end - pos) + 1;
  317. pos = end + 1;
  318. }
  319. if (NULL != op->list_cont)
  320. op->list_cont (op->cont_cls, GNUNET_ARM_REQUEST_SENT_OK, rcount, list);
  321. GNUNET_ARM_operation_cancel (op);
  322. }
  323. /**
  324. * Receive confirmation from test, ARM service is up.
  325. *
  326. * @param cls closure with the `struct GNUNET_ARM_Handle`
  327. * @param msg message received
  328. */
  329. static void
  330. handle_confirm (void *cls, const struct GNUNET_MessageHeader *msg)
  331. {
  332. struct GNUNET_ARM_Handle *h = cls;
  333. (void) msg;
  334. LOG (GNUNET_ERROR_TYPE_DEBUG, "Got confirmation from ARM that we are up!\n");
  335. if (GNUNET_NO == h->currently_up)
  336. {
  337. h->currently_up = GNUNET_YES;
  338. if (NULL != h->conn_status)
  339. h->conn_status (h->conn_status_cls, GNUNET_YES);
  340. }
  341. }
  342. /**
  343. * Generic error handler, called with the appropriate error code and
  344. * the same closure specified at the creation of the message queue.
  345. * Not every message queue implementation supports an error handler.
  346. *
  347. * @param cls closure with the `struct GNUNET_ARM_Handle *`
  348. * @param error error code
  349. */
  350. static void
  351. mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
  352. {
  353. struct GNUNET_ARM_Handle *h = cls;
  354. struct GNUNET_ARM_Operation *op;
  355. (void) error;
  356. h->currently_up = GNUNET_NO;
  357. if (NULL != (op = h->thm))
  358. {
  359. h->thm = NULL;
  360. op->result_cont (op->cont_cls,
  361. GNUNET_ARM_REQUEST_SENT_OK,
  362. GNUNET_ARM_RESULT_STOPPED);
  363. GNUNET_free (op);
  364. }
  365. reconnect_arm_later (h);
  366. }
  367. /**
  368. * Connect to arm.
  369. *
  370. * @param h arm handle
  371. * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
  372. */
  373. static int
  374. reconnect_arm (struct GNUNET_ARM_Handle *h)
  375. {
  376. struct GNUNET_MQ_MessageHandler handlers[] =
  377. {GNUNET_MQ_hd_fixed_size (arm_result,
  378. GNUNET_MESSAGE_TYPE_ARM_RESULT,
  379. struct GNUNET_ARM_ResultMessage,
  380. h),
  381. GNUNET_MQ_hd_var_size (arm_list_result,
  382. GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
  383. struct GNUNET_ARM_ListResultMessage,
  384. h),
  385. GNUNET_MQ_hd_fixed_size (confirm,
  386. GNUNET_MESSAGE_TYPE_ARM_TEST,
  387. struct GNUNET_MessageHeader,
  388. h),
  389. GNUNET_MQ_handler_end ()};
  390. struct GNUNET_MessageHeader *test;
  391. struct GNUNET_MQ_Envelope *env;
  392. if (NULL != h->mq)
  393. return GNUNET_OK;
  394. GNUNET_assert (GNUNET_NO == h->currently_up);
  395. h->mq = GNUNET_CLIENT_connect (h->cfg, "arm", handlers, &mq_error_handler, h);
  396. if (NULL == h->mq)
  397. {
  398. LOG (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_CLIENT_connect returned NULL\n");
  399. if (NULL != h->conn_status)
  400. h->conn_status (h->conn_status_cls, GNUNET_SYSERR);
  401. return GNUNET_SYSERR;
  402. }
  403. LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending TEST message to ARM\n");
  404. env = GNUNET_MQ_msg (test, GNUNET_MESSAGE_TYPE_ARM_TEST);
  405. GNUNET_MQ_send (h->mq, env);
  406. return GNUNET_OK;
  407. }
  408. /**
  409. * Set up a context for communicating with ARM, then
  410. * start connecting to the ARM service using that context.
  411. *
  412. * @param cfg configuration to use (needed to contact ARM;
  413. * the ARM service may internally use a different
  414. * configuration to determine how to start the service).
  415. * @param conn_status will be called when connecting/disconnecting
  416. * @param conn_status_cls closure for @a conn_status
  417. * @return context to use for further ARM operations, NULL on error.
  418. */
  419. struct GNUNET_ARM_Handle *
  420. GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
  421. GNUNET_ARM_ConnectionStatusCallback conn_status,
  422. void *conn_status_cls)
  423. {
  424. struct GNUNET_ARM_Handle *h;
  425. h = GNUNET_new (struct GNUNET_ARM_Handle);
  426. h->cfg = cfg;
  427. h->conn_status = conn_status;
  428. h->conn_status_cls = conn_status_cls;
  429. if (GNUNET_OK != reconnect_arm (h))
  430. {
  431. GNUNET_free (h);
  432. return NULL;
  433. }
  434. return h;
  435. }
  436. /**
  437. * Disconnect from the ARM service (if connected) and destroy the context.
  438. *
  439. * @param h the handle that was being used
  440. */
  441. void
  442. GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
  443. {
  444. struct GNUNET_ARM_Operation *op;
  445. LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
  446. while (NULL != (op = h->operation_pending_head))
  447. {
  448. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  449. h->operation_pending_tail,
  450. op);
  451. if (NULL != op->result_cont)
  452. op->result_cont (op->cont_cls, GNUNET_ARM_REQUEST_DISCONNECTED, 0);
  453. if (NULL != op->list_cont)
  454. op->list_cont (op->cont_cls, GNUNET_ARM_REQUEST_DISCONNECTED, 0, NULL);
  455. if (NULL != op->async)
  456. {
  457. GNUNET_SCHEDULER_cancel (op->async);
  458. op->async = NULL;
  459. }
  460. GNUNET_free (op);
  461. }
  462. if (NULL != h->mq)
  463. {
  464. GNUNET_MQ_destroy (h->mq);
  465. h->mq = NULL;
  466. }
  467. if (NULL != h->reconnect_task)
  468. {
  469. GNUNET_SCHEDULER_cancel (h->reconnect_task);
  470. h->reconnect_task = NULL;
  471. }
  472. GNUNET_free (h);
  473. }
  474. /**
  475. * A client specifically requested starting of ARM itself.
  476. * Starts the ARM service.
  477. *
  478. * @param h the handle with configuration details
  479. * @param std_inheritance inheritance of std streams
  480. * @return operation status code
  481. */
  482. static enum GNUNET_ARM_Result
  483. start_arm_service (struct GNUNET_ARM_Handle *h,
  484. enum GNUNET_OS_InheritStdioFlags std_inheritance)
  485. {
  486. struct GNUNET_OS_Process *proc;
  487. char *cbinary;
  488. char *binary;
  489. char *quotedbinary;
  490. char *config;
  491. char *loprefix;
  492. char *lopostfix;
  493. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
  494. "arm",
  495. "PREFIX",
  496. &loprefix))
  497. loprefix = GNUNET_strdup ("");
  498. else
  499. loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix);
  500. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
  501. "arm",
  502. "OPTIONS",
  503. &lopostfix))
  504. lopostfix = GNUNET_strdup ("");
  505. else
  506. lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, lopostfix);
  507. if (GNUNET_OK !=
  508. GNUNET_CONFIGURATION_get_value_string (h->cfg, "arm", "BINARY", &cbinary))
  509. {
  510. GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY");
  511. GNUNET_free (loprefix);
  512. GNUNET_free (lopostfix);
  513. return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
  514. }
  515. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
  516. "arm",
  517. "CONFIG",
  518. &config))
  519. config = NULL;
  520. binary = GNUNET_OS_get_libexec_binary_path (cbinary);
  521. GNUNET_asprintf (&quotedbinary, "\"%s\"", binary);
  522. GNUNET_free (cbinary);
  523. if ((GNUNET_YES ==
  524. GNUNET_CONFIGURATION_have_value (h->cfg, "TESTING", "WEAKRANDOM")) &&
  525. (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
  526. "TESTING",
  527. "WEAKRANDOM")) &&
  528. (GNUNET_NO ==
  529. GNUNET_CONFIGURATION_have_value (h->cfg, "TESTING", "HOSTFILE")))
  530. {
  531. /* Means we are ONLY running locally */
  532. /* we're clearly running a test, don't daemonize */
  533. if (NULL == config)
  534. proc = GNUNET_OS_start_process_s (GNUNET_NO,
  535. std_inheritance,
  536. NULL,
  537. loprefix,
  538. quotedbinary,
  539. /* no daemonization! */
  540. lopostfix,
  541. NULL);
  542. else
  543. proc = GNUNET_OS_start_process_s (GNUNET_NO,
  544. std_inheritance,
  545. NULL,
  546. loprefix,
  547. quotedbinary,
  548. "-c",
  549. config,
  550. /* no daemonization! */
  551. lopostfix,
  552. NULL);
  553. }
  554. else
  555. {
  556. if (NULL == config)
  557. proc = GNUNET_OS_start_process_s (GNUNET_NO,
  558. std_inheritance,
  559. NULL,
  560. loprefix,
  561. quotedbinary,
  562. "-d", /* do daemonize */
  563. lopostfix,
  564. NULL);
  565. else
  566. proc = GNUNET_OS_start_process_s (GNUNET_NO,
  567. std_inheritance,
  568. NULL,
  569. loprefix,
  570. quotedbinary,
  571. "-c",
  572. config,
  573. "-d", /* do daemonize */
  574. lopostfix,
  575. NULL);
  576. }
  577. GNUNET_free (binary);
  578. GNUNET_free (quotedbinary);
  579. GNUNET_free_non_null (config);
  580. GNUNET_free (loprefix);
  581. GNUNET_free (lopostfix);
  582. if (NULL == proc)
  583. return GNUNET_ARM_RESULT_START_FAILED;
  584. GNUNET_OS_process_destroy (proc);
  585. return GNUNET_ARM_RESULT_STARTING;
  586. }
  587. /**
  588. * Abort an operation. Only prevents the callback from being
  589. * called, the operation may still complete.
  590. *
  591. * @param op operation to cancel
  592. */
  593. void
  594. GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
  595. {
  596. struct GNUNET_ARM_Handle *h = op->h;
  597. if (h->thm == op)
  598. {
  599. op->result_cont = NULL;
  600. return;
  601. }
  602. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  603. h->operation_pending_tail,
  604. op);
  605. GNUNET_free (op);
  606. }
  607. /**
  608. * Start or stop a service.
  609. *
  610. * @param h handle to ARM
  611. * @param service_name name of the service
  612. * @param cb callback to invoke when service is ready
  613. * @param cb_cls closure for @a cb
  614. * @param type type of the request
  615. * @return handle to queue, NULL on error
  616. */
  617. static struct GNUNET_ARM_Operation *
  618. change_service (struct GNUNET_ARM_Handle *h,
  619. const char *service_name,
  620. GNUNET_ARM_ResultCallback cb,
  621. void *cb_cls,
  622. uint16_t type)
  623. {
  624. struct GNUNET_ARM_Operation *op;
  625. size_t slen;
  626. struct GNUNET_MQ_Envelope *env;
  627. struct GNUNET_ARM_Message *msg;
  628. slen = strlen (service_name) + 1;
  629. if (slen + sizeof (struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE)
  630. {
  631. GNUNET_break (0);
  632. return NULL;
  633. }
  634. if (0 == h->request_id_counter)
  635. h->request_id_counter++;
  636. op = GNUNET_new (struct GNUNET_ARM_Operation);
  637. op->h = h;
  638. op->result_cont = cb;
  639. op->cont_cls = cb_cls;
  640. op->id = h->request_id_counter++;
  641. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  642. h->operation_pending_tail,
  643. op);
  644. env = GNUNET_MQ_msg_extra (msg, slen, type);
  645. msg->reserved = htonl (0);
  646. msg->request_id = GNUNET_htonll (op->id);
  647. GNUNET_memcpy (&msg[1], service_name, slen);
  648. GNUNET_MQ_send (h->mq, env);
  649. return op;
  650. }
  651. /**
  652. * Task run to notify application that ARM is already up.
  653. *
  654. * @param cls the operation that asked ARM to be started
  655. */
  656. static void
  657. notify_running (void *cls)
  658. {
  659. struct GNUNET_ARM_Operation *op = cls;
  660. struct GNUNET_ARM_Handle *h = op->h;
  661. op->async = NULL;
  662. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  663. h->operation_pending_tail,
  664. op);
  665. if (NULL != op->result_cont)
  666. op->result_cont (op->cont_cls,
  667. GNUNET_ARM_REQUEST_SENT_OK,
  668. GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
  669. if ((GNUNET_YES == h->currently_up) && (NULL != h->conn_status))
  670. h->conn_status (h->conn_status_cls, GNUNET_YES);
  671. GNUNET_free (op);
  672. }
  673. /**
  674. * Task run to notify application that ARM is being started.
  675. *
  676. * @param cls the operation that asked ARM to be started
  677. */
  678. static void
  679. notify_starting (void *cls)
  680. {
  681. struct GNUNET_ARM_Operation *op = cls;
  682. struct GNUNET_ARM_Handle *h = op->h;
  683. op->async = NULL;
  684. LOG (GNUNET_ERROR_TYPE_DEBUG,
  685. "Notifying client that we started the ARM service\n");
  686. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  687. h->operation_pending_tail,
  688. op);
  689. if (NULL != op->result_cont)
  690. op->result_cont (op->cont_cls,
  691. GNUNET_ARM_REQUEST_SENT_OK,
  692. op->starting_ret);
  693. GNUNET_free (op);
  694. }
  695. /**
  696. * Request for a service to be started.
  697. *
  698. * @param h handle to ARM
  699. * @param service_name name of the service
  700. * @param std_inheritance inheritance of std streams
  701. * @param cont callback to invoke after request is sent or not sent
  702. * @param cont_cls closure for @a cont
  703. * @return handle for the operation, NULL on error
  704. */
  705. struct GNUNET_ARM_Operation *
  706. GNUNET_ARM_request_service_start (
  707. struct GNUNET_ARM_Handle *h,
  708. const char *service_name,
  709. enum GNUNET_OS_InheritStdioFlags std_inheritance,
  710. GNUNET_ARM_ResultCallback cont,
  711. void *cont_cls)
  712. {
  713. struct GNUNET_ARM_Operation *op;
  714. enum GNUNET_ARM_Result ret;
  715. LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting service `%s'\n", service_name);
  716. if (0 != strcasecmp ("arm", service_name))
  717. return change_service (h,
  718. service_name,
  719. cont,
  720. cont_cls,
  721. GNUNET_MESSAGE_TYPE_ARM_START);
  722. /* Possible cases:
  723. * 1) We're connected to ARM already. Invoke the callback immediately.
  724. * 2) We're not connected to ARM.
  725. * Cancel any reconnection attempts temporarily, then perform
  726. * a service test.
  727. */
  728. if (GNUNET_YES == h->currently_up)
  729. {
  730. LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n");
  731. op = GNUNET_new (struct GNUNET_ARM_Operation);
  732. op->h = h;
  733. op->result_cont = cont;
  734. op->cont_cls = cont_cls;
  735. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  736. h->operation_pending_tail,
  737. op);
  738. op->async = GNUNET_SCHEDULER_add_now (&notify_running, op);
  739. return op;
  740. }
  741. /* This is an inherently uncertain choice, as it is of course
  742. theoretically possible that ARM is up and we just did not
  743. yet complete the MQ handshake. However, given that users
  744. are unlikely to hammer 'gnunet-arm -s' on a busy system,
  745. the above check should catch 99.99% of the cases where ARM
  746. is already running. */
  747. LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting ARM service\n");
  748. ret = start_arm_service (h, std_inheritance);
  749. if (GNUNET_ARM_RESULT_STARTING == ret)
  750. reconnect_arm (h);
  751. op = GNUNET_new (struct GNUNET_ARM_Operation);
  752. op->h = h;
  753. op->result_cont = cont;
  754. op->cont_cls = cont_cls;
  755. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  756. h->operation_pending_tail,
  757. op);
  758. op->starting_ret = ret;
  759. op->async = GNUNET_SCHEDULER_add_now (&notify_starting, op);
  760. return op;
  761. }
  762. /**
  763. * Request a service to be stopped. Stopping arm itself will not
  764. * invalidate its handle, and ARM API will try to restore connection
  765. * to the ARM service, even if ARM connection was lost because you
  766. * asked for ARM to be stopped. Call
  767. * #GNUNET_ARM_disconnect() to free the handle and prevent
  768. * further connection attempts.
  769. *
  770. * @param h handle to ARM
  771. * @param service_name name of the service
  772. * @param cont callback to invoke after request is sent or is not sent
  773. * @param cont_cls closure for @a cont
  774. * @return handle for the operation, NULL on error
  775. */
  776. struct GNUNET_ARM_Operation *
  777. GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
  778. const char *service_name,
  779. GNUNET_ARM_ResultCallback cont,
  780. void *cont_cls)
  781. {
  782. struct GNUNET_ARM_Operation *op;
  783. LOG (GNUNET_ERROR_TYPE_DEBUG, "Stopping service `%s'\n", service_name);
  784. op = change_service (h,
  785. service_name,
  786. cont,
  787. cont_cls,
  788. GNUNET_MESSAGE_TYPE_ARM_STOP);
  789. if (NULL == op)
  790. return NULL;
  791. /* If the service is ARM, set a flag as we will use MQ errors
  792. to detect that the process is really gone. */
  793. if (0 == strcasecmp (service_name, "arm"))
  794. op->is_arm_stop = GNUNET_YES;
  795. return op;
  796. }
  797. /**
  798. * Request a list of running services.
  799. *
  800. * @param h handle to ARM
  801. * @param cont callback to invoke after request is sent or is not sent
  802. * @param cont_cls closure for @a cont
  803. * @return handle for the operation, NULL on error
  804. */
  805. struct GNUNET_ARM_Operation *
  806. GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
  807. GNUNET_ARM_ServiceListCallback cont,
  808. void *cont_cls)
  809. {
  810. struct GNUNET_ARM_Operation *op;
  811. struct GNUNET_MQ_Envelope *env;
  812. struct GNUNET_ARM_Message *msg;
  813. LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting LIST from ARM service\n");
  814. if (0 == h->request_id_counter)
  815. h->request_id_counter++;
  816. op = GNUNET_new (struct GNUNET_ARM_Operation);
  817. op->h = h;
  818. op->list_cont = cont;
  819. op->cont_cls = cont_cls;
  820. op->id = h->request_id_counter++;
  821. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  822. h->operation_pending_tail,
  823. op);
  824. env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_ARM_LIST);
  825. msg->reserved = htonl (0);
  826. msg->request_id = GNUNET_htonll (op->id);
  827. GNUNET_MQ_send (h->mq, env);
  828. return op;
  829. }
  830. /* end of arm_api.c */