2 This file is part of GNUnet
3 Copyright (C) 2008--2013 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file testbed/testbed_api.c
23 * @brief API for accessing the GNUnet testing service.
24 * This library is supposed to make it easier to write
25 * testcases and script large-scale benchmarks.
26 * @author Christian Grothoff
27 * @author Sree Harsha Totakura
30 #include "gnunet_testbed_service.h"
31 #include "gnunet_core_service.h"
32 #include "gnunet_constants.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_hello_lib.h"
38 #include "testbed_api.h"
39 #include "testbed_api_hosts.h"
40 #include "testbed_api_peers.h"
41 #include "testbed_api_operations.h"
42 #include "testbed_api_sd.h"
45 * Generic logging shorthand
47 #define LOG(kind, ...) GNUNET_log_from (kind, "testbed-api", __VA_ARGS__)
52 #define LOG_DEBUG(...) LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
55 * Relative time seconds shorthand
57 #define TIME_REL_SECS(sec) \
58 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec)
62 * Default server message sending retry timeout
64 #define TIMEOUT_REL TIME_REL_SECS (1)
68 * Context data for forwarded Operation
70 struct ForwardedOperationData
74 * The callback to call when reply is available
76 GNUNET_MQ_MessageCallback cc;
79 * The closure for the above callback
86 * Context data for get slave config operations
88 struct GetSlaveConfigData
91 * The id of the slave controller
98 * Context data for controller link operations
100 struct ControllerLinkData
103 * The controller link message
105 struct GNUNET_TESTBED_ControllerLinkRequest *msg;
108 * The id of the host which is hosting the controller to be linked
115 * Date context for OP_SHUTDOWN_PEERS operations
117 struct ShutdownPeersData
120 * The operation completion callback to call
122 GNUNET_TESTBED_OperationCompletionCallback cb;
125 * The closure for the above callback
132 * An entry in the stack for keeping operations which are about to expire
134 struct ExpireOperationEntry
137 * DLL head; new entries are to be inserted here
139 struct ExpireOperationEntry *next;
142 * DLL tail; entries are deleted from here
144 struct ExpireOperationEntry *prev;
147 * The operation. This will be a dangling pointer when the operation is freed
149 const struct GNUNET_TESTBED_Operation *op;
154 * DLL head for list of operations marked for expiry
156 static struct ExpireOperationEntry *exop_head;
159 * DLL tail for list of operation marked for expiry
161 static struct ExpireOperationEntry *exop_tail;
165 * Inserts an operation into the list of operations marked for expiry
167 * @param op the operation to insert
170 exop_insert (struct GNUNET_TESTBED_Operation *op)
172 struct ExpireOperationEntry *entry;
174 entry = GNUNET_new (struct ExpireOperationEntry);
176 GNUNET_CONTAINER_DLL_insert_tail (exop_head, exop_tail, entry);
181 * Checks if an operation is present in the list of operations marked for
182 * expiry. If the operation is found, it and the tail of operations after it
183 * are removed from the list.
185 * @param op the operation to check
186 * @return GNUNET_NO if the operation is not present in the list; GNUNET_YES if
187 * the operation is found in the list (the operation is then removed
188 * from the list -- calling this function again with the same
189 * paramenter will return GNUNET_NO)
192 exop_check (const struct GNUNET_TESTBED_Operation *const op)
194 struct ExpireOperationEntry *entry;
195 struct ExpireOperationEntry *entry2;
200 while (NULL != entry)
209 if (GNUNET_NO == found)
211 /* Truncate the tail */
212 while (NULL != entry)
214 entry2 = entry->next;
215 GNUNET_CONTAINER_DLL_remove (exop_head, exop_tail, entry);
224 * Context information to be used while searching for operation contexts
229 * The result of the search
231 struct OperationContext *opc;
234 * The id of the operation context we are searching for
241 * Search iterator for searching an operation context
243 * @param cls the serach context
244 * @param key current key code
245 * @param value value in the hash map
246 * @return #GNUNET_YES if we should continue to iterate,
250 opc_search_iterator (void *cls, uint32_t key, void *value)
252 struct SearchContext *sc = cls;
253 struct OperationContext *opc = value;
255 GNUNET_assert (NULL != opc);
256 GNUNET_assert (NULL == sc->opc);
257 if (opc->id != sc->id)
265 * Returns the operation context with the given id if found in the Operation
266 * context queues of the controller
268 * @param c the controller whose operation context map is searched
269 * @param id the id which has to be checked
270 * @return the matching operation context; NULL if no match found
272 static struct OperationContext *
273 find_opc (const struct GNUNET_TESTBED_Controller *c, const uint64_t id)
275 struct SearchContext sc;
279 GNUNET_assert (NULL != c->opc_map);
281 GNUNET_CONTAINER_multihashmap32_get_multiple (c->opc_map,
283 &opc_search_iterator,
291 * Inserts the given operation context into the operation context map of the
292 * given controller. Creates the operation context map if one does not exist
295 * @param c the controller
296 * @param opc the operation context to be inserted
299 GNUNET_TESTBED_insert_opc_ (struct GNUNET_TESTBED_Controller *c,
300 struct OperationContext *opc)
302 if (NULL == c->opc_map)
303 c->opc_map = GNUNET_CONTAINER_multihashmap32_create (256);
304 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap32_put (
308 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
313 * Removes the given operation context from the operation context map of the
316 * @param c the controller
317 * @param opc the operation context to remove
320 GNUNET_TESTBED_remove_opc_ (const struct GNUNET_TESTBED_Controller *c,
321 struct OperationContext *opc)
323 GNUNET_assert (NULL != c->opc_map);
324 GNUNET_assert (GNUNET_YES ==
325 GNUNET_CONTAINER_multihashmap32_remove (c->opc_map,
328 if ((0 == GNUNET_CONTAINER_multihashmap32_size (c->opc_map)) &&
329 (NULL != c->opcq_empty_cb))
330 c->opcq_empty_cb (c->opcq_empty_cls);
335 * Check #GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message is well-formed.
337 * @param cls the controller handler
338 * @param msg message received
339 * @return #GNUNET_OK if message is well-formed
342 check_add_host_confirm (void *cls,
343 const struct GNUNET_TESTBED_HostConfirmedMessage *msg)
348 msg_size = ntohs (msg->header.size) - sizeof (*msg);
351 /* We have an error message */
352 emsg = (const char *) &msg[1];
353 if ('\0' != emsg[msg_size - 1])
356 return GNUNET_SYSERR;
363 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message from
364 * controller (testbed service)
366 * @param cls the controller handler
367 * @param msg message received
370 handle_add_host_confirm (void *cls,
371 const struct GNUNET_TESTBED_HostConfirmedMessage *msg)
373 struct GNUNET_TESTBED_Controller *c = cls;
374 struct GNUNET_TESTBED_HostRegistrationHandle *rh = c->rh;
380 if (GNUNET_TESTBED_host_get_id_ (rh->host) != ntohl (msg->host_id))
382 LOG_DEBUG ("Mismatch in host id's %u, %u of host confirm msg\n",
383 GNUNET_TESTBED_host_get_id_ (rh->host),
384 ntohl (msg->host_id));
388 msg_size = ntohs (msg->header.size) - sizeof (*msg);
391 LOG_DEBUG ("Host %u successfully registered\n", ntohl (msg->host_id));
392 GNUNET_TESTBED_mark_host_registered_at_ (rh->host, c);
393 rh->cc (rh->cc_cls, NULL);
397 /* We have an error message */
398 emsg = (const char *) &msg[1];
399 LOG (GNUNET_ERROR_TYPE_ERROR,
400 _ ("Adding host %u failed with error: %s\n"),
401 ntohl (msg->host_id),
403 rh->cc (rh->cc_cls, emsg);
409 * Handler for forwarded operations
411 * @param c the controller handle
412 * @param opc the opearation context
413 * @param msg the message
416 handle_forwarded_operation_msg (void *cls,
417 struct OperationContext *opc,
418 const struct GNUNET_MessageHeader *msg)
420 struct GNUNET_TESTBED_Controller *c = cls;
421 struct ForwardedOperationData *fo_data;
424 if (NULL != fo_data->cc)
425 fo_data->cc (fo_data->cc_cls, msg);
426 GNUNET_TESTBED_remove_opc_ (c, opc);
427 GNUNET_free (fo_data);
433 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS message from
434 * controller (testbed service)
436 * @param c the controller handler
437 * @param msg message received
442 const struct GNUNET_TESTBED_GenericOperationSuccessEventMessage *msg)
444 struct GNUNET_TESTBED_Controller *c = cls;
445 struct OperationContext *opc;
446 GNUNET_TESTBED_OperationCompletionCallback op_comp_cb;
447 void *op_comp_cb_cls;
448 struct GNUNET_TESTBED_EventInformation event;
451 op_id = GNUNET_ntohll (msg->operation_id);
452 LOG_DEBUG ("Operation %lu successful\n", op_id);
453 if (NULL == (opc = find_opc (c, op_id)))
455 LOG_DEBUG ("Operation not found\n");
458 event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED;
460 event.op_cls = opc->op_cls;
461 event.details.operation_finished.emsg = NULL;
462 event.details.operation_finished.generic = NULL;
464 op_comp_cb_cls = NULL;
468 handle_forwarded_operation_msg (c,
470 (const struct GNUNET_MessageHeader *) msg);
474 case OP_PEER_DESTROY: {
475 struct GNUNET_TESTBED_Peer *peer;
478 GNUNET_TESTBED_peer_deregister_ (peer);
484 case OP_SHUTDOWN_PEERS: {
485 struct ShutdownPeersData *data;
488 op_comp_cb = data->cb;
489 op_comp_cb_cls = data->cb_cls;
492 GNUNET_TESTBED_cleanup_peers_ ();
495 case OP_MANAGE_SERVICE: {
496 struct ManageServiceData *data;
498 GNUNET_assert (NULL != (data = opc->data));
499 op_comp_cb = data->cb;
500 op_comp_cb_cls = data->cb_cls;
505 case OP_PEER_RECONFIGURE:
510 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
511 opc->state = OPC_STATE_FINISHED;
512 exop_insert (event.op);
513 if (0 != (c->event_mask & (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED)))
516 c->cc (c->cc_cls, &event);
517 if (GNUNET_NO == exop_check (event.op))
521 LOG_DEBUG ("Not calling callback\n");
522 if (NULL != op_comp_cb)
523 op_comp_cb (op_comp_cb_cls, event.op, NULL);
524 /* You could have marked the operation as done by now */
525 GNUNET_break (GNUNET_NO == exop_check (event.op));
530 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS message from
531 * controller (testbed service)
533 * @param c the controller handle
534 * @param msg message received
537 handle_peer_create_success (
539 const struct GNUNET_TESTBED_PeerCreateSuccessEventMessage *msg)
541 struct GNUNET_TESTBED_Controller *c = cls;
542 struct OperationContext *opc;
543 struct PeerCreateData *data;
544 struct GNUNET_TESTBED_Peer *peer;
545 struct GNUNET_TESTBED_Operation *op;
546 GNUNET_TESTBED_PeerCreateCallback cb;
550 GNUNET_assert (sizeof (struct GNUNET_TESTBED_PeerCreateSuccessEventMessage) ==
551 ntohs (msg->header.size));
552 op_id = GNUNET_ntohll (msg->operation_id);
553 if (NULL == (opc = find_opc (c, op_id)))
555 LOG_DEBUG ("Operation context for PeerCreateSuccessEvent not found\n");
558 if (OP_FORWARDED == opc->type)
560 handle_forwarded_operation_msg (c,
562 (const struct GNUNET_MessageHeader *) msg);
565 GNUNET_assert (OP_PEER_CREATE == opc->type);
566 GNUNET_assert (NULL != opc->data);
568 GNUNET_assert (NULL != data->peer);
570 GNUNET_assert (peer->unique_id == ntohl (msg->peer_id));
571 peer->state = TESTBED_PS_CREATED;
572 GNUNET_TESTBED_peer_register_ (peer);
576 GNUNET_free (opc->data);
577 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
578 opc->state = OPC_STATE_FINISHED;
581 cb (cb_cls, peer, NULL);
582 /* You could have marked the operation as done by now */
583 GNUNET_break (GNUNET_NO == exop_check (op));
588 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT message from
589 * controller (testbed service)
591 * @param c the controller handler
592 * @param msg message received
595 handle_peer_event (void *cls, const struct GNUNET_TESTBED_PeerEventMessage *msg)
597 struct GNUNET_TESTBED_Controller *c = cls;
598 struct OperationContext *opc;
599 struct GNUNET_TESTBED_Peer *peer;
600 struct PeerEventData *data;
601 GNUNET_TESTBED_PeerChurnCallback pcc;
603 struct GNUNET_TESTBED_EventInformation event;
607 GNUNET_assert (sizeof (struct GNUNET_TESTBED_PeerEventMessage) ==
608 ntohs (msg->header.size));
609 op_id = GNUNET_ntohll (msg->operation_id);
610 if (NULL == (opc = find_opc (c, op_id)))
612 LOG_DEBUG ("Operation not found\n");
615 if (OP_FORWARDED == opc->type)
617 handle_forwarded_operation_msg (c,
619 (const struct GNUNET_MessageHeader *) msg);
622 GNUNET_assert ((OP_PEER_START == opc->type) || (OP_PEER_STOP == opc->type));
624 GNUNET_assert (NULL != data);
626 GNUNET_assert (NULL != peer);
627 event.type = (enum GNUNET_TESTBED_EventType) ntohl (msg->event_type);
629 event.op_cls = opc->op_cls;
632 case GNUNET_TESTBED_ET_PEER_START:
633 peer->state = TESTBED_PS_STARTED;
634 event.details.peer_start.host = peer->host;
635 event.details.peer_start.peer = peer;
637 case GNUNET_TESTBED_ET_PEER_STOP:
638 peer->state = TESTBED_PS_STOPPED;
639 event.details.peer_stop.peer = peer;
642 GNUNET_assert (0); /* We should never reach this state */
645 pcc_cls = data->pcc_cls;
647 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
648 opc->state = OPC_STATE_FINISHED;
649 exop_insert (event.op);
650 mask = 1LL << GNUNET_TESTBED_ET_PEER_START;
651 mask |= 1LL << GNUNET_TESTBED_ET_PEER_STOP;
652 if (0 != (mask & c->event_mask))
655 c->cc (c->cc_cls, &event);
656 if (GNUNET_NO == exop_check (event.op))
661 /* You could have marked the operation as done by now */
662 GNUNET_break (GNUNET_NO == exop_check (event.op));
667 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONNECT_EVENT message from
668 * controller (testbed service)
670 * @param c the controller handler
671 * @param msg message received
674 handle_peer_conevent (void *cls,
675 const struct GNUNET_TESTBED_ConnectionEventMessage *msg)
677 struct GNUNET_TESTBED_Controller *c = cls;
678 struct OperationContext *opc;
679 struct OverlayConnectData *data;
680 GNUNET_TESTBED_OperationCompletionCallback cb;
682 struct GNUNET_TESTBED_EventInformation event;
686 op_id = GNUNET_ntohll (msg->operation_id);
687 if (NULL == (opc = find_opc (c, op_id)))
689 LOG_DEBUG ("Operation not found\n");
692 if (OP_FORWARDED == opc->type)
694 handle_forwarded_operation_msg (c,
696 (const struct GNUNET_MessageHeader *) msg);
699 GNUNET_assert (OP_OVERLAY_CONNECT == opc->type);
700 GNUNET_assert (NULL != (data = opc->data));
701 GNUNET_assert ((ntohl (msg->peer1) == data->p1->unique_id) &&
702 (ntohl (msg->peer2) == data->p2->unique_id));
703 event.type = (enum GNUNET_TESTBED_EventType) ntohl (msg->event_type);
705 event.op_cls = opc->op_cls;
708 case GNUNET_TESTBED_ET_CONNECT:
709 event.details.peer_connect.peer1 = data->p1;
710 event.details.peer_connect.peer2 = data->p2;
712 case GNUNET_TESTBED_ET_DISCONNECT:
713 GNUNET_assert (0); /* FIXME: implement */
716 GNUNET_assert (0); /* Should never reach here */
720 cb_cls = data->cb_cls;
721 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
722 opc->state = OPC_STATE_FINISHED;
723 exop_insert (event.op);
724 mask = 1LL << GNUNET_TESTBED_ET_CONNECT;
725 mask |= 1LL << GNUNET_TESTBED_ET_DISCONNECT;
726 if (0 != (mask & c->event_mask))
729 c->cc (c->cc_cls, &event);
730 if (GNUNET_NO == exop_check (event.op))
734 cb (cb_cls, opc->op, NULL);
735 /* You could have marked the operation as done by now */
736 GNUNET_break (GNUNET_NO == exop_check (event.op));
741 * Validate #GNUNET_MESSAGE_TYPE_TESTBED_PEER_INFORMATION message from
742 * controller (testbed service)
744 * @param c the controller handler
745 * @param msg message received
750 const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *msg)
758 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_PEER_INFORMATION message from
759 * controller (testbed service)
761 * @param c the controller handler
762 * @param msg message received
767 const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *msg)
769 struct GNUNET_TESTBED_Controller *c = cls;
770 struct OperationContext *opc;
771 struct GNUNET_TESTBED_Peer *peer;
772 struct PeerInfoData *data;
773 struct GNUNET_TESTBED_PeerInformation *pinfo;
774 GNUNET_TESTBED_PeerInfoCallback cb;
778 op_id = GNUNET_ntohll (msg->operation_id);
779 if (NULL == (opc = find_opc (c, op_id)))
781 LOG_DEBUG ("Operation not found\n");
784 if (OP_FORWARDED == opc->type)
786 handle_forwarded_operation_msg (c, opc, &msg->header);
790 GNUNET_assert (NULL != data);
792 GNUNET_assert (NULL != peer);
793 GNUNET_assert (ntohl (msg->peer_id) == peer->unique_id);
794 pinfo = GNUNET_new (struct GNUNET_TESTBED_PeerInformation);
795 pinfo->pit = data->pit;
797 cb_cls = data->cb_cls;
798 GNUNET_assert (NULL != cb);
803 case GNUNET_TESTBED_PIT_IDENTITY:
804 pinfo->result.id = GNUNET_new (struct GNUNET_PeerIdentity);
805 GNUNET_memcpy (pinfo->result.id,
807 sizeof (struct GNUNET_PeerIdentity));
809 case GNUNET_TESTBED_PIT_CONFIGURATION:
810 pinfo->result.cfg = /* Freed in oprelease_peer_getinfo */
811 GNUNET_TESTBED_extract_config_ (&msg->header);
813 case GNUNET_TESTBED_PIT_GENERIC:
814 GNUNET_assert (0); /* never reach here */
818 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
819 opc->state = OPC_STATE_FINISHED;
820 cb (cb_cls, opc->op, pinfo, NULL);
821 /* We dont check whether the operation is marked as done here as the
822 operation contains data (cfg/identify) which will be freed at a later point
828 * Validate #GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT message from
829 * controller (testbed service)
831 * @param c the controller handler
832 * @param msg message received
833 * @return #GNUNET_OK if message is well-formed
836 check_op_fail_event (
838 const struct GNUNET_TESTBED_OperationFailureEventMessage *msg)
840 /* we accept anything as a valid error message */
846 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT message from
847 * controller (testbed service)
849 * @param c the controller handler
850 * @param msg message received
853 handle_op_fail_event (
855 const struct GNUNET_TESTBED_OperationFailureEventMessage *msg)
857 struct GNUNET_TESTBED_Controller *c = cls;
858 struct OperationContext *opc;
862 struct GNUNET_TESTBED_EventInformation event;
864 op_id = GNUNET_ntohll (msg->operation_id);
865 if (NULL == (opc = find_opc (c, op_id)))
867 LOG_DEBUG ("Operation not found\n");
870 if (OP_FORWARDED == opc->type)
872 handle_forwarded_operation_msg (c,
874 (const struct GNUNET_MessageHeader *) msg);
877 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
878 opc->state = OPC_STATE_FINISHED;
879 emsg = GNUNET_TESTBED_parse_error_string_ (msg);
881 emsg = "Unknown error";
882 if (OP_PEER_INFO == opc->type)
884 struct PeerInfoData *data;
887 if (NULL != data->cb)
888 data->cb (data->cb_cls, opc->op, NULL, emsg);
890 return; /* We do not call controller callback for peer info */
892 event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED;
894 event.op_cls = opc->op_cls;
895 event.details.operation_finished.emsg = emsg;
896 event.details.operation_finished.generic = NULL;
897 mask = (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
898 if ((0 != (mask & c->event_mask)) && (NULL != c->cc))
900 exop_insert (event.op);
901 c->cc (c->cc_cls, &event);
902 if (GNUNET_NO == exop_check (event.op))
907 case OP_PEER_CREATE: {
908 struct PeerCreateData *data;
911 GNUNET_free (data->peer);
912 if (NULL != data->cb)
913 data->cb (data->cls, NULL, emsg);
919 struct PeerEventData *data;
922 if (NULL != data->pcc)
923 data->pcc (data->pcc_cls, emsg);
927 case OP_PEER_DESTROY:
931 case OP_OVERLAY_CONNECT: {
932 struct OverlayConnectData *data;
935 GNUNET_TESTBED_operation_mark_failed (opc->op);
936 if (NULL != data->cb)
937 data->cb (data->cb_cls, opc->op, emsg);
942 case OP_LINK_CONTROLLERS: /* No secondary callback */
944 case OP_SHUTDOWN_PEERS: {
945 struct ShutdownPeersData *data;
948 GNUNET_free (data); /* FIXME: Decide whether we call data->op_cb */
952 case OP_MANAGE_SERVICE: {
953 struct ManageServiceData *data = opc->data;
954 GNUNET_TESTBED_OperationCompletionCallback cb;
957 GNUNET_assert (NULL != data);
959 cb_cls = data->cb_cls;
962 exop_insert (event.op);
964 cb (cb_cls, opc->op, emsg);
965 /* You could have marked the operation as done by now */
966 GNUNET_break (GNUNET_NO == exop_check (event.op));
976 * Function to build GET_SLAVE_CONFIG message
978 * @param op_id the id this message should contain in its operation id field
979 * @param slave_id the id this message should contain in its slave id field
980 * @return newly allocated SlaveGetConfigurationMessage
982 static struct GNUNET_TESTBED_SlaveGetConfigurationMessage *
983 GNUNET_TESTBED_generate_slavegetconfig_msg_ (uint64_t op_id, uint32_t slave_id)
985 struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg;
988 msize = sizeof (struct GNUNET_TESTBED_SlaveGetConfigurationMessage);
989 msg = GNUNET_malloc (msize);
990 msg->header.size = htons (msize);
992 htons (GNUNET_MESSAGE_TYPE_TESTBED_GET_SLAVE_CONFIGURATION);
993 msg->operation_id = GNUNET_htonll (op_id);
994 msg->slave_id = htonl (slave_id);
1000 * Validate #GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_INFORMATION message from
1001 * controller (testbed service)
1003 * @param c the controller handler
1004 * @param msg message received
1007 check_slave_config (void *cls,
1008 const struct GNUNET_TESTBED_SlaveConfiguration *msg)
1010 /* anything goes? */
1016 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION message from controller
1019 * @param c the controller handler
1020 * @param msg message received
1023 handle_slave_config (void *cls,
1024 const struct GNUNET_TESTBED_SlaveConfiguration *msg)
1026 struct GNUNET_TESTBED_Controller *c = cls;
1027 struct OperationContext *opc;
1030 struct GNUNET_TESTBED_EventInformation event;
1032 op_id = GNUNET_ntohll (msg->operation_id);
1033 if (NULL == (opc = find_opc (c, op_id)))
1035 LOG_DEBUG ("Operation not found\n");
1038 if (OP_GET_SLAVE_CONFIG != opc->type)
1043 opc->state = OPC_STATE_FINISHED;
1044 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
1045 mask = 1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED;
1046 if ((0 != (mask & c->event_mask)) && (NULL != c->cc))
1048 opc->data = GNUNET_TESTBED_extract_config_ (&msg->header);
1049 event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED;
1051 event.op_cls = opc->op_cls;
1052 event.details.operation_finished.generic = opc->data;
1053 event.details.operation_finished.emsg = NULL;
1054 c->cc (c->cc_cls, &event);
1060 * Check #GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS_RESULT message from controller
1063 * @param c the controller handler
1064 * @param msg message received
1065 * @return #GNUNET_OK if @a msg is well-formed
1068 check_link_controllers_result (
1070 const struct GNUNET_TESTBED_ControllerLinkResponse *msg)
1072 /* actual check to be implemented */
1078 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS_RESULT message from controller
1081 * @param c the controller handler
1082 * @param msg message received
1085 handle_link_controllers_result (
1087 const struct GNUNET_TESTBED_ControllerLinkResponse *msg)
1089 struct GNUNET_TESTBED_Controller *c = cls;
1090 struct OperationContext *opc;
1091 struct ControllerLinkData *data;
1092 struct GNUNET_CONFIGURATION_Handle *cfg;
1093 struct GNUNET_TESTBED_Host *host;
1096 struct GNUNET_TESTBED_EventInformation event;
1098 op_id = GNUNET_ntohll (msg->operation_id);
1099 if (NULL == (opc = find_opc (c, op_id)))
1101 LOG_DEBUG ("Operation not found\n");
1104 if (OP_FORWARDED == opc->type)
1106 handle_forwarded_operation_msg (c,
1108 (const struct GNUNET_MessageHeader *) msg);
1111 if (OP_LINK_CONTROLLERS != opc->type)
1116 GNUNET_assert (NULL != (data = opc->data));
1117 host = GNUNET_TESTBED_host_lookup_by_id_ (data->host_id);
1118 GNUNET_assert (NULL != host);
1121 opc->state = OPC_STATE_FINISHED;
1122 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
1123 event.type = GNUNET_TESTBED_ET_OPERATION_FINISHED;
1125 event.op_cls = opc->op_cls;
1126 event.details.operation_finished.emsg = NULL;
1127 event.details.operation_finished.generic = NULL;
1130 if (GNUNET_NO == ntohs (msg->success))
1133 GNUNET_malloc (ntohs (msg->header.size) -
1134 sizeof (struct GNUNET_TESTBED_ControllerLinkResponse) + 1);
1135 GNUNET_memcpy (emsg,
1137 ntohs (msg->header.size) -
1138 sizeof (struct GNUNET_TESTBED_ControllerLinkResponse));
1139 event.details.operation_finished.emsg = emsg;
1143 if (0 != ntohs (msg->config_size))
1145 cfg = GNUNET_TESTBED_extract_config_ (
1146 (const struct GNUNET_MessageHeader *) msg);
1147 GNUNET_assert (NULL != cfg);
1148 GNUNET_TESTBED_host_replace_cfg_ (host, cfg);
1151 if (0 != (c->event_mask & (1L << GNUNET_TESTBED_ET_OPERATION_FINISHED)))
1154 c->cc (c->cc_cls, &event);
1157 LOG_DEBUG ("Not calling callback\n");
1159 GNUNET_CONFIGURATION_destroy (cfg);
1160 GNUNET_free_non_null (emsg);
1165 * Validate #GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS message.
1167 * @param cls the controller handle to determine the connection this message
1169 * @param msg the barrier status message
1170 * @return #GNUNET_OK if the message is valid; #GNUNET_SYSERR to tear it
1171 * down signalling an error (message malformed)
1174 check_barrier_status (void *cls,
1175 const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
1183 msize = ntohs (msg->header.size);
1185 name_len = ntohs (msg->name_len);
1187 if (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len + 1 > msize)
1189 GNUNET_break_op (0);
1190 return GNUNET_SYSERR;
1192 if ('\0' != name[name_len])
1194 GNUNET_break_op (0);
1195 return GNUNET_SYSERR;
1197 status = ntohs (msg->status);
1198 if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status)
1200 emsg_len = msize - (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) +
1201 name_len + 1); /* +1!? */
1204 GNUNET_break_op (0);
1205 return GNUNET_SYSERR;
1213 * Handler for #GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS messages
1215 * @param cls the controller handle to determine the connection this message
1217 * @param msg the barrier status message
1220 handle_barrier_status (void *cls,
1221 const struct GNUNET_TESTBED_BarrierStatusMsg *msg)
1223 struct GNUNET_TESTBED_Controller *c = cls;
1224 struct GNUNET_TESTBED_Barrier *barrier;
1227 struct GNUNET_HashCode key;
1235 msize = ntohs (msg->header.size);
1236 if (msize <= sizeof (struct GNUNET_TESTBED_BarrierStatusMsg))
1238 GNUNET_break_op (0);
1242 name_len = ntohs (msg->name_len);
1243 if (name_len >= //name_len is strlen(barrier_name)
1244 (msize - ((sizeof msg->header) + sizeof (msg->status))))
1246 GNUNET_break_op (0);
1249 if ('\0' != name[name_len])
1251 GNUNET_break_op (0);
1254 LOG_DEBUG ("Received BARRIER_STATUS msg\n");
1255 status = ntohs (msg->status);
1256 if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status)
1259 //unlike name_len, emsg_len includes the trailing zero
1260 emsg_len = msize - (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) +
1264 GNUNET_break_op (0);
1267 if ('\0' != (msg->data[(name_len + 1) + (emsg_len - 1)]))
1269 GNUNET_break_op (0);
1272 emsg = GNUNET_malloc (emsg_len);
1273 GNUNET_memcpy (emsg, msg->data + name_len + 1, emsg_len);
1275 if (NULL == c->barrier_map)
1277 GNUNET_break_op (0);
1280 GNUNET_CRYPTO_hash (name, name_len, &key);
1281 barrier = GNUNET_CONTAINER_multihashmap_get (c->barrier_map, &key);
1282 if (NULL == barrier)
1284 GNUNET_break_op (0);
1287 GNUNET_assert (NULL != barrier->cb);
1288 if ((GNUNET_YES == barrier->echo) &&
1289 (GNUNET_TESTBED_BARRIERSTATUS_CROSSED == status))
1290 GNUNET_TESTBED_queue_message_ (c, GNUNET_copy_message (&msg->header));
1291 barrier->cb (barrier->cls, name, barrier, status, emsg);
1292 if (GNUNET_TESTBED_BARRIERSTATUS_INITIALISED == status)
1293 return; /* just initialised; skip cleanup */
1296 GNUNET_free_non_null (emsg);
1298 * Do not remove the barrier if we did not echo the status back; this is
1299 * required at the chained testbed controller setup to ensure the only the
1300 * test-driver echos the status and the controller hierarchy properly
1301 * propagates the status.
1303 if ((NULL != barrier) && (GNUNET_YES == barrier->echo))
1304 GNUNET_TESTBED_barrier_remove_ (barrier);
1309 * Queues a message in send queue for sending to the service
1311 * @param controller the handle to the controller
1312 * @param msg the message to queue
1315 GNUNET_TESTBED_queue_message_ (struct GNUNET_TESTBED_Controller *controller,
1316 struct GNUNET_MessageHeader *msg)
1318 struct GNUNET_MQ_Envelope *env;
1319 struct GNUNET_MessageHeader *m2;
1323 type = ntohs (msg->type);
1324 size = ntohs (msg->size);
1325 GNUNET_assert ((GNUNET_MESSAGE_TYPE_TESTBED_INIT <= type) &&
1326 (GNUNET_MESSAGE_TYPE_TESTBED_MAX > type));
1327 env = GNUNET_MQ_msg_extra (m2, size - sizeof (*m2), type);
1328 GNUNET_memcpy (m2, msg, size);
1330 GNUNET_MQ_send (controller->mq, env);
1335 * Sends the given message as an operation. The given callback is called when a
1336 * reply for the operation is available. Call
1337 * GNUNET_TESTBED_forward_operation_msg_cancel_() to cleanup the returned
1338 * operation context if the cc hasn't been called
1340 * @param controller the controller to which the message has to be sent
1341 * @param operation_id the operation id of the message
1342 * @param msg the message to send
1343 * @param cc the callback to call when reply is available
1344 * @param cc_cls the closure for the above callback
1345 * @return the operation context which can be used to cancel the forwarded
1348 struct OperationContext *
1349 GNUNET_TESTBED_forward_operation_msg_ (
1350 struct GNUNET_TESTBED_Controller *controller,
1351 uint64_t operation_id,
1352 const struct GNUNET_MessageHeader *msg,
1353 GNUNET_MQ_MessageCallback cc,
1356 struct OperationContext *opc;
1357 struct ForwardedOperationData *data;
1358 struct GNUNET_MQ_Envelope *env;
1359 struct GNUNET_MessageHeader *m2;
1360 uint16_t type = ntohs (msg->type);
1361 uint16_t size = ntohs (msg->size);
1363 env = GNUNET_MQ_msg_extra (m2, size - sizeof (*m2), type);
1364 GNUNET_memcpy (m2, msg, size);
1365 GNUNET_MQ_send (controller->mq, env);
1366 data = GNUNET_new (struct ForwardedOperationData);
1368 data->cc_cls = cc_cls;
1369 opc = GNUNET_new (struct OperationContext);
1370 opc->c = controller;
1371 opc->type = OP_FORWARDED;
1373 opc->id = operation_id;
1374 GNUNET_TESTBED_insert_opc_ (controller, opc);
1380 * Function to cancel an operation created by simply forwarding an operation
1383 * @param opc the operation context from GNUNET_TESTBED_forward_operation_msg_()
1386 GNUNET_TESTBED_forward_operation_msg_cancel_ (struct OperationContext *opc)
1388 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
1389 GNUNET_free (opc->data);
1395 * Function to call to start a link-controllers type operation once all queues
1396 * the operation is part of declare that the operation can be activated.
1398 * @param cls the closure from GNUNET_TESTBED_operation_create_()
1401 opstart_link_controllers (void *cls)
1403 struct OperationContext *opc = cls;
1404 struct ControllerLinkData *data;
1405 struct GNUNET_TESTBED_ControllerLinkRequest *msg;
1407 GNUNET_assert (NULL != opc->data);
1411 opc->state = OPC_STATE_STARTED;
1412 GNUNET_TESTBED_insert_opc_ (opc->c, opc);
1413 GNUNET_TESTBED_queue_message_ (opc->c, &msg->header);
1418 * Callback which will be called when link-controllers type operation is released
1420 * @param cls the closure from GNUNET_TESTBED_operation_create_()
1423 oprelease_link_controllers (void *cls)
1425 struct OperationContext *opc = cls;
1426 struct ControllerLinkData *data;
1431 case OPC_STATE_INIT:
1432 GNUNET_free (data->msg);
1434 case OPC_STATE_STARTED:
1435 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
1437 case OPC_STATE_FINISHED:
1440 GNUNET_free_non_null (data);
1446 * Function to be called when get slave config operation is ready
1448 * @param cls the OperationContext of type OP_GET_SLAVE_CONFIG
1451 opstart_get_slave_config (void *cls)
1453 struct OperationContext *opc = cls;
1454 struct GetSlaveConfigData *data = opc->data;
1455 struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg;
1457 GNUNET_assert (NULL != data);
1458 msg = GNUNET_TESTBED_generate_slavegetconfig_msg_ (opc->id, data->slave_id);
1459 GNUNET_free (opc->data);
1462 GNUNET_TESTBED_insert_opc_ (opc->c, opc);
1463 GNUNET_TESTBED_queue_message_ (opc->c, &msg->header);
1464 opc->state = OPC_STATE_STARTED;
1469 * Function to be called when get slave config operation is cancelled or finished
1471 * @param cls the OperationContext of type OP_GET_SLAVE_CONFIG
1474 oprelease_get_slave_config (void *cls)
1476 struct OperationContext *opc = cls;
1480 case OPC_STATE_INIT:
1481 GNUNET_free (opc->data);
1483 case OPC_STATE_STARTED:
1484 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
1486 case OPC_STATE_FINISHED:
1487 if (NULL != opc->data)
1488 GNUNET_CONFIGURATION_destroy (opc->data);
1496 * Generic error handler, called with the appropriate error code and
1497 * the same closure specified at the creation of the message queue.
1498 * Not every message queue implementation supports an error handler.
1500 * @param cls closure, a `struct GNUNET_TESTBED_Controller *`
1501 * @param error error code
1504 mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
1506 /* struct GNUNET_TESTBED_Controller *c = cls; */
1508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Encountered MQ error: %d\n", error);
1510 GNUNET_SCHEDULER_shutdown (); /* seems most reasonable */
1515 * Start a controller process using the given configuration at the
1518 * @param host host to run the controller on; This should be the same host if
1519 * the controller was previously started with
1520 * GNUNET_TESTBED_controller_start()
1521 * @param event_mask bit mask with set of events to call 'cc' for;
1522 * or-ed values of "1LL" shifted by the
1523 * respective 'enum GNUNET_TESTBED_EventType'
1524 * (i.e. "(1LL << GNUNET_TESTBED_ET_CONNECT) | ...")
1525 * @param cc controller callback to invoke on events
1526 * @param cc_cls closure for cc
1527 * @return handle to the controller
1529 struct GNUNET_TESTBED_Controller *
1530 GNUNET_TESTBED_controller_connect (struct GNUNET_TESTBED_Host *host,
1531 uint64_t event_mask,
1532 GNUNET_TESTBED_ControllerCallback cc,
1535 struct GNUNET_TESTBED_Controller *controller =
1536 GNUNET_new (struct GNUNET_TESTBED_Controller);
1537 struct GNUNET_MQ_MessageHandler handlers[] =
1538 {GNUNET_MQ_hd_var_size (add_host_confirm,
1539 GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST_SUCCESS,
1540 struct GNUNET_TESTBED_HostConfirmedMessage,
1542 GNUNET_MQ_hd_fixed_size (peer_conevent,
1543 GNUNET_MESSAGE_TYPE_TESTBED_PEER_CONNECT_EVENT,
1544 struct GNUNET_TESTBED_ConnectionEventMessage,
1546 GNUNET_MQ_hd_fixed_size (opsuccess,
1547 GNUNET_MESSAGE_TYPE_TESTBED_GENERIC_OPERATION_SUCCESS,
1549 GNUNET_TESTBED_GenericOperationSuccessEventMessage,
1551 GNUNET_MQ_hd_var_size (op_fail_event,
1552 GNUNET_MESSAGE_TYPE_TESTBED_OPERATION_FAIL_EVENT,
1553 struct GNUNET_TESTBED_OperationFailureEventMessage,
1555 GNUNET_MQ_hd_fixed_size (peer_create_success,
1556 GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER_SUCCESS,
1558 GNUNET_TESTBED_PeerCreateSuccessEventMessage,
1560 GNUNET_MQ_hd_fixed_size (peer_event,
1561 GNUNET_MESSAGE_TYPE_TESTBED_PEER_EVENT,
1562 struct GNUNET_TESTBED_PeerEventMessage,
1564 GNUNET_MQ_hd_var_size (peer_config,
1565 GNUNET_MESSAGE_TYPE_TESTBED_PEER_INFORMATION,
1567 GNUNET_TESTBED_PeerConfigurationInformationMessage,
1569 GNUNET_MQ_hd_var_size (slave_config,
1570 GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION,
1571 struct GNUNET_TESTBED_SlaveConfiguration,
1573 GNUNET_MQ_hd_var_size (link_controllers_result,
1574 GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS_RESULT,
1575 struct GNUNET_TESTBED_ControllerLinkResponse,
1577 GNUNET_MQ_hd_var_size (barrier_status,
1578 GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS,
1579 struct GNUNET_TESTBED_BarrierStatusMsg,
1581 GNUNET_MQ_handler_end ()};
1582 struct GNUNET_TESTBED_InitMessage *msg;
1583 struct GNUNET_MQ_Envelope *env;
1584 const struct GNUNET_CONFIGURATION_Handle *cfg;
1585 const char *controller_hostname;
1586 unsigned long long max_parallel_operations;
1587 unsigned long long max_parallel_service_connections;
1588 unsigned long long max_parallel_topology_config_operations;
1591 GNUNET_assert (NULL != (cfg = GNUNET_TESTBED_host_get_cfg_ (host)));
1593 GNUNET_CONFIGURATION_get_value_number (cfg,
1595 "MAX_PARALLEL_OPERATIONS",
1596 &max_parallel_operations))
1599 GNUNET_free (controller);
1603 GNUNET_CONFIGURATION_get_value_number (cfg,
1605 "MAX_PARALLEL_SERVICE_CONNECTIONS",
1606 &max_parallel_service_connections))
1609 GNUNET_free (controller);
1612 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (
1615 "MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS",
1616 &max_parallel_topology_config_operations))
1619 GNUNET_free (controller);
1622 controller->cc = cc;
1623 controller->cc_cls = cc_cls;
1624 controller->event_mask = event_mask;
1625 controller->cfg = GNUNET_CONFIGURATION_dup (cfg);
1626 controller->mq = GNUNET_CLIENT_connect (controller->cfg,
1631 if (NULL == controller->mq)
1634 GNUNET_TESTBED_controller_disconnect (controller);
1637 GNUNET_TESTBED_mark_host_registered_at_ (host, controller);
1638 controller->host = host;
1639 controller->opq_parallel_operations =
1640 GNUNET_TESTBED_operation_queue_create_ (OPERATION_QUEUE_TYPE_FIXED,
1642 max_parallel_operations);
1643 controller->opq_parallel_service_connections =
1644 GNUNET_TESTBED_operation_queue_create_ (OPERATION_QUEUE_TYPE_FIXED,
1646 max_parallel_service_connections);
1647 controller->opq_parallel_topology_config_operations =
1648 GNUNET_TESTBED_operation_queue_create_ (
1649 OPERATION_QUEUE_TYPE_FIXED,
1650 (unsigned int) max_parallel_topology_config_operations);
1651 controller_hostname = GNUNET_TESTBED_host_get_hostname (host);
1652 if (NULL == controller_hostname)
1653 controller_hostname = "127.0.0.1";
1654 slen = strlen (controller_hostname) + 1;
1655 env = GNUNET_MQ_msg_extra (msg, slen, GNUNET_MESSAGE_TYPE_TESTBED_INIT);
1656 msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (host));
1657 msg->event_mask = GNUNET_htonll (controller->event_mask);
1658 GNUNET_memcpy (&msg[1], controller_hostname, slen);
1659 GNUNET_MQ_send (controller->mq, env);
1665 * Iterator to free opc map entries
1667 * @param cls closure
1668 * @param key current key code
1669 * @param value value in the hash map
1670 * @return #GNUNET_YES if we should continue to iterate,
1671 * #GNUNET_NO if not.
1674 opc_free_iterator (void *cls, uint32_t key, void *value)
1676 struct GNUNET_CONTAINER_MultiHashMap32 *map = cls;
1677 struct OperationContext *opc = value;
1679 GNUNET_assert (NULL != opc);
1681 GNUNET_assert (GNUNET_YES ==
1682 GNUNET_CONTAINER_multihashmap32_remove (map, key, value));
1689 * Stop the given controller (also will terminate all peers and
1690 * controllers dependent on this controller). This function
1691 * blocks until the testbed has been fully terminated (!).
1693 * @param c handle to controller to stop
1696 GNUNET_TESTBED_controller_disconnect (struct GNUNET_TESTBED_Controller *c)
1700 GNUNET_MQ_destroy (c->mq);
1703 if (NULL != c->host)
1704 GNUNET_TESTBED_deregister_host_at_ (c->host, c);
1705 GNUNET_CONFIGURATION_destroy (c->cfg);
1706 GNUNET_TESTBED_operation_queue_destroy_ (c->opq_parallel_operations);
1707 GNUNET_TESTBED_operation_queue_destroy_ (c->opq_parallel_service_connections);
1708 GNUNET_TESTBED_operation_queue_destroy_ (
1709 c->opq_parallel_topology_config_operations);
1710 if (NULL != c->opc_map)
1712 GNUNET_assert (GNUNET_SYSERR !=
1713 GNUNET_CONTAINER_multihashmap32_iterate (c->opc_map,
1716 GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (c->opc_map));
1717 GNUNET_CONTAINER_multihashmap32_destroy (c->opc_map);
1724 * Compresses given configuration using zlib compress
1726 * @param config the serialized configuration
1727 * @param size the size of config
1728 * @param xconfig will be set to the compressed configuration (memory is fresly
1730 * @return the size of the xconfig
1733 GNUNET_TESTBED_compress_config_ (const char *config,
1739 xsize = compressBound ((uLong) size);
1740 *xconfig = GNUNET_malloc (xsize);
1741 GNUNET_assert (Z_OK == compress2 ((Bytef *) *xconfig,
1743 (const Bytef *) config,
1751 * Function to serialize and compress using zlib a configuration through a
1752 * configuration handle
1754 * @param cfg the configuration
1755 * @param size the size of configuration when serialize. Will be set on success.
1756 * @param xsize the sizeo of the compressed configuration. Will be set on success.
1757 * @return the serialized and compressed configuration
1760 GNUNET_TESTBED_compress_cfg_ (const struct GNUNET_CONFIGURATION_Handle *cfg,
1769 config = GNUNET_CONFIGURATION_serialize (cfg, &size_);
1770 xsize_ = GNUNET_TESTBED_compress_config_ (config, size_, &xconfig);
1771 GNUNET_free (config);
1779 * Create a link from slave controller to delegated controller. Whenever the
1780 * master controller is asked to start a peer at the delegated controller the
1781 * request will be routed towards slave controller (if a route exists). The
1782 * slave controller will then route it to the delegated controller. The
1783 * configuration of the delegated controller is given and is used to either
1784 * create the delegated controller or to connect to an existing controller. Note
1785 * that while starting the delegated controller the configuration will be
1786 * modified to accommodate available free ports. the 'is_subordinate' specifies
1787 * if the given delegated controller should be started and managed by the slave
1788 * controller, or if the delegated controller already has a master and the slave
1789 * controller connects to it as a non master controller. The success or failure
1790 * of this operation will be signalled through the
1791 * GNUNET_TESTBED_ControllerCallback() with an event of type
1792 * GNUNET_TESTBED_ET_OPERATION_FINISHED
1794 * @param op_cls the operation closure for the event which is generated to
1795 * signal success or failure of this operation
1796 * @param master handle to the master controller who creates the association
1797 * @param delegated_host requests to which host should be delegated; cannot be NULL
1798 * @param slave_host which host is used to run the slave controller; use NULL to
1799 * make the master controller connect to the delegated host
1800 * @param is_subordinate GNUNET_YES if the controller at delegated_host should
1801 * be started by the slave controller; GNUNET_NO if the slave
1802 * controller has to connect to the already started delegated
1803 * controller via TCP/IP
1804 * @return the operation handle
1806 struct GNUNET_TESTBED_Operation *
1807 GNUNET_TESTBED_controller_link (void *op_cls,
1808 struct GNUNET_TESTBED_Controller *master,
1809 struct GNUNET_TESTBED_Host *delegated_host,
1810 struct GNUNET_TESTBED_Host *slave_host,
1813 struct OperationContext *opc;
1814 struct GNUNET_TESTBED_ControllerLinkRequest *msg;
1815 struct ControllerLinkData *data;
1816 uint32_t slave_host_id;
1817 uint32_t delegated_host_id;
1820 GNUNET_assert (GNUNET_YES ==
1821 GNUNET_TESTBED_is_host_registered_ (delegated_host, master));
1822 slave_host_id = GNUNET_TESTBED_host_get_id_ (
1823 (NULL != slave_host) ? slave_host : master->host);
1824 delegated_host_id = GNUNET_TESTBED_host_get_id_ (delegated_host);
1825 if ((NULL != slave_host) && (0 != slave_host_id))
1826 GNUNET_assert (GNUNET_YES ==
1827 GNUNET_TESTBED_is_host_registered_ (slave_host, master));
1828 msg_size = sizeof (struct GNUNET_TESTBED_ControllerLinkRequest);
1829 msg = GNUNET_malloc (msg_size);
1830 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS);
1831 msg->header.size = htons (msg_size);
1832 msg->delegated_host_id = htonl (delegated_host_id);
1833 msg->slave_host_id = htonl (slave_host_id);
1834 msg->is_subordinate = (GNUNET_YES == is_subordinate) ? 1 : 0;
1835 data = GNUNET_new (struct ControllerLinkData);
1837 data->host_id = delegated_host_id;
1838 opc = GNUNET_new (struct OperationContext);
1841 opc->type = OP_LINK_CONTROLLERS;
1842 opc->id = GNUNET_TESTBED_get_next_op_id (opc->c);
1843 opc->state = OPC_STATE_INIT;
1844 opc->op_cls = op_cls;
1845 msg->operation_id = GNUNET_htonll (opc->id);
1846 opc->op = GNUNET_TESTBED_operation_create_ (opc,
1847 &opstart_link_controllers,
1848 &oprelease_link_controllers);
1849 GNUNET_TESTBED_operation_queue_insert_ (master->opq_parallel_operations,
1851 GNUNET_TESTBED_operation_begin_wait_ (opc->op);
1857 * Like GNUNET_TESTBED_get_slave_config(), however without the host registration
1858 * check. Another difference is that this function takes the id of the slave
1861 * @param op_cls the closure for the operation
1862 * @param master the handle to master controller
1863 * @param slave_host_id id of the host where the slave controller is running to
1864 * the slave_host should remain valid until this operation is cancelled
1865 * or marked as finished
1866 * @return the operation handle;
1868 struct GNUNET_TESTBED_Operation *
1869 GNUNET_TESTBED_get_slave_config_ (void *op_cls,
1870 struct GNUNET_TESTBED_Controller *master,
1871 uint32_t slave_host_id)
1873 struct OperationContext *opc;
1874 struct GetSlaveConfigData *data;
1876 data = GNUNET_new (struct GetSlaveConfigData);
1877 data->slave_id = slave_host_id;
1878 opc = GNUNET_new (struct OperationContext);
1879 opc->state = OPC_STATE_INIT;
1881 opc->id = GNUNET_TESTBED_get_next_op_id (master);
1882 opc->type = OP_GET_SLAVE_CONFIG;
1884 opc->op_cls = op_cls;
1885 opc->op = GNUNET_TESTBED_operation_create_ (opc,
1886 &opstart_get_slave_config,
1887 &oprelease_get_slave_config);
1888 GNUNET_TESTBED_operation_queue_insert_ (master->opq_parallel_operations,
1890 GNUNET_TESTBED_operation_begin_wait_ (opc->op);
1896 * Function to acquire the configuration of a running slave controller. The
1897 * completion of the operation is signalled through the controller_cb from
1898 * GNUNET_TESTBED_controller_connect(). If the operation is successful the
1899 * handle to the configuration is available in the generic pointer of
1900 * operation_finished field of struct GNUNET_TESTBED_EventInformation.
1902 * @param op_cls the closure for the operation
1903 * @param master the handle to master controller
1904 * @param slave_host the host where the slave controller is running; the handle
1905 * to the slave_host should remain valid until this operation is
1906 * cancelled or marked as finished
1907 * @return the operation handle; NULL if the slave_host is not registered at
1910 struct GNUNET_TESTBED_Operation *
1911 GNUNET_TESTBED_get_slave_config (void *op_cls,
1912 struct GNUNET_TESTBED_Controller *master,
1913 struct GNUNET_TESTBED_Host *slave_host)
1915 if (GNUNET_NO == GNUNET_TESTBED_is_host_registered_ (slave_host, master))
1917 return GNUNET_TESTBED_get_slave_config_ (op_cls,
1919 GNUNET_TESTBED_host_get_id_ (
1925 * Ask the testbed controller to write the current overlay topology to
1926 * a file. Naturally, the file will only contain a snapshot as the
1927 * topology may evolve all the time.
1929 * @param controller overlay controller to inspect
1930 * @param filename name of the file the topology should
1934 GNUNET_TESTBED_overlay_write_topology_to_file (
1935 struct GNUNET_TESTBED_Controller *controller,
1936 const char *filename)
1943 * Creates a helper initialization message. This function is here because we
1944 * want to use this in testing
1946 * @param trusted_ip the ip address of the controller which will be set as TRUSTED
1947 * HOST(all connections form this ip are permitted by the testbed) when
1948 * starting testbed controller at host. This can either be a single ip
1949 * address or a network address in CIDR notation.
1950 * @param hostname the hostname of the destination this message is intended for
1951 * @param cfg the configuration that has to used to start the testbed service
1953 * @return the initialization message
1955 struct GNUNET_TESTBED_HelperInit *
1956 GNUNET_TESTBED_create_helper_init_msg_ (
1957 const char *trusted_ip,
1958 const char *hostname,
1959 const struct GNUNET_CONFIGURATION_Handle *cfg)
1961 struct GNUNET_TESTBED_HelperInit *msg;
1965 size_t xconfig_size;
1966 uint16_t trusted_ip_len;
1967 uint16_t hostname_len;
1970 config = GNUNET_CONFIGURATION_serialize (cfg, &config_size);
1971 GNUNET_assert (NULL != config);
1973 GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig);
1974 GNUNET_free (config);
1975 trusted_ip_len = strlen (trusted_ip);
1976 hostname_len = (NULL == hostname) ? 0 : strlen (hostname);
1977 msg_size = xconfig_size + trusted_ip_len + 1 +
1978 sizeof (struct GNUNET_TESTBED_HelperInit);
1979 msg_size += hostname_len;
1980 msg = GNUNET_realloc (xconfig, msg_size);
1981 (void) memmove (((void *) &msg[1]) + trusted_ip_len + 1 + hostname_len,
1984 msg->header.size = htons (msg_size);
1985 msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_INIT);
1986 msg->trusted_ip_size = htons (trusted_ip_len);
1987 msg->hostname_size = htons (hostname_len);
1988 msg->config_size = htons (config_size);
1989 (void) strcpy ((char *) &msg[1], trusted_ip);
1990 if (0 != hostname_len)
1991 GNUNET_memcpy ((char *) &msg[1] + trusted_ip_len + 1,
1999 * This function is used to signal that the event information (struct
2000 * GNUNET_TESTBED_EventInformation) from an operation has been fully processed
2001 * i.e. if the event callback is ever called for this operation. If the event
2002 * callback for this operation has not yet been called, calling this function
2003 * cancels the operation, frees its resources and ensures the no event is
2004 * generated with respect to this operation. Note that however cancelling an
2005 * operation does NOT guarantee that the operation will be fully undone (or that
2006 * nothing ever happened).
2008 * This function MUST be called for every operation to fully remove the
2009 * operation from the operation queue. After calling this function, if
2010 * operation is completed and its event information is of type
2011 * GNUNET_TESTBED_ET_OPERATION_FINISHED, the 'op_result' becomes invalid (!).
2013 * If the operation is generated from GNUNET_TESTBED_service_connect() then
2014 * calling this function on such as operation calls the disconnect adapter if
2015 * the connect adapter was ever called.
2017 * @param operation operation to signal completion or cancellation
2020 GNUNET_TESTBED_operation_done (struct GNUNET_TESTBED_Operation *operation)
2022 (void) exop_check (operation);
2023 GNUNET_TESTBED_operation_release_ (operation);
2028 * Generates configuration by uncompressing configuration in given message. The
2029 * given message should be of the following types:
2030 * #GNUNET_MESSAGE_TYPE_TESTBED_PEER_INFORMATION,
2031 * #GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION,
2032 * #GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST,
2033 * #GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS,
2034 * #GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS_RESULT,
2036 * FIXME: This API is incredibly ugly.
2038 * @param msg the message containing compressed configuration
2039 * @return handle to the parsed configuration; NULL upon error while parsing the message
2041 struct GNUNET_CONFIGURATION_Handle *
2042 GNUNET_TESTBED_extract_config_ (const struct GNUNET_MessageHeader *msg)
2044 struct GNUNET_CONFIGURATION_Handle *cfg;
2051 switch (ntohs (msg->type))
2053 case GNUNET_MESSAGE_TYPE_TESTBED_PEER_INFORMATION: {
2054 const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *imsg;
2057 (const struct GNUNET_TESTBED_PeerConfigurationInformationMessage *) msg;
2058 data_len = (uLong) ntohs (imsg->config_size);
2060 ntohs (imsg->header.size) -
2061 sizeof (struct GNUNET_TESTBED_PeerConfigurationInformationMessage);
2062 xdata = (const Bytef *) &imsg[1];
2065 case GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION: {
2066 const struct GNUNET_TESTBED_SlaveConfiguration *imsg;
2068 imsg = (const struct GNUNET_TESTBED_SlaveConfiguration *) msg;
2069 data_len = (uLong) ntohs (imsg->config_size);
2070 xdata_len = ntohs (imsg->header.size) -
2071 sizeof (struct GNUNET_TESTBED_SlaveConfiguration);
2072 xdata = (const Bytef *) &imsg[1];
2075 case GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST: {
2076 const struct GNUNET_TESTBED_AddHostMessage *imsg;
2079 imsg = (const struct GNUNET_TESTBED_AddHostMessage *) msg;
2080 data_len = (uLong) ntohs (imsg->config_size);
2081 osize = sizeof (struct GNUNET_TESTBED_AddHostMessage) +
2082 ntohs (imsg->username_length) + ntohs (imsg->hostname_length);
2083 xdata_len = ntohs (imsg->header.size) - osize;
2084 xdata = (const Bytef *) ((const void *) imsg + osize);
2087 case GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS_RESULT: {
2088 const struct GNUNET_TESTBED_ControllerLinkResponse *imsg;
2090 imsg = (const struct GNUNET_TESTBED_ControllerLinkResponse *) msg;
2091 data_len = ntohs (imsg->config_size);
2092 xdata_len = ntohs (imsg->header.size) -
2093 sizeof (const struct GNUNET_TESTBED_ControllerLinkResponse);
2094 xdata = (const Bytef *) &imsg[1];
2097 case GNUNET_MESSAGE_TYPE_TESTBED_CREATE_PEER: {
2098 const struct GNUNET_TESTBED_PeerCreateMessage *imsg;
2100 imsg = (const struct GNUNET_TESTBED_PeerCreateMessage *) msg;
2101 data_len = ntohs (imsg->config_size);
2102 xdata_len = ntohs (imsg->header.size) -
2103 sizeof (struct GNUNET_TESTBED_PeerCreateMessage);
2104 xdata = (const Bytef *) &imsg[1];
2107 case GNUNET_MESSAGE_TYPE_TESTBED_RECONFIGURE_PEER: {
2108 const struct GNUNET_TESTBED_PeerReconfigureMessage *imsg;
2110 imsg = (const struct GNUNET_TESTBED_PeerReconfigureMessage *) msg;
2111 data_len = ntohs (imsg->config_size);
2112 xdata_len = ntohs (imsg->header.size) -
2113 sizeof (struct GNUNET_TESTBED_PeerReconfigureMessage);
2114 xdata = (const Bytef *) &imsg[1];
2120 data = GNUNET_malloc (data_len);
2121 if (Z_OK != (ret = uncompress (data, &data_len, xdata, xdata_len)))
2124 GNUNET_break_op (0); /* Un-compression failure */
2127 cfg = GNUNET_CONFIGURATION_create ();
2128 if (GNUNET_OK != GNUNET_CONFIGURATION_deserialize (cfg,
2129 (const char *) data,
2134 GNUNET_break_op (0); /* De-serialization failure */
2143 * Checks the integrity of the OperationFailureEventMessage and if good returns
2144 * the error message it contains.
2146 * @param msg the OperationFailureEventMessage
2147 * @return the error message
2150 GNUNET_TESTBED_parse_error_string_ (
2151 const struct GNUNET_TESTBED_OperationFailureEventMessage *msg)
2156 msize = ntohs (msg->header.size);
2157 if (sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage) >= msize)
2159 msize -= sizeof (struct GNUNET_TESTBED_OperationFailureEventMessage);
2160 emsg = (const char *) &msg[1];
2161 if ('\0' != emsg[msize - 1])
2171 * Function to return the operation id for a controller. The operation id is
2172 * created from the controllers host id and its internal operation counter.
2174 * @param controller the handle to the controller whose operation id has to be incremented
2175 * @return the incremented operation id.
2178 GNUNET_TESTBED_get_next_op_id (struct GNUNET_TESTBED_Controller *controller)
2182 op_id = (uint64_t) GNUNET_TESTBED_host_get_id_ (controller->host);
2183 op_id = op_id << 32;
2184 op_id |= (uint64_t) controller->operation_counter++;
2190 * Function called when a shutdown peers operation is ready
2192 * @param cls the closure from GNUNET_TESTBED_operation_create_()
2195 opstart_shutdown_peers (void *cls)
2197 struct OperationContext *opc = cls;
2198 struct GNUNET_MQ_Envelope *env;
2199 struct GNUNET_TESTBED_ShutdownPeersMessage *msg;
2201 opc->state = OPC_STATE_STARTED;
2202 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_TESTBED_SHUTDOWN_PEERS);
2203 msg->operation_id = GNUNET_htonll (opc->id);
2204 GNUNET_TESTBED_insert_opc_ (opc->c, opc);
2205 GNUNET_MQ_send (opc->c->mq, env);
2210 * Callback which will be called when shutdown peers operation is released
2212 * @param cls the closure from GNUNET_TESTBED_operation_create_()
2215 oprelease_shutdown_peers (void *cls)
2217 struct OperationContext *opc = cls;
2221 case OPC_STATE_STARTED:
2222 GNUNET_TESTBED_remove_opc_ (opc->c, opc);
2223 /* no break; continue */
2224 case OPC_STATE_INIT:
2225 GNUNET_free (opc->data);
2227 case OPC_STATE_FINISHED:
2235 * Stops and destroys all peers. Is equivalent of calling
2236 * GNUNET_TESTBED_peer_stop() and GNUNET_TESTBED_peer_destroy() on all peers,
2237 * except that the peer stop event and operation finished event corresponding to
2238 * the respective functions are not generated. This function should be called
2239 * when there are no other pending operations. If there are pending operations,
2240 * it will return NULL
2242 * @param c the controller to send this message to
2243 * @param op_cls closure for the operation
2244 * @param cb the callback to call when all peers are stopped and destroyed
2245 * @param cb_cls the closure for the callback
2246 * @return operation handle on success; NULL if any pending operations are
2249 struct GNUNET_TESTBED_Operation *
2250 GNUNET_TESTBED_shutdown_peers (struct GNUNET_TESTBED_Controller *c,
2252 GNUNET_TESTBED_OperationCompletionCallback cb,
2255 struct OperationContext *opc;
2256 struct ShutdownPeersData *data;
2258 if (0 != GNUNET_CONTAINER_multihashmap32_size (c->opc_map))
2260 data = GNUNET_new (struct ShutdownPeersData);
2262 data->cb_cls = cb_cls;
2263 opc = GNUNET_new (struct OperationContext);
2265 opc->op_cls = op_cls;
2267 opc->id = GNUNET_TESTBED_get_next_op_id (c);
2268 opc->type = OP_SHUTDOWN_PEERS;
2269 opc->state = OPC_STATE_INIT;
2270 opc->op = GNUNET_TESTBED_operation_create_ (opc,
2271 &opstart_shutdown_peers,
2272 &oprelease_shutdown_peers);
2273 GNUNET_TESTBED_operation_queue_insert_ (opc->c->opq_parallel_operations,
2275 GNUNET_TESTBED_operation_begin_wait_ (opc->op);
2281 * Return the index of the peer inside of the total peer array,
2282 * aka. the peer's "unique ID".
2284 * @param peer Peer handle.
2286 * @return The peer's unique ID.
2289 GNUNET_TESTBED_get_index (const struct GNUNET_TESTBED_Peer *peer)
2291 return peer->unique_id;
2296 * Remove a barrier and it was the last one in the barrier hash map, destroy the
2299 * @param barrier the barrier to remove
2302 GNUNET_TESTBED_barrier_remove_ (struct GNUNET_TESTBED_Barrier *barrier)
2304 struct GNUNET_TESTBED_Controller *c = barrier->c;
2306 GNUNET_assert (NULL != c->barrier_map); /* No barriers present */
2307 GNUNET_assert (GNUNET_OK ==
2308 GNUNET_CONTAINER_multihashmap_remove (c->barrier_map,
2311 GNUNET_free (barrier->name);
2312 GNUNET_free (barrier);
2313 if (0 == GNUNET_CONTAINER_multihashmap_size (c->barrier_map))
2315 GNUNET_CONTAINER_multihashmap_destroy (c->barrier_map);
2316 c->barrier_map = NULL;
2322 * Initialise a barrier and call the given callback when the required percentage
2323 * of peers (quorum) reach the barrier OR upon error.
2325 * @param controller the handle to the controller
2326 * @param name identification name of the barrier
2327 * @param quorum the percentage of peers that is required to reach the barrier.
2328 * Peers signal reaching a barrier by calling
2329 * GNUNET_TESTBED_barrier_reached().
2330 * @param cb the callback to call when the barrier is reached or upon error.
2332 * @param cls closure for the above callback
2333 * @param echo GNUNET_YES to echo the barrier crossed status message back to the
2335 * @return barrier handle; NULL upon error
2337 struct GNUNET_TESTBED_Barrier *
2338 GNUNET_TESTBED_barrier_init_ (struct GNUNET_TESTBED_Controller *controller,
2340 unsigned int quorum,
2341 GNUNET_TESTBED_barrier_status_cb cb,
2345 struct GNUNET_TESTBED_BarrierInit *msg;
2346 struct GNUNET_MQ_Envelope *env;
2347 struct GNUNET_TESTBED_Barrier *barrier;
2348 struct GNUNET_HashCode key;
2351 GNUNET_assert (quorum <= 100);
2352 GNUNET_assert (NULL != cb);
2353 name_len = strlen (name);
2354 GNUNET_assert (0 < name_len);
2355 GNUNET_CRYPTO_hash (name, name_len, &key);
2356 if (NULL == controller->barrier_map)
2357 controller->barrier_map =
2358 GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
2360 GNUNET_CONTAINER_multihashmap_contains (controller->barrier_map, &key))
2365 LOG_DEBUG ("Initialising barrier `%s'\n", name);
2366 barrier = GNUNET_new (struct GNUNET_TESTBED_Barrier);
2367 barrier->c = controller;
2368 barrier->name = GNUNET_strdup (name);
2371 barrier->echo = echo;
2372 GNUNET_memcpy (&barrier->key, &key, sizeof (struct GNUNET_HashCode));
2373 GNUNET_assert (GNUNET_OK ==
2374 GNUNET_CONTAINER_multihashmap_put (
2375 controller->barrier_map,
2378 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
2380 env = GNUNET_MQ_msg_extra (msg,
2382 GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT);
2383 msg->quorum = (uint8_t) quorum;
2384 GNUNET_memcpy (msg->name, barrier->name, name_len);
2385 GNUNET_MQ_send (barrier->c->mq, env);
2391 * Initialise a barrier and call the given callback when the required percentage
2392 * of peers (quorum) reach the barrier OR upon error.
2394 * @param controller the handle to the controller
2395 * @param name identification name of the barrier
2396 * @param quorum the percentage of peers that is required to reach the barrier.
2397 * Peers signal reaching a barrier by calling
2398 * GNUNET_TESTBED_barrier_reached().
2399 * @param cb the callback to call when the barrier is reached or upon error.
2401 * @param cls closure for the above callback
2402 * @return barrier handle; NULL upon error
2404 struct GNUNET_TESTBED_Barrier *
2405 GNUNET_TESTBED_barrier_init (struct GNUNET_TESTBED_Controller *controller,
2407 unsigned int quorum,
2408 GNUNET_TESTBED_barrier_status_cb cb,
2411 return GNUNET_TESTBED_barrier_init_ (controller,
2423 * @param barrier the barrier handle
2426 GNUNET_TESTBED_barrier_cancel (struct GNUNET_TESTBED_Barrier *barrier)
2428 struct GNUNET_MQ_Envelope *env;
2429 struct GNUNET_TESTBED_BarrierCancel *msg;
2432 slen = strlen (barrier->name);
2434 GNUNET_MQ_msg_extra (msg, slen, GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL);
2435 GNUNET_memcpy (msg->name, barrier->name, slen);
2436 GNUNET_MQ_send (barrier->c->mq, env);
2437 GNUNET_TESTBED_barrier_remove_ (barrier);
2441 /* end of testbed_api.c */