diff --git a/pcap-hurd.c b/pcap-hurd.c index 134a109b35..fde0256ba7 100644 --- a/pcap-hurd.c +++ b/pcap-hurd.c @@ -7,16 +7,18 @@ #include #include -#include #include #include +#include #include #include #include #include +#include #include #include #include +#include #include #include "pcap-int.h" @@ -26,6 +28,8 @@ struct pcap_hurd { device_t mach_dev; mach_port_t rcv_port; int filtering_in_kernel; + pthread_t pipe_thread_id; + int pipefd[2]; }; /* Accept all packets. */ @@ -126,11 +130,11 @@ pcap_read_hurd(pcap_t *p, int cnt _U_, pcap_handler callback, u_char *user) struct pcap_hurd *ph; struct pcap_pkthdr h; struct timespec ts; - int wirelen, caplen; + int wirelen, caplen, rpipe, ret; u_char *pkt; - kern_return_t kr; ph = p->priv; + rpipe = ph->pipefd[0]; msg = (struct net_rcv_msg *)p->buffer; retry: @@ -139,19 +143,16 @@ pcap_read_hurd(pcap_t *p, int cnt _U_, pcap_handler callback, u_char *user) return PCAP_ERROR_BREAK; } - kr = mach_msg(&msg->msg_hdr, MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0, - p->bufsize, ph->rcv_port, MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL); - clock_gettime(CLOCK_REALTIME, &ts); - - if (kr) { - if (kr == MACH_RCV_INTERRUPTED) - goto retry; - - pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr, - "mach_msg"); + ret = read(rpipe, &msg->msg_hdr, p->bufsize); + if (ret < 0) { + pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, + PCAP_ERRBUF_SIZE, errno, "read"); return PCAP_ERROR; } + if (ret == 0) + /* Pipe closed, 0 packets read */ + return 0; + clock_gettime(CLOCK_REALTIME, &ts); ph->stat.ps_recv++; @@ -212,7 +213,7 @@ pcap_inject_hurd(pcap_t *p, const void *buf, int size) if (kr) { pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr, "device_write"); - return -1; + return PCAP_ERROR; } return count; @@ -232,9 +233,34 @@ static void pcap_cleanup_hurd(pcap_t *p) { struct pcap_hurd *ph; + int err; ph = p->priv; + /* Cancel the thread */ + if (ph->pipe_thread_id != 0) { + pthread_cancel(ph->pipe_thread_id); + + err = pthread_join(ph->pipe_thread_id, NULL); + if (err != 0) { + pcapint_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, err, "pthread_join"); + } + ph->pipe_thread_id = 0; + } + + /* Close the pipe ends */ + if (ph->pipefd[1] != -1) { + close(ph->pipefd[1]); + ph->pipefd[1] = -1; + } + + if (ph->pipefd[0] != -1) { + close(ph->pipefd[0]); + ph->pipefd[0] = -1; + } + + /* Release remaining resources */ if (ph->rcv_port != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), ph->rcv_port); ph->rcv_port = MACH_PORT_NULL; @@ -242,12 +268,83 @@ pcap_cleanup_hurd(pcap_t *p) if (ph->mach_dev != MACH_PORT_NULL) { device_close(ph->mach_dev); + mach_port_deallocate(mach_task_self(), ph->mach_dev); ph->mach_dev = MACH_PORT_NULL; } pcapint_cleanup_live_common(p); } +static void* +pipe_write_thread(void *arg) { + pcap_t *p; + struct pcap_hurd *ph; + int wpipe, ret; + struct net_rcv_msg msg; + u_int msgsize; + kern_return_t kr; + mach_msg_timeout_t timeout_ms; + sigset_t set; + + pthread_setname_np (pthread_self(), "pcap_hurd_pipe_thread"); + + /* Block SIGPIPE for this thread */ + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + p = (pcap_t *)arg; + ph = p->priv; + wpipe = ph->pipefd[1]; + msgsize = sizeof(struct net_rcv_msg); + timeout_ms = 100; + + while (1) { + kr = mach_msg(&msg.msg_hdr, + MACH_RCV_MSG | MACH_RCV_INTERRUPT| MACH_RCV_TIMEOUT, + 0, msgsize, ph->rcv_port, timeout_ms, + MACH_PORT_NULL); + + if (kr) { + if (kr == MACH_RCV_TIMED_OUT || kr == MACH_RCV_INTERRUPTED) { + pthread_testcancel(); + continue; + } + + pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, + PCAP_ERRBUF_SIZE, kr, "mach_msg"); + + return NULL; + } + + ret = write(wpipe, &msg, msgsize); + if (ret < 0) { + pcapint_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, "write"); + return NULL; + } + } + + return NULL; +} + +static int +init_pipe(pcap_t *p) { + int err; + struct pcap_hurd *ph; + + ph = p->priv; + err = pipe(ph->pipefd); + if (err < 0) + return errno; + + err = pthread_create(&ph->pipe_thread_id, NULL, pipe_write_thread, p); + if (err != 0) + return err; + + return 0; +} + static int pcap_activate_hurd(pcap_t *p) { @@ -310,6 +407,15 @@ pcap_activate_hurd(pcap_t *p) goto error; } + ret = init_pipe(p); + if (ret != 0) { + pcapint_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + ret, "init_pipe"); + goto error; + } + + p->selectable_fd = ph->pipefd[0]; + /* * XXX Ethernet only currently * @@ -332,6 +438,7 @@ pcap_activate_hurd(pcap_t *p) p->inject_op = pcap_inject_hurd; p->setfilter_op = pcap_setfilter_hurd; p->stats_op = pcap_stats_hurd; + p->cleanup_op = pcap_cleanup_hurd; return 0; @@ -353,6 +460,8 @@ pcapint_create_interface(const char *device _U_, char *ebuf) ph = p->priv; ph->mach_dev = MACH_PORT_NULL; ph->rcv_port = MACH_PORT_NULL; + ph->pipefd[0] = -1; + ph->pipefd[1] = -1; p->activate_op = pcap_activate_hurd; return p; }