source: mainline/uspace/srv/loc/loc.c@ cce8a83

lfn serial ticket/834-toolchain-update topic/fix-logger-deadlock topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cce8a83 was cce8a83, checked in by Jiri Svoboda <jiri@…>, 15 years ago

loc_service_get_name() to translate service ID to name.

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