Index: Makefile
===================================================================
--- Makefile	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ Makefile	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -54,5 +54,5 @@
 
 cscope:
-	find kernel boot uspace -regex '^.*\.[chsS]$$' | xargs $(CSCOPE) -b -k -u -f$(CSCOPE).out
+	find abi kernel boot uspace -regex '^.*\.[chsS]$$' | xargs $(CSCOPE) -b -k -u -f$(CSCOPE).out
 
 # Pre-integration build check
Index: abi/include/ipc/event.h
===================================================================
--- abi/include/ipc/event.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ abi/include/ipc/event.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -46,4 +46,10 @@
 } event_type_t;
 
+/** Per-task events. */
+typedef enum event_task_type {
+	EVENT_TASK_STATE_CHANGE = EVENT_END,
+	EVENT_TASK_END
+} event_task_type_t;
+
 #endif
 
Index: abi/include/ipc/methods.h
===================================================================
--- abi/include/ipc/methods.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ abi/include/ipc/methods.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -171,4 +171,16 @@
 #define IPC_M_DATA_READ  8
 
+/** Authorize change of recipient's state in a third party task.
+ * - ARG1 - user protocol defined data
+ * - ARG2 - user protocol defined data
+ * - ARG3 - user protocol defined data
+ * - ARG5 - sender's phone to the third party task
+ *
+ * on EOK answer, the recipient must set:
+ *
+ * - ARG1 - recipient's phone to the third party task
+ */
+#define IPC_M_STATE_CHANGE_AUTHORIZE	9
+
 /** Debug the recipient.
  * - ARG1 - specifies the debug method (from udebug_method_t)
@@ -176,5 +188,5 @@
  *
  */
-#define IPC_M_DEBUG  9
+#define IPC_M_DEBUG 10
 
 /** Last system IPC method */
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ boot/Makefile.common	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -150,4 +150,5 @@
 	$(USPACE_PATH)/app/kill/kill \
 	$(USPACE_PATH)/app/killall/killall \
+	$(USPACE_PATH)/app/locinfo/locinfo \
 	$(USPACE_PATH)/app/mkfat/mkfat \
 	$(USPACE_PATH)/app/lsusb/lsusb \
Index: contrib/bazaar/bzreml/__init__.py
===================================================================
--- contrib/bazaar/bzreml/__init__.py	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ contrib/bazaar/bzreml/__init__.py	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -134,42 +134,10 @@
 				body.write("New Revision: %s%s\n" % (revision_ac_no, merge_marker(revision_ac)))
 				body.write("New Id: %s\n" % revision_ac_id)
-				
-				body.write("\n")
-				
 				for parent_id in revision_ac.parent_ids:
 					body.write("Parent: %s\n" % parent_id)
-					
-					tree_old = branch.repository.revision_tree(parent_id)
-					tree_ac = branch.repository.revision_tree(revision_ac_id)
-					
-					delta = tree_ac.changes_from(tree_old)
-					
-					if (len(delta.added) > 0):
-						body.write("Added:\n")
-						for item in delta.added:
-							body.write("    %s\n" % item[0])
-					
-					if (len(delta.removed) > 0):
-						body.write("Removed:\n")
-						for item in delta.removed:
-							body.write("    %s\n" % item[0])
-					
-					if (len(delta.renamed) > 0):
-						body.write("Renamed:\n")
-						for item in delta.renamed:
-							body.write("    %s -> %s\n" % (item[0], item[1]))
-					
-					if (len(delta.kind_changed) > 0):
-						body.write("Changed:\n")
-						for item in delta.kind_changed:
-							body.write("    %s\n" % item[0])
-						
-					if (len(delta.modified) > 0):
-						body.write("Modified:\n")
-						for item in delta.modified:
-							body.write("    %s\n" % item[0])
-					
-					body.write("\n")
-				
+				
+				body.write("\n")
+				
+				commit_message = ""
 				body.write("Log:\n")
 				if (not revision_ac.message):
@@ -179,4 +147,9 @@
 					for line in log.split("\n"):
 						body.write("%s\n" % line)
+						if (commit_message == ""):
+							commit_message = line
+				
+				if (commit_message == ""):
+					commit_message = "(empty)"
 				
 				body.write("\n")
@@ -184,4 +157,34 @@
 			tree_old = branch.repository.revision_tree(revision_old_id)
 			tree_new = branch.repository.revision_tree(revision_new_id)
+			
+			revision_new_no = branch.revision_id_to_revno(revision_new_id)
+			delta = tree_new.changes_from(tree_old)
+			
+			if (len(delta.added) > 0):
+				body.write("Added:\n")
+				for item in delta.added:
+					body.write("    %s\n" % item[0])
+			
+			if (len(delta.removed) > 0):
+				body.write("Removed:\n")
+				for item in delta.removed:
+					body.write("    %s\n" % item[0])
+			
+			if (len(delta.renamed) > 0):
+				body.write("Renamed:\n")
+				for item in delta.renamed:
+					body.write("    %s -> %s\n" % (item[0], item[1]))
+			
+			if (len(delta.kind_changed) > 0):
+				body.write("Changed:\n")
+				for item in delta.kind_changed:
+					body.write("    %s\n" % item[0])
+			
+			if (len(delta.modified) > 0):
+				body.write("Modified:\n")
+				for item in delta.modified:
+					body.write("    %s\n" % item[0])
+			
+			body.write("\n")
 			
 			tree_old.lock_read()
@@ -196,25 +199,5 @@
 				tree_old.unlock()
 			
-			revision_new_no = branch.revision_id_to_revno(revision_new_id)
-			
-			delta = tree_new.changes_from(tree_old)
-			files = []
-			
-			for item in delta.added:
-				files.append(item[0])
-			
-			for item in delta.removed:
-				files.append(item[0])
-			
-			for item in delta.renamed:
-				files.append(item[0])
-			
-			for item in delta.kind_changed:
-				files.append(item[0])
-			
-			for item in delta.modified:
-				files.append(item[0])
-			
-			subject = "r%d - %s" % (revision_new_no, " ".join(files))
+			subject = "r%d - %s" % (revision_new_no, commit_message)
 			
 			send_smtp("localhost", config_sender(config), config_to(config), subject, body.getvalue())
Index: contrib/bazaar/bzreml/setup.py
===================================================================
--- contrib/bazaar/bzreml/setup.py	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ contrib/bazaar/bzreml/setup.py	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -7,5 +7,5 @@
 	description = 'Commit email plugin for Bazaar',
 	keywords = 'plugin bzr email',
-	version = '1.0',
+	version = '1.2',
 	url = 'http://www.decky.cz/',
 	license = 'BSD',
Index: kernel/generic/include/console/console.h
===================================================================
--- kernel/generic/include/console/console.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/include/console/console.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -63,5 +63,5 @@
 
 extern void klog_init(void);
-extern void klog_update(void);
+extern void klog_update(void *);
 
 extern wchar_t getc(indev_t *indev);
Index: kernel/generic/include/ipc/event.h
===================================================================
--- kernel/generic/include/ipc/event.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/include/ipc/event.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -41,5 +41,7 @@
 #include <ipc/ipc.h>
 
-typedef void (*event_callback_t)(void);
+struct task;
+
+typedef void (*event_callback_t)(void *);
 
 /** Event notification structure. */
@@ -61,6 +63,9 @@
 
 extern void event_init(void);
+extern void event_task_init(struct task *);
 extern void event_cleanup_answerbox(answerbox_t *);
 extern void event_set_unmask_callback(event_type_t, event_callback_t);
+extern void event_task_set_unmask_callback(struct task *, event_task_type_t,
+    event_callback_t);
 
 #define event_notify_0(e, m) \
@@ -77,6 +82,21 @@
 	event_notify((e), (m), (a1), (a2), (a3), (a4), (a5))
 
+#define event_task_notify_0(t, e, m) \
+	event_task_notify((t), (e), (m), 0, 0, 0, 0, 0)
+#define event_task_notify_1(t, e, m, a1) \
+	event_task_notify((t), (e), (m), (a1), 0, 0, 0, 0)
+#define event_task_notify_2(t, e, m, a1, a2) \
+	event_task_notify((t), (e), (m), (a1), (a2), 0, 0, 0)
+#define event_task_notify_3(t, e, m, a1, a2, a3) \
+	event_task_notify((t), (e), (m), (a1), (a2), (a3), 0, 0)
+#define event_task_notify_4(t, e, m, a1, a2, a3, a4) \
+	event_task_notify((t), (e), (m), (a1), (a2), (a3), (a4), 0)
+#define event_task_notify_5(t, e, m, a1, a2, a3, a4, a5) \
+	event_task_notify((t), (e), (m), (a1), (a2), (a3), (a4), (a5))
+
 extern int event_notify(event_type_t, bool, sysarg_t, sysarg_t, sysarg_t,
     sysarg_t, sysarg_t);
+extern int event_task_notify(struct task *, event_task_type_t, bool, sysarg_t, sysarg_t,
+    sysarg_t, sysarg_t, sysarg_t);
 
 extern sysarg_t sys_event_subscribe(sysarg_t, sysarg_t);
Index: kernel/generic/include/proc/task.h
===================================================================
--- kernel/generic/include/proc/task.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/include/proc/task.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -38,4 +38,6 @@
 #include <cpu.h>
 #include <ipc/ipc.h>
+#include <ipc/event.h>
+#include <ipc/kbox.h>
 #include <synch/spinlock.h>
 #include <synch/mutex.h>
@@ -53,5 +55,4 @@
 #include <proc/scheduler.h>
 #include <udebug/udebug.h>
-#include <ipc/kbox.h>
 #include <mm/as.h>
 #include <abi/sysinfo.h>
@@ -93,6 +94,6 @@
 	phone_t phones[IPC_MAX_PHONES];
 	stats_ipc_t ipc_info;   /**< IPC statistics */
-	/** List of synchronous answerboxes. */
-	list_t sync_boxes;
+	list_t sync_boxes;      /**< List of synchronous answerboxes. */
+	event_t events[EVENT_TASK_END - EVENT_END];
 	
 #ifdef CONFIG_UDEBUG
Index: kernel/generic/src/console/console.c
===================================================================
--- kernel/generic/src/console/console.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/src/console/console.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -248,5 +248,5 @@
 }
 
-void klog_update(void)
+void klog_update(void *e)
 {
 	if (!atomic_get(&klog_inited))
@@ -327,5 +327,5 @@
 	/* Force notification on newline */
 	if (ch == '\n')
-		klog_update();
+		klog_update(NULL);
 }
 
@@ -358,5 +358,5 @@
 		free(data);
 	} else
-		klog_update();
+		klog_update(NULL);
 	
 	return size;
Index: kernel/generic/src/ipc/event.c
===================================================================
--- kernel/generic/src/ipc/event.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/src/ipc/event.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -40,4 +40,5 @@
 #include <synch/spinlock.h>
 #include <console/console.h>
+#include <proc/task.h>
 #include <memstr.h>
 #include <errno.h>
@@ -47,4 +48,28 @@
 static event_t events[EVENT_END];
 
+static void event_initialize(event_t *event)
+{
+	spinlock_initialize(&event->lock, "event.lock");
+	event->answerbox = NULL;
+	event->counter = 0;
+	event->imethod = 0;
+	event->masked = false;
+	event->unmask_callback = NULL;
+}
+
+static event_t *evno2event(int evno, task_t *t)
+{
+	ASSERT(evno < EVENT_TASK_END);
+
+	event_t *event;
+
+	if (evno < EVENT_END)
+		event = &events[(event_type_t) evno];
+	else
+		event = &t->events[(event_task_type_t) evno - EVENT_END];
+
+	return event;
+}
+
 /** Initialize kernel events.
  *
@@ -52,13 +77,14 @@
 void event_init(void)
 {
-	for (unsigned int i = 0; i < EVENT_END; i++) {
-		spinlock_initialize(&events[i].lock, "event.lock");
-		events[i].answerbox = NULL;
-		events[i].counter = 0;
-		events[i].imethod = 0;
-		events[i].masked = false;
-		events[i].unmask_callback = NULL;
-	}
-}
+	for (unsigned int i = 0; i < EVENT_END; i++)
+		event_initialize(evno2event(i, NULL));
+}
+
+void event_task_init(task_t *t)
+{
+	for (unsigned int i = EVENT_END; i < EVENT_TASK_END; i++)
+		event_initialize(evno2event(i, t));
+}
+
 
 /** Unsubscribe kernel events associated with an answerbox
@@ -83,4 +109,11 @@
 }
 
+static void _event_set_unmask_callback(event_t *event, event_callback_t callback)
+{
+	spinlock_lock(&event->lock);
+	event->unmask_callback = callback;
+	spinlock_unlock(&event->lock);
+}
+
 /** Define a callback function for the event unmask event.
  *
@@ -94,7 +127,57 @@
 	ASSERT(evno < EVENT_END);
 	
-	spinlock_lock(&events[evno].lock);
-	events[evno].unmask_callback = callback;
-	spinlock_unlock(&events[evno].lock);
+	_event_set_unmask_callback(evno2event(evno, NULL), callback);
+}
+
+void event_task_set_unmask_callback(task_t *t, event_task_type_t evno,
+    event_callback_t callback)
+{
+	ASSERT(evno >= (int) EVENT_END);
+	ASSERT(evno < EVENT_TASK_END);
+		
+	_event_set_unmask_callback(evno2event(evno, t), callback);
+}
+
+static int event_enqueue(event_t *event, bool mask, sysarg_t a1, sysarg_t a2,
+    sysarg_t a3, sysarg_t a4, sysarg_t a5)
+{
+	int res;
+
+	spinlock_lock(&event->lock);
+	
+	if (event->answerbox != NULL) {
+		if (!event->masked) {
+			call_t *call = ipc_call_alloc(FRAME_ATOMIC);
+			
+			if (call) {
+				call->flags |= IPC_CALL_NOTIF;
+				call->priv = ++event->counter;
+				
+				IPC_SET_IMETHOD(call->data, event->imethod);
+				IPC_SET_ARG1(call->data, a1);
+				IPC_SET_ARG2(call->data, a2);
+				IPC_SET_ARG3(call->data, a3);
+				IPC_SET_ARG4(call->data, a4);
+				IPC_SET_ARG5(call->data, a5);
+				
+				irq_spinlock_lock(&event->answerbox->irq_lock, true);
+				list_append(&call->link, &event->answerbox->irq_notifs);
+				irq_spinlock_unlock(&event->answerbox->irq_lock, true);
+				
+				waitq_wakeup(&event->answerbox->wq, WAKEUP_FIRST);
+				
+				if (mask)
+					event->masked = true;
+				
+				res = EOK;
+			} else
+				res = ENOMEM;
+		} else
+			res = EBUSY;
+	} else
+		res = ENOENT;
+	
+	spinlock_unlock(&event->lock);
+	return res;
 }
 
@@ -123,43 +206,34 @@
 	ASSERT(evno < EVENT_END);
 	
-	spinlock_lock(&events[evno].lock);
-	
-	int ret;
-	
-	if (events[evno].answerbox != NULL) {
-		if (!events[evno].masked) {
-			call_t *call = ipc_call_alloc(FRAME_ATOMIC);
-			
-			if (call) {
-				call->flags |= IPC_CALL_NOTIF;
-				call->priv = ++events[evno].counter;
-				
-				IPC_SET_IMETHOD(call->data, events[evno].imethod);
-				IPC_SET_ARG1(call->data, a1);
-				IPC_SET_ARG2(call->data, a2);
-				IPC_SET_ARG3(call->data, a3);
-				IPC_SET_ARG4(call->data, a4);
-				IPC_SET_ARG5(call->data, a5);
-				
-				irq_spinlock_lock(&events[evno].answerbox->irq_lock, true);
-				list_append(&call->link, &events[evno].answerbox->irq_notifs);
-				irq_spinlock_unlock(&events[evno].answerbox->irq_lock, true);
-				
-				waitq_wakeup(&events[evno].answerbox->wq, WAKEUP_FIRST);
-				
-				if (mask)
-					events[evno].masked = true;
-				
-				ret = EOK;
-			} else
-				ret = ENOMEM;
-		} else
-			ret = EBUSY;
-	} else
-		ret = ENOENT;
-	
-	spinlock_unlock(&events[evno].lock);
-	
-	return ret;
+	return event_enqueue(evno2event(evno, NULL), mask, a1, a2, a3, a4, a5);
+}
+
+/** Send per-task kernel notification event
+ *
+ * @param t    Destination task.
+ * @param evno Event type.
+ * @param mask Mask further notifications after a successful
+ *             sending.
+ * @param a1   First argument.
+ * @param a2   Second argument.
+ * @param a3   Third argument.
+ * @param a4   Fourth argument.
+ * @param a5   Fifth argument.
+ *
+ * @return EOK if notification was successfully sent.
+ * @return ENOMEM if the notification IPC message failed to allocate.
+ * @return EBUSY if the notifications of the given type are
+ *         currently masked.
+ * @return ENOENT if the notifications of the given type are
+ *         currently not subscribed.
+ *
+ */
+int event_task_notify(task_t *t, event_task_type_t evno, bool mask, sysarg_t a1,
+    sysarg_t a2, sysarg_t a3, sysarg_t a4, sysarg_t a5)
+{
+	ASSERT(evno >= (int) EVENT_END);
+	ASSERT(evno < EVENT_TASK_END);
+	
+	return event_enqueue(evno2event(evno, t), mask, a1, a2, a3, a4, a5);
 }
 
@@ -176,23 +250,21 @@
  *
  */
