Use `errno_t` in all uspace and kernel code.
[helenos.git] / uspace / srv / locsrv / locsrv.c
blobdbb2956d3d717d051365946abfa90281e6189eba
1 /*
2 * Copyright (c) 2007 Josef Cejka
3 * Copyright (c) 2011 Jiri Svoboda
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
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.
30 /**
31 * @defgroup loc Location Service.
32 * @brief HelenOS location service.
33 * @{
36 /** @file
39 #include <ipc/services.h>
40 #include <ns.h>
41 #include <async.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #include <stdbool.h>
45 #include <fibril_synch.h>
46 #include <macros.h>
47 #include <stdlib.h>
48 #include <str.h>
49 #include <str_error.h>
50 #include <ipc/loc.h>
51 #include <assert.h>
53 #include "category.h"
54 #include "locsrv.h"
56 #define NAME "loc"
57 #define NULL_SERVICES 256
59 /** Callback session */
60 typedef struct {
61 link_t cb_sess_list;
62 async_sess_t *sess;
63 } cb_sess_t;
65 LIST_INITIALIZE(services_list);
66 LIST_INITIALIZE(namespaces_list);
67 LIST_INITIALIZE(servers_list);
69 /* Locking order:
70 * servers_list_mutex
71 * services_list_mutex
72 * (loc_server_t *)->services_mutex
73 * create_id_mutex
74 **/
76 FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
77 static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
78 static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
79 static FIBRIL_MUTEX_INITIALIZE(create_id_mutex);
80 static FIBRIL_MUTEX_INITIALIZE(null_services_mutex);
82 static service_id_t last_id = 0;
83 static loc_service_t *null_services[NULL_SERVICES];
86 * Dummy list for null services. This is necessary so that null services can
87 * be used just as any other services, e.g. in loc_service_unregister_core().
89 static LIST_INITIALIZE(dummy_null_services);
91 /** Service directory ogranized by categories (yellow pages) */
92 static categ_dir_t cdir;
94 static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
95 static LIST_INITIALIZE(callback_sess_list);
97 service_id_t loc_create_id(void)
99 /* TODO: allow reusing old ids after their unregistration
100 * and implement some version of LRU algorithm, avoid overflow
103 fibril_mutex_lock(&create_id_mutex);
104 last_id++;
105 fibril_mutex_unlock(&create_id_mutex);
107 return last_id;
110 /** Convert fully qualified service name to namespace and service name.
112 * A fully qualified service name can be either a plain service name
113 * (then the namespace is considered to be an empty string) or consist
114 * of two components separated by a slash. No more than one slash
115 * is allowed.
118 static bool loc_fqsn_split(const char *fqsn, char **ns_name, char **name)
120 size_t cnt = 0;
121 size_t slash_offset = 0;
122 size_t slash_after = 0;
124 size_t offset = 0;
125 size_t offset_prev = 0;
126 wchar_t c;
128 while ((c = str_decode(fqsn, &offset, STR_NO_LIMIT)) != 0) {
129 if (c == '/') {
130 cnt++;
131 slash_offset = offset_prev;
132 slash_after = offset;
134 offset_prev = offset;
137 /* More than one slash */
138 if (cnt > 1)
139 return false;
141 /* No slash -> namespace is empty */
142 if (cnt == 0) {
143 *ns_name = str_dup("");
144 if (*ns_name == NULL)
145 return false;
147 *name = str_dup(fqsn);
148 if (*name == NULL) {
149 free(*ns_name);
150 return false;
153 if (str_cmp(*name, "") == 0) {
154 free(*name);
155 free(*ns_name);
156 return false;
159 return true;
162 /* Exactly one slash */
163 *ns_name = str_ndup(fqsn, slash_offset);
164 if (*ns_name == NULL)
165 return false;
167 *name = str_dup(fqsn + slash_after);
168 if (*name == NULL) {
169 free(*ns_name);
170 return false;
173 if (str_cmp(*name, "") == 0) {
174 free(*name);
175 free(*ns_name);
176 return false;
179 return true;
182 /** Find namespace with given name. */
183 static loc_namespace_t *loc_namespace_find_name(const char *name)
185 assert(fibril_mutex_is_locked(&services_list_mutex));
187 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
188 if (str_cmp(namespace->name, name) == 0)
189 return namespace;
192 return NULL;
195 /** Find namespace with given ID.
197 * @todo: use hash table
200 static loc_namespace_t *loc_namespace_find_id(service_id_t id)
202 assert(fibril_mutex_is_locked(&services_list_mutex));
204 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
205 if (namespace->id == id)
206 return namespace;
209 return NULL;
212 /** Find service with given name. */
213 static loc_service_t *loc_service_find_name(const char *ns_name,
214 const char *name)
216 assert(fibril_mutex_is_locked(&services_list_mutex));
218 list_foreach(services_list, services, loc_service_t, service) {
219 if ((str_cmp(service->namespace->name, ns_name) == 0)
220 && (str_cmp(service->name, name) == 0))
221 return service;
224 return NULL;
227 /** Find service with given ID.
229 * @todo: use hash table
232 static loc_service_t *loc_service_find_id(service_id_t id)
234 assert(fibril_mutex_is_locked(&services_list_mutex));
236 list_foreach(services_list, services, loc_service_t, service) {
237 if (service->id == id)
238 return service;
241 return NULL;
244 /** Create a namespace (if not already present). */
245 static loc_namespace_t *loc_namespace_create(const char *ns_name)
247 loc_namespace_t *namespace;
249 assert(fibril_mutex_is_locked(&services_list_mutex));
251 namespace = loc_namespace_find_name(ns_name);
252 if (namespace != NULL)
253 return namespace;
255 namespace = (loc_namespace_t *) malloc(sizeof(loc_namespace_t));
256 if (namespace == NULL)
257 return NULL;
259 namespace->name = str_dup(ns_name);
260 if (namespace->name == NULL) {
261 free(namespace);
262 return NULL;
265 namespace->id = loc_create_id();
266 namespace->refcnt = 0;
269 * Insert new namespace into list of registered namespaces
271 list_append(&(namespace->namespaces), &namespaces_list);
273 return namespace;
276 /** Destroy a namespace (if it is no longer needed). */
277 static void loc_namespace_destroy(loc_namespace_t *namespace)
279 assert(fibril_mutex_is_locked(&services_list_mutex));
281 if (namespace->refcnt == 0) {
282 list_remove(&(namespace->namespaces));
284 free(namespace->name);
285 free(namespace);
289 /** Increase namespace reference count by including service. */
290 static void loc_namespace_addref(loc_namespace_t *namespace,
291 loc_service_t *service)
293 assert(fibril_mutex_is_locked(&services_list_mutex));
295 service->namespace = namespace;
296 namespace->refcnt++;
299 /** Decrease namespace reference count. */
300 static void loc_namespace_delref(loc_namespace_t *namespace)
302 assert(fibril_mutex_is_locked(&services_list_mutex));
304 namespace->refcnt--;
305 loc_namespace_destroy(namespace);
308 /** Unregister service and free it. */
309 static void loc_service_unregister_core(loc_service_t *service)
311 assert(fibril_mutex_is_locked(&services_list_mutex));
312 assert(fibril_mutex_is_locked(&cdir.mutex));
314 loc_namespace_delref(service->namespace);
315 list_remove(&(service->services));
316 list_remove(&(service->server_services));
318 /* Remove service from all categories. */
319 while (!list_empty(&service->cat_memb)) {
320 link_t *link = list_first(&service->cat_memb);
321 svc_categ_t *memb = list_get_instance(link, svc_categ_t,
322 svc_link);
323 category_t *cat = memb->cat;
325 fibril_mutex_lock(&cat->mutex);
326 category_remove_service(memb);
327 fibril_mutex_unlock(&cat->mutex);
330 free(service->name);
331 free(service);
335 * Read info about new server and add it into linked list of registered
336 * servers.
338 static loc_server_t *loc_server_register(void)
340 ipc_call_t icall;
341 ipc_callid_t iid = async_get_call(&icall);
343 if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
344 async_answer_0(iid, EREFUSED);
345 return NULL;
348 loc_server_t *server =
349 (loc_server_t *) malloc(sizeof(loc_server_t));
350 if (server == NULL) {
351 async_answer_0(iid, ENOMEM);
352 return NULL;
356 * Get server name
358 errno_t rc = async_data_write_accept((void **) &server->name, true, 0,
359 LOC_NAME_MAXLEN, 0, NULL);
360 if (rc != EOK) {
361 free(server);
362 async_answer_0(iid, rc);
363 return NULL;
367 * Create connection to the server
369 server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
370 if (!server->sess) {
371 free(server->name);
372 free(server);
373 async_answer_0(iid, ENOTSUP);
374 return NULL;
378 * Initialize mutex for list of services
379 * supplied by this server
381 fibril_mutex_initialize(&server->services_mutex);
384 * Initialize list of supplied services
386 list_initialize(&server->services);
387 link_initialize(&server->servers);
389 fibril_mutex_lock(&servers_list_mutex);
391 /* TODO:
392 * Check that no server with name equal to
393 * server->name is registered
397 * Insert new server into list of registered servers
399 list_append(&(server->servers), &servers_list);
400 fibril_mutex_unlock(&servers_list_mutex);
402 async_answer_0(iid, EOK);
404 return server;
408 * Unregister server, unregister all its services and free server
409 * structure.
412 static errno_t loc_server_unregister(loc_server_t *server)
414 if (server == NULL)
415 return EEXIST;
417 fibril_mutex_lock(&servers_list_mutex);
419 if (server->sess)
420 async_hangup(server->sess);
422 /* Remove it from list of servers */
423 list_remove(&(server->servers));
425 /* Unregister all its services */
426 fibril_mutex_lock(&services_list_mutex);
427 fibril_mutex_lock(&server->services_mutex);
428 fibril_mutex_lock(&cdir.mutex);
430 while (!list_empty(&server->services)) {
431 loc_service_t *service = list_get_instance(
432 list_first(&server->services), loc_service_t,
433 server_services);
434 loc_service_unregister_core(service);
437 fibril_mutex_unlock(&cdir.mutex);
438 fibril_mutex_unlock(&server->services_mutex);
439 fibril_mutex_unlock(&services_list_mutex);
440 fibril_mutex_unlock(&servers_list_mutex);
442 /* Free name and server */
443 if (server->name != NULL)
444 free(server->name);
446 free(server);
448 loc_category_change_event();
449 return EOK;
452 /** Register service
455 static void loc_service_register(ipc_callid_t iid, ipc_call_t *icall,
456 loc_server_t *server)
458 if (server == NULL) {
459 async_answer_0(iid, EREFUSED);
460 return;
463 /* Create new service entry */
464 loc_service_t *service =
465 (loc_service_t *) malloc(sizeof(loc_service_t));
466 if (service == NULL) {
467 async_answer_0(iid, ENOMEM);
468 return;
471 /* Get fqsn */
472 char *fqsn;
473 errno_t rc = async_data_write_accept((void **) &fqsn, true, 0,
474 LOC_NAME_MAXLEN, 0, NULL);
475 if (rc != EOK) {
476 free(service);
477 async_answer_0(iid, rc);
478 return;
481 char *ns_name;
482 if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
483 free(fqsn);
484 free(service);
485 async_answer_0(iid, EINVAL);
486 return;
489 free(fqsn);
491 fibril_mutex_lock(&services_list_mutex);
493 loc_namespace_t *namespace = loc_namespace_create(ns_name);
494 free(ns_name);
495 if (namespace == NULL) {
496 fibril_mutex_unlock(&services_list_mutex);
497 free(service->name);
498 free(service);
499 async_answer_0(iid, ENOMEM);
500 return;
503 link_initialize(&service->services);
504 link_initialize(&service->server_services);
505 list_initialize(&service->cat_memb);
507 /* Check that service is not already registered */
508 if (loc_service_find_name(namespace->name, service->name) != NULL) {
509 printf("%s: Service '%s/%s' already registered\n", NAME,
510 namespace->name, service->name);
511 loc_namespace_destroy(namespace);
512 fibril_mutex_unlock(&services_list_mutex);
513 free(service->name);
514 free(service);
515 async_answer_0(iid, EEXIST);
516 return;
519 /* Get unique service ID */
520 service->id = loc_create_id();
522 loc_namespace_addref(namespace, service);
523 service->server = server;
525 /* Insert service into list of all services */
526 list_append(&service->services, &services_list);
528 /* Insert service into list of services supplied by one server */
529 fibril_mutex_lock(&service->server->services_mutex);
531 list_append(&service->server_services, &service->server->services);
533 fibril_mutex_unlock(&service->server->services_mutex);
534 fibril_condvar_broadcast(&services_list_cv);
535 fibril_mutex_unlock(&services_list_mutex);
537 async_answer_1(iid, EOK, service->id);
543 static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall,
544 loc_server_t *server)
546 loc_service_t *svc;
548 fibril_mutex_lock(&services_list_mutex);
549 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
550 if (svc == NULL) {
551 fibril_mutex_unlock(&services_list_mutex);
552 async_answer_0(iid, ENOENT);
553 return;
556 fibril_mutex_lock(&cdir.mutex);
557 loc_service_unregister_core(svc);
558 fibril_mutex_unlock(&cdir.mutex);
559 fibril_mutex_unlock(&services_list_mutex);
562 * First send out all notifications and only then answer the request.
563 * Otherwise the current fibril might block and transitively wait for
564 * the completion of requests that are routed to it via an IPC loop.
566 loc_category_change_event();
567 async_answer_0(iid, EOK);
570 static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
572 ipc_callid_t callid;
573 size_t size;
574 size_t act_size;
575 category_t *cat;
577 if (!async_data_read_receive(&callid, &size)) {
578 async_answer_0(callid, EREFUSED);
579 async_answer_0(iid, EREFUSED);
580 return;
583 fibril_mutex_lock(&cdir.mutex);
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;
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;
601 errno_t retval = async_data_read_finalize(callid, cat->name,
602 min(size, act_size));
604 fibril_mutex_unlock(&cdir.mutex);
606 async_answer_0(iid, retval);
609 static void loc_service_get_name(ipc_callid_t iid, ipc_call_t *icall)
611 ipc_callid_t callid;
612 size_t size;
613 size_t act_size;
614 loc_service_t *svc;
615 char *fqn;
617 if (!async_data_read_receive(&callid, &size)) {
618 async_answer_0(callid, EREFUSED);
619 async_answer_0(iid, EREFUSED);
620 return;
623 fibril_mutex_lock(&services_list_mutex);
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;
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;
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;
649 errno_t retval = async_data_read_finalize(callid, fqn,
650 min(size, act_size));
651 free(fqn);
653 fibril_mutex_unlock(&services_list_mutex);
655 async_answer_0(iid, retval);
658 static void loc_service_get_server_name(ipc_callid_t iid, ipc_call_t *icall)
660 ipc_callid_t callid;
661 size_t size;
662 size_t act_size;
663 loc_service_t *svc;
665 if (!async_data_read_receive(&callid, &size)) {
666 async_answer_0(callid, EREFUSED);
667 async_answer_0(iid, EREFUSED);
668 return;
671 fibril_mutex_lock(&services_list_mutex);
673 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
674 if (svc == NULL) {
675 fibril_mutex_unlock(&services_list_mutex);
676 async_answer_0(callid, ENOENT);
677 async_answer_0(iid, ENOENT);
678 return;
681 if (svc->server == NULL) {
682 fibril_mutex_unlock(&services_list_mutex);
683 async_answer_0(callid, EINVAL);
684 async_answer_0(iid, EINVAL);
685 return;
688 act_size = str_size(svc->server->name);
689 if (act_size > size) {
690 fibril_mutex_unlock(&services_list_mutex);
691 async_answer_0(callid, EOVERFLOW);
692 async_answer_0(iid, EOVERFLOW);
693 return;
696 errno_t retval = async_data_read_finalize(callid, svc->server->name,
697 min(size, act_size));
699 fibril_mutex_unlock(&services_list_mutex);
701 async_answer_0(iid, retval);
704 /** Connect client to the service.
706 * Find server supplying requested service and forward
707 * the message to it.
710 static void loc_forward(ipc_callid_t callid, ipc_call_t *call, void *arg)
712 fibril_mutex_lock(&services_list_mutex);
715 * Get ID from request
717 iface_t iface = IPC_GET_ARG1(*call);
718 service_id_t id = IPC_GET_ARG2(*call);
719 loc_service_t *svc = loc_service_find_id(id);
721 if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
722 fibril_mutex_unlock(&services_list_mutex);
723 async_answer_0(callid, ENOENT);
724 return;
727 async_exch_t *exch = async_exchange_begin(svc->server->sess);
728 async_forward_fast(callid, exch, iface, svc->id, 0, IPC_FF_NONE);
729 async_exchange_end(exch);
731 fibril_mutex_unlock(&services_list_mutex);
734 /** Find ID for service identified by name.
736 * In answer will be send EOK and service ID in arg1 or a error
737 * code from errno.h.
740 static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
742 char *fqsn;
744 /* Get fqsn */
745 errno_t rc = async_data_write_accept((void **) &fqsn, true, 0,
746 LOC_NAME_MAXLEN, 0, NULL);
747 if (rc != EOK) {
748 async_answer_0(iid, rc);
749 return;
752 char *ns_name;
753 char *name;
754 if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
755 free(fqsn);
756 async_answer_0(iid, EINVAL);
757 return;
760 free(fqsn);
762 fibril_mutex_lock(&services_list_mutex);
763 const loc_service_t *svc;
765 recheck:
768 * Find service name in the list of known services.
770 svc = loc_service_find_name(ns_name, name);
773 * Device was not found.
775 if (svc == NULL) {
776 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
777 /* Blocking lookup */
778 fibril_condvar_wait(&services_list_cv,
779 &services_list_mutex);
780 goto recheck;
783 async_answer_0(iid, ENOENT);
784 free(ns_name);
785 free(name);
786 fibril_mutex_unlock(&services_list_mutex);
787 return;
790 async_answer_1(iid, EOK, svc->id);
792 fibril_mutex_unlock(&services_list_mutex);
793 free(ns_name);
794 free(name);
797 /** Find ID for namespace identified by name.
799 * In answer will be send EOK and service ID in arg1 or a error
800 * code from errno.h.
803 static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
805 char *name;
807 /* Get service name */
808 errno_t rc = async_data_write_accept((void **) &name, true, 0,
809 LOC_NAME_MAXLEN, 0, NULL);
810 if (rc != EOK) {
811 async_answer_0(iid, rc);
812 return;
815 fibril_mutex_lock(&services_list_mutex);
816 const loc_namespace_t *namespace;
818 recheck:
821 * Find namespace name in the list of known namespaces.
823 namespace = loc_namespace_find_name(name);
826 * Namespace was not found.
828 if (namespace == NULL) {
829 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
830 /* Blocking lookup */
831 fibril_condvar_wait(&services_list_cv,
832 &services_list_mutex);
833 goto recheck;
836 async_answer_0(iid, ENOENT);
837 free(name);
838 fibril_mutex_unlock(&services_list_mutex);
839 return;
842 async_answer_1(iid, EOK, namespace->id);
844 fibril_mutex_unlock(&services_list_mutex);
845 free(name);
848 /** Create callback connection.
850 * Create callback connection which will be used to send category change
851 * events.
853 * On success, answer will contain EOK errno_t retval.
854 * On failure, error code will be sent in retval.
857 static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
859 cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
860 if (cb_sess == NULL) {
861 async_answer_0(iid, ENOMEM);
862 return;
865 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
866 if (sess == NULL) {
867 free(cb_sess);
868 async_answer_0(iid, ENOMEM);
869 return;
872 cb_sess->sess = sess;
873 link_initialize(&cb_sess->cb_sess_list);
875 fibril_mutex_lock(&callback_sess_mutex);
876 list_append(&cb_sess->cb_sess_list, &callback_sess_list);
877 fibril_mutex_unlock(&callback_sess_mutex);
879 async_answer_0(iid, EOK);
882 void loc_category_change_event(void)
884 fibril_mutex_lock(&callback_sess_mutex);
886 list_foreach(callback_sess_list, cb_sess_list, cb_sess_t, cb_sess) {
887 async_exch_t *exch = async_exchange_begin(cb_sess->sess);
888 async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
889 async_exchange_end(exch);
892 fibril_mutex_unlock(&callback_sess_mutex);
895 /** Find ID for category specified by name.
897 * On success, answer will contain EOK errno_t retval and service ID in arg1.
898 * On failure, error code will be sent in retval.
901 static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
903 char *name;
904 category_t *cat;
906 /* Get service name */
907 errno_t rc = async_data_write_accept((void **) &name, true, 0,
908 LOC_NAME_MAXLEN, 0, NULL);
909 if (rc != EOK) {
910 async_answer_0(iid, rc);
911 return;
914 fibril_mutex_lock(&cdir.mutex);
916 cat = category_find_by_name(&cdir, name);
917 if (cat == NULL) {
918 /* Category not found */
919 async_answer_0(iid, ENOENT);
920 goto cleanup;
923 async_answer_1(iid, EOK, cat->id);
924 cleanup:
925 fibril_mutex_unlock(&cdir.mutex);
926 free(name);
929 static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
931 fibril_mutex_lock(&services_list_mutex);
933 loc_namespace_t *namespace =
934 loc_namespace_find_id(IPC_GET_ARG1(*icall));
935 if (namespace == NULL) {
936 loc_service_t *svc =
937 loc_service_find_id(IPC_GET_ARG1(*icall));
938 if (svc == NULL)
939 async_answer_1(iid, EOK, LOC_OBJECT_NONE);
940 else
941 async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
942 } else
943 async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
945 fibril_mutex_unlock(&services_list_mutex);
948 static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
950 fibril_mutex_lock(&services_list_mutex);
951 async_answer_1(iid, EOK, list_count(&namespaces_list));
952 fibril_mutex_unlock(&services_list_mutex);
955 static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
957 fibril_mutex_lock(&services_list_mutex);
959 loc_namespace_t *namespace =
960 loc_namespace_find_id(IPC_GET_ARG1(*icall));
961 if (namespace == NULL)
962 async_answer_0(iid, EEXIST);
963 else
964 async_answer_1(iid, EOK, namespace->refcnt);
966 fibril_mutex_unlock(&services_list_mutex);
969 static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
971 ipc_callid_t callid;
972 size_t size;
973 size_t act_size;
974 errno_t rc;
976 if (!async_data_read_receive(&callid, &size)) {
977 async_answer_0(callid, EREFUSED);
978 async_answer_0(iid, EREFUSED);
979 return;
982 category_id_t *id_buf = (category_id_t *) malloc(size);
983 if (id_buf == NULL) {
984 fibril_mutex_unlock(&cdir.mutex);
985 async_answer_0(callid, ENOMEM);
986 async_answer_0(iid, ENOMEM);
987 return;
990 fibril_mutex_lock(&cdir.mutex);
992 rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
993 if (rc != EOK) {
994 fibril_mutex_unlock(&cdir.mutex);
995 async_answer_0(callid, rc);
996 async_answer_0(iid, rc);
997 return;
1000 fibril_mutex_unlock(&cdir.mutex);
1002 errno_t retval = async_data_read_finalize(callid, id_buf, size);
1003 free(id_buf);
1005 async_answer_1(iid, retval, act_size);
1008 static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
1010 ipc_callid_t callid;
1011 size_t size;
1012 if (!async_data_read_receive(&callid, &size)) {
1013 async_answer_0(callid, EREFUSED);
1014 async_answer_0(iid, EREFUSED);
1015 return;
1018 if ((size % sizeof(loc_sdesc_t)) != 0) {
1019 async_answer_0(callid, EINVAL);
1020 async_answer_0(iid, EINVAL);
1021 return;
1024 fibril_mutex_lock(&services_list_mutex);
1026 size_t count = size / sizeof(loc_sdesc_t);
1027 if (count != list_count(&namespaces_list)) {
1028 fibril_mutex_unlock(&services_list_mutex);
1029 async_answer_0(callid, EOVERFLOW);
1030 async_answer_0(iid, EOVERFLOW);
1031 return;
1034 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1035 if (desc == NULL) {
1036 fibril_mutex_unlock(&services_list_mutex);
1037 async_answer_0(callid, ENOMEM);
1038 async_answer_0(iid, ENOMEM);
1039 return;
1042 size_t pos = 0;
1043 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
1044 desc[pos].id = namespace->id;
1045 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
1046 pos++;
1049 errno_t retval = async_data_read_finalize(callid, desc, size);
1051 free(desc);
1052 fibril_mutex_unlock(&services_list_mutex);
1054 async_answer_0(iid, retval);
1057 static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
1059 /* FIXME: Use faster algorithm which can make better use
1060 of namespaces */
1062 ipc_callid_t callid;
1063 size_t size;
1064 if (!async_data_read_receive(&callid, &size)) {
1065 async_answer_0(callid, EREFUSED);
1066 async_answer_0(iid, EREFUSED);
1067 return;
1070 if ((size % sizeof(loc_sdesc_t)) != 0) {
1071 async_answer_0(callid, EINVAL);
1072 async_answer_0(iid, EINVAL);
1073 return;
1076 fibril_mutex_lock(&services_list_mutex);
1078 loc_namespace_t *namespace =
1079 loc_namespace_find_id(IPC_GET_ARG1(*icall));
1080 if (namespace == NULL) {
1081 fibril_mutex_unlock(&services_list_mutex);
1082 async_answer_0(callid, ENOENT);
1083 async_answer_0(iid, ENOENT);
1084 return;
1087 size_t count = size / sizeof(loc_sdesc_t);
1088 if (count != namespace->refcnt) {
1089 fibril_mutex_unlock(&services_list_mutex);
1090 async_answer_0(callid, EOVERFLOW);
1091 async_answer_0(iid, EOVERFLOW);
1092 return;
1095 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1096 if (desc == NULL) {
1097 fibril_mutex_unlock(&services_list_mutex);
1098 async_answer_0(callid, ENOMEM);
1099 async_answer_0(iid, EREFUSED);
1100 return;
1103 size_t pos = 0;
1104 list_foreach(services_list, services, loc_service_t, service) {
1105 if (service->namespace == namespace) {
1106 desc[pos].id = service->id;
1107 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
1108 pos++;
1112 errno_t retval = async_data_read_finalize(callid, desc, size);
1114 free(desc);
1115 fibril_mutex_unlock(&services_list_mutex);
1117 async_answer_0(iid, retval);
1120 static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
1122 ipc_callid_t callid;
1123 size_t size;
1124 size_t act_size;
1125 errno_t rc;
1127 if (!async_data_read_receive(&callid, &size)) {
1128 async_answer_0(callid, EREFUSED);
1129 async_answer_0(iid, EREFUSED);
1130 return;
1133 fibril_mutex_lock(&cdir.mutex);
1135 category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
1136 if (cat == NULL) {
1137 fibril_mutex_unlock(&cdir.mutex);
1138 async_answer_0(callid, ENOENT);
1139 async_answer_0(iid, ENOENT);
1140 return;
1143 category_id_t *id_buf = (category_id_t *) malloc(size);
1144 if (id_buf == NULL) {
1145 fibril_mutex_unlock(&cdir.mutex);
1146 async_answer_0(callid, ENOMEM);
1147 async_answer_0(iid, ENOMEM);
1148 return;
1151 fibril_mutex_lock(&cat->mutex);
1153 rc = category_get_services(cat, id_buf, size, &act_size);
1154 if (rc != EOK) {
1155 fibril_mutex_unlock(&cat->mutex);
1156 fibril_mutex_unlock(&cdir.mutex);
1157 async_answer_0(callid, rc);
1158 async_answer_0(iid, rc);
1159 return;
1162 fibril_mutex_unlock(&cat->mutex);
1163 fibril_mutex_unlock(&cdir.mutex);
1165 errno_t retval = async_data_read_finalize(callid, id_buf, size);
1166 free(id_buf);
1168 async_answer_1(iid, retval, act_size);
1172 static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
1174 fibril_mutex_lock(&null_services_mutex);
1176 unsigned int i;
1177 bool fnd = false;
1179 for (i = 0; i < NULL_SERVICES; i++) {
1180 if (null_services[i] == NULL) {
1181 fnd = true;
1182 break;
1186 if (!fnd) {
1187 fibril_mutex_unlock(&null_services_mutex);
1188 async_answer_0(iid, ENOMEM);
1189 return;
1192 char null[LOC_NAME_MAXLEN];
1193 snprintf(null, LOC_NAME_MAXLEN, "%u", i);
1195 char *dev_name = str_dup(null);
1196 if (dev_name == NULL) {
1197 fibril_mutex_unlock(&null_services_mutex);
1198 async_answer_0(iid, ENOMEM);
1199 return;
1202 loc_service_t *service =
1203 (loc_service_t *) malloc(sizeof(loc_service_t));
1204 if (service == NULL) {
1205 fibril_mutex_unlock(&null_services_mutex);
1206 async_answer_0(iid, ENOMEM);
1207 return;
1210 fibril_mutex_lock(&services_list_mutex);
1212 loc_namespace_t *namespace = loc_namespace_create("null");
1213 if (namespace == NULL) {
1214 fibril_mutex_lock(&services_list_mutex);
1215 fibril_mutex_unlock(&null_services_mutex);
1216 async_answer_0(iid, ENOMEM);
1217 return;
1220 link_initialize(&service->services);
1221 link_initialize(&service->server_services);
1222 list_initialize(&service->cat_memb);
1224 /* Get unique service ID */
1225 service->id = loc_create_id();
1226 service->server = NULL;
1228 loc_namespace_addref(namespace, service);
1229 service->name = dev_name;
1232 * Insert service into list of all services and into null services array.
1233 * Insert service into a dummy list of null server's services so that it
1234 * can be safely removed later.
1236 list_append(&service->services, &services_list);
1237 list_append(&service->server_services, &dummy_null_services);
1238 null_services[i] = service;
1240 fibril_mutex_unlock(&services_list_mutex);
1241 fibril_mutex_unlock(&null_services_mutex);
1243 async_answer_1(iid, EOK, (sysarg_t) i);
1246 static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
1248 sysarg_t i = IPC_GET_ARG1(*icall);
1249 if (i >= NULL_SERVICES) {
1250 async_answer_0(iid, ELIMIT);
1251 return;
1254 fibril_mutex_lock(&null_services_mutex);
1256 if (null_services[i] == NULL) {
1257 fibril_mutex_unlock(&null_services_mutex);
1258 async_answer_0(iid, ENOENT);
1259 return;
1262 fibril_mutex_lock(&services_list_mutex);
1263 fibril_mutex_lock(&cdir.mutex);
1264 loc_service_unregister_core(null_services[i]);
1265 fibril_mutex_unlock(&cdir.mutex);
1266 fibril_mutex_unlock(&services_list_mutex);
1268 null_services[i] = NULL;
1270 fibril_mutex_unlock(&null_services_mutex);
1271 async_answer_0(iid, EOK);
1274 static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
1276 category_t *cat;
1277 loc_service_t *svc;
1278 catid_t cat_id;
1279 service_id_t svc_id;
1280 errno_t retval;
1282 svc_id = IPC_GET_ARG1(*icall);
1283 cat_id = IPC_GET_ARG2(*icall);
1285 fibril_mutex_lock(&services_list_mutex);
1286 fibril_mutex_lock(&cdir.mutex);
1288 cat = category_get(&cdir, cat_id);
1289 svc = loc_service_find_id(svc_id);
1291 if (cat == NULL || svc == NULL) {
1292 fibril_mutex_unlock(&cdir.mutex);
1293 fibril_mutex_unlock(&services_list_mutex);
1294 async_answer_0(iid, ENOENT);
1295 return;
1298 fibril_mutex_lock(&cat->mutex);
1299 retval = category_add_service(cat, svc);
1301 fibril_mutex_unlock(&cat->mutex);
1302 fibril_mutex_unlock(&cdir.mutex);
1303 fibril_mutex_unlock(&services_list_mutex);
1306 * First send out all notifications and only then answer the request.
1307 * Otherwise the current fibril might block and transitively wait for
1308 * the completion of requests that are routed to it via an IPC loop.
1310 loc_category_change_event();
1311 async_answer_0(iid, retval);
1315 /** Initialize location service.
1319 static bool loc_init(void)
1321 unsigned int i;
1322 category_t *cat;
1324 for (i = 0; i < NULL_SERVICES; i++)
1325 null_services[i] = NULL;
1327 categ_dir_init(&cdir);
1329 cat = category_new("disk");
1330 categ_dir_add_cat(&cdir, cat);
1332 cat = category_new("partition");
1333 categ_dir_add_cat(&cdir, cat);
1335 cat = category_new("iplink");
1336 categ_dir_add_cat(&cdir, cat);
1338 cat = category_new("keyboard");
1339 categ_dir_add_cat(&cdir, cat);
1341 cat = category_new("mouse");
1342 categ_dir_add_cat(&cdir, cat);
1344 cat = category_new("led");
1345 categ_dir_add_cat(&cdir, cat);
1347 cat = category_new("serial");
1348 categ_dir_add_cat(&cdir, cat);
1350 cat = category_new("console");
1351 categ_dir_add_cat(&cdir, cat);
1353 cat = category_new("clock");
1354 categ_dir_add_cat(&cdir, cat);
1356 cat = category_new("test3");
1357 categ_dir_add_cat(&cdir, cat);
1359 cat = category_new("usbhc");
1360 categ_dir_add_cat(&cdir, cat);
1362 cat = category_new("virt-null");
1363 categ_dir_add_cat(&cdir, cat);
1365 cat = category_new("virtual");
1366 categ_dir_add_cat(&cdir, cat);
1368 cat = category_new("nic");
1369 categ_dir_add_cat(&cdir, cat);
1371 cat = category_new("ieee80211");
1372 categ_dir_add_cat(&cdir, cat);
1374 cat = category_new("irc");
1375 categ_dir_add_cat(&cdir, cat);
1377 cat = category_new("visualizer");
1378 categ_dir_add_cat(&cdir, cat);
1380 cat = category_new("renderer");
1381 categ_dir_add_cat(&cdir, cat);
1383 cat = category_new("audio-pcm");
1384 categ_dir_add_cat(&cdir, cat);
1386 return true;
1389 /** Handle connection on supplier port.
1392 static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1394 /* Accept connection */
1395 async_answer_0(iid, EOK);
1397 loc_server_t *server = loc_server_register();
1398 if (server == NULL)
1399 return;
1401 while (true) {
1402 ipc_call_t call;
1403 ipc_callid_t callid = async_get_call(&call);
1405 if (!IPC_GET_IMETHOD(call))
1406 break;
1408 switch (IPC_GET_IMETHOD(call)) {
1409 case LOC_SERVER_UNREGISTER:
1410 if (server == NULL)
1411 async_answer_0(callid, ENOENT);
1412 else
1413 async_answer_0(callid, EOK);
1414 break;
1415 case LOC_SERVICE_ADD_TO_CAT:
1416 /* Add service to category */
1417 loc_service_add_to_cat(callid, &call);
1418 break;
1419 case LOC_SERVICE_REGISTER:
1420 /* Register one service */
1421 loc_service_register(callid, &call, server);
1422 break;
1423 case LOC_SERVICE_UNREGISTER:
1424 /* Remove one service */
1425 loc_service_unregister(callid, &call, server);
1426 break;
1427 case LOC_SERVICE_GET_ID:
1428 loc_service_get_id(callid, &call);
1429 break;
1430 case LOC_NAMESPACE_GET_ID:
1431 loc_namespace_get_id(callid, &call);
1432 break;
1433 default:
1434 async_answer_0(callid, ENOENT);
1438 if (server != NULL) {
1440 * Unregister the server and all its services.
1442 loc_server_unregister(server);
1443 server = NULL;
1447 /** Handle connection on consumer port.
1450 static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1452 /* Accept connection */
1453 async_answer_0(iid, EOK);
1455 while (true) {
1456 ipc_call_t call;
1457 ipc_callid_t callid = async_get_call(&call);
1459 if (!IPC_GET_IMETHOD(call))
1460 break;
1462 switch (IPC_GET_IMETHOD(call)) {
1463 case LOC_SERVICE_GET_ID:
1464 loc_service_get_id(callid, &call);
1465 break;
1466 case LOC_SERVICE_GET_NAME:
1467 loc_service_get_name(callid, &call);
1468 break;
1469 case LOC_SERVICE_GET_SERVER_NAME:
1470 loc_service_get_server_name(callid, &call);
1471 break;
1472 case LOC_NAMESPACE_GET_ID:
1473 loc_namespace_get_id(callid, &call);
1474 break;
1475 case LOC_CALLBACK_CREATE:
1476 loc_callback_create(callid, &call);
1477 break;
1478 case LOC_CATEGORY_GET_ID:
1479 loc_category_get_id(callid, &call);
1480 break;
1481 case LOC_CATEGORY_GET_NAME:
1482 loc_category_get_name(callid, &call);
1483 break;
1484 case LOC_CATEGORY_GET_SVCS:
1485 loc_category_get_svcs(callid, &call);
1486 break;
1487 case LOC_ID_PROBE:
1488 loc_id_probe(callid, &call);
1489 break;
1490 case LOC_NULL_CREATE:
1491 loc_null_create(callid, &call);
1492 break;
1493 case LOC_NULL_DESTROY:
1494 loc_null_destroy(callid, &call);
1495 break;
1496 case LOC_GET_NAMESPACE_COUNT:
1497 loc_get_namespace_count(callid, &call);
1498 break;
1499 case LOC_GET_SERVICE_COUNT:
1500 loc_get_service_count(callid, &call);
1501 break;
1502 case LOC_GET_CATEGORIES:
1503 loc_get_categories(callid, &call);
1504 break;
1505 case LOC_GET_NAMESPACES:
1506 loc_get_namespaces(callid, &call);
1507 break;
1508 case LOC_GET_SERVICES:
1509 loc_get_services(callid, &call);
1510 break;
1511 default:
1512 async_answer_0(callid, ENOENT);
1520 int main(int argc, char *argv[])
1522 printf("%s: HelenOS Location Service\n", NAME);
1524 if (!loc_init()) {
1525 printf("%s: Error while initializing service\n", NAME);
1526 return -1;
1529 port_id_t port;
1530 errno_t rc = async_create_port(INTERFACE_LOC_SUPPLIER,
1531 loc_connection_supplier, NULL, &port);
1532 if (rc != EOK) {
1533 printf("%s: Error while creating supplier port: %s\n", NAME, str_error(rc));
1534 return rc;
1537 rc = async_create_port(INTERFACE_LOC_CONSUMER,
1538 loc_connection_consumer, NULL, &port);
1539 if (rc != EOK) {
1540 printf("%s: Error while creating consumer port: %s\n", NAME, str_error(rc));
1541 return rc;
1544 /* Set a handler of incomming connections */
1545 async_set_fallback_port_handler(loc_forward, NULL);
1547 /* Register location service at naming service */
1548 rc = service_register(SERVICE_LOC);
1549 if (rc != EOK) {
1550 printf("%s: Error while registering service: %s\n", NAME, str_error(rc));
1551 return rc;
1554 printf("%s: Accepting connections\n", NAME);
1555 async_manager();
1557 /* Never reached */
1558 return 0;
1562 * @}