source: mainline/uspace/srv/locsrv/locsrv.c@ 41b764b7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 41b764b7 was 90478727, checked in by Maurizio Lombardi <m.lombardi85@…>, 13 years ago

merge mainline changes

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