2 * Copyright (c) 2007 Josef Cejka
3 * Copyright (c) 2011 Jiri Svoboda
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
31 * @defgroup loc Location Service.
32 * @brief HelenOS location service.
39 #include <ipc/services.h>
45 #include <fibril_synch.h>
49 #include <str_error.h>
57 #define NULL_SERVICES 256
59 /** Callback session */
65 LIST_INITIALIZE(services_list
);
66 LIST_INITIALIZE(namespaces_list
);
67 LIST_INITIALIZE(servers_list
);
72 * (loc_server_t *)->services_mutex
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
);
105 fibril_mutex_unlock(&create_id_mutex
);
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
118 static bool loc_fqsn_split(const char *fqsn
, char **ns_name
, char **name
)
121 size_t slash_offset
= 0;
122 size_t slash_after
= 0;
125 size_t offset_prev
= 0;
128 while ((c
= str_decode(fqsn
, &offset
, STR_NO_LIMIT
)) != 0) {
131 slash_offset
= offset_prev
;
132 slash_after
= offset
;
134 offset_prev
= offset
;
137 /* More than one slash */
141 /* No slash -> namespace is empty */
143 *ns_name
= str_dup("");
144 if (*ns_name
== NULL
)
147 *name
= str_dup(fqsn
);
153 if (str_cmp(*name
, "") == 0) {
162 /* Exactly one slash */
163 *ns_name
= str_ndup(fqsn
, slash_offset
);
164 if (*ns_name
== NULL
)
167 *name
= str_dup(fqsn
+ slash_after
);
173 if (str_cmp(*name
, "") == 0) {
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)
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
)
212 /** Find service with given name. */
213 static loc_service_t
*loc_service_find_name(const char *ns_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))
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
)
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
)
255 namespace = (loc_namespace_t
*) malloc(sizeof(loc_namespace_t
));
256 if (namespace == NULL
)
259 namespace->name
= str_dup(ns_name
);
260 if (namespace->name
== 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
);
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
);
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;
299 /** Decrease namespace reference count. */
300 static void loc_namespace_delref(loc_namespace_t
*namespace)
302 assert(fibril_mutex_is_locked(&services_list_mutex
));
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
,
323 category_t
*cat
= memb
->cat
;
325 fibril_mutex_lock(&cat
->mutex
);
326 category_remove_service(memb
);
327 fibril_mutex_unlock(&cat
->mutex
);
335 * Read info about new server and add it into linked list of registered
338 static loc_server_t
*loc_server_register(void)
341 ipc_callid_t iid
= async_get_call(&icall
);
343 if (IPC_GET_IMETHOD(icall
) != LOC_SERVER_REGISTER
) {
344 async_answer_0(iid
, EREFUSED
);
348 loc_server_t
*server
=
349 (loc_server_t
*) malloc(sizeof(loc_server_t
));
350 if (server
== NULL
) {
351 async_answer_0(iid
, ENOMEM
);
358 errno_t rc
= async_data_write_accept((void **) &server
->name
, true, 0,
359 LOC_NAME_MAXLEN
, 0, NULL
);
362 async_answer_0(iid
, rc
);
367 * Create connection to the server
369 server
->sess
= async_callback_receive(EXCHANGE_SERIALIZE
);
373 async_answer_0(iid
, ENOTSUP
);
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
);
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
);
408 * Unregister server, unregister all its services and free server
412 static errno_t
loc_server_unregister(loc_server_t
*server
)
417 fibril_mutex_lock(&servers_list_mutex
);
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
,
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
)
448 loc_category_change_event();
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
);
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
);
473 errno_t rc
= async_data_write_accept((void **) &fqsn
, true, 0,
474 LOC_NAME_MAXLEN
, 0, NULL
);
477 async_answer_0(iid
, rc
);
482 if (!loc_fqsn_split(fqsn
, &ns_name
, &service
->name
)) {
485 async_answer_0(iid
, EINVAL
);
491 fibril_mutex_lock(&services_list_mutex
);
493 loc_namespace_t
*namespace = loc_namespace_create(ns_name
);
495 if (namespace == NULL
) {
496 fibril_mutex_unlock(&services_list_mutex
);
499 async_answer_0(iid
, ENOMEM
);
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
);
515 async_answer_0(iid
, EEXIST
);
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
)
548 fibril_mutex_lock(&services_list_mutex
);
549 svc
= loc_service_find_id(IPC_GET_ARG1(*icall
));
551 fibril_mutex_unlock(&services_list_mutex
);
552 async_answer_0(iid
, ENOENT
);
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
)
577 if (!async_data_read_receive(&callid
, &size
)) {
578 async_answer_0(callid
, EREFUSED
);
579 async_answer_0(iid
, EREFUSED
);
583 fibril_mutex_lock(&cdir
.mutex
);
585 cat
= category_get(&cdir
, IPC_GET_ARG1(*icall
));
587 fibril_mutex_unlock(&cdir
.mutex
);
588 async_answer_0(callid
, ENOENT
);
589 async_answer_0(iid
, ENOENT
);
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
);
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
)
617 if (!async_data_read_receive(&callid
, &size
)) {
618 async_answer_0(callid
, EREFUSED
);
619 async_answer_0(iid
, EREFUSED
);
623 fibril_mutex_lock(&services_list_mutex
);
625 svc
= loc_service_find_id(IPC_GET_ARG1(*icall
));
627 fibril_mutex_unlock(&services_list_mutex
);
628 async_answer_0(callid
, ENOENT
);
629 async_answer_0(iid
, ENOENT
);
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
);
640 act_size
= str_size(fqn
);
641 if (act_size
> size
) {
643 fibril_mutex_unlock(&services_list_mutex
);
644 async_answer_0(callid
, EOVERFLOW
);
645 async_answer_0(iid
, EOVERFLOW
);
649 errno_t retval
= async_data_read_finalize(callid
, fqn
,
650 min(size
, act_size
));
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
)
665 if (!async_data_read_receive(&callid
, &size
)) {
666 async_answer_0(callid
, EREFUSED
);
667 async_answer_0(iid
, EREFUSED
);
671 fibril_mutex_lock(&services_list_mutex
);
673 svc
= loc_service_find_id(IPC_GET_ARG1(*icall
));
675 fibril_mutex_unlock(&services_list_mutex
);
676 async_answer_0(callid
, ENOENT
);
677 async_answer_0(iid
, ENOENT
);
681 if (svc
->server
== NULL
) {
682 fibril_mutex_unlock(&services_list_mutex
);
683 async_answer_0(callid
, EINVAL
);
684 async_answer_0(iid
, EINVAL
);
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
);
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
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
);
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
740 static void loc_service_get_id(ipc_callid_t iid
, ipc_call_t
*icall
)
745 errno_t rc
= async_data_write_accept((void **) &fqsn
, true, 0,
746 LOC_NAME_MAXLEN
, 0, NULL
);
748 async_answer_0(iid
, rc
);
754 if (!loc_fqsn_split(fqsn
, &ns_name
, &name
)) {
756 async_answer_0(iid
, EINVAL
);
762 fibril_mutex_lock(&services_list_mutex
);
763 const loc_service_t
*svc
;
768 * Find service name in the list of known services.
770 svc
= loc_service_find_name(ns_name
, name
);
773 * Device was not found.
776 if (IPC_GET_ARG1(*icall
) & IPC_FLAG_BLOCKING
) {
777 /* Blocking lookup */
778 fibril_condvar_wait(&services_list_cv
,
779 &services_list_mutex
);
783 async_answer_0(iid
, ENOENT
);
786 fibril_mutex_unlock(&services_list_mutex
);
790 async_answer_1(iid
, EOK
, svc
->id
);
792 fibril_mutex_unlock(&services_list_mutex
);
797 /** Find ID for namespace identified by name.
799 * In answer will be send EOK and service ID in arg1 or a error
803 static void loc_namespace_get_id(ipc_callid_t iid
, ipc_call_t
*icall
)
807 /* Get service name */
808 errno_t rc
= async_data_write_accept((void **) &name
, true, 0,
809 LOC_NAME_MAXLEN
, 0, NULL
);
811 async_answer_0(iid
, rc
);
815 fibril_mutex_lock(&services_list_mutex
);
816 const loc_namespace_t
*namespace;
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
);
836 async_answer_0(iid
, ENOENT
);
838 fibril_mutex_unlock(&services_list_mutex
);
842 async_answer_1(iid
, EOK
, namespace->id
);
844 fibril_mutex_unlock(&services_list_mutex
);
848 /** Create callback connection.
850 * Create callback connection which will be used to send category change
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
);
865 async_sess_t
*sess
= async_callback_receive(EXCHANGE_SERIALIZE
);
868 async_answer_0(iid
, ENOMEM
);
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
)
906 /* Get service name */
907 errno_t rc
= async_data_write_accept((void **) &name
, true, 0,
908 LOC_NAME_MAXLEN
, 0, NULL
);
910 async_answer_0(iid
, rc
);
914 fibril_mutex_lock(&cdir
.mutex
);
916 cat
= category_find_by_name(&cdir
, name
);
918 /* Category not found */
919 async_answer_0(iid
, ENOENT
);
923 async_answer_1(iid
, EOK
, cat
->id
);
925 fibril_mutex_unlock(&cdir
.mutex
);
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
) {
937 loc_service_find_id(IPC_GET_ARG1(*icall
));
939 async_answer_1(iid
, EOK
, LOC_OBJECT_NONE
);
941 async_answer_1(iid
, EOK
, LOC_OBJECT_SERVICE
);
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
);
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
)
976 if (!async_data_read_receive(&callid
, &size
)) {
977 async_answer_0(callid
, EREFUSED
);
978 async_answer_0(iid
, EREFUSED
);
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
);
990 fibril_mutex_lock(&cdir
.mutex
);
992 rc
= categ_dir_get_categories(&cdir
, id_buf
, size
, &act_size
);
994 fibril_mutex_unlock(&cdir
.mutex
);
995 async_answer_0(callid
, rc
);
996 async_answer_0(iid
, rc
);
1000 fibril_mutex_unlock(&cdir
.mutex
);
1002 errno_t retval
= async_data_read_finalize(callid
, id_buf
, size
);
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
;
1012 if (!async_data_read_receive(&callid
, &size
)) {
1013 async_answer_0(callid
, EREFUSED
);
1014 async_answer_0(iid
, EREFUSED
);
1018 if ((size
% sizeof(loc_sdesc_t
)) != 0) {
1019 async_answer_0(callid
, EINVAL
);
1020 async_answer_0(iid
, EINVAL
);
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
);
1034 loc_sdesc_t
*desc
= (loc_sdesc_t
*) malloc(size
);
1036 fibril_mutex_unlock(&services_list_mutex
);
1037 async_answer_0(callid
, ENOMEM
);
1038 async_answer_0(iid
, ENOMEM
);
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
);
1049 errno_t retval
= async_data_read_finalize(callid
, desc
, size
);
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
1062 ipc_callid_t callid
;
1064 if (!async_data_read_receive(&callid
, &size
)) {
1065 async_answer_0(callid
, EREFUSED
);
1066 async_answer_0(iid
, EREFUSED
);
1070 if ((size
% sizeof(loc_sdesc_t
)) != 0) {
1071 async_answer_0(callid
, EINVAL
);
1072 async_answer_0(iid
, EINVAL
);
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
);
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
);
1095 loc_sdesc_t
*desc
= (loc_sdesc_t
*) malloc(size
);
1097 fibril_mutex_unlock(&services_list_mutex
);
1098 async_answer_0(callid
, ENOMEM
);
1099 async_answer_0(iid
, EREFUSED
);
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
);
1112 errno_t retval
= async_data_read_finalize(callid
, desc
, size
);
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
;
1127 if (!async_data_read_receive(&callid
, &size
)) {
1128 async_answer_0(callid
, EREFUSED
);
1129 async_answer_0(iid
, EREFUSED
);
1133 fibril_mutex_lock(&cdir
.mutex
);
1135 category_t
*cat
= category_get(&cdir
, IPC_GET_ARG1(*icall
));
1137 fibril_mutex_unlock(&cdir
.mutex
);
1138 async_answer_0(callid
, ENOENT
);
1139 async_answer_0(iid
, ENOENT
);
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
);
1151 fibril_mutex_lock(&cat
->mutex
);
1153 rc
= category_get_services(cat
, id_buf
, size
, &act_size
);
1155 fibril_mutex_unlock(&cat
->mutex
);
1156 fibril_mutex_unlock(&cdir
.mutex
);
1157 async_answer_0(callid
, rc
);
1158 async_answer_0(iid
, rc
);
1162 fibril_mutex_unlock(&cat
->mutex
);
1163 fibril_mutex_unlock(&cdir
.mutex
);
1165 errno_t retval
= async_data_read_finalize(callid
, id_buf
, size
);
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
);
1179 for (i
= 0; i
< NULL_SERVICES
; i
++) {
1180 if (null_services
[i
] == NULL
) {
1187 fibril_mutex_unlock(&null_services_mutex
);
1188 async_answer_0(iid
, ENOMEM
);
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
);
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
);
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
);
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
);
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
);
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
)
1279 service_id_t svc_id
;
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
);
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)
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
);
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();
1403 ipc_callid_t callid
= async_get_call(&call
);
1405 if (!IPC_GET_IMETHOD(call
))
1408 switch (IPC_GET_IMETHOD(call
)) {
1409 case LOC_SERVER_UNREGISTER
:
1411 async_answer_0(callid
, ENOENT
);
1413 async_answer_0(callid
, EOK
);
1415 case LOC_SERVICE_ADD_TO_CAT
:
1416 /* Add service to category */
1417 loc_service_add_to_cat(callid
, &call
);
1419 case LOC_SERVICE_REGISTER
:
1420 /* Register one service */
1421 loc_service_register(callid
, &call
, server
);
1423 case LOC_SERVICE_UNREGISTER
:
1424 /* Remove one service */
1425 loc_service_unregister(callid
, &call
, server
);
1427 case LOC_SERVICE_GET_ID
:
1428 loc_service_get_id(callid
, &call
);
1430 case LOC_NAMESPACE_GET_ID
:
1431 loc_namespace_get_id(callid
, &call
);
1434 async_answer_0(callid
, ENOENT
);
1438 if (server
!= NULL
) {
1440 * Unregister the server and all its services.
1442 loc_server_unregister(server
);
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
);
1457 ipc_callid_t callid
= async_get_call(&call
);
1459 if (!IPC_GET_IMETHOD(call
))
1462 switch (IPC_GET_IMETHOD(call
)) {
1463 case LOC_SERVICE_GET_ID
:
1464 loc_service_get_id(callid
, &call
);
1466 case LOC_SERVICE_GET_NAME
:
1467 loc_service_get_name(callid
, &call
);
1469 case LOC_SERVICE_GET_SERVER_NAME
:
1470 loc_service_get_server_name(callid
, &call
);
1472 case LOC_NAMESPACE_GET_ID
:
1473 loc_namespace_get_id(callid
, &call
);
1475 case LOC_CALLBACK_CREATE
:
1476 loc_callback_create(callid
, &call
);
1478 case LOC_CATEGORY_GET_ID
:
1479 loc_category_get_id(callid
, &call
);
1481 case LOC_CATEGORY_GET_NAME
:
1482 loc_category_get_name(callid
, &call
);
1484 case LOC_CATEGORY_GET_SVCS
:
1485 loc_category_get_svcs(callid
, &call
);
1488 loc_id_probe(callid
, &call
);
1490 case LOC_NULL_CREATE
:
1491 loc_null_create(callid
, &call
);
1493 case LOC_NULL_DESTROY
:
1494 loc_null_destroy(callid
, &call
);
1496 case LOC_GET_NAMESPACE_COUNT
:
1497 loc_get_namespace_count(callid
, &call
);
1499 case LOC_GET_SERVICE_COUNT
:
1500 loc_get_service_count(callid
, &call
);
1502 case LOC_GET_CATEGORIES
:
1503 loc_get_categories(callid
, &call
);
1505 case LOC_GET_NAMESPACES
:
1506 loc_get_namespaces(callid
, &call
);
1508 case LOC_GET_SERVICES
:
1509 loc_get_services(callid
, &call
);
1512 async_answer_0(callid
, ENOENT
);
1520 int main(int argc
, char *argv
[])
1522 printf("%s: HelenOS Location Service\n", NAME
);
1525 printf("%s: Error while initializing service\n", NAME
);
1530 errno_t rc
= async_create_port(INTERFACE_LOC_SUPPLIER
,
1531 loc_connection_supplier
, NULL
, &port
);
1533 printf("%s: Error while creating supplier port: %s\n", NAME
, str_error(rc
));
1537 rc
= async_create_port(INTERFACE_LOC_CONSUMER
,
1538 loc_connection_consumer
, NULL
, &port
);
1540 printf("%s: Error while creating consumer port: %s\n", NAME
, str_error(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
);
1550 printf("%s: Error while registering service: %s\n", NAME
, str_error(rc
));
1554 printf("%s: Accepting connections\n", NAME
);