diff options
Diffstat (limited to 'src/testing_old/testing_group.c')
-rw-r--r-- | src/testing_old/testing_group.c | 7038 |
1 files changed, 7038 insertions, 0 deletions
diff --git a/src/testing_old/testing_group.c b/src/testing_old/testing_group.c new file mode 100644 index 000000000..3ba7c5cce --- /dev/null +++ b/src/testing_old/testing_group.c | |||
@@ -0,0 +1,7038 @@ | |||
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_group.c | ||
23 | * @brief convenience API for writing testcases for GNUnet | ||
24 | * @author Nathan Evans | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_constants.h" | ||
29 | #include "gnunet_arm_service.h" | ||
30 | #include "gnunet_testing_lib.h" | ||
31 | #include "gnunet_core_service.h" | ||
32 | |||
33 | #define USE_START_HELPER GNUNET_YES | ||
34 | |||
35 | #define OLD 1 | ||
36 | |||
37 | /* Before connecting peers, send all of the HELLOs */ | ||
38 | #define USE_SEND_HELLOS GNUNET_NO | ||
39 | |||
40 | #define TOPOLOGY_HACK GNUNET_YES | ||
41 | |||
42 | |||
43 | /** | ||
44 | * Lowest port used for GNUnet testing. Should be high enough to not | ||
45 | * conflict with other applications running on the hosts but be low | ||
46 | * enough to not conflict with client-ports (typically starting around | ||
47 | * 32k). | ||
48 | */ | ||
49 | #define LOW_PORT 12000 | ||
50 | |||
51 | /** | ||
52 | * Highest port used for GNUnet testing. Should be low enough to not | ||
53 | * conflict with the port range for "local" ports (client apps; see | ||
54 | * /proc/sys/net/ipv4/ip_local_port_range on Linux for example). | ||
55 | */ | ||
56 | #define HIGH_PORT 56000 | ||
57 | |||
58 | /* Maximum time to delay connect attempt */ | ||
59 | #define MAX_CONNECT_DELAY 300 | ||
60 | |||
61 | /** | ||
62 | * Which list of peers do we need to modify? | ||
63 | */ | ||
64 | enum PeerLists | ||
65 | { | ||
66 | /** Modify allowed peers */ | ||
67 | ALLOWED, | ||
68 | |||
69 | /** Modify connect peers */ | ||
70 | CONNECT, | ||
71 | |||
72 | /** Modify blacklist peers */ | ||
73 | BLACKLIST, | ||
74 | |||
75 | /** Modify workingset peers */ | ||
76 | WORKING_SET | ||
77 | }; | ||
78 | |||
79 | /** | ||
80 | * Prototype of a function called whenever two peers would be connected | ||
81 | * in a certain topology. | ||
82 | */ | ||
83 | typedef unsigned int (*GNUNET_TESTING_ConnectionProcessor) (struct | ||
84 | GNUNET_TESTING_PeerGroup | ||
85 | * pg, | ||
86 | unsigned int first, | ||
87 | unsigned int second, | ||
88 | enum PeerLists list, | ||
89 | unsigned int check); | ||
90 | |||
91 | /** | ||
92 | * Context for handling churning a peer group | ||
93 | */ | ||
94 | struct ChurnContext | ||
95 | { | ||
96 | /** | ||
97 | * The peergroup we are dealing with. | ||
98 | */ | ||
99 | struct GNUNET_TESTING_PeerGroup *pg; | ||
100 | |||
101 | /** | ||
102 | * Name of the service to churn on/off, NULL | ||
103 | * to churn entire peer. | ||
104 | */ | ||
105 | char *service; | ||
106 | |||
107 | /** | ||
108 | * Callback used to notify of churning finished | ||
109 | */ | ||
110 | GNUNET_TESTING_NotifyCompletion cb; | ||
111 | |||
112 | /** | ||
113 | * Closure for callback | ||
114 | */ | ||
115 | void *cb_cls; | ||
116 | |||
117 | /** | ||
118 | * Number of peers that still need to be started | ||
119 | */ | ||
120 | unsigned int num_to_start; | ||
121 | |||
122 | /** | ||
123 | * Number of peers that still need to be stopped | ||
124 | */ | ||
125 | unsigned int num_to_stop; | ||
126 | |||
127 | /** | ||
128 | * Number of peers that failed to start | ||
129 | */ | ||
130 | unsigned int num_failed_start; | ||
131 | |||
132 | /** | ||
133 | * Number of peers that failed to stop | ||
134 | */ | ||
135 | unsigned int num_failed_stop; | ||
136 | }; | ||
137 | |||
138 | struct RestartContext | ||
139 | { | ||
140 | /** | ||
141 | * The group of peers being restarted | ||
142 | */ | ||
143 | struct GNUNET_TESTING_PeerGroup *peer_group; | ||
144 | |||
145 | /** | ||
146 | * How many peers have been restarted thus far | ||
147 | */ | ||
148 | unsigned int peers_restarted; | ||
149 | |||
150 | /** | ||
151 | * How many peers got an error when restarting | ||
152 | */ | ||
153 | unsigned int peers_restart_failed; | ||
154 | |||
155 | /** | ||
156 | * The function to call once all peers have been restarted | ||
157 | */ | ||
158 | GNUNET_TESTING_NotifyCompletion callback; | ||
159 | |||
160 | /** | ||
161 | * Closure for callback function | ||
162 | */ | ||
163 | void *callback_cls; | ||
164 | |||
165 | }; | ||
166 | |||
167 | struct SendHelloContext | ||
168 | { | ||
169 | /** | ||
170 | * Global handle to the peer group. | ||
171 | */ | ||
172 | struct GNUNET_TESTING_PeerGroup *pg; | ||
173 | |||
174 | /** | ||
175 | * The data about this specific peer. | ||
176 | */ | ||
177 | struct PeerData *peer; | ||
178 | |||
179 | /** | ||
180 | * The next HELLO that needs sent to this peer. | ||
181 | */ | ||
182 | struct PeerConnection *peer_pos; | ||
183 | |||
184 | /** | ||
185 | * Are we connected to CORE yet? | ||
186 | */ | ||
187 | unsigned int core_ready; | ||
188 | |||
189 | /** | ||
190 | * How many attempts should we make for failed connections? | ||
191 | */ | ||
192 | unsigned int connect_attempts; | ||
193 | |||
194 | /** | ||
195 | * Task for scheduling core connect requests to be sent. | ||
196 | */ | ||
197 | GNUNET_SCHEDULER_TaskIdentifier core_connect_task; | ||
198 | }; | ||
199 | |||
200 | struct ShutdownContext | ||
201 | { | ||
202 | struct GNUNET_TESTING_PeerGroup *pg; | ||
203 | /** | ||
204 | * Total peers to wait for | ||
205 | */ | ||
206 | unsigned int total_peers; | ||
207 | |||
208 | /** | ||
209 | * Number of peers successfully shut down | ||
210 | */ | ||
211 | unsigned int peers_down; | ||
212 | |||
213 | /** | ||
214 | * Number of peers failed to shut down | ||
215 | */ | ||
216 | unsigned int peers_failed; | ||
217 | |||
218 | /** | ||
219 | * Number of peers we have started shutting | ||
220 | * down. If too many, wait on them. | ||
221 | */ | ||
222 | unsigned int outstanding; | ||
223 | |||
224 | /** | ||
225 | * Timeout for shutdown. | ||
226 | */ | ||
227 | struct GNUNET_TIME_Relative timeout; | ||
228 | |||
229 | /** | ||
230 | * Callback to call when all peers either | ||
231 | * shutdown or failed to shutdown | ||
232 | */ | ||
233 | GNUNET_TESTING_NotifyCompletion cb; | ||
234 | |||
235 | /** | ||
236 | * Closure for cb | ||
237 | */ | ||
238 | void *cb_cls; | ||
239 | |||
240 | /** | ||
241 | * Should we delete all of the files from the peers? | ||
242 | */ | ||
243 | int delete_files; | ||
244 | }; | ||
245 | |||
246 | /** | ||
247 | * Individual shutdown context for a particular peer. | ||
248 | */ | ||
249 | struct PeerShutdownContext | ||
250 | { | ||
251 | /** | ||
252 | * Pointer to the high level shutdown context. | ||
253 | */ | ||
254 | struct ShutdownContext *shutdown_ctx; | ||
255 | |||
256 | /** | ||
257 | * The daemon handle for the peer to shut down. | ||
258 | */ | ||
259 | struct GNUNET_TESTING_Daemon *daemon; | ||
260 | }; | ||
261 | |||
262 | /** | ||
263 | * Individual shutdown context for a particular peer. | ||
264 | */ | ||
265 | struct PeerRestartContext | ||
266 | { | ||
267 | /** | ||
268 | * Pointer to the high level restart context. | ||
269 | */ | ||
270 | struct ChurnRestartContext *churn_restart_ctx; | ||
271 | |||
272 | /** | ||
273 | * The daemon handle for the peer to shut down. | ||
274 | */ | ||
275 | struct GNUNET_TESTING_Daemon *daemon; | ||
276 | }; | ||
277 | |||
278 | struct ServiceStartContext | ||
279 | { | ||
280 | struct GNUNET_TESTING_PeerGroup *pg; | ||
281 | unsigned int remaining; | ||
282 | GNUNET_TESTING_NotifyCompletion cb; | ||
283 | unsigned int outstanding; | ||
284 | char *service; | ||
285 | struct GNUNET_TIME_Relative timeout; | ||
286 | void *cb_cls; | ||
287 | }; | ||
288 | |||
289 | /** | ||
290 | * Individual shutdown context for a particular peer. | ||
291 | */ | ||
292 | struct PeerServiceStartContext | ||
293 | { | ||
294 | /** | ||
295 | * Pointer to the high level start context. | ||
296 | */ | ||
297 | struct ServiceStartContext *start_ctx; | ||
298 | |||
299 | /** | ||
300 | * The daemon handle for the peer to start the service on. | ||
301 | */ | ||
302 | struct GNUNET_TESTING_Daemon *daemon; | ||
303 | }; | ||
304 | |||
305 | struct CreateTopologyContext | ||
306 | { | ||
307 | |||
308 | /** | ||
309 | * Function to call with number of connections | ||
310 | */ | ||
311 | GNUNET_TESTING_NotifyConnections cont; | ||
312 | |||
313 | /** | ||
314 | * Closure for connection notification | ||
315 | */ | ||
316 | void *cls; | ||
317 | }; | ||
318 | |||
319 | enum States | ||
320 | { | ||
321 | /** Waiting to read number of peers */ | ||
322 | NUM_PEERS, | ||
323 | |||
324 | /** Should find next peer index */ | ||
325 | PEER_INDEX, | ||
326 | |||
327 | /** Should find colon */ | ||
328 | COLON, | ||
329 | |||
330 | /** Should read other peer index, space, or endline */ | ||
331 | OTHER_PEER_INDEX | ||
332 | }; | ||
333 | |||
334 | #if OLD | ||
335 | struct PeerConnection | ||
336 | { | ||
337 | /** | ||
338 | * Doubly Linked list | ||
339 | */ | ||
340 | struct PeerConnection *prev; | ||
341 | |||
342 | /* | ||
343 | * Doubly Linked list | ||
344 | */ | ||
345 | struct PeerConnection *next; | ||
346 | |||
347 | /* | ||
348 | * Index of daemon in pg->peers | ||
349 | */ | ||
350 | uint32_t index; | ||
351 | |||
352 | }; | ||
353 | #endif | ||
354 | |||
355 | struct InternalStartContext | ||
356 | { | ||
357 | /** | ||
358 | * Pointer to peerdata | ||
359 | */ | ||
360 | struct PeerData *peer; | ||
361 | |||
362 | /** | ||
363 | * Timeout for peer startup | ||
364 | */ | ||
365 | struct GNUNET_TIME_Relative timeout; | ||
366 | |||
367 | /** | ||
368 | * Client callback for hostkey notification | ||
369 | */ | ||
370 | GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback; | ||
371 | |||
372 | /** | ||
373 | * Closure for hostkey_callback | ||
374 | */ | ||
375 | void *hostkey_cls; | ||
376 | |||
377 | /** | ||
378 | * Client callback for peer start notification | ||
379 | */ | ||
380 | GNUNET_TESTING_NotifyDaemonRunning start_cb; | ||
381 | |||
382 | /** | ||
383 | * Closure for cb | ||
384 | */ | ||
385 | void *start_cb_cls; | ||
386 | |||
387 | /** | ||
388 | * Hostname, where to start the peer | ||
389 | */ | ||
390 | const char *hostname; | ||
391 | |||
392 | /** | ||
393 | * Username to use when connecting to the | ||
394 | * host via ssh. | ||
395 | */ | ||
396 | const char *username; | ||
397 | |||
398 | /** | ||
399 | * Pointer to starting memory location of a hostkey | ||
400 | */ | ||
401 | const char *hostkey; | ||
402 | |||
403 | /** | ||
404 | * Port to use for ssh. | ||
405 | */ | ||
406 | uint16_t sshport; | ||
407 | |||
408 | }; | ||
409 | |||
410 | struct ChurnRestartContext | ||
411 | { | ||
412 | /** | ||
413 | * PeerGroup that we are working with. | ||
414 | */ | ||
415 | struct GNUNET_TESTING_PeerGroup *pg; | ||
416 | |||
417 | /** | ||
418 | * Number of restarts currently in flight. | ||
419 | */ | ||
420 | unsigned int outstanding; | ||
421 | |||
422 | /** | ||
423 | * Handle to the underlying churn context. | ||
424 | */ | ||
425 | struct ChurnContext *churn_ctx; | ||
426 | |||
427 | /** | ||
428 | * How long to allow the operation to take. | ||
429 | */ | ||
430 | struct GNUNET_TIME_Relative timeout; | ||
431 | }; | ||
432 | |||
433 | struct OutstandingSSH | ||
434 | { | ||
435 | struct OutstandingSSH *next; | ||
436 | |||
437 | struct OutstandingSSH *prev; | ||
438 | |||
439 | /** | ||
440 | * Number of current ssh connections. | ||
441 | */ | ||
442 | uint32_t outstanding; | ||
443 | |||
444 | /** | ||
445 | * The hostname of this peer. | ||
446 | */ | ||
447 | const char *hostname; | ||
448 | }; | ||
449 | |||
450 | /** | ||
451 | * Data we keep per peer. | ||
452 | */ | ||
453 | struct PeerData | ||
454 | { | ||
455 | /** | ||
456 | * (Initial) configuration of the host. | ||
457 | * (initial because clients could change | ||
458 | * it and we would not know about those | ||
459 | * updates). | ||
460 | */ | ||
461 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
462 | |||
463 | /** | ||
464 | * Handle for controlling the daemon. | ||
465 | */ | ||
466 | struct GNUNET_TESTING_Daemon *daemon; | ||
467 | |||
468 | /** | ||
469 | * The peergroup this peer belongs to. | ||
470 | */ | ||
471 | struct GNUNET_TESTING_PeerGroup *pg; | ||
472 | |||
473 | #if OLD | ||
474 | /** | ||
475 | * Linked list of allowed peer connections. | ||
476 | */ | ||
477 | struct PeerConnection *allowed_peers_head; | ||
478 | |||
479 | /** | ||
480 | * Linked list of allowed peer connections. | ||
481 | */ | ||
482 | struct PeerConnection *allowed_peers_tail; | ||
483 | |||
484 | /** | ||
485 | * Linked list of blacklisted peer connections. | ||
486 | */ | ||
487 | struct PeerConnection *blacklisted_peers_head; | ||
488 | |||
489 | /** | ||
490 | * Linked list of blacklisted peer connections. | ||
491 | */ | ||
492 | struct PeerConnection *blacklisted_peers_tail; | ||
493 | |||
494 | /** | ||
495 | * Linked list of connect peer connections. | ||
496 | */ | ||
497 | struct PeerConnection *connect_peers_head; | ||
498 | |||
499 | /** | ||
500 | * Linked list of connect peer connections. | ||
501 | */ | ||
502 | struct PeerConnection *connect_peers_tail; | ||
503 | |||
504 | /** | ||
505 | * Linked list of connect peer connections. | ||
506 | */ | ||
507 | struct PeerConnection *connect_peers_working_set_head; | ||
508 | |||
509 | /** | ||
510 | * Linked list of connect peer connections. | ||
511 | */ | ||
512 | struct PeerConnection *connect_peers_working_set_tail; | ||
513 | |||
514 | #else | ||
515 | /** | ||
516 | * Hash map of allowed peer connections (F2F created topology) | ||
517 | */ | ||
518 | struct GNUNET_CONTAINER_MultiHashMap *allowed_peers; | ||
519 | |||
520 | /** | ||
521 | * Hash map of blacklisted peers | ||
522 | */ | ||
523 | struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers; | ||
524 | |||
525 | /** | ||
526 | * Hash map of peer connections | ||
527 | */ | ||
528 | struct GNUNET_CONTAINER_MultiHashMap *connect_peers; | ||
529 | |||
530 | /** | ||
531 | * Temporary hash map of peer connections | ||
532 | */ | ||
533 | struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set; | ||
534 | #endif | ||
535 | |||
536 | /** | ||
537 | * Temporary variable for topology creation, should be reset before | ||
538 | * creating any topology so the count is valid once finished. | ||
539 | */ | ||
540 | int num_connections; | ||
541 | |||
542 | /** | ||
543 | * Context to keep track of peers being started, to | ||
544 | * stagger hostkey generation and peer startup. | ||
545 | */ | ||
546 | struct InternalStartContext internal_context; | ||
547 | |||
548 | /** | ||
549 | * Task ID for the queued internal_continue_startup task | ||
550 | */ | ||
551 | GNUNET_SCHEDULER_TaskIdentifier startup_task; | ||
552 | |||
553 | }; | ||
554 | |||
555 | /** | ||
556 | * Linked list of per-host data. | ||
557 | */ | ||
558 | struct HostData | ||
559 | { | ||
560 | /** | ||
561 | * Name of the host. | ||
562 | */ | ||
563 | char *hostname; | ||
564 | |||
565 | /** | ||
566 | * SSH username to use when connecting to this host. | ||
567 | */ | ||
568 | char *username; | ||
569 | |||
570 | /** | ||
571 | * SSH port to use when connecting to this host. | ||
572 | */ | ||
573 | uint16_t sshport; | ||
574 | |||
575 | /** | ||
576 | * Lowest port that we have not yet used | ||
577 | * for GNUnet. | ||
578 | */ | ||
579 | uint16_t minport; | ||
580 | }; | ||
581 | |||
582 | struct TopologyIterateContext | ||
583 | { | ||
584 | /** | ||
585 | * The peergroup we are working with. | ||
586 | */ | ||
587 | struct GNUNET_TESTING_PeerGroup *pg; | ||
588 | |||
589 | /** | ||
590 | * Callback for notifying of two connected peers. | ||
591 | */ | ||
592 | GNUNET_TESTING_NotifyTopology topology_cb; | ||
593 | |||
594 | /** | ||
595 | * Closure for topology_cb | ||
596 | */ | ||
597 | void *cls; | ||
598 | |||
599 | /** | ||
600 | * Number of peers currently connected to. | ||
601 | */ | ||
602 | unsigned int connected; | ||
603 | |||
604 | /** | ||
605 | * Number of peers we have finished iterating. | ||
606 | */ | ||
607 | unsigned int completed; | ||
608 | |||
609 | /** | ||
610 | * Number of peers total. | ||
611 | */ | ||
612 | unsigned int total; | ||
613 | }; | ||
614 | |||
615 | struct StatsIterateContext | ||
616 | { | ||
617 | /** | ||
618 | * The peergroup that we are dealing with. | ||
619 | */ | ||
620 | struct GNUNET_TESTING_PeerGroup *pg; | ||
621 | |||
622 | /** | ||
623 | * Continuation to call once all stats information has been retrieved. | ||
624 | */ | ||
625 | GNUNET_STATISTICS_Callback cont; | ||
626 | |||
627 | /** | ||
628 | * Proc function to call on each value received. | ||
629 | */ | ||
630 | GNUNET_TESTING_STATISTICS_Iterator proc; | ||
631 | |||
632 | /** | ||
633 | * Closure for topology_cb | ||
634 | */ | ||
635 | void *cls; | ||
636 | |||
637 | /** | ||
638 | * Number of peers currently connected to. | ||
639 | */ | ||
640 | unsigned int connected; | ||
641 | |||
642 | /** | ||
643 | * Number of peers we have finished iterating. | ||
644 | */ | ||
645 | unsigned int completed; | ||
646 | |||
647 | /** | ||
648 | * Number of peers total. | ||
649 | */ | ||
650 | unsigned int total; | ||
651 | }; | ||
652 | |||
653 | struct CoreContext | ||
654 | { | ||
655 | void *iter_context; | ||
656 | struct GNUNET_TESTING_Daemon *daemon; | ||
657 | }; | ||
658 | |||
659 | struct StatsCoreContext | ||
660 | { | ||
661 | void *iter_context; | ||
662 | struct GNUNET_TESTING_Daemon *daemon; | ||
663 | /** | ||
664 | * Handle to the statistics service. | ||
665 | */ | ||
666 | struct GNUNET_STATISTICS_Handle *stats_handle; | ||
667 | |||
668 | /** | ||
669 | * Handle for getting statistics. | ||
670 | */ | ||
671 | struct GNUNET_STATISTICS_GetHandle *stats_get_handle; | ||
672 | }; | ||
673 | |||
674 | struct ConnectTopologyContext | ||
675 | { | ||
676 | /** | ||
677 | * How many connections are left to create. | ||
678 | */ | ||
679 | unsigned int remaining_connections; | ||
680 | |||
681 | /** | ||
682 | * Handle to group of peers. | ||
683 | */ | ||
684 | struct GNUNET_TESTING_PeerGroup *pg; | ||
685 | |||
686 | /** | ||
687 | * How long to try this connection before timing out. | ||
688 | */ | ||
689 | struct GNUNET_TIME_Relative connect_timeout; | ||
690 | |||
691 | /** | ||
692 | * How many times to retry connecting the two peers. | ||
693 | */ | ||
694 | unsigned int connect_attempts; | ||
695 | |||
696 | /** | ||
697 | * Temp value set for each iteration. | ||
698 | */ | ||
699 | //struct PeerData *first; | ||
700 | |||
701 | /** | ||
702 | * Notification that all peers are connected. | ||
703 | */ | ||
704 | GNUNET_TESTING_NotifyCompletion notify_connections_done; | ||
705 | |||
706 | /** | ||
707 | * Closure for notify. | ||
708 | */ | ||
709 | void *notify_cls; | ||
710 | }; | ||
711 | |||
712 | struct ConnectContext; | ||
713 | |||
714 | /** | ||
715 | * Handle to a group of GNUnet peers. | ||
716 | */ | ||
717 | struct GNUNET_TESTING_PeerGroup | ||
718 | { | ||
719 | /** | ||
720 | * Configuration template. | ||
721 | */ | ||
722 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
723 | |||
724 | struct ConnectContext *cc_head; | ||
725 | |||
726 | struct ConnectContext *cc_tail; | ||
727 | |||
728 | /** | ||
729 | * Function to call on each started daemon. | ||
730 | */ | ||
731 | //GNUNET_TESTING_NotifyDaemonRunning cb; | ||
732 | |||
733 | /** | ||
734 | * Closure for cb. | ||
735 | */ | ||
736 | //void *cb_cls; | ||
737 | |||
738 | /* | ||
739 | * Function to call on each topology connection created | ||
740 | */ | ||
741 | GNUNET_TESTING_NotifyConnection notify_connection; | ||
742 | |||
743 | /* | ||
744 | * Callback for notify_connection | ||
745 | */ | ||
746 | void *notify_connection_cls; | ||
747 | |||
748 | /** | ||
749 | * Array of information about hosts. | ||
750 | */ | ||
751 | struct HostData *hosts; | ||
752 | |||
753 | /** | ||
754 | * Number of hosts (size of HostData) | ||
755 | */ | ||
756 | unsigned int num_hosts; | ||
757 | |||
758 | /** | ||
759 | * Array of "total" peers. | ||
760 | */ | ||
761 | struct PeerData *peers; | ||
762 | |||
763 | /** | ||
764 | * Number of peers in this group. | ||
765 | */ | ||
766 | unsigned int total; | ||
767 | |||
768 | /** | ||
769 | * At what time should we fail the peer startup process? | ||
770 | */ | ||
771 | struct GNUNET_TIME_Absolute max_timeout; | ||
772 | |||
773 | /** | ||
774 | * How many peers are being started right now? | ||
775 | */ | ||
776 | unsigned int starting; | ||
777 | |||
778 | /** | ||
779 | * How many peers have already been started? | ||
780 | */ | ||
781 | unsigned int started; | ||
782 | |||
783 | /** | ||
784 | * Number of possible connections to peers | ||
785 | * at a time. | ||
786 | */ | ||
787 | unsigned int max_outstanding_connections; | ||
788 | |||
789 | /** | ||
790 | * Number of ssh connections to peers (max). | ||
791 | */ | ||
792 | unsigned int max_concurrent_ssh; | ||
793 | |||
794 | /** | ||
795 | * Number of connects we are waiting on, allows us to rate limit | ||
796 | * connect attempts. | ||
797 | */ | ||
798 | unsigned int outstanding_connects; | ||
799 | |||
800 | /** | ||
801 | * Number of HELLOs we have yet to send. | ||
802 | */ | ||
803 | unsigned int remaining_hellos; | ||
804 | |||
805 | /** | ||
806 | * How many connects have already been scheduled? | ||
807 | */ | ||
808 | unsigned int total_connects_scheduled; | ||
809 | |||
810 | /** | ||
811 | * Hostkeys loaded from a file. | ||
812 | */ | ||
813 | char *hostkey_data; | ||
814 | |||
815 | /** | ||
816 | * Head of DLL to keep track of the number of outstanding | ||
817 | * ssh connections per peer. | ||
818 | */ | ||
819 | struct OutstandingSSH *ssh_head; | ||
820 | |||
821 | /** | ||
822 | * Tail of DLL to keep track of the number of outstanding | ||
823 | * ssh connections per peer. | ||
824 | */ | ||
825 | struct OutstandingSSH *ssh_tail; | ||
826 | |||
827 | /** | ||
828 | * Stop scheduling peers connecting. | ||
829 | */ | ||
830 | unsigned int stop_connects; | ||
831 | |||
832 | /** | ||
833 | * Connection context for peer group. | ||
834 | */ | ||
835 | struct ConnectTopologyContext ct_ctx; | ||
836 | }; | ||
837 | |||
838 | struct UpdateContext | ||
839 | { | ||
840 | /** | ||
841 | * The altered configuration. | ||
842 | */ | ||
843 | struct GNUNET_CONFIGURATION_Handle *ret; | ||
844 | |||
845 | /** | ||
846 | * The original configuration to alter. | ||
847 | */ | ||
848 | const struct GNUNET_CONFIGURATION_Handle *orig; | ||
849 | |||
850 | /** | ||
851 | * The hostname that this peer will run on. | ||
852 | */ | ||
853 | const char *hostname; | ||
854 | |||
855 | /** | ||
856 | * The next possible port to assign. | ||
857 | */ | ||
858 | unsigned int nport; | ||
859 | |||
860 | /** | ||
861 | * Unique number for unix domain sockets. | ||
862 | */ | ||
863 | unsigned int upnum; | ||
864 | |||
865 | /** | ||
866 | * Unique number for this peer/host to offset | ||
867 | * things that are grouped by host. | ||
868 | */ | ||
869 | unsigned int fdnum; | ||
870 | }; | ||
871 | |||
872 | struct ConnectContext | ||
873 | { | ||
874 | |||
875 | struct ConnectContext *next; | ||
876 | |||
877 | struct ConnectContext *prev; | ||
878 | |||
879 | /** | ||
880 | * Index of peer to connect second to. | ||
881 | */ | ||
882 | uint32_t first_index; | ||
883 | |||
884 | /** | ||
885 | * Index of peer to connect first to. | ||
886 | */ | ||
887 | uint32_t second_index; | ||
888 | |||
889 | /** | ||
890 | * Task associated with the attempt to connect. | ||
891 | */ | ||
892 | GNUNET_SCHEDULER_TaskIdentifier task; | ||
893 | |||
894 | /** | ||
895 | * Context in 'testing.c', to cancel connection attempt. | ||
896 | */ | ||
897 | struct GNUNET_TESTING_ConnectContext *cc; | ||
898 | |||
899 | /** | ||
900 | * Higher level topology connection context. | ||
901 | */ | ||
902 | struct ConnectTopologyContext *ct_ctx; | ||
903 | |||
904 | /** | ||
905 | * Whether this connection has been accounted for in the schedule_connect call. | ||
906 | */ | ||
907 | int counted; | ||
908 | }; | ||
909 | |||
910 | struct UnblacklistContext | ||
911 | { | ||
912 | /** | ||
913 | * The peergroup | ||
914 | */ | ||
915 | struct GNUNET_TESTING_PeerGroup *pg; | ||
916 | |||
917 | /** | ||
918 | * uid of the first peer | ||
919 | */ | ||
920 | uint32_t first_uid; | ||
921 | }; | ||
922 | |||
923 | struct RandomContext | ||
924 | { | ||
925 | /** | ||
926 | * The peergroup | ||
927 | */ | ||
928 | struct GNUNET_TESTING_PeerGroup *pg; | ||
929 | |||
930 | /** | ||
931 | * uid of the first peer | ||
932 | */ | ||
933 | uint32_t first_uid; | ||
934 | |||
935 | /** | ||
936 | * Peer data for first peer. | ||
937 | */ | ||
938 | struct PeerData *first; | ||
939 | |||
940 | /** | ||
941 | * Random percentage to use | ||
942 | */ | ||
943 | double percentage; | ||
944 | }; | ||
945 | |||
946 | struct MinimumContext | ||
947 | { | ||
948 | /** | ||
949 | * The peergroup | ||
950 | */ | ||
951 | struct GNUNET_TESTING_PeerGroup *pg; | ||
952 | |||
953 | /** | ||
954 | * uid of the first peer | ||
955 | */ | ||
956 | uint32_t first_uid; | ||
957 | |||
958 | /** | ||
959 | * Peer data for first peer. | ||
960 | */ | ||
961 | struct PeerData *first; | ||
962 | |||
963 | /** | ||
964 | * Number of conns per peer | ||
965 | */ | ||
966 | unsigned int num_to_add; | ||
967 | |||
968 | /** | ||
969 | * Permuted array of all possible connections. Only add the Nth | ||
970 | * peer if it's in the Nth position. | ||
971 | */ | ||
972 | unsigned int *pg_array; | ||
973 | |||
974 | /** | ||
975 | * What number is the current element we are iterating over? | ||
976 | */ | ||
977 | unsigned int current; | ||
978 | }; | ||
979 | |||
980 | struct DFSContext | ||
981 | { | ||
982 | /** | ||
983 | * The peergroup | ||
984 | */ | ||
985 | struct GNUNET_TESTING_PeerGroup *pg; | ||
986 | |||
987 | /** | ||
988 | * uid of the first peer | ||
989 | */ | ||
990 | uint32_t first_uid; | ||
991 | |||
992 | /** | ||
993 | * uid of the second peer | ||
994 | */ | ||
995 | uint32_t second_uid; | ||
996 | |||
997 | /** | ||
998 | * Peer data for first peer. | ||
999 | */ | ||
1000 | struct PeerData *first; | ||
1001 | |||
1002 | /** | ||
1003 | * Which peer has been chosen as the one to add? | ||
1004 | */ | ||
1005 | unsigned int chosen; | ||
1006 | |||
1007 | /** | ||
1008 | * What number is the current element we are iterating over? | ||
1009 | */ | ||
1010 | unsigned int current; | ||
1011 | }; | ||
1012 | |||
1013 | /** | ||
1014 | * Simple struct to keep track of progress, and print a | ||
1015 | * nice little percentage meter for long running tasks. | ||
1016 | */ | ||
1017 | struct ProgressMeter | ||
1018 | { | ||
1019 | unsigned int total; | ||
1020 | |||
1021 | unsigned int modnum; | ||
1022 | |||
1023 | unsigned int dotnum; | ||
1024 | |||
1025 | unsigned int completed; | ||
1026 | |||
1027 | int print; | ||
1028 | |||
1029 | char *startup_string; | ||
1030 | }; | ||
1031 | |||
1032 | #if !OLD | ||
1033 | /** | ||
1034 | * Convert unique ID to hash code. | ||
1035 | * | ||
1036 | * @param uid unique ID to convert | ||
1037 | * @param hash set to uid (extended with zeros) | ||
1038 | */ | ||
1039 | static void | ||
1040 | hash_from_uid (uint32_t uid, GNUNET_HashCode * hash) | ||
1041 | { | ||
1042 | memset (hash, 0, sizeof (GNUNET_HashCode)); | ||
1043 | *((uint32_t *) hash) = uid; | ||
1044 | } | ||
1045 | |||
1046 | /** | ||
1047 | * Convert hash code to unique ID. | ||
1048 | * | ||
1049 | * @param uid unique ID to convert | ||
1050 | * @param hash set to uid (extended with zeros) | ||
1051 | */ | ||
1052 | static void | ||
1053 | uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid) | ||
1054 | { | ||
1055 | memcpy (uid, hash, sizeof (uint32_t)); | ||
1056 | } | ||
1057 | #endif | ||
1058 | |||
1059 | #if USE_SEND_HELLOS | ||
1060 | static struct GNUNET_CORE_MessageHandler no_handlers[] = { | ||
1061 | {NULL, 0, 0} | ||
1062 | }; | ||
1063 | #endif | ||
1064 | |||
1065 | /** | ||
1066 | * Create a meter to keep track of the progress of some task. | ||
1067 | * | ||
1068 | * @param total the total number of items to complete | ||
1069 | * @param start_string a string to prefix the meter with (if printing) | ||
1070 | * @param print GNUNET_YES to print the meter, GNUNET_NO to count | ||
1071 | * internally only | ||
1072 | * | ||
1073 | * @return the progress meter | ||
1074 | */ | ||
1075 | static struct ProgressMeter * | ||
1076 | create_meter (unsigned int total, char *start_string, int print) | ||
1077 | { | ||
1078 | struct ProgressMeter *ret; | ||
1079 | |||
1080 | ret = GNUNET_malloc (sizeof (struct ProgressMeter)); | ||
1081 | ret->print = print; | ||
1082 | ret->total = total; | ||
1083 | ret->modnum = total / 4; | ||
1084 | if (ret->modnum == 0) /* Divide by zero check */ | ||
1085 | ret->modnum = 1; | ||
1086 | ret->dotnum = (total / 50) + 1; | ||
1087 | if (start_string != NULL) | ||
1088 | ret->startup_string = GNUNET_strdup (start_string); | ||
1089 | else | ||
1090 | ret->startup_string = GNUNET_strdup (""); | ||
1091 | |||
1092 | return ret; | ||
1093 | } | ||
1094 | |||
1095 | /** | ||
1096 | * Update progress meter (increment by one). | ||
1097 | * | ||
1098 | * @param meter the meter to update and print info for | ||
1099 | * | ||
1100 | * @return GNUNET_YES if called the total requested, | ||
1101 | * GNUNET_NO if more items expected | ||
1102 | */ | ||
1103 | static int | ||
1104 | update_meter (struct ProgressMeter *meter) | ||
1105 | { | ||
1106 | if (meter->print == GNUNET_YES) | ||
1107 | { | ||
1108 | if (meter->completed % meter->modnum == 0) | ||
1109 | { | ||
1110 | if (meter->completed == 0) | ||
1111 | { | ||
1112 | FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string); | ||
1113 | } | ||
1114 | else | ||
1115 | FPRINTF (stdout, "%d%%", | ||
1116 | (int) (((float) meter->completed / meter->total) * 100)); | ||
1117 | } | ||
1118 | else if (meter->completed % meter->dotnum == 0) | ||
1119 | FPRINTF (stdout, "%s", "."); | ||
1120 | |||
1121 | if (meter->completed + 1 == meter->total) | ||
1122 | FPRINTF (stdout, "%d%%]\n", 100); | ||
1123 | fflush (stdout); | ||
1124 | } | ||
1125 | meter->completed++; | ||
1126 | |||
1127 | if (meter->completed == meter->total) | ||
1128 | return GNUNET_YES; | ||
1129 | if (meter->completed > meter->total) | ||
1130 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n"); | ||
1131 | return GNUNET_NO; | ||
1132 | } | ||
1133 | |||
1134 | /** | ||
1135 | * Reset progress meter. | ||
1136 | * | ||
1137 | * @param meter the meter to reset | ||
1138 | * | ||
1139 | * @return GNUNET_YES if meter reset, | ||
1140 | * GNUNET_SYSERR on error | ||
1141 | */ | ||
1142 | static int | ||
1143 | reset_meter (struct ProgressMeter *meter) | ||
1144 | { | ||
1145 | if (meter == NULL) | ||
1146 | return GNUNET_SYSERR; | ||
1147 | |||
1148 | meter->completed = 0; | ||
1149 | return GNUNET_YES; | ||
1150 | } | ||
1151 | |||
1152 | /** | ||
1153 | * Release resources for meter | ||
1154 | * | ||
1155 | * @param meter the meter to free | ||
1156 | */ | ||
1157 | static void | ||
1158 | free_meter (struct ProgressMeter *meter) | ||
1159 | { | ||
1160 | GNUNET_free_non_null (meter->startup_string); | ||
1161 | GNUNET_free (meter); | ||
1162 | } | ||
1163 | |||
1164 | /** | ||
1165 | * Get a topology from a string input. | ||
1166 | * | ||
1167 | * @param topology where to write the retrieved topology | ||
1168 | * @param topology_string The string to attempt to | ||
1169 | * get a configuration value from | ||
1170 | * @return GNUNET_YES if topology string matched a | ||
1171 | * known topology, GNUNET_NO if not | ||
1172 | */ | ||
1173 | int | ||
1174 | GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology, | ||
1175 | const char *topology_string) | ||
1176 | { | ||
1177 | /** | ||
1178 | * Strings representing topologies in enum | ||
1179 | */ | ||
1180 | static const char *topology_strings[] = { | ||
1181 | /** | ||
1182 | * A clique (everyone connected to everyone else). | ||
1183 | */ | ||
1184 | "CLIQUE", | ||
1185 | |||
1186 | /** | ||
1187 | * Small-world network (2d torus plus random links). | ||
1188 | */ | ||
1189 | "SMALL_WORLD", | ||
1190 | |||
1191 | /** | ||
1192 | * Small-world network (ring plus random links). | ||
1193 | */ | ||
1194 | "SMALL_WORLD_RING", | ||
1195 | |||
1196 | /** | ||
1197 | * Ring topology. | ||
1198 | */ | ||
1199 | "RING", | ||
1200 | |||
1201 | /** | ||
1202 | * 2-d torus. | ||
1203 | */ | ||
1204 | "2D_TORUS", | ||
1205 | |||
1206 | /** | ||
1207 | * Random graph. | ||
1208 | */ | ||
1209 | "ERDOS_RENYI", | ||
1210 | |||
1211 | /** | ||
1212 | * Certain percentage of peers are unable to communicate directly | ||
1213 | * replicating NAT conditions | ||
1214 | */ | ||
1215 | "INTERNAT", | ||
1216 | |||
1217 | /** | ||
1218 | * Scale free topology. | ||
1219 | */ | ||
1220 | "SCALE_FREE", | ||
1221 | |||
1222 | /** | ||
1223 | * Straight line topology. | ||
1224 | */ | ||
1225 | "LINE", | ||
1226 | |||
1227 | /** | ||
1228 | * All peers are disconnected. | ||
1229 | */ | ||
1230 | "NONE", | ||
1231 | |||
1232 | /** | ||
1233 | * Read the topology from a file. | ||
1234 | */ | ||
1235 | "FROM_FILE", | ||
1236 | |||
1237 | NULL | ||
1238 | }; | ||
1239 | |||
1240 | int curr = 0; | ||
1241 | |||
1242 | if (topology_string == NULL) | ||
1243 | return GNUNET_NO; | ||
1244 | while (topology_strings[curr] != NULL) | ||
1245 | { | ||
1246 | if (strcasecmp (topology_strings[curr], topology_string) == 0) | ||
1247 | { | ||
1248 | *topology = curr; | ||
1249 | return GNUNET_YES; | ||
1250 | } | ||
1251 | curr++; | ||
1252 | } | ||
1253 | *topology = GNUNET_TESTING_TOPOLOGY_NONE; | ||
1254 | return GNUNET_NO; | ||
1255 | } | ||
1256 | |||
1257 | /** | ||
1258 | * Get connect topology option from string input. | ||
1259 | * | ||
1260 | * @param topology_option where to write the retrieved topology | ||
1261 | * @param topology_string The string to attempt to | ||
1262 | * get a configuration value from | ||
1263 | * @return GNUNET_YES if string matched a known | ||
1264 | * topology option, GNUNET_NO if not | ||
1265 | */ | ||
1266 | int | ||
1267 | GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption | ||
1268 | *topology_option, | ||
1269 | const char *topology_string) | ||
1270 | { | ||
1271 | /** | ||
1272 | * Options for connecting a topology as strings. | ||
1273 | */ | ||
1274 | static const char *topology_option_strings[] = { | ||
1275 | /** | ||
1276 | * Try to connect all peers specified in the topology. | ||
1277 | */ | ||
1278 | "CONNECT_ALL", | ||
1279 | |||
1280 | /** | ||
1281 | * Choose a random subset of connections to create. | ||
1282 | */ | ||
1283 | "CONNECT_RANDOM_SUBSET", | ||
1284 | |||
1285 | /** | ||
1286 | * Create at least X connections for each peer. | ||
1287 | */ | ||
1288 | "CONNECT_MINIMUM", | ||
1289 | |||
1290 | /** | ||
1291 | * Using a depth first search, create one connection | ||
1292 | * per peer. If any are missed (graph disconnected) | ||
1293 | * start over at those peers until all have at least one | ||
1294 | * connection. | ||
1295 | */ | ||
1296 | "CONNECT_DFS", | ||
1297 | |||
1298 | /** | ||
1299 | * Find the N closest peers to each allowed peer in the | ||
1300 | * topology and make sure a connection to those peers | ||
1301 | * exists in the connect topology. | ||
1302 | */ | ||
1303 | "CONNECT_CLOSEST", | ||
1304 | |||
1305 | /** | ||
1306 | * No options specified. | ||
1307 | */ | ||
1308 | "CONNECT_NONE", | ||
1309 | |||
1310 | NULL | ||
1311 | }; | ||
1312 | int curr = 0; | ||
1313 | |||
1314 | if (topology_string == NULL) | ||
1315 | return GNUNET_NO; | ||
1316 | while (NULL != topology_option_strings[curr]) | ||
1317 | { | ||
1318 | if (strcasecmp (topology_option_strings[curr], topology_string) == 0) | ||
1319 | { | ||
1320 | *topology_option = curr; | ||
1321 | return GNUNET_YES; | ||
1322 | } | ||
1323 | curr++; | ||
1324 | } | ||
1325 | *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE; | ||
1326 | return GNUNET_NO; | ||
1327 | } | ||
1328 | |||
1329 | /** | ||
1330 | * Function to iterate over options. Copies | ||
1331 | * the options to the target configuration, | ||
1332 | * updating PORT values as needed. | ||
1333 | * | ||
1334 | * @param cls closure | ||
1335 | * @param section name of the section | ||
1336 | * @param option name of the option | ||
1337 | * @param value value of the option | ||
1338 | */ | ||
1339 | static void | ||
1340 | update_config (void *cls, const char *section, const char *option, | ||
1341 | const char *value) | ||
1342 | { | ||
1343 | struct UpdateContext *ctx = cls; | ||
1344 | unsigned int ival; | ||
1345 | char cval[12]; | ||
1346 | char uval[128]; | ||
1347 | char *single_variable; | ||
1348 | char *per_host_variable; | ||
1349 | unsigned long long num_per_host; | ||
1350 | |||
1351 | GNUNET_asprintf (&single_variable, "single_%s_per_host", section); | ||
1352 | GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section); | ||
1353 | |||
1354 | if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival))) | ||
1355 | { | ||
1356 | if ((ival != 0) && | ||
1357 | (GNUNET_YES != | ||
1358 | GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing_old", | ||
1359 | single_variable))) | ||
1360 | { | ||
1361 | GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++); | ||
1362 | value = cval; | ||
1363 | } | ||
1364 | else if ((ival != 0) && | ||
1365 | (GNUNET_YES == | ||
1366 | GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing_old", | ||
1367 | single_variable)) && | ||
1368 | GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing_old", | ||
1369 | per_host_variable, | ||
1370 | &num_per_host)) | ||
1371 | { | ||
1372 | GNUNET_snprintf (cval, sizeof (cval), "%u", | ||
1373 | ival + ctx->fdnum % num_per_host); | ||
1374 | value = cval; | ||
1375 | } | ||
1376 | |||
1377 | /* FIXME: REMOVE FOREVER HACK HACK HACK */ | ||
1378 | if (0 == strcasecmp (section, "transport-tcp")) | ||
1379 | GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, | ||
1380 | "ADVERTISED_PORT", value); | ||
1381 | } | ||
1382 | |||
1383 | if (0 == strcmp (option, "UNIXPATH")) | ||
1384 | { | ||
1385 | if (GNUNET_YES != | ||
1386 | GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing_old", | ||
1387 | single_variable)) | ||
1388 | { | ||
1389 | GNUNET_snprintf (uval, sizeof (uval), "/tmp/test-service-%s-%u", section, | ||
1390 | ctx->upnum++); | ||
1391 | value = uval; | ||
1392 | } | ||
1393 | else if ((GNUNET_YES == | ||
1394 | GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing_old", | ||
1395 | per_host_variable, | ||
1396 | &num_per_host)) && | ||
1397 | (num_per_host > 0)) | ||
1398 | |||
1399 | { | ||
1400 | GNUNET_snprintf (uval, sizeof (uval), "/tmp/test-service-%s-%u", section, | ||
1401 | ctx->fdnum % num_per_host); | ||
1402 | value = uval; | ||
1403 | } | ||
1404 | } | ||
1405 | |||
1406 | if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL)) | ||
1407 | { | ||
1408 | value = ctx->hostname; | ||
1409 | } | ||
1410 | GNUNET_free (single_variable); | ||
1411 | GNUNET_free (per_host_variable); | ||
1412 | GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value); | ||
1413 | } | ||
1414 | |||
1415 | /** | ||
1416 | * Create a new configuration using the given configuration | ||
1417 | * as a template; however, each PORT in the existing cfg | ||
1418 | * must be renumbered by incrementing "*port". If we run | ||
1419 | * out of "*port" numbers, return NULL. | ||
1420 | * | ||
1421 | * @param cfg template configuration | ||
1422 | * @param off the current peer offset | ||
1423 | * @param port port numbers to use, update to reflect | ||
1424 | * port numbers that were used | ||
1425 | * @param upnum number to make unix domain socket names unique | ||
1426 | * @param hostname hostname of the controlling host, to allow control connections from | ||
1427 | * @param fdnum number used to offset the unix domain socket for grouped processes | ||
1428 | * (such as statistics or peerinfo, which can be shared among others) | ||
1429 | * | ||
1430 | * @return new configuration, NULL on error | ||
1431 | */ | ||
1432 | struct GNUNET_CONFIGURATION_Handle * | ||
1433 | GNUNET_TESTING_create_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, uint32_t off, | ||
1434 | uint16_t * port, uint32_t * upnum, const char *hostname, | ||
1435 | uint32_t * fdnum) | ||
1436 | { | ||
1437 | struct UpdateContext uc; | ||
1438 | uint16_t orig; | ||
1439 | char *control_host; | ||
1440 | char *allowed_hosts; | ||
1441 | unsigned long long skew_variance; | ||
1442 | unsigned long long skew_offset; | ||
1443 | long long actual_offset; | ||
1444 | |||
1445 | orig = *port; | ||
1446 | uc.nport = *port; | ||
1447 | uc.upnum = *upnum; | ||
1448 | uc.fdnum = *fdnum; | ||
1449 | uc.ret = GNUNET_CONFIGURATION_create (); | ||
1450 | uc.hostname = hostname; | ||
1451 | uc.orig = cfg; | ||
1452 | |||
1453 | GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc); | ||
1454 | if (uc.nport >= HIGH_PORT) | ||
1455 | { | ||
1456 | *port = orig; | ||
1457 | GNUNET_CONFIGURATION_destroy (uc.ret); | ||
1458 | return NULL; | ||
1459 | } | ||
1460 | |||
1461 | if ((GNUNET_OK == | ||
1462 | GNUNET_CONFIGURATION_get_value_number (cfg, "testing_old", "skew_variance", | ||
1463 | &skew_variance)) && | ||
1464 | (skew_variance > 0)) | ||
1465 | { | ||
1466 | skew_offset = | ||
1467 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1468 | skew_variance + 1); | ||
1469 | actual_offset = | ||
1470 | skew_offset - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1471 | skew_variance + 1); | ||
1472 | /* Min is -skew_variance, Max is skew_variance */ | ||
1473 | skew_offset = skew_variance + actual_offset; /* Normal distribution around 0 */ | ||
1474 | GNUNET_CONFIGURATION_set_value_number (uc.ret, "testing_old", "skew_offset", | ||
1475 | skew_offset); | ||
1476 | } | ||
1477 | |||
1478 | if (GNUNET_CONFIGURATION_get_value_string | ||
1479 | (cfg, "testing_old", "control_host", &control_host) == GNUNET_OK) | ||
1480 | { | ||
1481 | if (hostname != NULL) | ||
1482 | GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1; %s;", control_host, | ||
1483 | hostname); | ||
1484 | else | ||
1485 | GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", control_host); | ||
1486 | |||
1487 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "ACCEPT_FROM", | ||
1488 | allowed_hosts); | ||
1489 | |||
1490 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nse", "ACCEPT_FROM", | ||
1491 | allowed_hosts); | ||
1492 | |||
1493 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport", "ACCEPT_FROM", | ||
1494 | allowed_hosts); | ||
1495 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "ACCEPT_FROM", | ||
1496 | allowed_hosts); | ||
1497 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics", "ACCEPT_FROM", | ||
1498 | allowed_hosts); | ||
1499 | |||
1500 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "UNIXPATH", ""); | ||
1501 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport", "UNIXPATH", ""); | ||
1502 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "UNIXPATH", ""); | ||
1503 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics", "UNIXPATH", | ||
1504 | ""); | ||
1505 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nse", "UNIXPATH", ""); | ||
1506 | |||
1507 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", | ||
1508 | "USE_LOCALADDR", "YES"); | ||
1509 | GNUNET_free_non_null (control_host); | ||
1510 | GNUNET_free (allowed_hosts); | ||
1511 | } | ||
1512 | |||
1513 | /* arm needs to know to allow connections from the host on which it is running, | ||
1514 | * otherwise gnunet-arm is unable to connect to it in some instances */ | ||
1515 | if (hostname != NULL) | ||
1516 | { | ||
1517 | GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", hostname); | ||
1518 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "BINDTO", hostname); | ||
1519 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "INTERNAL_ADDRESS", | ||
1520 | hostname); | ||
1521 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "EXTERNAL_ADDRESS", | ||
1522 | hostname); | ||
1523 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "disablev6", "BINDTO", | ||
1524 | "YES"); | ||
1525 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", | ||
1526 | "USE_LOCALADDR", "YES"); | ||
1527 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", | ||
1528 | "USE_LOCALADDR", "YES"); | ||
1529 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "arm", "ACCEPT_FROM", | ||
1530 | allowed_hosts); | ||
1531 | GNUNET_free (allowed_hosts); | ||
1532 | } | ||
1533 | else | ||
1534 | { | ||
1535 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", | ||
1536 | "USE_LOCALADDR", "YES"); | ||
1537 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "BINDTO", | ||
1538 | "127.0.0.1"); | ||
1539 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "INTERNAL_ADDRESS", | ||
1540 | "127.0.0.1"); | ||
1541 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "EXTERNAL_ADDRESS", | ||
1542 | "127.0.0.1"); | ||
1543 | GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "disablev6", | ||
1544 | "YES"); | ||
1545 | } | ||
1546 | |||
1547 | *port = (uint16_t) uc.nport; | ||
1548 | *upnum = uc.upnum; | ||
1549 | uc.fdnum++; | ||
1550 | *fdnum = uc.fdnum; | ||
1551 | return uc.ret; | ||
1552 | } | ||
1553 | |||
1554 | /* | ||
1555 | * Remove entries from the peer connection list | ||
1556 | * | ||
1557 | * @param pg the peer group we are working with | ||
1558 | * @param first index of the first peer | ||
1559 | * @param second index of the second peer | ||
1560 | * @param list the peer list to use | ||
1561 | * @param check UNUSED | ||
1562 | * | ||
1563 | * @return the number of connections added (can be 0, 1 or 2) | ||
1564 | * | ||
1565 | */ | ||
1566 | static unsigned int | ||
1567 | remove_connections (struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, | ||
1568 | unsigned int second, enum PeerLists list, | ||
1569 | unsigned int check) | ||
1570 | { | ||
1571 | int removed; | ||
1572 | |||
1573 | #if OLD | ||
1574 | struct PeerConnection **first_list; | ||
1575 | struct PeerConnection **second_list; | ||
1576 | struct PeerConnection *first_iter; | ||
1577 | struct PeerConnection *second_iter; | ||
1578 | struct PeerConnection **first_tail; | ||
1579 | struct PeerConnection **second_tail; | ||
1580 | |||
1581 | #else | ||
1582 | GNUNET_HashCode hash_first; | ||
1583 | GNUNET_HashCode hash_second; | ||
1584 | |||
1585 | hash_from_uid (first, &hash_first); | ||
1586 | hash_from_uid (second, &hash_second); | ||
1587 | #endif | ||
1588 | |||
1589 | removed = 0; | ||
1590 | #if OLD | ||
1591 | switch (list) | ||
1592 | { | ||
1593 | case ALLOWED: | ||
1594 | first_list = &pg->peers[first].allowed_peers_head; | ||
1595 | second_list = &pg->peers[second].allowed_peers_head; | ||
1596 | first_tail = &pg->peers[first].allowed_peers_tail; | ||
1597 | second_tail = &pg->peers[second].allowed_peers_tail; | ||
1598 | break; | ||
1599 | case CONNECT: | ||
1600 | first_list = &pg->peers[first].connect_peers_head; | ||
1601 | second_list = &pg->peers[second].connect_peers_head; | ||
1602 | first_tail = &pg->peers[first].connect_peers_tail; | ||
1603 | second_tail = &pg->peers[second].connect_peers_tail; | ||
1604 | break; | ||
1605 | case BLACKLIST: | ||
1606 | first_list = &pg->peers[first].blacklisted_peers_head; | ||
1607 | second_list = &pg->peers[second].blacklisted_peers_head; | ||
1608 | first_tail = &pg->peers[first].blacklisted_peers_tail; | ||
1609 | second_tail = &pg->peers[second].blacklisted_peers_tail; | ||
1610 | break; | ||
1611 | case WORKING_SET: | ||
1612 | first_list = &pg->peers[first].connect_peers_working_set_head; | ||
1613 | second_list = &pg->peers[second].connect_peers_working_set_head; | ||
1614 | first_tail = &pg->peers[first].connect_peers_working_set_tail; | ||
1615 | second_tail = &pg->peers[second].connect_peers_working_set_tail; | ||
1616 | break; | ||
1617 | default: | ||
1618 | GNUNET_break (0); | ||
1619 | return 0; | ||
1620 | } | ||
1621 | |||
1622 | first_iter = *first_list; | ||
1623 | while (first_iter != NULL) | ||
1624 | { | ||
1625 | if (first_iter->index == second) | ||
1626 | { | ||
1627 | GNUNET_CONTAINER_DLL_remove (*first_list, *first_tail, first_iter); | ||
1628 | GNUNET_free (first_iter); | ||
1629 | removed++; | ||
1630 | break; | ||
1631 | } | ||
1632 | first_iter = first_iter->next; | ||
1633 | } | ||
1634 | |||
1635 | second_iter = *second_list; | ||
1636 | while (second_iter != NULL) | ||
1637 | { | ||
1638 | if (second_iter->index == first) | ||
1639 | { | ||
1640 | GNUNET_CONTAINER_DLL_remove (*second_list, *second_tail, second_iter); | ||
1641 | GNUNET_free (second_iter); | ||
1642 | removed++; | ||
1643 | break; | ||
1644 | } | ||
1645 | second_iter = second_iter->next; | ||
1646 | } | ||
1647 | #else | ||
1648 | if (GNUNET_YES == | ||
1649 | GNUNET_CONTAINER_multihashmap_contains (pg-> | ||
1650 | peers[first].blacklisted_peers, | ||
1651 | &hash_second)) | ||
1652 | { | ||
1653 | GNUNET_CONTAINER_multihashmap_remove_all (pg-> | ||
1654 | peers[first].blacklisted_peers, | ||
1655 | &hash_second); | ||
1656 | } | ||
1657 | |||
1658 | if (GNUNET_YES == | ||
1659 | GNUNET_CONTAINER_multihashmap_contains (pg-> | ||
1660 | peers[second].blacklisted_peers, | ||
1661 | &hash_first)) | ||
1662 | { | ||
1663 | GNUNET_CONTAINER_multihashmap_remove_all (pg-> | ||
1664 | peers[second].blacklisted_peers, | ||
1665 | &hash_first); | ||
1666 | } | ||
1667 | #endif | ||
1668 | |||
1669 | return removed; | ||
1670 | } | ||
1671 | |||
1672 | /** | ||
1673 | * Add entries to the some list | ||
1674 | * | ||
1675 | * @param pg the peer group we are working with | ||
1676 | * @param first index of the first peer | ||
1677 | * @param second index of the second peer | ||
1678 | * @param list the list type that we should modify | ||
1679 | * @param check GNUNET_YES to check lists before adding | ||
1680 | * GNUNET_NO to force add | ||
1681 | * | ||
1682 | * @return the number of connections added (can be 0, 1 or 2) | ||
1683 | * | ||
1684 | */ | ||
1685 | static unsigned int | ||
1686 | add_connections (struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, | ||
1687 | unsigned int second, enum PeerLists list, unsigned int check) | ||
1688 | { | ||
1689 | int added; | ||
1690 | int add_first; | ||
1691 | int add_second; | ||
1692 | |||
1693 | struct PeerConnection **first_list; | ||
1694 | struct PeerConnection **second_list; | ||
1695 | struct PeerConnection *first_iter; | ||
1696 | struct PeerConnection *second_iter; | ||
1697 | struct PeerConnection *new_first; | ||
1698 | struct PeerConnection *new_second; | ||
1699 | struct PeerConnection **first_tail; | ||
1700 | struct PeerConnection **second_tail; | ||
1701 | |||
1702 | switch (list) | ||
1703 | { | ||
1704 | case ALLOWED: | ||
1705 | first_list = &pg->peers[first].allowed_peers_head; | ||
1706 | second_list = &pg->peers[second].allowed_peers_head; | ||
1707 | first_tail = &pg->peers[first].allowed_peers_tail; | ||
1708 | second_tail = &pg->peers[second].allowed_peers_tail; | ||
1709 | break; | ||
1710 | case CONNECT: | ||
1711 | first_list = &pg->peers[first].connect_peers_head; | ||
1712 | second_list = &pg->peers[second].connect_peers_head; | ||
1713 | first_tail = &pg->peers[first].connect_peers_tail; | ||
1714 | second_tail = &pg->peers[second].connect_peers_tail; | ||
1715 | break; | ||
1716 | case BLACKLIST: | ||
1717 | first_list = &pg->peers[first].blacklisted_peers_head; | ||
1718 | second_list = &pg->peers[second].blacklisted_peers_head; | ||
1719 | first_tail = &pg->peers[first].blacklisted_peers_tail; | ||
1720 | second_tail = &pg->peers[second].blacklisted_peers_tail; | ||
1721 | break; | ||
1722 | case WORKING_SET: | ||
1723 | first_list = &pg->peers[first].connect_peers_working_set_head; | ||
1724 | second_list = &pg->peers[second].connect_peers_working_set_head; | ||
1725 | first_tail = &pg->peers[first].connect_peers_working_set_tail; | ||
1726 | second_tail = &pg->peers[second].connect_peers_working_set_tail; | ||
1727 | break; | ||
1728 | default: | ||
1729 | GNUNET_break (0); | ||
1730 | return 0; | ||
1731 | } | ||
1732 | |||
1733 | add_first = GNUNET_YES; | ||
1734 | add_second = GNUNET_YES; | ||
1735 | |||
1736 | if (check == GNUNET_YES) | ||
1737 | { | ||
1738 | first_iter = *first_list; | ||
1739 | while (first_iter != NULL) | ||
1740 | { | ||
1741 | if (first_iter->index == second) | ||
1742 | { | ||
1743 | add_first = GNUNET_NO; | ||
1744 | break; | ||
1745 | } | ||
1746 | first_iter = first_iter->next; | ||
1747 | } | ||
1748 | |||
1749 | second_iter = *second_list; | ||
1750 | while (second_iter != NULL) | ||
1751 | { | ||
1752 | if (second_iter->index == first) | ||
1753 | { | ||
1754 | add_second = GNUNET_NO; | ||
1755 | break; | ||
1756 | } | ||
1757 | second_iter = second_iter->next; | ||
1758 | } | ||
1759 | } | ||
1760 | |||
1761 | added = 0; | ||
1762 | if (add_first) | ||
1763 | { | ||
1764 | new_first = GNUNET_malloc (sizeof (struct PeerConnection)); | ||
1765 | new_first->index = second; | ||
1766 | GNUNET_CONTAINER_DLL_insert (*first_list, *first_tail, new_first); | ||
1767 | pg->peers[first].num_connections++; | ||
1768 | added++; | ||
1769 | } | ||
1770 | |||
1771 | if (add_second) | ||
1772 | { | ||
1773 | new_second = GNUNET_malloc (sizeof (struct PeerConnection)); | ||
1774 | new_second->index = first; | ||
1775 | GNUNET_CONTAINER_DLL_insert (*second_list, *second_tail, new_second); | ||
1776 | pg->peers[second].num_connections++; | ||
1777 | added++; | ||
1778 | } | ||
1779 | |||
1780 | return added; | ||
1781 | } | ||
1782 | |||
1783 | /** | ||
1784 | * Scale free network construction as described in: | ||
1785 | * | ||
1786 | * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999. | ||
1787 | * | ||
1788 | * Start with a network of "one" peer, then progressively add | ||
1789 | * peers up to the total number. At each step, iterate over | ||
1790 | * all possible peers and connect new peer based on number of | ||
1791 | * existing connections of the target peer. | ||
1792 | * | ||
1793 | * @param pg the peer group we are dealing with | ||
1794 | * @param proc the connection processor to use | ||
1795 | * @param list the peer list to use | ||
1796 | * | ||
1797 | * @return the number of connections created | ||
1798 | */ | ||
1799 | static unsigned int | ||
1800 | create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, | ||
1801 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list) | ||
1802 | { | ||
1803 | |||
1804 | unsigned int total_connections; | ||
1805 | unsigned int outer_count; | ||
1806 | unsigned int i; | ||
1807 | unsigned int previous_total_connections; | ||
1808 | double random; | ||
1809 | double probability; | ||
1810 | |||
1811 | GNUNET_assert (pg->total > 1); | ||
1812 | |||
1813 | /* Add a connection between the first two nodes */ | ||
1814 | total_connections = proc (pg, 0, 1, list, GNUNET_YES); | ||
1815 | |||
1816 | for (outer_count = 1; outer_count < pg->total; outer_count++) | ||
1817 | { | ||
1818 | previous_total_connections = total_connections; | ||
1819 | for (i = 0; i < outer_count; i++) | ||
1820 | { | ||
1821 | probability = | ||
1822 | pg->peers[i].num_connections / (double) previous_total_connections; | ||
1823 | random = | ||
1824 | ((double) | ||
1825 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1826 | UINT64_MAX)) / ((double) UINT64_MAX); | ||
1827 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1828 | "Considering connecting peer %d to peer %d\n", outer_count, | ||
1829 | i); | ||
1830 | if (random < probability) | ||
1831 | { | ||
1832 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", | ||
1833 | outer_count, i); | ||
1834 | total_connections += proc (pg, outer_count, i, list, GNUNET_YES); | ||
1835 | } | ||
1836 | } | ||
1837 | } | ||
1838 | |||
1839 | return total_connections; | ||
1840 | } | ||
1841 | |||
1842 | /** | ||
1843 | * Create a topology given a peer group (set of running peers) | ||
1844 | * and a connection processor. Creates a small world topology | ||
1845 | * according to the rewired ring construction. The basic | ||
1846 | * behavior is that a ring topology is created, but with some | ||
1847 | * probability instead of connecting a peer to the next | ||
1848 | * neighbor in the ring a connection will be created to a peer | ||
1849 | * selected uniformly at random. We use the TESTING | ||
1850 | * PERCENTAGE option to specify what number of | ||
1851 | * connections each peer should have. Default is 2, | ||
1852 | * which makes the ring, any given number is multiplied by | ||
1853 | * the log of the network size; i.e. a PERCENTAGE of 2 makes | ||
1854 | * each peer have on average 2logn connections. The additional | ||
1855 | * connections are made at increasing distance around the ring | ||
1856 | * from the original peer, or to random peers based on the re- | ||
1857 | * wiring probability. The TESTING | ||
1858 | * PROBABILITY option is used as the probability that a given | ||
1859 | * connection is rewired. | ||
1860 | * | ||
1861 | * @param pg the peergroup to create the topology on | ||
1862 | * @param proc the connection processor to call to actually set | ||
1863 | * up connections between two peers | ||
1864 | * @param list the peer list to use | ||
1865 | * | ||
1866 | * @return the number of connections that were set up | ||
1867 | * | ||
1868 | */ | ||
1869 | static unsigned int | ||
1870 | create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg, | ||
1871 | GNUNET_TESTING_ConnectionProcessor proc, | ||
1872 | enum PeerLists list) | ||
1873 | { | ||
1874 | unsigned int i, j; | ||
1875 | int nodeToConnect; | ||
1876 | unsigned int natLog; | ||
1877 | unsigned int randomPeer; | ||
1878 | double random, logNModifier, probability; | ||
1879 | unsigned int smallWorldConnections; | ||
1880 | int connsPerPeer; | ||
1881 | char *p_string; | ||
1882 | int max; | ||
1883 | int min; | ||
1884 | unsigned int useAnd; | ||
1885 | int connect_attempts; | ||
1886 | |||
1887 | logNModifier = 0.5; /* FIXME: default value? */ | ||
1888 | if (GNUNET_OK == | ||
1889 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PERCENTAGE", | ||
1890 | &p_string)) | ||
1891 | { | ||
1892 | if (SSCANF (p_string, "%lf", &logNModifier) != 1) | ||
1893 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1894 | _ | ||
1895 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
1896 | p_string, "LOGNMODIFIER", "TESTING_OLD"); | ||
1897 | GNUNET_free (p_string); | ||
1898 | } | ||
1899 | probability = 0.5; /* FIXME: default percentage? */ | ||
1900 | if (GNUNET_OK == | ||
1901 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PROBABILITY", | ||
1902 | &p_string)) | ||
1903 | { | ||
1904 | if (SSCANF (p_string, "%lf", &probability) != 1) | ||
1905 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1906 | _ | ||
1907 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
1908 | p_string, "PERCENTAGE", "TESTING_OLD"); | ||
1909 | GNUNET_free (p_string); | ||
1910 | } | ||
1911 | natLog = log (pg->total); | ||
1912 | connsPerPeer = ceil (natLog * logNModifier); | ||
1913 | |||
1914 | if (connsPerPeer % 2 == 1) | ||
1915 | connsPerPeer += 1; | ||
1916 | |||
1917 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Target is %d connections per peer.", | ||
1918 | connsPerPeer); | ||
1919 | |||
1920 | smallWorldConnections = 0; | ||
1921 | connect_attempts = 0; | ||
1922 | for (i = 0; i < pg->total; i++) | ||
1923 | { | ||
1924 | useAnd = 0; | ||
1925 | max = i + connsPerPeer / 2; | ||
1926 | min = i - connsPerPeer / 2; | ||
1927 | |||
1928 | if (max > pg->total - 1) | ||
1929 | { | ||
1930 | max = max - pg->total; | ||
1931 | useAnd = 1; | ||
1932 | } | ||
1933 | |||
1934 | if (min < 0) | ||
1935 | { | ||
1936 | min = pg->total - 1 + min; | ||
1937 | useAnd = 1; | ||
1938 | } | ||
1939 | |||
1940 | for (j = 0; j < connsPerPeer / 2; j++) | ||
1941 | { | ||
1942 | random = | ||
1943 | ((double) | ||
1944 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
1945 | UINT64_MAX) / ((double) UINT64_MAX)); | ||
1946 | if (random < probability) | ||
1947 | { | ||
1948 | /* Connect to uniformly selected random peer */ | ||
1949 | randomPeer = | ||
1950 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, pg->total); | ||
1951 | while ((((randomPeer < max) && (randomPeer > min)) && (useAnd == 0)) || | ||
1952 | (((randomPeer > min) || (randomPeer < max)) && (useAnd == 1))) | ||
1953 | { | ||
1954 | randomPeer = | ||
1955 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, pg->total); | ||
1956 | } | ||
1957 | smallWorldConnections += proc (pg, i, randomPeer, list, GNUNET_YES); | ||
1958 | } | ||
1959 | else | ||
1960 | { | ||
1961 | nodeToConnect = i + j + 1; | ||
1962 | if (nodeToConnect > pg->total - 1) | ||
1963 | { | ||
1964 | nodeToConnect = nodeToConnect - pg->total; | ||
1965 | } | ||
1966 | connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES); | ||
1967 | } | ||
1968 | } | ||
1969 | |||
1970 | } | ||
1971 | |||
1972 | connect_attempts += smallWorldConnections; | ||
1973 | |||
1974 | return connect_attempts; | ||
1975 | } | ||
1976 | |||
1977 | /** | ||
1978 | * Create a topology given a peer group (set of running peers) | ||
1979 | * and a connection processor. | ||
1980 | * | ||
1981 | * @param pg the peergroup to create the topology on | ||
1982 | * @param proc the connection processor to call to actually set | ||
1983 | * up connections between two peers | ||
1984 | * @param list the peer list to use | ||
1985 | * | ||
1986 | * @return the number of connections that were set up | ||
1987 | * | ||
1988 | */ | ||
1989 | static unsigned int | ||
1990 | create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, | ||
1991 | GNUNET_TESTING_ConnectionProcessor proc, | ||
1992 | enum PeerLists list) | ||
1993 | { | ||
1994 | unsigned int outer_count, inner_count; | ||
1995 | unsigned int cutoff; | ||
1996 | int connect_attempts; | ||
1997 | double nat_percentage; | ||
1998 | char *p_string; | ||
1999 | |||
2000 | nat_percentage = 0.6; /* FIXME: default percentage? */ | ||
2001 | if (GNUNET_OK == | ||
2002 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PERCENTAGE", | ||
2003 | &p_string)) | ||
2004 | { | ||
2005 | if (SSCANF (p_string, "%lf", &nat_percentage) != 1) | ||
2006 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2007 | _ | ||
2008 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
2009 | p_string, "PERCENTAGE", "TESTING_OLD"); | ||
2010 | GNUNET_free (p_string); | ||
2011 | } | ||
2012 | |||
2013 | cutoff = (unsigned int) (nat_percentage * pg->total); | ||
2014 | connect_attempts = 0; | ||
2015 | for (outer_count = 0; outer_count < pg->total - 1; outer_count++) | ||
2016 | { | ||
2017 | for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++) | ||
2018 | { | ||
2019 | if ((outer_count > cutoff) || (inner_count > cutoff)) | ||
2020 | { | ||
2021 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", | ||
2022 | outer_count, inner_count); | ||
2023 | connect_attempts += | ||
2024 | proc (pg, outer_count, inner_count, list, GNUNET_YES); | ||
2025 | } | ||
2026 | } | ||
2027 | } | ||
2028 | return connect_attempts; | ||
2029 | } | ||
2030 | |||
2031 | #if TOPOLOGY_HACK | ||
2032 | /** | ||
2033 | * Create a topology given a peer group (set of running peers) | ||
2034 | * and a connection processor. | ||
2035 | * | ||
2036 | * @param pg the peergroup to create the topology on | ||
2037 | * @param proc the connection processor to call to actually set | ||
2038 | * up connections between two peers | ||
2039 | * @param list the peer list to use | ||
2040 | * | ||
2041 | * @return the number of connections that were set up | ||
2042 | * | ||
2043 | */ | ||
2044 | static unsigned int | ||
2045 | create_nated_internet_copy (struct GNUNET_TESTING_PeerGroup *pg, | ||
2046 | GNUNET_TESTING_ConnectionProcessor proc, | ||
2047 | enum PeerLists list) | ||
2048 | { | ||
2049 | unsigned int outer_count, inner_count; | ||
2050 | unsigned int cutoff; | ||
2051 | int connect_attempts; | ||
2052 | double nat_percentage; | ||
2053 | char *p_string; | ||
2054 | unsigned int count; | ||
2055 | struct ProgressMeter *conn_meter; | ||
2056 | |||
2057 | nat_percentage = 0.6; /* FIXME: default percentage? */ | ||
2058 | if (GNUNET_OK == | ||
2059 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PERCENTAGE", | ||
2060 | &p_string)) | ||
2061 | { | ||
2062 | if (SSCANF (p_string, "%lf", &nat_percentage) != 1) | ||
2063 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2064 | _ | ||
2065 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
2066 | p_string, "PERCENTAGE", "TESTING_OLD"); | ||
2067 | GNUNET_free (p_string); | ||
2068 | } | ||
2069 | |||
2070 | cutoff = (unsigned int) (nat_percentage * pg->total); | ||
2071 | count = 0; | ||
2072 | for (outer_count = 0; outer_count < pg->total - 1; outer_count++) | ||
2073 | { | ||
2074 | for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++) | ||
2075 | { | ||
2076 | if ((outer_count > cutoff) || (inner_count > cutoff)) | ||
2077 | { | ||
2078 | count++; | ||
2079 | } | ||
2080 | } | ||
2081 | } | ||
2082 | conn_meter = create_meter (count, "NAT COPY", GNUNET_YES); | ||
2083 | connect_attempts = 0; | ||
2084 | for (outer_count = 0; outer_count < pg->total - 1; outer_count++) | ||
2085 | { | ||
2086 | for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++) | ||
2087 | { | ||
2088 | if ((outer_count > cutoff) || (inner_count > cutoff)) | ||
2089 | { | ||
2090 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", | ||
2091 | outer_count, inner_count); | ||
2092 | connect_attempts += | ||
2093 | proc (pg, outer_count, inner_count, list, GNUNET_YES); | ||
2094 | add_connections (pg, outer_count, inner_count, ALLOWED, GNUNET_NO); | ||
2095 | update_meter (conn_meter); | ||
2096 | } | ||
2097 | } | ||
2098 | } | ||
2099 | free_meter (conn_meter); | ||
2100 | |||
2101 | return connect_attempts; | ||
2102 | } | ||
2103 | #endif | ||
2104 | |||
2105 | /** | ||
2106 | * Create a topology given a peer group (set of running peers) | ||
2107 | * and a connection processor. | ||
2108 | * | ||
2109 | * @param pg the peergroup to create the topology on | ||
2110 | * @param proc the connection processor to call to actually set | ||
2111 | * up connections between two peers | ||
2112 | * @param list the peer list to use | ||
2113 | * | ||
2114 | * @return the number of connections that were set up | ||
2115 | * | ||
2116 | */ | ||
2117 | static unsigned int | ||
2118 | create_small_world (struct GNUNET_TESTING_PeerGroup *pg, | ||
2119 | GNUNET_TESTING_ConnectionProcessor proc, | ||
2120 | enum PeerLists list) | ||
2121 | { | ||
2122 | unsigned int i, j, k; | ||
2123 | unsigned int square; | ||
2124 | unsigned int rows; | ||
2125 | unsigned int cols; | ||
2126 | unsigned int toggle = 1; | ||
2127 | unsigned int nodeToConnect; | ||
2128 | unsigned int natLog; | ||
2129 | unsigned int node1Row; | ||
2130 | unsigned int node1Col; | ||
2131 | unsigned int node2Row; | ||
2132 | unsigned int node2Col; | ||
2133 | unsigned int distance; | ||
2134 | double probability, random, percentage; | ||
2135 | unsigned int smallWorldConnections; | ||
2136 | unsigned int small_world_it; | ||
2137 | char *p_string; | ||
2138 | int connect_attempts; | ||
2139 | |||
2140 | square = floor (sqrt (pg->total)); | ||
2141 | rows = square; | ||
2142 | cols = square; | ||
2143 | |||
2144 | percentage = 0.5; /* FIXME: default percentage? */ | ||
2145 | if (GNUNET_OK == | ||
2146 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PERCENTAGE", | ||
2147 | &p_string)) | ||
2148 | { | ||
2149 | if (SSCANF (p_string, "%lf", &percentage) != 1) | ||
2150 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2151 | _ | ||
2152 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
2153 | p_string, "PERCENTAGE", "TESTING_OLD"); | ||
2154 | GNUNET_free (p_string); | ||
2155 | } | ||
2156 | if (percentage < 0.0) | ||
2157 | { | ||
2158 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2159 | _ | ||
2160 | ("Invalid value `%s' for option `%s' in section `%s': got %f, needed value greater than 0\n"), | ||
2161 | "PERCENTAGE", "TESTING_OLD", percentage); | ||
2162 | percentage = 0.5; | ||
2163 | } | ||
2164 | probability = 0.5; /* FIXME: default percentage? */ | ||
2165 | if (GNUNET_OK == | ||
2166 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PROBABILITY", | ||
2167 | &p_string)) | ||
2168 | { | ||
2169 | if (SSCANF (p_string, "%lf", &probability) != 1) | ||
2170 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2171 | _ | ||
2172 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
2173 | p_string, "PROBABILITY", "TESTING_OLD"); | ||
2174 | GNUNET_free (p_string); | ||
2175 | } | ||
2176 | if (square * square != pg->total) | ||
2177 | { | ||
2178 | while (rows * cols < pg->total) | ||
2179 | { | ||
2180 | if (toggle % 2 == 0) | ||
2181 | rows++; | ||
2182 | else | ||
2183 | cols++; | ||
2184 | |||
2185 | toggle++; | ||
2186 | } | ||
2187 | } | ||
2188 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2189 | "Connecting nodes in 2d torus topology: %u rows %u columns\n", | ||
2190 | rows, cols); | ||
2191 | connect_attempts = 0; | ||
2192 | /* Rows and columns are all sorted out, now iterate over all nodes and connect each | ||
2193 | * to the node to its right and above. Once this is over, we'll have our torus! | ||
2194 | * Special case for the last node (if the rows and columns are not equal), connect | ||
2195 | * to the first in the row to maintain topology. | ||
2196 | */ | ||
2197 | for (i = 0; i < pg->total; i++) | ||
2198 | { | ||
2199 | /* First connect to the node to the right */ | ||
2200 | if (((i + 1) % cols != 0) && (i + 1 != pg->total)) | ||
2201 | nodeToConnect = i + 1; | ||
2202 | else if (i + 1 == pg->total) | ||
2203 | nodeToConnect = rows * cols - cols; | ||
2204 | else | ||
2205 | nodeToConnect = i - cols + 1; | ||
2206 | |||
2207 | connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES); | ||
2208 | |||
2209 | if (i < cols) | ||
2210 | { | ||
2211 | nodeToConnect = (rows * cols) - cols + i; | ||
2212 | if (nodeToConnect >= pg->total) | ||
2213 | nodeToConnect -= cols; | ||
2214 | } | ||
2215 | else | ||
2216 | nodeToConnect = i - cols; | ||
2217 | |||
2218 | if (nodeToConnect < pg->total) | ||
2219 | connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES); | ||
2220 | } | ||
2221 | natLog = log (pg->total); | ||
2222 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2223 | "natural log of %d is %d, will run %d iterations\n", pg->total, | ||
2224 | natLog, (int) (natLog * percentage)); | ||
2225 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2226 | "Total connections added thus far: %u!\n", connect_attempts); | ||
2227 | smallWorldConnections = 0; | ||
2228 | small_world_it = (unsigned int) (natLog * percentage); | ||
2229 | if (small_world_it < 1) | ||
2230 | small_world_it = 1; | ||
2231 | GNUNET_assert (small_world_it > 0 && small_world_it < (unsigned int) -1); | ||
2232 | for (i = 0; i < small_world_it; i++) | ||
2233 | { | ||
2234 | for (j = 0; j < pg->total; j++) | ||
2235 | { | ||
2236 | /* Determine the row and column of node at position j on the 2d torus */ | ||
2237 | node1Row = j / cols; | ||
2238 | node1Col = j - (node1Row * cols); | ||
2239 | for (k = 0; k < pg->total; k++) | ||
2240 | { | ||
2241 | /* Determine the row and column of node at position k on the 2d torus */ | ||
2242 | node2Row = k / cols; | ||
2243 | node2Col = k - (node2Row * cols); | ||
2244 | /* Simple Cartesian distance */ | ||
2245 | distance = abs (node1Row - node2Row) + abs (node1Col - node2Col); | ||
2246 | if (distance > 1) | ||
2247 | { | ||
2248 | /* Calculate probability as 1 over the square of the distance */ | ||
2249 | probability = 1.0 / (distance * distance); | ||
2250 | /* Choose a random value between 0 and 1 */ | ||
2251 | random = | ||
2252 | ((double) | ||
2253 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
2254 | UINT64_MAX)) / ((double) UINT64_MAX); | ||
2255 | /* If random < probability, then connect the two nodes */ | ||
2256 | if (random < probability) | ||
2257 | smallWorldConnections += proc (pg, j, k, list, GNUNET_YES); | ||
2258 | |||
2259 | } | ||
2260 | } | ||
2261 | } | ||
2262 | } | ||
2263 | connect_attempts += smallWorldConnections; | ||
2264 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2265 | "Total connections added for small world: %d!\n", | ||
2266 | smallWorldConnections); | ||
2267 | return connect_attempts; | ||
2268 | } | ||
2269 | |||
2270 | /** | ||
2271 | * Create a topology given a peer group (set of running peers) | ||
2272 | * and a connection processor. | ||
2273 | * | ||
2274 | * @param pg the peergroup to create the topology on | ||
2275 | * @param proc the connection processor to call to actually set | ||
2276 | * up connections between two peers | ||
2277 | * @param list the peer list to use | ||
2278 | * | ||
2279 | * @return the number of connections that were set up | ||
2280 | * | ||
2281 | */ | ||
2282 | static unsigned int | ||
2283 | create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, | ||
2284 | GNUNET_TESTING_ConnectionProcessor proc, | ||
2285 | enum PeerLists list) | ||
2286 | { | ||
2287 | double temp_rand; | ||
2288 | unsigned int outer_count; | ||
2289 | unsigned int inner_count; | ||
2290 | int connect_attempts; | ||
2291 | double probability; | ||
2292 | char *p_string; | ||
2293 | |||
2294 | probability = 0.5; /* FIXME: default percentage? */ | ||
2295 | if (GNUNET_OK == | ||
2296 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING_OLD", "PROBABILITY", | ||
2297 | &p_string)) | ||
2298 | { | ||
2299 | if (SSCANF (p_string, "%lf", &probability) != 1) | ||
2300 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2301 | _ | ||
2302 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
2303 | p_string, "PROBABILITY", "TESTING_OLD"); | ||
2304 | GNUNET_free (p_string); | ||
2305 | } | ||
2306 | connect_attempts = 0; | ||
2307 | for (outer_count = 0; outer_count < pg->total - 1; outer_count++) | ||
2308 | { | ||
2309 | for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++) | ||
2310 | { | ||
2311 | temp_rand = | ||
2312 | ((double) | ||
2313 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
2314 | UINT64_MAX)) / ((double) UINT64_MAX); | ||
2315 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "rand is %f probability is %f\n", | ||
2316 | temp_rand, probability); | ||
2317 | if (temp_rand < probability) | ||
2318 | { | ||
2319 | connect_attempts += | ||
2320 | proc (pg, outer_count, inner_count, list, GNUNET_YES); | ||
2321 | } | ||
2322 | } | ||
2323 | } | ||
2324 | |||
2325 | return connect_attempts; | ||
2326 | } | ||
2327 | |||
2328 | /** | ||
2329 | * Create a topology given a peer group (set of running peers) | ||
2330 | * and a connection processor. This particular function creates | ||
2331 | * the connections for a 2d-torus, plus additional "closest" | ||
2332 | * connections per peer. | ||
2333 | * | ||
2334 | * @param pg the peergroup to create the topology on | ||
2335 | * @param proc the connection processor to call to actually set | ||
2336 | * up connections between two peers | ||
2337 | * @param list the peer list to use | ||
2338 | * | ||
2339 | * @return the number of connections that were set up | ||
2340 | * | ||
2341 | */ | ||
2342 | static unsigned int | ||
2343 | create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, | ||
2344 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list) | ||
2345 | { | ||
2346 | unsigned int i; | ||
2347 | unsigned int square; | ||
2348 | unsigned int rows; | ||
2349 | unsigned int cols; | ||
2350 | unsigned int toggle = 1; | ||
2351 | unsigned int nodeToConnect; | ||
2352 | int connect_attempts; | ||
2353 | |||
2354 | connect_attempts = 0; | ||
2355 | |||
2356 | square = floor (sqrt (pg->total)); | ||
2357 | rows = square; | ||
2358 | cols = square; | ||
2359 | |||
2360 | if (square * square != pg->total) | ||
2361 | { | ||
2362 | while (rows * cols < pg->total) | ||
2363 | { | ||
2364 | if (toggle % 2 == 0) | ||
2365 | rows++; | ||
2366 | else | ||
2367 | cols++; | ||
2368 | |||
2369 | toggle++; | ||
2370 | } | ||
2371 | } | ||
2372 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2373 | "Connecting nodes in 2d torus topology: %u rows %u columns\n", | ||
2374 | rows, cols); | ||
2375 | /* Rows and columns are all sorted out, now iterate over all nodes and connect each | ||
2376 | * to the node to its right and above. Once this is over, we'll have our torus! | ||
2377 | * Special case for the last node (if the rows and columns are not equal), connect | ||
2378 | * to the first in the row to maintain topology. | ||
2379 | */ | ||
2380 | for (i = 0; i < pg->total; i++) | ||
2381 | { | ||
2382 | /* First connect to the node to the right */ | ||
2383 | if (((i + 1) % cols != 0) && (i + 1 != pg->total)) | ||
2384 | nodeToConnect = i + 1; | ||
2385 | else if (i + 1 == pg->total) | ||
2386 | nodeToConnect = rows * cols - cols; | ||
2387 | else | ||
2388 | nodeToConnect = i - cols + 1; | ||
2389 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", i, | ||
2390 | nodeToConnect); | ||
2391 | connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES); | ||
2392 | |||
2393 | /* Second connect to the node immediately above */ | ||
2394 | if (i < cols) | ||
2395 | { | ||
2396 | nodeToConnect = (rows * cols) - cols + i; | ||
2397 | if (nodeToConnect >= pg->total) | ||
2398 | nodeToConnect -= cols; | ||
2399 | } | ||
2400 | else | ||
2401 | nodeToConnect = i - cols; | ||
2402 | |||
2403 | if (nodeToConnect < pg->total) | ||
2404 | { | ||
2405 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", i, | ||
2406 | nodeToConnect); | ||
2407 | connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES); | ||
2408 | } | ||
2409 | |||
2410 | } | ||
2411 | |||
2412 | return connect_attempts; | ||
2413 | } | ||
2414 | |||
2415 | /** | ||
2416 | * Create a topology given a peer group (set of running peers) | ||
2417 | * and a connection processor. | ||
2418 | * | ||
2419 | * @param pg the peergroup to create the topology on | ||
2420 | * @param proc the connection processor to call to actually set | ||
2421 | * up connections between two peers | ||
2422 | * @param list the peer list to use | ||
2423 | * @param check does the connection processor need to check before | ||
2424 | * performing an action on the list? | ||
2425 | * | ||
2426 | * @return the number of connections that were set up | ||
2427 | * | ||
2428 | */ | ||
2429 | static unsigned int | ||
2430 | create_clique (struct GNUNET_TESTING_PeerGroup *pg, | ||
2431 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list, | ||
2432 | unsigned int check) | ||
2433 | { | ||
2434 | unsigned int outer_count; | ||
2435 | unsigned int inner_count; | ||
2436 | int connect_attempts; | ||
2437 | struct ProgressMeter *conn_meter; | ||
2438 | |||
2439 | connect_attempts = 0; | ||
2440 | |||
2441 | conn_meter = | ||
2442 | create_meter ((((pg->total * pg->total) + pg->total) / 2) - pg->total, | ||
2443 | "Create Clique ", GNUNET_NO); | ||
2444 | for (outer_count = 0; outer_count < pg->total - 1; outer_count++) | ||
2445 | { | ||
2446 | for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++) | ||
2447 | { | ||
2448 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", | ||
2449 | outer_count, inner_count); | ||
2450 | connect_attempts += proc (pg, outer_count, inner_count, list, check); | ||
2451 | update_meter (conn_meter); | ||
2452 | } | ||
2453 | } | ||
2454 | reset_meter (conn_meter); | ||
2455 | free_meter (conn_meter); | ||
2456 | return connect_attempts; | ||
2457 | } | ||
2458 | |||
2459 | #if !OLD | ||
2460 | /** | ||
2461 | * Iterator over hash map entries. | ||
2462 | * | ||
2463 | * @param cls closure the peer group | ||
2464 | * @param key the key stored in the hashmap is the | ||
2465 | * index of the peer to connect to | ||
2466 | * @param value value in the hash map, handle to the peer daemon | ||
2467 | * @return GNUNET_YES if we should continue to | ||
2468 | * iterate, | ||
2469 | * GNUNET_NO if not. | ||
2470 | */ | ||
2471 | static int | ||
2472 | unblacklist_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
2473 | { | ||
2474 | struct UnblacklistContext *un_ctx = cls; | ||
2475 | uint32_t second_pos; | ||
2476 | |||
2477 | uid_from_hash (key, &second_pos); | ||
2478 | |||
2479 | unblacklist_connections (un_ctx->pg, un_ctx->first_uid, second_pos); | ||
2480 | |||
2481 | return GNUNET_YES; | ||
2482 | } | ||
2483 | #endif | ||
2484 | |||
2485 | #if !OLD | ||
2486 | /** | ||
2487 | * Create a blacklist topology based on the allowed topology | ||
2488 | * which disallows any connections not in the allowed topology | ||
2489 | * at the transport level. | ||
2490 | * | ||
2491 | * @param pg the peergroup to create the topology on | ||
2492 | * @param proc the connection processor to call to allow | ||
2493 | * up connections between two peers | ||
2494 | * | ||
2495 | * @return the number of connections that were set up | ||
2496 | * | ||
2497 | */ | ||
2498 | static unsigned int | ||
2499 | copy_allowed (struct GNUNET_TESTING_PeerGroup *pg, | ||
2500 | GNUNET_TESTING_ConnectionProcessor proc) | ||
2501 | { | ||
2502 | unsigned int count; | ||
2503 | unsigned int total; | ||
2504 | struct PeerConnection *iter; | ||
2505 | |||
2506 | #if !OLD | ||
2507 | struct UnblacklistContext un_ctx; | ||
2508 | |||
2509 | un_ctx.pg = pg; | ||
2510 | #endif | ||
2511 | total = 0; | ||
2512 | for (count = 0; count < pg->total - 1; count++) | ||
2513 | { | ||
2514 | #if OLD | ||
2515 | iter = pg->peers[count].allowed_peers_head; | ||
2516 | while (iter != NULL) | ||
2517 | { | ||
2518 | remove_connections (pg, count, iter->index, BLACKLIST, GNUNET_YES); | ||
2519 | //unblacklist_connections(pg, count, iter->index); | ||
2520 | iter = iter->next; | ||
2521 | } | ||
2522 | #else | ||
2523 | un_ctx.first_uid = count; | ||
2524 | total += | ||
2525 | GNUNET_CONTAINER_multihashmap_iterate (pg->peers[count].allowed_peers, | ||
2526 | &unblacklist_iterator, &un_ctx); | ||
2527 | #endif | ||
2528 | } | ||
2529 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unblacklisted %u peers\n", total); | ||
2530 | return total; | ||
2531 | } | ||
2532 | #endif | ||
2533 | |||
2534 | /** | ||
2535 | * Create a topology given a peer group (set of running peers) | ||
2536 | * and a connection processor. | ||
2537 | * | ||
2538 | * @param pg the peergroup to create the topology on | ||
2539 | * @param proc the connection processor to call to actually set | ||
2540 | * up connections between two peers | ||
2541 | * @param list which list should be modified | ||
2542 | * | ||
2543 | * @return the number of connections that were set up | ||
2544 | * | ||
2545 | */ | ||
2546 | static unsigned int | ||
2547 | create_line (struct GNUNET_TESTING_PeerGroup *pg, | ||
2548 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list) | ||
2549 | { | ||
2550 | unsigned int count; | ||
2551 | unsigned int connect_attempts; | ||
2552 | |||
2553 | connect_attempts = 0; | ||
2554 | /* Connect each peer to the next highest numbered peer */ | ||
2555 | for (count = 0; count < pg->total - 1; count++) | ||
2556 | { | ||
2557 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", | ||
2558 | count, count + 1); | ||
2559 | connect_attempts += proc (pg, count, count + 1, list, GNUNET_YES); | ||
2560 | } | ||
2561 | |||
2562 | return connect_attempts; | ||
2563 | } | ||
2564 | |||
2565 | /** | ||
2566 | * Create a topology given a peer group (set of running peers) | ||
2567 | * and a connection processor. | ||
2568 | * | ||
2569 | * @param pg the peergroup to create the topology on | ||
2570 | * @param filename the file to read topology information from | ||
2571 | * @param proc the connection processor to call to actually set | ||
2572 | * up connections between two peers | ||
2573 | * @param list the peer list to use | ||
2574 | * | ||
2575 | * @return the number of connections that were set up | ||
2576 | * | ||
2577 | */ | ||
2578 | static unsigned int | ||
2579 | create_from_file (struct GNUNET_TESTING_PeerGroup *pg, char *filename, | ||
2580 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list) | ||
2581 | { | ||
2582 | int connect_attempts; | ||
2583 | unsigned int first_peer_index; | ||
2584 | unsigned int second_peer_index; | ||
2585 | struct stat frstat; | ||
2586 | int count; | ||
2587 | char *data; | ||
2588 | const char *buf; | ||
2589 | unsigned int total_peers; | ||
2590 | enum States curr_state; | ||
2591 | |||
2592 | connect_attempts = 0; | ||
2593 | if (GNUNET_OK != GNUNET_DISK_file_test (filename)) | ||
2594 | GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ); | ||
2595 | |||
2596 | if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0)) | ||
2597 | { | ||
2598 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2599 | "Could not open file `%s' specified for topology!", filename); | ||
2600 | return connect_attempts; | ||
2601 | } | ||
2602 | |||
2603 | data = GNUNET_malloc_large (frstat.st_size); | ||
2604 | GNUNET_assert (data != NULL); | ||
2605 | if (frstat.st_size != GNUNET_DISK_fn_read (filename, data, frstat.st_size)) | ||
2606 | { | ||
2607 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2608 | "Could not read file %s specified for host list, ending test!", | ||
2609 | filename); | ||
2610 | GNUNET_free (data); | ||
2611 | return connect_attempts; | ||
2612 | } | ||
2613 | |||
2614 | buf = data; | ||
2615 | count = 0; | ||
2616 | first_peer_index = 0; | ||
2617 | /* First line should contain a single integer, specifying the number of peers */ | ||
2618 | /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */ | ||
2619 | curr_state = NUM_PEERS; | ||
2620 | while (count < frstat.st_size - 1) | ||
2621 | { | ||
2622 | if ((buf[count] == '\n') || (buf[count] == ' ')) | ||
2623 | { | ||
2624 | count++; | ||
2625 | continue; | ||
2626 | } | ||
2627 | |||
2628 | switch (curr_state) | ||
2629 | { | ||
2630 | case NUM_PEERS: | ||
2631 | errno = 0; | ||
2632 | total_peers = strtoul (&buf[count], NULL, 10); | ||
2633 | if (errno != 0) | ||
2634 | { | ||
2635 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2636 | "Failed to read number of peers from topology file!\n"); | ||
2637 | GNUNET_free (data); | ||
2638 | return connect_attempts; | ||
2639 | } | ||
2640 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found %u total peers in topology\n", | ||
2641 | total_peers); | ||
2642 | GNUNET_assert (total_peers == pg->total); | ||
2643 | curr_state = PEER_INDEX; | ||
2644 | while ((buf[count] != '\n') && (count < frstat.st_size - 1)) | ||
2645 | count++; | ||
2646 | count++; | ||
2647 | break; | ||
2648 | case PEER_INDEX: | ||
2649 | errno = 0; | ||
2650 | first_peer_index = strtoul (&buf[count], NULL, 10); | ||
2651 | if (errno != 0) | ||
2652 | { | ||
2653 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2654 | "Failed to read peer index from topology file!\n"); | ||
2655 | GNUNET_free (data); | ||
2656 | return connect_attempts; | ||
2657 | } | ||
2658 | while ((buf[count] != ':') && (count < frstat.st_size - 1)) | ||
2659 | count++; | ||
2660 | count++; | ||
2661 | curr_state = OTHER_PEER_INDEX; | ||
2662 | break; | ||
2663 | case COLON: | ||
2664 | if (1 == sscanf (&buf[count], ":")) | ||
2665 | curr_state = OTHER_PEER_INDEX; | ||
2666 | count++; | ||
2667 | break; | ||
2668 | case OTHER_PEER_INDEX: | ||
2669 | errno = 0; | ||
2670 | second_peer_index = strtoul (&buf[count], NULL, 10); | ||
2671 | if (errno != 0) | ||
2672 | { | ||
2673 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2674 | "Failed to peer index from topology file!\n"); | ||
2675 | GNUNET_free (data); | ||
2676 | return connect_attempts; | ||
2677 | } | ||
2678 | /* Assume file is written with first peer 1, but array index is 0 */ | ||
2679 | connect_attempts += | ||
2680 | proc (pg, first_peer_index - 1, second_peer_index - 1, list, | ||
2681 | GNUNET_YES); | ||
2682 | while ((buf[count] != '\n') && (buf[count] != ',') && | ||
2683 | (count < frstat.st_size - 1)) | ||
2684 | count++; | ||
2685 | if (buf[count] == '\n') | ||
2686 | { | ||
2687 | curr_state = PEER_INDEX; | ||
2688 | } | ||
2689 | else if (buf[count] != ',') | ||
2690 | { | ||
2691 | curr_state = OTHER_PEER_INDEX; | ||
2692 | } | ||
2693 | count++; | ||
2694 | break; | ||
2695 | default: | ||
2696 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2697 | "Found bad data in topology file while in state %d!\n", | ||
2698 | curr_state); | ||
2699 | GNUNET_break (0); | ||
2700 | GNUNET_free (data); | ||
2701 | return connect_attempts; | ||
2702 | } | ||
2703 | } | ||
2704 | GNUNET_free (data); | ||
2705 | return connect_attempts; | ||
2706 | } | ||
2707 | |||
2708 | /** | ||
2709 | * Create a topology given a peer group (set of running peers) | ||
2710 | * and a connection processor. | ||
2711 | * | ||
2712 | * @param pg the peergroup to create the topology on | ||
2713 | * @param proc the connection processor to call to actually set | ||
2714 | * up connections between two peers | ||
2715 | * @param list the peer list to use | ||
2716 | * | ||
2717 | * @return the number of connections that were set up | ||
2718 | * | ||
2719 | */ | ||
2720 | static unsigned int | ||
2721 | create_ring (struct GNUNET_TESTING_PeerGroup *pg, | ||
2722 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list) | ||
2723 | { | ||
2724 | unsigned int count; | ||
2725 | int connect_attempts; | ||
2726 | |||
2727 | connect_attempts = 0; | ||
2728 | |||
2729 | /* Connect each peer to the next highest numbered peer */ | ||
2730 | for (count = 0; count < pg->total - 1; count++) | ||
2731 | { | ||
2732 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %d to peer %d\n", | ||
2733 | count, count + 1); | ||
2734 | connect_attempts += proc (pg, count, count + 1, list, GNUNET_YES); | ||
2735 | } | ||
2736 | |||
2737 | /* Connect the last peer to the first peer */ | ||
2738 | connect_attempts += proc (pg, pg->total - 1, 0, list, GNUNET_YES); | ||
2739 | |||
2740 | return connect_attempts; | ||
2741 | } | ||
2742 | |||
2743 | #if !OLD | ||
2744 | /** | ||
2745 | * Iterator for writing friends of a peer to a file. | ||
2746 | * | ||
2747 | * @param cls closure, an open writable file handle | ||
2748 | * @param key the key the daemon was stored under | ||
2749 | * @param value the GNUNET_TESTING_Daemon that needs to be written. | ||
2750 | * | ||
2751 | * @return GNUNET_YES to continue iteration | ||
2752 | * | ||
2753 | * TODO: Could replace friend_file_iterator and blacklist_file_iterator | ||
2754 | * with a single file_iterator that takes a closure which contains | ||
2755 | * the prefix to write before the peer. Then this could be used | ||
2756 | * for blacklisting multiple transports and writing the friend | ||
2757 | * file. I'm sure *someone* will complain loudly about other | ||
2758 | * things that negate these functions even existing so no point in | ||
2759 | * "fixing" now. | ||
2760 | */ | ||
2761 | static int | ||
2762 | friend_file_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
2763 | { | ||
2764 | FILE *temp_friend_handle = cls; | ||
2765 | struct GNUNET_TESTING_Daemon *peer = value; | ||
2766 | struct GNUNET_PeerIdentity *temppeer; | ||
2767 | struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; | ||
2768 | |||
2769 | temppeer = &peer->id; | ||
2770 | GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc); | ||
2771 | FPRINTF (temp_friend_handle, "%s\n", (char *) &peer_enc); | ||
2772 | |||
2773 | return GNUNET_YES; | ||
2774 | } | ||
2775 | |||
2776 | struct BlacklistContext | ||
2777 | { | ||
2778 | /* | ||
2779 | * The (open) file handle to write to | ||
2780 | */ | ||
2781 | FILE *temp_file_handle; | ||
2782 | |||
2783 | /* | ||
2784 | * The transport that this peer will be blacklisted on. | ||
2785 | */ | ||
2786 | char *transport; | ||
2787 | }; | ||
2788 | |||
2789 | /** | ||
2790 | * Iterator for writing blacklist data to appropriate files. | ||
2791 | * | ||
2792 | * @param cls closure, an open writable file handle | ||
2793 | * @param key the key the daemon was stored under | ||
2794 | * @param value the GNUNET_TESTING_Daemon that needs to be written. | ||
2795 | * | ||
2796 | * @return GNUNET_YES to continue iteration | ||
2797 | */ | ||
2798 | static int | ||
2799 | blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
2800 | { | ||
2801 | struct BlacklistContext *blacklist_ctx = cls; | ||
2802 | struct GNUNET_TESTING_Daemon *peer = value; | ||
2803 | struct GNUNET_PeerIdentity *temppeer; | ||
2804 | struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; | ||
2805 | |||
2806 | temppeer = &peer->id; | ||
2807 | GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc); | ||
2808 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", | ||
2809 | blacklist_ctx->transport, (char *) &peer_enc); | ||
2810 | FPRINTF (blacklist_ctx->temp_file_handle, "%s:%s\n", blacklist_ctx->transport, | ||
2811 | (char *) &peer_enc); | ||
2812 | |||
2813 | return GNUNET_YES; | ||
2814 | } | ||
2815 | #endif | ||
2816 | |||
2817 | /* | ||
2818 | * Create the friend files based on the PeerConnection's | ||
2819 | * of each peer in the peer group, and copy the files | ||
2820 | * to the appropriate place | ||
2821 | * | ||
2822 | * @param pg the peer group we are dealing with | ||
2823 | */ | ||
2824 | static int | ||
2825 | create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg) | ||
2826 | { | ||
2827 | FILE *temp_friend_handle; | ||
2828 | unsigned int pg_iter; | ||
2829 | char *temp_service_path; | ||
2830 | struct GNUNET_OS_Process **procarr; | ||
2831 | char *arg; | ||
2832 | char *mytemp; | ||
2833 | |||
2834 | #if NOT_STUPID | ||
2835 | enum GNUNET_OS_ProcessStatusType type; | ||
2836 | unsigned long return_code; | ||
2837 | int count; | ||
2838 | int max_wait = 10; | ||
2839 | #endif | ||
2840 | int ret; | ||
2841 | |||
2842 | ret = GNUNET_OK; | ||
2843 | #if OLD | ||
2844 | struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; | ||
2845 | struct PeerConnection *conn_iter; | ||
2846 | #endif | ||
2847 | procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total); | ||
2848 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
2849 | { | ||
2850 | mytemp = GNUNET_DISK_mktemp ("friends"); | ||
2851 | GNUNET_assert (mytemp != NULL); | ||
2852 | temp_friend_handle = FOPEN (mytemp, "wt"); | ||
2853 | GNUNET_assert (temp_friend_handle != NULL); | ||
2854 | #if OLD | ||
2855 | conn_iter = pg->peers[pg_iter].allowed_peers_head; | ||
2856 | while (conn_iter != NULL) | ||
2857 | { | ||
2858 | GNUNET_CRYPTO_hash_to_enc (&pg->peers[conn_iter->index].daemon-> | ||
2859 | id.hashPubKey, &peer_enc); | ||
2860 | FPRINTF (temp_friend_handle, "%s\n", (char *) &peer_enc); | ||
2861 | conn_iter = conn_iter->next; | ||
2862 | } | ||
2863 | #else | ||
2864 | GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers, | ||
2865 | &friend_file_iterator, | ||
2866 | temp_friend_handle); | ||
2867 | #endif | ||
2868 | FCLOSE (temp_friend_handle); | ||
2869 | |||
2870 | if (GNUNET_OK != | ||
2871 | GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].daemon->cfg, | ||
2872 | "PATHS", "SERVICEHOME", | ||
2873 | &temp_service_path)) | ||
2874 | { | ||
2875 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2876 | _ | ||
2877 | ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"), | ||
2878 | "SERVICEHOME", "PATHS"); | ||
2879 | if (UNLINK (mytemp) != 0) | ||
2880 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp); | ||
2881 | GNUNET_free (mytemp); | ||
2882 | break; | ||
2883 | } | ||
2884 | |||
2885 | if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */ | ||
2886 | { | ||
2887 | GNUNET_asprintf (&arg, "%s/friends", temp_service_path); | ||
2888 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2889 | "Copying file with RENAME(%s,%s)\n", mytemp, arg); | ||
2890 | RENAME (mytemp, arg); | ||
2891 | procarr[pg_iter] = NULL; | ||
2892 | GNUNET_free (arg); | ||
2893 | } | ||
2894 | else /* Remote, scp the file to the correct place */ | ||
2895 | { | ||
2896 | if (NULL != pg->peers[pg_iter].daemon->username) | ||
2897 | GNUNET_asprintf (&arg, "%s@%s:%s/friends", | ||
2898 | pg->peers[pg_iter].daemon->username, | ||
2899 | pg->peers[pg_iter].daemon->hostname, | ||
2900 | temp_service_path); | ||
2901 | else | ||
2902 | GNUNET_asprintf (&arg, "%s:%s/friends", | ||
2903 | pg->peers[pg_iter].daemon->hostname, | ||
2904 | temp_service_path); | ||
2905 | procarr[pg_iter] = | ||
2906 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", mytemp, arg, NULL); | ||
2907 | GNUNET_assert (procarr[pg_iter] != NULL); | ||
2908 | ret = GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: schedule this, throttle! */ | ||
2909 | GNUNET_OS_process_destroy (procarr[pg_iter]); | ||
2910 | if (ret != GNUNET_OK) | ||
2911 | { | ||
2912 | /* FIXME: free contents of 'procarr' array */ | ||
2913 | GNUNET_free (procarr); | ||
2914 | GNUNET_free (temp_service_path); | ||
2915 | GNUNET_free (mytemp); | ||
2916 | GNUNET_free (arg); | ||
2917 | return ret; | ||
2918 | } | ||
2919 | procarr[pg_iter] = NULL; | ||
2920 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2921 | "Copying file with command scp %s %s\n", mytemp, arg); | ||
2922 | GNUNET_free (arg); | ||
2923 | } | ||
2924 | GNUNET_free (temp_service_path); | ||
2925 | GNUNET_free (mytemp); | ||
2926 | } | ||
2927 | |||
2928 | #if NOT_STUPID | ||
2929 | count = 0; | ||
2930 | ret = GNUNET_SYSERR; | ||
2931 | while ((count < max_wait) && (ret != GNUNET_OK)) | ||
2932 | { | ||
2933 | ret = GNUNET_OK; | ||
2934 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
2935 | { | ||
2936 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking copy status of file %d\n", | ||
2937 | pg_iter); | ||
2938 | if (procarr[pg_iter] != NULL) /* Check for already completed! */ | ||
2939 | { | ||
2940 | if (GNUNET_OS_process_status (procarr[pg_iter], &type, &return_code) != | ||
2941 | GNUNET_OK) | ||
2942 | { | ||
2943 | ret = GNUNET_SYSERR; | ||
2944 | } | ||
2945 | else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0)) | ||
2946 | { | ||
2947 | ret = GNUNET_SYSERR; | ||
2948 | } | ||
2949 | else | ||
2950 | { | ||
2951 | GNUNET_OS_process_destroy (procarr[pg_iter]); | ||
2952 | procarr[pg_iter] = NULL; | ||
2953 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "File %d copied\n", pg_iter); | ||
2954 | } | ||
2955 | } | ||
2956 | } | ||
2957 | count++; | ||
2958 | if (ret == GNUNET_SYSERR) | ||
2959 | { | ||
2960 | /* FIXME: why sleep here? -CG */ | ||
2961 | sleep (1); | ||
2962 | } | ||
2963 | } | ||
2964 | |||
2965 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2966 | "Finished copying all friend files!\n"); | ||
2967 | #endif | ||
2968 | GNUNET_free (procarr); | ||
2969 | return ret; | ||
2970 | } | ||
2971 | |||
2972 | /* | ||
2973 | * Create the blacklist files based on the PeerConnection's | ||
2974 | * of each peer in the peer group, and copy the files | ||
2975 | * to the appropriate place. | ||
2976 | * | ||
2977 | * @param pg the peer group we are dealing with | ||
2978 | * @param transports space delimited list of transports to blacklist | ||
2979 | */ | ||
2980 | static int | ||
2981 | create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, | ||
2982 | const char *transports) | ||
2983 | { | ||
2984 | FILE *temp_file_handle; | ||
2985 | unsigned int pg_iter; | ||
2986 | char *temp_service_path; | ||
2987 | struct GNUNET_OS_Process **procarr; | ||
2988 | char *arg; | ||
2989 | char *mytemp; | ||
2990 | enum GNUNET_OS_ProcessStatusType type; | ||
2991 | unsigned long return_code; | ||
2992 | int count; | ||
2993 | int ret; | ||
2994 | int max_wait = 10; | ||
2995 | int transport_len; | ||
2996 | unsigned int i; | ||
2997 | char *pos; | ||
2998 | char *temp_transports; | ||
2999 | |||
3000 | #if OLD | ||
3001 | struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; | ||
3002 | struct PeerConnection *conn_iter; | ||
3003 | #else | ||
3004 | static struct BlacklistContext blacklist_ctx; | ||
3005 | #endif | ||
3006 | |||
3007 | procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total); | ||
3008 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
3009 | { | ||
3010 | mytemp = GNUNET_DISK_mktemp ("blacklist"); | ||
3011 | GNUNET_assert (mytemp != NULL); | ||
3012 | temp_file_handle = FOPEN (mytemp, "wt"); | ||
3013 | GNUNET_assert (temp_file_handle != NULL); | ||
3014 | temp_transports = GNUNET_strdup (transports); | ||
3015 | #if !OLD | ||
3016 | blacklist_ctx.temp_file_handle = temp_file_handle; | ||
3017 | #endif | ||
3018 | transport_len = strlen (temp_transports) + 1; | ||
3019 | pos = NULL; | ||
3020 | |||
3021 | for (i = 0; i < transport_len; i++) | ||
3022 | { | ||
3023 | if ((temp_transports[i] == ' ') && (pos == NULL)) | ||
3024 | continue; /* At start of string (whitespace) */ | ||
3025 | else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */ | ||
3026 | { | ||
3027 | temp_transports[i] = '\0'; | ||
3028 | #if OLD | ||
3029 | conn_iter = pg->peers[pg_iter].blacklisted_peers_head; | ||
3030 | while (conn_iter != NULL) | ||
3031 | { | ||
3032 | GNUNET_CRYPTO_hash_to_enc (&pg->peers[conn_iter->index].daemon-> | ||
3033 | id.hashPubKey, &peer_enc); | ||
3034 | FPRINTF (temp_file_handle, "%s:%s\n", pos, (char *) &peer_enc); | ||
3035 | conn_iter = conn_iter->next; | ||
3036 | } | ||
3037 | #else | ||
3038 | blacklist_ctx.transport = pos; | ||
3039 | (void) GNUNET_CONTAINER_multihashmap_iterate (pg-> | ||
3040 | peers | ||
3041 | [pg_iter].blacklisted_peers, | ||
3042 | &blacklist_file_iterator, | ||
3043 | &blacklist_ctx); | ||
3044 | #endif | ||
3045 | pos = NULL; | ||
3046 | } /* At beginning of actual string */ | ||
3047 | else if (pos == NULL) | ||
3048 | { | ||
3049 | pos = &temp_transports[i]; | ||
3050 | } | ||
3051 | } | ||
3052 | |||
3053 | GNUNET_free (temp_transports); | ||
3054 | FCLOSE (temp_file_handle); | ||
3055 | |||
3056 | if (GNUNET_OK != | ||
3057 | GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].daemon->cfg, | ||
3058 | "PATHS", "SERVICEHOME", | ||
3059 | &temp_service_path)) | ||
3060 | { | ||
3061 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3062 | _ | ||
3063 | ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"), | ||
3064 | "SERVICEHOME", "PATHS"); | ||
3065 | if (UNLINK (mytemp) != 0) | ||
3066 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp); | ||
3067 | GNUNET_free (mytemp); | ||
3068 | break; | ||
3069 | } | ||
3070 | |||
3071 | if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */ | ||
3072 | { | ||
3073 | GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path); | ||
3074 | RENAME (mytemp, arg); | ||
3075 | procarr[pg_iter] = NULL; | ||
3076 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3077 | "Copying file with RENAME (%s,%s)\n", mytemp, arg); | ||
3078 | GNUNET_free (arg); | ||
3079 | } | ||
3080 | else /* Remote, scp the file to the correct place */ | ||
3081 | { | ||
3082 | if (NULL != pg->peers[pg_iter].daemon->username) | ||
3083 | GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", | ||
3084 | pg->peers[pg_iter].daemon->username, | ||
3085 | pg->peers[pg_iter].daemon->hostname, | ||
3086 | temp_service_path); | ||
3087 | else | ||
3088 | GNUNET_asprintf (&arg, "%s:%s/blacklist", | ||
3089 | pg->peers[pg_iter].daemon->hostname, | ||
3090 | temp_service_path); | ||
3091 | procarr[pg_iter] = | ||
3092 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "scp", "scp", mytemp, arg, NULL); | ||
3093 | GNUNET_assert (procarr[pg_iter] != NULL); | ||
3094 | GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: add scheduled blacklist file copy that parallelizes file copying! */ | ||
3095 | |||
3096 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3097 | "Copying file with command scp %s %s\n", mytemp, arg); | ||
3098 | GNUNET_free (arg); | ||
3099 | } | ||
3100 | GNUNET_free (temp_service_path); | ||
3101 | GNUNET_free (mytemp); | ||
3102 | } | ||
3103 | |||
3104 | count = 0; | ||
3105 | ret = GNUNET_SYSERR; | ||
3106 | while ((count < max_wait) && (ret != GNUNET_OK)) | ||
3107 | { | ||
3108 | ret = GNUNET_OK; | ||
3109 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
3110 | { | ||
3111 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3112 | "Checking copy status of file %d\n", pg_iter); | ||
3113 | if (procarr[pg_iter] != NULL) /* Check for already completed! */ | ||
3114 | { | ||
3115 | if (GNUNET_OS_process_status (procarr[pg_iter], &type, &return_code) != | ||
3116 | GNUNET_OK) | ||
3117 | { | ||
3118 | ret = GNUNET_SYSERR; | ||
3119 | } | ||
3120 | else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0)) | ||
3121 | { | ||
3122 | ret = GNUNET_SYSERR; | ||
3123 | } | ||
3124 | else | ||
3125 | { | ||
3126 | GNUNET_OS_process_destroy (procarr[pg_iter]); | ||
3127 | procarr[pg_iter] = NULL; | ||
3128 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "File %d copied\n", pg_iter); | ||
3129 | } | ||
3130 | } | ||
3131 | } | ||
3132 | count++; | ||
3133 | if (ret == GNUNET_SYSERR) | ||
3134 | { | ||
3135 | /* FIXME: why sleep here? -CG */ | ||
3136 | sleep (1); | ||
3137 | } | ||
3138 | } | ||
3139 | |||
3140 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3141 | "Finished copying all blacklist files!\n"); | ||
3142 | GNUNET_free (procarr); | ||
3143 | return ret; | ||
3144 | } | ||
3145 | |||
3146 | /* Forward Declaration */ | ||
3147 | static void | ||
3148 | schedule_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
3149 | |||
3150 | /** | ||
3151 | * Choose a random peer's next connection to create, and | ||
3152 | * call schedule_connect to set up the connect task. | ||
3153 | * | ||
3154 | * @param pg the peer group to connect | ||
3155 | */ | ||
3156 | static void | ||
3157 | preschedule_connect (struct GNUNET_TESTING_PeerGroup *pg) | ||
3158 | { | ||
3159 | struct ConnectTopologyContext *ct_ctx = &pg->ct_ctx; | ||
3160 | struct PeerConnection *connection_iter; | ||
3161 | struct ConnectContext *connect_context; | ||
3162 | uint32_t random_peer; | ||
3163 | |||
3164 | if (ct_ctx->remaining_connections == 0) | ||
3165 | return; | ||
3166 | random_peer = | ||
3167 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, pg->total); | ||
3168 | while (pg->peers[random_peer].connect_peers_head == NULL) | ||
3169 | random_peer = | ||
3170 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, pg->total); | ||
3171 | |||
3172 | connection_iter = pg->peers[random_peer].connect_peers_head; | ||
3173 | connect_context = GNUNET_malloc (sizeof (struct ConnectContext)); | ||
3174 | connect_context->first_index = random_peer; | ||
3175 | connect_context->second_index = connection_iter->index; | ||
3176 | connect_context->ct_ctx = ct_ctx; | ||
3177 | connect_context->task = | ||
3178 | GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context); | ||
3179 | GNUNET_CONTAINER_DLL_insert (pg->cc_head, pg->cc_tail, connect_context); | ||
3180 | GNUNET_CONTAINER_DLL_remove (pg->peers[random_peer].connect_peers_head, | ||
3181 | pg->peers[random_peer].connect_peers_tail, | ||
3182 | connection_iter); | ||
3183 | GNUNET_free (connection_iter); | ||
3184 | ct_ctx->remaining_connections--; | ||
3185 | } | ||
3186 | |||
3187 | #if USE_SEND_HELLOS | ||
3188 | /* Forward declaration */ | ||
3189 | static void | ||
3190 | schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
3191 | |||
3192 | /** | ||
3193 | * Close connections and free the hello context. | ||
3194 | * | ||
3195 | * @param cls the 'struct SendHelloContext *' | ||
3196 | * @param tc scheduler context | ||
3197 | */ | ||
3198 | static void | ||
3199 | free_hello_context (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
3200 | { | ||
3201 | struct SendHelloContext *send_hello_context = cls; | ||
3202 | |||
3203 | if (send_hello_context->peer->daemon->server != NULL) | ||
3204 | { | ||
3205 | GNUNET_CORE_disconnect (send_hello_context->peer->daemon->server); | ||
3206 | send_hello_context->peer->daemon->server = NULL; | ||
3207 | } | ||
3208 | if (send_hello_context->peer->daemon->th != NULL) | ||
3209 | { | ||
3210 | GNUNET_TRANSPORT_disconnect (send_hello_context->peer->daemon->th); | ||
3211 | send_hello_context->peer->daemon->th = NULL; | ||
3212 | } | ||
3213 | if (send_hello_context->core_connect_task != GNUNET_SCHEDULER_NO_TASK) | ||
3214 | { | ||
3215 | GNUNET_SCHEDULER_cancel (send_hello_context->core_connect_task); | ||
3216 | send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK; | ||
3217 | } | ||
3218 | send_hello_context->pg->outstanding_connects--; | ||
3219 | GNUNET_free (send_hello_context); | ||
3220 | } | ||
3221 | |||
3222 | /** | ||
3223 | * For peers that haven't yet connected, notify | ||
3224 | * the caller that they have failed (timeout). | ||
3225 | * | ||
3226 | * @param cls the 'struct SendHelloContext *' | ||
3227 | * @param tc scheduler context | ||
3228 | */ | ||
3229 | static void | ||
3230 | notify_remaining_connections_failed (void *cls, | ||
3231 | const struct GNUNET_SCHEDULER_TaskContext | ||
3232 | *tc) | ||
3233 | { | ||
3234 | struct SendHelloContext *send_hello_context = cls; | ||
3235 | struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg; | ||
3236 | struct PeerConnection *connection; | ||
3237 | |||
3238 | GNUNET_CORE_disconnect (send_hello_context->peer->daemon->server); | ||
3239 | send_hello_context->peer->daemon->server = NULL; | ||
3240 | |||
3241 | connection = send_hello_context->peer->connect_peers_head; | ||
3242 | |||
3243 | while (connection != NULL) | ||
3244 | { | ||
3245 | if (pg->notify_connection != NULL) | ||
3246 | { | ||
3247 | pg->notify_connection (pg->notify_connection_cls, &send_hello_context->peer->daemon->id, &pg->peers[connection->index].daemon->id, 0, /* FIXME */ | ||
3248 | send_hello_context->peer->daemon->cfg, | ||
3249 | pg->peers[connection->index].daemon->cfg, | ||
3250 | send_hello_context->peer->daemon, | ||
3251 | pg->peers[connection->index].daemon, | ||
3252 | "Peers failed to connect (timeout)"); | ||
3253 | } | ||
3254 | GNUNET_CONTAINER_DLL_remove (send_hello_context->peer->connect_peers_head, | ||
3255 | send_hello_context->peer->connect_peers_tail, | ||
3256 | connection); | ||
3257 | GNUNET_free (connection); | ||
3258 | connection = connection->next; | ||
3259 | } | ||
3260 | GNUNET_SCHEDULER_add_now (&free_hello_context, send_hello_context); | ||
3261 | #if BAD | ||
3262 | other_peer = &pg->peers[connection->index]; | ||
3263 | #endif | ||
3264 | } | ||
3265 | |||
3266 | /** | ||
3267 | * For peers that haven't yet connected, send | ||
3268 | * CORE connect requests. | ||
3269 | * | ||
3270 | * @param cls the 'struct SendHelloContext *' | ||
3271 | * @param tc scheduler context | ||
3272 | */ | ||
3273 | static void | ||
3274 | send_core_connect_requests (void *cls, | ||
3275 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
3276 | { | ||
3277 | struct SendHelloContext *send_hello_context = cls; | ||
3278 | struct PeerConnection *conn; | ||
3279 | |||
3280 | GNUNET_assert (send_hello_context->peer->daemon->server != NULL); | ||
3281 | |||
3282 | send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK; | ||
3283 | |||
3284 | send_hello_context->connect_attempts++; | ||
3285 | if (send_hello_context->connect_attempts < | ||
3286 | send_hello_context->pg->ct_ctx.connect_attempts) | ||
3287 | { | ||
3288 | conn = send_hello_context->peer->connect_peers_head; | ||
3289 | while (conn != NULL) | ||
3290 | { | ||
3291 | GNUNET_TRANSPORT_try_connect (send_hello_context->peer->daemon->th, | ||
3292 | &send_hello_context->pg->peers[conn-> | ||
3293 | index].daemon-> | ||
3294 | id); | ||
3295 | conn = conn->next; | ||
3296 | } | ||
3297 | send_hello_context->core_connect_task = | ||
3298 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide | ||
3299 | (send_hello_context->pg-> | ||
3300 | ct_ctx.connect_timeout, | ||
3301 | send_hello_context->pg-> | ||
3302 | ct_ctx.connect_attempts), | ||
3303 | &send_core_connect_requests, | ||
3304 | send_hello_context); | ||
3305 | } | ||
3306 | else | ||
3307 | { | ||
3308 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3309 | "Timeout before all connections created, marking rest as failed!\n"); | ||
3310 | GNUNET_SCHEDULER_add_now (¬ify_remaining_connections_failed, | ||
3311 | send_hello_context); | ||
3312 | } | ||
3313 | |||
3314 | } | ||
3315 | |||
3316 | /** | ||
3317 | * Success, connection is up. Signal client our success. | ||
3318 | * | ||
3319 | * @param cls our "struct SendHelloContext" | ||
3320 | * @param peer identity of the peer that has connected | ||
3321 | * @param atsi performance information | ||
3322 | * | ||
3323 | * FIXME: remove peers from BOTH lists, call notify twice, should | ||
3324 | * double the speed of connections as long as the list iteration | ||
3325 | * doesn't take too long! | ||
3326 | */ | ||
3327 | static void | ||
3328 | core_connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
3329 | const struct GNUNET_ATS_Information *atsi) | ||
3330 | { | ||
3331 | struct SendHelloContext *send_hello_context = cls; | ||
3332 | struct PeerConnection *connection; | ||
3333 | struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg; | ||
3334 | |||
3335 | #if BAD | ||
3336 | struct PeerData *other_peer; | ||
3337 | #endif | ||
3338 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected peer %s to peer %s\n", | ||
3339 | ctx->d1->shortname, GNUNET_i2s (peer)); | ||
3340 | if (0 == | ||
3341 | memcmp (&send_hello_context->peer->daemon->id, peer, | ||
3342 | sizeof (struct GNUNET_PeerIdentity))) | ||
3343 | return; | ||
3344 | |||
3345 | connection = send_hello_context->peer->connect_peers_head; | ||
3346 | #if BAD | ||
3347 | other_peer = NULL; | ||
3348 | #endif | ||
3349 | |||
3350 | while ((connection != NULL) && | ||
3351 | (0 != | ||
3352 | memcmp (&pg->peers[connection->index].daemon->id, peer, | ||
3353 | sizeof (struct GNUNET_PeerIdentity)))) | ||
3354 | { | ||
3355 | connection = connection->next; | ||
3356 | } | ||
3357 | |||
3358 | if (connection == NULL) | ||
3359 | { | ||
3360 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3361 | "Connected peer %s to %s, not in list (no problem(?))\n", | ||
3362 | GNUNET_i2s (peer), send_hello_context->peer->daemon->shortname); | ||
3363 | } | ||
3364 | else | ||
3365 | { | ||
3366 | #if BAD | ||
3367 | other_peer = &pg->peers[connection->index]; | ||
3368 | #endif | ||
3369 | if (pg->notify_connection != NULL) | ||
3370 | { | ||
3371 | pg->notify_connection (pg->notify_connection_cls, &send_hello_context->peer->daemon->id, peer, 0, /* FIXME */ | ||
3372 | send_hello_context->peer->daemon->cfg, | ||
3373 | pg->peers[connection->index].daemon->cfg, | ||
3374 | send_hello_context->peer->daemon, | ||
3375 | pg->peers[connection->index].daemon, NULL); | ||
3376 | } | ||
3377 | GNUNET_CONTAINER_DLL_remove (send_hello_context->peer->connect_peers_head, | ||
3378 | send_hello_context->peer->connect_peers_tail, | ||
3379 | connection); | ||
3380 | GNUNET_free (connection); | ||
3381 | } | ||
3382 | |||
3383 | #if BAD | ||
3384 | /* Notify of reverse connection and remove from other peers list of outstanding */ | ||
3385 | if (other_peer != NULL) | ||
3386 | { | ||
3387 | connection = other_peer->connect_peers_head; | ||
3388 | while ((connection != NULL) && | ||
3389 | (0 != | ||
3390 | memcmp (&send_hello_context->peer->daemon->id, | ||
3391 | &pg->peers[connection->index].daemon->id, | ||
3392 | sizeof (struct GNUNET_PeerIdentity)))) | ||
3393 | { | ||
3394 | connection = connection->next; | ||
3395 | } | ||
3396 | if (connection != NULL) | ||
3397 | { | ||
3398 | if (pg->notify_connection != NULL) | ||
3399 | { | ||
3400 | pg->notify_connection (pg->notify_connection_cls, peer, &send_hello_context->peer->daemon->id, 0, /* FIXME */ | ||
3401 | pg->peers[connection->index].daemon->cfg, | ||
3402 | send_hello_context->peer->daemon->cfg, | ||
3403 | pg->peers[connection->index].daemon, | ||
3404 | send_hello_context->peer->daemon, NULL); | ||
3405 | } | ||
3406 | |||
3407 | GNUNET_CONTAINER_DLL_remove (other_peer->connect_peers_head, | ||
3408 | other_peer->connect_peers_tail, connection); | ||
3409 | GNUNET_free (connection); | ||
3410 | } | ||
3411 | } | ||
3412 | #endif | ||
3413 | |||
3414 | if (send_hello_context->peer->connect_peers_head == NULL) | ||
3415 | { | ||
3416 | GNUNET_SCHEDULER_add_now (&free_hello_context, send_hello_context); | ||
3417 | } | ||
3418 | } | ||
3419 | |||
3420 | /** | ||
3421 | * Notify of a successful connection to the core service. | ||
3422 | * | ||
3423 | * @param cls a struct SendHelloContext * | ||
3424 | * @param server handle to the core service | ||
3425 | * @param my_identity the peer identity of this peer | ||
3426 | */ | ||
3427 | void | ||
3428 | core_init (void *cls, struct GNUNET_CORE_Handle *server, | ||
3429 | struct GNUNET_PeerIdentity *my_identity) | ||
3430 | { | ||
3431 | struct SendHelloContext *send_hello_context = cls; | ||
3432 | |||
3433 | send_hello_context->core_ready = GNUNET_YES; | ||
3434 | } | ||
3435 | |||
3436 | /** | ||
3437 | * Function called once a hello has been sent | ||
3438 | * to the transport, move on to the next one | ||
3439 | * or go away forever. | ||
3440 | * | ||
3441 | * @param cls the 'struct SendHelloContext *' | ||
3442 | * @param tc scheduler context | ||
3443 | */ | ||
3444 | static void | ||
3445 | hello_sent_callback (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
3446 | { | ||
3447 | struct SendHelloContext *send_hello_context = cls; | ||
3448 | |||
3449 | //unsigned int pg_iter; | ||
3450 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
3451 | { | ||
3452 | GNUNET_free (send_hello_context); | ||
3453 | return; | ||
3454 | } | ||
3455 | |||
3456 | send_hello_context->pg->remaining_hellos--; | ||
3457 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent HELLO, have %d remaining!\n", | ||
3458 | send_hello_context->pg->remaining_hellos); | ||
3459 | if (send_hello_context->peer_pos == NULL) /* All HELLOs (for this peer!) have been transmitted! */ | ||
3460 | { | ||
3461 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3462 | "All hellos for this peer sent, disconnecting transport!\n"); | ||
3463 | GNUNET_assert (send_hello_context->peer->daemon->th != NULL); | ||
3464 | GNUNET_TRANSPORT_disconnect (send_hello_context->peer->daemon->th); | ||
3465 | send_hello_context->peer->daemon->th = NULL; | ||
3466 | GNUNET_assert (send_hello_context->peer->daemon->server == NULL); | ||
3467 | send_hello_context->peer->daemon->server = | ||
3468 | GNUNET_CORE_connect (send_hello_context->peer->cfg, 1, | ||
3469 | send_hello_context, &core_init, | ||
3470 | &core_connect_notify, NULL, NULL, NULL, GNUNET_NO, | ||
3471 | NULL, GNUNET_NO, no_handlers); | ||
3472 | |||
3473 | send_hello_context->core_connect_task = | ||
3474 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide | ||
3475 | (send_hello_context->pg-> | ||
3476 | ct_ctx.connect_timeout, | ||
3477 | send_hello_context->pg-> | ||
3478 | ct_ctx.connect_attempts), | ||
3479 | &send_core_connect_requests, | ||
3480 | send_hello_context); | ||
3481 | } | ||
3482 | else | ||
3483 | GNUNET_SCHEDULER_add_now (&schedule_send_hellos, send_hello_context); | ||
3484 | } | ||
3485 | |||
3486 | /** | ||
3487 | * Connect to a peer, give it all the HELLO's of those peers | ||
3488 | * we will later ask it to connect to. | ||
3489 | * | ||
3490 | * @param ct_ctx the overall connection context | ||
3491 | */ | ||
3492 | static void | ||
3493 | schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
3494 | { | ||
3495 | struct SendHelloContext *send_hello_context = cls; | ||
3496 | struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg; | ||
3497 | |||
3498 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
3499 | { | ||
3500 | GNUNET_free (send_hello_context); | ||
3501 | return; | ||
3502 | } | ||
3503 | |||
3504 | GNUNET_assert (send_hello_context->peer_pos != NULL); /* All of the HELLO sends to be scheduled have been scheduled! */ | ||
3505 | |||
3506 | if (((send_hello_context->peer->daemon->th == NULL) && | ||
3507 | (pg->outstanding_connects > pg->max_outstanding_connections)) || | ||
3508 | (pg->stop_connects == GNUNET_YES)) | ||
3509 | { | ||
3510 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3511 | "Delaying connect, we have too many outstanding connections!\n"); | ||
3512 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
3513 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
3514 | &schedule_send_hellos, send_hello_context); | ||
3515 | } | ||
3516 | else | ||
3517 | { | ||
3518 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3519 | "Creating connection, outstanding_connections is %d\n", | ||
3520 | outstanding_connects); | ||
3521 | if (send_hello_context->peer->daemon->th == NULL) | ||
3522 | { | ||
3523 | pg->outstanding_connects++; /* Actual TRANSPORT, CORE connections! */ | ||
3524 | send_hello_context->peer->daemon->th = | ||
3525 | GNUNET_TRANSPORT_connect (send_hello_context->peer->cfg, NULL, | ||
3526 | send_hello_context, NULL, NULL, NULL); | ||
3527 | } | ||
3528 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3529 | "Offering HELLO of peer %s to peer %s\n", | ||
3530 | send_hello_context->peer->daemon->shortname, | ||
3531 | pg->peers[send_hello_context->peer_pos->index]. | ||
3532 | daemon->shortname); | ||
3533 | GNUNET_TRANSPORT_offer_hello (send_hello_context->peer->daemon->th, | ||
3534 | (const struct GNUNET_MessageHeader *) | ||
3535 | pg->peers[send_hello_context->peer_pos-> | ||
3536 | index].daemon->hello, | ||
3537 | &hello_sent_callback, send_hello_context); | ||
3538 | send_hello_context->peer_pos = send_hello_context->peer_pos->next; | ||
3539 | GNUNET_assert (send_hello_context->peer->daemon->th != NULL); | ||
3540 | } | ||
3541 | } | ||
3542 | #endif | ||
3543 | |||
3544 | /** | ||
3545 | * Internal notification of a connection, kept so that we can ensure some connections | ||
3546 | * happen instead of flooding all testing daemons with requests to connect. | ||
3547 | */ | ||
3548 | static void | ||
3549 | internal_connect_notify (void *cls, const struct GNUNET_PeerIdentity *first, | ||
3550 | const struct GNUNET_PeerIdentity *second, | ||
3551 | uint32_t distance, | ||
3552 | const struct GNUNET_CONFIGURATION_Handle *first_cfg, | ||
3553 | const struct GNUNET_CONFIGURATION_Handle *second_cfg, | ||
3554 | struct GNUNET_TESTING_Daemon *first_daemon, | ||
3555 | struct GNUNET_TESTING_Daemon *second_daemon, | ||
3556 | const char *emsg) | ||
3557 | { | ||
3558 | struct ConnectContext *connect_ctx = cls; | ||
3559 | struct ConnectTopologyContext *ct_ctx = connect_ctx->ct_ctx; | ||
3560 | struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg; | ||
3561 | struct PeerConnection *connection; | ||
3562 | |||
3563 | GNUNET_assert (NULL != connect_ctx->cc); | ||
3564 | connect_ctx->cc = NULL; | ||
3565 | GNUNET_assert (0 < pg->outstanding_connects); | ||
3566 | pg->outstanding_connects--; | ||
3567 | GNUNET_CONTAINER_DLL_remove (pg->cc_head, pg->cc_tail, connect_ctx); | ||
3568 | /* | ||
3569 | * Check whether the inverse connection has been scheduled yet, | ||
3570 | * if not, we can remove it from the other peers list and avoid | ||
3571 | * even trying to connect them again! | ||
3572 | */ | ||
3573 | connection = pg->peers[connect_ctx->second_index].connect_peers_head; | ||
3574 | #if BAD | ||
3575 | other_peer = NULL; | ||
3576 | #endif | ||
3577 | |||
3578 | while ((connection != NULL) && | ||
3579 | (0 != | ||
3580 | memcmp (first, &pg->peers[connection->index].daemon->id, | ||
3581 | sizeof (struct GNUNET_PeerIdentity)))) | ||
3582 | connection = connection->next; | ||
3583 | |||
3584 | if (connection != NULL) /* Can safely remove! */ | ||
3585 | { | ||
3586 | GNUNET_assert (0 < ct_ctx->remaining_connections); | ||
3587 | ct_ctx->remaining_connections--; | ||
3588 | if (pg->notify_connection != NULL) /* Notify of reverse connection */ | ||
3589 | pg->notify_connection (pg->notify_connection_cls, second, first, distance, | ||
3590 | second_cfg, first_cfg, second_daemon, first_daemon, | ||
3591 | emsg); | ||
3592 | |||
3593 | GNUNET_CONTAINER_DLL_remove (pg-> | ||
3594 | peers[connect_ctx-> | ||
3595 | second_index].connect_peers_head, | ||
3596 | pg->peers[connect_ctx-> | ||
3597 | second_index].connect_peers_tail, | ||
3598 | connection); | ||
3599 | GNUNET_free (connection); | ||
3600 | } | ||
3601 | |||
3602 | if (ct_ctx->remaining_connections == 0) | ||
3603 | { | ||
3604 | if (ct_ctx->notify_connections_done != NULL) | ||
3605 | { | ||
3606 | ct_ctx->notify_connections_done (ct_ctx->notify_cls, NULL); | ||
3607 | ct_ctx->notify_connections_done = NULL; | ||
3608 | } | ||
3609 | } | ||
3610 | else | ||
3611 | preschedule_connect (pg); | ||
3612 | |||
3613 | if (pg->notify_connection != NULL) | ||
3614 | pg->notify_connection (pg->notify_connection_cls, first, second, distance, | ||
3615 | first_cfg, second_cfg, first_daemon, second_daemon, | ||
3616 | emsg); | ||
3617 | GNUNET_free (connect_ctx); | ||
3618 | } | ||
3619 | |||
3620 | /** | ||
3621 | * Either delay a connection (because there are too many outstanding) | ||
3622 | * or schedule it for right now. | ||
3623 | * | ||
3624 | * @param cls a connection context | ||
3625 | * @param tc the task runtime context | ||
3626 | */ | ||
3627 | static void | ||
3628 | schedule_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
3629 | { | ||
3630 | struct ConnectContext *connect_context = cls; | ||
3631 | struct GNUNET_TESTING_PeerGroup *pg = connect_context->ct_ctx->pg; | ||
3632 | |||
3633 | connect_context->task = GNUNET_SCHEDULER_NO_TASK; | ||
3634 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
3635 | return; | ||
3636 | |||
3637 | if ((pg->outstanding_connects > pg->max_outstanding_connections) || | ||
3638 | (pg->stop_connects == GNUNET_YES)) | ||
3639 | { | ||
3640 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3641 | "Delaying connect, we have too many outstanding connections!\n"); | ||
3642 | connect_context->task = | ||
3643 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
3644 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
3645 | &schedule_connect, connect_context); | ||
3646 | return; | ||
3647 | } | ||
3648 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3649 | "Creating connection, outstanding_connections is %d (max %d)\n", | ||
3650 | pg->outstanding_connects, pg->max_outstanding_connections); | ||
3651 | pg->outstanding_connects++; | ||
3652 | pg->total_connects_scheduled++; | ||
3653 | GNUNET_assert (NULL == connect_context->cc); | ||
3654 | connect_context->cc = | ||
3655 | GNUNET_TESTING_daemons_connect (pg-> | ||
3656 | peers[connect_context-> | ||
3657 | first_index].daemon, | ||
3658 | pg->peers[connect_context-> | ||
3659 | second_index].daemon, | ||
3660 | connect_context->ct_ctx->connect_timeout, | ||
3661 | connect_context->ct_ctx->connect_attempts, | ||
3662 | #if USE_SEND_HELLOS | ||
3663 | GNUNET_NO, | ||
3664 | #else | ||
3665 | GNUNET_YES, | ||
3666 | #endif | ||
3667 | &internal_connect_notify, | ||
3668 | connect_context); | ||
3669 | |||
3670 | } | ||
3671 | |||
3672 | #if !OLD | ||
3673 | /** | ||
3674 | * Iterator for actually scheduling connections to be created | ||
3675 | * between two peers. | ||
3676 | * | ||
3677 | * @param cls closure, a GNUNET_TESTING_Daemon | ||
3678 | * @param key the key the second Daemon was stored under | ||
3679 | * @param value the GNUNET_TESTING_Daemon that the first is to connect to | ||
3680 | * | ||
3681 | * @return GNUNET_YES to continue iteration | ||
3682 | */ | ||
3683 | static int | ||
3684 | connect_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
3685 | { | ||
3686 | struct ConnectTopologyContext *ct_ctx = cls; | ||
3687 | struct PeerData *first = ct_ctx->first; | ||
3688 | struct GNUNET_TESTING_Daemon *second = value; | ||
3689 | struct ConnectContext *connect_context; | ||
3690 | |||
3691 | connect_context = GNUNET_malloc (sizeof (struct ConnectContext)); | ||
3692 | connect_context->first = first->daemon; | ||
3693 | connect_context->second = second; | ||
3694 | connect_context->ct_ctx = ct_ctx; | ||
3695 | connect_context->task = | ||
3696 | GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context); | ||
3697 | GNUNET_CONTAINER_DLL_insert (ct_ctx->pg->cc_head, ct_ctx->pg->cc_tail, | ||
3698 | connect_context); | ||
3699 | return GNUNET_YES; | ||
3700 | } | ||
3701 | #endif | ||
3702 | |||
3703 | #if !OLD | ||
3704 | /** | ||
3705 | * Iterator for copying all entries in the allowed hashmap to the | ||
3706 | * connect hashmap. | ||
3707 | * | ||
3708 | * @param cls closure, a GNUNET_TESTING_Daemon | ||
3709 | * @param key the key the second Daemon was stored under | ||
3710 | * @param value the GNUNET_TESTING_Daemon that the first is to connect to | ||
3711 | * | ||
3712 | * @return GNUNET_YES to continue iteration | ||
3713 | */ | ||
3714 | static int | ||
3715 | copy_topology_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
3716 | { | ||
3717 | struct PeerData *first = cls; | ||
3718 | |||
3719 | GNUNET_assert (GNUNET_OK == | ||
3720 | GNUNET_CONTAINER_multihashmap_put (first->connect_peers, key, | ||
3721 | value, | ||
3722 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
3723 | |||
3724 | return GNUNET_YES; | ||
3725 | } | ||
3726 | #endif | ||
3727 | |||
3728 | /** | ||
3729 | * Make the peers to connect the same as those that are allowed to be | ||
3730 | * connected. | ||
3731 | * | ||
3732 | * @param pg the peer group | ||
3733 | */ | ||
3734 | static int | ||
3735 | copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg) | ||
3736 | { | ||
3737 | unsigned int pg_iter; | ||
3738 | int ret; | ||
3739 | int total; | ||
3740 | |||
3741 | #if OLD | ||
3742 | struct PeerConnection *iter; | ||
3743 | #endif | ||
3744 | total = 0; | ||
3745 | ret = 0; | ||
3746 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
3747 | { | ||
3748 | #if OLD | ||
3749 | iter = pg->peers[pg_iter].allowed_peers_head; | ||
3750 | while (iter != NULL) | ||
3751 | { | ||
3752 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3753 | "Creating connection between %d and %d\n", pg_iter, | ||
3754 | iter->index); | ||
3755 | total += add_connections (pg, pg_iter, iter->index, CONNECT, GNUNET_YES); | ||
3756 | //total += add_actual_connections(pg, pg_iter, iter->index); | ||
3757 | iter = iter->next; | ||
3758 | } | ||
3759 | #else | ||
3760 | ret = | ||
3761 | GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers, | ||
3762 | ©_topology_iterator, | ||
3763 | &pg->peers[pg_iter]); | ||
3764 | #endif | ||
3765 | if (GNUNET_SYSERR == ret) | ||
3766 | return GNUNET_SYSERR; | ||
3767 | |||
3768 | total = total + ret; | ||
3769 | } | ||
3770 | |||
3771 | return total; | ||
3772 | } | ||
3773 | |||
3774 | /** | ||
3775 | * Connect the topology as specified by the PeerConnection's | ||
3776 | * of each peer in the peer group | ||
3777 | * | ||
3778 | * @param pg the peer group we are dealing with | ||
3779 | * @param connect_timeout how long try connecting two peers | ||
3780 | * @param connect_attempts how many times (max) to attempt | ||
3781 | * @param notify_callback callback to notify when finished | ||
3782 | * @param notify_cls closure for notify callback | ||
3783 | * | ||
3784 | * @return the number of connections that will be attempted | ||
3785 | */ | ||
3786 | static int | ||
3787 | connect_topology (struct GNUNET_TESTING_PeerGroup *pg, | ||
3788 | struct GNUNET_TIME_Relative connect_timeout, | ||
3789 | unsigned int connect_attempts, | ||
3790 | GNUNET_TESTING_NotifyCompletion notify_callback, | ||
3791 | void *notify_cls) | ||
3792 | { | ||
3793 | unsigned int pg_iter; | ||
3794 | unsigned int total; | ||
3795 | |||
3796 | #if OLD | ||
3797 | struct PeerConnection *connection_iter; | ||
3798 | #endif | ||
3799 | #if USE_SEND_HELLOS | ||
3800 | struct SendHelloContext *send_hello_context; | ||
3801 | #endif | ||
3802 | |||
3803 | total = 0; | ||
3804 | pg->ct_ctx.notify_connections_done = notify_callback; | ||
3805 | pg->ct_ctx.notify_cls = notify_cls; | ||
3806 | pg->ct_ctx.pg = pg; | ||
3807 | |||
3808 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
3809 | { | ||
3810 | #if OLD | ||
3811 | connection_iter = pg->peers[pg_iter].connect_peers_head; | ||
3812 | while (connection_iter != NULL) | ||
3813 | { | ||
3814 | connection_iter = connection_iter->next; | ||
3815 | total++; | ||
3816 | } | ||
3817 | #else | ||
3818 | total += | ||
3819 | GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers); | ||
3820 | #endif | ||
3821 | } | ||
3822 | |||
3823 | if (total == 0) | ||
3824 | return total; | ||
3825 | |||
3826 | pg->ct_ctx.connect_timeout = connect_timeout; | ||
3827 | pg->ct_ctx.connect_attempts = connect_attempts; | ||
3828 | pg->ct_ctx.remaining_connections = total; | ||
3829 | |||
3830 | #if USE_SEND_HELLOS | ||
3831 | /* First give all peers the HELLO's of other peers (connect to first peer's transport service, give HELLO's of other peers, continue...) */ | ||
3832 | pg->remaining_hellos = total; | ||
3833 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
3834 | { | ||
3835 | send_hello_context = GNUNET_malloc (sizeof (struct SendHelloContext)); | ||
3836 | send_hello_context->peer = &pg->peers[pg_iter]; | ||
3837 | send_hello_context->peer_pos = pg->peers[pg_iter].connect_peers_head; | ||
3838 | send_hello_context->pg = pg; | ||
3839 | GNUNET_SCHEDULER_add_now (&schedule_send_hellos, send_hello_context); | ||
3840 | } | ||
3841 | #else | ||
3842 | for (pg_iter = 0; pg_iter < pg->max_outstanding_connections; pg_iter++) | ||
3843 | { | ||
3844 | preschedule_connect (pg); | ||
3845 | } | ||
3846 | #endif | ||
3847 | return total; | ||
3848 | |||
3849 | } | ||
3850 | |||
3851 | /** | ||
3852 | * Takes a peer group and creates a topology based on the | ||
3853 | * one specified. Creates a topology means generates friend | ||
3854 | * files for the peers so they can only connect to those allowed | ||
3855 | * by the topology. This will only have an effect once peers | ||
3856 | * are started if the FRIENDS_ONLY option is set in the base | ||
3857 | * config. Also takes an optional restrict topology which | ||
3858 | * disallows connections based on particular transports | ||
3859 | * UNLESS they are specified in the restricted topology. | ||
3860 | * | ||
3861 | * @param pg the peer group struct representing the running peers | ||
3862 | * @param topology which topology to connect the peers in | ||
3863 | * @param restrict_topology disallow restrict_transports transport | ||
3864 | * connections to peers NOT in this topology | ||
3865 | * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions | ||
3866 | * @param restrict_transports space delimited list of transports to blacklist | ||
3867 | * to create restricted topology | ||
3868 | * | ||
3869 | * @return the maximum number of connections were all allowed peers | ||
3870 | * connected to each other | ||
3871 | */ | ||
3872 | unsigned int | ||
3873 | GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, | ||
3874 | enum GNUNET_TESTING_Topology topology, | ||
3875 | enum GNUNET_TESTING_Topology restrict_topology, | ||
3876 | const char *restrict_transports) | ||
3877 | { | ||
3878 | int ret; | ||
3879 | |||
3880 | unsigned int num_connections; | ||
3881 | int unblacklisted_connections; | ||
3882 | char *filename; | ||
3883 | struct PeerConnection *conn_iter; | ||
3884 | struct PeerConnection *temp_conn; | ||
3885 | unsigned int off; | ||
3886 | |||
3887 | #if !OLD | ||
3888 | unsigned int i; | ||
3889 | |||
3890 | for (i = 0; i < pg->total; i++) | ||
3891 | { | ||
3892 | pg->peers[i].allowed_peers = GNUNET_CONTAINER_multihashmap_create (100); | ||
3893 | pg->peers[i].connect_peers = GNUNET_CONTAINER_multihashmap_create (100); | ||
3894 | pg->peers[i].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create (100); | ||
3895 | pg->peers[i].pg = pg; | ||
3896 | } | ||
3897 | #endif | ||
3898 | |||
3899 | switch (topology) | ||
3900 | { | ||
3901 | case GNUNET_TESTING_TOPOLOGY_CLIQUE: | ||
3902 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating clique topology\n"); | ||
3903 | num_connections = create_clique (pg, &add_connections, ALLOWED, GNUNET_NO); | ||
3904 | break; | ||
3905 | case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING: | ||
3906 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3907 | "Creating small world (ring) topology\n"); | ||
3908 | num_connections = create_small_world_ring (pg, &add_connections, ALLOWED); | ||
3909 | break; | ||
3910 | case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD: | ||
3911 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3912 | "Creating small world (2d-torus) topology\n"); | ||
3913 | num_connections = create_small_world (pg, &add_connections, ALLOWED); | ||
3914 | break; | ||
3915 | case GNUNET_TESTING_TOPOLOGY_RING: | ||
3916 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating ring topology\n"); | ||
3917 | num_connections = create_ring (pg, &add_connections, ALLOWED); | ||
3918 | break; | ||
3919 | case GNUNET_TESTING_TOPOLOGY_2D_TORUS: | ||
3920 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating 2d torus topology\n"); | ||
3921 | num_connections = create_2d_torus (pg, &add_connections, ALLOWED); | ||
3922 | break; | ||
3923 | case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI: | ||
3924 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating Erdos-Renyi topology\n"); | ||
3925 | num_connections = create_erdos_renyi (pg, &add_connections, ALLOWED); | ||
3926 | break; | ||
3927 | case GNUNET_TESTING_TOPOLOGY_INTERNAT: | ||
3928 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating InterNAT topology\n"); | ||
3929 | num_connections = create_nated_internet (pg, &add_connections, ALLOWED); | ||
3930 | break; | ||
3931 | case GNUNET_TESTING_TOPOLOGY_SCALE_FREE: | ||
3932 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating Scale Free topology\n"); | ||
3933 | num_connections = create_scale_free (pg, &add_connections, ALLOWED); | ||
3934 | break; | ||
3935 | case GNUNET_TESTING_TOPOLOGY_LINE: | ||
3936 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3937 | "Creating straight line topology\n"); | ||
3938 | num_connections = create_line (pg, &add_connections, ALLOWED); | ||
3939 | break; | ||
3940 | case GNUNET_TESTING_TOPOLOGY_FROM_FILE: | ||
3941 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating topology from file!\n"); | ||
3942 | if (GNUNET_OK == | ||
3943 | GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing_old", | ||
3944 | "topology_file", &filename)) | ||
3945 | num_connections = | ||
3946 | create_from_file (pg, filename, &add_connections, ALLOWED); | ||
3947 | else | ||
3948 | { | ||
3949 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
3950 | "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!\n"); | ||
3951 | num_connections = 0; | ||
3952 | } | ||
3953 | break; | ||
3954 | case GNUNET_TESTING_TOPOLOGY_NONE: | ||
3955 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3956 | _ | ||
3957 | ("Creating no allowed topology (all peers can connect at core level)\n")); | ||
3958 | num_connections = pg->total * pg->total; /* Clique is allowed! */ | ||
3959 | break; | ||
3960 | default: | ||
3961 | num_connections = 0; | ||
3962 | break; | ||
3963 | } | ||
3964 | |||
3965 | if (GNUNET_YES == | ||
3966 | GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING_OLD", "F2F")) | ||
3967 | { | ||
3968 | ret = create_and_copy_friend_files (pg); | ||
3969 | if (ret != GNUNET_OK) | ||
3970 | { | ||
3971 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3972 | "Failed during friend file copying!\n"); | ||
3973 | return GNUNET_SYSERR; | ||
3974 | } | ||
3975 | else | ||
3976 | { | ||
3977 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3978 | "Friend files created/copied successfully!\n"); | ||
3979 | } | ||
3980 | } | ||
3981 | |||
3982 | /* Use the create clique method to initially set all connections as blacklisted. */ | ||
3983 | if ((restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE) && | ||
3984 | (restrict_topology != GNUNET_TESTING_TOPOLOGY_FROM_FILE)) | ||
3985 | create_clique (pg, &add_connections, BLACKLIST, GNUNET_NO); | ||
3986 | else | ||
3987 | return num_connections; | ||
3988 | |||
3989 | unblacklisted_connections = 0; | ||
3990 | /* Un-blacklist connections as per the topology specified */ | ||
3991 | switch (restrict_topology) | ||
3992 | { | ||
3993 | case GNUNET_TESTING_TOPOLOGY_CLIQUE: | ||
3994 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3995 | "Blacklisting all but clique topology\n"); | ||
3996 | unblacklisted_connections = | ||
3997 | create_clique (pg, &remove_connections, BLACKLIST, GNUNET_NO); | ||
3998 | break; | ||
3999 | case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING: | ||
4000 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4001 | "Blacklisting all but small world (ring) topology\n"); | ||
4002 | unblacklisted_connections = | ||
4003 | create_small_world_ring (pg, &remove_connections, BLACKLIST); | ||
4004 | break; | ||
4005 | case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD: | ||
4006 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4007 | "Blacklisting all but small world (2d-torus) topology\n"); | ||
4008 | unblacklisted_connections = | ||
4009 | create_small_world (pg, &remove_connections, BLACKLIST); | ||
4010 | break; | ||
4011 | case GNUNET_TESTING_TOPOLOGY_RING: | ||
4012 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4013 | "Blacklisting all but ring topology\n"); | ||
4014 | unblacklisted_connections = | ||
4015 | create_ring (pg, &remove_connections, BLACKLIST); | ||
4016 | break; | ||
4017 | case GNUNET_TESTING_TOPOLOGY_2D_TORUS: | ||
4018 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4019 | "Blacklisting all but 2d torus topology\n"); | ||
4020 | unblacklisted_connections = | ||
4021 | create_2d_torus (pg, &remove_connections, BLACKLIST); | ||
4022 | break; | ||
4023 | case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI: | ||
4024 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4025 | "Blacklisting all but Erdos-Renyi topology\n"); | ||
4026 | unblacklisted_connections = | ||
4027 | create_erdos_renyi (pg, &remove_connections, BLACKLIST); | ||
4028 | break; | ||
4029 | case GNUNET_TESTING_TOPOLOGY_INTERNAT: | ||
4030 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4031 | "Blacklisting all but InterNAT topology\n"); | ||
4032 | |||
4033 | #if TOPOLOGY_HACK | ||
4034 | for (off = 0; off < pg->total; off++) | ||
4035 | { | ||
4036 | conn_iter = pg->peers[off].allowed_peers_head; | ||
4037 | while (conn_iter != NULL) | ||
4038 | { | ||
4039 | temp_conn = conn_iter->next; | ||
4040 | GNUNET_free (conn_iter); | ||
4041 | conn_iter = temp_conn; | ||
4042 | } | ||
4043 | pg->peers[off].allowed_peers_head = NULL; | ||
4044 | pg->peers[off].allowed_peers_tail = NULL; | ||
4045 | |||
4046 | conn_iter = pg->peers[off].connect_peers_head; | ||
4047 | while (conn_iter != NULL) | ||
4048 | { | ||
4049 | temp_conn = conn_iter->next; | ||
4050 | GNUNET_free (conn_iter); | ||
4051 | conn_iter = temp_conn; | ||
4052 | } | ||
4053 | pg->peers[off].connect_peers_head = NULL; | ||
4054 | pg->peers[off].connect_peers_tail = NULL; | ||
4055 | } | ||
4056 | unblacklisted_connections = | ||
4057 | create_nated_internet_copy (pg, &remove_connections, BLACKLIST); | ||
4058 | #else | ||
4059 | unblacklisted_connections = | ||
4060 | create_nated_internet (pg, &remove_connections, BLACKLIST); | ||
4061 | #endif | ||
4062 | |||
4063 | break; | ||
4064 | case GNUNET_TESTING_TOPOLOGY_SCALE_FREE: | ||
4065 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4066 | "Blacklisting all but Scale Free topology\n"); | ||
4067 | unblacklisted_connections = | ||
4068 | create_scale_free (pg, &remove_connections, BLACKLIST); | ||
4069 | break; | ||
4070 | case GNUNET_TESTING_TOPOLOGY_LINE: | ||
4071 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4072 | "Blacklisting all but straight line topology\n"); | ||
4073 | unblacklisted_connections = | ||
4074 | create_line (pg, &remove_connections, BLACKLIST); | ||
4075 | default: | ||
4076 | break; | ||
4077 | } | ||
4078 | |||
4079 | if ((unblacklisted_connections > 0) && (restrict_transports != NULL)) | ||
4080 | { | ||
4081 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating blacklist with `%s'\n", | ||
4082 | restrict_transports); | ||
4083 | ret = create_and_copy_blacklist_files (pg, restrict_transports); | ||
4084 | if (ret != GNUNET_OK) | ||
4085 | { | ||
4086 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4087 | "Failed during blacklist file copying!\n"); | ||
4088 | return 0; | ||
4089 | } | ||
4090 | else | ||
4091 | { | ||
4092 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4093 | "Blacklist files created/copied successfully!\n"); | ||
4094 | } | ||
4095 | } | ||
4096 | return num_connections; | ||
4097 | } | ||
4098 | |||
4099 | #if !OLD | ||
4100 | /** | ||
4101 | * Iterator for choosing random peers to connect. | ||
4102 | * | ||
4103 | * @param cls closure, a RandomContext | ||
4104 | * @param key the key the second Daemon was stored under | ||
4105 | * @param value the GNUNET_TESTING_Daemon that the first is to connect to | ||
4106 | * | ||
4107 | * @return GNUNET_YES to continue iteration | ||
4108 | */ | ||
4109 | static int | ||
4110 | random_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
4111 | { | ||
4112 | struct RandomContext *random_ctx = cls; | ||
4113 | double random_number; | ||
4114 | uint32_t second_pos; | ||
4115 | GNUNET_HashCode first_hash; | ||
4116 | |||
4117 | random_number = | ||
4118 | ((double) | ||
4119 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
4120 | UINT64_MAX)) / ((double) UINT64_MAX); | ||
4121 | if (random_number < random_ctx->percentage) | ||
4122 | { | ||
4123 | GNUNET_assert (GNUNET_OK == | ||
4124 | GNUNET_CONTAINER_multihashmap_put (random_ctx-> | ||
4125 | first->connect_peers_working_set, | ||
4126 | key, value, | ||
4127 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
4128 | } | ||
4129 | |||
4130 | /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */ | ||
4131 | uid_from_hash (key, &second_pos); | ||
4132 | hash_from_uid (random_ctx->first_uid, &first_hash); | ||
4133 | GNUNET_assert (random_ctx->pg->total > second_pos); | ||
4134 | GNUNET_assert (GNUNET_YES == | ||
4135 | GNUNET_CONTAINER_multihashmap_remove (random_ctx-> | ||
4136 | pg->peers | ||
4137 | [second_pos].connect_peers, | ||
4138 | &first_hash, | ||
4139 | random_ctx-> | ||
4140 | first->daemon)); | ||
4141 | |||
4142 | return GNUNET_YES; | ||
4143 | } | ||
4144 | |||
4145 | /** | ||
4146 | * Iterator for adding at least X peers to a peers connection set. | ||
4147 | * | ||
4148 | * @param cls closure, MinimumContext | ||
4149 | * @param key the key the second Daemon was stored under | ||
4150 | * @param value the GNUNET_TESTING_Daemon that the first is to connect to | ||
4151 | * | ||
4152 | * @return GNUNET_YES to continue iteration | ||
4153 | */ | ||
4154 | static int | ||
4155 | minimum_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
4156 | { | ||
4157 | struct MinimumContext *min_ctx = cls; | ||
4158 | uint32_t second_pos; | ||
4159 | GNUNET_HashCode first_hash; | ||
4160 | unsigned int i; | ||
4161 | |||
4162 | if (GNUNET_CONTAINER_multihashmap_size | ||
4163 | (min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add) | ||
4164 | { | ||
4165 | for (i = 0; i < min_ctx->num_to_add; i++) | ||
4166 | { | ||
4167 | if (min_ctx->pg_array[i] == min_ctx->current) | ||
4168 | { | ||
4169 | GNUNET_assert (GNUNET_OK == | ||
4170 | GNUNET_CONTAINER_multihashmap_put (min_ctx-> | ||
4171 | first->connect_peers_working_set, | ||
4172 | key, value, | ||
4173 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
4174 | uid_from_hash (key, &second_pos); | ||
4175 | hash_from_uid (min_ctx->first_uid, &first_hash); | ||
4176 | GNUNET_assert (min_ctx->pg->total > second_pos); | ||
4177 | GNUNET_assert (GNUNET_OK == | ||
4178 | GNUNET_CONTAINER_multihashmap_put (min_ctx-> | ||
4179 | pg->peers | ||
4180 | [second_pos].connect_peers_working_set, | ||
4181 | &first_hash, | ||
4182 | min_ctx->first-> | ||
4183 | daemon, | ||
4184 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
4185 | /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */ | ||
4186 | GNUNET_assert (GNUNET_YES == | ||
4187 | GNUNET_CONTAINER_multihashmap_remove (min_ctx-> | ||
4188 | pg->peers | ||
4189 | [second_pos].connect_peers, | ||
4190 | &first_hash, | ||
4191 | min_ctx-> | ||
4192 | first->daemon)); | ||
4193 | } | ||
4194 | } | ||
4195 | min_ctx->current++; | ||
4196 | return GNUNET_YES; | ||
4197 | } | ||
4198 | else | ||
4199 | return GNUNET_NO; /* We can stop iterating, we have enough peers! */ | ||
4200 | |||
4201 | } | ||
4202 | |||
4203 | /** | ||
4204 | * Iterator for adding peers to a connection set based on a depth first search. | ||
4205 | * | ||
4206 | * @param cls closure, MinimumContext | ||
4207 | * @param key the key the second daemon was stored under | ||
4208 | * @param value the GNUNET_TESTING_Daemon that the first is to connect to | ||
4209 | * | ||
4210 | * @return GNUNET_YES to continue iteration | ||
4211 | */ | ||
4212 | static int | ||
4213 | dfs_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value) | ||
4214 | { | ||
4215 | struct DFSContext *dfs_ctx = cls; | ||
4216 | GNUNET_HashCode first_hash; | ||
4217 | |||
4218 | if (dfs_ctx->current == dfs_ctx->chosen) | ||
4219 | { | ||
4220 | GNUNET_assert (GNUNET_OK == | ||
4221 | GNUNET_CONTAINER_multihashmap_put (dfs_ctx-> | ||
4222 | first->connect_peers_working_set, | ||
4223 | key, value, | ||
4224 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
4225 | uid_from_hash (key, &dfs_ctx->second_uid); | ||
4226 | hash_from_uid (dfs_ctx->first_uid, &first_hash); | ||
4227 | GNUNET_assert (GNUNET_OK == | ||
4228 | GNUNET_CONTAINER_multihashmap_put (dfs_ctx-> | ||
4229 | pg->peers[dfs_ctx-> | ||
4230 | second_uid].connect_peers_working_set, | ||
4231 | &first_hash, | ||
4232 | dfs_ctx->first->daemon, | ||
4233 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
4234 | GNUNET_assert (GNUNET_YES == | ||
4235 | GNUNET_CONTAINER_multihashmap_remove (dfs_ctx-> | ||
4236 | pg->peers | ||
4237 | [dfs_ctx->second_uid].connect_peers, | ||
4238 | &first_hash, | ||
4239 | dfs_ctx-> | ||
4240 | first->daemon)); | ||
4241 | /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */ | ||
4242 | return GNUNET_NO; /* We have found our peer, don't iterate more */ | ||
4243 | } | ||
4244 | |||
4245 | dfs_ctx->current++; | ||
4246 | return GNUNET_YES; | ||
4247 | } | ||
4248 | #endif | ||
4249 | |||
4250 | /** | ||
4251 | * From the set of connections possible, choose percentage percent of connections | ||
4252 | * to actually connect. | ||
4253 | * | ||
4254 | * @param pg the peergroup we are dealing with | ||
4255 | * @param percentage what percent of total connections to make | ||
4256 | */ | ||
4257 | void | ||
4258 | choose_random_connections (struct GNUNET_TESTING_PeerGroup *pg, | ||
4259 | double percentage) | ||
4260 | { | ||
4261 | uint32_t pg_iter; | ||
4262 | |||
4263 | #if OLD | ||
4264 | struct PeerConnection *conn_iter; | ||
4265 | double random_number; | ||
4266 | #else | ||
4267 | struct RandomContext random_ctx; | ||
4268 | #endif | ||
4269 | |||
4270 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4271 | { | ||
4272 | #if OLD | ||
4273 | conn_iter = pg->peers[pg_iter].connect_peers_head; | ||
4274 | while (conn_iter != NULL) | ||
4275 | { | ||
4276 | random_number = | ||
4277 | ((double) | ||
4278 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
4279 | UINT64_MAX)) / ((double) UINT64_MAX); | ||
4280 | if (random_number < percentage) | ||
4281 | { | ||
4282 | add_connections (pg, pg_iter, conn_iter->index, WORKING_SET, | ||
4283 | GNUNET_YES); | ||
4284 | } | ||
4285 | conn_iter = conn_iter->next; | ||
4286 | } | ||
4287 | #else | ||
4288 | random_ctx.first_uid = pg_iter; | ||
4289 | random_ctx.first = &pg->peers[pg_iter]; | ||
4290 | random_ctx.percentage = percentage; | ||
4291 | random_ctx.pg = pg; | ||
4292 | pg->peers[pg_iter].connect_peers_working_set = | ||
4293 | GNUNET_CONTAINER_multihashmap_create (pg->total); | ||
4294 | GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers, | ||
4295 | &random_connect_iterator, | ||
4296 | &random_ctx); | ||
4297 | /* Now remove the old connections */ | ||
4298 | GNUNET_CONTAINER_multihashmap_destroy (pg->peers[pg_iter].connect_peers); | ||
4299 | /* And replace with the random set */ | ||
4300 | pg->peers[pg_iter].connect_peers = | ||
4301 | pg->peers[pg_iter].connect_peers_working_set; | ||
4302 | #endif | ||
4303 | } | ||
4304 | |||
4305 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4306 | { | ||
4307 | conn_iter = pg->peers[pg_iter].connect_peers_head; | ||
4308 | while (pg->peers[pg_iter].connect_peers_head != NULL) | ||
4309 | remove_connections (pg, pg_iter, | ||
4310 | pg->peers[pg_iter].connect_peers_head->index, CONNECT, | ||
4311 | GNUNET_YES); | ||
4312 | |||
4313 | pg->peers[pg_iter].connect_peers_head = | ||
4314 | pg->peers[pg_iter].connect_peers_working_set_head; | ||
4315 | pg->peers[pg_iter].connect_peers_tail = | ||
4316 | pg->peers[pg_iter].connect_peers_working_set_tail; | ||
4317 | pg->peers[pg_iter].connect_peers_working_set_head = NULL; | ||
4318 | pg->peers[pg_iter].connect_peers_working_set_tail = NULL; | ||
4319 | } | ||
4320 | } | ||
4321 | |||
4322 | /** | ||
4323 | * Count the number of connections in a linked list of connections. | ||
4324 | * | ||
4325 | * @param conn_list the connection list to get the count of | ||
4326 | * | ||
4327 | * @return the number of elements in the list | ||
4328 | */ | ||
4329 | static unsigned int | ||
4330 | count_connections (struct PeerConnection *conn_list) | ||
4331 | { | ||
4332 | struct PeerConnection *iter; | ||
4333 | unsigned int count; | ||
4334 | |||
4335 | count = 0; | ||
4336 | iter = conn_list; | ||
4337 | while (iter != NULL) | ||
4338 | { | ||
4339 | iter = iter->next; | ||
4340 | count++; | ||
4341 | } | ||
4342 | return count; | ||
4343 | } | ||
4344 | |||
4345 | static unsigned int | ||
4346 | count_workingset_connections (struct GNUNET_TESTING_PeerGroup *pg) | ||
4347 | { | ||
4348 | unsigned int count; | ||
4349 | unsigned int pg_iter; | ||
4350 | |||
4351 | #if OLD | ||
4352 | struct PeerConnection *conn_iter; | ||
4353 | #endif | ||
4354 | count = 0; | ||
4355 | |||
4356 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4357 | { | ||
4358 | #if OLD | ||
4359 | conn_iter = pg->peers[pg_iter].connect_peers_working_set_head; | ||
4360 | while (conn_iter != NULL) | ||
4361 | { | ||
4362 | count++; | ||
4363 | conn_iter = conn_iter->next; | ||
4364 | } | ||
4365 | #else | ||
4366 | count += | ||
4367 | GNUNET_CONTAINER_multihashmap_size (pg-> | ||
4368 | peers | ||
4369 | [pg_iter].connect_peers_working_set); | ||
4370 | #endif | ||
4371 | } | ||
4372 | |||
4373 | return count; | ||
4374 | } | ||
4375 | |||
4376 | static unsigned int | ||
4377 | count_allowed_connections (struct GNUNET_TESTING_PeerGroup *pg) | ||
4378 | { | ||
4379 | unsigned int count; | ||
4380 | unsigned int pg_iter; | ||
4381 | |||
4382 | #if OLD | ||
4383 | struct PeerConnection *conn_iter; | ||
4384 | #endif | ||
4385 | |||
4386 | count = 0; | ||
4387 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4388 | { | ||
4389 | #if OLD | ||
4390 | conn_iter = pg->peers[pg_iter].allowed_peers_head; | ||
4391 | while (conn_iter != NULL) | ||
4392 | { | ||
4393 | count++; | ||
4394 | conn_iter = conn_iter->next; | ||
4395 | } | ||
4396 | #else | ||
4397 | count += | ||
4398 | GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].allowed_peers); | ||
4399 | #endif | ||
4400 | } | ||
4401 | |||
4402 | return count; | ||
4403 | } | ||
4404 | |||
4405 | /** | ||
4406 | * From the set of connections possible, choose at least num connections per | ||
4407 | * peer. | ||
4408 | * | ||
4409 | * @param pg the peergroup we are dealing with | ||
4410 | * @param num how many connections at least should each peer have (if possible)? | ||
4411 | */ | ||
4412 | static void | ||
4413 | choose_minimum (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num) | ||
4414 | { | ||
4415 | #if !OLD | ||
4416 | struct MinimumContext minimum_ctx; | ||
4417 | #else | ||
4418 | struct PeerConnection *conn_iter; | ||
4419 | unsigned int temp_list_size; | ||
4420 | unsigned int i; | ||
4421 | unsigned int count; | ||
4422 | uint32_t random; /* Random list entry to connect peer to */ | ||
4423 | #endif | ||
4424 | uint32_t pg_iter; | ||
4425 | |||
4426 | #if OLD | ||
4427 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4428 | { | ||
4429 | temp_list_size = count_connections (pg->peers[pg_iter].connect_peers_head); | ||
4430 | if (temp_list_size == 0) | ||
4431 | { | ||
4432 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Peer %d has 0 connections!?!?\n", | ||
4433 | pg_iter); | ||
4434 | break; | ||
4435 | } | ||
4436 | for (i = 0; i < num; i++) | ||
4437 | { | ||
4438 | random = | ||
4439 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, temp_list_size); | ||
4440 | conn_iter = pg->peers[pg_iter].connect_peers_head; | ||
4441 | for (count = 0; count < random; count++) | ||
4442 | conn_iter = conn_iter->next; | ||
4443 | /* We now have a random connection, connect it! */ | ||
4444 | GNUNET_assert (conn_iter != NULL); | ||
4445 | add_connections (pg, pg_iter, conn_iter->index, WORKING_SET, GNUNET_YES); | ||
4446 | } | ||
4447 | } | ||
4448 | #else | ||
4449 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4450 | { | ||
4451 | pg->peers[pg_iter].connect_peers_working_set = | ||
4452 | GNUNET_CONTAINER_multihashmap_create (num); | ||
4453 | } | ||
4454 | |||
4455 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4456 | { | ||
4457 | minimum_ctx.first_uid = pg_iter; | ||
4458 | minimum_ctx.pg_array = | ||
4459 | GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, | ||
4460 | GNUNET_CONTAINER_multihashmap_size | ||
4461 | (pg->peers[pg_iter].connect_peers)); | ||
4462 | minimum_ctx.first = &pg->peers[pg_iter]; | ||
4463 | minimum_ctx.pg = pg; | ||
4464 | minimum_ctx.num_to_add = num; | ||
4465 | minimum_ctx.current = 0; | ||
4466 | GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers, | ||
4467 | &minimum_connect_iterator, | ||
4468 | &minimum_ctx); | ||
4469 | } | ||
4470 | |||
4471 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4472 | { | ||
4473 | /* Remove the "old" connections */ | ||
4474 | GNUNET_CONTAINER_multihashmap_destroy (pg->peers[pg_iter].connect_peers); | ||
4475 | /* And replace with the working set */ | ||
4476 | pg->peers[pg_iter].connect_peers = | ||
4477 | pg->peers[pg_iter].connect_peers_working_set; | ||
4478 | } | ||
4479 | #endif | ||
4480 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4481 | { | ||
4482 | while (pg->peers[pg_iter].connect_peers_head != NULL) | ||
4483 | { | ||
4484 | conn_iter = pg->peers[pg_iter].connect_peers_head; | ||
4485 | GNUNET_CONTAINER_DLL_remove (pg->peers[pg_iter].connect_peers_head, | ||
4486 | pg->peers[pg_iter].connect_peers_tail, | ||
4487 | conn_iter); | ||
4488 | GNUNET_free (conn_iter); | ||
4489 | /*remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT, GNUNET_YES); */ | ||
4490 | } | ||
4491 | |||
4492 | pg->peers[pg_iter].connect_peers_head = | ||
4493 | pg->peers[pg_iter].connect_peers_working_set_head; | ||
4494 | pg->peers[pg_iter].connect_peers_tail = | ||
4495 | pg->peers[pg_iter].connect_peers_working_set_tail; | ||
4496 | pg->peers[pg_iter].connect_peers_working_set_head = NULL; | ||
4497 | pg->peers[pg_iter].connect_peers_working_set_tail = NULL; | ||
4498 | } | ||
4499 | } | ||
4500 | |||
4501 | #if !OLD | ||
4502 | struct FindClosestContext | ||
4503 | { | ||
4504 | /** | ||
4505 | * The currently known closest peer. | ||
4506 | */ | ||
4507 | struct GNUNET_TESTING_Daemon *closest; | ||
4508 | |||
4509 | /** | ||
4510 | * The info for the peer we are adding connections for. | ||
4511 | */ | ||
4512 | struct PeerData *curr_peer; | ||
4513 | |||
4514 | /** | ||
4515 | * The distance (bits) between the current | ||
4516 | * peer and the currently known closest. | ||
4517 | */ | ||
4518 | unsigned int closest_dist; | ||
4519 | |||
4520 | /** | ||
4521 | * The offset of the closest known peer in | ||
4522 | * the peer group. | ||
4523 | */ | ||
4524 | unsigned int closest_num; | ||
4525 | }; | ||
4526 | |||
4527 | /** | ||
4528 | * Iterator over hash map entries of the allowed | ||
4529 | * peer connections. Find the closest, not already | ||
4530 | * connected peer and return it. | ||
4531 | * | ||
4532 | * @param cls closure (struct FindClosestContext) | ||
4533 | * @param key current key code (hash of offset in pg) | ||
4534 | * @param value value in the hash map - a GNUNET_TESTING_Daemon | ||
4535 | * @return GNUNET_YES if we should continue to | ||
4536 | * iterate, | ||
4537 | * GNUNET_NO if not. | ||
4538 | */ | ||
4539 | static int | ||
4540 | find_closest_peers (void *cls, const GNUNET_HashCode * key, void *value) | ||
4541 | { | ||
4542 | struct FindClosestContext *closest_ctx = cls; | ||
4543 | struct GNUNET_TESTING_Daemon *daemon = value; | ||
4544 | |||
4545 | if (((closest_ctx->closest == NULL) || | ||
4546 | (GNUNET_CRYPTO_hash_matching_bits | ||
4547 | (&daemon->id.hashPubKey, | ||
4548 | &closest_ctx->curr_peer->daemon->id.hashPubKey) > | ||
4549 | closest_ctx->closest_dist)) && | ||
4550 | (GNUNET_YES != | ||
4551 | GNUNET_CONTAINER_multihashmap_contains (closest_ctx-> | ||
4552 | curr_peer->connect_peers, key))) | ||
4553 | { | ||
4554 | closest_ctx->closest_dist = | ||
4555 | GNUNET_CRYPTO_hash_matching_bits (&daemon->id.hashPubKey, | ||
4556 | &closest_ctx->curr_peer->daemon-> | ||
4557 | id.hashPubKey); | ||
4558 | closest_ctx->closest = daemon; | ||
4559 | uid_from_hash (key, &closest_ctx->closest_num); | ||
4560 | } | ||
4561 | return GNUNET_YES; | ||
4562 | } | ||
4563 | |||
4564 | /** | ||
4565 | * From the set of connections possible, choose at num connections per | ||
4566 | * peer based on depth which are closest out of those allowed. Guaranteed | ||
4567 | * to add num peers to connect to, provided there are that many peers | ||
4568 | * in the underlay topology to connect to. | ||
4569 | * | ||
4570 | * @param pg the peergroup we are dealing with | ||
4571 | * @param num how many connections at least should each peer have (if possible)? | ||
4572 | * @param proc processor to actually add the connections | ||
4573 | * @param list the peer list to use | ||
4574 | */ | ||
4575 | void | ||
4576 | add_closest (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num, | ||
4577 | GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list) | ||
4578 | { | ||
4579 | #if OLD | ||
4580 | |||
4581 | #else | ||
4582 | struct FindClosestContext closest_ctx; | ||
4583 | #endif | ||
4584 | uint32_t pg_iter; | ||
4585 | uint32_t i; | ||
4586 | |||
4587 | for (i = 0; i < num; i++) /* Each time find a closest peer (from those available) */ | ||
4588 | { | ||
4589 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4590 | { | ||
4591 | closest_ctx.curr_peer = &pg->peers[pg_iter]; | ||
4592 | closest_ctx.closest = NULL; | ||
4593 | closest_ctx.closest_dist = 0; | ||
4594 | closest_ctx.closest_num = 0; | ||
4595 | GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers, | ||
4596 | &find_closest_peers, &closest_ctx); | ||
4597 | if (closest_ctx.closest != NULL) | ||
4598 | { | ||
4599 | GNUNET_assert (closest_ctx.closest_num < pg->total); | ||
4600 | proc (pg, pg_iter, closest_ctx.closest_num, list); | ||
4601 | } | ||
4602 | } | ||
4603 | } | ||
4604 | } | ||
4605 | #endif | ||
4606 | |||
4607 | /** | ||
4608 | * From the set of connections possible, choose at least num connections per | ||
4609 | * peer based on depth first traversal of peer connections. If DFS leaves | ||
4610 | * peers unconnected, ensure those peers get connections. | ||
4611 | * | ||
4612 | * @param pg the peergroup we are dealing with | ||
4613 | * @param num how many connections at least should each peer have (if possible)? | ||
4614 | */ | ||
4615 | void | ||
4616 | perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num) | ||
4617 | { | ||
4618 | uint32_t pg_iter; | ||
4619 | uint32_t dfs_count; | ||
4620 | uint32_t starting_peer; | ||
4621 | uint32_t least_connections; | ||
4622 | uint32_t random_connection; | ||
4623 | |||
4624 | #if OLD | ||
4625 | unsigned int temp_count; | ||
4626 | struct PeerConnection *peer_iter; | ||
4627 | #else | ||
4628 | struct DFSContext dfs_ctx; | ||
4629 | GNUNET_HashCode second_hash; | ||
4630 | #endif | ||
4631 | |||
4632 | #if OLD | ||
4633 | starting_peer = 0; | ||
4634 | dfs_count = 0; | ||
4635 | while ((count_workingset_connections (pg) < num * pg->total) && | ||
4636 | (count_allowed_connections (pg) > 0)) | ||
4637 | { | ||
4638 | if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */ | ||
4639 | { | ||
4640 | least_connections = -1; /* Set to very high number */ | ||
4641 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4642 | { | ||
4643 | temp_count = | ||
4644 | count_connections (pg-> | ||
4645 | peers[pg_iter].connect_peers_working_set_head); | ||
4646 | if (temp_count < least_connections) | ||
4647 | { | ||
4648 | starting_peer = pg_iter; | ||
4649 | least_connections = temp_count; | ||
4650 | } | ||
4651 | } | ||
4652 | } | ||
4653 | |||
4654 | temp_count = | ||
4655 | count_connections (pg->peers[starting_peer].connect_peers_head); | ||
4656 | if (temp_count == 0) | ||
4657 | continue; /* FIXME: infinite loop? */ | ||
4658 | |||
4659 | random_connection = | ||
4660 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, temp_count); | ||
4661 | temp_count = 0; | ||
4662 | peer_iter = pg->peers[starting_peer].connect_peers_head; | ||
4663 | while (temp_count < random_connection) | ||
4664 | { | ||
4665 | peer_iter = peer_iter->next; | ||
4666 | temp_count++; | ||
4667 | } | ||
4668 | GNUNET_assert (peer_iter != NULL); | ||
4669 | add_connections (pg, starting_peer, peer_iter->index, WORKING_SET, | ||
4670 | GNUNET_NO); | ||
4671 | remove_connections (pg, starting_peer, peer_iter->index, CONNECT, | ||
4672 | GNUNET_YES); | ||
4673 | starting_peer = peer_iter->index; | ||
4674 | dfs_count++; | ||
4675 | } | ||
4676 | |||
4677 | #else | ||
4678 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4679 | { | ||
4680 | pg->peers[pg_iter].connect_peers_working_set = | ||
4681 | GNUNET_CONTAINER_multihashmap_create (num); | ||
4682 | } | ||
4683 | |||
4684 | starting_peer = 0; | ||
4685 | dfs_count = 0; | ||
4686 | while ((count_workingset_connections (pg) < num * pg->total) && | ||
4687 | (count_allowed_connections (pg) > 0)) | ||
4688 | { | ||
4689 | if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */ | ||
4690 | { | ||
4691 | least_connections = -1; /* Set to very high number */ | ||
4692 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4693 | { | ||
4694 | if (GNUNET_CONTAINER_multihashmap_size | ||
4695 | (pg->peers[pg_iter].connect_peers_working_set) < least_connections) | ||
4696 | { | ||
4697 | starting_peer = pg_iter; | ||
4698 | least_connections = | ||
4699 | GNUNET_CONTAINER_multihashmap_size (pg-> | ||
4700 | peers | ||
4701 | [pg_iter].connect_peers_working_set); | ||
4702 | } | ||
4703 | } | ||
4704 | } | ||
4705 | |||
4706 | if (GNUNET_CONTAINER_multihashmap_size (pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */ | ||
4707 | { | ||
4708 | dfs_count = 0; | ||
4709 | continue; | ||
4710 | } | ||
4711 | |||
4712 | /* Choose a random peer from the chosen peers set of connections to add */ | ||
4713 | dfs_ctx.chosen = | ||
4714 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
4715 | GNUNET_CONTAINER_multihashmap_size (pg->peers | ||
4716 | [starting_peer].connect_peers)); | ||
4717 | dfs_ctx.first_uid = starting_peer; | ||
4718 | dfs_ctx.first = &pg->peers[starting_peer]; | ||
4719 | dfs_ctx.pg = pg; | ||
4720 | dfs_ctx.current = 0; | ||
4721 | |||
4722 | GNUNET_CONTAINER_multihashmap_iterate (pg-> | ||
4723 | peers[starting_peer].connect_peers, | ||
4724 | &dfs_connect_iterator, &dfs_ctx); | ||
4725 | /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */ | ||
4726 | hash_from_uid (dfs_ctx.second_uid, &second_hash); | ||
4727 | GNUNET_assert (GNUNET_YES == | ||
4728 | GNUNET_CONTAINER_multihashmap_remove (pg->peers | ||
4729 | [starting_peer].connect_peers, | ||
4730 | &second_hash, | ||
4731 | pg-> | ||
4732 | peers | ||
4733 | [dfs_ctx.second_uid].daemon)); | ||
4734 | starting_peer = dfs_ctx.second_uid; | ||
4735 | } | ||
4736 | |||
4737 | for (pg_iter = 0; pg_iter < pg->total; pg_iter++) | ||
4738 | { | ||
4739 | /* Remove the "old" connections */ | ||
4740 | GNUNET_CONTAINER_multihashmap_destroy (pg->peers[pg_iter].connect_peers); | ||
4741 | /* And replace with the working set */ | ||
4742 | pg->peers[pg_iter].connect_peers = | ||
4743 | pg->peers[pg_iter].connect_peers_working_set; | ||
4744 | } | ||
4745 | #endif | ||
4746 | } | ||
4747 | |||
4748 | /** | ||
4749 | * Internal callback for topology information for a particular peer. | ||
4750 | */ | ||
4751 | static void | ||
4752 | internal_topology_callback (void *cls, const struct GNUNET_PeerIdentity *peer, | ||
4753 | const struct GNUNET_ATS_Information *atsi, | ||
4754 | unsigned int atsi_count) | ||
4755 | { | ||
4756 | struct CoreContext *core_ctx = cls; | ||
4757 | struct TopologyIterateContext *iter_ctx = core_ctx->iter_context; | ||
4758 | |||
4759 | if (peer == NULL) /* Either finished, or something went wrong */ | ||
4760 | { | ||
4761 | iter_ctx->completed++; | ||
4762 | iter_ctx->connected--; | ||
4763 | /* One core context allocated per iteration, must free! */ | ||
4764 | GNUNET_free (core_ctx); | ||
4765 | } | ||
4766 | else | ||
4767 | { | ||
4768 | iter_ctx->topology_cb (iter_ctx->cls, &core_ctx->daemon->id, peer, NULL); | ||
4769 | } | ||
4770 | |||
4771 | if (iter_ctx->completed == iter_ctx->total) | ||
4772 | { | ||
4773 | iter_ctx->topology_cb (iter_ctx->cls, NULL, NULL, NULL); | ||
4774 | /* Once all are done, free the iteration context */ | ||
4775 | GNUNET_free (iter_ctx); | ||
4776 | } | ||
4777 | } | ||
4778 | |||
4779 | /** | ||
4780 | * Check running topology iteration tasks, if below max start a new one, otherwise | ||
4781 | * schedule for some time in the future. | ||
4782 | */ | ||
4783 | static void | ||
4784 | schedule_get_topology (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
4785 | { | ||
4786 | struct CoreContext *core_context = cls; | ||
4787 | struct TopologyIterateContext *topology_context = | ||
4788 | (struct TopologyIterateContext *) core_context->iter_context; | ||
4789 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
4790 | return; | ||
4791 | |||
4792 | if (topology_context->connected > | ||
4793 | topology_context->pg->max_outstanding_connections) | ||
4794 | { | ||
4795 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4796 | "Delaying connect, we have too many outstanding connections!\n"); | ||
4797 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
4798 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
4799 | &schedule_get_topology, core_context); | ||
4800 | } | ||
4801 | else | ||
4802 | { | ||
4803 | topology_context->connected++; | ||
4804 | |||
4805 | if (GNUNET_OK != | ||
4806 | GNUNET_CORE_iterate_peers (core_context->daemon->cfg, | ||
4807 | &internal_topology_callback, core_context)) | ||
4808 | { | ||
4809 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Topology iteration failed.\n"); | ||
4810 | internal_topology_callback (core_context, NULL, NULL, 0); | ||
4811 | } | ||
4812 | } | ||
4813 | } | ||
4814 | |||
4815 | /** | ||
4816 | * Iterate over all (running) peers in the peer group, retrieve | ||
4817 | * all connections that each currently has. | ||
4818 | */ | ||
4819 | void | ||
4820 | GNUNET_TESTING_get_topology (struct GNUNET_TESTING_PeerGroup *pg, | ||
4821 | GNUNET_TESTING_NotifyTopology cb, void *cls) | ||
4822 | { | ||
4823 | struct TopologyIterateContext *topology_context; | ||
4824 | struct CoreContext *core_ctx; | ||
4825 | unsigned int i; | ||
4826 | unsigned int total_count; | ||
4827 | |||
4828 | /* Allocate a single topology iteration context */ | ||
4829 | topology_context = GNUNET_malloc (sizeof (struct TopologyIterateContext)); | ||
4830 | topology_context->topology_cb = cb; | ||
4831 | topology_context->cls = cls; | ||
4832 | topology_context->pg = pg; | ||
4833 | total_count = 0; | ||
4834 | for (i = 0; i < pg->total; i++) | ||
4835 | { | ||
4836 | if (pg->peers[i].daemon->running == GNUNET_YES) | ||
4837 | { | ||
4838 | /* Allocate one core context per core we need to connect to */ | ||
4839 | core_ctx = GNUNET_malloc (sizeof (struct CoreContext)); | ||
4840 | core_ctx->daemon = pg->peers[i].daemon; | ||
4841 | /* Set back pointer to topology iteration context */ | ||
4842 | core_ctx->iter_context = topology_context; | ||
4843 | GNUNET_SCHEDULER_add_now (&schedule_get_topology, core_ctx); | ||
4844 | total_count++; | ||
4845 | } | ||
4846 | } | ||
4847 | if (total_count == 0) | ||
4848 | { | ||
4849 | cb (cls, NULL, NULL, "Cannot iterate over topology, no running peers!"); | ||
4850 | GNUNET_free (topology_context); | ||
4851 | } | ||
4852 | else | ||
4853 | topology_context->total = total_count; | ||
4854 | return; | ||
4855 | } | ||
4856 | |||
4857 | /** | ||
4858 | * Callback function to process statistic values. | ||
4859 | * This handler is here only really to insert a peer | ||
4860 | * identity (or daemon) so the statistics can be uniquely | ||
4861 | * tied to a single running peer. | ||
4862 | * | ||
4863 | * @param cls closure | ||
4864 | * @param subsystem name of subsystem that created the statistic | ||
4865 | * @param name the name of the datum | ||
4866 | * @param value the current value | ||
4867 | * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not | ||
4868 | * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration | ||
4869 | */ | ||
4870 | static int | ||
4871 | internal_stats_callback (void *cls, const char *subsystem, const char *name, | ||
4872 | uint64_t value, int is_persistent) | ||
4873 | { | ||
4874 | struct StatsCoreContext *core_context = cls; | ||
4875 | struct StatsIterateContext *stats_context = | ||
4876 | (struct StatsIterateContext *) core_context->iter_context; | ||
4877 | |||
4878 | return stats_context->proc (stats_context->cls, &core_context->daemon->id, | ||
4879 | subsystem, name, value, is_persistent); | ||
4880 | } | ||
4881 | |||
4882 | |||
4883 | /** | ||
4884 | * We don't need the statistics handle anymore, destroy it. | ||
4885 | * | ||
4886 | * @param cls Closure (the statistics handle to destroy) | ||
4887 | * @param tc Task Context | ||
4888 | */ | ||
4889 | static void | ||
4890 | internal_destroy_statistics (void *cls, | ||
4891 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
4892 | { | ||
4893 | struct GNUNET_STATISTICS_Handle *h = cls; | ||
4894 | |||
4895 | GNUNET_STATISTICS_destroy (h, GNUNET_NO); | ||
4896 | } | ||
4897 | |||
4898 | |||
4899 | /** | ||
4900 | * Internal continuation call for statistics iteration. | ||
4901 | * | ||
4902 | * @param cls closure, the CoreContext for this iteration | ||
4903 | * @param success whether or not the statistics iterations | ||
4904 | * was canceled or not (we don't care) | ||
4905 | */ | ||
4906 | static void | ||
4907 | internal_stats_cont (void *cls, int success) | ||
4908 | { | ||
4909 | struct StatsCoreContext *core_context = cls; | ||
4910 | struct StatsIterateContext *stats_context = | ||
4911 | (struct StatsIterateContext *) core_context->iter_context; | ||
4912 | |||
4913 | stats_context->connected--; | ||
4914 | stats_context->completed++; | ||
4915 | |||
4916 | if (stats_context->completed == stats_context->total) | ||
4917 | { | ||
4918 | stats_context->cont (stats_context->cls, GNUNET_YES); | ||
4919 | GNUNET_free (stats_context); | ||
4920 | } | ||
4921 | |||
4922 | if (core_context->stats_handle != NULL) | ||
4923 | /* Cannot destroy handle inside the continuation */ | ||
4924 | GNUNET_SCHEDULER_add_now (&internal_destroy_statistics, | ||
4925 | core_context->stats_handle); | ||
4926 | |||
4927 | GNUNET_free (core_context); | ||
4928 | } | ||
4929 | |||
4930 | /** | ||
4931 | * Check running topology iteration tasks, if below max start a new one, otherwise | ||
4932 | * schedule for some time in the future. | ||
4933 | */ | ||
4934 | static void | ||
4935 | schedule_get_statistics (void *cls, | ||
4936 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
4937 | { | ||
4938 | struct StatsCoreContext *core_context = cls; | ||
4939 | struct StatsIterateContext *stats_context = | ||
4940 | (struct StatsIterateContext *) core_context->iter_context; | ||
4941 | |||
4942 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
4943 | return; | ||
4944 | |||
4945 | if (stats_context->connected > stats_context->pg->max_outstanding_connections) | ||
4946 | { | ||
4947 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
4948 | "Delaying connect, we have too many outstanding connections!\n"); | ||
4949 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
4950 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
4951 | &schedule_get_statistics, core_context); | ||
4952 | } | ||
4953 | else | ||
4954 | { | ||
4955 | stats_context->connected++; | ||
4956 | core_context->stats_handle = | ||
4957 | GNUNET_STATISTICS_create ("testing_old", core_context->daemon->cfg); | ||
4958 | if (core_context->stats_handle == NULL) | ||
4959 | { | ||
4960 | internal_stats_cont (core_context, GNUNET_NO); | ||
4961 | return; | ||
4962 | } | ||
4963 | |||
4964 | core_context->stats_get_handle = | ||
4965 | GNUNET_STATISTICS_get (core_context->stats_handle, NULL, NULL, | ||
4966 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
4967 | &internal_stats_cont, &internal_stats_callback, | ||
4968 | core_context); | ||
4969 | if (core_context->stats_get_handle == NULL) | ||
4970 | internal_stats_cont (core_context, GNUNET_NO); | ||
4971 | |||
4972 | } | ||
4973 | } | ||
4974 | |||
4975 | struct DuplicateStats | ||
4976 | { | ||
4977 | /** | ||
4978 | * Next item in the list | ||
4979 | */ | ||
4980 | struct DuplicateStats *next; | ||
4981 | |||
4982 | /** | ||
4983 | * Nasty string, concatenation of relevant information. | ||
4984 | */ | ||
4985 | char *unique_string; | ||
4986 | }; | ||
4987 | |||
4988 | /** | ||
4989 | * Check whether the combination of port/host/unix domain socket | ||
4990 | * already exists in the list of peers being checked for statistics. | ||
4991 | * | ||
4992 | * @param pg the peergroup in question | ||
4993 | * @param specific_peer the peer we're concerned with | ||
4994 | * @param stats_list the list to return to the caller | ||
4995 | * | ||
4996 | * @return GNUNET_YES if the statistics instance has been seen already, | ||
4997 | * GNUNET_NO if not (and we may have added it to the list) | ||
4998 | */ | ||
4999 | static int | ||
5000 | stats_check_existing (struct GNUNET_TESTING_PeerGroup *pg, | ||
5001 | struct PeerData *specific_peer, | ||
5002 | struct DuplicateStats **stats_list) | ||
5003 | { | ||
5004 | struct DuplicateStats *pos; | ||
5005 | char *unix_domain_socket; | ||
5006 | unsigned long long port; | ||
5007 | char *to_match; | ||
5008 | |||
5009 | if (GNUNET_YES != | ||
5010 | GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "testing_old", | ||
5011 | "single_statistics_per_host")) | ||
5012 | return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */ | ||
5013 | |||
5014 | pos = *stats_list; | ||
5015 | if (GNUNET_OK != | ||
5016 | GNUNET_CONFIGURATION_get_value_string (specific_peer->cfg, "statistics", | ||
5017 | "unixpath", &unix_domain_socket)) | ||
5018 | return GNUNET_NO; | ||
5019 | |||
5020 | if (GNUNET_OK != | ||
5021 | GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg, "statistics", | ||
5022 | "port", &port)) | ||
5023 | { | ||
5024 | GNUNET_free (unix_domain_socket); | ||
5025 | return GNUNET_NO; | ||
5026 | } | ||
5027 | |||
5028 | if (specific_peer->daemon->hostname != NULL) | ||
5029 | GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname, | ||
5030 | unix_domain_socket, port); | ||
5031 | else | ||
5032 | GNUNET_asprintf (&to_match, "%s%llu", unix_domain_socket, port); | ||
5033 | |||
5034 | while (pos != NULL) | ||
5035 | { | ||
5036 | if (0 == strcmp (to_match, pos->unique_string)) | ||
5037 | { | ||
5038 | GNUNET_free (unix_domain_socket); | ||
5039 | GNUNET_free (to_match); | ||
5040 | return GNUNET_YES; | ||
5041 | } | ||
5042 | pos = pos->next; | ||
5043 | } | ||
5044 | pos = GNUNET_malloc (sizeof (struct DuplicateStats)); | ||
5045 | pos->unique_string = to_match; | ||
5046 | pos->next = *stats_list; | ||
5047 | *stats_list = pos; | ||
5048 | GNUNET_free (unix_domain_socket); | ||
5049 | return GNUNET_NO; | ||
5050 | } | ||
5051 | |||
5052 | /** | ||
5053 | * Iterate over all (running) peers in the peer group, retrieve | ||
5054 | * all statistics from each. | ||
5055 | * | ||
5056 | * @param pg the peergroup to iterate statistics of | ||
5057 | * @param cont continuation to call once all stats have been retrieved | ||
5058 | * @param proc processing function for each statistic from each peer | ||
5059 | * @param cls closure to pass to proc | ||
5060 | * | ||
5061 | */ | ||
5062 | void | ||
5063 | GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg, | ||
5064 | GNUNET_STATISTICS_Callback cont, | ||
5065 | GNUNET_TESTING_STATISTICS_Iterator proc, | ||
5066 | void *cls) | ||
5067 | { | ||
5068 | struct StatsIterateContext *stats_context; | ||
5069 | struct StatsCoreContext *core_ctx; | ||
5070 | unsigned int i; | ||
5071 | unsigned int total_count; | ||
5072 | struct DuplicateStats *stats_list; | ||
5073 | struct DuplicateStats *pos; | ||
5074 | |||
5075 | stats_list = NULL; | ||
5076 | |||
5077 | /* Allocate a single stats iteration context */ | ||
5078 | stats_context = GNUNET_malloc (sizeof (struct StatsIterateContext)); | ||
5079 | stats_context->cont = cont; | ||
5080 | stats_context->proc = proc; | ||
5081 | stats_context->cls = cls; | ||
5082 | stats_context->pg = pg; | ||
5083 | total_count = 0; | ||
5084 | |||
5085 | for (i = 0; i < pg->total; i++) | ||
5086 | { | ||
5087 | if ((pg->peers[i].daemon->running == GNUNET_YES) && | ||
5088 | (GNUNET_NO == stats_check_existing (pg, &pg->peers[i], &stats_list))) | ||
5089 | { | ||
5090 | /* Allocate one core context per core we need to connect to */ | ||
5091 | core_ctx = GNUNET_malloc (sizeof (struct StatsCoreContext)); | ||
5092 | core_ctx->daemon = pg->peers[i].daemon; | ||
5093 | /* Set back pointer to topology iteration context */ | ||
5094 | core_ctx->iter_context = stats_context; | ||
5095 | GNUNET_SCHEDULER_add_now (&schedule_get_statistics, core_ctx); | ||
5096 | total_count++; | ||
5097 | } | ||
5098 | } | ||
5099 | |||
5100 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5101 | "Retrieving stats from %u total instances.\n", total_count); | ||
5102 | if (0 != total_count) | ||
5103 | stats_context->total = total_count; | ||
5104 | else | ||
5105 | GNUNET_free (stats_context); | ||
5106 | if (stats_list != NULL) | ||
5107 | { | ||
5108 | pos = stats_list; | ||
5109 | while (pos != NULL) | ||
5110 | { | ||
5111 | GNUNET_free (pos->unique_string); | ||
5112 | stats_list = pos->next; | ||
5113 | GNUNET_free (pos); | ||
5114 | pos = stats_list->next; | ||
5115 | } | ||
5116 | } | ||
5117 | return; | ||
5118 | } | ||
5119 | |||
5120 | /** | ||
5121 | * Stop the connection process temporarily. | ||
5122 | * | ||
5123 | * @param pg the peer group to stop connecting | ||
5124 | */ | ||
5125 | void | ||
5126 | GNUNET_TESTING_stop_connections (struct GNUNET_TESTING_PeerGroup *pg) | ||
5127 | { | ||
5128 | pg->stop_connects = GNUNET_YES; | ||
5129 | } | ||
5130 | |||
5131 | /** | ||
5132 | * Resume the connection process temporarily. | ||
5133 | * | ||
5134 | * @param pg the peer group to resume connecting | ||
5135 | */ | ||
5136 | void | ||
5137 | GNUNET_TESTING_resume_connections (struct GNUNET_TESTING_PeerGroup *pg) | ||
5138 | { | ||
5139 | pg->stop_connects = GNUNET_NO; | ||
5140 | } | ||
5141 | |||
5142 | /** | ||
5143 | * There are many ways to connect peers that are supported by this function. | ||
5144 | * To connect peers in the same topology that was created via the | ||
5145 | * GNUNET_TESTING_create_topology, the topology variable must be set to | ||
5146 | * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified, | ||
5147 | * a new instance of that topology will be generated and attempted to be | ||
5148 | * connected. This could result in some connections being impossible, | ||
5149 | * because some topologies are non-deterministic. | ||
5150 | * | ||
5151 | * @param pg the peer group struct representing the running peers | ||
5152 | * @param topology which topology to connect the peers in | ||
5153 | * @param options options for connecting the topology | ||
5154 | * @param option_modifier modifier for options that take a parameter | ||
5155 | * @param connect_timeout how long to wait before giving up on connecting | ||
5156 | * two peers | ||
5157 | * @param connect_attempts how many times to attempt to connect two peers | ||
5158 | * over the connect_timeout duration | ||
5159 | * @param notify_callback notification to be called once all connections completed | ||
5160 | * @param notify_cls closure for notification callback | ||
5161 | * | ||
5162 | * @return the number of connections that will be attempted, GNUNET_SYSERR on error | ||
5163 | */ | ||
5164 | int | ||
5165 | GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg, | ||
5166 | enum GNUNET_TESTING_Topology topology, | ||
5167 | enum GNUNET_TESTING_TopologyOption options, | ||
5168 | double option_modifier, | ||
5169 | struct GNUNET_TIME_Relative connect_timeout, | ||
5170 | unsigned int connect_attempts, | ||
5171 | GNUNET_TESTING_NotifyCompletion | ||
5172 | notify_callback, void *notify_cls) | ||
5173 | { | ||
5174 | switch (topology) | ||
5175 | { | ||
5176 | case GNUNET_TESTING_TOPOLOGY_CLIQUE: | ||
5177 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5178 | "Creating clique CONNECT topology\n"); | ||
5179 | create_clique (pg, &add_connections, CONNECT, GNUNET_NO); | ||
5180 | break; | ||
5181 | case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING: | ||
5182 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5183 | "Creating small world (ring) CONNECT topology\n"); | ||
5184 | create_small_world_ring (pg, &add_connections, CONNECT); | ||
5185 | break; | ||
5186 | case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD: | ||
5187 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5188 | "Creating small world (2d-torus) CONNECT topology\n"); | ||
5189 | create_small_world (pg, &add_connections, CONNECT); | ||
5190 | break; | ||
5191 | case GNUNET_TESTING_TOPOLOGY_RING: | ||
5192 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating ring CONNECT topology\n"); | ||
5193 | create_ring (pg, &add_connections, CONNECT); | ||
5194 | break; | ||
5195 | case GNUNET_TESTING_TOPOLOGY_2D_TORUS: | ||
5196 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5197 | "Creating 2d torus CONNECT topology\n"); | ||
5198 | create_2d_torus (pg, &add_connections, CONNECT); | ||
5199 | break; | ||
5200 | case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI: | ||
5201 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5202 | "Creating Erdos-Renyi CONNECT topology\n"); | ||
5203 | create_erdos_renyi (pg, &add_connections, CONNECT); | ||
5204 | break; | ||
5205 | case GNUNET_TESTING_TOPOLOGY_INTERNAT: | ||
5206 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5207 | "Creating InterNAT CONNECT topology\n"); | ||
5208 | create_nated_internet (pg, &add_connections, CONNECT); | ||
5209 | break; | ||
5210 | case GNUNET_TESTING_TOPOLOGY_SCALE_FREE: | ||
5211 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5212 | "Creating Scale Free CONNECT topology\n"); | ||
5213 | create_scale_free (pg, &add_connections, CONNECT); | ||
5214 | break; | ||
5215 | case GNUNET_TESTING_TOPOLOGY_LINE: | ||
5216 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5217 | "Creating straight line CONNECT topology\n"); | ||
5218 | create_line (pg, &add_connections, CONNECT); | ||
5219 | break; | ||
5220 | case GNUNET_TESTING_TOPOLOGY_NONE: | ||
5221 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating no CONNECT topology\n"); | ||
5222 | copy_allowed_topology (pg); | ||
5223 | break; | ||
5224 | default: | ||
5225 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
5226 | _("Unknown topology specification, can't connect peers!\n")); | ||
5227 | return GNUNET_SYSERR; | ||
5228 | } | ||
5229 | |||
5230 | switch (options) | ||
5231 | { | ||
5232 | case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM: | ||
5233 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5234 | "Connecting random subset (%'.2f percent) of possible peers\n", | ||
5235 | 100 * option_modifier); | ||
5236 | choose_random_connections (pg, option_modifier); | ||
5237 | break; | ||
5238 | case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM: | ||
5239 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5240 | "Connecting a minimum of %u peers each (if possible)\n", | ||
5241 | (unsigned int) option_modifier); | ||
5242 | choose_minimum (pg, (unsigned int) option_modifier); | ||
5243 | break; | ||
5244 | case GNUNET_TESTING_TOPOLOGY_OPTION_DFS: | ||
5245 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5246 | "Using DFS to connect a minimum of %u peers each (if possible)\n", | ||
5247 | (unsigned int) option_modifier); | ||
5248 | #if FIXME | ||
5249 | perform_dfs (pg, (int) option_modifier); | ||
5250 | #endif | ||
5251 | break; | ||
5252 | case GNUNET_TESTING_TOPOLOGY_OPTION_ADD_CLOSEST: | ||
5253 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5254 | "Finding additional %u closest peers each (if possible)\n", | ||
5255 | (unsigned int) option_modifier); | ||
5256 | #if FIXME | ||
5257 | add_closest (pg, (unsigned int) option_modifier, &add_connections, CONNECT); | ||
5258 | #endif | ||
5259 | break; | ||
5260 | case GNUNET_TESTING_TOPOLOGY_OPTION_NONE: | ||
5261 | break; | ||
5262 | case GNUNET_TESTING_TOPOLOGY_OPTION_ALL: | ||
5263 | break; | ||
5264 | default: | ||
5265 | break; | ||
5266 | } | ||
5267 | |||
5268 | return connect_topology (pg, connect_timeout, connect_attempts, | ||
5269 | notify_callback, notify_cls); | ||
5270 | } | ||
5271 | |||
5272 | /** | ||
5273 | * Lookup and return the number of SSH connections to a host. | ||
5274 | * | ||
5275 | * @param hostname the hostname to lookup in the list | ||
5276 | * @param pg the peergroup that the host belongs to | ||
5277 | * | ||
5278 | * @return the number of current ssh connections to the host | ||
5279 | */ | ||
5280 | static unsigned int | ||
5281 | count_outstanding_at_host (const char *hostname, | ||
5282 | struct GNUNET_TESTING_PeerGroup *pg) | ||
5283 | { | ||
5284 | struct OutstandingSSH *pos; | ||
5285 | |||
5286 | pos = pg->ssh_head; | ||
5287 | while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0)) | ||
5288 | pos = pos->next; | ||
5289 | GNUNET_assert (pos != NULL); | ||
5290 | return pos->outstanding; | ||
5291 | } | ||
5292 | |||
5293 | /** | ||
5294 | * Increment the number of SSH connections to a host by one. | ||
5295 | * | ||
5296 | * @param hostname the hostname to lookup in the list | ||
5297 | * @param pg the peergroup that the host belongs to | ||
5298 | * | ||
5299 | */ | ||
5300 | static void | ||
5301 | increment_outstanding_at_host (const char *hostname, | ||
5302 | struct GNUNET_TESTING_PeerGroup *pg) | ||
5303 | { | ||
5304 | struct OutstandingSSH *pos; | ||
5305 | |||
5306 | pos = pg->ssh_head; | ||
5307 | while ((NULL != pos) && (strcmp (pos->hostname, hostname) != 0)) | ||
5308 | pos = pos->next; | ||
5309 | GNUNET_assert (NULL != pos); | ||
5310 | pos->outstanding++; | ||
5311 | } | ||
5312 | |||
5313 | /** | ||
5314 | * Decrement the number of SSH connections to a host by one. | ||
5315 | * | ||
5316 | * @param hostname the hostname to lookup in the list | ||
5317 | * @param pg the peergroup that the host belongs to | ||
5318 | * | ||
5319 | */ | ||
5320 | static void | ||
5321 | decrement_outstanding_at_host (const char *hostname, | ||
5322 | struct GNUNET_TESTING_PeerGroup *pg) | ||
5323 | { | ||
5324 | struct OutstandingSSH *pos; | ||
5325 | |||
5326 | pos = pg->ssh_head; | ||
5327 | while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0)) | ||
5328 | pos = pos->next; | ||
5329 | GNUNET_assert (pos != NULL); | ||
5330 | pos->outstanding--; | ||
5331 | } | ||
5332 | |||
5333 | /** | ||
5334 | * Callback that is called whenever a hostkey is generated | ||
5335 | * for a peer. Call the real callback and decrement the | ||
5336 | * starting counter for the peergroup. | ||
5337 | * | ||
5338 | * @param cls closure | ||
5339 | * @param id identifier for the daemon, NULL on error | ||
5340 | * @param d handle for the daemon | ||
5341 | * @param emsg error message (NULL on success) | ||
5342 | */ | ||
5343 | static void | ||
5344 | internal_hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id, | ||
5345 | struct GNUNET_TESTING_Daemon *d, const char *emsg) | ||
5346 | { | ||
5347 | struct InternalStartContext *internal_context = cls; | ||
5348 | |||
5349 | internal_context->peer->pg->starting--; | ||
5350 | internal_context->peer->pg->started++; | ||
5351 | if (internal_context->hostname != NULL) | ||
5352 | decrement_outstanding_at_host (internal_context->hostname, | ||
5353 | internal_context->peer->pg); | ||
5354 | if (internal_context->hostkey_callback != NULL) | ||
5355 | internal_context->hostkey_callback (internal_context->hostkey_cls, id, d, | ||
5356 | emsg); | ||
5357 | else if (internal_context->peer->pg->started == | ||
5358 | internal_context->peer->pg->total) | ||
5359 | { | ||
5360 | internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */ | ||
5361 | GNUNET_TESTING_daemons_continue_startup (internal_context->peer->pg); | ||
5362 | } | ||
5363 | } | ||
5364 | |||
5365 | /** | ||
5366 | * Callback that is called whenever a peer has finished starting. | ||
5367 | * Call the real callback and decrement the starting counter | ||
5368 | * for the peergroup. | ||
5369 | * | ||
5370 | * @param cls closure | ||
5371 | * @param id identifier for the daemon, NULL on error | ||
5372 | * @param cfg config | ||
5373 | * @param d handle for the daemon | ||
5374 | * @param emsg error message (NULL on success) | ||
5375 | */ | ||
5376 | static void | ||
5377 | internal_startup_callback (void *cls, const struct GNUNET_PeerIdentity *id, | ||
5378 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
5379 | struct GNUNET_TESTING_Daemon *d, const char *emsg) | ||
5380 | { | ||
5381 | struct InternalStartContext *internal_context = cls; | ||
5382 | |||
5383 | internal_context->peer->pg->starting--; | ||
5384 | if (internal_context->hostname != NULL) | ||
5385 | decrement_outstanding_at_host (internal_context->hostname, | ||
5386 | internal_context->peer->pg); | ||
5387 | if (internal_context->start_cb != NULL) | ||
5388 | internal_context->start_cb (internal_context->start_cb_cls, id, cfg, d, | ||
5389 | emsg); | ||
5390 | } | ||
5391 | |||
5392 | |||
5393 | /** | ||
5394 | * Calls GNUNET_TESTING_daemon_continue_startup to set the daemon's state | ||
5395 | * from HOSTKEY_CREATED to TOPOLOGY_SETUP. Makes sure not to saturate a host | ||
5396 | * with requests delaying them when needed. | ||
5397 | * | ||
5398 | * @param cls closure: internal context of the daemon. | ||
5399 | * @param tc TaskContext | ||
5400 | */ | ||
5401 | static void | ||
5402 | internal_continue_startup (void *cls, | ||
5403 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5404 | { | ||
5405 | struct InternalStartContext *internal_context = cls; | ||
5406 | |||
5407 | internal_context->peer->startup_task = GNUNET_SCHEDULER_NO_TASK; | ||
5408 | |||
5409 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
5410 | { | ||
5411 | return; | ||
5412 | } | ||
5413 | |||
5414 | if ((internal_context->peer->pg->starting < | ||
5415 | internal_context->peer->pg->max_concurrent_ssh) || | ||
5416 | ((internal_context->hostname != NULL) && | ||
5417 | (count_outstanding_at_host | ||
5418 | (internal_context->hostname, | ||
5419 | internal_context->peer->pg) < | ||
5420 | internal_context->peer->pg->max_concurrent_ssh))) | ||
5421 | { | ||
5422 | if (internal_context->hostname != NULL) | ||
5423 | increment_outstanding_at_host (internal_context->hostname, | ||
5424 | internal_context->peer->pg); | ||
5425 | internal_context->peer->pg->starting++; | ||
5426 | GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon); | ||
5427 | } | ||
5428 | else | ||
5429 | { | ||
5430 | internal_context->peer->startup_task = | ||
5431 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
5432 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
5433 | &internal_continue_startup, | ||
5434 | internal_context); | ||
5435 | } | ||
5436 | } | ||
5437 | |||
5438 | /** | ||
5439 | * Callback for informing us about a successful | ||
5440 | * or unsuccessful churn start call. | ||
5441 | * | ||
5442 | * @param cls a ChurnContext | ||
5443 | * @param id the peer identity of the started peer | ||
5444 | * @param cfg the handle to the configuration of the peer | ||
5445 | * @param d handle to the daemon for the peer | ||
5446 | * @param emsg NULL on success, non-NULL on failure | ||
5447 | * | ||
5448 | */ | ||
5449 | void | ||
5450 | churn_start_callback (void *cls, const struct GNUNET_PeerIdentity *id, | ||
5451 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
5452 | struct GNUNET_TESTING_Daemon *d, const char *emsg) | ||
5453 | { | ||
5454 | struct ChurnRestartContext *startup_ctx = cls; | ||
5455 | struct ChurnContext *churn_ctx = startup_ctx->churn_ctx; | ||
5456 | |||
5457 | unsigned int total_left; | ||
5458 | char *error_message; | ||
5459 | |||
5460 | error_message = NULL; | ||
5461 | if (emsg != NULL) | ||
5462 | { | ||
5463 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
5464 | "Churn stop callback failed with error `%s'\n", emsg); | ||
5465 | churn_ctx->num_failed_start++; | ||
5466 | } | ||
5467 | else | ||
5468 | { | ||
5469 | churn_ctx->num_to_start--; | ||
5470 | } | ||
5471 | |||
5472 | total_left = | ||
5473 | (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + | ||
5474 | (churn_ctx->num_to_start - churn_ctx->num_failed_start); | ||
5475 | |||
5476 | if (total_left == 0) | ||
5477 | { | ||
5478 | if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0)) | ||
5479 | GNUNET_asprintf (&error_message, | ||
5480 | "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", | ||
5481 | churn_ctx->num_failed_start, churn_ctx->num_failed_stop); | ||
5482 | churn_ctx->cb (churn_ctx->cb_cls, error_message); | ||
5483 | GNUNET_free_non_null (error_message); | ||
5484 | GNUNET_free (churn_ctx); | ||
5485 | GNUNET_free (startup_ctx); | ||
5486 | } | ||
5487 | } | ||
5488 | |||
5489 | static void | ||
5490 | schedule_churn_restart (void *cls, | ||
5491 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5492 | { | ||
5493 | struct PeerRestartContext *peer_restart_ctx = cls; | ||
5494 | struct ChurnRestartContext *startup_ctx = peer_restart_ctx->churn_restart_ctx; | ||
5495 | |||
5496 | if (startup_ctx->outstanding > startup_ctx->pg->max_concurrent_ssh) | ||
5497 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
5498 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
5499 | &schedule_churn_restart, peer_restart_ctx); | ||
5500 | else | ||
5501 | { | ||
5502 | if (startup_ctx->churn_ctx->service != NULL) | ||
5503 | GNUNET_TESTING_daemon_start_stopped_service (peer_restart_ctx->daemon, | ||
5504 | startup_ctx-> | ||
5505 | churn_ctx->service, | ||
5506 | startup_ctx->timeout, | ||
5507 | &churn_start_callback, | ||
5508 | startup_ctx); | ||
5509 | else | ||
5510 | GNUNET_TESTING_daemon_start_stopped (peer_restart_ctx->daemon, | ||
5511 | startup_ctx->timeout, | ||
5512 | &churn_start_callback, startup_ctx); | ||
5513 | GNUNET_free (peer_restart_ctx); | ||
5514 | } | ||
5515 | } | ||
5516 | |||
5517 | /** | ||
5518 | * Callback for informing us about a successful | ||
5519 | * or unsuccessful churn start call. | ||
5520 | * | ||
5521 | * @param cls a struct ServiceStartContext *startup_ctx | ||
5522 | * @param id the peer identity of the started peer | ||
5523 | * @param cfg the handle to the configuration of the peer | ||
5524 | * @param d handle to the daemon for the peer | ||
5525 | * @param emsg NULL on success, non-NULL on failure | ||
5526 | * | ||
5527 | */ | ||
5528 | void | ||
5529 | service_start_callback (void *cls, const struct GNUNET_PeerIdentity *id, | ||
5530 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
5531 | struct GNUNET_TESTING_Daemon *d, const char *emsg) | ||
5532 | { | ||
5533 | struct ServiceStartContext *startup_ctx = (struct ServiceStartContext *) cls; | ||
5534 | |||
5535 | if (emsg != NULL) | ||
5536 | { | ||
5537 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
5538 | "Service start failed with error `%s'\n", emsg); | ||
5539 | } | ||
5540 | |||
5541 | startup_ctx->outstanding--; | ||
5542 | startup_ctx->remaining--; | ||
5543 | |||
5544 | if (startup_ctx->remaining == 0) | ||
5545 | { | ||
5546 | startup_ctx->cb (startup_ctx->cb_cls, NULL); | ||
5547 | GNUNET_free (startup_ctx->service); | ||
5548 | GNUNET_free (startup_ctx); | ||
5549 | } | ||
5550 | } | ||
5551 | |||
5552 | static void | ||
5553 | schedule_service_start (void *cls, | ||
5554 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5555 | { | ||
5556 | struct PeerServiceStartContext *peer_ctx = cls; | ||
5557 | struct ServiceStartContext *startup_ctx = peer_ctx->start_ctx; | ||
5558 | |||
5559 | if (startup_ctx->outstanding > startup_ctx->pg->max_concurrent_ssh) | ||
5560 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
5561 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
5562 | &schedule_service_start, peer_ctx); | ||
5563 | else | ||
5564 | { | ||
5565 | |||
5566 | GNUNET_TESTING_daemon_start_service (peer_ctx->daemon, startup_ctx->service, | ||
5567 | startup_ctx->timeout, | ||
5568 | &service_start_callback, startup_ctx); | ||
5569 | GNUNET_free (peer_ctx); | ||
5570 | } | ||
5571 | } | ||
5572 | |||
5573 | |||
5574 | static void | ||
5575 | internal_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5576 | { | ||
5577 | struct InternalStartContext *internal_context = cls; | ||
5578 | |||
5579 | if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
5580 | { | ||
5581 | return; | ||
5582 | } | ||
5583 | |||
5584 | if ((internal_context->peer->pg->starting < | ||
5585 | internal_context->peer->pg->max_concurrent_ssh) || | ||
5586 | ((internal_context->hostname != NULL) && | ||
5587 | (count_outstanding_at_host | ||
5588 | (internal_context->hostname, | ||
5589 | internal_context->peer->pg) < | ||
5590 | internal_context->peer->pg->max_concurrent_ssh))) | ||
5591 | { | ||
5592 | if (internal_context->hostname != NULL) | ||
5593 | increment_outstanding_at_host (internal_context->hostname, | ||
5594 | internal_context->peer->pg); | ||
5595 | internal_context->peer->pg->starting++; | ||
5596 | internal_context->peer->daemon = | ||
5597 | GNUNET_TESTING_daemon_start (internal_context->peer->cfg, | ||
5598 | internal_context->timeout, GNUNET_NO, | ||
5599 | internal_context->hostname, | ||
5600 | internal_context->username, | ||
5601 | internal_context->sshport, | ||
5602 | internal_context->hostkey, | ||
5603 | &internal_hostkey_callback, | ||
5604 | internal_context, | ||
5605 | &internal_startup_callback, | ||
5606 | internal_context); | ||
5607 | } | ||
5608 | else | ||
5609 | { | ||
5610 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
5611 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
5612 | &internal_start, internal_context); | ||
5613 | } | ||
5614 | } | ||
5615 | |||
5616 | #if USE_START_HELPER | ||
5617 | |||
5618 | struct PeerStartHelperContext | ||
5619 | { | ||
5620 | struct GNUNET_TESTING_PeerGroup *pg; | ||
5621 | |||
5622 | struct HostData *host; | ||
5623 | |||
5624 | struct GNUNET_OS_Process *proc; | ||
5625 | }; | ||
5626 | |||
5627 | static void | ||
5628 | check_peers_started (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5629 | { | ||
5630 | struct PeerStartHelperContext *helper = cls; | ||
5631 | enum GNUNET_OS_ProcessStatusType type; | ||
5632 | unsigned long code; | ||
5633 | unsigned int i; | ||
5634 | GNUNET_TESTING_NotifyDaemonRunning cb; | ||
5635 | |||
5636 | if (GNUNET_NO == GNUNET_OS_process_status (helper->proc, &type, &code)) /* Still running, wait some more! */ | ||
5637 | { | ||
5638 | GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_EXEC_WAIT, | ||
5639 | &check_peers_started, helper); | ||
5640 | return; | ||
5641 | } | ||
5642 | |||
5643 | helper->pg->starting--; | ||
5644 | if (helper->pg->starting == 0) /* All peers have finished starting! */ | ||
5645 | { | ||
5646 | /* Call the peer started callback for each peer, set proper FSM state (?) */ | ||
5647 | for (i = 0; i < helper->pg->total; i++) | ||
5648 | { | ||
5649 | cb = helper->pg->peers[i].daemon->cb; | ||
5650 | helper->pg->peers[i].daemon->cb = NULL; | ||
5651 | helper->pg->peers[i].daemon->running = GNUNET_YES; | ||
5652 | helper->pg->peers[i].daemon->phase = SP_START_DONE; | ||
5653 | if (NULL != cb) | ||
5654 | { | ||
5655 | if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0)) | ||
5656 | cb (helper->pg->peers[i].daemon->cb_cls, | ||
5657 | &helper->pg->peers[i].daemon->id, | ||
5658 | helper->pg->peers[i].daemon->cfg, helper->pg->peers[i].daemon, | ||
5659 | "Failed to execute peerStartHelper.pl, or return code bad!"); | ||
5660 | else | ||
5661 | cb (helper->pg->peers[i].daemon->cb_cls, | ||
5662 | &helper->pg->peers[i].daemon->id, | ||
5663 | helper->pg->peers[i].daemon->cfg, helper->pg->peers[i].daemon, | ||
5664 | NULL); | ||
5665 | |||
5666 | } | ||
5667 | |||
5668 | } | ||
5669 | } | ||
5670 | GNUNET_OS_process_destroy (helper->proc); | ||
5671 | } | ||
5672 | |||
5673 | static void | ||
5674 | start_peer_helper (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5675 | { | ||
5676 | struct PeerStartHelperContext *helper = cls; | ||
5677 | char *baseservicehome; | ||
5678 | char *tempdir; | ||
5679 | char *arg; | ||
5680 | |||
5681 | /* ssh user@host peerStartHelper /path/to/basedirectory */ | ||
5682 | GNUNET_assert (GNUNET_OK == | ||
5683 | GNUNET_CONFIGURATION_get_value_string (helper->pg->cfg, | ||
5684 | "PATHS", "SERVICEHOME", | ||
5685 | &baseservicehome)); | ||
5686 | GNUNET_asprintf (&tempdir, "%s/%s/", baseservicehome, helper->host->hostname); | ||
5687 | if (NULL != helper->host->username) | ||
5688 | GNUNET_asprintf (&arg, "%s@%s", helper->host->username, | ||
5689 | helper->host->hostname); | ||
5690 | else | ||
5691 | GNUNET_asprintf (&arg, "%s", helper->host->hostname); | ||
5692 | |||
5693 | /* FIXME: Doesn't support ssh_port option! */ | ||
5694 | helper->proc = | ||
5695 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", arg, | ||
5696 | "peerStartHelper.pl", tempdir, NULL); | ||
5697 | GNUNET_assert (helper->proc != NULL); | ||
5698 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "starting peers with cmd ssh %s %s %s\n", | ||
5699 | arg, "peerStartHelper.pl", tempdir); | ||
5700 | GNUNET_SCHEDULER_add_now (&check_peers_started, helper); | ||
5701 | GNUNET_free (tempdir); | ||
5702 | GNUNET_free (baseservicehome); | ||
5703 | GNUNET_free (arg); | ||
5704 | } | ||
5705 | #endif | ||
5706 | |||
5707 | /** | ||
5708 | * Function which continues a peer group starting up | ||
5709 | * after successfully generating hostkeys for each peer. | ||
5710 | * | ||
5711 | * @param pg the peer group to continue starting | ||
5712 | * | ||
5713 | */ | ||
5714 | void | ||
5715 | GNUNET_TESTING_daemons_continue_startup (struct GNUNET_TESTING_PeerGroup *pg) | ||
5716 | { | ||
5717 | unsigned int i; | ||
5718 | |||
5719 | #if USE_START_HELPER | ||
5720 | if ((pg->num_hosts > 0) && (pg->hostkey_data != NULL)) | ||
5721 | { | ||
5722 | struct PeerStartHelperContext *helper; | ||
5723 | |||
5724 | pg->starting = pg->num_hosts; | ||
5725 | for (i = 0; i < pg->num_hosts; i++) | ||
5726 | { | ||
5727 | helper = GNUNET_malloc (sizeof (struct PeerStartHelperContext)); | ||
5728 | helper->pg = pg; | ||
5729 | helper->host = &pg->hosts[i]; | ||
5730 | GNUNET_SCHEDULER_add_now (&start_peer_helper, helper); | ||
5731 | } | ||
5732 | } | ||
5733 | else | ||
5734 | { | ||
5735 | pg->starting = 0; | ||
5736 | for (i = 0; i < pg->total; i++) | ||
5737 | { | ||
5738 | pg->peers[i].startup_task = | ||
5739 | GNUNET_SCHEDULER_add_now (&internal_continue_startup, | ||
5740 | &pg->peers[i].internal_context); | ||
5741 | } | ||
5742 | } | ||
5743 | #else | ||
5744 | pg->starting = 0; | ||
5745 | for (i = 0; i < pg->total; i++) | ||
5746 | { | ||
5747 | pg->peers[i].startup_task = | ||
5748 | GNUNET_SCHEDULER_add_now (&internal_continue_startup, | ||
5749 | &pg->peers[i].internal_context); | ||
5750 | } | ||
5751 | #endif | ||
5752 | } | ||
5753 | |||
5754 | #if USE_START_HELPER | ||
5755 | static void | ||
5756 | call_hostkey_callbacks (void *cls, | ||
5757 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
5758 | { | ||
5759 | struct GNUNET_TESTING_PeerGroup *pg = cls; | ||
5760 | unsigned int i; | ||
5761 | |||
5762 | for (i = 0; i < pg->total; i++) | ||
5763 | { | ||
5764 | if (pg->peers[i].internal_context.hostkey_callback != NULL) | ||
5765 | pg->peers[i].internal_context.hostkey_callback (pg->peers[i]. | ||
5766 | internal_context.hostkey_cls, | ||
5767 | &pg->peers[i].daemon->id, | ||
5768 | pg->peers[i].daemon, | ||
5769 | NULL); | ||
5770 | } | ||
5771 | |||
5772 | if (pg->peers[0].internal_context.hostkey_callback == NULL) | ||
5773 | GNUNET_TESTING_daemons_continue_startup (pg); | ||
5774 | } | ||
5775 | #endif | ||
5776 | |||
5777 | /** | ||
5778 | * Start count gnunet instances with the same set of transports and | ||
5779 | * applications. The port numbers (any option called "PORT") will be | ||
5780 | * adjusted to ensure that no two peers running on the same system | ||
5781 | * have the same port(s) in their respective configurations. | ||
5782 | * | ||
5783 | * @param cfg configuration template to use | ||
5784 | * @param total number of daemons to start | ||
5785 | * @param max_concurrent_connections for testing, how many peers can | ||
5786 | * we connect to simultaneously | ||
5787 | * @param max_concurrent_ssh when starting with ssh, how many ssh | ||
5788 | * connections will we allow at once (based on remote hosts allowed!) | ||
5789 | * @param timeout total time allowed for peers to start | ||
5790 | * @param hostkey_callback function to call on each peers hostkey generation | ||
5791 | * if NULL, peers will be started by this call, if non-null, | ||
5792 | * GNUNET_TESTING_daemons_continue_startup must be called after | ||
5793 | * successful hostkey generation | ||
5794 | * @param hostkey_cls closure for hostkey callback | ||
5795 | * @param cb function to call on each daemon that was started | ||
5796 | * @param cb_cls closure for cb | ||
5797 | * @param connect_callback function to call each time two hosts are connected | ||
5798 | * @param connect_callback_cls closure for connect_callback | ||
5799 | * @param hostnames linked list of host structs to use to start peers on | ||
5800 | * (NULL to run on localhost only) | ||
5801 | * | ||
5802 | * @return NULL on error, otherwise handle to control peer group | ||
5803 | */ | ||
5804 | struct GNUNET_TESTING_PeerGroup * | ||
5805 | GNUNET_TESTING_daemons_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
5806 | unsigned int total, | ||
5807 | unsigned int max_concurrent_connections, | ||
5808 | unsigned int max_concurrent_ssh, | ||
5809 | struct GNUNET_TIME_Relative timeout, | ||
5810 | GNUNET_TESTING_NotifyHostkeyCreated | ||
5811 | hostkey_callback, void *hostkey_cls, | ||
5812 | GNUNET_TESTING_NotifyDaemonRunning cb, | ||
5813 | void *cb_cls, | ||
5814 | GNUNET_TESTING_NotifyConnection connect_callback, | ||
5815 | void *connect_callback_cls, | ||
5816 | const struct GNUNET_TESTING_Host *hostnames) | ||
5817 | { | ||
5818 | struct GNUNET_TESTING_PeerGroup *pg; | ||
5819 | const struct GNUNET_TESTING_Host *hostpos; | ||
5820 | const char *hostname; | ||
5821 | const char *username; | ||
5822 | char *baseservicehome; | ||
5823 | char *newservicehome; | ||
5824 | char *tmpdir; | ||
5825 | char *hostkeys_file; | ||
5826 | char *arg; | ||
5827 | char *ssh_port_str; | ||
5828 | struct GNUNET_DISK_FileHandle *fd; | ||
5829 | struct GNUNET_CONFIGURATION_Handle *pcfg; | ||
5830 | unsigned int off; | ||
5831 | struct OutstandingSSH *ssh_entry; | ||
5832 | unsigned int hostcnt; | ||
5833 | unsigned int i; | ||
5834 | uint16_t minport; | ||
5835 | uint16_t sshport; | ||
5836 | uint32_t upnum; | ||
5837 | uint32_t fdnum; | ||
5838 | uint64_t fs; | ||
5839 | uint64_t total_hostkeys; | ||
5840 | struct GNUNET_OS_Process *proc; | ||
5841 | |||
5842 | username = NULL; | ||
5843 | if (0 == total) | ||
5844 | { | ||
5845 | GNUNET_break (0); | ||
5846 | return NULL; | ||
5847 | } | ||
5848 | |||
5849 | upnum = 0; | ||
5850 | fdnum = 0; | ||
5851 | pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup)); | ||
5852 | pg->cfg = cfg; | ||
5853 | pg->notify_connection = connect_callback; | ||
5854 | pg->notify_connection_cls = connect_callback_cls; | ||
5855 | pg->total = total; | ||
5856 | pg->max_timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
5857 | pg->peers = GNUNET_malloc (total * sizeof (struct PeerData)); | ||
5858 | pg->max_outstanding_connections = max_concurrent_connections; | ||
5859 | pg->max_concurrent_ssh = max_concurrent_ssh; | ||
5860 | if (NULL != hostnames) | ||
5861 | { | ||
5862 | off = 0; | ||
5863 | hostpos = hostnames; | ||
5864 | while (hostpos != NULL) | ||
5865 | { | ||
5866 | hostpos = hostpos->next; | ||
5867 | off++; | ||
5868 | } | ||
5869 | pg->hosts = GNUNET_malloc (off * sizeof (struct HostData)); | ||
5870 | off = 0; | ||
5871 | |||
5872 | hostpos = hostnames; | ||
5873 | while (hostpos != NULL) | ||
5874 | { | ||
5875 | pg->hosts[off].minport = LOW_PORT; | ||
5876 | pg->hosts[off].hostname = GNUNET_strdup (hostpos->hostname); | ||
5877 | if (hostpos->username != NULL) | ||
5878 | pg->hosts[off].username = GNUNET_strdup (hostpos->username); | ||
5879 | pg->hosts[off].sshport = hostpos->port; | ||
5880 | hostpos = hostpos->next; | ||
5881 | off++; | ||
5882 | } | ||
5883 | |||
5884 | if (off == 0) | ||
5885 | { | ||
5886 | pg->hosts = NULL; | ||
5887 | } | ||
5888 | hostcnt = off; | ||
5889 | minport = 0; | ||
5890 | pg->num_hosts = off; | ||
5891 | } | ||
5892 | else | ||
5893 | { | ||
5894 | hostcnt = 0; | ||
5895 | minport = LOW_PORT; | ||
5896 | } | ||
5897 | |||
5898 | /* Create the servicehome directory for each remote peer */ | ||
5899 | GNUNET_assert (GNUNET_OK == | ||
5900 | GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", | ||
5901 | "SERVICEHOME", | ||
5902 | &baseservicehome)); | ||
5903 | for (i = 0; i < pg->num_hosts; i++) | ||
5904 | { | ||
5905 | ssh_entry = GNUNET_malloc (sizeof (struct OutstandingSSH)); | ||
5906 | ssh_entry->hostname = pg->hosts[i].hostname; /* Don't free! */ | ||
5907 | GNUNET_CONTAINER_DLL_insert (pg->ssh_head, pg->ssh_tail, ssh_entry); | ||
5908 | GNUNET_asprintf (&tmpdir, "%s/%s", baseservicehome, pg->hosts[i].hostname); | ||
5909 | if (NULL != pg->hosts[i].username) | ||
5910 | GNUNET_asprintf (&arg, "%s@%s", pg->hosts[i].username, | ||
5911 | pg->hosts[i].hostname); | ||
5912 | else | ||
5913 | GNUNET_asprintf (&arg, "%s", pg->hosts[i].hostname); | ||
5914 | if (pg->hosts[i].sshport != 0) | ||
5915 | { | ||
5916 | GNUNET_asprintf (&ssh_port_str, "%d", pg->hosts[i].sshport); | ||
5917 | proc = | ||
5918 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", "-P", ssh_port_str, | ||
5919 | "-q", | ||
5920 | arg, "mkdir -p", tmpdir, NULL); | ||
5921 | } | ||
5922 | else | ||
5923 | proc = | ||
5924 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "ssh", "ssh", arg, "mkdir -p", | ||
5925 | tmpdir, NULL); | ||
5926 | GNUNET_assert (proc != NULL); | ||
5927 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5928 | "Creating remote dir with command ssh %s %s %s\n", arg, | ||
5929 | " mkdir -p ", tmpdir); | ||
5930 | GNUNET_free (tmpdir); | ||
5931 | GNUNET_free (arg); | ||
5932 | GNUNET_OS_process_wait (proc); | ||
5933 | GNUNET_OS_process_destroy (proc); | ||
5934 | } | ||
5935 | GNUNET_free (baseservicehome); | ||
5936 | baseservicehome = NULL; | ||
5937 | |||
5938 | if (GNUNET_YES == | ||
5939 | GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING_OLD", "HOSTKEYSFILE", | ||
5940 | &hostkeys_file)) | ||
5941 | { | ||
5942 | if (GNUNET_YES != GNUNET_DISK_file_test (hostkeys_file)) | ||
5943 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
5944 | _("Could not read hostkeys file!\n")); | ||
5945 | else | ||
5946 | { | ||
5947 | /* Check hostkey file size, read entire thing into memory */ | ||
5948 | fd = GNUNET_DISK_file_open (hostkeys_file, GNUNET_DISK_OPEN_READ, | ||
5949 | GNUNET_DISK_PERM_NONE); | ||
5950 | if (NULL == fd) | ||
5951 | { | ||
5952 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", | ||
5953 | hostkeys_file); | ||
5954 | GNUNET_free (hostkeys_file); | ||
5955 | for (i = 0; i < pg->num_hosts; i++) | ||
5956 | { | ||
5957 | GNUNET_free (pg->hosts[i].hostname); | ||
5958 | GNUNET_free_non_null (pg->hosts[i].username); | ||
5959 | } | ||
5960 | GNUNET_free (pg->peers); | ||
5961 | GNUNET_free (pg->hosts); | ||
5962 | GNUNET_free (pg); | ||
5963 | return NULL; | ||
5964 | } | ||
5965 | |||
5966 | if (GNUNET_OK != GNUNET_DISK_file_size (hostkeys_file, &fs, GNUNET_YES, GNUNET_YES)) | ||
5967 | fs = 0; | ||
5968 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5969 | "Found file size %llu for hostkeys\n", fs); | ||
5970 | if (0 != (fs % HOSTKEYFILESIZE)) | ||
5971 | { | ||
5972 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
5973 | "File size %llu seems incorrect for hostkeys...\n", fs); | ||
5974 | } | ||
5975 | else | ||
5976 | { | ||
5977 | total_hostkeys = fs / HOSTKEYFILESIZE; | ||
5978 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
5979 | "Will read %llu hostkeys from file\n", total_hostkeys); | ||
5980 | pg->hostkey_data = GNUNET_malloc_large (fs); | ||
5981 | GNUNET_assert (fs == GNUNET_DISK_file_read (fd, pg->hostkey_data, fs)); | ||
5982 | } | ||
5983 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
5984 | } | ||
5985 | GNUNET_free (hostkeys_file); | ||
5986 | } | ||
5987 | |||
5988 | for (off = 0; off < total; off++) | ||
5989 | { | ||
5990 | if (hostcnt > 0) | ||
5991 | { | ||
5992 | hostname = pg->hosts[off % hostcnt].hostname; | ||
5993 | username = pg->hosts[off % hostcnt].username; | ||
5994 | sshport = pg->hosts[off % hostcnt].sshport; | ||
5995 | pcfg = | ||
5996 | GNUNET_TESTING_create_cfg (cfg, off, &pg->hosts[off % hostcnt].minport, &upnum, | ||
5997 | hostname, &fdnum); | ||
5998 | } | ||
5999 | else | ||
6000 | { | ||
6001 | hostname = NULL; | ||
6002 | username = NULL; | ||
6003 | sshport = 0; | ||
6004 | pcfg = GNUNET_TESTING_create_cfg (cfg, off, &minport, &upnum, hostname, &fdnum); | ||
6005 | } | ||
6006 | |||
6007 | if (NULL == pcfg) | ||
6008 | { | ||
6009 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
6010 | _ | ||
6011 | ("Could not create configuration for peer number %u on `%s'!\n"), | ||
6012 | off, hostname == NULL ? "localhost" : hostname); | ||
6013 | continue; | ||
6014 | } | ||
6015 | |||
6016 | if (GNUNET_YES == | ||
6017 | GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME", | ||
6018 | &baseservicehome)) | ||
6019 | { | ||
6020 | if (hostname != NULL) | ||
6021 | GNUNET_asprintf (&newservicehome, "%s/%s/%d/", baseservicehome, | ||
6022 | hostname, off); | ||
6023 | else | ||
6024 | GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off); | ||
6025 | GNUNET_free (baseservicehome); | ||
6026 | baseservicehome = NULL; | ||
6027 | } | ||
6028 | else | ||
6029 | { | ||
6030 | tmpdir = getenv ("TMPDIR"); | ||
6031 | tmpdir = tmpdir ? tmpdir : "/tmp"; | ||
6032 | if (hostname != NULL) | ||
6033 | GNUNET_asprintf (&newservicehome, "%s/%s/%s/%d/", tmpdir, hostname, | ||
6034 | "gnunet-testing-test-test", off); | ||
6035 | else | ||
6036 | GNUNET_asprintf (&newservicehome, "%s/%s/%d/", tmpdir, | ||
6037 | "gnunet-testing-test-test", off); | ||
6038 | } | ||
6039 | GNUNET_CONFIGURATION_set_value_string (pcfg, "PATHS", "SERVICEHOME", | ||
6040 | newservicehome); | ||
6041 | GNUNET_free (newservicehome); | ||
6042 | pg->peers[off].cfg = pcfg; | ||
6043 | pg->peers[off].pg = pg; | ||
6044 | pg->peers[off].internal_context.peer = &pg->peers[off]; | ||
6045 | pg->peers[off].internal_context.timeout = timeout; | ||
6046 | pg->peers[off].internal_context.hostname = hostname; | ||
6047 | pg->peers[off].internal_context.username = username; | ||
6048 | pg->peers[off].internal_context.sshport = sshport; | ||
6049 | if (pg->hostkey_data != NULL) | ||
6050 | pg->peers[off].internal_context.hostkey = | ||
6051 | &pg->hostkey_data[off * HOSTKEYFILESIZE]; | ||
6052 | pg->peers[off].internal_context.hostkey_callback = hostkey_callback; | ||
6053 | pg->peers[off].internal_context.hostkey_cls = hostkey_cls; | ||
6054 | pg->peers[off].internal_context.start_cb = cb; | ||
6055 | pg->peers[off].internal_context.start_cb_cls = cb_cls; | ||
6056 | #if !USE_START_HELPER | ||
6057 | GNUNET_SCHEDULER_add_now (&internal_start, | ||
6058 | &pg->peers[off].internal_context); | ||
6059 | #else | ||
6060 | if ((pg->hostkey_data != NULL) && (hostcnt > 0)) | ||
6061 | { | ||
6062 | pg->peers[off].daemon = | ||
6063 | GNUNET_TESTING_daemon_start (pcfg, timeout, GNUNET_YES, hostname, | ||
6064 | username, sshport, | ||
6065 | pg->peers[off].internal_context.hostkey, | ||
6066 | &internal_hostkey_callback, | ||
6067 | &pg->peers[off].internal_context, | ||
6068 | &internal_startup_callback, | ||
6069 | &pg->peers[off].internal_context); | ||
6070 | /** | ||
6071 | * At this point, given that we had a hostkeyfile, | ||
6072 | * we can call the hostkey callback! | ||
6073 | * But first, we should copy (rsync) all of the configs | ||
6074 | * and hostkeys to the remote peers. Then let topology | ||
6075 | * creation happen, then call the peer start helper processes, | ||
6076 | * then set pg->whatever_phase for each peer and let them | ||
6077 | * enter the fsm to get the HELLO's for peers and start connecting. | ||
6078 | */ | ||
6079 | } | ||
6080 | else | ||
6081 | { | ||
6082 | GNUNET_SCHEDULER_add_now (&internal_start, | ||
6083 | &pg->peers[off].internal_context); | ||
6084 | } | ||
6085 | |||
6086 | #endif | ||
6087 | } | ||
6088 | |||
6089 | #if USE_START_HELPER /* Now the peergroup has been set up, hostkeys and configs written to files. */ | ||
6090 | if ((pg->hostkey_data != NULL) && (hostcnt > 0)) | ||
6091 | { | ||
6092 | for (off = 0; off < hostcnt; off++) | ||
6093 | { | ||
6094 | |||
6095 | if (hostcnt > 0) | ||
6096 | { | ||
6097 | hostname = pg->hosts[off % hostcnt].hostname; | ||
6098 | username = pg->hosts[off % hostcnt].username; | ||
6099 | sshport = pg->hosts[off % hostcnt].sshport; | ||
6100 | } | ||
6101 | else | ||
6102 | { | ||
6103 | hostname = NULL; | ||
6104 | username = NULL; | ||
6105 | sshport = 0; | ||
6106 | } | ||
6107 | |||
6108 | if (GNUNET_YES == | ||
6109 | GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME", | ||
6110 | &baseservicehome)) | ||
6111 | { | ||
6112 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "baseservice home is %s\n", | ||
6113 | baseservicehome); | ||
6114 | if (hostname != NULL) | ||
6115 | GNUNET_asprintf (&newservicehome, "%s/%s/", baseservicehome, | ||
6116 | hostname); | ||
6117 | else | ||
6118 | GNUNET_asprintf (&newservicehome, "%s/", baseservicehome); | ||
6119 | GNUNET_free (baseservicehome); | ||
6120 | baseservicehome = NULL; | ||
6121 | } | ||
6122 | else | ||
6123 | { | ||
6124 | tmpdir = getenv ("TMPDIR"); | ||
6125 | tmpdir = tmpdir ? tmpdir : "/tmp"; | ||
6126 | if (hostname != NULL) | ||
6127 | GNUNET_asprintf (&newservicehome, "%s/%s/%s/", tmpdir, hostname, | ||
6128 | "gnunet-testing-test-test"); | ||
6129 | else | ||
6130 | GNUNET_asprintf (&newservicehome, "%s/%s/", tmpdir, | ||
6131 | "gnunet-testing-test-test", off); | ||
6132 | } | ||
6133 | |||
6134 | if (NULL != username) | ||
6135 | GNUNET_asprintf (&arg, "%s@%s:%s", username, pg->hosts[off].hostname, | ||
6136 | newservicehome); | ||
6137 | else | ||
6138 | GNUNET_asprintf (&arg, "%s:%s", pg->hosts[off].hostname, | ||
6139 | newservicehome); | ||
6140 | |||
6141 | /* FIXME: Doesn't support ssh_port option! */ | ||
6142 | proc = | ||
6143 | GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, "rsync", "rsync", "-r", | ||
6144 | newservicehome, arg, NULL); | ||
6145 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
6146 | "copying directory with command rsync -r %s %s\n", | ||
6147 | newservicehome, arg); | ||
6148 | GNUNET_free (newservicehome); | ||
6149 | GNUNET_free (arg); | ||
6150 | if (NULL == proc) | ||
6151 | { | ||
6152 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
6153 | _ | ||
6154 | ("Could not start `%s' process to copy configuration directory.\n"), | ||
6155 | "scp"); | ||
6156 | GNUNET_assert (0); | ||
6157 | } | ||
6158 | GNUNET_OS_process_wait (proc); | ||
6159 | GNUNET_OS_process_destroy (proc); | ||
6160 | } | ||
6161 | /* Now all the configuration files and hostkeys are copied to the remote host. Call the hostkey callback for each peer! */ | ||
6162 | GNUNET_SCHEDULER_add_now (&call_hostkey_callbacks, pg); | ||
6163 | } | ||
6164 | #endif | ||
6165 | return pg; | ||
6166 | } | ||
6167 | |||
6168 | /* | ||
6169 | * Get a daemon by number, so callers don't have to do nasty | ||
6170 | * offsetting operation. | ||
6171 | */ | ||
6172 | struct GNUNET_TESTING_Daemon * | ||
6173 | GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, | ||
6174 | unsigned int position) | ||
6175 | { | ||
6176 | if (position < pg->total) | ||
6177 | return pg->peers[position].daemon; | ||
6178 | return NULL; | ||
6179 | } | ||
6180 | |||
6181 | /* | ||
6182 | * Get a daemon by peer identity, so callers can | ||
6183 | * retrieve the daemon without knowing it's offset. | ||
6184 | * | ||
6185 | * @param pg the peer group to retrieve the daemon from | ||
6186 | * @param peer_id the peer identity of the daemon to retrieve | ||
6187 | * | ||
6188 | * @return the daemon on success, or NULL if no such peer identity is found | ||
6189 | */ | ||
6190 | struct GNUNET_TESTING_Daemon * | ||
6191 | GNUNET_TESTING_daemon_get_by_id (struct GNUNET_TESTING_PeerGroup *pg, | ||
6192 | const struct GNUNET_PeerIdentity *peer_id) | ||
6193 | { | ||
6194 | unsigned int i; | ||
6195 | |||
6196 | for (i = 0; i < pg->total; i++) | ||
6197 | { | ||
6198 | if (0 == | ||
6199 | memcmp (&pg->peers[i].daemon->id, peer_id, | ||
6200 | sizeof (struct GNUNET_PeerIdentity))) | ||
6201 | return pg->peers[i].daemon; | ||
6202 | } | ||
6203 | return NULL; | ||
6204 | } | ||
6205 | |||
6206 | /** | ||
6207 | * Prototype of a function that will be called when a | ||
6208 | * particular operation was completed the testing library. | ||
6209 | * | ||
6210 | * @param cls closure (a struct RestartContext) | ||
6211 | * @param id id of the peer that was restarted | ||
6212 | * @param cfg handle to the configuration of the peer | ||
6213 | * @param d handle to the daemon that was restarted | ||
6214 | * @param emsg NULL on success | ||
6215 | */ | ||
6216 | static void | ||
6217 | restart_callback (void *cls, const struct GNUNET_PeerIdentity *id, | ||
6218 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
6219 | struct GNUNET_TESTING_Daemon *d, const char *emsg) | ||
6220 | { | ||
6221 | struct RestartContext *restart_context = cls; | ||
6222 | |||
6223 | if (emsg == NULL) | ||
6224 | { | ||
6225 | restart_context->peers_restarted++; | ||
6226 | } | ||
6227 | else | ||
6228 | { | ||
6229 | restart_context->peers_restart_failed++; | ||
6230 | } | ||
6231 | |||
6232 | if (restart_context->peers_restarted == restart_context->peer_group->total) | ||
6233 | { | ||
6234 | restart_context->callback (restart_context->callback_cls, NULL); | ||
6235 | GNUNET_free (restart_context); | ||
6236 | } | ||
6237 | else if (restart_context->peers_restart_failed + | ||
6238 | restart_context->peers_restarted == | ||
6239 | restart_context->peer_group->total) | ||
6240 | { | ||
6241 | restart_context->callback (restart_context->callback_cls, | ||
6242 | "Failed to restart peers!"); | ||
6243 | GNUNET_free (restart_context); | ||
6244 | } | ||
6245 | |||
6246 | } | ||
6247 | |||
6248 | /** | ||
6249 | * Callback for informing us about a successful | ||
6250 | * or unsuccessful churn stop call. | ||
6251 | * | ||
6252 | * @param cls a ChurnContext | ||
6253 | * @param emsg NULL on success, non-NULL on failure | ||
6254 | * | ||
6255 | */ | ||
6256 | static void | ||
6257 | churn_stop_callback (void *cls, const char *emsg) | ||
6258 | { | ||
6259 | struct ShutdownContext *shutdown_ctx = cls; | ||
6260 | struct ChurnContext *churn_ctx = shutdown_ctx->cb_cls; | ||
6261 | unsigned int total_left; | ||
6262 | char *error_message; | ||
6263 | |||
6264 | error_message = NULL; | ||
6265 | shutdown_ctx->outstanding--; | ||
6266 | |||
6267 | if (emsg != NULL) | ||
6268 | { | ||
6269 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
6270 | "Churn stop callback failed with error `%s'\n", emsg); | ||
6271 | churn_ctx->num_failed_stop++; | ||
6272 | } | ||
6273 | else | ||
6274 | { | ||
6275 | churn_ctx->num_to_stop--; | ||
6276 | } | ||
6277 | |||
6278 | total_left = | ||
6279 | (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + | ||
6280 | (churn_ctx->num_to_start - churn_ctx->num_failed_start); | ||
6281 | |||
6282 | if (total_left == 0) | ||
6283 | { | ||
6284 | if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0)) | ||
6285 | { | ||
6286 | GNUNET_asprintf (&error_message, | ||
6287 | "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", | ||
6288 | churn_ctx->num_failed_start, churn_ctx->num_failed_stop); | ||
6289 | } | ||
6290 | churn_ctx->cb (churn_ctx->cb_cls, error_message); | ||
6291 | GNUNET_free_non_null (error_message); | ||
6292 | GNUNET_free (churn_ctx); | ||
6293 | GNUNET_free (shutdown_ctx); | ||
6294 | } | ||
6295 | } | ||
6296 | |||
6297 | /** | ||
6298 | * Count the number of running peers. | ||
6299 | * | ||
6300 | * @param pg handle for the peer group | ||
6301 | * | ||
6302 | * @return the number of currently running peers in the peer group | ||
6303 | */ | ||
6304 | unsigned int | ||
6305 | GNUNET_TESTING_daemons_running (struct GNUNET_TESTING_PeerGroup *pg) | ||
6306 | { | ||
6307 | unsigned int i; | ||
6308 | unsigned int running = 0; | ||
6309 | |||
6310 | for (i = 0; i < pg->total; i++) | ||
6311 | { | ||
6312 | if (pg->peers[i].daemon->running == GNUNET_YES) | ||
6313 | { | ||
6314 | GNUNET_assert (running != -1); | ||
6315 | running++; | ||
6316 | } | ||
6317 | } | ||
6318 | return running; | ||
6319 | } | ||
6320 | |||
6321 | /** | ||
6322 | * Task to rate limit the number of outstanding peer shutdown | ||
6323 | * requests. This is necessary for making sure we don't do | ||
6324 | * too many ssh connections at once, but is generally nicer | ||
6325 | * to any system as well (graduated task starts, as opposed | ||
6326 | * to calling gnunet-arm N times all at once). | ||
6327 | */ | ||
6328 | static void | ||
6329 | schedule_churn_shutdown_task (void *cls, | ||
6330 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
6331 | { | ||
6332 | struct PeerShutdownContext *peer_shutdown_ctx = cls; | ||
6333 | struct ShutdownContext *shutdown_ctx; | ||
6334 | struct ChurnContext *churn_ctx; | ||
6335 | |||
6336 | GNUNET_assert (peer_shutdown_ctx != NULL); | ||
6337 | shutdown_ctx = peer_shutdown_ctx->shutdown_ctx; | ||
6338 | GNUNET_assert (shutdown_ctx != NULL); | ||
6339 | churn_ctx = (struct ChurnContext *) shutdown_ctx->cb_cls; | ||
6340 | if (shutdown_ctx->outstanding > churn_ctx->pg->max_concurrent_ssh) | ||
6341 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
6342 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
6343 | &schedule_churn_shutdown_task, | ||
6344 | peer_shutdown_ctx); | ||
6345 | else | ||
6346 | { | ||
6347 | shutdown_ctx->outstanding++; | ||
6348 | if (churn_ctx->service != NULL) | ||
6349 | GNUNET_TESTING_daemon_stop_service (peer_shutdown_ctx->daemon, | ||
6350 | churn_ctx->service, | ||
6351 | shutdown_ctx->timeout, | ||
6352 | shutdown_ctx->cb, shutdown_ctx); | ||
6353 | else | ||
6354 | GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon, | ||
6355 | shutdown_ctx->timeout, shutdown_ctx->cb, | ||
6356 | shutdown_ctx, GNUNET_NO, GNUNET_YES); | ||
6357 | GNUNET_free (peer_shutdown_ctx); | ||
6358 | } | ||
6359 | } | ||
6360 | |||
6361 | |||
6362 | /** | ||
6363 | * Simulate churn by stopping some peers (and possibly | ||
6364 | * re-starting others if churn is called multiple times). This | ||
6365 | * function can only be used to create leave-join churn (peers "never" | ||
6366 | * leave for good). First "voff" random peers that are currently | ||
6367 | * online will be taken offline; then "von" random peers that are then | ||
6368 | * offline will be put back online. No notifications will be | ||
6369 | * generated for any of these operations except for the callback upon | ||
6370 | * completion. | ||
6371 | * | ||
6372 | * @param pg handle for the peer group | ||
6373 | * @param service the service to churn off/on, NULL to churn peer | ||
6374 | * @param voff number of peers that should go offline | ||
6375 | * @param von number of peers that should come back online; | ||
6376 | * must be zero on first call (since "testbed_start" | ||
6377 | * always starts all of the peers) | ||
6378 | * @param timeout how long to wait for operations to finish before | ||
6379 | * giving up | ||
6380 | * @param cb function to call at the end | ||
6381 | * @param cb_cls closure for cb | ||
6382 | */ | ||
6383 | void | ||
6384 | GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg, | ||
6385 | char *service, unsigned int voff, | ||
6386 | unsigned int von, | ||
6387 | struct GNUNET_TIME_Relative timeout, | ||
6388 | GNUNET_TESTING_NotifyCompletion cb, void *cb_cls) | ||
6389 | { | ||
6390 | struct ChurnContext *churn_ctx; | ||
6391 | struct ShutdownContext *shutdown_ctx; | ||
6392 | struct PeerShutdownContext *peer_shutdown_ctx; | ||
6393 | struct PeerRestartContext *peer_restart_ctx; | ||
6394 | struct ChurnRestartContext *churn_startup_ctx; | ||
6395 | |||
6396 | unsigned int running; | ||
6397 | unsigned int stopped; | ||
6398 | unsigned int total_running; | ||
6399 | unsigned int total_stopped; | ||
6400 | unsigned int i; | ||
6401 | unsigned int *running_arr; | ||
6402 | unsigned int *stopped_arr; | ||
6403 | unsigned int *running_permute; | ||
6404 | unsigned int *stopped_permute; | ||
6405 | char *pos; | ||
6406 | |||
6407 | shutdown_ctx = NULL; | ||
6408 | peer_shutdown_ctx = NULL; | ||
6409 | peer_restart_ctx = NULL; | ||
6410 | churn_startup_ctx = NULL; | ||
6411 | |||
6412 | running = 0; | ||
6413 | stopped = 0; | ||
6414 | |||
6415 | if ((von == 0) && (voff == 0)) /* No peers at all? */ | ||
6416 | { | ||
6417 | cb (cb_cls, NULL); | ||
6418 | return; | ||
6419 | } | ||
6420 | |||
6421 | for (i = 0; i < pg->total; i++) | ||
6422 | { | ||
6423 | if (service == NULL) | ||
6424 | { | ||
6425 | if (pg->peers[i].daemon->running == GNUNET_YES) | ||
6426 | { | ||
6427 | GNUNET_assert (running != -1); | ||
6428 | running++; | ||
6429 | } | ||
6430 | else | ||
6431 | { | ||
6432 | GNUNET_assert (stopped != -1); | ||
6433 | stopped++; | ||
6434 | } | ||
6435 | } | ||
6436 | else | ||
6437 | { | ||
6438 | /* FIXME: make churned services a list! */ | ||
6439 | pos = pg->peers[i].daemon->churned_services; | ||
6440 | /* FIXME: while (pos != NULL) */ | ||
6441 | if (pos != NULL) | ||
6442 | { | ||
6443 | #if FIXME | ||
6444 | if (0 == strcasecmp (pos, service)) | ||
6445 | { | ||
6446 | |||
6447 | break; | ||
6448 | } | ||
6449 | #endif | ||
6450 | GNUNET_assert (stopped != -1); | ||
6451 | stopped++; | ||
6452 | /* FIXME: pos = pos->next; */ | ||
6453 | } | ||
6454 | if (pos == NULL) | ||
6455 | { | ||
6456 | GNUNET_assert (running != -1); | ||
6457 | running++; | ||
6458 | } | ||
6459 | } | ||
6460 | } | ||
6461 | |||
6462 | if (voff > running) | ||
6463 | { | ||
6464 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
6465 | "Trying to stop more peers (%d) than are currently running (%d)!\n", | ||
6466 | voff, running); | ||
6467 | cb (cb_cls, "Trying to stop more peers than are currently running!"); | ||
6468 | return; | ||
6469 | } | ||
6470 | |||
6471 | if (von > stopped) | ||
6472 | { | ||
6473 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
6474 | "Trying to start more peers (%d) than are currently stopped (%d)!\n", | ||
6475 | von, stopped); | ||
6476 | cb (cb_cls, "Trying to start more peers than are currently stopped!"); | ||
6477 | return; | ||
6478 | } | ||
6479 | |||
6480 | churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext)); | ||
6481 | |||
6482 | if (service != NULL) | ||
6483 | churn_ctx->service = GNUNET_strdup (service); | ||
6484 | running_arr = NULL; | ||
6485 | if (running > 0) | ||
6486 | running_arr = GNUNET_malloc (running * sizeof (unsigned int)); | ||
6487 | |||
6488 | stopped_arr = NULL; | ||
6489 | if (stopped > 0) | ||
6490 | stopped_arr = GNUNET_malloc (stopped * sizeof (unsigned int)); | ||
6491 | |||
6492 | running_permute = NULL; | ||
6493 | stopped_permute = NULL; | ||
6494 | |||
6495 | if (running > 0) | ||
6496 | running_permute = | ||
6497 | GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, running); | ||
6498 | if (stopped > 0) | ||
6499 | stopped_permute = | ||
6500 | GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, stopped); | ||
6501 | |||
6502 | total_running = running; | ||
6503 | total_stopped = stopped; | ||
6504 | running = 0; | ||
6505 | stopped = 0; | ||
6506 | |||
6507 | churn_ctx->num_to_start = von; | ||
6508 | churn_ctx->num_to_stop = voff; | ||
6509 | churn_ctx->cb = cb; | ||
6510 | churn_ctx->cb_cls = cb_cls; | ||
6511 | churn_ctx->pg = pg; | ||
6512 | |||
6513 | for (i = 0; i < pg->total; i++) | ||
6514 | { | ||
6515 | if (service == NULL) | ||
6516 | { | ||
6517 | if (pg->peers[i].daemon->running == GNUNET_YES) | ||
6518 | { | ||
6519 | GNUNET_assert ((running_arr != NULL) && (total_running > running)); | ||
6520 | running_arr[running] = i; | ||
6521 | running++; | ||
6522 | } | ||
6523 | else | ||
6524 | { | ||
6525 | GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped)); | ||
6526 | stopped_arr[stopped] = i; | ||
6527 | stopped++; | ||
6528 | } | ||
6529 | } | ||
6530 | else | ||
6531 | { | ||
6532 | /* FIXME: make churned services a list! */ | ||
6533 | pos = pg->peers[i].daemon->churned_services; | ||
6534 | /* FIXME: while (pos != NULL) */ | ||
6535 | if (pos != NULL) | ||
6536 | { | ||
6537 | GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped)); | ||
6538 | stopped_arr[stopped] = i; | ||
6539 | stopped++; | ||
6540 | /* FIXME: pos = pos->next; */ | ||
6541 | } | ||
6542 | if (pos == NULL) | ||
6543 | { | ||
6544 | GNUNET_assert ((running_arr != NULL) && (total_running > running)); | ||
6545 | running_arr[running] = i; | ||
6546 | running++; | ||
6547 | } | ||
6548 | } | ||
6549 | } | ||
6550 | |||
6551 | GNUNET_assert (running >= voff); | ||
6552 | if (voff > 0) | ||
6553 | { | ||
6554 | shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext)); | ||
6555 | shutdown_ctx->cb = &churn_stop_callback; | ||
6556 | shutdown_ctx->cb_cls = churn_ctx; | ||
6557 | shutdown_ctx->total_peers = voff; | ||
6558 | shutdown_ctx->timeout = timeout; | ||
6559 | } | ||
6560 | |||
6561 | for (i = 0; i < voff; i++) | ||
6562 | { | ||
6563 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peer %d!\n", | ||
6564 | running_arr[running_permute[i]]); | ||
6565 | GNUNET_assert (running_arr != NULL); | ||
6566 | peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext)); | ||
6567 | peer_shutdown_ctx->daemon = | ||
6568 | pg->peers[running_arr[running_permute[i]]].daemon; | ||
6569 | peer_shutdown_ctx->shutdown_ctx = shutdown_ctx; | ||
6570 | GNUNET_SCHEDULER_add_now (&schedule_churn_shutdown_task, peer_shutdown_ctx); | ||
6571 | } | ||
6572 | |||
6573 | GNUNET_assert (stopped >= von); | ||
6574 | if (von > 0) | ||
6575 | { | ||
6576 | churn_startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext)); | ||
6577 | churn_startup_ctx->churn_ctx = churn_ctx; | ||
6578 | churn_startup_ctx->timeout = timeout; | ||
6579 | churn_startup_ctx->pg = pg; | ||
6580 | } | ||
6581 | for (i = 0; i < von; i++) | ||
6582 | { | ||
6583 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting up peer %d!\n", | ||
6584 | stopped_arr[stopped_permute[i]]); | ||
6585 | GNUNET_assert (stopped_arr != NULL); | ||
6586 | peer_restart_ctx = GNUNET_malloc (sizeof (struct PeerRestartContext)); | ||
6587 | peer_restart_ctx->churn_restart_ctx = churn_startup_ctx; | ||
6588 | peer_restart_ctx->daemon = | ||
6589 | pg->peers[stopped_arr[stopped_permute[i]]].daemon; | ||
6590 | GNUNET_SCHEDULER_add_now (&schedule_churn_restart, peer_restart_ctx); | ||
6591 | } | ||
6592 | |||
6593 | GNUNET_free_non_null (running_arr); | ||
6594 | GNUNET_free_non_null (stopped_arr); | ||
6595 | GNUNET_free_non_null (running_permute); | ||
6596 | GNUNET_free_non_null (stopped_permute); | ||
6597 | } | ||
6598 | |||
6599 | /* | ||
6600 | * Start a given service for each of the peers in the peer group. | ||
6601 | * | ||
6602 | * @param pg handle for the peer group | ||
6603 | * @param service the service to start | ||
6604 | * @param timeout how long to wait for operations to finish before | ||
6605 | * giving up | ||
6606 | * @param cb function to call once finished | ||
6607 | * @param cb_cls closure for cb | ||
6608 | * | ||
6609 | */ | ||
6610 | void | ||
6611 | GNUNET_TESTING_daemons_start_service (struct GNUNET_TESTING_PeerGroup *pg, | ||
6612 | char *service, | ||
6613 | struct GNUNET_TIME_Relative timeout, | ||
6614 | GNUNET_TESTING_NotifyCompletion cb, | ||
6615 | void *cb_cls) | ||
6616 | { | ||
6617 | struct ServiceStartContext *start_ctx; | ||
6618 | struct PeerServiceStartContext *peer_start_ctx; | ||
6619 | unsigned int i; | ||
6620 | |||
6621 | GNUNET_assert (service != NULL); | ||
6622 | |||
6623 | start_ctx = GNUNET_malloc (sizeof (struct ServiceStartContext)); | ||
6624 | start_ctx->pg = pg; | ||
6625 | start_ctx->remaining = pg->total; | ||
6626 | start_ctx->cb = cb; | ||
6627 | start_ctx->cb_cls = cb_cls; | ||
6628 | start_ctx->service = GNUNET_strdup (service); | ||
6629 | start_ctx->timeout = timeout; | ||
6630 | |||
6631 | for (i = 0; i < pg->total; i++) | ||
6632 | { | ||
6633 | peer_start_ctx = GNUNET_malloc (sizeof (struct PeerServiceStartContext)); | ||
6634 | peer_start_ctx->start_ctx = start_ctx; | ||
6635 | peer_start_ctx->daemon = pg->peers[i].daemon; | ||
6636 | GNUNET_SCHEDULER_add_now (&schedule_service_start, peer_start_ctx); | ||
6637 | } | ||
6638 | } | ||
6639 | |||
6640 | /** | ||
6641 | * Restart all peers in the given group. | ||
6642 | * | ||
6643 | * @param pg the handle to the peer group | ||
6644 | * @param callback function to call on completion (or failure) | ||
6645 | * @param callback_cls closure for the callback function | ||
6646 | */ | ||
6647 | void | ||
6648 | GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, | ||
6649 | GNUNET_TESTING_NotifyCompletion callback, | ||
6650 | void *callback_cls) | ||
6651 | { | ||
6652 | struct RestartContext *restart_context; | ||
6653 | unsigned int off; | ||
6654 | |||
6655 | if (pg->total > 0) | ||
6656 | { | ||
6657 | restart_context = GNUNET_malloc (sizeof (struct RestartContext)); | ||
6658 | restart_context->peer_group = pg; | ||
6659 | restart_context->peers_restarted = 0; | ||
6660 | restart_context->callback = callback; | ||
6661 | restart_context->callback_cls = callback_cls; | ||
6662 | |||
6663 | for (off = 0; off < pg->total; off++) | ||
6664 | { | ||
6665 | GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, | ||
6666 | restart_context); | ||
6667 | } | ||
6668 | } | ||
6669 | } | ||
6670 | |||
6671 | |||
6672 | /** | ||
6673 | * Start or stop an individual peer from the given group. | ||
6674 | * | ||
6675 | * @param pg handle to the peer group | ||
6676 | * @param offset which peer to start or stop | ||
6677 | * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it | ||
6678 | * @param timeout how long to wait for shutdown | ||
6679 | * @param cb function to call at the end | ||
6680 | * @param cb_cls closure for cb | ||
6681 | */ | ||
6682 | void | ||
6683 | GNUNET_TESTING_daemons_vary (struct GNUNET_TESTING_PeerGroup *pg, | ||
6684 | unsigned int offset, int desired_status, | ||
6685 | struct GNUNET_TIME_Relative timeout, | ||
6686 | GNUNET_TESTING_NotifyCompletion cb, void *cb_cls) | ||
6687 | { | ||
6688 | struct ShutdownContext *shutdown_ctx; | ||
6689 | struct ChurnRestartContext *startup_ctx; | ||
6690 | struct ChurnContext *churn_ctx; | ||
6691 | |||
6692 | if (GNUNET_NO == desired_status) | ||
6693 | { | ||
6694 | if (NULL != pg->peers[offset].daemon) | ||
6695 | { | ||
6696 | shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext)); | ||
6697 | churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext)); | ||
6698 | churn_ctx->num_to_start = 0; | ||
6699 | churn_ctx->num_to_stop = 1; | ||
6700 | churn_ctx->cb = cb; | ||
6701 | churn_ctx->cb_cls = cb_cls; | ||
6702 | shutdown_ctx->cb_cls = churn_ctx; | ||
6703 | GNUNET_TESTING_daemon_stop (pg->peers[offset].daemon, timeout, | ||
6704 | &churn_stop_callback, shutdown_ctx, GNUNET_NO, | ||
6705 | GNUNET_YES); | ||
6706 | } | ||
6707 | } | ||
6708 | else if (GNUNET_YES == desired_status) | ||
6709 | { | ||
6710 | if (NULL == pg->peers[offset].daemon) | ||
6711 | { | ||
6712 | startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext)); | ||
6713 | churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext)); | ||
6714 | churn_ctx->num_to_start = 1; | ||
6715 | churn_ctx->num_to_stop = 0; | ||
6716 | churn_ctx->cb = cb; | ||
6717 | churn_ctx->cb_cls = cb_cls; | ||
6718 | startup_ctx->churn_ctx = churn_ctx; | ||
6719 | GNUNET_TESTING_daemon_start_stopped (pg->peers[offset].daemon, timeout, | ||
6720 | &churn_start_callback, startup_ctx); | ||
6721 | } | ||
6722 | } | ||
6723 | else | ||
6724 | GNUNET_break (0); | ||
6725 | } | ||
6726 | |||
6727 | |||
6728 | /** | ||
6729 | * Callback for shutting down peers in a peer group. | ||
6730 | * | ||
6731 | * @param cls closure (struct ShutdownContext) | ||
6732 | * @param emsg NULL on success | ||
6733 | */ | ||
6734 | static void | ||
6735 | internal_shutdown_callback (void *cls, const char *emsg) | ||
6736 | { | ||
6737 | struct PeerShutdownContext *peer_shutdown_ctx = cls; | ||
6738 | struct ShutdownContext *shutdown_ctx = peer_shutdown_ctx->shutdown_ctx; | ||
6739 | unsigned int off; | ||
6740 | int i; | ||
6741 | struct OutstandingSSH *ssh_pos; | ||
6742 | |||
6743 | shutdown_ctx->outstanding--; | ||
6744 | if (peer_shutdown_ctx->daemon->hostname != NULL) | ||
6745 | decrement_outstanding_at_host (peer_shutdown_ctx->daemon->hostname, | ||
6746 | shutdown_ctx->pg); | ||
6747 | |||
6748 | if (emsg == NULL) | ||
6749 | { | ||
6750 | shutdown_ctx->peers_down++; | ||
6751 | } | ||
6752 | else | ||
6753 | { | ||
6754 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "internal_shutdown_callback", | ||
6755 | "Failed to stop a peer: %s\n", emsg); | ||
6756 | shutdown_ctx->peers_failed++; | ||
6757 | } | ||
6758 | |||
6759 | if ((shutdown_ctx->cb != NULL) && | ||
6760 | (shutdown_ctx->peers_down + shutdown_ctx->peers_failed == | ||
6761 | shutdown_ctx->total_peers)) | ||
6762 | { | ||
6763 | if (shutdown_ctx->peers_failed > 0) | ||
6764 | shutdown_ctx->cb (shutdown_ctx->cb_cls, | ||
6765 | "Not all peers successfully shut down!"); | ||
6766 | else | ||
6767 | shutdown_ctx->cb (shutdown_ctx->cb_cls, NULL); | ||
6768 | |||
6769 | for (i = 0; i < shutdown_ctx->pg->total; i++) | ||
6770 | { | ||
6771 | if (shutdown_ctx->pg->peers[i].startup_task != GNUNET_SCHEDULER_NO_TASK) | ||
6772 | GNUNET_SCHEDULER_cancel (shutdown_ctx->pg->peers[i].startup_task); | ||
6773 | } | ||
6774 | GNUNET_free (shutdown_ctx->pg->peers); | ||
6775 | GNUNET_free_non_null (shutdown_ctx->pg->hostkey_data); | ||
6776 | for (off = 0; off < shutdown_ctx->pg->num_hosts; off++) | ||
6777 | { | ||
6778 | GNUNET_free (shutdown_ctx->pg->hosts[off].hostname); | ||
6779 | GNUNET_free_non_null (shutdown_ctx->pg->hosts[off].username); | ||
6780 | } | ||
6781 | GNUNET_free_non_null (shutdown_ctx->pg->hosts); | ||
6782 | while (NULL != (ssh_pos = shutdown_ctx->pg->ssh_head)) | ||
6783 | { | ||
6784 | GNUNET_CONTAINER_DLL_remove (shutdown_ctx->pg->ssh_head, | ||
6785 | shutdown_ctx->pg->ssh_tail, ssh_pos); | ||
6786 | GNUNET_free (ssh_pos); | ||
6787 | } | ||
6788 | GNUNET_free (shutdown_ctx->pg); | ||
6789 | GNUNET_free (shutdown_ctx); | ||
6790 | } | ||
6791 | GNUNET_free (peer_shutdown_ctx); | ||
6792 | } | ||
6793 | |||
6794 | |||
6795 | /** | ||
6796 | * Task to rate limit the number of outstanding peer shutdown | ||
6797 | * requests. This is necessary for making sure we don't do | ||
6798 | * too many ssh connections at once, but is generally nicer | ||
6799 | * to any system as well (graduated task starts, as opposed | ||
6800 | * to calling gnunet-arm N times all at once). | ||
6801 | */ | ||
6802 | static void | ||
6803 | schedule_shutdown_task (void *cls, | ||
6804 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
6805 | { | ||
6806 | struct PeerShutdownContext *peer_shutdown_ctx = cls; | ||
6807 | struct ShutdownContext *shutdown_ctx; | ||
6808 | struct GNUNET_TESTING_Daemon *d; | ||
6809 | |||
6810 | GNUNET_assert (peer_shutdown_ctx != NULL); | ||
6811 | d = peer_shutdown_ctx->daemon; | ||
6812 | shutdown_ctx = peer_shutdown_ctx->shutdown_ctx; | ||
6813 | GNUNET_assert (shutdown_ctx != NULL); | ||
6814 | |||
6815 | if ((shutdown_ctx->outstanding < shutdown_ctx->pg->max_concurrent_ssh) || | ||
6816 | ((d->hostname != NULL) && | ||
6817 | (count_outstanding_at_host | ||
6818 | (d->hostname, | ||
6819 | shutdown_ctx->pg) < shutdown_ctx->pg->max_concurrent_ssh))) | ||
6820 | { | ||
6821 | if (d->hostname != NULL) | ||
6822 | increment_outstanding_at_host (d->hostname, | ||
6823 | shutdown_ctx->pg); | ||
6824 | shutdown_ctx->outstanding++; | ||
6825 | GNUNET_TESTING_daemon_stop (d, | ||
6826 | shutdown_ctx->timeout, | ||
6827 | &internal_shutdown_callback, peer_shutdown_ctx, | ||
6828 | shutdown_ctx->delete_files, GNUNET_NO); | ||
6829 | } | ||
6830 | else | ||
6831 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
6832 | (GNUNET_TIME_UNIT_MILLISECONDS, 100), | ||
6833 | &schedule_shutdown_task, peer_shutdown_ctx); | ||
6834 | |||
6835 | } | ||
6836 | |||
6837 | /** | ||
6838 | * Read a testing hosts file based on a configuration. | ||
6839 | * Returns a DLL of hosts (caller must free!) on success | ||
6840 | * or NULL on failure. | ||
6841 | * | ||
6842 | * @param cfg a configuration with a testing section | ||
6843 | * | ||
6844 | * @return DLL of hosts on success, NULL on failure | ||
6845 | */ | ||
6846 | struct GNUNET_TESTING_Host * | ||
6847 | GNUNET_TESTING_hosts_load (const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
6848 | { | ||
6849 | struct GNUNET_TESTING_Host *hosts; | ||
6850 | struct GNUNET_TESTING_Host *temphost; | ||
6851 | char *data; | ||
6852 | char *buf; | ||
6853 | char *hostfile; | ||
6854 | struct stat frstat; | ||
6855 | int count; | ||
6856 | int ret; | ||
6857 | |||
6858 | /* Check for a hostfile containing user@host:port triples */ | ||
6859 | if (GNUNET_OK != | ||
6860 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", "hostfile", | ||
6861 | &hostfile)) | ||
6862 | return NULL; | ||
6863 | |||
6864 | hosts = NULL; | ||
6865 | temphost = NULL; | ||
6866 | data = NULL; | ||
6867 | if (hostfile != NULL) | ||
6868 | { | ||
6869 | if (GNUNET_OK != GNUNET_DISK_file_test (hostfile)) | ||
6870 | GNUNET_DISK_fn_write (hostfile, NULL, 0, | ||
6871 | GNUNET_DISK_PERM_USER_READ | | ||
6872 | GNUNET_DISK_PERM_USER_WRITE); | ||
6873 | if ((0 != STAT (hostfile, &frstat)) || (frstat.st_size == 0)) | ||
6874 | { | ||
6875 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
6876 | "Could not open file specified for host list, ending test!"); | ||
6877 | GNUNET_free (hostfile); | ||
6878 | return NULL; | ||
6879 | } | ||
6880 | |||
6881 | data = GNUNET_malloc_large (frstat.st_size); | ||
6882 | GNUNET_assert (data != NULL); | ||
6883 | if (frstat.st_size != GNUNET_DISK_fn_read (hostfile, data, frstat.st_size)) | ||
6884 | { | ||
6885 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
6886 | "Could not read file %s specified for host list, ending test!", | ||
6887 | hostfile); | ||
6888 | GNUNET_free (hostfile); | ||
6889 | GNUNET_free (data); | ||
6890 | return NULL; | ||
6891 | } | ||
6892 | |||
6893 | GNUNET_free_non_null (hostfile); | ||
6894 | |||
6895 | buf = data; | ||
6896 | count = 0; | ||
6897 | while (count < frstat.st_size - 1) | ||
6898 | { | ||
6899 | count++; | ||
6900 | if (((data[count] == '\n')) && (buf != &data[count])) | ||
6901 | { | ||
6902 | data[count] = '\0'; | ||
6903 | temphost = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Host)); | ||
6904 | ret = | ||
6905 | SSCANF (buf, "%a[a-zA-Z0-9_]@%a[a-zA-Z0-9.]:%hd", | ||
6906 | &temphost->username, &temphost->hostname, &temphost->port); | ||
6907 | if (3 == ret) | ||
6908 | { | ||
6909 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
6910 | "Successfully read host %s, port %d and user %s from file\n", | ||
6911 | temphost->hostname, temphost->port, temphost->username); | ||
6912 | } | ||
6913 | else | ||
6914 | { | ||
6915 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
6916 | "Error reading line `%s' in hostfile\n", buf); | ||
6917 | GNUNET_free (temphost); | ||
6918 | buf = &data[count + 1]; | ||
6919 | continue; | ||
6920 | } | ||
6921 | temphost->next = hosts; | ||
6922 | hosts = temphost; | ||
6923 | buf = &data[count + 1]; | ||
6924 | } | ||
6925 | else if ((data[count] == '\n') || (data[count] == '\0')) | ||
6926 | buf = &data[count + 1]; | ||
6927 | } | ||
6928 | } | ||
6929 | GNUNET_free_non_null (data); | ||
6930 | |||
6931 | return hosts; | ||
6932 | } | ||
6933 | |||
6934 | /** | ||
6935 | * Shutdown all peers started in the given group. | ||
6936 | * | ||
6937 | * @param pg handle to the peer group | ||
6938 | * @param timeout how long to wait for shutdown | ||
6939 | * @param cb callback to notify upon success or failure | ||
6940 | * @param cb_cls closure for cb | ||
6941 | */ | ||
6942 | void | ||
6943 | GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg, | ||
6944 | struct GNUNET_TIME_Relative timeout, | ||
6945 | GNUNET_TESTING_NotifyCompletion cb, void *cb_cls) | ||
6946 | { | ||
6947 | unsigned int off; | ||
6948 | struct ShutdownContext *shutdown_ctx; | ||
6949 | struct PeerShutdownContext *peer_shutdown_ctx; | ||
6950 | |||
6951 | #if OLD | ||
6952 | struct PeerConnection *conn_iter; | ||
6953 | struct PeerConnection *temp_conn; | ||
6954 | #endif | ||
6955 | struct ConnectContext *cc; | ||
6956 | |||
6957 | GNUNET_assert (pg->total > 0); | ||
6958 | while (NULL != (cc = pg->cc_head)) | ||
6959 | { | ||
6960 | GNUNET_CONTAINER_DLL_remove (pg->cc_head, pg->cc_tail, cc); | ||
6961 | if (GNUNET_SCHEDULER_NO_TASK != cc->task) | ||
6962 | GNUNET_SCHEDULER_cancel (cc->task); | ||
6963 | if (NULL != cc->cc) | ||
6964 | GNUNET_TESTING_daemons_connect_cancel (cc->cc); | ||
6965 | GNUNET_free (cc); | ||
6966 | } | ||
6967 | |||
6968 | shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext)); | ||
6969 | shutdown_ctx->delete_files = | ||
6970 | GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING_OLD", "DELETE_FILES"); | ||
6971 | shutdown_ctx->cb = cb; | ||
6972 | shutdown_ctx->cb_cls = cb_cls; | ||
6973 | shutdown_ctx->total_peers = pg->total; | ||
6974 | shutdown_ctx->timeout = timeout; | ||
6975 | shutdown_ctx->pg = pg; | ||
6976 | |||
6977 | for (off = 0; off < pg->total; off++) | ||
6978 | { | ||
6979 | GNUNET_assert (NULL != pg->peers[off].daemon); | ||
6980 | peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext)); | ||
6981 | peer_shutdown_ctx->daemon = pg->peers[off].daemon; | ||
6982 | peer_shutdown_ctx->shutdown_ctx = shutdown_ctx; | ||
6983 | GNUNET_SCHEDULER_add_now (&schedule_shutdown_task, peer_shutdown_ctx); | ||
6984 | |||
6985 | if (NULL != pg->peers[off].cfg) | ||
6986 | { | ||
6987 | GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg); | ||
6988 | pg->peers[off].cfg = NULL; | ||
6989 | } | ||
6990 | #if OLD | ||
6991 | // FIXME Do DLL remove for all pg->peers[off].LIST | ||
6992 | conn_iter = pg->peers[off].allowed_peers_head; | ||
6993 | while (conn_iter != NULL) | ||
6994 | { | ||
6995 | temp_conn = conn_iter->next; | ||
6996 | GNUNET_free (conn_iter); | ||
6997 | conn_iter = temp_conn; | ||
6998 | } | ||
6999 | pg->peers[off].allowed_peers_head = NULL; | ||
7000 | |||
7001 | conn_iter = pg->peers[off].connect_peers_head; | ||
7002 | while (conn_iter != NULL) | ||
7003 | { | ||
7004 | temp_conn = conn_iter->next; | ||
7005 | GNUNET_free (conn_iter); | ||
7006 | conn_iter = temp_conn; | ||
7007 | } | ||
7008 | pg->peers[off].connect_peers_head = NULL; | ||
7009 | |||
7010 | conn_iter = pg->peers[off].blacklisted_peers_head; | ||
7011 | while (conn_iter != NULL) | ||
7012 | { | ||
7013 | temp_conn = conn_iter->next; | ||
7014 | GNUNET_free (conn_iter); | ||
7015 | conn_iter = temp_conn; | ||
7016 | } | ||
7017 | pg->peers[off].blacklisted_peers_head = NULL; | ||
7018 | |||
7019 | conn_iter = pg->peers[off].connect_peers_working_set_head; | ||
7020 | while (conn_iter != NULL) | ||
7021 | { | ||
7022 | temp_conn = conn_iter->next; | ||
7023 | GNUNET_free (conn_iter); | ||
7024 | conn_iter = temp_conn; | ||
7025 | } | ||
7026 | pg->peers[off].connect_peers_working_set_head = NULL; | ||
7027 | #else | ||
7028 | if (pg->peers[off].allowed_peers != NULL) | ||
7029 | GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].allowed_peers); | ||
7030 | if (pg->peers[off].connect_peers != NULL) | ||
7031 | GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].connect_peers); | ||
7032 | if (pg->peers[off].blacklisted_peers != NULL) | ||
7033 | GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].blacklisted_peers); | ||
7034 | #endif | ||
7035 | } | ||
7036 | } | ||
7037 | |||
7038 | /* end of testing_group.c */ | ||