source: mainline/uspace/srv/locsrv/locsrv.c@ 4f29118

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

Enable partition support with all disk drivers.

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