Esempio n. 1
0
// 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
	}
}