// DeviceChan binds the udev_monitor socket to the event source and spawns a // goroutine. The goroutine efficiently waits on the monitor socket using epoll. // Data is received from the udev monitor socket and a new Device is created // with the data received. Pointers to the device are sent on the returned channel. // The function takes a done signalling channel as a parameter, which when // triggered will stop the goroutine and close the device channel. // Only socket connections with uid=0 are accepted. func (m *Monitor) DeviceChan(done <-chan struct{}) (<-chan *Device, error) { var event unix.EpollEvent var events [maxEpollEvents]unix.EpollEvent // Lock the context m.lock() defer m.unlock() // Enable receiving if C.udev_monitor_enable_receiving(m.ptr) != 0 { return nil, errors.New("udev: udev_monitor_enable_receiving failed") } // Set the fd to non-blocking fd := C.udev_monitor_get_fd(m.ptr) if e := unix.SetNonblock(int(fd), true); e != nil { return nil, errors.New("udev: unix.SetNonblock failed") } // Create an epoll fd epfd, e := unix.EpollCreate1(0) if e != nil { return nil, errors.New("udev: unix.EpollCreate1 failed") } // Add the fd to the epoll fd event.Events = unix.EPOLLIN | unix.EPOLLET event.Fd = int32(fd) if e = unix.EpollCtl(epfd, unix.EPOLL_CTL_ADD, int(fd), &event); e != nil { return nil, errors.New("udev: unix.EpollCtl failed") } // Create the channel ch := make(chan *Device) // Create goroutine to epoll the fd go func(done <-chan struct{}, fd int32) { // Close the epoll fd when goroutine exits defer unix.Close(epfd) // Close the channel when goroutine exits defer close(ch) // Loop forever for { // Poll the file descriptor nevents, e := unix.EpollWait(epfd, events[:], epollTimeout) if e != nil { return } // Process events for ev := 0; ev < nevents; ev++ { if events[ev].Fd == fd { if (events[ev].Events & unix.EPOLLIN) != 0 { for d := m.receiveDevice(); d != nil; d = m.receiveDevice() { ch <- d } } } } // Check for done signal select { case <-done: return default: } } }(done, int32(fd)) return ch, nil }
// Wait using epoll. // Returns true if something is ready to be read, // false if there is not. func (poller *fdPoller) wait() (bool, error) { // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. // I don't know whether epoll_wait returns the number of events returned, // or the total number of events ready. // I decided to catch both by making the buffer one larger than the maximum. events := make([]unix.EpollEvent, 7) for { n, errno := unix.EpollWait(poller.epfd, events, -1) if n == -1 { if errno == unix.EINTR { continue } return false, errno } if n == 0 { // If there are no events, try again. continue } if n > 6 { // This should never happen. More events were returned than should be possible. return false, errors.New("epoll_wait returned more events than I know what to do with") } ready := events[:n] epollhup := false epollerr := false epollin := false for _, event := range ready { if event.Fd == int32(poller.fd) { if event.Events&unix.EPOLLHUP != 0 { // This should not happen, but if it does, treat it as a wakeup. epollhup = true } if event.Events&unix.EPOLLERR != 0 { // If an error is waiting on the file descriptor, we should pretend // something is ready to read, and let unix.Read pick up the error. epollerr = true } if event.Events&unix.EPOLLIN != 0 { // There is data to read. epollin = true } } if event.Fd == int32(poller.pipe[0]) { if event.Events&unix.EPOLLHUP != 0 { // Write pipe descriptor was closed, by us. This means we're closing down the // watcher, and we should wake up. } if event.Events&unix.EPOLLERR != 0 { // If an error is waiting on the pipe file descriptor. // This is an absolute mystery, and should never ever happen. return false, errors.New("Error on the pipe descriptor.") } if event.Events&unix.EPOLLIN != 0 { // This is a regular wakeup, so we have to clear the buffer. err := poller.clearWake() if err != nil { return false, err } } } } if epollhup || epollerr || epollin { return true, nil } return false, nil } }