-static int event_subscribe(event_type_t evno, sysarg_t imethod,
+static int event_subscribe(event_t *event, sysarg_t imethod,
     answerbox_t *answerbox)
 {
-	ASSERT(evno < EVENT_END);
-	
-	spinlock_lock(&events[evno].lock);
-	
 	int res;
-	
-	if (events[evno].answerbox == NULL) {
-		events[evno].answerbox = answerbox;
-		events[evno].imethod = imethod;
-		events[evno].counter = 0;
-		events[evno].masked = false;
+
+	spinlock_lock(&event->lock);
+	
+	if (event->answerbox == NULL) {
+		event->answerbox = answerbox;
+		event->imethod = imethod;
+		event->counter = 0;
+		event->masked = false;
 		res = EOK;
 	} else
 		res = EEXISTS;
 	
-	spinlock_unlock(&events[evno].lock);
+	spinlock_unlock(&event->lock);
 	
 	return res;
@@ -204,12 +276,10 @@
  *
  */
-static void event_unmask(event_type_t evno)
-{
-	ASSERT(evno < EVENT_END);
-	
-	spinlock_lock(&events[evno].lock);
-	events[evno].masked = false;
-	event_callback_t callback = events[evno].unmask_callback;
-	spinlock_unlock(&events[evno].lock);
+static void event_unmask(event_t *event)
+{
+	spinlock_lock(&event->lock);
+	event->masked = false;
+	event_callback_t callback = event->unmask_callback;
+	spinlock_unlock(&event->lock);
 	
 	/*
@@ -218,5 +288,5 @@
 	 */
 	if (callback != NULL)
-		callback();
+		callback(event);
 }
 
@@ -235,9 +305,9 @@
 sysarg_t sys_event_subscribe(sysarg_t evno, sysarg_t imethod)
 {
-	if (evno >= EVENT_END)
+	if (evno >= EVENT_TASK_END)
 		return ELIMIT;
 	
-	return (sysarg_t) event_subscribe((event_type_t) evno, (sysarg_t)
-	    imethod, &TASK->answerbox);
+	return (sysarg_t) event_subscribe(evno2event(evno, TASK),
+	    (sysarg_t) imethod, &TASK->answerbox);
 }
 
@@ -257,8 +327,9 @@
 sysarg_t sys_event_unmask(sysarg_t evno)
 {
-	if (evno >= EVENT_END)
+	if (evno >= EVENT_TASK_END)
 		return ELIMIT;
 	
-	event_unmask((event_type_t) evno);
+	event_unmask(evno2event(evno, TASK));
+
 	return EOK;
 }
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/src/ipc/sysipc.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -44,4 +44,5 @@
 #include <ipc/irq.h>
 #include <ipc/ipcrsc.h>
+#include <ipc/event.h>
 #include <ipc/kbox.h>
 #include <synch/waitq.h>
@@ -134,4 +135,5 @@
 	case IPC_M_DATA_WRITE:
 	case IPC_M_DATA_READ:
+	case IPC_M_STATE_CHANGE_AUTHORIZE:
 		return true;
 	default:
@@ -164,4 +166,5 @@
 	case IPC_M_DATA_WRITE:
 	case IPC_M_DATA_READ:
+	case IPC_M_STATE_CHANGE_AUTHORIZE:
 		return true;
 	default:
@@ -334,4 +337,50 @@
 		free(answer->buffer);
 		answer->buffer = NULL;
+	} else if (IPC_GET_IMETHOD(*olddata) == IPC_M_STATE_CHANGE_AUTHORIZE) {
+		if (!IPC_GET_RETVAL(answer->data)) {
+			/* The recipient authorized the change of state. */
+			phone_t *recipient_phone;
+			task_t *other_task_s;
+			task_t *other_task_r;
+			int rc;
+
+			rc = phone_get(IPC_GET_ARG1(answer->data),
+			    &recipient_phone);
+			if (rc != EOK) {
+				IPC_SET_RETVAL(answer->data, ENOENT);
+				return ENOENT;
+			}
+
+			mutex_lock(&recipient_phone->lock);
+			if (recipient_phone->state != IPC_PHONE_CONNECTED) {
+				mutex_unlock(&recipient_phone->lock);
+				IPC_SET_RETVAL(answer->data, EINVAL);
+				return EINVAL;
+			}
+
+			other_task_r = recipient_phone->callee->task;
+			other_task_s = (task_t *) IPC_GET_ARG5(*olddata);
+
+			/*
+			 * See if both the sender and the recipient meant the
+			 * same third party task.
+			 */
+			if (other_task_r != other_task_s) {
+				IPC_SET_RETVAL(answer->data, EINVAL);
+				rc = EINVAL;
+			} else {
+				rc = event_task_notify_5(other_task_r,
+				    EVENT_TASK_STATE_CHANGE, false,
+				    IPC_GET_ARG1(*olddata),
+				    IPC_GET_ARG2(*olddata),
+				    IPC_GET_ARG3(*olddata),
+				    (sysarg_t) olddata->task,
+				    (sysarg_t) TASK);
+				IPC_SET_RETVAL(answer->data, rc);
+			}
+
+			mutex_unlock(&recipient_phone->lock);
+			return rc;
+		}
 	}
 	
@@ -456,4 +505,25 @@
 		}
 		
+		break;
+	}
+	case IPC_M_STATE_CHANGE_AUTHORIZE: {
+		phone_t *sender_phone;
+		task_t *other_task_s;
+
+		if (phone_get(IPC_GET_ARG5(call->data), &sender_phone) != EOK)
+			return ENOENT;
+
+		mutex_lock(&sender_phone->lock);
+		if (sender_phone->state != IPC_PHONE_CONNECTED) {
+			mutex_unlock(&sender_phone->lock);
+			return EINVAL;
+		}
+
+		other_task_s = sender_phone->callee->task;
+
+		mutex_unlock(&sender_phone->lock);
+
+		/* Remember the third party task hash. */
+		IPC_SET_ARG5(call->data, (sysarg_t) other_task_s);
 		break;
 	}
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ kernel/generic/src/proc/task.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -50,4 +50,5 @@
 #include <ipc/ipc.h>
 #include <ipc/ipcrsc.h>
+#include <ipc/event.h>
 #include <print.h>
 #include <errno.h>
@@ -57,5 +58,4 @@
 #include <syscall/copy.h>
 #include <macros.h>
-#include <ipc/event.h>
 
 /** Spinlock protecting the tasks_tree AVL tree. */
@@ -201,4 +201,6 @@
 	task->ipc_info.irq_notif_received = 0;
 	task->ipc_info.forwarded = 0;
+
+	event_task_init(task);
 	
 #ifdef CONFIG_UDEBUG
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/Makefile	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -44,4 +44,5 @@
 	app/killall \
 	app/klog \
+	app/locinfo \
 	app/lsusb \
 	app/mkfat \
Index: uspace/app/bdsh/exec.c
===================================================================
--- uspace/app/bdsh/exec.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/bdsh/exec.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -40,4 +40,5 @@
 #include <str_error.h>
 #include <errno.h>
+#include <vfs/vfs.h>
 
 #include "config.h"
@@ -99,6 +100,6 @@
 	char *tmp;
 	int rc, retval, i;
-	fdi_node_t file_nodes[3];
-	fdi_node_t *file_nodes_p[4];
+	int file_handles[3];
+	int *file_handles_p[4];
 	FILE *files[3];
 
@@ -111,14 +112,14 @@
 	
 	for (i = 0; i < 3 && files[i] != NULL; i++) {
-		if (fnode(files[i], &file_nodes[i]) == EOK) {
-			file_nodes_p[i] = &file_nodes[i];
+		if (fhandle(files[i], &file_handles[i]) == EOK) {
+			file_handles_p[i] = &file_handles[i];
 		}
 		else {
-			file_nodes_p[i] = NULL;
+			file_handles_p[i] = NULL;
 		}
 	}
-	file_nodes_p[i] = NULL;
+	file_handles_p[i] = NULL;
 
-	rc = task_spawnvf(&tid, tmp, (const char **) argv, file_nodes_p);
+	rc = task_spawnvf(&tid, tmp, (const char **) argv, file_handles_p);
 	free(tmp);
 
Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/init/init.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -66,4 +66,5 @@
 #define APP_GETTERM  "/app/getterm"
 
+/** Print banner */
 static void info_print(void)
 {
@@ -71,4 +72,5 @@
 }
 
+/** Report mount operation success */
 static bool mount_report(const char *desc, const char *mntpt,
     const char *fstype, const char *dev, int rc)
@@ -100,4 +102,15 @@
 }
 
+/** Mount root filesystem
+ *
+ * The operation blocks until the root filesystem
+ * server is ready for mounting.
+ *
+ * @param[in] fstype Root filesystem type.
+ *
+ * @return True on success.
+ * @return False on failure.
+ *
+ */
 static bool mount_root(const char *fstype)
 {
@@ -113,4 +126,13 @@
 }
 
+/** Mount locfs filesystem
+ *
+ * The operation blocks until the locfs filesystem
+ * server is ready for mounting.
+ *
+ * @return True on success.
+ * @return False on failure.
+ *
+ */
 static bool mount_locfs(void)
 {
@@ -157,5 +179,5 @@
 	rc = task_wait(id, &texit, &retval);
 	if (rc != EOK) {
-		printf("%s: Error waiting for %s (%s(\n", NAME, fname,
+		printf("%s: Error waiting for %s (%s)\n", NAME, fname,
 		    str_error(rc));
 		return;
Index: uspace/app/locinfo/Makefile
===================================================================
--- uspace/app/locinfo/Makefile	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
+++ uspace/app/locinfo/Makefile	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -0,0 +1,36 @@
+#
+# 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: uspace/app/locinfo/locinfo.c
===================================================================
--- uspace/app/locinfo/locinfo.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
+++ uspace/app/locinfo/locinfo.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -0,0 +1,102 @@
+/*
+ * 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_ids);
+		free(cat_name);
+	}
+
+	free(cat_ids);
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/lsusb/main.c
===================================================================
--- uspace/app/lsusb/main.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/lsusb/main.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -50,15 +50,12 @@
 
 #define MAX_USB_ADDRESS USB11_ADDRESS_MAX
-#define MAX_FAILED_ATTEMPTS 10
 #define MAX_PATH_LENGTH 1024
 
-static void print_found_hc(size_t class_index, const char *path)
+static void print_found_hc(service_id_t sid, const char *path)
 {
-	// printf(NAME ": host controller %zu is `%s'.\n", class_index, path);
-	printf("Bus %02zu: %s\n", class_index, path);
+	printf("Bus %" PRIun ": %s\n", sid, path);
 }
 static void print_found_dev(usb_address_t addr, const char *path)
 {
-	// printf(NAME ":     device with address %d is `%s'.\n", addr, path);
 	printf("  Device %02d: %s\n", addr, path);
 }
@@ -95,13 +92,29 @@
 int main(int argc, char *argv[])
 {
-	size_t class_index = 0;
-	size_t failed_attempts = 0;
+	category_id_t usbhc_cat;
+	service_id_t *svcs;
+	size_t count;
+	size_t i;
+	int rc;
 
-	while (failed_attempts < MAX_FAILED_ATTEMPTS) {
-		class_index++;
+	rc = loc_category_get_id(USB_HC_CATEGORY, &usbhc_cat, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving category '%s'",
+		    USB_HC_CATEGORY);
+		return 1;
+	}
+
+	rc = loc_category_get_svcs(usbhc_cat, &svcs, &count);
+	if (rc != EOK) {
+		printf(NAME ": Error getting list of host controllers.\n");
+		return 1;
+	}
+
+	for (i = 0; i < count; i++) {
 		devman_handle_t hc_handle = 0;
-		int rc = usb_ddf_get_hc_handle_by_class(class_index, &hc_handle);
+		int rc = usb_ddf_get_hc_handle_by_sid(svcs[i], &hc_handle);
 		if (rc != EOK) {
-			failed_attempts++;
+			printf(NAME ": Error resolving handle of HC with SID %"
+			    PRIun ", skipping.\n", svcs[i]);
 			continue;
 		}
@@ -109,9 +122,13 @@
 		rc = devman_get_device_path(hc_handle, path, MAX_PATH_LENGTH);
 		if (rc != EOK) {
+			printf(NAME ": Error resolving path of HC with SID %"
+			    PRIun ", skipping.\n", svcs[i]);
 			continue;
 		}
-		print_found_hc(class_index, path);
+		print_found_hc(svcs[i], path);
 		print_hc_devices(hc_handle);
 	}
+
+	free(svcs);
 
 	return 0;
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/tester/Makefile	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -56,6 +56,4 @@
 	mm/malloc3.c \
 	mm/mapping1.c \
-	devs/devman1.c \
-	devs/devman2.c \
 	hw/misc/virtchar1.c \
 	hw/serial/serial1.c \
Index: pace/app/tester/devs/devman1.c
===================================================================
--- uspace/app/tester/devs/devman1.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ 	(revision )
@@ -1,89 +1,0 @@
-/*
- * Copyright (c) 2011 Vojtech Horky
- * 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 tester
- * @brief Test devman service.
- * @{
- */
-/**
- * @file
- */
-
-#include <inttypes.h>
-#include <errno.h>
-#include <str_error.h>
-#include <sys/types.h>
-#include <async.h>
-#include <devman.h>
-#include <str.h>
-#include <vfs/vfs.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "../tester.h"
-
-#define DEVICE_PATH_NORMAL "/virt/null/a"
-#define DEVICE_CLASS "virt-null"
-#define DEVICE_CLASS_NAME "1"
-#define DEVICE_PATH_CLASSES DEVICE_CLASS "/" DEVICE_CLASS_NAME
-
-const char *test_devman1(void)
-{
-	devman_handle_t handle_primary;
-	devman_handle_t handle_class;
-	
-	int rc;
-	
-	TPRINTF("Asking for handle of `%s'...\n", DEVICE_PATH_NORMAL);
-	rc = devman_device_get_handle(DEVICE_PATH_NORMAL, &handle_primary, 0);
-	if (rc != EOK) {
-		TPRINTF(" ...failed: %s.\n", str_error(rc));
-		if (rc == ENOENT) {
-			TPRINTF("Have you compiled the test drivers?\n");
-		}
-		return "Failed getting device handle";
-	}
-
-	TPRINTF("Asking for handle of `%s' by class..\n", DEVICE_PATH_CLASSES);
-	rc = devman_device_get_handle_by_class(DEVICE_CLASS, DEVICE_CLASS_NAME,
-	    &handle_class, 0);
-	if (rc != EOK) {
-		TPRINTF(" ...failed: %s.\n", str_error(rc));
-		return "Failed getting device class handle";
-	}
-
-	TPRINTF("Received handles %" PRIun " and %" PRIun ".\n",
-	    handle_primary, handle_class);
-	if (handle_primary != handle_class) {
-		return "Retrieved different handles for the same device";
-	}
-
-	return NULL;
-}
-
-/** @}
- */
Index: pace/app/tester/devs/devman1.def
===================================================================
--- uspace/app/tester/devs/devman1.def	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ 	(revision )
@@ -1,6 +1,0 @@
-{
-	"devman1",
-	"devman test",
-	&test_devman1,
-	false
-},
Index: pace/app/tester/devs/devman2.c
===================================================================
--- uspace/app/tester/devs/devman2.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ 	(revision )
@@ -1,94 +1,0 @@
-/*
- * Copyright (c) 2011 Vojtech Horky
- * 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 tester
- * @brief Test devman service.
- * @{
- */
-/**
- * @file
- */
-
-#include <inttypes.h>
-#include <errno.h>
-#include <str_error.h>
-#include <sys/types.h>
-#include <async.h>
-#include <devman.h>
-#include <str.h>
-#include <async.h>
-#include <vfs/vfs.h>
-#include <vfs/vfs_sess.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "../tester.h"
-
-#define DEVICE_CLASS "test3"
-
-const char *test_devman2(void)
-{
-	size_t idx = 1;
-	int rc = EOK;
-	const char *err_msg = NULL;
-	char *path = NULL;
-	while (rc == EOK) {
-		rc = asprintf(&path, "/loc/class/%s\\%zu", DEVICE_CLASS, idx);
-		if (rc < 0) {
-			continue;
-		}
-		int fd = open(path, O_RDONLY);
-		if (fd < 0) {
-			TPRINTF("Failed opening `%s': %s.\n",
-			    path, str_error(fd));
-			rc = fd;
-			err_msg = "Failed opening file";
-			continue;
-		}
-		async_sess_t *sess = fd_session(EXCHANGE_SERIALIZE, fd);
-		close(fd);
-		if (sess == NULL) {
-			TPRINTF("Failed opening phone: %s.\n", str_error(errno));
-			rc = errno;
-			err_msg = "Failed opening file descriptor phone";
-			continue;
-		}
-		async_hangup(sess);
-		TPRINTF("Path `%s' okay.\n", path);
-		free(path);
-		idx++;
-		rc = EOK;
-	}
-	
-	if (path != NULL)
-		free(path);
-	
-	return err_msg;
-}
-
-/** @}
- */
Index: pace/app/tester/devs/devman2.def
===================================================================
--- uspace/app/tester/devs/devman2.def	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ 	(revision )
@@ -1,6 +1,0 @@
-{
-	"devman2",
-	"devman test",
-	&test_devman2,
-	false
-},
Index: uspace/app/tester/hw/misc/virtchar1.c
===================================================================
--- uspace/app/tester/hw/misc/virtchar1.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/tester/hw/misc/virtchar1.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -49,5 +49,4 @@
 
 #define DEVICE_PATH_NORMAL "/loc/devices/\\virt\\null\\a"
-#define DEVICE_PATH_CLASSES "/loc/class/virt-null\\1"
 #define BUFFER_SIZE 64
 
@@ -105,9 +104,4 @@
 	}
 
-	res = test_virtchar1_internal(DEVICE_PATH_CLASSES);
-	if (res != NULL) {
-		return res;
-	}
-
 	return NULL;
 }
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/tester/tester.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -68,6 +68,4 @@
 #include "hw/misc/virtchar1.def"
 #include "libext2/libext2_1.def"
-#include "devs/devman1.def"
-#include "devs/devman2.def"
 	{NULL, NULL, NULL, false}
 };
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/app/trace/trace.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -50,4 +50,5 @@
 #include <sys/types.h>
 #include <sys/typefmt.h>
+#include <vfs/vfs.h>
 
 #include <libc.h>
@@ -586,21 +587,21 @@
 
 	/* Send default files */
-	fdi_node_t *files[4];
-	fdi_node_t stdin_node;
-	fdi_node_t stdout_node;
-	fdi_node_t stderr_node;
-	
-	if ((stdin != NULL) && (fnode(stdin, &stdin_node) == EOK))
-		files[0] = &stdin_node;
+	int *files[4];
+	int fd_stdin;
+	int fd_stdout;
+	int fd_stderr;
+	
+	if ((stdin != NULL) && (fhandle(stdin, &fd_stdin) == EOK))
+		files[0] = &fd_stdin;
 	else
 		files[0] = NULL;
 	
-	if ((stdout != NULL) && (fnode(stdout, &stdout_node) == EOK))
-		files[1] = &stdout_node;
+	if ((stdout != NULL) && (fhandle(stdout, &fd_stdout) == EOK))
+		files[1] = &fd_stdout;
 	else
 		files[1] = NULL;
 	
-	if ((stderr != NULL) && (fnode(stderr, &stderr_node) == EOK))
-		files[2] = &stderr_node;
+	if ((stderr != NULL) && (fhandle(stderr, &fd_stderr) == EOK))
+		files[2] = &fd_stderr;
 	else
 		files[2] = NULL;
@@ -761,6 +762,4 @@
 	o = oper_new("open", 2, arg_def, V_INT_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_OPEN, o);
-	o = oper_new("open_node", 4, arg_def, V_INT_ERRNO, 0, resp_def);
-	proto_add_oper(p, VFS_IN_OPEN_NODE, o);
 	o = oper_new("read", 1, arg_def, V_ERRNO, 1, resp_def);
 	proto_add_oper(p, VFS_IN_READ, o);
Index: uspace/drv/bus/usb/ehci/main.c
===================================================================
--- uspace/drv/bus/usb/ehci/main.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/ehci/main.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -101,5 +101,5 @@
 	    "Failed to bind EHCI function: %s.\n",
 	    str_error(ret));
-	ret = ddf_fun_add_to_class(hc_fun, USB_HC_DDF_CLASS_NAME);
+	ret = ddf_fun_add_to_category(hc_fun, USB_HC_CATEGORY);
 	CHECK_RET_RETURN(ret,
 	    "Failed to add EHCI to HC class: %s.\n",
Index: uspace/drv/bus/usb/ohci/ohci.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/ohci/ohci.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -244,5 +244,5 @@
 	    "Failed to bind OHCI device function: %s.\n", str_error(ret));
 
-	ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
+	ret = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
 	CHECK_RET_FINI_RETURN(ret,
 	    "Failed to add OHCI to HC class: %s.\n", str_error(ret));
Index: uspace/drv/bus/usb/uhci/uhci.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/uhci/uhci.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -267,5 +267,5 @@
 	    str_error(ret));
 
