gnunet_testbed_mpi_spawn.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. #include "platform.h"
  2. #include "gnunet_util_lib.h"
  3. #include "gnunet_testbed_service.h"
  4. /**
  5. * Generic logging shorthand
  6. */
  7. #define LOG(kind,...) \
  8. GNUNET_log (kind, __VA_ARGS__)
  9. /**
  10. * Debug logging shorthand
  11. */
  12. #define LOG_DEBUG(...) \
  13. LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
  14. /**
  15. * Global result
  16. */
  17. static int ret;
  18. /**
  19. * The child process we spawn
  20. */
  21. static struct GNUNET_OS_Process *child;
  22. /**
  23. * The arguments including the binary to spawn
  24. */
  25. static char **argv2;
  26. /**
  27. * Pipe used to communicate shutdown via signal.
  28. */
  29. static struct GNUNET_DISK_PipeHandle *sigpipe;
  30. /**
  31. * Filename of the unique file
  32. */
  33. static char *fn;
  34. /**
  35. * Handle to the unique file
  36. */
  37. static int fh;
  38. /**
  39. * The return code of the binary
  40. */
  41. static unsigned long child_exit_code;
  42. /**
  43. * The process status of the child
  44. */
  45. static enum GNUNET_OS_ProcessStatusType child_status;
  46. /**
  47. * The shutdown task
  48. */
  49. static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
  50. /**
  51. * Task to kill the child
  52. */
  53. static GNUNET_SCHEDULER_TaskIdentifier terminate_task_id;
  54. /**
  55. * Task to kill the child
  56. */
  57. static GNUNET_SCHEDULER_TaskIdentifier child_death_task_id;
  58. /**
  59. * The shutdown task
  60. */
  61. static void
  62. shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  63. {
  64. shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
  65. if (0 != child_exit_code)
  66. {
  67. LOG (GNUNET_ERROR_TYPE_WARNING, "Child exited with error code: %lu\n",
  68. child_exit_code);
  69. ret = 128 + (int) child_exit_code;
  70. }
  71. if (0 != fh)
  72. {
  73. close (fh);
  74. }
  75. if ((NULL != fn) && (0 != unlink (fn)))
  76. {
  77. GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open");
  78. ret = GNUNET_SYSERR;
  79. }
  80. }
  81. static void
  82. terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  83. {
  84. static int hard_kill;
  85. GNUNET_assert (NULL != child);
  86. terminate_task_id =
  87. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
  88. &terminate_task, NULL);
  89. if (0 != hard_kill)
  90. {
  91. switch (hard_kill)
  92. {
  93. case 1:
  94. case 2:
  95. LOG (GNUNET_ERROR_TYPE_WARNING,
  96. "%d more interrupts needed to send SIGKILL to the child\n",
  97. 3 - hard_kill);
  98. hard_kill++;
  99. return;
  100. case 3:
  101. GNUNET_break (0 == GNUNET_OS_process_kill (child, SIGKILL));
  102. return;
  103. }
  104. }
  105. hard_kill++;
  106. GNUNET_break (0 == GNUNET_OS_process_kill (child, GNUNET_TERM_SIG));
  107. LOG (GNUNET_ERROR_TYPE_INFO, _("Waiting for child to exit.\n"));
  108. }
  109. /**
  110. * Task triggered whenever we receive a SIGCHLD (child
  111. * process died).
  112. *
  113. * @param cls closure, NULL if we need to self-restart
  114. * @param tc context
  115. */
  116. static void
  117. child_death_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  118. {
  119. const struct GNUNET_DISK_FileHandle *pr;
  120. char c[16];
  121. pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
  122. child_death_task_id = GNUNET_SCHEDULER_NO_TASK;
  123. if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
  124. {
  125. child_death_task_id =
  126. GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  127. pr, &child_death_task, NULL);
  128. return;
  129. }
  130. /* consume the signal */
  131. GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
  132. LOG_DEBUG ("Child died\n");
  133. GNUNET_SCHEDULER_cancel (terminate_task_id);
  134. terminate_task_id = GNUNET_SCHEDULER_NO_TASK;
  135. GNUNET_assert (GNUNET_OK == GNUNET_OS_process_status (child, &child_status,
  136. &child_exit_code));
  137. GNUNET_OS_process_destroy (child);
  138. child = NULL;
  139. shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
  140. }
  141. static void
  142. destroy_hosts(struct GNUNET_TESTBED_Host **hosts, unsigned int nhosts)
  143. {
  144. unsigned int host;
  145. GNUNET_assert (NULL != hosts);
  146. for (host = 0; host < nhosts; host++)
  147. if (NULL != hosts[host])
  148. GNUNET_TESTBED_host_destroy (hosts[host]);
  149. GNUNET_free (hosts);
  150. hosts = NULL;
  151. }
  152. /**
  153. * The main scheduler run task
  154. *
  155. * @param cls NULL
  156. * @param tc scheduler task context
  157. */
  158. static void
  159. run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  160. {
  161. struct GNUNET_TESTBED_Host **hosts;
  162. const struct GNUNET_CONFIGURATION_Handle *null_cfg;
  163. char *tmpdir;
  164. char *hostname;
  165. size_t hostname_len;
  166. unsigned int nhosts;
  167. null_cfg = GNUNET_CONFIGURATION_create ();
  168. nhosts = GNUNET_TESTBED_hosts_load_from_loadleveler (null_cfg, &hosts);
  169. if (0 == nhosts)
  170. {
  171. GNUNET_break (0);
  172. ret = GNUNET_SYSERR;
  173. return;
  174. }
  175. hostname_len = GNUNET_OS_get_hostname_max_length ();
  176. hostname = GNUNET_malloc (hostname_len);
  177. if (0 != gethostname (hostname, hostname_len))
  178. {
  179. LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot get hostname. Exiting\n");
  180. GNUNET_free (hostname);
  181. destroy_hosts (hosts, nhosts);
  182. ret = GNUNET_SYSERR;
  183. return;
  184. }
  185. if (NULL == strstr (GNUNET_TESTBED_host_get_hostname (hosts[0]), hostname))
  186. {
  187. LOG_DEBUG ("Exiting as `%s' is not the lowest host\n", hostname);
  188. GNUNET_free (hostname);
  189. ret = GNUNET_OK;
  190. return;
  191. }
  192. LOG_DEBUG ("Will be executing `%s' on host `%s'\n", argv2[0], hostname);
  193. GNUNET_free (hostname);
  194. destroy_hosts (hosts, nhosts);
  195. tmpdir = getenv ("TMPDIR");
  196. if (NULL == tmpdir)
  197. tmpdir = getenv ("TMP");
  198. if (NULL == tmpdir)
  199. tmpdir = getenv ("TEMP");
  200. if (NULL == tmpdir)
  201. tmpdir = "/tmp";
  202. (void) GNUNET_asprintf (&fn, "%s/gnunet-testbed-spawn.lock", tmpdir);
  203. /* Open the unique file; we can create it then we can spawn the child process
  204. else we exit */
  205. fh = open (fn, O_CREAT | O_EXCL | O_CLOEXEC,
  206. S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
  207. if (-1 == fh)
  208. {
  209. if (EEXIST == errno)
  210. {
  211. LOG_DEBUG ("Lock file already created by other process. Exiting\n");
  212. ret = GNUNET_OK;
  213. return;
  214. }
  215. GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open");
  216. ret = GNUNET_SYSERR;
  217. return;
  218. }
  219. /* Spawn the new process here */
  220. LOG (GNUNET_ERROR_TYPE_INFO, _("Spawning process `%s'\n"), argv2[0]);
  221. child = GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL,
  222. NULL, NULL,
  223. argv2[0], argv2);
  224. if (NULL == child)
  225. {
  226. GNUNET_break (0);
  227. ret = GNUNET_SYSERR;
  228. shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
  229. return;
  230. }
  231. ret = GNUNET_OK;
  232. terminate_task_id =
  233. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
  234. &terminate_task, NULL);
  235. child_death_task_id =
  236. GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  237. GNUNET_DISK_pipe_handle (sigpipe,
  238. GNUNET_DISK_PIPE_END_READ),
  239. &child_death_task, NULL);
  240. }
  241. /**
  242. * Signal handler called for SIGCHLD.
  243. */
  244. static void
  245. sighandler_child_death ()
  246. {
  247. static char c;
  248. int old_errno = errno; /* back-up errno */
  249. GNUNET_break (1 ==
  250. GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
  251. (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
  252. &c, sizeof (c)));
  253. errno = old_errno; /* restore errno */
  254. }
  255. /**
  256. * Execution start point
  257. */
  258. int
  259. main (int argc, char *argv[])
  260. {
  261. struct GNUNET_SIGNAL_Context *shc_chld;
  262. unsigned int cnt;
  263. ret = -1;
  264. if (argc < 2)
  265. {
  266. printf ("Need arguments: gnunet-testbed-mpi-spawn <cmd> <cmd_args>");
  267. return 1;
  268. }
  269. if (GNUNET_OK != GNUNET_log_setup ("gnunet-testbed-spawn", NULL, NULL))
  270. {
  271. GNUNET_break (0);
  272. return 1;
  273. }
  274. if (NULL == (sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
  275. GNUNET_NO, GNUNET_NO)))
  276. {
  277. GNUNET_break (0);
  278. ret = GNUNET_SYSERR;
  279. return 1;
  280. }
  281. shc_chld =
  282. GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
  283. if (NULL == shc_chld)
  284. {
  285. LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot install a signal handler\n");
  286. return 1;
  287. }
  288. argv2 = GNUNET_malloc (sizeof (char *) * argc);
  289. for (cnt = 1; cnt < argc; cnt++)
  290. argv2[cnt - 1] = argv[cnt];
  291. GNUNET_SCHEDULER_run (run, NULL);
  292. GNUNET_free (argv2);
  293. GNUNET_SIGNAL_handler_uninstall (shc_chld);
  294. shc_chld = NULL;
  295. GNUNET_DISK_pipe_close (sigpipe);
  296. GNUNET_free_non_null (fn);
  297. if (GNUNET_OK != ret)
  298. return ret;
  299. return 0;
  300. }