X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fscalarproduct%2Fscalarproduct_api.c;h=dcacd85c98152c7ee1f11d21977b4e6738915610;hb=27c12911f4f2aba2d90099270d70de846e83854f;hp=7c1f5394e0f26c2883aecab6016a66089580a55d;hpb=9f064f70f7aff38119ebf1b4345118cb61302f2d;p=oweals%2Fgnunet.git diff --git a/src/scalarproduct/scalarproduct_api.c b/src/scalarproduct/scalarproduct_api.c index 7c1f5394e..dcacd85c9 100644 --- a/src/scalarproduct/scalarproduct_api.c +++ b/src/scalarproduct/scalarproduct_api.c @@ -23,7 +23,7 @@ * @brief API for the scalarproduct * @author Christian Fuchs * @author Gaurav Kukreja - * + * */ #include "platform.h" #include "gnunet_util_lib.h" @@ -38,6 +38,13 @@ *** Datatype Declarations ********** **************************************************************/ +/** + * the abstraction function for our internal callback + */ +typedef void (*GNUNET_SCALARPRODUCT_ResponseMessageHandler) (void *cls, + const struct GNUNET_MessageHeader *msg, + enum GNUNET_SCALARPRODUCT_ResponseStatus status); + /** * Entry in the request queue per client */ @@ -52,7 +59,7 @@ struct GNUNET_SCALARPRODUCT_ComputationHandle * This is a linked list. */ struct GNUNET_SCALARPRODUCT_ComputationHandle *prev; - + /** * Our configuration. */ @@ -71,8 +78,8 @@ struct GNUNET_SCALARPRODUCT_ComputationHandle /** * The shared session key identifying this computation */ - struct GNUNET_HashCode * key; - + struct GNUNET_HashCode key; + /** * Current transmit handle. */ @@ -88,17 +95,20 @@ struct GNUNET_SCALARPRODUCT_ComputationHandle */ struct GNUNET_SCALARPRODUCT_client_request * msg; + /** + * The msg handler callback + */ union { - /** - * Function to call after transmission of the request. - */ - GNUNET_SCALARPRODUCT_ContinuationWithStatus cont_status; - - /** - * Function to call after transmission of the request. - */ - GNUNET_SCALARPRODUCT_DatumProcessor cont_datum; + /** + * Function to call after transmission of the request. + */ + GNUNET_SCALARPRODUCT_ContinuationWithStatus cont_status; + + /** + * Function to call after transmission of the request. + */ + GNUNET_SCALARPRODUCT_DatumProcessor cont_datum; }; /** @@ -119,49 +129,30 @@ struct GNUNET_SCALARPRODUCT_ComputationHandle /** * Head of the active sessions queue */ -struct GNUNET_SCALARPRODUCT_ComputationHandle *head; +static struct GNUNET_SCALARPRODUCT_ComputationHandle *head; /** * Tail of the active sessions queue */ -struct GNUNET_SCALARPRODUCT_ComputationHandle *tail; +static struct GNUNET_SCALARPRODUCT_ComputationHandle *tail; /************************************************************** *** Function Declarations ********** **************************************************************/ -/** - * Called when a response is received from the service. After basic check - * handler in qe->response_proc is called. This functions handles the response - * to the client which used the API. - * - * @param cls Pointer to the Master Context - * @param msg Pointer to the data received in response - */ -static void -receive_cb (void *cls, const struct GNUNET_MessageHeader *msg); - -/** - * Transmits the request to the VectorProduct Sevice - * - * @param cls Closure - * @param size Size of the buffer - * @param buf Pointer to the buffer - * - * @return Size of the message sent - */ -static size_t transmit_request (void *cls, size_t size, - void *buf); +void +GNUNET_SCALARPRODUCT_cancel (struct GNUNET_SCALARPRODUCT_ComputationHandle * h); /************************************************************** *** Static Function Declarations ********** **************************************************************/ + /** - * Handles the RESULT received in reply of prepare_response from the - * service - * - * @param cls Handle to the Master Context + * Handles the STATUS received from the service for a response, does not contain a payload + * + * @param cls our Handle * @param msg Pointer to the response received + * @param status the condition the request was terminated with (eg: disconnect) */ static void process_status_message (void *cls, @@ -170,19 +161,16 @@ process_status_message (void *cls, { struct GNUNET_SCALARPRODUCT_ComputationHandle *qe = cls; - GNUNET_assert (qe != NULL); - - if (qe->cont_status != NULL) - qe->cont_status (qe->cont_cls, &qe->msg->key, status); + qe->cont_status (qe->cont_cls, status); } /** - * Handles the RESULT received in reply of prepare_response from the - * service - * - * @param cls Handle to the Master Context + * Handles the RESULT received from the service for a request, should contain a result MPI value + * + * @param cls our Handle * @param msg Pointer to the response received + * @param status the condition the request was terminated with (eg: disconnect) */ static void process_result_message (void *cls, @@ -190,111 +178,92 @@ process_result_message (void *cls, enum GNUNET_SCALARPRODUCT_ResponseStatus status) { struct GNUNET_SCALARPRODUCT_ComputationHandle *qe = cls; + const struct GNUNET_SCALARPRODUCT_client_response *message = + (const struct GNUNET_SCALARPRODUCT_client_response *) msg; + gcry_mpi_t result = NULL; + gcry_error_t rc; - GNUNET_assert (qe != NULL); - - if (msg == NULL && qe->cont_datum != NULL) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Timeout reached or session terminated.\n"); - } - if (qe->cont_datum != NULL) + if (GNUNET_SCALARPRODUCT_Status_Success == status) { - qe->cont_datum (qe->cont_cls, &qe->msg->key, &qe->msg->peer, status, (struct GNUNET_SCALARPRODUCT_client_response *) msg); + size_t product_len = ntohl (message->product_length); + result = gcry_mpi_new (0); + + if (0 < product_len) + { + gcry_mpi_t num; + size_t read = 0; + + if (0 != (rc = gcry_mpi_scan (&num, GCRYMPI_FMT_STD, &message[1], product_len, &read))) + { + LOG_GCRY(GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (result); + result = NULL; + status = GNUNET_SCALARPRODUCT_Status_InvalidResponse; + } + else + { + if (0 < message->range) + gcry_mpi_add (result, result, num); + else if (0 > message->range) + gcry_mpi_sub (result, result, num); + gcry_mpi_release (num); + } + } } + qe->cont_datum (qe->cont_cls, status, result); } /** - * Called when a response is received from the service. After basic check + * Called when a response is received from the service. After basic check, the * handler in qe->response_proc is called. This functions handles the response * to the client which used the API. - * + * * @param cls Pointer to the Master Context * @param msg Pointer to the data received in response */ static void receive_cb (void *cls, const struct GNUNET_MessageHeader *msg) { - struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls; - struct GNUNET_SCALARPRODUCT_ComputationHandle *qe; - int16_t was_transmitted; - struct GNUNET_SCALARPRODUCT_client_response *message = - (struct GNUNET_SCALARPRODUCT_client_response *) msg; - - h->in_receive = GNUNET_NO; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply from VectorProduct\n"); - - if (NULL == (qe = free_queue_head_entry (h))) - { - /** - * The queue head will be NULL if the client disconnected, - * * In case of Alice, client disconnected after sending request, before receiving response - * * In case of Bob, client disconnected after preparing response, before getting request from Alice. - */ - process_queue (h); - return; - } - - if (h->client == NULL) - { - // GKUKREJA : handle this correctly - /** - * The queue head will be NULL if the client disconnected, - * * In case of Alice, client disconnected after sending request, before receiving response - * * In case of Bob, client disconnected after preparing response, before getting request from Alice. - */ - process_queue (h); - return; - } - - was_transmitted = qe->was_transmitted; - // Control will only come here, when the request was transmitted to service, - // and service responded. - GNUNET_assert (was_transmitted == GNUNET_YES); + struct GNUNET_SCALARPRODUCT_ComputationHandle *qe = cls; + const struct GNUNET_SCALARPRODUCT_client_response *message = + (const struct GNUNET_SCALARPRODUCT_client_response *) msg; + enum GNUNET_SCALARPRODUCT_ResponseStatus status = GNUNET_SCALARPRODUCT_Status_InvalidResponse; - if (msg == NULL) + if (NULL == msg) { - LOG (GNUNET_ERROR_TYPE_WARNING, "Service responded with NULL!\n"); - qe->response_proc (qe, NULL, GNUNET_SCALARPRODUCT_Status_Failure); + LOG (GNUNET_ERROR_TYPE_WARNING, "Disconnected by Service.\n"); + status = GNUNET_SCALARPRODUCT_Status_ServiceDisconnected; } - else if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SERVICE_TO_CLIENT)) + else if (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SERVICE_TO_CLIENT != ntohs (msg->type)) { - LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid Message Received\n"); - qe->response_proc (qe, msg, GNUNET_SCALARPRODUCT_Status_InvalidResponse); + LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid message type received\n"); } - else if (ntohl (message->product_length) == 0) + else if (0 < ntohl (message->product_length) || (0 == message->range)) { // response for the responder client, successful - GNUNET_STATISTICS_update (h->stats, + GNUNET_STATISTICS_update (qe->stats, gettext_noop ("# SUC responder result messages received"), 1, GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from service without product attached.\n"); - qe->response_proc (qe, msg, GNUNET_SCALARPRODUCT_Status_Success); + status = GNUNET_SCALARPRODUCT_Status_Success; } - else if (ntohl (message->product_length) > 0) - { - // response for the requester client, successful - GNUNET_STATISTICS_update (h->stats, - gettext_noop ("# SUC requester result messages received"), 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from requester service for requester client.\n"); - qe->response_proc (qe, msg, GNUNET_SCALARPRODUCT_Status_Success); - } + if (qe->cont_datum != NULL) + qe->response_proc (qe, msg, status); + GNUNET_CONTAINER_DLL_remove (head, tail, qe); GNUNET_free (qe); - process_queue (h); } /** - * Transmits the request to the VectorProduct Sevice - * + * Transmits the request to the VectorProduct Service + * * @param cls Closure * @param size Size of the buffer * @param buf Pointer to the buffer - * + * * @return Size of the message sent */ static size_t @@ -302,31 +271,32 @@ transmit_request (void *cls, size_t size, void *buf) { struct GNUNET_SCALARPRODUCT_ComputationHandle *qe = cls; - size_t msize; - - if (buf == NULL) + + if (NULL == buf) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to transmit request to SCALARPRODUCT.\n"); GNUNET_STATISTICS_update (qe->stats, gettext_noop ("# transmission request failures"), 1, GNUNET_NO); - GNUNET_SCALARPRODUCT_disconnect (qe); + + // notify caller about the error, done here. + if (qe->cont_datum != NULL) + qe->response_proc (qe, NULL, GNUNET_SCALARPRODUCT_Status_Failure); + + GNUNET_SCALARPRODUCT_cancel (cls); return 0; } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting %u byte request to SCALARPRODUCT\n", - msize); - memcpy (buf, qe->msg, size); - GNUNET_free (qe->msg); - qe->was_transmitted = GNUNET_YES; + GNUNET_free (qe->msg); + qe->msg = NULL; qe->th = NULL; - GNUNET_CLIENT_receive (h->client, &receive_cb, h, + GNUNET_CLIENT_receive (qe->client, &receive_cb, qe, GNUNET_TIME_UNIT_FOREVER_REL); #if INSANE_STATISTICS - GNUNET_STATISTICS_update (h->stats, + GNUNET_STATISTICS_update (qe->stats, gettext_noop ("# bytes sent to scalarproduct"), 1, GNUNET_NO); #endif @@ -340,74 +310,73 @@ transmit_request (void *cls, size_t size, /** - * Used by Bob's client to cooperate with Alice, - * - * @param h handle to the master context - * @param key Session key - unique to the requesting client + * Used by Bob's client to cooperate with Alice, + * + * @param cfg the gnunet configuration handle + * @param key Session key unique to the requesting client * @param elements Array of elements of the vector * @param element_count Number of elements in the vector * @param cont Callback function * @param cont_cls Closure for the callback function + * + * @return a new handle for this computation */ struct GNUNET_SCALARPRODUCT_ComputationHandle * -GNUNET_SCALARPRODUCT_response (const struct GNUNET_CONFIGURATION_Handle *cfg, +GNUNET_SCALARPRODUCT_response (const struct GNUNET_CONFIGURATION_Handle * cfg, const struct GNUNET_HashCode * key, const int32_t * elements, uint32_t element_count, GNUNET_SCALARPRODUCT_ContinuationWithStatus cont, - void *cont_cls) + void * cont_cls) { struct GNUNET_SCALARPRODUCT_ComputationHandle *h; struct GNUNET_SCALARPRODUCT_client_request *msg; int32_t * vector; uint16_t size; uint64_t i; - - GNUNET_assert(key); - GNUNET_assert(elements); - GNUNET_assert(cont); - GNUNET_assert(element_count > 1); + GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_SCALARPRODUCT_client_request) - + element_count * sizeof (int32_t)); + + element_count * sizeof (int32_t)); h = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle); h->client = GNUNET_CLIENT_connect ("scalarproduct", cfg); if (!h->client) { LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Failed to connect to the scalarproduct service\n")); - GNUNET_free(h); + GNUNET_free (h); return NULL; } h->stats = GNUNET_STATISTICS_create ("scalarproduct-api", cfg); - if (!h->th){ + if (!h->stats) + { LOG (GNUNET_ERROR_TYPE_ERROR, - _("Failed to send a message to the statistics service\n")); - GNUNET_CLIENT_disconnect(h->client); - GNUNET_free(h); + _ ("Failed to send a message to the statistics service\n")); + GNUNET_CLIENT_disconnect (h->client); + GNUNET_free (h); return NULL; - } - + } + size = sizeof (struct GNUNET_SCALARPRODUCT_client_request) + element_count * sizeof (int32_t); - - h->cont_datum = cont; + + h->cont_status = cont; h->cont_cls = cont_cls; - h->response_proc = &process_result_message; + h->response_proc = &process_status_message; h->cfg = cfg; - h->msg = GNUNET_malloc (size); memcpy (&h->key, key, sizeof (struct GNUNET_HashCode)); - - msg = (struct GNUNET_SCALARPRODUCT_client_request*) h->msg; + + msg = (struct GNUNET_SCALARPRODUCT_client_request*) GNUNET_malloc (size); + h->msg = msg; msg->header.size = htons (size); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB); msg->element_count = htonl (element_count); - - vector = (int32_t*) &msg[1]; + + vector = (int32_t*) & msg[1]; // copy each element over to the message for (i = 0; i < element_count; i++) - vector[i] = htonl(elements[i]); + vector[i] = htonl (elements[i]); memcpy (&msg->key, key, sizeof (struct GNUNET_HashCode)); - + h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, size, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, // retry is OK in the initial stage @@ -416,10 +385,10 @@ GNUNET_SCALARPRODUCT_response (const struct GNUNET_CONFIGURATION_Handle *cfg, { LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Failed to send a message to the scalarproduct service\n")); - GNUNET_STATISTICS_destroy(h->GNUNET_YES); - GNUNET_CLIENT_disconnect(h->client); - GNUNET_free(h->msg); - GNUNET_free(h); + GNUNET_STATISTICS_destroy (h->stats, GNUNET_YES); + GNUNET_CLIENT_disconnect (h->client); + GNUNET_free (h->msg); + GNUNET_free (h); return NULL; } GNUNET_CONTAINER_DLL_insert (head, tail, h); @@ -429,9 +398,9 @@ GNUNET_SCALARPRODUCT_response (const struct GNUNET_CONFIGURATION_Handle *cfg, /** * Request by Alice's client for computing a scalar product - * - * @param h handle to the master context - * @param key Session key - unique to the requesting client + * + * @param cfg the gnunet configuration handle + * @param key Session key should be unique to the requesting client * @param peer PeerID of the other peer * @param elements Array of elements of the vector * @param element_count Number of elements in the vector @@ -439,9 +408,11 @@ GNUNET_SCALARPRODUCT_response (const struct GNUNET_CONFIGURATION_Handle *cfg, * @param mask_bytes number of bytes in the mask * @param cont Callback function * @param cont_cls Closure for the callback function + * + * @return a new handle for this computation */ struct GNUNET_SCALARPRODUCT_ComputationHandle * -GNUNET_SCALARPRODUCT_request (const struct GNUNET_CONFIGURATION_Handle *cfg, +GNUNET_SCALARPRODUCT_request (const struct GNUNET_CONFIGURATION_Handle * cfg, const struct GNUNET_HashCode * key, const struct GNUNET_PeerIdentity *peer, const int32_t * elements, @@ -449,60 +420,61 @@ GNUNET_SCALARPRODUCT_request (const struct GNUNET_CONFIGURATION_Handle *cfg, const unsigned char * mask, uint32_t mask_bytes, GNUNET_SCALARPRODUCT_DatumProcessor cont, - void *cont_cls) + void * cont_cls) { struct GNUNET_SCALARPRODUCT_ComputationHandle *h; struct GNUNET_SCALARPRODUCT_client_request *msg; int32_t * vector; uint16_t size; uint64_t i; - + GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_SCALARPRODUCT_client_request) - + element_count * sizeof (int32_t) - + mask_length); - + +element_count * sizeof (int32_t) + + mask_bytes); + h = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle); h->client = GNUNET_CLIENT_connect ("scalarproduct", cfg); if (!h->client) { LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Failed to connect to the scalarproduct service\n")); - GNUNET_free(h); + GNUNET_free (h); return NULL; } h->stats = GNUNET_STATISTICS_create ("scalarproduct-api", cfg); - if (!h->th){ + if (!h->stats) + { LOG (GNUNET_ERROR_TYPE_ERROR, - _("Failed to send a message to the statistics service\n")); - GNUNET_CLIENT_disconnect(h->client); - GNUNET_free(h); + _ ("Failed to send a message to the statistics service\n")); + GNUNET_CLIENT_disconnect (h->client); + GNUNET_free (h); return NULL; - } - - size = sizeof (struct GNUNET_SCALARPRODUCT_client_request) + element_count * sizeof (int32_t) + mask_length; - + } + + size = sizeof (struct GNUNET_SCALARPRODUCT_client_request) + element_count * sizeof (int32_t) + mask_bytes; + h->cont_datum = cont; h->cont_cls = cont_cls; - h->response_proc = &process_status_message; + h->response_proc = &process_result_message; h->cfg = cfg; - h->msg = GNUNET_malloc (size); memcpy (&h->key, key, sizeof (struct GNUNET_HashCode)); - - msg = (struct GNUNET_SCALARPRODUCT_client_request*) h->msg; + + msg = (struct GNUNET_SCALARPRODUCT_client_request*) GNUNET_malloc (size); + h->msg = msg; msg->header.size = htons (size); msg->header.type = htons (GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE); - msg->element_count = htons (element_count); - msg->mask_length = htons (mask_length); - - vector = (int32_t*) &msg[1]; + msg->element_count = htonl (element_count); + msg->mask_length = htonl (mask_bytes); + + vector = (int32_t*) & msg[1]; // copy each element over to the message for (i = 0; i < element_count; i++) - vector[i] = htonl(elements[i]); + vector[i] = htonl (elements[i]); memcpy (&msg->peer, peer, sizeof (struct GNUNET_PeerIdentity)); memcpy (&msg->key, key, sizeof (struct GNUNET_HashCode)); - memcpy (&vector[element_count], mask, mask_length); - + memcpy (&vector[element_count], mask, mask_bytes); + h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, size, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, // retry is OK in the initial stage @@ -511,40 +483,63 @@ GNUNET_SCALARPRODUCT_request (const struct GNUNET_CONFIGURATION_Handle *cfg, { LOG (GNUNET_ERROR_TYPE_ERROR, _ ("Failed to send a message to the scalarproduct service\n")); - GNUNET_STATISTICS_destroy(h->GNUNET_YES); - GNUNET_CLIENT_disconnect(h->client); - GNUNET_free(h->msg); - GNUNET_free(h); + GNUNET_STATISTICS_destroy (h->stats, GNUNET_YES); + GNUNET_CLIENT_disconnect (h->client); + GNUNET_free (h->msg); + GNUNET_free (h); return NULL; } GNUNET_CONTAINER_DLL_insert (head, tail, h); return h; } + /** - * Disconnect from the scalarproduct service. - * - * @param h handle to the scalarproduct + * Cancel an ongoing computation or revoke our collaboration offer. + * Closes the connection to the service + * + * @param h computation handle to terminate */ void -GNUNET_SCALARPRODUCT_disconnect (struct GNUNET_SCALARPRODUCT_ComputationHandle * h) +GNUNET_SCALARPRODUCT_cancel (struct GNUNET_SCALARPRODUCT_ComputationHandle * h) { struct GNUNET_SCALARPRODUCT_ComputationHandle * qe; - LOG (GNUNET_ERROR_TYPE_INFO, - "Disconnecting from VectorProduct\n"); - for (qe = head; head != NULL; qe = head) { - GNUNET_CONTAINER_DLL_remove (head, tail, qe); - if (NULL == qe->th) - GNUNET_CLIENT_notify_transmit_ready_cancel(qe->th); - GNUNET_CLIENT_disconnect (h->client); - GNUNET_STATISTICS_destroy (h->stats, GNUNET_YES); - qe->response_proc (qe, NULL, GNUNET_SCALARPRODUCT_Status_ServiceDisconnected); - GNUNET_free(qe->msg); - GNUNET_free(qe); + if (qe == h) + { + GNUNET_CONTAINER_DLL_remove (head, tail, qe); + if (NULL != qe->th) + GNUNET_CLIENT_notify_transmit_ready_cancel (qe->th); + GNUNET_CLIENT_disconnect (qe->client); + GNUNET_STATISTICS_destroy (qe->stats, GNUNET_YES); + GNUNET_free_non_null (qe->msg); + GNUNET_free (qe); + break; + } + } +} +/** + * Cancel ALL ongoing computation or revoke our collaboration offer. + * Closes ALL connections to the service + */ +void +GNUNET_SCALARPRODUCT_disconnect () +{ + struct GNUNET_SCALARPRODUCT_ComputationHandle * qe; + + LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting from VectorProduct\n"); + for (qe = head; head != NULL; qe = head) + { + GNUNET_CONTAINER_DLL_remove (head, tail, qe); + if (NULL != qe->th) + GNUNET_CLIENT_notify_transmit_ready_cancel (qe->th); + GNUNET_CLIENT_disconnect (qe->client); + GNUNET_STATISTICS_destroy (qe->stats, GNUNET_YES); + GNUNET_free_non_null (qe->msg); + GNUNET_free (qe); } } -/* end of ext_api.c */ +/* end of scalarproduct_api.c */