-	ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
+	ret = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
 	CHECK_RET_FINI_RETURN(ret,
 	    "Failed to add UHCI to HC class: %s.\n", str_error(ret));
Index: uspace/drv/bus/usb/usbhid/kbd/kbddev.c
===================================================================
--- uspace/drv/bus/usb/usbhid/kbd/kbddev.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/usbhid/kbd/kbddev.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -102,5 +102,5 @@
 
 const char *HID_KBD_FUN_NAME = "keyboard";
-const char *HID_KBD_CLASS_NAME = "keyboard";
+const char *HID_KBD_CATEGORY_NAME = "keyboard";
 
 static void usb_kbd_set_led(usb_hid_dev_t *hid_dev, usb_kbd_t *kbd_dev);
@@ -551,10 +551,10 @@
 	    HID_KBD_FUN_NAME, fun->handle);
 	
-	usb_log_debug("Adding DDF function to class %s...\n", 
+	usb_log_debug("Adding DDF function to category %s...\n", 
 	    HID_KBD_CLASS_NAME);
-	rc = ddf_fun_add_to_class(fun, HID_KBD_CLASS_NAME);
+	rc = ddf_fun_add_to_category(fun, HID_KBD_CATEGORY_NAME);
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class %s: %s.\n",
+		    "Could not add DDF function to category %s: %s.\n",
 		    HID_KBD_CLASS_NAME, str_error(rc));
 		ddf_fun_destroy(fun);
Index: uspace/drv/bus/usb/usbhid/mouse/mousedev.c
===================================================================
--- uspace/drv/bus/usb/usbhid/mouse/mousedev.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/usbhid/mouse/mousedev.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -74,6 +74,6 @@
 const char *HID_MOUSE_FUN_NAME = "mouse";
 const char *HID_MOUSE_WHEEL_FUN_NAME = "mouse-wheel";
-const char *HID_MOUSE_CLASS_NAME = "mouse";
-const char *HID_MOUSE_WHEEL_CLASS_NAME = "keyboard";
+const char *HID_MOUSE_CATEGORY = "mouse";
+const char *HID_MOUSE_WHEEL_CATEGORY = "keyboard";
 
 /** Default idle rate for mouses. */
@@ -345,11 +345,11 @@
 	}
 	
-	usb_log_debug("Adding DDF function to class %s...\n", 
-	    HID_MOUSE_CLASS_NAME);
-	rc = ddf_fun_add_to_class(fun, HID_MOUSE_CLASS_NAME);
+	usb_log_debug("Adding DDF function to category %s...\n", 
+	    HID_MOUSE_CATEGORY);
+	rc = ddf_fun_add_to_category(fun, HID_MOUSE_CATEGORY);
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class %s: %s.\n",
-		    HID_MOUSE_CLASS_NAME, str_error(rc));
+		    "Could not add DDF function to category %s: %s.\n",
+		    HID_MOUSE_CATEGORY, str_error(rc));
 		ddf_fun_destroy(fun);
 		return rc;
@@ -383,11 +383,11 @@
 	}
 	
-	usb_log_debug("Adding DDF function to class %s...\n", 
-	    HID_MOUSE_WHEEL_CLASS_NAME);
-	rc = ddf_fun_add_to_class(fun, HID_MOUSE_WHEEL_CLASS_NAME);
+	usb_log_debug("Adding DDF function to category %s...\n", 
+	    HID_MOUSE_WHEEL_CATEGORY);
+	rc = ddf_fun_add_to_category(fun, HID_MOUSE_WHEEL_CATEGORY);
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class %s: %s.\n",
-		    HID_MOUSE_WHEEL_CLASS_NAME, str_error(rc));
+		    "Could not add DDF function to category %s: %s.\n",
+		    HID_MOUSE_WHEEL_CATEGORY, str_error(rc));
 		ddf_fun_destroy(fun);
 		return rc;
Index: uspace/drv/bus/usb/usbhid/mouse/mousedev.h
===================================================================
--- uspace/drv/bus/usb/usbhid/mouse/mousedev.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/usbhid/mouse/mousedev.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -59,5 +59,5 @@
 
 const char *HID_MOUSE_FUN_NAME;
-const char *HID_MOUSE_CLASS_NAME;
+const char *HID_MOUSE_CATEGORY;
 
 /*----------------------------------------------------------------------------*/
Index: uspace/drv/bus/usb/usbhid/multimedia/multimedia.c
===================================================================
--- uspace/drv/bus/usb/usbhid/multimedia/multimedia.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/usbhid/multimedia/multimedia.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -205,11 +205,11 @@
 	}
 	
-	usb_log_debug("%s function created (jandle: %" PRIun ").\n",
+	usb_log_debug("%s function created (handle: %" PRIun ").\n",
 	    NAME, fun->handle);
 	
-	rc = ddf_fun_add_to_class(fun, "keyboard");
+	rc = ddf_fun_add_to_category(fun, "keyboard");
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class 'keyboard': %s.\n",
+		    "Could not add DDF function to category 'keyboard': %s.\n",
 		    str_error(rc));
 		// TODO: Can / should I destroy the DDF function?
Index: uspace/drv/bus/usb/usbhub/usbhub.c
===================================================================
--- uspace/drv/bus/usb/usbhub/usbhub.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/usbhub/usbhub.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -131,5 +131,5 @@
 	opResult = ddf_fun_bind(hub_fun);
 	assert(opResult == EOK);
-	opResult = ddf_fun_add_to_class(hub_fun, "hub");
+	opResult = ddf_fun_add_to_category(hub_fun, "hub");
 	assert(opResult == EOK);
 
Index: uspace/drv/bus/usb/usbmouse/init.c
===================================================================
--- uspace/drv/bus/usb/usbmouse/init.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/usbmouse/init.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -116,5 +116,5 @@
 	
 	/* Add the function to mouse class. */
-	rc = ddf_fun_add_to_class(mouse->mouse_fun, "mouse");
+	rc = ddf_fun_add_to_category(mouse->mouse_fun, "mouse");
 	if (rc != EOK)
 		goto leave;
Index: uspace/drv/bus/usb/vhc/main.c
===================================================================
--- uspace/drv/bus/usb/vhc/main.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/bus/usb/vhc/main.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -104,5 +104,5 @@
 	}
 
-	rc = ddf_fun_add_to_class(hc, USB_HC_DDF_CLASS_NAME);
+	rc = ddf_fun_add_to_category(hc, USB_HC_CATEGORY);
 	if (rc != EOK) {
 		usb_log_fatal("Failed to add function to HC class: %s.\n",
Index: uspace/drv/char/ns8250/ns8250.c
===================================================================
--- uspace/drv/char/ns8250/ns8250.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/char/ns8250/ns8250.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -781,5 +781,5 @@
 	ns->fun = fun;
 	
-	ddf_fun_add_to_class(fun, "serial");
+	ddf_fun_add_to_category(fun, "serial");
 	
 	ddf_msg(LVL_NOTE, "Device %s successfully initialized.",
Index: uspace/drv/test/test1/test1.c
===================================================================
--- uspace/drv/test/test1/test1.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/test/test1/test1.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -143,9 +143,9 @@
 	}
 
-	ddf_fun_add_to_class(fun_a, "virtual");
+	ddf_fun_add_to_category(fun_a, "virtual");
 
 	if (str_cmp(dev->name, "null") == 0) {
 		fun_a->ops = &char_device_ops;
-		ddf_fun_add_to_class(fun_a, "virt-null");
+		ddf_fun_add_to_category(fun_a, "virt-null");
 	} else if (str_cmp(dev->name, "test1") == 0) {
 		(void) register_fun_verbose(dev,
Index: uspace/drv/test/test2/test2.c
===================================================================
--- uspace/drv/test/test2/test2.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/test/test2/test2.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -123,5 +123,5 @@
 	}
 
-	ddf_fun_add_to_class(fun_a, "virtual");
+	ddf_fun_add_to_category(fun_a, "virtual");
 
 	return EOK;
Index: uspace/drv/test/test3/test3.c
===================================================================
--- uspace/drv/test/test3/test3.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/drv/test/test3/test3.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -50,5 +50,5 @@
 };
 
-static int register_fun_and_add_to_class(ddf_dev_t *parent,
+static int register_fun_and_add_to_category(ddf_dev_t *parent,
      const char *base_name, size_t index, const char *class_name)
 {
@@ -77,5 +77,5 @@
 	}
 	
-	ddf_fun_add_to_class(fun, class_name);
+	ddf_fun_add_to_category(fun, class_name);
 
 	ddf_msg(LVL_NOTE, "Registered exposed function `%s'.", fun_name);
@@ -100,5 +100,5 @@
 	size_t i;
 	for (i = 0; i < 20; i++) {
-		rc = register_fun_and_add_to_class(dev,
+		rc = register_fun_and_add_to_category(dev,
 		    "test3_", i, "test3");
 		if (rc != EOK) {
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/async.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -201,10 +201,4 @@
 {
 	async_client_data_destroy = dtor;
-}
-
-void *async_get_client_data(void)
-{
-	assert(fibril_connection);
-	return fibril_connection->client->data;
 }
 
@@ -580,4 +574,85 @@
 }
 
+static client_t *async_client_get(sysarg_t client_hash, bool create)
+{
+	unsigned long key = client_hash;
+	client_t *client = NULL;
+
+	futex_down(&async_futex);
+	link_t *lnk = hash_table_find(&client_hash_table, &key);
+	if (lnk) {
+		client = hash_table_get_instance(lnk, client_t, link);
+		atomic_inc(&client->refcnt);
+	} else if (create) {
+		client = malloc(sizeof(client_t));
+		if (client) {
+			client->in_task_hash = client_hash;
+			client->data = async_client_data_create();
+		
+			atomic_set(&client->refcnt, 1);
+			hash_table_insert(&client_hash_table, &key, &client->link);
+		}
+	}
+
+	futex_up(&async_futex);
+	return client;
+}
+
+static void async_client_put(client_t *client)
+{
+	bool destroy;
+	unsigned long key = client->in_task_hash;
+	
+	futex_down(&async_futex);
+	
+	if (atomic_predec(&client->refcnt) == 0) {
+		hash_table_remove(&client_hash_table, &key, 1);
+		destroy = true;
+	} else
+		destroy = false;
+	
+	futex_up(&async_futex);
+	
+	if (destroy) {
+		if (client->data)
+			async_client_data_destroy(client->data);
+		
+		free(client);
+	}
+}
+
+void *async_get_client_data(void)
+{
+	assert(fibril_connection);
+	return fibril_connection->client->data;
+}
+
+void *async_get_client_data_by_hash(sysarg_t client_hash)
+{
+	client_t *client = async_client_get(client_hash, false);
+	if (!client)
+		return NULL;
+	if (!client->data) {
+		async_client_put(client);
+		return NULL;
+	}
+
+	return client->data;
+}
+
+void async_put_client_data_by_hash(sysarg_t client_hash)
+{
+	client_t *client = async_client_get(client_hash, false);
+
+	assert(client);
+	assert(client->data);
+
+	/* Drop the reference we got in async_get_client_data_by_hash(). */
+	async_client_put(client);
+
+	/* Drop our own reference we got at the beginning of this function. */
+	async_client_put(client);
+}
+
 /** Wrapper for client connection fibril.
  *
@@ -598,6 +673,4 @@
 	 */
 	fibril_connection = (connection_t *) arg;
-	
-	futex_down(&async_futex);
 	
 	/*
@@ -606,30 +679,11 @@
 	 * hash in a new tracking structure.
 	 */
-	
-	unsigned long key = fibril_connection->in_task_hash;
-	link_t *lnk = hash_table_find(&client_hash_table, &key);
-	
-	client_t *client;
-	
-	if (lnk) {
-		client = hash_table_get_instance(lnk, client_t, link);
-		atomic_inc(&client->refcnt);
-	} else {
-		client = malloc(sizeof(client_t));
-		if (!client) {
-			ipc_answer_0(fibril_connection->callid, ENOMEM);
-			futex_up(&async_futex);
-			return 0;
-		}
-		
-		client->in_task_hash = fibril_connection->in_task_hash;
-		client->data = async_client_data_create();
-		
-		atomic_set(&client->refcnt, 1);
-		hash_table_insert(&client_hash_table, &key, &client->link);
-	}
-	
-	futex_up(&async_futex);
-	
+
+	client_t *client = async_client_get(fibril_connection->in_task_hash, true);
+	if (!client) {
+		ipc_answer_0(fibril_connection->callid, ENOMEM);
+		return 0;
+	}
+
 	fibril_connection->client = client;
 	
@@ -643,22 +697,5 @@
 	 * Remove the reference for this client task connection.
 	 */
-	bool destroy;
-	
-	futex_down(&async_futex);
-	
-	if (atomic_predec(&client->refcnt) == 0) {
-		hash_table_remove(&client_hash_table, &key, 1);
-		destroy = true;
-	} else
-		destroy = false;
-	
-	futex_up(&async_futex);
-	
-	if (destroy) {
-		if (client->data)
-			async_client_data_destroy(client->data);
-		
-		free(client);
-	}
+	async_client_put(client);
 	
 	/*
@@ -666,5 +703,5 @@
 	 */
 	futex_down(&async_futex);
-	key = fibril_connection->in_phone_hash;
+	unsigned long key = fibril_connection->in_phone_hash;
 	hash_table_remove(&conn_hash_table, &key, 1);
 	futex_up(&async_futex);
@@ -2429,4 +2466,37 @@
 }
 
+int async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
+    sysarg_t arg3, async_exch_t *other_exch)
+{
+	return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
+	    arg1, arg2, arg3, 0, other_exch->phone);
+}
+
+bool async_state_change_receive(ipc_callid_t *callid, sysarg_t *arg1,
+    sysarg_t *arg2, sysarg_t *arg3)
+{
+	assert(callid);
+
+	ipc_call_t call;
+	*callid = async_get_call(&call);
+
+	if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
+		return false;
+	
+	if (arg1)
+		*arg1 = IPC_GET_ARG1(call);
+	if (arg2)
+		*arg2 = IPC_GET_ARG2(call);
+	if (arg3)
+		*arg3 = IPC_GET_ARG3(call);
+
+	return true;
+}
+
+int async_state_change_finalize(ipc_callid_t callid, async_exch_t *other_exch)
+{
+	return ipc_answer_1(callid, EOK, other_exch->phone);
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/devman.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -271,14 +271,14 @@
 }
 
-int devman_add_device_to_class(devman_handle_t devman_handle,
-    const char *class_name)
+int devman_add_device_to_category(devman_handle_t devman_handle,
+    const char *cat_name)
 {
 	async_exch_t *exch = devman_exchange_begin_blocking(DEVMAN_DRIVER);
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CLASS,
+	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CATEGORY,
 	    devman_handle, &answer);
-	sysarg_t retval = async_data_write_start(exch, class_name,
-	    str_size(class_name));
+	sysarg_t retval = async_data_write_start(exch, cat_name,
+	    str_size(cat_name));
 	
 	devman_exchange_end(exch);
@@ -308,4 +308,23 @@
 }
 
+/** Remove function from device.
+ *
+ * Request devman to remove function owned by this driver task.
+ * @param funh      Devman handle of the function
+ *
+ * @return EOK on success or negative error code.
+ */
+int devman_remove_function(devman_handle_t funh)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = devman_exchange_begin_blocking(DEVMAN_DRIVER);
+	retval = async_req_1_0(exch, DEVMAN_REMOVE_FUNCTION, (sysarg_t) funh);
+	devman_exchange_end(exch);
+	
+	return (int) retval;
+}
+
 async_sess_t *devman_parent_device_connect(exch_mgmt_t mgmt,
     devman_handle_t handle, unsigned int flags)
@@ -333,5 +352,5 @@
 		exch = devman_exchange_begin(DEVMAN_CLIENT);
 		if (exch == NULL)
-			return errno;
+			return ENOMEM;
 	}
 	
@@ -364,59 +383,9 @@
 }
 
-int devman_device_get_handle_by_class(const char *classname,
-    const char *devname, devman_handle_t *handle, unsigned int flags)
-{
-	async_exch_t *exch;
-	
-	if (flags & IPC_FLAG_BLOCKING)
-		exch = devman_exchange_begin_blocking(DEVMAN_CLIENT);
-	else {
-		exch = devman_exchange_begin(DEVMAN_CLIENT);
-		if (exch == NULL)
-			return errno;
-	}
-	
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_DEVICE_GET_HANDLE_BY_CLASS,
-	    flags, &answer);
-	sysarg_t retval = async_data_write_start(exch, classname,
-	    str_size(classname));
-	
-	if (retval != EOK) {
-		devman_exchange_end(exch);
-		async_wait_for(req, NULL);
-		return retval;
-	}
-	
-	retval = async_data_write_start(exch, devname,
-	    str_size(devname));
-	
-	devman_exchange_end(exch);
-	
-	if (retval != EOK) {
-		async_wait_for(req, NULL);
-		return retval;
-	}
-	
-	async_wait_for(req, &retval);
-	
-	if (retval != EOK) {
-		if (handle != NULL)
-			*handle = (devman_handle_t) -1;
-		
-		return retval;
-	}
-	
-	if (handle != NULL)
-		*handle = (devman_handle_t) IPC_GET_ARG1(answer);
-	
-	return retval;
-}
-
 int devman_get_device_path(devman_handle_t handle, char *path, size_t path_size)
 {
 	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
 	if (exch == NULL)
-		return errno;
+		return ENOMEM;
 	
 	ipc_call_t answer;
@@ -463,4 +432,17 @@
 }
 
+int devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_1(exch, DEVMAN_FUN_SID_TO_HANDLE,
+	    sid, handle);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/event.c
===================================================================
--- uspace/lib/c/generic/event.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/event.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -54,4 +54,10 @@
 }
 
+int event_task_subscribe(event_task_type_t evno, sysarg_t imethod)
+{
+	return __SYSCALL2(SYS_EVENT_SUBSCRIBE, (sysarg_t) evno,
+	    (sysarg_t) imethod);
+}
+
 /** Unmask event notifications.
  *
@@ -66,4 +72,9 @@
 }
 
+int event_task_unmask(event_task_type_t evno)
+{
+	return __SYSCALL1(SYS_EVENT_UNMASK, (sysarg_t) evno);
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/io/io.c
===================================================================
--- uspace/lib/c/generic/io/io.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/io/io.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -101,8 +101,8 @@
 static LIST_INITIALIZE(files);
 
-void __stdio_init(int filc, fdi_node_t *filv[])
+void __stdio_init(int filc)
 {
 	if (filc > 0) {
-		stdin = fopen_node(filv[0], "r");
+		stdin = fdopen(0, "r");
 	} else {
 		stdin = &stdin_null;
@@ -111,5 +111,5 @@
 	
 	if (filc > 1) {
-		stdout = fopen_node(filv[1], "w");
+		stdout = fdopen(1, "w");
 	} else {
 		stdout = &stdout_klog;
@@ -118,5 +118,5 @@
 	
 	if (filc > 2) {
-		stderr = fopen_node(filv[2], "w");
+		stderr = fdopen(2, "w");
 	} else {
 		stderr = &stderr_klog;
@@ -285,36 +285,4 @@
 }
 
-FILE *fopen_node(fdi_node_t *node, const char *mode)
-{
-	int flags;
-	if (!parse_mode(mode, &flags))
-		return NULL;
-	
-	/* Open file. */
-	FILE *stream = malloc(sizeof(FILE));
-	if (stream == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-	
-	stream->fd = open_node(node, flags);
-	if (stream->fd < 0) {
-		/* errno was set by open_node() */
-		free(stream);
-		return NULL;
-	}
-	
-	stream->error = false;
-	stream->eof = false;
-	stream->klog = false;
-	stream->sess = NULL;
-	stream->need_sync = false;
-	_setvbuf(stream);
-	
-	list_append(&stream->link, &files);
-	
-	return stream;
-}
-
 int fclose(FILE *stream)
 {
@@ -780,8 +748,10 @@
 }
 
