source: mainline/uspace/srv/locsrv/locsrv.c@ 47f7390f

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

Simplify use of list_foreach.

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