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