-int fnode(FILE *stream, fdi_node_t *node)
-{
-	if (stream->fd >= 0)
-		return fd_node(stream->fd, node);
+int fhandle(FILE *stream, int *handle)
+{
+	if (stream->fd >= 0) {
+		*handle = stream->fd;
+		return EOK;
+	}
 	
 	return ENOENT;
Index: uspace/lib/c/generic/libc.c
===================================================================
--- uspace/lib/c/generic/libc.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/libc.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -91,9 +91,9 @@
 		argc = 0;
 		argv = NULL;
-		__stdio_init(0, NULL);
+		__stdio_init(0);
 	} else {
 		argc = __pcb->argc;
 		argv = __pcb->argv;
-		__stdio_init(__pcb->filc, __pcb->filv);
+		__stdio_init(__pcb->filc);
 		(void) chdir(__pcb->cwd);
 	}
Index: uspace/lib/c/generic/loader.c
===================================================================
--- uspace/lib/c/generic/loader.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/loader.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -256,38 +256,29 @@
  *
  */
-int loader_set_files(loader_t *ldr, fdi_node_t *const files[])
-{
-	/*
-	 * Serialize the arguments into a single array. First
-	 * compute size of the buffer needed.
-	 */
-	fdi_node_t *const *ap = files;
-	size_t count = 0;
-	while (*ap != NULL) {
-		count++;
-		ap++;
-	}
-	
-	fdi_node_t *files_buf;
-	files_buf = (fdi_node_t *) malloc(count * sizeof(fdi_node_t));
-	if (files_buf == NULL)
-		return ENOMEM;
-	
-	/* Fill the buffer */
-	size_t i;
-	for (i = 0; i < count; i++)
-		files_buf[i] = *files[i];
-	
+int loader_set_files(loader_t *ldr, int * const files[])
+{
 	/* Send serialized files to the loader */
 	async_exch_t *exch = async_exchange_begin(ldr->sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, LOADER_SET_FILES, &answer);
-	sysarg_t rc = async_data_write_start(exch, (void *) files_buf,
-	    count * sizeof(fdi_node_t));
-	
-	async_exchange_end(exch);
-	free(files_buf);
-	
+	async_exch_t *vfs_exch = vfs_exchange_begin();
+	
+	int i;
+	for (i = 0; files[i]; i++)
+		;
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, LOADER_SET_FILES, i, &answer);
+
+	sysarg_t rc = EOK;
+	
+	for (i = 0; files[i]; i++) {
+		rc = async_state_change_start(exch, VFS_PASS_HANDLE, *files[i],
+		    0, vfs_exch); 
+		if (rc != EOK)
+			break;
+	}
+	
+	vfs_exchange_end(vfs_exch);
+	async_exchange_end(exch);
+
 	if (rc != EOK) {
 		async_wait_for(req, NULL);
Index: uspace/lib/c/generic/loc.c
===================================================================
--- uspace/lib/c/generic/loc.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/loc.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -45,4 +45,7 @@
 static FIBRIL_MUTEX_INITIALIZE(loc_consumer_mutex);
 
+static FIBRIL_MUTEX_INITIALIZE(loc_callback_mutex);
+static bool loc_callback_created = false;
+
 static async_sess_t *loc_supp_block_sess = NULL;
 static async_sess_t *loc_cons_block_sess = NULL;
@@ -51,4 +54,40 @@
 static async_sess_t *loc_consumer_sess = NULL;
 
+static loc_cat_change_cb_t cat_change_cb = NULL;
+
+static void loc_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	loc_cat_change_cb_t cb_fun;
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+		
+		int retval;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOC_EVENT_CAT_CHANGE:
+			fibril_mutex_lock(&loc_callback_mutex);
+			cb_fun = cat_change_cb;
+			if (cb_fun != NULL) {
+				(*cb_fun)();
+			}
+			fibril_mutex_unlock(&loc_callback_mutex);
+			retval = 0;
+			break;
+		default:
+			retval = ENOTSUP;
+		}
+		
+		async_answer_0(callid, retval);
+	}
+}
+
+
 static void clone_session(fibril_mutex_t *mtx, async_sess_t *src,
     async_sess_t **dst)
@@ -60,4 +99,38 @@
 	
 	fibril_mutex_unlock(mtx);
+}
+
+static int loc_callback_create(void)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	int rc = EOK;
+
+	fibril_mutex_lock(&loc_callback_mutex);
+	
+	if (!loc_callback_created) {
+		exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
+		
+		ipc_call_t answer;
+		aid_t req = async_send_0(exch, LOC_CALLBACK_CREATE, &answer);
+		async_connect_to_me(exch, 0, 0, 0, loc_cb_conn, NULL);
+		loc_exchange_end(exch);
+		
+		async_wait_for(req, &retval);
+		if (rc != EOK)
+			goto done;
+		
+		if (retval != EOK) {
+			rc = retval;
+			goto done;
+		}
+		
+		loc_callback_created = true;
+	}
+	
+	rc = EOK;
+done:
+	fibril_mutex_unlock(&loc_callback_mutex);
+	return rc;
 }
 
@@ -194,7 +267,7 @@
 }
 
-/** Register new device.
- *
- * The @p interface is used when forwarding connection to the driver.
+/** Register new service.
+ *
+ * The @p interface is used when forwarding connection to the server.
  * If not 0, the first argument is the interface and the second argument
  * is the service ID.
@@ -203,11 +276,11 @@
  * the handle (to ensure backward compatibility).
  *
- * @param      fqdn      Fully qualified device name.
- * @param[out] handle    Handle to the created instance of device.
- * @param      interface Interface when forwarding.
- *
- */
-int loc_service_register_with_iface(const char *fqdn,
-    service_id_t *handle, sysarg_t interface)
+ * @param      fqsn      Fully qualified service name
+ * @param[out] sid       Service ID of new service
+ * @param      interface Interface when forwarding
+ *
+ */
+int loc_service_register_with_iface(const char *fqsn,
+    service_id_t *sid, sysarg_t interface)
 {
 	async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
@@ -216,5 +289,5 @@
 	aid_t req = async_send_2(exch, LOC_SERVICE_REGISTER, interface, 0,
 	    &answer);
-	sysarg_t retval = async_data_write_start(exch, fqdn, str_size(fqdn));
+	sysarg_t retval = async_data_write_start(exch, fqsn, str_size(fqsn));
 	
 	loc_exchange_end(exch);
@@ -228,25 +301,41 @@
 	
 	if (retval != EOK) {
-		if (handle != NULL)
-			*handle = -1;
-		
-		return retval;
-	}
-	
-	if (handle != NULL)
-		*handle = (service_id_t) IPC_GET_ARG1(answer);
+		if (sid != NULL)
+			*sid = -1;
+		
+		return retval;
+	}
+	
+	if (sid != NULL)
+		*sid = (service_id_t) IPC_GET_ARG1(answer);
 	
 	return retval;
 }
 
-/** Register new device.
- *
- * @param fqdn   Fully qualified device name.
- * @param handle Output: Handle to the created instance of device.
- *
- */
-int loc_service_register(const char *fqdn, service_id_t *handle)
-{
-	return loc_service_register_with_iface(fqdn, handle, 0);
+/** Register new service.
+ *
+ * @param fqsn	Fully qualified service name
+ * @param sid	Output: ID of new service
+ *
+ */
+int loc_service_register(const char *fqdn, service_id_t *sid)
+{
+	return loc_service_register_with_iface(fqdn, sid, 0);
+}
+
+/** Unregister service.
+ *
+ * @param sid	Service ID
+ */
+int loc_service_unregister(service_id_t sid)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
+	retval = async_req_1_0(exch, LOC_SERVICE_UNREGISTER, sid);
+	loc_exchange_end(exch);
+	
+	return (int)retval;
 }
 
@@ -289,4 +378,83 @@
 	
 	return retval;
+}
+
+/** Get object name.
+ *
+ * Provided ID of an object, return its name.
+ *
+ * @param method	IPC method
+ * @param id		Object ID
+ * @param name		Place to store pointer to new string. Caller should
+ *			free it using free().
+ * @return		EOK on success or negative error code
+ */
+static int loc_get_name_internal(sysarg_t method, sysarg_t id, char **name)
+{
+	async_exch_t *exch;
+	char name_buf[LOC_NAME_MAXLEN + 1];
+	ipc_call_t dreply;
+	size_t act_size;
+	sysarg_t dretval;
+	
+	*name = NULL;
+	exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, id, &answer);
+	aid_t dreq = async_data_read(exch, name_buf, LOC_NAME_MAXLEN,
+	    &dreply);
+	async_wait_for(dreq, &dretval);
+	
+	loc_exchange_end(exch);
+	
+	if (dretval != EOK) {
+		async_wait_for(req, NULL);
+		return dretval;
+	}
+	
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK)
+		return retval;
+	
+	act_size = IPC_GET_ARG2(dreply);
+	assert(act_size <= LOC_NAME_MAXLEN);
+	name_buf[act_size] = '\0';
+
+	*name = str_dup(name_buf);
+	if (*name == NULL)
+		return ENOMEM;
+	
+	return EOK;
+}
+
+/** Get category name.
+ *
+ * Provided ID of a service, return its name.
+ *
+ * @param cat_id	Category ID
+ * @param name		Place to store pointer to new string. Caller should
+ *			free it using free().
+ * @return		EOK on success or negative error code
+ */
+int loc_category_get_name(category_id_t cat_id, char **name)
+{
+	return loc_get_name_internal(LOC_CATEGORY_GET_NAME, cat_id, name);
+}
+
+/** Get service name.
+ *
+ * Provided ID of a service, return its name.
+ *
+ * @param svc_id	Service ID
+ * @param name		Place to store pointer to new string. Caller should
+ *			free it using free().
+ * @return		EOK on success or negative error code
+ */
+int loc_service_get_name(service_id_t svc_id, char **name)
+{
+	return loc_get_name_internal(LOC_SERVICE_GET_NAME, svc_id, name);
 }
 
@@ -589,12 +757,11 @@
 }
 
