source: mainline/uspace/srv/locsrv/locsrv.c@ 3bbd921

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3bbd921 was 3a9cf35, checked in by Jiri Svoboda <jiri@…>, 10 years ago

VBD should take note of devices disappearing.

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