arm_api.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2009, 2010, 2012, 2013, 2016, 2019 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. * File descriptor to close on operation stop, if not NULL.
  72. */
  73. struct GNUNET_DISK_FileHandle *rfd;
  74. /**
  75. * Is this an operation to stop the ARM service?
  76. */
  77. int is_arm_stop;
  78. };
  79. /**
  80. * Handle for interacting with ARM.
  81. */
  82. struct GNUNET_ARM_Handle
  83. {
  84. /**
  85. * Our connection to the ARM service.
  86. */
  87. struct GNUNET_MQ_Handle *mq;
  88. /**
  89. * The configuration that we are using.
  90. */
  91. const struct GNUNET_CONFIGURATION_Handle *cfg;
  92. /**
  93. * Head of doubly-linked list of pending operations.
  94. */
  95. struct GNUNET_ARM_Operation *operation_pending_head;
  96. /**
  97. * Tail of doubly-linked list of pending operations.
  98. */
  99. struct GNUNET_ARM_Operation *operation_pending_tail;
  100. /**
  101. * Callback to invoke on connection/disconnection.
  102. */
  103. GNUNET_ARM_ConnectionStatusCallback conn_status;
  104. /**
  105. * Closure for @e conn_status.
  106. */
  107. void *conn_status_cls;
  108. /**
  109. * ARM operation where the goal is to wait for ARM shutdown to
  110. * complete. This operation is special in that it waits for an
  111. * error on the @e mq. So we complete it by calling the
  112. * continuation in the #mq_error_handler(). Note that the operation
  113. * is no longer in the @e operation_pending_head DLL once it is
  114. * referenced from this field.
  115. */
  116. struct GNUNET_ARM_Operation *thm;
  117. /**
  118. * ID of the reconnect task (if any).
  119. */
  120. struct GNUNET_SCHEDULER_Task *reconnect_task;
  121. /**
  122. * Current delay we use for re-trying to connect to core.
  123. */
  124. struct GNUNET_TIME_Relative retry_backoff;
  125. /**
  126. * Counter for request identifiers. They are used to match replies
  127. * from ARM to operations in the @e operation_pending_head DLL.
  128. */
  129. uint64_t request_id_counter;
  130. /**
  131. * Have we detected that ARM is up?
  132. */
  133. int currently_up;
  134. };
  135. /**
  136. * Connect to arm.
  137. *
  138. * @param h arm handle
  139. * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
  140. */
  141. static int
  142. reconnect_arm (struct GNUNET_ARM_Handle *h);
  143. /**
  144. * Task scheduled to try to re-connect to arm.
  145. *
  146. * @param cls the `struct GNUNET_ARM_Handle`
  147. */
  148. static void
  149. reconnect_arm_task (void *cls)
  150. {
  151. struct GNUNET_ARM_Handle *h = cls;
  152. h->reconnect_task = NULL;
  153. reconnect_arm (h);
  154. }
  155. /**
  156. * Close down any existing connection to the ARM service and
  157. * try re-establishing it later.
  158. *
  159. * @param h our handle
  160. */
  161. static void
  162. reconnect_arm_later (struct GNUNET_ARM_Handle *h)
  163. {
  164. struct GNUNET_ARM_Operation *op;
  165. if (NULL != h->mq)
  166. {
  167. GNUNET_MQ_destroy (h->mq);
  168. h->mq = NULL;
  169. }
  170. h->currently_up = GNUNET_NO;
  171. GNUNET_assert (NULL == h->reconnect_task);
  172. h->reconnect_task =
  173. GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
  174. &reconnect_arm_task,
  175. h);
  176. while (NULL != (op = h->operation_pending_head))
  177. {
  178. if (NULL != op->result_cont)
  179. op->result_cont (op->cont_cls,
  180. GNUNET_ARM_REQUEST_DISCONNECTED,
  181. 0);
  182. if (NULL != op->list_cont)
  183. op->list_cont (op->cont_cls,
  184. GNUNET_ARM_REQUEST_DISCONNECTED,
  185. 0,
  186. NULL);
  187. GNUNET_ARM_operation_cancel (op);
  188. }
  189. GNUNET_assert (NULL == h->operation_pending_head);
  190. h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
  191. if (NULL != h->conn_status)
  192. h->conn_status (h->conn_status_cls,
  193. GNUNET_NO);
  194. }
  195. /**
  196. * Find a control message by its unique ID.
  197. *
  198. * @param h ARM handle
  199. * @param id unique message ID to use for the lookup
  200. * @return NULL if not found
  201. */
  202. static struct GNUNET_ARM_Operation *
  203. find_op_by_id (struct GNUNET_ARM_Handle *h,
  204. uint64_t id)
  205. {
  206. for (struct GNUNET_ARM_Operation *result = h->operation_pending_head;
  207. NULL != result;
  208. result = result->next)
  209. if (id == result->id)
  210. return result;
  211. return NULL;
  212. }
  213. /**
  214. * Handler for ARM replies.
  215. *
  216. * @param cls our `struct GNUNET_ARM_Handle`
  217. * @param res the message received from the arm service
  218. */
  219. static void
  220. handle_arm_result (void *cls,
  221. const struct GNUNET_ARM_ResultMessage *res)
  222. {
  223. struct GNUNET_ARM_Handle *h = cls;
  224. struct GNUNET_ARM_Operation *op;
  225. uint64_t id;
  226. enum GNUNET_ARM_Result result;
  227. GNUNET_ARM_ResultCallback result_cont;
  228. void *result_cont_cls;
  229. id = GNUNET_ntohll (res->arm_msg.request_id);
  230. op = find_op_by_id (h,
  231. id);
  232. if (NULL == op)
  233. {
  234. LOG (GNUNET_ERROR_TYPE_DEBUG,
  235. "Message with unknown id %llu\n",
  236. (unsigned long long) id);
  237. return;
  238. }
  239. result = (enum GNUNET_ARM_Result) ntohl (res->result);
  240. if ( (GNUNET_YES == op->is_arm_stop) &&
  241. (GNUNET_ARM_RESULT_STOPPING == result) )
  242. {
  243. /* special case: if we are stopping 'gnunet-service-arm', we do not just
  244. wait for the result message, but also wait for the service to close
  245. the connection (and then we have to close our client handle as well);
  246. this is done by installing a different receive handler, waiting for
  247. the connection to go down */if (NULL != h->thm)
  248. {
  249. GNUNET_break (0);
  250. op->result_cont (h->thm->cont_cls,
  251. GNUNET_ARM_REQUEST_SENT_OK,
  252. GNUNET_ARM_RESULT_IS_NOT_KNOWN);
  253. GNUNET_free (h->thm);
  254. }
  255. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  256. h->operation_pending_tail,
  257. op);
  258. h->thm = op;
  259. return;
  260. }
  261. result_cont = op->result_cont;
  262. result_cont_cls = op->cont_cls;
  263. GNUNET_ARM_operation_cancel (op);
  264. if (NULL != result_cont)
  265. result_cont (result_cont_cls,
  266. GNUNET_ARM_REQUEST_SENT_OK,
  267. result);
  268. }
  269. /**
  270. * Read from a string pool.
  271. *
  272. * @param pool_start start of the string pool
  273. * @param pool_size size of the string pool
  274. * @param str_index index into the string pool
  275. * @returns an index into the string pool, or
  276. * NULL if the index is out of bounds
  277. */
  278. static const char *
  279. pool_get (const char *pool_start,
  280. size_t pool_size,
  281. size_t str_index)
  282. {
  283. const char *str_start;
  284. const char *end;
  285. if (str_index >= pool_size)
  286. return NULL;
  287. str_start = pool_start + str_index;
  288. end = memchr (str_start, 0, pool_size - str_index);
  289. if (NULL == end)
  290. return NULL;
  291. return str_start;
  292. }
  293. /**
  294. * Check that list result message is well-formed.
  295. *
  296. * @param cls our `struct GNUNET_ARM_Handle`
  297. * @param lres the message received from the arm service
  298. * @return #GNUNET_OK if message is well-formed
  299. */
  300. static int
  301. check_arm_list_result (void *cls,
  302. const struct GNUNET_ARM_ListResultMessage *lres)
  303. {
  304. uint16_t rcount = ntohs (lres->count);
  305. uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres);
  306. struct GNUNET_ARM_ServiceInfoMessage *ssm;
  307. size_t pool_size;
  308. char *pool_start;
  309. (void) cls;
  310. if ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) > msize))
  311. {
  312. GNUNET_break_op (0);
  313. return GNUNET_NO;
  314. }
  315. ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1];
  316. pool_start = (char *) (ssm + rcount);
  317. pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage));
  318. for (unsigned int i = 0; i < rcount; i++)
  319. {
  320. uint16_t name_index = ntohs (ssm->name_index);
  321. uint16_t binary_index = ntohs (ssm->binary_index);
  322. if (NULL == pool_get (pool_start,
  323. pool_size,
  324. name_index))
  325. {
  326. GNUNET_break_op (0);
  327. return GNUNET_NO;
  328. }
  329. if (NULL == pool_get (pool_start,
  330. pool_size,
  331. binary_index))
  332. {
  333. GNUNET_break_op (0);
  334. return GNUNET_NO;
  335. }
  336. ssm++;
  337. }
  338. return GNUNET_OK;
  339. }
  340. /**
  341. * Handler for ARM list replies.
  342. *
  343. * @param cls our `struct GNUNET_ARM_Handle`
  344. * @param lres the message received from the arm service
  345. */
  346. static void
  347. handle_arm_list_result (void *cls,
  348. const struct GNUNET_ARM_ListResultMessage *lres)
  349. {
  350. struct GNUNET_ARM_Handle *h = cls;
  351. uint16_t rcount = ntohs (lres->count);
  352. uint16_t msize = ntohs (lres->arm_msg.header.size) - sizeof(*lres);
  353. struct GNUNET_ARM_ServiceInfo list[rcount];
  354. struct GNUNET_ARM_ServiceInfoMessage *ssm;
  355. struct GNUNET_ARM_Operation *op;
  356. uint64_t id;
  357. size_t pool_size;
  358. char *pool_start;
  359. id = GNUNET_ntohll (lres->arm_msg.request_id);
  360. op = find_op_by_id (h, id);
  361. if (NULL == op)
  362. {
  363. LOG (GNUNET_ERROR_TYPE_DEBUG,
  364. "Message with unknown id %llu\n",
  365. (unsigned long long) id);
  366. return;
  367. }
  368. GNUNET_assert ((rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage) <=
  369. msize));
  370. ssm = (struct GNUNET_ARM_ServiceInfoMessage *) &lres[1];
  371. pool_start = (char *) (ssm + rcount);
  372. pool_size = msize - (rcount * sizeof (struct GNUNET_ARM_ServiceInfoMessage));
  373. for (unsigned int i = 0; i < rcount; i++)
  374. {
  375. uint16_t name_index = ntohs (ssm->name_index);
  376. uint16_t binary_index = ntohs (ssm->binary_index);
  377. const char *name;
  378. const char *binary;
  379. GNUNET_assert (NULL != (name = pool_get (pool_start,
  380. pool_size,
  381. name_index)));
  382. GNUNET_assert (NULL != (binary = pool_get (pool_start,
  383. pool_size,
  384. binary_index)));
  385. list[i] = (struct GNUNET_ARM_ServiceInfo) {
  386. .name = name,
  387. .binary = binary,
  388. .status = ntohl (ssm->status),
  389. .last_started_at = GNUNET_TIME_absolute_ntoh (ssm->last_started_at),
  390. .restart_at = GNUNET_TIME_absolute_ntoh (ssm->restart_at),
  391. .last_exit_status = ntohs (ssm->last_exit_status),
  392. };
  393. ssm++;
  394. }
  395. if (NULL != op->list_cont)
  396. op->list_cont (op->cont_cls,
  397. GNUNET_ARM_REQUEST_SENT_OK,
  398. rcount,
  399. list);
  400. GNUNET_ARM_operation_cancel (op);
  401. }
  402. /**
  403. * Receive confirmation from test, ARM service is up.
  404. *
  405. * @param cls closure with the `struct GNUNET_ARM_Handle`
  406. * @param msg message received
  407. */
  408. static void
  409. handle_confirm (void *cls,
  410. const struct GNUNET_MessageHeader *msg)
  411. {
  412. struct GNUNET_ARM_Handle *h = cls;
  413. (void) msg;
  414. LOG (GNUNET_ERROR_TYPE_DEBUG,
  415. "Got confirmation from ARM that we are up!\n");
  416. if (GNUNET_NO == h->currently_up)
  417. {
  418. h->currently_up = GNUNET_YES;
  419. if (NULL != h->conn_status)
  420. h->conn_status (h->conn_status_cls, GNUNET_YES);
  421. }
  422. }
  423. /**
  424. * Generic error handler, called with the appropriate error code and
  425. * the same closure specified at the creation of the message queue.
  426. * Not every message queue implementation supports an error handler.
  427. *
  428. * @param cls closure with the `struct GNUNET_ARM_Handle *`
  429. * @param error error code
  430. */
  431. static void
  432. mq_error_handler (void *cls,
  433. enum GNUNET_MQ_Error error)
  434. {
  435. struct GNUNET_ARM_Handle *h = cls;
  436. struct GNUNET_ARM_Operation *op;
  437. (void) error;
  438. h->currently_up = GNUNET_NO;
  439. if (NULL != (op = h->thm))
  440. {
  441. h->thm = NULL;
  442. op->result_cont (op->cont_cls,
  443. GNUNET_ARM_REQUEST_SENT_OK,
  444. GNUNET_ARM_RESULT_STOPPED);
  445. GNUNET_free (op);
  446. }
  447. reconnect_arm_later (h);
  448. }
  449. /**
  450. * Connect to arm.
  451. *
  452. * @param h arm handle
  453. * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
  454. */
  455. static int
  456. reconnect_arm (struct GNUNET_ARM_Handle *h)
  457. {
  458. struct GNUNET_MQ_MessageHandler handlers[] = {
  459. GNUNET_MQ_hd_fixed_size (arm_result,
  460. GNUNET_MESSAGE_TYPE_ARM_RESULT,
  461. struct GNUNET_ARM_ResultMessage,
  462. h),
  463. GNUNET_MQ_hd_var_size (arm_list_result,
  464. GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
  465. struct GNUNET_ARM_ListResultMessage,
  466. h),
  467. GNUNET_MQ_hd_fixed_size (confirm,
  468. GNUNET_MESSAGE_TYPE_ARM_TEST,
  469. struct GNUNET_MessageHeader,
  470. h),
  471. GNUNET_MQ_handler_end ()
  472. };
  473. struct GNUNET_MessageHeader *test;
  474. struct GNUNET_MQ_Envelope *env;
  475. if (NULL != h->mq)
  476. return GNUNET_OK;
  477. GNUNET_assert (GNUNET_NO == h->currently_up);
  478. h->mq = GNUNET_CLIENT_connect (h->cfg,
  479. "arm",
  480. handlers,
  481. &mq_error_handler,
  482. h);
  483. if (NULL == h->mq)
  484. {
  485. LOG (GNUNET_ERROR_TYPE_DEBUG,
  486. "GNUNET_CLIENT_connect returned NULL\n");
  487. if (NULL != h->conn_status)
  488. h->conn_status (h->conn_status_cls,
  489. GNUNET_SYSERR);
  490. return GNUNET_SYSERR;
  491. }
  492. LOG (GNUNET_ERROR_TYPE_DEBUG,
  493. "Sending TEST message to ARM\n");
  494. env = GNUNET_MQ_msg (test,
  495. GNUNET_MESSAGE_TYPE_ARM_TEST);
  496. GNUNET_MQ_send (h->mq, env);
  497. return GNUNET_OK;
  498. }
  499. /**
  500. * Set up a context for communicating with ARM, then
  501. * start connecting to the ARM service using that context.
  502. *
  503. * @param cfg configuration to use (needed to contact ARM;
  504. * the ARM service may internally use a different
  505. * configuration to determine how to start the service).
  506. * @param conn_status will be called when connecting/disconnecting
  507. * @param conn_status_cls closure for @a conn_status
  508. * @return context to use for further ARM operations, NULL on error.
  509. */
  510. struct GNUNET_ARM_Handle *
  511. GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
  512. GNUNET_ARM_ConnectionStatusCallback conn_status,
  513. void *conn_status_cls)
  514. {
  515. struct GNUNET_ARM_Handle *h;
  516. h = GNUNET_new (struct GNUNET_ARM_Handle);
  517. h->cfg = cfg;
  518. h->conn_status = conn_status;
  519. h->conn_status_cls = conn_status_cls;
  520. if (GNUNET_OK != reconnect_arm (h))
  521. {
  522. GNUNET_free (h);
  523. return NULL;
  524. }
  525. return h;
  526. }
  527. /**
  528. * Disconnect from the ARM service (if connected) and destroy the context.
  529. *
  530. * @param h the handle that was being used
  531. */
  532. void
  533. GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h)
  534. {
  535. struct GNUNET_ARM_Operation *op;
  536. LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
  537. while (NULL != (op = h->operation_pending_head))
  538. {
  539. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  540. h->operation_pending_tail,
  541. op);
  542. if (NULL != op->result_cont)
  543. op->result_cont (op->cont_cls,
  544. GNUNET_ARM_REQUEST_DISCONNECTED,
  545. 0);
  546. if (NULL != op->list_cont)
  547. op->list_cont (op->cont_cls,
  548. GNUNET_ARM_REQUEST_DISCONNECTED,
  549. 0,
  550. NULL);
  551. if (NULL != op->async)
  552. {
  553. GNUNET_SCHEDULER_cancel (op->async);
  554. op->async = NULL;
  555. }
  556. GNUNET_free (op);
  557. }
  558. if (NULL != h->mq)
  559. {
  560. GNUNET_MQ_destroy (h->mq);
  561. h->mq = NULL;
  562. }
  563. if (NULL != h->reconnect_task)
  564. {
  565. GNUNET_SCHEDULER_cancel (h->reconnect_task);
  566. h->reconnect_task = NULL;
  567. }
  568. GNUNET_free (h);
  569. }
  570. /**
  571. * A client specifically requested starting of ARM itself.
  572. * Starts the ARM service.
  573. *
  574. * @param h the handle with configuration details
  575. * @param std_inheritance inheritance of std streams
  576. * @param sigfd socket to pass to ARM for signalling
  577. * @return operation status code
  578. */
  579. static enum GNUNET_ARM_Result
  580. start_arm_service (struct GNUNET_ARM_Handle *h,
  581. enum GNUNET_OS_InheritStdioFlags std_inheritance,
  582. struct GNUNET_DISK_FileHandle *sigfd)
  583. {
  584. struct GNUNET_OS_Process *proc;
  585. char *cbinary;
  586. char *binary;
  587. char *quotedbinary;
  588. char *config;
  589. char *loprefix;
  590. char *lopostfix;
  591. int ld[2];
  592. int *lsocks;
  593. if (NULL == sigfd)
  594. {
  595. lsocks = NULL;
  596. }
  597. else
  598. {
  599. ld[0] = sigfd->fd;
  600. ld[1] = -1;
  601. lsocks = ld;
  602. }
  603. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
  604. "arm",
  605. "PREFIX",
  606. &loprefix))
  607. loprefix = GNUNET_strdup ("");
  608. else
  609. loprefix = GNUNET_CONFIGURATION_expand_dollar (h->cfg, loprefix);
  610. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (h->cfg,
  611. "arm",
  612. "OPTIONS",
  613. &lopostfix))
  614. lopostfix = GNUNET_strdup ("");
  615. else
  616. lopostfix = GNUNET_CONFIGURATION_expand_dollar (h->cfg,
  617. lopostfix);
  618. if (GNUNET_OK !=
  619. GNUNET_CONFIGURATION_get_value_string (h->cfg,
  620. "arm",
  621. "BINARY",
  622. &cbinary))
  623. {
  624. GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
  625. "arm",
  626. "BINARY");
  627. GNUNET_free (loprefix);
  628. GNUNET_free (lopostfix);
  629. return GNUNET_ARM_RESULT_IS_NOT_KNOWN;
  630. }
  631. if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (h->cfg,
  632. "arm",
  633. "CONFIG",
  634. &config))
  635. config = NULL;
  636. binary = GNUNET_OS_get_libexec_binary_path (cbinary);
  637. GNUNET_asprintf (&quotedbinary,
  638. "\"%s\"",
  639. binary);
  640. GNUNET_free (cbinary);
  641. if ( (GNUNET_YES ==
  642. GNUNET_CONFIGURATION_have_value (h->cfg,
  643. "TESTING",
  644. "WEAKRANDOM")) &&
  645. (GNUNET_YES ==
  646. GNUNET_CONFIGURATION_get_value_yesno (h->cfg,
  647. "TESTING",
  648. "WEAKRANDOM")) &&
  649. (GNUNET_NO ==
  650. GNUNET_CONFIGURATION_have_value (h->cfg,
  651. "TESTING",
  652. "HOSTFILE")) )
  653. {
  654. /* Means we are ONLY running locally */
  655. /* we're clearly running a test, don't daemonize */
  656. if (NULL == config)
  657. proc = GNUNET_OS_start_process_s (std_inheritance,
  658. lsocks,
  659. loprefix,
  660. quotedbinary,
  661. /* no daemonization! */
  662. lopostfix,
  663. NULL);
  664. else
  665. proc = GNUNET_OS_start_process_s (std_inheritance,
  666. lsocks,
  667. loprefix,
  668. quotedbinary,
  669. "-c",
  670. config,
  671. /* no daemonization! */
  672. lopostfix,
  673. NULL);
  674. }
  675. else
  676. {
  677. if (NULL == config)
  678. proc = GNUNET_OS_start_process_s (std_inheritance,
  679. lsocks,
  680. loprefix,
  681. quotedbinary,
  682. "-d", /* do daemonize */
  683. lopostfix,
  684. NULL);
  685. else
  686. proc = GNUNET_OS_start_process_s (std_inheritance,
  687. lsocks,
  688. loprefix,
  689. quotedbinary,
  690. "-c",
  691. config,
  692. "-d", /* do daemonize */
  693. lopostfix,
  694. NULL);
  695. }
  696. GNUNET_free (binary);
  697. GNUNET_free (quotedbinary);
  698. GNUNET_free (config);
  699. GNUNET_free (loprefix);
  700. GNUNET_free (lopostfix);
  701. if (NULL == proc)
  702. return GNUNET_ARM_RESULT_START_FAILED;
  703. GNUNET_OS_process_destroy (proc);
  704. return GNUNET_ARM_RESULT_STARTING;
  705. }
  706. /**
  707. * Abort an operation. Only prevents the callback from being
  708. * called, the operation may still complete.
  709. *
  710. * @param op operation to cancel
  711. */
  712. void
  713. GNUNET_ARM_operation_cancel (struct GNUNET_ARM_Operation *op)
  714. {
  715. struct GNUNET_ARM_Handle *h = op->h;
  716. if (NULL != op->async)
  717. {
  718. GNUNET_SCHEDULER_cancel (op->async);
  719. op->async = NULL;
  720. }
  721. if (NULL != op->rfd)
  722. {
  723. GNUNET_DISK_file_close (op->rfd);
  724. op->rfd = NULL;
  725. }
  726. if (h->thm == op)
  727. {
  728. op->result_cont = NULL;
  729. return;
  730. }
  731. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  732. h->operation_pending_tail,
  733. op);
  734. GNUNET_free (op);
  735. }
  736. /**
  737. * Start or stop a service.
  738. *
  739. * @param h handle to ARM
  740. * @param service_name name of the service
  741. * @param cb callback to invoke when service is ready
  742. * @param cb_cls closure for @a cb
  743. * @param type type of the request
  744. * @return handle to queue, NULL on error
  745. */
  746. static struct GNUNET_ARM_Operation *
  747. change_service (struct GNUNET_ARM_Handle *h,
  748. const char *service_name,
  749. GNUNET_ARM_ResultCallback cb,
  750. void *cb_cls,
  751. uint16_t type)
  752. {
  753. struct GNUNET_ARM_Operation *op;
  754. size_t slen;
  755. struct GNUNET_MQ_Envelope *env;
  756. struct GNUNET_ARM_Message *msg;
  757. slen = strlen (service_name) + 1;
  758. if (slen + sizeof(struct GNUNET_ARM_Message) >= GNUNET_MAX_MESSAGE_SIZE)
  759. {
  760. GNUNET_break (0);
  761. return NULL;
  762. }
  763. if (0 == h->request_id_counter)
  764. h->request_id_counter++;
  765. op = GNUNET_new (struct GNUNET_ARM_Operation);
  766. op->h = h;
  767. op->result_cont = cb;
  768. op->cont_cls = cb_cls;
  769. op->id = h->request_id_counter++;
  770. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  771. h->operation_pending_tail,
  772. op);
  773. env = GNUNET_MQ_msg_extra (msg, slen, type);
  774. msg->reserved = htonl (0);
  775. msg->request_id = GNUNET_htonll (op->id);
  776. GNUNET_memcpy (&msg[1], service_name, slen);
  777. GNUNET_MQ_send (h->mq, env);
  778. return op;
  779. }
  780. /**
  781. * Task run to notify application that ARM is already up.
  782. *
  783. * @param cls the operation that asked ARM to be started
  784. */
  785. static void
  786. notify_running (void *cls)
  787. {
  788. struct GNUNET_ARM_Operation *op = cls;
  789. struct GNUNET_ARM_Handle *h = op->h;
  790. op->async = NULL;
  791. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  792. h->operation_pending_tail,
  793. op);
  794. if (NULL != op->result_cont)
  795. op->result_cont (op->cont_cls,
  796. GNUNET_ARM_REQUEST_SENT_OK,
  797. GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
  798. if ( (GNUNET_YES == h->currently_up) &&
  799. (NULL != h->conn_status) )
  800. h->conn_status (h->conn_status_cls,
  801. GNUNET_YES);
  802. GNUNET_free (op);
  803. }
  804. /**
  805. * Task run to notify application that ARM is being started.
  806. *
  807. * @param cls the operation that asked ARM to be started
  808. */
  809. static void
  810. notify_starting (void *cls)
  811. {
  812. struct GNUNET_ARM_Operation *op = cls;
  813. struct GNUNET_ARM_Handle *h = op->h;
  814. op->async = NULL;
  815. LOG (GNUNET_ERROR_TYPE_DEBUG,
  816. "Notifying client that we started the ARM service\n");
  817. GNUNET_CONTAINER_DLL_remove (h->operation_pending_head,
  818. h->operation_pending_tail,
  819. op);
  820. if (NULL != op->result_cont)
  821. op->result_cont (op->cont_cls,
  822. GNUNET_ARM_REQUEST_SENT_OK,
  823. op->starting_ret);
  824. GNUNET_free (op);
  825. }
  826. /**
  827. * Request for a service to be started.
  828. *
  829. * @param h handle to ARM
  830. * @param service_name name of the service
  831. * @param std_inheritance inheritance of std streams
  832. * @param cont callback to invoke after request is sent or not sent
  833. * @param cont_cls closure for @a cont
  834. * @return handle for the operation, NULL on error
  835. */
  836. struct GNUNET_ARM_Operation *
  837. GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
  838. const char *service_name,
  839. enum GNUNET_OS_InheritStdioFlags
  840. std_inheritance,
  841. GNUNET_ARM_ResultCallback cont,
  842. void *cont_cls)
  843. {
  844. struct GNUNET_ARM_Operation *op;
  845. enum GNUNET_ARM_Result ret;
  846. struct GNUNET_DISK_PipeHandle *sig;
  847. struct GNUNET_DISK_FileHandle *wsig;
  848. LOG (GNUNET_ERROR_TYPE_DEBUG,
  849. "Starting service `%s'\n",
  850. service_name);
  851. if (0 != strcasecmp ("arm",
  852. service_name))
  853. return change_service (h,
  854. service_name,
  855. cont,
  856. cont_cls,
  857. GNUNET_MESSAGE_TYPE_ARM_START);
  858. /* Possible cases:
  859. * 1) We're connected to ARM already. Invoke the callback immediately.
  860. * 2) We're not connected to ARM.
  861. * Cancel any reconnection attempts temporarily, then perform
  862. * a service test.
  863. */
  864. if (GNUNET_YES == h->currently_up)
  865. {
  866. LOG (GNUNET_ERROR_TYPE_DEBUG,
  867. "ARM is already running\n");
  868. op = GNUNET_new (struct GNUNET_ARM_Operation);
  869. op->h = h;
  870. op->result_cont = cont;
  871. op->cont_cls = cont_cls;
  872. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  873. h->operation_pending_tail,
  874. op);
  875. op->async = GNUNET_SCHEDULER_add_now (&notify_running, op);
  876. return op;
  877. }
  878. /* This is an inherently uncertain choice, as it is of course
  879. theoretically possible that ARM is up and we just did not
  880. yet complete the MQ handshake. However, given that users
  881. are unlikely to hammer 'gnunet-arm -s' on a busy system,
  882. the above check should catch 99.99% of the cases where ARM
  883. is already running. */
  884. LOG (GNUNET_ERROR_TYPE_DEBUG,
  885. "Starting ARM service\n");
  886. if (NULL == (sig = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE)))
  887. {
  888. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
  889. "pipe");
  890. }
  891. wsig = GNUNET_DISK_pipe_detach_end (sig,
  892. GNUNET_DISK_PIPE_END_WRITE);
  893. ret = start_arm_service (h,
  894. std_inheritance,
  895. wsig);
  896. GNUNET_DISK_file_close (wsig);
  897. if (GNUNET_ARM_RESULT_STARTING == ret)
  898. reconnect_arm (h);
  899. op = GNUNET_new (struct GNUNET_ARM_Operation);
  900. op->h = h;
  901. op->result_cont = cont;
  902. op->cont_cls = cont_cls;
  903. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  904. h->operation_pending_tail,
  905. op);
  906. op->starting_ret = ret;
  907. if (NULL != sig)
  908. {
  909. op->rfd = GNUNET_DISK_pipe_detach_end (sig,
  910. GNUNET_DISK_PIPE_END_READ);
  911. /* Wait at most a minute for gnunet-service-arm to be up, as beyond
  912. that something clearly just went wrong */
  913. op->async = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_MINUTES,
  914. op->rfd,
  915. &notify_starting,
  916. op);
  917. GNUNET_DISK_pipe_close (sig);
  918. }
  919. else
  920. {
  921. op->async = GNUNET_SCHEDULER_add_now (&notify_starting,
  922. op);
  923. }
  924. return op;
  925. }
  926. /**
  927. * Request a service to be stopped. Stopping arm itself will not
  928. * invalidate its handle, and ARM API will try to restore connection
  929. * to the ARM service, even if ARM connection was lost because you
  930. * asked for ARM to be stopped. Call
  931. * #GNUNET_ARM_disconnect() to free the handle and prevent
  932. * further connection attempts.
  933. *
  934. * @param h handle to ARM
  935. * @param service_name name of the service
  936. * @param cont callback to invoke after request is sent or is not sent
  937. * @param cont_cls closure for @a cont
  938. * @return handle for the operation, NULL on error
  939. */
  940. struct GNUNET_ARM_Operation *
  941. GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
  942. const char *service_name,
  943. GNUNET_ARM_ResultCallback cont,
  944. void *cont_cls)
  945. {
  946. struct GNUNET_ARM_Operation *op;
  947. LOG (GNUNET_ERROR_TYPE_DEBUG,
  948. "Stopping service `%s'\n",
  949. service_name);
  950. op = change_service (h,
  951. service_name,
  952. cont,
  953. cont_cls,
  954. GNUNET_MESSAGE_TYPE_ARM_STOP);
  955. if (NULL == op)
  956. return NULL;
  957. /* If the service is ARM, set a flag as we will use MQ errors
  958. to detect that the process is really gone. */
  959. if (0 == strcasecmp (service_name,
  960. "arm"))
  961. op->is_arm_stop = GNUNET_YES;
  962. return op;
  963. }
  964. /**
  965. * Request a list of running services.
  966. *
  967. * @param h handle to ARM
  968. * @param cont callback to invoke after request is sent or is not sent
  969. * @param cont_cls closure for @a cont
  970. * @return handle for the operation, NULL on error
  971. */
  972. struct GNUNET_ARM_Operation *
  973. GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
  974. GNUNET_ARM_ServiceListCallback cont,
  975. void *cont_cls)
  976. {
  977. struct GNUNET_ARM_Operation *op;
  978. struct GNUNET_MQ_Envelope *env;
  979. struct GNUNET_ARM_Message *msg;
  980. LOG (GNUNET_ERROR_TYPE_DEBUG,
  981. "Requesting LIST from ARM service\n");
  982. if (0 == h->request_id_counter)
  983. h->request_id_counter++;
  984. op = GNUNET_new (struct GNUNET_ARM_Operation);
  985. op->h = h;
  986. op->list_cont = cont;
  987. op->cont_cls = cont_cls;
  988. op->id = h->request_id_counter++;
  989. GNUNET_CONTAINER_DLL_insert_tail (h->operation_pending_head,
  990. h->operation_pending_tail,
  991. op);
  992. env = GNUNET_MQ_msg (msg,
  993. GNUNET_MESSAGE_TYPE_ARM_LIST);
  994. msg->reserved = htonl (0);
  995. msg->request_id = GNUNET_htonll (op->id);
  996. GNUNET_MQ_send (h->mq, env);
  997. return op;
  998. }
  999. /* end of arm_api.c */