void UdevMonitor::addTagFilter(const char* tag) { /* Add a tag filter: */ if(udev_monitor_filter_add_match_tag(monitor,tag)!=0) throw std::runtime_error("RawHID::UdevMonitor::addTagFilter: Internal libudev error"); /* If the monitor is already listening, update its filters: */ if(listening) if(udev_monitor_filter_update(monitor)!=0) throw std::runtime_error("RawHID::UdevMonitor::addTagFilter: Internal libudev error"); }
void UdevMonitor::removeFilters(void) { /* Remove all filters: */ if(udev_monitor_filter_remove(monitor)!=0) throw std::runtime_error("RawHID::UdevMonitor::removeFilters: Internal libudev error"); /* If the monitor is already listening, update its filters: */ if(listening) if(udev_monitor_filter_update(monitor)!=0) throw std::runtime_error("RawHID::UdevMonitor::removeFilters: Internal libudev error"); }
void UdevMonitor::addSubsystemFilter(const char* subsystem,const char* deviceType) { /* Add a subsystem filter: */ if(udev_monitor_filter_add_match_subsystem_devtype(monitor,subsystem,deviceType)!=0) throw std::runtime_error("RawHID::UdevMonitor::addSubsystemFilter: Internal libudev error"); /* If the monitor is already listening, update its filters: */ if(listening) if(udev_monitor_filter_update(monitor)!=0) throw std::runtime_error("RawHID::UdevMonitor::addSubsystemFilter: Internal libudev error"); }
int main(int argc, char **argv) { struct udev *udev = udev_new(); if (udev == NULL) { fprintf(stderr, "Unable to initialize udev context"); return -1; } #ifdef DEBUG struct udev_enumerate *enumerate = udev_enumerate_new(udev); if (enumerate == NULL) { fprintf(stderr, "Unable to enumerate devices"); return -1; } udev_enumerate_add_match_subsystem(enumerate, "drm"); udev_enumerate_scan_devices(enumerate); struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); if (devices == NULL) { fprintf(stderr, "Unable to enumerate devices"); return -1; } // List devices struct udev_list_entry *dev_entry; udev_list_entry_foreach(dev_entry, devices) { const char *path = udev_list_entry_get_name(dev_entry); struct udev_device *dev = udev_device_new_from_syspath(udev, path); printf("%s [devtype=%s]\n", path, udev_device_get_devtype(dev)); } udev_enumerate_unref(enumerate); #endif // Setup monitor struct udev_monitor *monitor = udev_monitor_new_from_netlink(udev, "udev"); if (monitor == NULL) { fprintf(stderr, "Unable to initialize udev monitor"); return -1; } if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "drm", "drm_minor") < 0) { fprintf(stderr, "Unable to setup udev monitor"); return -1; } if (udev_monitor_filter_update(monitor) < 0) { fprintf(stderr, "Unable to setup udev monitor"); return -1; } if (udev_monitor_enable_receiving(monitor) < 0) { fprintf(stderr, "Unable to listen in udev monitor"); return -1; } // The same issue as in SO, receive_device does not block, use select // http://stackoverflow.com/questions/15687784/libudev-monitoring-returns-null-pointer-on-raspbian int fd = udev_monitor_get_fd(monitor); while(1) { fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); int ret = select(fd+1, &fds, NULL, NULL, NULL); if (ret > 0 && FD_ISSET(fd, &fds)) { struct udev_device *dev = udev_monitor_receive_device(monitor); if (dev == NULL) { continue; } printf("%s\n", udev_device_get_syspath(dev)); for (int i=1; i<argc; i++) { system(argv[i]); } udev_device_unref(dev); } } udev_monitor_unref(monitor); udev_unref(udev); return 0; }
// on fork(), create a new events directory for each of this process's monitors // and point them all to them. This way, both the parent and child can continue // to receive device packets. // NOTE: can only call async-safe methods static void udev_monitor_atfork(void) { int errsv = errno; int rc = 0; int i = 0; int cnt = 0; pid_t pid = getpid(); struct udev_monitor* monitor = NULL; int socket_fds[2]; struct epoll_event ev; write( STDERR_FILENO, "forked! begin split\n", strlen("forked! begin split\n") ); memset( &ev, 0, sizeof(struct epoll_event) ); // reset each monitor's inotify fd to point to a new PID-specific directory instead g_monitor_table_spinlock(); if( g_pid != pid ) { // child; do the fork for( i = 0; i < UDEV_MAX_MONITORS; i++ ) { if( g_monitor_table[i] == NULL ) { continue; } monitor = g_monitor_table[i]; if( monitor->type != UDEV_MONITOR_TYPE_UDEV ) { continue; } if( monitor->inotify_fd < 0 ) { continue; } if( monitor->epoll_fd < 0 ) { continue; } if( monitor->events_wd < 0 ) { continue; } // reset the socket buffer--the parent will be said to have // received intermittent events before the child was created. if( monitor->sock >= 0 ) { // stop watching this socket--we'll regenerate it later epoll_ctl( monitor->epoll_fd, EPOLL_CTL_DEL, monitor->sock, NULL ); close( monitor->sock ); monitor->sock = -1; } if( monitor->sock_fs >= 0 ) { close( monitor->sock_fs ); monitor->sock_fs = -1; } rc = socketpair( AF_LOCAL, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, socket_fds ); if( rc < 0 ) { // not much we can do here, except log an error write( STDERR_FILENO, "Failed to generate a socketpair\n", strlen( "Failed to generate a socketpair\n" ) ); udev_monitor_fs_shutdown( monitor ); g_monitor_table[i] = NULL; continue; } // child's copy of the monitor has its own socketpair monitor->sock = socket_fds[0]; monitor->sock_fs = socket_fds[1]; // reinstall its filter udev_monitor_filter_update( monitor ); // watch the child's socket ev.events = EPOLLIN; ev.data.fd = monitor->sock; rc = epoll_ctl( monitor->epoll_fd, EPOLL_CTL_ADD, monitor->sock, &ev ); if( rc < 0 ) { // not much we can do here, except log an error write( STDERR_FILENO, "Failed to add monitor socket\n", strlen("Failed to add monitor socket\n") ); udev_monitor_fs_shutdown( monitor ); g_monitor_table[i] = NULL; continue; } // reset the inotify watch rc = inotify_rm_watch( monitor->inotify_fd, monitor->events_wd ); monitor->events_wd = -1; if( rc < 0 ) { rc = -errno; if( rc == -EINVAL ) { // monitor->events_wd was invalid rc = 0; } else if( rc == -EBADF ) { // monitor->inotify_fd is invalid. // not much we can do here, except log an error write( STDERR_FILENO, "Invalid inotify handle\n", strlen("Invalid inotify handle")); udev_monitor_fs_shutdown( monitor ); g_monitor_table[i] = NULL; continue; } } if( rc == 0 ) { udev_monitor_fs_events_path( "", monitor->events_dir, i ); // try to create a new directory for this monitor rc = mkdir( monitor->events_dir, 0700 ); if( rc < 0 ) { // failed, we have. // child will not get any notifications from this monitor rc = -errno; write( STDERR_FILENO, "Failed to mkdir ", strlen("Failed to mkdir ") ); write( STDERR_FILENO, monitor->events_dir, strlen(monitor->events_dir) ); write( STDERR_FILENO, "\n", 1 ); udev_monitor_fs_shutdown( monitor ); g_monitor_table[i] = NULL; return; } // reconnect to the new directory monitor->events_wd = inotify_add_watch( monitor->inotify_fd, monitor->events_dir, UDEV_FS_WATCH_DIR_FLAGS ); if( monitor->events_wd < 0 ) { // there's not much we can safely do here, besides log an error write( STDERR_FILENO, "Failed to watch ", strlen( "Failed to watch " ) ); write( STDERR_FILENO, monitor->events_dir, strlen(monitor->events_dir) ); write( STDERR_FILENO, "\n", 1 ); } } else { // there's not much we can safely do here, besides log an error rc = -errno; write( STDERR_FILENO, "Failed to disconnect!\n", strlen("Failed to disconnect!\n") ); } } g_pid = pid; } g_monitor_table_unlock(); write( STDERR_FILENO, "end atfork()\n", strlen("end atfork()\n") ); // restore... errno = errsv; }