diff options
Diffstat (limited to 'src/testing_old/testing_peergroup.c')
-rw-r--r-- | src/testing_old/testing_peergroup.c | 1017 |
1 files changed, 0 insertions, 1017 deletions
diff --git a/src/testing_old/testing_peergroup.c b/src/testing_old/testing_peergroup.c deleted file mode 100644 index acd68dd3c..000000000 --- a/src/testing_old/testing_peergroup.c +++ /dev/null | |||
@@ -1,1017 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2008-2011 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_old/testing_peergroup.c | ||
23 | * @brief API implementation for easy peer group creation | ||
24 | * @author Nathan Evans | ||
25 | * @author Christian Grothoff | ||
26 | * | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | #include "gnunet_constants.h" | ||
30 | #include "gnunet_arm_service.h" | ||
31 | #include "gnunet_testing_lib.h" | ||
32 | #include "gnunet_core_service.h" | ||
33 | #include "gnunet_disk_lib.h" | ||
34 | |||
35 | /** Globals **/ | ||
36 | #define DEFAULT_CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) | ||
37 | |||
38 | #define DEFAULT_CONNECT_ATTEMPTS 2 | ||
39 | |||
40 | /** Struct definitions **/ | ||
41 | |||
42 | struct PeerGroupStartupContext | ||
43 | { | ||
44 | struct GNUNET_TESTING_PeerGroup *pg; | ||
45 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
46 | unsigned int total; | ||
47 | unsigned int peers_left; | ||
48 | unsigned long long max_concurrent_connections; | ||
49 | |||
50 | /** | ||
51 | * Maximum attemps to connect two daemons. | ||
52 | */ | ||
53 | unsigned long long connect_attempts; | ||
54 | |||
55 | /** | ||
56 | * How long to spend trying to establish all the connections? | ||
57 | */ | ||
58 | struct GNUNET_TIME_Relative connect_timeout; | ||
59 | |||
60 | unsigned long long max_concurrent_ssh; | ||
61 | struct GNUNET_TIME_Absolute timeout; | ||
62 | GNUNET_TESTING_NotifyConnection connect_cb; | ||
63 | GNUNET_TESTING_NotifyCompletion peergroup_cb; | ||
64 | |||
65 | /** | ||
66 | * Closure for all peergroup callbacks. | ||
67 | */ | ||
68 | void *cls; | ||
69 | |||
70 | const struct GNUNET_TESTING_Host *hostnames; | ||
71 | |||
72 | /** | ||
73 | * FIXME document | ||
74 | */ | ||
75 | enum GNUNET_TESTING_Topology topology; | ||
76 | |||
77 | float topology_percentage; | ||
78 | |||
79 | float topology_probability; | ||
80 | |||
81 | /** | ||
82 | * FIXME document | ||
83 | */ | ||
84 | enum GNUNET_TESTING_Topology restrict_topology; | ||
85 | |||
86 | /** | ||
87 | * FIXME document | ||
88 | */ | ||
89 | char *restrict_transports; | ||
90 | |||
91 | /** | ||
92 | * Initial connections | ||
93 | */ | ||
94 | enum GNUNET_TESTING_Topology connect_topology; | ||
95 | enum GNUNET_TESTING_TopologyOption connect_topology_option; | ||
96 | double connect_topology_option_modifier; | ||
97 | int verbose; | ||
98 | |||
99 | struct ProgressMeter *hostkey_meter; | ||
100 | struct ProgressMeter *peer_start_meter; | ||
101 | struct ProgressMeter *connect_meter; | ||
102 | |||
103 | /** | ||
104 | * Task used to kill the peergroup. | ||
105 | */ | ||
106 | GNUNET_SCHEDULER_TaskIdentifier die_task; | ||
107 | |||
108 | char *fail_reason; | ||
109 | |||
110 | /** | ||
111 | * Variable used to store the number of connections we should wait for. | ||
112 | */ | ||
113 | unsigned int expected_connections; | ||
114 | |||
115 | /** | ||
116 | * Time when the connecting peers was started. | ||
117 | */ | ||
118 | struct GNUNET_TIME_Absolute connect_start_time; | ||
119 | |||
120 | /** | ||
121 | * The total number of connections that have been created so far. | ||
122 | */ | ||
123 | unsigned int total_connections; | ||
124 | |||
125 | /** | ||
126 | * The total number of connections that have failed so far. | ||
127 | */ | ||
128 | unsigned int failed_connections; | ||
129 | |||
130 | /** | ||
131 | * File handle to write out topology in dot format. | ||
132 | */ | ||
133 | struct GNUNET_DISK_FileHandle *topology_output_file; | ||
134 | }; | ||
135 | |||
136 | struct TopologyOutputContext | ||
137 | { | ||
138 | struct GNUNET_DISK_FileHandle *file; | ||
139 | GNUNET_TESTING_NotifyCompletion notify_cb; | ||
140 | void *notify_cb_cls; | ||
141 | }; | ||
142 | |||
143 | /** | ||
144 | * Simple struct to keep track of progress, and print a | ||
145 | * percentage meter for long running tasks. | ||
146 | */ | ||
147 | struct ProgressMeter | ||
148 | { | ||
149 | /** | ||
150 | * Total number of tasks to complete. | ||
151 | */ | ||
152 | unsigned int total; | ||
153 | |||
154 | /** | ||
155 | * Print percentage done after modnum tasks. | ||
156 | */ | ||
157 | unsigned int modnum; | ||
158 | |||
159 | /** | ||
160 | * Print a . each dotnum tasks. | ||
161 | */ | ||
162 | unsigned int dotnum; | ||
163 | |||
164 | /** | ||
165 | * Total number completed thus far. | ||
166 | */ | ||
167 | unsigned int completed; | ||
168 | |||
169 | /** | ||
170 | * Whether or not to print. | ||
171 | */ | ||
172 | int print; | ||
173 | |||
174 | /** | ||
175 | * Startup string for progress meter. | ||
176 | */ | ||
177 | char *startup_string; | ||
178 | }; | ||
179 | |||
180 | |||
181 | /** Utility functions **/ | ||
182 | |||
183 | /** | ||
184 | * Create a meter to keep track of the progress of some task. | ||
185 | * | ||
186 | * @param total the total number of items to complete | ||
187 | * @param start_string a string to prefix the meter with (if printing) | ||
188 | * @param print GNUNET_YES to print the meter, GNUNET_NO to count | ||
189 | * internally only | ||
190 | * | ||
191 | * @return the progress meter | ||
192 | */ | ||
193 | static struct ProgressMeter * | ||
194 | create_meter (unsigned int total, char *start_string, int print) | ||
195 | { | ||
196 | struct ProgressMeter *ret; | ||
197 | |||
198 | ret = GNUNET_malloc (sizeof (struct ProgressMeter)); | ||
199 | ret->print = print; | ||
200 | ret->total = total; | ||
201 | ret->modnum = (total / 4 == 0) ? 1 : (total / 4); | ||
202 | ret->dotnum = (total / 50) + 1; | ||
203 | if (start_string != NULL) | ||
204 | ret->startup_string = GNUNET_strdup (start_string); | ||
205 | else | ||
206 | ret->startup_string = GNUNET_strdup (""); | ||
207 | |||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | /** | ||
212 | * Update progress meter (increment by one). | ||
213 | * | ||
214 | * @param meter the meter to update and print info for | ||
215 | * | ||
216 | * @return GNUNET_YES if called the total requested, | ||
217 | * GNUNET_NO if more items expected | ||
218 | */ | ||
219 | static int | ||
220 | update_meter (struct ProgressMeter *meter) | ||
221 | { | ||
222 | if (meter->print == GNUNET_YES) | ||
223 | { | ||
224 | if (meter->completed % meter->modnum == 0) | ||
225 | { | ||
226 | if (meter->completed == 0) | ||
227 | { | ||
228 | FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string); | ||
229 | } | ||
230 | else | ||
231 | FPRINTF (stdout, "%d%%", | ||
232 | (int) (((float) meter->completed / meter->total) * 100)); | ||
233 | } | ||
234 | else if (meter->completed % meter->dotnum == 0) | ||
235 | FPRINTF (stdout, "%s", "."); | ||
236 | |||
237 | if (meter->completed + 1 == meter->total) | ||
238 | FPRINTF (stdout, "%d%%]\n", 100); | ||
239 | fflush (stdout); | ||
240 | } | ||
241 | meter->completed++; | ||
242 | |||
243 | if (meter->completed == meter->total) | ||
244 | return GNUNET_YES; | ||
245 | return GNUNET_NO; | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Reset progress meter. | ||
250 | * | ||
251 | * @param meter the meter to reset | ||
252 | * | ||
253 | * @return GNUNET_YES if meter reset, | ||
254 | * GNUNET_SYSERR on error | ||
255 | */ | ||
256 | static int | ||
257 | reset_meter (struct ProgressMeter *meter) | ||
258 | { | ||
259 | if (meter == NULL) | ||
260 | return GNUNET_SYSERR; | ||
261 | |||
262 | meter->completed = 0; | ||
263 | return GNUNET_YES; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * Release resources for meter | ||
268 | * | ||
269 | * @param meter the meter to free | ||
270 | */ | ||
271 | static void | ||
272 | free_meter (struct ProgressMeter *meter) | ||
273 | { | ||
274 | GNUNET_free_non_null (meter->startup_string); | ||
275 | GNUNET_free (meter); | ||
276 | } | ||
277 | |||
278 | |||
279 | /** Functions for creating, starting and connecting the peergroup **/ | ||
280 | |||
281 | /** | ||
282 | * Check whether peers successfully shut down. | ||
283 | */ | ||
284 | static void | ||
285 | internal_shutdown_callback (void *cls, const char *emsg) | ||
286 | { | ||
287 | struct PeerGroupStartupContext *pg_start_ctx = cls; | ||
288 | |||
289 | if (emsg != NULL) | ||
290 | pg_start_ctx->peergroup_cb (pg_start_ctx->cls, emsg); | ||
291 | else | ||
292 | pg_start_ctx->peergroup_cb (pg_start_ctx->cls, pg_start_ctx->fail_reason); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * Check if the get_handle is being used, if so stop the request. Either | ||
297 | * way, schedule the end_badly_cont function which actually shuts down the | ||
298 | * test. | ||
299 | */ | ||
300 | static void | ||
301 | end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
302 | { | ||
303 | struct PeerGroupStartupContext *pg_start_ctx = cls; | ||
304 | |||
305 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
306 | "Failing peer group startup with error: `%s'!\n", | ||
307 | pg_start_ctx->fail_reason); | ||
308 | |||
309 | GNUNET_TESTING_daemons_stop (pg_start_ctx->pg, | ||
310 | GNUNET_TIME_absolute_get_remaining | ||
311 | (pg_start_ctx->timeout), | ||
312 | &internal_shutdown_callback, pg_start_ctx); | ||
313 | |||
314 | if (pg_start_ctx->hostkey_meter != NULL) | ||
315 | { | ||
316 | free_meter (pg_start_ctx->hostkey_meter); | ||
317 | pg_start_ctx->hostkey_meter = NULL; | ||
318 | } | ||
319 | if (pg_start_ctx->peer_start_meter != NULL) | ||
320 | { | ||
321 | free_meter (pg_start_ctx->peer_start_meter); | ||
322 | pg_start_ctx->peer_start_meter = NULL; | ||
323 | } | ||
324 | if (pg_start_ctx->connect_meter != NULL) | ||
325 | { | ||
326 | free_meter (pg_start_ctx->connect_meter); | ||
327 | pg_start_ctx->connect_meter = NULL; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * This function is called whenever a connection attempt is finished between two of | ||
333 | * the started peers (started with GNUNET_TESTING_daemons_start). The total | ||
334 | * number of times this function is called should equal the number returned | ||
335 | * from the GNUNET_TESTING_connect_topology call. | ||
336 | * | ||
337 | * The emsg variable is NULL on success (peers connected), and non-NULL on | ||
338 | * failure (peers failed to connect). | ||
339 | */ | ||
340 | static void | ||
341 | internal_topology_callback (void *cls, const struct GNUNET_PeerIdentity *first, | ||
342 | const struct GNUNET_PeerIdentity *second, | ||
343 | uint32_t distance, | ||
344 | const struct GNUNET_CONFIGURATION_Handle *first_cfg, | ||
345 | const struct GNUNET_CONFIGURATION_Handle | ||
346 | *second_cfg, | ||
347 | struct GNUNET_TESTING_Daemon *first_daemon, | ||
348 | struct GNUNET_TESTING_Daemon *second_daemon, | ||
349 | const char *emsg) | ||
350 | { | ||
351 | struct PeerGroupStartupContext *pg_start_ctx = cls; | ||
352 | char *temp_str; | ||
353 | char *second_str; | ||
354 | int temp; | ||
355 | |||
356 | #if TIMING | ||
357 | unsigned long long duration; | ||
358 | unsigned long long total_duration; | ||
359 | unsigned int new_connections; | ||
360 | unsigned int new_failed_connections; | ||
361 | double conns_per_sec_recent; | ||
362 | double conns_per_sec_total; | ||
363 | double failed_conns_per_sec_recent; | ||
364 | double failed_conns_per_sec_total; | ||
365 | #endif | ||
366 | |||
367 | #if TIMING | ||
368 | if (GNUNET_TIME_absolute_get_difference | ||
369 | (connect_last_time, | ||
370 | GNUNET_TIME_absolute_get ()).rel_value > | ||
371 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, | ||
372 | CONN_UPDATE_DURATION).rel_value) | ||
373 | { | ||
374 | /* Get number of new connections */ | ||
375 | new_connections = total_connections - previous_connections; | ||
376 | |||
377 | /* Get number of new FAILED connections */ | ||
378 | new_failed_connections = failed_connections - previous_failed_connections; | ||
379 | |||
380 | /* Get duration in seconds */ | ||
381 | duration = | ||
382 | GNUNET_TIME_absolute_get_difference (connect_last_time, | ||
383 | GNUNET_TIME_absolute_get | ||
384 | ()).rel_value / 1000; | ||
385 | total_duration = | ||
386 | GNUNET_TIME_absolute_get_difference (connect_start_time, | ||
387 | GNUNET_TIME_absolute_get | ||
388 | ()).rel_value / 1000; | ||
389 | |||
390 | failed_conns_per_sec_recent = (double) new_failed_connections / duration; | ||
391 | failed_conns_per_sec_total = (double) failed_connections / total_duration; | ||
392 | conns_per_sec_recent = (double) new_connections / duration; | ||
393 | conns_per_sec_total = (double) total_connections / total_duration; | ||
394 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
395 | "Recent: %.2f/s, Total: %.2f/s, Recent failed: %.2f/s, total failed %.2f/s\n", | ||
396 | conns_per_sec_recent, CONN_UPDATE_DURATION, conns_per_sec_total, | ||
397 | failed_conns_per_sec_recent, failed_conns_per_sec_total); | ||
398 | connect_last_time = GNUNET_TIME_absolute_get (); | ||
399 | previous_connections = total_connections; | ||
400 | previous_failed_connections = failed_connections; | ||
401 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
402 | "have %u total_connections, %u failed\n", total_connections, | ||
403 | failed_connections); | ||
404 | } | ||
405 | #endif | ||
406 | |||
407 | |||
408 | if (emsg == NULL) | ||
409 | { | ||
410 | pg_start_ctx->total_connections++; | ||
411 | #if VERBOSE > 1 | ||
412 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
413 | "connected peer %s to peer %s, distance %u\n", | ||
414 | first_daemon->shortname, second_daemon->shortname, distance); | ||
415 | #endif | ||
416 | if (pg_start_ctx->topology_output_file != NULL) | ||
417 | { | ||
418 | second_str = GNUNET_strdup (GNUNET_i2s (second)); | ||
419 | temp = | ||
420 | GNUNET_asprintf (&temp_str, "\t\"%s\" -- \"%s\"\n", | ||
421 | GNUNET_i2s (first), second_str); | ||
422 | GNUNET_free (second_str); | ||
423 | if (temp > 0) | ||
424 | GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str, | ||
425 | temp); | ||
426 | GNUNET_free (temp_str); | ||
427 | } | ||
428 | } | ||
429 | else | ||
430 | { | ||
431 | pg_start_ctx->failed_connections++; | ||
432 | #if VERBOSE | ||
433 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
434 | "Failed to connect peer %s to peer %s with error :\n%s\n", | ||
435 | first_daemon->shortname, second_daemon->shortname, emsg); | ||
436 | |||
437 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
438 | "Failed to connect peer %s to peer %s with error :\n%s\n", | ||
439 | first_daemon->shortname, second_daemon->shortname, emsg); | ||
440 | #endif | ||
441 | } | ||
442 | |||
443 | GNUNET_assert (pg_start_ctx->connect_meter != NULL); | ||
444 | if (pg_start_ctx->connect_cb != NULL) | ||
445 | pg_start_ctx->connect_cb (pg_start_ctx->cls, first, second, distance, | ||
446 | first_cfg, second_cfg, first_daemon, | ||
447 | second_daemon, emsg); | ||
448 | if (GNUNET_YES != update_meter (pg_start_ctx->connect_meter)) | ||
449 | { | ||
450 | /* No finished yet */ | ||
451 | return; | ||
452 | } | ||
453 | #if VERBOSE | ||
454 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
455 | "Created %d total connections, which is our target number! Starting next phase of testing.\n", | ||
456 | pg_start_ctx->total_connections); | ||
457 | #endif | ||
458 | |||
459 | #if TIMING | ||
460 | total_duration = | ||
461 | GNUNET_TIME_absolute_get_difference (connect_start_time, | ||
462 | GNUNET_TIME_absolute_get | ||
463 | ()).rel_value / 1000; | ||
464 | failed_conns_per_sec_total = (double) failed_connections / total_duration; | ||
465 | conns_per_sec_total = (double) total_connections / total_duration; | ||
466 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
467 | "Overall connection info --- Total: %u, Total Failed %u/s\n", | ||
468 | total_connections, failed_connections); | ||
469 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
470 | "Overall connection info --- Total: %.2f/s, Total Failed %.2f/s\n", | ||
471 | conns_per_sec_total, failed_conns_per_sec_total); | ||
472 | #endif | ||
473 | |||
474 | GNUNET_assert (pg_start_ctx->die_task != GNUNET_SCHEDULER_NO_TASK); | ||
475 | GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task); | ||
476 | |||
477 | /* Call final callback, signifying that the peer group has been started and connected */ | ||
478 | if (pg_start_ctx->peergroup_cb != NULL) | ||
479 | pg_start_ctx->peergroup_cb (pg_start_ctx->cls, NULL); | ||
480 | |||
481 | if (pg_start_ctx->topology_output_file != NULL) | ||
482 | { | ||
483 | temp = GNUNET_asprintf (&temp_str, "}\n"); | ||
484 | if (temp > 0) | ||
485 | GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str, | ||
486 | temp); | ||
487 | GNUNET_free (temp_str); | ||
488 | GNUNET_DISK_file_close (pg_start_ctx->topology_output_file); | ||
489 | } | ||
490 | GNUNET_free_non_null (pg_start_ctx->fail_reason); | ||
491 | if (NULL != pg_start_ctx->hostkey_meter) | ||
492 | free_meter(pg_start_ctx->hostkey_meter); | ||
493 | if (NULL != pg_start_ctx->peer_start_meter) | ||
494 | free_meter(pg_start_ctx->peer_start_meter); | ||
495 | if (NULL != pg_start_ctx->connect_meter) | ||
496 | free_meter(pg_start_ctx->connect_meter); | ||
497 | GNUNET_free (pg_start_ctx); | ||
498 | } | ||
499 | |||
500 | |||
501 | /** | ||
502 | * Callback called for each started daemon. | ||
503 | * | ||
504 | * @param cls Clause (PG Context). | ||
505 | * @param id PeerIdentidy of started daemon. | ||
506 | * @param cfg Configuration used by the daemon. | ||
507 | * @param d Handle for the daemon. | ||
508 | * @param emsg Error message, NULL on success. | ||
509 | */ | ||
510 | static void | ||
511 | internal_peers_started_callback (void *cls, | ||
512 | const struct GNUNET_PeerIdentity *id, | ||
513 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
514 | struct GNUNET_TESTING_Daemon *d, | ||
515 | const char *emsg) | ||
516 | { | ||
517 | struct PeerGroupStartupContext *pg_start_ctx = cls; | ||
518 | |||
519 | if (emsg != NULL) | ||
520 | { | ||
521 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
522 | "Failed to start daemon with error: `%s'\n", emsg); | ||
523 | return; | ||
524 | } | ||
525 | GNUNET_assert (id != NULL); | ||
526 | |||
527 | #if VERBOSE > 1 | ||
528 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n", | ||
529 | (pg_start_ctx->total - pg_start_ctx->peers_left) + 1, | ||
530 | pg_start_ctx->total); | ||
531 | #endif | ||
532 | |||
533 | pg_start_ctx->peers_left--; | ||
534 | |||
535 | if (NULL == pg_start_ctx->peer_start_meter) | ||
536 | { | ||
537 | /* Cancelled Ctrl-C or error */ | ||
538 | return; | ||
539 | } | ||
540 | if (GNUNET_YES == update_meter (pg_start_ctx->peer_start_meter)) | ||
541 | { | ||
542 | #if VERBOSE | ||
543 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
544 | "All %d daemons started, now connecting peers!\n", | ||
545 | pg_start_ctx->total); | ||
546 | #endif | ||
547 | GNUNET_assert (pg_start_ctx->die_task != GNUNET_SCHEDULER_NO_TASK); | ||
548 | GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task); | ||
549 | |||
550 | pg_start_ctx->expected_connections = UINT_MAX; | ||
551 | // FIXME: why whould peers_left be != 0?? Or pg NULL? | ||
552 | if ((pg_start_ctx->pg != NULL) && (pg_start_ctx->peers_left == 0)) | ||
553 | { | ||
554 | pg_start_ctx->connect_start_time = GNUNET_TIME_absolute_get (); | ||
555 | pg_start_ctx->expected_connections = | ||
556 | GNUNET_TESTING_connect_topology (pg_start_ctx->pg, | ||
557 | pg_start_ctx->connect_topology, | ||
558 | pg_start_ctx->connect_topology_option, | ||
559 | pg_start_ctx->connect_topology_option_modifier, | ||
560 | pg_start_ctx->connect_timeout, | ||
561 | pg_start_ctx->connect_attempts, NULL, | ||
562 | NULL); | ||
563 | |||
564 | pg_start_ctx->connect_meter = | ||
565 | create_meter (pg_start_ctx->expected_connections, "Peer connection ", | ||
566 | pg_start_ctx->verbose); | ||
567 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n", | ||
568 | pg_start_ctx->expected_connections); | ||
569 | } | ||
570 | |||
571 | if (pg_start_ctx->expected_connections == 0) | ||
572 | { | ||
573 | GNUNET_free_non_null (pg_start_ctx->fail_reason); | ||
574 | pg_start_ctx->fail_reason = | ||
575 | GNUNET_strdup ("from connect topology (bad return)"); | ||
576 | pg_start_ctx->die_task = | ||
577 | GNUNET_SCHEDULER_add_now (&end_badly, pg_start_ctx); | ||
578 | return; | ||
579 | } | ||
580 | |||
581 | GNUNET_free_non_null (pg_start_ctx->fail_reason); | ||
582 | pg_start_ctx->fail_reason = | ||
583 | GNUNET_strdup ("from connect topology (timeout)"); | ||
584 | pg_start_ctx->die_task = | ||
585 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining | ||
586 | (pg_start_ctx->timeout), &end_badly, | ||
587 | pg_start_ctx); | ||
588 | } | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * Callback indicating that the hostkey was created for a peer. | ||
593 | * | ||
594 | * @param cls NULL | ||
595 | * @param id the peer identity | ||
596 | * @param d the daemon handle (pretty useless at this point, remove?) | ||
597 | * @param emsg non-null on failure | ||
598 | */ | ||
599 | static void | ||
600 | internal_hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id, | ||
601 | struct GNUNET_TESTING_Daemon *d, const char *emsg) | ||
602 | { | ||
603 | struct PeerGroupStartupContext *pg_start_ctx = cls; | ||
604 | unsigned int create_expected_connections; | ||
605 | |||
606 | if (emsg != NULL) | ||
607 | { | ||
608 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
609 | "Hostkey callback received error: %s\n", emsg); | ||
610 | } | ||
611 | |||
612 | #if VERBOSE > 1 | ||
613 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
614 | "Hostkey (%d/%d) created for peer `%s'\n", | ||
615 | pg_start_ctx->total - pg_start_ctx->peers_left + 1, | ||
616 | pg_start_ctx->total, GNUNET_i2s (id)); | ||
617 | #endif | ||
618 | |||
619 | pg_start_ctx->peers_left--; | ||
620 | if (GNUNET_YES == update_meter (pg_start_ctx->hostkey_meter)) | ||
621 | { | ||
622 | GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task); | ||
623 | GNUNET_free_non_null (pg_start_ctx->fail_reason); | ||
624 | /* Set up task in case topology creation doesn't finish | ||
625 | * within a reasonable amount of time */ | ||
626 | pg_start_ctx->fail_reason = GNUNET_strdup ("from create_topology"); | ||
627 | pg_start_ctx->die_task = | ||
628 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining | ||
629 | (pg_start_ctx->timeout), &end_badly, | ||
630 | pg_start_ctx); | ||
631 | pg_start_ctx->peers_left = pg_start_ctx->total; /* Reset counter */ | ||
632 | create_expected_connections = | ||
633 | GNUNET_TESTING_create_topology (pg_start_ctx->pg, | ||
634 | pg_start_ctx->topology, | ||
635 | pg_start_ctx->restrict_topology, | ||
636 | pg_start_ctx->restrict_transports); | ||
637 | if (create_expected_connections > 0) | ||
638 | { | ||
639 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
640 | "Topology set up, have %u expected connections, now starting peers!\n", | ||
641 | create_expected_connections); | ||
642 | GNUNET_TESTING_daemons_continue_startup (pg_start_ctx->pg); | ||
643 | } | ||
644 | else | ||
645 | { | ||
646 | GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task); | ||
647 | GNUNET_free_non_null (pg_start_ctx->fail_reason); | ||
648 | pg_start_ctx->fail_reason = | ||
649 | GNUNET_strdup ("from create topology (bad return)"); | ||
650 | pg_start_ctx->die_task = | ||
651 | GNUNET_SCHEDULER_add_now (&end_badly, pg_start_ctx); | ||
652 | return; | ||
653 | } | ||
654 | |||
655 | GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task); | ||
656 | GNUNET_free_non_null (pg_start_ctx->fail_reason); | ||
657 | pg_start_ctx->fail_reason = | ||
658 | GNUNET_strdup ("from continue startup (timeout)"); | ||
659 | pg_start_ctx->die_task = | ||
660 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining | ||
661 | (pg_start_ctx->timeout), &end_badly, | ||
662 | pg_start_ctx); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | |||
667 | /** | ||
668 | * Prototype of a callback function indicating that two peers | ||
669 | * are currently connected. | ||
670 | * | ||
671 | * @param cls closure | ||
672 | * @param first peer id for first daemon | ||
673 | * @param second peer id for the second daemon | ||
674 | * @param emsg error message (NULL on success) | ||
675 | */ | ||
676 | void | ||
677 | write_topology_cb (void *cls, const struct GNUNET_PeerIdentity *first, | ||
678 | const struct GNUNET_PeerIdentity *second, const char *emsg) | ||
679 | { | ||
680 | struct TopologyOutputContext *topo_ctx; | ||
681 | int temp; | ||
682 | char *temp_str; | ||
683 | char *temp_pid2; | ||
684 | |||
685 | topo_ctx = (struct TopologyOutputContext *) cls; | ||
686 | GNUNET_assert (topo_ctx->file != NULL); | ||
687 | if ((emsg == NULL) && (first != NULL) && (second != NULL)) | ||
688 | { | ||
689 | GNUNET_assert (first != NULL); | ||
690 | GNUNET_assert (second != NULL); | ||
691 | temp_pid2 = GNUNET_strdup (GNUNET_i2s (second)); | ||
692 | temp = | ||
693 | GNUNET_asprintf (&temp_str, "\t\"%s\" -- \"%s\"\n", GNUNET_i2s (first), | ||
694 | temp_pid2); | ||
695 | GNUNET_free (temp_pid2); | ||
696 | GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp); | ||
697 | } | ||
698 | else if ((emsg == NULL) && (first == NULL) && (second == NULL)) | ||
699 | { | ||
700 | temp = GNUNET_asprintf (&temp_str, "}\n"); | ||
701 | GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp); | ||
702 | GNUNET_DISK_file_close (topo_ctx->file); | ||
703 | topo_ctx->notify_cb (topo_ctx->notify_cb_cls, NULL); | ||
704 | GNUNET_free (topo_ctx); | ||
705 | } | ||
706 | else | ||
707 | { | ||
708 | temp = GNUNET_asprintf (&temp_str, "}\n"); | ||
709 | GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp); | ||
710 | GNUNET_DISK_file_close (topo_ctx->file); | ||
711 | topo_ctx->notify_cb (topo_ctx->notify_cb_cls, emsg); | ||
712 | GNUNET_free (topo_ctx); | ||
713 | } | ||
714 | } | ||
715 | |||
716 | /** | ||
717 | * Print current topology to a graphviz readable file. | ||
718 | * | ||
719 | * @param pg a currently running peergroup to print to file | ||
720 | * @param output_filename the file to write the topology to | ||
721 | * @param notify_cb callback to call upon completion or failure | ||
722 | * @param notify_cb_cls closure for notify_cb | ||
723 | * | ||
724 | */ | ||
725 | void | ||
726 | GNUNET_TESTING_peergroup_topology_to_file (struct GNUNET_TESTING_PeerGroup *pg, | ||
727 | const char *output_filename, | ||
728 | GNUNET_TESTING_NotifyCompletion | ||
729 | notify_cb, void *notify_cb_cls) | ||
730 | { | ||
731 | struct TopologyOutputContext *topo_ctx; | ||
732 | int temp; | ||
733 | char *temp_str; | ||
734 | |||
735 | topo_ctx = GNUNET_malloc (sizeof (struct TopologyOutputContext)); | ||
736 | |||
737 | topo_ctx->notify_cb = notify_cb; | ||
738 | topo_ctx->notify_cb_cls = notify_cb_cls; | ||
739 | topo_ctx->file = | ||
740 | GNUNET_DISK_file_open (output_filename, | ||
741 | GNUNET_DISK_OPEN_READWRITE | | ||
742 | GNUNET_DISK_OPEN_CREATE, | ||
743 | GNUNET_DISK_PERM_USER_READ | | ||
744 | GNUNET_DISK_PERM_USER_WRITE); | ||
745 | if (topo_ctx->file == NULL) | ||
746 | { | ||
747 | notify_cb (notify_cb_cls, "Failed to open output file!"); | ||
748 | GNUNET_free (topo_ctx); | ||
749 | return; | ||
750 | } | ||
751 | |||
752 | temp = GNUNET_asprintf (&temp_str, "strict graph G {\n"); | ||
753 | if (temp > 0) | ||
754 | GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp); | ||
755 | GNUNET_free_non_null (temp_str); | ||
756 | GNUNET_TESTING_get_topology (pg, &write_topology_cb, topo_ctx); | ||
757 | } | ||
758 | |||
759 | /** | ||
760 | * Start a peer group with a given number of peers. Notify | ||
761 | * on completion of peer startup and connection based on given | ||
762 | * topological constraints. Optionally notify on each | ||
763 | * established connection. | ||
764 | * | ||
765 | * @param cfg configuration template to use | ||
766 | * @param total number of daemons to start | ||
767 | * @param timeout total time allowed for peers to start | ||
768 | * @param connect_cb function to call each time two daemons are connected | ||
769 | * @param peergroup_cb function to call once all peers are up and connected | ||
770 | * @param peergroup_cls closure for peergroup callbacks | ||
771 | * @param hostnames linked list of host structs to use to start peers on | ||
772 | * (NULL to run on localhost only) | ||
773 | * | ||
774 | * @return NULL on error, otherwise handle to control peer group | ||
775 | */ | ||
776 | struct GNUNET_TESTING_PeerGroup * | ||
777 | GNUNET_TESTING_peergroup_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
778 | unsigned int total, | ||
779 | struct GNUNET_TIME_Relative timeout, | ||
780 | GNUNET_TESTING_NotifyConnection connect_cb, | ||
781 | GNUNET_TESTING_NotifyCompletion peergroup_cb, | ||
782 | void *peergroup_cls, | ||
783 | const struct GNUNET_TESTING_Host *hostnames) | ||
784 | { | ||
785 | struct PeerGroupStartupContext *pg_start_ctx; | ||
786 | char *temp_str; | ||
787 | int temp; | ||
788 | struct GNUNET_TIME_Relative rtimeout; | ||
789 | |||
790 | GNUNET_assert (total > 0); | ||
791 | GNUNET_assert (cfg != NULL); | ||
792 | |||
793 | pg_start_ctx = GNUNET_malloc (sizeof (struct PeerGroupStartupContext)); | ||
794 | |||
795 | if (GNUNET_OK != | ||
796 | GNUNET_CONFIGURATION_get_value_number (cfg, "testing_old", "connect_attempts", | ||
797 | &pg_start_ctx->connect_attempts)) | ||
798 | { | ||
799 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", | ||
800 | "testing_old", "connect_attempts"); | ||
801 | GNUNET_free (pg_start_ctx); | ||
802 | return NULL; | ||
803 | } | ||
804 | |||
805 | if (GNUNET_OK != | ||
806 | GNUNET_CONFIGURATION_get_value_time (cfg, "testing_old", "CONNECT_TIMEOUT", | ||
807 | &pg_start_ctx->connect_timeout)) | ||
808 | { | ||
809 | pg_start_ctx->connect_timeout = DEFAULT_CONNECT_TIMEOUT; | ||
810 | } | ||
811 | |||
812 | if (GNUNET_OK != | ||
813 | GNUNET_CONFIGURATION_get_value_number (cfg, "testing_old", | ||
814 | "max_outstanding_connections", | ||
815 | &pg_start_ctx->max_concurrent_connections)) | ||
816 | { | ||
817 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", | ||
818 | "testing_old", "max_outstanding_connections"); | ||
819 | GNUNET_free (pg_start_ctx); | ||
820 | return NULL; | ||
821 | } | ||
822 | |||
823 | if (GNUNET_OK != | ||
824 | GNUNET_CONFIGURATION_get_value_number (cfg, "testing_old", | ||
825 | "max_concurrent_ssh", | ||
826 | &pg_start_ctx->max_concurrent_ssh)) | ||
827 | { | ||
828 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", | ||
829 | "testing_old", "max_concurrent_ssh"); | ||
830 | GNUNET_free (pg_start_ctx); | ||
831 | return NULL; | ||
832 | } | ||
833 | |||
834 | if (GNUNET_SYSERR == | ||
835 | (pg_start_ctx->verbose = | ||
836 | GNUNET_CONFIGURATION_get_value_yesno (cfg, "testing_old", | ||
837 | "use_progressbars"))) | ||
838 | { | ||
839 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", | ||
840 | "testing_old", "use_progressbars"); | ||
841 | GNUNET_free (pg_start_ctx); | ||
842 | return NULL; | ||
843 | } | ||
844 | |||
845 | if (GNUNET_OK != | ||
846 | GNUNET_CONFIGURATION_get_value_time (cfg, "testing_old", "PEERGROUP_TIMEOUT", | ||
847 | &rtimeout)) | ||
848 | { | ||
849 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n", | ||
850 | "testing_old", "PEERGROUP_TIMEOUT"); | ||
851 | GNUNET_free (pg_start_ctx); | ||
852 | return NULL; | ||
853 | } | ||
854 | pg_start_ctx->timeout = GNUNET_TIME_relative_to_absolute (rtimeout); | ||
855 | |||
856 | |||
857 | /* Read topology related options from the configuration file */ | ||
858 | temp_str = NULL; | ||
859 | if ((GNUNET_YES == | ||
860 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", "topology", | ||
861 | &temp_str)) && | ||
862 | (GNUNET_NO == | ||
863 | GNUNET_TESTING_topology_get (&pg_start_ctx->topology, temp_str))) | ||
864 | { | ||
865 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
866 | "Invalid topology `%s' given for section %s option %s\n", | ||
867 | temp_str, "TESTING_old", "TOPOLOGY"); | ||
868 | pg_start_ctx->topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Defaults to NONE, so set better default here */ | ||
869 | } | ||
870 | GNUNET_free_non_null (temp_str); | ||
871 | |||
872 | if (GNUNET_YES == | ||
873 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", | ||
874 | "topology_output_file", &temp_str)) | ||
875 | { | ||
876 | pg_start_ctx->topology_output_file = | ||
877 | GNUNET_DISK_file_open (temp_str, | ||
878 | GNUNET_DISK_OPEN_READWRITE | | ||
879 | GNUNET_DISK_OPEN_CREATE, | ||
880 | GNUNET_DISK_PERM_USER_READ | | ||
881 | GNUNET_DISK_PERM_USER_WRITE); | ||
882 | if (pg_start_ctx->topology_output_file != NULL) | ||
883 | { | ||
884 | GNUNET_free (temp_str); | ||
885 | temp = GNUNET_asprintf (&temp_str, "strict graph G {\n"); | ||
886 | if (temp > 0) | ||
887 | GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str, | ||
888 | temp); | ||
889 | } | ||
890 | GNUNET_free (temp_str); | ||
891 | } | ||
892 | |||
893 | if (GNUNET_OK != | ||
894 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", "percentage", | ||
895 | &temp_str)) | ||
896 | pg_start_ctx->topology_percentage = 0.5; | ||
897 | else | ||
898 | { | ||
899 | pg_start_ctx->topology_percentage = atof (temp_str); | ||
900 | GNUNET_free (temp_str); | ||
901 | } | ||
902 | |||
903 | if (GNUNET_OK != | ||
904 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", "probability", | ||
905 | &temp_str)) | ||
906 | pg_start_ctx->topology_probability = 0.5; | ||
907 | else | ||
908 | { | ||
909 | pg_start_ctx->topology_probability = atof (temp_str); | ||
910 | GNUNET_free (temp_str); | ||
911 | } | ||
912 | |||
913 | if ((GNUNET_YES == | ||
914 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", | ||
915 | "connect_topology", &temp_str)) && | ||
916 | (GNUNET_NO == | ||
917 | GNUNET_TESTING_topology_get (&pg_start_ctx->connect_topology, temp_str))) | ||
918 | { | ||
919 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
920 | "Invalid connect topology `%s' given for section %s option %s\n", | ||
921 | temp_str, "TESTING_old", "CONNECT_TOPOLOGY"); | ||
922 | } | ||
923 | GNUNET_free_non_null (temp_str); | ||
924 | |||
925 | if ((GNUNET_YES == | ||
926 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", | ||
927 | "connect_topology_option", | ||
928 | &temp_str)) && | ||
929 | (GNUNET_NO == | ||
930 | GNUNET_TESTING_topology_option_get | ||
931 | (&pg_start_ctx->connect_topology_option, temp_str))) | ||
932 | { | ||
933 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
934 | "Invalid connect topology option `%s' given for section %s option %s\n", | ||
935 | temp_str, "TESTING_old", "CONNECT_TOPOLOGY_OPTION"); | ||
936 | pg_start_ctx->connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Defaults to NONE, set to ALL */ | ||
937 | } | ||
938 | GNUNET_free_non_null (temp_str); | ||
939 | |||
940 | if (GNUNET_YES == | ||
941 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", | ||
942 | "connect_topology_option_modifier", | ||
943 | &temp_str)) | ||
944 | { | ||
945 | if (SSCANF | ||
946 | (temp_str, "%lf", &pg_start_ctx->connect_topology_option_modifier) != 1) | ||
947 | { | ||
948 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
949 | _ | ||
950 | ("Invalid value `%s' for option `%s' in section `%s': expected float\n"), | ||
951 | temp_str, "connect_topology_option_modifier", "TESTING_old"); | ||
952 | GNUNET_free (temp_str); | ||
953 | GNUNET_free (pg_start_ctx); | ||
954 | return NULL; | ||
955 | } | ||
956 | GNUNET_free (temp_str); | ||
957 | } | ||
958 | |||
959 | if (GNUNET_YES != | ||
960 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", | ||
961 | "blacklist_transports", | ||
962 | &pg_start_ctx->restrict_transports)) | ||
963 | pg_start_ctx->restrict_transports = NULL; | ||
964 | |||
965 | pg_start_ctx->restrict_topology = GNUNET_TESTING_TOPOLOGY_NONE; | ||
966 | if ((GNUNET_YES == | ||
967 | GNUNET_CONFIGURATION_get_value_string (cfg, "testing_old", | ||
968 | "blacklist_topology", &temp_str)) | ||
969 | && (GNUNET_NO == | ||
970 | GNUNET_TESTING_topology_get (&pg_start_ctx->restrict_topology, | ||
971 | temp_str))) | ||
972 | { | ||
973 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
974 | "Invalid topology `%s' given for section %s option %s\n", | ||
975 | temp_str, "TESTING_OLD", "BLACKLIST_TOPOLOGY"); | ||
976 | } | ||
977 | |||
978 | GNUNET_free_non_null (temp_str); | ||
979 | |||
980 | pg_start_ctx->cfg = cfg; | ||
981 | pg_start_ctx->total = total; | ||
982 | pg_start_ctx->peers_left = total; | ||
983 | pg_start_ctx->connect_cb = connect_cb; | ||
984 | pg_start_ctx->peergroup_cb = peergroup_cb; | ||
985 | pg_start_ctx->cls = peergroup_cls; | ||
986 | pg_start_ctx->hostnames = hostnames; | ||
987 | pg_start_ctx->hostkey_meter = | ||
988 | create_meter (pg_start_ctx->peers_left, "Hostkeys created ", | ||
989 | pg_start_ctx->verbose); | ||
990 | pg_start_ctx->peer_start_meter = | ||
991 | create_meter (pg_start_ctx->peers_left, "Peers started ", | ||
992 | pg_start_ctx->verbose); | ||
993 | /* Make compilers happy */ | ||
994 | reset_meter (pg_start_ctx->peer_start_meter); | ||
995 | pg_start_ctx->fail_reason = | ||
996 | GNUNET_strdup | ||
997 | ("didn't generate all hostkeys within allowed startup time!"); | ||
998 | pg_start_ctx->die_task = | ||
999 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining | ||
1000 | (pg_start_ctx->timeout), &end_badly, | ||
1001 | pg_start_ctx); | ||
1002 | |||
1003 | pg_start_ctx->pg = | ||
1004 | GNUNET_TESTING_daemons_start (pg_start_ctx->cfg, pg_start_ctx->peers_left, | ||
1005 | pg_start_ctx->max_concurrent_connections, | ||
1006 | pg_start_ctx->max_concurrent_ssh, | ||
1007 | GNUNET_TIME_absolute_get_remaining | ||
1008 | (pg_start_ctx->timeout), | ||
1009 | &internal_hostkey_callback, pg_start_ctx, | ||
1010 | &internal_peers_started_callback, | ||
1011 | pg_start_ctx, &internal_topology_callback, | ||
1012 | pg_start_ctx, pg_start_ctx->hostnames); | ||
1013 | |||
1014 | return pg_start_ctx->pg; | ||
1015 | } | ||
1016 | |||
1017 | /* end of testing_peergroup.c */ | ||