mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-08 02:18:53 +00:00
238 lines
6.8 KiB
C++
238 lines
6.8 KiB
C++
/*
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
#include <boost/numeric/ublas/matrix.hpp>
|
|
|
|
#include <osquery/logger.h>
|
|
#include <osquery/tables.h>
|
|
|
|
#include "osquery/events/darwin/fsevents.h"
|
|
|
|
namespace osquery {
|
|
|
|
std::map<FSEventStreamEventFlags, std::string> kMaskActions = {
|
|
{kFSEventStreamEventFlagItemChangeOwner, "ATTRIBUTES_MODIFIED"},
|
|
{kFSEventStreamEventFlagItemXattrMod, "ATTRIBUTES_MODIFIED"},
|
|
{kFSEventStreamEventFlagItemInodeMetaMod, "ATTRIBUTES_MODIFIED"},
|
|
{kFSEventStreamEventFlagItemCreated, "CREATED"},
|
|
{kFSEventStreamEventFlagItemRemoved, "DELETED"},
|
|
{kFSEventStreamEventFlagItemModified, "UPDATED"},
|
|
{kFSEventStreamEventFlagItemRenamed, "MOVED_TO"},
|
|
{kFSEventStreamEventFlagMustScanSubDirs, "COLLISION_WITHIN"},
|
|
{kFSEventStreamEventFlagUnmount, "UNMOUNTED"},
|
|
{kFSEventStreamEventFlagRootChanged, "ROOT_CHANGED"},
|
|
};
|
|
|
|
REGISTER(FSEventsEventPublisher, "event_publisher", "fsevents");
|
|
|
|
void FSEventsEventPublisher::restart() {
|
|
if (paths_.empty()) {
|
|
// There are no paths to watch.
|
|
return;
|
|
}
|
|
|
|
if (run_loop_ == nullptr) {
|
|
// There is no run loop to restart.
|
|
return;
|
|
}
|
|
|
|
// Build paths as CFStrings
|
|
std::vector<CFStringRef> cf_paths;
|
|
for (const auto& path : paths_) {
|
|
auto cf_path =
|
|
CFStringCreateWithCString(nullptr, path.c_str(), kCFStringEncodingUTF8);
|
|
cf_paths.push_back(cf_path);
|
|
}
|
|
|
|
// The FSEvents watch takes a CFArrayRef
|
|
auto watch_list = CFArrayCreate(nullptr,
|
|
reinterpret_cast<const void**>(&cf_paths[0]),
|
|
cf_paths.size(),
|
|
&kCFTypeArrayCallBacks);
|
|
|
|
// Remove any existing stream.
|
|
stop();
|
|
|
|
// Create the FSEvent stream
|
|
stream_ = FSEventStreamCreate(nullptr,
|
|
&FSEventsEventPublisher::Callback,
|
|
nullptr,
|
|
watch_list,
|
|
kFSEventStreamEventIdSinceNow,
|
|
1,
|
|
kFSEventStreamCreateFlagFileEvents |
|
|
kFSEventStreamCreateFlagNoDefer |
|
|
kFSEventStreamCreateFlagWatchRoot);
|
|
if (stream_ != nullptr) {
|
|
// Schedule the stream on the run loop.
|
|
FSEventStreamScheduleWithRunLoop(stream_, run_loop_, kCFRunLoopDefaultMode);
|
|
if (FSEventStreamStart(stream_)) {
|
|
stream_started_ = true;
|
|
} else {
|
|
LOG(ERROR) << "Cannot start FSEvent stream: FSEventStreamStart failed";
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Cannot create FSEvent stream: FSEventStreamCreate failed";
|
|
}
|
|
|
|
// Clean up strings, watch list, and context.
|
|
CFRelease(watch_list);
|
|
for (auto& cf_path : cf_paths) {
|
|
CFRelease(cf_path);
|
|
}
|
|
}
|
|
|
|
void FSEventsEventPublisher::stop() {
|
|
// Stop the stream.
|
|
if (stream_ != nullptr) {
|
|
FSEventStreamStop(stream_);
|
|
stream_started_ = false;
|
|
FSEventStreamUnscheduleFromRunLoop(
|
|
stream_, run_loop_, kCFRunLoopDefaultMode);
|
|
FSEventStreamInvalidate(stream_);
|
|
FSEventStreamRelease(stream_);
|
|
stream_ = nullptr;
|
|
}
|
|
|
|
// Stop the run loop.
|
|
if (run_loop_ != nullptr) {
|
|
CFRunLoopStop(run_loop_);
|
|
}
|
|
}
|
|
|
|
void FSEventsEventPublisher::tearDown() {
|
|
stop();
|
|
|
|
// Do not keep a reference to the run loop.
|
|
run_loop_ = nullptr;
|
|
}
|
|
|
|
void FSEventsEventPublisher::configure() {
|
|
// Rebuild the watch paths.
|
|
paths_.clear();
|
|
for (const auto& subscription : subscriptions_) {
|
|
auto fs_subscription = getSubscriptionContext(subscription->context);
|
|
paths_.insert(fs_subscription->path);
|
|
}
|
|
|
|
// There were no paths in the subscriptions?
|
|
if (paths_.empty()) {
|
|
return;
|
|
}
|
|
|
|
restart();
|
|
}
|
|
|
|
Status FSEventsEventPublisher::run() {
|
|
// The run entrypoint executes in a dedicated thread.
|
|
if (run_loop_ == nullptr) {
|
|
run_loop_ = CFRunLoopGetCurrent();
|
|
// Restart the stream creation.
|
|
restart();
|
|
}
|
|
|
|
// Start the run loop, it may be removed with a tearDown.
|
|
CFRunLoopRun();
|
|
return Status(0, "OK");
|
|
}
|
|
|
|
void FSEventsEventPublisher::end() { stop(); }
|
|
|
|
void FSEventsEventPublisher::Callback(
|
|
ConstFSEventStreamRef stream,
|
|
void* callback_info,
|
|
size_t num_events,
|
|
void* event_paths,
|
|
const FSEventStreamEventFlags fsevent_flags[],
|
|
const FSEventStreamEventId fsevent_ids[]) {
|
|
for (size_t i = 0; i < num_events; ++i) {
|
|
auto ec = createEventContext();
|
|
ec->fsevent_stream = stream;
|
|
ec->fsevent_flags = fsevent_flags[i];
|
|
ec->transaction_id = fsevent_ids[i];
|
|
ec->path = std::string(((char**)event_paths)[i]);
|
|
|
|
if (ec->fsevent_flags & kFSEventStreamEventFlagMustScanSubDirs) {
|
|
// The FSEvents thread coalesced events within and will report a root.
|
|
TLOG << "FSEvents collision, root: " << ec->path;
|
|
}
|
|
|
|
if (ec->fsevent_flags & kFSEventStreamEventFlagRootChanged) {
|
|
// Must rescan for the changed root.
|
|
}
|
|
|
|
if (ec->fsevent_flags & kFSEventStreamEventFlagUnmount) {
|
|
// Should remove the watch on this path.
|
|
}
|
|
|
|
// Record the string-version of the first matched mask bit.
|
|
bool has_action = false;
|
|
for (const auto& action : kMaskActions) {
|
|
if (ec->fsevent_flags & action.first) {
|
|
// Actions may be multiplexed. Fire and event for each.
|
|
ec->action = action.second;
|
|
EventFactory::fire<FSEventsEventPublisher>(ec);
|
|
has_action = true;
|
|
}
|
|
}
|
|
|
|
if (!has_action) {
|
|
// If no action was matched for this path event, fire and unknown.
|
|
ec->action = "UNKNOWN";
|
|
EventFactory::fire<FSEventsEventPublisher>(ec);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FSEventsEventPublisher::shouldFire(
|
|
const FSEventsSubscriptionContextRef& mc,
|
|
const FSEventsEventContextRef& ec) const {
|
|
// This is stopping us from getting events on links.
|
|
// If we need this feature later, this line will have to be updated to
|
|
// understand links.
|
|
ssize_t found = ec->path.find(mc->path);
|
|
if (found != 0) {
|
|
return false;
|
|
}
|
|
|
|
if (mc->mask != 0 && !(ec->fsevent_flags & mc->mask)) {
|
|
// Compare the event context mask to the subscription context.
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FSEventsEventPublisher::flush(bool async) {
|
|
if (stream_ != nullptr && stream_started_) {
|
|
if (async) {
|
|
FSEventStreamFlushAsync(stream_);
|
|
} else {
|
|
FSEventStreamFlushSync(stream_);
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t FSEventsEventPublisher::numSubscriptionedPaths() {
|
|
return paths_.size();
|
|
}
|
|
|
|
bool FSEventsEventPublisher::isStreamRunning() {
|
|
if (stream_ == nullptr || !stream_started_) {
|
|
return false;
|
|
}
|
|
|
|
if (run_loop_ == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
return CFRunLoopIsWaiting(run_loop_);
|
|
}
|
|
}
|