+
+ if (st->rxfd!=-1) {
+ *nfds_io=2;
+ fds[0].fd=st->txfd;
+ fds[0].events=0; /* Might want to pick up POLLOUT sometime */
+ fds[1].fd=st->rxfd;
+ fds[1].events=POLLIN;
+ } else {
+ *nfds_io=0;
+ }
+ return 0;
+}
+
+static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+ struct userv *st=sst;
+ uint8_t rxbuf[DEFAULT_BUFSIZE];
+ int l;
+
+ if (nfds==0) return;
+
+ if (fds[1].revents&POLLERR) {
+ Message(M_ERR,"%s: userv_afterpoll: POLLERR!\n",st->slip.nl.name);
+ }
+ if (fds[1].revents&POLLIN) {
+ l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE);
+ if (l<0) {
+ if (errno!=EINTR)
+ fatal_perror("%s: userv_afterpoll: read(rxfd)",
+ st->slip.nl.name);
+ } else if (l==0) {
+ fatal("%s: userv_afterpoll: read(rxfd)=0; userv gone away?",
+ st->slip.nl.name);
+ } else slip_unstuff(&st->slip,rxbuf,l);
+ }
+}
+
+/* Send buf to the kernel. Free buf before returning. */
+static void userv_deliver_to_kernel(void *sst, struct buffer_if *buf)
+{
+ struct userv *st=sst;
+
+ if (buf->size > st->slip.nl.mtu) {
+ Message(M_ERR,"%s: packet of size %"PRIu32" exceeds mtu %"PRIu32":"
+ " cannot be injected into kernel, dropped\n",
+ st->slip.nl.name, buf->size, st->slip.nl.mtu);
+ BUF_FREE(buf);
+ return;
+ }
+
+ slip_stuff(&st->slip,buf,st->txfd);
+}
+
+static void userv_userv_callback(void *sst, pid_t pid, int status)
+{
+ struct userv *st=sst;
+
+ if (pid!=st->pid) {
+ Message(M_WARNING,"userv_callback called unexpectedly with pid %d "
+ "(expected %d)\n",pid,st->pid);
+ return;
+ }
+ if (!st->expecting_userv_exit) {
+ if (WIFEXITED(status)) {
+ fatal("%s: userv exited unexpectedly with status %d",
+ st->slip.nl.name,WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fatal("%s: userv exited unexpectedly: uncaught signal %d",
+ st->slip.nl.name,WTERMSIG(status));
+ } else {
+ fatal("%s: userv stopped unexpectedly",
+ st->slip.nl.name);
+ }
+ }
+ Message(M_WARNING,"%s: userv subprocess died with status %d\n",
+ st->slip.nl.name,WEXITSTATUS(status));
+ st->pid=0;
+}
+
+struct userv_entry_rec {
+ cstring_t path;
+ const char **argv;
+ int in;
+ int out;
+ /* XXX perhaps we should collect and log stderr? */
+};
+
+static void userv_entry(void *sst)
+{
+ struct userv_entry_rec *st=sst;
+
+ dup2(st->in,0);
+ dup2(st->out,1);
+
+ /* XXX close all other fds */
+ setsid();
+ /* XXX We really should strdup() all of argv[] but because we'll just
+ exit anyway if execvp() fails it doesn't seem worth bothering. */
+ execvp(st->path,(char *const*)st->argv);
+ perror("userv-entry: execvp()");
+ exit(1);
+}
+
+static void userv_invoke_userv(struct userv *st)
+{
+ struct userv_entry_rec *er;