//关闭监控句柄等,解除绑定reactor等 int ZCE_Event_INotify::close() { #if defined (ZCE_OS_LINUX) //由于是HASH MAP速度有点慢 HDL_TO_EIN_MAP::iterator iter_temp = watch_event_map_.begin(); for (; iter_temp != watch_event_map_.end();) { rm_watch(iter_temp->second.watch_handle_); //让迭代器继续从最开始干起 iter_temp = watch_event_map_.begin(); } //关闭监控句柄 if (inotify_handle_ != ZCE_INVALID_HANDLE) { //从反应器移除 reactor()->remove_handler(this, false); ::close(inotify_handle_); inotify_handle_ = ZCE_INVALID_HANDLE; } #elif defined (ZCE_OS_WINDOWS) rm_watch(watch_handle_); #endif return 0; }
static void rm_watch(int wd, bool update_parent) { watch_node* node = table_get(watches, wd); if (node == NULL) { return; } userlog(LOG_DEBUG, "unwatching %s: %d (%p)", node->path, node->wd, node); if (inotify_rm_watch(inotify_fd, node->wd) < 0) { userlog(LOG_DEBUG, "inotify_rm_watch(%d:%s): %s", node->wd, node->path, strerror(errno)); } for (int i=0; i<array_size(node->kids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL) { rm_watch(kid->wd, false); } } if (update_parent && node->parent != NULL) { for (int i=0; i<array_size(node->parent->kids); i++) { if (array_get(node->parent->kids, i) == node) { array_put(node->parent->kids, i, NULL); break; } } } array_delete(node->kids); free(node); table_put(watches, wd, NULL); }
static int walk_tree(int path_len, watch_node* parent, bool recursive, array* mounts) { for (int j=0; j<array_size(mounts); j++) { char* mount = array_get(mounts, j); if (strncmp(path_buf, mount, strlen(mount)) == 0) { userlog(LOG_DEBUG, "watch path '%s' crossed mount point '%s' - skipping", path_buf, mount); return ERR_IGNORE; } } DIR* dir = NULL; if (recursive) { if ((dir = opendir(path_buf)) == NULL) { if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) { userlog(LOG_DEBUG, "opendir(%s): %d", path_buf, errno); return ERR_IGNORE; } else { userlog(LOG_ERR, "opendir(%s): %s", path_buf, strerror(errno)); return ERR_CONTINUE; } } } int id = add_watch(path_len, parent); if (dir == NULL) { return id; } else if (id < 0) { closedir(dir); return id; } path_buf[path_len] = '/'; struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (entry->d_type != DT_DIR || strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } int name_len = strlen(entry->d_name); memcpy(path_buf + path_len + 1, entry->d_name, name_len + 1); int subdir_id = walk_tree(path_len + 1 + name_len, table_get(watches, id), recursive, mounts); if (subdir_id < 0 && subdir_id != ERR_IGNORE) { rm_watch(id, true); id = subdir_id; break; } } closedir(dir); return id; }
static int walk_tree(const char* path, watch_node* parent, array* ignores) { if (is_ignored(path, ignores)) { return ERR_IGNORE; } DIR* dir = opendir(path); if (dir == NULL) { if (errno == EACCES) { return ERR_IGNORE; } else if (errno == ENOTDIR) { // flat root return add_watch(path, parent); } userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno)); return ERR_CONTINUE; } int id = add_watch(path, parent); if (id < 0) { closedir(dir); return id; } struct dirent* entry; char subdir[PATH_MAX]; strcpy(subdir, path); if (subdir[strlen(subdir) - 1] != '/') { strcat(subdir, "/"); } char* p = subdir + strlen(subdir); while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } strcpy(p, entry->d_name); if (!is_directory(entry, subdir)) { continue; } int subdir_id = walk_tree(subdir, table_get(watches, id), ignores); if (subdir_id < 0 && subdir_id != ERR_IGNORE) { rm_watch(id, true); id = subdir_id; break; } } closedir(dir); return id; }
static bool process_inotify_event(struct inotify_event* event) { watch_node* node = table_get(watches, event->wd); if (node == NULL) { return true; } bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR; userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->path); memcpy(path_buf, node->path, node->path_len + 1); int path_len = node->path_len; if (event->len > 0) { path_buf[node->path_len] = '/'; int name_len = strlen(event->name); memcpy(path_buf + path_len + 1, event->name, name_len + 1); path_len += name_len + 1; } if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) { int result = walk_tree(path_len, node, true, NULL); if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) { return false; } } if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) { for (int i=0; i<array_size(node->kids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL && strncmp(path_buf, kid->path, kid->path_len) == 0) { rm_watch(kid->wd, false); array_put(node->kids, i, NULL); break; } } } if (callback != NULL) { (*callback)(path_buf, event->mask); } return true; }
static bool process_inotify_event(struct inotify_event* event) { watch_node* node = table_get(watches, event->wd); if (node == NULL) { return true; } userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), (event->mask & IN_ISDIR) != 0, node->name); char path[PATH_MAX]; strcpy(path, node->name); if (event->len > 0) { if (path[strlen(path) - 1] != '/') { strcat(path, "/"); } strcat(path, event->name); } if ((event->mask & IN_CREATE || event->mask & IN_MOVED_TO) && event->mask & IN_ISDIR) { int result = walk_tree(path, node, NULL); if (result < 0 && result != ERR_IGNORE) { return false; } } if ((event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) && event->mask & IN_ISDIR) { for (int i=0; i<array_size(node->kids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL && strcmp(kid->name, path) == 0) { rm_watch(kid->wd, false); array_put(node->kids, i, NULL); break; } } } if (callback != NULL) { (*callback)(path, event->mask); } return true; }
//读取事件触发调用函数 int ZCE_Event_INotify::handle_input () { #if defined (ZCE_OS_LINUX) ZCE_LOG(RS_DEBUG, "ZCE_Event_INotify::handle_input"); int detect_ret = 0; size_t watch_event_num = 0; //读取 ssize_t read_ret = ZCE_LIB::read(inotify_handle_, read_buffer_, READ_BUFFER_LEN); if (read_ret <= 0) { return -1; } uint32_t read_len = static_cast<uint32_t>(read_ret); uint32_t next_entry_offset = 0; //可能一次读取出来多个inotify_event数据,所以要循环处理 do { detect_ret = 0; ::inotify_event *ne_ptr = (::inotify_event *) (read_buffer_ + next_entry_offset); //检查读取的数据是否还有一个, read_len -= (sizeof(::inotify_event) + ne_ptr->len); next_entry_offset += sizeof(::inotify_event) + ne_ptr->len; HDL_TO_EIN_MAP::iterator active_iter = watch_event_map_.find(ne_ptr->wd); if (active_iter == watch_event_map_.end()) { //某个FD在MAP中间无法找到,最大的可能是 ZCE_LOG(RS_DEBUG, "You code error or a handle not in map (delete in this do while), please check you code. handle[%u]", ne_ptr->wd); continue; } EVENT_INOTIFY_NODE *node_ptr = &(active_iter->second); const char *active_path = ne_ptr->name; //根据返回的mask决定如何处理 //注意下面的代码分支用的if else if ,而不是if if,我的初步看法是这些事件不会一起触发,但也许不对。 //下面5个是和Windows 共有的, uint32_t event_mask = ne_ptr->mask; if (event_mask & IN_CREATE ) { detect_ret = inotify_create(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if (event_mask & IN_DELETE ) { detect_ret = inotify_delete(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if ( event_mask & IN_MODIFY ) { detect_ret = inotify_modify(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if ( event_mask & IN_MOVED_FROM) { detect_ret = inotify_moved_from(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if ( event_mask & IN_MOVED_TO) { detect_ret = inotify_moved_to(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } //下面这些是LINUX自己特有的 else if ( event_mask & IN_ACCESS) { detect_ret = inotify_access(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if (event_mask & IN_OPEN) { detect_ret = inotify_open(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if (event_mask & IN_CLOSE_WRITE || event_mask | IN_CLOSE_NOWRITE) { detect_ret = inotify_close(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if (event_mask & IN_ATTRIB) { detect_ret = inotify_attrib(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if (event_mask & IN_MOVE_SELF) { detect_ret = inotify_move_slef(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } else if (event_mask & IN_DELETE_SELF) { detect_ret = inotify_delete_slef(node_ptr->watch_handle_, event_mask, node_ptr->watch_path_, active_path); } //返回-1,关闭之, if (detect_ret == -1) { rm_watch(node_ptr->watch_handle_); } //累计发现事件计数 ++(watch_event_num); } while (read_len > 0); return 0; #elif defined (ZCE_OS_WINDOWS) int detect_ret = 0; DWORD num_read = 0; BOOL bret = ::GetOverlappedResult(watch_handle_, &over_lapped_, &num_read, FALSE); //读取结果失败 if (FALSE == bret) { ZCE_LOG(RS_ERROR, "[%s] ::GetOverlappedResult fail,error [%u].", __ZCE_FUNC__, ZCE_LIB::last_error()); return -1; } //记录当前处理的句柄, FILE_NOTIFY_INFORMATION *read_ptr = NULL; DWORD next_entry_offset = 0; do { detect_ret = 0; read_ptr = (FILE_NOTIFY_INFORMATION *)(read_buffer_ + next_entry_offset); //文件名称进行转换,从宽字节转换为多字节, //天杀的Windows在这些地方又埋了陷阱FILE_NOTIFY_INFORMATION里面的长度FileNameLength是字节长度 //而WideCharToMultiByte函数需要的长度是字长度,你能TMD统一一点吗? int length_of_ws = ::WideCharToMultiByte(CP_ACP, 0, read_ptr->FileName, read_ptr->FileNameLength / sizeof(wchar_t), NULL, 0, NULL, NULL); //Windows 的目录名称最大长度可以到3K,我没有兴趣去搞一套这个玩 if (length_of_ws >= MAX_PATH) { ZCE_LOG(RS_ALERT, "My God ,your path length [%u] more than MAX_PATH [%u],I don't process this.", length_of_ws, MAX_PATH); continue; } char active_path[MAX_PATH + 1]; ::WideCharToMultiByte(CP_ACP, 0, read_ptr->FileName, read_ptr->FileNameLength / sizeof(wchar_t), active_path, length_of_ws, NULL, NULL); active_path[length_of_ws] = '\0'; //根据Action和mask确定调用函数 switch (read_ptr->Action) { case FILE_ACTION_ADDED: if (watch_mask_ | IN_CREATE) { detect_ret = inotify_create(watch_handle_, watch_mask_, watch_path_, active_path); } break; case FILE_ACTION_REMOVED: if (watch_mask_ | IN_DELETE) { detect_ret = inotify_delete(watch_handle_, watch_mask_, watch_path_, active_path); } break; //注意Windows 下的这个类型,包括了属性更改 case FILE_ACTION_MODIFIED: if (watch_mask_ | IN_MODIFY) { detect_ret = inotify_modify(watch_handle_, watch_mask_, watch_path_, active_path); } break; case FILE_ACTION_RENAMED_OLD_NAME: if (watch_mask_ | IN_MOVED_FROM) { detect_ret = inotify_moved_from(watch_handle_, watch_mask_, watch_path_, active_path); } break; case FILE_ACTION_RENAMED_NEW_NAME: if (watch_mask_ | IN_MOVED_TO) { detect_ret = inotify_moved_to(watch_handle_, watch_mask_, watch_path_, active_path); } break; } //累计偏移长度 next_entry_offset += read_ptr->NextEntryOffset; //返回-1,关闭之 if (detect_ret == -1) { handle_close(); } //为什么要这样做,因为上面的处理过程,可能有人已经调用了rm_watch,或者handle_close, if (watch_handle_ == ZCE_INVALID_HANDLE) { return 0; } } while (read_ptr->NextEntryOffset != 0); DWORD bytes_returned = 0; //继续进行监控处理 bret = ::ReadDirectoryChangesW( watch_handle_, // handle to directory read_buffer_, // read results buffer READ_BUFFER_LEN, // length of buffer watch_sub_dir_, // monitoring option FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions &bytes_returned, // bytes returned & (over_lapped_), // overlapped buffer NULL // completion routine ); if (FALSE == bret) { ZCE_LOG(RS_ERROR, "[zcelib][%s] ::ReadDirectoryChangesW fail,error [%u].", __ZCE_FUNC__, ZCE_LIB::last_error()); } return 0; #endif }
pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const char *device_name) { pa_reserve_monitor_wrapper *w; int k; char *t; #ifdef HAVE_DBUS DBusError error; dbus_error_init(&error); #endif pa_assert(c); pa_assert(device_name); t = pa_sprintf_malloc("reserve-monitor-wrapper@%s", device_name); if ((w = pa_shared_get(c, t))) { pa_xfree(t); pa_assert(PA_REFCNT_VALUE(w) >= 1); PA_REFCNT_INC(w); return w; } w = pa_xnew0(pa_reserve_monitor_wrapper, 1); PA_REFCNT_INIT(w); w->core = c; pa_hook_init(&w->hook, w); w->shared_name = t; pa_assert_se(pa_shared_set(c, w->shared_name, w) >= 0); #ifdef HAVE_DBUS if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); /* We don't treat this as error here because we want allow PA * to run even when no session bus is available. */ return w; } if ((k = rm_watch( &w->monitor, pa_dbus_connection_get(w->connection), device_name, change_cb, NULL)) < 0) { pa_log_debug("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k)); goto fail; } pa_log_debug("Successfully create reservation lock monitor for device '%s'", device_name); rm_set_userdata(w->monitor, w); return w; fail: dbus_error_free(&error); reserve_monitor_wrapper_free(w); return NULL; #else return w; #endif }
void unwatch(int id) { rm_watch(id, true); }