/** * Function called to notify a client about the socket * begin ready to queue more data. "buf" will be * NULL and "size" zero if the socket was closed for * writing in the meantime. * * @param cls closure * @param size number of bytes available in buf * @param buf where the callee should write the message * @return number of bytes written to buf */ static size_t transmit_callback (void *cls, size_t size, void *buf) { struct TransmitCallbackContext *tcc = cls; size_t msize; tcc->th = NULL; GNUNET_CONTAINER_DLL_remove (tcc_head, tcc_tail, tcc); msize = ntohs (tcc->msg->size); if (size == 0) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Transmission to client failed!\n")); GNUNET_SERVER_receive_done (tcc->client, GNUNET_SYSERR); GNUNET_SERVER_client_drop (tcc->client); GNUNET_free (tcc->msg); GNUNET_free (tcc); return 0; } GNUNET_assert (size >= msize); memcpy (buf, tcc->msg, msize); GNUNET_SERVER_receive_done (tcc->client, GNUNET_OK); GNUNET_SERVER_client_drop (tcc->client); GNUNET_free (tcc->msg); GNUNET_free (tcc); return msize; }
/** * Task to clean up and shutdown nicely * * @param cls NULL * @param tc the TaskContext from scheduler */ static void shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct MessageQueue *mq_entry; uint32_t id; shutdown_task_id = NULL; LOG_DEBUG ("Shutting down testbed service\n"); /* cleanup any remaining forwarded operations */ GST_clear_fopcq (); GST_free_lcfq (); GST_free_mctxq (); GST_free_occq (); GST_free_roccq (); GST_free_nccq (); GST_neighbour_list_clean(); GST_free_prcq (); /* Clear peer list */ GST_destroy_peers (); /* Clear route list */ GST_route_list_clear (); /* Clear GST_slave_list */ GST_slave_list_clear (); /* Clear host list */ for (id = 0; id < GST_host_list_size; id++) if (NULL != GST_host_list[id]) GNUNET_TESTBED_host_destroy (GST_host_list[id]); GNUNET_free_non_null (GST_host_list); if (NULL != GST_context) { GNUNET_free_non_null (GST_context->master_ip); if (NULL != GST_context->system) GNUNET_TESTING_system_destroy (GST_context->system, GNUNET_YES); GNUNET_SERVER_client_drop (GST_context->client); GNUNET_free (GST_context); GST_context = NULL; } if (NULL != transmit_handle) GNUNET_SERVER_notify_transmit_ready_cancel (transmit_handle); while (NULL != (mq_entry = mq_head)) { GNUNET_free (mq_entry->msg); GNUNET_SERVER_client_drop (mq_entry->client); GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry); GNUNET_free (mq_entry); } GNUNET_free_non_null (hostname); /* Free hello cache */ GST_cache_clear (); GST_connection_pool_destroy (); GNUNET_TESTBED_operation_queue_destroy_ (GST_opq_openfds); GST_opq_openfds = NULL; GST_stats_destroy (); GST_barriers_destroy (); GNUNET_CONFIGURATION_destroy (GST_config); }
/** * Function called to notify a client about the connection begin ready to queue * more data. "buf" will be NULL and "size" zero if the connection was closed * for writing in the meantime. * * @param cls NULL * @param size number of bytes available in buf * @param buf where the callee should write the message * @return number of bytes written to buf */ static size_t transmit_ready_notify (void *cls, size_t size, void *buf) { struct MessageQueue *mq_entry; transmit_handle = NULL; mq_entry = mq_head; GNUNET_assert (NULL != mq_entry); if (0 == size) return 0; GNUNET_assert (ntohs (mq_entry->msg->size) <= size); size = ntohs (mq_entry->msg->size); memcpy (buf, mq_entry->msg, size); GNUNET_free (mq_entry->msg); GNUNET_SERVER_client_drop (mq_entry->client); GNUNET_CONTAINER_DLL_remove (mq_head, mq_tail, mq_entry); GNUNET_free (mq_entry); mq_entry = mq_head; if (NULL != mq_entry) transmit_handle = GNUNET_SERVER_notify_transmit_ready (mq_entry->client, ntohs (mq_entry->msg->size), GNUNET_TIME_UNIT_FOREVER_REL, &transmit_ready_notify, NULL); return size; }
/** * Cleans up the queue used for forwarding link controllers requests */ void GST_free_lcfq () { struct LCFContextQueue *lcfq; struct LCFContext *lcf; if (NULL != lcfq_head) { if (NULL != lcf_proc_task_id) { GNUNET_SCHEDULER_cancel (lcf_proc_task_id); lcf_proc_task_id = NULL; } } GNUNET_assert (NULL == lcf_proc_task_id); for (lcfq = lcfq_head; NULL != lcfq; lcfq = lcfq_head) { lcf = lcfq->lcf; GNUNET_SERVER_client_drop (lcf->client); if (NULL != lcf->op) GNUNET_TESTBED_operation_done (lcf->op); if (NULL != lcf->timeout_task) GNUNET_SCHEDULER_cancel (lcf->timeout_task); GNUNET_free (lcf); GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq); GNUNET_free (lcfq); } }
static void first_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg) { GNUNET_assert (ok == 4); ok = 5; GNUNET_SERVER_receive_done (argclient, GNUNET_OK); GNUNET_SERVER_client_drop (argclient); argclient = NULL; }
/** * Destroy a transmission context. This function must not be called * after 'GNUNET_SERVER_transmit_context_run'. * * @param tc transmission context to destroy * @param success code to give to 'GNUNET_SERVER_receive_done' for * the client: GNUNET_OK to keep the connection open and * continue to receive * GNUNET_NO to close the connection (normal behavior) * GNUNET_SYSERR to close the connection (signal * serious error) */ void GNUNET_SERVER_transmit_context_destroy (struct GNUNET_SERVER_TransmitContext *tc, int success) { GNUNET_SERVER_receive_done (tc->client, success); GNUNET_SERVER_client_drop (tc->client); GNUNET_free_non_null (tc->buf); GNUNET_free (tc); }
static void server_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_SERVER_Client *argclient = cls; GNUNET_assert (ok == 3); ok = 4; GNUNET_SERVER_client_disconnect (argclient); GNUNET_SERVER_client_drop (argclient); }
static void server_disconnect (void *cls) { struct GNUNET_SERVER_Client *argclient = cls; GNUNET_assert (ok == 3); ok = 4; GNUNET_SERVER_client_disconnect (argclient); GNUNET_SERVER_client_drop (argclient); }
/** * Cleanup neighbour connect contexts * * @param ncc the neighbour connect context to cleanup */ static void cleanup_ncc (struct NeighbourConnectCtxt *ncc) { if (NULL != ncc->nh) GST_neighbour_get_connection_cancel (ncc->nh); if (NULL != ncc->timeout_task) GNUNET_SCHEDULER_cancel (ncc->timeout_task); GNUNET_SERVER_client_drop (ncc->client); GNUNET_CONTAINER_DLL_remove (ncc_head, ncc_tail, ncc); GNUNET_free (ncc); }
/** * Callback to signal successfull startup of the controller process * * @param cls the handle to the slave whose status is to be found here * @param cfg the configuration with which the controller has been started; * NULL if status is not GNUNET_OK * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not, * GNUNET_TESTBED_controller_stop() shouldn't be called in this case */ static void slave_status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, int status) { struct Slave *slave = cls; struct LinkControllersContext *lcc; lcc = slave->lcc; if (GNUNET_SYSERR == status) { slave->controller_proc = NULL; /* Stop all link controller forwarding tasks since we shutdown here anyway and as these tasks they depend on the operation queues which are created through GNUNET_TESTBED_controller_connect() and in kill_slave() we call the destructor function GNUNET_TESTBED_controller_disconnect() */ GST_free_lcfq (); kill_slave (slave); destroy_slave (slave); slave = NULL; LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected slave shutdown\n"); GNUNET_SCHEDULER_shutdown (); /* We too shutdown */ goto clean_lcc; } slave->controller = GNUNET_TESTBED_controller_connect (GST_host_list[slave->host_id], EVENT_MASK, &slave_event_cb, slave); if (NULL != slave->controller) { send_controller_link_response (lcc->client, lcc->operation_id, cfg, NULL); } else { send_controller_link_response (lcc->client, lcc->operation_id, NULL, "Could not connect to delegated controller"); kill_slave (slave); destroy_slave (slave); slave = NULL; } clean_lcc: if (NULL != lcc) { if (NULL != lcc->client) { GNUNET_SERVER_receive_done (lcc->client, GNUNET_OK); GNUNET_SERVER_client_drop (lcc->client); lcc->client = NULL; } GNUNET_free (lcc); } if (NULL != slave) slave->lcc = NULL; }
/** * Task to free resources when forwarded operation has been timedout * * @param cls the ForwardedOperationContext * @param tc the task context from scheduler */ void GST_forwarded_operation_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ForwardedOperationContext *fopc = cls; GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc); LOG (GNUNET_ERROR_TYPE_DEBUG, "A forwarded operation has timed out\n"); GST_send_operation_fail_msg (fopc->client, fopc->operation_id, "A forwarded operation has timed out"); GNUNET_SERVER_client_drop (fopc->client); GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc); GNUNET_free (fopc); }
static void server_client_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) { struct ServerClientSocketState *state = impl_state; if (NULL != state->th) { GNUNET_SERVER_notify_transmit_ready_cancel (state->th); state->th = NULL; } GNUNET_assert (NULL != mq); GNUNET_assert (NULL != state); GNUNET_SERVER_client_drop (state->client); GNUNET_free (state); }
/** * Callback to relay the reply msg of a forwarded operation back to the client * * @param cls ForwardedOperationContext * @param msg the message to relay */ void GST_forwarded_operation_reply_relay (void *cls, const struct GNUNET_MessageHeader *msg) { struct ForwardedOperationContext *fopc = cls; struct GNUNET_MessageHeader *dup_msg; uint16_t msize; msize = ntohs (msg->size); LOG_DEBUG ("Relaying message with type: %u, size: %u\n", ntohs (msg->type), msize); dup_msg = GNUNET_copy_message (msg); GST_queue_message (fopc->client, dup_msg); GNUNET_SERVER_client_drop (fopc->client); GNUNET_SCHEDULER_cancel (fopc->timeout_task); GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc); GNUNET_free (fopc); }
/** * Send a reply of type #MY_TYPE from the server to the client. * Checks that we are in the right phase and transmits the * reply. Cleans up #argclient state. * * @param cls NULL * @param size number of bytes we are allowed to send * @param buf where to copy the reply * @return number of bytes written to @a buf */ static size_t reply_msg (void *cls, size_t size, void *buf) { struct GNUNET_MessageHeader msg; GNUNET_assert (3 == ok); ok = 4; GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); msg.type = htons (MY_TYPE); msg.size = htons (sizeof (struct GNUNET_MessageHeader)); GNUNET_memcpy (buf, &msg, sizeof (struct GNUNET_MessageHeader)); GNUNET_assert (NULL != argclient); GNUNET_SERVER_receive_done (argclient, GNUNET_OK); GNUNET_SERVER_client_drop (argclient); argclient = NULL; return sizeof (struct GNUNET_MessageHeader); }
/** * Clears the forwarded operations queue */ void GST_clear_fopcq () { struct ForwardedOperationContext *fopc; while (NULL != (fopc = fopcq_head)) { GNUNET_CONTAINER_DLL_remove (fopcq_head, fopcq_tail, fopc); GNUNET_TESTBED_forward_operation_msg_cancel_ (fopc->opc); if (NULL != fopc->timeout_task) GNUNET_SCHEDULER_cancel (fopc->timeout_task); GNUNET_SERVER_client_drop (fopc->client); switch (fopc->type) { case OP_PEER_CREATE: GNUNET_free (fopc->cls); break; case OP_SHUTDOWN_PEERS: { struct HandlerContext_ShutdownPeers *hc = fopc->cls; GNUNET_assert (0 < hc->nslaves); hc->nslaves--; if (0 == hc->nslaves) GNUNET_free (hc); } break; case OP_PEER_START: case OP_PEER_STOP: case OP_PEER_DESTROY: case OP_PEER_INFO: case OP_OVERLAY_CONNECT: case OP_LINK_CONTROLLERS: case OP_GET_SLAVE_CONFIG: case OP_MANAGE_SERVICE: case OP_PEER_RECONFIGURE: break; case OP_FORWARDED: GNUNET_assert (0); }; GNUNET_free (fopc); } }
/** * Helper function for incremental transmission of the response. */ static size_t transmit_response (void *cls, size_t size, void *buf) { struct GNUNET_SERVER_TransmitContext *tc = cls; size_t msize; if (NULL == buf) { GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR); return 0; } if (tc->total - tc->off > size) msize = size; else msize = tc->total - tc->off; memcpy (buf, &tc->buf[tc->off], msize); tc->off += msize; if (tc->total == tc->off) { GNUNET_SERVER_receive_done (tc->client, GNUNET_OK); GNUNET_SERVER_client_drop (tc->client); GNUNET_free_non_null (tc->buf); GNUNET_free (tc); } else { if (NULL == GNUNET_SERVER_notify_transmit_ready (tc->client, GNUNET_MIN (MIN_BLOCK_SIZE, tc->total - tc->off), GNUNET_TIME_absolute_get_remaining (tc->timeout), &transmit_response, tc)) { GNUNET_break (0); GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR); } } return msize; }
/** * Function that will transmit the given datastore entry * to the client. * * @param cls closure, pointer to the client (of type GNUNET_SERVER_Client). * @param key key for the content * @param size number of bytes in data * @param data content stored * @param type type of the content * @param priority priority of the content * @param anonymity anonymity-level for the content * @param expiration expiration time for the content * @param uid unique identifier for the datum; * maybe 0 if no unique identifier is available * * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue, * GNUNET_NO to delete the item and continue (if supported) */ static int transmit_item (void *cls, const struct GNUNET_HashCode * key, uint32_t size, const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority, uint32_t anonymity, struct GNUNET_TIME_Absolute expiration, uint64_t uid) { struct GNUNET_SERVER_Client *client = cls; struct GNUNET_MessageHeader *end; struct DataMessage *dm; if (key == NULL) { /* transmit 'DATA_END' */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting `%s' message\n", "DATA_END"); end = GNUNET_new (struct GNUNET_MessageHeader); end->size = htons (sizeof (struct GNUNET_MessageHeader)); end->type = htons (GNUNET_MESSAGE_TYPE_DATASTORE_DATA_END); transmit (client, end); GNUNET_SERVER_client_drop (client); return GNUNET_OK; }
/** * Function called after the transmission is done. Notify the client that it is * OK to send the next message. * * @param cls closure * @param success #GNUNET_OK on success, #GNUNET_NO on failure, #GNUNET_SYSERR if we're not connected * @param bytes_payload bytes payload sent * @param bytes_on_wire bytes sent on wire */ static void handle_send_transmit_continuation (void *cls, int success, size_t bytes_payload, size_t bytes_on_wire) { struct SendTransmitContinuationContext *stcc = cls; struct SendOkMessage send_ok_msg; if (GNUNET_OK == success) GST_neighbours_notify_payload_sent (&stcc->target, bytes_payload); send_ok_msg.header.size = htons (sizeof (send_ok_msg)); send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); send_ok_msg.bytes_msg = htonl (bytes_payload); send_ok_msg.bytes_physical = htonl (bytes_on_wire); send_ok_msg.success = htonl (success); send_ok_msg.latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_FOREVER_REL); send_ok_msg.peer = stcc->target; GST_clients_unicast (stcc->client, &send_ok_msg.header, GNUNET_NO); GNUNET_SERVER_client_drop (stcc->client); GNUNET_free (stcc); }
/** * Task triggered whenever we receive a SIGCHLD (child * process died). * * @param cls closure, NULL if we need to self-restart * @param tc context */ static void maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ServiceList *pos; struct ServiceList *next; struct ServiceListeningInfo *sli; const char *statstr; int statcode; int ret; char c[16]; enum GNUNET_OS_ProcessStatusType statusType; unsigned long statusCode; const struct GNUNET_DISK_FileHandle *pr; pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); child_death_task = GNUNET_SCHEDULER_NO_TASK; if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) { /* shutdown scheduled us, ignore! */ child_death_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL); return; } /* consume the signal */ GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); /* check for services that died (WAITPID) */ next = running_head; while (NULL != (pos = next)) { next = pos->next; if (pos->proc == NULL) { if (GNUNET_YES == in_shutdown) free_service (pos); continue; } if ((GNUNET_SYSERR == (ret = GNUNET_OS_process_status (pos->proc, &statusType, &statusCode))) || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) || (statusType == GNUNET_OS_PROCESS_RUNNING))) continue; if (statusType == GNUNET_OS_PROCESS_EXITED) { statstr = _( /* process termination method */ "exit"); statcode = statusCode; } else if (statusType == GNUNET_OS_PROCESS_SIGNALED) { statstr = _( /* process termination method */ "signal"); statcode = statusCode; } else { statstr = _( /* process termination method */ "unknown"); statcode = 0; } if (0 != pos->killed_at.abs_value) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' took %llu ms to terminate\n"), pos->name, GNUNET_TIME_absolute_get_duration (pos->killed_at).rel_value); } GNUNET_OS_process_destroy (pos->proc); pos->proc = NULL; if (NULL != pos->killing_client) { signal_result (pos->killing_client, pos->name, GNUNET_ARM_PROCESS_DOWN); GNUNET_SERVER_client_drop (pos->killing_client); pos->killing_client = NULL; /* process can still be re-started on-demand, ensure it is re-started if there is demand */ for (sli = pos->listen_head; NULL != sli; sli = sli->next) { GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task); sli->accept_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket, &accept_connection, sli); } continue; } if (GNUNET_YES != in_shutdown) { if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0)) { /* process terminated normally, allow restart at any time */ pos->restart_at.abs_value = 0; } else { if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Service `%s' terminated with status %s/%d, will restart in %llu ms\n"), pos->name, statstr, statcode, pos->backoff.rel_value); /* schedule restart */ pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); pos->backoff = GNUNET_TIME_relative_min (EXPONENTIAL_BACKOFF_THRESHOLD, GNUNET_TIME_relative_multiply (pos->backoff, 2)); } if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) GNUNET_SCHEDULER_cancel (child_restart_task); child_restart_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL); } else { free_service (pos); } } child_death_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL); if ((NULL == running_head) && (GNUNET_YES == in_shutdown)) do_shutdown (); }
/** * The Link Controller forwarding task * * @param cls the LCFContext * @param tc the Task context from scheduler */ static void lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct LCFContext *lcf = cls; struct LCFContextQueue *lcfq; lcf_proc_task_id = NULL; switch (lcf->state) { case INIT: if (GNUNET_NO == GNUNET_TESTBED_is_host_registered_ (GST_host_list [lcf->delegated_host_id], lcf->gateway->controller)) { GST_queue_host_registration (lcf->gateway, lcf_proc_cc, lcf, GST_host_list[lcf->delegated_host_id]); } else { lcf->state = DELEGATED_HOST_REGISTERED; lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); } break; case DELEGATED_HOST_REGISTERED: if (GNUNET_NO == GNUNET_TESTBED_is_host_registered_ (GST_host_list[lcf->slave_host_id], lcf->gateway->controller)) { GST_queue_host_registration (lcf->gateway, lcf_proc_cc, lcf, GST_host_list[lcf->slave_host_id]); } else { lcf->state = SLAVE_HOST_REGISTERED; lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); } break; case SLAVE_HOST_REGISTERED: lcf->op = GNUNET_TESTBED_controller_link (lcf, lcf->gateway->controller, GST_host_list[lcf->delegated_host_id], GST_host_list[lcf->slave_host_id], lcf->is_subordinate); lcf->timeout_task = GNUNET_SCHEDULER_add_delayed (GST_timeout, &lcf_forwarded_operation_timeout, lcf); lcf->state = FINISHED; break; case FINISHED: lcfq = lcfq_head; GNUNET_assert (lcfq->lcf == lcf); GNUNET_SERVER_client_drop (lcf->client); if (NULL != lcf->op) GNUNET_TESTBED_operation_done (lcf->op); GNUNET_free (lcf); GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq); GNUNET_free (lcfq); if (NULL != lcfq_head) lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcfq_head->lcf); } }