source: mainline/uspace/srv/locsrv/locsrv.c@ 802a8c8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 802a8c8 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 34.8 KB
Line 
1/*
2 * Copyright (c) 2007 Josef Cejka
3 * Copyright (c) 2011 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
31 * @defgroup loc Location Service.
32 * @brief HelenOS location service.
33 * @{
34 */
35
36/** @file
37 */
38
39#include <ipc/services.h>
40#include <ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
44#include <stdbool.h>
45#include <fibril_synch.h>
46#include <macros.h>
47#include <stdlib.h>
48#include <str.h>
49#include <str_error.h>
50#include <ipc/loc.h>
51#include <assert.h>
52
53#include "category.h"
54#include "locsrv.h"
55
56#define NAME "loc"
57#define NULL_SERVICES 256
58
59/** Callback session */
60typedef struct {
61 link_t cb_sess_list;
62 async_sess_t *sess;
63} cb_sess_t;
64
65LIST_INITIALIZE(services_list);
66LIST_INITIALIZE(namespaces_list);
67LIST_INITIALIZE(servers_list);
68
69/*
70 * Locking order:
71 * servers_list_mutex
72 * services_list_mutex
73 * (loc_server_t *)->services_mutex
74 * create_id_mutex
75 */
76
77FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
78static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
79static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
80static FIBRIL_MUTEX_INITIALIZE(create_id_mutex);
81static FIBRIL_MUTEX_INITIALIZE(null_services_mutex);
82
83static service_id_t last_id = 0;
84static loc_service_t *null_services[NULL_SERVICES];
85
86/*
87 * Dummy list for null services. This is necessary so that null services can
88 * be used just as any other services, e.g. in loc_service_unregister_core().
89 */
90static LIST_INITIALIZE(dummy_null_services);
91
92/** Service directory ogranized by categories (yellow pages) */
93static categ_dir_t cdir;
94
95static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
96static LIST_INITIALIZE(callback_sess_list);
97
98service_id_t loc_create_id(void)
99{
100 /*
101 * TODO: allow reusing old ids after their unregistration
102 * and implement some version of LRU algorithm, avoid overflow
103 */
104
105 fibril_mutex_lock(&create_id_mutex);
106 last_id++;
107 fibril_mutex_unlock(&create_id_mutex);
108
109 return last_id;
110}
111
112/** Convert fully qualified service name to namespace and service name.
113 *
114 * A fully qualified service name can be either a plain service name
115 * (then the namespace is considered to be an empty string) or consist
116 * of two components separated by a slash. No more than one slash
117 * is allowed.
118 *
119 */
120static bool loc_fqsn_split(const char *fqsn, char **ns_name, char **name)
121{
122 size_t cnt = 0;
123 size_t slash_offset = 0;
124 size_t slash_after = 0;
125
126 size_t offset = 0;
127 size_t offset_prev = 0;
128 wchar_t c;
129
130 while ((c = str_decode(fqsn, &offset, STR_NO_LIMIT)) != 0) {
131 if (c == '/') {
132 cnt++;
133 slash_offset = offset_prev;
134 slash_after = offset;
135 }
136 offset_prev = offset;
137 }
138
139 /* More than one slash */
140 if (cnt > 1)
141 return false;
142
143 /* No slash -> namespace is empty */
144 if (cnt == 0) {
145 *ns_name = str_dup("");
146 if (*ns_name == NULL)
147 return false;
148
149 *name = str_dup(fqsn);
150 if (*name == NULL) {
151 free(*ns_name);
152 return false;
153 }
154
155 if (str_cmp(*name, "") == 0) {
156 free(*name);
157 free(*ns_name);
158 return false;
159 }
160
161 return true;
162 }
163
164 /* Exactly one slash */
165 *ns_name = str_ndup(fqsn, slash_offset);
166 if (*ns_name == NULL)
167 return false;
168
169 *name = str_dup(fqsn + slash_after);
170 if (*name == NULL) {
171 free(*ns_name);
172 return false;
173 }
174
175 if (str_cmp(*name, "") == 0) {
176 free(*name);
177 free(*ns_name);
178 return false;
179 }
180
181 return true;
182}
183
184/** Find namespace with given name. */
185static loc_namespace_t *loc_namespace_find_name(const char *name)
186{
187 assert(fibril_mutex_is_locked(&services_list_mutex));
188
189 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
190 if (str_cmp(namespace->name, name) == 0)
191 return namespace;
192 }
193
194 return NULL;
195}
196
197/** Find namespace with given ID.
198 *
199 * @todo: use hash table
200 *
201 */
202static loc_namespace_t *loc_namespace_find_id(service_id_t id)
203{
204 assert(fibril_mutex_is_locked(&services_list_mutex));
205
206 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
207 if (namespace->id == id)
208 return namespace;
209 }
210
211 return NULL;
212}
213
214/** Find service with given name. */
215static loc_service_t *loc_service_find_name(const char *ns_name,
216 const char *name)
217{
218 assert(fibril_mutex_is_locked(&services_list_mutex));
219
220 list_foreach(services_list, services, loc_service_t, service) {
221 if ((str_cmp(service->namespace->name, ns_name) == 0) &&
222 (str_cmp(service->name, name) == 0))
223 return service;
224 }
225
226 return NULL;
227}
228
229/** Find service with given ID.
230 *
231 * @todo: use hash table
232 *
233 */
234static loc_service_t *loc_service_find_id(service_id_t id)
235{
236 assert(fibril_mutex_is_locked(&services_list_mutex));
237
238 list_foreach(services_list, services, loc_service_t, service) {
239 if (service->id == id)
240 return service;
241 }
242
243 return NULL;
244}
245
246/** Create a namespace (if not already present). */
247static loc_namespace_t *loc_namespace_create(const char *ns_name)
248{
249 loc_namespace_t *namespace;
250
251 assert(fibril_mutex_is_locked(&services_list_mutex));
252
253 namespace = loc_namespace_find_name(ns_name);
254 if (namespace != NULL)
255 return namespace;
256
257 namespace = (loc_namespace_t *) malloc(sizeof(loc_namespace_t));
258 if (namespace == NULL)
259 return NULL;
260
261 namespace->name = str_dup(ns_name);
262 if (namespace->name == NULL) {
263 free(namespace);
264 return NULL;
265 }
266
267 namespace->id = loc_create_id();
268 namespace->refcnt = 0;
269
270 /*
271 * Insert new namespace into list of registered namespaces
272 */
273 list_append(&(namespace->namespaces), &namespaces_list);
274
275 return namespace;
276}
277
278/** Destroy a namespace (if it is no longer needed). */
279static void loc_namespace_destroy(loc_namespace_t *namespace)
280{
281 assert(fibril_mutex_is_locked(&services_list_mutex));
282
283 if (namespace->refcnt == 0) {
284 list_remove(&(namespace->namespaces));
285
286 free(namespace->name);
287 free(namespace);
288 }
289}
290
291/** Increase namespace reference count by including service. */
292static void loc_namespace_addref(loc_namespace_t *namespace,
293 loc_service_t *service)
294{
295 assert(fibril_mutex_is_locked(&services_list_mutex));
296
297 service->namespace = namespace;
298 namespace->refcnt++;
299}
300
301/** Decrease namespace reference count. */
302static void loc_namespace_delref(loc_namespace_t *namespace)
303{
304 assert(fibril_mutex_is_locked(&services_list_mutex));
305
306 namespace->refcnt--;
307 loc_namespace_destroy(namespace);
308}
309
310/** Unregister service and free it. */
311static void loc_service_unregister_core(loc_service_t *service)
312{
313 assert(fibril_mutex_is_locked(&services_list_mutex));
314 assert(fibril_mutex_is_locked(&cdir.mutex));
315
316 loc_namespace_delref(service->namespace);
317 list_remove(&(service->services));
318 list_remove(&(service->server_services));
319
320 /* Remove service from all categories. */
321 while (!list_empty(&service->cat_memb)) {
322 link_t *link = list_first(&service->cat_memb);
323 svc_categ_t *memb = list_get_instance(link, svc_categ_t,
324 svc_link);
325 category_t *cat = memb->cat;
326
327 fibril_mutex_lock(&cat->mutex);
328 category_remove_service(memb);
329 fibril_mutex_unlock(&cat->mutex);
330 }
331
332 free(service->name);
333 free(service);
334}
335
336/**
337 * Read info about new server and add it into linked list of registered
338 * servers.
339 */
340static loc_server_t *loc_server_register(void)
341{
342 ipc_call_t icall;
343 async_get_call(&icall);
344
345 if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
346 async_answer_0(&icall, EREFUSED);
347 return NULL;
348 }
349
350 loc_server_t *server =
351 (loc_server_t *) malloc(sizeof(loc_server_t));
352 if (server == NULL) {
353 async_answer_0(&icall, ENOMEM);
354 return NULL;
355 }
356
357 /*
358 * Get server name
359 */
360 errno_t rc = async_data_write_accept((void **) &server->name, true, 0,
361 LOC_NAME_MAXLEN, 0, NULL);
362 if (rc != EOK) {
363 free(server);
364 async_answer_0(&icall, rc);
365 return NULL;
366 }
367
368 /*
369 * Create connection to the server
370 */
371 server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
372 if (!server->sess) {
373 free(server->name);
374 free(server);
375 async_answer_0(&icall, ENOTSUP);
376 return NULL;
377 }
378
379 /*
380 * Initialize mutex for list of services
381 * supplied by this server
382 */
383 fibril_mutex_initialize(&server->services_mutex);
384
385 /*
386 * Initialize list of supplied services
387 */
388 list_initialize(&server->services);
389 link_initialize(&server->servers);
390
391 fibril_mutex_lock(&servers_list_mutex);
392
393 /*
394 * TODO:
395 * Check that no server with name equal to
396 * server->name is registered
397 */
398
399 /*
400 * Insert new server into list of registered servers
401 */
402 list_append(&(server->servers), &servers_list);
403 fibril_mutex_unlock(&servers_list_mutex);
404
405 async_answer_0(&icall, EOK);
406
407 return server;
408}
409
410/**
411 * Unregister server, unregister all its services and free server
412 * structure.
413 *
414 */
415static errno_t loc_server_unregister(loc_server_t *server)
416{
417 if (server == NULL)
418 return EEXIST;
419
420 fibril_mutex_lock(&servers_list_mutex);
421
422 if (server->sess)
423 async_hangup(server->sess);
424
425 /* Remove it from list of servers */
426 list_remove(&(server->servers));
427
428 /* Unregister all its services */
429 fibril_mutex_lock(&services_list_mutex);
430 fibril_mutex_lock(&server->services_mutex);
431 fibril_mutex_lock(&cdir.mutex);
432
433 while (!list_empty(&server->services)) {
434 loc_service_t *service = list_get_instance(
435 list_first(&server->services), loc_service_t,
436 server_services);
437 loc_service_unregister_core(service);
438 }
439
440 fibril_mutex_unlock(&cdir.mutex);
441 fibril_mutex_unlock(&server->services_mutex);
442 fibril_mutex_unlock(&services_list_mutex);
443 fibril_mutex_unlock(&servers_list_mutex);
444
445 /* Free name and server */
446 if (server->name != NULL)
447 free(server->name);
448
449 free(server);
450
451 loc_category_change_event();
452 return EOK;
453}
454
455/** Register service
456 *
457 */
458static void loc_service_register(ipc_call_t *icall, loc_server_t *server)
459{
460 if (server == NULL) {
461 async_answer_0(icall, EREFUSED);
462 return;
463 }
464
465 /* Create new service entry */
466 loc_service_t *service =
467 (loc_service_t *) malloc(sizeof(loc_service_t));
468 if (service == NULL) {
469 async_answer_0(icall, ENOMEM);
470 return;
471 }
472
473 /* Get fqsn */
474 char *fqsn;
475 errno_t rc = async_data_write_accept((void **) &fqsn, true, 0,
476 LOC_NAME_MAXLEN, 0, NULL);
477 if (rc != EOK) {
478 free(service);
479 async_answer_0(icall, rc);
480 return;
481 }
482
483 char *ns_name;
484 if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
485 free(fqsn);
486 free(service);
487 async_answer_0(icall, EINVAL);
488 return;
489 }
490
491 free(fqsn);
492
493 fibril_mutex_lock(&services_list_mutex);
494
495 loc_namespace_t *namespace = loc_namespace_create(ns_name);
496 free(ns_name);
497 if (namespace == NULL) {
498 fibril_mutex_unlock(&services_list_mutex);
499 free(service->name);
500 free(service);
501 async_answer_0(icall, ENOMEM);
502 return;
503 }
504
505 link_initialize(&service->services);
506 link_initialize(&service->server_services);
507 list_initialize(&service->cat_memb);
508
509 /* Check that service is not already registered */
510 if (loc_service_find_name(namespace->name, service->name) != NULL) {
511 printf("%s: Service '%s/%s' already registered\n", NAME,
512 namespace->name, service->name);
513 loc_namespace_destroy(namespace);
514 fibril_mutex_unlock(&services_list_mutex);
515 free(service->name);
516 free(service);
517 async_answer_0(icall, EEXIST);
518 return;
519 }
520
521 /* Get unique service ID */
522 service->id = loc_create_id();
523
524 loc_namespace_addref(namespace, service);
525 service->server = server;
526
527 /* Insert service into list of all services */
528 list_append(&service->services, &services_list);
529
530 /* Insert service into list of services supplied by one server */
531 fibril_mutex_lock(&service->server->services_mutex);
532
533 list_append(&service->server_services, &service->server->services);
534
535 fibril_mutex_unlock(&service->server->services_mutex);
536 fibril_condvar_broadcast(&services_list_cv);
537 fibril_mutex_unlock(&services_list_mutex);
538
539 async_answer_1(icall, EOK, service->id);
540}
541
542/**
543 *
544 */
545static void loc_service_unregister(ipc_call_t *icall, loc_server_t *server)
546{
547 loc_service_t *svc;
548
549 fibril_mutex_lock(&services_list_mutex);
550 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
551 if (svc == NULL) {
552 fibril_mutex_unlock(&services_list_mutex);
553 async_answer_0(icall, ENOENT);
554 return;
555 }
556
557 fibril_mutex_lock(&cdir.mutex);
558 loc_service_unregister_core(svc);
559 fibril_mutex_unlock(&cdir.mutex);
560 fibril_mutex_unlock(&services_list_mutex);
561
562 /*
563 * First send out all notifications and only then answer the request.
564 * Otherwise the current fibril might block and transitively wait for
565 * the completion of requests that are routed to it via an IPC loop.
566 */
567 loc_category_change_event();
568 async_answer_0(icall, EOK);
569}
570
571static void loc_category_get_name(ipc_call_t *icall)
572{
573 ipc_call_t call;
574 size_t size;
575 size_t act_size;
576 category_t *cat;
577
578 if (!async_data_read_receive(&call, &size)) {
579 async_answer_0(&call, EREFUSED);
580 async_answer_0(icall, EREFUSED);
581 return;
582 }
583
584 fibril_mutex_lock(&cdir.mutex);
585
586 cat = category_get(&cdir, IPC_GET_ARG1(*icall));
587 if (cat == NULL) {
588 fibril_mutex_unlock(&cdir.mutex);
589 async_answer_0(&call, ENOENT);
590 async_answer_0(icall, ENOENT);
591 return;
592 }
593
594 act_size = str_size(cat->name);
595 if (act_size > size) {
596 fibril_mutex_unlock(&cdir.mutex);
597 async_answer_0(&call, EOVERFLOW);
598 async_answer_0(icall, EOVERFLOW);
599 return;
600 }
601
602 errno_t retval = async_data_read_finalize(&call, cat->name,
603 min(size, act_size));
604
605 fibril_mutex_unlock(&cdir.mutex);
606
607 async_answer_0(icall, retval);
608}
609
610static void loc_service_get_name(ipc_call_t *icall)
611{
612 ipc_call_t call;
613 size_t size;
614 size_t act_size;
615 loc_service_t *svc;
616 char *fqn;
617
618 if (!async_data_read_receive(&call, &size)) {
619 async_answer_0(&call, EREFUSED);
620 async_answer_0(icall, EREFUSED);
621 return;
622 }
623
624 fibril_mutex_lock(&services_list_mutex);
625
626 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
627 if (svc == NULL) {
628 fibril_mutex_unlock(&services_list_mutex);
629 async_answer_0(&call, ENOENT);
630 async_answer_0(icall, ENOENT);
631 return;
632 }
633
634 if (asprintf(&fqn, "%s/%s", svc->namespace->name, svc->name) < 0) {
635 fibril_mutex_unlock(&services_list_mutex);
636 async_answer_0(&call, ENOMEM);
637 async_answer_0(icall, ENOMEM);
638 return;
639 }
640
641 act_size = str_size(fqn);
642 if (act_size > size) {
643 free(fqn);
644 fibril_mutex_unlock(&services_list_mutex);
645 async_answer_0(&call, EOVERFLOW);
646 async_answer_0(icall, EOVERFLOW);
647 return;
648 }
649
650 errno_t retval = async_data_read_finalize(&call, fqn,
651 min(size, act_size));
652 free(fqn);
653
654 fibril_mutex_unlock(&services_list_mutex);
655
656 async_answer_0(icall, retval);
657}
658
659static void loc_service_get_server_name(ipc_call_t *icall)
660{
661 ipc_call_t call;
662 size_t size;
663 size_t act_size;
664 loc_service_t *svc;
665
666 if (!async_data_read_receive(&call, &size)) {
667 async_answer_0(&call, EREFUSED);
668 async_answer_0(icall, EREFUSED);
669 return;
670 }
671
672 fibril_mutex_lock(&services_list_mutex);
673
674 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
675 if (svc == NULL) {
676 fibril_mutex_unlock(&services_list_mutex);
677 async_answer_0(&call, ENOENT);
678 async_answer_0(icall, ENOENT);
679 return;
680 }
681
682 if (svc->server == NULL) {
683 fibril_mutex_unlock(&services_list_mutex);
684 async_answer_0(&call, EINVAL);
685 async_answer_0(icall, EINVAL);
686 return;
687 }
688
689 act_size = str_size(svc->server->name);
690 if (act_size > size) {
691 fibril_mutex_unlock(&services_list_mutex);
692 async_answer_0(&call, EOVERFLOW);
693 async_answer_0(icall, EOVERFLOW);
694 return;
695 }
696
697 errno_t retval = async_data_read_finalize(&call, svc->server->name,
698 min(size, act_size));
699
700 fibril_mutex_unlock(&services_list_mutex);
701
702 async_answer_0(icall, retval);
703}
704
705/** Connect client to the service.
706 *
707 * Find server supplying requested service and forward
708 * the message to it.
709 *
710 */
711static void loc_forward(ipc_call_t *call, void *arg)
712{
713 fibril_mutex_lock(&services_list_mutex);
714
715 /*
716 * Get ID from request
717 */
718 iface_t iface = IPC_GET_ARG1(*call);
719 service_id_t id = IPC_GET_ARG2(*call);
720 loc_service_t *svc = loc_service_find_id(id);
721
722 if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
723 fibril_mutex_unlock(&services_list_mutex);
724 async_answer_0(call, ENOENT);
725 return;
726 }
727
728 async_exch_t *exch = async_exchange_begin(svc->server->sess);
729 async_forward_fast(call, exch, iface, svc->id, 0, IPC_FF_NONE);
730 async_exchange_end(exch);
731
732 fibril_mutex_unlock(&services_list_mutex);
733}
734
735/** Find ID for service identified by name.
736 *
737 * In answer will be send EOK and service ID in arg1 or a error
738 * code from errno.h.
739 *
740 */
741static void loc_service_get_id(ipc_call_t *icall)
742{
743 char *fqsn;
744
745 /* Get fqsn */
746 errno_t rc = async_data_write_accept((void **) &fqsn, true, 0,
747 LOC_NAME_MAXLEN, 0, NULL);
748 if (rc != EOK) {
749 async_answer_0(icall, rc);
750 return;
751 }
752
753 char *ns_name;
754 char *name;
755 if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
756 free(fqsn);
757 async_answer_0(icall, EINVAL);
758 return;
759 }
760
761 free(fqsn);
762
763 fibril_mutex_lock(&services_list_mutex);
764 const loc_service_t *svc;
765
766recheck:
767
768 /*
769 * Find service name in the list of known services.
770 */
771 svc = loc_service_find_name(ns_name, name);
772
773 /*
774 * Device was not found.
775 */
776 if (svc == NULL) {
777 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
778 /* Blocking lookup */
779 fibril_condvar_wait(&services_list_cv,
780 &services_list_mutex);
781 goto recheck;
782 }
783
784 async_answer_0(icall, ENOENT);
785 free(ns_name);
786 free(name);
787 fibril_mutex_unlock(&services_list_mutex);
788 return;
789 }
790
791 async_answer_1(icall, EOK, svc->id);
792
793 fibril_mutex_unlock(&services_list_mutex);
794 free(ns_name);
795 free(name);
796}
797
798/** Find ID for namespace identified by name.
799 *
800 * In answer will be send EOK and service ID in arg1 or a error
801 * code from errno.h.
802 *
803 */
804static void loc_namespace_get_id(ipc_call_t *icall)
805{
806 char *name;
807
808 /* Get service name */
809 errno_t rc = async_data_write_accept((void **) &name, true, 0,
810 LOC_NAME_MAXLEN, 0, NULL);
811 if (rc != EOK) {
812 async_answer_0(icall, rc);
813 return;
814 }
815
816 fibril_mutex_lock(&services_list_mutex);
817 const loc_namespace_t *namespace;
818
819recheck:
820
821 /*
822 * Find namespace name in the list of known namespaces.
823 */
824 namespace = loc_namespace_find_name(name);
825
826 /*
827 * Namespace was not found.
828 */
829 if (namespace == NULL) {
830 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
831 /* Blocking lookup */
832 fibril_condvar_wait(&services_list_cv,
833 &services_list_mutex);
834 goto recheck;
835 }
836
837 async_answer_0(icall, ENOENT);
838 free(name);
839 fibril_mutex_unlock(&services_list_mutex);
840 return;
841 }
842
843 async_answer_1(icall, EOK, namespace->id);
844
845 fibril_mutex_unlock(&services_list_mutex);
846 free(name);
847}
848
849/** Create callback connection.
850 *
851 * Create callback connection which will be used to send category change
852 * events.
853 *
854 * On success, answer will contain EOK errno_t retval.
855 * On failure, error code will be sent in retval.
856 *
857 */
858static void loc_callback_create(ipc_call_t *icall)
859{
860 cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
861 if (cb_sess == NULL) {
862 async_answer_0(icall, ENOMEM);
863 return;
864 }
865
866 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
867 if (sess == NULL) {
868 free(cb_sess);
869 async_answer_0(icall, ENOMEM);
870 return;
871 }
872
873 cb_sess->sess = sess;
874 link_initialize(&cb_sess->cb_sess_list);
875
876 fibril_mutex_lock(&callback_sess_mutex);
877 list_append(&cb_sess->cb_sess_list, &callback_sess_list);
878 fibril_mutex_unlock(&callback_sess_mutex);
879
880 async_answer_0(icall, EOK);
881}
882
883void loc_category_change_event(void)
884{
885 fibril_mutex_lock(&callback_sess_mutex);
886
887 list_foreach(callback_sess_list, cb_sess_list, cb_sess_t, cb_sess) {
888 async_exch_t *exch = async_exchange_begin(cb_sess->sess);
889 async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
890 async_exchange_end(exch);
891 }
892
893 fibril_mutex_unlock(&callback_sess_mutex);
894}
895
896/** Find ID for category specified by name.
897 *
898 * On success, answer will contain EOK errno_t retval and service ID in arg1.
899 * On failure, error code will be sent in retval.
900 *
901 */
902static void loc_category_get_id(ipc_call_t *icall)
903{
904 char *name;
905 category_t *cat;
906
907 /* Get service name */
908 errno_t rc = async_data_write_accept((void **) &name, true, 0,
909 LOC_NAME_MAXLEN, 0, NULL);
910 if (rc != EOK) {
911 async_answer_0(icall, rc);
912 return;
913 }
914
915 fibril_mutex_lock(&cdir.mutex);
916
917 cat = category_find_by_name(&cdir, name);
918 if (cat == NULL) {
919 /* Category not found */
920 async_answer_0(icall, ENOENT);
921 goto cleanup;
922 }
923
924 async_answer_1(icall, EOK, cat->id);
925cleanup:
926 fibril_mutex_unlock(&cdir.mutex);
927 free(name);
928}
929
930static void loc_id_probe(ipc_call_t *icall)
931{
932 fibril_mutex_lock(&services_list_mutex);
933
934 loc_namespace_t *namespace =
935 loc_namespace_find_id(IPC_GET_ARG1(*icall));
936 if (namespace == NULL) {
937 loc_service_t *svc =
938 loc_service_find_id(IPC_GET_ARG1(*icall));
939 if (svc == NULL)
940 async_answer_1(icall, EOK, LOC_OBJECT_NONE);
941 else
942 async_answer_1(icall, EOK, LOC_OBJECT_SERVICE);
943 } else
944 async_answer_1(icall, EOK, LOC_OBJECT_NAMESPACE);
945
946 fibril_mutex_unlock(&services_list_mutex);
947}
948
949static void loc_get_namespace_count(ipc_call_t *icall)
950{
951 fibril_mutex_lock(&services_list_mutex);
952 async_answer_1(icall, EOK, list_count(&namespaces_list));
953 fibril_mutex_unlock(&services_list_mutex);
954}
955
956static void loc_get_service_count(ipc_call_t *icall)
957{
958 fibril_mutex_lock(&services_list_mutex);
959
960 loc_namespace_t *namespace =
961 loc_namespace_find_id(IPC_GET_ARG1(*icall));
962 if (namespace == NULL)
963 async_answer_0(icall, EEXIST);
964 else
965 async_answer_1(icall, EOK, namespace->refcnt);
966
967 fibril_mutex_unlock(&services_list_mutex);
968}
969
970static void loc_get_categories(ipc_call_t *icall)
971{
972 ipc_call_t call;
973 size_t size;
974 size_t act_size;
975 errno_t rc;
976
977 if (!async_data_read_receive(&call, &size)) {
978 async_answer_0(&call, EREFUSED);
979 async_answer_0(icall, EREFUSED);
980 return;
981 }
982
983 category_id_t *id_buf = (category_id_t *) malloc(size);
984 if (id_buf == NULL) {
985 fibril_mutex_unlock(&cdir.mutex);
986 async_answer_0(&call, ENOMEM);
987 async_answer_0(icall, ENOMEM);
988 return;
989 }
990
991 fibril_mutex_lock(&cdir.mutex);
992
993 rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
994 if (rc != EOK) {
995 fibril_mutex_unlock(&cdir.mutex);
996 async_answer_0(&call, rc);
997 async_answer_0(icall, rc);
998 return;
999 }
1000
1001 fibril_mutex_unlock(&cdir.mutex);
1002
1003 errno_t retval = async_data_read_finalize(&call, id_buf, size);
1004 free(id_buf);
1005
1006 async_answer_1(icall, retval, act_size);
1007}
1008
1009static void loc_get_namespaces(ipc_call_t *icall)
1010{
1011 ipc_call_t call;
1012 size_t size;
1013 if (!async_data_read_receive(&call, &size)) {
1014 async_answer_0(&call, EREFUSED);
1015 async_answer_0(icall, EREFUSED);
1016 return;
1017 }
1018
1019 if ((size % sizeof(loc_sdesc_t)) != 0) {
1020 async_answer_0(&call, EINVAL);
1021 async_answer_0(icall, EINVAL);
1022 return;
1023 }
1024
1025 fibril_mutex_lock(&services_list_mutex);
1026
1027 size_t count = size / sizeof(loc_sdesc_t);
1028 if (count != list_count(&namespaces_list)) {
1029 fibril_mutex_unlock(&services_list_mutex);
1030 async_answer_0(&call, EOVERFLOW);
1031 async_answer_0(icall, EOVERFLOW);
1032 return;
1033 }
1034
1035 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1036 if (desc == NULL) {
1037 fibril_mutex_unlock(&services_list_mutex);
1038 async_answer_0(&call, ENOMEM);
1039 async_answer_0(icall, ENOMEM);
1040 return;
1041 }
1042
1043 size_t pos = 0;
1044 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
1045 desc[pos].id = namespace->id;
1046 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
1047 pos++;
1048 }
1049
1050 errno_t retval = async_data_read_finalize(&call, desc, size);
1051
1052 free(desc);
1053 fibril_mutex_unlock(&services_list_mutex);
1054
1055 async_answer_0(icall, retval);
1056}
1057
1058static void loc_get_services(ipc_call_t *icall)
1059{
1060 /*
1061 * FIXME: Use faster algorithm which can make better use
1062 * of namespaces
1063 */
1064
1065 ipc_call_t call;
1066 size_t size;
1067 if (!async_data_read_receive(&call, &size)) {
1068 async_answer_0(&call, EREFUSED);
1069 async_answer_0(icall, EREFUSED);
1070 return;
1071 }
1072
1073 if ((size % sizeof(loc_sdesc_t)) != 0) {
1074 async_answer_0(&call, EINVAL);
1075 async_answer_0(icall, EINVAL);
1076 return;
1077 }
1078
1079 fibril_mutex_lock(&services_list_mutex);
1080
1081 loc_namespace_t *namespace =
1082 loc_namespace_find_id(IPC_GET_ARG1(*icall));
1083 if (namespace == NULL) {
1084 fibril_mutex_unlock(&services_list_mutex);
1085 async_answer_0(&call, ENOENT);
1086 async_answer_0(icall, ENOENT);
1087 return;
1088 }
1089
1090 size_t count = size / sizeof(loc_sdesc_t);
1091 if (count != namespace->refcnt) {
1092 fibril_mutex_unlock(&services_list_mutex);
1093 async_answer_0(&call, EOVERFLOW);
1094 async_answer_0(icall, EOVERFLOW);
1095 return;
1096 }
1097
1098 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1099 if (desc == NULL) {
1100 fibril_mutex_unlock(&services_list_mutex);
1101 async_answer_0(&call, ENOMEM);
1102 async_answer_0(icall, EREFUSED);
1103 return;
1104 }
1105
1106 size_t pos = 0;
1107 list_foreach(services_list, services, loc_service_t, service) {
1108 if (service->namespace == namespace) {
1109 desc[pos].id = service->id;
1110 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
1111 pos++;
1112 }
1113 }
1114
1115 errno_t retval = async_data_read_finalize(&call, desc, size);
1116
1117 free(desc);
1118 fibril_mutex_unlock(&services_list_mutex);
1119
1120 async_answer_0(icall, retval);
1121}
1122
1123static void loc_category_get_svcs(ipc_call_t *icall)
1124{
1125 ipc_call_t call;
1126 size_t size;
1127 size_t act_size;
1128 errno_t rc;
1129
1130 if (!async_data_read_receive(&call, &size)) {
1131 async_answer_0(&call, EREFUSED);
1132 async_answer_0(icall, EREFUSED);
1133 return;
1134 }
1135
1136 fibril_mutex_lock(&cdir.mutex);
1137
1138 category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
1139 if (cat == NULL) {
1140 fibril_mutex_unlock(&cdir.mutex);
1141 async_answer_0(&call, ENOENT);
1142 async_answer_0(icall, ENOENT);
1143 return;
1144 }
1145
1146 category_id_t *id_buf = (category_id_t *) malloc(size);
1147 if (id_buf == NULL) {
1148 fibril_mutex_unlock(&cdir.mutex);
1149 async_answer_0(&call, ENOMEM);
1150 async_answer_0(icall, ENOMEM);
1151 return;
1152 }
1153
1154 fibril_mutex_lock(&cat->mutex);
1155
1156 rc = category_get_services(cat, id_buf, size, &act_size);
1157 if (rc != EOK) {
1158 fibril_mutex_unlock(&cat->mutex);
1159 fibril_mutex_unlock(&cdir.mutex);
1160 async_answer_0(&call, rc);
1161 async_answer_0(icall, rc);
1162 return;
1163 }
1164
1165 fibril_mutex_unlock(&cat->mutex);
1166 fibril_mutex_unlock(&cdir.mutex);
1167
1168 errno_t retval = async_data_read_finalize(&call, id_buf, size);
1169 free(id_buf);
1170
1171 async_answer_1(icall, retval, act_size);
1172}
1173
1174
1175static void loc_null_create(ipc_call_t *icall)
1176{
1177 fibril_mutex_lock(&null_services_mutex);
1178
1179 unsigned int i;
1180 bool fnd = false;
1181
1182 for (i = 0; i < NULL_SERVICES; i++) {
1183 if (null_services[i] == NULL) {
1184 fnd = true;
1185 break;
1186 }
1187 }
1188
1189 if (!fnd) {
1190 fibril_mutex_unlock(&null_services_mutex);
1191 async_answer_0(icall, ENOMEM);
1192 return;
1193 }
1194
1195 char null[LOC_NAME_MAXLEN];
1196 snprintf(null, LOC_NAME_MAXLEN, "%u", i);
1197
1198 char *dev_name = str_dup(null);
1199 if (dev_name == NULL) {
1200 fibril_mutex_unlock(&null_services_mutex);
1201 async_answer_0(icall, ENOMEM);
1202 return;
1203 }
1204
1205 loc_service_t *service =
1206 (loc_service_t *) malloc(sizeof(loc_service_t));
1207 if (service == NULL) {
1208 fibril_mutex_unlock(&null_services_mutex);
1209 async_answer_0(icall, ENOMEM);
1210 return;
1211 }
1212
1213 fibril_mutex_lock(&services_list_mutex);
1214
1215 loc_namespace_t *namespace = loc_namespace_create("null");
1216 if (namespace == NULL) {
1217 fibril_mutex_lock(&services_list_mutex);
1218 fibril_mutex_unlock(&null_services_mutex);
1219 async_answer_0(icall, ENOMEM);
1220 return;
1221 }
1222
1223 link_initialize(&service->services);
1224 link_initialize(&service->server_services);
1225 list_initialize(&service->cat_memb);
1226
1227 /* Get unique service ID */
1228 service->id = loc_create_id();
1229 service->server = NULL;
1230
1231 loc_namespace_addref(namespace, service);
1232 service->name = dev_name;
1233
1234 /*
1235 * Insert service into list of all services and into null services array.
1236 * Insert service into a dummy list of null server's services so that it
1237 * can be safely removed later.
1238 */
1239 list_append(&service->services, &services_list);
1240 list_append(&service->server_services, &dummy_null_services);
1241 null_services[i] = service;
1242
1243 fibril_mutex_unlock(&services_list_mutex);
1244 fibril_mutex_unlock(&null_services_mutex);
1245
1246 async_answer_1(icall, EOK, (sysarg_t) i);
1247}
1248
1249static void loc_null_destroy(ipc_call_t *icall)
1250{
1251 sysarg_t i = IPC_GET_ARG1(*icall);
1252 if (i >= NULL_SERVICES) {
1253 async_answer_0(icall, ELIMIT);
1254 return;
1255 }
1256
1257 fibril_mutex_lock(&null_services_mutex);
1258
1259 if (null_services[i] == NULL) {
1260 fibril_mutex_unlock(&null_services_mutex);
1261 async_answer_0(icall, ENOENT);
1262 return;
1263 }
1264
1265 fibril_mutex_lock(&services_list_mutex);
1266 fibril_mutex_lock(&cdir.mutex);
1267 loc_service_unregister_core(null_services[i]);
1268 fibril_mutex_unlock(&cdir.mutex);
1269 fibril_mutex_unlock(&services_list_mutex);
1270
1271 null_services[i] = NULL;
1272
1273 fibril_mutex_unlock(&null_services_mutex);
1274 async_answer_0(icall, EOK);
1275}
1276
1277static void loc_service_add_to_cat(ipc_call_t *icall)
1278{
1279 category_t *cat;
1280 loc_service_t *svc;
1281 catid_t cat_id;
1282 service_id_t svc_id;
1283 errno_t retval;
1284
1285 svc_id = IPC_GET_ARG1(*icall);
1286 cat_id = IPC_GET_ARG2(*icall);
1287
1288 fibril_mutex_lock(&services_list_mutex);
1289 fibril_mutex_lock(&cdir.mutex);
1290
1291 cat = category_get(&cdir, cat_id);
1292 svc = loc_service_find_id(svc_id);
1293
1294 if (cat == NULL || svc == NULL) {
1295 fibril_mutex_unlock(&cdir.mutex);
1296 fibril_mutex_unlock(&services_list_mutex);
1297 async_answer_0(icall, ENOENT);
1298 return;
1299 }
1300
1301 fibril_mutex_lock(&cat->mutex);
1302 retval = category_add_service(cat, svc);
1303
1304 fibril_mutex_unlock(&cat->mutex);
1305 fibril_mutex_unlock(&cdir.mutex);
1306 fibril_mutex_unlock(&services_list_mutex);
1307
1308 /*
1309 * First send out all notifications and only then answer the request.
1310 * Otherwise the current fibril might block and transitively wait for
1311 * the completion of requests that are routed to it via an IPC loop.
1312 */
1313 loc_category_change_event();
1314 async_answer_0(icall, retval);
1315}
1316
1317
1318/** Initialize location service.
1319 *
1320 *
1321 */
1322static bool loc_init(void)
1323{
1324 unsigned int i;
1325 category_t *cat;
1326
1327 for (i = 0; i < NULL_SERVICES; i++)
1328 null_services[i] = NULL;
1329
1330 categ_dir_init(&cdir);
1331
1332 cat = category_new("disk");
1333 categ_dir_add_cat(&cdir, cat);
1334
1335 cat = category_new("partition");
1336 categ_dir_add_cat(&cdir, cat);
1337
1338 cat = category_new("iplink");
1339 categ_dir_add_cat(&cdir, cat);
1340
1341 cat = category_new("keyboard");
1342 categ_dir_add_cat(&cdir, cat);
1343
1344 cat = category_new("mouse");
1345 categ_dir_add_cat(&cdir, cat);
1346
1347 cat = category_new("led");
1348 categ_dir_add_cat(&cdir, cat);
1349
1350 cat = category_new("serial");
1351 categ_dir_add_cat(&cdir, cat);
1352
1353 cat = category_new("console");
1354 categ_dir_add_cat(&cdir, cat);
1355
1356 cat = category_new("clock");
1357 categ_dir_add_cat(&cdir, cat);
1358
1359 cat = category_new("test3");
1360 categ_dir_add_cat(&cdir, cat);
1361
1362 cat = category_new("usbdiag");
1363 categ_dir_add_cat(&cdir, cat);
1364
1365 cat = category_new("usbhc");
1366 categ_dir_add_cat(&cdir, cat);
1367
1368 cat = category_new("virt-null");
1369 categ_dir_add_cat(&cdir, cat);
1370
1371 cat = category_new("virtual");
1372 categ_dir_add_cat(&cdir, cat);
1373
1374 cat = category_new("nic");
1375 categ_dir_add_cat(&cdir, cat);
1376
1377 cat = category_new("ieee80211");
1378 categ_dir_add_cat(&cdir, cat);
1379
1380 cat = category_new("irc");
1381 categ_dir_add_cat(&cdir, cat);
1382
1383 cat = category_new("visualizer");
1384 categ_dir_add_cat(&cdir, cat);
1385
1386 cat = category_new("renderer");
1387 categ_dir_add_cat(&cdir, cat);
1388
1389 cat = category_new("audio-pcm");
1390 categ_dir_add_cat(&cdir, cat);
1391
1392 return true;
1393}
1394
1395/** Handle connection on supplier port.
1396 *
1397 */
1398static void loc_connection_supplier(ipc_call_t *icall, void *arg)
1399{
1400 /* Accept connection */
1401 async_answer_0(icall, EOK);
1402
1403 loc_server_t *server = loc_server_register();
1404 if (server == NULL)
1405 return;
1406
1407 while (true) {
1408 ipc_call_t call;
1409 async_get_call(&call);
1410
1411 if (!IPC_GET_IMETHOD(call))
1412 break;
1413
1414 switch (IPC_GET_IMETHOD(call)) {
1415 case LOC_SERVER_UNREGISTER:
1416 if (server == NULL)
1417 async_answer_0(&call, ENOENT);
1418 else
1419 async_answer_0(&call, EOK);
1420 break;
1421 case LOC_SERVICE_ADD_TO_CAT:
1422 /* Add service to category */
1423 loc_service_add_to_cat(&call);
1424 break;
1425 case LOC_SERVICE_REGISTER:
1426 /* Register one service */
1427 loc_service_register(&call, server);
1428 break;
1429 case LOC_SERVICE_UNREGISTER:
1430 /* Remove one service */
1431 loc_service_unregister(&call, server);
1432 break;
1433 case LOC_SERVICE_GET_ID:
1434 loc_service_get_id(&call);
1435 break;
1436 case LOC_NAMESPACE_GET_ID:
1437 loc_namespace_get_id(&call);
1438 break;
1439 default:
1440 async_answer_0(&call, ENOENT);
1441 }
1442 }
1443
1444 if (server != NULL) {
1445 /*
1446 * Unregister the server and all its services.
1447 */
1448 loc_server_unregister(server);
1449 server = NULL;
1450 }
1451}
1452
1453/** Handle connection on consumer port.
1454 *
1455 */
1456static void loc_connection_consumer(ipc_call_t *icall, void *arg)
1457{
1458 /* Accept connection */
1459 async_answer_0(icall, EOK);
1460
1461 while (true) {
1462 ipc_call_t call;
1463 async_get_call(&call);
1464
1465 if (!IPC_GET_IMETHOD(call))
1466 break;
1467
1468 switch (IPC_GET_IMETHOD(call)) {
1469 case LOC_SERVICE_GET_ID:
1470 loc_service_get_id(&call);
1471 break;
1472 case LOC_SERVICE_GET_NAME:
1473 loc_service_get_name(&call);
1474 break;
1475 case LOC_SERVICE_GET_SERVER_NAME:
1476 loc_service_get_server_name(&call);
1477 break;
1478 case LOC_NAMESPACE_GET_ID:
1479 loc_namespace_get_id(&call);
1480 break;
1481 case LOC_CALLBACK_CREATE:
1482 loc_callback_create(&call);
1483 break;
1484 case LOC_CATEGORY_GET_ID:
1485 loc_category_get_id(&call);
1486 break;
1487 case LOC_CATEGORY_GET_NAME:
1488 loc_category_get_name(&call);
1489 break;
1490 case LOC_CATEGORY_GET_SVCS:
1491 loc_category_get_svcs(&call);
1492 break;
1493 case LOC_ID_PROBE:
1494 loc_id_probe(&call);
1495 break;
1496 case LOC_NULL_CREATE:
1497 loc_null_create(&call);
1498 break;
1499 case LOC_NULL_DESTROY:
1500 loc_null_destroy(&call);
1501 break;
1502 case LOC_GET_NAMESPACE_COUNT:
1503 loc_get_namespace_count(&call);
1504 break;
1505 case LOC_GET_SERVICE_COUNT:
1506 loc_get_service_count(&call);
1507 break;
1508 case LOC_GET_CATEGORIES:
1509 loc_get_categories(&call);
1510 break;
1511 case LOC_GET_NAMESPACES:
1512 loc_get_namespaces(&call);
1513 break;
1514 case LOC_GET_SERVICES:
1515 loc_get_services(&call);
1516 break;
1517 default:
1518 async_answer_0(&call, ENOENT);
1519 }
1520 }
1521}
1522
1523/**
1524 *
1525 */
1526int main(int argc, char *argv[])
1527{
1528 printf("%s: HelenOS Location Service\n", NAME);
1529
1530 if (!loc_init()) {
1531 printf("%s: Error while initializing service\n", NAME);
1532 return -1;
1533 }
1534
1535 port_id_t port;
1536 errno_t rc = async_create_port(INTERFACE_LOC_SUPPLIER,
1537 loc_connection_supplier, NULL, &port);
1538 if (rc != EOK) {
1539 printf("%s: Error while creating supplier port: %s\n", NAME, str_error(rc));
1540 return rc;
1541 }
1542
1543 rc = async_create_port(INTERFACE_LOC_CONSUMER,
1544 loc_connection_consumer, NULL, &port);
1545 if (rc != EOK) {
1546 printf("%s: Error while creating consumer port: %s\n", NAME, str_error(rc));
1547 return rc;
1548 }
1549
1550 /* Set a handler of incomming connections */
1551 async_set_fallback_port_handler(loc_forward, NULL);
1552
1553 /* Register location service at naming service */
1554 rc = service_register(SERVICE_LOC);
1555 if (rc != EOK) {
1556 printf("%s: Error while registering service: %s\n", NAME, str_error(rc));
1557 return rc;
1558 }
1559
1560 printf("%s: Accepting connections\n", NAME);
1561 async_manager();
1562
1563 /* Never reached */
1564 return 0;
1565}
1566
1567/**
1568 * @}
1569 */
Note: See TracBrowser for help on using the repository browser.