-static int loc_category_get_svcs_internal(category_id_t cat_id,
-    service_id_t *id_buf, size_t buf_size, size_t *act_size)
+static int loc_category_get_ids_once(sysarg_t method, sysarg_t arg1,
+    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
 {
 	async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
 
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, LOC_CATEGORY_GET_SVCS, cat_id,
-	    &answer);
+	aid_t req = async_send_1(exch, method, arg1, &answer);
 	int rc = async_data_read_start(exch, id_buf, buf_size);
 	
@@ -614,4 +781,57 @@
 	
 	*act_size = IPC_GET_ARG1(answer);
+	return EOK;
+}
+
+/** Get list of IDs.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param method	IPC method
+ * @param arg1		IPC argument 1
+ * @param data		Place to store pointer to array of IDs
+ * @param count		Place to store number of IDs
+ * @return 		EOK on success or negative error code
+ */
+static int loc_get_ids_internal(sysarg_t method, sysarg_t arg1,
+    sysarg_t **data, size_t *count)
+{
+	service_id_t *ids;
+	size_t act_size;
+	size_t alloc_size;
+	int rc;
+
+	*data = NULL;
+	act_size = 0;	/* silence warning */
+
+	rc = loc_category_get_ids_once(method, arg1, NULL, 0,
+	    &act_size);
+	if (rc != EOK)
+		return rc;
+
+	alloc_size = act_size;
+	ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = loc_category_get_ids_once(method, arg1, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size *= 2;
+		free(ids);
+
+		ids = malloc(alloc_size);
+		if (ids == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(category_id_t);
+	*data = ids;
 	return EOK;
 }
@@ -626,43 +846,31 @@
  * @return 		EOK on success or negative error code
  */
-int loc_category_get_svcs(category_id_t cat_id, category_id_t **data,
+int loc_category_get_svcs(category_id_t cat_id, service_id_t **data,
     size_t *count)
 {
-	service_id_t *ids;
-	size_t act_size;
-	size_t alloc_size;
-	int rc;
-
-	*data = NULL;
-	act_size = 0;	/* silence warning */
-
-	rc = loc_category_get_svcs_internal(cat_id, NULL, 0, &act_size);
-	if (rc != EOK)
-		return rc;
-
-	alloc_size = act_size;
-	ids = malloc(alloc_size);
-	if (ids == NULL)
-		return ENOMEM;
-
-	while (true) {
-		rc = loc_category_get_svcs_internal(cat_id, ids, alloc_size,
-		    &act_size);
-		if (rc != EOK)
-			return rc;
-
-		if (act_size <= alloc_size)
-			break;
-
-		alloc_size *= 2;
-		free(ids);
-
-		ids = malloc(alloc_size);
-		if (ids == NULL)
-			return ENOMEM;
-	}
-
-	*count = act_size / sizeof(category_id_t);
-	*data = ids;
+	return loc_get_ids_internal(LOC_CATEGORY_GET_SVCS, cat_id,
+	    data, count);
+}
+
+/** Get list of categories.
+ *
+ * Returns an allocated array of category IDs.
+ *
+ * @param data		Place to store pointer to array of IDs
+ * @param count		Place to store number of IDs
+ * @return 		EOK on success or negative error code
+ */
+int loc_get_categories(category_id_t **data, size_t *count)
+{
+	return loc_get_ids_internal(LOC_GET_CATEGORIES, 0,
+	    data, count);
+}
+
+int loc_register_cat_change_cb(loc_cat_change_cb_t cb_fun)
+{
+	if (loc_callback_create() != EOK)
+		return EIO;
+
+	cat_change_cb = cb_fun;
 	return EOK;
 }
Index: uspace/lib/c/generic/private/async.h
===================================================================
--- uspace/lib/c/generic/private/async.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/private/async.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -36,4 +36,5 @@
 #define LIBC_PRIVATE_ASYNC_H_
 
+#include <ipc/common.h>
 #include <adt/list.h>
 #include <fibril.h>
Index: uspace/lib/c/generic/private/io.h
===================================================================
--- uspace/lib/c/generic/private/io.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/private/io.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -36,7 +36,5 @@
 #define LIBC_PRIVATE_IO_H_
 
-#include <vfs/vfs.h>
-
-extern void __stdio_init(int filc, fdi_node_t *filv[]);
+extern void __stdio_init(int);
 extern void __stdio_done(void);
 
Index: uspace/lib/c/generic/task.c
===================================================================
--- uspace/lib/c/generic/task.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/task.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -46,4 +46,5 @@
 #include <libc.h>
 #include "private/ns.h"
+#include <vfs/vfs.h>
 
 task_id_t task_get_id(void)
@@ -102,21 +103,21 @@
 {
 	/* Send default files */
-	fdi_node_t *files[4];
-	fdi_node_t stdin_node;
-	fdi_node_t stdout_node;
-	fdi_node_t stderr_node;
-	
-	if ((stdin != NULL) && (fnode(stdin, &stdin_node) == EOK))
-		files[0] = &stdin_node;
+	int *files[4];
+	int fd_stdin;
+	int fd_stdout;
+	int fd_stderr;
+	
+	if ((stdin != NULL) && (fhandle(stdin, &fd_stdin) == EOK))
+		files[0] = &fd_stdin;
 	else
 		files[0] = NULL;
 	
-	if ((stdout != NULL) && (fnode(stdout, &stdout_node) == EOK))
-		files[1] = &stdout_node;
+	if ((stdout != NULL) && (fhandle(stdout, &fd_stdout) == EOK))
+		files[1] = &fd_stdout;
 	else
 		files[1] = NULL;
 	
-	if ((stderr != NULL) && (fnode(stderr, &stderr_node) == EOK))
-		files[2] = &stderr_node;
+	if ((stderr != NULL) && (fhandle(stderr, &fd_stderr) == EOK))
+		files[2] = &fd_stderr;
 	else
 		files[2] = NULL;
@@ -142,5 +143,5 @@
  */
 int task_spawnvf(task_id_t *id, const char *path, const char *const args[],
-    fdi_node_t *const files[])
+    int *const files[])
 {
 	/* Connect to a program loader. */
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -69,5 +69,5 @@
  *
  */
-static async_exch_t *vfs_exchange_begin(void)
+async_exch_t *vfs_exchange_begin(void)
 {
 	fibril_mutex_lock(&vfs_mutex);
@@ -87,5 +87,5 @@
  *
  */
-static void vfs_exchange_end(async_exch_t *exch)
+void vfs_exchange_end(async_exch_t *exch)
 {
 	async_exchange_end(exch);
@@ -329,23 +329,4 @@
 }
 
-int open_node(fdi_node_t *node, int oflag)
-{
-	async_exch_t *exch = vfs_exchange_begin();
-	
-	ipc_call_t answer;
-	aid_t req = async_send_4(exch, VFS_IN_OPEN_NODE, node->fs_handle,
-	    node->service_id, node->index, oflag, &answer);
-	
-	vfs_exchange_end(exch);
-
-	sysarg_t rc;
-	async_wait_for(req, &rc);
-	
-	if (rc != EOK)
-		return (int) rc;
-	
-	return (int) IPC_GET_ARG1(answer);
-}
-
 int close(int fildes)
 {
@@ -819,18 +800,4 @@
 }
 
-int fd_node(int fildes, fdi_node_t *node)
-{
-	struct stat stat;
-	int rc = fstat(fildes, &stat);
-	
-	if (rc == EOK) {
-		node->fs_handle = stat.fs_handle;
-		node->service_id = stat.service_id;
-		node->index = stat.index;
-	}
-	
-	return rc;
-}
-
 int dup2(int oldfd, int newfd)
 {
@@ -848,4 +815,19 @@
 }
 
+int fd_wait(void)
+{
+	async_exch_t *exch = vfs_exchange_begin();
+	
+	sysarg_t ret;
+	sysarg_t rc = async_req_0_1(exch, VFS_IN_WAIT_HANDLE, &ret);
+	
+	vfs_exchange_end(exch);
+	
+	if (rc == EOK)
+		return (int) ret;
+	
+	return (int) rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/async.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -186,4 +186,6 @@
 extern void async_set_client_data_destructor(async_client_data_dtor_t);
 extern void *async_get_client_data(void);
+extern void *async_get_client_data_by_hash(sysarg_t);
+extern void async_put_client_data_by_hash(sysarg_t);
 
 extern void async_set_client_connection(async_client_conn_t);
@@ -477,4 +479,10 @@
 extern async_sess_t *async_callback_receive_start(exch_mgmt_t, ipc_call_t *);
 
+extern int async_state_change_start(async_exch_t *, sysarg_t, sysarg_t,
+    sysarg_t, async_exch_t *);
+extern bool async_state_change_receive(ipc_callid_t *, sysarg_t *, sysarg_t *,
+    sysarg_t *);
+extern int async_state_change_finalize(ipc_callid_t, async_exch_t *);
+
 #endif
 
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/devman.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -38,4 +38,5 @@
 
 #include <ipc/devman.h>
+#include <ipc/loc.h>
 #include <async.h>
 #include <bool.h>
@@ -48,4 +49,5 @@
 extern int devman_add_function(const char *, fun_type_t, match_id_list_t *,
     devman_handle_t, devman_handle_t *);
+extern int devman_remove_function(devman_handle_t);
 
 extern async_sess_t *devman_device_connect(exch_mgmt_t, devman_handle_t,
@@ -56,9 +58,8 @@
 extern int devman_device_get_handle(const char *, devman_handle_t *,
     unsigned int);
-extern int devman_device_get_handle_by_class(const char *, const char *,
-    devman_handle_t *, unsigned int);
 extern int devman_get_device_path(devman_handle_t, char *, size_t);
 
-extern int devman_add_device_to_class(devman_handle_t, const char *);
+extern int devman_add_device_to_category(devman_handle_t, const char *);
+extern int devman_fun_sid_to_handle(service_id_t, devman_handle_t *);
 
 #endif
Index: uspace/lib/c/include/event.h
===================================================================
--- uspace/lib/c/include/event.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/event.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -37,7 +37,10 @@
 
 #include <abi/ipc/event.h>
+#include <libarch/types.h>
 
 extern int event_subscribe(event_type_t, sysarg_t);
+extern int event_task_subscribe(event_task_type_t, sysarg_t);
 extern int event_unmask(event_type_t);
+extern int event_task_unmask(event_task_type_t);
 
 #endif
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/ipc/devman.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -138,6 +138,6 @@
 	DEVMAN_ADD_FUNCTION,
 	DEVMAN_ADD_MATCH_ID,
-	DEVMAN_ADD_DEVICE_TO_CLASS
-
+	DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	DEVMAN_REMOVE_FUNCTION
 } driver_to_devman_t;
 
@@ -149,6 +149,6 @@
 typedef enum {
 	DEVMAN_DEVICE_GET_HANDLE = IPC_FIRST_USER_METHOD,
-	DEVMAN_DEVICE_GET_HANDLE_BY_CLASS,
-	DEVMAN_DEVICE_GET_DEVICE_PATH
+	DEVMAN_DEVICE_GET_DEVICE_PATH,
+	DEVMAN_FUN_SID_TO_HANDLE
 } client_to_devman_t;
 
Index: uspace/lib/c/include/ipc/loc.h
===================================================================
--- uspace/lib/c/include/ipc/loc.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/ipc/loc.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -55,6 +55,9 @@
 	LOC_SERVICE_UNREGISTER,
 	LOC_SERVICE_GET_ID,
+	LOC_SERVICE_GET_NAME,
 	LOC_NAMESPACE_GET_ID,
+	LOC_CALLBACK_CREATE,
 	LOC_CATEGORY_GET_ID,
+	LOC_CATEGORY_GET_NAME,
 	LOC_CATEGORY_GET_SVCS,
 	LOC_ID_PROBE,
@@ -63,7 +66,12 @@
 	LOC_GET_NAMESPACE_COUNT,
 	LOC_GET_SERVICE_COUNT,
+	LOC_GET_CATEGORIES,
 	LOC_GET_NAMESPACES,
 	LOC_GET_SERVICES
 } loc_request_t;
+
+typedef enum {
+	LOC_EVENT_CAT_CHANGE = IPC_FIRST_USER_METHOD
+} loc_event_t;
 
 /** Ports provided by location service.
Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/ipc/vfs.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -62,5 +62,4 @@
 typedef enum {
 	VFS_IN_OPEN = IPC_FIRST_USER_METHOD,
-	VFS_IN_OPEN_NODE,
 	VFS_IN_READ,
 	VFS_IN_WRITE,
@@ -78,5 +77,6 @@
 	VFS_IN_RENAME,
 	VFS_IN_STAT,
-	VFS_IN_DUP
+	VFS_IN_DUP,
+	VFS_IN_WAIT_HANDLE,
 } vfs_in_request_t;
 
Index: uspace/lib/c/include/loader/loader.h
===================================================================
--- uspace/lib/c/include/loader/loader.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/loader/loader.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -38,5 +38,4 @@
 
 #include <task.h>
-#include <vfs/vfs.h>
 
 /** Forward declararion */
@@ -50,5 +49,5 @@
 extern int loader_set_pathname(loader_t *, const char *);
 extern int loader_set_args(loader_t *, const char *const[]);
-extern int loader_set_files(loader_t *, fdi_node_t *const[]);
+extern int loader_set_files(loader_t *, int *const[]);
 extern int loader_load_program(loader_t *);
 extern int loader_run(loader_t *);
Index: uspace/lib/c/include/loader/pcb.h
===================================================================
--- uspace/lib/c/include/loader/pcb.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/loader/pcb.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -38,5 +38,4 @@
 
 #include <sys/types.h>
-#include <vfs/vfs.h>
 
 typedef void (*entry_point_t)(void);
@@ -62,7 +61,5 @@
 	
 	/** Number of preset files. */
-	int filc;
-	/** Preset files. */
-	fdi_node_t **filv;
+	unsigned int filc;
 	
 	/*
Index: uspace/lib/c/include/loc.h
===================================================================
--- uspace/lib/c/include/loc.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/loc.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -40,4 +40,6 @@
 #include <bool.h>
 
+typedef void (*loc_cat_change_cb_t)(void);
+
 extern async_exch_t *loc_exchange_begin_blocking(loc_interface_t);
 extern async_exch_t *loc_exchange_begin(loc_interface_t);
@@ -48,12 +50,15 @@
 extern int loc_service_register_with_iface(const char *, service_id_t *,
     sysarg_t);
+extern int loc_service_unregister(service_id_t);
 extern int loc_service_add_to_cat(service_id_t, category_id_t);
 
 extern int loc_service_get_id(const char *, service_id_t *,
     unsigned int);
+extern int loc_service_get_name(service_id_t, char **);
 extern int loc_namespace_get_id(const char *, service_id_t *,
     unsigned int);
 extern int loc_category_get_id(const char *, category_id_t *,
     unsigned int);
+extern int loc_category_get_name(category_id_t, char **);
 extern int loc_category_get_svcs(category_id_t, category_id_t **, size_t *);
 extern loc_object_type_t loc_id_probe(service_id_t);
@@ -70,4 +75,7 @@
 extern size_t loc_get_namespaces(loc_sdesc_t **);
 extern size_t loc_get_services(service_id_t, loc_sdesc_t **);
+extern int loc_get_categories(category_id_t **, size_t *);
+extern int loc_register_cat_change_cb(loc_cat_change_cb_t);
+
 
 #endif
Index: uspace/lib/c/include/task.h
===================================================================
--- uspace/lib/c/include/task.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/task.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -37,5 +37,4 @@
 
 #include <sys/types.h>
-#include <vfs/vfs.h>
 
 typedef uint64_t task_id_t;
@@ -53,5 +52,5 @@
 extern int task_spawnv(task_id_t *, const char *path, const char *const []);
 extern int task_spawnvf(task_id_t *, const char *path, const char *const [],
-    fdi_node_t *const []);
+    int *const []);
 extern int task_spawnl(task_id_t *, const char *path, ...);
 
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/c/include/vfs/vfs.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -40,16 +40,9 @@
 #include <ipc/loc.h>
 #include <stdio.h>
+#include <async.h>
 
-/** Libc version of the VFS triplet.
- *
- * Unique identification of a file system node
- * within a file system instance.
- *
- */
-typedef struct {
-	fs_handle_t fs_handle;
-	service_id_t service_id;
-	fs_index_t index;
-} fdi_node_t;
+enum vfs_change_state_type {
+	VFS_PASS_HANDLE
+};
 
 extern char *absolutize(const char *, size_t *);
@@ -59,9 +52,10 @@
 extern int unmount(const char *);
 
-extern int open_node(fdi_node_t *, int);
-extern int fd_node(int, fdi_node_t *);
+extern int fhandle(FILE *, int *);
 
-extern FILE *fopen_node(fdi_node_t *, const char *);
-extern int fnode(FILE *, fdi_node_t *);
+extern int fd_wait(void);
+
+extern async_exch_t *vfs_exchange_begin(void);
+extern void vfs_exchange_end(async_exch_t *);
 
 #endif
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/drv/generic/driver.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -582,4 +582,5 @@
 int ddf_fun_bind(ddf_fun_t *fun)
 {
+	assert(fun->bound == false);
 	assert(fun->name != NULL);
 	
@@ -598,4 +599,29 @@
 }
 
+/** Unbind a function node.
+ *
+ * Unbind the specified function from the system. This effectively makes
+ * the function invisible to the system.
+ *
+ * @param fun		Function to bind
+ * @return		EOK on success or negative error code
+ */
+int ddf_fun_unbind(ddf_fun_t *fun)
+{
+	int res;
+	
+	assert(fun->bound == true);
+	
+	add_to_functions_list(fun);
+	res = devman_remove_function(fun->handle);
+	if (res != EOK)
+		return res;
+
+	remove_from_functions_list(fun);
+	
+	fun->bound = false;
+	return EOK;
+}
+
 /** Add single match ID to inner function.
  *
@@ -635,14 +661,14 @@
 }
 
-/** Add exposed function to class.
+/** Add exposed function to category.
  *
  * Must only be called when the function is bound.
  */
-int ddf_fun_add_to_class(ddf_fun_t *fun, const char *class_name)
+int ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
 {
 	assert(fun->bound == true);
 	assert(fun->ftype == fun_exposed);
 	
-	return devman_add_device_to_class(fun->handle, class_name);
+	return devman_add_device_to_category(fun->handle, cat_name);
 }
 
Index: uspace/lib/drv/include/ddf/driver.h
===================================================================
--- uspace/lib/drv/include/ddf/driver.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/drv/include/ddf/driver.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -149,7 +149,8 @@
 extern void ddf_fun_destroy(ddf_fun_t *);
 extern int ddf_fun_bind(ddf_fun_t *);
+extern int ddf_fun_unbind(ddf_fun_t *);
 extern int ddf_fun_add_match_id(ddf_fun_t *, const char *, int);
 
-extern int ddf_fun_add_to_class(ddf_fun_t *, const char *);
+extern int ddf_fun_add_to_category(ddf_fun_t *, const char *);
 
 #endif
Index: uspace/lib/usb/include/usb/hc.h
===================================================================
--- uspace/lib/usb/include/usb/hc.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/usb/include/usb/hc.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -38,4 +38,5 @@
 #include <sys/types.h>
 #include <ipc/devman.h>
+#include <ipc/loc.h>
 #include <ddf/driver.h>
 #include <bool.h>
@@ -68,5 +69,5 @@
     devman_handle_t *);
 
-int usb_ddf_get_hc_handle_by_class(size_t, devman_handle_t *);
+int usb_ddf_get_hc_handle_by_sid(service_id_t, devman_handle_t *);
 
 
Index: uspace/lib/usb/include/usb/usb.h
===================================================================
--- uspace/lib/usb/include/usb/usb.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/usb/include/usb/usb.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -174,6 +174,6 @@
 } usb_packet_id;
 
-/** Class name for USB host controllers. */
-#define USB_HC_DDF_CLASS_NAME "usbhc"
+/** Category for USB host controllers. */
+#define USB_HC_CATEGORY "usbhc"
 
 #endif
Index: uspace/lib/usb/src/hc.c
===================================================================
--- uspace/lib/usb/src/hc.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/usb/src/hc.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -201,32 +201,19 @@
 /** Get host controller handle by its class index.
  *
- * @param class_index Class index for the host controller.
+ * @param sid Service ID of the HC function.
  * @param hc_handle Where to store the HC handle
  *	(can be NULL for existence test only).
  * @return Error code.
  */
-int usb_ddf_get_hc_handle_by_class(size_t class_index,
-    devman_handle_t *hc_handle)
-{
-	char *class_index_str;
-	devman_handle_t hc_handle_tmp;
+int usb_ddf_get_hc_handle_by_sid(service_id_t sid, devman_handle_t *hc_handle)
+{
+	devman_handle_t handle;
 	int rc;
-
-	rc = asprintf(&class_index_str, "%zu", class_index);
-	if (rc < 0) {
-		return ENOMEM;
-	}
-	rc = devman_device_get_handle_by_class("usbhc", class_index_str,
-	    &hc_handle_tmp, 0);
-	free(class_index_str);
-	if (rc != EOK) {
-		return rc;
-	}
-
-	if (hc_handle != NULL) {
-		*hc_handle = hc_handle_tmp;
-	}
-
-	return EOK;
+	
+	rc = devman_fun_sid_to_handle(sid, &handle);
+	if (hc_handle != NULL)
+		*hc_handle = handle;
+	
+	return rc;
 }
 
Index: uspace/lib/usb/src/resolve.c
===================================================================
--- uspace/lib/usb/src/resolve.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/lib/usb/src/resolve.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -46,10 +46,10 @@
     devman_handle_t *out_hc_handle, usb_address_t *out_device_address)
 {
-	size_t class_index;
+	uint64_t sid;
 	size_t address;
 	int rc;
 	char *ptr;
 
-	rc = str_size_t(path, &ptr, 10, false, &class_index);
+	rc = str_uint64(path, &ptr, 10, false, &sid);
 	if (rc != EOK) {
 		return false;
@@ -64,5 +64,5 @@
 		return false;
 	}
-	rc = usb_ddf_get_hc_handle_by_class(class_index, out_hc_handle);
+	rc = usb_ddf_get_hc_handle_by_sid(sid, out_hc_handle);
 	if (rc != EOK) {
 		return false;
Index: uspace/srv/bd/part/mbr_part/mbr_part.c
===================================================================
--- uspace/srv/bd/part/mbr_part/mbr_part.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/bd/part/mbr_part/mbr_part.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -66,5 +66,4 @@
 #include <inttypes.h>
 #include <libblock.h>
-#include <loc.h>
 #include <errno.h>
 #include <bool.h>
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/devman/devman.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -73,14 +73,4 @@
 }
 
-static int loc_devices_class_compare(unsigned long key[], hash_count_t keys,
-    link_t *item)
-{
-	dev_class_info_t *class_info
-	    = hash_table_get_instance(item, dev_class_info_t, loc_link);
-	assert(class_info != NULL);
-
-	return (class_info->service_id == (service_id_t) key[0]);
-}
-
 static void devices_remove_callback(link_t *item)
 {
@@ -102,10 +92,4 @@
 	.hash = devices_hash,
 	.compare = loc_functions_compare,
-	.remove_callback = devices_remove_callback
-};
-
-static hash_table_operations_t loc_devices_class_ops = {
-	.hash = devices_hash,
-	.compare = loc_devices_class_compare,
 	.remove_callback = devices_remove_callback
 };
@@ -950,5 +934,4 @@
 		link_initialize(&res->dev_functions);
 		list_initialize(&res->match_ids.ids);
-		list_initialize(&res->classes);
 		link_initialize(&res->devman_fun);
 		link_initialize(&res->loc_fun);
@@ -1115,4 +1098,24 @@
 	
 	return true;
+}
+
+/** Remove function from device tree.
+ *
+ * @param tree		Device tree
+ * @param node		Function node to remove
+ */
+void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
+{
+	assert(tree != NULL);
+	assert(fun != NULL);
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/* Remove the node from the handle-to-node map. */
+	unsigned long key = fun->handle;
+	hash_table_remove(&tree->devman_functions, &key, 1);
+	
+	/* Remove the node from the list of its parent's children. */
+	if (fun->dev != NULL)
+		list_remove(&fun->dev_functions);
 }
 
@@ -1193,34 +1196,4 @@
 }
 
-/** Find function node by its class name and index. */
-fun_node_t *find_fun_node_by_class(class_list_t *class_list,
-    const char *class_name, const char *dev_name)
-{
-	assert(class_list != NULL);
-	assert(class_name != NULL);
-	assert(dev_name != NULL);
-
-	fibril_rwlock_read_lock(&class_list->rwlock);
-
-	dev_class_t *cl = find_dev_class_no_lock(class_list, class_name);
-	if (cl == NULL) {
-		fibril_rwlock_read_unlock(&class_list->rwlock);
-		return NULL;
-	}
-
-	dev_class_info_t *dev = find_dev_in_class(cl, dev_name);
-	if (dev == NULL) {
-		fibril_rwlock_read_unlock(&class_list->rwlock);
-		return NULL;
-	}
-
-	fun_node_t *fun = dev->fun;
-
-	fibril_rwlock_read_unlock(&class_list->rwlock);
-
-	return fun;
-}
-
-
 /** Find child function node with a specified name.
  *
@@ -1235,187 +1208,4 @@
 	return find_fun_node_in_device(pfun->child, name);
 }
-
-/* Device classes */
-
-/** Create device class.
- *
- * @return	Device class.
- */
-dev_class_t *create_dev_class(void)
-{
-	dev_class_t *cl;
-	
-	cl = (dev_class_t *) malloc(sizeof(dev_class_t));
-	if (cl != NULL) {
-		memset(cl, 0, sizeof(dev_class_t));
-		list_initialize(&cl->devices);
-		fibril_mutex_initialize(&cl->mutex);
-	}
-	
-	return cl;
-}
-
-/** Create device class info.
- *
- * @return		Device class info.
- */
-dev_class_info_t *create_dev_class_info(void)
-{
-	dev_class_info_t *info;
-	
-	info = (dev_class_info_t *) malloc(sizeof(dev_class_info_t));
-	if (info != NULL) {
-		memset(info, 0, sizeof(dev_class_info_t));
-		link_initialize(&info->dev_classes);
-		link_initialize(&info->loc_link);
-		link_initialize(&info->link);
-	}
-	
-	return info;
-}
-
-size_t get_new_class_dev_idx(dev_class_t *cl)
-{
-	size_t dev_idx;
-	
-	fibril_mutex_lock(&cl->mutex);
-	dev_idx = ++cl->curr_dev_idx;
-	fibril_mutex_unlock(&cl->mutex);
-	
-	return dev_idx;
-}
-
-
-/** Create unique device name within the class.
- *
- * @param cl		The class.
- * @param base_dev_name	Contains the base name for the device if it was
- *			specified by the driver when it registered the device by
- *			the class; NULL if driver specified no base name.
- * @return		The unique name for the device within the class.
- */
-char *create_dev_name_for_class(dev_class_t *cl, const char *base_dev_name)
-{
-	char *dev_name;
-	const char *base_name;
-	
-	if (base_dev_name != NULL)
-		base_name = base_dev_name;
-	else
-		base_name = cl->base_dev_name;
-	
-	size_t idx = get_new_class_dev_idx(cl);
-	asprintf(&dev_name, "%s%zu", base_name, idx);
-	
-	return dev_name;
-}
-
-/** Add the device function to the class.
- *
- * The device may be added to multiple classes and a class may contain multiple
- * devices. The class and the device are associated with each other by the
- * dev_class_info_t structure.
- *
- * @param dev		The device.
- * @param class		The class.
- * @param base_dev_name	The base name of the device within the class if
- *			specified by the driver, NULL otherwise.
- * @return		dev_class_info_t structure which associates the device
- *			with the class.
- */
-dev_class_info_t *add_function_to_class(fun_node_t *fun, dev_class_t *cl,
-    const char *base_dev_name)
-{
-	dev_class_info_t *info;
-
-	assert(fun != NULL);
-	assert(cl != NULL);
-
-	info = create_dev_class_info();
-
-	
-	if (info != NULL) {
-		info->dev_class = cl;
-		info->fun = fun;
-		
-		/* Add the device to the class. */
-		fibril_mutex_lock(&cl->mutex);
-		list_append(&info->link, &cl->devices);
-		fibril_mutex_unlock(&cl->mutex);
-		
-		/* Add the class to the device. */
-		list_append(&info->dev_classes, &fun->classes);
-		
-		/* Create unique name for the device within the class. */
-		info->dev_name = create_dev_name_for_class(cl, base_dev_name);
-	}
-	
-	return info;
-}
-
-dev_class_t *get_dev_class(class_list_t *class_list, char *class_name)
-{
-	dev_class_t *cl;
-	
-	fibril_rwlock_write_lock(&class_list->rwlock);
-	cl = find_dev_class_no_lock(class_list, class_name);
-	if (cl == NULL) {
-		cl = create_dev_class();
-		if (cl != NULL) {
-			cl->name = class_name;
-			cl->base_dev_name = "";
-			add_dev_class_no_lock(class_list, cl);
-		}
-	}
-
-	fibril_rwlock_write_unlock(&class_list->rwlock);
-	return cl;
-}
-
-dev_class_t *find_dev_class_no_lock(class_list_t *class_list,
-    const char *class_name)
-{
-	dev_class_t *cl;
-	
-	list_foreach(class_list->classes, link) {
-		cl = list_get_instance(link, dev_class_t, link);
-		if (str_cmp(cl->name, class_name) == 0) {
-			return cl;
-		}
-	}
-	
-	return NULL;
-}
-
-void add_dev_class_no_lock(class_list_t *class_list, dev_class_t *cl)
-{
-	list_append(&cl->link, &class_list->classes);
-}
-
-dev_class_info_t *find_dev_in_class(dev_class_t *dev_class, const char *dev_name)
-{
-	assert(dev_class != NULL);
-	assert(dev_name != NULL);
-
-	list_foreach(dev_class->devices, link) {
-		dev_class_info_t *dev = list_get_instance(link,
-		    dev_class_info_t, link);
-
-		if (str_cmp(dev->dev_name, dev_name) == 0) {
-			return dev;
-		}
-	}
-
-	return NULL;
-}
-
-void init_class_list(class_list_t *class_list)
-{
-	list_initialize(&class_list->classes);
-	fibril_rwlock_initialize(&class_list->rwlock);
-	hash_table_create(&class_list->loc_functions, DEVICE_BUCKETS, 1,
-	    &loc_devices_class_ops);
-}
-
 
 /* loc devices */
@@ -1436,35 +1226,4 @@
 }
 
-fun_node_t *find_loc_class_function(class_list_t *classes,
-    service_id_t service_id)
-{
-	fun_node_t *fun = NULL;
-	dev_class_info_t *cli;
-	link_t *link;
-	unsigned long key = (unsigned long)service_id;
-	
-	fibril_rwlock_read_lock(&classes->rwlock);
-	link = hash_table_find(&classes->loc_functions, &key);
-	if (link != NULL) {
-		cli = hash_table_get_instance(link, dev_class_info_t,
-		    loc_link);
-		fun = cli->fun;
-	}
-	fibril_rwlock_read_unlock(&classes->rwlock);
-	
-	return fun;
-}
-
-void class_add_loc_function(class_list_t *class_list, dev_class_info_t *cli)
-{
-	unsigned long key = (unsigned long) cli->service_id;
-	
-	fibril_rwlock_write_lock(&class_list->rwlock);
-	hash_table_insert(&class_list->loc_functions, &key, &cli->loc_link);
-	fibril_rwlock_write_unlock(&class_list->rwlock);
-
-	assert(find_loc_class_function(class_list, cli->service_id) != NULL);
-}
-
 void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
 {
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/devman/devman.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -53,5 +54,4 @@
 #define DEVICE_BUCKETS 256
 
-#define LOC_CLASS_NAMESPACE "class"
 #define LOC_DEVICE_NAMESPACE "devices"
 #define LOC_SEPARATOR '\\'
@@ -155,4 +155,6 @@
 	/** Name of the function, assigned by the device driver */
 	char *name;
+	/** Function type */
+	fun_type_t ftype;
 	
 	/** Full path and name of the device in device hierarchy */
@@ -170,6 +172,4 @@
 	match_id_list_t match_ids;
 	
-	/** List of device classes to which this device function belongs. */
-	list_t classes;
 	/** Service ID if the device function is registered with loc. */
 	service_id_t service_id;
@@ -213,82 +213,4 @@
 	hash_table_t loc_functions;
 } dev_tree_t;
-
-typedef struct dev_class {
-	/** The name of the class. */
-	const char *name;
-	
-	/**
-	 * Pointer to the previous and next class in the list of registered
-	 * classes.
-	 */
-	link_t link;
-	
-	/**
-	 * List of dev_class_info structures - one for each device registered by
-	 * this class.
-	 */
-	list_t devices;
-	
-	/**
-	 * Default base name for the device within the class, might be overrided
-	 * by the driver.
-	 */
-	const char *base_dev_name;
-	
-	/** Unique numerical identifier of the newly added device. */
-	size_t curr_dev_idx;
-	/** Synchronize access to the list of devices in this class. */
-	fibril_mutex_t mutex;
-} dev_class_t;
-
-/**
- * Provides n-to-m mapping between function nodes and classes - each function
- * can register in an arbitrary number of classes and each class can contain
- * an arbitrary number of device functions.
- */
-typedef struct dev_class_info {
-	/** The class. */
-	dev_class_t *dev_class;
-	/** The device. */
-	fun_node_t *fun;
-	
-	/**
-	 * Pointer to the previous and next class info in the list of devices
-	 * registered by the class.
-	 */
-	link_t link;
-	
-	/**
-	 * Pointer to the previous and next class info in the list of classes
-	 * by which the device is registered.
-	 */
-	link_t dev_classes;
-	
-	/** The name of the device function within the class. */
-	char *dev_name;
-	/** Service ID in the class namespace. */
-	service_id_t service_id;
-	
-	/**
-	 * Link to hash table of services registered with location service using
-	 * their class names.
-	 */
-	link_t loc_link;
-} dev_class_info_t;
-
-/** The list of device classes. */
-typedef struct class_list {
-	/** List of classes. */
-	list_t classes;
-	
-	/**
-	 * Hash table of services registered with location service using their
-	 * class name, indexed by service IDs.
-	 */
-	hash_table_t loc_functions;
-	
-	/** Fibril mutex for list of classes. */
-	fibril_rwlock_t rwlock;
-} class_list_t;
 
 /* Match ids and scores */
@@ -339,5 +261,4 @@
 extern fun_node_t *find_fun_node_by_path(dev_tree_t *, char *);
 extern fun_node_t *find_fun_node_in_device(dev_node_t *, const char *);
-extern fun_node_t *find_fun_node_by_class(class_list_t *, const char *, const char *);
 
 /* Device tree */
@@ -347,20 +268,5 @@
 extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
 extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
-
-/* Device classes */
-
-extern dev_class_t *create_dev_class(void);
-extern dev_class_info_t *create_dev_class_info(void);
-extern size_t get_new_class_dev_idx(dev_class_t *);
-extern char *create_dev_name_for_class(dev_class_t *, const char *);
-extern dev_class_info_t *add_function_to_class(fun_node_t *, dev_class_t *,
-    const char *);
-
-extern void init_class_list(class_list_t *);
-
-extern dev_class_t *get_dev_class(class_list_t *, char *);
-extern dev_class_t *find_dev_class_no_lock(class_list_t *, const char *);
-extern dev_class_info_t *find_dev_in_class(dev_class_t *, const char *);
-extern void add_dev_class_no_lock(class_list_t *, dev_class_t *);
+extern void remove_fun_node(dev_tree_t *, fun_node_t *);
 
 /* Loc services */
@@ -369,7 +275,5 @@
 
 extern fun_node_t *find_loc_tree_function(dev_tree_t *, service_id_t);
-extern fun_node_t *find_loc_class_function(class_list_t *, service_id_t);
-
-extern void class_add_loc_function(class_list_t *, dev_class_info_t *);
+
 extern void tree_add_loc_function(dev_tree_t *, fun_node_t *);
 
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/devman/main.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -64,5 +64,4 @@
 static driver_list_t drivers_list;
 static dev_tree_t device_tree;
-static class_list_t class_list;
 
 /** Register running driver. */
@@ -279,6 +278,8 @@
 		return;
 	}
