Index: generic/include/errno.h
===================================================================
--- generic/include/errno.h	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/include/errno.h	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -36,4 +36,6 @@
 #define ELIMIT     -3  /* Limit exceeded */
 #define EREFUSED   -4  /* Connection refused */
+#define EFORWARD   -5  /* Forward error */
+#define EPERM      -6  /* Permission denied */
 
 #endif
Index: generic/include/ipc/ipc.h
===================================================================
--- generic/include/ipc/ipc.h	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/include/ipc/ipc.h	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -72,5 +72,4 @@
  * These methods have special behaviour
  */
-#define IPC_M_IAMCONNECTING   0
 /** Protocol for CONNECT - TO - ME 
  *
@@ -78,5 +77,5 @@
  * so that it can start initiating new messages.
  *
- * The protocol for negotiating is as follows:
+ * The protocol for negotiating is:
  * - sys_connecttome - sends a message IPC_M_CONNECTTOME
  * - sys_wait_for_call - upon receipt tries to allocate new phone
@@ -86,14 +85,32 @@
  *                       error is sent back to caller. Otherwise 
  *                       the call is accepted and the response is sent back.
- *                     - the allocated phoneid is passed to userspace as
- *                       ARG3 of the call.
+ *                     - the allocated phoneid is passed to userspace 
+ *                       (on the receiving sid) as ARG3 of the call.
  *                     - the caller obtains taskid of the called thread
  */
 #define IPC_M_CONNECTTOME     1
+/** Protocol for CONNECT - ME - TO
+ *
+ * Calling process asks the callee to create for him a new connection.
+ * E.g. the caller wants a name server to connect him to print server.
+ *
+ * The protocol for negotiating is:
+ * - sys_connect_me_to - send a synchronous message to name server
+ *                       indicating that it wants to be connected to some
+ *                       service
+ *   recepient         -  if ipc_answer == 0, then accept connection
+ *                     -  otherwise connection refused
+ *                     -  recepient may forward message. Forwarding
+ *                        system message 
+ *
+ */
 #define IPC_M_CONNECTMETO     2
+/* Control messages that the server sends to the processes 
+ * about their connections.
+ */
 
 
 /* Well-known methods */
-#define IPC_M_FIRST_USER      512
+#define IPC_M_LAST_SYSTEM     511
 #define IPC_M_PING            512
 /* User methods */
@@ -124,6 +141,5 @@
 	task_t *task;
 
-	mutex_t mutex;
-	condvar_t cv;
+	waitq_t wq;
 
 	link_t connected_phones; /**< Phones connected to this answerbox */
@@ -138,4 +154,5 @@
 	link_t list;
 	answerbox_t *callee;
+	int busy;
 } phone_t;
 
@@ -146,10 +163,11 @@
 extern void ipc_call_sync(phone_t *phone, call_t *request);
 extern void ipc_phone_destroy(phone_t *phone);
-extern void ipc_phone_init(phone_t *phone, answerbox_t *box);
+extern void ipc_phone_init(phone_t *phone);
+extern void ipc_phone_connect(phone_t *phone, answerbox_t *box);
 extern void ipc_call_free(call_t *call);
 extern call_t * ipc_call_alloc(void);
 extern void ipc_answerbox_init(answerbox_t *box);
-extern void ipc_phone_init(phone_t *phone, answerbox_t *box);
 extern void ipc_call_init(call_t *call);
+extern void ipc_forward(call_t *call, answerbox_t *newbox,answerbox_t *oldbox);
 
 extern answerbox_t *ipc_phone_0;
Index: generic/include/ipc/sysipc.h
===================================================================
--- generic/include/ipc/sysipc.h	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/include/ipc/sysipc.h	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -44,4 +44,9 @@
 __native sys_ipc_wait_for_call(ipc_data_t *calldata, task_id_t *taskid,
 			       __native flags);
+__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
+			       __native arg2);
+__native sys_ipc_forward_fast(__native callid, __native phoneid,
+			      __native method, __native arg1);
+
 
 #endif
