diff --git a/src/io.c b/src/io.c
index b43b140bf675497c735ea4e377f36880532fa6b4..5f0e58429c6cded8795d66166ad57c753f421d60 100644
--- a/src/io.c
+++ b/src/io.c
@@ -450,7 +450,31 @@ static void init_file(struct io_backend *b, struct lsh_fd *f, int fd)
   f->next = b->files;
   b->files = f;
 }
-    
+
+/* Blocking read from a file descriptor (i.e. don't use the backend).
+ * The fd should *not* be in non-blocking mode. */
+
+int blocking_read(int fd, struct read_handler *handler)
+{
+  struct fd_read r =
+  { { STACK_HEADER, do_read }, fd };
+
+  while (1)
+    {
+      int res = READ_HANDLER(handler,
+			     &r.super);
+
+      assert(!(res & (LSH_HOLD | LSH_KILL_OTHERS)));
+
+      if (res & (LSH_CLOSE | LSH_DIE))
+	{
+	  close(fd);
+	  return res;
+	}
+      if (res & LSH_FAIL)
+	werror("blocking_read: Ignoring error %d\n", res);
+    }
+}
 /*
  * Fill in ADDR from HOST, SERVICE and PROTOCOL.
  * Supplying a null pointer for HOST means use INADDR_ANY.
diff --git a/src/io.h b/src/io.h
index dcc3e66b8e6f52eba9c86f2ae7de2a844e90ceb3..1343c03a54eb632c75ab7e7eb58880056971649c 100644
--- a/src/io.h
+++ b/src/io.h
@@ -154,6 +154,8 @@ void init_backend(struct io_backend *b);
 int io_iter(struct io_backend *b);
 void io_run(struct io_backend *b);
 
+int blocking_read(int fd, struct read_handler *r);
+
 int get_inaddr(struct sockaddr_in	* addr,
 	       const char		* host,
 	       const char		* service,