osquery-1/osquery/events/linux/inotify.cpp

244 lines
6.9 KiB
C++
Raw Normal View History

/*
* 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.
*
*/
2014-09-19 08:54:33 +00:00
2014-09-23 03:33:58 +00:00
#include <sstream>
2014-09-19 08:54:33 +00:00
#include <linux/limits.h>
#include <osquery/events.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
2014-09-19 08:54:33 +00:00
#include "osquery/events/linux/inotify.h"
namespace osquery {
2014-09-23 03:33:58 +00:00
int kINotifyULatency = 200;
static const uint32_t BUFFER_SIZE =
(10 * ((sizeof(struct inotify_event)) + NAME_MAX + 1));
2014-09-19 08:54:33 +00:00
2014-10-28 00:37:36 +00:00
std::map<int, std::string> kMaskActions = {
{IN_ACCESS, "ACCESSED"},
{IN_ATTRIB, "ATTRIBUTES_MODIFIED"},
{IN_CLOSE_WRITE, "UPDATED"},
{IN_CREATE, "CREATED"},
{IN_DELETE, "DELETED"},
{IN_MODIFY, "UPDATED"},
{IN_MOVED_FROM, "MOVED_FROM"},
{IN_MOVED_TO, "MOVED_TO"},
{IN_OPEN, "OPENED"},
};
2014-09-23 03:33:58 +00:00
REGISTER(INotifyEventPublisher, "event_publisher", "inotify");
2015-01-30 18:44:25 +00:00
2014-12-08 10:22:59 +00:00
Status INotifyEventPublisher::setUp() {
2014-09-19 08:54:33 +00:00
inotify_handle_ = ::inotify_init();
// If this does not work throw an exception.
if (inotify_handle_ == -1) {
2014-12-08 10:22:59 +00:00
return Status(1, "Could not init inotify.");
2014-09-19 08:54:33 +00:00
}
2014-12-08 10:22:59 +00:00
return Status(0, "OK");
2014-09-19 08:54:33 +00:00
}
void INotifyEventPublisher::configure() {
2014-10-06 21:23:26 +00:00
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);
}
2014-09-19 08:54:33 +00:00
}
void INotifyEventPublisher::tearDown() {
2014-09-19 08:54:33 +00:00
::close(inotify_handle_);
inotify_handle_ = -1;
}
Status INotifyEventPublisher::run() {
2014-10-30 08:27:00 +00:00
// Get a while wrapper for free.
2014-09-19 08:54:33 +00:00
char buffer[BUFFER_SIZE];
fd_set set;
FD_ZERO(&set);
FD_SET(getHandle(), &set);
2014-09-23 03:33:58 +00:00
struct timeval timeout = {0, kINotifyULatency};
2014-09-19 08:54:33 +00:00
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;) {
2014-09-19 08:54:33 +00:00
// Cast the inotify struct, make shared pointer, and append to contexts.
auto event = reinterpret_cast<struct inotify_event*>(p);
2014-09-23 03:33:58 +00:00
if (event->mask & IN_Q_OVERFLOW) {
2014-10-06 21:23:26 +00:00
// The inotify queue was overflown (remove all paths).
2014-09-23 03:33:58 +00:00
return Status(1, "Overflow");
}
if (event->mask & IN_IGNORED) {
// This inotify watch was removed.
2014-10-06 21:23:26 +00:00
removeMonitor(event->wd, false);
2014-09-23 03:33:58 +00:00
} else if (event->mask & IN_MOVE_SELF) {
// This inotify path was moved, but is still watched.
2014-10-06 21:23:26 +00:00
removeMonitor(event->wd, true);
2014-09-23 03:33:58 +00:00
} else if (event->mask & IN_DELETE_SELF) {
// A file was moved to replace the watched path.
2014-10-06 21:23:26 +00:00
removeMonitor(event->wd, false);
2014-09-23 03:33:58 +00:00
} else {
auto ec = createEventContextFrom(event);
2014-09-23 03:33:58 +00:00
fire(ec);
}
2014-09-19 08:54:33 +00:00
// Continue to iterate
p += (sizeof(struct inotify_event)) + event->len;
}
2014-09-23 03:33:58 +00:00
::usleep(kINotifyULatency);
2014-09-19 08:54:33 +00:00
return Status(0, "Continue");
}
INotifyEventContextRef INotifyEventPublisher::createEventContextFrom(
2014-09-19 08:54:33 +00:00
struct inotify_event* event) {
auto shared_event = std::make_shared<struct inotify_event>(*event);
2014-09-19 08:54:33 +00:00
auto ec = createEventContext();
ec->event = shared_event;
2014-09-23 03:33:58 +00:00
// Get the pathname the watch fired on.
std::ostringstream path;
path << descriptor_paths_[event->wd];
if (event->len > 1) {
path << "/" << event->name;
}
ec->path = path.str();
// Set the action (may be multiple)
for (const auto& action : kMaskActions) {
if (event->mask & action.first) {
ec->action = action.second;
break;
}
}
2014-09-19 08:54:33 +00:00
return ec;
}
2014-12-15 18:17:56 +00:00
bool INotifyEventPublisher::shouldFire(const INotifySubscriptionContextRef& sc,
const INotifyEventContextRef& ec) {
2014-10-06 21:23:26 +00:00
if (!sc->recursive && sc->path != ec->path) {
// Monitored path is not recursive and path is not an exact match.
return false;
}
if (ec->path.find(sc->path) != 0) {
// The path does not exist as the base event path.
2014-09-23 03:33:58 +00:00
return false;
}
// The subscription may supply a required event mask.
2014-10-06 21:23:26 +00:00
if (sc->mask != 0 && !(ec->event->mask & sc->mask)) {
return false;
}
return true;
}
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).
if (!listFilesInDirectory(path, children).ok()) {
return false;
}
for (const auto& child : children) {
// Only watch child directories, a watch on the directory implies files.
if (isDirectory(child).ok()) {
addMonitor(child, recursive);
}
}
}
return true;
}
bool INotifyEventPublisher::removeMonitor(const std::string& path, bool force) {
// If force then remove from INotify, otherwise cleanup file descriptors.
if (path_descriptors_.find(path) == path_descriptors_.end()) {
2014-09-23 03:33:58 +00:00
return false;
}
2014-10-06 21:23:26 +00:00
int watch = path_descriptors_[path];
path_descriptors_.erase(path);
descriptor_paths_.erase(watch);
auto position = std::find(descriptors_.begin(), descriptors_.end(), watch);
descriptors_.erase(position);
if (force) {
::inotify_rm_watch(getHandle(), watch);
}
2014-09-23 03:33:58 +00:00
return true;
}
2014-10-06 21:23:26 +00:00
bool INotifyEventPublisher::removeMonitor(int watch, bool force) {
if (descriptor_paths_.find(watch) == descriptor_paths_.end()) {
return false;
}
std::string path = descriptor_paths_[watch];
return removeMonitor(path, force);
2014-09-19 08:54:33 +00:00
}
2014-10-06 21:23:26 +00:00
bool INotifyEventPublisher::isPathMonitored(const std::string& path) {
2014-11-04 07:36:55 +00:00
boost::filesystem::path parent_path;
2014-10-06 21:23:26 +00:00
if (!isDirectory(path).ok()) {
if (path_descriptors_.find(path) != path_descriptors_.end()) {
// Path is a file, and is directly monitored.
return true;
}
if (!getDirectory(path, parent_path).ok()) {
// Could not get parent of unmonitored file.
return false;
}
} else {
parent_path = path;
}
// Directory or parent of file monitoring
2014-11-04 07:36:55 +00:00
auto path_iterator = path_descriptors_.find(parent_path.string());
return (path_iterator != path_descriptors_.end());
2014-09-19 08:54:33 +00:00
}
}