Index: generic/include/syscall/syscall.h
===================================================================
--- generic/include/syscall/syscall.h	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/include/syscall/syscall.h	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -41,6 +41,8 @@
 	SYS_IPC_ANSWER_FAST,
 	SYS_IPC_ANSWER,
+	SYS_IPC_FORWARD_FAST,
 	SYS_IPC_WAIT,
 	SYS_IPC_CONNECT_TO_ME,
+	SYS_IPC_CONNECT_ME_TO,
 	SYSCALL_END
 } syscall_t;
Index: generic/src/ipc/ipc.c
===================================================================
--- generic/src/ipc/ipc.c	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/src/ipc/ipc.c	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -32,6 +32,6 @@
  */
 
-#include <synch/condvar.h>
-#include <synch/mutex.h>
+#include <synch/spinlock.h>
+#include <synch/waitq.h>
 #include <ipc/ipc.h>
 #include <errno.h>
@@ -85,6 +85,6 @@
 void ipc_answerbox_init(answerbox_t *box)
 {
-	mutex_initialize(&box->mutex);
-	condvar_initialize(&box->cv);
+	spinlock_initialize(&box->lock, "ipc_box_lock");
+	waitq_initialize(&box->wq);
 	list_initialize(&box->connected_phones);
 	list_initialize(&box->calls);
@@ -94,15 +94,23 @@
 }
 
+/** Connect phone to answerbox */
+void ipc_phone_connect(phone_t *phone, answerbox_t *box)
+{
+	ASSERT(!phone->callee);
+	phone->busy = 1;
+	phone->callee = box;
+
+	spinlock_lock(&box->lock);
+	list_append(&phone->list, &box->connected_phones);
+	spinlock_unlock(&box->lock);
+}
+
 /** Initialize phone structure and connect phone to naswerbox
  */
-void ipc_phone_init(phone_t *phone, answerbox_t *box)
+void ipc_phone_init(phone_t *phone)
 {
 	spinlock_initialize(&phone->lock, "phone_lock");
-	
-	phone->callee = box;
-
-	mutex_lock(&box->mutex);
-	list_append(&phone->list, &box->connected_phones);
-	mutex_unlock(&box->mutex);
+	phone->callee = NULL;
+	phone->busy = 0;
 }
 
@@ -114,7 +122,7 @@
 	ASSERT(box);
 
-	mutex_lock(&box->mutex);
+	spinlock_lock(&box->lock);
 	list_remove(&phone->list);
-	mutex_unlock(&box->mutex);
+	spinlock_unlock(&box->lock);
 }
 
@@ -138,5 +146,5 @@
  * @param request Request to be sent
  */
-void ipc_call(phone_t *phone, call_t *request)
+void ipc_call(phone_t *phone, call_t *call)
 {
 	answerbox_t *box = phone->callee;
@@ -144,8 +152,26 @@
 	ASSERT(box);
 
-	mutex_lock(&box->mutex);
-	list_append(&request->list, &box->calls);
-	mutex_unlock(&box->mutex);
-	condvar_signal(&box->cv);
+	spinlock_lock(&box->lock);
+	list_append(&call->list, &box->calls);
+	spinlock_unlock(&box->lock);
+	waitq_wakeup(&box->wq, 0);
+}
+
+/** Forwards call from one answerbox to a new one
+ *
+ * @param request Request to be forwarded
+ * @param newbox Target answerbox
+ * @param oldbox Old answerbox
+ */
+void ipc_forward(call_t *call, answerbox_t *newbox, answerbox_t *oldbox)
+{
+	spinlock_lock(&oldbox->lock);
+	list_remove(&call->list);
+	spinlock_unlock(&oldbox->lock);
+
+	spinlock_lock(&newbox->lock);
+	list_append(&call->list, &newbox->calls);
+	spinlock_lock(&newbox->lock);
+	waitq_wakeup(&newbox->wq, 0);
 }
 
@@ -161,12 +187,12 @@
 	request->flags |= IPC_CALL_ANSWERED;
 
-	mutex_lock(&box->mutex);
+	spinlock_lock(&box->lock);
 	list_remove(&request->list);
-	mutex_unlock(&box->mutex);
-
-	mutex_lock(&callerbox->mutex);
+	spinlock_unlock(&box->lock);
+
+	spinlock_lock(&callerbox->lock);
 	list_append(&request->list, &callerbox->answers);
