0,0 → 1,254 |
/* |
* 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 <ipc/ipc.h> |
#include <libadt/hash_table.h> |
#include <assert.h> |
#include <errno.h> |
#include "service.h" |
#include "ns.h" |
|
#define SERVICE_HASH_TABLE_CHAINS 20 |
|
/** Service hash table item. */ |
typedef struct { |
link_t link; |
ipcarg_t service; /**< Number of the service. */ |
ipcarg_t phone; /**< Phone registered with the service. */ |
ipcarg_t in_phone_hash; /**< Incoming phone hash. */ |
} hashed_service_t; |
|
/** Compute hash index into service hash table. |
* |
* @param key Pointer keys. However, only the first key (i.e. service number) |
* is used to compute the hash index. |
* |
* @return Hash index corresponding to key[0]. |
* |
*/ |
static hash_index_t service_hash(unsigned long *key) |
{ |
assert(key); |
return (*key % SERVICE_HASH_TABLE_CHAINS); |
} |
|
/** Compare a key with hashed item. |
* |
* This compare function always ignores the third key. |
* It exists only to make it possible to remove records |
* originating from connection with key[1] in_phone_hash |
* value. Note that this is close to being classified |
* as a nasty hack. |
* |
* @param key Array of keys. |
* @param keys Must be lesser or equal to 3. |
* @param item Pointer to a hash table item. |
* |
* @return Non-zero if the key matches the item, zero otherwise. |
* |
*/ |
static int service_compare(unsigned long key[], hash_count_t keys, link_t *item) |
{ |
assert(key); |
assert(keys <= 3); |
assert(item); |
|
hashed_service_t *hs = hash_table_get_instance(item, hashed_service_t, link); |
|
if (keys == 2) |
return (key[1] == hs->in_phone_hash); |
else |
return (key[0] == hs->service); |
} |
|
/** Perform actions after removal of item from the hash table. |
* |
* @param item Item that was removed from the hash table. |
* |
*/ |
static void service_remove(link_t *item) |
{ |
assert(item); |
free(hash_table_get_instance(item, hashed_service_t, link)); |
} |
|
/** Operations for service hash table. */ |
static hash_table_operations_t service_hash_table_ops = { |
.hash = service_hash, |
.compare = service_compare, |
.remove_callback = service_remove |
}; |
|
/** Service hash table structure. */ |
static hash_table_t service_hash_table; |
|
/** Pending connection structure. */ |
typedef struct { |
link_t link; |
ipcarg_t service; /**< Number of the service. */ |
ipc_callid_t callid; /**< Call ID waiting for the connection */ |
ipcarg_t arg2; /**< Second argument */ |
ipcarg_t arg3; /**< Third argument */ |
} pending_conn_t; |
|
static link_t pending_conn; |
|
int service_init(void) |
{ |
if (!hash_table_create(&service_hash_table, SERVICE_HASH_TABLE_CHAINS, |
3, &service_hash_table_ops)) { |
printf(NAME ": No memory available for services\n"); |
return ENOMEM; |
} |
|
list_initialize(&pending_conn); |
|
return EOK; |
} |
|
/** Process pending connection requests */ |
void process_pending_conn(void) |
{ |
link_t *cur; |
|
loop: |
for (cur = pending_conn.next; cur != &pending_conn; cur = cur->next) { |
pending_conn_t *pr = list_get_instance(cur, pending_conn_t, link); |
|
unsigned long keys[3] = { |
pr->service, |
0, |
0 |
}; |
|
link_t *link = hash_table_find(&service_hash_table, keys); |
if (!link) |
continue; |
|
hashed_service_t *hs = hash_table_get_instance(link, hashed_service_t, link); |
ipcarg_t retval = ipc_forward_fast(pr->callid, hs->phone, |
pr->arg2, pr->arg3, 0, IPC_FF_NONE); |
|
if (!(pr->callid & IPC_CALLID_NOTIFICATION)) |
ipc_answer_0(pr->callid, retval); |
|
list_remove(cur); |
free(pr); |
goto loop; |
} |
} |
|
/** Register service. |
* |
* @param service Service to be registered. |
* @param phone Phone to be used for connections to the service. |
* @param call Pointer to call structure. |
* |
* @return Zero on success or a value from @ref errno.h. |
* |
*/ |
int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call) |
{ |
unsigned long keys[3] = { |
service, |
call->in_phone_hash, |
0 |
}; |
|
if (hash_table_find(&service_hash_table, keys)) |
return EEXISTS; |
|
hashed_service_t *hs = (hashed_service_t *) malloc(sizeof(hashed_service_t)); |
if (!hs) |
return ENOMEM; |
|
link_initialize(&hs->link); |
hs->service = service; |
hs->phone = phone; |
hs->in_phone_hash = call->in_phone_hash; |
hash_table_insert(&service_hash_table, keys, &hs->link); |
|
return 0; |
} |
|
/** Connect client to service. |
* |
* @param service Service to be connected to. |
* @param call Pointer to call structure. |
* @param callid Call ID of the request. |
* |
* @return Zero on success or a value from @ref errno.h. |
* |
*/ |
void connect_to_service(ipcarg_t service, ipc_call_t *call, ipc_callid_t callid) |
{ |
ipcarg_t retval; |
unsigned long keys[3] = { |
service, |
0, |
0 |
}; |
|
link_t *link = hash_table_find(&service_hash_table, keys); |
if (!link) { |
if (IPC_GET_ARG4(*call) & IPC_FLAG_BLOCKING) { |
/* Blocking connection, add to pending list */ |
pending_conn_t *pr = |
(pending_conn_t *) malloc(sizeof(pending_conn_t)); |
if (!pr) { |
retval = ENOMEM; |
goto out; |
} |
|
pr->service = service; |
pr->callid = callid; |
pr->arg2 = IPC_GET_ARG2(*call); |
pr->arg3 = IPC_GET_ARG3(*call); |
list_append(&pr->link, &pending_conn); |
return; |
} |
retval = ENOENT; |
goto out; |
} |
|
hashed_service_t *hs = hash_table_get_instance(link, hashed_service_t, link); |
retval = ipc_forward_fast(callid, hs->phone, IPC_GET_ARG2(*call), |
IPC_GET_ARG3(*call), 0, IPC_FF_NONE); |
|
out: |
if (!(callid & IPC_CALLID_NOTIFICATION)) |
ipc_answer_0(callid, retval); |
} |
|
/** |
* @} |
*/ |