-
+	
 	fun_node_t *fun = create_fun_node();
+	fun->ftype = ftype;
+	
 	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
 		fibril_rwlock_write_unlock(&tree->rwlock);
@@ -333,31 +334,5 @@
 }
 
-static void loc_register_class_dev(dev_class_info_t *cli)
-{
-	/* Create loc path and name for the service. */
-	char *loc_pathname = NULL;
-
-	asprintf(&loc_pathname, "%s/%s%c%s", LOC_CLASS_NAMESPACE,
-	    cli->dev_class->name, LOC_SEPARATOR, cli->dev_name);
-	if (loc_pathname == NULL)
-		return;
-	
-	/*
-	 * Register the device with location service and remember its
-	 * service ID.
-	 */
-	loc_service_register_with_iface(loc_pathname,
-	    &cli->service_id, DEVMAN_CONNECT_FROM_LOC);
-	
-	/*
-	 * Add device to the hash map of class devices registered with
-	 * location service.
-	 */
-	class_add_loc_function(&class_list, cli);
-	
-	free(loc_pathname);
-}
-
-static void devman_add_function_to_class(ipc_callid_t callid, ipc_call_t *call)
+static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
 {
 	devman_handle_t handle = IPC_GET_ARG1(*call);
@@ -365,7 +340,7 @@
 	int rc;
 	
-	/* Get class name. */
-	char *class_name;
-	rc = async_data_write_accept((void **) &class_name, true,
+	/* Get category name. */
+	char *cat_name;
+	rc = async_data_write_accept((void **) &cat_name, true,
 	    0, 0, 0, 0);
 	if (rc != EOK) {
@@ -380,21 +355,57 @@
 	}
 	
-	dev_class_t *cl = get_dev_class(&class_list, class_name);
-	dev_class_info_t *class_info = add_function_to_class(fun, cl, NULL);
-	
-	/* Register the device's class alias with location service. */
-	loc_register_class_dev(class_info);
-	
-	rc = loc_category_get_id(class_name, &cat_id, IPC_FLAG_BLOCKING);
+	rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
 	if (rc == EOK) {
 		loc_service_add_to_cat(fun->service_id, cat_id);
 	} else {
 		log_msg(LVL_ERROR, "Failed adding function `%s' to category "
-		    "`%s'.", fun->pathname, class_name);
-	}
-	
-	log_msg(LVL_NOTE, "Function `%s' added to class `%s' as `%s'.",
-	    fun->pathname, class_name, class_info->dev_name);
-
+		    "`%s'.", fun->pathname, cat_name);
+	}
+	
+	log_msg(LVL_NOTE, "Function `%s' added to category `%s'.",
+	    fun->pathname, cat_name);
+
+	async_answer_0(callid, EOK);
+}
+
+/** Remove function. */
+static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	devman_handle_t fun_handle = IPC_GET_ARG1(*call);
+	dev_tree_t *tree = &device_tree;
+	int rc;
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	fun_node_t *fun = find_fun_node_no_lock(&device_tree, fun_handle);
+	if (fun == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	log_msg(LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
+	
+	if (fun->ftype == fun_inner) {
+		/* Handle possible descendants */
+		/* TODO */
+		log_msg(LVL_WARN, "devman_remove_function(): not handling "
+		    "descendants\n");
+	} else {
+		/* Unregister from location service */
+		rc = loc_service_unregister(fun->service_id);
+		if (rc != EOK) {
+			log_msg(LVL_ERROR, "Failed unregistering tree service.");
+			fibril_rwlock_write_unlock(&tree->rwlock);
+			async_answer_0(callid, EIO);
+			return;
+		}
+	}
+	
+	remove_fun_node(&device_tree, fun);
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	delete_fun_node(fun);
+	
+	log_msg(LVL_DEBUG, "devman_remove_function() succeeded.");
 	async_answer_0(callid, EOK);
 }
@@ -449,6 +460,9 @@
 			devman_add_function(callid, &call);
 			break;
-		case DEVMAN_ADD_DEVICE_TO_CLASS:
-			devman_add_function_to_class(callid, &call);
+		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
+			devman_add_function_to_cat(callid, &call);
+			break;
+		case DEVMAN_REMOVE_FUNCTION:
+			devman_remove_function(callid, &call);
 			break;
 		default:
@@ -474,38 +488,4 @@
 	
 	free(pathname);
-
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	async_answer_1(iid, EOK, fun->handle);
-}
-
-/** Find handle for the device instance identified by device class name. */
-static void devman_function_get_handle_by_class(ipc_callid_t iid,
-    ipc_call_t *icall)
-{
-	char *classname;
-	char *devname;
-
-	int rc = async_data_write_accept((void **) &classname, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	rc = async_data_write_accept((void **) &devname, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		free(classname);
-		async_answer_0(iid, rc);
-		return;
-	}
-
-
-	fun_node_t *fun = find_fun_node_by_class(&class_list,
-	    classname, devname);
-
-	free(classname);
-	free(devname);
 
 	if (fun == NULL) {
@@ -554,4 +534,18 @@
 }
 
+/** Find handle for the function instance identified by its service ID. */
+static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+
+	fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	async_answer_1(iid, EOK, fun->handle);
+}
 
 /** Function for handling connections from a client to the device manager. */
@@ -572,9 +566,9 @@
 			devman_function_get_handle(callid, &call);
 			break;
-		case DEVMAN_DEVICE_GET_HANDLE_BY_CLASS:
-			devman_function_get_handle_by_class(callid, &call);
-			break;
 		case DEVMAN_DEVICE_GET_DEVICE_PATH:
 			devman_get_device_path_by_handle(callid, &call);
+			break;
+		case DEVMAN_FUN_SID_TO_HANDLE:
+			devman_fun_sid_to_handle(callid, &call);
 			break;
 		default:
@@ -678,8 +672,8 @@
 
 	fun = find_loc_tree_function(&device_tree, service_id);
-	if (fun == NULL)
-		fun = find_loc_class_function(&class_list, service_id);
 	
 	if (fun == NULL || fun->dev->drv == NULL) {
+		log_msg(LVL_WARN, "devman_connection_loc(): function "
+		    "not found.\n");
 		async_answer_0(iid, ENOENT);
 		return;
@@ -687,9 +681,4 @@
 	
 	dev = fun->dev;
-	
-	if ((dev->state != DEVICE_USABLE) || (!dev->drv->sess)) {
-		async_answer_0(iid, EINVAL);
-		return;
-	}
 	
 	async_exch_t *exch = async_exchange_begin(dev->drv->sess);
@@ -753,6 +742,4 @@
 	}
 
-	init_class_list(&class_list);
-	
 	/*
 	 * !!! devman_connection ... as the device manager is not a real loc
Index: uspace/srv/hid/input/ctl/kbdev.c
===================================================================
--- uspace/srv/hid/input/ctl/kbdev.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hid/input/ctl/kbdev.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -50,7 +50,6 @@
 #include <loc.h>
 #include <stdlib.h>
+#include <vfs/vfs_sess.h>
 #include <sys/typefmt.h>
-#include <vfs/vfs_sess.h>
-
 
 static int kbdev_ctl_init(kbd_dev_t *);
@@ -99,14 +98,10 @@
 	async_exch_t *exch;
 	kbdev_t *kbdev;
-	char *svc_name;
 	int rc;
 
-	if (asprintf(&svc_name, "devname%" PRIun, kdev->service_id) > 0)
-		svc_name = (char *) "unknown";
-
-	sess = loc_service_connect(EXCHANGE_SERIALIZE, kdev->service_id, 0);
+	sess = loc_service_connect(EXCHANGE_SERIALIZE, kdev->svc_id, 0);
 	if (sess == NULL) {
 		printf("%s: Failed starting session with '%s.'\n", NAME,
-		    svc_name);
+		    kdev->svc_name);
 		return -1;
 	}
@@ -115,5 +110,5 @@
 	if (kbdev == NULL) {
 		printf("%s: Failed allocating device structure for '%s'.\n",
-		    NAME, svc_name);
+		    NAME, kdev->svc_name);
 		return -1;
 	}
@@ -124,5 +119,5 @@
 	if (exch == NULL) {
 		printf("%s: Failed starting exchange with '%s'.\n", NAME,
-		    svc_name);
+		    kdev->svc_name);
 		kbdev_destroy(kbdev);
 		return -1;
@@ -132,5 +127,5 @@
 	if (rc != EOK) {
 		printf("%s: Failed creating callback connection from '%s'.\n",
-		    NAME, svc_name);
+		    NAME, kdev->svc_name);
 		async_exchange_end(exch);
 		kbdev_destroy(kbdev);
Index: uspace/srv/hid/input/generic/input.c
===================================================================
--- uspace/srv/hid/input/generic/input.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hid/input/generic/input.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -66,7 +66,4 @@
 #include <abi/ipc/methods.h>
 
-/* In microseconds */
-#define DISCOVERY_POLL_INTERVAL  (10 * 1000 * 1000)
-
 #define NUM_LAYOUTS  3
 
@@ -276,5 +273,5 @@
 	kdev->port_ops = port;
 	kdev->ctl_ops = ctl;
-	kdev->service_id = 0;
+	kdev->svc_id = 0;
 	
 	/* Initialize port driver. */
@@ -304,5 +301,5 @@
 	mdev->port_ops = port;
 	mdev->proto_ops = proto;
-	mdev->service_id = 0;
+	mdev->svc_id = 0;
 	
 	/* Initialize port driver. */
@@ -328,5 +325,5 @@
  *
  */
-static int kbd_add_kbdev(service_id_t service_id)
+static int kbd_add_kbdev(service_id_t service_id, kbd_dev_t **kdevp)
 {
 	kbd_dev_t *kdev = kbd_dev_new();
@@ -334,8 +331,14 @@
 		return -1;
 	
-	kdev->service_id = service_id;
+	kdev->svc_id = service_id;
 	kdev->port_ops = NULL;
 	kdev->ctl_ops = &kbdev_ctl;
 	
+	int rc = loc_service_get_name(service_id, &kdev->svc_name);
+	if (rc != EOK) {
+		kdev->svc_name = NULL;
+		goto fail;
+	}
+	
 	/* Initialize controller driver. */
 	if ((*kdev->ctl_ops->init)(kdev) != 0) {
@@ -344,7 +347,10 @@
 	
 	list_append(&kdev->kbd_devs, &kbd_devs);
+	*kdevp = kdev;
 	return EOK;
 	
 fail:
+	if (kdev->svc_name != NULL)
+		free(kdev->svc_name);
 	free(kdev);
 	return -1;
@@ -356,5 +362,5 @@
  *
  */
-static int mouse_add_mousedev(service_id_t service_id)
+static int mouse_add_mousedev(service_id_t service_id, mouse_dev_t **mdevp)
 {
 	mouse_dev_t *mdev = mouse_dev_new();
@@ -362,8 +368,14 @@
 		return -1;
 	
-	mdev->service_id = service_id;
+	mdev->svc_id = service_id;
 	mdev->port_ops = NULL;
 	mdev->proto_ops = &mousedev_proto;
 	
+	int rc = loc_service_get_name(service_id, &mdev->svc_name);
+	if (rc != EOK) {
+		mdev->svc_name = NULL;
+		goto fail;
+	}
+	
 	/* Initialize controller driver. */
 	if ((*mdev->proto_ops->init)(mdev) != 0) {
@@ -372,4 +384,5 @@
 	
 	list_append(&mdev->mouse_devs, &mouse_devs);
+	*mdevp = mdev;
 	return EOK;
 	
@@ -481,19 +494,10 @@
 }
 
-/** Periodically check for new input devices.
- *
- * Looks under /loc/class/keyboard and /loc/class/mouse.
- *
- * @param arg Ignored
- *
- */
-#include <sys/typefmt.h>
-static int dev_discovery_fibril(void *arg)
-{
-	category_id_t keyboard_cat, mouse_cat;
+static int dev_check_new_kbdevs(void)
+{
+	category_id_t keyboard_cat;
 	service_id_t *svcs;
 	size_t count, i;
 	bool already_known;
-	const char *dev_name = "todo";
 	int rc;
 	
@@ -504,4 +508,51 @@
 	}
 	
+	/*
+	 * Check for new keyboard devices
+	 */
+	rc = loc_category_get_svcs(keyboard_cat, &svcs, &count);
+	if (rc != EOK) {
+		printf("%s: Failed getting list of keyboard devices.\n",
+		    NAME);
+		return EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		already_known = false;
+		
+		/* Determine whether we already know this device. */
+		list_foreach(kbd_devs, kdev_link) {
+			kbd_dev_t *kdev = list_get_instance(kdev_link,
+			    kbd_dev_t, kbd_devs);
+			if (kdev->svc_id == svcs[i]) {
+				already_known = true;
+				break;
+			}
+		}
+		
+		if (!already_known) {
+			kbd_dev_t *kdev;
+			if (kbd_add_kbdev(svcs[i], &kdev) == EOK) {
+				printf("%s: Connected keyboard device '%s'\n",
+				    NAME, kdev->svc_name);
+			}
+		}
+	}
+	
+	free(svcs);
+	
+	/* XXX Handle device removal */
+	
+	return EOK;
+}
+
+static int dev_check_new_mousedevs(void)
+{
+	category_id_t mouse_cat;
+	service_id_t *svcs;
+	size_t count, i;
+	bool already_known;
+	int rc;
+	
 	rc = loc_category_get_id("mouse", &mouse_cat, IPC_FLAG_BLOCKING);
 	if (rc != EOK) {
@@ -510,88 +561,76 @@
 	}
 	
-	while (true) {
-		async_usleep(DISCOVERY_POLL_INTERVAL);
-		
-		/*
-		 * Check for new keyboard devices
-		 */
-		rc = loc_category_get_svcs(keyboard_cat, &svcs, &count);
-		if (rc != EOK) {
-			printf("%s: Failed getting list of keyboard devices.\n",
-			    NAME);
-			continue;
-		}
-
-		for (i = 0; i < count; i++) {
-			already_known = false;
-			
-			/* Determine whether we already know this device. */
-			list_foreach(kbd_devs, kdev_link) {
-				kbd_dev_t *kdev = list_get_instance(kdev_link,
-				    kbd_dev_t, kbd_devs);
-				if (kdev->service_id == svcs[i]) {
-					already_known = true;
-					break;
-				}
-			}
-
-			if (!already_known) {
-				if (kbd_add_kbdev(svcs[i]) == EOK) {
-					printf("%s: Connected keyboard device '%s'\n",
-					    NAME, dev_name);
-				}
+	/*
+	 * Check for new mouse devices
+	 */
+	rc = loc_category_get_svcs(mouse_cat, &svcs, &count);
+	if (rc != EOK) {
+		printf("%s: Failed getting list of mouse devices.\n",
+		    NAME);
+		return EIO;
+	}
+	
+	for (i = 0; i < count; i++) {
+		already_known = false;
+		
+		/* Determine whether we already know this device. */
+		list_foreach(mouse_devs, mdev_link) {
+			mouse_dev_t *mdev = list_get_instance(mdev_link,
+			    mouse_dev_t, mouse_devs);
+			if (mdev->svc_id == svcs[i]) {
+				already_known = true;
+				break;
 			}
 		}
 		
-		/* XXX Handle device removal */
-		
-		/*
-		 * Check for new mouse devices
-		 */
-		rc = loc_category_get_svcs(mouse_cat, &svcs, &count);
-		if (rc != EOK) {
-			printf("%s: Failed getting list of mouse devices.\n",
-			    NAME);
-			continue;
-		}
-
-		for (i = 0; i < count; i++) {
-			already_known = false;
-			
-			/* Determine whether we already know this device. */
-			list_foreach(mouse_devs, mdev_link) {
-				mouse_dev_t *mdev = list_get_instance(mdev_link,
-				    mouse_dev_t, mouse_devs);
-				if (mdev->service_id == svcs[i]) {
-					already_known = true;
-					break;
-				}
-			}
-
-			if (!already_known) {
-				if (mouse_add_mousedev(svcs[i]) == EOK) {
-					printf("%s: Connected mouse device '%s'\n",
-					    NAME, dev_name);
-				}
+		if (!already_known) {
+			mouse_dev_t *mdev;
+			if (mouse_add_mousedev(svcs[i], &mdev) == EOK) {
+				printf("%s: Connected mouse device '%s'\n",
+				    NAME, mdev->svc_name);
 			}
 		}
-		
-		/* XXX Handle device removal */
-	}
+	}
+	
+	free(svcs);
+	
+	/* XXX Handle device removal */
 	
 	return EOK;
 }
 
-/** Start a fibril for discovering new devices. */
-static void input_start_dev_discovery(void)
-{
-	fid_t fid = fibril_create(dev_discovery_fibril, NULL);
-	if (!fid) {
-		printf("%s: Failed to create device discovery fibril.\n",
-		    NAME);
-		return;
-	}
-	
-	fibril_add_ready(fid);
+static int dev_check_new(void)
+{
+	int rc;
+	
+	rc = dev_check_new_kbdevs();
+	if (rc != EOK)
+		return rc;
+	
+	rc = dev_check_new_mousedevs();
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static void cat_change_cb(void)
+{
+	dev_check_new();
+}
+
+/** Start listening for new devices. */
+static int input_start_dev_discovery(void)
+{
+	int rc;
+
+	rc = loc_register_cat_change_cb(cat_change_cb);
+	if (rc != EOK) {
+		printf("%s: Failed registering callback for device discovery. "
+		    "(%d)\n", NAME, rc);
+		return rc;
+	}
+
+	return dev_check_new();
 }
 
@@ -615,5 +654,5 @@
 	/* Add legacy keyboard devices. */
 	kbd_add_legacy_devs();
-
+	
 	/* Add legacy mouse devices. */
 	mouse_add_legacy_devs();
Index: uspace/srv/hid/input/include/kbd.h
===================================================================
--- uspace/srv/hid/input/include/kbd.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hid/input/include/kbd.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -51,5 +51,8 @@
 
 	/** Service ID (only for kbdev devices) */
-	service_id_t service_id;
+	service_id_t svc_id;
+
+	/** Device service name (only for kbdev devices) */
+	char *svc_name;
 
 	/** Port ops */
Index: uspace/srv/hid/input/include/mouse.h
===================================================================
--- uspace/srv/hid/input/include/mouse.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hid/input/include/mouse.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -49,5 +49,8 @@
 	
 	/** Service ID (only for mousedev devices) */
-	service_id_t service_id;
+	service_id_t svc_id;
+	
+	/** Device service name (only for mousedev devices) */
+	char *svc_name;
 	
 	/** Port ops */
Index: uspace/srv/hid/input/port/adb_mouse.c
===================================================================
--- uspace/srv/hid/input/port/adb_mouse.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hid/input/port/adb_mouse.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -42,4 +42,5 @@
 #include <errno.h>
 #include <loc.h>
+#include <stdio.h>
 
 static mouse_dev_t *mouse_dev;
Index: uspace/srv/hid/input/proto/mousedev.c
===================================================================
--- uspace/srv/hid/input/proto/mousedev.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hid/input/proto/mousedev.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -117,13 +117,9 @@
 static int mousedev_proto_init(mouse_dev_t *mdev)
 {
-	char *svc_name;
-	
-	if (asprintf(&svc_name, "devname%" PRIun, mdev->service_id) > 0)
-		svc_name = (char *) "unknown";
-	
 	async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE,
-	    mdev->service_id, 0);
+	    mdev->svc_id, 0);
 	if (sess == NULL) {
-		printf("%s: Failed starting session with '%s'\n", NAME, svc_name);
+		printf("%s: Failed starting session with '%s'\n", NAME,
+		    mdev->svc_name);
 		return -1;
 	}
@@ -132,5 +128,5 @@
 	if (mousedev == NULL) {
 		printf("%s: Failed allocating device structure for '%s'.\n",
-		    NAME, svc_name);
+		    NAME, mdev->svc_name);
 		return -1;
 	}
@@ -141,5 +137,5 @@
 	if (exch == NULL) {
 		printf("%s: Failed starting exchange with '%s'.\n", NAME,
-		    svc_name);
+		    mdev->svc_name);
 		mousedev_destroy(mousedev);
 		return -1;
@@ -151,5 +147,5 @@
 	if (rc != EOK) {
 		printf("%s: Failed creating callback connection from '%s'.\n",
-		    NAME, svc_name);
+		    NAME, mdev->svc_name);
 		mousedev_destroy(mousedev);
 		return -1;
Index: uspace/srv/hw/irc/apic/apic.c
===================================================================
--- uspace/srv/hw/irc/apic/apic.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/hw/irc/apic/apic.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -45,4 +45,5 @@
 #include <errno.h>
 #include <async.h>
+#include <stdio.h>
 
 #define NAME  "apic"
Index: uspace/srv/loader/main.c
===================================================================
--- uspace/srv/loader/main.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/loader/main.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -61,4 +61,5 @@
 #include <elf/elf.h>
 #include <elf/elf_load.h>
+#include <vfs/vfs.h>
 
 #ifdef CONFIG_RTLD
@@ -89,9 +90,5 @@
 
 /** Number of preset files */
-static int filc = 0;
-/** Preset files vector */
-static fdi_node_t **filv = NULL;
-/** Buffer holding all preset files */
-static fdi_node_t *fil_buf = NULL;
+static unsigned int filc = 0;
 
 static elf_info_t prog_info;
@@ -239,45 +236,23 @@
 static void ldr_set_files(ipc_callid_t rid, ipc_call_t *request)
 {
-	fdi_node_t *buf;
-	size_t buf_size;
-	int rc = async_data_write_accept((void **) &buf, false, 0, 0,
-	    sizeof(fdi_node_t), &buf_size);
-	
-	if (rc == EOK) {
-		int count = buf_size / sizeof(fdi_node_t);
-		
-		/*
-		 * Allocate new filv
-		 */
-		fdi_node_t **_filv = (fdi_node_t **) calloc(count + 1, sizeof(fdi_node_t *));
-		if (_filv == NULL) {
-			free(buf);
-			async_answer_0(rid, ENOMEM);
-			return;
+	size_t count = IPC_GET_ARG1(*request);
+
+	async_exch_t *vfs_exch = vfs_exchange_begin();
+
+	for (filc = 0; filc < count; filc++) {
+		ipc_callid_t callid;
+		int fd;
+
+		if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
+			async_answer_0(callid, EINVAL);
+			break;
 		}
-		
-		/*
-		 * Fill the new filv with argument pointers
-		 */
-		int i;
-		for (i = 0; i < count; i++)
-			_filv[i] = &buf[i];
-		
-		_filv[count] = NULL;
-		
-		/*
-		 * Copy temporary data to global variables
-		 */
-		if (fil_buf != NULL)
-			free(fil_buf);
-		
-		if (filv != NULL)
-			free(filv);
-		
-		filc = count;
-		fil_buf = buf;
-		filv = _filv;
-	}
-	
+		async_state_change_finalize(callid, vfs_exch);
+		fd = fd_wait();
+		assert(fd == (int) filc);
+	}
+
+	vfs_exchange_end(vfs_exch);
+
 	async_answer_0(rid, EOK);
 }
@@ -308,5 +283,4 @@
 	
 	pcb.filc = filc;
-	pcb.filv = filv;
 	
 	if (prog_info.interp == NULL) {
Index: uspace/srv/loc/category.c
===================================================================
--- uspace/srv/loc/category.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/loc/category.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -55,4 +55,35 @@
 }
 
+/** 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)
@@ -62,5 +93,5 @@
 	cat->id = loc_create_id();
 	link_initialize(&cat->cat_list);
-	list_initialize(&cat->services);
+	list_initialize(&cat->svc_memb);
 }
 
@@ -82,17 +113,38 @@
 {
 	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->services, item) {
-
-		loc_service_t *csvc = list_get_instance(item, loc_service_t,
-		    cat_services);
-		if (csvc == svc) {
+	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;
 		}
 	}
 
-	list_append(&svc->cat_services, &cat->services);
+	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);
 }
 
@@ -138,5 +190,5 @@
 	buf_cnt = buf_size / sizeof(service_id_t);
 
-	act_cnt = list_count(&cat->services);
+	act_cnt = list_count(&cat->svc_memb);
 	*act_size = act_cnt * sizeof(service_id_t);
 
@@ -145,10 +197,10 @@
 
 	size_t pos = 0;
-	list_foreach(cat->services, item) {
-		loc_service_t *svc =
-		    list_get_instance(item, loc_service_t, cat_services);
+	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] = svc->id;
+			id_buf[pos] = memb->svc->id;
 		pos++;
 	}
Index: uspace/srv/loc/category.h
===================================================================
--- uspace/srv/loc/category.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/loc/category.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -55,6 +55,6 @@
 	link_t cat_list;
 
-	/** List of services in this category (loc_service_t) */
-	list_t services;
+	/** List of service memberships in this category (svc_categ_t) */
+	list_t svc_memb;
 } category_t;
 
@@ -67,8 +67,24 @@
 } 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 *);
Index: uspace/srv/loc/loc.c
===================================================================
--- uspace/srv/loc/loc.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/loc/loc.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -44,4 +44,5 @@
 #include <bool.h>
 #include <fibril_synch.h>
+#include <macros.h>
 #include <stdlib.h>
 #include <str.h>
@@ -66,5 +67,5 @@
  **/
 
-static FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
+FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
 static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
 static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
@@ -83,4 +84,7 @@
 /** Service directory ogranized by categories (yellow pages) */
 static categ_dir_t cdir;
+
+static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
+static async_sess_t *callback_sess = NULL;
 
 service_id_t loc_create_id(void)
@@ -307,8 +311,19 @@
 {
 	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);
@@ -411,4 +426,5 @@
 	fibril_mutex_lock(&services_list_mutex);
 	fibril_mutex_lock(&server->services_mutex);
+	fibril_mutex_lock(&cdir.mutex);
 	
 	while (!list_empty(&server->services)) {
@@ -419,4 +435,5 @@
 	}
 	
+	fibril_mutex_unlock(&cdir.mutex);
 	fibril_mutex_unlock(&server->services_mutex);
 	fibril_mutex_unlock(&services_list_mutex);
@@ -488,4 +505,5 @@
 	link_initialize(&service->services);
 	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
 	
 	/* Check that service is not already registered */
@@ -525,9 +543,100 @@
  *
  */
-static int loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
+static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
     loc_server_t *server)
 {
-	/* TODO */
-	return EOK;
+	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;
+	
+	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;
+	}
+	
+	act_size = str_size(svc->name);
+	if (act_size > size) {
+		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, svc->name,
+	    min(size, act_size));
+	
+	fibril_mutex_unlock(&services_list_mutex);
+	
+	async_answer_0(iid, retval);
 }
 
@@ -687,4 +796,44 @@
  *
  */
+static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
+{
+	async_sess_t *cb_sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (cb_sess == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&callback_sess_mutex);
+	if (callback_sess != NULL) {
+		fibril_mutex_unlock(&callback_sess_mutex);
+		async_answer_0(iid, EEXIST);
+		return;
+	}
+	
+	callback_sess = cb_sess;
+	fibril_mutex_unlock(&callback_sess_mutex);
+	
+	async_answer_0(iid, EOK);
+}
+
+void loc_category_change_event(void)
+{
+	fibril_mutex_lock(&callback_sess_mutex);
+
+	if (callback_sess != NULL) {
+		async_exch_t *exch = async_exchange_begin(callback_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)
 {
@@ -755,4 +904,43 @@
 }
 
+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)
 {
@@ -1015,5 +1203,7 @@
 	
 	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);
 	
@@ -1049,4 +1239,6 @@
 
 	async_answer_0(iid, retval);
+
+	loc_category_change_event();
 }
 
@@ -1076,4 +1268,10 @@
 
 	cat = category_new("serial");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("usbhc");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virtual");
 	categ_dir_add_cat(&cdir, cat);
 
@@ -1158,10 +1356,19 @@
 			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);
@@ -1181,4 +1388,7 @@
 		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:
Index: uspace/srv/loc/loc.h
===================================================================
--- uspace/srv/loc/loc.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/loc/loc.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -36,4 +36,5 @@
 #define LOC_H_
 
+#include <ipc/loc.h>
 #include <async.h>
 #include <fibril_synch.h>
@@ -89,4 +90,6 @@
 	/** 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;
@@ -101,5 +104,8 @@
 } 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/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/vfs/vfs.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -37,4 +37,6 @@
 
 #include <ipc/services.h>
+#include <abi/ipc/event.h>
+#include <event.h>
 #include <ns.h>
 #include <async.h>
@@ -45,4 +47,5 @@
 #include <as.h>
 #include <atomic.h>
+#include <vfs/vfs.h>
 #include "vfs.h"
 
@@ -80,7 +83,4 @@
 			vfs_open(callid, &call);
 			break;
-		case VFS_IN_OPEN_NODE:
-			vfs_open_node(callid, &call);
-			break;
 		case VFS_IN_CLOSE:
 			vfs_close(callid, &call);
@@ -118,4 +118,8 @@
 		case VFS_IN_DUP:
 			vfs_dup(callid, &call);
+			break;
+		case VFS_IN_WAIT_HANDLE:
+			vfs_wait_handle(callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOTSUP);
@@ -128,4 +132,21 @@
 	 * connection fibril terminates.
 	 */
+}
+
+enum {
+	VFS_TASK_STATE_CHANGE
+};
+
+static void notification_received(ipc_callid_t callid, ipc_call_t *call)
+{
+	switch (IPC_GET_IMETHOD(*call)) {
+	case VFS_TASK_STATE_CHANGE:
+		if (IPC_GET_ARG1(*call) == VFS_PASS_HANDLE)
+			vfs_pass_handle(IPC_GET_ARG4(*call),
+			    IPC_GET_ARG5(*call), (int) IPC_GET_ARG2(*call));
+		break;
+	default:
+		break;
+	}
 }
 
@@ -170,4 +191,10 @@
 
 	/*
+	 * Set notification handler and subscribe to notifications.
+	 */
+	async_set_interrupt_received(notification_received);
+	event_task_subscribe(EVENT_TASK_STATE_CHANGE, VFS_TASK_STATE_CHANGE);
+
+	/*
 	 * Register at the naming service.
 	 */
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/vfs/vfs.h	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -175,5 +175,4 @@
 extern int vfs_lookup_internal(char *, int, vfs_lookup_res_t *,
     vfs_pair_t *, ...);
-extern int vfs_open_node_internal(vfs_lookup_res_t *);
 
 extern bool vfs_nodes_init(void);
@@ -189,4 +188,7 @@
 extern void vfs_client_data_destroy(void *);
 
+extern void vfs_pass_handle(sysarg_t, sysarg_t, int);
+extern int vfs_wait_handle_internal(void);
+
 extern vfs_file_t *vfs_file_get(int);
 extern void vfs_file_put(vfs_file_t *);
@@ -197,4 +199,5 @@
 extern void vfs_node_addref(vfs_node_t *);
 extern void vfs_node_delref(vfs_node_t *);
+extern int vfs_open_node_remote(vfs_node_t *);
 
 extern void vfs_register(ipc_callid_t, ipc_call_t *);
@@ -202,5 +205,4 @@
 extern void vfs_unmount(ipc_callid_t, ipc_call_t *);
 extern void vfs_open(ipc_callid_t, ipc_call_t *);
-extern void vfs_open_node(ipc_callid_t, ipc_call_t *);
 extern void vfs_sync(ipc_callid_t, ipc_call_t *);
 extern void vfs_dup(ipc_callid_t, ipc_call_t *);
@@ -215,4 +217,5 @@
 extern void vfs_unlink(ipc_callid_t, ipc_call_t *);
 extern void vfs_rename(ipc_callid_t, ipc_call_t *);
+extern void vfs_wait_handle(ipc_callid_t, ipc_call_t *);
 
 #endif
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/vfs/vfs_file.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -43,4 +43,5 @@
 #include <fibril.h>
 #include <fibril_synch.h>
+#include <adt/list.h>
 #include "vfs.h"
 
@@ -50,38 +51,57 @@
 typedef struct {
 	fibril_mutex_t lock;
+	fibril_condvar_t cv;
+	list_t passed_handles;
 	vfs_file_t **files;
 } vfs_client_data_t;
 
+typedef struct {
+	link_t link;
+	int handle;
+} vfs_boxed_handle_t;
+
+static int _vfs_fd_free(vfs_client_data_t *, int);
+
 /** Initialize the table of open files. */
-static bool vfs_files_init(void)
-{
-	fibril_mutex_lock(&VFS_DATA->lock);
-	if (!FILES) {
-		FILES = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
-		if (!FILES) {
-			fibril_mutex_unlock(&VFS_DATA->lock);
+static bool vfs_files_init(vfs_client_data_t *vfs_data)
+{
+	fibril_mutex_lock(&vfs_data->lock);
+	if (!vfs_data->files) {
+		vfs_data->files = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
+		if (!vfs_data->files) {
+			fibril_mutex_unlock(&vfs_data->lock);
 			return false;
 		}
-		memset(FILES, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
-	}
-	fibril_mutex_unlock(&VFS_DATA->lock);
+		memset(vfs_data->files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
+	}
+	fibril_mutex_unlock(&vfs_data->lock);
 	return true;
 }
 
 /** Cleanup the table of open files. */
-static void vfs_files_done(void)
+static void vfs_files_done(vfs_client_data_t *vfs_data)
 {
 	int i;
 
-	if (!FILES)
+	if (!vfs_data->files)
 		return;
 
 	for (i = 0; i < MAX_OPEN_FILES; i++) {
-		if (FILES[i]) {
-			(void) vfs_fd_free(i);
-		}
-	}
-	
-	free(FILES);
+		if (vfs_data->files[i])
+			(void) _vfs_fd_free(vfs_data, i);
+	}
+	
+	free(vfs_data->files);
+
+	while (!list_empty(&vfs_data->passed_handles)) {
+		link_t *lnk;
+		vfs_boxed_handle_t *bh;
+		
+		lnk = list_first(&vfs_data->passed_handles);
+		list_remove(lnk);
+
+		bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
+		free(bh);
+	}
 }
 
@@ -93,4 +113,6 @@
 	if (vfs_data) {
 		fibril_mutex_initialize(&vfs_data->lock);
+		fibril_condvar_initialize(&vfs_data->cv);
+		list_initialize(&vfs_data->passed_handles);
 		vfs_data->files = NULL;
 	}
@@ -103,5 +125,5 @@
 	vfs_client_data_t *vfs_data = (vfs_client_data_t *) data;
 
-	vfs_files_done();
+	vfs_files_done(vfs_data);
 	free(vfs_data);
 }
@@ -131,7 +153,7 @@
  *			incremented.
  */
-static void vfs_file_addref(vfs_file_t *file)
-{
-	assert(fibril_mutex_is_locked(&VFS_DATA->lock));
+static void vfs_file_addref(vfs_client_data_t *vfs_data, vfs_file_t *file)
+{
+	assert(fibril_mutex_is_locked(&vfs_data->lock));
 
 	file->refcnt++;
@@ -143,9 +165,9 @@
  *			decremented.
  */
-static int vfs_file_delref(vfs_file_t *file)
+static int vfs_file_delref(vfs_client_data_t *vfs_data, vfs_file_t *file)
 {
 	int rc = EOK;
 
-	assert(fibril_mutex_is_locked(&VFS_DATA->lock));
+	assert(fibril_mutex_is_locked(&vfs_data->lock));
 
 	if (file->refcnt-- == 1) {
@@ -162,16 +184,7 @@
 }
 
-
-/** Allocate a file descriptor.
- *
- * @param desc If true, look for an available file descriptor
- *             in a descending order.
- *
- * @return First available file descriptor or a negative error
- *         code.
- */
-int vfs_fd_alloc(bool desc)
-{
-	if (!vfs_files_init())
+static int _vfs_fd_alloc(vfs_client_data_t *vfs_data, bool desc)
+{
+	if (!vfs_files_init(vfs_data))
 		return ENOMEM;
 	
@@ -182,17 +195,17 @@
 		i = 0;
 	
-	fibril_mutex_lock(&VFS_DATA->lock);
+	fibril_mutex_lock(&vfs_data->lock);
 	while (true) {
-		if (!FILES[i]) {
-			FILES[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
-			if (!FILES[i]) {
-				fibril_mutex_unlock(&VFS_DATA->lock);
+		if (!vfs_data->files[i]) {
+			vfs_data->files[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
+			if (!vfs_data->files[i]) {
+				fibril_mutex_unlock(&vfs_data->lock);
 				return ENOMEM;
 			}
 			
-			memset(FILES[i], 0, sizeof(vfs_file_t));
-			fibril_mutex_initialize(&FILES[i]->lock);
-			vfs_file_addref(FILES[i]);
-			fibril_mutex_unlock(&VFS_DATA->lock);
+			memset(vfs_data->files[i], 0, sizeof(vfs_file_t));
+			fibril_mutex_initialize(&vfs_data->files[i]->lock);
+			vfs_file_addref(vfs_data, vfs_data->files[i]);
+			fibril_mutex_unlock(&vfs_data->lock);
 			return (int) i;
 		}
@@ -210,7 +223,40 @@
 		}
 	}
-	fibril_mutex_unlock(&VFS_DATA->lock);
+	fibril_mutex_unlock(&vfs_data->lock);
 	
 	return EMFILE;
+}
+
+/** Allocate a file descriptor.
+ *
+ * @param desc If true, look for an available file descriptor
+ *             in a descending order.
+ *
+ * @return First available file descriptor or a negative error
+ *         code.
+ */
+int vfs_fd_alloc(bool desc)
+{
+	return _vfs_fd_alloc(VFS_DATA, desc);
+}
+
+static int _vfs_fd_free(vfs_client_data_t *vfs_data, int fd)
+{
+	int rc;
+
+	if (!vfs_files_init(vfs_data))
+		return ENOMEM;
+
+	fibril_mutex_lock(&vfs_data->lock);	
+	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || !vfs_data->files[fd]) {
+		fibril_mutex_unlock(&vfs_data->lock);
+		return EBADF;
+	}
+	
+	rc = vfs_file_delref(vfs_data, vfs_data->files[fd]);
+	vfs_data->files[fd] = NULL;
+	fibril_mutex_unlock(&vfs_data->lock);
+	
+	return rc;
 }
 
@@ -224,20 +270,5 @@
 int vfs_fd_free(int fd)
 {
-	int rc;
-
-	if (!vfs_files_init())
-		return ENOMEM;
-
-	fibril_mutex_lock(&VFS_DATA->lock);	
-	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (FILES[fd] == NULL)) {
-		fibril_mutex_unlock(&VFS_DATA->lock);
-		return EBADF;
-	}
-	
-	rc = vfs_file_delref(FILES[fd]);
-	FILES[fd] = NULL;
-	fibril_mutex_unlock(&VFS_DATA->lock);
-	
-	return rc;
+	return _vfs_fd_free(VFS_DATA, fd);
 }
 
@@ -253,5 +284,5 @@
 int vfs_fd_assign(vfs_file_t *file, int fd)
 {
-	if (!vfs_files_init())
+	if (!vfs_files_init(VFS_DATA))
 		return ENOMEM;
 
@@ -263,5 +294,5 @@
 	
 	FILES[fd] = file;
-	vfs_file_addref(FILES[fd]);
+	vfs_file_addref(VFS_DATA, FILES[fd]);
 	fibril_mutex_unlock(&VFS_DATA->lock);
 	
@@ -269,29 +300,41 @@
 }
 
-/** Find VFS file structure for a given file descriptor.
- *
- * @param fd		File descriptor.
- *
- * @return		VFS file structure corresponding to fd.
- */
-vfs_file_t *vfs_file_get(int fd)
-{
-	if (!vfs_files_init())
+static vfs_file_t *_vfs_file_get(vfs_client_data_t *vfs_data, int fd)
+{
+	if (!vfs_files_init(vfs_data))
 		return NULL;
 	
-	fibril_mutex_lock(&VFS_DATA->lock);
+	fibril_mutex_lock(&vfs_data->lock);
 	if ((fd >= 0) && (fd < MAX_OPEN_FILES)) {
-		vfs_file_t *file = FILES[fd];
+		vfs_file_t *file = vfs_data->files[fd];
 		if (file != NULL) {
-			vfs_file_addref(file);
-			fibril_mutex_unlock(&VFS_DATA->lock);
+			vfs_file_addref(vfs_data, file);
+			fibril_mutex_unlock(&vfs_data->lock);
 			return file;
 		}
 	}
-	fibril_mutex_unlock(&VFS_DATA->lock);
+	fibril_mutex_unlock(&vfs_data->lock);
 	
 	return NULL;
 }
 
+/** Find VFS file structure for a given file descriptor.
+ *
+ * @param fd		File descriptor.
+ *
+ * @return		VFS file structure corresponding to fd.
+ */
+vfs_file_t *vfs_file_get(int fd)
+{
+	return _vfs_file_get(VFS_DATA, fd);
+}
+
+static void _vfs_file_put(vfs_client_data_t *vfs_data, vfs_file_t *file)
+{
+	fibril_mutex_lock(&vfs_data->lock);
+	vfs_file_delref(vfs_data, file);
+	fibril_mutex_unlock(&vfs_data->lock);
+}
+
 /** Stop using a file structure.
  *
@@ -300,7 +343,90 @@
 void vfs_file_put(vfs_file_t *file)
 {
-	fibril_mutex_lock(&VFS_DATA->lock);
-	vfs_file_delref(file);
-	fibril_mutex_unlock(&VFS_DATA->lock);
+	_vfs_file_put(VFS_DATA, file);
+}
+
+void vfs_pass_handle(sysarg_t donor_hash, sysarg_t acceptor_hash, int donor_fd)
+{
+	vfs_client_data_t *donor_data = NULL;
+	vfs_client_data_t *acceptor_data = NULL;
+	vfs_file_t *donor_file = NULL;
+	vfs_file_t *acceptor_file = NULL;
+	vfs_boxed_handle_t *bh;
+	int acceptor_fd;
+
+	acceptor_data = async_get_client_data_by_hash(acceptor_hash);
+	if (!acceptor_data)
+		return;
+
+	bh = malloc(sizeof(vfs_boxed_handle_t));
+	assert(bh);
+
+	link_initialize(&bh->link);
+	bh->handle = -1;
+
+	donor_data = async_get_client_data_by_hash(donor_hash);
+	if (!donor_data)
+		goto out;
+
+	donor_file = _vfs_file_get(donor_data, donor_fd);
+	if (!donor_file)
+		goto out;
+
+	acceptor_fd = _vfs_fd_alloc(acceptor_data, false);
+	if (acceptor_fd < 0)
+		goto out;
+
+	bh->handle = acceptor_fd;
+
+	/*
+	 * Add a new reference to the underlying VFS node.
+	 */
+	vfs_node_addref(donor_file->node);
+	(void) vfs_open_node_remote(donor_file->node);
+
+	acceptor_file = _vfs_file_get(acceptor_data, acceptor_fd);
+	assert(acceptor_file);
+
+	/*
+	 * Inherit attributes from the donor.
+	 */
+	acceptor_file->node = donor_file->node;
+	acceptor_file->append = donor_file->append;
+	acceptor_file->pos = donor_file->pos;
+
+out:
+	fibril_mutex_lock(&acceptor_data->lock);
+	list_append(&bh->link, &acceptor_data->passed_handles);
+	fibril_condvar_broadcast(&acceptor_data->cv);
+	fibril_mutex_unlock(&acceptor_data->lock);
+
+	if (donor_data)
+		async_put_client_data_by_hash(donor_hash);
+	if (acceptor_data)
+		async_put_client_data_by_hash(acceptor_hash);
+	if (donor_file)
+		_vfs_file_put(donor_data, donor_file);
+	if (acceptor_file)
+		_vfs_file_put(acceptor_data, acceptor_file);
+
+}
+
+int vfs_wait_handle_internal(void)
+{
+	vfs_client_data_t *vfs_data = VFS_DATA;	
+	int fd;
+	
+	fibril_mutex_lock(&vfs_data->lock);
+	while (list_empty(&vfs_data->passed_handles))
+		fibril_condvar_wait(&vfs_data->cv, &vfs_data->lock);
+	link_t *lnk = list_first(&vfs_data->passed_handles);
+	list_remove(lnk);
+	fibril_mutex_unlock(&vfs_data->lock);
+
+	vfs_boxed_handle_t *bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
+	fd = bh->handle;
+	free(bh);
+
+	return fd;
 }
 
Index: uspace/srv/vfs/vfs_lookup.c
===================================================================
--- uspace/srv/vfs/vfs_lookup.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/vfs/vfs_lookup.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -201,37 +201,4 @@
 }
 
-/** Perform a node open operation.
- *
- * @return EOK on success or an error code from errno.h.
- *
- */
-int vfs_open_node_internal(vfs_lookup_res_t *result)
-{
-	async_exch_t *exch = vfs_exchange_grab(result->triplet.fs_handle);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
-	    (sysarg_t) result->triplet.service_id,
-	    (sysarg_t) result->triplet.index, &answer);
-	
-	sysarg_t rc;
-	async_wait_for(req, &rc);
-	vfs_exchange_release(exch);
-	
-	if (rc == EOK) {
-		result->size =
-		    MERGE_LOUP32(IPC_GET_ARG1(answer), IPC_GET_ARG2(answer));
-		result->lnkcnt = (unsigned int) IPC_GET_ARG3(answer);
-		if (IPC_GET_ARG4(answer) & L_FILE)
-			result->type = VFS_NODE_FILE;
-		else if (IPC_GET_ARG4(answer) & L_DIRECTORY)
-			result->type = VFS_NODE_DIRECTORY;
-		else
-			result->type = VFS_NODE_UNKNOWN;
-	}
-	
-	return rc;
-}
-
 /**
  * @}
Index: uspace/srv/vfs/vfs_node.c
===================================================================
--- uspace/srv/vfs/vfs_node.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/vfs/vfs_node.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -293,4 +293,26 @@
 }
 
+
+/** Perform a remote node open operation.
+ *
+ * @return EOK on success or an error code from errno.h.
+ *
+ */
+int vfs_open_node_remote(vfs_node_t *node)
+{
+	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
+	    (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);
+	
+	vfs_exchange_release(exch);
+
+	sysarg_t rc;
+	async_wait_for(req, &rc);
+	
+	return rc;
+}
+
 /**
  * @}
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision ac7f81d7ebec01bd079fe1251f6be26b48b73a44)
+++ uspace/srv/vfs/vfs_ops.c	(revision d767cb1f2e5e380442819b0ff1e5a265d08b8060)
@@ -618,77 +618,4 @@
 }
 
-void vfs_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	// FIXME: check for sanity of the supplied fs, dev and index
-	
-	/*
-	 * The interface is open_node(fs, dev, index, oflag).
-	 */
-	vfs_lookup_res_t lr;
-	
-	lr.triplet.fs_handle = IPC_GET_ARG1(*request);
-	lr.triplet.service_id = IPC_GET_ARG2(*request);
-	lr.triplet.index = IPC_GET_ARG3(*request);
-	int oflag = IPC_GET_ARG4(*request);
-	
-	fibril_rwlock_read_lock(&namespace_rwlock);
-	
-	int rc = vfs_open_node_internal(&lr);
-	if (rc != EOK) {
-		fibril_rwlock_read_unlock(&namespace_rwlock);
-		async_answer_0(rid, rc);
-		return;
-	}
-	
-	vfs_node_t *node = vfs_node_get(&lr);
-	fibril_rwlock_read_unlock(&namespace_rwlock);
-	
-	/* Truncate the file if requested and if necessary. */
-	if (oflag & O_TRUNC) {
-		fibril_rwlock_write_lock(&node->contents_rwlock);
-		if (node->size) {
-			rc = vfs_truncate_internal(node->fs_handle,
-			    node->service_id, node->index, 0);
-			if (rc) {
-				fibril_rwlock_write_unlock(&node->contents_rwlock);
-				vfs_node_put(node);
-				async_answer_0(rid, rc);
-				return;
-			}
-			node->size = 0;
-		}
-		fibril_rwlock_write_unlock(&node->contents_rwlock);
-	}
-	
-	/*
-	 * Get ourselves a file descriptor and the corresponding vfs_file_t
-	 * structure.
-	 */
-	int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
-	if (fd < 0) {
-		vfs_node_put(node);
-		async_answer_0(rid, fd);
-		return;
-	}
-	vfs_file_t *file = vfs_file_get(fd);
-	file->node = node;
-	if (oflag & O_APPEND)
-		file->append = true;
-	
-	/*
-	 * The following increase in reference count is for the fact that the
-	 * file is being opened and that a file structure is pointing to it.
-	 * It is necessary so that the file will not disappear when
-	 * vfs_node_put() is called. The reference will be dropped by the
-	 * respective VFS_IN_CLOSE.
-	 */
-	vfs_node_addref(node);
-	vfs_node_put(node);
-	vfs_file_put(file);
-	
-	/* Success! Return the new file descriptor to the client. */
-	async_answer_1(rid, EOK, fd);
-}
-
 void vfs_sync(ipc_callid_t rid, ipc_call_t *request)
 {
@@ -1349,4 +1276,10 @@
 }
 
+void vfs_wait_handle(ipc_callid_t rid, ipc_call_t *request)
+{
+	int fd = vfs_wait_handle_internal();
+	async_answer_1(rid, EOK, fd);
+}
+
 /**
  * @}
