func (m Monitor) Fd() error { err := C.udev_monitor_get_fd(m.ptr) if err == 0 { return nil } return Error(err) }
func MonitorUdev(subsystem string) (*Monitor, error) { cName := C.CString("udev") defer C.free(unsafe.Pointer(cName)) udev := C.udev_new() udev_monitor := C.udev_monitor_new_from_netlink(udev, cName) if udev_monitor == nil { C.udev_unref(udev) return nil, Error{"udev_monitor_new_from_netlink"} } if subsystem != "" { cSubsystem := C.CString(subsystem) defer C.free(unsafe.Pointer(cSubsystem)) if C.udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, cSubsystem, nil) < 0 { C.udev_monitor_unref(udev_monitor) C.udev_unref(udev) return nil, Error{"udev_monitor_filter_add_match_subsystem_devtype"} } } if C.udev_monitor_enable_receiving(udev_monitor) < 0 { C.udev_monitor_unref(udev_monitor) C.udev_unref(udev) return nil, Error{"udev_monitor_enable_receiving"} } // set blocking if fd := C.udev_monitor_get_fd(udev_monitor); fd < 0 { C.udev_monitor_unref(udev_monitor) C.udev_unref(udev) return nil, Error{"udev_monitor_get_fd"} } else if err := syscall.SetNonblock(int(fd), false); err != nil { C.udev_monitor_unref(udev_monitor) C.udev_unref(udev) return nil, err } return &Monitor{udev, udev_monitor}, 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 }