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