source: mainline/uspace/srv/ns/ns.c@ 482c86f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 482c86f was 482c86f, checked in by Jiri Svoboda <jirik.svoboda@…>, 16 years ago

NS was forgetting to hangup phones. One phone was leaked for every load attempt and when all 16 phones were exhausted, NS would lock up.

  • Property mode set to 100644
File size: 11.5 KB
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup ns
30 * @{
31 */
32
33/**
34 * @file ns.c
35 * @brief Naming service for HelenOS IPC.
36 */
37
38
39#include <ipc/ipc.h>
40#include <ipc/ns.h>
41#include <ipc/services.h>
42#include <stdio.h>
43#include <bool.h>
44#include <unistd.h>
45#include <stdlib.h>
46#include <errno.h>
47#include <assert.h>
48#include <libadt/list.h>
49#include <libadt/hash_table.h>
50#include <sysinfo.h>
51#include <loader/loader.h>
52#include <ddi.h>
53#include <as.h>
54
55#define NAME "ns"
56
57#define NS_HASH_TABLE_CHAINS 20
58
59static int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call);
60static void connect_to_service(ipcarg_t service, ipc_call_t *call,
61 ipc_callid_t callid);
62
63void register_clonable(ipcarg_t service, ipcarg_t phone, ipc_call_t *call,
64 ipc_callid_t callid);
65void connect_to_clonable(ipcarg_t service, ipc_call_t *call,
66 ipc_callid_t callid);
67
68
69/* Static functions implementing NS hash table operations. */
70static hash_index_t ns_hash(unsigned long *key);
71static int ns_compare(unsigned long *key, hash_count_t keys, link_t *item);
72static void ns_remove(link_t *item);
73
74/** Operations for NS hash table. */
75static hash_table_operations_t ns_hash_table_ops = {
76 .hash = ns_hash,
77 .compare = ns_compare,
78 .remove_callback = ns_remove
79};
80
81/** NS hash table structure. */
82static hash_table_t ns_hash_table;
83
84/** NS hash table item. */
85typedef struct {
86 link_t link;
87 ipcarg_t service; /**< Number of the service. */
88 ipcarg_t phone; /**< Phone registered with the service. */
89 ipcarg_t in_phone_hash; /**< Incoming phone hash. */
90} hashed_service_t;
91
92/** Pending connection structure. */
93typedef struct {
94 link_t link;
95 ipcarg_t service; /**< Number of the service. */
96 ipc_callid_t callid; /**< Call ID waiting for the connection */
97 ipcarg_t arg2; /**< Second argument */
98 ipcarg_t arg3; /**< Third argument */
99} pending_req_t;
100
101static link_t pending_req;
102
103/** Request for connection to a clonable service. */
104typedef struct {
105 link_t link;
106 ipcarg_t service;
107 ipc_call_t call;
108 ipc_callid_t callid;
109} cs_req_t;
110
111/** List of clonable-service connection requests. */
112static link_t cs_req;
113
114static void *clockaddr = NULL;
115static void *klogaddr = NULL;
116
117/** Return true if @a service is clonable. */
118static bool service_clonable(int service)
119{
120 return (service == SERVICE_LOAD);
121}
122
123static void get_as_area(ipc_callid_t callid, ipc_call_t *call, char *name, void **addr)
124{
125 void *ph_addr;
126
127 if (!*addr) {
128 ph_addr = (void *) sysinfo_value(name);
129 if (!ph_addr) {
130 ipc_answer_0(callid, ENOENT);
131 return;
132 }
133 *addr = as_get_mappable_page(PAGE_SIZE);
134 if (physmem_map(ph_addr, *addr, 1,
135 AS_AREA_READ | AS_AREA_CACHEABLE) != 0) {
136 ipc_answer_0(callid, ENOENT);
137 return;
138 }
139 }
140 ipc_answer_2(callid, EOK, (ipcarg_t) *addr, AS_AREA_READ);
141}
142
143/** Process pending connection requests */
144static void process_pending_req()
145{
146 link_t *cur;
147
148loop:
149 for (cur = pending_req.next; cur != &pending_req; cur = cur->next) {
150 pending_req_t *pr = list_get_instance(cur, pending_req_t, link);
151
152 unsigned long keys[3] = {
153 pr->service,
154 0,
155 0
156 };
157
158 link_t *link = hash_table_find(&ns_hash_table, keys);
159 if (!link)
160 continue;
161
162 hashed_service_t *hs = hash_table_get_instance(link, hashed_service_t, link);
163 ipcarg_t retval = ipc_forward_fast(pr->callid, hs->phone,
164 pr->arg2, pr->arg3, 0, IPC_FF_NONE);
165
166 if (!(pr->callid & IPC_CALLID_NOTIFICATION))
167 ipc_answer_0(pr->callid, retval);
168
169 list_remove(cur);
170 free(pr);
171 goto loop;
172 }
173}
174
175int main(int argc, char **argv)
176{
177 printf(NAME ": HelenOS IPC Naming Service\n");
178
179 if (!hash_table_create(&ns_hash_table, NS_HASH_TABLE_CHAINS, 3,
180 &ns_hash_table_ops)) {
181 printf(NAME ": No memory available for services\n");
182 return ENOMEM;
183 }
184
185 list_initialize(&pending_req);
186 list_initialize(&cs_req);
187
188 printf(NAME ": Accepting connections\n");
189 while (true) {
190 process_pending_req();
191
192 ipc_call_t call;
193 ipc_callid_t callid = ipc_wait_for_call(&call);
194 ipcarg_t retval;
195
196 switch (IPC_GET_METHOD(call)) {
197 case IPC_M_SHARE_IN:
198 switch (IPC_GET_ARG3(call)) {
199 case SERVICE_MEM_REALTIME:
200 get_as_area(callid, &call, "clock.faddr", &clockaddr);
201 break;
202 case SERVICE_MEM_KLOG:
203 get_as_area(callid, &call, "klog.faddr", &klogaddr);
204 break;
205 default:
206 ipc_answer_0(callid, ENOENT);
207 }
208 continue;
209 case IPC_M_PHONE_HUNGUP:
210 retval = EOK;
211 break;
212 case IPC_M_CONNECT_TO_ME:
213 /*
214 * Server requests service registration.
215 */
216 if (service_clonable(IPC_GET_ARG1(call))) {
217 register_clonable(IPC_GET_ARG1(call),
218 IPC_GET_ARG5(call), &call, callid);
219 continue;
220 } else {
221 retval = register_service(IPC_GET_ARG1(call),
222 IPC_GET_ARG5(call), &call);
223 }
224 break;
225 case IPC_M_CONNECT_ME_TO:
226 /*
227 * Client requests to be connected to a service.
228 */
229 if (service_clonable(IPC_GET_ARG1(call))) {
230 connect_to_clonable(IPC_GET_ARG1(call),
231 &call, callid);
232 continue;
233 } else {
234 connect_to_service(IPC_GET_ARG1(call), &call,
235 callid);
236 continue;
237 }
238 break;
239 default:
240 retval = ENOENT;
241 break;
242 }
243
244 if (!(callid & IPC_CALLID_NOTIFICATION))
245 ipc_answer_0(callid, retval);
246 }
247
248 /* Not reached */
249 return 0;
250}
251
252/** Register service.
253 *
254 * @param service Service to be registered.
255 * @param phone Phone to be used for connections to the service.
256 * @param call Pointer to call structure.
257 *
258 * @return Zero on success or a value from @ref errno.h.
259 *
260 */
261int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call)
262{
263 unsigned long keys[3] = {
264 service,
265 call->in_phone_hash,
266 0
267 };
268
269 if (hash_table_find(&ns_hash_table, keys))
270 return EEXISTS;
271
272 hashed_service_t *hs = (hashed_service_t *) malloc(sizeof(hashed_service_t));
273 if (!hs)
274 return ENOMEM;
275
276 link_initialize(&hs->link);
277 hs->service = service;
278 hs->phone = phone;
279 hs->in_phone_hash = call->in_phone_hash;
280 hash_table_insert(&ns_hash_table, keys, &hs->link);
281
282 return 0;
283}
284
285/** Connect client to service.
286 *
287 * @param service Service to be connected to.
288 * @param call Pointer to call structure.
289 * @param callid Call ID of the request.
290 *
291 * @return Zero on success or a value from @ref errno.h.
292 *
293 */
294void connect_to_service(ipcarg_t service, ipc_call_t *call, ipc_callid_t callid)
295{
296 ipcarg_t retval;
297 unsigned long keys[3] = {
298 service,
299 0,
300 0
301 };
302
303 link_t *link = hash_table_find(&ns_hash_table, keys);
304 if (!link) {
305 if (IPC_GET_ARG4(*call) & IPC_FLAG_BLOCKING) {
306 /* Blocking connection, add to pending list */
307 pending_req_t *pr = (pending_req_t *) malloc(sizeof(pending_req_t));
308 if (!pr) {
309 retval = ENOMEM;
310 goto out;
311 }
312
313 pr->service = service;
314 pr->callid = callid;
315 pr->arg2 = IPC_GET_ARG2(*call);
316 pr->arg3 = IPC_GET_ARG3(*call);
317 list_append(&pr->link, &pending_req);
318 return;
319 }
320 retval = ENOENT;
321 goto out;
322 }
323
324 hashed_service_t *hs = hash_table_get_instance(link, hashed_service_t, link);
325 retval = ipc_forward_fast(callid, hs->phone, IPC_GET_ARG2(*call),
326 IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
327out:
328 if (!(callid & IPC_CALLID_NOTIFICATION))
329 ipc_answer_0(callid, retval);
330}
331
332/** Register clonable service.
333 *
334 * @param service Service to be registered.
335 * @param phone Phone to be used for connections to the service.
336 * @param call Pointer to call structure.
337 *
338 */
339void register_clonable(ipcarg_t service, ipcarg_t phone, ipc_call_t *call,
340 ipc_callid_t callid)
341{
342 if (list_empty(&cs_req)) {
343 /* There was no pending connection request. */
344 printf(NAME ": Unexpected clonable server.\n");
345 ipc_answer_0(callid, EBUSY);
346 return;
347 }
348
349 cs_req_t *csr = list_get_instance(cs_req.next, cs_req_t, link);
350 list_remove(&csr->link);
351
352 /* Currently we can only handle a single type of clonable service. */
353 assert(csr->service == SERVICE_LOAD);
354
355 ipc_answer_0(callid, EOK);
356
357 int rc = ipc_forward_fast(csr->callid, phone, IPC_GET_ARG2(csr->call),
358 IPC_GET_ARG3(csr->call), 0, IPC_FF_NONE);
359
360 free(csr);
361 ipc_hangup(phone);
362}
363
364/** Connect client to clonable service.
365 *
366 * @param service Service to be connected to.
367 * @param call Pointer to call structure.
368 * @param callid Call ID of the request.
369 *
370 * @return Zero on success or a value from @ref errno.h.
371 *
372 */
373void connect_to_clonable(ipcarg_t service, ipc_call_t *call,
374 ipc_callid_t callid)
375{
376 assert(service == SERVICE_LOAD);
377
378 cs_req_t *csr = malloc(sizeof(cs_req_t));
379 if (csr == NULL) {
380 ipc_answer_0(callid, ENOMEM);
381 return;
382 }
383
384 /* Spawn a loader. */
385 int rc = loader_spawn("loader");
386
387 if (rc < 0) {
388 free(csr);
389 ipc_answer_0(callid, rc);
390 return;
391 }
392
393 csr->service = service;
394 csr->call = *call;
395 csr->callid = callid;
396
397 /*
398 * We can forward the call only after the server we spawned connects
399 * to us. Meanwhile we might need to service more connection requests.
400 * Thus we store the call in a queue.
401 */
402 list_append(&csr->link, &cs_req);
403}
404
405/** Compute hash index into NS hash table.
406 *
407 * @param key Pointer keys. However, only the first key (i.e. service number)
408 * is used to compute the hash index.
409 *
410 * @return Hash index corresponding to key[0].
411 *
412 */
413hash_index_t ns_hash(unsigned long *key)
414{
415 assert(key);
416 return (*key % NS_HASH_TABLE_CHAINS);
417}
418
419/** Compare a key with hashed item.
420 *
421 * This compare function always ignores the third key.
422 * It exists only to make it possible to remove records
423 * originating from connection with key[1] in_phone_hash
424 * value. Note that this is close to being classified
425 * as a nasty hack.
426 *
427 * @param key Array of keys.
428 * @param keys Must be lesser or equal to 3.
429 * @param item Pointer to a hash table item.
430 *
431 * @return Non-zero if the key matches the item, zero otherwise.
432 *
433 */
434int ns_compare(unsigned long key[], hash_count_t keys, link_t *item)
435{
436 assert(key);
437 assert(keys <= 3);
438 assert(item);
439
440 hashed_service_t *hs = hash_table_get_instance(item, hashed_service_t, link);
441
442 if (keys == 2)
443 return key[1] == hs->in_phone_hash;
444 else
445 return key[0] == hs->service;
446}
447
448/** Perform actions after removal of item from the hash table.
449 *
450 * @param item Item that was removed from the hash table.
451 *
452 */
453void ns_remove(link_t *item)
454{
455 assert(item);
456 free(hash_table_get_instance(item, hashed_service_t, link));
457}
458
459/**
460 * @}
461 */
Note: See TracBrowser for help on using the repository browser.