+ struct dispatch_proc *p = &cl->dispatch.proc;
+
+ uloop_timeout_cancel(&p->timeout);
+ blob_buf_free(&p->hdr);
+ proc_write_close(cl);
+ uh_relay_free(&p->r);
+}
+
+static void proc_write_cb(struct uloop_fd *fd, unsigned int events)
+{
+ struct client *cl = container_of(fd, struct client, dispatch.proc.wrfd);
+
+ client_poll_post_data(cl);
+}
+
+static void proc_relay_write_cb(struct client *cl)
+{
+ struct dispatch_proc *p = &cl->dispatch.proc;
+
+ if (ustream_pending_data(cl->us, true))
+ return;
+
+ ustream_set_read_blocked(&p->r.sfd.stream, false);
+ p->r.sfd.stream.notify_read(&p->r.sfd.stream, 0);
+}
+
+static int proc_data_send(struct client *cl, const char *data, int len)
+{
+ struct dispatch_proc *p = &cl->dispatch.proc;
+ int retlen = 0;
+ int ret;
+
+ while (len) {
+ ret = write(p->wrfd.fd, data, len);
+
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+
+ /* error, no retry */
+ len = 0;
+ break;
+ }
+
+ if (!ret)
+ break;
+
+ retlen += ret;
+ len -= ret;
+ data += ret;
+ }
+
+ if (len)
+ uloop_fd_add(&p->wrfd, ULOOP_WRITE);
+ else
+ uloop_fd_delete(&p->wrfd);
+
+ return retlen;
+}
+
+static void proc_timeout_cb(struct uloop_timeout *timeout)
+{
+ struct dispatch_proc *proc = container_of(timeout, struct dispatch_proc, timeout);
+ struct client *cl = container_of(proc, struct client, dispatch.proc);
+
+ uh_relay_kill(cl, &proc->r);