gnunet-service-arm.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2009, 2010 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/gnunet-service-arm.c
  19. * @brief the automated restart manager service
  20. * @author Christian Grothoff
  21. *
  22. * TODO:
  23. * - need to test auto-restart code on configuration changes;
  24. * - should refine restart code to check if *relevant* parts of the
  25. * configuration were changed (anything in the section for the service)
  26. * - should have a way to specify dependencies between services and
  27. * manage restarts of groups of services
  28. *
  29. * + install handler for disconnecting clients!?
  30. */
  31. #include "platform.h"
  32. #include "gnunet_util_lib.h"
  33. #include "gnunet_protocols.h"
  34. #include "gnunet-service-arm.h"
  35. #include "arm.h"
  36. /**
  37. * Check for configuration file changes every 5s.
  38. */
  39. #define MAINT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
  40. /**
  41. * Threshold after which exponential backoff shouldn't increase (in ms); 30m
  42. */
  43. #define EXPONENTIAL_BACKOFF_THRESHOLD (1000 * 60 * 30)
  44. /**
  45. * List of our services.
  46. */
  47. struct ServiceList;
  48. /**
  49. * List of our services.
  50. */
  51. struct ServiceList
  52. {
  53. /**
  54. * This is a doubly-linked list.
  55. */
  56. struct ServiceList *next;
  57. /**
  58. * This is a doubly-linked list.
  59. */
  60. struct ServiceList *prev;
  61. /**
  62. * Name of the service.
  63. */
  64. char *name;
  65. /**
  66. * Name of the binary used.
  67. */
  68. char *binary;
  69. /**
  70. * Name of the configuration file used.
  71. */
  72. char *config;
  73. /**
  74. * Client to notify upon kill completion (waitpid), NULL
  75. * if we should simply restart the process.
  76. */
  77. struct GNUNET_SERVER_Client *killing_client;
  78. /**
  79. * Process structure pointer of the child.
  80. */
  81. struct GNUNET_OS_Process *proc;
  82. /**
  83. * Last time the config of this service was
  84. * modified.
  85. */
  86. time_t mtime;
  87. /**
  88. * Process exponential backoff time
  89. */
  90. struct GNUNET_TIME_Relative backoff;
  91. /**
  92. * Absolute time at which the process is scheduled to restart in case of death
  93. */
  94. struct GNUNET_TIME_Absolute restartAt;
  95. };
  96. /**
  97. * List of running services.
  98. */
  99. static struct ServiceList *running_head;
  100. /**
  101. * List of running services.
  102. */
  103. static struct ServiceList *running_tail;
  104. /**
  105. * Our configuration
  106. */
  107. static const struct GNUNET_CONFIGURATION_Handle *cfg;
  108. /**
  109. * Command to prepend to each actual command.
  110. */
  111. static char *prefix_command;
  112. /**
  113. * Option to append to each actual command.
  114. */
  115. static char *final_option;
  116. /**
  117. * ID of task called whenever we get a SIGCHILD.
  118. */
  119. static GNUNET_SCHEDULER_TaskIdentifier child_death_task;
  120. /**
  121. * ID of task called whenever the timeout for restarting a child
  122. * expires.
  123. */
  124. static GNUNET_SCHEDULER_TaskIdentifier child_restart_task;
  125. /**
  126. * Pipe used to communicate shutdown via signal.
  127. */
  128. static struct GNUNET_DISK_PipeHandle *sigpipe;
  129. /**
  130. * Reading end of the signal pipe.
  131. */
  132. static const struct GNUNET_DISK_FileHandle *pr;
  133. /**
  134. * Are we in shutdown mode?
  135. */
  136. static int in_shutdown;
  137. /**
  138. * Handle to our server instance. Our server is a bit special in that
  139. * its service is not immediately stopped once we get a shutdown
  140. * request (since we need to continue service until all of our child
  141. * processes are dead). This handle is used to shut down the server
  142. * (and thus trigger process termination) once all child processes are
  143. * also dead. A special option in the ARM configuration modifies the
  144. * behaviour of the service implementation to not do the shutdown
  145. * immediately.
  146. */
  147. static struct GNUNET_SERVER_Handle *server;
  148. /**
  149. * If the configuration file changes, restart tasks that depended on that
  150. * option.
  151. *
  152. * @param cls closure, NULL if we need to self-restart
  153. * @param tc context
  154. */
  155. static void
  156. config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  157. {
  158. struct ServiceList *pos;
  159. struct stat sbuf;
  160. pos = running_head;
  161. while (pos != NULL)
  162. {
  163. /* FIXME: this test for config change may be a bit too coarse grained */
  164. if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime) &&
  165. (pos->proc != NULL))
  166. {
  167. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  168. _
  169. ("Restarting service `%s' due to configuration file change.\n"));
  170. if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
  171. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
  172. else
  173. pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
  174. }
  175. pos = pos->next;
  176. }
  177. }
  178. /**
  179. * Transmit a status result message.
  180. *
  181. * @param cls pointer to "unit16_t*" with message type
  182. * @param size number of bytes available in buf
  183. * @param buf where to copy the message, NULL on error
  184. * @return number of bytes copied to buf
  185. */
  186. static size_t
  187. write_result (void *cls, size_t size, void *buf)
  188. {
  189. uint16_t *res = cls;
  190. struct GNUNET_MessageHeader *msg;
  191. if (buf == NULL)
  192. {
  193. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  194. _("Could not send status result to client\n"));
  195. return 0; /* error, not much we can do */
  196. }
  197. #if DEBUG_ARM
  198. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending status response %u to client\n",
  199. (unsigned int) *res);
  200. #endif
  201. GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
  202. msg = buf;
  203. msg->size = htons (sizeof (struct GNUNET_MessageHeader));
  204. msg->type = htons (*res);
  205. GNUNET_free (res);
  206. return sizeof (struct GNUNET_MessageHeader);
  207. }
  208. /**
  209. * Signal our client that we will start or stop the
  210. * service.
  211. *
  212. * @param client who is being signalled
  213. * @param name name of the service
  214. * @param result message type to send
  215. * @return NULL if it was not found
  216. */
  217. static void
  218. signal_result (struct GNUNET_SERVER_Client *client, const char *name,
  219. uint16_t result)
  220. {
  221. uint16_t *res;
  222. if (NULL == client)
  223. {
  224. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  225. _("Not sending status result to client: no client known\n"));
  226. return;
  227. }
  228. #if DEBUG_ARM
  229. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  230. "Telling client that service `%s' is now %s\n", name,
  231. result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up");
  232. #endif
  233. res = GNUNET_malloc (sizeof (uint16_t));
  234. *res = result;
  235. GNUNET_SERVER_notify_transmit_ready (client,
  236. sizeof (struct GNUNET_MessageHeader),
  237. GNUNET_TIME_UNIT_FOREVER_REL,
  238. &write_result, res);
  239. }
  240. /**
  241. * Find the process with the given service
  242. * name in the given list and return it.
  243. *
  244. * @param name which service entry to look up
  245. * @return NULL if it was not found
  246. */
  247. static struct ServiceList *
  248. find_service (const char *name)
  249. {
  250. struct ServiceList *pos;
  251. pos = running_head;
  252. while (pos != NULL)
  253. {
  254. if (0 == strcmp (pos->name, name))
  255. return pos;
  256. pos = pos->next;
  257. }
  258. return NULL;
  259. }
  260. /**
  261. * Remove and free an entry in the service list.
  262. *
  263. * @param pos entry to free
  264. */
  265. static void
  266. free_service (struct ServiceList *pos)
  267. {
  268. GNUNET_CONTAINER_DLL_remove (running_head, running_tail, pos);
  269. GNUNET_free_non_null (pos->config);
  270. GNUNET_free_non_null (pos->binary);
  271. GNUNET_free (pos->name);
  272. GNUNET_free (pos);
  273. }
  274. #include "do_start_process.c"
  275. /**
  276. * Actually start the process for the given service.
  277. *
  278. * @param sl identifies service to start
  279. * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
  280. */
  281. static void
  282. start_process (struct ServiceList *sl, const int *lsocks)
  283. {
  284. char *loprefix;
  285. char *options;
  286. char *optpos;
  287. char *optend;
  288. const char *next;
  289. int use_debug;
  290. char b;
  291. char *val;
  292. /* start service */
  293. if (GNUNET_OK !=
  294. GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX",
  295. &loprefix))
  296. loprefix = GNUNET_strdup (prefix_command);
  297. if (GNUNET_OK !=
  298. GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS",
  299. &options))
  300. {
  301. options = GNUNET_strdup (final_option);
  302. if (NULL == strstr (options, "%"))
  303. {
  304. /* replace '{}' with service name */
  305. while (NULL != (optpos = strstr (options, "{}")))
  306. {
  307. optpos[0] = '%';
  308. optpos[1] = 's';
  309. GNUNET_asprintf (&optpos, options, sl->name);
  310. GNUNET_free (options);
  311. options = optpos;
  312. }
  313. /* replace '$PATH' with value associated with "PATH" */
  314. while (NULL != (optpos = strstr (options, "$")))
  315. {
  316. optend = optpos + 1;
  317. while (isupper ((unsigned char) *optend))
  318. optend++;
  319. b = *optend;
  320. if ('\0' == b)
  321. next = "";
  322. else
  323. next = optend + 1;
  324. *optend = '\0';
  325. if (GNUNET_OK !=
  326. GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", optpos + 1,
  327. &val))
  328. val = GNUNET_strdup ("");
  329. *optpos = '\0';
  330. GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next);
  331. GNUNET_free (options);
  332. GNUNET_free (val);
  333. options = optpos;
  334. }
  335. }
  336. }
  337. use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
  338. #if DEBUG_ARM
  339. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  340. "Starting service `%s' using binary `%s' and configuration `%s'\n",
  341. sl->name, sl->binary, sl->config);
  342. #endif
  343. if (GNUNET_YES == use_debug)
  344. sl->proc =
  345. do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
  346. "DEBUG", options, NULL);
  347. else
  348. sl->proc =
  349. do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config,
  350. options, NULL);
  351. if (sl->proc == NULL)
  352. GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
  353. sl->name);
  354. else
  355. GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
  356. GNUNET_free (loprefix);
  357. GNUNET_free (options);
  358. }
  359. /**
  360. * Start the specified service.
  361. *
  362. * @param client who is asking for this
  363. * @param servicename name of the service to start
  364. * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
  365. * @return GNUNET_OK on success, GNUNET_SYSERR on error
  366. */
  367. int
  368. start_service (struct GNUNET_SERVER_Client *client, const char *servicename,
  369. const int *lsocks)
  370. {
  371. struct ServiceList *sl;
  372. char *binary;
  373. char *config;
  374. struct stat sbuf;
  375. if (GNUNET_YES == in_shutdown)
  376. {
  377. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  378. _("ARM is shutting down, service `%s' not started.\n"),
  379. servicename);
  380. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  381. return GNUNET_SYSERR;
  382. }
  383. sl = find_service (servicename);
  384. if (sl != NULL)
  385. {
  386. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Service `%s' already running.\n"),
  387. servicename);
  388. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
  389. return GNUNET_SYSERR;
  390. }
  391. if (GNUNET_OK !=
  392. GNUNET_CONFIGURATION_get_value_string (cfg, servicename, "BINARY",
  393. &binary))
  394. {
  395. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  396. _("Binary implementing service `%s' not known!\n"),
  397. servicename);
  398. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  399. return GNUNET_SYSERR;
  400. }
  401. if ((GNUNET_OK !=
  402. GNUNET_CONFIGURATION_get_value_filename (cfg, servicename, "CONFIG",
  403. &config)) ||
  404. (0 != STAT (config, &sbuf)))
  405. {
  406. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  407. _("Configuration file `%s' for service `%s' not known!\n"),
  408. config, servicename);
  409. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  410. GNUNET_free (binary);
  411. GNUNET_free_non_null (config);
  412. return GNUNET_SYSERR;
  413. }
  414. (void) stop_listening (servicename);
  415. sl = GNUNET_malloc (sizeof (struct ServiceList));
  416. sl->name = GNUNET_strdup (servicename);
  417. sl->binary = binary;
  418. sl->config = config;
  419. sl->mtime = sbuf.st_mtime;
  420. sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
  421. sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS;
  422. GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl);
  423. start_process (sl, lsocks);
  424. if (NULL != client)
  425. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
  426. return GNUNET_OK;
  427. }
  428. /**
  429. * Stop the specified service.
  430. *
  431. * @param client who is asking for this
  432. * @param servicename name of the service to stop
  433. */
  434. static void
  435. stop_service (struct GNUNET_SERVER_Client *client, const char *servicename)
  436. {
  437. struct ServiceList *pos;
  438. GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Preparing to stop `%s'\n"),
  439. servicename);
  440. pos = find_service (servicename);
  441. if (pos == NULL)
  442. {
  443. if (GNUNET_OK == stop_listening (servicename))
  444. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  445. else
  446. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN);
  447. GNUNET_SERVER_receive_done (client, GNUNET_OK);
  448. return;
  449. }
  450. if (pos->killing_client != NULL)
  451. {
  452. /* killing already in progress */
  453. #if DEBUG_ARM
  454. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' is already down\n",
  455. servicename);
  456. #endif
  457. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  458. GNUNET_SERVER_receive_done (client, GNUNET_OK);
  459. return;
  460. }
  461. if (GNUNET_YES == in_shutdown)
  462. {
  463. #if DEBUG_ARM
  464. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  465. "Termination request already sent to `%s' (since ARM is in shutdown).\n",
  466. servicename);
  467. #endif
  468. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  469. GNUNET_SERVER_receive_done (client, GNUNET_OK);
  470. return;
  471. }
  472. if (pos->proc == NULL)
  473. {
  474. /* process is in delayed restart, simply remove it! */
  475. free_service (pos);
  476. signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  477. GNUNET_SERVER_receive_done (client, GNUNET_OK);
  478. return;
  479. }
  480. #if DEBUG_ARM
  481. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  482. "Sending kill signal to service `%s', waiting for process to die.\n",
  483. servicename);
  484. #endif
  485. if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
  486. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
  487. pos->killing_client = client;
  488. GNUNET_SERVER_client_keep (client);
  489. }
  490. /**
  491. * Handle START-message.
  492. *
  493. * @param cls closure (always NULL)
  494. * @param client identification of the client
  495. * @param message the actual message
  496. * @return GNUNET_OK to keep the connection open,
  497. * GNUNET_SYSERR to close it (signal serious error)
  498. */
  499. static void
  500. handle_start (void *cls, struct GNUNET_SERVER_Client *client,
  501. const struct GNUNET_MessageHeader *message)
  502. {
  503. const char *servicename;
  504. uint16_t size;
  505. size = ntohs (message->size);
  506. size -= sizeof (struct GNUNET_MessageHeader);
  507. servicename = (const char *) &message[1];
  508. if ((size == 0) || (servicename[size - 1] != '\0'))
  509. {
  510. GNUNET_break (0);
  511. GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
  512. return;
  513. }
  514. start_service (client, servicename, NULL);
  515. GNUNET_SERVER_receive_done (client, GNUNET_OK);
  516. }
  517. /**
  518. * Handle STOP-message.
  519. *
  520. * @param cls closure (always NULL)
  521. * @param client identification of the client
  522. * @param message the actual message
  523. * @return GNUNET_OK to keep the connection open,
  524. * GNUNET_SYSERR to close it (signal serious error)
  525. */
  526. static void
  527. handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
  528. const struct GNUNET_MessageHeader *message)
  529. {
  530. const char *servicename;
  531. uint16_t size;
  532. size = ntohs (message->size);
  533. size -= sizeof (struct GNUNET_MessageHeader);
  534. servicename = (const char *) &message[1];
  535. if ((size == 0) || (servicename[size - 1] != '\0'))
  536. {
  537. GNUNET_break (0);
  538. GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
  539. return;
  540. }
  541. stop_service (client, servicename);
  542. }
  543. /**
  544. * Remove all entries for tasks that are not running
  545. * (proc = NULL) from the running list (they will no longer
  546. * be restarted since we are shutting down).
  547. */
  548. static void
  549. clean_up_running ()
  550. {
  551. struct ServiceList *pos;
  552. struct ServiceList *next;
  553. next = running_head;
  554. while (NULL != (pos = next))
  555. {
  556. next = pos->next;
  557. if (pos->proc == NULL)
  558. free_service (pos);
  559. }
  560. }
  561. /**
  562. * We are done with everything. Stop remaining
  563. * tasks, signal handler and the server.
  564. */
  565. static void
  566. do_shutdown ()
  567. {
  568. if (NULL != server)
  569. {
  570. GNUNET_SERVER_destroy (server);
  571. server = NULL;
  572. }
  573. if (GNUNET_SCHEDULER_NO_TASK != child_death_task)
  574. {
  575. GNUNET_SCHEDULER_cancel (child_death_task);
  576. child_death_task = GNUNET_SCHEDULER_NO_TASK;
  577. }
  578. }
  579. /**
  580. * Task run for shutdown.
  581. *
  582. * @param cls closure, NULL if we need to self-restart
  583. * @param tc context
  584. */
  585. static void
  586. shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  587. {
  588. struct ServiceList *pos;
  589. #if DEBUG_ARM
  590. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n"));
  591. #endif
  592. if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
  593. {
  594. GNUNET_SCHEDULER_cancel (child_restart_task);
  595. child_restart_task = GNUNET_SCHEDULER_NO_TASK;
  596. }
  597. in_shutdown = GNUNET_YES;
  598. stop_listening (NULL);
  599. pos = running_head;
  600. while (NULL != pos)
  601. {
  602. if (pos->proc != NULL)
  603. {
  604. GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", pos->name);
  605. if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
  606. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
  607. }
  608. pos = pos->next;
  609. }
  610. if (running_head == NULL)
  611. do_shutdown ();
  612. }
  613. /**
  614. * Task run whenever it is time to restart a child that died.
  615. *
  616. * @param cls closure, always NULL
  617. * @param tc context
  618. */
  619. static void
  620. delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  621. {
  622. struct ServiceList *pos;
  623. struct GNUNET_TIME_Relative lowestRestartDelay;
  624. child_restart_task = GNUNET_SCHEDULER_NO_TASK;
  625. if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  626. return;
  627. GNUNET_assert (GNUNET_NO == in_shutdown);
  628. lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
  629. /* check for services that need to be restarted due to
  630. * configuration changes or because the last restart failed */
  631. pos = running_head;
  632. while (pos != NULL)
  633. {
  634. if (pos->proc == NULL)
  635. {
  636. if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).rel_value == 0)
  637. {
  638. GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Restarting service `%s'.\n"),
  639. pos->name);
  640. start_process (pos, NULL);
  641. }
  642. else
  643. {
  644. lowestRestartDelay =
  645. GNUNET_TIME_relative_min (lowestRestartDelay,
  646. GNUNET_TIME_absolute_get_remaining
  647. (pos->restartAt));
  648. }
  649. }
  650. pos = pos->next;
  651. }
  652. if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
  653. {
  654. #if DEBUG_ARM
  655. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n",
  656. (unsigned long long) lowestRestartDelay.rel_value);
  657. #endif
  658. child_restart_task =
  659. GNUNET_SCHEDULER_add_delayed (lowestRestartDelay, &delayed_restart_task,
  660. NULL);
  661. }
  662. }
  663. /**
  664. * Task triggered whenever we receive a SIGCHLD (child
  665. * process died).
  666. *
  667. * @param cls closure, NULL if we need to self-restart
  668. * @param tc context
  669. */
  670. static void
  671. maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  672. {
  673. struct ServiceList *pos;
  674. struct ServiceList *next;
  675. const char *statstr;
  676. int statcode;
  677. int ret;
  678. char c[16];
  679. enum GNUNET_OS_ProcessStatusType statusType;
  680. unsigned long statusCode;
  681. child_death_task = GNUNET_SCHEDULER_NO_TASK;
  682. if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
  683. {
  684. /* shutdown scheduled us, ignore! */
  685. child_death_task =
  686. GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
  687. &maint_child_death, NULL);
  688. return;
  689. }
  690. /* consume the signal */
  691. GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
  692. /* check for services that died (WAITPID) */
  693. next = running_head;
  694. while (NULL != (pos = next))
  695. {
  696. next = pos->next;
  697. if (pos->proc == NULL)
  698. continue;
  699. if ((GNUNET_SYSERR ==
  700. (ret = GNUNET_OS_process_status (pos->proc, &statusType, &statusCode)))
  701. || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) ||
  702. (statusType == GNUNET_OS_PROCESS_RUNNING)))
  703. continue;
  704. if (statusType == GNUNET_OS_PROCESS_EXITED)
  705. {
  706. statstr = _( /* process termination method */ "exit");
  707. statcode = statusCode;
  708. }
  709. else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
  710. {
  711. statstr = _( /* process termination method */ "signal");
  712. statcode = statusCode;
  713. }
  714. else
  715. {
  716. statstr = _( /* process termination method */ "unknown");
  717. statcode = 0;
  718. }
  719. GNUNET_OS_process_close (pos->proc);
  720. pos->proc = NULL;
  721. if (NULL != pos->killing_client)
  722. {
  723. GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' stopped\n"),
  724. pos->name);
  725. signal_result (pos->killing_client, pos->name,
  726. GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
  727. GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK);
  728. GNUNET_SERVER_client_drop (pos->killing_client);
  729. free_service (pos);
  730. continue;
  731. }
  732. if (GNUNET_YES != in_shutdown)
  733. {
  734. if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  735. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  736. _
  737. ("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
  738. pos->name, statstr, statcode);
  739. /* schedule restart */
  740. pos->restartAt = GNUNET_TIME_relative_to_absolute (pos->backoff);
  741. if (pos->backoff.rel_value < EXPONENTIAL_BACKOFF_THRESHOLD)
  742. pos->backoff = GNUNET_TIME_relative_multiply (pos->backoff, 2);
  743. if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
  744. GNUNET_SCHEDULER_cancel (child_restart_task);
  745. child_restart_task =
  746. GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
  747. &delayed_restart_task, NULL);
  748. }
  749. #if DEBUG_ARM
  750. else
  751. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  752. "Service `%s' terminated with status %s/%d\n", pos->name,
  753. statstr, statcode);
  754. #endif
  755. }
  756. child_death_task =
  757. GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
  758. &maint_child_death, NULL);
  759. if (GNUNET_YES == in_shutdown)
  760. clean_up_running ();
  761. if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
  762. do_shutdown ();
  763. }
  764. static size_t
  765. transmit_shutdown_ack (void *cls, size_t size, void *buf)
  766. {
  767. struct GNUNET_SERVER_Client *client = cls;
  768. struct GNUNET_MessageHeader *msg;
  769. if (size < sizeof (struct GNUNET_MessageHeader))
  770. {
  771. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  772. _("Failed to transmit shutdown ACK.\n"));
  773. GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
  774. return 0; /* client disconnected */
  775. }
  776. GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting shutdown ACK.\n"));
  777. /* Make the connection flushing for the purpose of ACK transmitting,
  778. * needed on W32 to ensure that the message is even received, harmless
  779. * on other platforms... */
  780. GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client));
  781. msg = (struct GNUNET_MessageHeader *) buf;
  782. msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK);
  783. msg->size = htons (sizeof (struct GNUNET_MessageHeader));
  784. GNUNET_SERVER_receive_done (client, GNUNET_OK);
  785. GNUNET_SERVER_client_drop (client);
  786. return sizeof (struct GNUNET_MessageHeader);
  787. }
  788. /**
  789. * Handler for SHUTDOWN message.
  790. *
  791. * @param cls closure (refers to service)
  792. * @param client identification of the client
  793. * @param message the actual message
  794. */
  795. static void
  796. handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
  797. const struct GNUNET_MessageHeader *message)
  798. {
  799. GNUNET_SERVER_client_keep (client);
  800. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  801. _("Initiating shutdown as requested by client.\n"));
  802. GNUNET_SERVER_notify_transmit_ready (client,
  803. sizeof (struct GNUNET_MessageHeader),
  804. GNUNET_TIME_UNIT_FOREVER_REL,
  805. &transmit_shutdown_ack, client);
  806. GNUNET_SERVER_client_persist_ (client);
  807. GNUNET_SCHEDULER_shutdown ();
  808. }
  809. /**
  810. * Signal handler called for SIGCHLD. Triggers the
  811. * respective handler by writing to the trigger pipe.
  812. */
  813. static void
  814. sighandler_child_death ()
  815. {
  816. static char c;
  817. int old_errno = errno; /* back-up errno */
  818. GNUNET_break (1 ==
  819. GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
  820. (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
  821. &c, sizeof (c)));
  822. errno = old_errno; /* restore errno */
  823. }
  824. /**
  825. * Process arm requests.
  826. *
  827. * @param cls closure
  828. * @param serv the initialized server
  829. * @param c configuration to use
  830. */
  831. static void
  832. run (void *cls, struct GNUNET_SERVER_Handle *serv,
  833. const struct GNUNET_CONFIGURATION_Handle *c)
  834. {
  835. static const struct GNUNET_SERVER_MessageHandler handlers[] = {
  836. {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
  837. {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
  838. {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
  839. sizeof (struct GNUNET_MessageHeader)},
  840. {NULL, NULL, 0, 0}
  841. };
  842. char *defaultservices;
  843. char *pos;
  844. cfg = c;
  845. server = serv;
  846. GNUNET_assert (serv != NULL);
  847. pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
  848. GNUNET_assert (pr != NULL);
  849. GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
  850. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
  851. NULL);
  852. child_death_task =
  853. GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
  854. &maint_child_death, NULL);
  855. if (GNUNET_OK !=
  856. GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX",
  857. &prefix_command))
  858. prefix_command = GNUNET_strdup ("");
  859. if (GNUNET_OK !=
  860. GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX",
  861. &final_option))
  862. final_option = GNUNET_strdup ("");
  863. /* start default services... */
  864. if (GNUNET_OK ==
  865. GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES",
  866. &defaultservices))
  867. {
  868. #if DEBUG_ARM
  869. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting default services `%s'\n",
  870. defaultservices);
  871. #endif
  872. if (0 < strlen (defaultservices))
  873. {
  874. pos = strtok (defaultservices, " ");
  875. while (pos != NULL)
  876. {
  877. start_service (NULL, pos, NULL);
  878. pos = strtok (NULL, " ");
  879. }
  880. }
  881. GNUNET_free (defaultservices);
  882. }
  883. else
  884. {
  885. #if DEBUG_ARM
  886. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No default services configured.\n");
  887. #endif
  888. }
  889. /* create listening sockets for future services */
  890. prepareServices (cfg);
  891. /* process client requests */
  892. GNUNET_SERVER_add_handlers (server, handlers);
  893. /* manage services */
  894. GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
  895. &config_change_task, NULL);
  896. }
  897. /**
  898. * The main function for the arm service.
  899. *
  900. * @param argc number of arguments from the command line
  901. * @param argv command line arguments
  902. * @return 0 ok, 1 on error
  903. */
  904. int
  905. main (int argc, char *const *argv)
  906. {
  907. int ret;
  908. struct GNUNET_SIGNAL_Context *shc_chld;
  909. sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
  910. GNUNET_assert (sigpipe != NULL);
  911. shc_chld =
  912. GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
  913. ret =
  914. (GNUNET_OK ==
  915. GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
  916. GNUNET_SIGNAL_handler_uninstall (shc_chld);
  917. shc_chld = NULL;
  918. GNUNET_DISK_pipe_close (sigpipe);
  919. sigpipe = NULL;
  920. return ret;
  921. }
  922. #ifdef LINUX
  923. #include <malloc.h>
  924. /**
  925. * MINIMIZE heap size (way below 128k) since this process doesn't need much.
  926. */
  927. void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
  928. {
  929. mallopt (M_TRIM_THRESHOLD, 4 * 1024);
  930. mallopt (M_TOP_PAD, 1 * 1024);
  931. malloc_trim (0);
  932. }
  933. #endif
  934. /* end of gnunet-service-arm.c */