-	mutex_unlock(&callerbox->mutex);
-	condvar_signal(&callerbox->cv);
+	spinlock_unlock(&callerbox->lock);
+	waitq_wakeup(&callerbox->wq, 0);
 }
 
@@ -180,5 +206,5 @@
 	call_t *request;
 
-	mutex_lock(&box->mutex);
+	spinlock_lock(&box->lock);
 	while (1) { 
 		if (!list_empty(&box->answers)) {
@@ -195,5 +221,7 @@
 			if (!(flags & IPC_WAIT_NONBLOCKING)) {
 				/* Wait for event to appear */
-				condvar_wait(&box->cv, &box->mutex);
+				spinlock_unlock(&box->lock);
+				waitq_sleep(&box->wq);
+				spinlock_lock(&box->lock);
 				continue;
 			}
@@ -202,5 +230,5 @@
 		break;
 	}
-	mutex_unlock(&box->mutex);
+	spinlock_unlock(&box->lock);
 	return request;
 }
Index: generic/src/ipc/sysipc.c
===================================================================
--- generic/src/ipc/sysipc.c	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/src/ipc/sysipc.c	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -41,6 +41,34 @@
  * on phone, add counter + thread_kill??
  *
- * - don't allow userspace app to send system messages
- */
+ */
+
+/** Return true if the method is a system method */
+static inline int is_system_method(__native method)
+{
+	if (method <= IPC_M_LAST_SYSTEM)
+		return 1;
+	return 0;
+}
+
+/** Return true if the message with this method is forwardable
+ *
+ * - some system messages may be forwarded, for some of them
+ *   it is useless
+ */
+static inline int is_forwardable(__native method)
+{
+	return 1;
+}
+
+/** Find call_t * in call table according to callid
+ *
+ * @return NULL on not found, otherwise pointer to call structure
+ */
+static inline call_t * get_call(__native callid)
+{
+	/* TODO: Traverse list of dispatched calls and find one */
+	/* TODO: locking of call, ripping it from dispatched calls etc.  */
+	return (call_t *) callid;
+}
 
 /** Return pointer to phone identified by phoneid or NULL if non-existent */
@@ -59,5 +87,5 @@
 
 /** Allocate new phone slot in current TASK structure */
