2 #include "gnunet_util_lib.h"
3 #include "gnunet_testbed_service.h"
7 * Generic logging shorthand
9 #define LOG(kind,...) \
10 GNUNET_log (kind, __VA_ARGS__)
13 * Debug logging shorthand
15 #define LOG_DEBUG(...) \
16 LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
24 * The child process we spawn
26 static struct GNUNET_OS_Process *child;
29 * The arguments including the binary to spawn
34 * Pipe used to communicate shutdown via signal.
36 static struct GNUNET_DISK_PipeHandle *sigpipe;
39 * Filename of the unique file
44 * Handle to the unique file
49 * The return code of the binary
51 static unsigned long child_exit_code;
54 * The process status of the child
56 static enum GNUNET_OS_ProcessStatusType child_status;
61 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
64 * Task to kill the child
66 static GNUNET_SCHEDULER_TaskIdentifier terminate_task_id;
69 * Task to kill the child
71 static GNUNET_SCHEDULER_TaskIdentifier child_death_task_id;
77 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
79 shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
80 if (0 != child_exit_code)
82 LOG (GNUNET_ERROR_TYPE_WARNING, "Child exited with error code: %lu\n",
84 ret = 128 + (int) child_exit_code;
90 if ((NULL != fn) && (0 != unlink (fn)))
92 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open");
99 terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
101 static int hard_kill;
103 GNUNET_assert (NULL != child);
105 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
106 &terminate_task, NULL);
113 LOG (GNUNET_ERROR_TYPE_WARNING,
114 "%d more interrupts needed to send SIGKILL to the child\n",
119 GNUNET_break (0 == GNUNET_OS_process_kill (child, SIGKILL));
124 GNUNET_break (0 == GNUNET_OS_process_kill (child, SIGTERM));
125 LOG (GNUNET_ERROR_TYPE_INFO, _("Waiting for child to exit.\n"));
130 * Task triggered whenever we receive a SIGCHLD (child
133 * @param cls closure, NULL if we need to self-restart
137 child_death_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
139 const struct GNUNET_DISK_FileHandle *pr;
142 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
143 child_death_task_id = GNUNET_SCHEDULER_NO_TASK;
144 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
146 child_death_task_id =
147 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
148 pr, &child_death_task, NULL);
151 /* consume the signal */
152 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
153 LOG_DEBUG ("Child died\n");
154 GNUNET_SCHEDULER_cancel (terminate_task_id);
155 terminate_task_id = GNUNET_SCHEDULER_NO_TASK;
156 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_status (child, &child_status,
158 GNUNET_OS_process_destroy (child);
160 shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
165 destroy_hosts(struct GNUNET_TESTBED_Host **hosts, unsigned int nhosts)
169 GNUNET_assert (NULL != hosts);
170 for (host = 0; host < nhosts; host++)
171 if (NULL != hosts[host])
172 GNUNET_TESTBED_host_destroy (hosts[host]);
179 * The main scheduler run task
182 * @param tc scheduler task context
185 run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
187 struct GNUNET_TESTBED_Host **hosts;
188 const struct GNUNET_CONFIGURATION_Handle *null_cfg;
194 null_cfg = GNUNET_CONFIGURATION_create ();
195 nhosts = GNUNET_TESTBED_hosts_load_from_loadleveler (null_cfg, &hosts);
202 hostname_len = GNUNET_OS_get_hostname_max_length ();
203 hostname = GNUNET_malloc (hostname_len);
204 if (0 != gethostname (hostname, hostname_len))
206 LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot get hostname. Exiting\n");
207 GNUNET_free (hostname);
208 destroy_hosts (hosts, nhosts);
212 if (NULL == strstr (GNUNET_TESTBED_host_get_hostname (hosts[0]), hostname))
214 LOG_DEBUG ("Exiting as `%s' is not the lowest host\n", hostname);
215 GNUNET_free (hostname);
219 LOG_DEBUG ("Will be executing `%s' on host `%s'\n", argv2[0], hostname);
220 GNUNET_free (hostname);
221 destroy_hosts (hosts, nhosts);
222 tmpdir = getenv ("TMPDIR");
224 tmpdir = getenv ("TMP");
226 tmpdir = getenv ("TEMP");
229 (void) GNUNET_asprintf (&fn, "%s/gnunet-testbed-spawn.lock", tmpdir);
230 /* Open the unique file; we can create it then we can spawn the child process
232 fh = open (fn, O_CREAT | O_EXCL | O_CLOEXEC,
233 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
238 LOG_DEBUG ("Lock file already created by other process. Exiting\n");
242 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open");
246 /* Spawn the new process here */
247 LOG (GNUNET_ERROR_TYPE_INFO, _("Spawning process `%s'\n"), argv2[0]);
248 child = GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL,
255 shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
260 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
261 &terminate_task, NULL);
262 child_death_task_id =
263 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
264 GNUNET_DISK_pipe_handle (sigpipe,
265 GNUNET_DISK_PIPE_END_READ),
266 &child_death_task, NULL);
271 * Signal handler called for SIGCHLD.
274 sighandler_child_death ()
277 int old_errno = errno; /* back-up errno */
280 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
281 (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
283 errno = old_errno; /* restore errno */
288 * Execution start point
291 main (int argc, char *argv[])
293 struct GNUNET_SIGNAL_Context *shc_chld;
299 printf ("Need arguments: gnunet-testbed-mpi-spawn <cmd> <cmd_args>");
302 if (GNUNET_OK != GNUNET_log_setup ("gnunet-testbed-spawn", NULL, NULL))
307 if (NULL == (sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
308 GNUNET_NO, GNUNET_NO)))
315 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
316 if (NULL == shc_chld)
318 LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot install a signal handler\n");
321 argv2 = GNUNET_malloc (sizeof (char *) * argc);
322 for (cnt = 1; cnt < argc; cnt++)
323 argv2[cnt - 1] = argv[cnt];
324 GNUNET_SCHEDULER_run (run, NULL);
326 GNUNET_SIGNAL_handler_uninstall (shc_chld);
328 GNUNET_DISK_pipe_close (sigpipe);
329 GNUNET_free_non_null (fn);
330 if (GNUNET_OK != ret)