source: mainline/uspace/srv/locsrv/locsrv.c@ 0b05082

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

ns: register service interfaces individually

Each service interface is now registered individually with the naming
service. This adds a degree of type safety, potentially allows the
individual interfaces to be implemented by independent tasks and moves
the code slightly closer to the full-fledged ports design.

Broker services (e.g. the location service) can still register a
fallback port for receiving connections to all interface types
explicitly using service_register_broker().

  • Property mode set to 100644
File size: 34.7 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 /* Register location service at naming service */
1536 errno_t rc = service_register(SERVICE_LOC, INTERFACE_LOC_SUPPLIER,
1537 loc_connection_supplier, NULL);
1538 if (rc != EOK) {
1539 printf("%s: Error while registering supplier service: %s\n", NAME, str_error(rc));
1540 return rc;
1541 }
1542
1543 rc = service_register(SERVICE_LOC, INTERFACE_LOC_CONSUMER,
1544 loc_connection_consumer, NULL);
1545 if (rc != EOK) {
1546 printf("%s: Error while registering consumer service: %s\n", NAME, str_error(rc));
1547 return rc;
1548 }
1549
1550 rc = service_register_broker(SERVICE_LOC, loc_forward, NULL);
1551 if (rc != EOK) {
1552 printf("%s: Error while registering broker service: %s\n", NAME, str_error(rc));
1553 return rc;
1554 }
1555
1556 printf("%s: Accepting connections\n", NAME);
1557 async_manager();
1558
1559 /* Never reached */
1560 return 0;
1561}
1562
1563/**
1564 * @}
1565 */
Note: See TracBrowser for help on using the repository browser.