source: mainline/uspace/srv/locsrv/locsrv.c@ ba0eac5

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

avoid use after free of memb
(detected by Coverity, CID 10451)

  • 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 category_t *cat = memb->cat;
323
324 fibril_mutex_lock(&cat->mutex);
325 category_remove_service(memb);
326 fibril_mutex_unlock(&cat->mutex);
327 }
328
329 free(service->name);
330 free(service);
331}
332
333/**
334 * Read info about new server and add it into linked list of registered
335 * servers.
336 */
337static loc_server_t *loc_server_register(void)
338{
339 ipc_call_t icall;
340 ipc_callid_t iid = async_get_call(&icall);
341
342 if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
343 async_answer_0(iid, EREFUSED);
344 return NULL;
345 }
346
347 loc_server_t *server =
348 (loc_server_t *) malloc(sizeof(loc_server_t));
349 if (server == NULL) {
350 async_answer_0(iid, ENOMEM);
351 return NULL;
352 }
353
354 /*
355 * Get server name
356 */
357 int rc = async_data_write_accept((void **) &server->name, true, 0,
358 LOC_NAME_MAXLEN, 0, NULL);
359 if (rc != EOK) {
360 free(server);
361 async_answer_0(iid, rc);
362 return NULL;
363 }
364
365 /*
366 * Create connection to the server
367 */
368 server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
369 if (!server->sess) {
370 free(server->name);
371 free(server);
372 async_answer_0(iid, ENOTSUP);
373 return NULL;
374 }
375
376 /*
377 * Initialize mutex for list of services
378 * supplied by this server
379 */
380 fibril_mutex_initialize(&server->services_mutex);
381
382 /*
383 * Initialize list of supplied services
384 */
385 list_initialize(&server->services);
386 link_initialize(&server->servers);
387
388 fibril_mutex_lock(&servers_list_mutex);
389
390 /* TODO:
391 * Check that no server with name equal to
392 * server->name is registered
393 */
394
395 /*
396 * Insert new server into list of registered servers
397 */
398 list_append(&(server->servers), &servers_list);
399 fibril_mutex_unlock(&servers_list_mutex);
400
401 async_answer_0(iid, EOK);
402
403 return server;
404}
405
406/**
407 * Unregister server, unregister all its services and free server
408 * structure.
409 *
410 */
411static int loc_server_unregister(loc_server_t *server)
412{
413 if (server == NULL)
414 return EEXISTS;
415
416 fibril_mutex_lock(&servers_list_mutex);
417
418 if (server->sess)
419 async_hangup(server->sess);
420
421 /* Remove it from list of servers */
422 list_remove(&(server->servers));
423
424 /* Unregister all its services */
425 fibril_mutex_lock(&services_list_mutex);
426 fibril_mutex_lock(&server->services_mutex);
427 fibril_mutex_lock(&cdir.mutex);
428
429 while (!list_empty(&server->services)) {
430 loc_service_t *service = list_get_instance(
431 list_first(&server->services), loc_service_t,
432 server_services);
433 loc_service_unregister_core(service);
434 }
435
436 fibril_mutex_unlock(&cdir.mutex);
437 fibril_mutex_unlock(&server->services_mutex);
438 fibril_mutex_unlock(&services_list_mutex);
439 fibril_mutex_unlock(&servers_list_mutex);
440
441 /* Free name and server */
442 if (server->name != NULL)
443 free(server->name);
444
445 free(server);
446
447 return EOK;
448}
449
450/** Register service
451 *
452 */
453static void loc_service_register(ipc_callid_t iid, ipc_call_t *icall,
454 loc_server_t *server)
455{
456 if (server == NULL) {
457 async_answer_0(iid, EREFUSED);
458 return;
459 }
460
461 /* Create new service entry */
462 loc_service_t *service =
463 (loc_service_t *) malloc(sizeof(loc_service_t));
464 if (service == NULL) {
465 async_answer_0(iid, ENOMEM);
466 return;
467 }
468
469 /* Set the interface, if any. */
470 service->forward_interface = IPC_GET_ARG1(*icall);
471
472 /* Get fqsn */
473 char *fqsn;
474 int rc = async_data_write_accept((void **) &fqsn, true, 0,
475 LOC_NAME_MAXLEN, 0, NULL);
476 if (rc != EOK) {
477 free(service);
478 async_answer_0(iid, rc);
479 return;
480 }
481
482 char *ns_name;
483 if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
484 free(fqsn);
485 free(service);
486 async_answer_0(iid, EINVAL);
487 return;
488 }
489
490 free(fqsn);
491
492 fibril_mutex_lock(&services_list_mutex);
493
494 loc_namespace_t *namespace = loc_namespace_create(ns_name);
495 free(ns_name);
496 if (namespace == NULL) {
497 fibril_mutex_unlock(&services_list_mutex);
498 free(service->name);
499 free(service);
500 async_answer_0(iid, ENOMEM);
501 return;
502 }
503
504 link_initialize(&service->services);
505 link_initialize(&service->server_services);
506 list_initialize(&service->cat_memb);
507
508 /* Check that service is not already registered */
509 if (loc_service_find_name(namespace->name, service->name) != NULL) {
510 printf("%s: Service '%s/%s' already registered\n", NAME,
511 namespace->name, service->name);
512 loc_namespace_destroy(namespace);
513 fibril_mutex_unlock(&services_list_mutex);
514 free(service->name);
515 free(service);
516 async_answer_0(iid, EEXISTS);
517 return;
518 }
519
520 /* Get unique service ID */
521 service->id = loc_create_id();
522
523 loc_namespace_addref(namespace, service);
524 service->server = server;
525
526 /* Insert service into list of all services */
527 list_append(&service->services, &services_list);
528
529 /* Insert service into list of services supplied by one server */
530 fibril_mutex_lock(&service->server->services_mutex);
531
532 list_append(&service->server_services, &service->server->services);
533
534 fibril_mutex_unlock(&service->server->services_mutex);
535 fibril_condvar_broadcast(&services_list_cv);
536 fibril_mutex_unlock(&services_list_mutex);
537
538 async_answer_1(iid, EOK, service->id);
539}
540
541/**
542 *
543 */
544static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall,
545 loc_server_t *server)
546{
547 loc_service_t *svc;
548
549 fibril_mutex_lock(&services_list_mutex);
550 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
551 if (svc == NULL) {
552 fibril_mutex_unlock(&services_list_mutex);
553 async_answer_0(iid, ENOENT);
554 return;
555 }
556
557 fibril_mutex_lock(&cdir.mutex);
558 loc_service_unregister_core(svc);
559 fibril_mutex_unlock(&cdir.mutex);
560 fibril_mutex_unlock(&services_list_mutex);
561 async_answer_0(iid, EOK);
562}
563
564static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
565{
566 ipc_callid_t callid;
567 size_t size;
568 size_t act_size;
569 category_t *cat;
570
571 if (!async_data_read_receive(&callid, &size)) {
572 async_answer_0(callid, EREFUSED);
573 async_answer_0(iid, EREFUSED);
574 return;
575 }
576
577 fibril_mutex_lock(&cdir.mutex);
578
579 cat = category_get(&cdir, IPC_GET_ARG1(*icall));
580 if (cat == NULL) {
581 fibril_mutex_unlock(&cdir.mutex);
582 async_answer_0(callid, ENOENT);
583 async_answer_0(iid, ENOENT);
584 return;
585 }
586
587 act_size = str_size(cat->name);
588 if (act_size > size) {
589 fibril_mutex_unlock(&cdir.mutex);
590 async_answer_0(callid, EOVERFLOW);
591 async_answer_0(iid, EOVERFLOW);
592 return;
593 }
594
595 sysarg_t retval = async_data_read_finalize(callid, cat->name,
596 min(size, act_size));
597
598 fibril_mutex_unlock(&cdir.mutex);
599
600 async_answer_0(iid, retval);
601}
602
603static void loc_service_get_name(ipc_callid_t iid, ipc_call_t *icall)
604{
605 ipc_callid_t callid;
606 size_t size;
607 size_t act_size;
608 loc_service_t *svc;
609 char *fqn;
610
611 if (!async_data_read_receive(&callid, &size)) {
612 async_answer_0(callid, EREFUSED);
613 async_answer_0(iid, EREFUSED);
614 return;
615 }
616
617 fibril_mutex_lock(&services_list_mutex);
618
619 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
620 if (svc == NULL) {
621 fibril_mutex_unlock(&services_list_mutex);
622 async_answer_0(callid, ENOENT);
623 async_answer_0(iid, ENOENT);
624 return;
625 }
626
627 if (asprintf(&fqn, "%s/%s", svc->namespace->name, svc->name) < 0) {
628 fibril_mutex_unlock(&services_list_mutex);
629 async_answer_0(callid, ENOMEM);
630 async_answer_0(iid, ENOMEM);
631 return;
632 }
633
634 act_size = str_size(fqn);
635 if (act_size > size) {
636 free(fqn);
637 fibril_mutex_unlock(&services_list_mutex);
638 async_answer_0(callid, EOVERFLOW);
639 async_answer_0(iid, EOVERFLOW);
640 return;
641 }
642
643 sysarg_t retval = async_data_read_finalize(callid, fqn,
644 min(size, act_size));
645 free(fqn);
646
647 fibril_mutex_unlock(&services_list_mutex);
648
649 async_answer_0(iid, retval);
650}
651
652static void loc_service_get_server_name(ipc_callid_t iid, ipc_call_t *icall)
653{
654 ipc_callid_t callid;
655 size_t size;
656 size_t act_size;
657 loc_service_t *svc;
658
659 if (!async_data_read_receive(&callid, &size)) {
660 async_answer_0(callid, EREFUSED);
661 async_answer_0(iid, EREFUSED);
662 return;
663 }
664
665 fibril_mutex_lock(&services_list_mutex);
666
667 svc = loc_service_find_id(IPC_GET_ARG1(*icall));
668 if (svc == NULL) {
669 fibril_mutex_unlock(&services_list_mutex);
670 async_answer_0(callid, ENOENT);
671 async_answer_0(iid, ENOENT);
672 return;
673 }
674
675 if (svc->server == NULL) {
676 fibril_mutex_unlock(&services_list_mutex);
677 async_answer_0(callid, EINVAL);
678 async_answer_0(iid, EINVAL);
679 return;
680 }
681
682 act_size = str_size(svc->server->name);
683 if (act_size > size) {
684 fibril_mutex_unlock(&services_list_mutex);
685 async_answer_0(callid, EOVERFLOW);
686 async_answer_0(iid, EOVERFLOW);
687 return;
688 }
689
690 sysarg_t retval = async_data_read_finalize(callid, svc->server->name,
691 min(size, act_size));
692
693 fibril_mutex_unlock(&services_list_mutex);
694
695 async_answer_0(iid, retval);
696}
697
698/** Connect client to the service.
699 *
700 * Find server supplying requested service and forward
701 * the message to it.
702 *
703 */
704static void loc_forward(ipc_callid_t callid, ipc_call_t *call)
705{
706 fibril_mutex_lock(&services_list_mutex);
707
708 /*
709 * Get ID from request
710 */
711 service_id_t id = IPC_GET_ARG2(*call);
712 loc_service_t *svc = loc_service_find_id(id);
713
714 if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
715 fibril_mutex_unlock(&services_list_mutex);
716 async_answer_0(callid, ENOENT);
717 return;
718 }
719
720 async_exch_t *exch = async_exchange_begin(svc->server->sess);
721
722 if (svc->forward_interface == 0)
723 async_forward_fast(callid, exch, svc->id, 0, 0, IPC_FF_NONE);
724 else
725 async_forward_fast(callid, exch, svc->forward_interface,
726 svc->id, 0, IPC_FF_NONE);
727
728 async_exchange_end(exch);
729
730 fibril_mutex_unlock(&services_list_mutex);
731}
732
733/** Find ID for service identified by name.
734 *
735 * In answer will be send EOK and service ID in arg1 or a error
736 * code from errno.h.
737 *
738 */
739static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
740{
741 char *fqsn;
742
743 /* Get fqsn */
744 int rc = async_data_write_accept((void **) &fqsn, true, 0,
745 LOC_NAME_MAXLEN, 0, NULL);
746 if (rc != EOK) {
747 async_answer_0(iid, rc);
748 return;
749 }
750
751 char *ns_name;
752 char *name;
753 if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
754 free(fqsn);
755 async_answer_0(iid, EINVAL);
756 return;
757 }
758
759 free(fqsn);
760
761 fibril_mutex_lock(&services_list_mutex);
762 const loc_service_t *svc;
763
764recheck:
765
766 /*
767 * Find service name in the list of known services.
768 */
769 svc = loc_service_find_name(ns_name, name);
770
771 /*
772 * Device was not found.
773 */
774 if (svc == NULL) {
775 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
776 /* Blocking lookup */
777 fibril_condvar_wait(&services_list_cv,
778 &services_list_mutex);
779 goto recheck;
780 }
781
782 async_answer_0(iid, ENOENT);
783 free(ns_name);
784 free(name);
785 fibril_mutex_unlock(&services_list_mutex);
786 return;
787 }
788
789 async_answer_1(iid, EOK, svc->id);
790
791 fibril_mutex_unlock(&services_list_mutex);
792 free(ns_name);
793 free(name);
794}
795
796/** Find ID for namespace identified by name.
797 *
798 * In answer will be send EOK and service ID in arg1 or a error
799 * code from errno.h.
800 *
801 */
802static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
803{
804 char *name;
805
806 /* Get service name */
807 int rc = async_data_write_accept((void **) &name, true, 0,
808 LOC_NAME_MAXLEN, 0, NULL);
809 if (rc != EOK) {
810 async_answer_0(iid, rc);
811 return;
812 }
813
814 fibril_mutex_lock(&services_list_mutex);
815 const loc_namespace_t *namespace;
816
817recheck:
818
819 /*
820 * Find namespace name in the list of known namespaces.
821 */
822 namespace = loc_namespace_find_name(name);
823
824 /*
825 * Namespace was not found.
826 */
827 if (namespace == NULL) {
828 if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
829 /* Blocking lookup */
830 fibril_condvar_wait(&services_list_cv,
831 &services_list_mutex);
832 goto recheck;
833 }
834
835 async_answer_0(iid, ENOENT);
836 free(name);
837 fibril_mutex_unlock(&services_list_mutex);
838 return;
839 }
840
841 async_answer_1(iid, EOK, namespace->id);
842
843 fibril_mutex_unlock(&services_list_mutex);
844 free(name);
845}
846
847/** Create callback connection.
848 *
849 * Create callback connection which will be used to send category change
850 * events.
851 *
852 * On success, answer will contain EOK int retval.
853 * On failure, error code will be sent in retval.
854 *
855 */
856static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
857{
858 cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
859 if (cb_sess == NULL) {
860 async_answer_0(iid, ENOMEM);
861 return;
862 }
863
864 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
865 if (sess == NULL) {
866 free(cb_sess);
867 async_answer_0(iid, ENOMEM);
868 return;
869 }
870
871 cb_sess->sess = sess;
872 link_initialize(&cb_sess->cb_sess_list);
873
874 fibril_mutex_lock(&callback_sess_mutex);
875 list_append(&cb_sess->cb_sess_list, &callback_sess_list);
876 fibril_mutex_unlock(&callback_sess_mutex);
877
878 async_answer_0(iid, EOK);
879}
880
881void loc_category_change_event(void)
882{
883 fibril_mutex_lock(&callback_sess_mutex);
884
885 list_foreach(callback_sess_list, cb_sess_list, cb_sess_t, cb_sess) {
886 async_exch_t *exch = async_exchange_begin(cb_sess->sess);
887 async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
888 async_exchange_end(exch);
889 }
890
891 fibril_mutex_unlock(&callback_sess_mutex);
892}
893
894/** Find ID for category specified by name.
895 *
896 * On success, answer will contain EOK int retval and service ID in arg1.
897 * On failure, error code will be sent in retval.
898 *
899 */
900static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
901{
902 char *name;
903 category_t *cat;
904
905 /* Get service name */
906 int rc = async_data_write_accept((void **) &name, true, 0,
907 LOC_NAME_MAXLEN, 0, NULL);
908 if (rc != EOK) {
909 async_answer_0(iid, rc);
910 return;
911 }
912
913 fibril_mutex_lock(&cdir.mutex);
914
915 cat = category_find_by_name(&cdir, name);
916 if (cat == NULL) {
917 /* Category not found */
918 async_answer_0(iid, ENOENT);
919 goto cleanup;
920 }
921
922 async_answer_1(iid, EOK, cat->id);
923cleanup:
924 fibril_mutex_unlock(&cdir.mutex);
925 free(name);
926}
927
928static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
929{
930 fibril_mutex_lock(&services_list_mutex);
931
932 loc_namespace_t *namespace =
933 loc_namespace_find_id(IPC_GET_ARG1(*icall));
934 if (namespace == NULL) {
935 loc_service_t *svc =
936 loc_service_find_id(IPC_GET_ARG1(*icall));
937 if (svc == NULL)
938 async_answer_1(iid, EOK, LOC_OBJECT_NONE);
939 else
940 async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
941 } else
942 async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
943
944 fibril_mutex_unlock(&services_list_mutex);
945}
946
947static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
948{
949 fibril_mutex_lock(&services_list_mutex);
950 async_answer_1(iid, EOK, list_count(&namespaces_list));
951 fibril_mutex_unlock(&services_list_mutex);
952}
953
954static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
955{
956 fibril_mutex_lock(&services_list_mutex);
957
958 loc_namespace_t *namespace =
959 loc_namespace_find_id(IPC_GET_ARG1(*icall));
960 if (namespace == NULL)
961 async_answer_0(iid, EEXISTS);
962 else
963 async_answer_1(iid, EOK, namespace->refcnt);
964
965 fibril_mutex_unlock(&services_list_mutex);
966}
967
968static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
969{
970 ipc_callid_t callid;
971 size_t size;
972 size_t act_size;
973 int rc;
974
975 if (!async_data_read_receive(&callid, &size)) {
976 async_answer_0(callid, EREFUSED);
977 async_answer_0(iid, EREFUSED);
978 return;
979 }
980
981 category_id_t *id_buf = (category_id_t *) malloc(size);
982 if (id_buf == NULL) {
983 fibril_mutex_unlock(&cdir.mutex);
984 async_answer_0(callid, ENOMEM);
985 async_answer_0(iid, ENOMEM);
986 return;
987 }
988
989 fibril_mutex_lock(&cdir.mutex);
990
991 rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
992 if (rc != EOK) {
993 fibril_mutex_unlock(&cdir.mutex);
994 async_answer_0(callid, rc);
995 async_answer_0(iid, rc);
996 return;
997 }
998
999 fibril_mutex_unlock(&cdir.mutex);
1000
1001 sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
1002 free(id_buf);
1003
1004 async_answer_1(iid, retval, act_size);
1005}
1006
1007static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
1008{
1009 ipc_callid_t callid;
1010 size_t size;
1011 if (!async_data_read_receive(&callid, &size)) {
1012 async_answer_0(callid, EREFUSED);
1013 async_answer_0(iid, EREFUSED);
1014 return;
1015 }
1016
1017 if ((size % sizeof(loc_sdesc_t)) != 0) {
1018 async_answer_0(callid, EINVAL);
1019 async_answer_0(iid, EINVAL);
1020 return;
1021 }
1022
1023 fibril_mutex_lock(&services_list_mutex);
1024
1025 size_t count = size / sizeof(loc_sdesc_t);
1026 if (count != list_count(&namespaces_list)) {
1027 fibril_mutex_unlock(&services_list_mutex);
1028 async_answer_0(callid, EOVERFLOW);
1029 async_answer_0(iid, EOVERFLOW);
1030 return;
1031 }
1032
1033 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1034 if (desc == NULL) {
1035 fibril_mutex_unlock(&services_list_mutex);
1036 async_answer_0(callid, ENOMEM);
1037 async_answer_0(iid, ENOMEM);
1038 return;
1039 }
1040
1041 size_t pos = 0;
1042 list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
1043 desc[pos].id = namespace->id;
1044 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
1045 pos++;
1046 }
1047
1048 sysarg_t retval = async_data_read_finalize(callid, desc, size);
1049
1050 free(desc);
1051 fibril_mutex_unlock(&services_list_mutex);
1052
1053 async_answer_0(iid, retval);
1054}
1055
1056static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
1057{
1058 /* FIXME: Use faster algorithm which can make better use
1059 of namespaces */
1060
1061 ipc_callid_t callid;
1062 size_t size;
1063 if (!async_data_read_receive(&callid, &size)) {
1064 async_answer_0(callid, EREFUSED);
1065 async_answer_0(iid, EREFUSED);
1066 return;
1067 }
1068
1069 if ((size % sizeof(loc_sdesc_t)) != 0) {
1070 async_answer_0(callid, EINVAL);
1071 async_answer_0(iid, EINVAL);
1072 return;
1073 }
1074
1075 fibril_mutex_lock(&services_list_mutex);
1076
1077 loc_namespace_t *namespace =
1078 loc_namespace_find_id(IPC_GET_ARG1(*icall));
1079 if (namespace == NULL) {
1080 fibril_mutex_unlock(&services_list_mutex);
1081 async_answer_0(callid, ENOENT);
1082 async_answer_0(iid, ENOENT);
1083 return;
1084 }
1085
1086 size_t count = size / sizeof(loc_sdesc_t);
1087 if (count != namespace->refcnt) {
1088 fibril_mutex_unlock(&services_list_mutex);
1089 async_answer_0(callid, EOVERFLOW);
1090 async_answer_0(iid, EOVERFLOW);
1091 return;
1092 }
1093
1094 loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
1095 if (desc == NULL) {
1096 fibril_mutex_unlock(&services_list_mutex);
1097 async_answer_0(callid, ENOMEM);
1098 async_answer_0(iid, EREFUSED);
1099 return;
1100 }
1101
1102 size_t pos = 0;
1103 list_foreach(services_list, services, loc_service_t, service) {
1104 if (service->namespace == namespace) {
1105 desc[pos].id = service->id;
1106 str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
1107 pos++;
1108 }
1109 }
1110
1111 sysarg_t retval = async_data_read_finalize(callid, desc, size);
1112
1113 free(desc);
1114 fibril_mutex_unlock(&services_list_mutex);
1115
1116 async_answer_0(iid, retval);
1117}
1118
1119static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
1120{
1121 ipc_callid_t callid;
1122 size_t size;
1123 size_t act_size;
1124 int rc;
1125
1126 if (!async_data_read_receive(&callid, &size)) {
1127 async_answer_0(callid, EREFUSED);
1128 async_answer_0(iid, EREFUSED);
1129 return;
1130 }
1131
1132 fibril_mutex_lock(&cdir.mutex);
1133
1134 category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
1135 if (cat == NULL) {
1136 fibril_mutex_unlock(&cdir.mutex);
1137 async_answer_0(callid, ENOENT);
1138 async_answer_0(iid, ENOENT);
1139 return;
1140 }
1141
1142 category_id_t *id_buf = (category_id_t *) malloc(size);
1143 if (id_buf == NULL) {
1144 fibril_mutex_unlock(&cdir.mutex);
1145 async_answer_0(callid, ENOMEM);
1146 async_answer_0(iid, ENOMEM);
1147 return;
1148 }
1149
1150 fibril_mutex_lock(&cat->mutex);
1151
1152 rc = category_get_services(cat, id_buf, size, &act_size);
1153 if (rc != EOK) {
1154 fibril_mutex_unlock(&cat->mutex);
1155 fibril_mutex_unlock(&cdir.mutex);
1156 async_answer_0(callid, rc);
1157 async_answer_0(iid, rc);
1158 return;
1159 }
1160
1161 fibril_mutex_unlock(&cat->mutex);
1162 fibril_mutex_unlock(&cdir.mutex);
1163
1164 sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
1165 free(id_buf);
1166
1167 async_answer_1(iid, retval, act_size);
1168}
1169
1170
1171static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
1172{
1173 fibril_mutex_lock(&null_services_mutex);
1174
1175 unsigned int i;
1176 bool fnd = false;
1177
1178 for (i = 0; i < NULL_SERVICES; i++) {
1179 if (null_services[i] == NULL) {
1180 fnd = true;
1181 break;
1182 }
1183 }
1184
1185 if (!fnd) {
1186 fibril_mutex_unlock(&null_services_mutex);
1187 async_answer_0(iid, ENOMEM);
1188 return;
1189 }
1190
1191 char null[LOC_NAME_MAXLEN];
1192 snprintf(null, LOC_NAME_MAXLEN, "%u", i);
1193
1194 char *dev_name = str_dup(null);
1195 if (dev_name == NULL) {
1196 fibril_mutex_unlock(&null_services_mutex);
1197 async_answer_0(iid, ENOMEM);
1198 return;
1199 }
1200
1201 loc_service_t *service =
1202 (loc_service_t *) malloc(sizeof(loc_service_t));
1203 if (service == NULL) {
1204 fibril_mutex_unlock(&null_services_mutex);
1205 async_answer_0(iid, ENOMEM);
1206 return;
1207 }
1208
1209 fibril_mutex_lock(&services_list_mutex);
1210
1211 loc_namespace_t *namespace = loc_namespace_create("null");
1212 if (namespace == NULL) {
1213 fibril_mutex_lock(&services_list_mutex);
1214 fibril_mutex_unlock(&null_services_mutex);
1215 async_answer_0(iid, ENOMEM);
1216 return;
1217 }
1218
1219 link_initialize(&service->services);
1220 link_initialize(&service->server_services);
1221 list_initialize(&service->cat_memb);
1222
1223 /* Get unique service ID */
1224 service->id = loc_create_id();
1225 service->server = NULL;
1226
1227 loc_namespace_addref(namespace, service);
1228 service->name = dev_name;
1229
1230 /*
1231 * Insert service into list of all services and into null services array.
1232 * Insert service into a dummy list of null server's services so that it
1233 * can be safely removed later.
1234 */
1235 list_append(&service->services, &services_list);
1236 list_append(&service->server_services, &dummy_null_services);
1237 null_services[i] = service;
1238
1239 fibril_mutex_unlock(&services_list_mutex);
1240 fibril_mutex_unlock(&null_services_mutex);
1241
1242 async_answer_1(iid, EOK, (sysarg_t) i);
1243}
1244
1245static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
1246{
1247 sysarg_t i = IPC_GET_ARG1(*icall);
1248 if (i >= NULL_SERVICES) {
1249 async_answer_0(iid, ELIMIT);
1250 return;
1251 }
1252
1253 fibril_mutex_lock(&null_services_mutex);
1254
1255 if (null_services[i] == NULL) {
1256 fibril_mutex_unlock(&null_services_mutex);
1257 async_answer_0(iid, ENOENT);
1258 return;
1259 }
1260
1261 fibril_mutex_lock(&services_list_mutex);
1262 fibril_mutex_lock(&cdir.mutex);
1263 loc_service_unregister_core(null_services[i]);
1264 fibril_mutex_unlock(&cdir.mutex);
1265 fibril_mutex_unlock(&services_list_mutex);
1266
1267 null_services[i] = NULL;
1268
1269 fibril_mutex_unlock(&null_services_mutex);
1270 async_answer_0(iid, EOK);
1271}
1272
1273static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
1274{
1275 category_t *cat;
1276 loc_service_t *svc;
1277 catid_t cat_id;
1278 service_id_t svc_id;
1279 sysarg_t retval;
1280
1281 svc_id = IPC_GET_ARG1(*icall);
1282 cat_id = IPC_GET_ARG2(*icall);
1283
1284 fibril_mutex_lock(&services_list_mutex);
1285 fibril_mutex_lock(&cdir.mutex);
1286
1287 cat = category_get(&cdir, cat_id);
1288 svc = loc_service_find_id(svc_id);
1289
1290 if (cat == NULL || svc == NULL) {
1291 fibril_mutex_unlock(&cdir.mutex);
1292 fibril_mutex_unlock(&services_list_mutex);
1293 async_answer_0(iid, ENOENT);
1294 return;
1295 }
1296
1297 fibril_mutex_lock(&cat->mutex);
1298 retval = category_add_service(cat, svc);
1299
1300 fibril_mutex_unlock(&cat->mutex);
1301 fibril_mutex_unlock(&cdir.mutex);
1302 fibril_mutex_unlock(&services_list_mutex);
1303
1304 async_answer_0(iid, retval);
1305
1306 loc_category_change_event();
1307}
1308
1309
1310/** Initialize location service.
1311 *
1312 *
1313 */
1314static bool loc_init(void)
1315{
1316 unsigned int i;
1317 category_t *cat;
1318
1319 for (i = 0; i < NULL_SERVICES; i++)
1320 null_services[i] = NULL;
1321
1322 categ_dir_init(&cdir);
1323
1324 cat = category_new("bd");
1325 categ_dir_add_cat(&cdir, cat);
1326
1327 cat = category_new("iplink");
1328 categ_dir_add_cat(&cdir, cat);
1329
1330 cat = category_new("keyboard");
1331 categ_dir_add_cat(&cdir, cat);
1332
1333 cat = category_new("mouse");
1334 categ_dir_add_cat(&cdir, cat);
1335
1336 cat = category_new("led");
1337 categ_dir_add_cat(&cdir, cat);
1338
1339 cat = category_new("serial");
1340 categ_dir_add_cat(&cdir, cat);
1341
1342 cat = category_new("clock");
1343 categ_dir_add_cat(&cdir, cat);
1344
1345 cat = category_new("test3");
1346 categ_dir_add_cat(&cdir, cat);
1347
1348 cat = category_new("usbhc");
1349 categ_dir_add_cat(&cdir, cat);
1350
1351 cat = category_new("virt-null");
1352 categ_dir_add_cat(&cdir, cat);
1353
1354 cat = category_new("virtual");
1355 categ_dir_add_cat(&cdir, cat);
1356
1357 cat = category_new("nic");
1358 categ_dir_add_cat(&cdir, cat);
1359
1360 cat = category_new("visualizer");
1361 categ_dir_add_cat(&cdir, cat);
1362
1363 cat = category_new("renderer");
1364 categ_dir_add_cat(&cdir, cat);
1365
1366 cat = category_new("audio-pcm");
1367 categ_dir_add_cat(&cdir, cat);
1368
1369 return true;
1370}
1371
1372/** Handle connection on supplier port.
1373 *
1374 */
1375static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall)
1376{
1377 /* Accept connection */
1378 async_answer_0(iid, EOK);
1379
1380 loc_server_t *server = loc_server_register();
1381 if (server == NULL)
1382 return;
1383
1384 while (true) {
1385 ipc_call_t call;
1386 ipc_callid_t callid = async_get_call(&call);
1387
1388 if (!IPC_GET_IMETHOD(call))
1389 break;
1390
1391 switch (IPC_GET_IMETHOD(call)) {
1392 case LOC_SERVER_UNREGISTER:
1393 if (server == NULL)
1394 async_answer_0(callid, ENOENT);
1395 else
1396 async_answer_0(callid, EOK);
1397 break;
1398 case LOC_SERVICE_ADD_TO_CAT:
1399 /* Add service to category */
1400 loc_service_add_to_cat(callid, &call);
1401 break;
1402 case LOC_SERVICE_REGISTER:
1403 /* Register one service */
1404 loc_service_register(callid, &call, server);
1405 break;
1406 case LOC_SERVICE_UNREGISTER:
1407 /* Remove one service */
1408 loc_service_unregister(callid, &call, server);
1409 break;
1410 case LOC_SERVICE_GET_ID:
1411 loc_service_get_id(callid, &call);
1412 break;
1413 case LOC_NAMESPACE_GET_ID:
1414 loc_namespace_get_id(callid, &call);
1415 break;
1416 default:
1417 async_answer_0(callid, ENOENT);
1418 }
1419 }
1420
1421 if (server != NULL) {
1422 /*
1423 * Unregister the server and all its services.
1424 */
1425 loc_server_unregister(server);
1426 server = NULL;
1427 }
1428}
1429
1430/** Handle connection on consumer port.
1431 *
1432 */
1433static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall)
1434{
1435 /* Accept connection */
1436 async_answer_0(iid, EOK);
1437
1438 while (true) {
1439 ipc_call_t call;
1440 ipc_callid_t callid = async_get_call(&call);
1441
1442 if (!IPC_GET_IMETHOD(call))
1443 break;
1444
1445 switch (IPC_GET_IMETHOD(call)) {
1446 case LOC_SERVICE_GET_ID:
1447 loc_service_get_id(callid, &call);
1448 break;
1449 case LOC_SERVICE_GET_NAME:
1450 loc_service_get_name(callid, &call);
1451 break;
1452 case LOC_SERVICE_GET_SERVER_NAME:
1453 loc_service_get_server_name(callid, &call);
1454 break;
1455 case LOC_NAMESPACE_GET_ID:
1456 loc_namespace_get_id(callid, &call);
1457 break;
1458 case LOC_CALLBACK_CREATE:
1459 loc_callback_create(callid, &call);
1460 break;
1461 case LOC_CATEGORY_GET_ID:
1462 loc_category_get_id(callid, &call);
1463 break;
1464 case LOC_CATEGORY_GET_NAME:
1465 loc_category_get_name(callid, &call);
1466 break;
1467 case LOC_CATEGORY_GET_SVCS:
1468 loc_category_get_svcs(callid, &call);
1469 break;
1470 case LOC_ID_PROBE:
1471 loc_id_probe(callid, &call);
1472 break;
1473 case LOC_NULL_CREATE:
1474 loc_null_create(callid, &call);
1475 break;
1476 case LOC_NULL_DESTROY:
1477 loc_null_destroy(callid, &call);
1478 break;
1479 case LOC_GET_NAMESPACE_COUNT:
1480 loc_get_namespace_count(callid, &call);
1481 break;
1482 case LOC_GET_SERVICE_COUNT:
1483 loc_get_service_count(callid, &call);
1484 break;
1485 case LOC_GET_CATEGORIES:
1486 loc_get_categories(callid, &call);
1487 break;
1488 case LOC_GET_NAMESPACES:
1489 loc_get_namespaces(callid, &call);
1490 break;
1491 case LOC_GET_SERVICES:
1492 loc_get_services(callid, &call);
1493 break;
1494 default:
1495 async_answer_0(callid, ENOENT);
1496 }
1497 }
1498}
1499
1500/** Function for handling connections to location service
1501 *
1502 */
1503static void loc_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1504{
1505 /* Select interface */
1506 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
1507 case LOC_PORT_SUPPLIER:
1508 loc_connection_supplier(iid, icall);
1509 break;
1510 case LOC_PORT_CONSUMER:
1511 loc_connection_consumer(iid, icall);
1512 break;
1513 case LOC_CONNECT_TO_SERVICE:
1514 /* Connect client to selected service */
1515 loc_forward(iid, icall);
1516 break;
1517 default:
1518 /* No such interface */
1519 async_answer_0(iid, ENOENT);
1520 }
1521}
1522
1523/**
1524 *
1525 */
1526int main(int argc, char *argv[])
1527{
1528 printf("%s: HelenOS Location Service\n", NAME);
1529
1530 if (!loc_init()) {
1531 printf("%s: Error while initializing service\n", NAME);
1532 return -1;
1533 }
1534
1535 /* Set a handler of incomming connections */
1536 async_set_client_connection(loc_connection);
1537
1538 /* Register location service at naming service */
1539 int rc = service_register(SERVICE_LOC);
1540 if (rc != EOK)
1541 return rc;
1542
1543 printf("%s: Accepting connections\n", NAME);
1544 async_manager();
1545
1546 /* Never reached */
1547 return 0;
1548}
1549
1550/**
1551 * @}
1552 */
Note: See TracBrowser for help on using the repository browser.