diff options
author | Sree Harsha Totakura <totakura@in.tum.de> | 2013-02-27 14:22:24 +0000 |
---|---|---|
committer | Sree Harsha Totakura <totakura@in.tum.de> | 2013-02-27 14:22:24 +0000 |
commit | d6353593a73706c2bdbaac7fbb111fca6bdab9a5 (patch) | |
tree | b0d90b7b24fb82c4583d048b3fae0c6f3d35f505 /src | |
parent | e9a65d965436f0233a30951e89b01503cf88be35 (diff) | |
download | gnunet-d6353593a73706c2bdbaac7fbb111fca6bdab9a5.tar.gz gnunet-d6353593a73706c2bdbaac7fbb111fca6bdab9a5.zip |
Parsing LoadLeveler allocated hosts and creating testbed hosts from them
Diffstat (limited to 'src')
-rw-r--r-- | src/include/gnunet_testbed_service.h | 18 | ||||
-rw-r--r-- | src/testbed/gnunet_testbed_mpi_spawn.c | 218 | ||||
-rw-r--r-- | src/testbed/testbed_api_hosts.c | 200 |
3 files changed, 229 insertions, 207 deletions
diff --git a/src/include/gnunet_testbed_service.h b/src/include/gnunet_testbed_service.h index 25378e890..4ffcde76f 100644 --- a/src/include/gnunet_testbed_service.h +++ b/src/include/gnunet_testbed_service.h | |||
@@ -132,6 +132,24 @@ GNUNET_TESTBED_hosts_load_from_file (const char *filename, | |||
132 | 132 | ||
133 | 133 | ||
134 | /** | 134 | /** |
135 | * Loads the set of host allocated by the LoadLeveler Job Scheduler. This | ||
136 | * function is only available when compiled with support for LoadLeveler and is | ||
137 | * used for running on the SuperMUC | ||
138 | * | ||
139 | * @param cfg the configuration to use as a template while starting a controller | ||
140 | * on any of the loaded hosts. Operation queue sizes specific to a host | ||
141 | * are also read from this configuration handle | ||
142 | * @param hosts set to the hosts found in the file; caller must free this if | ||
143 | * number of hosts returned is greater than 0 | ||
144 | * @return number of hosts returned in 'hosts', 0 on error | ||
145 | */ | ||
146 | unsigned int | ||
147 | GNUNET_TESTBED_hosts_load_from_loadleveler (const struct | ||
148 | GNUNET_CONFIGURATION_Handle *cfg, | ||
149 | struct GNUNET_TESTBED_Host | ||
150 | ***hosts); | ||
151 | |||
152 | /** | ||
135 | * Destroy a host handle. Must only be called once everything | 153 | * Destroy a host handle. Must only be called once everything |
136 | * running on that host has been stopped. | 154 | * running on that host has been stopped. |
137 | * | 155 | * |
diff --git a/src/testbed/gnunet_testbed_mpi_spawn.c b/src/testbed/gnunet_testbed_mpi_spawn.c index c147d460b..2ff1972a6 100644 --- a/src/testbed/gnunet_testbed_mpi_spawn.c +++ b/src/testbed/gnunet_testbed_mpi_spawn.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "platform.h" | 1 | #include "platform.h" |
2 | #include "gnunet_util_lib.h" | 2 | #include "gnunet_util_lib.h" |
3 | #include "gnunet_resolver_service.h" | 3 | #include "gnunet_resolver_service.h" |
4 | #include "gnunet_testbed_service.h" | ||
4 | #include <mpi.h> | 5 | #include <mpi.h> |
5 | 6 | ||
6 | /** | 7 | /** |
@@ -22,216 +23,21 @@ | |||
22 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) | 23 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) |
23 | 24 | ||
24 | /** | 25 | /** |
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 | 26 | * Global result |
33 | */ | 27 | */ |
34 | static int ret; | 28 | static int ret; |
35 | 29 | ||
36 | /** | 30 | /** |
37 | * The array of hostnames | 31 | * The host list |
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 | */ | 32 | */ |
49 | struct GNUNET_RESOLVER_RequestHandle **rhs; | 33 | static struct GNUNET_TESTBED_Host **hosts; |
50 | 34 | ||
51 | /** | 35 | /** |
52 | * Number of hosts in the hostname array | 36 | * Number of hosts in the host list |
53 | */ | 37 | */ |
54 | static unsigned int nhosts; | 38 | static unsigned int nhosts; |
55 | 39 | ||
56 | /** | 40 | /** |
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. | 41 | * Main function that will be run by the scheduler. |
236 | * | 42 | * |
237 | * @param cls closure | 43 | * @param cls closure |
@@ -247,7 +53,7 @@ run (void *cls, char *const *args, const char *cfgfile, | |||
247 | unsigned long code; | 53 | unsigned long code; |
248 | enum GNUNET_OS_ProcessStatusType proc_status; | 54 | enum GNUNET_OS_ProcessStatusType proc_status; |
249 | int rank; | 55 | int rank; |
250 | int msg_size; | 56 | unsigned int host; |
251 | 57 | ||
252 | if (MPI_SUCCESS != MPI_Comm_rank (MPI_COMM_WORLD, &rank)) | 58 | if (MPI_SUCCESS != MPI_Comm_rank (MPI_COMM_WORLD, &rank)) |
253 | { | 59 | { |
@@ -291,13 +97,17 @@ run (void *cls, char *const *args, const char *cfgfile, | |||
291 | GNUNET_break (0); | 97 | GNUNET_break (0); |
292 | return; | 98 | return; |
293 | } | 99 | } |
294 | if (0 == get_loadleveler_hosts()) | 100 | if (0 == (nhosts = GNUNET_TESTBED_hosts_load_from_loadleveler (config, &hosts))) |
295 | { | 101 | { |
296 | GNUNET_break (0); | 102 | GNUNET_break (0); |
297 | ret = GNUNET_SYSERR; | 103 | ret = GNUNET_SYSERR; |
298 | return; | 104 | return; |
299 | } | 105 | } |
300 | resolve_task_id = GNUNET_SCHEDULER_add_now (&resolve_task, NULL); | 106 | for (host = 0; host < nhosts; host++) |
107 | GNUNET_TESTBED_host_destroy (hosts[host]); | ||
108 | GNUNET_free (hosts); | ||
109 | hosts = NULL; | ||
110 | ret = GNUNET_OK; | ||
301 | } | 111 | } |
302 | 112 | ||
303 | 113 | ||
@@ -330,12 +140,6 @@ main (int argc, char *argv[]) | |||
330 | _("Spawns cmd after starting my the MPI run-time"), | 140 | _("Spawns cmd after starting my the MPI run-time"), |
331 | options, &run, NULL); | 141 | options, &run, NULL); |
332 | (void) MPI_Finalize (); | 142 | (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)) | 143 | if ((GNUNET_OK == rres) && (GNUNET_OK == ret)) |
340 | return 0; | 144 | return 0; |
341 | printf ("Something went wrong\n"); | 145 | printf ("Something went wrong\n"); |
diff --git a/src/testbed/testbed_api_hosts.c b/src/testbed/testbed_api_hosts.c index 198e72c0e..d0349c4b3 100644 --- a/src/testbed/testbed_api_hosts.c +++ b/src/testbed/testbed_api_hosts.c | |||
@@ -63,6 +63,12 @@ | |||
63 | } while (0) | 63 | } while (0) |
64 | 64 | ||
65 | /** | 65 | /** |
66 | * Log an error message at log-level 'level' that indicates a failure of the | ||
67 | * command 'cmd' with the message given by gai_strerror(rc). | ||
68 | */ | ||
69 | #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) | ||
70 | |||
71 | /** | ||
66 | * Number of extra elements we create space for when we grow host list | 72 | * Number of extra elements we create space for when we grow host list |
67 | */ | 73 | */ |
68 | #define HOST_LIST_GROW_STEP 10 | 74 | #define HOST_LIST_GROW_STEP 10 |
@@ -525,6 +531,200 @@ GNUNET_TESTBED_hosts_load_from_file (const char *filename, | |||
525 | 531 | ||
526 | 532 | ||
527 | /** | 533 | /** |
534 | * Resolves a hostname using getaddrinfo | ||
535 | * | ||
536 | * @param host the hostname | ||
537 | * @return the string representing the IPv4 address of the given host; NULL upon error | ||
538 | */ | ||
539 | const char * | ||
540 | simple_resolve (const char *host) | ||
541 | { | ||
542 | struct addrinfo *res; | ||
543 | const struct sockaddr_in *in_addr; | ||
544 | char *hostip; | ||
545 | struct addrinfo hint; | ||
546 | unsigned int rc; | ||
547 | |||
548 | hint.ai_family = AF_INET; /* IPv4 */ | ||
549 | hint.ai_socktype = 0; | ||
550 | hint.ai_protocol = 0; | ||
551 | hint.ai_addrlen = 0; | ||
552 | hint.ai_addr = NULL; | ||
553 | hint.ai_canonname = NULL; | ||
554 | hint.ai_next = NULL; | ||
555 | hint.ai_flags = AI_NUMERICSERV; | ||
556 | res = NULL; | ||
557 | LOG_DEBUG ("Resolving [%s]\n", host); | ||
558 | if (0 != (rc = getaddrinfo (host, "22", &hint, &res))) | ||
559 | { | ||
560 | LOG_GAI (GNUNET_ERROR_TYPE_ERROR, "getaddrinfo", rc); | ||
561 | return NULL; | ||
562 | } | ||
563 | GNUNET_assert (NULL != res); | ||
564 | GNUNET_assert (NULL != res->ai_addr); | ||
565 | GNUNET_assert (sizeof (struct sockaddr_in) == res->ai_addrlen); | ||
566 | in_addr = (const struct sockaddr_in *) res->ai_addr; | ||
567 | hostip = inet_ntoa (in_addr->sin_addr); | ||
568 | GNUNET_assert (NULL != hostip); | ||
569 | LOG_DEBUG ("Resolved [%s] to [%s]\n", host, hostip); | ||
570 | return hostip; | ||
571 | } | ||
572 | |||
573 | |||
574 | /** | ||
575 | * Loads the set of host allocated by the LoadLeveler Job Scheduler. This | ||
576 | * function is only available when compiled with support for LoadLeveler and is | ||
577 | * used for running on the SuperMUC | ||
578 | * | ||
579 | * @param cfg the configuration to use as a template while starting a controller | ||
580 | * on any of the loaded hosts. Operation queue sizes specific to a host | ||
581 | * are also read from this configuration handle | ||
582 | * @param hosts set to the hosts found in the file; caller must free this if | ||
583 | * number of hosts returned is greater than 0 | ||
584 | * @return number of hosts returned in 'hosts', 0 on error | ||
585 | */ | ||
586 | unsigned int | ||
587 | GNUNET_TESTBED_hosts_load_from_loadleveler (const struct | ||
588 | GNUNET_CONFIGURATION_Handle *cfg, | ||
589 | struct GNUNET_TESTBED_Host ***hosts) | ||
590 | { | ||
591 | const char *hostfile; | ||
592 | char *buf; | ||
593 | char *hostname; | ||
594 | char **hostnames; | ||
595 | char **hostaddrs; | ||
596 | char *hostip; | ||
597 | struct GNUNET_TESTBED_Host **host_list; | ||
598 | ssize_t rsize; | ||
599 | uint64_t size; | ||
600 | uint64_t offset; | ||
601 | enum { | ||
602 | SCAN, | ||
603 | SKIP, | ||
604 | TRIM, | ||
605 | READHOST | ||
606 | } pstep; | ||
607 | unsigned int host; | ||
608 | unsigned int nhosts; | ||
609 | unsigned int nhostaddrs; | ||
610 | |||
611 | if (NULL == (hostfile = getenv ("MP_SAVEHOSTFILE"))) | ||
612 | { | ||
613 | GNUNET_break (0); | ||
614 | return 0; | ||
615 | } | ||
616 | if (GNUNET_SYSERR == GNUNET_DISK_file_size (hostfile, &size, GNUNET_YES, | ||
617 | GNUNET_YES)) | ||
618 | { | ||
619 | GNUNET_break (0); | ||
620 | return 0; | ||
621 | } | ||
622 | if (0 == size) | ||
623 | { | ||
624 | GNUNET_break (0); | ||
625 | return 0; | ||
626 | } | ||
627 | buf = GNUNET_malloc (size + 1); | ||
628 | rsize = GNUNET_DISK_fn_read (hostfile, buf, (size_t) size); | ||
629 | if ( (GNUNET_SYSERR == rsize) || ((ssize_t) size != rsize) ) | ||
630 | { | ||
631 | GNUNET_free (buf); | ||
632 | GNUNET_break (0); | ||
633 | return 0; | ||
634 | } | ||
635 | size++; | ||
636 | offset = 0; | ||
637 | pstep = SCAN; | ||
638 | hostname = NULL; | ||
639 | hostnames = NULL; | ||
640 | hostaddrs = NULL; | ||
641 | nhosts = 0; | ||
642 | nhostaddrs = 0; | ||
643 | while (offset < size) | ||
644 | { | ||
645 | switch (pstep) | ||
646 | { | ||
647 | case SCAN: | ||
648 | if ('!' == buf[offset]) | ||
649 | pstep = SKIP; | ||
650 | else | ||
651 | pstep = TRIM; | ||
652 | break; | ||
653 | case SKIP: | ||
654 | if ('\n' == buf[offset]) | ||
655 | pstep = SCAN; | ||
656 | break; | ||
657 | case TRIM: | ||
658 | if ('!' == buf[offset]) | ||
659 | { | ||
660 | pstep = SKIP; | ||
661 | break; | ||
662 | } | ||
663 | if ( (' ' == buf[offset]) | ||
664 | || ('\t' == buf[offset]) | ||
665 | || ('\r' == buf[offset]) ) | ||
666 | pstep = TRIM; | ||
667 | else | ||
668 | { | ||
669 | pstep = READHOST; | ||
670 | hostname = &buf[offset]; | ||
671 | } | ||
672 | break; | ||
673 | case READHOST: | ||
674 | if (isspace (buf[offset])) | ||
675 | { | ||
676 | buf[offset] = '\0'; | ||
677 | for (host = 0; host < nhosts; host++) | ||
678 | if (0 == strcmp (hostnames[host], hostname)) | ||
679 | break; | ||
680 | if (host == nhosts) | ||
681 | { | ||
682 | LOG_DEBUG ("Adding host [%s]\n", hostname); | ||
683 | hostname = GNUNET_strdup (hostname); | ||
684 | GNUNET_array_append (hostnames, nhosts, hostname); | ||
685 | } | ||
686 | else | ||
687 | LOG_DEBUG ("Not adding host [%s] as it is already included\n", hostname); | ||
688 | hostname = NULL; | ||
689 | pstep = SCAN; | ||
690 | } | ||
691 | break; | ||
692 | } | ||
693 | offset++; | ||
694 | } | ||
695 | GNUNET_free_non_null (buf); | ||
696 | if (NULL == hostnames) | ||
697 | return 0; | ||
698 | for (host = 0; host < nhosts; host++) | ||
699 | { | ||
700 | hostip = simple_resolve (hostnames[host]); | ||
701 | if (NULL == hostip) | ||
702 | { | ||
703 | nhosts = 0; | ||
704 | goto cleanup; | ||
705 | } | ||
706 | GNUNET_array_append (hostaddrs, nhostaddrs, GNUNET_strdup (hostip)); | ||
707 | } | ||
708 | GNUNET_assert (nhostaddrs == nhosts); | ||
709 | if (NULL == hosts) | ||
710 | goto cleanup; | ||
711 | host_list = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host *) * nhostaddrs); | ||
712 | for (host = 0; host < nhosts; host++) | ||
713 | host_list[host] = GNUNET_TESTBED_host_create (hostaddrs[host], NULL, cfg, 0); | ||
714 | *hosts = host_list; | ||
715 | |||
716 | cleanup: | ||
717 | for (host = 0; host < nhosts; host++) | ||
718 | GNUNET_free (hostnames[host]); | ||
719 | GNUNET_free(hostnames); | ||
720 | for (host = 0; (NULL != hostaddrs) && (host < nhostaddrs); host++) | ||
721 | GNUNET_free (hostaddrs[host]); | ||
722 | GNUNET_free (hostaddrs); | ||
723 | return nhosts; | ||
724 | } | ||
725 | |||
726 | |||
727 | /** | ||
528 | * Destroy a host handle. Must only be called once everything | 728 | * Destroy a host handle. Must only be called once everything |
529 | * running on that host has been stopped. | 729 | * running on that host has been stopped. |
530 | * | 730 | * |