Introduce generator/yield-style virtual tables (#3060)

This commit is contained in:
Teddy Reed 2017-03-15 18:46:42 -07:00 committed by GitHub
parent cea5981182
commit 1e71f4aab8
14 changed files with 338 additions and 96 deletions

View File

@ -516,11 +516,6 @@ class RegistryFactory : private boost::noncopyable {
static Status call(const std::string& registry_name,
const PluginRequest& request);
/// A helper call optimized for table data generation.
static Status callTable(const std::string& table_name,
QueryContext& context,
PluginResponse& response);
/// Run `setUp` on every registry that is not marked 'lazy'.
static void setUp();

View File

@ -23,31 +23,6 @@ namespace osquery {
DECLARE_int32(value_max);
/**
* @brief An abstract similar to boost's noncopyable that defines moves.
*
* By defining protected move constructors we allow the children to assign
* their's as default.
*/
class only_movable {
protected:
/// Boilerplate self default constructor.
only_movable() {}
/// Boilerplate self destructor.
~only_movable() {}
/// Important, existance of a move constructor.
only_movable(only_movable&&) {}
private:
/// Important, a private copy constructor prevents copying.
only_movable(const only_movable&);
/// Important, a private copy assignment constructor prevents copying.
only_movable& operator=(const only_movable&);
};
/**
* @brief The core interface to executing osquery SQL commands.
*

View File

@ -17,6 +17,12 @@
#include <unordered_map>
#include <vector>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#endif
#include <boost/coroutine2/all.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/ptree.hpp>
@ -36,6 +42,31 @@
namespace osquery {
/**
* @brief An abstract similar to boost's noncopyable that defines moves.
*
* By defining protected move constructors we allow the children to assign
* their's as default.
*/
class only_movable {
protected:
/// Boilerplate self default constructor.
only_movable() {}
/// Boilerplate self destructor.
~only_movable() {}
/// Important, existance of a move constructor.
only_movable(only_movable&&) {}
private:
/// Important, a private copy constructor prevents copying.
only_movable(const only_movable&);
/// Important, a private copy assignment constructor prevents copying.
only_movable& operator=(const only_movable&);
};
/**
* @brief osquery does not yet use a NULL type.
*
@ -492,11 +523,14 @@ struct VirtualTableContent {
std::map<std::string, Row> cache;
};
using RowGenerator = boost::coroutines2::coroutine<Row&>;
using RowYield = RowGenerator::push_type;
/**
* @brief A QueryContext is provided to every table generator for optimization
* on query components like predicate constraints and limits.
*/
struct QueryContext : private boost::noncopyable {
struct QueryContext : private only_movable {
/// Construct a context without cache support.
QueryContext() : enable_cache_(false), table_(new VirtualTableContent()) {}
@ -511,6 +545,12 @@ struct QueryContext : private boost::noncopyable {
explicit QueryContext(VirtualTableContent* content)
: enable_cache_(true), table_(content) {}
/// Allow moving.
QueryContext(QueryContext&&) = default;
/// Allow move assignment.
QueryContext& operator=(QueryContext&&) = default;
/**
* @brief Check if a constraint exists for a given column operator pair.
*
@ -542,9 +582,9 @@ struct QueryContext : private boost::noncopyable {
* @param predicate A predicate receiving each expression.
*/
template <typename T>
void forEachConstraint(const std::string& column,
ConstraintOperator op,
std::function<void(const T& expr)> predicate) const {
void iteritems(const std::string& column,
ConstraintOperator op,
std::function<void(const T& expr)> predicate) const {
if (constraints.count(column) > 0) {
const auto& list = constraints.at(column);
if (list.affinity == TEXT_TYPE) {
@ -563,11 +603,10 @@ struct QueryContext : private boost::noncopyable {
}
/// Helper for string type (most all types are TEXT/VARCHAR).
void forEachConstraint(
const std::string& column,
ConstraintOperator op,
std::function<void(const std::string& expr)> predicate) const {
return forEachConstraint<std::string>(column, op, predicate);
void iteritems(const std::string& column,
ConstraintOperator op,
std::function<void(const std::string& expr)> predicate) const {
return iteritems<std::string>(column, op, predicate);
}
/**
@ -650,7 +689,7 @@ using Constraint = struct Constraint;
* in osquery/tables/templates/default.cpp.in
*/
class TablePlugin : public Plugin {
protected:
public:
/**
* @brief Table name aliases create full-scan VIEWs for tables.
*
@ -695,10 +734,37 @@ class TablePlugin : public Plugin {
* @param request A query context filled in by SQLite's virtual table API.
* @return The result rows for this table, given the query context.
*/
virtual QueryData generate(QueryContext& request) {
virtual QueryData generate(QueryContext& context) {
return QueryData();
}
/**
* @brief Generate a table representation by yielding each row.
*
* For tables that set generator=True in their spec's implementation, this
* generator will be bound to an asymmetric coroutine. It should call the
* provided yield function for each Row returned. Treat this like Python's
* generator-type methods where the only difference is yield is not reserved
* but rather provided with some boilerplate syntax.
*
* This implementation uses nearly %5 more cycles than the generate method
* when the table content is small (less than 100 rows) and has a disadvantage
* of not being cachable since the entire contents are not available before
* post-filter aggregations. This implementation prevents the need for
* multiple representations of table content existing simultaneously and is
* always more memory efficient. It can be more compute efficient for tables
* with over 1000 rows.
*
* @param yield a callable that takes a single Row as input.
* @param context a query context filled in by SQLite's virtual table API.
*/
virtual void generator(RowYield& yield, QueryContext& context) {}
/// Override and return true to use the generator and yield method.
virtual bool usesGenerator() const {
return false;
}
protected:
/// An SQL table containing the table definition/syntax.
std::string columnDefinition() const;
@ -810,6 +876,7 @@ class TablePlugin : public Plugin {
FRIEND_TEST(VirtualTableTests, test_tableplugin_columndefinition);
FRIEND_TEST(VirtualTableTests, test_tableplugin_statement);
FRIEND_TEST(VirtualTableTests, test_indexing_costs);
FRIEND_TEST(VirtualTableTests, test_yield_generator);
};
/// Helper method to generate the virtual table CREATE statement.

View File

@ -47,6 +47,7 @@ if(WINDOWS)
ADD_OSQUERY_LINK_CORE("libboost_system-vc140-mt-s-1_63")
ADD_OSQUERY_LINK_CORE("libboost_regex-vc140-mt-s-1_63")
ADD_OSQUERY_LINK_CORE("libboost_filesystem-vc140-mt-s-1_63")
ADD_OSQUERY_LINK_CORE("libboost_context-vc140-mt-s-1_63")
ADD_OSQUERY_LINK_CORE("rocksdblib")
ADD_OSQUERY_LINK_CORE("snappy64")
ADD_OSQUERY_LINK_CORE("gflags_static")
@ -71,6 +72,8 @@ if(APPLE OR LINUX)
ADD_OSQUERY_LINK_CORE("libdl")
ADD_OSQUERY_LINK_CORE("boost_system-mt")
ADD_OSQUERY_LINK_CORE("boost_filesystem-mt")
ADD_OSQUERY_LINK_CORE("boost_context-mt")
ADD_OSQUERY_LINK_ADDITIONAL("rocksdb_lite")
ADD_OSQUERY_LINK_ADDITIONAL("boost_regex-mt")
elseif(FREEBSD)
@ -78,6 +81,8 @@ elseif(FREEBSD)
ADD_OSQUERY_LINK_CORE("boost_system")
ADD_OSQUERY_LINK_CORE("boost_filesystem")
ADD_OSQUERY_LINK_CORE("boost_thread")
ADD_OSQUERY_LINK_CORE("boost_context")
ADD_OSQUERY_LINK_ADDITIONAL("rocksdb")
ADD_OSQUERY_LINK_ADDITIONAL("boost_regex")
endif()

View File

@ -447,23 +447,6 @@ Status RegistryFactory::call(const std::string& registry_name,
return call(registry_name, request, response);
}
Status RegistryFactory::callTable(const std::string& table_name,
QueryContext& context,
PluginResponse& response) {
auto& tables = get().registry("table")->items_;
// This only works for local tables.
if (tables.count(table_name) > 0) {
auto plugin = std::dynamic_pointer_cast<TablePlugin>(tables.at(table_name));
response = plugin->generate(context);
return Status(0);
} else {
// If the table is not local then it does not benefit from complex contexts.
PluginRequest request = {{"action", "generate"}};
TablePlugin::setRequestFromContext(context, request);
return call("table", table_name, request, response);
}
}
Status RegistryFactory::setActive(const std::string& registry_name,
const std::string& item_name) {
WriteLock lock(mutex_);

View File

@ -20,7 +20,7 @@
namespace osquery {
class BenchmarkTablePlugin : public TablePlugin {
private:
protected:
TableColumns columns() const {
return {
std::make_tuple("test_int", INTEGER_TYPE, ColumnOptions::DEFAULT),
@ -36,6 +36,28 @@ class BenchmarkTablePlugin : public TablePlugin {
}
};
class BenchmarkTableYieldPlugin : public BenchmarkTablePlugin {
public:
bool usesGenerator() const override {
return true;
}
void generator(RowYield& yield, QueryContext& ctx) override {
{
Row r;
r["test_int"] = "0";
yield(r);
}
{
Row r;
r["test_int"] = "0";
r["test_text"] = "hello";
yield(r);
}
}
};
static void SQL_virtual_table_registry(benchmark::State& state) {
// Add a sample virtual table plugin.
// Profile calling the plugin's column data.
@ -57,17 +79,38 @@ static void SQL_virtual_table_internal(benchmark::State& state) {
Registry::call("table", "benchmark", {{"action", "columns"}}, res);
// Attach a sample virtual table.
auto dbc = SQLiteDBManager::get();
auto dbc = SQLiteDBManager::getUnique();
attachTableInternal("benchmark", columnDefinition(res), dbc);
while (state.KeepRunning()) {
QueryData results;
queryInternal("select * from benchmark", results, dbc->db());
dbc->clearAffectedTables();
}
}
BENCHMARK(SQL_virtual_table_internal);
static void SQL_virtual_table_internal_yield(benchmark::State& state) {
auto tables = RegistryFactory::get().registry("table");
tables->add("benchmark_yield", std::make_shared<BenchmarkTableYieldPlugin>());
PluginResponse res;
Registry::call("table", "benchmark_yield", {{"action", "columns"}}, res);
// Attach a sample virtual table.
auto dbc = SQLiteDBManager::getUnique();
attachTableInternal("benchmark_yield", columnDefinition(res), dbc);
while (state.KeepRunning()) {
QueryData results;
queryInternal("select * from benchmark_yield", results, dbc->db());
dbc->clearAffectedTables();
}
}
BENCHMARK(SQL_virtual_table_internal_yield);
static void SQL_virtual_table_internal_global(benchmark::State& state) {
auto tables = RegistryFactory::get().registry("table");
tables->add("benchmark", std::make_shared<BenchmarkTablePlugin>());
@ -82,6 +125,7 @@ static void SQL_virtual_table_internal_global(benchmark::State& state) {
QueryData results;
queryInternal("select * from benchmark", results, dbc->db());
dbc->clearAffectedTables();
}
}
@ -101,6 +145,7 @@ static void SQL_virtual_table_internal_unique(benchmark::State& state) {
QueryData results;
queryInternal("select * from benchmark", results, dbc->db());
dbc->clearAffectedTables();
}
}
@ -138,13 +183,16 @@ static void SQL_virtual_table_internal_long(benchmark::State& state) {
while (state.KeepRunning()) {
QueryData results;
queryInternal("select * from long_benchmark", results, dbc->db());
dbc->clearAffectedTables();
}
}
BENCHMARK(SQL_virtual_table_internal_long);
size_t kWideCount{0};
class BenchmarkWideTablePlugin : public TablePlugin {
private:
protected:
TableColumns columns() const override {
TableColumns cols;
for (int i = 0; i < 20; i++) {
@ -156,7 +204,7 @@ class BenchmarkWideTablePlugin : public TablePlugin {
QueryData generate(QueryContext& ctx) override {
QueryData results;
for (int k = 0; k < 50; k++) {
for (int k = 0; k < kWideCount; k++) {
Row r;
for (int i = 0; i < 20; i++) {
r["test_" + std::to_string(i)] = "0";
@ -167,6 +215,23 @@ class BenchmarkWideTablePlugin : public TablePlugin {
}
};
class BenchmarkWideTableYieldPlugin : public BenchmarkWideTablePlugin {
public:
bool usesGenerator() const override {
return true;
}
void generator(RowYield& yield, QueryContext& ctx) override {
for (int k = 0; k < kWideCount; k++) {
Row r;
for (int i = 0; i < 20; i++) {
r["test_" + std::to_string(i)] = "0";
}
yield(r);
}
}
};
static void SQL_virtual_table_internal_wide(benchmark::State& state) {
auto tables = RegistryFactory::get().registry("table");
tables->add("wide_benchmark", std::make_shared<BenchmarkWideTablePlugin>());
@ -178,20 +243,53 @@ static void SQL_virtual_table_internal_wide(benchmark::State& state) {
auto dbc = SQLiteDBManager::getUnique();
attachTableInternal("wide_benchmark", columnDefinition(res), dbc);
kWideCount = state.range_y();
while (state.KeepRunning()) {
QueryData results;
queryInternal("select * from wide_benchmark", results, dbc->db());
dbc->clearAffectedTables();
}
}
BENCHMARK(SQL_virtual_table_internal_wide);
BENCHMARK(SQL_virtual_table_internal_wide)
->ArgPair(0, 1)
->ArgPair(0, 10)
->ArgPair(0, 100)
->ArgPair(0, 1000);
static void SQL_virtual_table_internal_wide_yield(benchmark::State& state) {
auto tables = RegistryFactory::get().registry("table");
tables->add("wide_benchmark_yield",
std::make_shared<BenchmarkWideTableYieldPlugin>());
PluginResponse res;
Registry::call("table", "wide_benchmark_yield", {{"action", "columns"}}, res);
// Attach a sample virtual table.
auto dbc = SQLiteDBManager::getUnique();
attachTableInternal("wide_benchmark_yield", columnDefinition(res), dbc);
kWideCount = state.range_y();
while (state.KeepRunning()) {
QueryData results;
queryInternal("select * from wide_benchmark_yield", results, dbc->db());
dbc->clearAffectedTables();
}
}
BENCHMARK(SQL_virtual_table_internal_wide_yield)
->ArgPair(0, 1)
->ArgPair(0, 10)
->ArgPair(0, 100)
->ArgPair(0, 1000);
static void SQL_select_metadata(benchmark::State& state) {
auto dbc = SQLiteDBManager::get();
auto dbc = SQLiteDBManager::getUnique();
while (state.KeepRunning()) {
QueryData results;
queryInternal(
"select count(*) from sqlite_temp_master;", results, dbc->db());
dbc->clearAffectedTables();
}
}

View File

@ -421,6 +421,51 @@ TEST_F(VirtualTableTests, test_table_cache) {
ASSERT_EQ(results[0]["data"], "awesome_data");
}
class yieldTablePlugin : public TablePlugin {
private:
TableColumns columns() const override {
return {
std::make_tuple("index", INTEGER_TYPE, ColumnOptions::DEFAULT),
};
}
public:
bool usesGenerator() const override {
return true;
}
void generator(RowYield& yield, QueryContext& qc) override {
for (size_t i = 0; i < 10; i++) {
Row r;
r["index"] = std::to_string(index_++);
yield(r);
}
}
private:
size_t index_{0};
};
TEST_F(VirtualTableTests, test_yield_generator) {
auto table = std::make_shared<yieldTablePlugin>();
auto table_registry = RegistryFactory::get().registry("table");
table_registry->add("yield", table);
auto dbc = SQLiteDBManager::getUnique();
attachTableInternal("yield", table->columnDefinition(), dbc);
QueryData results;
queryInternal("SELECT * from yield", results, dbc->db());
dbc->clearAffectedTables();
EXPECT_EQ(results.size(), 10U);
EXPECT_EQ(results[0]["index"], "0");
results.clear();
queryInternal("SELECT * from yield", results, dbc->db());
dbc->clearAffectedTables();
EXPECT_EQ(results[0]["index"], "10");
}
class indexIOptimizedTablePlugin : public TablePlugin {
private:
TableColumns columns() const override {
@ -528,6 +573,8 @@ TEST_F(VirtualTableTests, test_indexing_costs) {
QueryData results;
queryInternal(
"SELECT * from default_scan JOIN index_i using (i);", results, dbc->db());
dbc->clearAffectedTables();
// We expect index_i to optimize, meaning the constraint evaluation
// understood the marked columns and returned a low cost.
ASSERT_EQ(1U, default_scan->scans);
@ -540,6 +587,7 @@ TEST_F(VirtualTableTests, test_indexing_costs) {
// The inverse should also hold, all cost evaluations will be high.
queryInternal(
"SELECT * from index_i JOIN default_scan using (i);", results, dbc->db());
dbc->clearAffectedTables();
EXPECT_EQ(10U, i->scans);
EXPECT_EQ(1U, default_scan->scans);
@ -552,6 +600,7 @@ TEST_F(VirtualTableTests, test_indexing_costs) {
"(j);",
results,
dbc->db());
dbc->clearAffectedTables();
ASSERT_EQ(1U, default_scan->scans);
EXPECT_EQ(10U, i->scans);
EXPECT_EQ(10U, j->scans);

View File

@ -102,6 +102,14 @@ int xClose(sqlite3_vtab_cursor* cur) {
int xEof(sqlite3_vtab_cursor* cur) {
BaseCursor* pCur = (BaseCursor*)cur;
if (pCur->uses_generator) {
if (*pCur->generator) {
return false;
}
pCur->generator = nullptr;
return true;
}
if (pCur->row >= pCur->n) {
// If the requested row exceeds the size of the row set then all rows
// have been visited, clear the data container.
@ -119,6 +127,12 @@ int xDestroy(sqlite3_vtab* p) {
int xNext(sqlite3_vtab_cursor* cur) {
BaseCursor* pCur = (BaseCursor*)cur;
if (pCur->uses_generator) {
pCur->generator->operator()();
if (*pCur->generator) {
pCur->current = pCur->generator->get();
}
}
pCur->row++;
return SQLITE_OK;
}
@ -233,7 +247,7 @@ int xColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col) {
// Requested column index greater than column set size.
return SQLITE_ERROR;
}
if (pCur->row >= pCur->data.size()) {
if (!pCur->uses_generator && pCur->row >= pCur->data.size()) {
// Request row index greater than row set size.
return SQLITE_ERROR;
}
@ -248,9 +262,16 @@ int xColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col) {
pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
}
Row* row = nullptr;
if (pCur->uses_generator) {
row = &pCur->current;
} else {
row = &pCur->data[pCur->row];
}
// Attempt to cast each xFilter-populated row/column to the SQLite type.
const auto& value = pCur->data[pCur->row][column_name];
if (pCur->data[pCur->row].count(column_name) == 0) {
const auto& value = (*row)[column_name];
if (row->count(column_name) == 0) {
// Missing content.
VLOG(1) << "Error " << column_name << " is empty";
sqlite3_result_null(ctx);
@ -465,8 +486,8 @@ static int xFilter(sqlite3_vtab_cursor* pVtabCursor,
// Constraints failed.
}
// Evaluate index and optimized constratint requirements.
// These are satisfied regarless of expression content availability.
// Evaluate index and optimized constraint requirements.
// These are satisfied regardless of expression content availability.
for (const auto& constraint : constraints) {
if (options[constraint.first] & ColumnOptions::REQUIRED) {
// A required option exists in the constraints.
@ -506,7 +527,27 @@ static int xFilter(sqlite3_vtab_cursor* pVtabCursor,
// Generate the row data set.
plan("Scanning rows for cursor (" + std::to_string(pCur->id) + ")");
Registry::callTable(pVtab->content->name, context, pCur->data);
if (Registry::get().exists("table", pVtab->content->name, true)) {
auto plugin = Registry::get().plugin("table", pVtab->content->name);
auto table = std::dynamic_pointer_cast<TablePlugin>(plugin);
if (table->usesGenerator()) {
pCur->uses_generator = true;
pCur->generator = std::make_unique<RowGenerator::pull_type>(
std::bind(&TablePlugin::generator,
table,
std::placeholders::_1,
std::move(context)));
if (*pCur->generator) {
pCur->current = pCur->generator->get();
}
return SQLITE_OK;
}
pCur->data = table->generate(context);
} else {
PluginRequest request = {{"action", "generate"}};
TablePlugin::setRequestFromContext(context, request);
Registry::call("table", pVtab->content->name, request, pCur->data);
}
// Set the number of rows.
pCur->n = pCur->data.size();

View File

@ -35,6 +35,7 @@ extern RecursiveMutex kAttachMutex;
* Only used in the SQLite virtual table module methods.
*/
struct BaseCursor : private boost::noncopyable {
public:
/// SQLite virtual table cursor.
sqlite3_vtab_cursor base;
@ -44,6 +45,15 @@ struct BaseCursor : private boost::noncopyable {
/// Table data generated from last access.
QueryData data;
/// Callable generator.
std::unique_ptr<RowGenerator::pull_type> generator{nullptr};
/// Results of current call.
Row current;
/// Does the backing local table use a generator type.
bool uses_generator{false};
/// Current cursor position.
size_t row{0};

View File

@ -526,12 +526,11 @@ QueryData genSMCKeys(QueryContext &context) {
// If the query is requesting an SMC key by name within the predicate.
if (context.hasConstraint("key", EQUALS)) {
context.forEachConstraint("key",
EQUALS,
([&smc, &results](const std::string &expr) {
bool hidden = (kSMCHiddenKeys.count(expr) > 0);
genSMCKey(expr, smc, results, hidden);
}));
context.iteritems(
"key", EQUALS, ([&smc, &results](const std::string& expr) {
bool hidden = (kSMCHiddenKeys.count(expr) > 0);
genSMCKey(expr, smc, results, hidden);
}));
return results;
}
@ -575,7 +574,7 @@ inline QueryData getSMCKeysUsingPredicate(
});
if (context.hasConstraint("key", EQUALS)) {
context.forEachConstraint("key", EQUALS, wrapped);
context.iteritems("key", EQUALS, wrapped);
} else {
// Perform a full scan of the keys category.
for (const auto &key : keys) {

View File

@ -18,11 +18,11 @@ namespace tables {
QueryData usersFromContext(const QueryContext& context, bool all) {
QueryData users;
if (context.hasConstraint("uid", EQUALS)) {
context.forEachConstraint(
"uid", EQUALS, ([&users](const std::string& expr) {
auto user = SQL::selectAllFrom("users", "uid", EQUALS, expr);
users.insert(users.end(), user.begin(), user.end());
}));
context.iteritems("uid", EQUALS, ([&users](const std::string& expr) {
auto user =
SQL::selectAllFrom("users", "uid", EQUALS, expr);
users.insert(users.end(), user.begin(), user.end());
}));
} else if (!all) {
users = SQL::selectAllFrom(
"users", "uid", EQUALS, std::to_string(platformGetUid()));
@ -35,11 +35,11 @@ QueryData usersFromContext(const QueryContext& context, bool all) {
QueryData pidsFromContext(const QueryContext& context, bool all) {
QueryData procs;
if (context.hasConstraint("pid", EQUALS)) {
context.forEachConstraint(
"pid", EQUALS, ([&procs](const std::string& expr) {
auto proc = SQL::selectAllFrom("processes", "pid", EQUALS, expr);
procs.insert(procs.end(), procs.begin(), procs.end());
}));
context.iteritems("pid", EQUALS, ([&procs](const std::string& expr) {
auto proc = SQL::selectAllFrom(
"processes", "pid", EQUALS, expr);
procs.insert(procs.end(), procs.begin(), procs.end());
}));
} else if (!all) {
procs = SQL::selectAllFrom(
"processes", "pid", EQUALS, std::to_string(platformGetPid()));

View File

@ -183,6 +183,7 @@ class TableState(Singleton):
self.fuzz_paths = []
self.has_options = False
self.has_column_aliases = False
self.generator = False
def columns(self):
return [i for i in self.schema if isinstance(i, Column)]
@ -212,6 +213,10 @@ class TableState(Singleton):
if len(set(all_options).intersection(NON_CACHEABLE)) > 0:
print(lightred("Table cannot be marked cacheable: %s" % (path)))
exit(1)
if self.generator:
print(lightred(
"Table cannot use a generator and be marked cacheable: %s" % (path)))
exit(1)
if self.table_name == "" or self.function == "":
print(lightred("Invalid table spec: %s" % (path)))
exit(1)
@ -249,6 +254,7 @@ class TableState(Singleton):
aliases=self.aliases,
has_options=self.has_options,
has_column_aliases=self.has_column_aliases,
generator=self.generator,
attribute_set=[TABLE_ATTRIBUTES[attr] for attr in self.attributes],
)
@ -339,7 +345,7 @@ def fuzz_paths(paths):
table.fuzz_paths = paths
def implementation(impl_string):
def implementation(impl_string, generator=False):
"""
define the path to the implementation file and the function which
implements the virtual table. You should use the following format:
@ -360,6 +366,7 @@ def implementation(impl_string):
table.impl = impl
table.function = function
table.class_name = class_name
table.generator = generator
'''Check if the table has a subscriber attribute, if so, enforce time.'''
if "event_subscriber" in table.attributes:

View File

@ -21,11 +21,15 @@ namespace osquery {
/// BEGIN[GENTABLE]
namespace tables {
{% if class_name == "" %}\
osquery::QueryData {{function}}(QueryContext& request);
{% if generator %}\
void {{function}}(RowYield& yield, QueryContext& context);
{% else %}\
osquery::QueryData {{function}}(QueryContext& context);
{% endif %}\
{% else %}
class {{class_name}} {
public:
osquery::QueryData {{function}}(QueryContext& request);
osquery::QueryData {{function}}(QueryContext& context);
};
{% endif %}\
}
@ -78,11 +82,18 @@ class {{table_name_cc}}TablePlugin : public TablePlugin {
TableAttributes::NONE;
}
QueryData generate(QueryContext& request) override {
{% if generator %}\
bool usesGenerator() const override { return true; }
void generator(RowYield& yield, QueryContext& context) override {
tables::{{function}}(yield, context);
}
{% else %}\
QueryData generate(QueryContext& context) override {
{% if class_name != "" %}\
if (EventFactory::exists(getName())) {
auto subscriber = EventFactory::getEventSubscriber(getName());
return subscriber->{{function}}(request);
return subscriber->{{function}}(context);
} else {
LOG(ERROR) << "Subscriber table missing: " << getName();
return QueryData();
@ -93,13 +104,15 @@ class {{table_name_cc}}TablePlugin : public TablePlugin {
return getCache();
}
{% endif %}\
auto results = tables::{{function}}(request);
auto results = tables::{{function}}(context);
{% if attributes.cacheable %}\
setCache(kCacheStep, kCacheInterval, results);
{% endif %}
return results;
{% endif %}\
}
{% endif %}\
};
{% if attributes.utility %}

View File

@ -6,7 +6,7 @@ class Boost < AbstractOsqueryFormula
url "https://downloads.sourceforge.net/project/boost/boost/1.63.0/boost_1_63_0.tar.bz2"
sha256 "beae2529f759f6b3bf3f4969a19c2e9d6f0c503edcb2de4a61d1428519fcb3b0"
head "https://github.com/boostorg/boost.git"
revision 4
revision 7
bottle do
root_url "https://osquery-packages.s3.amazonaws.com/bottles"
@ -15,8 +15,6 @@ class Boost < AbstractOsqueryFormula
sha256 "429a64c15405fcd9c1d5f8712da9834859a8cb5695660d83d8f0d4b380add1c7" => :x86_64_linux
end
patch :DATA
env :userpaths
option :universal
@ -60,6 +58,8 @@ class Boost < AbstractOsqueryFormula
"--with-regex",
"--with-system",
"--with-thread",
"--with-coroutine2",
"--with-context",
"threading=multi",
"link=static",
"optimization=space",