/*
* Copyright (c) 2009 Martin Decky
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup ns
* @{
*/
#include <async.h>
#include <ipc/services.h>
#include <adt/list.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <loader/loader.h>
#include "clonable.h"
#include "ns.h"
/** Request for connection to a clonable service. */
typedef struct {
link_t link;
service_t service;
iface_t iface;
ipc_call_t call;
} cs_req_t;
/** List of clonable-service connection requests. */
static list_t cs_req;
errno_t ns_clonable_init(void)
{
list_initialize(&cs_req);
return EOK;
}
/** Return true if @a service is clonable. */
bool ns_service_is_clonable(service_t service, iface_t iface)
{
return (service == SERVICE_LOADER) && (iface == INTERFACE_LOADER);
}
/** Register clonable service.
*
* @param call Pointer to call structure.
*
*/
void ns_clonable_register(ipc_call_t *call)
{
link_t *req_link = list_first(&cs_req);
if (req_link == NULL) {
/* There was no pending connection request. */
printf("%s: Unexpected clonable server.\n", NAME);
async_answer_0(call, EBUSY);
return;
}
cs_req_t *csr = list_get_instance(req_link, cs_req_t, link);
list_remove(req_link);
/* Currently we can only handle a single type of clonable service. */
assert(ns_service_is_clonable(csr->service, csr->iface));
async_answer_0(call, EOK);
async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
if (sess == NULL)
async_answer_0(call, EIO);
async_exch_t *exch = async_exchange_begin(sess);
async_forward_1(&csr->call, exch, csr->iface,
ipc_get_arg3(&csr->call), IPC_FF_NONE);
async_exchange_end(exch);
free(csr);
async_hangup(sess);
}
/** Connect client to clonable service.
*
* @param service Service to be connected to.
* @param iface Interface to be connected to.
* @param call Pointer to call structure.
*
* @return Zero on success or a value from @ref errno.h.
*
*/
void ns_clonable_forward(service_t service, iface_t iface, ipc_call_t *call)
{
assert(ns_service_is_clonable(service, iface));
cs_req_t *csr = malloc(sizeof(cs_req_t));
if (csr == NULL) {
async_answer_0(call, ENOMEM);
return;
}
/* Spawn a loader. */
errno_t rc = loader_spawn("loader");
if (rc != EOK) {
free(csr);
async_answer_0(call, rc);
return;
}
link_initialize(&csr->link);
csr->service = service;
csr->iface = iface;
csr->call = *call;
/*
* We can forward the call only after the server we spawned connects
* to us. Meanwhile we might need to service more connection requests.
* Thus we store the call in a queue.
*/
list_append(&csr->link, &cs_req);
}
/**
* @}
*/