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