bool INotifyEventPublisher::monitorSubscription( INotifySubscriptionContextRef& sc, bool add_watch) { sc->discovered_ = sc->path; if (sc->path.find("**") != std::string::npos) { sc->recursive = true; sc->discovered_ = sc->path.substr(0, sc->path.find("**")); sc->path = sc->discovered_; } if (sc->path.find('*') != std::string::npos) { // If the wildcard exists within the file (leaf), remove and monitor the // directory instead. Apply a fnmatch on fired events to filter leafs. auto fullpath = fs::path(sc->path); if (fullpath.filename().string().find('*') != std::string::npos) { sc->discovered_ = fullpath.parent_path().string() + '/'; } if (sc->discovered_.find('*') != std::string::npos) { // If a wildcard exists within the tree (stem), resolve at configure // time and monitor each path. std::vector<std::string> paths; resolveFilePattern(sc->discovered_, paths); for (const auto& _path : paths) { addMonitor(_path, sc->mask, sc->recursive, add_watch); } sc->recursive_match = sc->recursive; return true; } } if (isDirectory(sc->discovered_) && sc->discovered_.back() != '/') { sc->path += '/'; sc->discovered_ += '/'; } return addMonitor(sc->discovered_, sc->mask, sc->recursive, add_watch); }
bool INotifyEventPublisher::addMonitor(const std::string& path, bool recursive) { if (!isPathMonitored(path)) { int watch = ::inotify_add_watch(getHandle(), path.c_str(), IN_ALL_EVENTS); if (watch == -1) { LOG(ERROR) << "Could not add inotfy watch on: " << path; return false; } // Keep a list of the watch descriptors descriptors_.push_back(watch); // Keep a map of the path -> watch descriptor path_descriptors_[path] = watch; // Keep a map of the opposite (descriptor -> path) descriptor_paths_[watch] = path; } if (recursive && isDirectory(path).ok()) { std::vector<std::string> children; // Get a list of children of this directory (requesed recursive watches). listDirectoriesInDirectory(path, children); for (const auto& child : children) { addMonitor(child, recursive); } } return true; }
bool INotifyEventPublisher::addMonitor(const std::string& path, uint32_t mask, bool recursive, bool add_watch) { if (!isPathMonitored(path)) { int watch = ::inotify_add_watch( getHandle(), path.c_str(), ((mask == 0) ? kFileDefaultMasks : mask)); if (add_watch && watch == -1) { LOG(WARNING) << "Could not add inotify watch on: " << path; return false; } // Keep a list of the watch descriptors descriptors_.push_back(watch); // Keep a map of the path -> watch descriptor path_descriptors_[path] = watch; // Keep a map of the opposite (descriptor -> path) descriptor_paths_[watch] = path; } if (recursive && isDirectory(path).ok()) { std::vector<std::string> children; // Get a list of children of this directory (requested recursive watches). listDirectoriesInDirectory(path, children, true); boost::system::error_code ec; for (const auto& child : children) { auto canonicalized = fs::canonical(child, ec).string() + '/'; addMonitor(canonicalized, mask, false); } } return true; }
Status INotifyEventPublisher::run() { // Get a while wrapper for free. char buffer[BUFFER_SIZE]; fd_set set; FD_ZERO(&set); FD_SET(getHandle(), &set); struct timeval timeout = {3, 3000}; int selector = ::select(getHandle() + 1, &set, nullptr, nullptr, &timeout); if (selector == -1) { LOG(ERROR) << "Could not read inotify handle"; return Status(1, "INotify handle failed"); } if (selector == 0) { // Read timeout. return Status(0, "Continue"); } ssize_t record_num = ::read(getHandle(), buffer, BUFFER_SIZE); if (record_num == 0 || record_num == -1) { return Status(1, "INotify read failed"); } for (char* p = buffer; p < buffer + record_num;) { // Cast the inotify struct, make shared pointer, and append to contexts. auto event = reinterpret_cast<struct inotify_event*>(p); if (event->mask & IN_Q_OVERFLOW) { // The inotify queue was overflown (remove all paths). Status stat = restartMonitoring(); if(!stat.ok()){ return stat; } } if (event->mask & IN_IGNORED) { // This inotify watch was removed. removeMonitor(event->wd, false); } else if (event->mask & IN_MOVE_SELF) { // This inotify path was moved, but is still watched. removeMonitor(event->wd, true); } else if (event->mask & IN_DELETE_SELF) { // A file was moved to replace the watched path. removeMonitor(event->wd, false); } else { auto ec = createEventContextFrom(event); if(event->mask & IN_CREATE && isDirectory(ec->path).ok()){ addMonitor(ec->path, 1); } fire(ec); } // Continue to iterate p += (sizeof(struct inotify_event)) + event->len; } osquery::publisherSleep(kINotifyMLatency); return Status(0, "Continue"); }
void INotifyEventPublisher::configure() { for (const auto& sub : subscriptions_) { // Anytime a configure is called, try to monitor all subscriptions. // Configure is called as a response to removing/adding subscriptions. // This means recalculating all monitored paths. auto sc = getSubscriptionContext(sub->context); addMonitor(sc->path, sc->recursive); } }
void INotifyEventPublisher::configure() { for (auto& sub : subscriptions_) { // Anytime a configure is called, try to monitor all subscriptions. // Configure is called as a response to removing/adding subscriptions. // This means recalculating all monitored paths. auto sc = getSubscriptionContext(sub->context); if (sc->discovered_.size() > 0) { continue; } sc->discovered_ = sc->path; if (sc->path.find("**") != std::string::npos) { sc->recursive = true; sc->discovered_ = sc->path.substr(0, sc->path.find("**")); sc->path = sc->discovered_; } if (sc->path.find('*') != std::string::npos) { // If the wildcard exists within the file (leaf), remove and monitor the // directory instead. Apply a fnmatch on fired events to filter leafs. auto fullpath = fs::path(sc->path); if (fullpath.filename().string().find('*') != std::string::npos) { sc->discovered_ = fullpath.parent_path().string(); } if (sc->discovered_.find('*') != std::string::npos) { // If a wildcard exists within the tree (stem), resolve at configure // time and monitor each path. std::vector<std::string> paths; resolveFilePattern(sc->discovered_, paths); for (const auto& _path : paths) { addMonitor(_path, sc->recursive); } sc->recursive_match = sc->recursive; continue; } } addMonitor(sc->discovered_, sc->recursive); } }
TEST_F(INotifyTests, test_inotify_match_subscription) { event_pub_ = std::make_shared<INotifyEventPublisher>(true); addMonitor("/etc", IN_ALL_EVENTS, false, false); EXPECT_EQ(event_pub_->path_descriptors_.count("/etc"), 1U); // This will fail because there is no trailing "/" at the end. // The configure component should take care of these paths. EXPECT_FALSE(event_pub_->isPathMonitored("/etc/passwd")); event_pub_->path_descriptors_.clear(); // Calling addMonitor the correct way. addMonitor("/etc/", IN_ALL_EVENTS, false, false); EXPECT_TRUE(event_pub_->isPathMonitored("/etc/passwd")); event_pub_->path_descriptors_.clear(); // Test the matching capability. { auto sc = event_pub_->createSubscriptionContext(); sc->path = "/etc"; event_pub_->monitorSubscription(sc, false); EXPECT_EQ(sc->path, "/etc/"); EXPECT_TRUE(event_pub_->isPathMonitored("/etc/")); EXPECT_TRUE(event_pub_->isPathMonitored("/etc/passwd")); } std::vector<std::string> valid_dirs = {"/etc", "/etc/", "/etc/*"}; for (const auto& dir : valid_dirs) { event_pub_->path_descriptors_.clear(); auto sc = event_pub_->createSubscriptionContext(); sc->path = dir; event_pub_->monitorSubscription(sc, false); auto ec = event_pub_->createEventContext(); ec->path = "/etc/"; EXPECT_TRUE(event_pub_->shouldFire(sc, ec)); ec->path = "/etc/passwd"; EXPECT_TRUE(event_pub_->shouldFire(sc, ec)); } }