-static int phone_alloc(answerbox_t *callee)
+static int phone_alloc(void)
 {
 	int i;
@@ -66,6 +94,6 @@
 	
 	for (i=0; i < IPC_MAX_PHONES; i++) {
-		if (!TASK->phones[i].callee) {
-			TASK->phones[i].callee = callee;
+		if (!TASK->phones[i].busy) {
+			TASK->phones[i].busy = 1;
 			break;
 		}
@@ -83,8 +111,18 @@
 	spinlock_lock(&TASK->lock);
 
-	ASSERT(TASK->phones[phoneid].callee);
-
-	TASK->phones[phoneid].callee = NULL;
+	ASSERT(TASK->phones[phoneid].busy);
+
+	if (TASK->phones[phoneid].callee)
+		ipc_phone_destroy(&TASK->phones[phoneid]);
+
+	TASK->phones[phoneid].busy = 0;
 	spinlock_unlock(&TASK->lock);
+}
+
+static void phone_connect(int phoneid, answerbox_t *box)
+{
+	phone_t *phone = &TASK->phones[phoneid];
+	
+	ipc_phone_connect(phone, box);
 }
 
@@ -105,14 +143,16 @@
 
 /** Interpret process answer as control information */
-static inline void answer_preprocess(call_t *answer, call_t *call)
+static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
 {
 	int phoneid;
 
-	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
+	if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
+		phoneid = IPC_GET_ARG3(*olddata);
 		if (IPC_GET_RETVAL(answer->data)) {
 			/* The connection was not accepted */
-			/* TODO...race for multi-threaded process */
-			phoneid = IPC_GET_ARG3(call->data);
 			phone_dealloc(phoneid);
+		} else {
+			/* The connection was accepted */
+			phone_connect(phoneid,&answer->sender->answerbox);
 		}
 	}
@@ -139,5 +179,5 @@
 
 	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
-		phoneid = phone_alloc(&call->sender->answerbox);
+		phoneid = phone_alloc();
 		if (phoneid < 0) { /* Failed to allocate phone */
 			IPC_SET_RETVAL(call->data, ELIMIT);
@@ -163,5 +203,8 @@
 	phone = get_phone(phoneid);
 	if (!phone)
-		return IPC_CALLRET_FATAL;
+		return ENOENT;
+
+	if (is_system_method(method))
+		return EPERM;
 
 	ipc_call_init(&call);
@@ -185,8 +228,11 @@
 	phone = get_phone(phoneid);
 	if (!phone)
-		return IPC_CALLRET_FATAL;
+		return ENOENT;
 
 	ipc_call_init(&call);
 	copy_from_uspace(&call.data, question, sizeof(call.data));
+
+	if (is_system_method(IPC_GET_METHOD(call.data)))
+		return EPERM;
 	
 	ipc_call_sync(phone, &call);
@@ -223,4 +269,7 @@
 	phone = get_phone(phoneid);
 	if (!phone)
+		return IPC_CALLRET_FATAL;
+
+	if (is_system_method(method))
 		return IPC_CALLRET_FATAL;
 
@@ -256,8 +305,56 @@
 	call = ipc_call_alloc();
 	copy_from_uspace(&call->data, data, sizeof(call->data));
+
+	if (is_system_method(IPC_GET_METHOD(call->data))) {
+		ipc_call_free(call);
+		return EPERM;
+	}
 	
 	ipc_call(phone, call);
 
 	return (__native) call;
+}
+
+/** Forward received call to another destination
+ *
+ * The arg1 and arg2 are changed in the forwarded message
+ */
+__native sys_ipc_forward_fast(__native callid, __native phoneid,
+			      __native method, __native arg1)
+{
+	call_t *call;
+	phone_t *phone;
+
+	call = get_call(callid);
+	if (!call)
+		return ENOENT;
+
+	phone = get_phone(phoneid);
+	if (!phone) {
+		IPC_SET_RETVAL(call->data, EFORWARD);
+		ipc_answer(&TASK->answerbox, call);
+		return ENOENT;
+	}
+
+	if (!is_forwardable(IPC_GET_METHOD(call->data))) {
+		IPC_SET_RETVAL(call->data, EFORWARD);
+		ipc_answer(&TASK->answerbox, call);
+		return EPERM;
+	}
+
+	/* Userspace is not allowed to change method of system methods
+	 * on forward, allow changing ARG1 and ARG2 by means of method and arg1
+	 */
+	if (is_system_method(IPC_GET_METHOD(call->data))) {
+		IPC_SET_ARG1(call->data, method);
+		IPC_SET_ARG2(call->data, arg1);
+	} else {
+		IPC_SET_METHOD(call->data, method);
+		IPC_SET_ARG1(call->data, arg1);
+	}
+
+	ipc_forward(call, phone->callee, &TASK->answerbox);
+
+	return 0;
 }
 
@@ -267,14 +364,13 @@
 {
 	call_t *call;
-	call_t saved_call;
+	ipc_data_t saved_data;
 	int preprocess = 0;
 
-	/* Check that the user is not sending us answer callid */
-	ASSERT(! (callid & 1));
-	/* TODO: Check that the callid is in the dispatch table */
-	call = (call_t *) callid;
+	call = get_call(callid);
+	if (!call)
+		return ENOENT;
 
 	if (answer_will_preprocess(call)) {
-		memcpy(&saved_call.data, &call->data, sizeof(call->data));
+		memcpy(&saved_data, &call->data, sizeof(call->data));
 		preprocess = 1;
 	}
@@ -283,6 +379,7 @@
 	IPC_SET_ARG1(call->data, arg1);
 	IPC_SET_ARG2(call->data, arg2);
+
 	if (preprocess)
-		answer_preprocess(call, &saved_call);
+		answer_preprocess(call, &saved_data);
 
 	ipc_answer(&TASK->answerbox, call);
@@ -294,14 +391,13 @@
 {
 	call_t *call;
-	call_t saved_call;
+	ipc_data_t saved_data;
 	int preprocess = 0;
 
-	/* Check that the user is not sending us answer callid */
-	ASSERT(! (callid & 1));
-	/* TODO: Check that the callid is in the dispatch table */
-	call = (call_t *) callid;
+	call = get_call(callid);
+	if (!call)
+		return ENOENT;
 
 	if (answer_will_preprocess(call)) {
-		memcpy(&saved_call.data, &call->data, sizeof(call->data));
+		memcpy(&saved_data, &call->data, sizeof(call->data));
 		preprocess = 1;
 	}
@@ -309,5 +405,5 @@
 
 	if (preprocess)
-		answer_preprocess(call, &saved_call);
+		answer_preprocess(call, &saved_data);
 	
 	ipc_answer(&TASK->answerbox, call);
@@ -328,5 +424,5 @@
 	phone = get_phone(phoneid);
 	if (!phone)
-		return IPC_CALLRET_FATAL;
+		return ENOENT;
 
 	ipc_call_init(&call);
@@ -344,4 +440,31 @@
 }
 
+/** Ask target process to connect me somewhere
+ *
+ * @return phoneid - on success, error otherwise
+ */
+__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
+			       __native arg2)
+{
+	call_t call;
+	phone_t *phone;
+
+	phone = get_phone(phoneid);
+	if (!phone)
+		return ENOENT;
+
+	ipc_call_init(&call);
+	IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
+	IPC_SET_ARG1(call.data, arg1);
+	IPC_SET_ARG2(call.data, arg2);
+
+	ipc_call_sync(phone, &call);
+	if (!IPC_GET_RETVAL(call.data)) {
+		/* Everybody accepted, we should be connected by now */
+	}
+
+	return 0;
+}
+
 /** Wait for incoming ipc call or answer
  *
@@ -363,4 +486,5 @@
 restart:	
 	call = ipc_wait_for_call(&TASK->answerbox, flags);
+	printf("Received call %P from sender: %P\n", call, call->sender);
 
 	if (call->flags & IPC_CALL_ANSWERED) {
Index: generic/src/main/kinit.c
===================================================================
--- generic/src/main/kinit.c	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/src/main/kinit.c	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -144,7 +144,8 @@
 
 		utask = task_run_program((void *) init.tasks[i].addr);
-		if (utask) 
-			ipc_phone_0 = &utask->answerbox;
-		else 
+		if (utask) {
+			if (!ipc_phone_0) 
+				ipc_phone_0 = &utask->answerbox;
+		} else
 			printf("Userspace not started.\n");
 	}
Index: generic/src/proc/task.c
===================================================================
--- generic/src/proc/task.c	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/src/proc/task.c	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -70,4 +70,5 @@
 	ipl_t ipl;
 	task_t *ta;
+	int i;
 	
 	ta = (task_t *) malloc(sizeof(task_t), 0);
@@ -78,8 +79,10 @@
 	ta->as = as;
 
+	
 	ipc_answerbox_init(&ta->answerbox);
-	memsetb((__address)&ta->phones, sizeof(ta->phones[0])*IPC_MAX_PHONES, 0);
+	for (i=0; i < IPC_MAX_PHONES;i++)
+		ipc_phone_init(&ta->phones[i]);
 	if (ipc_phone_0)
-		ipc_phone_init(&ta->phones[0], ipc_phone_0);
+		ipc_phone_connect(&ta->phones[0], ipc_phone_0);
 	atomic_set(&ta->active_calls, 0);
 	
Index: generic/src/syscall/syscall.c
===================================================================
--- generic/src/syscall/syscall.c	(revision d764ddcbe18be0470e47c313dfb3b7325aad5b11)
+++ generic/src/syscall/syscall.c	(revision 2ba78108c2b47014f0f5835ad107f014e6f9e4d0)
@@ -82,5 +82,7 @@
 	sys_ipc_answer_fast,
 	sys_ipc_answer,
+	sys_ipc_forward_fast,
 	sys_ipc_wait_for_call,
-	sys_ipc_connect_to_me
+	sys_ipc_connect_to_me,
+	sys_ipc_connect_me_to
 };
