2014-12-18 18:50:47 +00:00
|
|
|
/*
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2014-12-18 18:50:47 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
2015-05-12 06:31:13 +00:00
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
2014-12-18 18:50:47 +00:00
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
2014-09-18 04:20:30 +00:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
#include <atomic>
|
2014-09-18 04:20:30 +00:00
|
|
|
#include <functional>
|
|
|
|
#include <map>
|
2016-03-11 08:30:20 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
2014-09-18 04:20:30 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
#include <osquery/core.h>
|
2016-03-21 22:27:51 +00:00
|
|
|
#include <osquery/dispatcher.h>
|
2014-12-03 23:31:09 +00:00
|
|
|
#include <osquery/registry.h>
|
|
|
|
#include <osquery/status.h>
|
2014-12-15 02:03:41 +00:00
|
|
|
#include <osquery/tables.h>
|
2014-09-18 04:20:30 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
|
2014-10-03 15:26:41 +00:00
|
|
|
struct Subscription;
|
2015-12-08 01:15:30 +00:00
|
|
|
template <class SC, class EC>
|
|
|
|
class EventPublisher;
|
|
|
|
template <class PUB>
|
|
|
|
class EventSubscriber;
|
2014-12-15 00:05:07 +00:00
|
|
|
class EventFactory;
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
using EventPublisherID = const std::string;
|
|
|
|
using EventSubscriberID = const std::string;
|
|
|
|
using EventID = const std::string;
|
|
|
|
using EventContextID = uint64_t;
|
2016-09-02 22:04:03 +00:00
|
|
|
using EventTime = uint64_t;
|
2015-12-08 01:15:30 +00:00
|
|
|
using EventRecord = std::pair<EventID, EventTime>;
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2014-09-30 19:50:14 +00:00
|
|
|
/**
|
2014-10-03 15:36:22 +00:00
|
|
|
* @brief An EventPublisher will define a SubscriptionContext for
|
|
|
|
* EventSubscriber%s to use.
|
|
|
|
*
|
2015-04-12 02:50:35 +00:00
|
|
|
* Most EventPublisher%s will require specific information for interacting with
|
2014-10-03 15:36:22 +00:00
|
|
|
* an OS to receive events. The SubscriptionContext contains information the
|
|
|
|
* EventPublisher will use to register OS API callbacks, create
|
|
|
|
* subscriptioning/listening handles, etc.
|
|
|
|
*
|
2015-07-19 21:02:02 +00:00
|
|
|
* Linux `inotify` should implement a SubscriptionContext that subscribes
|
2014-12-18 04:10:51 +00:00
|
|
|
* filesystem events based on a filesystem path. `libpcap` will subscribe on
|
|
|
|
* networking protocols at various stacks. Process creation may subscribe on
|
2014-10-03 15:36:22 +00:00
|
|
|
* process name, parent pid, etc.
|
2014-09-30 19:50:14 +00:00
|
|
|
*/
|
2015-12-08 07:08:00 +00:00
|
|
|
struct SubscriptionContext : private boost::noncopyable {};
|
2014-09-30 19:50:14 +00:00
|
|
|
|
|
|
|
/**
|
2014-10-03 15:08:06 +00:00
|
|
|
* @brief An EventSubscriber EventCallback method will receive an EventContext.
|
2014-09-30 19:50:14 +00:00
|
|
|
*
|
2014-10-03 15:36:22 +00:00
|
|
|
* The EventContext contains the event-related data supplied by an
|
2015-04-12 02:50:35 +00:00
|
|
|
* EventPublisher when the event occurs. If a subscribing EventSubscriber
|
2014-12-18 04:10:51 +00:00
|
|
|
* would be called for the event, the EventSubscriber%'s EventCallback is
|
2014-10-03 15:36:22 +00:00
|
|
|
* passed an EventContext.
|
2014-09-30 19:50:14 +00:00
|
|
|
*/
|
2015-12-08 01:15:30 +00:00
|
|
|
struct EventContext : private boost::noncopyable {
|
2014-10-03 15:14:36 +00:00
|
|
|
/// An unique counting ID specific to the EventPublisher%'s fired events.
|
2015-12-08 01:15:30 +00:00
|
|
|
EventContextID id{0};
|
2015-04-01 04:18:56 +00:00
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
/// The time the event occurred, as determined by the publisher.
|
|
|
|
EventTime time{0};
|
2014-09-25 17:17:32 +00:00
|
|
|
};
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
using SubscriptionRef = std::shared_ptr<Subscription>;
|
|
|
|
using BaseEventPublisher = EventPublisher<SubscriptionContext, EventContext>;
|
|
|
|
using EventPublisherRef = std::shared_ptr<BaseEventPublisher>;
|
|
|
|
using SubscriptionContextRef = std::shared_ptr<SubscriptionContext>;
|
|
|
|
using EventContextRef = std::shared_ptr<EventContext>;
|
|
|
|
using BaseEventSubscriber = EventSubscriber<BaseEventPublisher>;
|
2016-03-18 02:11:18 +00:00
|
|
|
using EventSubscriberRef = std::shared_ptr<EventSubscriber<BaseEventPublisher>>;
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2015-04-01 04:18:56 +00:00
|
|
|
/**
|
2016-10-30 18:15:55 +00:00
|
|
|
* @brief EventSubscriber%s and Publishers may exist in various states.
|
2015-04-01 04:18:56 +00:00
|
|
|
*
|
2016-10-30 18:15:55 +00:00
|
|
|
* The class will move through states when osquery is initializing the
|
2015-04-01 04:18:56 +00:00
|
|
|
* registry, starting event publisher loops, and requesting initialization of
|
|
|
|
* each subscriber and the optional set of subscriptions it creates. If this
|
|
|
|
* initialization fails the publishers or EventFactory may eject, warn, or
|
|
|
|
* otherwise not use the subscriber's subscriptions.
|
|
|
|
*
|
|
|
|
* The supported states are:
|
|
|
|
* - None: The default state, uninitialized.
|
2016-10-30 18:15:55 +00:00
|
|
|
* - Setup: The Subscriber is attached and has run setup.
|
2015-04-01 04:18:56 +00:00
|
|
|
* - Running: Subscriber is ready for events.
|
2015-07-19 21:02:02 +00:00
|
|
|
* - Paused: Subscriber was initialized but is not currently accepting events.
|
2015-04-01 04:18:56 +00:00
|
|
|
* - Failed: Subscriber failed to initialize or is otherwise offline.
|
|
|
|
*/
|
2016-10-30 18:15:55 +00:00
|
|
|
enum class EventState {
|
|
|
|
EVENT_NONE = 0,
|
|
|
|
EVENT_SETUP,
|
|
|
|
EVENT_RUNNING,
|
|
|
|
EVENT_PAUSED,
|
|
|
|
EVENT_FAILED,
|
2015-04-01 04:18:56 +00:00
|
|
|
};
|
|
|
|
|
2014-12-15 05:20:20 +00:00
|
|
|
/// Use a single placeholder for the EventContextRef passed to EventCallback.
|
2016-02-11 04:08:34 +00:00
|
|
|
using EventCallback = std::function<Status(const EventContextRef&,
|
|
|
|
const SubscriptionContextRef&)>;
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2014-10-03 15:26:41 +00:00
|
|
|
/// An EventPublisher must track every subscription added.
|
2015-12-08 01:15:30 +00:00
|
|
|
using SubscriptionVector = std::vector<SubscriptionRef>;
|
2014-09-22 21:35:07 +00:00
|
|
|
|
2014-09-19 08:54:33 +00:00
|
|
|
/// The set of search-time binned lookup tables.
|
|
|
|
extern const std::vector<size_t> kEventTimeLists;
|
|
|
|
|
2014-12-18 04:10:51 +00:00
|
|
|
/**
|
|
|
|
* @brief DECLARE_PUBLISHER supplies needed boilerplate code that applies a
|
|
|
|
* string-type EventPublisherID to identify the publisher declaration.
|
|
|
|
*/
|
2016-09-23 23:46:02 +00:00
|
|
|
#define DECLARE_PUBLISHER(TYPE) \
|
|
|
|
public: \
|
|
|
|
EventPublisherID type() const override final { \
|
|
|
|
return TYPE; \
|
|
|
|
}
|
2014-12-15 02:03:41 +00:00
|
|
|
|
2014-09-18 04:20:30 +00:00
|
|
|
/**
|
2014-10-03 15:36:22 +00:00
|
|
|
* @brief A Subscription is used to configure an EventPublisher and bind a
|
2014-12-18 04:10:51 +00:00
|
|
|
* callback to a SubscriptionContext.
|
2014-09-24 15:03:16 +00:00
|
|
|
*
|
2014-10-03 15:36:22 +00:00
|
|
|
* A Subscription is the input to an EventPublisher when the EventPublisher
|
2014-12-18 04:10:51 +00:00
|
|
|
* decides on the scope and details of the events it watches/generates.
|
2014-10-03 15:36:22 +00:00
|
|
|
* An example includes a filesystem change event. A subscription would include
|
|
|
|
* a path with optional recursion and attribute selectors as well as a callback
|
|
|
|
* function to fire when an event for that path and selector occurs.
|
2014-09-24 15:03:16 +00:00
|
|
|
*
|
2014-10-03 15:26:41 +00:00
|
|
|
* A Subscription also functions to greatly scope an EventPublisher%'s work.
|
2014-10-03 15:36:22 +00:00
|
|
|
* Using the same filesystem example and the Linux inotify subsystem a
|
|
|
|
* Subscription limits the number of inode watches to only those requested by
|
|
|
|
* appropriate EventSubscriber%s.
|
|
|
|
* Note: EventSubscriber%s and Subscriptions can be configured by the osquery
|
|
|
|
* user.
|
2014-09-24 15:03:16 +00:00
|
|
|
*
|
2014-10-03 15:26:41 +00:00
|
|
|
* Subscriptions are usually created with EventFactory members:
|
2014-09-18 04:20:30 +00:00
|
|
|
*
|
2014-09-24 15:03:16 +00:00
|
|
|
* @code{.cpp}
|
2014-10-03 15:26:41 +00:00
|
|
|
* EventFactory::addSubscription("MyEventPublisher", my_subscription_context);
|
2014-09-24 15:03:16 +00:00
|
|
|
* @endcode
|
2014-09-18 04:20:30 +00:00
|
|
|
*/
|
2015-12-08 01:15:30 +00:00
|
|
|
struct Subscription : private boost::noncopyable {
|
2014-09-21 21:29:28 +00:00
|
|
|
public:
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
// EventSubscriber name.
|
|
|
|
std::string subscriber_name;
|
|
|
|
|
2014-10-03 15:26:41 +00:00
|
|
|
/// An EventPublisher%-specific SubscriptionContext.
|
|
|
|
SubscriptionContextRef context;
|
2015-12-08 01:15:30 +00:00
|
|
|
|
2014-10-03 15:26:41 +00:00
|
|
|
/// An EventSubscription member EventCallback method.
|
2014-09-18 04:20:30 +00:00
|
|
|
EventCallback callback;
|
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
explicit Subscription(EventSubscriberID& name) : subscriber_name(name){};
|
2015-04-01 04:18:56 +00:00
|
|
|
|
2015-04-12 02:50:35 +00:00
|
|
|
static SubscriptionRef create(EventSubscriberID& name) {
|
2015-12-08 01:15:30 +00:00
|
|
|
return std::make_shared<Subscription>(name);
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
}
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2015-04-12 02:50:35 +00:00
|
|
|
static SubscriptionRef create(EventSubscriberID& name,
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
const SubscriptionContextRef& mc,
|
2015-12-08 01:15:30 +00:00
|
|
|
EventCallback ec = nullptr) {
|
2015-04-01 04:18:56 +00:00
|
|
|
auto subscription = std::make_shared<Subscription>(name);
|
2014-10-03 15:26:41 +00:00
|
|
|
subscription->context = mc;
|
|
|
|
subscription->callback = ec;
|
|
|
|
return subscription;
|
2014-09-18 04:20:30 +00:00
|
|
|
}
|
2016-02-11 04:08:34 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
Subscription() = delete;
|
2014-09-18 04:20:30 +00:00
|
|
|
};
|
|
|
|
|
2016-10-30 18:15:55 +00:00
|
|
|
class Eventer {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Request the subscriber's initialization state.
|
|
|
|
*
|
|
|
|
* When event subscribers are created (initialized) they are expected to emit
|
|
|
|
* a set of subscriptions to their publisher "type". If the subscriber fails
|
|
|
|
* to initialize then the publisher may remove any intermediate subscriptions.
|
|
|
|
*/
|
|
|
|
EventState state() const {
|
|
|
|
return state_;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/// Set the subscriber state.
|
|
|
|
void state(EventState state) {
|
|
|
|
state_ = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The event subscriber's run state.
|
|
|
|
EventState state_{EventState::EVENT_NONE};
|
|
|
|
|
|
|
|
friend class EventFactory;
|
|
|
|
};
|
|
|
|
|
|
|
|
class EventPublisherPlugin : public Plugin,
|
|
|
|
public InterruptableRunnable,
|
|
|
|
public Eventer {
|
2014-09-21 21:29:28 +00:00
|
|
|
public:
|
2014-09-30 19:50:14 +00:00
|
|
|
/**
|
2014-10-03 15:26:41 +00:00
|
|
|
* @brief A new Subscription was added, potentially change state based on all
|
|
|
|
* subscriptions for this EventPublisher.
|
2014-09-30 19:50:14 +00:00
|
|
|
*
|
2014-10-03 15:36:22 +00:00
|
|
|
* `configure` allows the EventPublisher to optimize on the state of all
|
|
|
|
* subscriptions. An example is Linux `inotify` where multiple
|
|
|
|
* EventSubscription%s will subscription identical paths, e.g., /etc for
|
|
|
|
* config changes. Since Linux `inotify` has a subscription limit, `configure`
|
2015-04-12 02:50:35 +00:00
|
|
|
* can dedup paths.
|
2014-09-30 19:50:14 +00:00
|
|
|
*/
|
2015-12-08 07:08:00 +00:00
|
|
|
virtual void configure() override{};
|
2014-09-24 15:03:16 +00:00
|
|
|
|
2014-09-30 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* @brief Perform handle opening, OS API callback registration.
|
|
|
|
*
|
2015-04-12 02:50:35 +00:00
|
|
|
* `setUp` is the event framework's EventPublisher constructor equivalent.
|
2015-05-07 03:02:23 +00:00
|
|
|
* This is called in the main thread before the publisher's run loop has
|
|
|
|
* started, immediately following registration.
|
2014-09-30 19:50:14 +00:00
|
|
|
*/
|
2016-09-23 23:46:02 +00:00
|
|
|
virtual Status setUp() override {
|
|
|
|
return Status(0, "Not used");
|
|
|
|
}
|
2014-09-24 15:03:16 +00:00
|
|
|
|
2014-09-30 19:50:14 +00:00
|
|
|
/**
|
|
|
|
* @brief Perform handle closing, resource cleanup.
|
|
|
|
*
|
2014-10-03 15:14:36 +00:00
|
|
|
* osquery is about to end, the EventPublisher should close handle descriptors
|
2015-05-07 03:02:23 +00:00
|
|
|
* unblock resources, and prepare to exit. This will be called from the main
|
|
|
|
* thread after the run loop thread has exited.
|
2014-09-30 19:50:14 +00:00
|
|
|
*/
|
2015-12-08 01:15:30 +00:00
|
|
|
virtual void tearDown() override {}
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2014-09-24 15:03:16 +00:00
|
|
|
/**
|
2015-05-07 03:02:23 +00:00
|
|
|
* @brief Implement a "step" of an optional run loop.
|
2014-09-24 15:03:16 +00:00
|
|
|
*
|
|
|
|
* @return A SUCCESS status will immediately call `run` again. A FAILED status
|
|
|
|
* will exit the run loop and the thread.
|
|
|
|
*/
|
2016-09-23 23:46:02 +00:00
|
|
|
virtual Status run() {
|
|
|
|
return Status(1, "No run loop required");
|
|
|
|
}
|
2015-05-07 03:02:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Allow the EventFactory to interrupt the run loop.
|
|
|
|
*
|
|
|
|
* Assume the main thread may ask the run loop to stop at anytime.
|
|
|
|
* Before end is called the publisher's `isEnding` is set and the EventFactory
|
|
|
|
* run loop manager will exit the stepping loop and fall through to a call
|
|
|
|
* to tearDown followed by a removal of the publisher.
|
|
|
|
*/
|
2016-03-21 22:27:51 +00:00
|
|
|
virtual void stop() override {}
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2017-01-07 20:21:35 +00:00
|
|
|
/// This is a plugin type and must implement a call method.
|
|
|
|
Status call(const PluginRequest&, PluginResponse&) override {
|
|
|
|
return Status(0);
|
|
|
|
}
|
|
|
|
|
2014-09-24 15:03:16 +00:00
|
|
|
/**
|
2015-07-19 21:02:02 +00:00
|
|
|
* @brief A new EventSubscriber is subscribing events of this publisher type.
|
2014-09-24 15:03:16 +00:00
|
|
|
*
|
2014-10-03 15:36:22 +00:00
|
|
|
* @param subscription The Subscription context information and optional
|
|
|
|
* EventCallback.
|
2014-09-24 15:03:16 +00:00
|
|
|
*
|
2014-10-03 15:26:41 +00:00
|
|
|
* @return If the Subscription is not appropriate (mismatched type) fail.
|
2014-09-24 15:03:16 +00:00
|
|
|
*/
|
2016-08-01 05:41:37 +00:00
|
|
|
virtual Status addSubscription(const SubscriptionRef& subscription);
|
2015-12-08 07:08:00 +00:00
|
|
|
|
2016-02-11 04:08:34 +00:00
|
|
|
/// Remove all subscriptions from a named subscriber.
|
|
|
|
virtual void removeSubscriptions(const std::string& subscriber);
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
public:
|
|
|
|
/// Overriding the EventPublisher constructor is not recommended.
|
2015-12-08 06:06:32 +00:00
|
|
|
EventPublisherPlugin() {}
|
2015-07-19 21:02:02 +00:00
|
|
|
virtual ~EventPublisherPlugin() {}
|
2014-12-15 05:20:20 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
/// Return a string identifier associated with this EventPublisher.
|
2016-09-23 23:46:02 +00:00
|
|
|
virtual EventPublisherID type() const {
|
|
|
|
return "publisher";
|
|
|
|
}
|
2015-07-19 21:02:02 +00:00
|
|
|
|
|
|
|
public:
|
2014-10-03 15:26:41 +00:00
|
|
|
/// Number of Subscription%s watching this EventPublisher.
|
2016-09-23 23:46:02 +00:00
|
|
|
size_t numSubscriptions() const {
|
|
|
|
return subscriptions_.size();
|
|
|
|
}
|
2014-09-24 15:03:16 +00:00
|
|
|
|
2014-09-30 19:50:14 +00:00
|
|
|
/**
|
2014-10-03 15:14:36 +00:00
|
|
|
* @brief The number of events fired by this EventPublisher.
|
2014-09-30 19:50:14 +00:00
|
|
|
*
|
|
|
|
* @return The number of events.
|
|
|
|
*/
|
2016-09-23 23:46:02 +00:00
|
|
|
EventContextID numEvents() const {
|
|
|
|
return next_ec_id_;
|
|
|
|
}
|
2014-09-18 04:20:30 +00:00
|
|
|
|
2015-05-07 03:02:23 +00:00
|
|
|
/// Check if the EventFactory is ending all publisher threads.
|
2016-09-23 23:46:02 +00:00
|
|
|
bool isEnding() const {
|
|
|
|
return ending_;
|
|
|
|
}
|
2015-05-07 03:02:23 +00:00
|
|
|
|
|
|
|
/// Set the ending status for this publisher.
|
2016-09-23 23:46:02 +00:00
|
|
|
void isEnding(bool ending) {
|
|
|
|
ending_ = ending;
|
|
|
|
}
|
2015-05-07 03:02:23 +00:00
|
|
|
|
|
|
|
/// Check if the publisher's run loop has started.
|
2016-09-23 23:46:02 +00:00
|
|
|
bool hasStarted() const {
|
|
|
|
return started_;
|
|
|
|
}
|
2015-05-07 03:02:23 +00:00
|
|
|
|
|
|
|
/// Set the run or started status for this publisher.
|
2016-09-23 23:46:02 +00:00
|
|
|
void hasStarted(bool started) {
|
|
|
|
started_ = started;
|
|
|
|
}
|
2015-01-12 03:43:04 +00:00
|
|
|
|
2015-09-03 19:22:42 +00:00
|
|
|
/// Get the number of publisher restarts.
|
2016-09-23 23:46:02 +00:00
|
|
|
size_t restartCount() const {
|
|
|
|
return restart_count_;
|
|
|
|
}
|
2015-09-03 19:22:42 +00:00
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
public:
|
|
|
|
explicit EventPublisherPlugin(EventPublisherPlugin const&) = delete;
|
|
|
|
EventPublisherPlugin& operator=(EventPublisherPlugin const&) = delete;
|
|
|
|
|
2014-12-15 05:20:20 +00:00
|
|
|
protected:
|
2015-07-19 21:02:02 +00:00
|
|
|
/**
|
|
|
|
* @brief The generic check loop to call SubscriptionContext callback methods.
|
|
|
|
*
|
|
|
|
* It is NOT recommended to override `fire`. The simple logic of enumerating
|
|
|
|
* the Subscription%s and using `shouldFire` is more appropriate.
|
|
|
|
*
|
|
|
|
* @param ec The EventContext created and fired by the EventPublisher.
|
|
|
|
* @param time The most accurate time associated with the event.
|
|
|
|
*/
|
|
|
|
virtual void fire(const EventContextRef& ec, EventTime time = 0) final;
|
|
|
|
|
2014-12-18 04:10:51 +00:00
|
|
|
/// The internal fire method used by the typed EventPublisher.
|
2014-12-15 18:17:56 +00:00
|
|
|
virtual void fireCallback(const SubscriptionRef& sub,
|
2015-02-01 11:32:18 +00:00
|
|
|
const EventContextRef& ec) const = 0;
|
2014-12-15 05:20:20 +00:00
|
|
|
|
|
|
|
/// The EventPublisher will keep track of Subscription%s that contain callins.
|
|
|
|
SubscriptionVector subscriptions_;
|
|
|
|
|
|
|
|
/// An Event ID is assigned by the EventPublisher within the EventContext.
|
|
|
|
/// This is not used to store event date in the backing store.
|
2016-03-12 09:23:09 +00:00
|
|
|
std::atomic<EventContextID> next_ec_id_{0};
|
2014-12-15 05:20:20 +00:00
|
|
|
|
|
|
|
private:
|
2015-01-12 03:43:04 +00:00
|
|
|
/// Set ending to True to cause event type run loops to finish.
|
2016-02-05 03:12:48 +00:00
|
|
|
std::atomic<bool> ending_{false};
|
2015-05-07 03:02:23 +00:00
|
|
|
|
2015-02-01 08:35:44 +00:00
|
|
|
/// Set to indicate whether the event run loop ever started.
|
2016-02-05 03:12:48 +00:00
|
|
|
std::atomic<bool> started_{false};
|
2015-01-12 03:43:04 +00:00
|
|
|
|
2014-12-15 05:20:20 +00:00
|
|
|
/// A lock for incrementing the next EventContextID.
|
2016-03-11 08:30:20 +00:00
|
|
|
std::mutex ec_id_lock_;
|
2014-12-15 05:20:20 +00:00
|
|
|
|
2016-08-01 05:41:37 +00:00
|
|
|
/// A lock for subscription manipulation.
|
|
|
|
std::mutex subscription_lock_;
|
|
|
|
|
2015-09-03 19:22:42 +00:00
|
|
|
/// A helper count of event publisher runloop iterations.
|
2016-02-05 03:12:48 +00:00
|
|
|
std::atomic<size_t> restart_count_{0};
|
2015-09-03 19:22:42 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
private:
|
|
|
|
/// Enable event factory "callins" through static publisher callbacks.
|
|
|
|
friend class EventFactory;
|
|
|
|
|
2014-12-15 05:20:20 +00:00
|
|
|
private:
|
2016-02-11 04:08:34 +00:00
|
|
|
FRIEND_TEST(EventsTests, test_event_publisher);
|
2014-12-15 05:20:20 +00:00
|
|
|
FRIEND_TEST(EventsTests, test_fire_event);
|
|
|
|
};
|
|
|
|
|
2016-10-30 18:15:55 +00:00
|
|
|
class EventSubscriberPlugin : public Plugin, public Eventer {
|
2016-02-11 04:08:34 +00:00
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Add Subscription%s to the EventPublisher this module will act on.
|
|
|
|
*
|
|
|
|
* When the EventSubscriber%'s `init` method is called you are assured the
|
|
|
|
* EventPublisher has `setUp` and is ready to subscription for events.
|
|
|
|
*/
|
2016-09-23 23:46:02 +00:00
|
|
|
virtual Status init() {
|
|
|
|
return Status(0);
|
|
|
|
}
|
2016-02-11 04:08:34 +00:00
|
|
|
|
2017-01-07 20:21:35 +00:00
|
|
|
/// This is a plugin type and must implement a call method.
|
|
|
|
Status call(const PluginRequest&, PluginResponse&) override {
|
|
|
|
return Status(0);
|
|
|
|
}
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* @brief Store parsed event data from an EventCallback in a backing store.
|
|
|
|
*
|
2015-04-12 02:50:35 +00:00
|
|
|
* Within a EventCallback the EventSubscriber has an opportunity to create
|
2014-12-15 00:05:07 +00:00
|
|
|
* an osquery Row element, add the relevant table data for the EventSubscriber
|
|
|
|
* and store that element in the osquery backing store. At query-time
|
|
|
|
* the added data will apply selection criteria and return these elements.
|
|
|
|
* The backing store data retrieval is optimized by time-based indexes. It
|
|
|
|
* is important to added EventTime as it relates to "when the event occurred".
|
|
|
|
*
|
|
|
|
* @param r An osquery Row element.
|
|
|
|
*
|
|
|
|
* @return Was the element added to the backing store.
|
|
|
|
*/
|
2016-09-23 23:46:02 +00:00
|
|
|
Status add(Row& r) {
|
|
|
|
return add(r, 0);
|
|
|
|
}
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return all events added by this EventSubscriber within start, stop.
|
|
|
|
*
|
|
|
|
* This is used internally (for the most part) by EventSubscriber::genTable.
|
|
|
|
*
|
|
|
|
* @param start Inclusive lower bound time limit.
|
|
|
|
* @param stop Inclusive upper bound time limit.
|
|
|
|
* @return Set of event rows matching time limits.
|
|
|
|
*/
|
2015-12-08 01:15:30 +00:00
|
|
|
virtual QueryData get(EventTime start, EventTime stop) final;
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2016-09-23 23:46:02 +00:00
|
|
|
private:
|
|
|
|
/// Overload add for tests and allow them to override the event time.
|
|
|
|
virtual Status add(Row& r, EventTime event_time) final;
|
|
|
|
|
2015-01-02 05:55:10 +00:00
|
|
|
private:
|
2014-12-15 00:05:07 +00:00
|
|
|
/*
|
2015-04-12 02:50:35 +00:00
|
|
|
* @brief When `get`ing event results, return EventID%s from time indexes.
|
2014-12-15 00:05:07 +00:00
|
|
|
*
|
|
|
|
* Used by EventSubscriber::get to retrieve EventID, EventTime indexes. This
|
|
|
|
* applies the lookup-efficiency checks for time list appropriate bins.
|
|
|
|
* If the time range in 24 hours and there is a 24-hour list bin it will
|
|
|
|
* be queried using a single backing store `Get` followed by two `Get`s of
|
|
|
|
* the most-specific boundary lists.
|
|
|
|
*
|
|
|
|
* @return List of EventID, EventTime%s
|
|
|
|
*/
|
2016-09-26 05:19:31 +00:00
|
|
|
std::vector<EventRecord> getRecords(const std::vector<std::string>& indexes);
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get a unique storage-related EventID.
|
|
|
|
*
|
|
|
|
* An EventID is an index/element-identifier for the backing store.
|
|
|
|
* Each EventPublisher maintains a fired EventContextID to identify the many
|
2015-07-19 21:02:02 +00:00
|
|
|
* events that may or may not be fired based on subscription criteria for this
|
2014-12-15 00:05:07 +00:00
|
|
|
* EventSubscriber. This EventContextID is NOT the same as an EventID.
|
2015-07-19 21:02:02 +00:00
|
|
|
* EventSubscriber development should not require use of EventID%s. If this
|
2014-12-15 00:05:07 +00:00
|
|
|
* indexing is required within-EventCallback consider an
|
|
|
|
* EventSubscriber%-unique indexing, counting mechanic.
|
|
|
|
*
|
|
|
|
* @return A unique ID for backing storage.
|
|
|
|
*/
|
|
|
|
EventID getEventID();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Plan the best set of indexes for event record access.
|
|
|
|
*
|
|
|
|
* @param start an inclusive time to begin searching.
|
|
|
|
* @param stop an inclusive time to end searching.
|
|
|
|
*
|
|
|
|
* @return List of 'index.step' index strings.
|
|
|
|
*/
|
2016-09-26 05:19:31 +00:00
|
|
|
std::vector<std::string> getIndexes(EventTime start, EventTime stop);
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Expire indexes and eventually records.
|
|
|
|
*
|
|
|
|
* @param list_type the string representation of list binning type.
|
|
|
|
* @param indexes complete set of 'index.step' indexes for the list_type.
|
|
|
|
* @param expirations of the indexes, the set to expire.
|
|
|
|
*/
|
2015-07-02 23:47:16 +00:00
|
|
|
void expireIndexes(const std::string& list_type,
|
|
|
|
const std::vector<std::string>& indexes,
|
|
|
|
const std::vector<std::string>& expirations);
|
2015-12-08 01:15:30 +00:00
|
|
|
|
2015-07-02 23:47:16 +00:00
|
|
|
/// Expire all datums within a bin.
|
|
|
|
void expireRecords(const std::string& list_type,
|
|
|
|
const std::string& index,
|
|
|
|
bool all);
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2016-01-30 02:12:37 +00:00
|
|
|
/**
|
|
|
|
* @brief Inspect the number of events, expire those overflowing events_max.
|
|
|
|
*
|
|
|
|
* When the event manager starts, or after a checkpoint number of events,
|
|
|
|
* the EventFactory will call expireCheck for each subscriber.
|
|
|
|
*
|
|
|
|
* The subscriber must count the number of buffered records and check if
|
|
|
|
* that count exceeds the configured `events_max` limit. If an overflow
|
|
|
|
* occurs the subscriber will expire N-events_max from the end of the queue.
|
2016-03-18 02:11:18 +00:00
|
|
|
*
|
|
|
|
* @param cleanup Perform an intense scan of zombie event IDs.
|
2016-01-30 02:12:37 +00:00
|
|
|
*/
|
2016-03-18 02:11:18 +00:00
|
|
|
void expireCheck(bool cleanup = false);
|
2016-01-30 02:12:37 +00:00
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
/**
|
|
|
|
* @brief Add an EventID, EventTime pair to all matching list types.
|
|
|
|
*
|
|
|
|
* The list types are defined by time size. Based on the EventTime this pair
|
|
|
|
* is added to the list bin for each list type. If there are two list types:
|
|
|
|
* 60 seconds and 3600 seconds and `time` is 92, this pair will be added to
|
|
|
|
* list type 1 bin 4 and list type 2 bin 1.
|
|
|
|
*
|
|
|
|
* @param eid A unique EventID.
|
|
|
|
* @param time The time when this EventID%'s event occurred.
|
|
|
|
*
|
|
|
|
* @return Were the indexes recorded.
|
|
|
|
*/
|
2014-12-15 18:17:56 +00:00
|
|
|
Status recordEvent(EventID& eid, EventTime time);
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2016-03-09 18:31:00 +00:00
|
|
|
/**
|
|
|
|
* @brief Get the expiration timeout for this event type
|
|
|
|
*
|
|
|
|
* The default implementation retrieves this value from FLAGS_events_expiry.
|
|
|
|
* This method can be overridden to allow custom event expiration timeouts in
|
|
|
|
* subclasses of EventSubscriberPlugin.
|
|
|
|
*
|
|
|
|
* @return The events expiration timeout for this event type
|
|
|
|
*/
|
|
|
|
virtual size_t getEventsExpiry();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the max number of events for this event type
|
|
|
|
*
|
|
|
|
* The default implementation retrieves this value from FLAGS_events_max.
|
|
|
|
* This method can be overridden to allow custom max event numbers in
|
|
|
|
* subclasses of EventSubscriberPlugin.
|
|
|
|
*
|
|
|
|
* @return The max number of events for this event type
|
|
|
|
*/
|
|
|
|
virtual size_t getEventsMax();
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief A single instance requirement for static callback facilities.
|
|
|
|
*
|
|
|
|
* The EventSubscriber constructor is NOT responsible for adding
|
|
|
|
* Subscription%s. Please use `init` for adding Subscription%s as all
|
|
|
|
* EventPublisher instances will have run `setUp` and initialized their run
|
|
|
|
* loops.
|
|
|
|
*/
|
2015-07-06 07:04:37 +00:00
|
|
|
EventSubscriberPlugin()
|
2016-10-30 18:15:55 +00:00
|
|
|
: expire_events_(true), expire_time_(0), optimize_time_(0) {}
|
2015-02-01 11:32:18 +00:00
|
|
|
virtual ~EventSubscriberPlugin() {}
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Suggested entrypoint for table generation.
|
|
|
|
*
|
|
|
|
* The EventSubscriber is a convention that removes a lot of boilerplate event
|
2015-07-19 21:02:02 +00:00
|
|
|
* 'subscribing' and acting. The `genTable` static entrypoint is the
|
2014-12-15 00:05:07 +00:00
|
|
|
* suggested method for table specs.
|
|
|
|
*
|
|
|
|
* @return The query-time table data, retrieved from a backing store.
|
|
|
|
*/
|
2016-05-20 15:39:04 +00:00
|
|
|
virtual QueryData genTable(QueryContext& context) USED_SYMBOL;
|
2014-12-15 08:25:28 +00:00
|
|
|
|
2015-09-03 19:22:42 +00:00
|
|
|
/// Number of Subscription%s this EventSubscriber has used.
|
2016-09-23 23:46:02 +00:00
|
|
|
size_t numSubscriptions() const {
|
|
|
|
return subscription_count_;
|
|
|
|
}
|
2015-09-03 19:22:42 +00:00
|
|
|
|
|
|
|
/// The number of events this EventSubscriber has received.
|
2016-09-23 23:46:02 +00:00
|
|
|
EventContextID numEvents() const {
|
|
|
|
return event_count_;
|
|
|
|
}
|
2015-09-03 19:22:42 +00:00
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
private:
|
|
|
|
explicit EventSubscriberPlugin(EventSubscriberPlugin const&) = delete;
|
|
|
|
EventSubscriberPlugin& operator=(EventSubscriberPlugin const&) = delete;
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
protected:
|
2015-07-19 21:02:02 +00:00
|
|
|
/**
|
|
|
|
* @brief Backing storage indexing namespace.
|
|
|
|
*
|
|
|
|
* The backing storage will accumulate events for this subscriber. A namespace
|
|
|
|
* is provided to prevent event indexing collisions between subscribers and
|
|
|
|
* publishers. The namespace is a combination of the publisher and subscriber
|
|
|
|
* registry plugin names.
|
|
|
|
*/
|
2016-02-11 04:08:34 +00:00
|
|
|
/// See getType for lookup rational.
|
|
|
|
virtual EventPublisherID dbNamespace() const {
|
|
|
|
return getType() + '.' + getName();
|
|
|
|
}
|
2014-12-15 08:25:28 +00:00
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
/// Disable event expiration for this subscriber.
|
2016-09-23 23:46:02 +00:00
|
|
|
void doNotExpire() {
|
|
|
|
expire_events_ = false;
|
|
|
|
}
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2016-02-11 04:08:34 +00:00
|
|
|
/// Trampoline into the EventFactory and lookup the name of the publisher.
|
|
|
|
virtual EventPublisherID& getType() const = 0;
|
|
|
|
|
|
|
|
/// Get a handle to the EventPublisher.
|
|
|
|
EventPublisherRef getPublisher() const;
|
|
|
|
|
|
|
|
/// Remove all subscriptions from this subscriber.
|
|
|
|
void removeSubscriptions();
|
|
|
|
|
|
|
|
protected:
|
2015-09-03 19:22:42 +00:00
|
|
|
/// A helper value counting the number of fired events tracked by publishers.
|
|
|
|
EventContextID event_count_{0};
|
|
|
|
|
|
|
|
/// A helper value counting the number of subscriptions created.
|
|
|
|
size_t subscription_count_{0};
|
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
private:
|
2016-09-23 23:46:02 +00:00
|
|
|
Status setUp() override {
|
|
|
|
return Status(0, "Setup never used");
|
|
|
|
}
|
2015-03-13 15:11:08 +00:00
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
private:
|
|
|
|
/// Do not respond to periodic/scheduled/triggered event expiration requests.
|
2015-09-03 19:22:42 +00:00
|
|
|
bool expire_events_{false};
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
/// Events before the expire_time_ are invalid and will be purged.
|
2015-09-03 19:22:42 +00:00
|
|
|
EventTime expire_time_{0};
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2016-01-30 02:12:37 +00:00
|
|
|
/// Cached value of last generated EventID.
|
|
|
|
size_t last_eid_{0};
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
/**
|
|
|
|
* @brief Optimize subscriber selects by tracking the last select time.
|
|
|
|
*
|
|
|
|
* Event subscribers may optimize selects when used in a daemon schedule by
|
|
|
|
* requiring an event 'time' constraint and otherwise applying a minimum time
|
|
|
|
* as the last time the scheduled query ran.
|
|
|
|
*/
|
2016-01-30 02:12:37 +00:00
|
|
|
EventTime optimize_time_{0};
|
2015-07-06 07:04:37 +00:00
|
|
|
|
2016-09-23 23:46:02 +00:00
|
|
|
/**
|
|
|
|
* @brief Last event ID returned while using events-optimization.
|
|
|
|
*
|
|
|
|
* A time with second precision is not sufficient, but it works for index
|
|
|
|
* retrieval. While sorting using the time optimization, discard events
|
|
|
|
* before or equal to the optimization ID.
|
|
|
|
*/
|
|
|
|
size_t optimize_eid_{0};
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
/// Lock used when incrementing the EventID database index.
|
2016-03-11 08:30:20 +00:00
|
|
|
std::mutex event_id_lock_;
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
/// Lock used when recording an EventID and time into search bins.
|
2016-03-11 08:30:20 +00:00
|
|
|
std::mutex event_record_lock_;
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2015-09-03 19:22:42 +00:00
|
|
|
private:
|
|
|
|
friend class EventFactory;
|
|
|
|
friend class EventPublisherPlugin;
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
private:
|
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_event_module_id);
|
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_record_indexing);
|
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_record_range);
|
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_record_expiration);
|
2015-12-17 17:28:27 +00:00
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_gentable);
|
2016-01-30 02:12:37 +00:00
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_expire_check);
|
2016-09-23 23:46:02 +00:00
|
|
|
FRIEND_TEST(EventsDatabaseTests, test_optimize);
|
|
|
|
friend class DBFakeEventSubscriber;
|
2015-07-29 23:50:19 +00:00
|
|
|
friend class BenchmarkEventSubscriber;
|
2014-12-15 00:05:07 +00:00
|
|
|
};
|
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
/**
|
|
|
|
* @brief A factory for associating event generators to EventPublisherID%s.
|
|
|
|
*
|
|
|
|
* This factory both registers new event types and the subscriptions that use
|
2015-04-12 02:50:35 +00:00
|
|
|
* them. An EventPublisher is also a factory, the single event factory
|
|
|
|
* arbitrates Subscription creation and management for each associated
|
|
|
|
* EventPublisher.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
|
|
|
* Since event types may be plugins, they are created using the factory.
|
|
|
|
* Since subscriptions may be configured/disabled they are also factory-managed.
|
|
|
|
*/
|
2015-05-04 03:02:01 +00:00
|
|
|
class EventFactory : private boost::noncopyable {
|
2015-02-19 07:51:41 +00:00
|
|
|
public:
|
|
|
|
/// Access to the EventFactory instance.
|
|
|
|
static EventFactory& getInstance();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Add an EventPublisher to the factory.
|
|
|
|
*
|
2015-04-12 02:50:35 +00:00
|
|
|
* The registration is mostly abstracted using osquery's registry.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
2016-07-25 16:21:26 +00:00
|
|
|
* @param pub If for some reason the caller needs access to the
|
2015-02-19 07:51:41 +00:00
|
|
|
* EventPublisher instance they can register-by-instance.
|
|
|
|
*
|
|
|
|
* Access to the EventPublisher instance is not discouraged, but using the
|
|
|
|
* EventFactory `getEventPublisher` accessor is encouraged.
|
|
|
|
*/
|
|
|
|
static Status registerEventPublisher(const PluginRef& pub);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Add an EventSubscriber to the factory.
|
|
|
|
*
|
|
|
|
* The registration is mostly abstracted using osquery's registry.
|
|
|
|
*/
|
|
|
|
template <class T>
|
|
|
|
static Status registerEventSubscriber() {
|
|
|
|
auto sub = std::make_shared<T>();
|
|
|
|
return registerEventSubscriber(sub);
|
2015-12-08 01:15:30 +00:00
|
|
|
};
|
2015-02-19 07:51:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Add an EventSubscriber to the factory.
|
|
|
|
*
|
|
|
|
* The registration is mostly abstracted using osquery's registry.
|
|
|
|
*
|
|
|
|
* @param sub If the caller must access the EventSubscriber instance
|
|
|
|
* control may be passed to the registry.
|
|
|
|
*
|
|
|
|
* Access to the EventSubscriber instance outside of the within-instance
|
|
|
|
* table generation method and set of EventCallback%s is discouraged.
|
|
|
|
*/
|
|
|
|
static Status registerEventSubscriber(const PluginRef& sub);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Add a SubscriptionContext and EventCallback Subscription to an
|
2015-04-01 04:18:56 +00:00
|
|
|
* EventPublisher.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
|
|
|
* Create a Subscription from a given SubscriptionContext and EventCallback
|
|
|
|
* and add that Subscription to the EventPublisher associated identifier.
|
|
|
|
*
|
2016-07-25 16:21:26 +00:00
|
|
|
* @param type_id ID string for an EventPublisher receiving the Subscription.
|
|
|
|
* @param name_id ID string for the EventSubscriber.
|
2015-07-19 21:02:02 +00:00
|
|
|
* @param sc A SubscriptionContext related to the EventPublisher.
|
2015-02-19 07:51:41 +00:00
|
|
|
* @param cb When the EventPublisher fires an event the SubscriptionContext
|
|
|
|
* will be evaluated, if the event matches optional specifics in the context
|
|
|
|
* this callback function will be called. It should belong to an
|
|
|
|
* EventSubscription.
|
|
|
|
*
|
|
|
|
* @return Was the SubscriptionContext appropriate for the EventPublisher.
|
|
|
|
*/
|
|
|
|
static Status addSubscription(EventPublisherID& type_id,
|
2015-04-12 02:50:35 +00:00
|
|
|
EventSubscriberID& name_id,
|
2015-07-19 21:02:02 +00:00
|
|
|
const SubscriptionContextRef& sc,
|
2015-12-08 01:15:30 +00:00
|
|
|
EventCallback cb = nullptr);
|
2015-02-19 07:51:41 +00:00
|
|
|
|
|
|
|
/// Add a Subscription using a caller Subscription instance.
|
|
|
|
static Status addSubscription(EventPublisherID& type_id,
|
|
|
|
const SubscriptionRef& subscription);
|
|
|
|
|
|
|
|
/// Get the total number of Subscription%s across ALL EventPublisher%s.
|
|
|
|
static size_t numSubscriptions(EventPublisherID& type_id);
|
|
|
|
|
|
|
|
/// Get the number of EventPublishers.
|
|
|
|
static size_t numEventPublishers() {
|
|
|
|
return EventFactory::getInstance().event_pubs_.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-05-07 03:02:23 +00:00
|
|
|
* @brief Halt the EventPublisher run loop.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
|
|
|
* Any EventSubscriber%s with Subscription%s for this EventPublisher will
|
|
|
|
* become useless. osquery callers MUST deregister events.
|
|
|
|
* EventPublisher%s assume they can hook/trampoline, which requires cleanup.
|
2015-05-07 03:02:23 +00:00
|
|
|
* This will tear down and remove the publisher if the run loop did not start.
|
|
|
|
* Otherwise it will call end on the publisher and assume the run loop will
|
|
|
|
* tear down and remove.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
2016-07-25 16:21:26 +00:00
|
|
|
* @param pub The string label for the EventPublisher.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
|
|
|
* @return Did the EventPublisher deregister cleanly.
|
|
|
|
*/
|
|
|
|
static Status deregisterEventPublisher(const EventPublisherRef& pub);
|
|
|
|
|
|
|
|
/// Deregister an EventPublisher by EventPublisherID.
|
|
|
|
static Status deregisterEventPublisher(EventPublisherID& type_id);
|
|
|
|
|
|
|
|
/// Return an instance to a registered EventPublisher.
|
|
|
|
static EventPublisherRef getEventPublisher(EventPublisherID& pub);
|
|
|
|
|
|
|
|
/// Return an instance to a registered EventSubscriber.
|
2015-04-26 10:54:27 +00:00
|
|
|
static EventSubscriberRef getEventSubscriber(EventSubscriberID& sub);
|
2015-05-07 03:02:23 +00:00
|
|
|
|
|
|
|
/// Check if an event subscriber exists.
|
2015-04-26 10:54:27 +00:00
|
|
|
static bool exists(EventSubscriberID& sub);
|
2015-02-19 07:51:41 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
/// Return a list of publisher types, these are their registry names.
|
2015-02-19 07:51:41 +00:00
|
|
|
static std::vector<std::string> publisherTypes();
|
2015-07-19 21:02:02 +00:00
|
|
|
|
|
|
|
/// Return a list of subscriber registry names,
|
2015-02-19 07:51:41 +00:00
|
|
|
static std::vector<std::string> subscriberNames();
|
|
|
|
|
2016-05-14 02:48:40 +00:00
|
|
|
/// Set log forwarding by adding a logger receiver.
|
|
|
|
static void addForwarder(const std::string& logger);
|
|
|
|
|
|
|
|
/// Optionally forward events to loggers.
|
|
|
|
static void forwardEvent(const std::string& event);
|
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
public:
|
|
|
|
/// The dispatched event thread's entry-point (if needed).
|
|
|
|
static Status run(EventPublisherID& type_id);
|
|
|
|
|
|
|
|
/// An initializer's entry-point for spawning all event type run loops.
|
|
|
|
static void delay();
|
|
|
|
|
|
|
|
/// If a static EventPublisher callback wants to fire
|
|
|
|
template <typename PUB>
|
|
|
|
static void fire(const EventContextRef& ec) {
|
2015-07-19 21:02:02 +00:00
|
|
|
auto event_pub = getEventPublisher(getType<PUB>());
|
2015-02-19 07:51:41 +00:00
|
|
|
event_pub->fire(ec);
|
|
|
|
}
|
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
/**
|
|
|
|
* @brief Return the publisher registry name given a type.
|
|
|
|
*
|
|
|
|
* Subscriber initialization and runtime static callbacks can lookup the
|
|
|
|
* publisher type name, which is the registry plugin name. This allows static
|
|
|
|
* callbacks to fire into subscribers.
|
|
|
|
*/
|
|
|
|
template <class PUB>
|
|
|
|
static EventPublisherID getType() {
|
|
|
|
auto pub = std::make_shared<PUB>();
|
|
|
|
return pub->type();
|
|
|
|
}
|
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
/**
|
2015-05-07 03:02:23 +00:00
|
|
|
* @brief End all EventPublisher run loops and deregister.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
2015-05-07 03:02:23 +00:00
|
|
|
* End is NOT the same as deregistration. End will call deregister on all
|
|
|
|
* publishers then either join or detach their run loop threads.
|
|
|
|
* See EventFactory::deregisterEventPublisher for actions taken during
|
|
|
|
* deregistration.
|
2015-02-19 07:51:41 +00:00
|
|
|
*
|
2016-07-25 16:21:26 +00:00
|
|
|
* @param join if true, threads will be joined
|
2015-02-19 07:51:41 +00:00
|
|
|
*/
|
|
|
|
static void end(bool join = false);
|
|
|
|
|
2015-12-08 01:15:30 +00:00
|
|
|
public:
|
|
|
|
EventFactory(EventFactory const&) = delete;
|
|
|
|
EventFactory& operator=(EventFactory const&) = delete;
|
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
private:
|
|
|
|
/// An EventFactory will exist for the lifetime of the application.
|
|
|
|
EventFactory() {}
|
|
|
|
~EventFactory() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// Set of registered EventPublisher instances.
|
|
|
|
std::map<EventPublisherID, EventPublisherRef> event_pubs_;
|
|
|
|
|
|
|
|
/// Set of instantiated EventSubscriber subscriptions.
|
|
|
|
std::map<EventSubscriberID, EventSubscriberRef> event_subs_;
|
|
|
|
|
|
|
|
/// Set of running EventPublisher run loop threads.
|
2016-03-18 02:11:18 +00:00
|
|
|
std::vector<std::shared_ptr<std::thread>> threads_;
|
2016-02-05 03:12:48 +00:00
|
|
|
|
2016-05-14 02:48:40 +00:00
|
|
|
/// Set of logger plugins to forward events.
|
|
|
|
std::vector<std::string> loggers_;
|
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
/// Factory publisher state manipulation.
|
2016-03-11 08:30:20 +00:00
|
|
|
Mutex factory_lock_;
|
2015-02-19 07:51:41 +00:00
|
|
|
};
|
|
|
|
|
2015-12-08 06:06:32 +00:00
|
|
|
/**
|
|
|
|
* @brief Generate OS events of a type (FS, Network, Syscall, ioctl).
|
|
|
|
*
|
|
|
|
* A 'class' of OS events is abstracted into an EventPublisher responsible for
|
|
|
|
* remaining as agile as possible given a known-set of subscriptions.
|
|
|
|
*
|
|
|
|
* The life cycle of an EventPublisher may include, `setUp`, `configure`, `run`,
|
|
|
|
* `tearDown`, and `fire`. `setUp` and `tearDown` happen when osquery starts and
|
|
|
|
* stops either as a daemon or interactive shell. `configure` is a pseudo-start
|
|
|
|
* called every time a Subscription is added. EventPublisher%s can adjust their
|
|
|
|
* scope/agility specific to each added subscription by overriding
|
|
|
|
*`addSubscription`, and/or globally in `configure`.
|
|
|
|
*
|
|
|
|
* Not all EventPublisher%s leverage pure async OS APIs, and most will require a
|
|
|
|
* run loop either polling with a timeout on a descriptor or for a change. When
|
|
|
|
* osquery initializes the EventFactory will optionally create a thread for each
|
|
|
|
* EventPublisher using `run` as the thread's entrypoint. `run` is called in a
|
|
|
|
* within-thread loop where returning a FAILED status ends the run loop and
|
|
|
|
* shuts down the thread.
|
|
|
|
*
|
|
|
|
* To opt-out of polling in a thread, consider the following run implementation:
|
|
|
|
*
|
|
|
|
* @code{.cpp}
|
|
|
|
* Status run() { return Status(1, "Not Implemented"); }
|
|
|
|
* @endcode
|
|
|
|
*
|
|
|
|
* The final life cycle component, `fire` will iterate over the EventPublisher
|
|
|
|
* Subscription%s and call `shouldFire` for each, using the EventContext fired.
|
|
|
|
* The `shouldFire` method should check the subscription-specific selectors and
|
|
|
|
* only call the Subscription%'s callback function if the EventContext
|
|
|
|
* (thus event) matches.
|
|
|
|
*/
|
|
|
|
template <typename SC, typename EC>
|
|
|
|
class EventPublisher : public EventPublisherPlugin {
|
|
|
|
public:
|
|
|
|
/// A nested helper typename for the templated SubscriptionContextRef.
|
|
|
|
using SCRef = typename std::shared_ptr<SC>;
|
|
|
|
/// A nested helper typename for the templated EventContextRef.
|
|
|
|
using ECRef = typename std::shared_ptr<EC>;
|
|
|
|
|
|
|
|
public:
|
|
|
|
EventPublisher(){};
|
|
|
|
virtual ~EventPublisher() {}
|
|
|
|
|
|
|
|
/// Up-cast a base EventContext reference to the templated ECRef.
|
|
|
|
static ECRef getEventContext(const EventContextRef& ec) {
|
|
|
|
return std::static_pointer_cast<EC>(ec);
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Up-cast a base SubscriptionContext reference to the templated SCRef.
|
|
|
|
static SCRef getSubscriptionContext(const SubscriptionContextRef& sc) {
|
|
|
|
return std::static_pointer_cast<SC>(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a EventContext based on the templated type.
|
2016-09-23 23:46:02 +00:00
|
|
|
static ECRef createEventContext() {
|
|
|
|
return std::make_shared<EC>();
|
|
|
|
}
|
2015-12-08 06:06:32 +00:00
|
|
|
|
|
|
|
/// Create a SubscriptionContext based on the templated type.
|
2016-09-23 23:46:02 +00:00
|
|
|
static SCRef createSubscriptionContext() {
|
|
|
|
return std::make_shared<SC>();
|
|
|
|
}
|
2015-12-08 06:06:32 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* @brief The internal `fire` phase of publishing.
|
|
|
|
*
|
|
|
|
* This is a template-generated method that up-casts the generic fired
|
|
|
|
* event/subscription contexts, and calls the callback if the event should
|
|
|
|
* fire given a subscription.
|
|
|
|
*
|
|
|
|
* @param sub The SubscriptionContext and optional EventCallback.
|
|
|
|
* @param ec The event that was fired.
|
|
|
|
*/
|
|
|
|
void fireCallback(const SubscriptionRef& sub,
|
2016-02-11 04:08:34 +00:00
|
|
|
const EventContextRef& ec) const override {
|
2015-12-08 06:06:32 +00:00
|
|
|
auto pub_sc = getSubscriptionContext(sub->context);
|
|
|
|
auto pub_ec = getEventContext(ec);
|
|
|
|
if (shouldFire(pub_sc, pub_ec) && sub->callback != nullptr) {
|
|
|
|
sub->callback(pub_ec, pub_sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* @brief The generic `fire` will call `shouldFire` for each Subscription.
|
|
|
|
*
|
|
|
|
* @param sc A SubscriptionContext with optional specifications for events
|
|
|
|
* details.
|
|
|
|
* @param ec The event fired with event details.
|
|
|
|
*
|
|
|
|
* @return should the Subscription%'s EventCallback be called for this event.
|
|
|
|
*/
|
|
|
|
virtual bool shouldFire(const SCRef& sc, const ECRef& ec) const {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-02-11 04:08:34 +00:00
|
|
|
FRIEND_TEST(EventsTests, test_event_subscriber_subscribe);
|
|
|
|
FRIEND_TEST(EventsTests, test_event_subscriber_context);
|
2015-12-08 06:06:32 +00:00
|
|
|
FRIEND_TEST(EventsTests, test_fire_event);
|
|
|
|
};
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
/**
|
|
|
|
* @brief An interface binding Subscriptions, event response, and table
|
|
|
|
*generation.
|
|
|
|
*
|
|
|
|
* Use the EventSubscriber interface when adding event subscriptions and
|
|
|
|
* defining callin functions. The EventCallback is usually a member function
|
|
|
|
* for an EventSubscriber. The EventSubscriber interface includes a very
|
|
|
|
* important `add` method that abstracts the needed event to backing store
|
|
|
|
* interaction.
|
|
|
|
*
|
|
|
|
* Storing event data in the backing store must match a table spec for queries.
|
|
|
|
* Small overheads exist that help query-time indexing and lookups.
|
|
|
|
*/
|
|
|
|
template <class PUB>
|
2015-01-30 18:44:25 +00:00
|
|
|
class EventSubscriber : public EventSubscriberPlugin {
|
2014-12-15 00:05:07 +00:00
|
|
|
protected:
|
2015-12-08 01:15:30 +00:00
|
|
|
using SCRef = typename PUB::SCRef;
|
|
|
|
using ECRef = typename PUB::ECRef;
|
2014-12-15 00:05:07 +00:00
|
|
|
|
|
|
|
public:
|
2015-09-03 19:22:42 +00:00
|
|
|
/**
|
|
|
|
* @brief The registry plugin name for the subscriber's publisher.
|
|
|
|
*
|
|
|
|
* During event factory initialization the subscribers 'peek' at the registry
|
|
|
|
* plugin name assigned to publishers. The corresponding publisher name is
|
|
|
|
* interpreted as the subscriber's event 'type'.
|
|
|
|
*/
|
2016-02-11 04:08:34 +00:00
|
|
|
virtual EventPublisherID& getType() const override {
|
2015-09-03 19:22:42 +00:00
|
|
|
static EventPublisherID type = EventFactory::getType<PUB>();
|
|
|
|
return type;
|
2015-12-08 01:15:30 +00:00
|
|
|
};
|
2015-09-03 19:22:42 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
protected:
|
2014-12-18 04:10:51 +00:00
|
|
|
/// Helper function to call the publisher's templated subscription generator.
|
2015-02-01 11:32:18 +00:00
|
|
|
SCRef createSubscriptionContext() const {
|
|
|
|
return PUB::createSubscriptionContext();
|
|
|
|
}
|
2014-12-15 06:17:38 +00:00
|
|
|
|
2014-12-18 04:10:51 +00:00
|
|
|
/**
|
|
|
|
* @brief Bind a registered EventSubscriber member function to a Subscription.
|
|
|
|
*
|
|
|
|
* @param entry A templated EventSubscriber member function.
|
|
|
|
* @param sc The subscription context.
|
|
|
|
*/
|
2015-12-08 06:06:32 +00:00
|
|
|
template <class T, typename E>
|
|
|
|
void subscribe(Status (T::*entry)(const std::shared_ptr<E>&, const SCRef&),
|
|
|
|
const SCRef& sc) {
|
2015-12-08 01:15:30 +00:00
|
|
|
using std::placeholders::_1;
|
|
|
|
using std::placeholders::_2;
|
2015-12-08 06:06:32 +00:00
|
|
|
using CallbackFunc =
|
|
|
|
Status (T::*)(const EventContextRef&, const SubscriptionContextRef&);
|
2015-12-08 01:15:30 +00:00
|
|
|
|
2014-12-18 04:10:51 +00:00
|
|
|
// Down-cast the pointer to the member function.
|
2015-12-08 01:15:30 +00:00
|
|
|
auto base_entry = reinterpret_cast<CallbackFunc>(entry);
|
2015-12-08 06:06:32 +00:00
|
|
|
// Up-cast the EventSubscriber to the caller.
|
|
|
|
auto sub = dynamic_cast<T*>(this);
|
2015-12-08 07:08:00 +00:00
|
|
|
if (base_entry != nullptr && sub != nullptr) {
|
|
|
|
// Create a callable through the member function using the instance of the
|
|
|
|
// EventSubscriber and a single parameter placeholder (the EventContext).
|
|
|
|
auto cb = std::bind(base_entry, sub, _1, _2);
|
|
|
|
// Add a subscription using the callable and SubscriptionContext.
|
|
|
|
EventFactory::addSubscription(sub->getType(), sub->getName(), sc, cb);
|
|
|
|
subscription_count_++;
|
|
|
|
}
|
2015-07-19 21:02:02 +00:00
|
|
|
}
|
2014-12-15 00:05:07 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
public:
|
2015-12-08 01:15:30 +00:00
|
|
|
explicit EventSubscriber(bool enabled = true)
|
2016-10-30 18:15:55 +00:00
|
|
|
: EventSubscriberPlugin(), disabled(!enabled) {}
|
2015-12-08 07:08:00 +00:00
|
|
|
virtual ~EventSubscriber() {}
|
2015-10-26 08:14:51 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* @brief Allow subscriber implementations to default disable themselves.
|
|
|
|
*
|
|
|
|
* A subscriber may induce latency on a system within the callback routines.
|
|
|
|
* Before the initialization and set up is performed the EventFactory can
|
|
|
|
* choose to exclude a subscriber if it is not explicitly enabled within
|
|
|
|
* the config.
|
|
|
|
*
|
|
|
|
* EventSubscriber%s that should be default-disabled should set this flag
|
|
|
|
* in their constructor or worst case before EventSubsciber::init.
|
|
|
|
*/
|
|
|
|
bool disabled{false};
|
2015-04-01 04:18:56 +00:00
|
|
|
|
2015-10-26 08:14:51 +00:00
|
|
|
private:
|
|
|
|
friend class EventFactory;
|
|
|
|
|
2014-12-15 00:05:07 +00:00
|
|
|
private:
|
2014-12-15 05:20:20 +00:00
|
|
|
FRIEND_TEST(EventsTests, test_event_sub);
|
|
|
|
FRIEND_TEST(EventsTests, test_event_sub_subscribe);
|
|
|
|
FRIEND_TEST(EventsTests, test_event_sub_context);
|
2015-10-26 08:14:51 +00:00
|
|
|
FRIEND_TEST(EventsTests, test_event_toggle_subscribers);
|
2014-12-15 00:05:07 +00:00
|
|
|
};
|
2014-09-24 15:03:16 +00:00
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
/// Iterate the event publisher registry and create run loops for each using
|
|
|
|
/// the event factory.
|
|
|
|
void attachEvents();
|
2014-09-24 18:25:05 +00:00
|
|
|
}
|