@@ -26,9 +26,11 @@ namespace fs = boost::filesystem;

namespace osquery {

static const int kINotifyMLatency = 200;
static const uint32_t kINotifyBufferSize =
(10 * ((sizeof(struct inotify_event)) + NAME_MAX + 1));
static const size_t kINotifyMaxEvents = 512;
static const size_t kINotifyEventSize =
sizeof(struct inotify_event) + (NAME_MAX + 1);
static const size_t kINotifyBufferSize =
(kINotifyMaxEvents * kINotifyEventSize);

std::map<int, std::string> kMaskActions = {
{IN_ACCESS, "ACCESSED"},
@@ -64,41 +66,64 @@ Status INotifyEventPublisher::setUp() {
return Status(0, "OK");
}

bool INotifyEventPublisher::needMonitoring(const std::string& path,
INotifySubscriptionContextRef& isc,
uint32_t mask,
bool recursive,
bool add_watch) {
bool rc = true;
struct stat file_dir_stat;
time_t sc_time = isc->path_sc_time_[path];

if (stat(path.c_str(), &file_dir_stat) == -1) {
LOG(WARNING) << "Failed to do stat on: " << path;
return false;
}

if (sc_time != file_dir_stat.st_ctime) {
if ((rc = addMonitor(path, isc, isc->mask, isc->recursive, add_watch))) {
isc->path_sc_time_[path] = file_dir_stat.st_ctime;
}
}
return rc;
}

bool INotifyEventPublisher::monitorSubscription(
INotifySubscriptionContextRef& sc, bool add_watch) {
sc->discovered_ = sc->path;
std::string 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_;
discovered = sc->path.substr(0, sc->path.find("**"));
sc->path = 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() + '/';
discovered = fullpath.parent_path().string() + '/';
}

if (sc->discovered_.find('*') != std::string::npos) {
if (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);
resolveFilePattern(discovered, paths);
sc->recursive_match = sc->recursive;
for (const auto& _path : paths) {
addMonitor(_path, sc->mask, sc->recursive, add_watch);
needMonitoring(_path, sc, sc->mask, sc->recursive, add_watch);
}
sc->recursive_match = sc->recursive;
return true;
}
}

if (isDirectory(sc->discovered_) && sc->discovered_.back() != '/') {
if (isDirectory(discovered) && discovered.back() != '/') {
sc->path += '/';
sc->discovered_ += '/';
discovered += '/';
}
return addMonitor(sc->discovered_, sc->mask, sc->recursive, add_watch);

return needMonitoring(discovered, sc, sc->mask, sc->recursive, add_watch);
}

void INotifyEventPublisher::configure() {
@@ -107,14 +132,38 @@ void INotifyEventPublisher::configure() {
return;
}

SubscriptionVector delete_subscriptions;
{
WriteLock lock(subscription_lock_);
auto end = std::remove_if(
subscriptions_.begin(),
subscriptions_.end(),
[&delete_subscriptions](const SubscriptionRef& subscription) {
auto inotify_sc = getSubscriptionContext(subscription->context);
if (inotify_sc->mark_for_deletion == true) {
delete_subscriptions.push_back(subscription);
return true;
}
return false;
});
subscriptions_.erase(end, subscriptions_.end());
}

for (auto& sub : delete_subscriptions) {
auto ino_sc = getSubscriptionContext(sub->context);
for (const auto& path : ino_sc->descriptor_paths_) {
removeMonitor(path.first, true, true);
}
ino_sc->descriptor_paths_.clear();
}

delete_subscriptions.clear();

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;
}
monitorSubscription(sc);
}
}
@@ -125,47 +174,37 @@ void INotifyEventPublisher::tearDown() {
}
inotify_handle_ = -1;

auto descriptors = descriptors_;
for (const auto& desc : descriptors) {
removeMonitor(desc, false);
}

{
// Then remove all path/descriptor mappings.
WriteLock lock(path_mutex_);
path_descriptors_.clear();
descriptor_paths_.clear();
}

WriteLock lock(scratch_mutex_);
if (scratch_ != nullptr) {
free(scratch_);
scratch_ = nullptr;
}
}

