// Create a new inotify poller. // This creates an inotify handler, and an epoll handler. func newFdPoller(fd int) (*fdPoller, error) { var errno error poller := emptyPoller(fd) defer func() { if errno != nil { poller.close() } }() poller.fd = fd // Create epoll fd poller.epfd, errno = unix.EpollCreate1(0) if poller.epfd == -1 { return nil, errno } // Create pipe; pipe[0] is the read end, pipe[1] the write end. errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) if errno != nil { return nil, errno } // Register inotify fd with epoll event := unix.EpollEvent{ Fd: int32(poller.fd), Events: unix.EPOLLIN, } errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) if errno != nil { return nil, errno } // Register pipe fd with epoll event = unix.EpollEvent{ Fd: int32(poller.pipe[0]), Events: unix.EPOLLIN, } errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) if errno != nil { return nil, errno } return poller, nil }
// 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 }