/**
 * 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;
}
Esempio n. 2
0
/**
 * 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);
}
Esempio n. 3
0
/**
 * 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);
  }
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
/**
 * 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);
}
Esempio n. 8
0
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;
}
Esempio n. 11
0
/**
 * 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);
}
Esempio n. 12
0
File: mq.c Progetto: tg-x/gnunet
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);
}
Esempio n. 13
0
/**
 * 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);
}
Esempio n. 14
0
/**
 * 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);
}
Esempio n. 15
0
/**
 * 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);
  }
}
Esempio n. 16
0
/**
 * 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);
  }
}