c147d460ba9458369c26791bcbc6a167c1905c63
[oweals/gnunet.git] / src / testbed / gnunet_testbed_mpi_spawn.c
1 #include "platform.h"
2 #include "gnunet_util_lib.h"
3 #include "gnunet_resolver_service.h"
4 #include <mpi.h>
5
6 /**
7  * Generic logging shorthand
8  */
9 #define LOG(kind,...)                                           \
10   GNUNET_log_from (kind, "gnunet-mpi-test", __VA_ARGS__)
11
12 /**
13  * Debug logging shorthand
14  */
15 #define LOG_DEBUG(...)                          \
16   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
17
18 /**
19  * Timeout for resolving IPs
20  */
21 #define RESOLVE_TIMEOUT                         \
22   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
23
24 /**
25  * Log an error message at log-level 'level' that indicates
26  * a failure of the command 'cmd' with the message given
27  * by gcry_strerror(rc).
28  */
29 #define LOG_GAI(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gai_strerror(rc)); } while(0)
30
31 /**
32  * Global result
33  */
34 static int ret;
35
36 /**
37  * The array of hostnames
38  */
39 static char **hostnames;
40
41 /**
42  * The array of host's addresses
43  */
44 static char **hostaddrs;
45
46 /**
47  * The resolution request handles; one per each hostname resolution
48  */
49 struct GNUNET_RESOLVER_RequestHandle **rhs;
50
51 /**
52  * Number of hosts in the hostname array
53  */
54 static unsigned int nhosts;
55
56 /**
57  * Number of addresses in the hostaddr array
58  */
59 static unsigned int nhostaddrs;
60
61 /**
62  * Did we connect to the resolver service
63  */
64 static unsigned int resolver_connected;
65
66 /**
67  * Task for resolving ips
68  */
69 static GNUNET_SCHEDULER_TaskIdentifier resolve_task_id;
70
71
72 /**
73  * Resolves the hostnames array
74  *
75  * @param cls NULL
76  * @param tc the scheduler task context
77  */
78 static void
79 resolve_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
80 {
81   struct addrinfo hint;
82   const struct sockaddr_in *in_addr; 
83   struct addrinfo *res;
84   char *hostip;
85   unsigned int host;
86   unsigned int rc;
87
88   resolve_task_id = GNUNET_SCHEDULER_NO_TASK;
89   hint.ai_family = AF_INET;     /* IPv4 */
90   hint.ai_socktype = 0;
91   hint.ai_protocol = 0;
92   hint.ai_addrlen = 0;
93   hint.ai_addr = NULL;
94   hint.ai_canonname = NULL;
95   hint.ai_next = NULL;
96   hint.ai_flags = AI_NUMERICSERV;
97   for (host = 0; host < nhosts; host++)
98   {
99     res = NULL;
100     LOG_DEBUG ("Resolving: %s host\n", hostnames[host]);
101     if (0 != (rc = getaddrinfo (hostnames[host], "22", &hint, &res)))
102     {
103       LOG_GAI (GNUNET_ERROR_TYPE_ERROR, "getaddrinfo", rc);
104       ret = GNUNET_SYSERR;
105       return;
106     }
107     GNUNET_assert (NULL != res);
108     GNUNET_assert (NULL != res->ai_addr);
109     GNUNET_assert (sizeof (struct sockaddr_in) == res->ai_addrlen);
110     in_addr = (const struct sockaddr_in *) res->ai_addr;
111     hostip = inet_ntoa (in_addr->sin_addr);
112     GNUNET_assert (NULL != hostip);
113     GNUNET_array_append (hostaddrs, nhostaddrs, GNUNET_strdup (hostip));
114     LOG_DEBUG ("%s --> %s\n", hostnames[host], hostaddrs[host]);
115     freeaddrinfo (res);
116   }
117   ret = GNUNET_OK;
118 }
119
120
121 /**
122  * Loads the set of host allocated by the LoadLeveler Job Scheduler.  This
123  * function is only available when compiled with support for LoadLeveler and is
124  * used for running on the SuperMUC
125  *
126  * @param hostlist set to the hosts found in the file; caller must free this if
127  *          number of hosts returned is greater than 0
128  * @return number of hosts returned in 'hosts', 0 on error
129  */
130 unsigned int
131 get_loadleveler_hosts ()
132 {
133   const char *hostfile;
134   char *buf;
135   char *hostname;
136   struct addrinfo *ret;
137   struct addrinfo hint;
138   ssize_t rsize;
139   uint64_t size;
140   uint64_t offset;
141   enum {
142     SCAN,
143     SKIP,
144     TRIM,
145     READHOST
146   } pstep;
147   unsigned int host;
148
149   if (NULL == (hostfile = getenv ("MP_SAVEHOSTFILE")))
150   {
151     GNUNET_break (0);
152     return 0;
153   }
154   if (GNUNET_SYSERR == GNUNET_DISK_file_size (hostfile, &size, GNUNET_YES,
155                                               GNUNET_YES))
156   {
157     GNUNET_break (0);
158     return 0;
159   }
160   if (0 == size)
161   {
162     GNUNET_break (0);
163     return 0;
164   }
165   buf = GNUNET_malloc (size + 1);
166   rsize = GNUNET_DISK_fn_read (hostfile, buf, (size_t) size);
167   if ( (GNUNET_SYSERR == rsize) || ((ssize_t) size != rsize) )
168   {
169     GNUNET_free (buf);
170     GNUNET_break (0);
171     return 0;
172   }
173   size++;
174   offset = 0;
175   pstep = SCAN;
176   hostname = NULL;
177   while (offset < size)
178   {
179     switch (pstep)
180     {
181     case SCAN:
182       if ('!' == buf[offset])
183         pstep = SKIP;
184       else 
185         pstep = TRIM;
186       break;
187     case SKIP:
188       if ('\n' == buf[offset])
189         pstep = SCAN;
190       break;
191     case TRIM:
192       if ('!' == buf[offset])
193       {
194         pstep = SKIP;
195         break;
196       }
197       if ( (' ' == buf[offset]) 
198            || ('\t' == buf[offset])
199            || ('\r' == buf[offset]) )
200         pstep = TRIM;
201       else
202       {
203         pstep = READHOST;
204         hostname = &buf[offset];        
205       }
206       break;
207     case READHOST:
208       if (isspace (buf[offset]))
209       {
210         buf[offset] = '\0';
211         for (host = 0; host < nhosts; host++)
212           if (0 == strcmp (hostnames[host], hostname))
213             break;
214         if (host == nhosts)
215         {
216           LOG_DEBUG ("Adding host: %s\n", hostname);
217           hostname = GNUNET_strdup (hostname);
218           GNUNET_array_append (hostnames, nhosts, hostname);
219         }
220         else
221           LOG_DEBUG ("Not adding host %s as it is already included\n", hostname);
222         hostname = NULL;
223         pstep = SCAN;
224       }
225       break;
226     }
227     offset++;
228   }
229   GNUNET_free_non_null (buf);
230   return nhosts;
231 }
232
233
234 /**
235  * Main function that will be run by the scheduler.
236  *
237  * @param cls closure
238  * @param args remaining command-line arguments
239  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
240  * @param config configuration
241  */
242 static void
243 run (void *cls, char *const *args, const char *cfgfile,
244      const struct GNUNET_CONFIGURATION_Handle *config)
245 {
246   struct GNUNET_OS_Process *proc;
247   unsigned long code;
248   enum GNUNET_OS_ProcessStatusType proc_status;
249   int rank;
250   int msg_size;
251   
252   if (MPI_SUCCESS != MPI_Comm_rank (MPI_COMM_WORLD, &rank))
253   {
254     GNUNET_break (0);
255     return;
256   }
257   if (0 != rank)
258   {
259     ret = GNUNET_OK;
260     return;
261   }
262   PRINTF ("Spawning process\n");
263   proc =
264       GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL,
265                                    NULL, args[0], args);
266   if (NULL == proc)
267   {
268     printf ("Cannot exec\n");
269     return;
270   }
271   do
272   {
273     (void) sleep (1);
274     ret = GNUNET_OS_process_status (proc, &proc_status, &code);
275   }
276   while (GNUNET_NO == ret);
277   GNUNET_assert (GNUNET_NO != ret);
278   if (GNUNET_OK == ret)
279   {
280     if (0 != code)
281     {
282       LOG (GNUNET_ERROR_TYPE_WARNING, "Child terminated abnormally\n");
283       ret = GNUNET_SYSERR;
284       GNUNET_break (0);
285       return;
286     }
287   }
288   else
289   {
290     ret = GNUNET_SYSERR;
291     GNUNET_break (0);
292     return;
293   }
294   if (0 == get_loadleveler_hosts())
295   {
296     GNUNET_break (0);
297     ret = GNUNET_SYSERR;
298     return;
299   }
300   resolve_task_id = GNUNET_SCHEDULER_add_now (&resolve_task, NULL);
301 }
302
303
304 /**
305  * Execution start point
306  */
307 int
308 main (int argc, char *argv[])
309 {
310   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
311     GNUNET_GETOPT_OPTION_END
312   };
313   unsigned int host;
314   int rres;
315   
316   ret = GNUNET_SYSERR;
317   if (argc < 2)
318   {
319     printf ("Need arguments: gnunet-testbed-mpi-spawn <cmd> <cmd_args>");
320     return 1;
321   }
322   if (MPI_SUCCESS != MPI_Init (&argc, &argv))
323   {
324     GNUNET_break (0);
325     return 1;
326   }
327   rres =
328       GNUNET_PROGRAM_run (argc, argv,
329                           "gnunet-testbed-mpi-spawn <cmd> <cmd_args>",
330                           _("Spawns cmd after starting my the MPI run-time"),
331                           options, &run, NULL);
332   (void) MPI_Finalize ();
333   for (host = 0; host < nhosts; host++)
334     GNUNET_free (hostnames[host]);
335   for (host = 0; host < nhostaddrs; host++)
336     GNUNET_free (hostaddrs[host]);
337   GNUNET_free_non_null (hostnames);
338   GNUNET_free_non_null (hostaddrs);
339   if ((GNUNET_OK == rres) && (GNUNET_OK == ret))
340     return 0;
341   printf ("Something went wrong\n");
342   return 1;
343 }