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