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 * All our IP addresses
36 static char **our_addrs;
39 * Pipe used to communicate shutdown via signal.
41 static struct GNUNET_DISK_PipeHandle *sigpipe;
44 * Filename of the unique file
49 * Handle to the unique file
54 * The return code of the binary
56 static unsigned long child_exit_code;
59 * The process status of the child
61 static enum GNUNET_OS_ProcessStatusType child_status;
64 * how many IP addresses are currently assigned to us
66 static unsigned int num_addrs;
71 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
74 * Task to kill the child
76 static GNUNET_SCHEDULER_TaskIdentifier terminate_task_id;
79 * Task to kill the child
81 static GNUNET_SCHEDULER_TaskIdentifier child_death_task_id;
87 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
89 shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
90 if (0 != child_exit_code)
92 LOG (GNUNET_ERROR_TYPE_WARNING, "Child exited with error code: %lu\n",
94 ret = 128 + (int) child_exit_code;
100 if ((NULL != fn) && (0 != unlink (fn)))
102 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open");
109 terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
111 static int hard_kill;
113 GNUNET_assert (NULL != child);
115 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
116 &terminate_task, NULL);
123 LOG (GNUNET_ERROR_TYPE_WARNING,
124 "%d more interrupts needed to send SIGKILL to the child\n",
129 GNUNET_break (0 == GNUNET_OS_process_kill (child, SIGKILL));
134 GNUNET_break (0 == GNUNET_OS_process_kill (child, SIGTERM));
135 LOG (GNUNET_ERROR_TYPE_INFO, _("Waiting for child to exit.\n"));
140 * Task triggered whenever we receive a SIGCHLD (child
143 * @param cls closure, NULL if we need to self-restart
147 child_death_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
149 const struct GNUNET_DISK_FileHandle *pr;
152 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
153 child_death_task_id = GNUNET_SCHEDULER_NO_TASK;
154 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
156 child_death_task_id =
157 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
158 pr, &child_death_task, NULL);
161 /* consume the signal */
162 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
163 LOG_DEBUG ("Child died\n");
164 GNUNET_SCHEDULER_cancel (terminate_task_id);
165 terminate_task_id = GNUNET_SCHEDULER_NO_TASK;
166 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_status (child, &child_status,
168 GNUNET_OS_process_destroy (child);
170 shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
175 * Callback function invoked for each interface found.
178 * @param name name of the interface (can be NULL for unknown)
179 * @param isDefault is this presumably the default interface
180 * @param addr address of this interface (can be NULL for unknown or unassigned)
181 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
182 * @param netmask the network mask (can be NULL for unknown or unassigned))
183 * @param addrlen length of the address
184 * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
187 addr_proc (void *cls, const char *name, int isDefault,
188 const struct sockaddr *addr,
189 const struct sockaddr *broadcast_addr,
190 const struct sockaddr *netmask, socklen_t addrlen)
192 const struct sockaddr_in *in_addr;
195 if (sizeof (struct sockaddr_in) != addrlen)
197 in_addr = (const struct sockaddr_in *) addr;
198 if (NULL == (ipaddr = inet_ntoa (in_addr->sin_addr)))
200 GNUNET_array_append (our_addrs, num_addrs, GNUNET_strdup (ipaddr));
206 destroy_hosts(struct GNUNET_TESTBED_Host **hosts, unsigned int nhosts)
210 GNUNET_assert (NULL != hosts);
211 for (host = 0; host < nhosts; host++)
212 if (NULL != hosts[host])
213 GNUNET_TESTBED_host_destroy (hosts[host]);
220 * The main scheduler run task
223 * @param tc scheduler task context
226 run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
228 struct GNUNET_TESTBED_Host **hosts;
229 const struct GNUNET_CONFIGURATION_Handle *null_cfg;
233 unsigned int host_cnt;
234 unsigned int addr_cnt;
236 GNUNET_OS_network_interfaces_list (&addr_proc, NULL);
243 null_cfg = GNUNET_CONFIGURATION_create ();
244 nhosts = GNUNET_TESTBED_hosts_load_from_loadleveler (null_cfg, &hosts);
251 for (host_cnt = 0; host_cnt < nhosts; host_cnt++)
253 host_ip = GNUNET_TESTBED_host_get_hostname (hosts[host_cnt]);
254 for (addr_cnt = 0; addr_cnt < num_addrs; addr_cnt++)
255 if (0 == strcmp (host_ip, our_addrs[addr_cnt]))
260 destroy_hosts (hosts, nhosts);
264 destroy_hosts (hosts, nhosts);
267 LOG_DEBUG ("Exiting as we are not the lowest host\n");
271 tmpdir = getenv ("TMPDIR");
273 tmpdir = getenv ("TMP");
275 tmpdir = getenv ("TEMP");
278 (void) GNUNET_asprintf (&fn, "%s/gnunet-testbed-spawn.lock", tmpdir);
279 /* Open the unique file; we can create it then we can spawn the child process
281 fh = open (fn, O_CREAT | O_EXCL | O_CLOEXEC,
282 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
287 LOG_DEBUG ("Lock file already created by other process. Exiting\n");
291 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open");
295 /* Spawn the new process here */
296 LOG (GNUNET_ERROR_TYPE_INFO, _("Spawning process `%s'\n"), argv2[0]);
297 child = GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL,
304 shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
309 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
310 &terminate_task, NULL);
311 child_death_task_id =
312 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
313 GNUNET_DISK_pipe_handle (sigpipe,
314 GNUNET_DISK_PIPE_END_READ),
315 &child_death_task, NULL);
320 * Signal handler called for SIGCHLD.
323 sighandler_child_death ()
326 int old_errno = errno; /* back-up errno */
329 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
330 (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
332 errno = old_errno; /* restore errno */
337 * Execution start point
340 main (int argc, char *argv[])
342 struct GNUNET_SIGNAL_Context *shc_chld;
348 printf ("Need arguments: gnunet-testbed-mpi-spawn <cmd> <cmd_args>");
351 if (GNUNET_OK != GNUNET_log_setup ("gnunet-testbed-spawn", NULL, NULL))
356 if (NULL == (sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
357 GNUNET_NO, GNUNET_NO)))
364 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
365 if (NULL == shc_chld)
367 LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot install a signal handler\n");
370 argv2 = GNUNET_malloc (sizeof (char *) * argc);
371 for (cnt = 1; cnt < argc; cnt++)
372 argv2[cnt - 1] = argv[cnt];
373 GNUNET_SCHEDULER_run (run, NULL);
375 GNUNET_SIGNAL_handler_uninstall (shc_chld);
377 GNUNET_DISK_pipe_close (sigpipe);
378 GNUNET_free_non_null (fn);
379 if (GNUNET_OK != ret)