From 86de034767ea5ce560a55fa1cb59fec9ea6fcc20 Mon Sep 17 00:00:00 2001
From: Jo-Philipp Wich <jow@openwrt.org>
Date: Mon, 9 Jul 2012 00:08:20 +0000
Subject: uhttpd: various fixes

    - avoid closing descriptors before removing them from uloop (#11755, #11830)
    - do not auto-initialize ubus if no prefix is set (#11832)
    - remove extraneous client context pointer from cgi and lua states
    - code cleanups and debug message changes

SVN-Revision: 32651
---
 package/uhttpd/Makefile           |  2 +-
 package/uhttpd/src/uhttpd-cgi.c   | 83 +++++++++++++++++----------------------
 package/uhttpd/src/uhttpd-cgi.h   |  3 --
 package/uhttpd/src/uhttpd-lua.c   | 55 +++++++++++---------------
 package/uhttpd/src/uhttpd-lua.h   |  3 --
 package/uhttpd/src/uhttpd-utils.c | 62 ++++++++++++++++++++++-------
 package/uhttpd/src/uhttpd-utils.h |  6 ++-
 package/uhttpd/src/uhttpd.c       | 78 +++++++++++++++++-------------------
 package/uhttpd/src/uhttpd.h       |  3 +-
 9 files changed, 153 insertions(+), 142 deletions(-)

diff --git a/package/uhttpd/Makefile b/package/uhttpd/Makefile
index 7cd75807ea..89ca4b69e8 100644
--- a/package/uhttpd/Makefile
+++ b/package/uhttpd/Makefile
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=uhttpd
-PKG_RELEASE:=37
+PKG_RELEASE:=38
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 PKG_CONFIG_DEPENDS := \
diff --git a/package/uhttpd/src/uhttpd-cgi.c b/package/uhttpd/src/uhttpd-cgi.c
index aa79478115..e527922624 100644
--- a/package/uhttpd/src/uhttpd-cgi.c
+++ b/package/uhttpd/src/uhttpd-cgi.c
@@ -128,8 +128,6 @@ static char * uh_cgi_header_lookup(struct http_response *res,
 
 static void uh_cgi_shutdown(struct uh_cgi_state *state)
 {
-	close(state->rfd);
-	close(state->wfd);
 	free(state);
 }
 
@@ -139,38 +137,36 @@ static bool uh_cgi_socket_cb(struct client *cl)
 	char buf[UH_LIMIT_MSGHEAD];
 
 	struct uh_cgi_state *state = (struct uh_cgi_state *)cl->priv;
-	struct http_response *res = &state->cl->response;
-	struct http_request *req = &state->cl->request;
+	struct http_response *res = &cl->response;
+	struct http_request *req = &cl->request;
 
 	/* there is unread post data waiting */
 	while (state->content_length > 0)
 	{
 		/* remaining data in http head buffer ... */
-		if (state->cl->httpbuf.len > 0)
+		if (cl->httpbuf.len > 0)
 		{
-			len = min(state->content_length, state->cl->httpbuf.len);
+			len = min(state->content_length, cl->httpbuf.len);
 
-			D("CGI: Child(%d) feed %d HTTP buffer bytes\n",
-			  state->cl->proc.pid, len);
+			D("CGI: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len);
 
-			memcpy(buf, state->cl->httpbuf.ptr, len);
+			memcpy(buf, cl->httpbuf.ptr, len);
 
-			state->cl->httpbuf.len -= len;
-			state->cl->httpbuf.ptr +=len;
+			cl->httpbuf.len -= len;
+			cl->httpbuf.ptr +=len;
 		}
 
 		/* read it from socket ... */
 		else
 		{
-			len = uh_tcp_recv(state->cl, buf,
+			len = uh_tcp_recv(cl, buf,
 							  min(state->content_length, sizeof(buf)));
 
 			if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 				break;
 
 			D("CGI: Child(%d) feed %d/%d TCP socket bytes\n",
-			  state->cl->proc.pid, len,
-			  min(state->content_length, sizeof(buf)));
+			  cl->proc.pid, len, min(state->content_length, sizeof(buf)));
 		}
 
 		if (len)
@@ -179,16 +175,16 @@ static bool uh_cgi_socket_cb(struct client *cl)
 			state->content_length = 0;
 
 		/* ... write to CGI process */
-		len = uh_raw_send(state->wfd, buf, len,
+		len = uh_raw_send(cl->wpipe.fd, buf, len,
 						  cl->server->conf->script_timeout);
 
 		/* explicit EOF notification for the child */
 		if (state->content_length <= 0)
-			close(state->wfd);
+			uh_ufd_remove(&cl->wpipe);
 	}
 
 	/* try to read data from child */
-	while ((len = uh_raw_recv(state->rfd, buf, sizeof(buf), -1)) > 0)
+	while ((len = uh_raw_recv(cl->rpipe.fd, buf, sizeof(buf), -1)) > 0)
 	{
 		/* we have not pushed out headers yet, parse input */
 		if (!state->header_sent)
@@ -199,7 +195,7 @@ static bool uh_cgi_socket_cb(struct client *cl)
 			if (uh_cgi_header_parse(res, state->httpbuf, len, &hdroff))
 			{
 				/* write status */
-				ensure_out(uh_http_sendf(state->cl, NULL,
+				ensure_out(uh_http_sendf(cl, NULL,
 					"HTTP/%.1f %03d %s\r\n"
 					"Connection: close\r\n",
 					req->version, res->statuscode, res->statusmsg));
@@ -208,7 +204,7 @@ static bool uh_cgi_socket_cb(struct client *cl)
 				if (!uh_cgi_header_lookup(res, "Location") &&
 					!uh_cgi_header_lookup(res, "Content-Type"))
 				{
-					ensure_out(uh_http_send(state->cl, NULL,
+					ensure_out(uh_http_send(cl, NULL,
 						"Content-Type: text/plain\r\n", -1));
 				}
 
@@ -216,19 +212,19 @@ static bool uh_cgi_socket_cb(struct client *cl)
 				if ((req->version > 1.0) &&
 					!uh_cgi_header_lookup(res, "Transfer-Encoding"))
 				{
-					ensure_out(uh_http_send(state->cl, NULL,
+					ensure_out(uh_http_send(cl, NULL,
 						"Transfer-Encoding: chunked\r\n", -1));
 				}
 
 				/* write headers from CGI program */
 				foreach_header(i, res->headers)
 				{
-					ensure_out(uh_http_sendf(state->cl, NULL, "%s: %s\r\n",
+					ensure_out(uh_http_sendf(cl, NULL, "%s: %s\r\n",
 						res->headers[i], res->headers[i+1]));
 				}
 
 				/* terminate header */
-				ensure_out(uh_http_send(state->cl, NULL, "\r\n", -1));
+				ensure_out(uh_http_send(cl, NULL, "\r\n", -1));
 
 				state->header_sent = true;
 
@@ -236,9 +232,9 @@ static bool uh_cgi_socket_cb(struct client *cl)
 				if (hdroff < len)
 				{
 					D("CGI: Child(%d) relaying %d rest bytes\n",
-					  state->cl->proc.pid, len - hdroff);
+					  cl->proc.pid, len - hdroff);
 
-					ensure_out(uh_http_send(state->cl, req,
+					ensure_out(uh_http_send(cl, req,
 											&buf[hdroff], len - hdroff));
 				}
 			}
@@ -257,7 +253,7 @@ static bool uh_cgi_socket_cb(struct client *cl)
 				 * build the required headers here.
 				 */
 
-				ensure_out(uh_http_sendf(state->cl, NULL,
+				ensure_out(uh_http_sendf(cl, NULL,
 										 "HTTP/%.1f 200 OK\r\n"
 										 "Content-Type: text/plain\r\n"
 										 "%s\r\n",
@@ -268,18 +264,16 @@ static bool uh_cgi_socket_cb(struct client *cl)
 				state->header_sent = true;
 
 				D("CGI: Child(%d) relaying %d invalid bytes\n",
-				  state->cl->proc.pid, len);
+				  cl->proc.pid, len);
 
-				ensure_out(uh_http_send(state->cl, req, buf, len));
+				ensure_out(uh_http_send(cl, req, buf, len));
 			}
 		}
 		else
 		{
 			/* headers complete, pass through buffer to socket */
-			D("CGI: Child(%d) relaying %d normal bytes\n",
-			  state->cl->proc.pid, len);
-
-			ensure_out(uh_http_send(state->cl, req, buf, len));
+			D("CGI: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len);
+			ensure_out(uh_http_send(cl, req, buf, len));
 		}
 	}
 
@@ -287,8 +281,7 @@ static bool uh_cgi_socket_cb(struct client *cl)
 	if ((len == 0) ||
 		((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1)))
 	{
-		D("CGI: Child(%d) presumed dead [%s]\n",
-		  state->cl->proc.pid, strerror(errno));
+		D("CGI: Child(%d) presumed dead [%s]\n", cl->proc.pid, strerror(errno));
 
 		goto out;
 	}
@@ -298,17 +291,17 @@ static bool uh_cgi_socket_cb(struct client *cl)
 out:
 	if (!state->header_sent)
 	{
-		if (state->cl->timeout.pending)
-			uh_http_sendhf(state->cl, 502, "Bad Gateway",
+		if (cl->timeout.pending)
+			uh_http_sendhf(cl, 502, "Bad Gateway",
 						   "The CGI process did not produce any response\n");
 		else
-			uh_http_sendhf(state->cl, 504, "Gateway Timeout",
+			uh_http_sendhf(cl, 504, "Gateway Timeout",
 						   "The CGI process took too long to produce a "
 						   "response\n");
 	}
 	else
 	{
-		uh_http_send(state->cl, req, "", 0);
+		uh_http_send(cl, req, "", 0);
 	}
 
 	uh_cgi_shutdown(state);
@@ -529,9 +522,13 @@ bool uh_cgi_request(struct client *cl, struct path_info *pi,
 	default:
 		memset(state, 0, sizeof(*state));
 
-		state->cl = cl;
-		state->cl->pipe.fd = rfd[0];
-		state->cl->proc.pid = child;
+		cl->rpipe.fd = rfd[0];
+		cl->wpipe.fd = wfd[1];
+		cl->proc.pid = child;
+
+		/* make pipe non-blocking */
+		fd_nonblock(cl->rpipe.fd);
+		fd_nonblock(cl->wpipe.fd);
 
 		/* close unneeded pipe ends */
 		close(rfd[1]);
@@ -554,12 +551,6 @@ bool uh_cgi_request(struct client *cl, struct path_info *pi,
 			}
 		}
 
-		state->rfd = rfd[0];
-		fd_nonblock(state->rfd);
-
-		state->wfd = wfd[1];
-		fd_nonblock(state->wfd);
-
 		cl->cb = uh_cgi_socket_cb;
 		cl->priv = state;
 
diff --git a/package/uhttpd/src/uhttpd-cgi.h b/package/uhttpd/src/uhttpd-cgi.h
index 18816bae11..fffcc5d353 100644
--- a/package/uhttpd/src/uhttpd-cgi.h
+++ b/package/uhttpd/src/uhttpd-cgi.h
@@ -28,9 +28,6 @@
 
 
 struct uh_cgi_state {
-	int rfd;
-	int wfd;
-	struct client *cl;
 	char httpbuf[UH_LIMIT_MSGHEAD];
 	int content_length;
 	bool header_sent;
diff --git a/package/uhttpd/src/uhttpd-lua.c b/package/uhttpd/src/uhttpd-lua.c
index 10d6de402a..94626bb56b 100644
--- a/package/uhttpd/src/uhttpd-lua.c
+++ b/package/uhttpd/src/uhttpd-lua.c
@@ -250,8 +250,6 @@ lua_State * uh_lua_init(const struct config *conf)
 
 static void uh_lua_shutdown(struct uh_lua_state *state)
 {
-	close(state->rfd);
-	close(state->wfd);
 	free(state);
 }
 
@@ -266,31 +264,28 @@ static bool uh_lua_socket_cb(struct client *cl)
 	while (state->content_length > 0)
 	{
 		/* remaining data in http head buffer ... */
-		if (state->cl->httpbuf.len > 0)
+		if (cl->httpbuf.len > 0)
 		{
-			len = min(state->content_length, state->cl->httpbuf.len);
+			len = min(state->content_length, cl->httpbuf.len);
 
-			D("Lua: Child(%d) feed %d HTTP buffer bytes\n",
-			  state->cl->proc.pid, len);
+			D("Lua: Child(%d) feed %d HTTP buffer bytes\n", cl->proc.pid, len);
 
-			memcpy(buf, state->cl->httpbuf.ptr, len);
+			memcpy(buf, cl->httpbuf.ptr, len);
 
-			state->cl->httpbuf.len -= len;
-			state->cl->httpbuf.ptr += len;
+			cl->httpbuf.len -= len;
+			cl->httpbuf.ptr += len;
 		}
 
 		/* read it from socket ... */
 		else
 		{
-			len = uh_tcp_recv(state->cl, buf,
-							  min(state->content_length, sizeof(buf)));
+			len = uh_tcp_recv(cl, buf, min(state->content_length, sizeof(buf)));
 
 			if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 				break;
 
 			D("Lua: Child(%d) feed %d/%d TCP socket bytes\n",
-			  state->cl->proc.pid, len,
-			  min(state->content_length, sizeof(buf)));
+			  cl->proc.pid, len, min(state->content_length, sizeof(buf)));
 		}
 
 		if (len)
@@ -299,20 +294,20 @@ static bool uh_lua_socket_cb(struct client *cl)
 			state->content_length = 0;
 
 		/* ... write to Lua process */
-		len = uh_raw_send(state->wfd, buf, len,
+		len = uh_raw_send(cl->wpipe.fd, buf, len,
 						  cl->server->conf->script_timeout);
 
 		/* explicit EOF notification for the child */
 		if (state->content_length <= 0)
-			close(state->wfd);
+			uh_ufd_remove(&cl->wpipe);
 	}
 
 	/* try to read data from child */
-	while ((len = uh_raw_recv(state->rfd, buf, sizeof(buf), -1)) > 0)
+	while ((len = uh_raw_recv(cl->rpipe.fd, buf, sizeof(buf), -1)) > 0)
 	{
 		/* pass through buffer to socket */
-		D("Lua: Child(%d) relaying %d normal bytes\n", state->cl->proc.pid, len);
-		ensure_out(uh_tcp_send(state->cl, buf, len));
+		D("Lua: Child(%d) relaying %d normal bytes\n", cl->proc.pid, len);
+		ensure_out(uh_tcp_send(cl, buf, len));
 		state->data_sent = true;
 	}
 
@@ -321,7 +316,7 @@ static bool uh_lua_socket_cb(struct client *cl)
 		((errno != EAGAIN) && (errno != EWOULDBLOCK) && (len == -1)))
 	{
 		D("Lua: Child(%d) presumed dead [%s]\n",
-		  state->cl->proc.pid, strerror(errno));
+		  cl->proc.pid, strerror(errno));
 
 		goto out;
 	}
@@ -331,11 +326,11 @@ static bool uh_lua_socket_cb(struct client *cl)
 out:
 	if (!state->data_sent)
 	{
-		if (state->cl->timeout.pending)
-			uh_http_sendhf(state->cl, 502, "Bad Gateway",
+		if (cl->timeout.pending)
+			uh_http_sendhf(cl, 502, "Bad Gateway",
 						   "The Lua process did not produce any response\n");
 		else
-			uh_http_sendhf(state->cl, 504, "Gateway Timeout",
+			uh_http_sendhf(cl, 504, "Gateway Timeout",
 						   "The Lua process took too long to produce a "
 						   "response\n");
 	}
@@ -557,9 +552,13 @@ bool uh_lua_request(struct client *cl, lua_State *L)
 	default:
 		memset(state, 0, sizeof(*state));
 
-		state->cl = cl;
-		state->cl->pipe.fd = rfd[0];
-		state->cl->proc.pid = child;
+		cl->rpipe.fd = rfd[0];
+		cl->wpipe.fd = wfd[1];
+		cl->proc.pid = child;
+
+		/* make pipe non-blocking */
+		fd_nonblock(cl->rpipe.fd);
+		fd_nonblock(cl->wpipe.fd);
 
 		/* close unneeded pipe ends */
 		close(rfd[1]);
@@ -582,12 +581,6 @@ bool uh_lua_request(struct client *cl, lua_State *L)
 			}
 		}
 
-		state->rfd = rfd[0];
-		fd_nonblock(state->rfd);
-
-		state->wfd = wfd[1];
-		fd_nonblock(state->wfd);
-
 		cl->cb = uh_lua_socket_cb;
 		cl->priv = state;
 
diff --git a/package/uhttpd/src/uhttpd-lua.h b/package/uhttpd/src/uhttpd-lua.h
index 9a10933fc7..ae573a3cec 100644
--- a/package/uhttpd/src/uhttpd-lua.h
+++ b/package/uhttpd/src/uhttpd-lua.h
@@ -33,9 +33,6 @@
 
 
 struct uh_lua_state {
-	int rfd;
-	int wfd;
-	struct client *cl;
 	char httpbuf[UH_LIMIT_MSGHEAD];
 	int content_length;
 	bool data_sent;
diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c
index d31f756d14..50b10e6050 100644
--- a/package/uhttpd/src/uhttpd-utils.c
+++ b/package/uhttpd/src/uhttpd-utils.c
@@ -119,7 +119,7 @@ bool uh_socket_wait(int fd, int sec, bool write)
 	while (((rv = select(fd+1, write ? NULL : &fds, write ? &fds : NULL,
 						 NULL, &timeout)) < 0) && (errno == EINTR))
 	{
-		D("IO: Socket(%d) select interrupted: %s\n",
+		D("IO: FD(%d) select interrupted: %s\n",
 				fd, strerror(errno));
 
 		continue;
@@ -127,7 +127,7 @@ bool uh_socket_wait(int fd, int sec, bool write)
 
 	if (rv <= 0)
 	{
-		D("IO: Socket(%d) appears dead (rv=%d)\n", fd, rv);
+		D("IO: FD(%d) appears dead (rv=%d)\n", fd, rv);
 		return false;
 	}
 
@@ -146,7 +146,7 @@ static int __uh_raw_send(struct client *cl, const char *buf, int len, int sec,
 		{
 			if (errno == EINTR)
 			{
-				D("IO: Socket(%d) interrupted\n", cl->fd.fd);
+				D("IO: FD(%d) interrupted\n", cl->fd.fd);
 				continue;
 			}
 			else if ((sec > 0) && (errno == EAGAIN || errno == EWOULDBLOCK))
@@ -156,7 +156,7 @@ static int __uh_raw_send(struct client *cl, const char *buf, int len, int sec,
 			}
 			else
 			{
-				D("IO: Socket(%d) write error: %s\n", fd, strerror(errno));
+				D("IO: FD(%d) write error: %s\n", fd, strerror(errno));
 				return -1;
 			}
 		}
@@ -168,19 +168,19 @@ static int __uh_raw_send(struct client *cl, const char *buf, int len, int sec,
 		 */
 		else if (rv == 0)
 		{
-			D("IO: Socket(%d) closed\n", fd);
+			D("IO: FD(%d) appears closed\n", fd);
 			return 0;
 		}
 		else if (rv < len)
 		{
-			D("IO: Socket(%d) short write %d/%d bytes\n", fd, rv, len);
+			D("IO: FD(%d) short write %d/%d bytes\n", fd, rv, len);
 			len -= rv;
 			buf += rv;
 			continue;
 		}
 		else
 		{
-			D("IO: Socket(%d) sent %d/%d bytes\n", fd, rv, len);
+			D("IO: FD(%d) sent %d/%d bytes\n", fd, rv, len);
 			return rv;
 		}
 	}
@@ -230,18 +230,18 @@ static int __uh_raw_recv(struct client *cl, char *buf, int len, int sec,
 			}
 			else
 			{
-				D("IO: Socket(%d) read error: %s\n", fd, strerror(errno));
+				D("IO: FD(%d) read error: %s\n", fd, strerror(errno));
 				return -1;
 			}
 		}
 		else if (rv == 0)
 		{
-			D("IO: Socket(%d) closed\n", fd);
+			D("IO: FD(%d) appears closed\n", fd);
 			return 0;
 		}
 		else
 		{
-			D("IO: Socket(%d) read %d bytes\n", fd, rv);
+			D("IO: FD(%d) read %d bytes\n", fd, rv);
 			return rv;
 		}
 	}
@@ -934,6 +934,9 @@ struct client * uh_client_add(int sock, struct listener *serv)
 		new->fd.fd  = sock;
 		new->server = serv;
 
+		new->rpipe.fd = -1;
+		new->wpipe.fd = -1;
+
 		/* get remote endpoint addr */
 		sl = sizeof(struct sockaddr_in6);
 		memset(&(new->peeraddr), 0, sl);
@@ -948,6 +951,8 @@ struct client * uh_client_add(int sock, struct listener *serv)
 		uh_clients = new;
 
 		serv->n_clients++;
+
+		D("IO: Client(%d) allocated\n", new->fd.fd);
 	}
 
 	return new;
@@ -996,13 +1001,12 @@ void uh_client_remove(struct client *cl)
 			if (cur->proc.pid)
 				uloop_process_delete(&cur->proc);
 
-			if (cur->pipe.fd)
-				uloop_fd_delete(&cur->pipe);
+			D("IO: Client(%d) freeing\n", cur->fd.fd);
 
-			uloop_fd_delete(&cur->fd);
-			close(cur->fd.fd);
+			uh_ufd_remove(&cur->rpipe);
+			uh_ufd_remove(&cur->wpipe);
+			uh_ufd_remove(&cur->fd);
 
-			D("IO: Socket(%d) closing\n", cur->fd.fd);
 			cur->server->n_clients--;
 
 			free(cur);
@@ -1012,6 +1016,34 @@ void uh_client_remove(struct client *cl)
 }
 
 
+void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev)
+{
+	if (h != NULL)
+	{
+		u->cb = h;
+		uloop_fd_add(u, ev);
+		D("IO: FD(%d) added to uloop\n", u->fd);
+	}
+}
+
+void uh_ufd_remove(struct uloop_fd *u)
+{
+	if (u->cb != NULL)
+	{
+		uloop_fd_delete(u);
+		D("IO: FD(%d) removed from uloop\n", u->fd);
+		u->cb = NULL;
+	}
+
+	if (u->fd > -1)
+	{
+		close(u->fd);
+		D("IO: FD(%d) closed\n", u->fd);
+		u->fd = -1;
+	}
+}
+
+
 #ifdef HAVE_CGI
 static struct interpreter *uh_interpreters = NULL;
 
diff --git a/package/uhttpd/src/uhttpd-utils.h b/package/uhttpd/src/uhttpd-utils.h
index 797b07def4..31047f5554 100644
--- a/package/uhttpd/src/uhttpd-utils.h
+++ b/package/uhttpd/src/uhttpd-utils.h
@@ -23,6 +23,9 @@
 #include <pwd.h>
 #include <sys/stat.h>
 
+#include <libubox/uloop.h>
+
+
 #ifdef HAVE_SHADOW
 #include <shadow.h>
 #endif
@@ -123,7 +126,8 @@ struct client * uh_client_lookup(int sock);
 void uh_client_shutdown(struct client *cl);
 void uh_client_remove(struct client *cl);
 
-#define uh_client_gc() uh_client_remove(NULL)
+void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev);
+void uh_ufd_remove(struct uloop_fd *u);
 
 
 #ifdef HAVE_CGI
diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c
index 3237fbab57..9e5a574263 100644
--- a/package/uhttpd/src/uhttpd.c
+++ b/package/uhttpd/src/uhttpd.c
@@ -218,8 +218,7 @@ static int uh_socket_bind(fd_set *serv_fds, int *max_fd,
 		fd_cloexec(sock);
 		*max_fd = max(*max_fd, sock);
 
-		l->fd.cb = uh_listener_cb;
-		uloop_fd_add(&l->fd, ULOOP_READ);
+		uh_ufd_add(&l->fd, uh_listener_cb, ULOOP_READ);
 
 		bound++;
 		continue;
@@ -514,7 +513,7 @@ static bool uh_dispatch_request(struct client *cl, struct http_request *req)
 	return false;
 }
 
-static void uh_client_cb(struct uloop_fd *u, unsigned int events);
+static void uh_socket_cb(struct uloop_fd *u, unsigned int events);
 
 static void uh_listener_cb(struct uloop_fd *u, unsigned int events)
 {
@@ -539,7 +538,8 @@ static void uh_listener_cb(struct uloop_fd *u, unsigned int events)
 		if ((cl = uh_client_add(new_fd, serv)) != NULL)
 		{
 			/* add client socket to global fdset */
-			uloop_fd_add(&cl->fd, ULOOP_READ);
+			uh_ufd_add(&cl->fd, uh_socket_cb, ULOOP_READ);
+			fd_cloexec(cl->fd.fd);
 
 #ifdef HAVE_TLS
 			/* setup client tls context */
@@ -555,9 +555,6 @@ static void uh_listener_cb(struct uloop_fd *u, unsigned int events)
 				}
 			}
 #endif
-
-			cl->fd.cb = uh_client_cb;
-			fd_cloexec(new_fd);
 		}
 
 		/* insufficient resources */
@@ -569,28 +566,33 @@ static void uh_listener_cb(struct uloop_fd *u, unsigned int events)
 	}
 }
 
-static void uh_pipe_cb(struct uloop_fd *u, unsigned int events)
+static void uh_client_cb(struct client *cl, unsigned int events);
+
+static void uh_rpipe_cb(struct uloop_fd *u, unsigned int events)
 {
-	struct client *cl = container_of(u, struct client, pipe);
+	struct client *cl = container_of(u, struct client, rpipe);
 
-	if (!u->error)
-	{
-		D("SRV: Client(%d) pipe(%d) readable\n",
-		  cl->fd.fd, cl->pipe.fd);
+	D("SRV: Client(%d) rpipe readable\n", cl->fd.fd);
 
-		uh_client_cb(&cl->fd, ULOOP_WRITE);
-	}
+	uh_client_cb(cl, ULOOP_WRITE);
+}
+
+static void uh_socket_cb(struct uloop_fd *u, unsigned int events)
+{
+	struct client *cl = container_of(u, struct client, fd);
+
+	D("SRV: Client(%d) socket readable\n", cl->fd.fd);
+
+	uh_client_cb(cl, ULOOP_READ);
 }
 
 static void uh_child_cb(struct uloop_process *p, int rv)
 {
 	struct client *cl = container_of(p, struct client, proc);
 
-	D("SRV: Client(%d) child(%d) is dead\n", cl->fd.fd, cl->proc.pid);
+	D("SRV: Client(%d) child(%d) dead\n", cl->fd.fd, cl->proc.pid);
 
-	cl->dead = true;
-	cl->fd.eof = true;
-	uh_client_cb(&cl->fd, ULOOP_READ | ULOOP_WRITE);
+	uh_client_cb(cl, ULOOP_READ | ULOOP_WRITE);
 }
 
 static void uh_kill9_cb(struct uloop_timeout *t)
@@ -624,17 +626,15 @@ static void uh_timeout_cb(struct uloop_timeout *t)
 	}
 }
 
-static void uh_client_cb(struct uloop_fd *u, unsigned int events)
+static void uh_client_cb(struct client *cl, unsigned int events)
 {
 	int i;
-	struct client *cl;
 	struct config *conf;
 	struct http_request *req;
 
-	cl = container_of(u, struct client, fd);
 	conf = cl->server->conf;
 
-	D("SRV: Client(%d) enter callback\n", u->fd);
+	D("SRV: Client(%d) enter callback\n", cl->fd.fd);
 
 	/* undispatched yet */
 	if (!cl->dispatched)
@@ -642,14 +642,14 @@ static void uh_client_cb(struct uloop_fd *u, unsigned int events)
 		/* we have no headers yet and this was a write event, ignore... */
 		if (!(events & ULOOP_READ))
 		{
-			D("SRV: Client(%d) ignoring write event before headers\n", u->fd);
+			D("SRV: Client(%d) ignoring write event before headers\n", cl->fd.fd);
 			return;
 		}
 
 		/* attempt to receive and parse headers */
 		if (!(req = uh_http_header_recv(cl)))
 		{
-			D("SRV: Client(%d) failed to receive header\n", u->fd);
+			D("SRV: Client(%d) failed to receive header\n", cl->fd.fd);
 			uh_client_shutdown(cl);
 			return;
 		}
@@ -663,7 +663,7 @@ static void uh_client_cb(struct uloop_fd *u, unsigned int events)
 			if (strcasecmp(req->headers[i+1], "100-continue"))
 			{
 				D("SRV: Client(%d) unknown expect header (%s)\n",
-				  u->fd, req->headers[i+1]);
+				  cl->fd.fd, req->headers[i+1]);
 
 				uh_http_response(cl, 417, "Precondition Failed");
 				uh_client_shutdown(cl);
@@ -671,7 +671,7 @@ static void uh_client_cb(struct uloop_fd *u, unsigned int events)
 			}
 			else
 			{
-				D("SRV: Client(%d) sending HTTP/1.1 100 Continue\n", u->fd);
+				D("SRV: Client(%d) sending HTTP/1.1 100 Continue\n", cl->fd.fd);
 
 				uh_http_sendf(cl, NULL, "HTTP/1.1 100 Continue\r\n\r\n");
 				cl->httpbuf.len = 0; /* client will re-send the body */
@@ -694,24 +694,23 @@ static void uh_client_cb(struct uloop_fd *u, unsigned int events)
 		/* dispatch request */
 		if (!uh_dispatch_request(cl, req))
 		{
-			D("SRV: Client(%d) failed to dispach request\n", u->fd);
+			D("SRV: Client(%d) failed to dispach request\n", cl->fd.fd);
 			uh_client_shutdown(cl);
 			return;
 		}
 
 		/* request handler spawned a pipe, register handler */
-		if (cl->pipe.fd)
+		if (cl->rpipe.fd > -1)
 		{
-			D("SRV: Client(%d) pipe(%d) spawned\n", u->fd, cl->pipe.fd);
+			D("SRV: Client(%d) pipe(%d) spawned\n", cl->fd.fd, cl->rpipe.fd);
 
-			cl->pipe.cb = uh_pipe_cb;
-			uloop_fd_add(&cl->pipe, ULOOP_READ);
+			uh_ufd_add(&cl->rpipe, uh_rpipe_cb, ULOOP_READ);
 		}
 
 		/* request handler spawned a child, register handler */
 		if (cl->proc.pid)
 		{
-			D("SRV: Client(%d) child(%d) spawned\n", u->fd, cl->proc.pid);
+			D("SRV: Client(%d) child(%d) spawned\n", cl->fd.fd, cl->proc.pid);
 
 			cl->proc.cb = uh_child_cb;
 			uloop_process_add(&cl->proc);
@@ -721,13 +720,13 @@ static void uh_client_cb(struct uloop_fd *u, unsigned int events)
 		}
 
 		/* header processing complete */
-		D("SRV: Client(%d) dispatched\n", u->fd);
+		D("SRV: Client(%d) dispatched\n", cl->fd.fd);
 		cl->dispatched = true;
 	}
 
 	if (!cl->cb(cl))
 	{
-		D("SRV: Client(%d) response callback signalized EOF\n", u->fd);
+		D("SRV: Client(%d) response callback signalized EOF\n", cl->fd.fd);
 		uh_client_shutdown(cl);
 		return;
 	}
@@ -1098,7 +1097,7 @@ int main (int argc, char **argv)
 					"	-L file         Lua handler script, omit to disable Lua\n"
 #endif
 #ifdef HAVE_UBUS
-					"	-u string       URL prefix for HTTP/JSON handler, default is '/ubus'\n"
+					"	-u string       URL prefix for HTTP/JSON handler\n"
 					"	-U file         Override ubus socket path\n"
 #endif
 #ifdef HAVE_CGI
@@ -1210,7 +1209,7 @@ int main (int argc, char **argv)
 				"Notice: Unable to load ubus plugin - disabling ubus support! "
 				"(Reason: %s)\n", dlerror());
 	}
-	else
+	else if (conf.ubus_prefix)
 	{
 		/* resolve functions */
 		if (!(conf.ubus_init    = dlsym(lib, "uh_ubus_init"))    ||
@@ -1224,10 +1223,7 @@ int main (int argc, char **argv)
 			exit(1);
 		}
 
-		/* default ubus prefix */
-		if (!conf.ubus_prefix)
-			conf.ubus_prefix = "/ubus";
-
+		/* initialize ubus */
 		conf.ubus_state = conf.ubus_init(&conf);
 	}
 #endif
diff --git a/package/uhttpd/src/uhttpd.h b/package/uhttpd/src/uhttpd.h
index 69fe21a418..fe86b01d40 100644
--- a/package/uhttpd/src/uhttpd.h
+++ b/package/uhttpd/src/uhttpd.h
@@ -160,7 +160,8 @@ struct client {
 	SSL *tls;
 #endif
 	struct uloop_fd fd;
-	struct uloop_fd pipe;
+	struct uloop_fd rpipe;
+	struct uloop_fd wpipe;
 	struct uloop_process proc;
 	struct uloop_timeout timeout;
 	bool (*cb)(struct client *);
-- 
cgit v1.2.3