source: mainline/uspace/srv/locsrv/locsrv.c@ 50ad3f3

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

trivial changes (error code reporting, messages)

  • Property mode set to 100644
File size: 33.8 KB
Line 
1/*
2 * Copyright (c) 2007 Josef Cejka
3 * Copyright (c) 2011 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
31 * @defgroup loc Location Service.
32 * @brief HelenOS location service.
33 * @{
34 */
35
36/** @file
37 */
38
39#include <ipc/services.h>
40#include <ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
44#include <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
658/** Connect client to the service.
659 *
660 * Find server supplying requested service and forward
661 * the message to it.
662 *
663 */
664static void loc_forward(ipc_callid_t callid, ipc_call_t *call)
665{
666 fibril_mutex_lock(&services_list_mutex);
667
668 /*
669 * Get ID from request
670 */
671 service_id_t id = IPC_GET_ARG2(*call);
672 loc_service_t *svc = loc_service_find_id(id);
673
674 if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
675 fibril_mutex_unlock(&services_list_mutex);
676 async_answer_0(callid, ENOENT);
677 return;
678 }
679
680 async_exch_t *exch = async_exchange_begin(svc->server->sess);
681
682 if (svc->forward_interface == 0)
683 async_forward_fast(callid, exch, svc->id, 0, 0, IPC_FF_NONE);
684 else
685 async_forward_fast(callid, exch, svc->forward_interface,
686 svc->id, 0, IPC_FF_NONE);
687
688 async_exchange_end(exch);
689
690 fibril_mutex_unlock(&services_list_mutex);
691}
692
693/** Find ID for service identified by name.
694 *
695 * In answer will be send EOK and service ID in arg1 or a error
696 * code from errno.h.
697 *
698 */
699static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
700{
701 char *fqsn;
702
703 /* Get fqsn */
704 int rc = async_data_write_accept((void **) &fqsn, true, 0,
705 LOC_NAME_MAXLEN, 0, NULL);
706 if (rc != EOK) {
707 async_answer_0(iid, rc);
708 return;
709 }
710
711 char *ns_name;
712 char *name;
713 if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
714 free(fqsn);
715 async_answer_0(iid, EINVAL);
716 return;
717 }
718
719 free(fqsn);
720
721 fibril_mutex_lock(&services_list_mutex);
722 const loc_service_t *svc;
723
724recheck:
725
726 /*
727 * Find service name in the list of known services.
728 */
729 svc = loc_service_find_name(ns_name, name);
730
731 /*
732 * Device was not found.
733 */
734 if (svc == NULL) {
735 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
736 /* Blocking lookup */
737 fibril_condvar_wait(&services_list_cv,
738 &services_list_mutex);
739 goto recheck;
740 }
741
742 async_answer_0(iid, ENOENT);
743 free(ns_name);
744 free(name);
745 fibril_mutex_unlock(&services_list_mutex);
746 return;
747 }
748
749 async_answer_1(iid, EOK, svc->id);
750
751 fibril_mutex_unlock(&services_list_mutex);
752 free(ns_name);
753 free(name);
754}
755
756/** Find ID for namespace identified by name.
757 *
758 * In answer will be send EOK and service ID in arg1 or a error
759 * code from errno.h.
760 *
761 */
762static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
763{
764 char *name;
765
766 /* Get service name */
767 int rc = async_data_write_accept((void **) &name, true, 0,
768 LOC_NAME_MAXLEN, 0, NULL);
769 if (rc != EOK) {
770 async_answer_0(iid, rc);
771 return;
772 }
773
774 fibril_mutex_lock(&services_list_mutex);
775 const loc_namespace_t *namespace;
776
777recheck:
778
779 /*
780 * Find namespace name in the list of known namespaces.
781 */
782 namespace = loc_namespace_find_name(name);
783
784 /*
785 * Namespace was not found.
786 */
787 if (namespace == NULL) {
788 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
789 /* Blocking lookup */
790 fibril_condvar_wait(&services_list_cv,
791 &services_list_mutex);
792 goto recheck;
793 }
794
795 async_answer_0(iid, ENOENT);
796 free(name);
797 fibril_mutex_unlock(&services_list_mutex);
798 return;
799 }
800
801 async_answer_1(iid, EOK, namespace->id);
802
803 fibril_mutex_unlock(&services_list_mutex);
804 free(name);
805}
806
807/** Create callback connection.
808 *
809 * Create callback connection which will be used to send category change
810 * events.
811 *
812 * On success, answer will contain EOK int retval.
813 * On failure, error code will be sent in retval.
814 *
815 */
816static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
817{
818 cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
819 if (cb_sess == NULL) {
820 async_answer_0(iid, ENOMEM);
821 return;
822 }
823
824 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
825 if (sess == NULL) {
826 free(cb_sess);
827 async_answer_0(iid, ENOMEM);
828 return;
829 }
830
831 cb_sess->sess = sess;
832 link_initialize(&cb_sess->cb_sess_list);
833
834 fibril_mutex_lock(&callback_sess_mutex);
835 list_append(&cb_sess->cb_sess_list, &callback_sess_list);
836 fibril_mutex_unlock(&callback_sess_mutex);
837
838 async_answer_0(iid, EOK);
839}
840
841void loc_category_change_event(void)
842{
843 fibril_mutex_lock(&callback_sess_mutex);
844
845 list_foreach(callback_sess_list, link) {
846 cb_sess_t *cb_sess;
847
848 cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
849
850 async_exch_t *exch = async_exchange_begin(cb_sess->sess);
851 async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
852 async_exchange_end(exch);
853 }
854
855 fibril_mutex_unlock(&callback_sess_mutex);
856}
857
858/** Find ID for category specified by name.
859 *
860 * On success, answer will contain EOK int retval and service ID in arg1.
861 * On failure, error code will be sent in retval.
862 *
863 */
864static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
865{
866 char *name;
867 category_t *cat;
868
869 /* Get service name */
870 int rc = async_data_write_accept((void **) &name, true, 0,
871 LOC_NAME_MAXLEN, 0, NULL);
872 if (rc != EOK) {
873 async_answer_0(iid, rc);
874 return;
875 }
876
877 fibril_mutex_lock(&cdir.mutex);
878
879 cat = category_find_by_name(&cdir, name);
880 if (cat == NULL) {
881 /* Category not found */
882 async_answer_0(iid, ENOENT);
883 goto cleanup;
884 }
885
886 async_answer_1(iid, EOK, cat->id);
887cleanup:
888 fibril_mutex_unlock(&cdir.mutex);
889 free(name);
890}
891
892static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
893{
894 fibril_mutex_lock(&services_list_mutex);
895
896 loc_namespace_t *namespace =
897 loc_namespace_find_id(IPC_GET_ARG1(*icall));
898 if (namespace == NULL) {
899 loc_service_t *svc =
900 loc_service_find_id(IPC_GET_ARG1(*icall));
901 if (svc == NULL)
902 async_answer_1(iid, EOK, LOC_OBJECT_NONE);
903 else
904 async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
905 } else
906 async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
907
908 fibril_mutex_unlock(&services_list_mutex);
909}
910
911static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
912{
913 fibril_mutex_lock(&services_list_mutex);
914 async_answer_1(iid, EOK, list_count(&namespaces_list));
915 fibril_mutex_unlock(&services_list_mutex);
916}
917
918static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
919{
920 fibril_mutex_lock(&services_list_mutex);
921
922 loc_namespace_t *namespace =
923 loc_namespace_find_id(IPC_GET_ARG1(*icall));
924 if (namespace == NULL)
925 async_answer_0(iid, EEXISTS);
926 else
927 async_answer_1(iid, EOK, namespace->refcnt);
928
929 fibril_mutex_unlock(&services_list_mutex);
930}
931
932static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
933{
934 ipc_callid_t callid;
935 size_t size;
936 size_t act_size;
937 int rc;
938
939 if (!async_data_read_receive(&callid, &size)) {
940 async_answer_0(callid, EREFUSED);
941 async_answer_0(iid, EREFUSED);
942 return;
943 }
944
945 category_id_t *id_buf = (category_id_t *) malloc(size);
946 if (id_buf == NULL) {
947 fibril_mutex_unlock(&cdir.mutex);
948 async_answer_0(callid, ENOMEM);
949 async_answer_0(iid, ENOMEM);
950 return;
951 }
952
953 fibril_mutex_lock(&cdir.mutex);
954
955 rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
956 if (rc != EOK) {
957 fibril_mutex_unlock(&cdir.mutex);
958 async_answer_0(callid, rc);
959 async_answer_0(iid, rc);
960 return;
961 }
962
963 fibril_mutex_unlock(&cdir.mutex);
964
965 sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
966 free(id_buf);
967
968 async_answer_1(iid, retval, act_size);
969}
970
971static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
972{
973 ipc_callid_t callid;
974 size_t size;
975 if (!async_data_read_receive(&callid, &size)) {
976 async_answer_0(callid, EREFUSED);
977 async_answer_0(iid, EREFUSED);
978 return;
979 }
980
981 if ((size % sizeof(loc_sdesc_t)) != 0) {
982 async_answer_0(callid, EINVAL);
983 async_answer_0(iid, EINVAL);
984 return;
985 }
986
987 fibril_mutex_lock(&services_list_mutex);
988
989 size_t count = size / sizeof(loc_sdesc_t);
990 if (count != list_count(&namespaces_list)) {
991 fibril_mutex_unlock(&services_list_mutex);
992 async_answer_0(callid, EOVERFLOW);
993 async_answer_0(iid, EOVERFLOW);
994 return;
995 }
996
997 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
998 if (desc == NULL) {
999 fibril_mutex_unlock(&services_list_mutex);
1000 async_answer_0(callid, ENOMEM);
1001 async_answer_0(iid, ENOMEM);
1002 return;
1003 }
1004
1005 size_t pos = 0;
1006 list_foreach(namespaces_list, item) {
1007 loc_namespace_t *namespace =
1008 list_get_instance(item, loc_namespace_t, namespaces);
1009
1010 desc[pos].id = namespace->id;
1011 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
1012 pos++;
1013 }
1014
1015 sysarg_t retval = async_data_read_finalize(callid, desc, size);
1016
1017 free(desc);
1018 fibril_mutex_unlock(&services_list_mutex);
1019
1020 async_answer_0(iid, retval);
1021}
1022
1023static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
1024{
1025 /* FIXME: Use faster algorithm which can make better use
1026 of namespaces */
1027
1028 ipc_callid_t callid;
1029 size_t size;
1030 if (!async_data_read_receive(&callid, &size)) {
1031 async_answer_0(callid, EREFUSED);
1032 async_answer_0(iid, EREFUSED);
1033 return;
1034 }
1035
1036 if ((size % sizeof(loc_sdesc_t)) != 0) {
1037 async_answer_0(callid, EINVAL);
1038 async_answer_0(iid, EINVAL);
1039 return;
1040 }
1041
1042 fibril_mutex_lock(&services_list_mutex);
1043
1044 loc_namespace_t *namespace =
1045 loc_namespace_find_id(IPC_GET_ARG1(*icall));
1046 if (namespace == NULL) {
1047 fibril_mutex_unlock(&services_list_mutex);
1048 async_answer_0(callid, ENOENT);
1049 async_answer_0(iid, ENOENT);
1050 return;
1051 }
1052
1053 size_t count = size / sizeof(loc_sdesc_t);
1054 if (count != namespace->refcnt) {
1055 fibril_mutex_unlock(&services_list_mutex);
1056 async_answer_0(callid, EOVERFLOW);
1057 async_answer_0(iid, EOVERFLOW);
1058 return;
1059 }
1060
1061 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1062 if (desc == NULL) {
1063 fibril_mutex_unlock(&services_list_mutex);
1064 async_answer_0(callid, ENOMEM);
1065 async_answer_0(iid, EREFUSED);
1066 return;
1067 }
1068
1069 size_t pos = 0;
1070 list_foreach(services_list, item) {
1071 loc_service_t *service =
1072 list_get_instance(item, loc_service_t, services);
1073
1074 if (service->namespace == namespace) {
1075 desc[pos].id = service->id;
1076 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
1077 pos++;
1078 }
1079 }
1080
1081 sysarg_t retval = async_data_read_finalize(callid, desc, size);
1082
1083 free(desc);
1084 fibril_mutex_unlock(&services_list_mutex);
1085
1086 async_answer_0(iid, retval);
1087}
1088
1089static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
1090{
1091 ipc_callid_t callid;
1092 size_t size;
1093 size_t act_size;
1094 int rc;
1095
1096 if (!async_data_read_receive(&callid, &size)) {
1097 async_answer_0(callid, EREFUSED);
1098 async_answer_0(iid, EREFUSED);
1099 return;
1100 }
1101
1102 fibril_mutex_lock(&cdir.mutex);
1103
1104 category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
1105 if (cat == NULL) {
1106 fibril_mutex_unlock(&cdir.mutex);
1107 async_answer_0(callid, ENOENT);
1108 async_answer_0(iid, ENOENT);
1109 return;
1110 }
1111
1112 category_id_t *id_buf = (category_id_t *) malloc(size);
1113 if (id_buf == NULL) {
1114 fibril_mutex_unlock(&cdir.mutex);
1115 async_answer_0(callid, ENOMEM);
1116 async_answer_0(iid, ENOMEM);
1117 return;
1118 }
1119
1120 fibril_mutex_lock(&cat->mutex);
1121
1122 rc = category_get_services(cat, id_buf, size, &act_size);
1123 if (rc != EOK) {
1124 fibril_mutex_unlock(&cat->mutex);
1125 fibril_mutex_unlock(&cdir.mutex);
1126 async_answer_0(callid, rc);
1127 async_answer_0(iid, rc);
1128 return;
1129 }
1130
1131 fibril_mutex_unlock(&cat->mutex);
1132 fibril_mutex_unlock(&cdir.mutex);
1133
1134 sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
1135 free(id_buf);
1136
1137 async_answer_1(iid, retval, act_size);
1138}
1139
1140
1141static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
1142{
1143 fibril_mutex_lock(&null_services_mutex);
1144
1145 unsigned int i;
1146 bool fnd = false;
1147
1148 for (i = 0; i < NULL_SERVICES; i++) {
1149 if (null_services[i] == NULL) {
1150 fnd = true;
1151 break;
1152 }
1153 }
1154
1155 if (!fnd) {
1156 fibril_mutex_unlock(&null_services_mutex);
1157 async_answer_0(iid, ENOMEM);
1158 return;
1159 }
1160
1161 char null[LOC_NAME_MAXLEN];
1162 snprintf(null, LOC_NAME_MAXLEN, "%u", i);
1163
1164 char *dev_name = str_dup(null);
1165 if (dev_name == NULL) {
1166 fibril_mutex_unlock(&null_services_mutex);
1167 async_answer_0(iid, ENOMEM);
1168 return;
1169 }
1170
1171 loc_service_t *service =
1172 (loc_service_t *) malloc(sizeof(loc_service_t));
1173 if (service == NULL) {
1174 fibril_mutex_unlock(&null_services_mutex);
1175 async_answer_0(iid, ENOMEM);
1176 return;
1177 }
1178
1179 fibril_mutex_lock(&services_list_mutex);
1180
1181 loc_namespace_t *namespace = loc_namespace_create("null");
1182 if (namespace == NULL) {
1183 fibril_mutex_lock(&services_list_mutex);
1184 fibril_mutex_unlock(&null_services_mutex);
1185 async_answer_0(iid, ENOMEM);
1186 return;
1187 }
1188
1189 link_initialize(&service->services);
1190 link_initialize(&service->server_services);
1191 list_initialize(&service->cat_memb);
1192
1193 /* Get unique service ID */
1194 service->id = loc_create_id();
1195 service->server = NULL;
1196
1197 loc_namespace_addref(namespace, service);
1198 service->name = dev_name;
1199
1200 /*
1201 * Insert service into list of all services and into null services array.
1202 * Insert service into a dummy list of null server's services so that it
1203 * can be safely removed later.
1204 */
1205 list_append(&service->services, &services_list);
1206 list_append(&service->server_services, &dummy_null_services);
1207 null_services[i] = service;
1208
1209 fibril_mutex_unlock(&services_list_mutex);
1210 fibril_mutex_unlock(&null_services_mutex);
1211
1212 async_answer_1(iid, EOK, (sysarg_t) i);
1213}
1214
1215static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
1216{
1217 sysarg_t i = IPC_GET_ARG1(*icall);
1218 if (i >= NULL_SERVICES) {
1219 async_answer_0(iid, ELIMIT);
1220 return;
1221 }
1222
1223 fibril_mutex_lock(&null_services_mutex);
1224
1225 if (null_services[i] == NULL) {
1226 fibril_mutex_unlock(&null_services_mutex);
1227 async_answer_0(iid, ENOENT);
1228 return;
1229 }
1230
1231 fibril_mutex_lock(&services_list_mutex);
1232 fibril_mutex_lock(&cdir.mutex);
1233 loc_service_unregister_core(null_services[i]);
1234 fibril_mutex_unlock(&cdir.mutex);
1235 fibril_mutex_unlock(&services_list_mutex);
1236
1237 null_services[i] = NULL;
1238
1239 fibril_mutex_unlock(&null_services_mutex);
1240 async_answer_0(iid, EOK);
1241}
1242
1243static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
1244{
1245 category_t *cat;
1246 loc_service_t *svc;
1247 catid_t cat_id;
1248 service_id_t svc_id;
1249 sysarg_t retval;
1250
1251 svc_id = IPC_GET_ARG1(*icall);
1252 cat_id = IPC_GET_ARG2(*icall);
1253
1254 fibril_mutex_lock(&services_list_mutex);
1255 fibril_mutex_lock(&cdir.mutex);
1256
1257 cat = category_get(&cdir, cat_id);
1258 svc = loc_service_find_id(svc_id);
1259
1260 if (cat == NULL || svc == NULL) {
1261 fibril_mutex_unlock(&cdir.mutex);
1262 fibril_mutex_unlock(&services_list_mutex);
1263 async_answer_0(iid, ENOENT);
1264 return;
1265 }
1266
1267 fibril_mutex_lock(&cat->mutex);
1268 retval = category_add_service(cat, svc);
1269
1270 fibril_mutex_unlock(&cat->mutex);
1271 fibril_mutex_unlock(&cdir.mutex);
1272 fibril_mutex_unlock(&services_list_mutex);
1273
1274 async_answer_0(iid, retval);
1275
1276 loc_category_change_event();
1277}
1278
1279
1280/** Initialize location service.
1281 *
1282 *
1283 */
1284static bool loc_init(void)
1285{
1286 unsigned int i;
1287 category_t *cat;
1288
1289 for (i = 0; i < NULL_SERVICES; i++)
1290 null_services[i] = NULL;
1291
1292 categ_dir_init(&cdir);
1293
1294 cat = category_new("bd");
1295 categ_dir_add_cat(&cdir, cat);
1296
1297 cat = category_new("iplink");
1298 categ_dir_add_cat(&cdir, cat);
1299
1300 cat = category_new("keyboard");
1301 categ_dir_add_cat(&cdir, cat);
1302
1303 cat = category_new("mouse");
1304 categ_dir_add_cat(&cdir, cat);
1305
1306 cat = category_new("serial");
1307 categ_dir_add_cat(&cdir, cat);
1308
1309 cat = category_new("test3");
1310 categ_dir_add_cat(&cdir, cat);
1311
1312 cat = category_new("usbhc");
1313 categ_dir_add_cat(&cdir, cat);
1314
1315 cat = category_new("virt-null");
1316 categ_dir_add_cat(&cdir, cat);
1317
1318 cat = category_new("virtual");
1319 categ_dir_add_cat(&cdir, cat);
1320
1321 cat = category_new("nic");
1322 categ_dir_add_cat(&cdir, cat);
1323
1324 return true;
1325}
1326
1327/** Handle connection on supplier port.
1328 *
1329 */
1330static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall)
1331{
1332 /* Accept connection */
1333 async_answer_0(iid, EOK);
1334
1335 loc_server_t *server = loc_server_register();
1336 if (server == NULL)
1337 return;
1338
1339 while (true) {
1340 ipc_call_t call;
1341 ipc_callid_t callid = async_get_call(&call);
1342
1343 if (!IPC_GET_IMETHOD(call))
1344 break;
1345
1346 switch (IPC_GET_IMETHOD(call)) {
1347 case LOC_SERVER_UNREGISTER:
1348 if (server == NULL)
1349 async_answer_0(callid, ENOENT);
1350 else
1351 async_answer_0(callid, EOK);
1352 break;
1353 case LOC_SERVICE_ADD_TO_CAT:
1354 /* Add service to category */
1355 loc_service_add_to_cat(callid, &call);
1356 break;
1357 case LOC_SERVICE_REGISTER:
1358 /* Register one service */
1359 loc_service_register(callid, &call, server);
1360 break;
1361 case LOC_SERVICE_UNREGISTER:
1362 /* Remove one service */
1363 loc_service_unregister(callid, &call, server);
1364 break;
1365 case LOC_SERVICE_GET_ID:
1366 loc_service_get_id(callid, &call);
1367 break;
1368 case LOC_NAMESPACE_GET_ID:
1369 loc_namespace_get_id(callid, &call);
1370 break;
1371 default:
1372 async_answer_0(callid, ENOENT);
1373 }
1374 }
1375
1376 if (server != NULL) {
1377 /*
1378 * Unregister the server and all its services.
1379 */
1380 loc_server_unregister(server);
1381 server = NULL;
1382 }
1383}
1384
1385/** Handle connection on consumer port.
1386 *
1387 */
1388static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall)
1389{
1390 /* Accept connection */
1391 async_answer_0(iid, EOK);
1392
1393 while (true) {
1394 ipc_call_t call;
1395 ipc_callid_t callid = async_get_call(&call);
1396
1397 if (!IPC_GET_IMETHOD(call))
1398 break;
1399
1400 switch (IPC_GET_IMETHOD(call)) {
1401 case LOC_SERVICE_GET_ID:
1402 loc_service_get_id(callid, &call);
1403 break;
1404 case LOC_SERVICE_GET_NAME:
1405 loc_service_get_name(callid, &call);
1406 break;
1407 case LOC_NAMESPACE_GET_ID:
1408 loc_namespace_get_id(callid, &call);
1409 break;
1410 case LOC_CALLBACK_CREATE:
1411 loc_callback_create(callid, &call);
1412 break;
1413 case LOC_CATEGORY_GET_ID:
1414 loc_category_get_id(callid, &call);
1415 break;
1416 case LOC_CATEGORY_GET_NAME:
1417 loc_category_get_name(callid, &call);
1418 break;
1419 case LOC_CATEGORY_GET_SVCS:
1420 loc_category_get_svcs(callid, &call);
1421 break;
1422 case LOC_ID_PROBE:
1423 loc_id_probe(callid, &call);
1424 break;
1425 case LOC_NULL_CREATE:
1426 loc_null_create(callid, &call);
1427 break;
1428 case LOC_NULL_DESTROY:
1429 loc_null_destroy(callid, &call);
1430 break;
1431 case LOC_GET_NAMESPACE_COUNT:
1432 loc_get_namespace_count(callid, &call);
1433 break;
1434 case LOC_GET_SERVICE_COUNT:
1435 loc_get_service_count(callid, &call);
1436 break;
1437 case LOC_GET_CATEGORIES:
1438 loc_get_categories(callid, &call);
1439 break;
1440 case LOC_GET_NAMESPACES:
1441 loc_get_namespaces(callid, &call);
1442 break;
1443 case LOC_GET_SERVICES:
1444 loc_get_services(callid, &call);
1445 break;
1446 default:
1447 async_answer_0(callid, ENOENT);
1448 }
1449 }
1450}
1451
1452/** Function for handling connections to location service
1453 *
1454 */
1455static void loc_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1456{
1457 /* Select interface */
1458 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
1459 case LOC_PORT_SUPPLIER:
1460 loc_connection_supplier(iid, icall);
1461 break;
1462 case LOC_PORT_CONSUMER:
1463 loc_connection_consumer(iid, icall);
1464 break;
1465 case LOC_CONNECT_TO_SERVICE:
1466 /* Connect client to selected service */
1467 loc_forward(iid, icall);
1468 break;
1469 default:
1470 /* No such interface */
1471 async_answer_0(iid, ENOENT);
1472 }
1473}
1474
1475/**
1476 *
1477 */
1478int main(int argc, char *argv[])
1479{
1480 printf("%s: HelenOS Location Service\n", NAME);
1481
1482 if (!loc_init()) {
1483 printf("%s: Error while initializing service\n", NAME);
1484 return -1;
1485 }
1486
1487 /* Set a handler of incomming connections */
1488 async_set_client_connection(loc_connection);
1489
1490 /* Register location service at naming service */
1491 int rc = service_register(SERVICE_LOC);
1492 if (rc != EOK)
1493 return rc;
1494
1495 printf("%s: Accepting connections\n", NAME);
1496 async_manager();
1497
1498 /* Never reached */
1499 return 0;
1500}
1501
1502/**
1503 * @}
1504 */
Note: See TracBrowser for help on using the repository browser.