Rev 4377 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 4377 | Rev 4692 | ||
|---|---|---|---|
| Line 33... | Line 33... | ||
| 33 | /** |
33 | /** |
| 34 | * @file ns.c |
34 | * @file ns.c |
| 35 | * @brief Naming service for HelenOS IPC. |
35 | * @brief Naming service for HelenOS IPC. |
| 36 | */ |
36 | */ |
| 37 | 37 | ||
| 38 | - | ||
| 39 | #include <ipc/ipc.h> |
38 | #include <ipc/ipc.h> |
| 40 | #include <ipc/ns.h> |
- | |
| 41 | #include <ipc/services.h> |
39 | #include <ipc/services.h> |
| 42 | #include <stdio.h> |
40 | #include <ipc/ns.h> |
| 43 | #include <bool.h> |
- | |
| 44 | #include <unistd.h> |
41 | #include <unistd.h> |
| 45 | #include <stdlib.h> |
42 | #include <stdio.h> |
| 46 | #include <errno.h> |
43 | #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> |
44 | #include <as.h> |
| 54 | - | ||
| 55 | #define NAME "ns" |
- | |
| 56 | - | ||
| 57 | #define NS_HASH_TABLE_CHAINS 20 |
- | |
| 58 | - | ||
| 59 | static int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call); |
- | |
| 60 | static void connect_to_service(ipcarg_t service, ipc_call_t *call, |
- | |
| 61 | ipc_callid_t callid); |
- | |
| 62 | - | ||
| 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 | - | ||
| 68 | - | ||
| 69 | /* Static functions implementing NS hash table operations. */ |
- | |
| 70 | static hash_index_t ns_hash(unsigned long *key); |
- | |
| 71 | static int ns_compare(unsigned long *key, hash_count_t keys, link_t *item); |
- | |
| 72 | static void ns_remove(link_t *item); |
- | |
| 73 | - | ||
| 74 | /** Operations for NS hash table. */ |
- | |
| 75 | static hash_table_operations_t ns_hash_table_ops = { |
- | |
| 76 | .hash = ns_hash, |
45 | #include <ddi.h> |
| 77 | .compare = ns_compare, |
- | |
| 78 | .remove_callback = ns_remove |
- | |
| 79 | }; |
- | |
| 80 | - | ||
| 81 | /** NS hash table structure. */ |
- | |
| 82 | static hash_table_t ns_hash_table; |
- | |
| 83 | - | ||
| 84 | /** NS hash table item. */ |
- | |
| 85 | typedef struct { |
46 | #include <event.h> |
| 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. */ |
- | |
| 93 | typedef struct { |
47 | #include <macros.h> |
| 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 | - | ||
| 101 | static link_t pending_req; |
48 | #include <sysinfo.h> |
| 102 | - | ||
| 103 | /** Request for connection to a clonable service. */ |
- | |
| 104 | typedef struct { |
- | |
| 105 | link_t link; |
49 | #include "ns.h" |
| 106 | ipcarg_t service; |
50 | #include "service.h" |
| 107 | ipc_call_t call; |
51 | #include "clonable.h" |
| 108 | ipc_callid_t callid; |
52 | #include "task.h" |
| 109 | } cs_req_t; |
- | |
| 110 | - | ||
| 111 | /** List of clonable-service connection requests. */ |
- | |
| 112 | static link_t cs_req; |
- | |
| 113 | 53 | ||
| 114 | static void *clockaddr = NULL; |
54 | static void *clockaddr = NULL; |
| 115 | static void *klogaddr = NULL; |
55 | static void *klogaddr = NULL; |
| 116 | 56 | ||
| 117 | /** Return true if @a service is clonable. */ |
- | |
| 118 | static bool service_clonable(int service) |
57 | static void get_as_area(ipc_callid_t callid, ipc_call_t *call, void *ph_addr, |
| 119 | { |
- | |
| 120 | return (service == SERVICE_LOAD); |
58 | size_t pages, void **addr) |
| 121 | } |
- | |
| 122 | - | ||
| 123 | static void get_as_area(ipc_callid_t callid, ipc_call_t *call, void *ph_addr, count_t pages, void **addr) |
- | |
| 124 | { |
59 | { |
| 125 | if (ph_addr == NULL) { |
60 | if (ph_addr == NULL) { |
| 126 | ipc_answer_0(callid, ENOENT); |
61 | ipc_answer_0(callid, ENOENT); |
| 127 | return; |
62 | return; |
| 128 | } |
63 | } |
| Line 143... | Line 78... | ||
| 143 | } |
78 | } |
| 144 | 79 | ||
| 145 | ipc_answer_2(callid, EOK, (ipcarg_t) *addr, AS_AREA_READ); |
80 | ipc_answer_2(callid, EOK, (ipcarg_t) *addr, AS_AREA_READ); |
| 146 | } |
81 | } |
| 147 | 82 | ||
| 148 | /** Process pending connection requests */ |
- | |
| 149 | static void process_pending_req() |
- | |
| 150 | { |
- | |
| 151 | link_t *cur; |
- | |
| 152 | - | ||
| 153 | loop: |
- | |
| 154 | for (cur = pending_req.next; cur != &pending_req; cur = cur->next) { |
- | |
| 155 | pending_req_t *pr = list_get_instance(cur, pending_req_t, link); |
- | |
| 156 | - | ||
| 157 | unsigned long keys[3] = { |
- | |
| 158 | pr->service, |
- | |
| 159 | 0, |
- | |
| 160 | 0 |
- | |
| 161 | }; |
- | |
| 162 | - | ||
| 163 | link_t *link = hash_table_find(&ns_hash_table, keys); |
- | |
| 164 | if (!link) |
- | |
| 165 | continue; |
- | |
| 166 | - | ||
| 167 | hashed_service_t *hs = hash_table_get_instance(link, hashed_service_t, link); |
- | |
| 168 | ipcarg_t retval = ipc_forward_fast(pr->callid, hs->phone, |
- | |
| 169 | pr->arg2, pr->arg3, 0, IPC_FF_NONE); |
- | |
| 170 | - | ||
| 171 | if (!(pr->callid & IPC_CALLID_NOTIFICATION)) |
- | |
| 172 | ipc_answer_0(pr->callid, retval); |
- | |
| 173 | - | ||
| 174 | list_remove(cur); |
- | |
| 175 | free(pr); |
- | |
| 176 | goto loop; |
- | |
| 177 | } |
- | |
| 178 | } |
- | |
| 179 | - | ||
| 180 | int main(int argc, char **argv) |
83 | int main(int argc, char **argv) |
| 181 | { |
84 | { |
| 182 | printf(NAME ": HelenOS IPC Naming Service\n"); |
85 | printf(NAME ": HelenOS IPC Naming Service\n"); |
| 183 | 86 | ||
| 184 | if (!hash_table_create(&ns_hash_table, NS_HASH_TABLE_CHAINS, 3, |
- | |
| 185 | &ns_hash_table_ops)) { |
87 | int rc = service_init(); |
| 186 | printf(NAME ": No memory available for services\n"); |
88 | if (rc != EOK) |
| 187 | return ENOMEM; |
89 | return rc; |
| 188 | } |
90 | |
| - | 91 | rc = clonable_init(); |
|
| - | 92 | if (rc != EOK) |
|
| - | 93 | return rc; |
|
| 189 | 94 | ||
| 190 | list_initialize(&pending_req); |
95 | rc = task_init(); |
| - | 96 | if (rc != EOK) |
|
| 191 | list_initialize(&cs_req); |
97 | return rc; |
| 192 | 98 | ||
| 193 | printf(NAME ": Accepting connections\n"); |
99 | printf(NAME ": Accepting connections\n"); |
| - | 100 | ||
| 194 | while (true) { |
101 | while (true) { |
| - | 102 | process_pending_conn(); |
|
| 195 | process_pending_req(); |
103 | process_pending_wait(); |
| 196 | 104 | ||
| 197 | ipc_call_t call; |
105 | ipc_call_t call; |
| 198 | ipc_callid_t callid = ipc_wait_for_call(&call); |
106 | ipc_callid_t callid = ipc_wait_for_call(&call); |
| - | 107 | ||
| - | 108 | task_id_t id; |
|
| 199 | ipcarg_t retval; |
109 | ipcarg_t retval; |
| 200 | 110 | ||
| 201 | switch (IPC_GET_METHOD(call)) { |
111 | switch (IPC_GET_METHOD(call)) { |
| 202 | case IPC_M_SHARE_IN: |
112 | case IPC_M_SHARE_IN: |
| 203 | switch (IPC_GET_ARG3(call)) { |
113 | switch (IPC_GET_ARG3(call)) { |
| 204 | case SERVICE_MEM_REALTIME: |
114 | case SERVICE_MEM_REALTIME: |
| - | 115 | get_as_area(callid, &call, |
|
| 205 | get_as_area(callid, &call, sysinfo_value("clock.faddr"), 1, &clockaddr); |
116 | (void *) sysinfo_value("clock.faddr"), |
| - | 117 | 1, &clockaddr); |
|
| 206 | break; |
118 | break; |
| 207 | case SERVICE_MEM_KLOG: |
119 | case SERVICE_MEM_KLOG: |
| - | 120 | get_as_area(callid, &call, |
|
| - | 121 | (void *) sysinfo_value("klog.faddr"), |
|
| 208 | get_as_area(callid, &call, sysinfo_value("klog.faddr"), sysinfo_value("klog.pages"), &klogaddr); |
122 | sysinfo_value("klog.pages"), &klogaddr); |
| 209 | break; |
123 | break; |
| 210 | default: |
124 | default: |
| 211 | ipc_answer_0(callid, ENOENT); |
125 | ipc_answer_0(callid, ENOENT); |
| 212 | } |
126 | } |
| 213 | continue; |
127 | continue; |
| 214 | case IPC_M_PHONE_HUNGUP: |
128 | case IPC_M_PHONE_HUNGUP: |
| 215 | retval = EOK; |
129 | retval = ns_task_disconnect(&call); |
| 216 | break; |
130 | break; |
| 217 | case IPC_M_CONNECT_TO_ME: |
131 | case IPC_M_CONNECT_TO_ME: |
| 218 | /* |
132 | /* |
| 219 | * Server requests service registration. |
133 | * Server requests service registration. |
| 220 | */ |
134 | */ |
| Line 239... | Line 153... | ||
| 239 | connect_to_service(IPC_GET_ARG1(call), &call, |
153 | connect_to_service(IPC_GET_ARG1(call), &call, |
| 240 | callid); |
154 | callid); |
| 241 | continue; |
155 | continue; |
| 242 | } |
156 | } |
| 243 | break; |
157 | break; |
| - | 158 | case NS_PING: |
|
| - | 159 | retval = EOK; |
|
| - | 160 | break; |
|
| - | 161 | case NS_TASK_WAIT: |
|
| - | 162 | id = (task_id_t) |
|
| - | 163 | MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call)); |
|
| - | 164 | wait_for_task(id, &call, callid); |
|
| - | 165 | continue; |
|
| - | 166 | case NS_ID_INTRO: |
|
| - | 167 | retval = ns_task_id_intro(&call); |
|
| - | 168 | break; |
|
| - | 169 | case NS_RETVAL: |
|
| - | 170 | retval = ns_task_retval(&call); |
|
| - | 171 | break; |
|
| 244 | default: |
172 | default: |
| 245 | retval = ENOENT; |
173 | retval = ENOENT; |
| 246 | break; |
174 | break; |
| 247 | } |
175 | } |
| 248 | 176 | ||
| Line 252... | Line 180... | ||
| 252 | 180 | ||
| 253 | /* Not reached */ |
181 | /* Not reached */ |
| 254 | return 0; |
182 | return 0; |
| 255 | } |
183 | } |
| 256 | 184 | ||
| 257 | /** Register service. |
- | |
| 258 | * |
- | |
| 259 | * @param service Service to be registered. |
- | |
| 260 | * @param phone Phone to be used for connections to the service. |
- | |
| 261 | * @param call Pointer to call structure. |
- | |
| 262 | * |
- | |
| 263 | * @return Zero on success or a value from @ref errno.h. |
- | |
| 264 | * |
- | |
| 265 | */ |
- | |
| 266 | int register_service(ipcarg_t service, ipcarg_t phone, ipc_call_t *call) |
- | |
| 267 | { |
- | |
| 268 | unsigned long keys[3] = { |
- | |
| 269 | service, |
- | |
| 270 | call->in_phone_hash, |
- | |
| 271 | 0 |
- | |
| 272 | }; |
- | |
| 273 | - | ||
| 274 | if (hash_table_find(&ns_hash_table, keys)) |
- | |
| 275 | return EEXISTS; |
- | |
| 276 | - | ||
| 277 | hashed_service_t *hs = (hashed_service_t *) malloc(sizeof(hashed_service_t)); |
- | |
| 278 | if (!hs) |
- | |
| 279 | return ENOMEM; |
- | |
| 280 | - | ||
| 281 | link_initialize(&hs->link); |
- | |
| 282 | hs->service = service; |
- | |
| 283 | hs->phone = phone; |
- | |
| 284 | hs->in_phone_hash = call->in_phone_hash; |
- | |
| 285 | hash_table_insert(&ns_hash_table, keys, &hs->link); |
- | |
| 286 | - | ||
| 287 | return 0; |
- | |
| 288 | } |
- | |
| 289 | - | ||
| 290 | /** Connect client to service. |
- | |
| 291 | * |
- | |
| 292 | * @param service Service to be connected to. |
- | |
| 293 | * @param call Pointer to call structure. |
- | |
| 294 | * @param callid Call ID of the request. |
- | |
| 295 | * |
- | |
| 296 | * @return Zero on success or a value from @ref errno.h. |
- | |
| 297 | * |
- | |
| 298 | */ |
- | |
| 299 | void connect_to_service(ipcarg_t service, ipc_call_t *call, ipc_callid_t callid) |
- | |
| 300 | { |
- | |
| 301 | ipcarg_t retval; |
- | |
| 302 | unsigned long keys[3] = { |
- | |
| 303 | service, |
- | |
| 304 | 0, |
- | |
| 305 | 0 |
- | |
| 306 | }; |
- | |
| 307 | - | ||
| 308 | link_t *link = hash_table_find(&ns_hash_table, keys); |
- | |
| 309 | if (!link) { |
- | |
| 310 | if (IPC_GET_ARG4(*call) & IPC_FLAG_BLOCKING) { |
- | |
| 311 | /* Blocking connection, add to pending list */ |
- | |
| 312 | pending_req_t *pr = (pending_req_t *) malloc(sizeof(pending_req_t)); |
- | |
| 313 | if (!pr) { |
- | |
| 314 | retval = ENOMEM; |
- | |
| 315 | goto out; |
- | |
| 316 | } |
- | |
| 317 | - | ||
| 318 | pr->service = service; |
- | |
| 319 | pr->callid = callid; |
- | |
| 320 | pr->arg2 = IPC_GET_ARG2(*call); |
- | |
| 321 | pr->arg3 = IPC_GET_ARG3(*call); |
- | |
| 322 | list_append(&pr->link, &pending_req); |
- | |
| 323 | return; |
- | |
| 324 | } |
- | |
| 325 | retval = ENOENT; |
- | |
| 326 | goto out; |
- | |
| 327 | } |
- | |
| 328 | - | ||
| 329 | hashed_service_t *hs = hash_table_get_instance(link, hashed_service_t, link); |
- | |
| 330 | retval = ipc_forward_fast(callid, hs->phone, IPC_GET_ARG2(*call), |
- | |
| 331 | IPC_GET_ARG3(*call), 0, IPC_FF_NONE); |
- | |
| 332 | out: |
- | |
| 333 | if (!(callid & IPC_CALLID_NOTIFICATION)) |
- | |
| 334 | ipc_answer_0(callid, retval); |
- | |
| 335 | } |
- | |
| 336 | - | ||
| 337 | /** Register clonable service. |
- | |
| 338 | * |
- | |
| 339 | * @param service Service to be registered. |
- | |
| 340 | * @param phone Phone to be used for connections to the service. |
- | |
| 341 | * @param call Pointer to call structure. |
- | |
| 342 | * |
- | |
| 343 | */ |
- | |
| 344 | void register_clonable(ipcarg_t service, ipcarg_t phone, ipc_call_t *call, |
- | |
| 345 | ipc_callid_t callid) |
- | |
| 346 | { |
- | |
| 347 | if (list_empty(&cs_req)) { |
- | |
| 348 | /* There was no pending connection request. */ |
- | |
| 349 | printf(NAME ": Unexpected clonable server.\n"); |
- | |
| 350 | ipc_answer_0(callid, EBUSY); |
- | |
| 351 | return; |
- | |
| 352 | } |
- | |
| 353 | - | ||
| 354 | cs_req_t *csr = list_get_instance(cs_req.next, cs_req_t, link); |
- | |
| 355 | list_remove(&csr->link); |
- | |
| 356 | - | ||
| 357 | /* Currently we can only handle a single type of clonable service. */ |
- | |
| 358 | assert(csr->service == SERVICE_LOAD); |
- | |
| 359 | - | ||
| 360 | ipc_answer_0(callid, EOK); |
- | |
| 361 | - | ||
| 362 | int rc = ipc_forward_fast(csr->callid, phone, IPC_GET_ARG2(csr->call), |
- | |
| 363 | IPC_GET_ARG3(csr->call), 0, IPC_FF_NONE); |
- | |
| 364 | - | ||
| 365 | free(csr); |
- | |
| 366 | ipc_hangup(phone); |
- | |
| 367 | } |
- | |
| 368 | - | ||
| 369 | /** Connect client to clonable service. |
- | |
| 370 | * |
- | |
| 371 | * @param service Service to be connected to. |
- | |
| 372 | * @param call Pointer to call structure. |
- | |
| 373 | * @param callid Call ID of the request. |
- | |
| 374 | * |
- | |
| 375 | * @return Zero on success or a value from @ref errno.h. |
- | |
| 376 | * |
- | |
| 377 | */ |
- | |
| 378 | void connect_to_clonable(ipcarg_t service, ipc_call_t *call, |
- | |
| 379 | ipc_callid_t callid) |
- | |
| 380 | { |
- | |
| 381 | assert(service == SERVICE_LOAD); |
- | |
| 382 | - | ||
| 383 | cs_req_t *csr = malloc(sizeof(cs_req_t)); |
- | |
| 384 | if (csr == NULL) { |
- | |
| 385 | ipc_answer_0(callid, ENOMEM); |
- | |
| 386 | return; |
- | |
| 387 | } |
- | |
| 388 | - | ||
| 389 | /* Spawn a loader. */ |
- | |
| 390 | int rc = loader_spawn("loader"); |
- | |
| 391 | - | ||
| 392 | if (rc < 0) { |
- | |
| 393 | free(csr); |
- | |
| 394 | ipc_answer_0(callid, rc); |
- | |
| 395 | return; |
- | |
| 396 | } |
- | |
| 397 | - | ||
| 398 | csr->service = service; |
- | |
| 399 | csr->call = *call; |
- | |
| 400 | csr->callid = callid; |
- | |
| 401 | - | ||
| 402 | /* |
- | |
| 403 | * We can forward the call only after the server we spawned connects |
- | |
| 404 | * to us. Meanwhile we might need to service more connection requests. |
- | |
| 405 | * Thus we store the call in a queue. |
- | |
| 406 | */ |
- | |
| 407 | list_append(&csr->link, &cs_req); |
- | |
| 408 | } |
- | |
| 409 | - | ||
| 410 | /** Compute hash index into NS hash table. |
- | |
| 411 | * |
- | |
| 412 | * @param key Pointer keys. However, only the first key (i.e. service number) |
- | |
| 413 | * is used to compute the hash index. |
- | |
| 414 | * |
- | |
| 415 | * @return Hash index corresponding to key[0]. |
- | |
| 416 | * |
- | |
| 417 | */ |
- | |
| 418 | hash_index_t ns_hash(unsigned long *key) |
- | |
| 419 | { |
- | |
| 420 | assert(key); |
- | |
| 421 | return (*key % NS_HASH_TABLE_CHAINS); |
- | |
| 422 | } |
- | |
| 423 | - | ||
| 424 | /** Compare a key with hashed item. |
- | |
| 425 | * |
- | |
| 426 | * This compare function always ignores the third key. |
- | |
| 427 | * It exists only to make it possible to remove records |
- | |
| 428 | * originating from connection with key[1] in_phone_hash |
- | |
| 429 | * value. Note that this is close to being classified |
- | |
| 430 | * as a nasty hack. |
- | |
| 431 | * |
- | |
| 432 | * @param key Array of keys. |
- | |
| 433 | * @param keys Must be lesser or equal to 3. |
- | |
| 434 | * @param item Pointer to a hash table item. |
- | |
| 435 | * |
- | |
| 436 | * @return Non-zero if the key matches the item, zero otherwise. |
- | |
| 437 | * |
- | |
| 438 | */ |
- | |
| 439 | int ns_compare(unsigned long key[], hash_count_t keys, link_t *item) |
- | |
| 440 | { |
- | |
| 441 | assert(key); |
- | |
| 442 | assert(keys <= 3); |
- | |
| 443 | assert(item); |
- | |
| 444 | - | ||
| 445 | hashed_service_t *hs = hash_table_get_instance(item, hashed_service_t, link); |
- | |
| 446 | - | ||
| 447 | if (keys == 2) |
- | |
| 448 | return key[1] == hs->in_phone_hash; |
- | |
| 449 | else |
- | |
| 450 | return key[0] == hs->service; |
- | |
| 451 | } |
- | |
| 452 | - | ||
| 453 | /** Perform actions after removal of item from the hash table. |
- | |
| 454 | * |
- | |
| 455 | * @param item Item that was removed from the hash table. |
- | |
| 456 | * |
- | |
| 457 | */ |
- | |
| 458 | void ns_remove(link_t *item) |
- | |
| 459 | { |
- | |
| 460 | assert(item); |
- | |
| 461 | free(hash_table_get_instance(item, hashed_service_t, link)); |
- | |
| 462 | } |
- | |
| 463 | - | ||
| 464 | /** |
185 | /** |
| 465 | * @} |
186 | * @} |
| 466 | */ |
187 | */ |