diff options
Diffstat (limited to 'src/testing_old/testing.c')
-rw-r--r-- | src/testing_old/testing.c | 2204 |
1 files changed, 2204 insertions, 0 deletions
diff --git a/src/testing_old/testing.c b/src/testing_old/testing.c new file mode 100644 index 000000000..5456d56cb --- /dev/null +++ b/src/testing_old/testing.c | |||
@@ -0,0 +1,2204 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2008, 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file testing/testing.c | ||
23 | * @brief convenience API for writing testcases for GNUnet | ||
24 | * Many testcases need to start and stop gnunetd, | ||
25 | * and this library is supposed to make that easier | ||
26 | * for TESTCASES. Normal programs should always | ||
27 | * use functions from gnunet_{util,arm}_lib.h. This API is | ||
28 | * ONLY for writing testcases! | ||
29 | * @author Christian Grothoff | ||
30 | * | ||
31 | */ | ||
32 | #include "platform.h" | ||
33 | #include "gnunet_arm_service.h" | ||
34 | #include "gnunet_core_service.h" | ||
35 | #include "gnunet_constants.h" | ||
36 | #include "gnunet_testing_lib.h" | ||
37 | #include "gnunet_transport_service.h" | ||
38 | #include "gnunet_hello_lib.h" | ||
39 | |||
40 | /** | ||
41 | * Hack to deal with initial HELLO's being often devoid of addresses. | ||
42 | * This hack causes 'process_hello' to ignore HELLOs without addresses. | ||
43 | * The correct implementation would continue with 'process_hello' until | ||
44 | * the connection could be established... | ||
45 | */ | ||
46 | #define EMPTY_HACK GNUNET_YES | ||
47 | |||
48 | /** | ||
49 | * How long do we wait after starting gnunet-service-arm | ||
50 | * for the core service to be alive? | ||
51 | */ | ||
52 | #define ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) | ||
53 | |||
54 | /** | ||
55 | * How many times are we willing to try to wait for "scp" or | ||
56 | * "gnunet-service-arm" to complete (waitpid) before giving up? | ||
57 | */ | ||
58 | #define MAX_EXEC_WAIT_RUNS 250 | ||
59 | |||
60 | static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} }; | ||
61 | |||
62 | #if EMPTY_HACK | ||
63 | static int | ||
64 | test_address (void *cls, const struct GNUNET_HELLO_Address *address, | ||
65 | struct GNUNET_TIME_Absolute expiration) | ||
66 | { | ||
67 | int *empty = cls; | ||
68 | |||
69 | *empty = GNUNET_NO; | ||
70 | return GNUNET_OK; | ||
71 | } | ||
72 | #endif | ||
73 | |||
74 | /** | ||
75 | * Receive the HELLO from one peer, give it to the other | ||
76 | * and ask them to connect. | ||
77 | * | ||
78 | * @param cls Closure (daemon whose hello is this). | ||
79 | * @param message HELLO message of peer | ||
80 | */ | ||
81 | static void | ||
82 | process_hello (void *cls, const struct GNUNET_MessageHeader *message) | ||
83 | { | ||
84 | struct GNUNET_TESTING_Daemon *daemon = cls; | ||
85 | int msize; | ||
86 | |||
87 | #if EMPTY_HACK | ||
88 | int empty; | ||
89 | |||
90 | empty = GNUNET_YES; | ||
91 | GNUNET_assert (message != NULL); | ||
92 | GNUNET_HELLO_iterate_addresses ((const struct GNUNET_HELLO_Message *) message, | ||
93 | GNUNET_NO, &test_address, &empty); | ||
94 | if (GNUNET_YES == empty) | ||
95 | { | ||
96 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
97 | "Skipping empty HELLO address of peer %s\n", | ||
98 | GNUNET_i2s (&daemon->id)); | ||
99 | return; | ||
100 | } | ||
101 | #endif | ||
102 | GNUNET_assert (daemon->phase == SP_GET_HELLO || | ||
103 | daemon->phase == SP_START_DONE); | ||
104 | daemon->cb = NULL; // FIXME: why??? (see fsm:SP_START_CORE, notify_daemon_started) | ||
105 | if (daemon->task != GNUNET_SCHEDULER_NO_TASK) /* Assertion here instead? */ | ||
106 | GNUNET_SCHEDULER_cancel (daemon->task); | ||
107 | |||
108 | if (daemon->server != NULL) | ||
109 | { | ||
110 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
111 | "Received `%s' from transport service of `%4s', disconnecting core!\n", | ||
112 | "HELLO", GNUNET_i2s (&daemon->id)); | ||
113 | GNUNET_CORE_disconnect (daemon->server); | ||
114 | daemon->server = NULL; | ||
115 | } | ||
116 | |||
117 | msize = ntohs (message->size); | ||
118 | if (msize < 1) | ||
119 | { | ||
120 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
121 | "HELLO message of peer %s is of size 0\n", | ||
122 | GNUNET_i2s (&daemon->id)); | ||
123 | return; | ||
124 | } | ||
125 | if (daemon->ghh != NULL) | ||
126 | { | ||
127 | GNUNET_TRANSPORT_get_hello_cancel (daemon->ghh); | ||
128 | daemon->ghh = NULL; | ||
129 | } | ||
130 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
131 | "Received `%s' from transport service of `%4s'\n", "HELLO", | ||
132 | GNUNET_i2s (&daemon->id)); | ||
133 | GNUNET_free_non_null (daemon->hello); | ||
134 | daemon->hello = GNUNET_malloc (msize); | ||
135 | memcpy (daemon->hello, message, msize); | ||
136 | |||
137 | if (daemon->th != NULL) | ||
138 | { | ||
139 | GNUNET_TRANSPORT_disconnect (daemon->th); | ||
140 | daemon->th = NULL; | ||
141 | } | ||
142 | daemon->phase = SP_START_DONE; | ||
143 | } | ||
144 | |||
145 | |||
146 | /** | ||
147 | * Notify of a peer being up and running. Scheduled as a task | ||
148 | * so that variables which may need to be set are set before | ||
149 | * the connect callback can set up new operations. | ||
150 | * FIXME: what variables?????? where from???? | ||
151 | * | ||
152 | * @param cls the testing daemon | ||
153 | * @param tc task scheduler context | ||
154 | */ | ||
155 | static void | ||
156 | notify_daemon_started (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
157 | { | ||
158 | struct GNUNET_TESTING_Daemon *d = cls; | ||
159 | GNUNET_TESTING_NotifyDaemonRunning cb; | ||
160 | |||
161 | cb = d->cb; | ||
162 | d->cb = NULL; | ||
163 | if (NULL != cb) | ||
164 | cb (d->cb_cls, &d->id, d->cfg, d, NULL); | ||
165 | } | ||
166 | |||
167 | |||
168 | /** | ||
169 | * Finite-state machine for starting GNUnet. | ||
170 | * | ||
171 | * @param cls our "struct GNUNET_TESTING_Daemon" | ||
172 | * @param tc unused | ||
173 | */ | ||
174 | static void | ||
175 | start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
176 | { | ||
177 | struct GNUNET_TESTING_Daemon *d = cls; | ||
178 | GNUNET_TESTING_NotifyDaemonRunning cb; | ||
179 | enum GNUNET_OS_ProcessStatusType type; | ||
180 | unsigned long code; | ||
181 | char *dst; | ||
182 | int bytes_read; | ||
183 | |||
184 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %s FSM is in phase %u.\n", | ||
185 | GNUNET_i2s (&d->id), d->phase); | ||
186 | d->task = GNUNET_SCHEDULER_NO_TASK; | ||
187 | switch (d->phase) | ||
188 | { | ||
189 | case SP_COPYING: | ||
190 | /* confirm copying complete */ | ||
191 | if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code)) | ||
192 | { | ||
193 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) | ||
194 | { | ||
195 | cb = d->cb; | ||
196 | d->cb = NULL; | ||
197 | if (NULL != cb) | ||
198 | cb (d->cb_cls, NULL, d->cfg, d, | ||
199 | _ | ||
200 | ("`scp' does not seem to terminate (timeout copying config).\n")); | ||
201 | return; | ||
202 | } | ||
203 | /* wait some more */ | ||
204 | d->task = | ||
205 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
206 | d); | ||
207 | return; | ||
208 | } | ||
209 | if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0)) | ||
210 | { | ||
211 | cb = d->cb; | ||
212 | d->cb = NULL; | ||
213 | if (NULL != cb) | ||
214 | cb (d->cb_cls, NULL, d->cfg, d, _("`scp' did not complete cleanly.\n")); | ||
215 | return; | ||
216 | } | ||
217 | GNUNET_OS_process_destroy (d->proc_arm_copying); | ||
218 | d->proc_arm_copying = NULL; | ||
219 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
220 | "Successfully copied configuration file.\n"); | ||
221 | d->phase = SP_COPIED; | ||
222 | /* fall-through */ | ||
223 | case SP_COPIED: | ||
224 | /* Start create hostkey process if we don't already know the peer identity! */ | ||
225 | if (GNUNET_NO == d->have_hostkey) | ||
226 | { | ||
227 | GNUNET_assert (NULL == d->proc_arm_peerinfo); | ||
228 | d->pipe_stdout = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_YES); | ||
229 | if (d->pipe_stdout == NULL) | ||
230 | { | ||
231 | cb = d->cb; | ||
232 | d->cb = NULL; | ||
233 | if (NULL != cb) | ||
234 | cb (d->cb_cls, NULL, d->cfg, d, | ||
235 | (NULL == | ||
236 | d->hostname) ? | ||
237 | _("Failed to create pipe for `gnunet-peerinfo' process.\n") : | ||
238 | _("Failed to create pipe for `ssh' process.\n")); | ||
239 | return; | ||
240 | } | ||
241 | if (NULL == d->hostname) | ||
242 | { | ||
243 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
244 | "Starting `%s', with command `%s %s %s %s'.\n", | ||
245 | "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile, | ||
246 | "-sq"); | ||
247 | d->proc_arm_peerinfo = | ||
248 | GNUNET_OS_start_process (GNUNET_YES, NULL, d->pipe_stdout, "gnunet-peerinfo", | ||
249 | "gnunet-peerinfo", "-c", d->cfgfile, "-sq", | ||
250 | NULL); | ||
251 | GNUNET_DISK_pipe_close_end (d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE); | ||
252 | } | ||
253 | else | ||
254 | { | ||
255 | if (d->username != NULL) | ||
256 | GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname); | ||
257 | else | ||
258 | dst = GNUNET_strdup (d->hostname); | ||
259 | |||
260 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
261 | "Starting `%s', with command `%s %s %s %s %s %s'.\n", | ||
262 | "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c", | ||
263 | d->cfgfile, "-sq"); | ||
264 | if (d->ssh_port_str == NULL) | ||
265 | { | ||
266 | d->proc_arm_peerinfo = GNUNET_OS_start_process (GNUNET_NO, NULL, d->pipe_stdout, "ssh", "ssh", | ||
267 | "-q", | ||
268 | dst, "gnunet-peerinfo", "-c", | ||
269 | d->cfgfile, "-sq", NULL); | ||
270 | } | ||
271 | else | ||
272 | { | ||
273 | d->proc_arm_peerinfo = | ||
274 | GNUNET_OS_start_process (GNUNET_NO, NULL, d->pipe_stdout, "ssh", "ssh", "-p", | ||
275 | d->ssh_port_str, | ||
276 | "-q", | ||
277 | dst, "gnunet-peerinfo", "-c", d->cfgfile, | ||
278 | "-sq", NULL); | ||
279 | } | ||
280 | GNUNET_DISK_pipe_close_end (d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE); | ||
281 | GNUNET_free (dst); | ||
282 | } | ||
283 | if (NULL == d->proc_arm_peerinfo) | ||
284 | { | ||
285 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
286 | _("Could not start `%s' process to create hostkey.\n"), | ||
287 | (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh"); | ||
288 | cb = d->cb; | ||
289 | d->cb = NULL; | ||
290 | if (NULL != cb) | ||
291 | cb (d->cb_cls, NULL, d->cfg, d, | ||
292 | (NULL == | ||
293 | d->hostname) ? _("Failed to start `gnunet-peerinfo' process.\n") | ||
294 | : _("Failed to start `ssh' process.\n")); | ||
295 | GNUNET_DISK_pipe_close (d->pipe_stdout); | ||
296 | return; | ||
297 | } | ||
298 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
299 | "Started `%s', waiting for hostkey.\n", "gnunet-peerinfo"); | ||
300 | d->phase = SP_HOSTKEY_CREATE; | ||
301 | d->task = | ||
302 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining | ||
303 | (d->max_timeout), | ||
304 | GNUNET_DISK_pipe_handle | ||
305 | (d->pipe_stdout, | ||
306 | GNUNET_DISK_PIPE_END_READ), | ||
307 | &start_fsm, d); | ||
308 | } | ||
309 | else /* Already have a hostkey! */ | ||
310 | { | ||
311 | if (d->hostkey_callback != NULL) | ||
312 | { | ||
313 | d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL); | ||
314 | d->hostkey_callback = NULL; | ||
315 | d->phase = SP_HOSTKEY_CREATED; | ||
316 | } | ||
317 | else | ||
318 | d->phase = SP_TOPOLOGY_SETUP; | ||
319 | |||
320 | /* wait some more */ | ||
321 | d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d); | ||
322 | } | ||
323 | break; | ||
324 | case SP_HOSTKEY_CREATE: | ||
325 | bytes_read = | ||
326 | GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle | ||
327 | (d->pipe_stdout, GNUNET_DISK_PIPE_END_READ), | ||
328 | &d->hostkeybuf[d->hostkeybufpos], | ||
329 | sizeof (d->hostkeybuf) - d->hostkeybufpos); | ||
330 | if (bytes_read > 0) | ||
331 | d->hostkeybufpos += bytes_read; | ||
332 | |||
333 | if ((d->hostkeybufpos < 104) && (bytes_read > 0)) | ||
334 | { | ||
335 | /* keep reading */ | ||
336 | d->task = | ||
337 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining | ||
338 | (d->max_timeout), | ||
339 | GNUNET_DISK_pipe_handle | ||
340 | (d->pipe_stdout, | ||
341 | GNUNET_DISK_PIPE_END_READ), | ||
342 | &start_fsm, d); | ||
343 | return; | ||
344 | } | ||
345 | d->hostkeybuf[103] = '\0'; | ||
346 | |||
347 | if ((bytes_read < 0) || | ||
348 | (GNUNET_OK != | ||
349 | GNUNET_CRYPTO_hash_from_string (d->hostkeybuf, &d->id.hashPubKey))) | ||
350 | { | ||
351 | /* error */ | ||
352 | if (bytes_read < 0) | ||
353 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
354 | _("Error reading from gnunet-peerinfo: %s\n"), | ||
355 | STRERROR (errno)); | ||
356 | else | ||
357 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
358 | _("Malformed output from gnunet-peerinfo!\n")); | ||
359 | cb = d->cb; | ||
360 | d->cb = NULL; | ||
361 | GNUNET_DISK_pipe_close (d->pipe_stdout); | ||
362 | d->pipe_stdout = NULL; | ||
363 | (void) GNUNET_OS_process_kill (d->proc_arm_peerinfo, SIGKILL); | ||
364 | GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc_arm_peerinfo)); | ||
365 | GNUNET_OS_process_destroy (d->proc_arm_peerinfo); | ||
366 | d->proc_arm_peerinfo = NULL; | ||
367 | if (NULL != cb) | ||
368 | cb (d->cb_cls, NULL, d->cfg, d, _("Failed to get hostkey!\n")); | ||
369 | return; | ||
370 | } | ||
371 | d->shortname = GNUNET_strdup (GNUNET_i2s (&d->id)); | ||
372 | GNUNET_DISK_pipe_close (d->pipe_stdout); | ||
373 | d->pipe_stdout = NULL; | ||
374 | (void) GNUNET_OS_process_kill (d->proc_arm_peerinfo, SIGKILL); | ||
375 | GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (d->proc_arm_peerinfo)); | ||
376 | GNUNET_OS_process_destroy (d->proc_arm_peerinfo); | ||
377 | d->proc_arm_peerinfo = NULL; | ||
378 | d->have_hostkey = GNUNET_YES; | ||
379 | if (d->hostkey_callback != NULL) | ||
380 | { | ||
381 | d->hostkey_callback (d->hostkey_cls, &d->id, d, NULL); | ||
382 | d->hostkey_callback = NULL; | ||
383 | d->phase = SP_HOSTKEY_CREATED; | ||
384 | } | ||
385 | else | ||
386 | { | ||
387 | d->phase = SP_TOPOLOGY_SETUP; | ||
388 | } | ||
389 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully got hostkey!\n"); | ||
390 | /* Fall through */ | ||
391 | case SP_HOSTKEY_CREATED: | ||
392 | /* wait for topology finished */ | ||
393 | if ((GNUNET_YES == d->dead) || | ||
394 | (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0)) | ||
395 | { | ||
396 | cb = d->cb; | ||
397 | d->cb = NULL; | ||
398 | if (NULL != cb) | ||
399 | cb (d->cb_cls, NULL, d->cfg, d, | ||
400 | _("`Failed while waiting for topology setup!\n")); | ||
401 | return; | ||
402 | } | ||
403 | |||
404 | d->task = | ||
405 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
406 | d); | ||
407 | break; | ||
408 | case SP_TOPOLOGY_SETUP: /* Indicates topology setup has completed! */ | ||
409 | /* start GNUnet on remote host */ | ||
410 | if (NULL == d->hostname) | ||
411 | { | ||
412 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
413 | "Starting `%s', with command `%s %s %s %s'.\n", | ||
414 | "gnunet-arm", "gnunet-arm", "-c", d->cfgfile, | ||
415 | "-s"); | ||
416 | d->proc_arm_start = | ||
417 | GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", "-c", | ||
418 | d->cfgfile, | ||
419 | "-s", "-q", "-T", | ||
420 | GNUNET_TIME_relative_to_string | ||
421 | (GNUNET_TIME_absolute_get_remaining | ||
422 | (d->max_timeout)), NULL); | ||
423 | } | ||
424 | else | ||
425 | { | ||
426 | if (d->username != NULL) | ||
427 | GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname); | ||
428 | else | ||
429 | dst = GNUNET_strdup (d->hostname); | ||
430 | |||
431 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
432 | "Starting `%s', with command `%s %s %s %s %s %s %s'.\n", | ||
433 | "gnunet-arm", "ssh", dst, "gnunet-arm", "-c", d->cfgfile, | ||
434 | "-s", "-q"); | ||
435 | if (d->ssh_port_str == NULL) | ||
436 | { | ||
437 | d->proc_arm_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", | ||
438 | "-q", | ||
439 | dst, "gnunet-arm", | ||
440 | "-c", d->cfgfile, "-s", "-q", "-T", | ||
441 | GNUNET_TIME_relative_to_string | ||
442 | (GNUNET_TIME_absolute_get_remaining | ||
443 | (d->max_timeout)), NULL); | ||
444 | } | ||
445 | else | ||
446 | { | ||
447 | |||
448 | d->proc_arm_start = | ||
449 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", "-p", | ||
450 | d->ssh_port_str, | ||
451 | "-q", | ||
452 | dst, "gnunet-arm", | ||
453 | "-c", d->cfgfile, "-s", "-q", "-T", | ||
454 | GNUNET_TIME_relative_to_string | ||
455 | (GNUNET_TIME_absolute_get_remaining | ||
456 | (d->max_timeout)), NULL); | ||
457 | } | ||
458 | GNUNET_free (dst); | ||
459 | } | ||
460 | if (NULL == d->proc_arm_start) | ||
461 | { | ||
462 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
463 | _("Could not start `%s' process to start GNUnet.\n"), | ||
464 | (NULL == d->hostname) ? "gnunet-arm" : "ssh"); | ||
465 | cb = d->cb; | ||
466 | d->cb = NULL; | ||
467 | if (NULL != cb) | ||
468 | cb (d->cb_cls, NULL, d->cfg, d, | ||
469 | (NULL == | ||
470 | d->hostname) ? _("Failed to start `gnunet-arm' process.\n") : | ||
471 | _("Failed to start `ssh' process.\n")); | ||
472 | return; | ||
473 | } | ||
474 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
475 | "Started `%s', waiting for `%s' to be up.\n", "gnunet-arm", | ||
476 | "gnunet-service-core"); | ||
477 | d->phase = SP_START_ARMING; | ||
478 | d->task = | ||
479 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
480 | d); | ||
481 | // FIXME: busy wait? | ||
482 | break; | ||
483 | case SP_START_ARMING: | ||
484 | if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_start, &type, &code)) | ||
485 | { | ||
486 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) | ||
487 | { | ||
488 | cb = d->cb; | ||
489 | d->cb = NULL; | ||
490 | if (NULL != cb) | ||
491 | cb (d->cb_cls, NULL, d->cfg, d, | ||
492 | (NULL == | ||
493 | d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") : | ||
494 | _("`ssh' does not seem to terminate.\n")); | ||
495 | if (d->cfg != NULL) | ||
496 | { | ||
497 | GNUNET_CONFIGURATION_destroy (d->cfg); | ||
498 | d->cfg = NULL; | ||
499 | } | ||
500 | if (d->cfgfile != NULL) | ||
501 | { | ||
502 | GNUNET_free (d->cfgfile); | ||
503 | d->cfgfile = NULL; | ||
504 | } | ||
505 | GNUNET_free_non_null (d->hostname); | ||
506 | GNUNET_free_non_null (d->username); | ||
507 | GNUNET_OS_process_destroy (d->proc_arm_start); | ||
508 | d->proc_arm_start = NULL; | ||
509 | d->username = NULL; | ||
510 | d->hostname = NULL; // Quick hack to avoid crashing (testing need to be | ||
511 | d->cfg = NULL; // overhauled anyway, and the error managing is | ||
512 | // GNUNET_free (d); // FIXME (could this leak) | ||
513 | // pretty broken anyway. | ||
514 | return; | ||
515 | } | ||
516 | /* wait some more */ | ||
517 | d->task = | ||
518 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
519 | d); | ||
520 | // FIXME: busy wait? | ||
521 | return; | ||
522 | } | ||
523 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully started `%s'.\n", | ||
524 | "gnunet-arm"); | ||
525 | GNUNET_OS_process_destroy (d->proc_arm_start); | ||
526 | d->proc_arm_start = NULL; | ||
527 | d->phase = SP_START_CORE; | ||
528 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling CORE_connect\n"); | ||
529 | /* Fall through */ | ||
530 | case SP_START_CORE: | ||
531 | if (d->server != NULL) | ||
532 | GNUNET_CORE_disconnect (d->server); | ||
533 | |||
534 | d->th = GNUNET_TRANSPORT_connect (d->cfg, &d->id, d, NULL, NULL, NULL); | ||
535 | if (d->th == NULL) | ||
536 | { | ||
537 | if (GNUNET_YES == d->dead) | ||
538 | GNUNET_TESTING_daemon_stop (d, | ||
539 | GNUNET_TIME_absolute_get_remaining | ||
540 | (d->max_timeout), d->dead_cb, | ||
541 | d->dead_cb_cls, GNUNET_YES, GNUNET_NO); | ||
542 | else if (NULL != d->cb) | ||
543 | d->cb (d->cb_cls, &d->id, d->cfg, d, | ||
544 | _("Failed to connect to transport service!\n")); | ||
545 | return; | ||
546 | } | ||
547 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
548 | "Connected to transport service `%s', getting HELLO\n", | ||
549 | GNUNET_i2s (&d->id)); | ||
550 | d->ghh = GNUNET_TRANSPORT_get_hello (d->th, &process_hello, d); | ||
551 | /* FIXME: store task ID somewhere! */ | ||
552 | GNUNET_SCHEDULER_add_now (¬ify_daemon_started, d); | ||
553 | /*cb = d->cb; | ||
554 | * d->cb = NULL; | ||
555 | * if (NULL != cb) | ||
556 | * cb (d->cb_cls, &d->id, d->cfg, d, NULL); */ | ||
557 | d->running = GNUNET_YES; | ||
558 | d->phase = SP_GET_HELLO; | ||
559 | break; | ||
560 | case SP_GET_HELLO: | ||
561 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) | ||
562 | { | ||
563 | if (d->server != NULL) | ||
564 | GNUNET_CORE_disconnect (d->server); | ||
565 | if (d->th != NULL) | ||
566 | GNUNET_TRANSPORT_disconnect (d->th); | ||
567 | cb = d->cb; | ||
568 | d->cb = NULL; | ||
569 | if (NULL != cb) | ||
570 | cb (d->cb_cls, NULL, d->cfg, d, _("Unable to get HELLO for peer!\n")); | ||
571 | GNUNET_CONFIGURATION_destroy (d->cfg); | ||
572 | GNUNET_free (d->cfgfile); | ||
573 | GNUNET_free_non_null (d->hostname); | ||
574 | GNUNET_free_non_null (d->username); | ||
575 | GNUNET_free (d); | ||
576 | return; | ||
577 | } | ||
578 | if (d->hello != NULL) | ||
579 | return; | ||
580 | GNUNET_assert (d->task == GNUNET_SCHEDULER_NO_TASK); | ||
581 | d->task = | ||
582 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
583 | (GNUNET_CONSTANTS_SERVICE_RETRY, 2), | ||
584 | &start_fsm, d); | ||
585 | break; | ||
586 | case SP_START_DONE: | ||
587 | GNUNET_break (0); | ||
588 | break; | ||
589 | case SP_SERVICE_START: | ||
590 | /* confirm gnunet-arm exited */ | ||
591 | if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_start, &type, &code)) | ||
592 | { | ||
593 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) | ||
594 | { | ||
595 | cb = d->cb; | ||
596 | d->cb = NULL; | ||
597 | if (NULL != cb) | ||
598 | cb (d->cb_cls, NULL, d->cfg, d, | ||
599 | (NULL == | ||
600 | d->hostname) ? _("`gnunet-arm' does not seem to terminate.\n") : | ||
601 | _("`ssh' does not seem to terminate.\n")); | ||
602 | return; | ||
603 | } | ||
604 | /* wait some more */ | ||
605 | d->task = | ||
606 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
607 | d); | ||
608 | return; | ||
609 | } | ||
610 | #if EXTRA_CHECKS | ||
611 | if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0)) | ||
612 | { | ||
613 | cb = d->cb; | ||
614 | d->cb = NULL; | ||
615 | if (NULL != cb) | ||
616 | cb (d->cb_cls, NULL, d->cfg, d, | ||
617 | (NULL == | ||
618 | d->hostname) ? | ||
619 | _ | ||
620 | ("`gnunet-arm' terminated with non-zero exit status (or timed out)!\n") | ||
621 | : _("`ssh' does not seem to terminate.\n")); | ||
622 | return; | ||
623 | } | ||
624 | #endif | ||
625 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service startup complete!\n"); | ||
626 | cb = d->cb; | ||
627 | d->cb = NULL; | ||
628 | d->phase = SP_START_DONE; | ||
629 | if (NULL != cb) | ||
630 | cb (d->cb_cls, &d->id, d->cfg, d, NULL); | ||
631 | GNUNET_OS_process_destroy (d->proc_arm_srv_start); | ||
632 | d->proc_arm_srv_start = NULL; | ||
633 | break; | ||
634 | case SP_SERVICE_SHUTDOWN_START: | ||
635 | /* confirm copying complete */ | ||
636 | if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_srv_stop, &type, &code)) | ||
637 | { | ||
638 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) | ||
639 | { | ||
640 | if (NULL != d->dead_cb) | ||
641 | d->dead_cb (d->dead_cb_cls, | ||
642 | _ | ||
643 | ("either `gnunet-arm' or `ssh' does not seem to terminate.\n")); | ||
644 | return; | ||
645 | } | ||
646 | /* wait some more */ | ||
647 | d->task = | ||
648 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
649 | d); | ||
650 | return; | ||
651 | } | ||
652 | #if EXTRA_CHECKS | ||
653 | if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0)) | ||
654 | { | ||
655 | if (NULL != d->dead_cb) | ||
656 | d->dead_cb (d->dead_cb_cls, | ||
657 | _ | ||
658 | ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n")); | ||
659 | return; | ||
660 | } | ||
661 | #endif | ||
662 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); | ||
663 | if (NULL != d->dead_cb) | ||
664 | d->dead_cb (d->dead_cb_cls, NULL); | ||
665 | break; | ||
666 | case SP_SHUTDOWN_START: | ||
667 | /* confirm copying complete !??? */ | ||
668 | if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_stop, &type, &code)) | ||
669 | { | ||
670 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) | ||
671 | { | ||
672 | if (NULL != d->dead_cb) | ||
673 | d->dead_cb (d->dead_cb_cls, | ||
674 | _ | ||
675 | ("either `gnunet-arm' or `ssh' does not seem to terminate.\n")); | ||
676 | if (d->th != NULL) | ||
677 | { | ||
678 | GNUNET_TRANSPORT_get_hello_cancel (d->ghh); | ||
679 | d->ghh = NULL; | ||
680 | GNUNET_TRANSPORT_disconnect (d->th); | ||
681 | d->th = NULL; | ||
682 | } | ||
683 | if (d->cfg != NULL) | ||
684 | { | ||
685 | GNUNET_CONFIGURATION_destroy (d->cfg); | ||
686 | d->cfg = NULL; | ||
687 | } | ||
688 | if (d->cfgfile != NULL) | ||
689 | { | ||
690 | GNUNET_free (d->cfgfile); | ||
691 | d->cfgfile = NULL; | ||
692 | } | ||
693 | GNUNET_free_non_null (d->hello); | ||
694 | GNUNET_free_non_null (d->hostname); | ||
695 | GNUNET_free_non_null (d->username); | ||
696 | GNUNET_free_non_null (d->shortname); | ||
697 | GNUNET_OS_process_destroy (d->proc_arm_stop); | ||
698 | d->proc_arm_stop = NULL; | ||
699 | GNUNET_free (d); | ||
700 | return; | ||
701 | } | ||
702 | /* wait some more */ | ||
703 | d->task = | ||
704 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
705 | d); | ||
706 | return; | ||
707 | } | ||
708 | if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0)) | ||
709 | { | ||
710 | if (NULL != d->dead_cb) | ||
711 | d->dead_cb (d->dead_cb_cls, | ||
712 | _ | ||
713 | ("shutdown (either `gnunet-arm' or `ssh') did not complete cleanly.\n")); | ||
714 | if (d->th != NULL) | ||
715 | { | ||
716 | GNUNET_TRANSPORT_get_hello_cancel (d->ghh); | ||
717 | d->ghh = NULL; | ||
718 | GNUNET_TRANSPORT_disconnect (d->th); | ||
719 | d->th = NULL; | ||
720 | } | ||
721 | if (d->server != NULL) | ||
722 | { | ||
723 | GNUNET_CORE_disconnect (d->server); | ||
724 | d->server = NULL; | ||
725 | } | ||
726 | GNUNET_CONFIGURATION_destroy (d->cfg); | ||
727 | d->cfg = NULL; | ||
728 | GNUNET_free (d->cfgfile); | ||
729 | GNUNET_free_non_null (d->hello); | ||
730 | GNUNET_free_non_null (d->hostname); | ||
731 | GNUNET_free_non_null (d->username); | ||
732 | GNUNET_free_non_null (d->shortname); | ||
733 | GNUNET_OS_process_destroy (d->proc_arm_stop); | ||
734 | d->proc_arm_stop = NULL; | ||
735 | GNUNET_free (d); | ||
736 | return; | ||
737 | } | ||
738 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer shutdown complete.\n"); | ||
739 | if (d->server != NULL) | ||
740 | { | ||
741 | GNUNET_CORE_disconnect (d->server); | ||
742 | d->server = NULL; | ||
743 | } | ||
744 | |||
745 | if (d->th != NULL) | ||
746 | { | ||
747 | GNUNET_TRANSPORT_get_hello_cancel (d->ghh); | ||
748 | d->ghh = NULL; | ||
749 | GNUNET_TRANSPORT_disconnect (d->th); | ||
750 | d->th = NULL; | ||
751 | } | ||
752 | |||
753 | if (NULL != d->dead_cb) | ||
754 | d->dead_cb (d->dead_cb_cls, NULL); | ||
755 | |||
756 | /* state clean up and notifications */ | ||
757 | if (d->churn == GNUNET_NO) | ||
758 | { | ||
759 | GNUNET_CONFIGURATION_destroy (d->cfg); | ||
760 | d->cfg = NULL; | ||
761 | GNUNET_free (d->cfgfile); | ||
762 | GNUNET_free_non_null (d->hostname); | ||
763 | GNUNET_free_non_null (d->username); | ||
764 | } | ||
765 | |||
766 | GNUNET_free_non_null (d->hello); | ||
767 | d->hello = NULL; | ||
768 | GNUNET_free_non_null (d->shortname); | ||
769 | GNUNET_OS_process_destroy (d->proc_arm_stop); | ||
770 | d->proc_arm_stop = NULL; | ||
771 | d->shortname = NULL; | ||
772 | if (d->churn == GNUNET_NO) | ||
773 | GNUNET_free (d); | ||
774 | |||
775 | break; | ||
776 | case SP_CONFIG_UPDATE: | ||
777 | /* confirm copying complete */ | ||
778 | if (GNUNET_OK != GNUNET_OS_process_status (d->proc_arm_copying, &type, &code)) | ||
779 | { | ||
780 | if (GNUNET_TIME_absolute_get_remaining (d->max_timeout).rel_value == 0) /* FIXME: config update should take timeout parameter! */ | ||
781 | { | ||
782 | cb = d->cb; | ||
783 | d->cb = NULL; | ||
784 | if (NULL != cb) | ||
785 | cb (d->cb_cls, NULL, d->cfg, d, | ||
786 | _("`scp' does not seem to terminate.\n")); | ||
787 | return; | ||
788 | } | ||
789 | /* wait some more */ | ||
790 | d->task = | ||
791 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
792 | d); | ||
793 | return; | ||
794 | } | ||
795 | if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0)) | ||
796 | { | ||
797 | if (NULL != d->update_cb) | ||
798 | d->update_cb (d->update_cb_cls, _("`scp' did not complete cleanly.\n")); | ||
799 | return; | ||
800 | } | ||
801 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
802 | "Successfully copied configuration file.\n"); | ||
803 | if (NULL != d->update_cb) | ||
804 | d->update_cb (d->update_cb_cls, NULL); | ||
805 | d->phase = SP_START_DONE; | ||
806 | break; | ||
807 | } | ||
808 | } | ||
809 | |||
810 | /** | ||
811 | * Continues GNUnet daemon startup when user wanted to be notified | ||
812 | * once a hostkey was generated (for creating friends files, blacklists, | ||
813 | * etc.). | ||
814 | * | ||
815 | * @param daemon the daemon to finish starting | ||
816 | */ | ||
817 | void | ||
818 | GNUNET_TESTING_daemon_continue_startup (struct GNUNET_TESTING_Daemon *daemon) | ||
819 | { | ||
820 | GNUNET_assert (daemon->phase == SP_HOSTKEY_CREATED); | ||
821 | daemon->phase = SP_TOPOLOGY_SETUP; | ||
822 | } | ||
823 | |||
824 | /** | ||
825 | * Check whether the given daemon is running. | ||
826 | * | ||
827 | * @param daemon the daemon to check | ||
828 | * | ||
829 | * @return GNUNET_YES if the daemon is up, GNUNET_NO if the | ||
830 | * daemon is down, GNUNET_SYSERR on error. | ||
831 | */ | ||
832 | int | ||
833 | GNUNET_TESTING_test_daemon_running (struct GNUNET_TESTING_Daemon *daemon) | ||
834 | { | ||
835 | if (daemon == NULL) | ||
836 | return GNUNET_SYSERR; | ||
837 | |||
838 | if (daemon->running == GNUNET_YES) | ||
839 | return GNUNET_YES; | ||
840 | return GNUNET_NO; | ||
841 | } | ||
842 | |||
843 | |||
844 | /** | ||
845 | * Starts a GNUnet daemon service which has been previously stopped. | ||
846 | * | ||
847 | * @param d the daemon for which the service should be started | ||
848 | * @param service the name of the service to start | ||
849 | * @param timeout how long to wait for process for shutdown to complete | ||
850 | * @param cb function called once the service starts | ||
851 | * @param cb_cls closure for cb | ||
852 | */ | ||
853 | void | ||
854 | GNUNET_TESTING_daemon_start_stopped_service (struct GNUNET_TESTING_Daemon *d, | ||
855 | char *service, | ||
856 | struct GNUNET_TIME_Relative | ||
857 | timeout, | ||
858 | GNUNET_TESTING_NotifyDaemonRunning | ||
859 | cb, void *cb_cls) | ||
860 | { | ||
861 | char *arg; | ||
862 | |||
863 | d->cb = cb; | ||
864 | d->cb_cls = cb_cls; | ||
865 | |||
866 | GNUNET_assert (d->running == GNUNET_YES); | ||
867 | |||
868 | if (d->phase == SP_CONFIG_UPDATE) | ||
869 | { | ||
870 | GNUNET_SCHEDULER_cancel (d->task); | ||
871 | d->phase = SP_START_DONE; | ||
872 | } | ||
873 | |||
874 | if (d->churned_services == NULL) | ||
875 | { | ||
876 | d->cb (d->cb_cls, &d->id, d->cfg, d, | ||
877 | "No service has been churned off yet!!"); | ||
878 | return; | ||
879 | } | ||
880 | d->phase = SP_SERVICE_START; | ||
881 | GNUNET_free (d->churned_services); | ||
882 | d->churned_services = NULL; | ||
883 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
884 | /* Check if this is a local or remote process */ | ||
885 | if (NULL != d->hostname) | ||
886 | { | ||
887 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
888 | "Starting gnunet-arm with config `%s' on host `%s'.\n", | ||
889 | d->cfgfile, d->hostname); | ||
890 | if (d->username != NULL) | ||
891 | GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname); | ||
892 | else | ||
893 | arg = GNUNET_strdup (d->hostname); | ||
894 | |||
895 | d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", | ||
896 | "-q", | ||
897 | arg, "gnunet-arm", | ||
898 | "-c", d->cfgfile, "-i", service, "-q", | ||
899 | "-T", | ||
900 | GNUNET_TIME_relative_to_string (timeout), | ||
901 | NULL); | ||
902 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
903 | "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q\n", | ||
904 | arg, "gnunet-arm", d->cfgfile, service); | ||
905 | GNUNET_free (arg); | ||
906 | } | ||
907 | else | ||
908 | { | ||
909 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
910 | "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile); | ||
911 | d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", | ||
912 | "-c", d->cfgfile, "-i", service, "-q", | ||
913 | "-T", | ||
914 | GNUNET_TIME_relative_to_string (timeout), | ||
915 | NULL); | ||
916 | } | ||
917 | |||
918 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
919 | d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d); | ||
920 | } | ||
921 | |||
922 | /** | ||
923 | * Starts a GNUnet daemon's service. | ||
924 | * | ||
925 | * @param d the daemon for which the service should be started | ||
926 | * @param service the name of the service to start | ||
927 | * @param timeout how long to wait for process for startup | ||
928 | * @param cb function called once gnunet-arm returns | ||
929 | * @param cb_cls closure for cb | ||
930 | */ | ||
931 | void | ||
932 | GNUNET_TESTING_daemon_start_service (struct GNUNET_TESTING_Daemon *d, | ||
933 | const char *service, | ||
934 | struct GNUNET_TIME_Relative timeout, | ||
935 | GNUNET_TESTING_NotifyDaemonRunning cb, | ||
936 | void *cb_cls) | ||
937 | { | ||
938 | char *arg; | ||
939 | |||
940 | d->cb = cb; | ||
941 | d->cb_cls = cb_cls; | ||
942 | |||
943 | GNUNET_assert (service != NULL); | ||
944 | GNUNET_assert (d->running == GNUNET_YES); | ||
945 | GNUNET_assert (d->phase == SP_START_DONE); | ||
946 | |||
947 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
948 | _("Starting service %s for peer `%4s'\n"), service, | ||
949 | GNUNET_i2s (&d->id)); | ||
950 | d->phase = SP_SERVICE_START; | ||
951 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
952 | /* Check if this is a local or remote process */ | ||
953 | if (NULL != d->hostname) | ||
954 | { | ||
955 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
956 | "Starting gnunet-arm with config `%s' on host `%s'.\n", | ||
957 | d->cfgfile, d->hostname); | ||
958 | if (d->username != NULL) | ||
959 | GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname); | ||
960 | else | ||
961 | arg = GNUNET_strdup (d->hostname); | ||
962 | |||
963 | d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", | ||
964 | "-q", | ||
965 | arg, "gnunet-arm", | ||
966 | "-c", d->cfgfile, "-i", service, "-q", | ||
967 | "-T", | ||
968 | GNUNET_TIME_relative_to_string (timeout), | ||
969 | NULL); | ||
970 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
971 | "Starting gnunet-arm with command ssh %s gnunet-arm -c %s -i %s -q -T %s\n", | ||
972 | arg, "gnunet-arm", d->cfgfile, service, | ||
973 | GNUNET_TIME_relative_to_string (timeout)); | ||
974 | GNUNET_free (arg); | ||
975 | } | ||
976 | else | ||
977 | { | ||
978 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
979 | "Starting gnunet-arm with config `%s' locally.\n", d->cfgfile); | ||
980 | d->proc_arm_srv_start = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", | ||
981 | "-c", d->cfgfile, "-i", service, "-q", | ||
982 | "-T", | ||
983 | GNUNET_TIME_relative_to_string (timeout), | ||
984 | NULL); | ||
985 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
986 | "Starting gnunet-arm with command %s -c %s -i %s -q -T %s\n", | ||
987 | "gnunet-arm", d->cfgfile, service, | ||
988 | GNUNET_TIME_relative_to_string (timeout)); | ||
989 | } | ||
990 | |||
991 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
992 | d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d); | ||
993 | } | ||
994 | |||
995 | /** | ||
996 | * Start a peer that has previously been stopped using the daemon_stop | ||
997 | * call (and files weren't deleted and the allow restart flag) | ||
998 | * | ||
999 | * @param daemon the daemon to start (has been previously stopped) | ||
1000 | * @param timeout how long to wait for restart | ||
1001 | * @param cb the callback for notification when the peer is running | ||
1002 | * @param cb_cls closure for the callback | ||
1003 | */ | ||
1004 | void | ||
1005 | GNUNET_TESTING_daemon_start_stopped (struct GNUNET_TESTING_Daemon *daemon, | ||
1006 | struct GNUNET_TIME_Relative timeout, | ||
1007 | GNUNET_TESTING_NotifyDaemonRunning cb, | ||
1008 | void *cb_cls) | ||
1009 | { | ||
1010 | if (daemon->running == GNUNET_YES) | ||
1011 | { | ||
1012 | cb (cb_cls, &daemon->id, daemon->cfg, daemon, | ||
1013 | "Daemon already running, can't restart!"); | ||
1014 | return; | ||
1015 | } | ||
1016 | |||
1017 | daemon->cb = cb; | ||
1018 | daemon->cb_cls = cb_cls; | ||
1019 | daemon->phase = SP_TOPOLOGY_SETUP; | ||
1020 | daemon->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1021 | /* FIXME: why add_continuation? */ | ||
1022 | GNUNET_SCHEDULER_add_continuation (&start_fsm, daemon, | ||
1023 | GNUNET_SCHEDULER_REASON_PREREQ_DONE); | ||
1024 | } | ||
1025 | |||
1026 | /** | ||
1027 | * Starts a GNUnet daemon. GNUnet must be installed on the target | ||
1028 | * system and available in the PATH. The machine must furthermore be | ||
1029 | * reachable via "ssh" (unless the hostname is "NULL") without the | ||
1030 | * need to enter a password. | ||
1031 | * | ||
1032 | * @param cfg configuration to use | ||
1033 | * @param timeout how long to wait starting up peers | ||
1034 | * @param pretend GNUNET_YES to set up files but not start peer GNUNET_NO | ||
1035 | * to really start the peer (default) | ||
1036 | * @param hostname name of the machine where to run GNUnet | ||
1037 | * (use NULL for localhost). | ||
1038 | * @param ssh_username ssh username to use when connecting to hostname | ||
1039 | * @param sshport port to pass to ssh process when connecting to hostname | ||
1040 | * @param hostkey pointer to a hostkey to be written to disk (instead of being generated) | ||
1041 | * @param hostkey_callback function to call once the hostkey has been | ||
1042 | * generated for this peer, but it hasn't yet been started | ||
1043 | * (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start) | ||
1044 | * @param hostkey_cls closure for hostkey callback | ||
1045 | * @param cb function to call once peer is up, or failed to start | ||
1046 | * @param cb_cls closure for cb | ||
1047 | * @return handle to the daemon (actual start will be completed asynchronously) | ||
1048 | */ | ||
1049 | struct GNUNET_TESTING_Daemon * | ||
1050 | GNUNET_TESTING_daemon_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1051 | struct GNUNET_TIME_Relative timeout, int pretend, | ||
1052 | const char *hostname, const char *ssh_username, | ||
1053 | uint16_t sshport, const char *hostkey, | ||
1054 | GNUNET_TESTING_NotifyHostkeyCreated | ||
1055 | hostkey_callback, void *hostkey_cls, | ||
1056 | GNUNET_TESTING_NotifyDaemonRunning cb, | ||
1057 | void *cb_cls) | ||
1058 | { | ||
1059 | struct GNUNET_TESTING_Daemon *ret; | ||
1060 | char *arg; | ||
1061 | char *username; | ||
1062 | char *servicehome; | ||
1063 | char *baseservicehome; | ||
1064 | char *slash; | ||
1065 | char *hostkeyfile; | ||
1066 | char *temp_file_name; | ||
1067 | struct GNUNET_DISK_FileHandle *fn; | ||
1068 | struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; | ||
1069 | struct GNUNET_CRYPTO_RsaPrivateKey *private_key; | ||
1070 | |||
1071 | ret = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Daemon)); | ||
1072 | ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname); | ||
1073 | if (sshport != 0) | ||
1074 | { | ||
1075 | GNUNET_asprintf (&ret->ssh_port_str, "%d", sshport); | ||
1076 | } | ||
1077 | else | ||
1078 | ret->ssh_port_str = NULL; | ||
1079 | |||
1080 | /* Find service home and base service home directories, create it if it doesn't exist */ | ||
1081 | GNUNET_assert (GNUNET_OK == | ||
1082 | GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", | ||
1083 | "SERVICEHOME", | ||
1084 | &servicehome)); | ||
1085 | |||
1086 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (servicehome)); | ||
1087 | GNUNET_asprintf (&temp_file_name, "%s/gnunet-testing-config", servicehome); | ||
1088 | ret->cfgfile = GNUNET_DISK_mktemp (temp_file_name); | ||
1089 | GNUNET_free (temp_file_name); | ||
1090 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1091 | "Setting up peer with configuration file `%s'.\n", ret->cfgfile); | ||
1092 | if (NULL == ret->cfgfile) | ||
1093 | { | ||
1094 | GNUNET_free_non_null (ret->ssh_port_str); | ||
1095 | GNUNET_free_non_null (ret->hostname); | ||
1096 | GNUNET_free (ret); | ||
1097 | return NULL; | ||
1098 | } | ||
1099 | ret->hostkey_callback = hostkey_callback; | ||
1100 | ret->hostkey_cls = hostkey_cls; | ||
1101 | ret->cb = cb; | ||
1102 | ret->cb_cls = cb_cls; | ||
1103 | ret->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1104 | ret->cfg = GNUNET_CONFIGURATION_dup (cfg); | ||
1105 | GNUNET_CONFIGURATION_set_value_string (ret->cfg, "PATHS", "DEFAULTCONFIG", | ||
1106 | ret->cfgfile); | ||
1107 | |||
1108 | if (hostkey != NULL) /* Get the peer identity from the hostkey */ | ||
1109 | { | ||
1110 | private_key = GNUNET_CRYPTO_rsa_decode_key (hostkey, HOSTKEYFILESIZE); | ||
1111 | GNUNET_assert (private_key != NULL); | ||
1112 | GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key); | ||
1113 | GNUNET_CRYPTO_hash (&public_key, | ||
1114 | sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), | ||
1115 | &ret->id.hashPubKey); | ||
1116 | ret->shortname = GNUNET_strdup (GNUNET_i2s (&ret->id)); | ||
1117 | ret->have_hostkey = GNUNET_YES; | ||
1118 | GNUNET_CRYPTO_rsa_key_free (private_key); | ||
1119 | } | ||
1120 | |||
1121 | /* Write hostkey to file, if we were given one */ | ||
1122 | hostkeyfile = NULL; | ||
1123 | if (hostkey != NULL) | ||
1124 | { | ||
1125 | GNUNET_asprintf (&hostkeyfile, "%s/.hostkey", servicehome); | ||
1126 | fn = GNUNET_DISK_file_open (hostkeyfile, | ||
1127 | GNUNET_DISK_OPEN_READWRITE | | ||
1128 | GNUNET_DISK_OPEN_CREATE, | ||
1129 | GNUNET_DISK_PERM_USER_READ | | ||
1130 | GNUNET_DISK_PERM_USER_WRITE); | ||
1131 | GNUNET_assert (fn != NULL); | ||
1132 | GNUNET_assert (HOSTKEYFILESIZE == | ||
1133 | GNUNET_DISK_file_write (fn, hostkey, HOSTKEYFILESIZE)); | ||
1134 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fn)); | ||
1135 | } | ||
1136 | |||
1137 | /* write configuration to temporary file */ | ||
1138 | if (GNUNET_OK != GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile)) | ||
1139 | { | ||
1140 | if (0 != UNLINK (ret->cfgfile)) | ||
1141 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", | ||
1142 | ret->cfgfile); | ||
1143 | GNUNET_CONFIGURATION_destroy (ret->cfg); | ||
1144 | GNUNET_free_non_null (ret->hostname); | ||
1145 | GNUNET_free (ret->cfgfile); | ||
1146 | GNUNET_free (ret); | ||
1147 | return NULL; | ||
1148 | } | ||
1149 | if (ssh_username != NULL) | ||
1150 | username = GNUNET_strdup (ssh_username); | ||
1151 | if ((ssh_username == NULL) && | ||
1152 | (GNUNET_OK != | ||
1153 | GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING_OLD", "USERNAME", | ||
1154 | &username))) | ||
1155 | { | ||
1156 | if (NULL != getenv ("USER")) | ||
1157 | username = GNUNET_strdup (getenv ("USER")); | ||
1158 | else | ||
1159 | username = NULL; | ||
1160 | } | ||
1161 | ret->username = username; | ||
1162 | |||
1163 | if (GNUNET_NO == pretend) /* Copy files, enter finite state machine */ | ||
1164 | { | ||
1165 | /* copy directory to remote host */ | ||
1166 | if (NULL != hostname) | ||
1167 | { | ||
1168 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1169 | "Copying configuration directory to host `%s'.\n", hostname); | ||
1170 | baseservicehome = GNUNET_strdup (servicehome); | ||
1171 | /* Remove trailing /'s */ | ||
1172 | while (baseservicehome[strlen (baseservicehome) - 1] == '/') | ||
1173 | baseservicehome[strlen (baseservicehome) - 1] = '\0'; | ||
1174 | /* Find next directory /, jump one ahead */ | ||
1175 | slash = strrchr (baseservicehome, '/'); | ||
1176 | if (slash != NULL) | ||
1177 | *(++slash) = '\0'; | ||
1178 | |||
1179 | ret->phase = SP_COPYING; | ||
1180 | if (NULL != username) | ||
1181 | GNUNET_asprintf (&arg, "%s@%s:%s", username, hostname, baseservicehome); | ||
1182 | else | ||
1183 | GNUNET_asprintf (&arg, "%s:%s", hostname, baseservicehome); | ||
1184 | GNUNET_free (baseservicehome); | ||
1185 | if (ret->ssh_port_str == NULL) | ||
1186 | { | ||
1187 | ret->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r", | ||
1188 | "-q", | ||
1189 | servicehome, arg, NULL); | ||
1190 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1191 | "copying directory with command scp -r %s %s\n", | ||
1192 | servicehome, arg); | ||
1193 | } | ||
1194 | else | ||
1195 | { | ||
1196 | ret->proc_arm_copying = | ||
1197 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", "-r", "-P", | ||
1198 | ret->ssh_port_str, | ||
1199 | "-q", | ||
1200 | servicehome, arg, NULL); | ||
1201 | } | ||
1202 | GNUNET_free (arg); | ||
1203 | if (NULL == ret->proc_arm_copying) | ||
1204 | { | ||
1205 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1206 | _ | ||
1207 | ("Could not start `%s' process to copy configuration directory.\n"), | ||
1208 | "scp"); | ||
1209 | if (0 != UNLINK (ret->cfgfile)) | ||
1210 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", | ||
1211 | ret->cfgfile); | ||
1212 | GNUNET_CONFIGURATION_destroy (ret->cfg); | ||
1213 | GNUNET_free_non_null (ret->hostname); | ||
1214 | GNUNET_free_non_null (ret->username); | ||
1215 | GNUNET_free (ret->cfgfile); | ||
1216 | GNUNET_free (ret); | ||
1217 | if ((hostkey != NULL) && (0 != UNLINK (hostkeyfile))) | ||
1218 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", | ||
1219 | hostkeyfile); | ||
1220 | GNUNET_free_non_null (hostkeyfile); | ||
1221 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (servicehome)); | ||
1222 | GNUNET_free (servicehome); | ||
1223 | return NULL; | ||
1224 | } | ||
1225 | |||
1226 | ret->task = | ||
1227 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, | ||
1228 | ret); | ||
1229 | GNUNET_free_non_null (hostkeyfile); | ||
1230 | GNUNET_free (servicehome); | ||
1231 | return ret; | ||
1232 | } | ||
1233 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1234 | "No need to copy configuration file since we are running locally.\n"); | ||
1235 | ret->phase = SP_COPIED; | ||
1236 | /* FIXME: why add_cont? */ | ||
1237 | GNUNET_SCHEDULER_add_continuation (&start_fsm, ret, | ||
1238 | GNUNET_SCHEDULER_REASON_PREREQ_DONE); | ||
1239 | } | ||
1240 | GNUNET_free_non_null (hostkeyfile); | ||
1241 | GNUNET_free (servicehome); | ||
1242 | return ret; | ||
1243 | } | ||
1244 | |||
1245 | |||
1246 | /** | ||
1247 | * Restart (stop and start) a GNUnet daemon. | ||
1248 | * | ||
1249 | * @param d the daemon that should be restarted | ||
1250 | * @param cb function called once the daemon is (re)started | ||
1251 | * @param cb_cls closure for cb | ||
1252 | */ | ||
1253 | void | ||
1254 | GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d, | ||
1255 | GNUNET_TESTING_NotifyDaemonRunning cb, | ||
1256 | void *cb_cls) | ||
1257 | { | ||
1258 | char *arg; | ||
1259 | char *del_arg; | ||
1260 | |||
1261 | del_arg = NULL; | ||
1262 | if (NULL != d->cb) | ||
1263 | { | ||
1264 | d->dead = GNUNET_YES; | ||
1265 | return; | ||
1266 | } | ||
1267 | |||
1268 | d->cb = cb; | ||
1269 | d->cb_cls = cb_cls; | ||
1270 | |||
1271 | if (d->phase == SP_CONFIG_UPDATE) | ||
1272 | { | ||
1273 | GNUNET_SCHEDULER_cancel (d->task); | ||
1274 | d->phase = SP_START_DONE; | ||
1275 | } | ||
1276 | if (d->server != NULL) | ||
1277 | { | ||
1278 | GNUNET_CORE_disconnect (d->server); | ||
1279 | d->server = NULL; | ||
1280 | } | ||
1281 | |||
1282 | if (d->th != NULL) | ||
1283 | { | ||
1284 | GNUNET_TRANSPORT_get_hello_cancel (d->ghh); | ||
1285 | d->ghh = NULL; | ||
1286 | GNUNET_TRANSPORT_disconnect (d->th); | ||
1287 | d->th = NULL; | ||
1288 | } | ||
1289 | /* state clean up and notifications */ | ||
1290 | GNUNET_free_non_null (d->hello); | ||
1291 | |||
1292 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"), | ||
1293 | GNUNET_i2s (&d->id)); | ||
1294 | d->phase = SP_START_ARMING; | ||
1295 | |||
1296 | /* Check if this is a local or remote process */ | ||
1297 | if (NULL != d->hostname) | ||
1298 | { | ||
1299 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1300 | "Stopping gnunet-arm with config `%s' on host `%s'.\n", | ||
1301 | d->cfgfile, d->hostname); | ||
1302 | if (d->username != NULL) | ||
1303 | GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname); | ||
1304 | else | ||
1305 | arg = GNUNET_strdup (d->hostname); | ||
1306 | |||
1307 | d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", | ||
1308 | "-q", | ||
1309 | arg, "gnunet-arm", | ||
1310 | "-c", d->cfgfile, "-e", "-r", NULL); | ||
1311 | /* Use -r to restart arm and all services */ | ||
1312 | |||
1313 | GNUNET_free (arg); | ||
1314 | } | ||
1315 | else | ||
1316 | { | ||
1317 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1318 | "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile); | ||
1319 | d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", | ||
1320 | "-c", d->cfgfile, "-e", "-r", NULL); | ||
1321 | } | ||
1322 | |||
1323 | GNUNET_free_non_null (del_arg); | ||
1324 | d->task = | ||
1325 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d); | ||
1326 | |||
1327 | } | ||
1328 | |||
1329 | |||
1330 | /** | ||
1331 | * Stops a GNUnet daemon. | ||
1332 | * | ||
1333 | * @param d the daemon that should be stopped | ||
1334 | * @param service the name of the service to stop | ||
1335 | * @param timeout how long to wait for process for shutdown to complete | ||
1336 | * @param cb function called once the daemon was stopped | ||
1337 | * @param cb_cls closure for cb | ||
1338 | */ | ||
1339 | void | ||
1340 | GNUNET_TESTING_daemon_stop_service (struct GNUNET_TESTING_Daemon *d, | ||
1341 | const char *service, | ||
1342 | struct GNUNET_TIME_Relative timeout, | ||
1343 | GNUNET_TESTING_NotifyCompletion cb, | ||
1344 | void *cb_cls) | ||
1345 | { | ||
1346 | char *arg; | ||
1347 | |||
1348 | d->dead_cb = cb; | ||
1349 | d->dead_cb_cls = cb_cls; | ||
1350 | |||
1351 | GNUNET_assert (d->running == GNUNET_YES); | ||
1352 | |||
1353 | if (d->phase == SP_CONFIG_UPDATE) | ||
1354 | { | ||
1355 | GNUNET_SCHEDULER_cancel (d->task); | ||
1356 | d->phase = SP_START_DONE; | ||
1357 | } | ||
1358 | |||
1359 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"), | ||
1360 | GNUNET_i2s (&d->id)); | ||
1361 | if (d->churned_services != NULL) | ||
1362 | { | ||
1363 | d->dead_cb (d->dead_cb_cls, "A service has already been turned off!!"); | ||
1364 | return; | ||
1365 | } | ||
1366 | d->phase = SP_SERVICE_SHUTDOWN_START; | ||
1367 | d->churned_services = GNUNET_strdup (service); | ||
1368 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1369 | /* Check if this is a local or remote process */ | ||
1370 | if (NULL != d->hostname) | ||
1371 | { | ||
1372 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1373 | "Stopping gnunet-arm with config `%s' on host `%s'.\n", | ||
1374 | d->cfgfile, d->hostname); | ||
1375 | if (d->username != NULL) | ||
1376 | GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname); | ||
1377 | else | ||
1378 | arg = GNUNET_strdup (d->hostname); | ||
1379 | |||
1380 | d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", | ||
1381 | "-q", | ||
1382 | arg, "gnunet-arm", | ||
1383 | "-c", d->cfgfile, "-k", service, "-q", | ||
1384 | "-T", | ||
1385 | GNUNET_TIME_relative_to_string (timeout), | ||
1386 | NULL); | ||
1387 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1388 | "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -k %s -q\n", | ||
1389 | arg, "gnunet-arm", d->cfgfile, service); | ||
1390 | GNUNET_free (arg); | ||
1391 | } | ||
1392 | else | ||
1393 | { | ||
1394 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1395 | "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile); | ||
1396 | d->proc_arm_srv_stop = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-arm", "gnunet-arm", | ||
1397 | "-c", d->cfgfile, "-k", service, "-q", | ||
1398 | "-T", | ||
1399 | GNUNET_TIME_relative_to_string (timeout), | ||
1400 | NULL); | ||
1401 | } | ||
1402 | |||
1403 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1404 | d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d); | ||
1405 | } | ||
1406 | |||
1407 | |||
1408 | /** | ||
1409 | * Forcefully terminate a process and clean up the child. | ||
1410 | * | ||
1411 | * @param proc handle to process to kill | ||
1412 | */ | ||
1413 | static void | ||
1414 | kill_and_close_process (struct GNUNET_OS_Process *proc) | ||
1415 | { | ||
1416 | (void) GNUNET_OS_process_kill (proc, SIGKILL); | ||
1417 | GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (proc)); | ||
1418 | GNUNET_OS_process_destroy (proc); | ||
1419 | } | ||
1420 | |||
1421 | |||
1422 | /** | ||
1423 | * Stops a GNUnet daemon. | ||
1424 | * | ||
1425 | * @param d the daemon that should be stopped | ||
1426 | * @param timeout how long to wait for process for shutdown to complete | ||
1427 | * @param cb function called once the daemon was stopped | ||
1428 | * @param cb_cls closure for cb | ||
1429 | * @param delete_files GNUNET_YES to remove files, GNUNET_NO | ||
1430 | * to leave them | ||
1431 | * @param allow_restart GNUNET_YES to restart peer later (using this API) | ||
1432 | * GNUNET_NO to kill off and clean up for good | ||
1433 | */ | ||
1434 | void | ||
1435 | GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, | ||
1436 | struct GNUNET_TIME_Relative timeout, | ||
1437 | GNUNET_TESTING_NotifyCompletion cb, void *cb_cls, | ||
1438 | int delete_files, int allow_restart) | ||
1439 | { | ||
1440 | char *arg; | ||
1441 | char *del_arg; | ||
1442 | |||
1443 | d->dead_cb = cb; | ||
1444 | d->dead_cb_cls = cb_cls; | ||
1445 | |||
1446 | if (NULL != d->cb) | ||
1447 | { | ||
1448 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Setting d->dead on peer `%4s'\n"), | ||
1449 | GNUNET_i2s (&d->id)); | ||
1450 | d->dead = GNUNET_YES; | ||
1451 | return; | ||
1452 | } | ||
1453 | if (NULL != d->proc_arm_start) | ||
1454 | { | ||
1455 | kill_and_close_process (d->proc_arm_start); | ||
1456 | d->proc_arm_start = NULL; | ||
1457 | } | ||
1458 | if (NULL != d->proc_arm_srv_start) | ||
1459 | { | ||
1460 | kill_and_close_process (d->proc_arm_srv_start); | ||
1461 | d->proc_arm_srv_start = NULL; | ||
1462 | } | ||
1463 | if (NULL != d->proc_arm_srv_stop) | ||
1464 | { | ||
1465 | kill_and_close_process (d->proc_arm_srv_stop); | ||
1466 | d->proc_arm_srv_stop = NULL; | ||
1467 | } | ||
1468 | if (NULL != d->proc_arm_copying) | ||
1469 | { | ||
1470 | kill_and_close_process (d->proc_arm_copying); | ||
1471 | d->proc_arm_copying = NULL; | ||
1472 | } | ||
1473 | if (NULL != d->proc_arm_peerinfo) | ||
1474 | { | ||
1475 | kill_and_close_process (d->proc_arm_peerinfo); | ||
1476 | d->proc_arm_peerinfo = NULL; | ||
1477 | } | ||
1478 | if ((d->running == GNUNET_NO) && (d->churn == GNUNET_YES)) | ||
1479 | { | ||
1480 | /* Peer has already been stopped in churn context! | ||
1481 | * Free what was left from churning! */ | ||
1482 | GNUNET_assert (d->cfg != NULL); | ||
1483 | GNUNET_CONFIGURATION_destroy (d->cfg); | ||
1484 | if (delete_files == GNUNET_YES) | ||
1485 | { | ||
1486 | if (0 != UNLINK (d->cfgfile)) | ||
1487 | { | ||
1488 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "unlink"); | ||
1489 | } | ||
1490 | } | ||
1491 | GNUNET_free (d->cfgfile); | ||
1492 | GNUNET_free_non_null (d->hostname); | ||
1493 | GNUNET_free_non_null (d->username); | ||
1494 | if (NULL != d->dead_cb) | ||
1495 | d->dead_cb (d->dead_cb_cls, NULL); | ||
1496 | /* FIXME: this should be an assert and the test below | ||
1497 | should not be required, but testing is broken... */ | ||
1498 | GNUNET_break (NULL == d->proc_arm_stop); | ||
1499 | if (NULL == d->proc_arm_stop) | ||
1500 | GNUNET_free (d); | ||
1501 | return; | ||
1502 | } | ||
1503 | |||
1504 | del_arg = NULL; | ||
1505 | if (delete_files == GNUNET_YES) | ||
1506 | { | ||
1507 | GNUNET_asprintf (&del_arg, "-d"); | ||
1508 | } | ||
1509 | |||
1510 | if (d->phase == SP_CONFIG_UPDATE) | ||
1511 | { | ||
1512 | GNUNET_SCHEDULER_cancel (d->task); | ||
1513 | d->phase = SP_START_DONE; | ||
1514 | } | ||
1515 | /** Move this call to scheduled shutdown as fix for CORE_connect calling daemon_stop? | ||
1516 | if (d->server != NULL) | ||
1517 | { | ||
1518 | GNUNET_CORE_disconnect (d->server); | ||
1519 | d->server = NULL; | ||
1520 | } | ||
1521 | */ | ||
1522 | /* shutdown ARM process (will terminate others) */ | ||
1523 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Terminating peer `%4s'\n" , | ||
1524 | GNUNET_i2s (&d->id)); | ||
1525 | d->phase = SP_SHUTDOWN_START; | ||
1526 | d->running = GNUNET_NO; | ||
1527 | if (allow_restart == GNUNET_YES) | ||
1528 | d->churn = GNUNET_YES; | ||
1529 | if (d->th != NULL) | ||
1530 | { | ||
1531 | GNUNET_TRANSPORT_get_hello_cancel (d->ghh); | ||
1532 | d->ghh = NULL; | ||
1533 | GNUNET_TRANSPORT_disconnect (d->th); | ||
1534 | d->th = NULL; | ||
1535 | } | ||
1536 | /* Check if this is a local or remote process */ | ||
1537 | |||
1538 | |||
1539 | if (NULL != d->hostname) | ||
1540 | { | ||
1541 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1542 | "Stopping gnunet-arm with config `%s' on host `%s'.\n", | ||
1543 | d->cfgfile, d->hostname); | ||
1544 | if (d->username != NULL) | ||
1545 | GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname); | ||
1546 | else | ||
1547 | arg = GNUNET_strdup (d->hostname); | ||
1548 | |||
1549 | d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", | ||
1550 | "-q", | ||
1551 | arg, "gnunet-arm", | ||
1552 | "-c", d->cfgfile, "-e", "-q", "-T", | ||
1553 | GNUNET_TIME_relative_to_string (timeout), | ||
1554 | del_arg, NULL); | ||
1555 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1556 | "Stopping gnunet-arm with command ssh %s gnunet-arm -c %s -e -q %s\n", | ||
1557 | arg, "gnunet-arm", d->cfgfile, del_arg); | ||
1558 | /* Use -e to end arm, and -d to remove temp files */ | ||
1559 | GNUNET_free (arg); | ||
1560 | } | ||
1561 | else | ||
1562 | { | ||
1563 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1564 | "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile); | ||
1565 | d->proc_arm_stop = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "gnunet-arm", "gnunet-arm", | ||
1566 | "-c", d->cfgfile, "-e", "-q", "-T", | ||
1567 | GNUNET_TIME_relative_to_string (timeout), | ||
1568 | del_arg, NULL); | ||
1569 | GNUNET_assert (NULL != d->proc_arm_stop); | ||
1570 | } | ||
1571 | |||
1572 | GNUNET_free_non_null (del_arg); | ||
1573 | d->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1574 | if (GNUNET_SCHEDULER_NO_TASK != d->task) | ||
1575 | GNUNET_SCHEDULER_cancel(d->task); | ||
1576 | d->task = GNUNET_SCHEDULER_add_now (&start_fsm, d); | ||
1577 | } | ||
1578 | |||
1579 | |||
1580 | /** | ||
1581 | * Changes the configuration of a GNUnet daemon. | ||
1582 | * | ||
1583 | * @param d the daemon that should be modified | ||
1584 | * @param cfg the new configuration for the daemon | ||
1585 | * @param cb function called once the configuration was changed | ||
1586 | * @param cb_cls closure for cb | ||
1587 | */ | ||
1588 | void | ||
1589 | GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, | ||
1590 | struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1591 | GNUNET_TESTING_NotifyCompletion cb, | ||
1592 | void *cb_cls) | ||
1593 | { | ||
1594 | char *arg; | ||
1595 | |||
1596 | if (d->phase != SP_START_DONE) | ||
1597 | { | ||
1598 | if (NULL != cb) | ||
1599 | cb (cb_cls, | ||
1600 | _ | ||
1601 | ("Peer not yet running, can not change configuration at this point.")); | ||
1602 | return; | ||
1603 | } | ||
1604 | |||
1605 | /* 1) write configuration to temporary file */ | ||
1606 | if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, d->cfgfile)) | ||
1607 | { | ||
1608 | if (NULL != cb) | ||
1609 | cb (cb_cls, _("Failed to write new configuration to disk.")); | ||
1610 | return; | ||
1611 | } | ||
1612 | |||
1613 | /* 2) copy file to remote host (if necessary) */ | ||
1614 | if (NULL == d->hostname) | ||
1615 | { | ||
1616 | /* signal success */ | ||
1617 | if (NULL != cb) | ||
1618 | cb (cb_cls, NULL); | ||
1619 | return; | ||
1620 | } | ||
1621 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1622 | "Copying updated configuration file to remote host `%s'.\n", | ||
1623 | d->hostname); | ||
1624 | d->phase = SP_CONFIG_UPDATE; | ||
1625 | if (NULL != d->username) | ||
1626 | GNUNET_asprintf (&arg, "%s@%s:%s", d->username, d->hostname, d->cfgfile); | ||
1627 | else | ||
1628 | GNUNET_asprintf (&arg, "%s:%s", d->hostname, d->cfgfile); | ||
1629 | d->proc_arm_copying = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", | ||
1630 | "-q", | ||
1631 | d->cfgfile, arg, NULL); | ||
1632 | GNUNET_free (arg); | ||
1633 | if (NULL == d->proc_arm_copying) | ||
1634 | { | ||
1635 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1636 | _("Could not start `%s' process to copy configuration file.\n"), | ||
1637 | "scp"); | ||
1638 | if (NULL != cb) | ||
1639 | cb (cb_cls, _("Failed to copy new configuration to remote machine.")); | ||
1640 | d->phase = SP_START_DONE; | ||
1641 | return; | ||
1642 | } | ||
1643 | d->update_cb = cb; | ||
1644 | d->update_cb_cls = cb_cls; | ||
1645 | d->task = | ||
1646 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, &start_fsm, d); | ||
1647 | } | ||
1648 | |||
1649 | |||
1650 | /** | ||
1651 | * Data kept for each pair of peers that we try | ||
1652 | * to connect. | ||
1653 | */ | ||
1654 | struct GNUNET_TESTING_ConnectContext | ||
1655 | { | ||
1656 | /** | ||
1657 | * Testing handle to the first daemon. | ||
1658 | */ | ||
1659 | struct GNUNET_TESTING_Daemon *d1; | ||
1660 | |||
1661 | /** | ||
1662 | * Handle to core of first daemon (to check connect) | ||
1663 | */ | ||
1664 | struct GNUNET_CORE_Handle *d1core; | ||
1665 | |||
1666 | /** | ||
1667 | * Have we actually connected to the core of the first daemon yet? | ||
1668 | */ | ||
1669 | int d1core_ready; | ||
1670 | |||
1671 | /** | ||
1672 | * Testing handle to the second daemon. | ||
1673 | */ | ||
1674 | struct GNUNET_TESTING_Daemon *d2; | ||
1675 | |||
1676 | /** | ||
1677 | * Transport handle to the first daemon (to offer the HELLO of the second daemon to). | ||
1678 | */ | ||
1679 | struct GNUNET_TRANSPORT_Handle *d1th; | ||
1680 | |||
1681 | /** | ||
1682 | * Function to call once we are done (or have timed out). | ||
1683 | */ | ||
1684 | GNUNET_TESTING_NotifyConnection cb; | ||
1685 | |||
1686 | /** | ||
1687 | * Closure for "nb". | ||
1688 | */ | ||
1689 | void *cb_cls; | ||
1690 | |||
1691 | /** | ||
1692 | * The relative timeout from whence this connect attempt was | ||
1693 | * started. Allows for reconnect attempts. | ||
1694 | */ | ||
1695 | struct GNUNET_TIME_Relative relative_timeout; | ||
1696 | |||
1697 | /** | ||
1698 | * Maximum number of connect attempts, will retry connection | ||
1699 | * this number of times on failures. | ||
1700 | */ | ||
1701 | unsigned int connect_attempts; | ||
1702 | |||
1703 | /** | ||
1704 | * Hello timeout task | ||
1705 | */ | ||
1706 | GNUNET_SCHEDULER_TaskIdentifier hello_send_task; | ||
1707 | |||
1708 | /** | ||
1709 | * Connect timeout task | ||
1710 | */ | ||
1711 | GNUNET_SCHEDULER_TaskIdentifier timeout_task; | ||
1712 | |||
1713 | /** | ||
1714 | * When should this operation be complete (or we must trigger | ||
1715 | * a timeout). | ||
1716 | */ | ||
1717 | struct GNUNET_TIME_Relative timeout_hello; | ||
1718 | |||
1719 | /** | ||
1720 | * Was the connection attempt successful? | ||
1721 | */ | ||
1722 | int connected; | ||
1723 | |||
1724 | /** | ||
1725 | * When connecting, do we need to send the HELLO? | ||
1726 | */ | ||
1727 | int send_hello; | ||
1728 | |||
1729 | /** | ||
1730 | * The distance between the two connected peers | ||
1731 | */ | ||
1732 | uint32_t distance; | ||
1733 | }; | ||
1734 | |||
1735 | |||
1736 | /** Forward declaration **/ | ||
1737 | static void | ||
1738 | reattempt_daemons_connect (void *cls, | ||
1739 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
1740 | |||
1741 | |||
1742 | /** | ||
1743 | * Notify callback about success or failure of the attempt | ||
1744 | * to connect the two peers | ||
1745 | * | ||
1746 | * @param cls our "struct GNUNET_TESTING_ConnectContext" (freed) | ||
1747 | * @param tc reason tells us if we succeeded or failed | ||
1748 | */ | ||
1749 | static void | ||
1750 | notify_connect_result (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1751 | { | ||
1752 | struct GNUNET_TESTING_ConnectContext *ctx = cls; | ||
1753 | |||
1754 | ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK; | ||
1755 | if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK) | ||
1756 | { | ||
1757 | GNUNET_SCHEDULER_cancel (ctx->hello_send_task); | ||
1758 | ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK; | ||
1759 | } | ||
1760 | |||
1761 | if (ctx->d1th != NULL) | ||
1762 | GNUNET_TRANSPORT_disconnect (ctx->d1th); | ||
1763 | ctx->d1th = NULL; | ||
1764 | if (ctx->d1core != NULL) | ||
1765 | GNUNET_CORE_disconnect (ctx->d1core); | ||
1766 | ctx->d1core = NULL; | ||
1767 | |||
1768 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
1769 | { | ||
1770 | GNUNET_free (ctx); | ||
1771 | return; | ||
1772 | } | ||
1773 | |||
1774 | if (ctx->connected == GNUNET_YES) | ||
1775 | { | ||
1776 | if (ctx->cb != NULL) | ||
1777 | { | ||
1778 | ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, ctx->distance, | ||
1779 | ctx->d1->cfg, ctx->d2->cfg, ctx->d1, ctx->d2, NULL); | ||
1780 | } | ||
1781 | } | ||
1782 | else if (ctx->connect_attempts > 0) | ||
1783 | { | ||
1784 | ctx->d1core_ready = GNUNET_NO; | ||
1785 | ctx->timeout_task = | ||
1786 | GNUNET_SCHEDULER_add_now (&reattempt_daemons_connect, ctx); | ||
1787 | return; | ||
1788 | } | ||
1789 | else | ||
1790 | { | ||
1791 | if (ctx->cb != NULL) | ||
1792 | { | ||
1793 | ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg, | ||
1794 | ctx->d2->cfg, ctx->d1, ctx->d2, _("Peers failed to connect")); | ||
1795 | } | ||
1796 | } | ||
1797 | GNUNET_free (ctx); | ||
1798 | } | ||
1799 | |||
1800 | |||
1801 | /** | ||
1802 | * Success, connection is up. Signal client our success. | ||
1803 | * | ||
1804 | * @param cls our "struct GNUNET_TESTING_ConnectContext" | ||
1805 | * @param peer identity of the peer that has connected | ||
1806 | * @param atsi performance information | ||
1807 | * @param atsi_count number of records in 'atsi' | ||
1808 | * | ||
1809 | */ | ||
1810 | static void | ||
1811 | connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
1812 | const struct GNUNET_ATS_Information *atsi, | ||
1813 | unsigned int atsi_count) | ||
1814 | { | ||
1815 | struct GNUNET_TESTING_ConnectContext *ctx = cls; | ||
1816 | |||
1817 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected peer %s to peer %s\n", | ||
1818 | ctx->d1->shortname, GNUNET_i2s (peer)); | ||
1819 | if (0 != memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity))) | ||
1820 | return; | ||
1821 | ctx->connected = GNUNET_YES; | ||
1822 | ctx->distance = 0; /* FIXME: distance */ | ||
1823 | if (ctx->hello_send_task != GNUNET_SCHEDULER_NO_TASK) | ||
1824 | { | ||
1825 | GNUNET_SCHEDULER_cancel (ctx->hello_send_task); | ||
1826 | ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK; | ||
1827 | } | ||
1828 | GNUNET_SCHEDULER_cancel (ctx->timeout_task); | ||
1829 | ctx->timeout_task = GNUNET_SCHEDULER_add_now (¬ify_connect_result, ctx); | ||
1830 | } | ||
1831 | |||
1832 | |||
1833 | static void | ||
1834 | send_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1835 | { | ||
1836 | struct GNUNET_TESTING_ConnectContext *ctx = cls; | ||
1837 | struct GNUNET_MessageHeader *hello; | ||
1838 | |||
1839 | ctx->hello_send_task = GNUNET_SCHEDULER_NO_TASK; | ||
1840 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
1841 | return; | ||
1842 | if ((ctx->d1core_ready == GNUNET_YES) && (ctx->d2->hello != NULL) && | ||
1843 | (NULL != GNUNET_HELLO_get_header (ctx->d2->hello)) && | ||
1844 | (ctx->d1->phase == SP_START_DONE) && (ctx->d2->phase == SP_START_DONE)) | ||
1845 | { | ||
1846 | hello = GNUNET_HELLO_get_header (ctx->d2->hello); | ||
1847 | GNUNET_assert (hello != NULL); | ||
1848 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Offering hello of %s to %s\n", | ||
1849 | ctx->d2->shortname, ctx->d1->shortname); | ||
1850 | GNUNET_TRANSPORT_offer_hello (ctx->d1th, hello, NULL, NULL); | ||
1851 | GNUNET_assert (ctx->d1core != NULL); | ||
1852 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1853 | "Sending connect request to TRANSPORT of %s for peer %s\n", | ||
1854 | GNUNET_i2s (&ctx->d1->id), | ||
1855 | GNUNET_h2s (&ctx->d2->id.hashPubKey)); | ||
1856 | GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id); | ||
1857 | ctx->timeout_hello = | ||
1858 | GNUNET_TIME_relative_add (ctx->timeout_hello, | ||
1859 | GNUNET_TIME_relative_multiply | ||
1860 | (GNUNET_TIME_UNIT_MILLISECONDS, 500)); | ||
1861 | } | ||
1862 | ctx->hello_send_task = | ||
1863 | GNUNET_SCHEDULER_add_delayed (ctx->timeout_hello, &send_hello, ctx); | ||
1864 | } | ||
1865 | |||
1866 | /** | ||
1867 | * Notify of a successful connection to the core service. | ||
1868 | * | ||
1869 | * @param cls a ConnectContext | ||
1870 | * @param server handle to the core service | ||
1871 | * @param my_identity the peer identity of this peer | ||
1872 | */ | ||
1873 | void | ||
1874 | core_init_notify (void *cls, struct GNUNET_CORE_Handle *server, | ||
1875 | const struct GNUNET_PeerIdentity *my_identity) | ||
1876 | { | ||
1877 | struct GNUNET_TESTING_ConnectContext *connect_ctx = cls; | ||
1878 | |||
1879 | connect_ctx->d1core_ready = GNUNET_YES; | ||
1880 | |||
1881 | if (connect_ctx->send_hello == GNUNET_NO) | ||
1882 | { | ||
1883 | GNUNET_TRANSPORT_try_connect (connect_ctx->d1th, &connect_ctx->d2->id); | ||
1884 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1885 | "Sending connect request to TRANSPORT of %s for peer %s\n", | ||
1886 | connect_ctx->d1->shortname, connect_ctx->d2->shortname); | ||
1887 | } | ||
1888 | } | ||
1889 | |||
1890 | |||
1891 | /** | ||
1892 | * Try to connect again some peers that failed in an earlier attempt. This will | ||
1893 | * be tried as many times as connection_attempts in the configuration file. | ||
1894 | * | ||
1895 | * @param cls Closure (connection context between the two peers). | ||
1896 | * @param tc TaskContext. | ||
1897 | */ | ||
1898 | static void | ||
1899 | reattempt_daemons_connect (void *cls, | ||
1900 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1901 | { | ||
1902 | struct GNUNET_TESTING_ConnectContext *ctx = cls; | ||
1903 | |||
1904 | ctx->timeout_task = GNUNET_SCHEDULER_NO_TASK; | ||
1905 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
1906 | return; | ||
1907 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1908 | "re-attempting connect of peer %s to peer %s\n", | ||
1909 | ctx->d1->shortname, ctx->d2->shortname); | ||
1910 | ctx->connect_attempts--; | ||
1911 | GNUNET_assert (ctx->d1core == NULL); | ||
1912 | ctx->d1core_ready = GNUNET_NO; | ||
1913 | ctx->d1core = | ||
1914 | GNUNET_CORE_connect (ctx->d1->cfg, ctx, &core_init_notify, | ||
1915 | &connect_notify, NULL, NULL, GNUNET_NO, NULL, | ||
1916 | GNUNET_NO, no_handlers); | ||
1917 | if (ctx->d1core == NULL) | ||
1918 | { | ||
1919 | if (NULL != ctx->cb) | ||
1920 | ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg, | ||
1921 | ctx->d2->cfg, ctx->d1, ctx->d2, | ||
1922 | _("Failed to connect to core service of first peer!\n")); | ||
1923 | GNUNET_free (ctx); | ||
1924 | return; | ||
1925 | } | ||
1926 | |||
1927 | /* Don't know reason for initial connect failure, update the HELLO for the second peer */ | ||
1928 | if (NULL != ctx->d2->hello) | ||
1929 | { | ||
1930 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "updating %s's HELLO\n", | ||
1931 | ctx->d2->shortname); | ||
1932 | GNUNET_free (ctx->d2->hello); | ||
1933 | ctx->d2->hello = NULL; | ||
1934 | if (NULL != ctx->d2->th) | ||
1935 | { | ||
1936 | GNUNET_TRANSPORT_get_hello_cancel (ctx->d2->ghh); | ||
1937 | ctx->d2->ghh = NULL; | ||
1938 | GNUNET_TRANSPORT_disconnect (ctx->d2->th); | ||
1939 | } | ||
1940 | ctx->d2->th = | ||
1941 | GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL, | ||
1942 | NULL); | ||
1943 | GNUNET_assert (ctx->d2->th != NULL); | ||
1944 | ctx->d2->ghh = | ||
1945 | GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2); | ||
1946 | } | ||
1947 | else | ||
1948 | { | ||
1949 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "didn't have %s's HELLO\n", | ||
1950 | ctx->d2->shortname); | ||
1951 | } | ||
1952 | |||
1953 | if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL)) | ||
1954 | { | ||
1955 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1956 | "didn't have %s's HELLO, trying to get it now\n", | ||
1957 | ctx->d2->shortname); | ||
1958 | ctx->d2->th = | ||
1959 | GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL, | ||
1960 | NULL); | ||
1961 | if (NULL == ctx->d2->th) | ||
1962 | { | ||
1963 | GNUNET_CORE_disconnect (ctx->d1core); | ||
1964 | GNUNET_free (ctx); | ||
1965 | if (NULL != ctx->cb) | ||
1966 | ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg, | ||
1967 | ctx->d2->cfg, ctx->d1, ctx->d2, | ||
1968 | _("Failed to connect to transport service!\n")); | ||
1969 | return; | ||
1970 | } | ||
1971 | ctx->d2->ghh = | ||
1972 | GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2); | ||
1973 | } | ||
1974 | else | ||
1975 | { | ||
1976 | if (NULL == ctx->d2->hello) | ||
1977 | { | ||
1978 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1979 | "didn't have %s's HELLO but th wasn't NULL, not trying!!\n", | ||
1980 | ctx->d2->shortname); | ||
1981 | } | ||
1982 | } | ||
1983 | |||
1984 | if (ctx->send_hello == GNUNET_YES) | ||
1985 | { | ||
1986 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending %s's HELLO to %s\n", | ||
1987 | ctx->d1->shortname, ctx->d2->shortname); | ||
1988 | ctx->d1th = | ||
1989 | GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL, | ||
1990 | NULL, NULL); | ||
1991 | if (ctx->d1th == NULL) | ||
1992 | { | ||
1993 | GNUNET_CORE_disconnect (ctx->d1core); | ||
1994 | GNUNET_free (ctx); | ||
1995 | if (NULL != ctx->cb) | ||
1996 | ctx->cb (ctx->cb_cls, &ctx->d1->id, &ctx->d2->id, 0, ctx->d1->cfg, | ||
1997 | ctx->d2->cfg, ctx->d1, ctx->d2, | ||
1998 | _("Failed to connect to transport service!\n")); | ||
1999 | return; | ||
2000 | } | ||
2001 | GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task); | ||
2002 | ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx); | ||
2003 | } | ||
2004 | else | ||
2005 | { | ||
2006 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to reconnect %s to %s\n", | ||
2007 | ctx->d1->shortname, ctx->d2->shortname); | ||
2008 | GNUNET_TRANSPORT_try_connect (ctx->d1th, &ctx->d2->id); | ||
2009 | } | ||
2010 | ctx->timeout_task = | ||
2011 | GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout, | ||
2012 | ¬ify_connect_result, ctx); | ||
2013 | } | ||
2014 | |||
2015 | /** | ||
2016 | * Iterator for currently known peers, to ensure | ||
2017 | * that we don't try to send duplicate connect | ||
2018 | * requests to core. | ||
2019 | * | ||
2020 | * @param cls our "struct GNUNET_TESTING_ConnectContext" | ||
2021 | * @param peer identity of the peer that has connected, | ||
2022 | * NULL when iteration has finished | ||
2023 | * @param atsi performance information | ||
2024 | * @param atsi_count number of records in 'atsi' | ||
2025 | * | ||
2026 | */ | ||
2027 | static void | ||
2028 | core_initial_iteration (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
2029 | const struct GNUNET_ATS_Information *atsi, | ||
2030 | unsigned int atsi_count) | ||
2031 | { | ||
2032 | struct GNUNET_TESTING_ConnectContext *ctx = cls; | ||
2033 | |||
2034 | if ((peer != NULL) && | ||
2035 | (0 == memcmp (&ctx->d2->id, peer, sizeof (struct GNUNET_PeerIdentity)))) | ||
2036 | { | ||
2037 | ctx->connected = GNUNET_YES; | ||
2038 | ctx->distance = 0; /* FIXME: distance */ | ||
2039 | return; | ||
2040 | } | ||
2041 | if (peer != NULL) | ||
2042 | return; /* ignore other peers */ | ||
2043 | /* peer == NULL: End of iteration over peers */ | ||
2044 | |||
2045 | GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->timeout_task); | ||
2046 | if (ctx->connected == GNUNET_YES) | ||
2047 | { | ||
2048 | ctx->timeout_task = GNUNET_SCHEDULER_add_now (¬ify_connect_result, ctx); | ||
2049 | return; | ||
2050 | } | ||
2051 | |||
2052 | /* Peer not already connected, need to schedule connect request! */ | ||
2053 | if (ctx->d1core == NULL) | ||
2054 | { | ||
2055 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2056 | "Peers are NOT connected, connecting to core!\n"); | ||
2057 | ctx->d1core = | ||
2058 | GNUNET_CORE_connect (ctx->d1->cfg, ctx, &core_init_notify, | ||
2059 | &connect_notify, NULL, NULL, GNUNET_NO, NULL, | ||
2060 | GNUNET_NO, no_handlers); | ||
2061 | } | ||
2062 | |||
2063 | if (ctx->d1core == NULL) | ||
2064 | { | ||
2065 | ctx->timeout_task = GNUNET_SCHEDULER_add_now (¬ify_connect_result, ctx); | ||
2066 | return; | ||
2067 | } | ||
2068 | |||
2069 | if ((NULL == ctx->d2->hello) && (ctx->d2->th == NULL)) /* Do not yet have the second peer's hello, set up a task to get it */ | ||
2070 | { | ||
2071 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2072 | "Don't have d2's HELLO, trying to get it!\n"); | ||
2073 | ctx->d2->th = | ||
2074 | GNUNET_TRANSPORT_connect (ctx->d2->cfg, &ctx->d2->id, NULL, NULL, NULL, | ||
2075 | NULL); | ||
2076 | if (ctx->d2->th == NULL) | ||
2077 | { | ||
2078 | GNUNET_CORE_disconnect (ctx->d1core); | ||
2079 | ctx->d1core = NULL; | ||
2080 | ctx->timeout_task = | ||
2081 | GNUNET_SCHEDULER_add_now (¬ify_connect_result, ctx); | ||
2082 | return; | ||
2083 | } | ||
2084 | ctx->d2->ghh = | ||
2085 | GNUNET_TRANSPORT_get_hello (ctx->d2->th, &process_hello, ctx->d2); | ||
2086 | } | ||
2087 | |||
2088 | if (ctx->send_hello == GNUNET_YES) | ||
2089 | { | ||
2090 | ctx->d1th = | ||
2091 | GNUNET_TRANSPORT_connect (ctx->d1->cfg, &ctx->d1->id, ctx->d1, NULL, | ||
2092 | NULL, NULL); | ||
2093 | if (ctx->d1th == NULL) | ||
2094 | { | ||
2095 | GNUNET_CORE_disconnect (ctx->d1core); | ||
2096 | ctx->d1core = NULL; | ||
2097 | ctx->timeout_task = | ||
2098 | GNUNET_SCHEDULER_add_now (¬ify_connect_result, ctx); | ||
2099 | return; | ||
2100 | } | ||
2101 | GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ctx->hello_send_task); | ||
2102 | ctx->hello_send_task = GNUNET_SCHEDULER_add_now (&send_hello, ctx); | ||
2103 | } | ||
2104 | |||
2105 | ctx->timeout_task = | ||
2106 | GNUNET_SCHEDULER_add_delayed (ctx->relative_timeout, | ||
2107 | ¬ify_connect_result, ctx); | ||
2108 | |||
2109 | } | ||
2110 | |||
2111 | |||
2112 | /** | ||
2113 | * Establish a connection between two GNUnet daemons. The daemons | ||
2114 | * must both be running and not be stopped until either the | ||
2115 | * 'cb' callback is called OR the connection request has been | ||
2116 | * explicitly cancelled. | ||
2117 | * | ||
2118 | * @param d1 handle for the first daemon | ||
2119 | * @param d2 handle for the second daemon | ||
2120 | * @param timeout how long is the connection attempt | ||
2121 | * allowed to take? | ||
2122 | * @param max_connect_attempts how many times should we try to reconnect | ||
2123 | * (within timeout) | ||
2124 | * @param send_hello GNUNET_YES to send the HELLO, GNUNET_NO to assume | ||
2125 | * the HELLO has already been exchanged | ||
2126 | * @param cb function to call at the end | ||
2127 | * @param cb_cls closure for cb | ||
2128 | * @return handle to cancel the request | ||
2129 | */ | ||
2130 | struct GNUNET_TESTING_ConnectContext * | ||
2131 | GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, | ||
2132 | struct GNUNET_TESTING_Daemon *d2, | ||
2133 | struct GNUNET_TIME_Relative timeout, | ||
2134 | unsigned int max_connect_attempts, | ||
2135 | int send_hello, | ||
2136 | GNUNET_TESTING_NotifyConnection cb, | ||
2137 | void *cb_cls) | ||
2138 | { | ||
2139 | struct GNUNET_TESTING_ConnectContext *ctx; | ||
2140 | |||
2141 | if ((d1->running == GNUNET_NO) || (d2->running == GNUNET_NO)) | ||
2142 | { | ||
2143 | if (NULL != cb) | ||
2144 | cb (cb_cls, &d1->id, &d2->id, 0, d1->cfg, d2->cfg, d1, d2, | ||
2145 | _("Peers are not fully running yet, can not connect!\n")); | ||
2146 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Peers are not up!\n"); | ||
2147 | return NULL; | ||
2148 | } | ||
2149 | |||
2150 | ctx = GNUNET_malloc (sizeof (struct GNUNET_TESTING_ConnectContext)); | ||
2151 | ctx->d1 = d1; | ||
2152 | ctx->d2 = d2; | ||
2153 | ctx->timeout_hello = | ||
2154 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500); | ||
2155 | ctx->relative_timeout = | ||
2156 | GNUNET_TIME_relative_divide (timeout, max_connect_attempts); | ||
2157 | ctx->cb = cb; | ||
2158 | ctx->cb_cls = cb_cls; | ||
2159 | ctx->connect_attempts = max_connect_attempts; | ||
2160 | ctx->connected = GNUNET_NO; | ||
2161 | ctx->send_hello = send_hello; | ||
2162 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asked to connect peer %s to peer %s\n", | ||
2163 | d1->shortname, d2->shortname); | ||
2164 | /* Core is up! Iterate over all _known_ peers first to check if we are already connected to the peer! */ | ||
2165 | GNUNET_assert (NULL != | ||
2166 | GNUNET_CORE_is_peer_connected (ctx->d1->cfg, &ctx->d2->id, | ||
2167 | &core_initial_iteration, ctx)); | ||
2168 | return ctx; | ||
2169 | } | ||
2170 | |||
2171 | |||
2172 | /** | ||
2173 | * Cancel an attempt to connect two daemons. | ||
2174 | * | ||
2175 | * @param cc connect context | ||
2176 | */ | ||
2177 | void | ||
2178 | GNUNET_TESTING_daemons_connect_cancel (struct GNUNET_TESTING_ConnectContext *cc) | ||
2179 | { | ||
2180 | if (GNUNET_SCHEDULER_NO_TASK != cc->timeout_task) | ||
2181 | { | ||
2182 | GNUNET_SCHEDULER_cancel (cc->timeout_task); | ||
2183 | cc->timeout_task = GNUNET_SCHEDULER_NO_TASK; | ||
2184 | } | ||
2185 | if (GNUNET_SCHEDULER_NO_TASK != cc->hello_send_task) | ||
2186 | { | ||
2187 | GNUNET_SCHEDULER_cancel (cc->hello_send_task); | ||
2188 | cc->hello_send_task = GNUNET_SCHEDULER_NO_TASK; | ||
2189 | } | ||
2190 | if (NULL != cc->d1core) | ||
2191 | { | ||
2192 | GNUNET_CORE_disconnect (cc->d1core); | ||
2193 | cc->d1core = NULL; | ||
2194 | } | ||
2195 | if (NULL != cc->d1th) | ||
2196 | { | ||
2197 | GNUNET_TRANSPORT_disconnect (cc->d1th); | ||
2198 | cc->d1th = NULL; | ||
2199 | } | ||
2200 | GNUNET_free (cc); | ||
2201 | } | ||
2202 | |||
2203 | |||
2204 | /* end of testing.c */ | ||