source: mainline/uspace/srv/loc/loc.c@ f7cbc6f

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

Stub IP/Ethernet server. Implement IP link discovery in inet server.

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