Rev 3150 | Rev 4345 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1335 | jermar | 1 | /* |
2071 | jermar | 2 | * Copyright (c) 2006 Ondrej Palkovsky |
1335 | jermar | 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 | |||
1740 | jermar | 29 | /** @addtogroup ns |
1649 | cejka | 30 | * @{ |
31 | */ |
||
32 | |||
1335 | jermar | 33 | /** |
34 | * @file ns.c |
||
35 | * @brief Naming service for HelenOS IPC. |
||
36 | */ |
||
37 | |||
1649 | cejka | 38 | |
1353 | jermar | 39 | #include <ipc/ipc.h> |
40 | #include <ipc/ns.h> |
||
1596 | palkovsky | 41 | #include <ipc/services.h> |
1030 | palkovsky | 42 | #include <stdio.h> |
4343 | svoboda | 43 | #include <bool.h> |
1030 | palkovsky | 44 | #include <unistd.h> |
45 | #include <stdlib.h> |
||
46 | #include <errno.h> |
||
1335 | jermar | 47 | #include <assert.h> |
48 | #include <libadt/list.h> |
||
49 | #include <libadt/hash_table.h> |
||
1435 | palkovsky | 50 | #include <sysinfo.h> |
4343 | svoboda | 51 | #include <loader/loader.h> |
1435 | palkovsky | 52 | #include <ddi.h> |
53 | #include <as.h> |
||
1335 | jermar | 54 | |
3150 | svoboda | 55 | #define NAME "ns" |
1335 | jermar | 56 | |
57 | #define NS_HASH_TABLE_CHAINS 20 |
||
58 | |||
1336 | jermar | 59 | static int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call); |
2476 | jermar | 60 | static int connect_to_service(ipcarg_t service, ipc_call_t *call, |
61 | ipc_callid_t callid); |
||
1336 | jermar | 62 | |
4343 | svoboda | 63 | void register_clonable(ipcarg_t service, ipcarg_t phone, ipc_call_t *call, |
64 | ipc_callid_t callid); |
||
65 | void connect_to_clonable(ipcarg_t service, ipc_call_t *call, |
||
66 | ipc_callid_t callid); |
||
67 | |||
1335 | jermar | 68 | /* Static functions implementing NS hash table operations. */ |
69 | static hash_index_t ns_hash(unsigned long *key); |
||
70 | static int ns_compare(unsigned long *key, hash_count_t keys, link_t *item); |
||
1336 | jermar | 71 | static void ns_remove(link_t *item); |
1335 | jermar | 72 | |
73 | /** Operations for NS hash table. */ |
||
74 | static hash_table_operations_t ns_hash_table_ops = { |
||
75 | .hash = ns_hash, |
||
76 | .compare = ns_compare, |
||
1336 | jermar | 77 | .remove_callback = ns_remove |
1335 | jermar | 78 | }; |
79 | |||
80 | /** NS hash table structure. */ |
||
81 | static hash_table_t ns_hash_table; |
||
82 | |||
83 | /** NS hash table item. */ |
||
84 | typedef struct { |
||
85 | link_t link; |
||
86 | ipcarg_t service; /**< Number of the service. */ |
||
87 | ipcarg_t phone; /**< Phone registered with the service. */ |
||
1336 | jermar | 88 | ipcarg_t in_phone_hash; /**< Incoming phone hash. */ |
1335 | jermar | 89 | } hashed_service_t; |
90 | |||
1596 | palkovsky | 91 | static void *clockaddr = NULL; |
92 | static void *klogaddr = NULL; |
||
93 | |||
4343 | svoboda | 94 | /** Request for connection to a clonable service. */ |
95 | typedef struct { |
||
96 | link_t link; |
||
97 | ipcarg_t service; |
||
98 | ipc_call_t call; |
||
99 | ipc_callid_t callid; |
||
100 | } cs_req_t; |
||
101 | |||
102 | /** List of clonable-service connection requests. */ |
||
103 | static link_t cs_req; |
||
104 | |||
105 | /** Return true if @a service is clonable. */ |
||
106 | static bool service_clonable(int service) |
||
1435 | palkovsky | 107 | { |
4343 | svoboda | 108 | return service == SERVICE_LOAD; |
109 | } |
||
110 | |||
111 | static void get_as_area(ipc_callid_t callid, ipc_call_t *call, char *name, void **addr) |
||
112 | { |
||
1435 | palkovsky | 113 | void *ph_addr; |
4343 | svoboda | 114 | |
1596 | palkovsky | 115 | if (!*addr) { |
2015 | jermar | 116 | ph_addr = (void *) sysinfo_value(name); |
1435 | palkovsky | 117 | if (!ph_addr) { |
2619 | jermar | 118 | ipc_answer_0(callid, ENOENT); |
1435 | palkovsky | 119 | return; |
120 | } |
||
2141 | jermar | 121 | *addr = as_get_mappable_page(PAGE_SIZE); |
4343 | svoboda | 122 | if (physmem_map(ph_addr, *addr, 1, |
123 | AS_AREA_READ | AS_AREA_CACHEABLE) != 0) { |
||
124 | ipc_answer_0(callid, ENOENT); |
||
125 | return; |
||
126 | } |
||
1435 | palkovsky | 127 | } |
2619 | jermar | 128 | ipc_answer_2(callid, EOK, (ipcarg_t) *addr, AS_AREA_READ); |
1435 | palkovsky | 129 | } |
130 | |||
1030 | palkovsky | 131 | int main(int argc, char **argv) |
132 | { |
||
3150 | svoboda | 133 | printf(NAME ": HelenOS IPC Naming Service\n"); |
134 | |||
1030 | palkovsky | 135 | ipc_call_t call; |
136 | ipc_callid_t callid; |
||
137 | |||
1719 | decky | 138 | ipcarg_t retval; |
1030 | palkovsky | 139 | |
2476 | jermar | 140 | if (!hash_table_create(&ns_hash_table, NS_HASH_TABLE_CHAINS, 3, |
141 | &ns_hash_table_ops)) { |
||
3150 | svoboda | 142 | printf(NAME ": No memory available\n"); |
1335 | jermar | 143 | return ENOMEM; |
144 | } |
||
4343 | svoboda | 145 | |
146 | list_initialize(&cs_req); |
||
3150 | svoboda | 147 | |
148 | printf(NAME ": Accepting connections\n"); |
||
1030 | palkovsky | 149 | while (1) { |
1365 | jermar | 150 | callid = ipc_wait_for_call(&call); |
1091 | palkovsky | 151 | switch (IPC_GET_METHOD(call)) { |
2677 | jermar | 152 | case IPC_M_SHARE_IN: |
1596 | palkovsky | 153 | switch (IPC_GET_ARG3(call)) { |
154 | case SERVICE_MEM_REALTIME: |
||
4343 | svoboda | 155 | get_as_area(callid, &call, "clock.faddr", &clockaddr); |
1596 | palkovsky | 156 | break; |
157 | case SERVICE_MEM_KLOG: |
||
4343 | svoboda | 158 | get_as_area(callid, &call, "klog.faddr", &klogaddr); |
1596 | palkovsky | 159 | break; |
160 | default: |
||
2619 | jermar | 161 | ipc_answer_0(callid, ENOENT); |
1596 | palkovsky | 162 | } |
1435 | palkovsky | 163 | continue; |
1089 | palkovsky | 164 | case IPC_M_PHONE_HUNGUP: |
2588 | jermar | 165 | retval = EOK; |
1089 | palkovsky | 166 | break; |
1336 | jermar | 167 | case IPC_M_CONNECT_TO_ME: |
1335 | jermar | 168 | /* |
1336 | jermar | 169 | * Server requests service registration. |
1335 | jermar | 170 | */ |
4343 | svoboda | 171 | if (service_clonable(IPC_GET_ARG1(call))) { |
172 | register_clonable(IPC_GET_ARG1(call), |
||
173 | IPC_GET_ARG5(call), &call, callid); |
||
174 | continue; |
||
175 | } else { |
||
176 | retval = register_service(IPC_GET_ARG1(call), |
||
177 | IPC_GET_ARG5(call), &call); |
||
178 | } |
||
1030 | palkovsky | 179 | break; |
1089 | palkovsky | 180 | case IPC_M_CONNECT_ME_TO: |
1336 | jermar | 181 | /* |
182 | * Client requests to be connected to a service. |
||
183 | */ |
||
4343 | svoboda | 184 | if (service_clonable(IPC_GET_ARG1(call))) { |
185 | connect_to_clonable(IPC_GET_ARG1(call), |
||
186 | &call, callid); |
||
187 | continue; |
||
188 | } else { |
||
189 | retval = connect_to_service(IPC_GET_ARG1(call), |
||
190 | &call, callid); |
||
191 | } |
||
1061 | palkovsky | 192 | break; |
1030 | palkovsky | 193 | default: |
194 | retval = ENOENT; |
||
195 | break; |
||
196 | } |
||
2476 | jermar | 197 | if (!(callid & IPC_CALLID_NOTIFICATION)) { |
2619 | jermar | 198 | ipc_answer_0(callid, retval); |
1282 | palkovsky | 199 | } |
1030 | palkovsky | 200 | } |
3150 | svoboda | 201 | |
202 | /* Not reached */ |
||
203 | return 0; |
||
1030 | palkovsky | 204 | } |
1335 | jermar | 205 | |
1338 | jermar | 206 | /** Register service. |
1336 | jermar | 207 | * |
4343 | svoboda | 208 | * @param service Service to be registered. |
209 | * @param phone Phone to be used for connections to the service. |
||
210 | * @param call Pointer to call structure. |
||
1336 | jermar | 211 | * |
4343 | svoboda | 212 | * @return Zero on success or a value from @ref errno.h. |
1336 | jermar | 213 | */ |
214 | int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call) |
||
215 | { |
||
2476 | jermar | 216 | unsigned long keys[3] = { |
217 | service, |
||
218 | call->in_phone_hash, |
||
219 | |||
220 | }; |
||
1336 | jermar | 221 | hashed_service_t *hs; |
4343 | svoboda | 222 | |
1336 | jermar | 223 | if (hash_table_find(&ns_hash_table, keys)) { |
224 | return EEXISTS; |
||
225 | } |
||
226 | |||
227 | hs = (hashed_service_t *) malloc(sizeof(hashed_service_t)); |
||
228 | if (!hs) { |
||
229 | return ENOMEM; |
||
230 | } |
||
231 | |||
232 | link_initialize(&hs->link); |
||
233 | hs->service = service; |
||
234 | hs->phone = phone; |
||
235 | hs->in_phone_hash = call->in_phone_hash; |
||
236 | hash_table_insert(&ns_hash_table, keys, &hs->link); |
||
237 | |||
238 | return 0; |
||
239 | } |
||
240 | |||
241 | /** Connect client to service. |
||
242 | * |
||
4343 | svoboda | 243 | * @param service Service to be connected to. |
244 | * @param call Pointer to call structure. |
||
245 | * @param callid Call ID of the request. |
||
1336 | jermar | 246 | * |
247 | * @return Zero on success or a value from @ref errno.h. |
||
248 | */ |
||
249 | int connect_to_service(ipcarg_t service, ipc_call_t *call, ipc_callid_t callid) |
||
250 | { |
||
251 | unsigned long keys[3] = { service, 0, 0 }; |
||
252 | link_t *hlp; |
||
253 | hashed_service_t *hs; |
||
4343 | svoboda | 254 | |
1336 | jermar | 255 | hlp = hash_table_find(&ns_hash_table, keys); |
256 | if (!hlp) { |
||
257 | return ENOENT; |
||
258 | } |
||
259 | hs = hash_table_get_instance(hlp, hashed_service_t, link); |
||
2635 | cejka | 260 | return ipc_forward_fast(callid, hs->phone, IPC_GET_ARG2(*call), |
261 | IPC_GET_ARG3(*call), 0, IPC_FF_NONE); |
||
1336 | jermar | 262 | } |
263 | |||
4343 | svoboda | 264 | /** Register clonable service. |
265 | * |
||
266 | * @param service Service to be registered. |
||
267 | * @param phone Phone to be used for connections to the service. |
||
268 | * @param call Pointer to call structure. |
||
269 | */ |
||
270 | void register_clonable(ipcarg_t service, ipcarg_t phone, ipc_call_t *call, |
||
271 | ipc_callid_t callid) |
||
272 | { |
||
273 | int rc; |
||
274 | cs_req_t *csr; |
||
275 | |||
276 | if (list_empty(&cs_req)) { |
||
277 | /* There was no pending connection request. */ |
||
278 | printf(NAME ": Unexpected clonable server.\n"); |
||
279 | ipc_answer_0(callid, EBUSY); |
||
280 | return; |
||
281 | } |
||
282 | |||
283 | csr = list_get_instance(cs_req.next, cs_req_t, link); |
||
284 | list_remove(&csr->link); |
||
285 | |||
286 | /* Currently we can only handle a single type of clonable service. */ |
||
287 | assert(csr->service == SERVICE_LOAD); |
||
288 | |||
289 | ipc_answer_0(callid, EOK); |
||
290 | |||
291 | rc = ipc_forward_fast(csr->callid, phone, IPC_GET_ARG2(csr->call), |
||
292 | IPC_GET_ARG3(csr->call), 0, IPC_FF_NONE); |
||
293 | |||
294 | free(csr); |
||
295 | } |
||
296 | |||
297 | /** Connect client to clonable service. |
||
298 | * |
||
299 | * @param service Service to be connected to. |
||
300 | * @param call Pointer to call structure. |
||
301 | * @param callid Call ID of the request. |
||
302 | * |
||
303 | * @return Zero on success or a value from @ref errno.h. |
||
304 | */ |
||
305 | void connect_to_clonable(ipcarg_t service, ipc_call_t *call, |
||
306 | ipc_callid_t callid) |
||
307 | { |
||
308 | int rc; |
||
309 | cs_req_t *csr; |
||
310 | |||
311 | assert(service == SERVICE_LOAD); |
||
312 | |||
313 | csr = malloc(sizeof(cs_req_t)); |
||
314 | if (csr == NULL) { |
||
315 | ipc_answer_0(callid, ENOMEM); |
||
316 | return; |
||
317 | } |
||
318 | |||
319 | /* Spawn a loader. */ |
||
320 | rc = loader_spawn("loader"); |
||
321 | |||
322 | if (rc < 0) { |
||
323 | free(csr); |
||
324 | ipc_answer_0(callid, rc); |
||
325 | return; |
||
326 | } |
||
327 | |||
328 | csr->service = service; |
||
329 | csr->call = *call; |
||
330 | csr->callid = callid; |
||
331 | |||
332 | /* |
||
333 | * We can forward the call only after the server we spawned connects |
||
334 | * to us. Meanwhile we might need to service more connection requests. |
||
335 | * Thus we store the call in a queue. |
||
336 | */ |
||
337 | list_append(&csr->link, &cs_req); |
||
338 | } |
||
339 | |||
1335 | jermar | 340 | /** Compute hash index into NS hash table. |
341 | * |
||
1336 | jermar | 342 | * @param key Pointer keys. However, only the first key (i.e. service number) |
343 | * is used to compute the hash index. |
||
344 | * @return Hash index corresponding to key[0]. |
||
1335 | jermar | 345 | */ |
346 | hash_index_t ns_hash(unsigned long *key) |
||
347 | { |
||
348 | assert(key); |
||
349 | return *key % NS_HASH_TABLE_CHAINS; |
||
350 | } |
||
351 | |||
352 | /** Compare a key with hashed item. |
||
353 | * |
||
1336 | jermar | 354 | * This compare function always ignores the third key. |
355 | * It exists only to make it possible to remove records |
||
356 | * originating from connection with key[1] in_phone_hash |
||
357 | * value. Note that this is close to being classified |
||
358 | * as a nasty hack. |
||
359 | * |
||
360 | * @param key Array of keys. |
||
361 | * @param keys Must be lesser or equal to 3. |
||
1335 | jermar | 362 | * @param item Pointer to a hash table item. |
363 | * @return Non-zero if the key matches the item, zero otherwise. |
||
364 | */ |
||
1336 | jermar | 365 | int ns_compare(unsigned long key[], hash_count_t keys, link_t *item) |
1335 | jermar | 366 | { |
367 | hashed_service_t *hs; |
||
368 | |||
369 | assert(key); |
||
1336 | jermar | 370 | assert(keys <= 3); |
1335 | jermar | 371 | assert(item); |
372 | |||
373 | hs = hash_table_get_instance(item, hashed_service_t, link); |
||
374 | |||
1336 | jermar | 375 | if (keys == 2) |
376 | return key[1] == hs->in_phone_hash; |
||
377 | else |
||
378 | return key[0] == hs->service; |
||
1335 | jermar | 379 | } |
1336 | jermar | 380 | |
381 | /** Perform actions after removal of item from the hash table. |
||
382 | * |
||
383 | * @param item Item that was removed from the hash table. |
||
384 | */ |
||
385 | void ns_remove(link_t *item) |
||
386 | { |
||
387 | assert(item); |
||
388 | free(hash_table_get_instance(item, hashed_service_t, link)); |
||
389 | } |
||
1719 | decky | 390 | |
1649 | cejka | 391 | /** |
392 | * @} |
||
393 | */ |