Status INotifyEventPublisher::restartMonitoring() {
if (last_restart_ != 0 && getUnixTime() - last_restart_ < 10) {
return Status(1, "Overflow");
void INotifyEventPublisher::handleOverflow() {
if (inotify_events_ < kINotifyMaxEvents) {
VLOG(1) << "inotify was overflown: increasing scratch buffer";
// Exponential increment.
inotify_events_ = inotify_events_ * 2;
} else if (last_overflow_ != -1 && getUnixTime() - last_overflow_ < 60) {
return;
} else {
VLOG(1) << "inotify was overflown";
last_overflow_ = getUnixTime();
}
last_restart_ = getUnixTime();

tearDown();
setUp();

// Reconfigure ourself, the subscribers will not reconfigure.
configure();
return Status(0, "OK");
}

Status INotifyEventPublisher::run() {
struct pollfd fds[1];
fds[0].fd = getHandle();
fds[0].events = POLLIN;
int selector = ::poll(fds, 1, 1000);
if (selector == -1 && errno != EINTR && errno != EAGAIN) {
if (selector == -1) {
if (errno == EINTR) {
return Status(0, "inotify poll interrupted");
}
LOG(WARNING) << "Could not read inotify handle";
return restartMonitoring();
return Status(1, "inotify poll failed");
}

if (selector == 0) {
@@ -178,7 +217,8 @@ Status INotifyEventPublisher::run() {
}

WriteLock lock(scratch_mutex_);
ssize_t record_num = ::read(getHandle(), scratch_, kINotifyBufferSize);
ssize_t record_num =
::read(getHandle(), scratch_, inotify_events_ * kINotifyEventSize);
if (record_num == 0 || record_num == -1) {
return Status(1, "INotify read failed");
}
@@ -187,11 +227,9 @@ Status INotifyEventPublisher::run() {
// 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).
return restartMonitoring();
}

if (event->mask & IN_IGNORED) {
// The inotify queue was overflown (try to recieve more events from OS).
handleOverflow();
} else if (event->mask & IN_IGNORED) {
// This inotify watch was removed.
removeMonitor(event->wd, false);
} else if (event->mask & IN_MOVE_SELF) {
@@ -210,7 +248,6 @@ Status INotifyEventPublisher::run() {
p += (sizeof(struct inotify_event)) + event->len;
}

pauseMilli(kINotifyMLatency);
return Status(0, "OK");
}

@@ -223,11 +260,12 @@ INotifyEventContextRef INotifyEventPublisher::createEventContextFrom(
// Get the pathname the watch fired on.
{
WriteLock lock(path_mutex_);
if (descriptor_paths_.find(event->wd) == descriptor_paths_.end()) {
if (descriptor_inosubctx_.find(event->wd) == descriptor_inosubctx_.end()) {
// return a blank event context if we can't find the paths for the event
return ec;
} else {
ec->path = descriptor_paths_.at(event->wd);
auto isc = descriptor_inosubctx_.at(event->wd);
ec->path = isc->descriptor_paths_.at(event->wd);
}
}

@@ -271,32 +309,45 @@ bool INotifyEventPublisher::shouldFire(const INotifySubscriptionContextRef& sc,
// inotify will not monitor recursively, new directories need watches.
if (sc->recursive && ec->action == "CREATED" && isDirectory(ec->path)) {
const_cast<INotifyEventPublisher*>(this)->addMonitor(
ec->path + '/', sc->mask, true);
ec->path + '/',
const_cast<INotifySubscriptionContextRef&>(sc),
sc->mask,
true);
}

return true;
}

bool INotifyEventPublisher::addMonitor(const std::string& path,
INotifySubscriptionContextRef& isc,
uint32_t mask,
bool recursive,
bool add_watch) {
if (!isPathMonitored(path)) {
{
WriteLock lock(path_mutex_);
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;
}

{
WriteLock lock(path_mutex_);
// Keep a list of the watch descriptors
descriptors_.push_back(watch);
if (descriptor_inosubctx_.find(watch) != descriptor_inosubctx_.end()) {
auto ino_sc = descriptor_inosubctx_.at(watch);
if (inotify_sanity_check) {
std::string watched_path = ino_sc->descriptor_paths_[watch];
path_descriptors_.erase(watched_path);
}
ino_sc->descriptor_paths_.erase(watch);
descriptor_inosubctx_.erase(watch);
}

// Keep a map of (descriptor -> path)
isc->descriptor_paths_[watch] = path;
descriptor_inosubctx_[watch] = isc;
if (inotify_sanity_check) {
// Keep a map of the path -> watch descriptor
path_descriptors_[path] = watch;
// Keep a map of the opposite (descriptor -> path)
descriptor_paths_[watch] = path;
}
}

@@ -308,57 +359,73 @@ bool INotifyEventPublisher::addMonitor(const std::string& path,
boost::system::error_code ec;
for (const auto& child : children) {
auto canonicalized = fs::canonical(child, ec).string() + '/';
addMonitor(canonicalized, mask, false);
addMonitor(canonicalized, isc, mask, false);
}
}

return true;
}

bool INotifyEventPublisher::removeMonitor(const std::string& path, bool force) {
bool INotifyEventPublisher::removeMonitor(int watch,
bool force,
bool batch_del) {
{
WriteLock lock(path_mutex_);
// If force then remove from INotify, otherwise cleanup file descriptors.
if (path_descriptors_.find(path) == path_descriptors_.end()) {
if (descriptor_inosubctx_.find(watch) == descriptor_inosubctx_.end()) {
return false;
}
}

int watch = 0;
{
WriteLock lock(path_mutex_);
watch = path_descriptors_[path];
path_descriptors_.erase(path);
descriptor_paths_.erase(watch);
auto isc = descriptor_inosubctx_.at(watch);
descriptor_inosubctx_.erase(watch);

if (inotify_sanity_check) {
std::string watched_path = isc->descriptor_paths_[watch];
path_descriptors_.erase(watched_path);
}

auto position = std::find(descriptors_.begin(), descriptors_.end(), watch);
descriptors_.erase(position);
if (!batch_del) {
isc->descriptor_paths_.erase(watch);
}
}

if (force) {
::inotify_rm_watch(getHandle(), watch);
}

return true;
}

bool INotifyEventPublisher::removeMonitor(int watch, bool force) {
std::string path;
{
WriteLock lock(path_mutex_);
if (descriptor_paths_.find(watch) == descriptor_paths_.end()) {
return false;
}
path = descriptor_paths_[watch];
}
return removeMonitor(path, force);
void INotifyEventPublisher::removeSubscriptions(const std::string& subscriber) {
WriteLock lock(subscription_lock_);
std::for_each(subscriptions_.begin(),
subscriptions_.end(),
[&subscriber](const SubscriptionRef& sub) {
if (sub->subscriber_name == subscriber) {
getSubscriptionContext(sub->context)->mark_for_deletion =
true;
}
});
}

void INotifyEventPublisher::removeSubscriptions(const std::string& subscriber) {
auto paths = descriptor_paths_;
for (const auto& path : paths) {
removeMonitor(path.first, true);
Status INotifyEventPublisher::addSubscription(
const SubscriptionRef& subscription) {
WriteLock lock(subscription_lock_);
auto received_inotify_sc = getSubscriptionContext(subscription->context);
for (auto& sub : subscriptions_) {
auto inotify_sc = getSubscriptionContext(sub->context);
if (*received_inotify_sc == *inotify_sc) {
if (inotify_sc->mark_for_deletion) {
inotify_sc->mark_for_deletion = false;
return Status(0);
}
// Returing non zero signals EventSubscriber::subscribe
// dont bumpup subscription_count_.
return Status(1);
}
}
EventPublisherPlugin::removeSubscriptions(subscriber);

subscriptions_.push_back(subscription);
return Status(0);
}

bool INotifyEventPublisher::isPathMonitored(const std::string& path) const {
@@ -25,6 +25,11 @@ extern std::map<int, std::string> kMaskActions;
extern const uint32_t kFileDefaultMasks;
extern const uint32_t kFileAccessMasks;

// INotifySubscriptionContext containers
using PathDescriptorMap = std::map<std::string, int>;
using DescriptorPathMap = std::map<int, std::string>;
using PathStatusChangeTimeMap = std::map<std::string, time_t>;

/**
* @brief Subscription details for INotifyEventPublisher events.
*
@@ -40,6 +45,9 @@ struct INotifySubscriptionContext : public SubscriptionContext {
/// Subscription the following filesystem path.
std::string path;

/// original path, read from config
std::string opath;

/// Limit the `inotify` actions to the subscription mask (if not 0).
uint32_t mask{0};

@@ -49,6 +57,9 @@ struct INotifySubscriptionContext : public SubscriptionContext {
/// Save the category this path originated form within the config.
std::string category;

/// Lazy deletion of a subscription.
bool mark_for_deletion{false};

/**
* @brief Helper method to map a string action to `inotify` action mask bit.
*
@@ -65,16 +76,25 @@ struct INotifySubscriptionContext : public SubscriptionContext {
}

private:
/// During configure the INotify publisher may modify/optimize the paths.
std::string discovered_;

/// A configure-time pattern was expanded to match absolute paths.
bool recursive_match{false};

/// Map of inotify watch file descriptor to watched path string.
DescriptorPathMap descriptor_paths_;

/// Map of path and status change time of file/directory.
PathStatusChangeTimeMap path_sc_time_;

private:
friend class INotifyEventPublisher;
};

/// Overloaded '==' operator, to check if two inotify subscriptions are same.
inline bool operator==(const INotifySubscriptionContext& lsc,
const INotifySubscriptionContext& rsc) {
return ((lsc.category == rsc.category) && (lsc.opath == rsc.opath));
}

/**
* @brief Event details for INotifyEventPublisher events.
*/
@@ -96,10 +116,8 @@ using INotifyEventContextRef = std::shared_ptr<INotifyEventContext>;
using INotifySubscriptionContextRef =
std::shared_ptr<INotifySubscriptionContext>;

// Publisher containers
using DescriptorVector = std::vector<int>;
using PathDescriptorMap = std::map<std::string, int>;
using DescriptorPathMap = std::map<int, std::string>;
// Publisher container
using DescriptorINotifySubCtxMap = std::map<int, INotifySubscriptionContextRef>;

/**
* @brief A Linux `inotify` EventPublisher.
@@ -118,6 +136,10 @@ class INotifyEventPublisher
DECLARE_PUBLISHER("inotify");

public:
//@param unit_test publisher is instantiated for unit test.
INotifyEventPublisher(bool unit_test = false)
: inotify_sanity_check(unit_test) {}

virtual ~INotifyEventPublisher() {
tearDown();
}
@@ -134,9 +156,12 @@ class INotifyEventPublisher
/// The calling for beginning the thread's run loop.
Status run() override;

/// Remove all monitors and subscriptions.
/// Mark for delete, subscriptions.
void removeSubscriptions(const std::string& subscriber) override;

/// Only add the subscription, if it not already part of subscription list.
Status addSubscription(const SubscriptionRef& subscription) override;

private:
/// Helper/specialized event context creation.
INotifyEventContextRef createEventContextFrom(
@@ -148,6 +173,7 @@ class INotifyEventPublisher
}

/// Check all added Subscription%s for a path.
/// Used for sanity check from unit test(s).
bool isPathMonitored(const std::string& path) const;

/**
@@ -161,22 +187,36 @@ class INotifyEventPublisher
* recursively and add monitors to them.
*
* @param path complete (non-glob) canonical path to monitor.
* @param subscription context tracking the path.
* @param recursive perform a single recursive search of subdirectories.
* @param add_watch (testing only) should an inotify watch be created.
* @return success if the inotify watch was created.
*/
bool addMonitor(const std::string& path,
INotifySubscriptionContextRef& isc,
uint32_t mask,
bool recursive,
bool add_watch = true);

/**
* Some decision making code refactored in needMonitoring before calling
* addMonitor in the context of monitorSubscription.
* Decision to call addMonitor from the context of monitorSubscription
* is done based on the status change time of file/directory, since
* creation time is not available on linux.
*/
bool needMonitoring(const std::string& path,
INotifySubscriptionContextRef& isc,
uint32_t mask,
bool recursive,
bool add_watch);

/// Helper method to parse a subscription and add an equivalent monitor.
bool monitorSubscription(INotifySubscriptionContextRef& sc,
bool add_watch = true);

/// Remove an INotify watch (monitor) from our tracking.
bool removeMonitor(const std::string& path, bool force = false);
bool removeMonitor(int watch, bool force = false);
bool removeMonitor(int watch, bool force = false, bool batch_del = false);

/// Given a SubscriptionContext and INotifyEventContext match path and action.
bool shouldFire(const INotifySubscriptionContextRef& mc,
@@ -189,26 +229,30 @@ class INotifyEventPublisher

/// Get the number of actual INotify active descriptors.
size_t numDescriptors() const {
return descriptors_.size();
return descriptor_inosubctx_.size();
}

/// If we overflow, try and restart the monitor
Status restartMonitoring();

// Consider an event queue if separating buffering from firing/servicing.
DescriptorVector descriptors_;
/// If we overflow, try to read more events from OS at time.
void handleOverflow();

/// Map of watched path string to inotify watch file descriptor.
/// Used for sanity check from unit test(s).
PathDescriptorMap path_descriptors_;

/// Map of inotify watch file descriptor to watched path string.
DescriptorPathMap descriptor_paths_;
/// Map of inotify watch file descriptor to subscription context.
DescriptorINotifySubCtxMap descriptor_inosubctx_;

/// The inotify file descriptor handle.
std::atomic<int> inotify_handle_{-1};

/// Time in seconds of the last inotify restart.
std::atomic<int> last_restart_{-1};
/// Time in seconds of the last inotify overflow.
std::atomic<int> last_overflow_{-1};

/// Tracks how many events to be received from OS.
size_t inotify_events_{16};

/// Enable for sanity check from unit test(s).
bool inotify_sanity_check{false};

/**
* @brief Scratch space for reading INotify responses.
@@ -54,7 +54,7 @@ class INotifyTests : public testing::Test {
}

void StartEventLoop() {
event_pub_ = std::make_shared<INotifyEventPublisher>();
event_pub_ = std::make_shared<INotifyEventPublisher>(true);
auto status = EventFactory::registerEventPublisher(event_pub_);
FILE* fd = fopen(real_test_path.c_str(), "w");
fclose(fd);
@@ -101,15 +101,23 @@ class INotifyTests : public testing::Test {
fclose(fd);
}

void addMonitor(const std::string& path,
uint32_t mask,
bool recursive,
bool add_watch) {
auto sc = event_pub_->createSubscriptionContext();
event_pub_->addMonitor(path, sc, mask, recursive, add_watch);
}

void RemoveAll(std::shared_ptr<INotifyEventPublisher>& pub) {
pub->subscriptions_.clear();
// Reset monitors.
std::vector<std::string> monitors;
for (const auto& path : pub->path_descriptors_) {
monitors.push_back(path.first);
std::vector<int> wds;
for (const auto& path : pub->descriptor_inosubctx_) {
wds.push_back(path.first);
}
for (const auto& path : monitors) {
pub->removeMonitor(path, true);
for (const auto& wd : wds) {
pub->removeMonitor(wd, true);
}
}

@@ -137,7 +145,7 @@ class INotifyTests : public testing::Test {
};

TEST_F(INotifyTests, test_register_event_pub) {
auto pub = std::make_shared<INotifyEventPublisher>();
auto pub = std::make_shared<INotifyEventPublisher>(true);
auto status = EventFactory::registerEventPublisher(pub);
EXPECT_TRUE(status.ok());

@@ -150,7 +158,7 @@ TEST_F(INotifyTests, test_register_event_pub) {

TEST_F(INotifyTests, test_inotify_init) {
// Handle should not be initialized during ctor.
auto event_pub = std::make_shared<INotifyEventPublisher>();
auto event_pub = std::make_shared<INotifyEventPublisher>(true);
EXPECT_FALSE(event_pub->isHandleOpen());

// Registering the event type initializes inotify.
@@ -164,7 +172,7 @@ TEST_F(INotifyTests, test_inotify_init) {
}

TEST_F(INotifyTests, test_inotify_add_subscription_missing_path) {
auto pub = std::make_shared<INotifyEventPublisher>();
auto pub = std::make_shared<INotifyEventPublisher>(true);
EventFactory::registerEventPublisher(pub);

// This subscription path is fake, and will succeed.
@@ -178,7 +186,7 @@ TEST_F(INotifyTests, test_inotify_add_subscription_missing_path) {
}

TEST_F(INotifyTests, test_inotify_add_subscription_success) {
auto pub = std::make_shared<INotifyEventPublisher>();
auto pub = std::make_shared<INotifyEventPublisher>(true);
EventFactory::registerEventPublisher(pub);

// This subscription path *should* be real.
@@ -193,40 +201,40 @@ TEST_F(INotifyTests, test_inotify_add_subscription_success) {
}

TEST_F(INotifyTests, test_inotify_match_subscription) {
auto pub = std::make_shared<INotifyEventPublisher>();
pub->addMonitor("/etc", IN_ALL_EVENTS, false, false);
EXPECT_EQ(pub->path_descriptors_.count("/etc"), 1U);
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(pub->isPathMonitored("/etc/passwd"));
pub->path_descriptors_.clear();
EXPECT_FALSE(event_pub_->isPathMonitored("/etc/passwd"));
event_pub_->path_descriptors_.clear();

// Calling addMonitor the correct way.
pub->addMonitor("/etc/", IN_ALL_EVENTS, false, false);
EXPECT_TRUE(pub->isPathMonitored("/etc/passwd"));
pub->path_descriptors_.clear();
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 = pub->createSubscriptionContext();
auto sc = event_pub_->createSubscriptionContext();
sc->path = "/etc";
pub->monitorSubscription(sc, false);
event_pub_->monitorSubscription(sc, false);
EXPECT_EQ(sc->path, "/etc/");
EXPECT_TRUE(pub->isPathMonitored("/etc/"));
EXPECT_TRUE(pub->isPathMonitored("/etc/passwd"));
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) {
pub->path_descriptors_.clear();
auto sc = pub->createSubscriptionContext();
event_pub_->path_descriptors_.clear();
auto sc = event_pub_->createSubscriptionContext();
sc->path = dir;
pub->monitorSubscription(sc, false);
auto ec = pub->createEventContext();
event_pub_->monitorSubscription(sc, false);
auto ec = event_pub_->createEventContext();
ec->path = "/etc/";
EXPECT_TRUE(pub->shouldFire(sc, ec));
EXPECT_TRUE(event_pub_->shouldFire(sc, ec));
ec->path = "/etc/passwd";
EXPECT_TRUE(pub->shouldFire(sc, ec));
EXPECT_TRUE(event_pub_->shouldFire(sc, ec));
}
}

@@ -307,7 +315,7 @@ class TestINotifyEventSubscriber

TEST_F(INotifyTests, test_inotify_run) {
// Assume event type is registered.
event_pub_ = std::make_shared<INotifyEventPublisher>();
event_pub_ = std::make_shared<INotifyEventPublisher>(true);
auto status = EventFactory::registerEventPublisher(event_pub_);
EXPECT_TRUE(status.ok());

@@ -425,7 +433,7 @@ TEST_F(INotifyTests, test_inotify_directory_watch) {

TEST_F(INotifyTests, test_inotify_recursion) {
// Create a non-registered publisher and subscriber.
auto pub = std::make_shared<INotifyEventPublisher>();
auto pub = std::make_shared<INotifyEventPublisher>(true);
EventFactory::registerEventPublisher(pub);
auto sub = std::make_shared<TestINotifyEventSubscriber>();

@@ -477,7 +485,7 @@ TEST_F(INotifyTests, test_inotify_recursion) {

TEST_F(INotifyTests, test_inotify_embedded_wildcards) {
// Assume event type is not registered.
event_pub_ = std::make_shared<INotifyEventPublisher>();
event_pub_ = std::make_shared<INotifyEventPublisher>(true);
EventFactory::registerEventPublisher(event_pub_);

auto sub = std::make_shared<TestINotifyEventSubscriber>();
@@ -69,7 +69,7 @@ void FileEventSubscriber::configure() {
auto sc = createSubscriptionContext();
// Use the filesystem globbing pattern to determine recursiveness.
sc->recursive = 0;
sc->path = file;
sc->opath = sc->path = file;
sc->mask = kFileDefaultMasks;
if (accesses.count(category) > 0) {
sc->mask |= kFileAccessMasks;