source: mainline/uspace/srv/locsrv/locsrv.c@ 63a045c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63a045c was ee9c703, checked in by Jakub Jermar <jakub@…>, 7 years ago

Make INTERFACE_LOC_SUPPLIER serial again

This commit entirely reverts commit cbdb38ff8f. The interface
unfortunately cannot be parallel/cloned due to the fact that each loc
supplier connection is stateful and is expected to begin with a
LOC_SERVER_REGISTER call. Failing that, cloned exchanges to the
location service supplier interface immediately fail with EREFUSED,
because the method mismatches.

This results in various sporadic failures to register services or add
services into categories, which in turn results in symptoms such as
non-functioning keyboard and mouse input.

If memory serves well, the issue that cbdb38ff8f addressed was that
category change events can transitively result in new requests to the
interface over the same phone. Around the time of cbdb38ff8f there used
to be a hard limit on the number of concurrent async calls and calls
that exceeded this limit got blocked. This is no longer the case, so the
loc supplier fibril is not waiting for any of its own IPC. Hence
reverting cbdb38ff8f should be safe.

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