Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ boot/Makefile.common	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -72,5 +72,5 @@
 	$(USPACE_PATH)/srv/loader/loader \
 	$(USPACE_PATH)/app/init/init \
-	$(USPACE_PATH)/srv/loc/loc \
+	$(USPACE_PATH)/srv/locsrv/locsrv \
 	$(USPACE_PATH)/srv/bd/rd/rd \
 	$(USPACE_PATH)/srv/vfs/vfs
@@ -161,5 +161,5 @@
 	$(USPACE_PATH)/app/kill/kill \
 	$(USPACE_PATH)/app/killall/killall \
-	$(USPACE_PATH)/app/locinfo/locinfo \
+	$(USPACE_PATH)/app/loc/loc \
 	$(USPACE_PATH)/app/mkfat/mkfat \
 	$(USPACE_PATH)/app/mkexfat/mkexfat \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ uspace/Makefile	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -46,5 +46,5 @@
 	app/killall \
 	app/klog \
-	app/locinfo \
+	app/loc \
 	app/lsusb \
 	app/mkfat \
@@ -72,5 +72,5 @@
 	app/mkbd \
 	srv/clipboard \
-	srv/loc \
+	srv/locsrv \
 	srv/devman \
 	srv/loader \
Index: uspace/app/loc/Makefile
===================================================================
--- uspace/app/loc/Makefile	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/app/loc/Makefile	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2011 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = loc
+
+SOURCES = \
+	loc.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/loc/loc.c
===================================================================
--- uspace/app/loc/loc.c	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/app/loc/loc.c	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup loc
+ * @{
+ */
+/** @file loc.c Print information from location service.
+ */
+
+#include <errno.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <sys/types.h>
+#include <sys/typefmt.h>
+
+#define NAME "loc"
+
+int main(int argc, char *argv[])
+{
+	category_id_t *cat_ids;
+	size_t cat_cnt;
+	service_id_t *svc_ids;
+	size_t svc_cnt;
+
+	size_t i, j;
+	char *cat_name;
+	char *svc_name;
+	int rc;
+
+	rc = loc_get_categories(&cat_ids, &cat_cnt);
+	if (rc != EOK) {
+		printf(NAME ": Error getting list of categories.\n");
+		return 1;
+	}
+
+	for (i = 0; i < cat_cnt; i++) {
+		rc = loc_category_get_name(cat_ids[i], &cat_name);
+		if (rc != EOK)
+			cat_name = str_dup("<unknown>");
+
+		if (cat_name == NULL) {
+			printf(NAME ": Error allocating memory.\n");
+			return 1;
+		}
+
+		printf("%s (%" PRIun "):\n", cat_name, cat_ids[i]);
+
+		rc = loc_category_get_svcs(cat_ids[i], &svc_ids, &svc_cnt);
+		if (rc != EOK) {
+			printf(NAME ": Failed getting list of services in "
+			    "category %s, skipping.\n", cat_name);
+			free(cat_name);
+			continue;
+		}
+
+		for (j = 0; j < svc_cnt; j++) {
+			rc = loc_service_get_name(svc_ids[j], &svc_name);
+			if (rc != EOK) {
+				printf(NAME ": Unknown service name (SID %"
+				    PRIun ").\n", svc_ids[j]);
+				continue;
+			}
+			printf("\t%s (%" PRIun ")\n", svc_name, svc_ids[j]);
+			free(svc_name);
+		}
+
+		free(svc_ids);
+		free(cat_name);
+	}
+
+	free(cat_ids);
+	return 0;
+}
+
+/** @}
+ */
Index: pace/app/locinfo/Makefile
===================================================================
--- uspace/app/locinfo/Makefile	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,36 +1,0 @@
-#
-# Copyright (c) 2011 Jiri Svoboda
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# - Redistributions of source code must retain the above copyright
-#   notice, this list of conditions and the following disclaimer.
-# - Redistributions in binary form must reproduce the above copyright
-#   notice, this list of conditions and the following disclaimer in the
-#   documentation and/or other materials provided with the distribution.
-# - The name of the author may not be used to endorse or promote products
-#   derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-
-USPACE_PREFIX = ../..
-EXTRA_CFLAGS = -Iinclude
-BINARY = locinfo
-
-SOURCES = \
-	locinfo.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/locinfo/locinfo.c
===================================================================
--- uspace/app/locinfo/locinfo.c	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,103 +1,0 @@
-/*
- * Copyright (c) 2011 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup locinfo
- * @{
- */
-/** @file locinfo.c Print information from location service.
- */
-
-#include <errno.h>
-#include <loc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <str.h>
-#include <sys/types.h>
-#include <sys/typefmt.h>
-
-#define NAME "locinfo"
-
-int main(int argc, char *argv[])
-{
-	category_id_t *cat_ids;
-	size_t cat_cnt;
-	service_id_t *svc_ids;
-	size_t svc_cnt;
-
-	size_t i, j;
-	char *cat_name;
-	char *svc_name;
-	int rc;
-
-	rc = loc_get_categories(&cat_ids, &cat_cnt);
-	if (rc != EOK) {
-		printf(NAME ": Error getting list of categories.\n");
-		return 1;
-	}
-
-	for (i = 0; i < cat_cnt; i++) {
-		rc = loc_category_get_name(cat_ids[i], &cat_name);
-		if (rc != EOK)
-			cat_name = str_dup("<unknown>");
-
-		if (cat_name == NULL) {
-			printf(NAME ": Error allocating memory.\n");
-			return 1;
-		}
-
-		printf("%s (%" PRIun "):\n", cat_name, cat_ids[i]);
-
-		rc = loc_category_get_svcs(cat_ids[i], &svc_ids, &svc_cnt);
-		if (rc != EOK) {
-			printf(NAME ": Failed getting list of services in "
-			    "category %s, skipping.\n", cat_name);
-			free(cat_name);
-			continue;
-		}
-
-		for (j = 0; j < svc_cnt; j++) {
-			rc = loc_service_get_name(svc_ids[j], &svc_name);
-			if (rc != EOK) {
-				printf(NAME ": Unknown service name (SID %"
-				    PRIun ").\n", svc_ids[j]);
-				continue;
-			}
-			printf("\t%s (%" PRIun ")\n", svc_name, svc_ids[j]);
-			free(svc_name);
-		}
-
-		free(svc_ids);
-		free(cat_name);
-	}
-
-	free(cat_ids);
-	return 0;
-}
-
-/** @}
- */
Index: pace/srv/loc/Makefile
===================================================================
--- uspace/srv/loc/Makefile	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,38 +1,0 @@
-#
-# Copyright (c) 2005 Martin Decky
-# Copyright (c) 2007 Jakub Jermar
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# - Redistributions of source code must retain the above copyright
-#   notice, this list of conditions and the following disclaimer.
-# - Redistributions in binary form must reproduce the above copyright
-#   notice, this list of conditions and the following disclaimer in the
-#   documentation and/or other materials provided with the distribution.
-# - The name of the author may not be used to endorse or promote products
-#   derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-
-USPACE_PREFIX = ../..
-BINARY = loc
-STATIC_NEEDED = y
-
-SOURCES = \
-	category.c \
-	loc.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/srv/loc/category.c
===================================================================
--- uspace/srv/loc/category.c	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,213 +1,0 @@
-/*
- * Copyright (c) 2011 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup loc
- * @{
- */
-/** @file Categories for location service.
- */
-
-#include <adt/list.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <stdlib.h>
-#include <str.h>
-
-#include "category.h"
-#include "loc.h"
-
-/** Initialize category directory. */
-void categ_dir_init(categ_dir_t *cdir)
-{
-	fibril_mutex_initialize(&cdir->mutex);
-	list_initialize(&cdir->categories);
-}
-
-/** Add new category to directory. */
-void categ_dir_add_cat(categ_dir_t *cdir, category_t *cat)
-{
-	list_append(&cat->cat_list, &cdir->categories);
-}
-
-/** Get list of categories. */
-int categ_dir_get_categories(categ_dir_t *cdir, category_id_t *id_buf,
-    size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_mutex_is_locked(&cdir->mutex));
-
-	buf_cnt = buf_size / sizeof(category_id_t);
-
-	act_cnt = list_count(&cdir->categories);
-	*act_size = act_cnt * sizeof(category_id_t);
-
-	if (buf_size % sizeof(category_id_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(cdir->categories, item) {
-		category_t *cat =
-		    list_get_instance(item, category_t, cat_list);
-
-		if (pos < buf_cnt)
-			id_buf[pos] = cat->id;
-		pos++;
-	}
-
-	return EOK;
-}
-
-
-/** Initialize category structure. */
-static void category_init(category_t *cat, const char *name)
-{
-	fibril_mutex_initialize(&cat->mutex);
-	cat->name = str_dup(name);
-	cat->id = loc_create_id();
-	link_initialize(&cat->cat_list);
-	list_initialize(&cat->svc_memb);
-}
-
-/** Allocate new category. */
-category_t *category_new(const char *name)
-{
-	category_t *cat;
-
-	cat = malloc(sizeof(category_t));
-	if (cat == NULL)
-		return NULL;
-
-	category_init(cat, name);
-	return cat;
-}
-
-/** Add service to category. */
-int category_add_service(category_t *cat, loc_service_t *svc)
-{
-	assert(fibril_mutex_is_locked(&cat->mutex));
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	/* Verify that category does not contain this service yet. */
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
-		    cat_link);
-		if (memb->svc == svc) {
-			return EEXIST;
-		}
-	}
-
-	svc_categ_t *nmemb = malloc(sizeof(svc_categ_t));
-	if (nmemb == NULL)
-		return ENOMEM;
-
-	nmemb->svc = svc;
-	nmemb->cat = cat;
-
-	list_append(&nmemb->cat_link, &cat->svc_memb);
-	list_append(&nmemb->svc_link, &svc->cat_memb);
-
-	return EOK;
-}
-
-/** Remove service from category. */
-void category_remove_service(svc_categ_t *memb)
-{
-	assert(fibril_mutex_is_locked(&memb->cat->mutex));
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	list_remove(&memb->cat_link);
-	list_remove(&memb->svc_link);
-
-	free(memb);
-}
-
-/** Get category by ID. */
-category_t *category_get(categ_dir_t *cdir, catid_t catid)
-{
-	assert(fibril_mutex_is_locked(&cdir->mutex));
-
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
-		if (cat->id == catid)
-			return cat;
-	}
-
-	return NULL;
-}
-
-/** Find category by name. */
-category_t *category_find_by_name(categ_dir_t *cdir, const char *name)
-{
-	assert(fibril_mutex_is_locked(&cdir->mutex));
-
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
-		if (str_cmp(cat->name, name) == 0)
-			return cat;
-	}
-
-	return NULL;
-}
-
-/** Get list of services in category. */
-int category_get_services(category_t *cat, service_id_t *id_buf,
-    size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_mutex_is_locked(&cat->mutex));
-
-	buf_cnt = buf_size / sizeof(service_id_t);
-
-	act_cnt = list_count(&cat->svc_memb);
-	*act_size = act_cnt * sizeof(service_id_t);
-
-	if (buf_size % sizeof(service_id_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb =
-		    list_get_instance(item, svc_categ_t, cat_link);
-
-		if (pos < buf_cnt)
-			id_buf[pos] = memb->svc->id;
-		pos++;
-	}
-
-	return EOK;
-}
-
-/**
- * @}
- */
Index: pace/srv/loc/category.h
===================================================================
--- uspace/srv/loc/category.h	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/*
- * Copyright (c) 2011 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup loc
- * @{
- */
-/** @file Categories for location service.
- */
-
-#ifndef CATEGORY_H_
-#define CATEGORY_H_
-
-#include <adt/list.h>
-#include "loc.h"
-
-typedef sysarg_t catid_t;
-
-/** Service category */
-typedef struct {
-	/** Protects this structure, list of services */
-	fibril_mutex_t mutex;
-
-	/** Identifier */
-	catid_t id;
-
-	/** Category name */
-	const char *name;
-
-	/** Link to list of categories (categ_dir_t.categories) */
-	link_t cat_list;
-
-	/** List of service memberships in this category (svc_categ_t) */
-	list_t svc_memb;
-} category_t;
-
-/** Service directory ogranized by categories (yellow pages) */
-typedef struct {
-	/** Protects this structure, list of categories */
-	fibril_mutex_t mutex;
-	/** List of all categories (category_t) */
-	list_t categories;
-} categ_dir_t;
-
-/** Service in category membership. */
-typedef struct {
-	/** Link to category_t.svc_memb list */
-	link_t cat_link;
-	/** Link to loc_service_t.cat_memb list */
-	link_t svc_link;
-	
-	/** Category */
-	category_t *cat;
-	/** Service */
-	loc_service_t *svc;
-} svc_categ_t;
-
-extern void categ_dir_init(categ_dir_t *);
-extern void categ_dir_add_cat(categ_dir_t *, category_t *);
-extern int categ_dir_get_categories(categ_dir_t *, service_id_t *, size_t,
-    size_t *);
-extern category_t *category_new(const char *);
-extern int category_add_service(category_t *, loc_service_t *);
-extern void category_remove_service(svc_categ_t *);
-extern category_t *category_get(categ_dir_t *, catid_t);
-extern category_t *category_find_by_name(categ_dir_t *, const char *);
-extern int category_get_services(category_t *, service_id_t *, size_t,
-    size_t *);
-
-
-#endif
-
-/** @}
- */
Index: pace/srv/loc/loc.c
===================================================================
--- uspace/srv/loc/loc.c	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,1504 +1,0 @@
-/*
- * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2011 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @defgroup loc Location Service.
- * @brief HelenOS location service.
- * @{
- */
-
-/** @file
- */
-
-#include <ipc/services.h>
-#include <ns.h>
-#include <async.h>
-#include <stdio.h>
-#include <errno.h>
-#include <bool.h>
-#include <fibril_synch.h>
-#include <macros.h>
-#include <stdlib.h>
-#include <str.h>
-#include <ipc/loc.h>
-#include <assert.h>
-
-#include "category.h"
-#include "loc.h"
-
-#define NAME          "loc"
-#define NULL_SERVICES  256
-
-/** Callback session */
-typedef struct {
-	link_t cb_sess_list;
-	async_sess_t *sess;
-} cb_sess_t;
-
-LIST_INITIALIZE(services_list);
-LIST_INITIALIZE(namespaces_list);
-LIST_INITIALIZE(servers_list);
-
-/* Locking order:
- *  servers_list_mutex
- *  services_list_mutex
- *  (loc_server_t *)->services_mutex
- *  create_id_mutex
- **/
-
-FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
-static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
-static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
-static FIBRIL_MUTEX_INITIALIZE(create_id_mutex);
-static FIBRIL_MUTEX_INITIALIZE(null_services_mutex);
-
-static service_id_t last_id = 0;
-static loc_service_t *null_services[NULL_SERVICES];
-
-/*
- * Dummy list for null services. This is necessary so that null services can
- * be used just as any other services, e.g. in loc_service_unregister_core().
- */
-static LIST_INITIALIZE(dummy_null_services);
-
-/** Service directory ogranized by categories (yellow pages) */
-static categ_dir_t cdir;
-
-static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
-static LIST_INITIALIZE(callback_sess_list);
-
-service_id_t loc_create_id(void)
-{
-	/* TODO: allow reusing old ids after their unregistration
-	 * and implement some version of LRU algorithm, avoid overflow
-	 */
-	
-	fibril_mutex_lock(&create_id_mutex);
-	last_id++;
-	fibril_mutex_unlock(&create_id_mutex);
-	
-	return last_id;
-}
-
-/** Convert fully qualified service name to namespace and service name.
- *
- * A fully qualified service name can be either a plain service name
- * (then the namespace is considered to be an empty string) or consist
- * of two components separated by a slash. No more than one slash
- * is allowed.
- *
- */
-static bool loc_fqsn_split(const char *fqsn, char **ns_name, char **name)
-{
-	size_t cnt = 0;
-	size_t slash_offset = 0;
-	size_t slash_after = 0;
-	
-	size_t offset = 0;
-	size_t offset_prev = 0;
-	wchar_t c;
-	
-	while ((c = str_decode(fqsn, &offset, STR_NO_LIMIT)) != 0) {
-		if (c == '/') {
-			cnt++;
-			slash_offset = offset_prev;
-			slash_after = offset;
-		}
-		offset_prev = offset;
-	}
-	
-	/* More than one slash */
-	if (cnt > 1)
-		return false;
-	
-	/* No slash -> namespace is empty */
-	if (cnt == 0) {
-		*ns_name = str_dup("");
-		if (*ns_name == NULL)
-			return false;
-		
-		*name = str_dup(fqsn);
-		if (*name == NULL) {
-			free(*ns_name);
-			return false;
-		}
-		
-		if (str_cmp(*name, "") == 0) {
-			free(*name);
-			free(*ns_name);
-			return false;
-		}
-		
-		return true;
-	}
-	
-	/* Exactly one slash */
-	*ns_name = str_ndup(fqsn, slash_offset);
-	if (*ns_name == NULL)
-		return false;
-	
-	*name = str_dup(fqsn + slash_after);
-	if (*name == NULL) {
-		free(*ns_name);
-		return false;
-	}
-	
-	if (str_cmp(*name, "") == 0) {
-		free(*name);
-		free(*ns_name);
-		return false;
-	}
-	
-	return true;
-}
-
-/** Find namespace with given name. */
-static loc_namespace_t *loc_namespace_find_name(const char *name)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		if (str_cmp(namespace->name, name) == 0)
-			return namespace;
-	}
-	
-	return NULL;
-}
-
-/** Find namespace with given ID.
- *
- * @todo: use hash table
- *
- */
-static loc_namespace_t *loc_namespace_find_id(service_id_t id)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		if (namespace->id == id)
-			return namespace;
-	}
-	
-	return NULL;
-}
-
-/** Find service with given name. */
-static loc_service_t *loc_service_find_name(const char *ns_name,
-    const char *name)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		if ((str_cmp(service->namespace->name, ns_name) == 0)
-		    && (str_cmp(service->name, name) == 0))
-			return service;
-	}
-	
-	return NULL;
-}
-
-/** Find service with given ID.
- *
- * @todo: use hash table
- *
- */
-static loc_service_t *loc_service_find_id(service_id_t id)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		if (service->id == id)
-			return service;
-	}
-	
-	return NULL;
-}
-
-/** Create a namespace (if not already present). */
-static loc_namespace_t *loc_namespace_create(const char *ns_name)
-{
-	loc_namespace_t *namespace;
-	
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	
-	namespace = loc_namespace_find_name(ns_name);
-	if (namespace != NULL)
-		return namespace;
-	
-	namespace = (loc_namespace_t *) malloc(sizeof(loc_namespace_t));
-	if (namespace == NULL)
-		return NULL;
-	
-	namespace->name = str_dup(ns_name);
-	if (namespace->name == NULL) {
-		free(namespace);
-		return NULL;
-	}
-	
-	namespace->id = loc_create_id();
-	namespace->refcnt = 0;
-	
-	/*
-	 * Insert new namespace into list of registered namespaces
-	 */
-	list_append(&(namespace->namespaces), &namespaces_list);
-	
-	return namespace;
-}
-
-/** Destroy a namespace (if it is no longer needed). */
-static void loc_namespace_destroy(loc_namespace_t *namespace)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	if (namespace->refcnt == 0) {
-		list_remove(&(namespace->namespaces));
-		
-		free(namespace->name);
-		free(namespace);
-	}
-}
-
-/** Increase namespace reference count by including service. */
-static void loc_namespace_addref(loc_namespace_t *namespace,
-    loc_service_t *service)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	service->namespace = namespace;
-	namespace->refcnt++;
-}
-
-/** Decrease namespace reference count. */
-static void loc_namespace_delref(loc_namespace_t *namespace)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-
-	namespace->refcnt--;
-	loc_namespace_destroy(namespace);
-}
-
-/** Unregister service and free it. */
-static void loc_service_unregister_core(loc_service_t *service)
-{
-	assert(fibril_mutex_is_locked(&services_list_mutex));
-	assert(fibril_mutex_is_locked(&cdir.mutex));
-	
-	loc_namespace_delref(service->namespace);
-	list_remove(&(service->services));
-	list_remove(&(service->server_services));
-	
-	/* Remove service from all categories. */
-	while (!list_empty(&service->cat_memb)) {
-		link_t *link = list_first(&service->cat_memb);
-		svc_categ_t *memb = list_get_instance(link, svc_categ_t,
-		    svc_link);
-		fibril_mutex_lock(&memb->cat->mutex);
-		category_remove_service(memb);
-		fibril_mutex_unlock(&memb->cat->mutex);
-	}
-	
-	free(service->name);
-	free(service);
-}
-
-/**
- * Read info about new server and add it into linked list of registered
- * servers.
- */
-static loc_server_t *loc_server_register(void)
-{
-	ipc_call_t icall;
-	ipc_callid_t iid = async_get_call(&icall);
-	
-	if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
-		async_answer_0(iid, EREFUSED);
-		return NULL;
-	}
-	
-	loc_server_t *server =
-	    (loc_server_t *) malloc(sizeof(loc_server_t));
-	if (server == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return NULL;
-	}
-	
-	/*
-	 * Get server name
-	 */
-	int rc = async_data_write_accept((void **) &server->name, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		free(server);
-		async_answer_0(iid, rc);
-		return NULL;
-	}
-	
-	/*
-	 * Create connection to the server
-	 */
-	server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (!server->sess) {
-		free(server->name);
-		free(server);
-		async_answer_0(iid, ENOTSUP);
-		return NULL;
-	}
-	
-	/*
-	 * Initialize mutex for list of services
-	 * supplied by this server
-	 */
-	fibril_mutex_initialize(&server->services_mutex);
-	
-	/*
-	 * Initialize list of supplied services
-	 */
-	list_initialize(&server->services);
-
-	link_initialize(&server->servers);
-	
-	fibril_mutex_lock(&servers_list_mutex);
-	
-	/* TODO:
-	 * Check that no server with name equal to
-	 * server->name is registered
-	 */
-	
-	/*
-	 * Insert new server into list of registered servers
-	 */
-	list_append(&(server->servers), &servers_list);
-	fibril_mutex_unlock(&servers_list_mutex);
-	
-	async_answer_0(iid, EOK);
-	
-	return server;
-}
-
-/**
- * Unregister server, unregister all its services and free server
- * structure.
- *
- */
-static int loc_server_unregister(loc_server_t *server)
-{
-	if (server == NULL)
-		return EEXISTS;
-	
-	fibril_mutex_lock(&servers_list_mutex);
-	
-	if (server->sess)
-		async_hangup(server->sess);
-	
-	/* Remove it from list of servers */
-	list_remove(&(server->servers));
-	
-	/* Unregister all its services */
-	fibril_mutex_lock(&services_list_mutex);
-	fibril_mutex_lock(&server->services_mutex);
-	fibril_mutex_lock(&cdir.mutex);
-	
-	while (!list_empty(&server->services)) {
-		loc_service_t *service = list_get_instance(
-		    list_first(&server->services), loc_service_t,
-		    server_services);
-		loc_service_unregister_core(service);
-	}
-	
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&server->services_mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-	fibril_mutex_unlock(&servers_list_mutex);
-	
-	/* Free name and server */
-	if (server->name != NULL)
-		free(server->name);
-	
-	free(server);
-	
-	return EOK;
-}
-
-/** Register service
- *
- */
-static void loc_service_register(ipc_callid_t iid, ipc_call_t *icall,
-    loc_server_t *server)
-{
-	if (server == NULL) {
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	/* Create new service entry */
-	loc_service_t *service =
-	    (loc_service_t *) malloc(sizeof(loc_service_t));
-	if (service == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	/* Set the interface, if any. */
-	service->forward_interface = IPC_GET_ARG1(*icall);
-
-	/* Get fqsn */
-	char *fqsn;
-	int rc = async_data_write_accept((void **) &fqsn, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		free(service);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	char *ns_name;
-	if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
-		free(fqsn);
-		free(service);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	free(fqsn);
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace = loc_namespace_create(ns_name);
-	free(ns_name);
-	if (namespace == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		free(service->name);
-		free(service);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	link_initialize(&service->services);
-	link_initialize(&service->server_services);
-	list_initialize(&service->cat_memb);
-	
-	/* Check that service is not already registered */
-	if (loc_service_find_name(namespace->name, service->name) != NULL) {
-		printf("%s: Service '%s/%s' already registered\n", NAME,
-		    namespace->name, service->name);
-		loc_namespace_destroy(namespace);
-		fibril_mutex_unlock(&services_list_mutex);
-		free(service->name);
-		free(service);
-		async_answer_0(iid, EEXISTS);
-		return;
-	}
-	
-	/* Get unique service ID */
-	service->id = loc_create_id();
-
-	loc_namespace_addref(namespace, service);
-	service->server = server;
-	
-	/* Insert service into list of all services  */
-	list_append(&service->services, &services_list);
-	
-	/* Insert service into list of services supplied by one server */
-	fibril_mutex_lock(&service->server->services_mutex);
-	
-	list_append(&service->server_services, &service->server->services);
-	
-	fibril_mutex_unlock(&service->server->services_mutex);
-	fibril_condvar_broadcast(&services_list_cv);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_1(iid, EOK, service->id);
-}
-
-/**
- *
- */
-static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
-    loc_server_t *server)
-{
-	loc_service_t *svc;
-	
-	fibril_mutex_lock(&services_list_mutex);
-	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
-	if (svc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	loc_service_unregister_core(svc);
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-	async_answer_0(iid, EOK);
-}
-
-static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	category_t *cat;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	
-	cat = category_get(&cdir, IPC_GET_ARG1(*icall));
-	if (cat == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	act_size = str_size(cat->name);
-	if (act_size > size) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, cat->name,
-	    min(size, act_size));
-	
-	fibril_mutex_unlock(&cdir.mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-static void loc_service_get_name(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	loc_service_t *svc;
-	char *fqn;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
-	if (svc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	if (asprintf(&fqn, "%s/%s", svc->namespace->name, svc->name) < 0) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	act_size = str_size(fqn);
-	if (act_size > size) {
-		free(fqn);
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, fqn,
-	    min(size, act_size));
-	free(fqn);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-/** Connect client to the service.
- *
- * Find server supplying requested service and forward
- * the message to it.
- *
- */
-static void loc_forward(ipc_callid_t callid, ipc_call_t *call)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	
-	/*
-	 * Get ID from request
-	 */
-	service_id_t id = IPC_GET_ARG2(*call);
-	loc_service_t *svc = loc_service_find_id(id);
-	
-	if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	async_exch_t *exch = async_exchange_begin(svc->server->sess);
-	
-	if (svc->forward_interface == 0)
-		async_forward_fast(callid, exch, svc->id, 0, 0, IPC_FF_NONE);
-	else
-		async_forward_fast(callid, exch, svc->forward_interface,
-		    svc->id, 0, IPC_FF_NONE);
-	
-	async_exchange_end(exch);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-/** Find ID for service identified by name.
- *
- * In answer will be send EOK and service ID in arg1 or a error
- * code from errno.h.
- *
- */
-static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *fqsn;
-	
-	/* Get fqsn */
-	int rc = async_data_write_accept((void **) &fqsn, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	char *ns_name;
-	char *name;
-	if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
-		free(fqsn);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	free(fqsn);
-	
-	fibril_mutex_lock(&services_list_mutex);
-	const loc_service_t *svc;
-	
-recheck:
-	
-	/*
-	 * Find service name in the list of known services.
-	 */
-	svc = loc_service_find_name(ns_name, name);
-	
-	/*
-	 * Device was not found.
-	 */
-	if (svc == NULL) {
-		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
-			/* Blocking lookup */
-			fibril_condvar_wait(&services_list_cv,
-			    &services_list_mutex);
-			goto recheck;
-		}
-		
-		async_answer_0(iid, ENOENT);
-		free(ns_name);
-		free(name);
-		fibril_mutex_unlock(&services_list_mutex);
-		return;
-	}
-	
-	async_answer_1(iid, EOK, svc->id);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	free(ns_name);
-	free(name);
-}
-
-/** Find ID for namespace identified by name.
- *
- * In answer will be send EOK and service ID in arg1 or a error
- * code from errno.h.
- *
- */
-static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *name;
-	
-	/* Get service name */
-	int rc = async_data_write_accept((void **) &name, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	const loc_namespace_t *namespace;
-	
-recheck:
-	
-	/*
-	 * Find namespace name in the list of known namespaces.
-	 */
-	namespace = loc_namespace_find_name(name);
-	
-	/*
-	 * Namespace was not found.
-	 */
-	if (namespace == NULL) {
-		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
-			/* Blocking lookup */
-			fibril_condvar_wait(&services_list_cv,
-			    &services_list_mutex);
-			goto recheck;
-		}
-		
-		async_answer_0(iid, ENOENT);
-		free(name);
-		fibril_mutex_unlock(&services_list_mutex);
-		return;
-	}
-	
-	async_answer_1(iid, EOK, namespace->id);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	free(name);
-}
-
-/** Create callback connection.
- *
- * Create callback connection which will be used to send category change
- * events.
- *
- * On success, answer will contain EOK int retval.
- * On failure, error code will be sent in retval.
- *
- */
-static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
-{
-	cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
-	if (cb_sess == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL) {
-		free(cb_sess);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	cb_sess->sess = sess;
-	link_initialize(&cb_sess->cb_sess_list);
-	
-	fibril_mutex_lock(&callback_sess_mutex);
-	list_append(&cb_sess->cb_sess_list, &callback_sess_list);
-	fibril_mutex_unlock(&callback_sess_mutex);
-	
-	async_answer_0(iid, EOK);
-}
-
-void loc_category_change_event(void)
-{
-	fibril_mutex_lock(&callback_sess_mutex);
-	
-	list_foreach(callback_sess_list, link) {
-		cb_sess_t *cb_sess;
-		
-		cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
-		
-		async_exch_t *exch = async_exchange_begin(cb_sess->sess);
-		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
-		async_exchange_end(exch);
-	}
-	
-	fibril_mutex_unlock(&callback_sess_mutex);
-}
-
-/** Find ID for category specified by name.
- *
- * On success, answer will contain EOK int retval and service ID in arg1.
- * On failure, error code will be sent in retval.
- *
- */
-static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *name;
-	category_t *cat;
-	
-	/* Get service name */
-	int rc = async_data_write_accept((void **) &name, true, 0,
-	    LOC_NAME_MAXLEN, 0, NULL);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-
-	cat = category_find_by_name(&cdir, name);
-	if (cat == NULL) {
-		/* Category not found */
-		async_answer_0(iid, ENOENT);
-		goto cleanup;
-	}
-	
-	async_answer_1(iid, EOK, cat->id);
-cleanup:
-	fibril_mutex_unlock(&cdir.mutex);
-	free(name);
-}
-
-static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace =
-	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
-	if (namespace == NULL) {
-		loc_service_t *svc =
-		    loc_service_find_id(IPC_GET_ARG1(*icall));
-		if (svc == NULL)
-			async_answer_1(iid, EOK, LOC_OBJECT_NONE);
-		else
-			async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
-	} else
-		async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	async_answer_1(iid, EOK, list_count(&namespaces_list));
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace =
-	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
-	if (namespace == NULL)
-		async_answer_0(iid, EEXISTS);
-	else
-		async_answer_1(iid, EOK, namespace->refcnt);
-	
-	fibril_mutex_unlock(&services_list_mutex);
-}
-
-static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	int rc;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	category_id_t *id_buf = (category_id_t *) malloc(size);
-	if (id_buf == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	
-	rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_unlock(&cdir.mutex);
-	
-	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
-	free(id_buf);
-	
-	async_answer_1(iid, retval, act_size);
-}
-
-static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if ((size % sizeof(loc_sdesc_t)) != 0) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	size_t count = size / sizeof(loc_sdesc_t);
-	if (count != list_count(&namespaces_list)) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
-	if (desc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	size_t pos = 0;
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		
-		desc[pos].id = namespace->id;
-		str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
-		pos++;
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, desc, size);
-	
-	free(desc);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* FIXME: Use faster algorithm which can make better use
-	   of namespaces */
-	
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if ((size % sizeof(loc_sdesc_t)) != 0) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace =
-	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
-	if (namespace == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	size_t count = size / sizeof(loc_sdesc_t);
-	if (count != namespace->refcnt) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, EOVERFLOW);
-		async_answer_0(iid, EOVERFLOW);
-		return;
-	}
-	
-	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
-	if (desc == NULL) {
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	size_t pos = 0;
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		
-		if (service->namespace == namespace) {
-			desc[pos].id = service->id;
-			str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
-			pos++;
-		}
-	}
-	
-	sysarg_t retval = async_data_read_finalize(callid, desc, size);
-	
-	free(desc);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	async_answer_0(iid, retval);
-}
-
-static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	int rc;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_mutex_lock(&cdir.mutex);
-	
-	category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
-	if (cat == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	category_id_t *id_buf = (category_id_t *) malloc(size);
-	if (id_buf == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	fibril_mutex_lock(&cat->mutex);
-	
-	rc = category_get_services(cat, id_buf, size, &act_size);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&cat->mutex);
-		fibril_mutex_unlock(&cdir.mutex);
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_mutex_unlock(&cat->mutex);
-	fibril_mutex_unlock(&cdir.mutex);
-	
-	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
-	free(id_buf);
-	
-	async_answer_1(iid, retval, act_size);
-}
-
-
-static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fibril_mutex_lock(&null_services_mutex);
-	
-	unsigned int i;
-	bool fnd = false;
-	
-	for (i = 0; i < NULL_SERVICES; i++) {
-		if (null_services[i] == NULL) {
-			fnd = true;
-			break;
-		}
-	}
-	
-	if (!fnd) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	char null[LOC_NAME_MAXLEN];
-	snprintf(null, LOC_NAME_MAXLEN, "%u", i);
-	
-	char *dev_name = str_dup(null);
-	if (dev_name == NULL) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	loc_service_t *service =
-	    (loc_service_t *) malloc(sizeof(loc_service_t));
-	if (service == NULL) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	
-	loc_namespace_t *namespace = loc_namespace_create("null");
-	if (namespace == NULL) {
-		fibril_mutex_lock(&services_list_mutex);
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	link_initialize(&service->services);
-	link_initialize(&service->server_services);
-	list_initialize(&service->cat_memb);
-	
-	/* Get unique service ID */
-	service->id = loc_create_id();
-	service->server = NULL;
-	
-	loc_namespace_addref(namespace, service);
-	service->name = dev_name;
-	
-	/*
-	 * Insert service into list of all services and into null services array.
-	 * Insert service into a dummy list of null server's services so that it
-	 * can be safely removed later.
-	 */
-	list_append(&service->services, &services_list);
-	list_append(&service->server_services, &dummy_null_services);
-	null_services[i] = service;
-	
-	fibril_mutex_unlock(&services_list_mutex);
-	fibril_mutex_unlock(&null_services_mutex);
-	
-	async_answer_1(iid, EOK, (sysarg_t) i);
-}
-
-static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
-{
-	sysarg_t i = IPC_GET_ARG1(*icall);
-	if (i >= NULL_SERVICES) {
-		async_answer_0(iid, ELIMIT);
-		return;
-	}
-	
-	fibril_mutex_lock(&null_services_mutex);
-	
-	if (null_services[i] == NULL) {
-		fibril_mutex_unlock(&null_services_mutex);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fibril_mutex_lock(&services_list_mutex);
-	fibril_mutex_lock(&cdir.mutex);
-	loc_service_unregister_core(null_services[i]);
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-	
-	null_services[i] = NULL;
-	
-	fibril_mutex_unlock(&null_services_mutex);
-	async_answer_0(iid, EOK);
-}
-
-static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
-{
-	category_t *cat;
-	loc_service_t *svc;
-	catid_t cat_id;
-	service_id_t svc_id;
-	sysarg_t retval;
-	
-	svc_id = IPC_GET_ARG1(*icall);
-	cat_id = IPC_GET_ARG2(*icall);
-	
-	fibril_mutex_lock(&services_list_mutex);
-	fibril_mutex_lock(&cdir.mutex);
-	
-	cat = category_get(&cdir, cat_id);
-	svc = loc_service_find_id(svc_id);
-	
-	if (cat == NULL || svc == NULL) {
-		fibril_mutex_unlock(&cdir.mutex);
-		fibril_mutex_unlock(&services_list_mutex);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fibril_mutex_lock(&cat->mutex);
-	retval = category_add_service(cat, svc);
-
-	fibril_mutex_unlock(&cat->mutex);
-	fibril_mutex_unlock(&cdir.mutex);
-	fibril_mutex_unlock(&services_list_mutex);
-
-	async_answer_0(iid, retval);
-
-	loc_category_change_event();
-}
-
-
-/** Initialize location service.
- *
- *
- */
-static bool loc_init(void)
-{
-	unsigned int i;
-	category_t *cat;
-
-	for (i = 0; i < NULL_SERVICES; i++)
-		null_services[i] = NULL;
-	
-	categ_dir_init(&cdir);
-
-	cat = category_new("bd");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("iplink");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("keyboard");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("mouse");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("serial");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("test3");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("usbhc");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("virt-null");
-	categ_dir_add_cat(&cdir, cat);
-
-	cat = category_new("virtual");
-	categ_dir_add_cat(&cdir, cat);
-	
-	cat = category_new("nic");
-	categ_dir_add_cat(&cdir, cat);
-	
-	return true;
-}
-
-/** Handle connection on supplier port.
- *
- */
-static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* Accept connection */
-	async_answer_0(iid, EOK);
-	
-	loc_server_t *server = loc_server_register();
-	if (server == NULL)
-		return;
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case LOC_SERVER_UNREGISTER:
-			if (server == NULL)
-				async_answer_0(callid, ENOENT);
-			else
-				async_answer_0(callid, EOK);
-			break;
-		case LOC_SERVICE_ADD_TO_CAT:
-			/* Add service to category */
-			loc_service_add_to_cat(callid, &call);
-			break;
-		case LOC_SERVICE_REGISTER:
-			/* Register one service */
-			loc_service_register(callid, &call, server);
-			break;
-		case LOC_SERVICE_UNREGISTER:
-			/* Remove one service */
-			loc_service_unregister(callid, &call, server);
-			break;
-		case LOC_SERVICE_GET_ID:
-			loc_service_get_id(callid, &call);
-			break;
-		case LOC_NAMESPACE_GET_ID:
-			loc_namespace_get_id(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOENT);
-		}
-	}
-	
-	if (server != NULL) {
-		/*
-		 * Unregister the server and all its services.
-		 */
-		loc_server_unregister(server);
-		server = NULL;
-	}
-}
-
-/** Handle connection on consumer port.
- *
- */
-static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* Accept connection */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case LOC_SERVICE_GET_ID:
-			loc_service_get_id(callid, &call);
-			break;
-		case LOC_SERVICE_GET_NAME:
-			loc_service_get_name(callid, &call);
-			break;
-		case LOC_NAMESPACE_GET_ID:
-			loc_namespace_get_id(callid, &call);
-			break;
-		case LOC_CALLBACK_CREATE:
-			loc_callback_create(callid, &call);
-			break;
-		case LOC_CATEGORY_GET_ID:
-			loc_category_get_id(callid, &call);
-			break;
-		case LOC_CATEGORY_GET_NAME:
-			loc_category_get_name(callid, &call);
-			break;
-		case LOC_CATEGORY_GET_SVCS:
-			loc_category_get_svcs(callid, &call);
-			break;
-		case LOC_ID_PROBE:
-			loc_id_probe(callid, &call);
-			break;
-		case LOC_NULL_CREATE:
-			loc_null_create(callid, &call);
-			break;
-		case LOC_NULL_DESTROY:
-			loc_null_destroy(callid, &call);
-			break;
-		case LOC_GET_NAMESPACE_COUNT:
-			loc_get_namespace_count(callid, &call);
-			break;
-		case LOC_GET_SERVICE_COUNT:
-			loc_get_service_count(callid, &call);
-			break;
-		case LOC_GET_CATEGORIES:
-			loc_get_categories(callid, &call);
-			break;
-		case LOC_GET_NAMESPACES:
-			loc_get_namespaces(callid, &call);
-			break;
-		case LOC_GET_SERVICES:
-			loc_get_services(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOENT);
-		}
-	}
-}
-
-/** Function for handling connections to location service
- *
- */
-static void loc_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	/* Select interface */
-	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
-	case LOC_PORT_SUPPLIER:
-		loc_connection_supplier(iid, icall);
-		break;
-	case LOC_PORT_CONSUMER:
-		loc_connection_consumer(iid, icall);
-		break;
-	case LOC_CONNECT_TO_SERVICE:
-		/* Connect client to selected service */
-		loc_forward(iid, icall);
-		break;
-	default:
-		/* No such interface */
-		async_answer_0(iid, ENOENT);
-	}
-}
-
-/**
- *
- */
-int main(int argc, char *argv[])
-{
-	printf("%s: HelenOS Location Service\n", NAME);
-	
-	if (!loc_init()) {
-		printf("%s: Error while initializing service\n", NAME);
-		return -1;
-	}
-	
-	/* Set a handler of incomming connections */
-	async_set_client_connection(loc_connection);
-	
-	/* Register location service at naming service */
-	if (service_register(SERVICE_LOC) != EOK)
-		return -1;
-	
-	printf("%s: Accepting connections\n", NAME);
-	async_manager();
-	
-	/* Never reached */
-	return 0;
-}
-
-/**
- * @}
- */
Index: pace/srv/loc/loc.h
===================================================================
--- uspace/srv/loc/loc.h	(revision b4ec1ea0d925eb817dda078a65808347de80c0c4)
+++ 	(revision )
@@ -1,114 +1,0 @@
-/*
- * Copyright (c) 2011 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup loc
- * @{
- */
-/** @file HelenOS location service.
- */
-
-#ifndef LOC_H_
-#define LOC_H_
-
-#include <ipc/loc.h>
-#include <async.h>
-#include <fibril_synch.h>
-#include <sys/types.h>
-
-/** Representation of server (supplier).
- *
- * Each server supplies a set of services.
- *
- */
-typedef struct {
-	/** Link to servers_list */
-	link_t servers;
-	
-	/** List of services supplied by this server */
-	list_t services;
-	
-	/** Session asociated with this server */
-	async_sess_t *sess;
-	
-	/** Server name */
-	char *name;
-	
-	/** Fibril mutex for list of services owned by this server */
-	fibril_mutex_t services_mutex;
-} loc_server_t;
-
-/** Info about registered namespaces
- *
- */
-typedef struct {
-	/** Link to namespaces_list */
-	link_t namespaces;
-	
-	/** Unique namespace identifier */
-	service_id_t id;
-	
-	/** Namespace name */
-	char *name;
-	
-	/** Reference count */
-	size_t refcnt;
-} loc_namespace_t;
-
-/** Info about registered service
- *
- */
-typedef struct {
-	/** Link to global list of services (services_list) */
-	link_t services;
-	/** Link to server list of services (loc_server_t.services) */
-	link_t server_services;
-	/** Link to list of services in category (category_t.services) */
-	link_t cat_services;
-	/** List of category memberships (svc_categ_t) */
-	list_t cat_memb;
-	/** Unique service identifier */
-	service_id_t id;
-	/** Service namespace */
-	loc_namespace_t *namespace;
-	/** Service name */
-	char *name;
-	/** Supplier of this service */
-	loc_server_t *server;
-	/** Use this interface when forwarding to server. */
-	sysarg_t forward_interface;
-} loc_service_t;
-
-extern fibril_mutex_t services_list_mutex;
-
-extern service_id_t loc_create_id(void);
-extern void loc_category_change_event(void);
-
-#endif
-
-/** @}
- */
Index: uspace/srv/locsrv/Makefile
===================================================================
--- uspace/srv/locsrv/Makefile	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/srv/locsrv/Makefile	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2005 Martin Decky
+# Copyright (c) 2007 Jakub Jermar
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = locsrv
+STATIC_NEEDED = y
+
+SOURCES = \
+	category.c \
+	locsrv.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/locsrv/category.c
===================================================================
--- uspace/srv/locsrv/category.c	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/srv/locsrv/category.c	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup loc
+ * @{
+ */
+/** @file Categories for location service.
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "category.h"
+#include "locsrv.h"
+
+/** Initialize category directory. */
+void categ_dir_init(categ_dir_t *cdir)
+{
+	fibril_mutex_initialize(&cdir->mutex);
+	list_initialize(&cdir->categories);
+}
+
+/** Add new category to directory. */
+void categ_dir_add_cat(categ_dir_t *cdir, category_t *cat)
+{
+	list_append(&cat->cat_list, &cdir->categories);
+}
+
+/** Get list of categories. */
+int categ_dir_get_categories(categ_dir_t *cdir, category_id_t *id_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	buf_cnt = buf_size / sizeof(category_id_t);
+
+	act_cnt = list_count(&cdir->categories);
+	*act_size = act_cnt * sizeof(category_id_t);
+
+	if (buf_size % sizeof(category_id_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(cdir->categories, item) {
+		category_t *cat =
+		    list_get_instance(item, category_t, cat_list);
+
+		if (pos < buf_cnt)
+			id_buf[pos] = cat->id;
+		pos++;
+	}
+
+	return EOK;
+}
+
+
+/** Initialize category structure. */
+static void category_init(category_t *cat, const char *name)
+{
+	fibril_mutex_initialize(&cat->mutex);
+	cat->name = str_dup(name);
+	cat->id = loc_create_id();
+	link_initialize(&cat->cat_list);
+	list_initialize(&cat->svc_memb);
+}
+
+/** Allocate new category. */
+category_t *category_new(const char *name)
+{
+	category_t *cat;
+
+	cat = malloc(sizeof(category_t));
+	if (cat == NULL)
+		return NULL;
+
+	category_init(cat, name);
+	return cat;
+}
+
+/** Add service to category. */
+int category_add_service(category_t *cat, loc_service_t *svc)
+{
+	assert(fibril_mutex_is_locked(&cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	/* Verify that category does not contain this service yet. */
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
+		    cat_link);
+		if (memb->svc == svc) {
+			return EEXIST;
+		}
+	}
+
+	svc_categ_t *nmemb = malloc(sizeof(svc_categ_t));
+	if (nmemb == NULL)
+		return ENOMEM;
+
+	nmemb->svc = svc;
+	nmemb->cat = cat;
+
+	list_append(&nmemb->cat_link, &cat->svc_memb);
+	list_append(&nmemb->svc_link, &svc->cat_memb);
+
+	return EOK;
+}
+
+/** Remove service from category. */
+void category_remove_service(svc_categ_t *memb)
+{
+	assert(fibril_mutex_is_locked(&memb->cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	list_remove(&memb->cat_link);
+	list_remove(&memb->svc_link);
+
+	free(memb);
+}
+
+/** Get category by ID. */
+category_t *category_get(categ_dir_t *cdir, catid_t catid)
+{
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	list_foreach(cdir->categories, item) {
+		category_t *cat = list_get_instance(item, category_t,
+		    cat_list);
+		if (cat->id == catid)
+			return cat;
+	}
+
+	return NULL;
+}
+
+/** Find category by name. */
+category_t *category_find_by_name(categ_dir_t *cdir, const char *name)
+{
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	list_foreach(cdir->categories, item) {
+		category_t *cat = list_get_instance(item, category_t,
+		    cat_list);
+		if (str_cmp(cat->name, name) == 0)
+			return cat;
+	}
+
+	return NULL;
+}
+
+/** Get list of services in category. */
+int category_get_services(category_t *cat, service_id_t *id_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_mutex_is_locked(&cat->mutex));
+
+	buf_cnt = buf_size / sizeof(service_id_t);
+
+	act_cnt = list_count(&cat->svc_memb);
+	*act_size = act_cnt * sizeof(service_id_t);
+
+	if (buf_size % sizeof(service_id_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb =
+		    list_get_instance(item, svc_categ_t, cat_link);
+
+		if (pos < buf_cnt)
+			id_buf[pos] = memb->svc->id;
+		pos++;
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/locsrv/category.h
===================================================================
--- uspace/srv/locsrv/category.h	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/srv/locsrv/category.h	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup loc
+ * @{
+ */
+/** @file Categories for location service.
+ */
+
+#ifndef CATEGORY_H_
+#define CATEGORY_H_
+
+#include <adt/list.h>
+#include "locsrv.h"
+
+typedef sysarg_t catid_t;
+
+/** Service category */
+typedef struct {
+	/** Protects this structure, list of services */
+	fibril_mutex_t mutex;
+
+	/** Identifier */
+	catid_t id;
+
+	/** Category name */
+	const char *name;
+
+	/** Link to list of categories (categ_dir_t.categories) */
+	link_t cat_list;
+
+	/** List of service memberships in this category (svc_categ_t) */
+	list_t svc_memb;
+} category_t;
+
+/** Service directory ogranized by categories (yellow pages) */
+typedef struct {
+	/** Protects this structure, list of categories */
+	fibril_mutex_t mutex;
+	/** List of all categories (category_t) */
+	list_t categories;
+} categ_dir_t;
+
+/** Service in category membership. */
+typedef struct {
+	/** Link to category_t.svc_memb list */
+	link_t cat_link;
+	/** Link to loc_service_t.cat_memb list */
+	link_t svc_link;
+	
+	/** Category */
+	category_t *cat;
+	/** Service */
+	loc_service_t *svc;
+} svc_categ_t;
+
+extern void categ_dir_init(categ_dir_t *);
+extern void categ_dir_add_cat(categ_dir_t *, category_t *);
+extern int categ_dir_get_categories(categ_dir_t *, service_id_t *, size_t,
+    size_t *);
+extern category_t *category_new(const char *);
+extern int category_add_service(category_t *, loc_service_t *);
+extern void category_remove_service(svc_categ_t *);
+extern category_t *category_get(categ_dir_t *, catid_t);
+extern category_t *category_find_by_name(categ_dir_t *, const char *);
+extern int category_get_services(category_t *, service_id_t *, size_t,
+    size_t *);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/srv/locsrv/locsrv.c	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,1504 @@
+/*
+ * Copyright (c) 2007 Josef Cejka
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup loc Location Service.
+ * @brief HelenOS location service.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <ipc/services.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <bool.h>
+#include <fibril_synch.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ipc/loc.h>
+#include <assert.h>
+
+#include "category.h"
+#include "locsrv.h"
+
+#define NAME          "loc"
+#define NULL_SERVICES  256
+
+/** Callback session */
+typedef struct {
+	link_t cb_sess_list;
+	async_sess_t *sess;
+} cb_sess_t;
+
+LIST_INITIALIZE(services_list);
+LIST_INITIALIZE(namespaces_list);
+LIST_INITIALIZE(servers_list);
+
+/* Locking order:
+ *  servers_list_mutex
+ *  services_list_mutex
+ *  (loc_server_t *)->services_mutex
+ *  create_id_mutex
+ **/
+
+FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
+static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
+static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
+static FIBRIL_MUTEX_INITIALIZE(create_id_mutex);
+static FIBRIL_MUTEX_INITIALIZE(null_services_mutex);
+
+static service_id_t last_id = 0;
+static loc_service_t *null_services[NULL_SERVICES];
+
+/*
+ * Dummy list for null services. This is necessary so that null services can
+ * be used just as any other services, e.g. in loc_service_unregister_core().
+ */
+static LIST_INITIALIZE(dummy_null_services);
+
+/** Service directory ogranized by categories (yellow pages) */
+static categ_dir_t cdir;
+
+static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
+static LIST_INITIALIZE(callback_sess_list);
+
+service_id_t loc_create_id(void)
+{
+	/* TODO: allow reusing old ids after their unregistration
+	 * and implement some version of LRU algorithm, avoid overflow
+	 */
+	
+	fibril_mutex_lock(&create_id_mutex);
+	last_id++;
+	fibril_mutex_unlock(&create_id_mutex);
+	
+	return last_id;
+}
+
+/** Convert fully qualified service name to namespace and service name.
+ *
+ * A fully qualified service name can be either a plain service name
+ * (then the namespace is considered to be an empty string) or consist
+ * of two components separated by a slash. No more than one slash
+ * is allowed.
+ *
+ */
+static bool loc_fqsn_split(const char *fqsn, char **ns_name, char **name)
+{
+	size_t cnt = 0;
+	size_t slash_offset = 0;
+	size_t slash_after = 0;
+	
+	size_t offset = 0;
+	size_t offset_prev = 0;
+	wchar_t c;
+	
+	while ((c = str_decode(fqsn, &offset, STR_NO_LIMIT)) != 0) {
+		if (c == '/') {
+			cnt++;
+			slash_offset = offset_prev;
+			slash_after = offset;
+		}
+		offset_prev = offset;
+	}
+	
+	/* More than one slash */
+	if (cnt > 1)
+		return false;
+	
+	/* No slash -> namespace is empty */
+	if (cnt == 0) {
+		*ns_name = str_dup("");
+		if (*ns_name == NULL)
+			return false;
+		
+		*name = str_dup(fqsn);
+		if (*name == NULL) {
+			free(*ns_name);
+			return false;
+		}
+		
+		if (str_cmp(*name, "") == 0) {
+			free(*name);
+			free(*ns_name);
+			return false;
+		}
+		
+		return true;
+	}
+	
+	/* Exactly one slash */
+	*ns_name = str_ndup(fqsn, slash_offset);
+	if (*ns_name == NULL)
+		return false;
+	
+	*name = str_dup(fqsn + slash_after);
+	if (*name == NULL) {
+		free(*ns_name);
+		return false;
+	}
+	
+	if (str_cmp(*name, "") == 0) {
+		free(*name);
+		free(*ns_name);
+		return false;
+	}
+	
+	return true;
+}
+
+/** Find namespace with given name. */
+static loc_namespace_t *loc_namespace_find_name(const char *name)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(namespaces_list, item) {
+		loc_namespace_t *namespace =
+		    list_get_instance(item, loc_namespace_t, namespaces);
+		if (str_cmp(namespace->name, name) == 0)
+			return namespace;
+	}
+	
+	return NULL;
+}
+
+/** Find namespace with given ID.
+ *
+ * @todo: use hash table
+ *
+ */
+static loc_namespace_t *loc_namespace_find_id(service_id_t id)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(namespaces_list, item) {
+		loc_namespace_t *namespace =
+		    list_get_instance(item, loc_namespace_t, namespaces);
+		if (namespace->id == id)
+			return namespace;
+	}
+	
+	return NULL;
+}
+
+/** Find service with given name. */
+static loc_service_t *loc_service_find_name(const char *ns_name,
+    const char *name)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(services_list, item) {
+		loc_service_t *service =
+		    list_get_instance(item, loc_service_t, services);
+		if ((str_cmp(service->namespace->name, ns_name) == 0)
+		    && (str_cmp(service->name, name) == 0))
+			return service;
+	}
+	
+	return NULL;
+}
+
+/** Find service with given ID.
+ *
+ * @todo: use hash table
+ *
+ */
+static loc_service_t *loc_service_find_id(service_id_t id)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	list_foreach(services_list, item) {
+		loc_service_t *service =
+		    list_get_instance(item, loc_service_t, services);
+		if (service->id == id)
+			return service;
+	}
+	
+	return NULL;
+}
+
+/** Create a namespace (if not already present). */
+static loc_namespace_t *loc_namespace_create(const char *ns_name)
+{
+	loc_namespace_t *namespace;
+	
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	
+	namespace = loc_namespace_find_name(ns_name);
+	if (namespace != NULL)
+		return namespace;
+	
+	namespace = (loc_namespace_t *) malloc(sizeof(loc_namespace_t));
+	if (namespace == NULL)
+		return NULL;
+	
+	namespace->name = str_dup(ns_name);
+	if (namespace->name == NULL) {
+		free(namespace);
+		return NULL;
+	}
+	
+	namespace->id = loc_create_id();
+	namespace->refcnt = 0;
+	
+	/*
+	 * Insert new namespace into list of registered namespaces
+	 */
+	list_append(&(namespace->namespaces), &namespaces_list);
+	
+	return namespace;
+}
+
+/** Destroy a namespace (if it is no longer needed). */
+static void loc_namespace_destroy(loc_namespace_t *namespace)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	if (namespace->refcnt == 0) {
+		list_remove(&(namespace->namespaces));
+		
+		free(namespace->name);
+		free(namespace);
+	}
+}
+
+/** Increase namespace reference count by including service. */
+static void loc_namespace_addref(loc_namespace_t *namespace,
+    loc_service_t *service)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	service->namespace = namespace;
+	namespace->refcnt++;
+}
+
+/** Decrease namespace reference count. */
+static void loc_namespace_delref(loc_namespace_t *namespace)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	namespace->refcnt--;
+	loc_namespace_destroy(namespace);
+}
+
+/** Unregister service and free it. */
+static void loc_service_unregister_core(loc_service_t *service)
+{
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+	assert(fibril_mutex_is_locked(&cdir.mutex));
+	
+	loc_namespace_delref(service->namespace);
+	list_remove(&(service->services));
+	list_remove(&(service->server_services));
+	
+	/* Remove service from all categories. */
+	while (!list_empty(&service->cat_memb)) {
+		link_t *link = list_first(&service->cat_memb);
+		svc_categ_t *memb = list_get_instance(link, svc_categ_t,
+		    svc_link);
+		fibril_mutex_lock(&memb->cat->mutex);
+		category_remove_service(memb);
+		fibril_mutex_unlock(&memb->cat->mutex);
+	}
+	
+	free(service->name);
+	free(service);
+}
+
+/**
+ * Read info about new server and add it into linked list of registered
+ * servers.
+ */
+static loc_server_t *loc_server_register(void)
+{
+	ipc_call_t icall;
+	ipc_callid_t iid = async_get_call(&icall);
+	
+	if (IPC_GET_IMETHOD(icall) != LOC_SERVER_REGISTER) {
+		async_answer_0(iid, EREFUSED);
+		return NULL;
+	}
+	
+	loc_server_t *server =
+	    (loc_server_t *) malloc(sizeof(loc_server_t));
+	if (server == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return NULL;
+	}
+	
+	/*
+	 * Get server name
+	 */
+	int rc = async_data_write_accept((void **) &server->name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		free(server);
+		async_answer_0(iid, rc);
+		return NULL;
+	}
+	
+	/*
+	 * Create connection to the server
+	 */
+	server->sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (!server->sess) {
+		free(server->name);
+		free(server);
+		async_answer_0(iid, ENOTSUP);
+		return NULL;
+	}
+	
+	/*
+	 * Initialize mutex for list of services
+	 * supplied by this server
+	 */
+	fibril_mutex_initialize(&server->services_mutex);
+	
+	/*
+	 * Initialize list of supplied services
+	 */
+	list_initialize(&server->services);
+
+	link_initialize(&server->servers);
+	
+	fibril_mutex_lock(&servers_list_mutex);
+	
+	/* TODO:
+	 * Check that no server with name equal to
+	 * server->name is registered
+	 */
+	
+	/*
+	 * Insert new server into list of registered servers
+	 */
+	list_append(&(server->servers), &servers_list);
+	fibril_mutex_unlock(&servers_list_mutex);
+	
+	async_answer_0(iid, EOK);
+	
+	return server;
+}
+
+/**
+ * Unregister server, unregister all its services and free server
+ * structure.
+ *
+ */
+static int loc_server_unregister(loc_server_t *server)
+{
+	if (server == NULL)
+		return EEXISTS;
+	
+	fibril_mutex_lock(&servers_list_mutex);
+	
+	if (server->sess)
+		async_hangup(server->sess);
+	
+	/* Remove it from list of servers */
+	list_remove(&(server->servers));
+	
+	/* Unregister all its services */
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&server->services_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	
+	while (!list_empty(&server->services)) {
+		loc_service_t *service = list_get_instance(
+		    list_first(&server->services), loc_service_t,
+		    server_services);
+		loc_service_unregister_core(service);
+	}
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&server->services_mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	fibril_mutex_unlock(&servers_list_mutex);
+	
+	/* Free name and server */
+	if (server->name != NULL)
+		free(server->name);
+	
+	free(server);
+	
+	return EOK;
+}
+
+/** Register service
+ *
+ */
+static void loc_service_register(ipc_callid_t iid, ipc_call_t *icall,
+    loc_server_t *server)
+{
+	if (server == NULL) {
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	/* Create new service entry */
+	loc_service_t *service =
+	    (loc_service_t *) malloc(sizeof(loc_service_t));
+	if (service == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	/* Set the interface, if any. */
+	service->forward_interface = IPC_GET_ARG1(*icall);
+
+	/* Get fqsn */
+	char *fqsn;
+	int rc = async_data_write_accept((void **) &fqsn, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		free(service);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	char *ns_name;
+	if (!loc_fqsn_split(fqsn, &ns_name, &service->name)) {
+		free(fqsn);
+		free(service);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	free(fqsn);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace = loc_namespace_create(ns_name);
+	free(ns_name);
+	if (namespace == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		free(service->name);
+		free(service);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	link_initialize(&service->services);
+	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
+	
+	/* Check that service is not already registered */
+	if (loc_service_find_name(namespace->name, service->name) != NULL) {
+		printf("%s: Service '%s/%s' already registered\n", NAME,
+		    namespace->name, service->name);
+		loc_namespace_destroy(namespace);
+		fibril_mutex_unlock(&services_list_mutex);
+		free(service->name);
+		free(service);
+		async_answer_0(iid, EEXISTS);
+		return;
+	}
+	
+	/* Get unique service ID */
+	service->id = loc_create_id();
+
+	loc_namespace_addref(namespace, service);
+	service->server = server;
+	
+	/* Insert service into list of all services  */
+	list_append(&service->services, &services_list);
+	
+	/* Insert service into list of services supplied by one server */
+	fibril_mutex_lock(&service->server->services_mutex);
+	
+	list_append(&service->server_services, &service->server->services);
+	
+	fibril_mutex_unlock(&service->server->services_mutex);
+	fibril_condvar_broadcast(&services_list_cv);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_1(iid, EOK, service->id);
+}
+
+/**
+ *
+ */
+static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
+    loc_server_t *server)
+{
+	loc_service_t *svc;
+	
+	fibril_mutex_lock(&services_list_mutex);
+	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
+	if (svc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	loc_service_unregister_core(svc);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	async_answer_0(iid, EOK);
+}
+
+static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	category_t *cat;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	cat = category_get(&cdir, IPC_GET_ARG1(*icall));
+	if (cat == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	act_size = str_size(cat->name);
+	if (act_size > size) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, cat->name,
+	    min(size, act_size));
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+static void loc_service_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	loc_service_t *svc;
+	char *fqn;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
+	if (svc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (asprintf(&fqn, "%s/%s", svc->namespace->name, svc->name) < 0) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	act_size = str_size(fqn);
+	if (act_size > size) {
+		free(fqn);
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, fqn,
+	    min(size, act_size));
+	free(fqn);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+/** Connect client to the service.
+ *
+ * Find server supplying requested service and forward
+ * the message to it.
+ *
+ */
+static void loc_forward(ipc_callid_t callid, ipc_call_t *call)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	
+	/*
+	 * Get ID from request
+	 */
+	service_id_t id = IPC_GET_ARG2(*call);
+	loc_service_t *svc = loc_service_find_id(id);
+	
+	if ((svc == NULL) || (svc->server == NULL) || (!svc->server->sess)) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	async_exch_t *exch = async_exchange_begin(svc->server->sess);
+	
+	if (svc->forward_interface == 0)
+		async_forward_fast(callid, exch, svc->id, 0, 0, IPC_FF_NONE);
+	else
+		async_forward_fast(callid, exch, svc->forward_interface,
+		    svc->id, 0, IPC_FF_NONE);
+	
+	async_exchange_end(exch);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+/** Find ID for service identified by name.
+ *
+ * In answer will be send EOK and service ID in arg1 or a error
+ * code from errno.h.
+ *
+ */
+static void loc_service_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *fqsn;
+	
+	/* Get fqsn */
+	int rc = async_data_write_accept((void **) &fqsn, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	char *ns_name;
+	char *name;
+	if (!loc_fqsn_split(fqsn, &ns_name, &name)) {
+		free(fqsn);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	free(fqsn);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	const loc_service_t *svc;
+	
+recheck:
+	
+	/*
+	 * Find service name in the list of known services.
+	 */
+	svc = loc_service_find_name(ns_name, name);
+	
+	/*
+	 * Device was not found.
+	 */
+	if (svc == NULL) {
+		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
+			/* Blocking lookup */
+			fibril_condvar_wait(&services_list_cv,
+			    &services_list_mutex);
+			goto recheck;
+		}
+		
+		async_answer_0(iid, ENOENT);
+		free(ns_name);
+		free(name);
+		fibril_mutex_unlock(&services_list_mutex);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, svc->id);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	free(ns_name);
+	free(name);
+}
+
+/** Find ID for namespace identified by name.
+ *
+ * In answer will be send EOK and service ID in arg1 or a error
+ * code from errno.h.
+ *
+ */
+static void loc_namespace_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *name;
+	
+	/* Get service name */
+	int rc = async_data_write_accept((void **) &name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	const loc_namespace_t *namespace;
+	
+recheck:
+	
+	/*
+	 * Find namespace name in the list of known namespaces.
+	 */
+	namespace = loc_namespace_find_name(name);
+	
+	/*
+	 * Namespace was not found.
+	 */
+	if (namespace == NULL) {
+		if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
+			/* Blocking lookup */
+			fibril_condvar_wait(&services_list_cv,
+			    &services_list_mutex);
+			goto recheck;
+		}
+		
+		async_answer_0(iid, ENOENT);
+		free(name);
+		fibril_mutex_unlock(&services_list_mutex);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, namespace->id);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	free(name);
+}
+
+/** Create callback connection.
+ *
+ * Create callback connection which will be used to send category change
+ * events.
+ *
+ * On success, answer will contain EOK int retval.
+ * On failure, error code will be sent in retval.
+ *
+ */
+static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
+{
+	cb_sess_t *cb_sess = calloc(1, sizeof(cb_sess_t));
+	if (cb_sess == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		free(cb_sess);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	cb_sess->sess = sess;
+	link_initialize(&cb_sess->cb_sess_list);
+	
+	fibril_mutex_lock(&callback_sess_mutex);
+	list_append(&cb_sess->cb_sess_list, &callback_sess_list);
+	fibril_mutex_unlock(&callback_sess_mutex);
+	
+	async_answer_0(iid, EOK);
+}
+
+void loc_category_change_event(void)
+{
+	fibril_mutex_lock(&callback_sess_mutex);
+	
+	list_foreach(callback_sess_list, link) {
+		cb_sess_t *cb_sess;
+		
+		cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
+		
+		async_exch_t *exch = async_exchange_begin(cb_sess->sess);
+		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
+		async_exchange_end(exch);
+	}
+	
+	fibril_mutex_unlock(&callback_sess_mutex);
+}
+
+/** Find ID for category specified by name.
+ *
+ * On success, answer will contain EOK int retval and service ID in arg1.
+ * On failure, error code will be sent in retval.
+ *
+ */
+static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *name;
+	category_t *cat;
+	
+	/* Get service name */
+	int rc = async_data_write_accept((void **) &name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+
+	cat = category_find_by_name(&cdir, name);
+	if (cat == NULL) {
+		/* Category not found */
+		async_answer_0(iid, ENOENT);
+		goto cleanup;
+	}
+	
+	async_answer_1(iid, EOK, cat->id);
+cleanup:
+	fibril_mutex_unlock(&cdir.mutex);
+	free(name);
+}
+
+static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace =
+	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
+	if (namespace == NULL) {
+		loc_service_t *svc =
+		    loc_service_find_id(IPC_GET_ARG1(*icall));
+		if (svc == NULL)
+			async_answer_1(iid, EOK, LOC_OBJECT_NONE);
+		else
+			async_answer_1(iid, EOK, LOC_OBJECT_SERVICE);
+	} else
+		async_answer_1(iid, EOK, LOC_OBJECT_NAMESPACE);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+static void loc_get_namespace_count(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	async_answer_1(iid, EOK, list_count(&namespaces_list));
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+static void loc_get_service_count(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace =
+	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
+	if (namespace == NULL)
+		async_answer_0(iid, EEXISTS);
+	else
+		async_answer_1(iid, EOK, namespace->refcnt);
+	
+	fibril_mutex_unlock(&services_list_mutex);
+}
+
+static void loc_get_categories(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	category_id_t *id_buf = (category_id_t *) malloc(size);
+	if (id_buf == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	rc = categ_dir_get_categories(&cdir, id_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
+	free(id_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+static void loc_get_namespaces(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	if ((size % sizeof(loc_sdesc_t)) != 0) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	size_t count = size / sizeof(loc_sdesc_t);
+	if (count != list_count(&namespaces_list)) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
+	if (desc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	size_t pos = 0;
+	list_foreach(namespaces_list, item) {
+		loc_namespace_t *namespace =
+		    list_get_instance(item, loc_namespace_t, namespaces);
+		
+		desc[pos].id = namespace->id;
+		str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
+		pos++;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, desc, size);
+	
+	free(desc);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+static void loc_get_services(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* FIXME: Use faster algorithm which can make better use
+	   of namespaces */
+	
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	if ((size % sizeof(loc_sdesc_t)) != 0) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace =
+	    loc_namespace_find_id(IPC_GET_ARG1(*icall));
+	if (namespace == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	size_t count = size / sizeof(loc_sdesc_t);
+	if (count != namespace->refcnt) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	loc_sdesc_t *desc = (loc_sdesc_t *) malloc(size);
+	if (desc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	size_t pos = 0;
+	list_foreach(services_list, item) {
+		loc_service_t *service =
+		    list_get_instance(item, loc_service_t, services);
+		
+		if (service->namespace == namespace) {
+			desc[pos].id = service->id;
+			str_cpy(desc[pos].name, LOC_NAME_MAXLEN, service->name);
+			pos++;
+		}
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, desc, size);
+	
+	free(desc);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
+}
+
+static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
+	if (cat == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	category_id_t *id_buf = (category_id_t *) malloc(size);
+	if (id_buf == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&cat->mutex);
+	
+	rc = category_get_services(cat, id_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&cat->mutex);
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_unlock(&cat->mutex);
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
+	free(id_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+
+static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fibril_mutex_lock(&null_services_mutex);
+	
+	unsigned int i;
+	bool fnd = false;
+	
+	for (i = 0; i < NULL_SERVICES; i++) {
+		if (null_services[i] == NULL) {
+			fnd = true;
+			break;
+		}
+	}
+	
+	if (!fnd) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	char null[LOC_NAME_MAXLEN];
+	snprintf(null, LOC_NAME_MAXLEN, "%u", i);
+	
+	char *dev_name = str_dup(null);
+	if (dev_name == NULL) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	loc_service_t *service =
+	    (loc_service_t *) malloc(sizeof(loc_service_t));
+	if (service == NULL) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	
+	loc_namespace_t *namespace = loc_namespace_create("null");
+	if (namespace == NULL) {
+		fibril_mutex_lock(&services_list_mutex);
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	link_initialize(&service->services);
+	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
+	
+	/* Get unique service ID */
+	service->id = loc_create_id();
+	service->server = NULL;
+	
+	loc_namespace_addref(namespace, service);
+	service->name = dev_name;
+	
+	/*
+	 * Insert service into list of all services and into null services array.
+	 * Insert service into a dummy list of null server's services so that it
+	 * can be safely removed later.
+	 */
+	list_append(&service->services, &services_list);
+	list_append(&service->server_services, &dummy_null_services);
+	null_services[i] = service;
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	fibril_mutex_unlock(&null_services_mutex);
+	
+	async_answer_1(iid, EOK, (sysarg_t) i);
+}
+
+static void loc_null_destroy(ipc_callid_t iid, ipc_call_t *icall)
+{
+	sysarg_t i = IPC_GET_ARG1(*icall);
+	if (i >= NULL_SERVICES) {
+		async_answer_0(iid, ELIMIT);
+		return;
+	}
+	
+	fibril_mutex_lock(&null_services_mutex);
+	
+	if (null_services[i] == NULL) {
+		fibril_mutex_unlock(&null_services_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	loc_service_unregister_core(null_services[i]);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	null_services[i] = NULL;
+	
+	fibril_mutex_unlock(&null_services_mutex);
+	async_answer_0(iid, EOK);
+}
+
+static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
+{
+	category_t *cat;
+	loc_service_t *svc;
+	catid_t cat_id;
+	service_id_t svc_id;
+	sysarg_t retval;
+	
+	svc_id = IPC_GET_ARG1(*icall);
+	cat_id = IPC_GET_ARG2(*icall);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	
+	cat = category_get(&cdir, cat_id);
+	svc = loc_service_find_id(svc_id);
+	
+	if (cat == NULL || svc == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&cat->mutex);
+	retval = category_add_service(cat, svc);
+
+	fibril_mutex_unlock(&cat->mutex);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+
+	async_answer_0(iid, retval);
+
+	loc_category_change_event();
+}
+
+
+/** Initialize location service.
+ *
+ *
+ */
+static bool loc_init(void)
+{
+	unsigned int i;
+	category_t *cat;
+
+	for (i = 0; i < NULL_SERVICES; i++)
+		null_services[i] = NULL;
+	
+	categ_dir_init(&cdir);
+
+	cat = category_new("bd");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("iplink");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("keyboard");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("mouse");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("serial");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("test3");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("usbhc");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virt-null");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virtual");
+	categ_dir_add_cat(&cdir, cat);
+	
+	cat = category_new("nic");
+	categ_dir_add_cat(&cdir, cat);
+	
+	return true;
+}
+
+/** Handle connection on supplier port.
+ *
+ */
+static void loc_connection_supplier(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* Accept connection */
+	async_answer_0(iid, EOK);
+	
+	loc_server_t *server = loc_server_register();
+	if (server == NULL)
+		return;
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOC_SERVER_UNREGISTER:
+			if (server == NULL)
+				async_answer_0(callid, ENOENT);
+			else
+				async_answer_0(callid, EOK);
+			break;
+		case LOC_SERVICE_ADD_TO_CAT:
+			/* Add service to category */
+			loc_service_add_to_cat(callid, &call);
+			break;
+		case LOC_SERVICE_REGISTER:
+			/* Register one service */
+			loc_service_register(callid, &call, server);
+			break;
+		case LOC_SERVICE_UNREGISTER:
+			/* Remove one service */
+			loc_service_unregister(callid, &call, server);
+			break;
+		case LOC_SERVICE_GET_ID:
+			loc_service_get_id(callid, &call);
+			break;
+		case LOC_NAMESPACE_GET_ID:
+			loc_namespace_get_id(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+	
+	if (server != NULL) {
+		/*
+		 * Unregister the server and all its services.
+		 */
+		loc_server_unregister(server);
+		server = NULL;
+	}
+}
+
+/** Handle connection on consumer port.
+ *
+ */
+static void loc_connection_consumer(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* Accept connection */
+	async_answer_0(iid, EOK);
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOC_SERVICE_GET_ID:
+			loc_service_get_id(callid, &call);
+			break;
+		case LOC_SERVICE_GET_NAME:
+			loc_service_get_name(callid, &call);
+			break;
+		case LOC_NAMESPACE_GET_ID:
+			loc_namespace_get_id(callid, &call);
+			break;
+		case LOC_CALLBACK_CREATE:
+			loc_callback_create(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_ID:
+			loc_category_get_id(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_NAME:
+			loc_category_get_name(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_SVCS:
+			loc_category_get_svcs(callid, &call);
+			break;
+		case LOC_ID_PROBE:
+			loc_id_probe(callid, &call);
+			break;
+		case LOC_NULL_CREATE:
+			loc_null_create(callid, &call);
+			break;
+		case LOC_NULL_DESTROY:
+			loc_null_destroy(callid, &call);
+			break;
+		case LOC_GET_NAMESPACE_COUNT:
+			loc_get_namespace_count(callid, &call);
+			break;
+		case LOC_GET_SERVICE_COUNT:
+			loc_get_service_count(callid, &call);
+			break;
+		case LOC_GET_CATEGORIES:
+			loc_get_categories(callid, &call);
+			break;
+		case LOC_GET_NAMESPACES:
+			loc_get_namespaces(callid, &call);
+			break;
+		case LOC_GET_SERVICES:
+			loc_get_services(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+}
+
+/** Function for handling connections to location service
+ *
+ */
+static void loc_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Select interface */
+	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
+	case LOC_PORT_SUPPLIER:
+		loc_connection_supplier(iid, icall);
+		break;
+	case LOC_PORT_CONSUMER:
+		loc_connection_consumer(iid, icall);
+		break;
+	case LOC_CONNECT_TO_SERVICE:
+		/* Connect client to selected service */
+		loc_forward(iid, icall);
+		break;
+	default:
+		/* No such interface */
+		async_answer_0(iid, ENOENT);
+	}
+}
+
+/**
+ *
+ */
+int main(int argc, char *argv[])
+{
+	printf("%s: HelenOS Location Service\n", NAME);
+	
+	if (!loc_init()) {
+		printf("%s: Error while initializing service\n", NAME);
+		return -1;
+	}
+	
+	/* Set a handler of incomming connections */
+	async_set_client_connection(loc_connection);
+	
+	/* Register location service at naming service */
+	if (service_register(SERVICE_LOC) != EOK)
+		return -1;
+	
+	printf("%s: Accepting connections\n", NAME);
+	async_manager();
+	
+	/* Never reached */
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/locsrv/locsrv.h
===================================================================
--- uspace/srv/locsrv/locsrv.h	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
+++ uspace/srv/locsrv/locsrv.h	(revision 32d96e1cd86f98d2d5d5a97f1dbf864765de311d)
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup loc
+ * @{
+ */
+/** @file HelenOS location service.
+ */
+
+#ifndef LOCSRV_H_
+#define LOCSRV_H_
+
+#include <ipc/loc.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <sys/types.h>
+
+/** Representation of server (supplier).
+ *
+ * Each server supplies a set of services.
+ *
+ */
+typedef struct {
+	/** Link to servers_list */
+	link_t servers;
+	
+	/** List of services supplied by this server */
+	list_t services;
+	
+	/** Session asociated with this server */
+	async_sess_t *sess;
+	
+	/** Server name */
+	char *name;
+	
+	/** Fibril mutex for list of services owned by this server */
+	fibril_mutex_t services_mutex;
+} loc_server_t;
+
+/** Info about registered namespaces
+ *
+ */
+typedef struct {
+	/** Link to namespaces_list */
+	link_t namespaces;
+	
+	/** Unique namespace identifier */
+	service_id_t id;
+	
+	/** Namespace name */
+	char *name;
+	
+	/** Reference count */
+	size_t refcnt;
+} loc_namespace_t;
+
+/** Info about registered service
+ *
+ */
+typedef struct {
+	/** Link to global list of services (services_list) */
+	link_t services;
+	/** Link to server list of services (loc_server_t.services) */
+	link_t server_services;
+	/** Link to list of services in category (category_t.services) */
+	link_t cat_services;
+	/** List of category memberships (svc_categ_t) */
+	list_t cat_memb;
+	/** Unique service identifier */
+	service_id_t id;
+	/** Service namespace */
+	loc_namespace_t *namespace;
+	/** Service name */
+	char *name;
+	/** Supplier of this service */
+	loc_server_t *server;
+	/** Use this interface when forwarding to server. */
+	sysarg_t forward_interface;
+} loc_service_t;
+
+extern fibril_mutex_t services_list_mutex;
+
+extern service_id_t loc_create_id(void);
+extern void loc_category_change_event(void);
+
+#endif
+
+/** @}
+ */
