aboutsummaryrefslogtreecommitdiff
path: root/src/testing/testing.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/testing.c')
-rw-r--r--src/testing/testing.c529
1 files changed, 527 insertions, 2 deletions
diff --git a/src/testing/testing.c b/src/testing/testing.c
index 598015340..e425171bb 100644
--- a/src/testing/testing.c
+++ b/src/testing/testing.c
@@ -30,8 +30,37 @@
30 */ 30 */
31#include "platform.h" 31#include "platform.h"
32#include "gnunet_arm_service.h" 32#include "gnunet_arm_service.h"
33#include "gnunet_constants.h"
33#include "gnunet_testing_lib.h" 34#include "gnunet_testing_lib.h"
34 35
36#define DEBUG_TESTING GNUNET_YES
37
38/**
39 * How long do we wait after starting gnunet-service-arm
40 * for the core service to be alive?
41 */
42#define GNUNET_ARM_START_WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
43
44/**
45 * How many times are we willing to try to
46 * wait for "scp" or "gnunet-service-arm" to
47 * complete (waitpid) before giving up?
48 */
49#define MAX_EXEC_WAIT_RUNS 50
50
51/**
52 * Phases of starting GNUnet on a system.
53 */
54enum StartPhase
55 {
56 SP_COPYING,
57 SP_COPIED,
58 SP_START_ARMING,
59 SP_START_CORE,
60 SP_START_DONE
61 };
62
63
35/** 64/**
36 * Handle for a GNUnet daemon (technically a set of 65 * Handle for a GNUnet daemon (technically a set of
37 * daemons; the handle is really for the master ARM 66 * daemons; the handle is really for the master ARM
@@ -39,10 +68,304 @@
39 */ 68 */
40struct GNUNET_TESTING_Daemon 69struct GNUNET_TESTING_Daemon
41{ 70{
71 /**
72 * Our scheduler.
73 */
74 struct GNUNET_SCHEDULER_Handle *sched;
75
76 /**
77 * Our configuration.
78 */
79 const struct GNUNET_CONFIGURATION_Handle *cfg;
80
81 /**
82 * Host to run GNUnet on.
83 */
84 char *hostname;
85
86 /**
87 * Username we are using.
88 */
89 char *username;
90
91 /**
92 * Name of the configuration file
93 */
94 char *cfgfile;
95
96 /**
97 * Function to call when the peer is running.
98 */
99 GNUNET_TESTING_NotifyDaemonRunning cb;
100
101 /**
102 * Closure for cb.
103 */
104 void *cb_cls;
105
106 /**
107 * Arguments from "daemon_stop" call.
108 */
109 GNUNET_TESTING_NotifyCompletion dead_cb;
110
111 /**
112 * Closure for 'dead_cb'.
113 */
114 void *dead_cb_cls;
115
116 /**
117 * Flag to indicate that we've already been asked
118 * to terminate (but could not because some action
119 * was still pending).
120 */
121 int dead;
122
123 /**
124 * PID of the process that we started last.
125 */
126 pid_t pid;
127
128 /**
129 * How many iterations have we been waiting for
130 * the started process to complete?
131 */
132 unsigned int wait_runs;
133
134 /**
135 * In which phase are we during the start of
136 * this process?
137 */
138 enum StartPhase phase;
139
140 /**
141 * ID of the current task.
142 */
143 GNUNET_SCHEDULER_Task task;
144
42}; 145};
43 146
44 147
45/** 148/**
149 * Function called after GNUNET_CORE_connect has succeeded
150 * (or failed for good). Note that the private key of the
151 * peer is intentionally not exposed here; if you need it,
152 * your process should try to read the private key file
153 * directly (which should work if you are authorized...).
154 *
155 * @param cls closure
156 * @param server handle to the server, NULL if we failed
157 * @param my_identity ID of this peer, NULL if we failed
158 * @param publicKey public key of this peer, NULL if we failed
159 */
160static void
161testing_init (void *cls,
162 struct GNUNET_CORE_Handle * server,
163 const struct GNUNET_PeerIdentity *
164 my_identity,
165 const struct
166 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
167 publicKey)
168{
169 struct GNUNET_TESTING_Daemon *d = cls;
170 GNUNET_TESTING_NotifyDaemonRunning cb;
171
172 d->phsae = SP_START_DONE;
173 cb = d->cb;
174 d->cb = NULL;
175 if (server == NULL)
176 {
177 cb (d->cb_cls, NULL, d->cfg, d,
178 _("Failed to connect to core service\n"));
179 if (GNUNET_YES == d->dead)
180 GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
181 return;
182 }
183#if DEBUG_TESTING
184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
185 "Successfully started peer `%4s'.\n",
186 GNUNET_i2s(my_identity));
187#endif
188 if (GNUNET_YES == d->dead)
189 GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
190 else
191 cb (d->cb_cls, my_identity, d->cfg, d, NULL);
192}
193
194
195/**
196 * Finite-state machine for starting GNUnet.
197 *
198 * @param cls our "struct GNUNET_TESTING_Daemon"
199 * @param tc unused
200 */
201static void
202start_fsm (void *cls,
203 const struct GNUNET_SCHEDULER_TaskContext *tc)
204{
205 struct GNUNET_TESTING_Daemon * d = cls;
206 GNUNET_TESTING_NotifyDaemonRunning cb;
207 enum GNUNET_OS_ProcessStatusType type;
208 unsigned long code;
209 char *dst;
210
211 d->task = GNUNET_SCHEDULER_NO_TASK;
212 switch (d->phase)
213 {
214 case SP_COPYING:
215 /* confirm copying complete */
216 if (GNUNET_OK !=
217 GNUNET_OS_process_status (d->pid,
218 &type,
219 &code))
220 {
221 d->wait_runs++;
222 if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
223 {
224 cb = d->cb;
225 d->cb = NULL;
226 cb (d->cb_cls,
227 NULL,
228 d->cfg,
229 d,
230 _("`scp' does not seem to terminate.\n"));
231 return;
232 }
233 /* wait some more */
234 d->task
235 = GNUNET_SCHEDULER_add_delayed (sched,
236 GNUNET_NO,
237 GNUNET_SCHEDULER_PRIORITY_KEEP,
238 GNUNET_SCHEDULER_NO_TASK,
239 GNUNET_CONSTANTS_EXEC_WAIT,
240 &start_fsm,
241 d);
242 return;
243 }
244 if ( (type != GNUNET_OS_PROCESS_EXITED) ||
245 (code != 0) )
246 {
247 cb = d->cb;
248 d->cb = NULL;
249 cb (d->cb_cls,
250 NULL,
251 d->cfg,
252 d,
253 _("`scp' did not complete cleanly.\n"));
254 return;
255 }
256#if DEBUG_TESTING
257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
258 "Successfully copied configuration file.\n");
259#endif
260 d->phase = SP_COPIED;
261 /* fall-through */
262 case SP_COPIED:
263 /* start GNUnet on remote host */
264 if (NULL == hostname)
265 {
266 d->pid = GNUNET_OS_start_process ("gnunet-service-arm",
267 "gnunet-service-arm",
268 "-c",
269 d->cfgfile,
270 "-d",
271 NULL);
272 }
273 else
274 {
275 if (username != NULL)
276 GNUNET_asprintf (&dst,
277 "%s@%s",
278 username,
279 hostname);
280 else
281 dst = GNUNET_strdup (hostname);
282 d->pid = GNUNET_OS_start_process ("ssh",
283 "ssh",
284 dst,
285 "gnunet-service-arm",
286 "-c",
287 d->cfgfile,
288 "-d",
289 NULL);
290 GNUNET_free (dst);
291 }
292 if (-1 == d->pid)
293 {
294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
295 _("Could not start `%s' process to start GNUnet.\n"),
296 (NULL == hostname) ? "gnunet-service-arm" : "ssh");
297 cb = d->cb;
298 d->cb = NULL;
299 cb (d->cb_cls,
300 NULL,
301 d->cfg,
302 d,
303 (NULL == d->hostname)
304 ? _("Failed to start `gnunet-service-arm' process.\n")
305 : _("Failed to start `ssh' process.\n"));
306 }
307 d->phase = SP_START_ARMING;
308 d->wait_runs = 0;
309 break;
310 case SP_START_ARMING:
311 if (GNUNET_OK !=
312 GNUNET_OS_process_status (d->pid,
313 &type,
314 &code))
315 {
316 d->wait_runs++;
317 if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
318 {
319 cb = d->cb;
320 d->cb = NULL;
321 cb (d->cb_cls,
322 NULL,
323 d->cfg,
324 d,
325 (NULL == d->hostname)
326 ? _("`gnunet-service-arm' does not seem to terminate.\n")
327 : _("`ssh' does not seem to terminate.\n"));
328 return;
329 }
330 /* wait some more */
331 d->task
332 = GNUNET_SCHEDULER_add_delayed (sched,
333 GNUNET_NO,
334 GNUNET_SCHEDULER_PRIORITY_KEEP,
335 GNUNET_SCHEDULER_NO_TASK,
336 GNUNET_CONSTANTS_EXEC_WAIT,
337 &start_fsm,
338 d);
339 return;
340 }
341#if DEBUG_TESTING
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "Successfully started `%s'.\n",
344 "gnunet-service-arm");
345#endif
346 d->phase = SP_START_CORE;
347 GNUNET_CORE_connect (d->sched,
348 d->cfg,
349 timeout,
350 d,
351 &testing_init,
352 NULL, NULL, NULL,
353 NULL, GNUNET_NO,
354 NULL, GNUNET_NO,
355 no_handlers);
356 break;
357 case SP_START_CORE:
358 GNUNET_break (0);
359 break;
360 case SP_START_DONE:
361 GNUNET_break (0);
362 break;
363 }
364 return ret;
365}
366
367
368/**
46 * Starts a GNUnet daemon. GNUnet must be installed on the target 369 * Starts a GNUnet daemon. GNUnet must be installed on the target
47 * system and available in the PATH. The machine must furthermore be 370 * system and available in the PATH. The machine must furthermore be
48 * reachable via "ssh" (unless the hostname is "NULL") without the 371 * reachable via "ssh" (unless the hostname is "NULL") without the
@@ -58,12 +381,111 @@ struct GNUNET_TESTING_Daemon
58 */ 381 */
59struct GNUNET_TESTING_Daemon * 382struct GNUNET_TESTING_Daemon *
60GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, 383GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
61 struct GNUNET_CONFIGURATION_Handle *cfg, 384 const struct GNUNET_CONFIGURATION_Handle *cfg,
62 const char *hostname, 385 const char *hostname,
63 GNUNET_TESTING_NotifyDaemonRunning cb, 386 GNUNET_TESTING_NotifyDaemonRunning cb,
64 void *cb_cls) 387 void *cb_cls)
65{ 388{
66 return NULL; 389 static struct GNUNET_CORE_MessageHandler no_handlers[] =
390 { NULL, 0, 0 };
391 struct GNUNET_TESTING_Daemon * ret;
392 char *arg;
393 char *username;
394 int unused;
395
396 ret = GNUNET_malloc (sizeof(struct GNUNET_TESTING_Daemon));
397 ret->sched = sched;
398 ret->cfg = cfg;
399 ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
400 ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config");
401 if (NULL == ret->cfgfile)
402 {
403 GNUNET_free_non_null (ret->hostname);
404 GNUNET_free (ret);
405 return NULL;
406 }
407 ret->cb = cb;
408 ret->cb_cls = cb_cls;
409 /* 1) write configuration to temporary file */
410 if (GNUNET_OK !=
411 GNUNET_CONFIGURATION_write (cfg,
412 ret->cfgfile))
413 {
414 if (0 != UNLINK (ret->cfgfile))
415 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
416 "unlink",
417 ret->cfgfile);
418 GNUNET_free_non_null (ret->hostname);
419 GNUNET_free (ret->cfgfile);
420 GNUNET_free (ret);
421 return NULL;
422 }
423 if (GNUNET_OK !=
424 GNUNET_CONFIGURATION_get_value_string (cfg,
425 "TESTING",
426 "USERNAME",
427 &username))
428 {
429 if (NULL != getenv ("USER"))
430 username = GNUNET_strdup (getenv("USER"));
431 else
432 username = NULL;
433 }
434 ret->username = username;
435
436 /* 2) copy file to remote host */
437 if (NULL != hostname)
438 {
439 ret->phase = SP_COPYING;
440 if (NULL != username)
441 GNUNET_asprintf (&arg,
442 "%s@%s:%s",
443 username,
444 hostname,
445 ret->cfgfile);
446 else
447 GNUNET_asprintf (&arg,
448 "%s:%s",
449 hostname,
450 ret->cfgfile);
451 ret->pid = GNUNET_OS_start_process ("scp",
452 "scp",
453 ret->cfgfile,
454 arg,
455 NULL);
456 if (-1 == ret->pid)
457 {
458 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
459 _("Could not start `%s' process to copy configuration file.\n"),
460 "scp");
461 if (0 != UNLINK (ret->cfgfile))
462 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
463 "unlink",
464 ret->cfgfile);
465 GNUNET_free_non_null (ret->hostname);
466 GNUNET_free_non_null (ret->username);
467 GNUNET_free (ret->cfgfile);
468 GNUNET_free (ret);
469 return NULL;
470 }
471 ret->task
472 = GNUNET_SCHEDULER_add_delayed (sched,
473 GNUNET_NO,
474 GNUNET_SCHEDULER_PRIORITY_KEEP,
475 GNUNET_SCHEDULER_NO_TASK,
476 GNUNET_CONSTANTS_EXEC_WAIT,
477 &start_fsm,
478 ret);
479 GNUNET_free (arg);
480 return;
481 }
482 ret->phase = SP_COPIED;
483 ret->task
484 = GNUNET_SCHEDULER_add_continuation (sched,
485 GNUNET_NO,
486 &start_fsm,
487 ret,
488 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
67} 489}
68 490
69 491
@@ -78,6 +500,108 @@ void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
78 GNUNET_TESTING_NotifyCompletion cb, 500 GNUNET_TESTING_NotifyCompletion cb,
79 void * cb_cls) 501 void * cb_cls)
80{ 502{
503 if (NULL != d->cb)
504 {
505 d->dead = GNUNET_YES;
506 d->dead_cb = cb;
507 d->dead_cb_cls = cb_cls;
508 return;
509 }
510
511 /* FIXME: shutdown processes! */
512 char *cmd;
513 int length;
514 unsigned int is_local = 0;
515 int unused;
516 FILE *output;
517 pid_t pid;
518
519 if (strcmp (tokill->hostname, "localhost") == 0)
520 {
521 is_local = 1;
522 }
523
524 if (is_local)
525 {
526 length = snprintf (NULL, 0, "cat %s", tokill->pid);
527 cmd = GNUNET_malloc (length + 1);
528 snprintf (cmd, length + 1, "cat %s", tokill->pid);
529 }
530 else
531 {
532 length =
533 snprintf (NULL, 0, "ssh %s@%s cat %s", tokill->username,
534 tokill->hostname, tokill->pid);
535 cmd = GNUNET_malloc (length + 1);
536 snprintf (cmd, length + 1, "ssh %s@%s cat %s", tokill->username,
537 tokill->hostname, tokill->pid);
538 }
539#if VERBOSE
540 fprintf (stderr, _("exec command is : %s \n"), cmd);
541#endif
542
543 output = popen (cmd, "r");
544 GNUNET_free (cmd);
545 if (fscanf (output, "%d", &pid) == 1)
546 {
547#if VERBOSE
548 fprintf (stderr, _("Got pid %d\n"), pid);
549#endif
550 }
551 else
552 {
553 return -1;
554 }
555
556 if (is_local)
557 {
558 length = snprintf (NULL, 0, "kill %d", pid);
559 cmd = GNUNET_malloc (length + 1);
560 snprintf (cmd, length + 1, "kill %d", pid);
561 }
562 else
563 {
564 length =
565 snprintf (NULL, 0, "ssh %s@%s kill %d", tokill->username,
566 tokill->hostname, pid);
567 cmd = GNUNET_malloc (length + 1);
568 snprintf (cmd, length + 1, "ssh %s@%s kill %d",
569 tokill->username, tokill->hostname, pid);
570
571 }
572#if VERBOSE
573 fprintf (stderr, _("exec command is : %s \n"), cmd);
574#endif
575
576 unused = system (cmd);
577
578 GNUNET_free (cmd);
579
580 /* state clean up and notifications */
581 if (0 != UNLINK (d->cfgfile))
582 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
583 "unlink",
584 d->cfgfile);
585 if (d->hostname != NULL)
586 {
587 GNUNET_asprintf (&cmd,
588 "ssh %s@%s rm %s &",
589 d->username,
590 d->hostname,
591 d->cfgfile);
592#if DEBUG_TESTING
593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
594 _("exec command is: `%s'\n"),
595 cmd);
596#endif
597 unused = system (cmd);
598 GNUNET_free (cmd);
599 }
600 GNUNET_free (d->cfgfile);
601 GNUNET_free_non_null (d->hostname);
602 GNUNET_free_non_null (d->username);
603 GNUNET_free (d);
604 cb (cb_cls, NULL);
81} 605}
82 606
83 607
@@ -94,6 +618,7 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d,
94 GNUNET_TESTING_NotifyCompletion cb, 618 GNUNET_TESTING_NotifyCompletion cb,
95 void * cb_cls) 619 void * cb_cls)
96{ 620{
621 cb (cb_cls, "not implemented");
97} 622}
98 623
99 624