namespace osquery {
-void Plugin::setName(const std::string& name) {
- if (!name_.empty() && name != name_) {
- std::string error = "Cannot rename plugin " + name_ + " to " + name;
- throw std::runtime_error(error);
- }
+void Plugin::setName(const std::string& name)
+{
+ if (!name_.empty() && name != name_) {
+ std::string error = "Cannot rename plugin " + name_ + " to " + name;
+ throw std::runtime_error(error);
+ }
- name_ = name;
+ name_ = name;
}
-PluginResponse tableRowsToPluginResponse(const TableRows& rows) {
- PluginResponse result;
- for (const auto& row : rows) {
- result.push_back(static_cast<Row>(*row));
- }
- return result;
+PluginResponse tableRowsToPluginResponse(const TableRows& rows)
+{
+ PluginResponse result;
+ for (const auto& row : rows) {
+ result.push_back(static_cast<Row>(*row));
+ }
+ return result;
}
} // namespace osquery
using PluginResponse = std::vector<PluginRequest>;
class Plugin : private boost::noncopyable {
- public:
- virtual ~Plugin() = default;
-
- public:
- /// The plugin may perform some initialization, not required.
- virtual Status setUp() {
- return Status::success();
- }
-
- /// The plugin may perform some tear down, release, not required.
- virtual void tearDown() {}
-
- /// The plugin may react to configuration updates.
- virtual void configure() {}
-
- /// The plugin may publish route info (other than registry type and name).
- virtual PluginResponse routeInfo() const {
- return PluginResponse();
- }
-
- /**
- * @brief Plugins act by being called, using a request, returning a response.
- *
- * The plugin request is a thrift-serializable object. A response is optional
- * but the API for using a plugin's call is defined by the registry. In most
- * cases there are multiple supported call 'actions'. A registry type, or
- * the plugin class, will define the action key and supported actions.
- *
- * @param request A plugin request input, including optional action.
- * @param response A plugin response output.
- *
- * @return Status of the call, if the action was handled corrected.
- */
- virtual Status call(const PluginRequest& request,
- PluginResponse& response) = 0;
-
- /// Allow the plugin to introspect into the registered name (for logging).
- virtual void setName(const std::string& name) final;
-
- /// Force call-sites to use #getName to access the plugin item's name.
- virtual const std::string& getName() const {
- return name_;
- }
-
- protected:
- /// Customized name for the plugin, usually set by the registry.
- std::string name_;
+public:
+ virtual ~Plugin() = default;
+
+public:
+ /// The plugin may perform some initialization, not required.
+ virtual Status setUp()
+ {
+ return Status::success();
+ }
+
+ /// The plugin may perform some tear down, release, not required.
+ virtual void tearDown() {}
+
+ /// The plugin may react to configuration updates.
+ virtual void configure() {}
+
+ /// The plugin may publish route info (other than registry type and name).
+ virtual PluginResponse routeInfo() const
+ {
+ return PluginResponse();
+ }
+
+ /**
+ * @brief Plugins act by being called, using a request, returning a response.
+ *
+ * The plugin request is a thrift-serializable object. A response is optional
+ * but the API for using a plugin's call is defined by the registry. In most
+ * cases there are multiple supported call 'actions'. A registry type, or
+ * the plugin class, will define the action key and supported actions.
+ *
+ * @param request A plugin request input, including optional action.
+ * @param response A plugin response output.
+ *
+ * @return Status of the call, if the action was handled corrected.
+ */
+ virtual Status call(const PluginRequest& request,
+ PluginResponse& response) = 0;
+
+ /// Allow the plugin to introspect into the registered name (for logging).
+ virtual void setName(const std::string& name) final;
+
+ /// Force call-sites to use #getName to access the plugin item's name.
+ virtual const std::string& getName() const
+ {
+ return name_;
+ }
+
+protected:
+ /// Customized name for the plugin, usually set by the registry.
+ std::string name_;
};
/// Helper definition for a shared pointer to a Plugin.
namespace osquery {
class SQLPlugin : public Plugin {
- public:
- /// Run a SQL query string against the SQL implementation.
- virtual Status query(const std::string& query,
- QueryData& results,
- bool use_cache) const = 0;
-
- /// Use the SQL implementation to parse a query string and return details
- /// (name, type) about the columns.
- virtual Status getQueryColumns(const std::string& query,
- TableColumns& columns) const = 0;
-
- /// Given a query, return the list of scanned tables.
- virtual Status getQueryTables(const std::string& query,
- std::vector<std::string>& tables) const = 0;
-
- /**
- * @brief Attach a table at runtime.
- *
- * The SQL implementation plugin may need to manage how virtual tables are
- * attached at run time. In the case of SQLite where a single DB object is
- * managed, tables are enumerated and attached during initialization.
- */
- virtual Status attach(const std::string& /*name*/) {
- return Status::success();
- }
-
- /// Tables may be detached by name.
- virtual void detach(const std::string& /*name*/) {}
-
- public:
- Status call(const PluginRequest& request, PluginResponse& response) override;
+public:
+ /// Run a SQL query string against the SQL implementation.
+ virtual Status query(const std::string& query,
+ QueryData& results,
+ bool use_cache) const = 0;
+
+ /// Use the SQL implementation to parse a query string and return details
+ /// (name, type) about the columns.
+ virtual Status getQueryColumns(const std::string& query,
+ TableColumns& columns) const = 0;
+
+ /// Given a query, return the list of scanned tables.
+ virtual Status getQueryTables(const std::string& query,
+ std::vector<std::string>& tables) const = 0;
+
+ /**
+ * @brief Attach a table at runtime.
+ *
+ * The SQL implementation plugin may need to manage how virtual tables are
+ * attached at run time. In the case of SQLite where a single DB object is
+ * managed, tables are enumerated and attached during initialization.
+ */
+ virtual Status attach(const std::string& /*name*/)
+ {
+ return Status::success();
+ }
+
+ /// Tables may be detached by name.
+ virtual void detach(const std::string& /*name*/) {}
+
+public:
+ Status call(const PluginRequest& request, PluginResponse& response) override;
};
} // namespace osquery
namespace osquery {
-uint64_t Query::getPreviousEpoch() const {
- return 0;
+uint64_t Query::getPreviousEpoch() const
+{
+ return 0;
}
-uint64_t Query::getQueryCounter(bool new_query) const {
- return 0;
+uint64_t Query::getQueryCounter(bool new_query) const
+{
+ return 0;
}
-Status Query::getPreviousQueryResults(QueryDataSet& results) const {
- return Status::success();
+Status Query::getPreviousQueryResults(QueryDataSet& results) const
+{
+ return Status::success();
}
-std::vector<std::string> Query::getStoredQueryNames() {
- std::vector<std::string> results;
- return results;
+std::vector<std::string> Query::getStoredQueryNames()
+{
+ std::vector<std::string> results;
+ return results;
}
-bool Query::isQueryNameInDatabase() const {
- auto names = Query::getStoredQueryNames();
- return std::find(names.begin(), names.end(), name_) != names.end();
+bool Query::isQueryNameInDatabase() const
+{
+ auto names = Query::getStoredQueryNames();
+ return std::find(names.begin(), names.end(), name_) != names.end();
}
static inline void saveQuery(const std::string& name,
- const std::string& query) {
+ const std::string& query)
+{
}
-bool Query::isNewQuery() const {
- return true;
+bool Query::isNewQuery() const
+{
+ return true;
}
Status Query::addNewResults(QueryDataTyped qd,
- const uint64_t epoch,
- uint64_t& counter) const {
- DiffResults dr;
- return addNewResults(std::move(qd), epoch, counter, dr, false);
+ const uint64_t epoch,
+ uint64_t& counter) const
+{
+ DiffResults dr;
+ return addNewResults(std::move(qd), epoch, counter, dr, false);
}
Status Query::addNewResults(QueryDataTyped current_qd,
- const uint64_t current_epoch,
- uint64_t& counter,
- DiffResults& dr,
- bool calculate_diff) const {
- // The current results are 'fresh' when not calculating a differential.
- bool fresh_results = !calculate_diff;
- bool new_query = false;
- if (!isQueryNameInDatabase()) {
- // This is the first encounter of the scheduled query.
- fresh_results = true;
- INFO(OSQUERY) << "Storing initial results for new scheduled query: " << name_;
- saveQuery(name_, query_);
- } else if (getPreviousEpoch() != current_epoch) {
- fresh_results = true;
- INFO(OSQUERY) << "New Epoch " << current_epoch << " for scheduled query "
- << name_;
- } else if (isNewQuery()) {
- // This query is 'new' in that the previous results may be invalid.
- new_query = true;
- INFO(OSQUERY) << "Scheduled query has been updated: " + name_;
- saveQuery(name_, query_);
- }
-
- // Use a 'target' avoid copying the query data when serializing and saving.
- // If a differential is requested and needed the target remains the original
- // query data, otherwise the content is moved to the differential's added set.
- const auto* target_gd = ¤t_qd;
- bool update_db = true;
- if (!fresh_results && calculate_diff) {
- // Get the rows from the last run of this query name.
- QueryDataSet previous_qd;
- auto status = getPreviousQueryResults(previous_qd);
- if (!status.ok()) {
- return status;
- }
-
- // Calculate the differential between previous and current query results.
- dr = diff(previous_qd, current_qd);
-
- update_db = (!dr.added.empty() || !dr.removed.empty());
- } else {
- dr.added = std::move(current_qd);
- target_gd = &dr.added;
- }
-
- return Status::success();
+ const uint64_t current_epoch,
+ uint64_t& counter,
+ DiffResults& dr,
+ bool calculate_diff) const
+{
+ // The current results are 'fresh' when not calculating a differential.
+ bool fresh_results = !calculate_diff;
+ bool new_query = false;
+ if (!isQueryNameInDatabase()) {
+ // This is the first encounter of the scheduled query.
+ fresh_results = true;
+ INFO(OSQUERY) << "Storing initial results for new scheduled query: " << name_;
+ saveQuery(name_, query_);
+ } else if (getPreviousEpoch() != current_epoch) {
+ fresh_results = true;
+ INFO(OSQUERY) << "New Epoch " << current_epoch << " for scheduled query "
+ << name_;
+ } else if (isNewQuery()) {
+ // This query is 'new' in that the previous results may be invalid.
+ new_query = true;
+ INFO(OSQUERY) << "Scheduled query has been updated: " + name_;
+ saveQuery(name_, query_);
+ }
+
+ // Use a 'target' avoid copying the query data when serializing and saving.
+ // If a differential is requested and needed the target remains the original
+ // query data, otherwise the content is moved to the differential's added set.
+ const auto* target_gd = ¤t_qd;
+ bool update_db = true;
+ if (!fresh_results && calculate_diff) {
+ // Get the rows from the last run of this query name.
+ QueryDataSet previous_qd;
+ auto status = getPreviousQueryResults(previous_qd);
+ if (!status.ok()) {
+ return status;
+ }
+
+ // Calculate the differential between previous and current query results.
+ dr = diff(previous_qd, current_qd);
+
+ update_db = (!dr.added.empty() || !dr.removed.empty());
+ } else {
+ dr.added = std::move(current_qd);
+ target_gd = &dr.added;
+ }
+
+ return Status::success();
}
}
namespace osquery {
const std::map<ColumnType, std::string> kColumnTypeNames = {
- {UNKNOWN_TYPE, "UNKNOWN"},
- {TEXT_TYPE, "TEXT"},
- {INTEGER_TYPE, "INTEGER"},
- {BIGINT_TYPE, "BIGINT"},
- {UNSIGNED_BIGINT_TYPE, "UNSIGNED BIGINT"},
- {DOUBLE_TYPE, "DOUBLE"},
- {BLOB_TYPE, "BLOB"},
+ {UNKNOWN_TYPE, "UNKNOWN"},
+ {TEXT_TYPE, "TEXT"},
+ {INTEGER_TYPE, "INTEGER"},
+ {BIGINT_TYPE, "BIGINT"},
+ {UNSIGNED_BIGINT_TYPE, "UNSIGNED BIGINT"},
+ {DOUBLE_TYPE, "DOUBLE"},
+ {BLOB_TYPE, "BLOB"},
};
}
* to communicate these subtleties to the user.
*/
enum class ColumnOptions {
- /// Default/no options.
- DEFAULT = 0,
-
- /// Treat this column as a primary key.
- INDEX = 1,
-
- /// This column MUST be included in the query predicate.
- REQUIRED = 2,
-
- /*
- * @brief This column is used to generate additional information.
- *
- * If this column is included in the query predicate, the table will generate
- * additional information. Consider the browser_plugins or shell history
- * tables: by default they list the plugins or history relative to the user
- * running the query. However, if the calling query specifies a UID explicitly
- * in the predicate, the meaning of the table changes and results for that
- * user are returned instead.
- */
- ADDITIONAL = 4,
-
- /*
- * @brief This column can be used to optimize the query.
- *
- * If this column is included in the query predicate, the table will generate
- * optimized information. Consider the system_controls table, a default filter
- * without a query predicate lists all of the keys. When a specific domain is
- * included in the predicate then the table will only issue syscalls/lookups
- * for that domain, greatly optimizing the time and utilization.
- *
- * This optimization does not mean the column is an index.
- */
- OPTIMIZED = 8,
-
- /// This column should be hidden from '*'' selects.
- HIDDEN = 16,
+ /// Default/no options.
+ DEFAULT = 0,
+
+ /// Treat this column as a primary key.
+ INDEX = 1,
+
+ /// This column MUST be included in the query predicate.
+ REQUIRED = 2,
+
+ /*
+ * @brief This column is used to generate additional information.
+ *
+ * If this column is included in the query predicate, the table will generate
+ * additional information. Consider the browser_plugins or shell history
+ * tables: by default they list the plugins or history relative to the user
+ * running the query. However, if the calling query specifies a UID explicitly
+ * in the predicate, the meaning of the table changes and results for that
+ * user are returned instead.
+ */
+ ADDITIONAL = 4,
+
+ /*
+ * @brief This column can be used to optimize the query.
+ *
+ * If this column is included in the query predicate, the table will generate
+ * optimized information. Consider the system_controls table, a default filter
+ * without a query predicate lists all of the keys. When a specific domain is
+ * included in the predicate then the table will only issue syscalls/lookups
+ * for that domain, greatly optimizing the time and utilization.
+ *
+ * This optimization does not mean the column is an index.
+ */
+ OPTIMIZED = 8,
+
+ /// This column should be hidden from '*'' selects.
+ HIDDEN = 16,
};
/// Treat column options as a set of flags.
-inline ColumnOptions operator|(ColumnOptions a, ColumnOptions b) {
- return static_cast<ColumnOptions>(static_cast<int>(a) | static_cast<int>(b));
+inline ColumnOptions operator|(ColumnOptions a, ColumnOptions b)
+{
+ return static_cast<ColumnOptions>(static_cast<int>(a) | static_cast<int>(b));
}
/// Treat column options as a set of flags.
-inline size_t operator&(ColumnOptions a, ColumnOptions b) {
- return static_cast<size_t>(a) & static_cast<size_t>(b);
+inline size_t operator&(ColumnOptions a, ColumnOptions b)
+{
+ return static_cast<size_t>(a) & static_cast<size_t>(b);
}
enum ColumnType {
- UNKNOWN_TYPE = 0,
- TEXT_TYPE,
- INTEGER_TYPE,
- BIGINT_TYPE,
- UNSIGNED_BIGINT_TYPE,
- DOUBLE_TYPE,
- BLOB_TYPE,
+ UNKNOWN_TYPE = 0,
+ TEXT_TYPE,
+ INTEGER_TYPE,
+ BIGINT_TYPE,
+ UNSIGNED_BIGINT_TYPE,
+ DOUBLE_TYPE,
+ BLOB_TYPE,
};
/// Map of type constant to the SQLite string-name representation.
/// Alias for an ordered list of column name and corresponding SQL type.
using TableColumns =
- std::vector<std::tuple<std::string, ColumnType, ColumnOptions>>;
+ std::vector<std::tuple<std::string, ColumnType, ColumnOptions>>;
/// Alias for map of column alias sets.
using ColumnAliasSet = std::map<std::string, std::set<std::string>>;
namespace osquery {
-DiffResults diff(QueryDataSet& old, QueryDataTyped& current) {
- DiffResults r;
+DiffResults diff(QueryDataSet& old, QueryDataTyped& current)
+{
+ DiffResults r;
- for (auto& i : current) {
- auto item = old.find(i);
- if (item != old.end()) {
- old.erase(item);
- } else {
- r.added.push_back(i);
- }
- }
+ for (auto& i : current) {
+ auto item = old.find(i);
+ if (item != old.end()) {
+ old.erase(item);
+ } else {
+ r.added.push_back(i);
+ }
+ }
- for (auto& i : old) {
- r.removed.push_back(std::move(i));
- }
+ for (auto& i : old) {
+ r.removed.push_back(std::move(i));
+ }
- return r;
+ return r;
}
} // namespace osquery
* "removed" subset of rows.
*/
struct DiffResults : private only_movable {
- public:
- /// vector of added rows
- QueryDataTyped added;
-
- /// vector of removed rows
- QueryDataTyped removed;
-
- DiffResults() {}
- DiffResults(DiffResults&&) = default;
- DiffResults& operator=(DiffResults&&) = default;
-
- /// equals operator
- bool operator==(const DiffResults& comp) const {
- return (comp.added == added) && (comp.removed == removed);
- }
-
- /// not equals operator
- bool operator!=(const DiffResults& comp) const {
- return !(*this == comp);
- }
+public:
+ /// vector of added rows
+ QueryDataTyped added;
+
+ /// vector of removed rows
+ QueryDataTyped removed;
+
+ DiffResults() {}
+ DiffResults(DiffResults&&) = default;
+ DiffResults& operator=(DiffResults&&) = default;
+
+ /// equals operator
+ bool operator==(const DiffResults& comp) const
+ {
+ return (comp.added == added) && (comp.removed == removed);
+ }
+
+ /// not equals operator
+ bool operator!=(const DiffResults& comp) const
+ {
+ return !(*this == comp);
+ }
};
/**
* attributes. Those attributes are represented in this data structure.
*/
struct ScheduledQuery : private only_movable {
- /// Name of the pack containing query
- std::string pack_name;
-
- /// Name of the query
- std::string name;
-
- /// The SQL query.
- std::string query;
-
- /// Owner of the query
- std::string oncall;
-
- /// How often the query should be executed, in second.
- size_t interval{0};
-
- /// A temporary splayed internal.
- size_t splayed_interval{0};
-
- /**
- * @brief Queries are blacklisted based on logic in the configuration.
- *
- * Most calls to inspect scheduled queries will abstract away the blacklisting
- * concept and only return non-blacklisted queries. The config may be asked
- * to return all queries, thus it is important to capture this optional data.
- */
- bool blacklisted{false};
-
- /// Set of query options.
- std::map<std::string, bool> options;
-
- ScheduledQuery(const std::string& pack_name,
- const std::string& name,
- const std::string& query)
- : pack_name(pack_name), name(name), query(query) {}
- ScheduledQuery() = default;
- ScheduledQuery(ScheduledQuery&&) = default;
- ScheduledQuery& operator=(ScheduledQuery&&) = default;
-
- /// equals operator
- bool operator==(const ScheduledQuery& comp) const {
- return (comp.query == query) && (comp.interval == interval);
- }
-
- /// not equals operator
- bool operator!=(const ScheduledQuery& comp) const {
- return !(*this == comp);
- }
+ /// Name of the pack containing query
+ std::string pack_name;
+
+ /// Name of the query
+ std::string name;
+
+ /// The SQL query.
+ std::string query;
+
+ /// Owner of the query
+ std::string oncall;
+
+ /// How often the query should be executed, in second.
+ size_t interval{0};
+
+ /// A temporary splayed internal.
+ size_t splayed_interval{0};
+
+ /**
+ * @brief Queries are blacklisted based on logic in the configuration.
+ *
+ * Most calls to inspect scheduled queries will abstract away the blacklisting
+ * concept and only return non-blacklisted queries. The config may be asked
+ * to return all queries, thus it is important to capture this optional data.
+ */
+ bool blacklisted{false};
+
+ /// Set of query options.
+ std::map<std::string, bool> options;
+
+ ScheduledQuery(const std::string& pack_name,
+ const std::string& name,
+ const std::string& query)
+ : pack_name(pack_name), name(name), query(query) {}
+ ScheduledQuery() = default;
+ ScheduledQuery(ScheduledQuery&&) = default;
+ ScheduledQuery& operator=(ScheduledQuery&&) = default;
+
+ /// equals operator
+ bool operator==(const ScheduledQuery& comp) const
+ {
+ return (comp.query == query) && (comp.interval == interval);
+ }
+
+ /// not equals operator
+ bool operator!=(const ScheduledQuery& comp) const
+ {
+ return !(*this == comp);
+ }
};
} // namespace osquery
* a map<string, string> or code generated with type-safe fields.
*/
class TableRow {
- public:
- TableRow() = default;
- virtual ~TableRow() {}
-
- /**
- * Output the rowid of the current row into pRowid, returning SQLITE_OK if
- * successful or SQLITE_ERROR if not.
- */
- virtual int get_rowid(sqlite_int64 default_value,
- sqlite_int64* pRowid) const = 0;
- /**
- * Invoke the appropriate sqlite3_result_xxx method for the given column, or
- * null if the value does not fit the column type.
- */
- virtual int get_column(sqlite3_context* ctx,
- sqlite3_vtab* pVtab,
- int col) = 0;
- /**
- * Clone this row.
- */
- virtual TableRowHolder clone() const = 0;
-
- /**
- * Convert this row to a string map.
- */
- virtual operator Row() const = 0;
-
- protected:
- TableRow(const TableRow&) = default;
- TableRow& operator=(const TableRow&) = default;
+public:
+ TableRow() = default;
+ virtual ~TableRow() {}
+
+ /**
+ * Output the rowid of the current row into pRowid, returning SQLITE_OK if
+ * successful or SQLITE_ERROR if not.
+ */
+ virtual int get_rowid(sqlite_int64 default_value,
+ sqlite_int64* pRowid) const = 0;
+ /**
+ * Invoke the appropriate sqlite3_result_xxx method for the given column, or
+ * null if the value does not fit the column type.
+ */
+ virtual int get_column(sqlite3_context* ctx,
+ sqlite3_vtab* pVtab,
+ int col) = 0;
+ /**
+ * Clone this row.
+ */
+ virtual TableRowHolder clone() const = 0;
+
+ /**
+ * Convert this row to a string map.
+ */
+ virtual operator Row() const = 0;
+
+protected:
+ TableRow(const TableRow&) = default;
+ TableRow& operator=(const TableRow&) = default;
};
} // namespace osquery
size_t TablePlugin::kCacheStep = 0;
Status TablePlugin::addExternal(const std::string& name,
- const PluginResponse& response) {
- // Attach the table.
- if (response.size() == 0) {
- // Invalid table route info.
- // Tables must broadcast their column information, this is used while the
- // core is deciding if the extension's route is valid.
- return Status(1, "Invalid route info");
- }
+ const PluginResponse& response)
+{
+ // Attach the table.
+ if (response.size() == 0) {
+ // Invalid table route info.
+ // Tables must broadcast their column information, this is used while the
+ // core is deciding if the extension's route is valid.
+ return Status(1, "Invalid route info");
+ }
- // Use the SQL registry to attach the name/definition.
- return Registry::call("sql", "sql", {{"action", "attach"}, {"table", name}});
+ // Use the SQL registry to attach the name/definition.
+ return Registry::call("sql", "sql", {{"action", "attach"}, {"table", name}});
}
-void TablePlugin::removeExternal(const std::string& name) {
- // Detach the table name.
- Registry::call("sql", "sql", {{"action", "detach"}, {"table", name}});
+void TablePlugin::removeExternal(const std::string& name)
+{
+ // Detach the table name.
+ Registry::call("sql", "sql", {{"action", "detach"}, {"table", name}});
}
void TablePlugin::setRequestFromContext(const QueryContext& context,
- PluginRequest& request) {
+ PluginRequest& request)
+{
vist::json::Json document;
vist::json::Array constraints;
DEBUG(OSQUERY) << "request context->" << request["context"];
}
-QueryContext TablePlugin::getContextFromRequest(const PluginRequest& request) const {
+QueryContext TablePlugin::getContextFromRequest(const PluginRequest& request) const
+{
QueryContext context;
if (request.count("context") == 0)
return context;
UsedColumns colsUsed;
Array array = document.get<Array>("colsUsed");
for (auto i = 0; i < array.size(); i++) {
- std::string name = array.at(i);
+ std::string name = array.at(i);
colsUsed.insert(name);
}
context.colsUsed = colsUsed;
}
- Array constraints = document.get<Array>("constraints");
+ Array constraints = document.get<Array>("constraints");
for (auto i = 0; i < constraints.size(); i++) {
auto constraint = Object::Create(constraints.at(i));
std::string name = constraint["name"];
}
UsedColumnsBitset TablePlugin::usedColumnsToBitset(
- const UsedColumns usedColumns) const {
- UsedColumnsBitset result;
-
- const auto columns = this->columns();
- const auto aliases = this->aliasedColumns();
- for (size_t i = 0; i < columns.size(); i++) {
- auto column_name = std::get<0>(columns[i]);
- const auto& aliased_name = aliases.find(column_name);
- if (aliased_name != aliases.end()) {
- column_name = aliased_name->second;
- }
- if (usedColumns.find(column_name) != usedColumns.end()) {
- result.set(i);
- }
- }
+ const UsedColumns usedColumns) const
+{
+ UsedColumnsBitset result;
+
+ const auto columns = this->columns();
+ const auto aliases = this->aliasedColumns();
+ for (size_t i = 0; i < columns.size(); i++) {
+ auto column_name = std::get<0>(columns[i]);
+ const auto& aliased_name = aliases.find(column_name);
+ if (aliased_name != aliases.end()) {
+ column_name = aliased_name->second;
+ }
+ if (usedColumns.find(column_name) != usedColumns.end()) {
+ result.set(i);
+ }
+ }
- return result;
+ return result;
}
Status TablePlugin::call(const PluginRequest& request,
- PluginResponse& response) {
- response.clear();
-
- // TablePlugin API calling requires an action.
- if (request.count("action") == 0) {
- return Status(1, "Table plugins must include a request action");
- }
-
- const auto& action = request.at("action");
-
- if (action == "generate") {
- auto context = getContextFromRequest(request);
- TableRows result = generate(context);
- response = tableRowsToPluginResponse(result);
- } else if (action == "delete") {
- auto context = getContextFromRequest(request);
- response = delete_(context, request);
- } else if (action == "insert") {
- auto context = getContextFromRequest(request);
- response = insert(context, request);
- } else if (action == "update") {
- auto context = getContextFromRequest(request);
- response = update(context, request);
- } else if (action == "columns") {
- response = routeInfo();
- } else {
- return Status(1, "Unknown table plugin action: " + action);
- }
-
- return Status::success();
-}
-
-std::string TablePlugin::columnDefinition(bool is_extension) const {
- return osquery::columnDefinition(columns(), is_extension);
-}
-
-PluginResponse TablePlugin::routeInfo() const {
- // Route info consists of the serialized column information.
- PluginResponse response;
- for (const auto& column : columns()) {
- response.push_back(
- {{"id", "column"},
- {"name", std::get<0>(column)},
- {"type", columnTypeName(std::get<1>(column))},
- {"op", INTEGER(static_cast<size_t>(std::get<2>(column)))}});
- }
- // Each table name alias is provided such that the core may add the views.
- // These views need to be removed when the backing table is detached.
- for (const auto& alias : aliases()) {
- response.push_back({{"id", "alias"}, {"alias", alias}});
- }
-
- // Each column alias must be provided, additionally to the column's option.
- // This sets up the value-replacement move within the SQL implementation.
- for (const auto& target : columnAliases()) {
- for (const auto& alias : target.second) {
- response.push_back(
- {{"id", "columnAlias"}, {"name", alias}, {"target", target.first}});
- }
- }
-
- response.push_back(
- {{"id", "attributes"},
- {"attributes", INTEGER(static_cast<size_t>(attributes()))}});
- return response;
-}
-
-static bool cacheAllowed(const TableColumns& cols, const QueryContext& ctx) {
- if (!ctx.useCache()) {
- // The query execution did not request use of the warm cache.
- return false;
- }
-
- auto uncachable = ColumnOptions::INDEX | ColumnOptions::REQUIRED |
- ColumnOptions::ADDITIONAL | ColumnOptions::OPTIMIZED;
- for (const auto& column : cols) {
- auto opts = std::get<2>(column) & uncachable;
- if (opts && ctx.constraints.at(std::get<0>(column)).exists()) {
- return false;
- }
- }
- return true;
-}
-
-bool TablePlugin::isCached(size_t step, const QueryContext& ctx) const {
- // Perform the step comparison first, because it's easy.
- return (step < last_cached_ + last_interval_ && cacheAllowed(columns(), ctx));
-}
-
-TableRows TablePlugin::getCache() const {
- TableRows results;
- return results;
+ PluginResponse& response)
+{
+ response.clear();
+
+ // TablePlugin API calling requires an action.
+ if (request.count("action") == 0) {
+ return Status(1, "Table plugins must include a request action");
+ }
+
+ const auto& action = request.at("action");
+
+ if (action == "generate") {
+ auto context = getContextFromRequest(request);
+ TableRows result = generate(context);
+ response = tableRowsToPluginResponse(result);
+ } else if (action == "delete") {
+ auto context = getContextFromRequest(request);
+ response = delete_(context, request);
+ } else if (action == "insert") {
+ auto context = getContextFromRequest(request);
+ response = insert(context, request);
+ } else if (action == "update") {
+ auto context = getContextFromRequest(request);
+ response = update(context, request);
+ } else if (action == "columns") {
+ response = routeInfo();
+ } else {
+ return Status(1, "Unknown table plugin action: " + action);
+ }
+
+ return Status::success();
+}
+
+std::string TablePlugin::columnDefinition(bool is_extension) const
+{
+ return osquery::columnDefinition(columns(), is_extension);
+}
+
+PluginResponse TablePlugin::routeInfo() const
+{
+ // Route info consists of the serialized column information.
+ PluginResponse response;
+ for (const auto& column : columns()) {
+ response.push_back({
+ {"id", "column"},
+ {"name", std::get<0>(column)},
+ {"type", columnTypeName(std::get<1>(column))},
+ {"op", INTEGER(static_cast<size_t>(std::get<2>(column)))}});
+ }
+ // Each table name alias is provided such that the core may add the views.
+ // These views need to be removed when the backing table is detached.
+ for (const auto& alias : aliases()) {
+ response.push_back({{"id", "alias"}, {"alias", alias}});
+ }
+
+ // Each column alias must be provided, additionally to the column's option.
+ // This sets up the value-replacement move within the SQL implementation.
+ for (const auto& target : columnAliases()) {
+ for (const auto& alias : target.second) {
+ response.push_back(
+ {{"id", "columnAlias"}, {"name", alias}, {"target", target.first}});
+ }
+ }
+
+ response.push_back({
+ {"id", "attributes"},
+ {"attributes", INTEGER(static_cast<size_t>(attributes()))}});
+ return response;
+}
+
+static bool cacheAllowed(const TableColumns& cols, const QueryContext& ctx)
+{
+ if (!ctx.useCache()) {
+ // The query execution did not request use of the warm cache.
+ return false;
+ }
+
+ auto uncachable = ColumnOptions::INDEX | ColumnOptions::REQUIRED |
+ ColumnOptions::ADDITIONAL | ColumnOptions::OPTIMIZED;
+ for (const auto& column : cols) {
+ auto opts = std::get<2>(column) & uncachable;
+ if (opts && ctx.constraints.at(std::get<0>(column)).exists()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool TablePlugin::isCached(size_t step, const QueryContext& ctx) const
+{
+ // Perform the step comparison first, because it's easy.
+ return (step < last_cached_ + last_interval_ && cacheAllowed(columns(), ctx));
+}
+
+TableRows TablePlugin::getCache() const
+{
+ TableRows results;
+ return results;
}
void TablePlugin::setCache(size_t step,
- size_t interval,
- const QueryContext& ctx,
- const TableRows& results) {
- return;
-}
-
-std::string columnDefinition(const TableColumns& columns, bool is_extension) {
- std::map<std::string, bool> epilog;
- bool indexed = false;
- std::vector<std::string> pkeys;
-
- std::string statement = "(";
- for (size_t i = 0; i < columns.size(); ++i) {
- const auto& column = columns.at(i);
- statement +=
- '`' + std::get<0>(column) + "` " + columnTypeName(std::get<1>(column));
- auto& options = std::get<2>(column);
- if (options & (ColumnOptions::INDEX | ColumnOptions::ADDITIONAL)) {
- if (options & ColumnOptions::INDEX) {
- indexed = true;
- }
- pkeys.push_back(std::get<0>(column));
- epilog["WITHOUT ROWID"] = true;
- }
- if (options & ColumnOptions::HIDDEN) {
- statement += " HIDDEN";
- }
- if (i < columns.size() - 1) {
- statement += ", ";
- }
- }
-
- // If there are only 'additional' columns (rare), do not attempt a pkey.
- if (!indexed) {
- epilog["WITHOUT ROWID"] = false;
- pkeys.clear();
- }
-
- // Append the primary keys, if any were defined.
- if (!pkeys.empty()) {
- statement += ", PRIMARY KEY (";
- for (auto pkey = pkeys.begin(); pkey != pkeys.end();) {
- statement += '`' + std::move(*pkey) + '`';
- if (++pkey != pkeys.end()) {
- statement += ", ";
- }
- }
- statement += ')';
- }
-
- // Tables implemented by extension can be made read/write; make sure to always
- // keep the rowid column, as we need it to reference rows when handling UPDATE
- // and DELETE queries
- if (is_extension) {
- epilog["WITHOUT ROWID"] = false;
- }
-
- statement += ')';
- for (auto& ei : epilog) {
- if (ei.second) {
- statement += ' ' + std::move(ei.first);
- }
- }
- return statement;
+ size_t interval,
+ const QueryContext& ctx,
+ const TableRows& results)
+{
+ return;
+}
+
+std::string columnDefinition(const TableColumns& columns, bool is_extension)
+{
+ std::map<std::string, bool> epilog;
+ bool indexed = false;
+ std::vector<std::string> pkeys;
+
+ std::string statement = "(";
+ for (size_t i = 0; i < columns.size(); ++i) {
+ const auto& column = columns.at(i);
+ statement +=
+ '`' + std::get<0>(column) + "` " + columnTypeName(std::get<1>(column));
+ auto& options = std::get<2>(column);
+ if (options & (ColumnOptions::INDEX | ColumnOptions::ADDITIONAL)) {
+ if (options & ColumnOptions::INDEX) {
+ indexed = true;
+ }
+ pkeys.push_back(std::get<0>(column));
+ epilog["WITHOUT ROWID"] = true;
+ }
+ if (options & ColumnOptions::HIDDEN) {
+ statement += " HIDDEN";
+ }
+ if (i < columns.size() - 1) {
+ statement += ", ";
+ }
+ }
+
+ // If there are only 'additional' columns (rare), do not attempt a pkey.
+ if (!indexed) {
+ epilog["WITHOUT ROWID"] = false;
+ pkeys.clear();
+ }
+
+ // Append the primary keys, if any were defined.
+ if (!pkeys.empty()) {
+ statement += ", PRIMARY KEY (";
+ for (auto pkey = pkeys.begin(); pkey != pkeys.end();) {
+ statement += '`' + std::move(*pkey) + '`';
+ if (++pkey != pkeys.end()) {
+ statement += ", ";
+ }
+ }
+ statement += ')';
+ }
+
+ // Tables implemented by extension can be made read/write; make sure to always
+ // keep the rowid column, as we need it to reference rows when handling UPDATE
+ // and DELETE queries
+ if (is_extension) {
+ epilog["WITHOUT ROWID"] = false;
+ }
+
+ statement += ')';
+ for (auto& ei : epilog) {
+ if (ei.second) {
+ statement += ' ' + std::move(ei.first);
+ }
+ }
+ return statement;
}
std::string columnDefinition(const PluginResponse& response,
- bool aliases,
- bool is_extension) {
- TableColumns columns;
- // Maintain a map of column to the type, for alias type lookups.
- std::map<std::string, ColumnType> column_types;
- for (const auto& column : response) {
- auto id = column.find("id");
- if (id == column.end()) {
- continue;
- }
-
- auto cname = column.find("name");
- auto ctype = column.find("type");
- if (id->second == "column" && cname != column.end() &&
- ctype != column.end()) {
- auto options = ColumnOptions::DEFAULT;
-
- auto cop = column.find("op");
- if (cop != column.end()) {
- auto op = tryTo<int>(cop->second);
- if (op) {
- options = static_cast<ColumnOptions>(op.take());
- }
- }
- auto column_type = columnTypeName(ctype->second);
- columns.push_back(make_tuple(cname->second, column_type, options));
- if (aliases) {
- column_types[cname->second] = column_type;
- }
- } else if (id->second == "columnAlias" && cname != column.end() &&
- aliases) {
- auto ctarget = column.find("target");
- if (ctarget != column.end()) {
- auto target_ctype = column_types.find(ctarget->second);
- if (target_ctype != column_types.end()) {
- columns.push_back(make_tuple(
- cname->second, target_ctype->second, ColumnOptions::HIDDEN));
- }
- }
- }
- }
- return columnDefinition(columns, is_extension);
-}
-
-ColumnType columnTypeName(const std::string& type) {
- for (const auto& col : kColumnTypeNames) {
- if (col.second == type) {
- return col.first;
- }
- }
- return UNKNOWN_TYPE;
-}
-
-bool ConstraintList::exists(const ConstraintOperatorFlag ops) const {
- if (ops == ANY_OP) {
- return (constraints_.size() > 0);
- } else {
- for (const struct Constraint& c : constraints_) {
- if (c.op & ops) {
- return true;
- }
- }
- return false;
- }
-}
-
-bool ConstraintList::matches(const std::string& expr) const {
- // Support each SQL affinity type casting.
- if (affinity == TEXT_TYPE) {
- return literal_matches<TEXT_LITERAL>(expr);
- } else if (affinity == INTEGER_TYPE) {
- auto lexpr = tryTo<INTEGER_LITERAL>(expr);
- if (lexpr) {
- return literal_matches<INTEGER_LITERAL>(lexpr.take());
- }
- } else if (affinity == BIGINT_TYPE) {
- auto lexpr = tryTo<BIGINT_LITERAL>(expr);
- if (lexpr) {
- return literal_matches<BIGINT_LITERAL>(lexpr.take());
- }
- } else if (affinity == UNSIGNED_BIGINT_TYPE) {
- auto lexpr = tryTo<UNSIGNED_BIGINT_LITERAL>(expr);
- if (lexpr) {
- return literal_matches<UNSIGNED_BIGINT_LITERAL>(lexpr.take());
- }
- }
-
- return false;
+ bool aliases,
+ bool is_extension)
+{
+ TableColumns columns;
+ // Maintain a map of column to the type, for alias type lookups.
+ std::map<std::string, ColumnType> column_types;
+ for (const auto& column : response) {
+ auto id = column.find("id");
+ if (id == column.end()) {
+ continue;
+ }
+
+ auto cname = column.find("name");
+ auto ctype = column.find("type");
+ if (id->second == "column" && cname != column.end() &&
+ ctype != column.end()) {
+ auto options = ColumnOptions::DEFAULT;
+
+ auto cop = column.find("op");
+ if (cop != column.end()) {
+ auto op = tryTo<int>(cop->second);
+ if (op) {
+ options = static_cast<ColumnOptions>(op.take());
+ }
+ }
+ auto column_type = columnTypeName(ctype->second);
+ columns.push_back(make_tuple(cname->second, column_type, options));
+ if (aliases) {
+ column_types[cname->second] = column_type;
+ }
+ } else if (id->second == "columnAlias" && cname != column.end() &&
+ aliases) {
+ auto ctarget = column.find("target");
+ if (ctarget != column.end()) {
+ auto target_ctype = column_types.find(ctarget->second);
+ if (target_ctype != column_types.end()) {
+ columns.push_back(make_tuple(
+ cname->second, target_ctype->second, ColumnOptions::HIDDEN));
+ }
+ }
+ }
+ }
+ return columnDefinition(columns, is_extension);
+}
+
+ColumnType columnTypeName(const std::string& type)
+{
+ for (const auto& col : kColumnTypeNames) {
+ if (col.second == type) {
+ return col.first;
+ }
+ }
+ return UNKNOWN_TYPE;
+}
+
+bool ConstraintList::exists(const ConstraintOperatorFlag ops) const
+{
+ if (ops == ANY_OP) {
+ return (constraints_.size() > 0);
+ } else {
+ for (const struct Constraint& c : constraints_) {
+ if (c.op & ops) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+bool ConstraintList::matches(const std::string& expr) const
+{
+ // Support each SQL affinity type casting.
+ if (affinity == TEXT_TYPE) {
+ return literal_matches<TEXT_LITERAL>(expr);
+ } else if (affinity == INTEGER_TYPE) {
+ auto lexpr = tryTo<INTEGER_LITERAL>(expr);
+ if (lexpr) {
+ return literal_matches<INTEGER_LITERAL>(lexpr.take());
+ }
+ } else if (affinity == BIGINT_TYPE) {
+ auto lexpr = tryTo<BIGINT_LITERAL>(expr);
+ if (lexpr) {
+ return literal_matches<BIGINT_LITERAL>(lexpr.take());
+ }
+ } else if (affinity == UNSIGNED_BIGINT_TYPE) {
+ auto lexpr = tryTo<UNSIGNED_BIGINT_LITERAL>(expr);
+ if (lexpr) {
+ return literal_matches<UNSIGNED_BIGINT_LITERAL>(lexpr.take());
+ }
+ }
+
+ return false;
}
template <typename T>
-bool ConstraintList::literal_matches(const T& base_expr) const {
- bool aggregate = true;
- for (size_t i = 0; i < constraints_.size(); ++i) {
- auto constraint_expr = tryTo<T>(constraints_[i].expr);
- if (!constraint_expr) {
- // Cannot cast input constraint to column type.
- return false;
- }
- if (constraints_[i].op == EQUALS) {
- aggregate = aggregate && (base_expr == constraint_expr.take());
- } else if (constraints_[i].op == GREATER_THAN) {
- aggregate = aggregate && (base_expr > constraint_expr.take());
- } else if (constraints_[i].op == LESS_THAN) {
- aggregate = aggregate && (base_expr < constraint_expr.take());
- } else if (constraints_[i].op == GREATER_THAN_OR_EQUALS) {
- aggregate = aggregate && (base_expr >= constraint_expr.take());
- } else if (constraints_[i].op == LESS_THAN_OR_EQUALS) {
- aggregate = aggregate && (base_expr <= constraint_expr.take());
- } else {
- // Unsupported constraint. Should match every thing.
- return true;
- }
- if (!aggregate) {
- // Speed up comparison.
- return false;
- }
- }
- return true;
-}
-
-std::set<std::string> ConstraintList::getAll(ConstraintOperator op) const {
- std::set<std::string> set;
- for (size_t i = 0; i < constraints_.size(); ++i) {
- if (constraints_[i].op == op) {
- // TODO: this does not apply a distinct.
- set.insert(constraints_[i].expr);
- }
- }
- return set;
+bool ConstraintList::literal_matches(const T& base_expr) const
+{
+ bool aggregate = true;
+ for (size_t i = 0; i < constraints_.size(); ++i) {
+ auto constraint_expr = tryTo<T>(constraints_[i].expr);
+ if (!constraint_expr) {
+ // Cannot cast input constraint to column type.
+ return false;
+ }
+ if (constraints_[i].op == EQUALS) {
+ aggregate = aggregate && (base_expr == constraint_expr.take());
+ } else if (constraints_[i].op == GREATER_THAN) {
+ aggregate = aggregate && (base_expr > constraint_expr.take());
+ } else if (constraints_[i].op == LESS_THAN) {
+ aggregate = aggregate && (base_expr < constraint_expr.take());
+ } else if (constraints_[i].op == GREATER_THAN_OR_EQUALS) {
+ aggregate = aggregate && (base_expr >= constraint_expr.take());
+ } else if (constraints_[i].op == LESS_THAN_OR_EQUALS) {
+ aggregate = aggregate && (base_expr <= constraint_expr.take());
+ } else {
+ // Unsupported constraint. Should match every thing.
+ return true;
+ }
+ if (!aggregate) {
+ // Speed up comparison.
+ return false;
+ }
+ }
+ return true;
+}
+
+std::set<std::string> ConstraintList::getAll(ConstraintOperator op) const
+{
+ std::set<std::string> set;
+ for (size_t i = 0; i < constraints_.size(); ++i) {
+ if (constraints_[i].op == op) {
+ // TODO: this does not apply a distinct.
+ set.insert(constraints_[i].expr);
+ }
+ }
+ return set;
}
template <typename T>
-std::set<T> ConstraintList::getAll(ConstraintOperator /* op */) const {
- std::set<T> cs;
- for (const auto& item : constraints_) {
- auto exp = tryTo<T>(item.expr);
- if (exp) {
- cs.insert(exp.take());
- }
- }
- return cs;
+std::set<T> ConstraintList::getAll(ConstraintOperator /* op */) const
+{
+ std::set<T> cs;
+ for (const auto& item : constraints_) {
+ auto exp = tryTo<T>(item.expr);
+ if (exp) {
+ cs.insert(exp.take());
+ }
+ }
+ return cs;
}
template <>
-std::set<std::string> ConstraintList::getAll(ConstraintOperator op) const {
- return getAll(op);
+std::set<std::string> ConstraintList::getAll(ConstraintOperator op) const
+{
+ return getAll(op);
}
/// Explicit getAll for INTEGER.
template std::set<INTEGER_LITERAL> ConstraintList::getAll<int>(
- ConstraintOperator) const;
+ ConstraintOperator) const;
/// Explicit getAll for BIGINT.
template std::set<long long> ConstraintList::getAll<long long>(
- ConstraintOperator) const;
+ ConstraintOperator) const;
/// Explicit getAll for UNSIGNED_BIGINT.
template std::set<unsigned long long>
- ConstraintList::getAll<unsigned long long>(ConstraintOperator) const;
+ConstraintList::getAll<unsigned long long>(ConstraintOperator) const;
-vist::json::Object ConstraintList::serialize() const {
+vist::json::Object ConstraintList::serialize() const
+{
// format: { "affinity": "TEXT", "list": [ { "expr": "1", "op": 2 } ] }
vist::json::Array list;
for (const auto& constraint : constraints_) {
return object;
}
-void ConstraintList::deserialize(vist::json::Object& obj) {
+void ConstraintList::deserialize(vist::json::Object& obj)
+{
using namespace vist::json;
Array list = obj.get<Array>("list");
for (auto i = 0; i < list.size(); i++) {
auto element = vist::json::Object::Create(list.at(i));
- int op = element["op"];
+ int op = element["op"];
Constraint constraint(static_cast<unsigned char>(op));
constraint.expr = static_cast<std::string>(element["expr"]);
this->constraints_.emplace_back(std::move(constraint));
this->affinity = columnTypeName(name);
}
-bool QueryContext::isColumnUsed(const std::string& colName) const {
- return !colsUsed || colsUsed->find(colName) != colsUsed->end();
+bool QueryContext::isColumnUsed(const std::string& colName) const
+{
+ return !colsUsed || colsUsed->find(colName) != colsUsed->end();
}
bool QueryContext::isAnyColumnUsed(
- std::initializer_list<std::string> colNames) const {
- for (auto& colName : colNames) {
- if (isColumnUsed(colName)) {
- return true;
- }
- }
- return false;
+ std::initializer_list<std::string> colNames) const
+{
+ for (auto& colName : colNames) {
+ if (isColumnUsed(colName)) {
+ return true;
+ }
+ }
+ return false;
}
-void QueryContext::useCache(bool use_cache) {
- use_cache_ = use_cache;
+void QueryContext::useCache(bool use_cache)
+{
+ use_cache_ = use_cache;
}
-bool QueryContext::useCache() const {
- return use_cache_;
+bool QueryContext::useCache() const
+{
+ return use_cache_;
}
void QueryContext::setCache(const std::string& index,
- const TableRowHolder& cache) {
- table_->cache[index] = cache->clone();
+ const TableRowHolder& cache)
+{
+ table_->cache[index] = cache->clone();
}
-bool QueryContext::isCached(const std::string& index) const {
- return (table_->cache.count(index) != 0);
+bool QueryContext::isCached(const std::string& index) const
+{
+ return (table_->cache.count(index) != 0);
}
-TableRowHolder QueryContext::getCache(const std::string& index) {
- return table_->cache[index]->clone();
+TableRowHolder QueryContext::getCache(const std::string& index)
+{
+ return table_->cache[index]->clone();
}
bool QueryContext::hasConstraint(const std::string& column,
- ConstraintOperator op) const {
- if (constraints.count(column) == 0) {
- return false;
- }
- return constraints.at(column).exists(op);
+ ConstraintOperator op) const
+{
+ if (constraints.count(column) == 0) {
+ return false;
+ }
+ return constraints.at(column).exists(op);
}
Status QueryContext::expandConstraints(
- const std::string& column,
- ConstraintOperator op,
- std::set<std::string>& output,
- std::function<Status(const std::string& constraint,
- std::set<std::string>& output)> predicate) {
- for (const auto& constraint : constraints[column].getAll(op)) {
- auto status = predicate(constraint, output);
- if (!status) {
- return status;
- }
- }
- return Status(0);
+ const std::string& column,
+ ConstraintOperator op,
+ std::set<std::string>& output,
+ std::function<Status(const std::string& constraint,
+ std::set<std::string>& output)> predicate)
+{
+ for (const auto& constraint : constraints[column].getAll(op)) {
+ auto status = predicate(constraint, output);
+ if (!status) {
+ return status;
+ }
+ }
+ return Status(0);
}
} // namespace osquery
DECLARE_bool(disable_database);
DECLARE_bool(log_numerics_as_numbers);
class QueryTests : public testing::Test {
- public:
- QueryTests() {
- registryAndPluginInit();
- FLAGS_disable_database = true;
- DatabasePlugin::setAllowOpen(true);
- DatabasePlugin::initPlugin();
- }
+public:
+ QueryTests()
+ {
+ registryAndPluginInit();
+ FLAGS_disable_database = true;
+ DatabasePlugin::setAllowOpen(true);
+ DatabasePlugin::initPlugin();
+ }
};
-TEST_F(QueryTests, test_private_members) {
- auto query = getOsqueryScheduledQuery();
- auto cf = Query("foobar", query);
- EXPECT_EQ(cf.query_, query.query);
+TEST_F(QueryTests, test_private_members)
+{
+ auto query = getOsqueryScheduledQuery();
+ auto cf = Query("foobar", query);
+ EXPECT_EQ(cf.query_, query.query);
}
-TEST_F(QueryTests, test_add_and_get_current_results) {
- FLAGS_log_numerics_as_numbers = true;
- // Test adding a "current" set of results to a scheduled query instance.
- auto query = getOsqueryScheduledQuery();
- auto cf = Query("foobar", query);
- uint64_t counter = 128;
- auto status = cf.addNewResults(getTestDBExpectedResults(), 0, counter);
- EXPECT_TRUE(status.ok());
- EXPECT_EQ(status.toString(), "OK");
- EXPECT_EQ(counter, 0UL);
-
- // Simulate results from several schedule runs, calculate differentials.
- uint64_t expected_counter = counter + 1;
- for (auto result : getTestDBResultStream()) {
- // Get the results from the previous query execution (from the DB).
- QueryDataSet previous_qd;
- status = cf.getPreviousQueryResults(previous_qd);
- EXPECT_TRUE(status.ok());
- EXPECT_EQ(status.toString(), "OK");
-
- // Add the "current" results and output the differentials.
- DiffResults dr;
- counter = 128;
- auto s = cf.addNewResults(result.second, 0, counter, dr, true);
- EXPECT_TRUE(s.ok());
- EXPECT_EQ(counter, expected_counter++);
-
- // Call the diffing utility directly.
- DiffResults expected = diff(previous_qd, result.second);
- EXPECT_EQ(dr, expected);
-
- // After Query::addNewResults the previous results are now current.
- QueryDataSet qds_previous;
- cf.getPreviousQueryResults(qds_previous);
-
- QueryDataSet qds;
- for (auto& i : result.second) {
- qds.insert(i);
- }
-
- EXPECT_EQ(qds_previous, qds);
- }
+TEST_F(QueryTests, test_add_and_get_current_results)
+{
+ FLAGS_log_numerics_as_numbers = true;
+ // Test adding a "current" set of results to a scheduled query instance.
+ auto query = getOsqueryScheduledQuery();
+ auto cf = Query("foobar", query);
+ uint64_t counter = 128;
+ auto status = cf.addNewResults(getTestDBExpectedResults(), 0, counter);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(status.toString(), "OK");
+ EXPECT_EQ(counter, 0UL);
+
+ // Simulate results from several schedule runs, calculate differentials.
+ uint64_t expected_counter = counter + 1;
+ for (auto result : getTestDBResultStream()) {
+ // Get the results from the previous query execution (from the DB).
+ QueryDataSet previous_qd;
+ status = cf.getPreviousQueryResults(previous_qd);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(status.toString(), "OK");
+
+ // Add the "current" results and output the differentials.
+ DiffResults dr;
+ counter = 128;
+ auto s = cf.addNewResults(result.second, 0, counter, dr, true);
+ EXPECT_TRUE(s.ok());
+ EXPECT_EQ(counter, expected_counter++);
+
+ // Call the diffing utility directly.
+ DiffResults expected = diff(previous_qd, result.second);
+ EXPECT_EQ(dr, expected);
+
+ // After Query::addNewResults the previous results are now current.
+ QueryDataSet qds_previous;
+ cf.getPreviousQueryResults(qds_previous);
+
+ QueryDataSet qds;
+ for (auto& i : result.second) {
+ qds.insert(i);
+ }
+
+ EXPECT_EQ(qds_previous, qds);
+ }
}
-TEST_F(QueryTests, test_get_query_results) {
- // Grab an expected set of query data and add it as the previous result.
- auto encoded_qd = getSerializedQueryDataJSON();
- auto query = getOsqueryScheduledQuery();
- auto status = setDatabaseValue(kQueries, "foobar", encoded_qd.first);
- EXPECT_TRUE(status.ok());
-
- // Use the Query retrieval API to check the now "previous" result.
- QueryDataSet previous_qd;
- auto cf = Query("foobar", query);
- status = cf.getPreviousQueryResults(previous_qd);
- EXPECT_TRUE(status.ok());
+TEST_F(QueryTests, test_get_query_results)
+{
+ // Grab an expected set of query data and add it as the previous result.
+ auto encoded_qd = getSerializedQueryDataJSON();
+ auto query = getOsqueryScheduledQuery();
+ auto status = setDatabaseValue(kQueries, "foobar", encoded_qd.first);
+ EXPECT_TRUE(status.ok());
+
+ // Use the Query retrieval API to check the now "previous" result.
+ QueryDataSet previous_qd;
+ auto cf = Query("foobar", query);
+ status = cf.getPreviousQueryResults(previous_qd);
+ EXPECT_TRUE(status.ok());
}
-TEST_F(QueryTests, test_query_name_not_found_in_db) {
- // Try to retrieve results from a query that has not executed.
- QueryDataSet previous_qd;
- auto query = getOsqueryScheduledQuery();
- auto cf = Query("not_a_real_query", query);
- auto status = cf.getPreviousQueryResults(previous_qd);
- EXPECT_FALSE(status.ok());
- EXPECT_TRUE(previous_qd.empty());
+TEST_F(QueryTests, test_query_name_not_found_in_db)
+{
+ // Try to retrieve results from a query that has not executed.
+ QueryDataSet previous_qd;
+ auto query = getOsqueryScheduledQuery();
+ auto cf = Query("not_a_real_query", query);
+ auto status = cf.getPreviousQueryResults(previous_qd);
+ EXPECT_FALSE(status.ok());
+ EXPECT_TRUE(previous_qd.empty());
}
-TEST_F(QueryTests, test_is_query_name_in_database) {
- auto query = getOsqueryScheduledQuery();
- auto cf = Query("foobar", query);
- auto encoded_qd = getSerializedQueryDataJSON();
- auto status = setDatabaseValue(kQueries, "foobar", encoded_qd.first);
- EXPECT_TRUE(status.ok());
- // Now test that the query name exists.
- EXPECT_TRUE(cf.isQueryNameInDatabase());
+TEST_F(QueryTests, test_is_query_name_in_database)
+{
+ auto query = getOsqueryScheduledQuery();
+ auto cf = Query("foobar", query);
+ auto encoded_qd = getSerializedQueryDataJSON();
+ auto status = setDatabaseValue(kQueries, "foobar", encoded_qd.first);
+ EXPECT_TRUE(status.ok());
+ // Now test that the query name exists.
+ EXPECT_TRUE(cf.isQueryNameInDatabase());
}
-TEST_F(QueryTests, test_query_name_updated) {
- // Try to retrieve results from a query that has not executed.
- QueryDataSet previous_qd;
- auto query = getOsqueryScheduledQuery();
- auto cf = Query("will_update_query", query);
- EXPECT_TRUE(cf.isNewQuery());
- EXPECT_TRUE(cf.isNewQuery());
-
- DiffResults dr;
- uint64_t counter = 128;
- auto results = getTestDBExpectedResults();
- cf.addNewResults(results, 0, counter, dr);
- EXPECT_FALSE(cf.isNewQuery());
- EXPECT_EQ(counter, 0UL);
-
- query.query += " LIMIT 1";
- counter = 128;
- auto cf2 = Query("will_update_query", query);
- EXPECT_TRUE(cf2.isQueryNameInDatabase());
- EXPECT_TRUE(cf2.isNewQuery());
- cf2.addNewResults(results, 0, counter, dr);
- EXPECT_FALSE(cf2.isNewQuery());
- EXPECT_EQ(counter, 0UL);
+TEST_F(QueryTests, test_query_name_updated)
+{
+ // Try to retrieve results from a query that has not executed.
+ QueryDataSet previous_qd;
+ auto query = getOsqueryScheduledQuery();
+ auto cf = Query("will_update_query", query);
+ EXPECT_TRUE(cf.isNewQuery());
+ EXPECT_TRUE(cf.isNewQuery());
+
+ DiffResults dr;
+ uint64_t counter = 128;
+ auto results = getTestDBExpectedResults();
+ cf.addNewResults(results, 0, counter, dr);
+ EXPECT_FALSE(cf.isNewQuery());
+ EXPECT_EQ(counter, 0UL);
+
+ query.query += " LIMIT 1";
+ counter = 128;
+ auto cf2 = Query("will_update_query", query);
+ EXPECT_TRUE(cf2.isQueryNameInDatabase());
+ EXPECT_TRUE(cf2.isNewQuery());
+ cf2.addNewResults(results, 0, counter, dr);
+ EXPECT_FALSE(cf2.isNewQuery());
+ EXPECT_EQ(counter, 0UL);
}
-TEST_F(QueryTests, test_get_stored_query_names) {
- auto query = getOsqueryScheduledQuery();
- auto cf = Query("foobar", query);
- auto encoded_qd = getSerializedQueryDataJSON();
- auto status = setDatabaseValue(kQueries, "foobar", encoded_qd.first);
- EXPECT_TRUE(status.ok());
-
- // Stored query names is a factory method included alongside every query.
- // It will include the set of query names with existing "previous" results.
- auto names = cf.getStoredQueryNames();
- auto in_vector = std::find(names.begin(), names.end(), "foobar");
- EXPECT_NE(in_vector, names.end());
+TEST_F(QueryTests, test_get_stored_query_names)
+{
+ auto query = getOsqueryScheduledQuery();
+ auto cf = Query("foobar", query);
+ auto encoded_qd = getSerializedQueryDataJSON();
+ auto status = setDatabaseValue(kQueries, "foobar", encoded_qd.first);
+ EXPECT_TRUE(status.ok());
+
+ // Stored query names is a factory method included alongside every query.
+ // It will include the set of query names with existing "previous" results.
+ auto names = cf.getStoredQueryNames();
+ auto in_vector = std::find(names.begin(), names.end(), "foobar");
+ EXPECT_NE(in_vector, names.end());
}
}
class TablesTests : public testing::Test {
protected:
- void SetUp() {
- Initializer::platformSetup();
- registryAndPluginInit();
- FLAGS_disable_database = true;
- DatabasePlugin::setAllowOpen(true);
- DatabasePlugin::initPlugin();
- }
+ void SetUp()
+ {
+ Initializer::platformSetup();
+ registryAndPluginInit();
+ FLAGS_disable_database = true;
+ DatabasePlugin::setAllowOpen(true);
+ DatabasePlugin::initPlugin();
+ }
};
-TEST_F(TablesTests, test_constraint) {
- auto constraint = Constraint(EQUALS);
- constraint.expr = "none";
+TEST_F(TablesTests, test_constraint)
+{
+ auto constraint = Constraint(EQUALS);
+ constraint.expr = "none";
- EXPECT_EQ(constraint.op, EQUALS);
- EXPECT_EQ(constraint.expr, "none");
+ EXPECT_EQ(constraint.op, EQUALS);
+ EXPECT_EQ(constraint.expr, "none");
}
-TEST_F(TablesTests, test_constraint_list) {
- struct ConstraintList cl;
+TEST_F(TablesTests, test_constraint_list)
+{
+ struct ConstraintList cl;
- auto constraint = Constraint(EQUALS);
- constraint.expr = "some";
+ auto constraint = Constraint(EQUALS);
+ constraint.expr = "some";
- // The constraint list is a simple struct.
- cl.add(constraint);
- EXPECT_EQ(cl.constraints_.size(), 1U);
+ // The constraint list is a simple struct.
+ cl.add(constraint);
+ EXPECT_EQ(cl.constraints_.size(), 1U);
- constraint = Constraint(EQUALS);
- constraint.expr = "some_other";
- cl.add(constraint);
+ constraint = Constraint(EQUALS);
+ constraint.expr = "some_other";
+ cl.add(constraint);
- constraint = Constraint(GREATER_THAN);
- constraint.expr = "more_than";
- cl.add(constraint);
- EXPECT_EQ(cl.constraints_.size(), 3U);
+ constraint = Constraint(GREATER_THAN);
+ constraint.expr = "more_than";
+ cl.add(constraint);
+ EXPECT_EQ(cl.constraints_.size(), 3U);
- auto all_equals = cl.getAll(EQUALS);
- EXPECT_EQ(all_equals.size(), 2U);
+ auto all_equals = cl.getAll(EQUALS);
+ EXPECT_EQ(all_equals.size(), 2U);
}
-TEST_F(TablesTests, test_constraint_matching) {
- struct ConstraintList cl;
- // An empty constraint list has expectations.
- EXPECT_FALSE(cl.exists());
- EXPECT_FALSE(cl.exists(GREATER_THAN));
- EXPECT_TRUE(cl.notExistsOrMatches("some"));
-
- auto constraint = Constraint(EQUALS);
- constraint.expr = "some";
- cl.add(constraint);
-
- // Test existence checks based on flags.
- EXPECT_TRUE(cl.exists());
- EXPECT_TRUE(cl.exists(EQUALS));
- EXPECT_TRUE(cl.exists(EQUALS | LESS_THAN));
- EXPECT_FALSE(cl.exists(LESS_THAN));
-
- EXPECT_TRUE(cl.notExistsOrMatches("some"));
- EXPECT_TRUE(cl.matches("some"));
- EXPECT_FALSE(cl.notExistsOrMatches("not_some"));
-
- struct ConstraintList cl2;
- cl2.affinity = INTEGER_TYPE;
- constraint = Constraint(LESS_THAN);
- constraint.expr = "1000";
- cl2.add(constraint);
- constraint = Constraint(GREATER_THAN);
- constraint.expr = "1";
- cl2.add(constraint);
-
- // Test both SQL-provided string types.
- EXPECT_TRUE(cl2.matches("10"));
- // ...and the type literal.
- EXPECT_TRUE(cl2.matches(10));
-
- // Test operator lower bounds.
- EXPECT_FALSE(cl2.matches(0));
- EXPECT_FALSE(cl2.matches(1));
-
- // Test operator upper bounds.
- EXPECT_FALSE(cl2.matches(1000));
- EXPECT_FALSE(cl2.matches(1001));
-
- // Now test inclusive bounds.
- struct ConstraintList cl3;
- constraint = Constraint(LESS_THAN_OR_EQUALS);
- constraint.expr = "1000";
- cl3.add(constraint);
- constraint = Constraint(GREATER_THAN_OR_EQUALS);
- constraint.expr = "1";
- cl3.add(constraint);
-
- EXPECT_FALSE(cl3.matches(1001));
- EXPECT_TRUE(cl3.matches(1000));
-
- EXPECT_FALSE(cl3.matches(0));
- EXPECT_TRUE(cl3.matches(1));
+TEST_F(TablesTests, test_constraint_matching)
+{
+ struct ConstraintList cl;
+ // An empty constraint list has expectations.
+ EXPECT_FALSE(cl.exists());
+ EXPECT_FALSE(cl.exists(GREATER_THAN));
+ EXPECT_TRUE(cl.notExistsOrMatches("some"));
+
+ auto constraint = Constraint(EQUALS);
+ constraint.expr = "some";
+ cl.add(constraint);
+
+ // Test existence checks based on flags.
+ EXPECT_TRUE(cl.exists());
+ EXPECT_TRUE(cl.exists(EQUALS));
+ EXPECT_TRUE(cl.exists(EQUALS | LESS_THAN));
+ EXPECT_FALSE(cl.exists(LESS_THAN));
+
+ EXPECT_TRUE(cl.notExistsOrMatches("some"));
+ EXPECT_TRUE(cl.matches("some"));
+ EXPECT_FALSE(cl.notExistsOrMatches("not_some"));
+
+ struct ConstraintList cl2;
+ cl2.affinity = INTEGER_TYPE;
+ constraint = Constraint(LESS_THAN);
+ constraint.expr = "1000";
+ cl2.add(constraint);
+ constraint = Constraint(GREATER_THAN);
+ constraint.expr = "1";
+ cl2.add(constraint);
+
+ // Test both SQL-provided string types.
+ EXPECT_TRUE(cl2.matches("10"));
+ // ...and the type literal.
+ EXPECT_TRUE(cl2.matches(10));
+
+ // Test operator lower bounds.
+ EXPECT_FALSE(cl2.matches(0));
+ EXPECT_FALSE(cl2.matches(1));
+
+ // Test operator upper bounds.
+ EXPECT_FALSE(cl2.matches(1000));
+ EXPECT_FALSE(cl2.matches(1001));
+
+ // Now test inclusive bounds.
+ struct ConstraintList cl3;
+ constraint = Constraint(LESS_THAN_OR_EQUALS);
+ constraint.expr = "1000";
+ cl3.add(constraint);
+ constraint = Constraint(GREATER_THAN_OR_EQUALS);
+ constraint.expr = "1";
+ cl3.add(constraint);
+
+ EXPECT_FALSE(cl3.matches(1001));
+ EXPECT_TRUE(cl3.matches(1000));
+
+ EXPECT_FALSE(cl3.matches(0));
+ EXPECT_TRUE(cl3.matches(1));
}
-TEST_F(TablesTests, test_constraint_map) {
- ConstraintMap cm;
+TEST_F(TablesTests, test_constraint_map)
+{
+ ConstraintMap cm;
- cm["path"].add(Constraint(EQUALS, "some"));
+ cm["path"].add(Constraint(EQUALS, "some"));
- // If a constraint list exists for a map key, normal constraints apply.
- EXPECT_TRUE(cm["path"].matches("some"));
- EXPECT_FALSE(cm["path"].matches("not_some"));
+ // If a constraint list exists for a map key, normal constraints apply.
+ EXPECT_TRUE(cm["path"].matches("some"));
+ EXPECT_FALSE(cm["path"].matches("not_some"));
- // If a constraint list does not exist, then all checks will match.
- // If there is no predicate clause then all results will match.
- EXPECT_TRUE(cm["not_path"].matches("some"));
- EXPECT_TRUE(cm["not_path"].notExistsOrMatches("some"));
- EXPECT_FALSE(cm["not_path"].exists());
- EXPECT_FALSE(cm["not_path"].existsAndMatches("some"));
+ // If a constraint list does not exist, then all checks will match.
+ // If there is no predicate clause then all results will match.
+ EXPECT_TRUE(cm["not_path"].matches("some"));
+ EXPECT_TRUE(cm["not_path"].notExistsOrMatches("some"));
+ EXPECT_FALSE(cm["not_path"].exists());
+ EXPECT_FALSE(cm["not_path"].existsAndMatches("some"));
- // And of the column has constraints:
- EXPECT_TRUE(cm["path"].notExistsOrMatches("some"));
- EXPECT_FALSE(cm["path"].notExistsOrMatches("not_some"));
- EXPECT_TRUE(cm["path"].exists());
- EXPECT_TRUE(cm["path"].existsAndMatches("some"));
+ // And of the column has constraints:
+ EXPECT_TRUE(cm["path"].notExistsOrMatches("some"));
+ EXPECT_FALSE(cm["path"].notExistsOrMatches("not_some"));
+ EXPECT_TRUE(cm["path"].exists());
+ EXPECT_TRUE(cm["path"].existsAndMatches("some"));
}
-TEST_F(TablesTests, test_constraint_map_cast) {
- ConstraintMap cm;
+TEST_F(TablesTests, test_constraint_map_cast)
+{
+ ConstraintMap cm;
- cm["num"].affinity = INTEGER_TYPE;
- cm["num"].add(Constraint(EQUALS, "hello"));
+ cm["num"].affinity = INTEGER_TYPE;
+ cm["num"].add(Constraint(EQUALS, "hello"));
- EXPECT_FALSE(cm["num"].existsAndMatches("hello"));
+ EXPECT_FALSE(cm["num"].existsAndMatches("hello"));
}
class TestTablePlugin : public TablePlugin {
- public:
- void testSetCache(size_t step, size_t interval) {
- TableRows r;
- QueryContext ctx;
- ctx.useCache(true);
- setCache(step, interval, ctx, r);
- }
-
- bool testIsCached(size_t interval) {
- QueryContext ctx;
- ctx.useCache(true);
- return isCached(interval, ctx);
- }
+public:
+ void testSetCache(size_t step, size_t interval)
+ {
+ TableRows r;
+ QueryContext ctx;
+ ctx.useCache(true);
+ setCache(step, interval, ctx, r);
+ }
+
+ bool testIsCached(size_t interval)
+ {
+ QueryContext ctx;
+ ctx.useCache(true);
+ return isCached(interval, ctx);
+ }
};
-TEST_F(TablesTests, test_caching) {
- TestTablePlugin test;
- // By default the interval and step is 0, so a step of 5 will not be cached.
- EXPECT_FALSE(test.testIsCached(5));
-
- TablePlugin::kCacheInterval = 5;
- TablePlugin::kCacheStep = 1;
- EXPECT_FALSE(test.testIsCached(5));
- // Set the current time to 1, and the interval at 5.
- test.testSetCache(TablePlugin::kCacheStep, TablePlugin::kCacheInterval);
- // Time at 1 is cached for an interval of 5, so at time 5 the cache is fresh.
- EXPECT_TRUE(test.testIsCached(5));
- // 6 is the end of the cache, it is not fresh.
- EXPECT_FALSE(test.testIsCached(6));
- // 7 is past the cache, it is not fresh.
- EXPECT_FALSE(test.testIsCached(7));
-
- // Set the time at now to 2.
- TablePlugin::kCacheStep = 2;
- test.testSetCache(TablePlugin::kCacheStep, TablePlugin::kCacheInterval);
- EXPECT_TRUE(test.testIsCached(5));
- // Now 6 is within the freshness of 2 + 5.
- EXPECT_TRUE(test.testIsCached(6));
- EXPECT_FALSE(test.testIsCached(7));
+TEST_F(TablesTests, test_caching)
+{
+ TestTablePlugin test;
+ // By default the interval and step is 0, so a step of 5 will not be cached.
+ EXPECT_FALSE(test.testIsCached(5));
+
+ TablePlugin::kCacheInterval = 5;
+ TablePlugin::kCacheStep = 1;
+ EXPECT_FALSE(test.testIsCached(5));
+ // Set the current time to 1, and the interval at 5.
+ test.testSetCache(TablePlugin::kCacheStep, TablePlugin::kCacheInterval);
+ // Time at 1 is cached for an interval of 5, so at time 5 the cache is fresh.
+ EXPECT_TRUE(test.testIsCached(5));
+ // 6 is the end of the cache, it is not fresh.
+ EXPECT_FALSE(test.testIsCached(6));
+ // 7 is past the cache, it is not fresh.
+ EXPECT_FALSE(test.testIsCached(7));
+
+ // Set the time at now to 2.
+ TablePlugin::kCacheStep = 2;
+ test.testSetCache(TablePlugin::kCacheStep, TablePlugin::kCacheInterval);
+ EXPECT_TRUE(test.testIsCached(5));
+ // Now 6 is within the freshness of 2 + 5.
+ EXPECT_TRUE(test.testIsCached(6));
+ EXPECT_FALSE(test.testIsCached(7));
}
}
+++ /dev/null
-../../../core/plugins/logger.h
\ No newline at end of file
* results in potentially-differential form for a logger.
*/
struct QueryLogItem {
- public:
- /// Differential results from the query.
- DiffResults results;
+public:
+ /// Differential results from the query.
+ DiffResults results;
- /// Optional snapshot results, no differential applied.
- QueryDataTyped snapshot_results;
+ /// Optional snapshot results, no differential applied.
+ QueryDataTyped snapshot_results;
- /// The name of the scheduled query.
- std::string name;
+ /// The name of the scheduled query.
+ std::string name;
- /// The identifier (hostname, or uuid) of the host.
- std::string identifier;
+ /// The identifier (hostname, or uuid) of the host.
+ std::string identifier;
- /// The time that the query was executed, seconds as UNIX time.
- size_t time{0};
+ /// The time that the query was executed, seconds as UNIX time.
+ size_t time{0};
- /// The epoch at the time the query was executed
- uint64_t epoch{};
+ /// The epoch at the time the query was executed
+ uint64_t epoch{};
- /// Query execution counter for current epoch
- uint64_t counter{0};
+ /// Query execution counter for current epoch
+ uint64_t counter{0};
- /// The time that the query was executed, an ASCII string.
- std::string calendar_time;
+ /// The time that the query was executed, an ASCII string.
+ std::string calendar_time;
- /// A set of additional fields to emit with the log line.
- std::map<std::string, std::string> decorations;
+ /// A set of additional fields to emit with the log line.
+ std::map<std::string, std::string> decorations;
- /// equals operator
- bool operator==(const QueryLogItem& comp) const {
- return (comp.results == results) && (comp.name == name);
- }
+ /// equals operator
+ bool operator==(const QueryLogItem& comp) const
+ {
+ return (comp.results == results) && (comp.name == name);
+ }
- /// not equals operator
- bool operator!=(const QueryLogItem& comp) const {
- return !(*this == comp);
- }
+ /// not equals operator
+ bool operator!=(const QueryLogItem& comp) const
+ {
+ return !(*this == comp);
+ }
};
/**
* @brief Interact with the historical on-disk storage for a given query.
*/
class Query {
- public:
- /**
- * @brief Constructor which sets up necessary parameters of a Query object.
- *
- * Given a query, this constructor calculates the value of columnFamily_,
- * which can be accessed via the getColumnFamilyName getter method.
- *
- * @param name The query name.
- * @param q a SheduledQuery struct.
- */
- explicit Query(std::string name, const ScheduledQuery& q)
- : query_(q.query), name_(std::move(name)) {}
-
- /**
- * @brief Serialize the data in RocksDB into a useful data structure
- *
- * This method retrieves the data from RocksDB and returns the data in a
- * std::multiset, in-order to apply binary search in diff function.
- *
- * @param results the output QueryDataSet struct.
- *
- * @return the success or failure of the operation.
- */
- Status getPreviousQueryResults(QueryDataSet& results) const;
-
- /**
- * @brief Get the epoch associated with the previous query results.
- *
- * This method retrieves the epoch associated with the results data that was
- * was stored in rocksdb.
- *
- * @return the epoch associated with the previous query results.
- */
- uint64_t getPreviousEpoch() const;
-
- /**
- * @brief Get the query invocation counter.
- *
- * This method returns query invocation counter. If the query is a new query,
- * 0 is returned. Otherwise the counter associated with the query is retrieved
- * from database and incremented by 1.
- *
- * @param new_query Whether or not the query is new.
- *
- * @return the query invocation counter.
- */
- uint64_t getQueryCounter(bool new_query) const;
-
- /**
- * @brief Check if a given scheduled query exists in the database.
- *
- * @return true if the scheduled query already exists in the database.
- */
- bool isQueryNameInDatabase() const;
-
- /**
- * @brief Check if a query (not query name) is 'new' or altered.
- *
- * @return true if the scheduled query has not been altered.
- */
- bool isNewQuery() const;
-
- /**
- * @brief Add a new set of results to the persistent storage.
- *
- * Given the results of the execution of a scheduled query, add the results
- * to the database using addNewResults.
- *
- * @param qd the QueryDataTyped object, which has the results of the query.
- * @param epoch the epoch associated with QueryData
- * @param counter [output] the output that holds the query execution counter.
- *
- * @return the success or failure of the operation.
- */
- Status addNewResults(QueryDataTyped qd,
- uint64_t epoch,
- uint64_t& counter) const;
-
- /**
- * @brief Add a new set of results to the persistent storage and get back
- * the differential results.
- *
- * Given the results of an execution of a scheduled query, add the results
- * to the database using addNewResults and get back a data structure
- * indicating what rows in the query's results have changed.
- *
- * @param qd the QueryDataTyped object containing query results to store.
- * @param epoch the epoch associated with QueryData
- * @param counter the output that holds the query execution counter.
- * @param dr an output to a DiffResults object populated based on last run.
- * @param calculate_diff default true to populate dr.
- *
- * @return the success or failure of the operation.
- */
- Status addNewResults(QueryDataTyped qd,
- uint64_t epoch,
- uint64_t& counter,
- DiffResults& dr,
- bool calculate_diff = true) const;
-
- /**
- * @brief The most recent result set for a scheduled query.
- *
- * @param qd the output QueryData object.
- *
- * @return the success or failure of the operation.
- */
- Status getCurrentResults(QueryData& qd);
-
- public:
- /**
- * @brief Get the names of all historical queries.
- *
- * If you'd like to perform some database maintenance, getStoredQueryNames()
- * allows you to get a vector of the names of all queries which are
- * currently stored in RocksDB
- *
- * @return a vector containing the string names of all scheduled queries.
- */
- static std::vector<std::string> getStoredQueryNames();
-
- private:
- /// The scheduled query's query string.
- std::string query_;
-
- /// The scheduled query name.
- std::string name_;
-
- private:
- FRIEND_TEST(QueryTests, test_private_members);
- FRIEND_TEST(QueryTests, test_add_and_get_current_results);
- FRIEND_TEST(QueryTests, test_is_query_name_in_database);
- FRIEND_TEST(QueryTests, test_get_stored_query_names);
- FRIEND_TEST(QueryTests, test_get_executions);
- FRIEND_TEST(QueryTests, test_get_query_results);
- FRIEND_TEST(QueryTests, test_query_name_not_found_in_db);
+public:
+ /**
+ * @brief Constructor which sets up necessary parameters of a Query object.
+ *
+ * Given a query, this constructor calculates the value of columnFamily_,
+ * which can be accessed via the getColumnFamilyName getter method.
+ *
+ * @param name The query name.
+ * @param q a SheduledQuery struct.
+ */
+ explicit Query(std::string name, const ScheduledQuery& q)
+ : query_(q.query), name_(std::move(name)) {}
+
+ /**
+ * @brief Serialize the data in RocksDB into a useful data structure
+ *
+ * This method retrieves the data from RocksDB and returns the data in a
+ * std::multiset, in-order to apply binary search in diff function.
+ *
+ * @param results the output QueryDataSet struct.
+ *
+ * @return the success or failure of the operation.
+ */
+ Status getPreviousQueryResults(QueryDataSet& results) const;
+
+ /**
+ * @brief Get the epoch associated with the previous query results.
+ *
+ * This method retrieves the epoch associated with the results data that was
+ * was stored in rocksdb.
+ *
+ * @return the epoch associated with the previous query results.
+ */
+ uint64_t getPreviousEpoch() const;
+
+ /**
+ * @brief Get the query invocation counter.
+ *
+ * This method returns query invocation counter. If the query is a new query,
+ * 0 is returned. Otherwise the counter associated with the query is retrieved
+ * from database and incremented by 1.
+ *
+ * @param new_query Whether or not the query is new.
+ *
+ * @return the query invocation counter.
+ */
+ uint64_t getQueryCounter(bool new_query) const;
+
+ /**
+ * @brief Check if a given scheduled query exists in the database.
+ *
+ * @return true if the scheduled query already exists in the database.
+ */
+ bool isQueryNameInDatabase() const;
+
+ /**
+ * @brief Check if a query (not query name) is 'new' or altered.
+ *
+ * @return true if the scheduled query has not been altered.
+ */
+ bool isNewQuery() const;
+
+ /**
+ * @brief Add a new set of results to the persistent storage.
+ *
+ * Given the results of the execution of a scheduled query, add the results
+ * to the database using addNewResults.
+ *
+ * @param qd the QueryDataTyped object, which has the results of the query.
+ * @param epoch the epoch associated with QueryData
+ * @param counter [output] the output that holds the query execution counter.
+ *
+ * @return the success or failure of the operation.
+ */
+ Status addNewResults(QueryDataTyped qd,
+ uint64_t epoch,
+ uint64_t& counter) const;
+
+ /**
+ * @brief Add a new set of results to the persistent storage and get back
+ * the differential results.
+ *
+ * Given the results of an execution of a scheduled query, add the results
+ * to the database using addNewResults and get back a data structure
+ * indicating what rows in the query's results have changed.
+ *
+ * @param qd the QueryDataTyped object containing query results to store.
+ * @param epoch the epoch associated with QueryData
+ * @param counter the output that holds the query execution counter.
+ * @param dr an output to a DiffResults object populated based on last run.
+ * @param calculate_diff default true to populate dr.
+ *
+ * @return the success or failure of the operation.
+ */
+ Status addNewResults(QueryDataTyped qd,
+ uint64_t epoch,
+ uint64_t& counter,
+ DiffResults& dr,
+ bool calculate_diff = true) const;
+
+ /**
+ * @brief The most recent result set for a scheduled query.
+ *
+ * @param qd the output QueryData object.
+ *
+ * @return the success or failure of the operation.
+ */
+ Status getCurrentResults(QueryData& qd);
+
+public:
+ /**
+ * @brief Get the names of all historical queries.
+ *
+ * If you'd like to perform some database maintenance, getStoredQueryNames()
+ * allows you to get a vector of the names of all queries which are
+ * currently stored in RocksDB
+ *
+ * @return a vector containing the string names of all scheduled queries.
+ */
+ static std::vector<std::string> getStoredQueryNames();
+
+private:
+ /// The scheduled query's query string.
+ std::string query_;
+
+ /// The scheduled query name.
+ std::string name_;
+
+private:
+ FRIEND_TEST(QueryTests, test_private_members);
+ FRIEND_TEST(QueryTests, test_add_and_get_current_results);
+ FRIEND_TEST(QueryTests, test_is_query_name_in_database);
+ FRIEND_TEST(QueryTests, test_get_stored_query_names);
+ FRIEND_TEST(QueryTests, test_get_executions);
+ FRIEND_TEST(QueryTests, test_get_query_results);
+ FRIEND_TEST(QueryTests, test_query_name_not_found_in_db);
};
} // namespace osquery
namespace osquery {
class RegistryFactory : private boost::noncopyable {
- public:
- /// Singleton accessor.
- static RegistryFactory& get();
-
- /**
- * @brief Call a registry item.
- *
- * Registry 'calling' is the primary interaction osquery has with the Plugin
- * APIs, which register items. Each item is an instance of a specialized
- * Plugin, whose life/scope is maintained by the specific registry identified
- * by a unique name.
- *
- * The specialized plugin type will expose a `call` method that parses a
- * PluginRequest then perform some action and return a PluginResponse.
- * Each registry provides a `call` method that performs the registry item
- * (Plugin instance) look up, and passes and retrieves the request and
- * response.
- *
- * @param registry_name The unique registry name containing item_name,
- * @param item_name The name of the plugin used to REGISTER.
- * @param request The PluginRequest object handled by the Plugin item.
- * @param response The output.
- * @return A status from the Plugin.
- */
- static Status call(const std::string& registry_name,
- const std::string& item_name,
- const PluginRequest& request,
- PluginResponse& response);
-
- /// A helper call that does not return a response (only status).
- static Status call(const std::string& registry_name,
- const std::string& item_name,
- const PluginRequest& request);
-
- /// A helper call that uses the active plugin (if the registry has one).
- static Status call(const std::string& registry_name,
- const PluginRequest& request,
- PluginResponse& response);
-
- /// A helper call that uses the active plugin (if the registry has one).
- static Status call(const std::string& registry_name,
- const PluginRequest& request);
-
- /// Run `setUp` on every registry that is not marked 'lazy'.
- static void setUp();
-
- public:
- /// Direct access to a registry instance.
- RegistryInterfaceRef registry(const std::string& registry_name) const;
-
- void add(const std::string& name, RegistryInterfaceRef reg);
-
- /// Direct access to all registries.
- std::map<std::string, RegistryInterfaceRef> all() const;
-
- /// Direct access to all plugin instances for a given registry name.
- std::map<std::string, PluginRef> plugins(
- const std::string& registry_name) const;
-
- /// Direct access to a plugin instance.
- PluginRef plugin(const std::string& registry_name,
- const std::string& item_name) const;
-
- /// Adds an alias for an internal registry item. This registry will only
- /// broadcast the alias name.
- Status addAlias(const std::string& registry_name,
- const std::string& item_name,
- const std::string& alias);
-
- /// Returns the item_name or the item alias if an alias exists.
- std::string getAlias(const std::string& registry_name,
- const std::string& alias) const;
-
- /// Set a registry's active plugin.
- Status setActive(const std::string& registry_name,
- const std::string& item_name);
-
- /// Get a registry's active plugin.
- std::string getActive(const std::string& registry_nane) const;
-
- bool exists(const std::string& registry_name) const {
- return (registries_.count(registry_name) > 0);
- }
-
- /// Check if a registry item exists, optionally search only local registries.
- bool exists(const std::string& registry_name,
- const std::string& item_name,
- bool local = false) const;
-
- /// Get a list of the registry names.
- std::vector<std::string> names() const;
-
- /// Get a list of the registry item names for a given registry.
- std::vector<std::string> names(const std::string& registry_name) const;
-
- /// Return the number of registries.
- size_t count() const {
- return registries_.size();
- }
-
- /// Return the number of registry items for a given registry name.
- size_t count(const std::string& registry_name) const;
-
- /// Enable/disable duplicate registry item support using aliasing.
- void allowDuplicates(bool allow) {
- allow_duplicates_ = allow;
- }
-
- /// Check if duplicate registry items using registry aliasing are allowed.
- bool allowDuplicates() {
- return allow_duplicates_;
- }
-
- private:
- /// Check if the registries are locked.
- bool locked() {
- return locked_;
- }
-
- /// Set the registry locked status.
- void locked(bool locked) {
- locked_ = locked;
- }
-
- protected:
- RegistryFactory() = default;
- virtual ~RegistryFactory() = default;
-
- private:
- /// Track duplicate registry item support, used for testing.
- bool allow_duplicates_{false};
-
- /// Track registry "locking", while locked a registry cannot add/create.
- bool locked_{false};
-
- /// The primary storage for constructed registries.
- std::map<std::string, RegistryInterfaceRef> registries_;
-
- /// Protector for broadcast lookups and external registry mutations.
- mutable Mutex mutex_;
-
- private:
- friend class RegistryInterface;
+public:
+ /// Singleton accessor.
+ static RegistryFactory& get();
+
+ /**
+ * @brief Call a registry item.
+ *
+ * Registry 'calling' is the primary interaction osquery has with the Plugin
+ * APIs, which register items. Each item is an instance of a specialized
+ * Plugin, whose life/scope is maintained by the specific registry identified
+ * by a unique name.
+ *
+ * The specialized plugin type will expose a `call` method that parses a
+ * PluginRequest then perform some action and return a PluginResponse.
+ * Each registry provides a `call` method that performs the registry item
+ * (Plugin instance) look up, and passes and retrieves the request and
+ * response.
+ *
+ * @param registry_name The unique registry name containing item_name,
+ * @param item_name The name of the plugin used to REGISTER.
+ * @param request The PluginRequest object handled by the Plugin item.
+ * @param response The output.
+ * @return A status from the Plugin.
+ */
+ static Status call(const std::string& registry_name,
+ const std::string& item_name,
+ const PluginRequest& request,
+ PluginResponse& response);
+
+ /// A helper call that does not return a response (only status).
+ static Status call(const std::string& registry_name,
+ const std::string& item_name,
+ const PluginRequest& request);
+
+ /// A helper call that uses the active plugin (if the registry has one).
+ static Status call(const std::string& registry_name,
+ const PluginRequest& request,
+ PluginResponse& response);
+
+ /// A helper call that uses the active plugin (if the registry has one).
+ static Status call(const std::string& registry_name,
+ const PluginRequest& request);
+
+ /// Run `setUp` on every registry that is not marked 'lazy'.
+ static void setUp();
+
+public:
+ /// Direct access to a registry instance.
+ RegistryInterfaceRef registry(const std::string& registry_name) const;
+
+ void add(const std::string& name, RegistryInterfaceRef reg);
+
+ /// Direct access to all registries.
+ std::map<std::string, RegistryInterfaceRef> all() const;
+
+ /// Direct access to all plugin instances for a given registry name.
+ std::map<std::string, PluginRef> plugins(
+ const std::string& registry_name) const;
+
+ /// Direct access to a plugin instance.
+ PluginRef plugin(const std::string& registry_name,
+ const std::string& item_name) const;
+
+ /// Adds an alias for an internal registry item. This registry will only
+ /// broadcast the alias name.
+ Status addAlias(const std::string& registry_name,
+ const std::string& item_name,
+ const std::string& alias);
+
+ /// Returns the item_name or the item alias if an alias exists.
+ std::string getAlias(const std::string& registry_name,
+ const std::string& alias) const;
+
+ /// Set a registry's active plugin.
+ Status setActive(const std::string& registry_name,
+ const std::string& item_name);
+
+ /// Get a registry's active plugin.
+ std::string getActive(const std::string& registry_nane) const;
+
+ bool exists(const std::string& registry_name) const
+ {
+ return (registries_.count(registry_name) > 0);
+ }
+
+ /// Check if a registry item exists, optionally search only local registries.
+ bool exists(const std::string& registry_name,
+ const std::string& item_name,
+ bool local = false) const;
+
+ /// Get a list of the registry names.
+ std::vector<std::string> names() const;
+
+ /// Get a list of the registry item names for a given registry.
+ std::vector<std::string> names(const std::string& registry_name) const;
+
+ /// Return the number of registries.
+ size_t count() const
+ {
+ return registries_.size();
+ }
+
+ /// Return the number of registry items for a given registry name.
+ size_t count(const std::string& registry_name) const;
+
+ /// Enable/disable duplicate registry item support using aliasing.
+ void allowDuplicates(bool allow)
+ {
+ allow_duplicates_ = allow;
+ }
+
+ /// Check if duplicate registry items using registry aliasing are allowed.
+ bool allowDuplicates()
+ {
+ return allow_duplicates_;
+ }
+
+private:
+ /// Check if the registries are locked.
+ bool locked()
+ {
+ return locked_;
+ }
+
+ /// Set the registry locked status.
+ void locked(bool locked)
+ {
+ locked_ = locked;
+ }
+
+protected:
+ RegistryFactory() = default;
+ virtual ~RegistryFactory() = default;
+
+private:
+ /// Track duplicate registry item support, used for testing.
+ bool allow_duplicates_{false};
+
+ /// Track registry "locking", while locked a registry cannot add/create.
+ bool locked_{false};
+
+ /// The primary storage for constructed registries.
+ std::map<std::string, RegistryInterfaceRef> registries_;
+
+ /// Protector for broadcast lookups and external registry mutations.
+ mutable Mutex mutex_;
+
+private:
+ friend class RegistryInterface;
};
/**
template <class R>
class AR : public AutoRegisterInterface {
- public:
- AR(const char* t, const char* n, bool optional)
- : AutoRegisterInterface(t, n, optional) {}
-
- void run() override {
- RegistryFactory::get().add(
- type_, std::make_shared<RegistryType<R>>(name_, optional_));
- }
+public:
+ AR(const char* t, const char* n, bool optional)
+ : AutoRegisterInterface(t, n, optional) {}
+
+ void run() override
+ {
+ RegistryFactory::get().add(
+ type_, std::make_shared<RegistryType<R>>(name_, optional_));
+ }
};
template <class P>
class AP : public AutoRegisterInterface {
- public:
- AP(const char* t, const char* n, bool optional)
- : AutoRegisterInterface(t, n, optional) {}
-
- void run() override {
- auto registry = RegistryFactory::get().registry(type_);
- registry->add(name_, std::make_shared<P>(), optional_);
- }
+public:
+ AP(const char* t, const char* n, bool optional)
+ : AutoRegisterInterface(t, n, optional) {}
+
+ void run() override
+ {
+ auto registry = RegistryFactory::get().registry(type_);
+ registry->add(name_, std::make_shared<P>(), optional_);
+ }
};
template <class R>
struct RI {
- RI(const char* class_name,
- const char* registry_name,
- bool is_optional = false) {
- AutoRegisterInterface::autoloadRegistry(
- std::make_unique<AR<R>>(class_name, registry_name, is_optional));
- }
+ RI(const char* class_name,
+ const char* registry_name,
+ bool is_optional = false)
+ {
+ AutoRegisterInterface::autoloadRegistry(
+ std::make_unique<AR<R>>(class_name, registry_name, is_optional));
+ }
};
template <class P>
struct PI {
- PI(const char* registry_name,
- const char* plugin_name,
- bool is_optional = false) {
- AutoRegisterInterface::autoloadPlugin(
- std::make_unique<AP<P>>(registry_name, plugin_name, is_optional));
- }
+ PI(const char* registry_name,
+ const char* plugin_name,
+ bool is_optional = false)
+ {
+ AutoRegisterInterface::autoloadPlugin(
+ std::make_unique<AP<P>>(registry_name, plugin_name, is_optional));
+ }
};
} // namespace registries
#define CREATE_REGISTRY(class_name, registry_name) \
- namespace registries { \
- const RI<class_name> k##class_name(registry_name, registry_name, false); \
- }
+ namespace registries { \
+ const RI<class_name> k##class_name(registry_name, registry_name, false); \
+ }
#define CREATE_LAZY_REGISTRY(class_name, registry_name) \
- namespace registries { \
- const RI<class_name> k##class_name(registry_name, registry_name, true); \
- }
+ namespace registries { \
+ const RI<class_name> k##class_name(registry_name, registry_name, true); \
+ }
#define REGISTER(class_name, registry_name, plugin_name) \
- namespace registries { \
- const PI<class_name> k##class_name(registry_name, plugin_name, false); \
- }
+ namespace registries { \
+ const PI<class_name> k##class_name(registry_name, plugin_name, false); \
+ }
#define REGISTER_INTERNAL(class_name, registry_name, plugin_name) \
- namespace registries { \
- const PI<class_name> k##class_name(registry_name, plugin_name, true); \
- }
+ namespace registries { \
+ const PI<class_name> k##class_name(registry_name, plugin_name, true); \
+ }
} // namespace osquery
* @brief This is the registry interface.
*/
class RegistryInterface : private boost::noncopyable {
- public:
- explicit RegistryInterface(std::string name, bool auto_setup = false)
- : name_(std::move(name)), auto_setup_(auto_setup) {}
- virtual ~RegistryInterface() = default;
-
- /**
- * @brief This is the only way to add plugins to a registry.
- *
- * It must be implemented by the templated child, which knows the type of
- * registry and which can downcast the input plugin.
- *
- * @param plugin_name An indexable name for the plugin.
- * @param plugin_item A type-specific plugin reference.
- * @param internal true if this is internal to the osquery SDK.
- */
- virtual Status add(const std::string& plugin_name,
- const PluginRef& plugin_item,
- bool internal = false) = 0;
-
- /**
- * @brief Remove a registry item by its identifier.
- *
- * @param item_name An identifier for this registry plugin.
- */
- void remove(const std::string& item_name);
-
- /// Allow a registry type to react to configuration updates.
- virtual void configure();
-
- /// Check if a given plugin name is considered internal.
- bool isInternal(const std::string& item_name) const;
-
- /// Get the 'active' plugin, return success with the active plugin name.
- std::string getActive() const;
-
- /// Allow others to introspect into the registered name (for reporting).
- virtual std::string getName() const;
-
- /// Facility method to check if a registry item exists.
- bool exists(const std::string& item_name, bool local = false) const;
-
- /// Facility method to count the number of items in this registry.
- size_t count() const;
-
- /// Facility method to list the registry item identifiers.
- std::vector<std::string> names() const;
-
- /**
- * @brief Allow a plugin to perform some setup functions when osquery starts.
- *
- * Doing work in a plugin constructor has unknown behavior. Plugins may
- * be constructed at anytime during osquery's life, including global variable
- * instantiation. To have a reliable state (aka, flags have been parsed,
- * and logs are ready to stream), do construction work in Plugin::setUp.
- *
- * The registry `setUp` will iterate over all of its registry items and call
- * their setup unless the registry is lazy (see CREATE_REGISTRY).
- */
- virtual void setUp();
-
- virtual PluginRef plugin(const std::string& plugin_name) const = 0;
-
- /// Construct and return a map of plugin names to their implementation.
- std::map<std::string, PluginRef> plugins();
-
- protected:
- /**
- * @brief The only method a plugin user should call.
- *
- * Registry plugins are used internally and externally. They may belong
- * to the process making the call or to an external process via a thrift
- * transport.
- *
- * All plugin input and output must be serializable. The plugin types
- * RegistryType usually exposes protected serialization methods for the
- * data structures used by plugins (registry items).
- *
- * @param item_name The plugin identifier to call.
- * @param request The plugin request, usually containing an action request.
- * @param response If successful, the requested information.
- * @return Success if the plugin was called, and response was filled.
- */
- virtual Status call(const std::string& item_name,
- const PluginRequest& request,
- PluginResponse& response);
-
- /// Allow the registry to introspect into the registered name (for logging).
- void setname(const std::string& name);
-
- /**
- * @brief The implementation adder will call addPlugin.
- *
- * Once a downcast is completed the work for adding internal/external
- * indexes is provided here.
- */
- Status addPlugin(const std::string& plugin_name,
- const PluginRef& plugin_item,
- bool internal);
-
- /// Set an 'active' plugin to receive registry calls when no item name given.
- Status setActive(const std::string& item_name);
-
- /// Create a registry item alias for a given item name.
- Status addAlias(const std::string& item_name, const std::string& alias);
-
- /// Get the registry item name for a given alias.
- std::string getAlias(const std::string& alias) const;
-
- protected:
- /// The identifier for this registry, used to register items.
- std::string name_;
-
- /// Does this registry run setUp on each registry item at initialization.
- bool auto_setup_;
-
- protected:
- /// A map of registered plugin instances to their registered identifier.
- std::map<std::string, PluginRef> items_;
-
- /// If aliases are used, a map of alias to item name.
- std::map<std::string, std::string> aliases_;
-
- /// Keep a lookup of registry items that are blacklisted from broadcast.
- std::vector<std::string> internal_;
-
- /// Support an 'active' mode where calls without a specific item name will
- /// be directed to the 'active' plugin.
- std::string active_;
-
- /// Protect concurrent accesses to object's data
- mutable Mutex mutex_;
-
- private:
- friend class RegistryFactory;
-
- bool isInternal_(const std::string& item_name) const;
-
- bool exists_(const std::string& item_name, bool local) const;
+public:
+ explicit RegistryInterface(std::string name, bool auto_setup = false)
+ : name_(std::move(name)), auto_setup_(auto_setup) {}
+ virtual ~RegistryInterface() = default;
+
+ /**
+ * @brief This is the only way to add plugins to a registry.
+ *
+ * It must be implemented by the templated child, which knows the type of
+ * registry and which can downcast the input plugin.
+ *
+ * @param plugin_name An indexable name for the plugin.
+ * @param plugin_item A type-specific plugin reference.
+ * @param internal true if this is internal to the osquery SDK.
+ */
+ virtual Status add(const std::string& plugin_name,
+ const PluginRef& plugin_item,
+ bool internal = false) = 0;
+
+ /**
+ * @brief Remove a registry item by its identifier.
+ *
+ * @param item_name An identifier for this registry plugin.
+ */
+ void remove(const std::string& item_name);
+
+ /// Allow a registry type to react to configuration updates.
+ virtual void configure();
+
+ /// Check if a given plugin name is considered internal.
+ bool isInternal(const std::string& item_name) const;
+
+ /// Get the 'active' plugin, return success with the active plugin name.
+ std::string getActive() const;
+
+ /// Allow others to introspect into the registered name (for reporting).
+ virtual std::string getName() const;
+
+ /// Facility method to check if a registry item exists.
+ bool exists(const std::string& item_name, bool local = false) const;
+
+ /// Facility method to count the number of items in this registry.
+ size_t count() const;
+
+ /// Facility method to list the registry item identifiers.
+ std::vector<std::string> names() const;
+
+ /**
+ * @brief Allow a plugin to perform some setup functions when osquery starts.
+ *
+ * Doing work in a plugin constructor has unknown behavior. Plugins may
+ * be constructed at anytime during osquery's life, including global variable
+ * instantiation. To have a reliable state (aka, flags have been parsed,
+ * and logs are ready to stream), do construction work in Plugin::setUp.
+ *
+ * The registry `setUp` will iterate over all of its registry items and call
+ * their setup unless the registry is lazy (see CREATE_REGISTRY).
+ */
+ virtual void setUp();
+
+ virtual PluginRef plugin(const std::string& plugin_name) const = 0;
+
+ /// Construct and return a map of plugin names to their implementation.
+ std::map<std::string, PluginRef> plugins();
+
+protected:
+ /**
+ * @brief The only method a plugin user should call.
+ *
+ * Registry plugins are used internally and externally. They may belong
+ * to the process making the call or to an external process via a thrift
+ * transport.
+ *
+ * All plugin input and output must be serializable. The plugin types
+ * RegistryType usually exposes protected serialization methods for the
+ * data structures used by plugins (registry items).
+ *
+ * @param item_name The plugin identifier to call.
+ * @param request The plugin request, usually containing an action request.
+ * @param response If successful, the requested information.
+ * @return Success if the plugin was called, and response was filled.
+ */
+ virtual Status call(const std::string& item_name,
+ const PluginRequest& request,
+ PluginResponse& response);
+
+ /// Allow the registry to introspect into the registered name (for logging).
+ void setname(const std::string& name);
+
+ /**
+ * @brief The implementation adder will call addPlugin.
+ *
+ * Once a downcast is completed the work for adding internal/external
+ * indexes is provided here.
+ */
+ Status addPlugin(const std::string& plugin_name,
+ const PluginRef& plugin_item,
+ bool internal);
+
+ /// Set an 'active' plugin to receive registry calls when no item name given.
+ Status setActive(const std::string& item_name);
+
+ /// Create a registry item alias for a given item name.
+ Status addAlias(const std::string& item_name, const std::string& alias);
+
+ /// Get the registry item name for a given alias.
+ std::string getAlias(const std::string& alias) const;
+
+protected:
+ /// The identifier for this registry, used to register items.
+ std::string name_;
+
+ /// Does this registry run setUp on each registry item at initialization.
+ bool auto_setup_;
+
+protected:
+ /// A map of registered plugin instances to their registered identifier.
+ std::map<std::string, PluginRef> items_;
+
+ /// If aliases are used, a map of alias to item name.
+ std::map<std::string, std::string> aliases_;
+
+ /// Keep a lookup of registry items that are blacklisted from broadcast.
+ std::vector<std::string> internal_;
+
+ /// Support an 'active' mode where calls without a specific item name will
+ /// be directed to the 'active' plugin.
+ std::string active_;
+
+ /// Protect concurrent accesses to object's data
+ mutable Mutex mutex_;
+
+private:
+ friend class RegistryFactory;
+
+ bool isInternal_(const std::string& item_name) const;
+
+ bool exists_(const std::string& item_name, bool local) const;
};
/**
*/
template <class PluginType>
class RegistryType : public RegistryInterface {
- protected:
- using PluginTypeRef = std::shared_ptr<PluginType>;
-
- public:
- explicit RegistryType(const std::string& name, bool auto_setup = false)
- : RegistryInterface(name, auto_setup) {}
- ~RegistryType() override = default;
-
- Status add(const std::string& plugin_name,
- const PluginRef& plugin_item,
- bool internal = false) override {
- if (nullptr == std::dynamic_pointer_cast<PluginType>(plugin_item)) {
- throw std::runtime_error("Cannot add foreign plugin type: " +
- plugin_name);
- }
- return addPlugin(plugin_name, plugin_item, internal);
- }
-
- /**
- * @brief A raw accessor for a registry plugin.
- *
- * If there is no plugin with an item_name identifier this will throw
- * and out_of_range exception.
- *
- * @param plugin_name An identifier for this registry plugin.
- * @return A std::shared_ptr of type RegistryType.
- */
- PluginRef plugin(const std::string& plugin_name) const override {
- ReadLock lock(mutex_);
-
- if (items_.count(plugin_name) == 0) {
- return nullptr;
- }
- return items_.at(plugin_name);
- }
-
- private:
- FRIEND_TEST(EventsTests, test_event_subscriber_configure);
- FRIEND_TEST(VirtualTableTests, test_indexing_costs);
+protected:
+ using PluginTypeRef = std::shared_ptr<PluginType>;
+
+public:
+ explicit RegistryType(const std::string& name, bool auto_setup = false)
+ : RegistryInterface(name, auto_setup) {}
+ ~RegistryType() override = default;
+
+ Status add(const std::string& plugin_name,
+ const PluginRef& plugin_item,
+ bool internal = false) override
+ {
+ if (nullptr == std::dynamic_pointer_cast<PluginType>(plugin_item)) {
+ throw std::runtime_error("Cannot add foreign plugin type: " +
+ plugin_name);
+ }
+ return addPlugin(plugin_name, plugin_item, internal);
+ }
+
+ /**
+ * @brief A raw accessor for a registry plugin.
+ *
+ * If there is no plugin with an item_name identifier this will throw
+ * and out_of_range exception.
+ *
+ * @param plugin_name An identifier for this registry plugin.
+ * @return A std::shared_ptr of type RegistryType.
+ */
+ PluginRef plugin(const std::string& plugin_name) const override
+ {
+ ReadLock lock(mutex_);
+
+ if (items_.count(plugin_name) == 0) {
+ return nullptr;
+ }
+ return items_.at(plugin_name);
+ }
+
+private:
+ FRIEND_TEST(EventsTests, test_event_subscriber_configure);
+ FRIEND_TEST(VirtualTableTests, test_indexing_costs);
};
/// Helper definitions for a shared pointer to the basic Registry type.
using AutoRegisterSet = std::vector<std::unique_ptr<AutoRegisterInterface>>;
class AutoRegisterInterface {
- public:
- /// The registry name, or type identifier.
- std::string type_;
+public:
+ /// The registry name, or type identifier.
+ std::string type_;
- /// The registry or plugin name.
- std::string name_;
+ /// The registry or plugin name.
+ std::string name_;
- /// Either autoload a registry, or create an internal plugin.
- bool optional_;
+ /// Either autoload a registry, or create an internal plugin.
+ bool optional_;
- AutoRegisterInterface(const char* _type, const char* _name, bool optional);
- virtual ~AutoRegisterInterface() = default;
+ AutoRegisterInterface(const char* _type, const char* _name, bool optional);
+ virtual ~AutoRegisterInterface() = default;
- /// A call-in for the iterator.
- virtual void run() = 0;
+ /// A call-in for the iterator.
+ virtual void run() = 0;
- public:
- /// Access all registries.
- static AutoRegisterSet& registries();
+public:
+ /// Access all registries.
+ static AutoRegisterSet& registries();
- /// Insert a new registry.
- static void autoloadRegistry(std::unique_ptr<AutoRegisterInterface> ar_);
+ /// Insert a new registry.
+ static void autoloadRegistry(std::unique_ptr<AutoRegisterInterface> ar_);
- /// Access all plugins.
- static AutoRegisterSet& plugins();
+ /// Access all plugins.
+ static AutoRegisterSet& plugins();
- /// Insert a new plugin.
- static void autoloadPlugin(std::unique_ptr<AutoRegisterInterface> ar_);
+ /// Insert a new plugin.
+ static void autoloadPlugin(std::unique_ptr<AutoRegisterInterface> ar_);
};
void registryAndPluginInit();
* @endcode
*/
class SQL : private only_movable {
- public:
- /**
- * @brief Instantiate an instance of the class with a query.
- *
- * @param query An osquery SQL query.
- * @param use_cache [optional] Set true to use the query cache.
- */
- explicit SQL(const std::string& query, bool use_cache = false);
-
- /// Allow moving.
- SQL(SQL&&) noexcept = default;
-
- /// Allow move assignment.
- SQL& operator=(SQL&&) = default;
-
- public:
- /**
- * @brief Const accessor for the rows returned by the query.
- *
- * @return A QueryData object of the query results.
- */
- const QueryData& rows() const;
-
- /**
- * @brief Accessor for the rows returned by the query.
- *
- * @return A QueryData object of the query results.
- */
- QueryData& rows();
-
- /**
- * @brief Column information for the query
- *
- * @return A ColumnNames object for the query
- */
- const ColumnNames& columns() const;
-
- /**
- * @brief Accessor to switch off of when checking the success of a query.
- *
- * @return A bool indicating the success or failure of the operation.
- */
- bool ok() const;
-
- /**
- * @brief Get the status returned by the query.
- *
- * @return The query status.
- */
- const Status& getStatus() const;
-
- /**
- * @brief Accessor for the message string indicating the status of the query.
- *
- * @return The message string indicating the status of the query.
- */
- std::string getMessageString() const;
-
-
- public:
- /**
- * @brief Get all, 'SELECT * ...', results given a virtual table name.
- *
- * @param table The name of the virtual table.
- * @return A QueryData object of the 'SELECT *...' query results.
- */
- static QueryData selectAllFrom(const std::string& table);
-
- /**
- * @brief Get all with constraint, 'SELECT * ... where', results given
- * a virtual table name and single constraint.
- *
- * @param table The name of the virtual table.
- * @param column Table column name to apply constraint.
- * @param op The SQL comparative operator.
- * @param expr The constraint expression.
- * @return A QueryData object of the 'SELECT *...' query results.
- */
- static QueryData selectAllFrom(const std::string& table,
- const std::string& column,
- ConstraintOperator op,
- const std::string& expr);
-
- /**
- * @brief Get columns with constraint, 'SELECT [columns] ... where', results
- * given a virtual table name, column names, and single constraint.
- *
- * @param columns the columns to return
- * @param table The name of the virtual table.
- * @param column Table column name to apply constraint.
- * @param op The SQL comparative operator.
- * @param expr The constraint expression.
- * @return A QueryData object of the 'SELECT [columns] ...' query results.
- */
- static QueryData selectFrom(const std::initializer_list<std::string>& columns,
- const std::string& table,
- const std::string& column,
- ConstraintOperator op,
- const std::string& expr);
-
- protected:
- /**
- * @brief Private default constructor.
- *
- * The osquery::SQL class should only ever be instantiated with a query.
- */
- SQL() = default;
-
- protected:
- /// The internal member which holds the results of the query.
- QueryData results_;
-
- /// The internal member which holds the status of the query.
- Status status_;
-
- /// The internal member which holds the column names and order for the query
- ColumnNames columns_;
+public:
+ /**
+ * @brief Instantiate an instance of the class with a query.
+ *
+ * @param query An osquery SQL query.
+ * @param use_cache [optional] Set true to use the query cache.
+ */
+ explicit SQL(const std::string& query, bool use_cache = false);
+
+ /// Allow moving.
+ SQL(SQL&&) noexcept = default;
+
+ /// Allow move assignment.
+ SQL& operator=(SQL&&) = default;
+
+public:
+ /**
+ * @brief Const accessor for the rows returned by the query.
+ *
+ * @return A QueryData object of the query results.
+ */
+ const QueryData& rows() const;
+
+ /**
+ * @brief Accessor for the rows returned by the query.
+ *
+ * @return A QueryData object of the query results.
+ */
+ QueryData& rows();
+
+ /**
+ * @brief Column information for the query
+ *
+ * @return A ColumnNames object for the query
+ */
+ const ColumnNames& columns() const;
+
+ /**
+ * @brief Accessor to switch off of when checking the success of a query.
+ *
+ * @return A bool indicating the success or failure of the operation.
+ */
+ bool ok() const;
+
+ /**
+ * @brief Get the status returned by the query.
+ *
+ * @return The query status.
+ */
+ const Status& getStatus() const;
+
+ /**
+ * @brief Accessor for the message string indicating the status of the query.
+ *
+ * @return The message string indicating the status of the query.
+ */
+ std::string getMessageString() const;
+
+
+public:
+ /**
+ * @brief Get all, 'SELECT * ...', results given a virtual table name.
+ *
+ * @param table The name of the virtual table.
+ * @return A QueryData object of the 'SELECT *...' query results.
+ */
+ static QueryData selectAllFrom(const std::string& table);
+
+ /**
+ * @brief Get all with constraint, 'SELECT * ... where', results given
+ * a virtual table name and single constraint.
+ *
+ * @param table The name of the virtual table.
+ * @param column Table column name to apply constraint.
+ * @param op The SQL comparative operator.
+ * @param expr The constraint expression.
+ * @return A QueryData object of the 'SELECT *...' query results.
+ */
+ static QueryData selectAllFrom(const std::string& table,
+ const std::string& column,
+ ConstraintOperator op,
+ const std::string& expr);
+
+ /**
+ * @brief Get columns with constraint, 'SELECT [columns] ... where', results
+ * given a virtual table name, column names, and single constraint.
+ *
+ * @param columns the columns to return
+ * @param table The name of the virtual table.
+ * @param column Table column name to apply constraint.
+ * @param op The SQL comparative operator.
+ * @param expr The constraint expression.
+ * @return A QueryData object of the 'SELECT [columns] ...' query results.
+ */
+ static QueryData selectFrom(const std::initializer_list<std::string>& columns,
+ const std::string& table,
+ const std::string& column,
+ ConstraintOperator op,
+ const std::string& expr);
+
+protected:
+ /**
+ * @brief Private default constructor.
+ *
+ * The osquery::SQL class should only ever be instantiated with a query.
+ */
+ SQL() = default;
+
+protected:
+ /// The internal member which holds the results of the query.
+ QueryData results_;
+
+ /// The internal member which holds the status of the query.
+ Status status_;
+
+ /// The internal member which holds the column names and order for the query
+ ColumnNames columns_;
};
* @return A status indicating query success.
*/
Status query(const std::string& query,
- QueryData& results,
- bool use_cache = false);
+ QueryData& results,
+ bool use_cache = false);
/**
* @brief Analyze a query, providing information about the result columns.
* time.
*/
template <typename Type>
-inline std::string __sqliteField(const Type& source) noexcept {
- return std::to_string(source);
+inline std::string __sqliteField(const Type& source) noexcept
+{
+ return std::to_string(source);
}
template <size_t N>
-inline std::string __sqliteField(const char (&source)[N]) noexcept {
- return std::string(source, N - 1U);
+inline std::string __sqliteField(const char (&source)[N]) noexcept
+{
+ return std::string(source, N - 1U);
}
template <size_t N>
-inline std::string __sqliteField(const unsigned char (&source)[N]) noexcept {
- return std::string(reinterpret_cast<const char*>(source), N - 1U);
+inline std::string __sqliteField(const unsigned char (&source)[N]) noexcept
+{
+ return std::string(reinterpret_cast<const char*>(source), N - 1U);
}
-inline std::string __sqliteField(const char* source) noexcept {
- return std::string(source);
+inline std::string __sqliteField(const char* source) noexcept
+{
+ return std::string(source);
}
-inline std::string __sqliteField(char* const source) noexcept {
- return std::string(source);
+inline std::string __sqliteField(char* const source) noexcept
+{
+ return std::string(source);
}
-inline std::string __sqliteField(const unsigned char* source) noexcept {
- return std::string(reinterpret_cast<const char*>(source));
+inline std::string __sqliteField(const unsigned char* source) noexcept
+{
+ return std::string(reinterpret_cast<const char*>(source));
}
-inline std::string __sqliteField(unsigned char* const source) noexcept {
- return std::string(reinterpret_cast<char* const>(source));
+inline std::string __sqliteField(unsigned char* const source) noexcept
+{
+ return std::string(reinterpret_cast<char* const>(source));
}
-inline std::string __sqliteField(const std::string& source) noexcept {
- return source;
+inline std::string __sqliteField(const std::string& source) noexcept
+{
+ return source;
}
#ifdef WIN32
* expression the table generator may limit the data appropriately.
*/
enum ConstraintOperator : unsigned char {
- EQUALS = 2,
- GREATER_THAN = 4,
- LESS_THAN_OR_EQUALS = 8,
- LESS_THAN = 16,
- GREATER_THAN_OR_EQUALS = 32,
- MATCH = 64,
- LIKE = 65,
- GLOB = 66,
- REGEXP = 67,
- UNIQUE = 1,
+ EQUALS = 2,
+ GREATER_THAN = 4,
+ LESS_THAN_OR_EQUALS = 8,
+ LESS_THAN = 16,
+ GREATER_THAN_OR_EQUALS = 32,
+ MATCH = 64,
+ LIKE = 65,
+ GLOB = 66,
+ REGEXP = 67,
+ UNIQUE = 1,
};
/// Type for flags for what constraint operators are admissible.
* The constraint is applied to columns which have literal and affinity types.
*/
struct Constraint {
- unsigned char op;
- std::string expr;
-
- /// Construct a Constraint with the most-basic information, the operator.
- explicit Constraint(unsigned char _op) {
- op = _op;
- }
-
- // A constraint list in a context knows only the operator at creation.
- explicit Constraint(unsigned char _op, std::string _expr)
- : op(_op), expr(std::move(_expr)) {}
+ unsigned char op;
+ std::string expr;
+
+ /// Construct a Constraint with the most-basic information, the operator.
+ explicit Constraint(unsigned char _op)
+ {
+ op = _op;
+ }
+
+ // A constraint list in a context knows only the operator at creation.
+ explicit Constraint(unsigned char _op, std::string _expr)
+ : op(_op), expr(std::move(_expr)) {}
};
* @brief Attributes about a Table implementation.
*/
enum class TableAttributes {
- /// Default no-op attribute.
- NONE = 0,
+ /// Default no-op attribute.
+ NONE = 0,
- /// This table is a 'utility' and is always available locally.
- UTILITY = 1,
+ /// This table is a 'utility' and is always available locally.
+ UTILITY = 1,
- /// The results from this table may be cached within a schedule.
- CACHEABLE = 2,
+ /// The results from this table may be cached within a schedule.
+ CACHEABLE = 2,
- /// The results are backed by a set time-indexed, always growing, events.
- EVENT_BASED = 4,
+ /// The results are backed by a set time-indexed, always growing, events.
+ EVENT_BASED = 4,
- /// This table inspect items relative to each user, a JOIN may be required.
- USER_BASED = 8,
+ /// This table inspect items relative to each user, a JOIN may be required.
+ USER_BASED = 8,
- /// This table's data requires an osquery kernel extension/module.
- KERNEL_REQUIRED = 16,
+ /// This table's data requires an osquery kernel extension/module.
+ KERNEL_REQUIRED = 16,
};
/// Treat table attributes as a set of flags.
-inline TableAttributes operator|(TableAttributes a, TableAttributes b) {
- return static_cast<TableAttributes>(static_cast<int>(a) |
- static_cast<int>(b));
+inline TableAttributes operator|(TableAttributes a, TableAttributes b)
+{
+ return static_cast<TableAttributes>(static_cast<int>(a) |
+ static_cast<int>(b));
}
/// Treat column options as a set of flags.
-inline size_t operator&(TableAttributes a, TableAttributes b) {
- return static_cast<size_t>(a) & static_cast<size_t>(b);
+inline size_t operator&(TableAttributes a, TableAttributes b)
+{
+ return static_cast<size_t>(a) & static_cast<size_t>(b);
}
/// Alias for an ordered list of column name and corresponding SQL type.
using TableColumns =
- std::vector<std::tuple<std::string, ColumnType, ColumnOptions>>;
+ std::vector<std::tuple<std::string, ColumnType, ColumnOptions>>;
/// Alias for map of column alias sets.
using ColumnAliasSet = std::map<std::string, std::set<std::string>>;
* A constraint list supports all AS_LITERAL types, and all ConstraintOperators.
*/
struct ConstraintList : private boost::noncopyable {
- public:
- /// The SQLite affinity type.
- ColumnType affinity{TEXT_TYPE};
-
- /**
- * @brief Check if an expression matches the query constraints.
- *
- * Evaluate ALL constraints in this ConstraintList against the string
- * expression. The affinity of the constraint will be used as the affinite
- * and lexical type of the expression and set of constraint expressions.
- * If there are no predicate constraints in this list, all expression will
- * match. Constraints are limitations.
- *
- * @param expr a SQL type expression of the column literal type to check.
- * @return If the expression matched all constraints.
- */
- bool matches(const std::string& expr) const;
-
- /**
- * @brief Check if an expression matches the query constraints.
- *
- * `matches` also supports the set of SQL affinite types.
- * The expression expr will be evaluated as a string and compared using
- * the affinity of the constraint.
- *
- * @param expr a SQL type expression of the column literal type to check.
- * @return If the expression matched all constraints.
- */
- template <typename T>
- bool matches(const T& expr) const {
- return matches(SQL_TEXT(expr));
- }
-
- /**
- * @brief Check and return if there are constraints on this column.
- *
- * A ConstraintList is used in a ConstraintMap with a column name as the
- * map index. Tables that act on optional constraints should check if any
- * constraint was provided. The ops parameter serves to specify which
- * operators we want to check existence for.
- *
- * @param ops (Optional: default ANY_OP) The operators types to look for.
- * @return true if any constraint exists.
- */
- bool exists(ConstraintOperatorFlag ops = ANY_OP) const;
-
- /**
- * @brief Check if a constraint exists AND matches the type expression.
- *
- * See ConstraintList::exists and ConstraintList::matches.
- *
- * @param expr The expression to match.
- * @return true if any constraint exists AND matches the type expression.
- */
- template <typename T>
- bool existsAndMatches(const T& expr) const {
- return (exists() && matches(expr));
- }
-
- /**
- * @brief Check if a constraint is missing or matches a type expression.
- *
- * A ConstraintList is used in a ConstraintMap with a column name as the
- * map index. Tables that act on required constraints can make decisions
- * on missing constraints or a constraint match.
- *
- * @param expr The expression to match.
- * @return true if constraint is missing or matches the type expression.
- */
- template <typename T>
- bool notExistsOrMatches(const T& expr) const {
- return (!exists() || matches(expr));
- }
-
- /**
- * @brief Helper templated function for ConstraintList::matches.
- */
- template <typename T>
- bool literal_matches(const T& base_expr) const;
-
- /**
- * @brief Get all expressions for a given ConstraintOperator.
- *
- * This is most useful if the table generation requires as column.
- * The generator may `getAll(EQUALS)` then iterate.
- *
- * @param op the ConstraintOperator.
- * @return A list of TEXT%-represented types matching the operator.
- */
- std::set<std::string> getAll(ConstraintOperator op) const;
-
- /// See ConstraintList::getAll, but as a selected literal type.
- template <typename T>
- std::set<T> getAll(ConstraintOperator op) const;
-
- /// Constraint list accessor, types and operator.
- const std::vector<struct Constraint>& getAll() const {
- return constraints_;
- }
-
- /**
- * @brief Add a new Constraint to the list of constraints.
- *
- * @param constraint a new operator/expression to constrain.
- */
- void add(const struct Constraint& constraint) {
- constraints_.push_back(constraint);
- }
-
- /**
- * @brief Serialize a ConstraintList into a property tree.
- *
- * The property tree will use the format:
- * {
- * "affinity": affinity,
- * "list": [
- * {"op": op, "expr": expr}, ...
- * ]
- * }
- */
- vist::json::Object serialize() const;
-
- void deserialize(vist::json::Object& obj);
-
-
- private:
- /// List of constraint operator/expressions.
- std::vector<struct Constraint> constraints_;
-
- private:
- friend struct QueryContext;
-
- private:
- FRIEND_TEST(TablesTests, test_constraint_list);
+public:
+ /// The SQLite affinity type.
+ ColumnType affinity{TEXT_TYPE};
+
+ /**
+ * @brief Check if an expression matches the query constraints.
+ *
+ * Evaluate ALL constraints in this ConstraintList against the string
+ * expression. The affinity of the constraint will be used as the affinite
+ * and lexical type of the expression and set of constraint expressions.
+ * If there are no predicate constraints in this list, all expression will
+ * match. Constraints are limitations.
+ *
+ * @param expr a SQL type expression of the column literal type to check.
+ * @return If the expression matched all constraints.
+ */
+ bool matches(const std::string& expr) const;
+
+ /**
+ * @brief Check if an expression matches the query constraints.
+ *
+ * `matches` also supports the set of SQL affinite types.
+ * The expression expr will be evaluated as a string and compared using
+ * the affinity of the constraint.
+ *
+ * @param expr a SQL type expression of the column literal type to check.
+ * @return If the expression matched all constraints.
+ */
+ template <typename T>
+ bool matches(const T& expr) const
+ {
+ return matches(SQL_TEXT(expr));
+ }
+
+ /**
+ * @brief Check and return if there are constraints on this column.
+ *
+ * A ConstraintList is used in a ConstraintMap with a column name as the
+ * map index. Tables that act on optional constraints should check if any
+ * constraint was provided. The ops parameter serves to specify which
+ * operators we want to check existence for.
+ *
+ * @param ops (Optional: default ANY_OP) The operators types to look for.
+ * @return true if any constraint exists.
+ */
+ bool exists(ConstraintOperatorFlag ops = ANY_OP) const;
+
+ /**
+ * @brief Check if a constraint exists AND matches the type expression.
+ *
+ * See ConstraintList::exists and ConstraintList::matches.
+ *
+ * @param expr The expression to match.
+ * @return true if any constraint exists AND matches the type expression.
+ */
+ template <typename T>
+ bool existsAndMatches(const T& expr) const
+ {
+ return (exists() && matches(expr));
+ }
+
+ /**
+ * @brief Check if a constraint is missing or matches a type expression.
+ *
+ * A ConstraintList is used in a ConstraintMap with a column name as the
+ * map index. Tables that act on required constraints can make decisions
+ * on missing constraints or a constraint match.
+ *
+ * @param expr The expression to match.
+ * @return true if constraint is missing or matches the type expression.
+ */
+ template <typename T>
+ bool notExistsOrMatches(const T& expr) const
+ {
+ return (!exists() || matches(expr));
+ }
+
+ /**
+ * @brief Helper templated function for ConstraintList::matches.
+ */
+ template <typename T>
+ bool literal_matches(const T& base_expr) const;
+
+ /**
+ * @brief Get all expressions for a given ConstraintOperator.
+ *
+ * This is most useful if the table generation requires as column.
+ * The generator may `getAll(EQUALS)` then iterate.
+ *
+ * @param op the ConstraintOperator.
+ * @return A list of TEXT%-represented types matching the operator.
+ */
+ std::set<std::string> getAll(ConstraintOperator op) const;
+
+ /// See ConstraintList::getAll, but as a selected literal type.
+ template <typename T>
+ std::set<T> getAll(ConstraintOperator op) const;
+
+ /// Constraint list accessor, types and operator.
+ const std::vector<struct Constraint>& getAll() const
+ {
+ return constraints_;
+ }
+
+ /**
+ * @brief Add a new Constraint to the list of constraints.
+ *
+ * @param constraint a new operator/expression to constrain.
+ */
+ void add(const struct Constraint& constraint)
+ {
+ constraints_.push_back(constraint);
+ }
+
+ /**
+ * @brief Serialize a ConstraintList into a property tree.
+ *
+ * The property tree will use the format:
+ * {
+ * "affinity": affinity,
+ * "list": [
+ * {"op": op, "expr": expr}, ...
+ * ]
+ * }
+ */
+ vist::json::Object serialize() const;
+
+ void deserialize(vist::json::Object& obj);
+
+
+private:
+ /// List of constraint operator/expressions.
+ std::vector<struct Constraint> constraints_;
+
+private:
+ friend struct QueryContext;
+
+private:
+ FRIEND_TEST(TablesTests, test_constraint_list);
};
/// Pass a constraint map to the query request.
using UsedColumns = std::unordered_set<std::string>;
/// Keep track of which columns are used, as a bitset
-using UsedColumnsBitset = std::bitset<
- std::numeric_limits<decltype(sqlite3_index_info().colUsed)>::digits>;
+using UsedColumnsBitset = std::bitset <
+ std::numeric_limits<decltype(sqlite3_index_info().colUsed)>::digits >;
/**
* @brief osquery table content descriptor.
* a cache or choose not to generate certain columns.
*/
struct VirtualTableContent {
- /// Friendly name for the table.
- TableName name;
-
- /// Table column structure, retrieved once via the TablePlugin call API.
- TableColumns columns;
-
- /// Attributes are copied into the content such that they can be quickly
- /// passed to the SQL and optional Query for inspection.
- TableAttributes attributes{TableAttributes::NONE};
-
- /**
- * @brief Table column aliases structure.
- *
- * This is used within xColumn to move content from special HIDDEN columns
- * that act as aliases. If these columns are requested the content is moved
- * from the new non-deprecated name.
- */
- std::map<std::string, size_t> aliases;
-
- /// Transient set of virtual table access constraints.
- std::unordered_map<size_t, ConstraintSet> constraints;
-
- /// Transient set of virtual table used columns
- std::unordered_map<size_t, UsedColumns> colsUsed;
-
- /// Transient set of virtual table used columns (as bitmasks)
- std::unordered_map<size_t, UsedColumnsBitset> colsUsedBitsets;
-
- /*
- * @brief A table implementation specific query result cache.
- *
- * Virtual tables may 'cache' information between filter requests. This is
- * intended to provide optimization for very latent/expensive tables where
- * complex joins may result in duplicate filter requests.
- *
- * The cache is implemented as a map of row data. The cache concept
- * should utilize a primary key as an index, and may store arbitrary data.
- * More intense caching may use the backing store though the general database
- * set and get calls.
- *
- * The in-memory, non-backing store, cache is expired after each query run.
- * This caching does not affect or use the schedule results cache.
- */
- std::map<std::string, TableRowHolder> cache;
+ /// Friendly name for the table.
+ TableName name;
+
+ /// Table column structure, retrieved once via the TablePlugin call API.
+ TableColumns columns;
+
+ /// Attributes are copied into the content such that they can be quickly
+ /// passed to the SQL and optional Query for inspection.
+ TableAttributes attributes{TableAttributes::NONE};
+
+ /**
+ * @brief Table column aliases structure.
+ *
+ * This is used within xColumn to move content from special HIDDEN columns
+ * that act as aliases. If these columns are requested the content is moved
+ * from the new non-deprecated name.
+ */
+ std::map<std::string, size_t> aliases;
+
+ /// Transient set of virtual table access constraints.
+ std::unordered_map<size_t, ConstraintSet> constraints;
+
+ /// Transient set of virtual table used columns
+ std::unordered_map<size_t, UsedColumns> colsUsed;
+
+ /// Transient set of virtual table used columns (as bitmasks)
+ std::unordered_map<size_t, UsedColumnsBitset> colsUsedBitsets;
+
+ /*
+ * @brief A table implementation specific query result cache.
+ *
+ * Virtual tables may 'cache' information between filter requests. This is
+ * intended to provide optimization for very latent/expensive tables where
+ * complex joins may result in duplicate filter requests.
+ *
+ * The cache is implemented as a map of row data. The cache concept
+ * should utilize a primary key as an index, and may store arbitrary data.
+ * More intense caching may use the backing store though the general database
+ * set and get calls.
+ *
+ * The in-memory, non-backing store, cache is expired after each query run.
+ * This caching does not affect or use the schedule results cache.
+ */
+ std::map<std::string, TableRowHolder> cache;
};
/**
* on query components like predicate constraints and limits.
*/
struct QueryContext {
- /// Construct a context without cache support.
- QueryContext() {
- table_ = std::make_shared<VirtualTableContent>();
- }
-
- /// If the context was created without content, it is ephemeral.
- ~QueryContext() = default;
-
- /// Construct a context and set the table content for caching.
- explicit QueryContext(std::shared_ptr<VirtualTableContent> content)
- : enable_cache_(true), table_(std::move(content)) {}
-
- /// Disallow copying
- QueryContext(const QueryContext&) = delete;
-
- /// Disallow copy assignment.
- QueryContext& operator=(const QueryContext&) = delete;
-
- /// Allow moving.
- QueryContext(QueryContext&& other)
- : constraints(std::move(other.constraints)),
- colsUsed(std::move(other.colsUsed)),
- enable_cache_(other.enable_cache_),
- use_cache_(other.use_cache_),
- table_(other.table_) {
- other.enable_cache_ = false;
- other.table_ = nullptr;
- }
-
- /// Allow move assignment.
- QueryContext& operator=(QueryContext&& other) {
- std::swap(constraints, other.constraints);
- std::swap(colsUsed, other.colsUsed);
- std::swap(enable_cache_, other.enable_cache_);
- std::swap(use_cache_, other.use_cache_);
- std::swap(table_, other.table_);
-
- return *this;
- }
-
- /**
- * @brief Check if a constraint exists for a given column operator pair.
- *
- * Operator and expression existence and matching occurs on the constraint
- * list for a given column name. The query context maintains a map of columns
- * to potentially empty constraint lists. Check if a constraint exists with
- * any operator or for a specific operator, usually equality (EQUALS).
- *
- * @param column The name of a column within this table.
- * @param op Check for a specific constraint operator (default EQUALS).
- * @return true if a constraint exists, false if empty or no operator match.
- */
- bool hasConstraint(const std::string& column,
- ConstraintOperator op = EQUALS) const;
-
- /**
- * @brief Apply a predicate function to each expression in a constraint list.
- *
- * Most constraint sets are use to extract expressions or perform a row
- * generation for each expressions (given an operator).
- *
- * This prevents the caller (table implementation) from extracting the set
- * and iterating separately on potentially duplicate and copied data. The
- * predicate function is provided two arguments:
- * - An iterating reference to each expression for the given operator.
- *
- * @param column The name of a column within this table.
- * @param op The comparison or expression operator (e.g., EQUALS).
- * @param predicate A predicate receiving each expression.
- */
- template <typename T>
- 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) {
- for (const auto& constraint : list.constraints_) {
- if (constraint.op == op) {
- predicate(constraint.expr);
- }
- }
- } else {
- auto constraint_set = list.getAll<T>(op);
- for (const auto& constraint : constraint_set) {
- predicate(constraint);
- }
- }
- }
- }
-
- /// Helper for string type (most all types are TEXT/VARCHAR).
- void iteritems(const std::string& column,
- ConstraintOperator op,
- std::function<void(const std::string& expr)> predicate) const {
- return iteritems<std::string>(column, op, std::move(predicate));
- }
-
- /**
- * @brief Expand a list of constraints into a set of values.
- *
- * This is most (perhaps only) helpful with filesystem globbing inputs.
- * The requirement is a constraint column that takes an expandable input.
- * This method will accept an expand predicate and return the aggregate set of
- * expanded items.
- *
- * In the future this will be a templated type that restricts the predicate
- * to act on the column's affinite type and returns a similar-typed set.
- *
- * @param column The name of a column within this table.
- * @param op An operator to retrieve from the constraint list.
- * @param output The output parameter, a set of expanded values.
- * @param predicate A predicate lambda to apply to each constraint.
- * @return An aggregate status, if any predicate fails the operation fails.
- */
- Status expandConstraints(
- const std::string& column,
- ConstraintOperator op,
- std::set<std::string>& output,
- std::function<Status(const std::string& constraint,
- std::set<std::string>& output)> predicate);
-
- /// Check if the given column is used by the query
- bool isColumnUsed(const std::string& colName) const;
-
- /// Check if any of the given columns is used by the query
- bool isAnyColumnUsed(std::initializer_list<std::string> colNames) const;
-
- inline bool isAnyColumnUsed(UsedColumnsBitset desiredBitset) const {
- return !colsUsedBitset || (*colsUsedBitset & desiredBitset).any();
- }
-
- template <typename Type>
- inline void setTextColumnIfUsed(Row& r,
- const std::string& colName,
- const Type& value) const {
- if (isColumnUsed(colName)) {
- r[colName] = TEXT(value);
- }
- }
-
- template <typename Type>
- inline void setIntegerColumnIfUsed(Row& r,
- const std::string& colName,
- const Type& value) const {
- if (isColumnUsed(colName)) {
- r[colName] = INTEGER(value);
- }
- }
-
- template <typename Type>
- inline void setBigIntColumnIfUsed(Row& r,
- const std::string& colName,
- const Type& value) const {
- if (isColumnUsed(colName)) {
- r[colName] = BIGINT(value);
- }
- }
-
- inline void setColumnIfUsed(Row& r,
- const std::string& colName,
- const std::string& value) const {
- if (isColumnUsed(colName)) {
- r[colName] = value;
- }
- }
-
- /// Check if a table-defined index exists within the query cache.
- bool isCached(const std::string& index) const;
-
- /// Retrieve an index within the query cache.
- TableRowHolder getCache(const std::string& index);
-
- /// Request the context use the warm query cache.
- void useCache(bool use_cache);
-
- /// Check if the query requested use of the warm query cache.
- bool useCache() const;
-
- /// Set the entire cache for an index.
- void setCache(const std::string& index, const TableRowHolder& _cache);
-
- /// The map of column name to constraint list.
- ConstraintMap constraints;
-
- boost::optional<UsedColumns> colsUsed;
- boost::optional<UsedColumnsBitset> colsUsedBitset;
-
- private:
- /// If false then the context is maintaining an ephemeral cache.
- bool enable_cache_{false};
-
- /// If the context is allowed to use the warm query cache.
- bool use_cache_{false};
-
- /// Persistent table content for table caching.
- std::shared_ptr<VirtualTableContent> table_;
-
- private:
- friend class TablePlugin;
+ /// Construct a context without cache support.
+ QueryContext()
+ {
+ table_ = std::make_shared<VirtualTableContent>();
+ }
+
+ /// If the context was created without content, it is ephemeral.
+ ~QueryContext() = default;
+
+ /// Construct a context and set the table content for caching.
+ explicit QueryContext(std::shared_ptr<VirtualTableContent> content)
+ : enable_cache_(true), table_(std::move(content)) {}
+
+ /// Disallow copying
+ QueryContext(const QueryContext&) = delete;
+
+ /// Disallow copy assignment.
+ QueryContext& operator=(const QueryContext&) = delete;
+
+ /// Allow moving.
+ QueryContext(QueryContext&& other)
+ : constraints(std::move(other.constraints)),
+ colsUsed(std::move(other.colsUsed)),
+ enable_cache_(other.enable_cache_),
+ use_cache_(other.use_cache_),
+ table_(other.table_)
+ {
+ other.enable_cache_ = false;
+ other.table_ = nullptr;
+ }
+
+ /// Allow move assignment.
+ QueryContext& operator=(QueryContext&& other)
+ {
+ std::swap(constraints, other.constraints);
+ std::swap(colsUsed, other.colsUsed);
+ std::swap(enable_cache_, other.enable_cache_);
+ std::swap(use_cache_, other.use_cache_);
+ std::swap(table_, other.table_);
+
+ return *this;
+ }
+
+ /**
+ * @brief Check if a constraint exists for a given column operator pair.
+ *
+ * Operator and expression existence and matching occurs on the constraint
+ * list for a given column name. The query context maintains a map of columns
+ * to potentially empty constraint lists. Check if a constraint exists with
+ * any operator or for a specific operator, usually equality (EQUALS).
+ *
+ * @param column The name of a column within this table.
+ * @param op Check for a specific constraint operator (default EQUALS).
+ * @return true if a constraint exists, false if empty or no operator match.
+ */
+ bool hasConstraint(const std::string& column,
+ ConstraintOperator op = EQUALS) const;
+
+ /**
+ * @brief Apply a predicate function to each expression in a constraint list.
+ *
+ * Most constraint sets are use to extract expressions or perform a row
+ * generation for each expressions (given an operator).
+ *
+ * This prevents the caller (table implementation) from extracting the set
+ * and iterating separately on potentially duplicate and copied data. The
+ * predicate function is provided two arguments:
+ * - An iterating reference to each expression for the given operator.
+ *
+ * @param column The name of a column within this table.
+ * @param op The comparison or expression operator (e.g., EQUALS).
+ * @param predicate A predicate receiving each expression.
+ */
+ template <typename T>
+ 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) {
+ for (const auto& constraint : list.constraints_) {
+ if (constraint.op == op) {
+ predicate(constraint.expr);
+ }
+ }
+ } else {
+ auto constraint_set = list.getAll<T>(op);
+ for (const auto& constraint : constraint_set) {
+ predicate(constraint);
+ }
+ }
+ }
+ }
+
+ /// Helper for string type (most all types are TEXT/VARCHAR).
+ void iteritems(const std::string& column,
+ ConstraintOperator op,
+ std::function<void(const std::string& expr)> predicate) const
+ {
+ return iteritems<std::string>(column, op, std::move(predicate));
+ }
+
+ /**
+ * @brief Expand a list of constraints into a set of values.
+ *
+ * This is most (perhaps only) helpful with filesystem globbing inputs.
+ * The requirement is a constraint column that takes an expandable input.
+ * This method will accept an expand predicate and return the aggregate set of
+ * expanded items.
+ *
+ * In the future this will be a templated type that restricts the predicate
+ * to act on the column's affinite type and returns a similar-typed set.
+ *
+ * @param column The name of a column within this table.
+ * @param op An operator to retrieve from the constraint list.
+ * @param output The output parameter, a set of expanded values.
+ * @param predicate A predicate lambda to apply to each constraint.
+ * @return An aggregate status, if any predicate fails the operation fails.
+ */
+ Status expandConstraints(
+ const std::string& column,
+ ConstraintOperator op,
+ std::set<std::string>& output,
+ std::function<Status(const std::string& constraint,
+ std::set<std::string>& output)> predicate);
+
+ /// Check if the given column is used by the query
+ bool isColumnUsed(const std::string& colName) const;
+
+ /// Check if any of the given columns is used by the query
+ bool isAnyColumnUsed(std::initializer_list<std::string> colNames) const;
+
+ inline bool isAnyColumnUsed(UsedColumnsBitset desiredBitset) const
+ {
+ return !colsUsedBitset || (*colsUsedBitset & desiredBitset).any();
+ }
+
+ template <typename Type>
+ inline void setTextColumnIfUsed(Row& r,
+ const std::string& colName,
+ const Type& value) const
+ {
+ if (isColumnUsed(colName)) {
+ r[colName] = TEXT(value);
+ }
+ }
+
+ template <typename Type>
+ inline void setIntegerColumnIfUsed(Row& r,
+ const std::string& colName,
+ const Type& value) const
+ {
+ if (isColumnUsed(colName)) {
+ r[colName] = INTEGER(value);
+ }
+ }
+
+ template <typename Type>
+ inline void setBigIntColumnIfUsed(Row& r,
+ const std::string& colName,
+ const Type& value) const
+ {
+ if (isColumnUsed(colName)) {
+ r[colName] = BIGINT(value);
+ }
+ }
+
+ inline void setColumnIfUsed(Row& r,
+ const std::string& colName,
+ const std::string& value) const
+ {
+ if (isColumnUsed(colName)) {
+ r[colName] = value;
+ }
+ }
+
+ /// Check if a table-defined index exists within the query cache.
+ bool isCached(const std::string& index) const;
+
+ /// Retrieve an index within the query cache.
+ TableRowHolder getCache(const std::string& index);
+
+ /// Request the context use the warm query cache.
+ void useCache(bool use_cache);
+
+ /// Check if the query requested use of the warm query cache.
+ bool useCache() const;
+
+ /// Set the entire cache for an index.
+ void setCache(const std::string& index, const TableRowHolder& _cache);
+
+ /// The map of column name to constraint list.
+ ConstraintMap constraints;
+
+ boost::optional<UsedColumns> colsUsed;
+ boost::optional<UsedColumnsBitset> colsUsedBitset;
+
+private:
+ /// If false then the context is maintaining an ephemeral cache.
+ bool enable_cache_{false};
+
+ /// If the context is allowed to use the warm query cache.
+ bool use_cache_{false};
+
+ /// Persistent table content for table caching.
+ std::shared_ptr<VirtualTableContent> table_;
+
+private:
+ friend class TablePlugin;
};
using QueryContext = struct QueryContext;
* in osquery/tables/templates/default.cpp.in
*/
class TablePlugin : public Plugin {
- public:
- /**
- * @brief Table name aliases create full-scan VIEWs for tables.
- *
- * Aliases allow table names to be changed/deprecated without breaking
- * existing deployments and scheduled queries.
- *
- * @return A string vector of qtable name aliases.
- */
- virtual std::vector<std::string> aliases() const {
- return {};
- }
-
- /// Return the table's column name and type pairs.
- virtual TableColumns columns() const {
- return TableColumns();
- }
-
- /// Define a map of target columns to optional aliases.
- virtual ColumnAliasSet columnAliases() const {
- return ColumnAliasSet();
- }
-
- /// Define a map of aliases to canoical columns
- virtual AliasColumnMap aliasedColumns() const {
- AliasColumnMap result;
- ColumnAliasSet aliases = columnAliases();
-
- for (const auto& columnAliases : aliases) {
- const auto& columnName = columnAliases.first;
- for (const auto& alias : columnAliases.second) {
- result[alias] = columnName;
- }
- }
-
- return AliasColumnMap();
- }
-
- /// Return a set of attribute flags.
- virtual TableAttributes attributes() const {
- return TableAttributes::NONE;
- }
-
- /**
- * @brief Generate a complete table representation.
- *
- * The TablePlugin::generate method is the most important part of the table.
- * This should return a best-effort match of the expected results for a
- * query. In common cases, this returns all rows for a virtual table.
- * For EventSubscriber tables this will perform database lookups for events
- * matching several conditions such as time within the SQL query or the last
- * time the EventSubscriber was called.
- *
- * The context input is filled in "as best possible" by SQLite's
- * virtual table APIs. In the best case this context include a limit or
- * constraints organized by each possible column.
- *
- * @param context A query context filled in by SQLite's virtual table API.
- * @return The result rows for this table, given the query context.
- */
- virtual TableRows generate(QueryContext& context) {
- (void)context;
- return TableRows();
- }
-
- /// Callback for DELETE statements
- virtual QueryData delete_(QueryContext& context,
- const PluginRequest& request) {
- boost::ignore_unused(context);
- boost::ignore_unused(request);
-
- return {{std::make_pair("status", "readonly")}};
- }
-
- /// Callback for INSERT statements
- virtual QueryData insert(QueryContext& context,
- const PluginRequest& request) {
- boost::ignore_unused(context);
- boost::ignore_unused(request);
-
- return {{std::make_pair("status", "readonly")}};
- }
-
- /// Callback for UPDATE statements
- virtual QueryData update(QueryContext& context,
- const PluginRequest& request) {
- boost::ignore_unused(context);
- boost::ignore_unused(request);
-
- return {{std::make_pair("status", "readonly")}};
- }
-
- protected:
- /// An SQL table containing the table definition/syntax.
- std::string columnDefinition(bool is_extension = false) const;
-
- /// Return the name and column pairs for attaching virtual tables.
- PluginResponse routeInfo() const override;
-
- /**
- * @brief Check if there are fresh cache results for this table.
- *
- * Table results are considered fresh when evaluated against a given interval.
- * The interval is the expected rate for which this data should be generated.
- * Caching and cache freshness only applies to queries acting on tables
- * within a schedule. If two queries "one" and "two" both inspect the
- * table "processes" at the interval 60. The first executed will cache results
- * and the second will use the cached results.
- *
- * Table results are not cached if a QueryContext contains constraints or
- * provides HOB (hand-off blocks) to additional tables within a query.
- * Currently, the query scheduler cannot communicate to table implementations.
- * An interval is set globally by the scheduler and passed to the table
- * implementation as a future-proof API. There is no "shortcut" for caching
- * when used in external tables. A cache lookup within an extension means
- * a database call API and re-serialization to the virtual table APIs. In
- * practice this does not perform well and is explicitly disabled.
- *
- * @param interval The interval this query expects the tables results.
- * @param ctx The query context.
- * @return True if the cache contains fresh results, otherwise false.
- */
- bool isCached(size_t interval, const QueryContext& ctx) const;
-
- /**
- * @brief Perform a database lookup of cached results and deserialize.
- *
- * If a query determined the table's cached results are fresh, it may ask the
- * table to retrieve results from the database and deserialized them into
- * table row data.
- *
- * @return The deserialized row data of cached results.
- */
- TableRows getCache() const;
-
- /**
- * @brief Similar to getCache, stores the results from generate.
- *
- * Set will serialize and save the results as JSON to be retrieved later.
- * It will inspect the query context, if any required/indexed/optimized or
- * additional columns are used then the cache will not be saved.
- */
- void setCache(size_t step,
- size_t interval,
- const QueryContext& ctx,
- const TableRows& results);
-
- private:
- /// The last time in seconds the table data results were saved to cache.
- size_t last_cached_{0};
-
- /// The last interval in seconds when the table data was cached.
- size_t last_interval_{0};
-
- public:
- /**
- * @brief The scheduled interval for the executing query.
- *
- * Scheduled queries execute within a pseudo-mutex, and each may communicate
- * their scheduled interval to internal TablePlugin implementations. If the
- * table is cachable then the interval can be used to calculate freshness.
- */
- static size_t kCacheInterval;
-
- /// The schedule step, this is the current position of the schedule.
- static size_t kCacheStep;
-
- public:
- /**
- * @brief The registry call "router".
- *
- * Like all of osquery's Plugin%s, the TablePlugin uses a "call" router to
- * handle requests and responses from extensions. The TablePlugin uses an
- * "action" key, which can be:
- * - generate: call the plugin's row generate method (defined in spec).
- * - columns: return a list of column name and SQLite types.
- * - definition: return an SQL statement for table creation.
- *
- * @param request The plugin request, must include an action key.
- * @param response A plugin response, for generation this contains the rows.
- */
- Status call(const PluginRequest& request, PluginResponse& response) override;
-
- public:
- /// Helper data structure transformation methods.
- static void setRequestFromContext(const QueryContext& context,
- PluginRequest& request);
-
- public:
- /**
- * @brief Add a virtual table that exists in an extension.
- *
- * When external table plugins are registered the core will attach them
- * as virtual tables to the SQL internal implementation.
- *
- * @param name The table name.
- * @param info The route info (column name and type pairs).
- */
- static Status addExternal(const std::string& name,
- const PluginResponse& info);
-
- /// Remove an extension's table from the SQL virtual database.
- static void removeExternal(const std::string& name);
-
- private:
- /// Helper data structure transformation methods.
- QueryContext getContextFromRequest(const PluginRequest& request) const;
-
- UsedColumnsBitset usedColumnsToBitset(const UsedColumns usedColumns) const;
- friend class RegistryFactory;
- FRIEND_TEST(VirtualTableTests, test_tableplugin_columndefinition);
- FRIEND_TEST(VirtualTableTests, test_tableplugin_statement);
- FRIEND_TEST(VirtualTableTests, test_indexing_costs);
- FRIEND_TEST(VirtualTableTests, test_table_results_cache);
- FRIEND_TEST(VirtualTableTests, test_yield_generator);
+public:
+ /**
+ * @brief Table name aliases create full-scan VIEWs for tables.
+ *
+ * Aliases allow table names to be changed/deprecated without breaking
+ * existing deployments and scheduled queries.
+ *
+ * @return A string vector of qtable name aliases.
+ */
+ virtual std::vector<std::string> aliases() const
+ {
+ return {};
+ }
+
+ /// Return the table's column name and type pairs.
+ virtual TableColumns columns() const
+ {
+ return TableColumns();
+ }
+
+ /// Define a map of target columns to optional aliases.
+ virtual ColumnAliasSet columnAliases() const
+ {
+ return ColumnAliasSet();
+ }
+
+ /// Define a map of aliases to canoical columns
+ virtual AliasColumnMap aliasedColumns() const
+ {
+ AliasColumnMap result;
+ ColumnAliasSet aliases = columnAliases();
+
+ for (const auto& columnAliases : aliases) {
+ const auto& columnName = columnAliases.first;
+ for (const auto& alias : columnAliases.second) {
+ result[alias] = columnName;
+ }
+ }
+
+ return AliasColumnMap();
+ }
+
+ /// Return a set of attribute flags.
+ virtual TableAttributes attributes() const
+ {
+ return TableAttributes::NONE;
+ }
+
+ /**
+ * @brief Generate a complete table representation.
+ *
+ * The TablePlugin::generate method is the most important part of the table.
+ * This should return a best-effort match of the expected results for a
+ * query. In common cases, this returns all rows for a virtual table.
+ * For EventSubscriber tables this will perform database lookups for events
+ * matching several conditions such as time within the SQL query or the last
+ * time the EventSubscriber was called.
+ *
+ * The context input is filled in "as best possible" by SQLite's
+ * virtual table APIs. In the best case this context include a limit or
+ * constraints organized by each possible column.
+ *
+ * @param context A query context filled in by SQLite's virtual table API.
+ * @return The result rows for this table, given the query context.
+ */
+ virtual TableRows generate(QueryContext& context)
+ {
+ (void)context;
+ return TableRows();
+ }
+
+ /// Callback for DELETE statements
+ virtual QueryData delete_(QueryContext& context,
+ const PluginRequest& request)
+ {
+ boost::ignore_unused(context);
+ boost::ignore_unused(request);
+
+ return {{std::make_pair("status", "readonly")}};
+ }
+
+ /// Callback for INSERT statements
+ virtual QueryData insert(QueryContext& context,
+ const PluginRequest& request)
+ {
+ boost::ignore_unused(context);
+ boost::ignore_unused(request);
+
+ return {{std::make_pair("status", "readonly")}};
+ }
+
+ /// Callback for UPDATE statements
+ virtual QueryData update(QueryContext& context,
+ const PluginRequest& request)
+ {
+ boost::ignore_unused(context);
+ boost::ignore_unused(request);
+
+ return {{std::make_pair("status", "readonly")}};
+ }
+
+protected:
+ /// An SQL table containing the table definition/syntax.
+ std::string columnDefinition(bool is_extension = false) const;
+
+ /// Return the name and column pairs for attaching virtual tables.
+ PluginResponse routeInfo() const override;
+
+ /**
+ * @brief Check if there are fresh cache results for this table.
+ *
+ * Table results are considered fresh when evaluated against a given interval.
+ * The interval is the expected rate for which this data should be generated.
+ * Caching and cache freshness only applies to queries acting on tables
+ * within a schedule. If two queries "one" and "two" both inspect the
+ * table "processes" at the interval 60. The first executed will cache results
+ * and the second will use the cached results.
+ *
+ * Table results are not cached if a QueryContext contains constraints or
+ * provides HOB (hand-off blocks) to additional tables within a query.
+ * Currently, the query scheduler cannot communicate to table implementations.
+ * An interval is set globally by the scheduler and passed to the table
+ * implementation as a future-proof API. There is no "shortcut" for caching
+ * when used in external tables. A cache lookup within an extension means
+ * a database call API and re-serialization to the virtual table APIs. In
+ * practice this does not perform well and is explicitly disabled.
+ *
+ * @param interval The interval this query expects the tables results.
+ * @param ctx The query context.
+ * @return True if the cache contains fresh results, otherwise false.
+ */
+ bool isCached(size_t interval, const QueryContext& ctx) const;
+
+ /**
+ * @brief Perform a database lookup of cached results and deserialize.
+ *
+ * If a query determined the table's cached results are fresh, it may ask the
+ * table to retrieve results from the database and deserialized them into
+ * table row data.
+ *
+ * @return The deserialized row data of cached results.
+ */
+ TableRows getCache() const;
+
+ /**
+ * @brief Similar to getCache, stores the results from generate.
+ *
+ * Set will serialize and save the results as JSON to be retrieved later.
+ * It will inspect the query context, if any required/indexed/optimized or
+ * additional columns are used then the cache will not be saved.
+ */
+ void setCache(size_t step,
+ size_t interval,
+ const QueryContext& ctx,
+ const TableRows& results);
+
+private:
+ /// The last time in seconds the table data results were saved to cache.
+ size_t last_cached_{0};
+
+ /// The last interval in seconds when the table data was cached.
+ size_t last_interval_{0};
+
+public:
+ /**
+ * @brief The scheduled interval for the executing query.
+ *
+ * Scheduled queries execute within a pseudo-mutex, and each may communicate
+ * their scheduled interval to internal TablePlugin implementations. If the
+ * table is cachable then the interval can be used to calculate freshness.
+ */
+ static size_t kCacheInterval;
+
+ /// The schedule step, this is the current position of the schedule.
+ static size_t kCacheStep;
+
+public:
+ /**
+ * @brief The registry call "router".
+ *
+ * Like all of osquery's Plugin%s, the TablePlugin uses a "call" router to
+ * handle requests and responses from extensions. The TablePlugin uses an
+ * "action" key, which can be:
+ * - generate: call the plugin's row generate method (defined in spec).
+ * - columns: return a list of column name and SQLite types.
+ * - definition: return an SQL statement for table creation.
+ *
+ * @param request The plugin request, must include an action key.
+ * @param response A plugin response, for generation this contains the rows.
+ */
+ Status call(const PluginRequest& request, PluginResponse& response) override;
+
+public:
+ /// Helper data structure transformation methods.
+ static void setRequestFromContext(const QueryContext& context,
+ PluginRequest& request);
+
+public:
+ /**
+ * @brief Add a virtual table that exists in an extension.
+ *
+ * When external table plugins are registered the core will attach them
+ * as virtual tables to the SQL internal implementation.
+ *
+ * @param name The table name.
+ * @param info The route info (column name and type pairs).
+ */
+ static Status addExternal(const std::string& name,
+ const PluginResponse& info);
+
+ /// Remove an extension's table from the SQL virtual database.
+ static void removeExternal(const std::string& name);
+
+private:
+ /// Helper data structure transformation methods.
+ QueryContext getContextFromRequest(const PluginRequest& request) const;
+
+ UsedColumnsBitset usedColumnsToBitset(const UsedColumns usedColumns) const;
+ friend class RegistryFactory;
+ FRIEND_TEST(VirtualTableTests, test_tableplugin_columndefinition);
+ FRIEND_TEST(VirtualTableTests, test_tableplugin_statement);
+ FRIEND_TEST(VirtualTableTests, test_indexing_costs);
+ FRIEND_TEST(VirtualTableTests, test_table_results_cache);
+ FRIEND_TEST(VirtualTableTests, test_yield_generator);
};
/// Helper method to generate the virtual table CREATE statement.
std::string columnDefinition(const TableColumns& columns,
- bool is_extension = false);
+ bool is_extension = false);
/// Helper method to generate the virtual table CREATE statement.
std::string columnDefinition(const PluginResponse& response,
- bool aliases = false,
- bool is_extension = false);
+ bool aliases = false,
+ bool is_extension = false);
/// Get the string representation for an SQLite column type.
-inline const std::string& columnTypeName(ColumnType type) {
- return kColumnTypeNames.at(type);
+inline const std::string& columnTypeName(ColumnType type)
+{
+ return kColumnTypeNames.at(type);
}
/// Get the column type from the string representation.
namespace osquery {
-RegistryFactory& RegistryFactory::get() {
- static RegistryFactory instance;
- return instance;
+RegistryFactory& RegistryFactory::get()
+{
+ static RegistryFactory instance;
+ return instance;
}
-void RegistryFactory::add(const std::string& name, RegistryInterfaceRef reg) {
- if (exists(name)) {
- throw std::runtime_error("Cannot add duplicate registry: " + name);
- }
- registries_[name] = std::move(reg);
+void RegistryFactory::add(const std::string& name, RegistryInterfaceRef reg)
+{
+ if (exists(name)) {
+ throw std::runtime_error("Cannot add duplicate registry: " + name);
+ }
+ registries_[name] = std::move(reg);
}
-RegistryInterfaceRef RegistryFactory::registry(const std::string& t) const {
- if (!exists(t)) {
- throw std::runtime_error("Unknown registry requested: " + t);
- }
- return registries_.at(t);
+RegistryInterfaceRef RegistryFactory::registry(const std::string& t) const
+{
+ if (!exists(t)) {
+ throw std::runtime_error("Unknown registry requested: " + t);
+ }
+ return registries_.at(t);
}
-std::map<std::string, RegistryInterfaceRef> RegistryFactory::all() const {
- return registries_;
+std::map<std::string, RegistryInterfaceRef> RegistryFactory::all() const
+{
+ return registries_;
}
std::map<std::string, PluginRef> RegistryFactory::plugins(
- const std::string& registry_name) const {
- return registry(registry_name)->plugins();
+ const std::string& registry_name) const
+{
+ return registry(registry_name)->plugins();
}
PluginRef RegistryFactory::plugin(const std::string& registry_name,
- const std::string& item_name) const {
- return registry(registry_name)->plugin(item_name);
+ const std::string& item_name) const
+{
+ return registry(registry_name)->plugin(item_name);
}
/// Adds an alias for an internal registry item. This registry will only
/// broadcast the alias name.
Status RegistryFactory::addAlias(const std::string& registry_name,
- const std::string& item_name,
- const std::string& alias) {
- if (!exists(registry_name)) {
- return Status(1, "Unknown registry: " + registry_name);
- }
- return registries_.at(registry_name)->addAlias(item_name, alias);
+ const std::string& item_name,
+ const std::string& alias)
+{
+ if (!exists(registry_name)) {
+ return Status(1, "Unknown registry: " + registry_name);
+ }
+ return registries_.at(registry_name)->addAlias(item_name, alias);
}
/// Returns the item_name or the item alias if an alias exists.
std::string RegistryFactory::getAlias(const std::string& registry_name,
- const std::string& alias) const {
- if (!exists(registry_name)) {
- return alias;
- }
- return registries_.at(registry_name)->getAlias(alias);
+ const std::string& alias) const
+{
+ if (!exists(registry_name)) {
+ return alias;
+ }
+ return registries_.at(registry_name)->getAlias(alias);
}
Status RegistryFactory::call(const std::string& registry_name,
- const std::string& item_name,
- const PluginRequest& request,
- PluginResponse& response) {
- // Forward factory call to the registry.
- try {
- if (item_name.find(',') != std::string::npos) {
- // Call is multiplexing plugins (usually for multiple loggers).
- for (const auto& item : osquery::split(item_name, ",")) {
- get().registry(registry_name)->call(item, request, response);
- }
- // All multiplexed items are called without regard for statuses.
- return Status(0);
- }
- return get().registry(registry_name)->call(item_name, request, response);
- } catch (const std::exception& e) {
- ERROR(OSQUERY) << registry_name << " registry " << item_name
- << " plugin caused exception: " << e.what();
- return Status(1, e.what());
- } catch (...) {
- ERROR(OSQUERY) << registry_name << " registry " << item_name
- << " plugin caused unknown exception";
- return Status(2, "Unknown exception");
- }
+ const std::string& item_name,
+ const PluginRequest& request,
+ PluginResponse& response)
+{
+ // Forward factory call to the registry.
+ try {
+ if (item_name.find(',') != std::string::npos) {
+ // Call is multiplexing plugins (usually for multiple loggers).
+ for (const auto& item : osquery::split(item_name, ",")) {
+ get().registry(registry_name)->call(item, request, response);
+ }
+ // All multiplexed items are called without regard for statuses.
+ return Status(0);
+ }
+ return get().registry(registry_name)->call(item_name, request, response);
+ } catch (const std::exception& e) {
+ ERROR(OSQUERY) << registry_name << " registry " << item_name
+ << " plugin caused exception: " << e.what();
+ return Status(1, e.what());
+ } catch (...) {
+ ERROR(OSQUERY) << registry_name << " registry " << item_name
+ << " plugin caused unknown exception";
+ return Status(2, "Unknown exception");
+ }
}
Status RegistryFactory::call(const std::string& registry_name,
- const std::string& item_name,
- const PluginRequest& request) {
- PluginResponse response;
- // Wrapper around a call expecting a response.
- return call(registry_name, item_name, request, response);
+ const std::string& item_name,
+ const PluginRequest& request)
+{
+ PluginResponse response;
+ // Wrapper around a call expecting a response.
+ return call(registry_name, item_name, request, response);
}
Status RegistryFactory::call(const std::string& registry_name,
- const PluginRequest& request,
- PluginResponse& response) {
- auto plugin = get().registry(registry_name)->getActive();
- return call(registry_name, plugin, request, response);
+ const PluginRequest& request,
+ PluginResponse& response)
+{
+ auto plugin = get().registry(registry_name)->getActive();
+ return call(registry_name, plugin, request, response);
}
Status RegistryFactory::call(const std::string& registry_name,
- const PluginRequest& request) {
- PluginResponse response;
- return call(registry_name, request, response);
+ const PluginRequest& request)
+{
+ PluginResponse response;
+ return call(registry_name, request, response);
}
Status RegistryFactory::setActive(const std::string& registry_name,
- const std::string& item_name) {
- WriteLock lock(mutex_);
- return registry(registry_name)->setActive(item_name);
+ const std::string& item_name)
+{
+ WriteLock lock(mutex_);
+ return registry(registry_name)->setActive(item_name);
}
-std::string RegistryFactory::getActive(const std::string& registry_name) const {
- return registry(registry_name)->getActive();
+std::string RegistryFactory::getActive(const std::string& registry_name) const
+{
+ return registry(registry_name)->getActive();
}
-void RegistryFactory::setUp() {
- for (const auto& registry : get().all()) {
- registry.second->setUp();
- }
+void RegistryFactory::setUp()
+{
+ for (const auto& registry : get().all()) {
+ registry.second->setUp();
+ }
}
bool RegistryFactory::exists(const std::string& registry_name,
- const std::string& item_name,
- bool local) const {
- if (!exists(registry_name)) {
- return false;
- }
+ const std::string& item_name,
+ bool local) const
+{
+ if (!exists(registry_name)) {
+ return false;
+ }
- // Check the registry.
- return registry(registry_name)->exists(item_name, local);
+ // Check the registry.
+ return registry(registry_name)->exists(item_name, local);
}
-std::vector<std::string> RegistryFactory::names() const {
- std::vector<std::string> names;
- for (const auto& registry : all()) {
- names.push_back(registry.second->getName());
- }
- return names;
+std::vector<std::string> RegistryFactory::names() const
+{
+ std::vector<std::string> names;
+ for (const auto& registry : all()) {
+ names.push_back(registry.second->getName());
+ }
+ return names;
}
std::vector<std::string> RegistryFactory::names(
- const std::string& registry_name) const {
- if (registries_.at(registry_name) == nullptr) {
- std::vector<std::string> names;
- return names;
- }
- return registry(registry_name)->names();
-}
-
-size_t RegistryFactory::count(const std::string& registry_name) const {
- if (!exists(registry_name)) {
- return 0;
- }
- return registry(registry_name)->count();
+ const std::string& registry_name) const
+{
+ if (registries_.at(registry_name) == nullptr) {
+ std::vector<std::string> names;
+ return names;
+ }
+ return registry(registry_name)->names();
+}
+
+size_t RegistryFactory::count(const std::string& registry_name) const
+{
+ if (!exists(registry_name)) {
+ return 0;
+ }
+ return registry(registry_name)->count();
}
} // namespace osquery
#include <osquery/utils/conversions/split.h>
namespace osquery {
-void RegistryInterface::remove(const std::string& item_name) {
- if (items_.count(item_name) > 0) {
- items_[item_name]->tearDown();
- items_.erase(item_name);
- }
-
- // Populate list of aliases to remove (those that mask item_name).
- std::vector<std::string> removed_aliases;
- for (const auto& alias : aliases_) {
- if (alias.second == item_name) {
- removed_aliases.push_back(alias.first);
- }
- }
-
- for (const auto& alias : removed_aliases) {
- aliases_.erase(alias);
- }
+void RegistryInterface::remove(const std::string& item_name)
+{
+ if (items_.count(item_name) > 0) {
+ items_[item_name]->tearDown();
+ items_.erase(item_name);
+ }
+
+ // Populate list of aliases to remove (those that mask item_name).
+ std::vector<std::string> removed_aliases;
+ for (const auto& alias : aliases_) {
+ if (alias.second == item_name) {
+ removed_aliases.push_back(alias.first);
+ }
+ }
+
+ for (const auto& alias : removed_aliases) {
+ aliases_.erase(alias);
+ }
}
-bool RegistryInterface::isInternal(const std::string& item_name) const {
- ReadLock lock(mutex_);
+bool RegistryInterface::isInternal(const std::string& item_name) const
+{
+ ReadLock lock(mutex_);
- return isInternal_(item_name);
+ return isInternal_(item_name);
}
-std::string RegistryInterface::getActive() const {
- ReadLock lock(mutex_);
+std::string RegistryInterface::getActive() const
+{
+ ReadLock lock(mutex_);
- return active_;
+ return active_;
}
-std::string RegistryInterface::getName() const {
- ReadLock lock(mutex_);
+std::string RegistryInterface::getName() const
+{
+ ReadLock lock(mutex_);
- return name_;
+ return name_;
}
-size_t RegistryInterface::count() const {
- ReadLock lock(mutex_);
+size_t RegistryInterface::count() const
+{
+ ReadLock lock(mutex_);
- return items_.size();
+ return items_.size();
}
-Status RegistryInterface::setActive(const std::string& item_name) {
-// FIXME
-// UpgradeLock lock(mutex_);
-
- // Default support multiple active plugins.
- for (const auto& item : osquery::split(item_name, ",")) {
- if (items_.count(item) == 0) {
- return Status::failure("Unknown registry plugin: " + item);
- }
- }
-
- Status status;
- {
-// FIXME
-// WriteUpgradeLock wlock(lock);
- active_ = item_name;
- }
-
- // The active plugin is setup when initialized.
- for (const auto& item : osquery::split(item_name, ",")) {
- if (exists_(item, true)) {
- status = RegistryFactory::get().plugin(name_, item)->setUp();
- }
-
- if (!status.ok()) {
- break;
- }
- }
- return status;
+Status RegistryInterface::setActive(const std::string& item_name)
+{
+ // FIXME
+ // UpgradeLock lock(mutex_);
+
+ // Default support multiple active plugins.
+ for (const auto& item : osquery::split(item_name, ",")) {
+ if (items_.count(item) == 0) {
+ return Status::failure("Unknown registry plugin: " + item);
+ }
+ }
+
+ Status status;
+ {
+ // FIXME
+ // WriteUpgradeLock wlock(lock);
+ active_ = item_name;
+ }
+
+ // The active plugin is setup when initialized.
+ for (const auto& item : osquery::split(item_name, ",")) {
+ if (exists_(item, true)) {
+ status = RegistryFactory::get().plugin(name_, item)->setUp();
+ }
+
+ if (!status.ok()) {
+ break;
+ }
+ }
+ return status;
}
Status RegistryInterface::call(const std::string& item_name,
- const PluginRequest& request,
- PluginResponse& response) {
- if (item_name.empty()) {
- return Status::failure("No registry item name specified");
- }
- PluginRef plugin;
- {
- ReadLock lock(mutex_);
-
- // Search local plugins (items) for the plugin.
- if (items_.count(item_name) > 0) {
- plugin = items_.at(item_name);
- }
- }
- if (plugin) {
- return plugin->call(request, response);
- }
-
- return Status::failure("Unknown registry name: " + item_name);
+ const PluginRequest& request,
+ PluginResponse& response)
+{
+ if (item_name.empty()) {
+ return Status::failure("No registry item name specified");
+ }
+ PluginRef plugin;
+ {
+ ReadLock lock(mutex_);
+
+ // Search local plugins (items) for the plugin.
+ if (items_.count(item_name) > 0) {
+ plugin = items_.at(item_name);
+ }
+ }
+ if (plugin) {
+ return plugin->call(request, response);
+ }
+
+ return Status::failure("Unknown registry name: " + item_name);
}
Status RegistryInterface::addAlias(const std::string& item_name,
- const std::string& alias) {
- WriteLock lock(mutex_);
-
- if (aliases_.count(alias) > 0) {
- return Status::failure("Duplicate alias: " + alias);
- }
- aliases_[alias] = item_name;
- return Status::success();
+ const std::string& alias)
+{
+ WriteLock lock(mutex_);
+
+ if (aliases_.count(alias) > 0) {
+ return Status::failure("Duplicate alias: " + alias);
+ }
+ aliases_[alias] = item_name;
+ return Status::success();
}
-std::string RegistryInterface::getAlias(const std::string& alias) const {
- ReadLock lock(mutex_);
+std::string RegistryInterface::getAlias(const std::string& alias) const
+{
+ ReadLock lock(mutex_);
- if (aliases_.count(alias) == 0) {
- return alias;
- }
- return aliases_.at(alias);
+ if (aliases_.count(alias) == 0) {
+ return alias;
+ }
+ return aliases_.at(alias);
}
Status RegistryInterface::addPlugin(const std::string& plugin_name,
- const PluginRef& plugin_item,
- bool internal) {
- WriteLock lock(mutex_);
+ const PluginRef& plugin_item,
+ bool internal)
+{
+ WriteLock lock(mutex_);
- if (items_.count(plugin_name) > 0) {
- return Status::failure("Duplicate registry item exists: " + plugin_name);
- }
+ if (items_.count(plugin_name) > 0) {
+ return Status::failure("Duplicate registry item exists: " + plugin_name);
+ }
- plugin_item->setName(plugin_name);
- items_.emplace(std::make_pair(plugin_name, plugin_item));
+ plugin_item->setName(plugin_name);
+ items_.emplace(std::make_pair(plugin_name, plugin_item));
- // The item can be listed as internal, meaning it does not broadcast.
- if (internal) {
- internal_.push_back(plugin_name);
- }
+ // The item can be listed as internal, meaning it does not broadcast.
+ if (internal) {
+ internal_.push_back(plugin_name);
+ }
- return Status::success();
+ return Status::success();
}
-void RegistryInterface::setUp() {
- ReadLock lock(mutex_);
-
- // If this registry does not auto-setup do NOT setup the registry items.
- if (!auto_setup_) {
- return;
- }
-
- // If the registry is using a single 'active' plugin, setUp that plugin.
- // For config and logger, only setUp the selected plugin.
- if (active_.size() != 0 && exists_(active_, true)) {
- items_.at(active_)->setUp();
- return;
- }
-
- // Try to set up each of the registry items.
- // If they fail, remove them from the registry.
- std::vector<std::string> failed;
- for (auto& item : items_) {
- if (!item.second->setUp().ok()) {
- failed.push_back(item.first);
- }
- }
-
- for (const auto& failed_item : failed) {
- remove(failed_item);
- }
+void RegistryInterface::setUp()
+{
+ ReadLock lock(mutex_);
+
+ // If this registry does not auto-setup do NOT setup the registry items.
+ if (!auto_setup_) {
+ return;
+ }
+
+ // If the registry is using a single 'active' plugin, setUp that plugin.
+ // For config and logger, only setUp the selected plugin.
+ if (active_.size() != 0 && exists_(active_, true)) {
+ items_.at(active_)->setUp();
+ return;
+ }
+
+ // Try to set up each of the registry items.
+ // If they fail, remove them from the registry.
+ std::vector<std::string> failed;
+ for (auto& item : items_) {
+ if (!item.second->setUp().ok()) {
+ failed.push_back(item.first);
+ }
+ }
+
+ for (const auto& failed_item : failed) {
+ remove(failed_item);
+ }
}
-void RegistryInterface::configure() {
- ReadLock lock(mutex_);
-
- if (!active_.empty() && exists_(active_, true)) {
- items_.at(active_)->configure();
- } else {
- for (auto& item : items_) {
- item.second->configure();
- }
- }
+void RegistryInterface::configure()
+{
+ ReadLock lock(mutex_);
+
+ if (!active_.empty() && exists_(active_, true)) {
+ items_.at(active_)->configure();
+ } else {
+ for (auto& item : items_) {
+ item.second->configure();
+ }
+ }
}
/// Facility method to check if a registry item exists.
-bool RegistryInterface::exists(const std::string& item_name, bool local) const {
- ReadLock lock(mutex_);
+bool RegistryInterface::exists(const std::string& item_name, bool local) const
+{
+ ReadLock lock(mutex_);
- return exists_(item_name, local);
+ return exists_(item_name, local);
}
/// Facility method to list the registry item identifiers.
-std::vector<std::string> RegistryInterface::names() const {
- ReadLock lock(mutex_);
+std::vector<std::string> RegistryInterface::names() const
+{
+ ReadLock lock(mutex_);
- std::vector<std::string> names;
- for (const auto& item : items_) {
- names.push_back(item.first);
- }
+ std::vector<std::string> names;
+ for (const auto& item : items_) {
+ names.push_back(item.first);
+ }
- return names;
+ return names;
}
-std::map<std::string, PluginRef> RegistryInterface::plugins() {
- ReadLock lock(mutex_);
+std::map<std::string, PluginRef> RegistryInterface::plugins()
+{
+ ReadLock lock(mutex_);
- return items_;
+ return items_;
}
-void RegistryInterface::setname(const std::string& name) {
- WriteLock lock(mutex_);
+void RegistryInterface::setname(const std::string& name)
+{
+ WriteLock lock(mutex_);
- name_ = name;
+ name_ = name;
}
-bool RegistryInterface::isInternal_(const std::string& item_name) const {
- if (std::find(internal_.begin(), internal_.end(), item_name) ==
- internal_.end()) {
- return false;
- }
- return true;
+bool RegistryInterface::isInternal_(const std::string& item_name) const
+{
+ if (std::find(internal_.begin(), internal_.end(), item_name) ==
+ internal_.end()) {
+ return false;
+ }
+ return true;
}
bool RegistryInterface::exists_(const std::string& item_name,
- bool local) const {
- return (items_.count(item_name) > 0);
+ bool local) const
+{
+ return (items_.count(item_name) > 0);
}
AutoRegisterInterface::AutoRegisterInterface(const char* _type,
- const char* _name,
- bool optional)
- : type_(_type), name_(_name), optional_(optional) {}
-
-AutoRegisterSet& AutoRegisterInterface::registries() {
- static AutoRegisterSet registries_;
- return registries_;
+ const char* _name,
+ bool optional)
+ : type_(_type), name_(_name), optional_(optional) {}
+
+AutoRegisterSet& AutoRegisterInterface::registries()
+{
+ static AutoRegisterSet registries_;
+ return registries_;
}
-AutoRegisterSet& AutoRegisterInterface::plugins() {
- static AutoRegisterSet plugins_;
- return plugins_;
+AutoRegisterSet& AutoRegisterInterface::plugins()
+{
+ static AutoRegisterSet plugins_;
+ return plugins_;
}
void AutoRegisterInterface::autoloadRegistry(
- std::unique_ptr<AutoRegisterInterface> ar_) {
- registries().push_back(std::move(ar_));
+ std::unique_ptr<AutoRegisterInterface> ar_)
+{
+ registries().push_back(std::move(ar_));
}
void AutoRegisterInterface::autoloadPlugin(
- std::unique_ptr<AutoRegisterInterface> ar_) {
- plugins().push_back(std::move(ar_));
+ std::unique_ptr<AutoRegisterInterface> ar_)
+{
+ plugins().push_back(std::move(ar_));
}
-void registryAndPluginInit() {
- for (const auto& it : AutoRegisterInterface::registries()) {
- it->run();
- }
+void registryAndPluginInit()
+{
+ for (const auto& it : AutoRegisterInterface::registries()) {
+ it->run();
+ }
- for (const auto& it : AutoRegisterInterface::plugins()) {
- it->run();
- }
+ for (const auto& it : AutoRegisterInterface::plugins()) {
+ it->run();
+ }
- AutoRegisterSet().swap(AutoRegisterInterface::registries());
- AutoRegisterSet().swap(AutoRegisterInterface::plugins());
+ AutoRegisterSet().swap(AutoRegisterInterface::registries());
+ AutoRegisterSet().swap(AutoRegisterInterface::plugins());
}
} // namespace osquery
class TestCoreRegistry : public RegistryFactory {};
class CatPlugin : public Plugin {
- public:
- CatPlugin() : some_value_(0) {}
+public:
+ CatPlugin() : some_value_(0) {}
- Status call(const PluginRequest&, PluginResponse&) override {
- return Status(0);
- }
+ Status call(const PluginRequest&, PluginResponse&) override
+ {
+ return Status(0);
+ }
- protected:
- int some_value_;
+protected:
+ int some_value_;
};
class DogPlugin : public Plugin {
- public:
- DogPlugin() : some_value_(10000) {}
+public:
+ DogPlugin() : some_value_(10000) {}
- Status call(const PluginRequest&, PluginResponse&) override {
- return Status(0);
- }
+ Status call(const PluginRequest&, PluginResponse&) override
+ {
+ return Status(0);
+ }
- protected:
- int some_value_;
+protected:
+ int some_value_;
};
class RegistryTests : public testing::Test {
- public:
- void SetUp() override {
- if (!kSetUp) {
- TestCoreRegistry::get().add(
- "cat", std::make_shared<RegistryType<CatPlugin>>("cat"));
- TestCoreRegistry::get().add(
- "dog", std::make_shared<RegistryType<DogPlugin>>("dog"));
- kSetUp = true;
- }
- }
-
- static bool kSetUp;
+public:
+ void SetUp() override
+ {
+ if (!kSetUp) {
+ TestCoreRegistry::get().add(
+ "cat", std::make_shared<RegistryType<CatPlugin>>("cat"));
+ TestCoreRegistry::get().add(
+ "dog", std::make_shared<RegistryType<DogPlugin>>("dog"));
+ kSetUp = true;
+ }
+ }
+
+ static bool kSetUp;
};
bool RegistryTests::kSetUp{false};
class HouseCat : public CatPlugin {
- public:
- Status setUp() {
- // Make sure the Plugin implementation's init is called.
- some_value_ = 9000;
- return Status::success();
- }
+public:
+ Status setUp()
+ {
+ // Make sure the Plugin implementation's init is called.
+ some_value_ = 9000;
+ return Status::success();
+ }
};
/// This is a manual registry type without a name, so we cannot broadcast
/// this registry type and it does NOT need to conform to a registry API.
class CatRegistry : public RegistryType<CatPlugin> {
- public:
- CatRegistry(const std::string& name) : RegistryType(name) {}
+public:
+ CatRegistry(const std::string& name) : RegistryType(name) {}
};
-TEST_F(RegistryTests, test_registry) {
- CatRegistry cats("cats");
+TEST_F(RegistryTests, test_registry)
+{
+ CatRegistry cats("cats");
- /// Add a CatRegistry item (a plugin) called "house".
- cats.add("house", std::make_shared<HouseCat>());
- EXPECT_EQ(cats.count(), 1U);
+ /// Add a CatRegistry item (a plugin) called "house".
+ cats.add("house", std::make_shared<HouseCat>());
+ EXPECT_EQ(cats.count(), 1U);
- /// Try to add the same plugin with the same name, this is meaningless.
- cats.add("house", std::make_shared<HouseCat>());
+ /// Try to add the same plugin with the same name, this is meaningless.
+ cats.add("house", std::make_shared<HouseCat>());
- /// Now add the same plugin with a different name, a new plugin instance
- /// will be created and registered.
- cats.add("house2", std::make_shared<HouseCat>());
- EXPECT_EQ(cats.count(), 2U);
+ /// Now add the same plugin with a different name, a new plugin instance
+ /// will be created and registered.
+ cats.add("house2", std::make_shared<HouseCat>());
+ EXPECT_EQ(cats.count(), 2U);
- /// Request a plugin to call an API method.
- auto cat = cats.plugin("house");
- cats.setUp();
+ /// Request a plugin to call an API method.
+ auto cat = cats.plugin("house");
+ cats.setUp();
- /// Now let's iterate over every registered Cat plugin.
- EXPECT_EQ(cats.plugins().size(), 2U);
+ /// Now let's iterate over every registered Cat plugin.
+ EXPECT_EQ(cats.plugins().size(), 2U);
}
-TEST_F(RegistryTests, test_auto_factory) {
- /// Using the registry, and a registry type by name, we can register a
- /// plugin HouseCat called "house" like above.
- auto cat_registry = TestCoreRegistry::get().registry("cat");
- cat_registry->add("auto_house", std::make_shared<HouseCat>());
- cat_registry->setUp();
-
- /// When acting on registries by name we can check the broadcasted
- /// registry name of other plugin processes (via Thrift) as well as
- /// internally registered plugins like HouseCat.
- EXPECT_EQ(TestCoreRegistry::get().registry("cat")->count(), 1U);
- EXPECT_EQ(TestCoreRegistry::get().count("cat"), 1U);
-
- /// And we can call an API method, since we guarantee CatPlugins conform
- /// to the "TestCoreRegistry"'s "TestPluginAPI".
- auto cat = TestCoreRegistry::get().plugin("cat", "auto_house");
- auto same_cat = TestCoreRegistry::get().plugin("cat", "auto_house");
- EXPECT_EQ(cat, same_cat);
+TEST_F(RegistryTests, test_auto_factory)
+{
+ /// Using the registry, and a registry type by name, we can register a
+ /// plugin HouseCat called "house" like above.
+ auto cat_registry = TestCoreRegistry::get().registry("cat");
+ cat_registry->add("auto_house", std::make_shared<HouseCat>());
+ cat_registry->setUp();
+
+ /// When acting on registries by name we can check the broadcasted
+ /// registry name of other plugin processes (via Thrift) as well as
+ /// internally registered plugins like HouseCat.
+ EXPECT_EQ(TestCoreRegistry::get().registry("cat")->count(), 1U);
+ EXPECT_EQ(TestCoreRegistry::get().count("cat"), 1U);
+
+ /// And we can call an API method, since we guarantee CatPlugins conform
+ /// to the "TestCoreRegistry"'s "TestPluginAPI".
+ auto cat = TestCoreRegistry::get().plugin("cat", "auto_house");
+ auto same_cat = TestCoreRegistry::get().plugin("cat", "auto_house");
+ EXPECT_EQ(cat, same_cat);
}
class Doge : public DogPlugin {
- public:
- Doge() {
- some_value_ = 100000;
- }
+public:
+ Doge()
+ {
+ some_value_ = 100000;
+ }
};
class BadDoge : public DogPlugin {
- public:
- Status setUp() {
- return Status(1, "Expect error... this is a bad dog");
- }
+public:
+ Status setUp()
+ {
+ return Status(1, "Expect error... this is a bad dog");
+ }
};
-TEST_F(RegistryTests, test_auto_registries) {
- auto dog_registry = TestCoreRegistry::get().registry("dog");
- dog_registry->add("doge", std::make_shared<Doge>());
- dog_registry->setUp();
+TEST_F(RegistryTests, test_auto_registries)
+{
+ auto dog_registry = TestCoreRegistry::get().registry("dog");
+ dog_registry->add("doge", std::make_shared<Doge>());
+ dog_registry->setUp();
- EXPECT_EQ(TestCoreRegistry::get().count("dog"), 1U);
+ EXPECT_EQ(TestCoreRegistry::get().count("dog"), 1U);
}
-TEST_F(RegistryTests, test_persistent_registries) {
- EXPECT_EQ(TestCoreRegistry::get().count("cat"), 1U);
+TEST_F(RegistryTests, test_persistent_registries)
+{
+ EXPECT_EQ(TestCoreRegistry::get().count("cat"), 1U);
}
-TEST_F(RegistryTests, test_registry_exceptions) {
- auto dog_registry = TestCoreRegistry::get().registry("dog");
- EXPECT_TRUE(dog_registry->add("doge2", std::make_shared<Doge>()).ok());
- // Bad dog will be added fine.
- EXPECT_TRUE(dog_registry->add("bad_doge", std::make_shared<BadDoge>()).ok());
- dog_registry->setUp();
- // Make sure bad dog does exist.
- EXPECT_TRUE(TestCoreRegistry::get().exists("dog", "bad_doge"));
- EXPECT_EQ(TestCoreRegistry::get().count("dog"), 3U);
-
- unsigned int exception_count = 0;
- try {
- TestCoreRegistry::get().registry("does_not_exist");
- } catch (const std::runtime_error& /* e */) {
- exception_count++;
- }
-
- EXPECT_EQ(exception_count, 1U);
+TEST_F(RegistryTests, test_registry_exceptions)
+{
+ auto dog_registry = TestCoreRegistry::get().registry("dog");
+ EXPECT_TRUE(dog_registry->add("doge2", std::make_shared<Doge>()).ok());
+ // Bad dog will be added fine.
+ EXPECT_TRUE(dog_registry->add("bad_doge", std::make_shared<BadDoge>()).ok());
+ dog_registry->setUp();
+ // Make sure bad dog does exist.
+ EXPECT_TRUE(TestCoreRegistry::get().exists("dog", "bad_doge"));
+ EXPECT_EQ(TestCoreRegistry::get().count("dog"), 3U);
+
+ unsigned int exception_count = 0;
+ try {
+ TestCoreRegistry::get().registry("does_not_exist");
+ } catch (const std::runtime_error& /* e */) {
+ exception_count++;
+ }
+
+ EXPECT_EQ(exception_count, 1U);
}
class WidgetPlugin : public Plugin {
- public:
- /// The route information will usually be provided by the plugin type.
- /// The plugin/registry item will set some structures for the plugin
- /// to parse and format. BUT a plugin/registry item can also fill this
- /// information in if the plugin type/registry type exposes routeInfo as
- /// a virtual method.
- PluginResponse routeInfo() const {
- PluginResponse info;
- info.push_back({{"name", name_}});
- return info;
- }
-
- /// Plugin types should contain generic request/response formatters and
- /// decorators.
- std::string secretPower(const PluginRequest& request) const {
- if (request.count("secret_power") > 0U) {
- return request.at("secret_power");
- }
- return "no_secret_power";
- }
+public:
+ /// The route information will usually be provided by the plugin type.
+ /// The plugin/registry item will set some structures for the plugin
+ /// to parse and format. BUT a plugin/registry item can also fill this
+ /// information in if the plugin type/registry type exposes routeInfo as
+ /// a virtual method.
+ PluginResponse routeInfo() const
+ {
+ PluginResponse info;
+ info.push_back({{"name", name_}});
+ return info;
+ }
+
+ /// Plugin types should contain generic request/response formatters and
+ /// decorators.
+ std::string secretPower(const PluginRequest& request) const
+ {
+ if (request.count("secret_power") > 0U) {
+ return request.at("secret_power");
+ }
+ return "no_secret_power";
+ }
};
class SpecialWidget : public WidgetPlugin {
- public:
- Status call(const PluginRequest& request, PluginResponse& response);
+public:
+ Status call(const PluginRequest& request, PluginResponse& response);
};
Status SpecialWidget::call(const PluginRequest& request,
- PluginResponse& response) {
- response.push_back(request);
- response[0]["from"] = name_;
- response[0]["secret_power"] = secretPower(request);
- return Status::success();
+ PluginResponse& response)
+{
+ response.push_back(request);
+ response[0]["from"] = name_;
+ response[0]["secret_power"] = secretPower(request);
+ return Status::success();
}
#define UNUSED(x) (void)(x)
-TEST_F(RegistryTests, test_registry_api) {
- TestCoreRegistry::get().add(
- "widgets", std::make_shared<RegistryType<WidgetPlugin>>("widgets"));
+TEST_F(RegistryTests, test_registry_api)
+{
+ TestCoreRegistry::get().add(
+ "widgets", std::make_shared<RegistryType<WidgetPlugin>>("widgets"));
- auto widgets = TestCoreRegistry::get().registry("widgets");
- widgets->add("special", std::make_shared<SpecialWidget>());
+ auto widgets = TestCoreRegistry::get().registry("widgets");
+ widgets->add("special", std::make_shared<SpecialWidget>());
- // Test route info propagation, from item to registry, to broadcast.
- auto ri = TestCoreRegistry::get().plugin("widgets", "special")->routeInfo();
- EXPECT_EQ(ri[0].at("name"), "special");
+ // Test route info propagation, from item to registry, to broadcast.
+ auto ri = TestCoreRegistry::get().plugin("widgets", "special")->routeInfo();
+ EXPECT_EQ(ri[0].at("name"), "special");
- PluginResponse response;
- PluginRequest request;
- auto status = TestCoreRegistry::call("widgets", "special", request, response);
- EXPECT_TRUE(status.ok());
- EXPECT_EQ(response[0].at("from"), "special");
- EXPECT_EQ(response[0].at("secret_power"), "no_secret_power");
+ PluginResponse response;
+ PluginRequest request;
+ auto status = TestCoreRegistry::call("widgets", "special", request, response);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(response[0].at("from"), "special");
+ EXPECT_EQ(response[0].at("secret_power"), "no_secret_power");
- request["secret_power"] = "magic";
- status = TestCoreRegistry::call("widgets", "special", request, response);
- EXPECT_EQ(response[0].at("secret_power"), "magic");
+ request["secret_power"] = "magic";
+ status = TestCoreRegistry::call("widgets", "special", request, response);
+ EXPECT_EQ(response[0].at("secret_power"), "magic");
}
-TEST_F(RegistryTests, test_real_registry) {
- EXPECT_TRUE(Registry::get().count() > 0U);
-
- bool has_one_registered = false;
- for (const auto& registry : Registry::get().all()) {
- if (Registry::get().count(registry.first) > 0) {
- has_one_registered = true;
- break;
- }
- }
- EXPECT_TRUE(has_one_registered);
+TEST_F(RegistryTests, test_real_registry)
+{
+ EXPECT_TRUE(Registry::get().count() > 0U);
+
+ bool has_one_registered = false;
+ for (const auto& registry : Registry::get().all()) {
+ if (Registry::get().count(registry.first) > 0) {
+ has_one_registered = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(has_one_registered);
}
}
namespace osquery {
-TableRows tableRowsFromQueryData(QueryData&& rows) {
- TableRows result;
+TableRows tableRowsFromQueryData(QueryData&& rows)
+{
+ TableRows result;
- for (auto&& row : rows) {
- result.push_back(TableRowHolder(new DynamicTableRow(std::move(row))));
- }
+ for (auto&& row : rows) {
+ result.push_back(TableRowHolder(new DynamicTableRow(std::move(row))));
+ }
- return result;
+ return result;
}
int DynamicTableRow::get_rowid(sqlite_int64 default_value,
- sqlite_int64* pRowid) const {
- auto& current_row = this->row;
- auto rowid_it = current_row.find("rowid");
- if (rowid_it != current_row.end()) {
- const auto& rowid_text_field = rowid_it->second;
+ sqlite_int64* pRowid) const
+{
+ auto& current_row = this->row;
+ auto rowid_it = current_row.find("rowid");
+ if (rowid_it != current_row.end()) {
+ const auto& rowid_text_field = rowid_it->second;
- auto exp = tryTo<long long>(rowid_text_field, 10);
- if (exp.isError()) {
- DEBUG(OSQUERY) << "Invalid rowid value returned " << exp.getError();
- return SQLITE_ERROR;
- }
- *pRowid = exp.take();
+ auto exp = tryTo<long long>(rowid_text_field, 10);
+ if (exp.isError()) {
+ DEBUG(OSQUERY) << "Invalid rowid value returned " << exp.getError();
+ return SQLITE_ERROR;
+ }
+ *pRowid = exp.take();
- } else {
- *pRowid = default_value;
- }
- return SQLITE_OK;
+ } else {
+ *pRowid = default_value;
+ }
+ return SQLITE_OK;
}
int DynamicTableRow::get_column(sqlite3_context* ctx,
- sqlite3_vtab* vtab,
- int col) {
- VirtualTable* pVtab = (VirtualTable*)vtab;
- auto& column_name = std::get<0>(pVtab->content->columns[col]);
- auto& type = std::get<1>(pVtab->content->columns[col]);
- if (pVtab->content->aliases.count(column_name)) {
- // Overwrite the aliased column with the type and name of the new column.
- type = std::get<1>(
- pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
- column_name = std::get<0>(
- pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
- }
+ sqlite3_vtab* vtab,
+ int col)
+{
+ VirtualTable* pVtab = (VirtualTable*)vtab;
+ auto& column_name = std::get<0>(pVtab->content->columns[col]);
+ auto& type = std::get<1>(pVtab->content->columns[col]);
+ if (pVtab->content->aliases.count(column_name)) {
+ // Overwrite the aliased column with the type and name of the new column.
+ type = std::get<1>(
+ pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
+ column_name = std::get<0>(
+ pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
+ }
- // Attempt to cast each xFilter-populated row/column to the SQLite type.
- const auto& value = row[column_name];
- if (this->row.count(column_name) == 0) {
- // Missing content.
- DEBUG(OSQUERY) << "Error " << column_name << " is empty";
- sqlite3_result_null(ctx);
- } else if (type == TEXT_TYPE || type == BLOB_TYPE) {
- sqlite3_result_text(
- ctx, value.c_str(), static_cast<int>(value.size()), SQLITE_STATIC);
- } else if (type == INTEGER_TYPE) {
- auto afinite = tryTo<long>(value, 0);
- if (afinite.isError()) {
- DEBUG(OSQUERY) << "Error casting " << column_name << " (" << value
- << ") to INTEGER";
- sqlite3_result_null(ctx);
- } else {
- sqlite3_result_int(ctx, afinite.take());
- }
- } else if (type == BIGINT_TYPE || type == UNSIGNED_BIGINT_TYPE) {
- auto afinite = tryTo<long long>(value, 0);
- if (afinite.isError()) {
- DEBUG(OSQUERY) << "Error casting " << column_name << " (" << value
- << ") to BIGINT";
- sqlite3_result_null(ctx);
- } else {
- sqlite3_result_int64(ctx, afinite.take());
- }
- } else if (type == DOUBLE_TYPE) {
- char* end = nullptr;
- double afinite = strtod(value.c_str(), &end);
- if (end == nullptr || end == value.c_str() || *end != '\0') {
- DEBUG(OSQUERY) << "Error casting " << column_name << " (" << value
- << ") to DOUBLE";
- sqlite3_result_null(ctx);
- } else {
- sqlite3_result_double(ctx, afinite);
- }
- } else {
- ERROR(OSQUERY) << "Error unknown column type " << column_name;
- }
+ // Attempt to cast each xFilter-populated row/column to the SQLite type.
+ const auto& value = row[column_name];
+ if (this->row.count(column_name) == 0) {
+ // Missing content.
+ DEBUG(OSQUERY) << "Error " << column_name << " is empty";
+ sqlite3_result_null(ctx);
+ } else if (type == TEXT_TYPE || type == BLOB_TYPE) {
+ sqlite3_result_text(
+ ctx, value.c_str(), static_cast<int>(value.size()), SQLITE_STATIC);
+ } else if (type == INTEGER_TYPE) {
+ auto afinite = tryTo<long>(value, 0);
+ if (afinite.isError()) {
+ DEBUG(OSQUERY) << "Error casting " << column_name << " (" << value
+ << ") to INTEGER";
+ sqlite3_result_null(ctx);
+ } else {
+ sqlite3_result_int(ctx, afinite.take());
+ }
+ } else if (type == BIGINT_TYPE || type == UNSIGNED_BIGINT_TYPE) {
+ auto afinite = tryTo<long long>(value, 0);
+ if (afinite.isError()) {
+ DEBUG(OSQUERY) << "Error casting " << column_name << " (" << value
+ << ") to BIGINT";
+ sqlite3_result_null(ctx);
+ } else {
+ sqlite3_result_int64(ctx, afinite.take());
+ }
+ } else if (type == DOUBLE_TYPE) {
+ char* end = nullptr;
+ double afinite = strtod(value.c_str(), &end);
+ if (end == nullptr || end == value.c_str() || *end != '\0') {
+ DEBUG(OSQUERY) << "Error casting " << column_name << " (" << value
+ << ") to DOUBLE";
+ sqlite3_result_null(ctx);
+ } else {
+ sqlite3_result_double(ctx, afinite);
+ }
+ } else {
+ ERROR(OSQUERY) << "Error unknown column type " << column_name;
+ }
- return SQLITE_OK;
+ return SQLITE_OK;
}
-TableRowHolder DynamicTableRow::clone() const {
- Row new_row = row;
- return TableRowHolder(new DynamicTableRow(std::move(new_row)));
+TableRowHolder DynamicTableRow::clone() const
+{
+ Row new_row = row;
+ return TableRowHolder(new DynamicTableRow(std::move(new_row)));
}
} // namespace osquery
/** A TableRow backed by a string map. */
class DynamicTableRow : public TableRow {
- public:
- DynamicTableRow() : row() {}
- DynamicTableRow(Row&& r) : row(std::move(r)) {}
- DynamicTableRow(
- std::initializer_list<std::pair<const std::string, std::string>> init)
- : row(init) {}
- DynamicTableRow(const DynamicTableRow&) = delete;
- DynamicTableRow& operator=(const DynamicTableRow&) = delete;
- explicit operator Row() const {
- return row;
- }
- virtual int get_rowid(sqlite_int64 default_value, sqlite_int64* pRowid) const;
- virtual int get_column(sqlite3_context* ctx, sqlite3_vtab* pVtab, int col);
- virtual TableRowHolder clone() const;
- inline std::string& operator[](const std::string& key) {
- return row[key];
- }
- inline std::string& operator[](std::string&& key) {
- return row[key];
- }
- inline size_t count(const std::string& key) const {
- return row.count(key);
- }
+public:
+ DynamicTableRow() : row() {}
+ DynamicTableRow(Row&& r) : row(std::move(r)) {}
+ DynamicTableRow(
+ std::initializer_list<std::pair<const std::string, std::string>> init)
+ : row(init) {}
+ DynamicTableRow(const DynamicTableRow&) = delete;
+ DynamicTableRow& operator=(const DynamicTableRow&) = delete;
+ explicit operator Row() const
+ {
+ return row;
+ }
+ virtual int get_rowid(sqlite_int64 default_value, sqlite_int64* pRowid) const;
+ virtual int get_column(sqlite3_context* ctx, sqlite3_vtab* pVtab, int col);
+ virtual TableRowHolder clone() const;
+ inline std::string& operator[](const std::string& key)
+ {
+ return row[key];
+ }
+ inline std::string& operator[](std::string&& key)
+ {
+ return row[key];
+ }
+ inline size_t count(const std::string& key) const
+ {
+ return row.count(key);
+ }
- private:
- Row row;
+private:
+ Row row;
};
/// Syntactic sugar making DynamicRows inside of TableRowHolders easier to work
/// with. This should go away once strongly typed rows are used everywhere.
class DynamicTableRowHolder {
- public:
- DynamicTableRowHolder() : row(new DynamicTableRow()), ptr(row) {}
- DynamicTableRowHolder(
- std::initializer_list<std::pair<const std::string, std::string>> init)
- : row(new DynamicTableRow(init)), ptr(row) {}
- inline operator TableRowHolder &&() {
- return std::move(ptr);
- }
- inline std::string& operator[](const std::string& key) {
- return (*row)[key];
- }
- inline std::string& operator[](std::string&& key) {
- return (*row)[key];
- }
- inline size_t count(const std::string& key) {
- return (*row).count(key);
- }
+public:
+ DynamicTableRowHolder() : row(new DynamicTableRow()), ptr(row) {}
+ DynamicTableRowHolder(
+ std::initializer_list<std::pair<const std::string, std::string>> init)
+ : row(new DynamicTableRow(init)), ptr(row) {}
+ inline operator TableRowHolder&& ()
+ {
+ return std::move(ptr);
+ }
+ inline std::string& operator[](const std::string& key)
+ {
+ return (*row)[key];
+ }
+ inline std::string& operator[](std::string&& key)
+ {
+ return (*row)[key];
+ }
+ inline size_t count(const std::string& key)
+ {
+ return (*row).count(key);
+ }
- private:
- DynamicTableRow* row;
- TableRowHolder ptr;
+private:
+ DynamicTableRow* row;
+ TableRowHolder ptr;
};
-inline DynamicTableRowHolder make_table_row() {
- return DynamicTableRowHolder();
+inline DynamicTableRowHolder make_table_row()
+{
+ return DynamicTableRowHolder();
}
inline DynamicTableRowHolder make_table_row(
- std::initializer_list<std::pair<const std::string, std::string>> init) {
- return DynamicTableRowHolder(init);
+ std::initializer_list<std::pair<const std::string, std::string>> init)
+{
+ return DynamicTableRowHolder(init);
}
/// Converts a QueryData struct to TableRows. Intended for use only in
CREATE_LAZY_REGISTRY(SQLPlugin, "sql");
-SQL::SQL(const std::string& query, bool use_cache) {
- TableColumns table_columns;
- status_ = getQueryColumns(query, table_columns);
- if (status_.ok()) {
- for (auto c : table_columns) {
- columns_.push_back(std::get<0>(c));
- }
- status_ = osquery::query(query, results_, use_cache);
- }
+SQL::SQL(const std::string& query, bool use_cache)
+{
+ TableColumns table_columns;
+ status_ = getQueryColumns(query, table_columns);
+ if (status_.ok()) {
+ for (auto c : table_columns) {
+ columns_.push_back(std::get<0>(c));
+ }
+ status_ = osquery::query(query, results_, use_cache);
+ }
}
-const QueryData& SQL::rows() const {
- return results_;
+const QueryData& SQL::rows() const
+{
+ return results_;
}
-QueryData& SQL::rows() {
- return results_;
+QueryData& SQL::rows()
+{
+ return results_;
}
-const ColumnNames& SQL::columns() const {
- return columns_;
+const ColumnNames& SQL::columns() const
+{
+ return columns_;
}
-bool SQL::ok() const {
- return status_.ok();
+bool SQL::ok() const
+{
+ return status_.ok();
}
-const Status& SQL::getStatus() const {
- return status_;
+const Status& SQL::getStatus() const
+{
+ return status_;
}
-std::string SQL::getMessageString() const {
- return status_.toString();
+std::string SQL::getMessageString() const
+{
+ return status_.toString();
}
-static inline void escapeNonPrintableBytes(std::string& data) {
- std::string escaped;
- // clang-format off
- char const hex_chars[16] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
- };
- // clang-format on
+static inline void escapeNonPrintableBytes(std::string& data)
+{
+ std::string escaped;
+ // clang-format off
+ char const hex_chars[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ };
+ // clang-format on
- bool needs_replacement = false;
- for (size_t i = 0; i < data.length(); i++) {
- if (((unsigned char)data[i]) < 0x20 || ((unsigned char)data[i]) >= 0x80) {
- needs_replacement = true;
- escaped += "\\x";
- escaped += hex_chars[(((unsigned char)data[i])) >> 4];
- escaped += hex_chars[((unsigned char)data[i] & 0x0F) >> 0];
- } else {
- escaped += data[i];
- }
- }
+ bool needs_replacement = false;
+ for (size_t i = 0; i < data.length(); i++) {
+ if (((unsigned char)data[i]) < 0x20 || ((unsigned char)data[i]) >= 0x80) {
+ needs_replacement = true;
+ escaped += "\\x";
+ escaped += hex_chars[(((unsigned char)data[i])) >> 4];
+ escaped += hex_chars[((unsigned char)data[i] & 0x0F) >> 0];
+ } else {
+ escaped += data[i];
+ }
+ }
- // Only replace if any escapes were made.
- if (needs_replacement) {
- data = std::move(escaped);
- }
+ // Only replace if any escapes were made.
+ if (needs_replacement) {
+ data = std::move(escaped);
+ }
}
-void escapeNonPrintableBytesEx(std::string& data) {
- return escapeNonPrintableBytes(data);
+void escapeNonPrintableBytesEx(std::string& data)
+{
+ return escapeNonPrintableBytes(data);
}
-QueryData SQL::selectAllFrom(const std::string& table) {
- PluginResponse response;
- Registry::call("table", table, {{"action", "generate"}}, response);
- return response;
+QueryData SQL::selectAllFrom(const std::string& table)
+{
+ PluginResponse response;
+ Registry::call("table", table, {{"action", "generate"}}, response);
+ return response;
}
QueryData SQL::selectAllFrom(const std::string& table,
- const std::string& column,
- ConstraintOperator op,
- const std::string& expr) {
- return selectFrom({}, table, column, op, expr);
+ const std::string& column,
+ ConstraintOperator op,
+ const std::string& expr)
+{
+ return selectFrom({}, table, column, op, expr);
}
QueryData SQL::selectFrom(const std::initializer_list<std::string>& columns,
- const std::string& table,
- const std::string& column,
- ConstraintOperator op,
- const std::string& expr) {
- PluginRequest request = {{"action", "generate"}};
- // Create a fake content, there will be no caching.
- QueryContext ctx;
- ctx.constraints[column].add(Constraint(op, expr));
- if (columns.size() > 0) {
- auto colsUsed = UsedColumns(columns);
- colsUsed.insert(column);
- ctx.colsUsed = colsUsed;
- }
- // We can't set colsUsedBitset here (because we don't know the column
- // indexes). The plugin that handles the request will figure it out from the
- // column names.
- TablePlugin::setRequestFromContext(ctx, request);
-
- PluginResponse response;
- Registry::call("table", table, request, response);
- response.erase(
- std::remove_if(response.begin(),
- response.end(),
- [&ctx, &column](const PluginRequest& row) -> bool {
- return !ctx.constraints[column].matches(row.at(column));
- }),
- response.end());
- return response;
-}
-
-Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response) {
- response.clear();
- if (request.count("action") == 0) {
- return Status(1, "SQL plugin must include a request action");
- }
-
- if (request.at("action") == "query") {
- bool use_cache = (request.count("cache") && request.at("cache") == "1");
- return this->query(request.at("query"), response, use_cache);
- } else if (request.at("action") == "columns") {
- TableColumns columns;
- auto status = this->getQueryColumns(request.at("query"), columns);
- // Convert columns to response
- for (const auto& column : columns) {
- response.push_back(
- {{"n", std::get<0>(column)},
- {"t", columnTypeName(std::get<1>(column))},
- {"o", INTEGER(static_cast<size_t>(std::get<2>(column)))}});
- }
- return status;
- } else if (request.at("action") == "attach") {
- // Attach a virtual table name using an optional included definition.
- return this->attach(request.at("table"));
- } else if (request.at("action") == "detach") {
- this->detach(request.at("table"));
- return Status::success();
- } else if (request.at("action") == "tables") {
- std::vector<std::string> tables;
- auto status = this->getQueryTables(request.at("query"), tables);
- if (status.ok()) {
- for (const auto& table : tables) {
- response.push_back({{"t", table}});
- }
- }
- return status;
- }
- return Status(1, "Unknown action");
-}
-
-Status query(const std::string& q, QueryData& results, bool use_cache) {
- return Registry::call(
- "sql",
- "sql",
- {{"action", "query"}, {"cache", (use_cache) ? "1" : "0"}, {"query", q}},
- results);
-}
-
-Status getQueryColumns(const std::string& q, TableColumns& columns) {
- PluginResponse response;
- auto status = Registry::call(
- "sql", "sql", {{"action", "columns"}, {"query", q}}, response);
-
- // Convert response to columns
- for (const auto& item : response) {
- columns.push_back(make_tuple(
- item.at("n"), columnTypeName(item.at("t")), ColumnOptions::DEFAULT));
- }
- return status;
+ const std::string& table,
+ const std::string& column,
+ ConstraintOperator op,
+ const std::string& expr)
+{
+ PluginRequest request = {{"action", "generate"}};
+ // Create a fake content, there will be no caching.
+ QueryContext ctx;
+ ctx.constraints[column].add(Constraint(op, expr));
+ if (columns.size() > 0) {
+ auto colsUsed = UsedColumns(columns);
+ colsUsed.insert(column);
+ ctx.colsUsed = colsUsed;
+ }
+ // We can't set colsUsedBitset here (because we don't know the column
+ // indexes). The plugin that handles the request will figure it out from the
+ // column names.
+ TablePlugin::setRequestFromContext(ctx, request);
+
+ PluginResponse response;
+ Registry::call("table", table, request, response);
+ response.erase(
+ std::remove_if(response.begin(),
+ response.end(),
+ [&ctx, &column](const PluginRequest & row) -> bool {
+ return !ctx.constraints[column].matches(row.at(column));
+ }),
+ response.end());
+ return response;
+}
+
+Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response)
+{
+ response.clear();
+ if (request.count("action") == 0) {
+ return Status(1, "SQL plugin must include a request action");
+ }
+
+ if (request.at("action") == "query") {
+ bool use_cache = (request.count("cache") && request.at("cache") == "1");
+ return this->query(request.at("query"), response, use_cache);
+ } else if (request.at("action") == "columns") {
+ TableColumns columns;
+ auto status = this->getQueryColumns(request.at("query"), columns);
+ // Convert columns to response
+ for (const auto& column : columns) {
+ response.push_back({
+ {"n", std::get<0>(column)},
+ {"t", columnTypeName(std::get<1>(column))},
+ {"o", INTEGER(static_cast<size_t>(std::get<2>(column)))}});
+ }
+ return status;
+ } else if (request.at("action") == "attach") {
+ // Attach a virtual table name using an optional included definition.
+ return this->attach(request.at("table"));
+ } else if (request.at("action") == "detach") {
+ this->detach(request.at("table"));
+ return Status::success();
+ } else if (request.at("action") == "tables") {
+ std::vector<std::string> tables;
+ auto status = this->getQueryTables(request.at("query"), tables);
+ if (status.ok()) {
+ for (const auto& table : tables) {
+ response.push_back({{"t", table}});
+ }
+ }
+ return status;
+ }
+ return Status(1, "Unknown action");
+}
+
+Status query(const std::string& q, QueryData& results, bool use_cache)
+{
+ return Registry::call(
+ "sql",
+ "sql",
+ {{"action", "query"}, {"cache", (use_cache) ? "1" : "0"}, {"query", q}},
+ results);
+}
+
+Status getQueryColumns(const std::string& q, TableColumns& columns)
+{
+ PluginResponse response;
+ auto status = Registry::call(
+ "sql", "sql", {{"action", "columns"}, {"query", q}}, response);
+
+ // Convert response to columns
+ for (const auto& item : response) {
+ columns.push_back(make_tuple(
+ item.at("n"), columnTypeName(item.at("t")), ColumnOptions::DEFAULT));
+ }
+ return status;
}
Status mockGetQueryTables(std::string copy_q,
- std::vector<std::string>& tables) {
- std::transform(copy_q.begin(), copy_q.end(), copy_q.begin(), ::tolower);
- auto offset_from = copy_q.find("from ");
- if (offset_from == std::string::npos) {
- return Status(1);
- }
-
- auto simple_tables = osquery::split(copy_q.substr(offset_from + 5), ",");
- for (const auto& table : simple_tables) {
- tables.push_back(table);
- }
- return Status(0);
-}
-
-Status getQueryTables(const std::string& q, std::vector<std::string>& tables) {
- PluginResponse response;
- auto status = Registry::call(
- "sql", "sql", {{"action", "tables"}, {"query", q}}, response);
-
- for (const auto& table : response) {
- tables.push_back(table.at("t"));
- }
- return status;
+ std::vector<std::string>& tables)
+{
+ std::transform(copy_q.begin(), copy_q.end(), copy_q.begin(), ::tolower);
+ auto offset_from = copy_q.find("from ");
+ if (offset_from == std::string::npos) {
+ return Status(1);
+ }
+
+ auto simple_tables = osquery::split(copy_q.substr(offset_from + 5), ",");
+ for (const auto& table : simple_tables) {
+ tables.push_back(table);
+ }
+ return Status(0);
+}
+
+Status getQueryTables(const std::string& q, std::vector<std::string>& tables)
+{
+ PluginResponse response;
+ auto status = Registry::call(
+ "sql", "sql", {{"action", "tables"}, {"query", q}}, response);
+
+ for (const auto& table : response) {
+ tables.push_back(table.at("t"));
+ }
+ return status;
}
}
*/
// clang-format off
const std::map<int, std::string> kSQLiteReturnCodes = {
- {0, "SQLITE_OK"}, {1, "SQLITE_ERROR"}, {2, "SQLITE_INTERNAL"},
- {3, "SQLITE_PERM"}, {4, "SQLITE_ABORT"}, {5, "SQLITE_BUSY"},
- {6, "SQLITE_LOCKED"}, {7, "SQLITE_NOMEM"}, {8, "SQLITE_READONLY"},
- {9, "SQLITE_INTERRUPT"}, {10, "SQLITE_IOERR"}, {11, "SQLITE_CORRUPT"},
- {12, "SQLITE_NOTFOUND"}, {13, "SQLITE_FULL"}, {14, "SQLITE_CANTOPEN"},
- {15, "SQLITE_PROTOCOL"}, {16, "SQLITE_EMPTY"}, {17, "SQLITE_SCHEMA"},
- {18, "SQLITE_TOOBIG"}, {19, "SQLITE_CONSTRAINT"}, {20, "SQLITE_MISMATCH"},
- {21, "SQLITE_MISUSE"}, {22, "SQLITE_NOLFS"}, {23, "SQLITE_AUTH"},
- {24, "SQLITE_FORMAT"}, {25, "SQLITE_RANGE"}, {26, "SQLITE_NOTADB"},
- {27, "SQLITE_NOTICE"}, {28, "SQLITE_WARNING"}, {100, "SQLITE_ROW"},
- {101, "SQLITE_DONE"},
+ {0, "SQLITE_OK"}, {1, "SQLITE_ERROR"}, {2, "SQLITE_INTERNAL"},
+ {3, "SQLITE_PERM"}, {4, "SQLITE_ABORT"}, {5, "SQLITE_BUSY"},
+ {6, "SQLITE_LOCKED"}, {7, "SQLITE_NOMEM"}, {8, "SQLITE_READONLY"},
+ {9, "SQLITE_INTERRUPT"}, {10, "SQLITE_IOERR"}, {11, "SQLITE_CORRUPT"},
+ {12, "SQLITE_NOTFOUND"}, {13, "SQLITE_FULL"}, {14, "SQLITE_CANTOPEN"},
+ {15, "SQLITE_PROTOCOL"}, {16, "SQLITE_EMPTY"}, {17, "SQLITE_SCHEMA"},
+ {18, "SQLITE_TOOBIG"}, {19, "SQLITE_CONSTRAINT"}, {20, "SQLITE_MISMATCH"},
+ {21, "SQLITE_MISUSE"}, {22, "SQLITE_NOLFS"}, {23, "SQLITE_AUTH"},
+ {24, "SQLITE_FORMAT"}, {25, "SQLITE_RANGE"}, {26, "SQLITE_NOTADB"},
+ {27, "SQLITE_NOTICE"}, {28, "SQLITE_WARNING"}, {100, "SQLITE_ROW"},
+ {101, "SQLITE_DONE"},
};
const std::map<std::string, std::string> kMemoryDBSettings = {
- {"synchronous", "OFF"}, {"count_changes", "OFF"},
- {"default_temp_store", "0"}, {"auto_vacuum", "FULL"},
- {"journal_mode", "OFF"}, {"cache_size", "0"},
- {"page_count", "0"},
+ {"synchronous", "OFF"}, {"count_changes", "OFF"},
+ {"default_temp_store", "0"}, {"auto_vacuum", "FULL"},
+ {"journal_mode", "OFF"}, {"cache_size", "0"},
+ {"page_count", "0"},
};
// clang-format on
#define OpComparator(x) \
- { x, QueryPlanner::Opcode(OpReg::P2, INTEGER_TYPE) }
+ { x, QueryPlanner::Opcode(OpReg::P2, INTEGER_TYPE) }
#define Arithmetic(x) \
- { x, QueryPlanner::Opcode(OpReg::P3, BIGINT_TYPE) }
+ { x, QueryPlanner::Opcode(OpReg::P3, BIGINT_TYPE) }
/**
* @brief A map from opcode to pair of result register and resultant type.
* comparators, aggregates, and copies.
*/
const std::map<std::string, QueryPlanner::Opcode> kSQLOpcodes = {
- {"Concat", QueryPlanner::Opcode(OpReg::P3, TEXT_TYPE)},
- {"AggStep", QueryPlanner::Opcode(OpReg::P3, BIGINT_TYPE)},
- {"AggStep0", QueryPlanner::Opcode(OpReg::P3, BIGINT_TYPE)},
- {"Integer", QueryPlanner::Opcode(OpReg::P2, INTEGER_TYPE)},
- {"Int64", QueryPlanner::Opcode(OpReg::P2, BIGINT_TYPE)},
- {"String", QueryPlanner::Opcode(OpReg::P2, TEXT_TYPE)},
- {"String8", QueryPlanner::Opcode(OpReg::P2, TEXT_TYPE)},
- {"Or", QueryPlanner::Opcode(OpReg::P3, INTEGER_TYPE)},
- {"And", QueryPlanner::Opcode(OpReg::P3, INTEGER_TYPE)},
-
- // Arithmetic yields a BIGINT for safety.
- Arithmetic("BitAnd"),
- Arithmetic("BitOr"),
- Arithmetic("ShiftLeft"),
- Arithmetic("ShiftRight"),
- Arithmetic("Add"),
- Arithmetic("Subtract"),
- Arithmetic("Multiply"),
- Arithmetic("Divide"),
- Arithmetic("Remainder"),
-
- // Comparators result in booleans and are treated as INTEGERs.
- OpComparator("Not"),
- OpComparator("IsNull"),
- OpComparator("NotNull"),
- OpComparator("Ne"),
- OpComparator("Eq"),
- OpComparator("Gt"),
- OpComparator("Le"),
- OpComparator("Lt"),
- OpComparator("Ge"),
- OpComparator("IfNeg"),
- OpComparator("IfNotZero"),
+ {"Concat", QueryPlanner::Opcode(OpReg::P3, TEXT_TYPE)},
+ {"AggStep", QueryPlanner::Opcode(OpReg::P3, BIGINT_TYPE)},
+ {"AggStep0", QueryPlanner::Opcode(OpReg::P3, BIGINT_TYPE)},
+ {"Integer", QueryPlanner::Opcode(OpReg::P2, INTEGER_TYPE)},
+ {"Int64", QueryPlanner::Opcode(OpReg::P2, BIGINT_TYPE)},
+ {"String", QueryPlanner::Opcode(OpReg::P2, TEXT_TYPE)},
+ {"String8", QueryPlanner::Opcode(OpReg::P2, TEXT_TYPE)},
+ {"Or", QueryPlanner::Opcode(OpReg::P3, INTEGER_TYPE)},
+ {"And", QueryPlanner::Opcode(OpReg::P3, INTEGER_TYPE)},
+
+ // Arithmetic yields a BIGINT for safety.
+ Arithmetic("BitAnd"),
+ Arithmetic("BitOr"),
+ Arithmetic("ShiftLeft"),
+ Arithmetic("ShiftRight"),
+ Arithmetic("Add"),
+ Arithmetic("Subtract"),
+ Arithmetic("Multiply"),
+ Arithmetic("Divide"),
+ Arithmetic("Remainder"),
+
+ // Comparators result in booleans and are treated as INTEGERs.
+ OpComparator("Not"),
+ OpComparator("IsNull"),
+ OpComparator("NotNull"),
+ OpComparator("Ne"),
+ OpComparator("Eq"),
+ OpComparator("Gt"),
+ OpComparator("Le"),
+ OpComparator("Lt"),
+ OpComparator("Ge"),
+ OpComparator("IfNeg"),
+ OpComparator("IfNotZero"),
};
RecursiveMutex SQLiteDBInstance::kPrimaryAttachMutex;
/// The SQLiteSQLPlugin implements the "sql" registry for internal/core.
class SQLiteSQLPlugin : public SQLPlugin {
- public:
- /// Execute SQL and store results.
- Status query(const std::string& query,
- QueryData& results,
- bool use_cache) const override;
+public:
+ /// Execute SQL and store results.
+ Status query(const std::string& query,
+ QueryData& results,
+ bool use_cache) const override;
- /// Introspect, explain, the suspected types selected in an SQL statement.
- Status getQueryColumns(const std::string& query,
- TableColumns& columns) const override;
+ /// Introspect, explain, the suspected types selected in an SQL statement.
+ Status getQueryColumns(const std::string& query,
+ TableColumns& columns) const override;
- /// Similar to getQueryColumns but return the scanned tables.
- Status getQueryTables(const std::string& query,
- std::vector<std::string>& tables) const override;
+ /// Similar to getQueryColumns but return the scanned tables.
+ Status getQueryTables(const std::string& query,
+ std::vector<std::string>& tables) const override;
- /// Create a SQLite module and attach (CREATE).
- Status attach(const std::string& name) override;
+ /// Create a SQLite module and attach (CREATE).
+ Status attach(const std::string& name) override;
- /// Detach a virtual table (DROP).
- void detach(const std::string& name) override;
+ /// Detach a virtual table (DROP).
+ void detach(const std::string& name) override;
};
/// SQL provider for osquery internal/core.
REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql");
-std::string getStringForSQLiteReturnCode(int code) {
- if (kSQLiteReturnCodes.find(code) != kSQLiteReturnCodes.end()) {
- return kSQLiteReturnCodes.at(code);
- } else {
- std::ostringstream s;
- s << "Error: " << code << " is not a valid SQLite result code";
- return s.str();
- }
+std::string getStringForSQLiteReturnCode(int code)
+{
+ if (kSQLiteReturnCodes.find(code) != kSQLiteReturnCodes.end()) {
+ return kSQLiteReturnCodes.at(code);
+ } else {
+ std::ostringstream s;
+ s << "Error: " << code << " is not a valid SQLite result code";
+ return s.str();
+ }
}
Status SQLiteSQLPlugin::query(const std::string& query,
- QueryData& results,
- bool use_cache) const {
- auto dbc = SQLiteDBManager::get();
- dbc->useCache(use_cache);
- auto result = queryInternal(query, results, dbc);
- dbc->clearAffectedTables();
- return result;
+ QueryData& results,
+ bool use_cache) const
+{
+ auto dbc = SQLiteDBManager::get();
+ dbc->useCache(use_cache);
+ auto result = queryInternal(query, results, dbc);
+ dbc->clearAffectedTables();
+ return result;
}
Status SQLiteSQLPlugin::getQueryColumns(const std::string& query,
- TableColumns& columns) const {
- auto dbc = SQLiteDBManager::get();
- return getQueryColumnsInternal(query, columns, dbc);
+ TableColumns& columns) const
+{
+ auto dbc = SQLiteDBManager::get();
+ return getQueryColumnsInternal(query, columns, dbc);
}
Status SQLiteSQLPlugin::getQueryTables(const std::string& query,
- std::vector<std::string>& tables) const {
- auto dbc = SQLiteDBManager::get();
- QueryPlanner planner(query, dbc);
- tables = planner.tables();
- return Status(0);
+ std::vector<std::string>& tables) const
+{
+ auto dbc = SQLiteDBManager::get();
+ QueryPlanner planner(query, dbc);
+ tables = planner.tables();
+ return Status(0);
}
-SQLInternal::SQLInternal(const std::string& query, bool use_cache) {
- auto dbc = SQLiteDBManager::get();
- dbc->useCache(use_cache);
- status_ = queryInternal(query, resultsTyped_, dbc);
+SQLInternal::SQLInternal(const std::string& query, bool use_cache)
+{
+ auto dbc = SQLiteDBManager::get();
+ dbc->useCache(use_cache);
+ status_ = queryInternal(query, resultsTyped_, dbc);
- // One of the advantages of using SQLInternal (aside from the Registry-bypass)
- // is the ability to "deep-inspect" the table attributes and actions.
- event_based_ = (dbc->getAttributes() & TableAttributes::EVENT_BASED) != 0;
+ // One of the advantages of using SQLInternal (aside from the Registry-bypass)
+ // is the ability to "deep-inspect" the table attributes and actions.
+ event_based_ = (dbc->getAttributes() & TableAttributes::EVENT_BASED) != 0;
- dbc->clearAffectedTables();
+ dbc->clearAffectedTables();
}
-QueryDataTyped& SQLInternal::rowsTyped() {
- return resultsTyped_;
+QueryDataTyped& SQLInternal::rowsTyped()
+{
+ return resultsTyped_;
}
-const Status& SQLInternal::getStatus() const {
- return status_;
+const Status& SQLInternal::getStatus() const
+{
+ return status_;
}
-bool SQLInternal::eventBased() const {
- return event_based_;
+bool SQLInternal::eventBased() const
+{
+ return event_based_;
}
// Temporary: I'm going to move this from sql.cpp to here in change immediately
extern void escapeNonPrintableBytesEx(std::string& str);
class StringEscaperVisitor : public boost::static_visitor<> {
- public:
- void operator()(long long& i) const { // NO-OP
- }
-
- void operator()(double& d) const { // NO-OP
- }
-
- void operator()(std::string& str) const {
- escapeNonPrintableBytesEx(str);
- }
+public:
+ void operator()(long long& i) const // NO-OP
+ {
+ }
+
+ void operator()(double& d) const // NO-OP
+ {
+ }
+
+ void operator()(std::string& str) const
+ {
+ escapeNonPrintableBytesEx(str);
+ }
};
-void SQLInternal::escapeResults() {
- StringEscaperVisitor visitor;
- for (auto& rowTyped : resultsTyped_) {
- for (auto& column : rowTyped) {
- boost::apply_visitor(visitor, column.second);
- }
- }
+void SQLInternal::escapeResults()
+{
+ StringEscaperVisitor visitor;
+ for (auto& rowTyped : resultsTyped_) {
+ for (auto& column : rowTyped) {
+ boost::apply_visitor(visitor, column.second);
+ }
+ }
}
-Status SQLiteSQLPlugin::attach(const std::string& name) {
- PluginResponse response;
- auto status =
- Registry::call("table", name, {{"action", "columns"}}, response);
- if (!status.ok()) {
- return status;
- }
+Status SQLiteSQLPlugin::attach(const std::string& name)
+{
+ PluginResponse response;
+ auto status =
+ Registry::call("table", name, {{"action", "columns"}}, response);
+ if (!status.ok()) {
+ return status;
+ }
- bool is_extension = true;
- auto statement = columnDefinition(response, false, is_extension);
+ bool is_extension = true;
+ auto statement = columnDefinition(response, false, is_extension);
- // Attach requests occurring via the plugin/registry APIs must act on the
- // primary database. To allow this, getConnection can explicitly request the
- // primary instance and avoid the contention decisions.
- auto dbc = SQLiteDBManager::getConnection(true);
+ // Attach requests occurring via the plugin/registry APIs must act on the
+ // primary database. To allow this, getConnection can explicitly request the
+ // primary instance and avoid the contention decisions.
+ auto dbc = SQLiteDBManager::getConnection(true);
- // Attach as an extension, allowing read/write tables
- return attachTableInternal(name, statement, dbc, is_extension);
+ // Attach as an extension, allowing read/write tables
+ return attachTableInternal(name, statement, dbc, is_extension);
}
-void SQLiteSQLPlugin::detach(const std::string& name) {
- auto dbc = SQLiteDBManager::get();
- if (!dbc->isPrimary()) {
- return;
- }
- detachTableInternal(name, dbc);
+void SQLiteSQLPlugin::detach(const std::string& name)
+{
+ auto dbc = SQLiteDBManager::get();
+ if (!dbc->isPrimary()) {
+ return;
+ }
+ detachTableInternal(name, dbc);
}
SQLiteDBInstance::SQLiteDBInstance(sqlite3*& db, Mutex& mtx)
- : db_(db), lock_(mtx, std::try_to_lock) {
- if (lock_.owns_lock()) {
- primary_ = true;
- } else {
- db_ = nullptr;
- DEBUG(OSQUERY) << "DBManager contention: opening transient SQLite database";
- init();
- }
+ : db_(db), lock_(mtx, std::try_to_lock)
+{
+ if (lock_.owns_lock()) {
+ primary_ = true;
+ } else {
+ db_ = nullptr;
+ DEBUG(OSQUERY) << "DBManager contention: opening transient SQLite database";
+ init();
+ }
}
-static inline void openOptimized(sqlite3*& db) {
- sqlite3_open(":memory:", &db);
+static inline void openOptimized(sqlite3*& db)
+{
+ sqlite3_open(":memory:", &db);
- std::string settings;
- for (const auto& setting : kMemoryDBSettings) {
- settings += "PRAGMA " + setting.first + "=" + setting.second + "; ";
- }
- sqlite3_exec(db, settings.c_str(), nullptr, nullptr, nullptr);
+ std::string settings;
+ for (const auto& setting : kMemoryDBSettings) {
+ settings += "PRAGMA " + setting.first + "=" + setting.second + "; ";
+ }
+ sqlite3_exec(db, settings.c_str(), nullptr, nullptr, nullptr);
}
-void SQLiteDBInstance::init() {
- primary_ = false;
- openOptimized(db_);
+void SQLiteDBInstance::init()
+{
+ primary_ = false;
+ openOptimized(db_);
}
-void SQLiteDBInstance::useCache(bool use_cache) {
- use_cache_ = use_cache;
+void SQLiteDBInstance::useCache(bool use_cache)
+{
+ use_cache_ = use_cache;
}
-bool SQLiteDBInstance::useCache() const {
- return use_cache_;
+bool SQLiteDBInstance::useCache() const
+{
+ return use_cache_;
}
-RecursiveLock SQLiteDBInstance::attachLock() const {
- if (isPrimary()) {
- return RecursiveLock(kPrimaryAttachMutex);
- }
- return RecursiveLock(attach_mutex_);
+RecursiveLock SQLiteDBInstance::attachLock() const
+{
+ if (isPrimary()) {
+ return RecursiveLock(kPrimaryAttachMutex);
+ }
+ return RecursiveLock(attach_mutex_);
}
void SQLiteDBInstance::addAffectedTable(
- std::shared_ptr<VirtualTableContent> table) {
- // An xFilter/scan was requested for this virtual table.
- affected_tables_.insert(std::make_pair(table->name, std::move(table)));
-}
-
-bool SQLiteDBInstance::tableCalled(VirtualTableContent const& table) {
- return (affected_tables_.count(table.name) > 0);
-}
-
-TableAttributes SQLiteDBInstance::getAttributes() const {
- const SQLiteDBInstance* rdbc = this;
- if (isPrimary() && !managed_) {
- // Similarly to clearAffectedTables, the connection may be forwarded.
- rdbc = SQLiteDBManager::getConnection(true).get();
- }
-
- TableAttributes attributes = TableAttributes::NONE;
- for (const auto& table : rdbc->affected_tables_) {
- attributes = table.second->attributes | attributes;
- }
- return attributes;
-}
-
-void SQLiteDBInstance::clearAffectedTables() {
- if (isPrimary() && !managed_) {
- // A primary instance must forward clear requests to the DB manager's
- // 'connection' instance. This is a temporary primary instance.
- SQLiteDBManager::getConnection(true)->clearAffectedTables();
- return;
- }
-
- for (const auto& table : affected_tables_) {
- table.second->constraints.clear();
- table.second->cache.clear();
- table.second->colsUsed.clear();
- table.second->colsUsedBitsets.clear();
- }
- // Since the affected tables are cleared, there are no more affected tables.
- // There is no concept of compounding tables between queries.
- affected_tables_.clear();
- use_cache_ = false;
-}
-
-SQLiteDBInstance::~SQLiteDBInstance() {
- if (!isPrimary() && db_ != nullptr) {
- sqlite3_close(db_);
- } else {
- db_ = nullptr;
- }
-}
-
-SQLiteDBManager::SQLiteDBManager() : db_(nullptr) {
- sqlite3_soft_heap_limit64(1);
-}
-
-bool SQLiteDBManager::isDisabled(const std::string& table_name) {
- const auto& element = instance().disabled_tables_.find(table_name);
- return (element != instance().disabled_tables_.end());
-}
-
-void SQLiteDBManager::resetPrimary() {
- auto& self = instance();
-
- WriteLock connection_lock(self.mutex_);
- self.connection_.reset();
-
- {
- WriteLock create_lock(self.create_mutex_);
- sqlite3_close(self.db_);
- self.db_ = nullptr;
- }
-}
-
-SQLiteDBInstanceRef SQLiteDBManager::getUnique() {
- auto instance = std::make_shared<SQLiteDBInstance>();
- attachVirtualTables(instance);
- return instance;
-}
-
-SQLiteDBInstanceRef SQLiteDBManager::getConnection(bool primary) {
- auto& self = instance();
- WriteLock lock(self.create_mutex_);
-
- if (self.db_ == nullptr) {
- // Create primary SQLite DB instance.
- openOptimized(self.db_);
- self.connection_ = SQLiteDBInstanceRef(new SQLiteDBInstance(self.db_));
- attachVirtualTables(self.connection_);
- }
-
- // Internal usage may request the primary connection explicitly.
- if (primary) {
- return self.connection_;
- }
-
- // Create a 'database connection' for the managed database instance.
- auto instance = std::make_shared<SQLiteDBInstance>(self.db_, self.mutex_);
- if (!instance->isPrimary()) {
- attachVirtualTables(instance);
- }
- return instance;
-}
+ std::shared_ptr<VirtualTableContent> table)
+{
+ // An xFilter/scan was requested for this virtual table.
+ affected_tables_.insert(std::make_pair(table->name, std::move(table)));
+}
+
+bool SQLiteDBInstance::tableCalled(VirtualTableContent const& table)
+{
+ return (affected_tables_.count(table.name) > 0);
+}
+
+TableAttributes SQLiteDBInstance::getAttributes() const
+{
+ const SQLiteDBInstance* rdbc = this;
+ if (isPrimary() && !managed_) {
+ // Similarly to clearAffectedTables, the connection may be forwarded.
+ rdbc = SQLiteDBManager::getConnection(true).get();
+ }
+
+ TableAttributes attributes = TableAttributes::NONE;
+ for (const auto& table : rdbc->affected_tables_) {
+ attributes = table.second->attributes | attributes;
+ }
+ return attributes;
+}
+
+void SQLiteDBInstance::clearAffectedTables()
+{
+ if (isPrimary() && !managed_) {
+ // A primary instance must forward clear requests to the DB manager's
+ // 'connection' instance. This is a temporary primary instance.
+ SQLiteDBManager::getConnection(true)->clearAffectedTables();
+ return;
+ }
+
+ for (const auto& table : affected_tables_) {
+ table.second->constraints.clear();
+ table.second->cache.clear();
+ table.second->colsUsed.clear();
+ table.second->colsUsedBitsets.clear();
+ }
+ // Since the affected tables are cleared, there are no more affected tables.
+ // There is no concept of compounding tables between queries.
+ affected_tables_.clear();
+ use_cache_ = false;
+}
+
+SQLiteDBInstance::~SQLiteDBInstance()
+{
+ if (!isPrimary() && db_ != nullptr) {
+ sqlite3_close(db_);
+ } else {
+ db_ = nullptr;
+ }
+}
+
+SQLiteDBManager::SQLiteDBManager() : db_(nullptr)
+{
+ sqlite3_soft_heap_limit64(1);
+}
+
+bool SQLiteDBManager::isDisabled(const std::string& table_name)
+{
+ const auto& element = instance().disabled_tables_.find(table_name);
+ return (element != instance().disabled_tables_.end());
+}
+
+void SQLiteDBManager::resetPrimary()
+{
+ auto& self = instance();
+
+ WriteLock connection_lock(self.mutex_);
+ self.connection_.reset();
+
+ {
+ WriteLock create_lock(self.create_mutex_);
+ sqlite3_close(self.db_);
+ self.db_ = nullptr;
+ }
+}
+
+SQLiteDBInstanceRef SQLiteDBManager::getUnique()
+{
+ auto instance = std::make_shared<SQLiteDBInstance>();
+ attachVirtualTables(instance);
+ return instance;
+}
+
+SQLiteDBInstanceRef SQLiteDBManager::getConnection(bool primary)
+{
+ auto& self = instance();
+ WriteLock lock(self.create_mutex_);
+
+ if (self.db_ == nullptr) {
+ // Create primary SQLite DB instance.
+ openOptimized(self.db_);
+ self.connection_ = SQLiteDBInstanceRef(new SQLiteDBInstance(self.db_));
+ attachVirtualTables(self.connection_);
+ }
+
+ // Internal usage may request the primary connection explicitly.
+ if (primary) {
+ return self.connection_;
+ }
-SQLiteDBManager::~SQLiteDBManager() {
- connection_ = nullptr;
- if (db_ != nullptr) {
- sqlite3_close(db_);
- db_ = nullptr;
- }
+ // Create a 'database connection' for the managed database instance.
+ auto instance = std::make_shared<SQLiteDBInstance>(self.db_, self.mutex_);
+ if (!instance->isPrimary()) {
+ attachVirtualTables(instance);
+ }
+ return instance;
+}
+
+SQLiteDBManager::~SQLiteDBManager()
+{
+ connection_ = nullptr;
+ if (db_ != nullptr) {
+ sqlite3_close(db_);
+ db_ = nullptr;
+ }
}
QueryPlanner::QueryPlanner(const std::string& query,
- const SQLiteDBInstanceRef& instance) {
- QueryData plan;
- queryInternal("EXPLAIN QUERY PLAN " + query, plan, instance);
- queryInternal("EXPLAIN " + query, program_, instance);
-
- for (const auto& row : plan) {
- auto details = osquery::split(row.at("detail"));
- if (details.size() > 2 && details[0] == "SCAN") {
- tables_.push_back(details[2]);
- }
- }
-}
-
-Status QueryPlanner::applyTypes(TableColumns& columns) {
- std::map<size_t, ColumnType> column_types;
- for (const auto& row : program_) {
- if (row.at("opcode") == "ResultRow") {
- // The column parsing is finished.
- auto k = boost::lexical_cast<size_t>(row.at("p1"));
- for (const auto& type : column_types) {
- if (type.first - k < columns.size()) {
- std::get<1>(columns[type.first - k]) = type.second;
- }
- }
- }
-
- if (row.at("opcode") == "Copy") {
- // Copy P1 -> P1 + P3 into P2 -> P2 + P3.
- auto from = boost::lexical_cast<size_t>(row.at("p1"));
- auto to = boost::lexical_cast<size_t>(row.at("p2"));
- auto size = boost::lexical_cast<size_t>(row.at("p3"));
- for (size_t i = 0; i <= size; i++) {
- if (column_types.count(from + i)) {
- column_types[to + i] = std::move(column_types[from + i]);
- column_types.erase(from + i);
- }
- }
- } else if (row.at("opcode") == "Cast") {
- auto value = boost::lexical_cast<size_t>(row.at("p1"));
- auto to = boost::lexical_cast<size_t>(row.at("p2"));
- switch (to) {
- case 'A': // BLOB
- column_types[value] = BLOB_TYPE;
- break;
- case 'B': // TEXT
- column_types[value] = TEXT_TYPE;
- break;
- case 'C': // NUMERIC
- // We don't exactly have an equivalent to NUMERIC (which includes such
- // things as DATETIME and DECIMAL
- column_types[value] = UNKNOWN_TYPE;
- break;
- case 'D': // INTEGER
- column_types[value] = BIGINT_TYPE;
- break;
- case 'E': // REAL
- column_types[value] = DOUBLE_TYPE;
- break;
- default:
- column_types[value] = UNKNOWN_TYPE;
- break;
- }
- }
-
- if (kSQLOpcodes.count(row.at("opcode"))) {
- const auto& op = kSQLOpcodes.at(row.at("opcode"));
- auto k = boost::lexical_cast<size_t>(row.at(Opcode::regString(op.reg)));
- column_types[k] = op.type;
- }
- }
-
- return Status(0);
+ const SQLiteDBInstanceRef& instance)
+{
+ QueryData plan;
+ queryInternal("EXPLAIN QUERY PLAN " + query, plan, instance);
+ queryInternal("EXPLAIN " + query, program_, instance);
+
+ for (const auto& row : plan) {
+ auto details = osquery::split(row.at("detail"));
+ if (details.size() > 2 && details[0] == "SCAN") {
+ tables_.push_back(details[2]);
+ }
+ }
+}
+
+Status QueryPlanner::applyTypes(TableColumns& columns)
+{
+ std::map<size_t, ColumnType> column_types;
+ for (const auto& row : program_) {
+ if (row.at("opcode") == "ResultRow") {
+ // The column parsing is finished.
+ auto k = boost::lexical_cast<size_t>(row.at("p1"));
+ for (const auto& type : column_types) {
+ if (type.first - k < columns.size()) {
+ std::get<1>(columns[type.first - k]) = type.second;
+ }
+ }
+ }
+
+ if (row.at("opcode") == "Copy") {
+ // Copy P1 -> P1 + P3 into P2 -> P2 + P3.
+ auto from = boost::lexical_cast<size_t>(row.at("p1"));
+ auto to = boost::lexical_cast<size_t>(row.at("p2"));
+ auto size = boost::lexical_cast<size_t>(row.at("p3"));
+ for (size_t i = 0; i <= size; i++) {
+ if (column_types.count(from + i)) {
+ column_types[to + i] = std::move(column_types[from + i]);
+ column_types.erase(from + i);
+ }
+ }
+ } else if (row.at("opcode") == "Cast") {
+ auto value = boost::lexical_cast<size_t>(row.at("p1"));
+ auto to = boost::lexical_cast<size_t>(row.at("p2"));
+ switch (to) {
+ case 'A': // BLOB
+ column_types[value] = BLOB_TYPE;
+ break;
+ case 'B': // TEXT
+ column_types[value] = TEXT_TYPE;
+ break;
+ case 'C': // NUMERIC
+ // We don't exactly have an equivalent to NUMERIC (which includes such
+ // things as DATETIME and DECIMAL
+ column_types[value] = UNKNOWN_TYPE;
+ break;
+ case 'D': // INTEGER
+ column_types[value] = BIGINT_TYPE;
+ break;
+ case 'E': // REAL
+ column_types[value] = DOUBLE_TYPE;
+ break;
+ default:
+ column_types[value] = UNKNOWN_TYPE;
+ break;
+ }
+ }
+
+ if (kSQLOpcodes.count(row.at("opcode"))) {
+ const auto& op = kSQLOpcodes.at(row.at("opcode"));
+ auto k = boost::lexical_cast<size_t>(row.at(Opcode::regString(op.reg)));
+ column_types[k] = op.type;
+ }
+ }
+
+ return Status(0);
}
// Wrapper for legacy method until all uses can be replaced
Status queryInternal(const std::string& query,
- QueryData& results,
- const SQLiteDBInstanceRef& instance) {
- QueryDataTyped typedResults;
- Status status = queryInternal(query, typedResults, instance);
- if (status.ok()) {
- results.reserve(typedResults.size());
- for (const auto& row : typedResults) {
- Row r;
- for (const auto& col : row) {
- r[col.first] = castVariant(col.second);
- }
- results.push_back(std::move(r));
- }
- }
- return status;
+ QueryData& results,
+ const SQLiteDBInstanceRef& instance)
+{
+ QueryDataTyped typedResults;
+ Status status = queryInternal(query, typedResults, instance);
+ if (status.ok()) {
+ results.reserve(typedResults.size());
+ for (const auto& row : typedResults) {
+ Row r;
+ for (const auto& col : row) {
+ r[col.first] = castVariant(col.second);
+ }
+ results.push_back(std::move(r));
+ }
+ }
+ return status;
}
Status readRows(sqlite3_stmt* prepared_statement,
- QueryDataTyped& results,
- const SQLiteDBInstanceRef& instance) {
- // Do nothing with a null prepared_statement (eg, if the sql was just
- // whitespace)
- if (prepared_statement == nullptr) {
- return Status::success();
- }
- int rc = sqlite3_step(prepared_statement);
- /* if we have a result set row... */
- if (SQLITE_ROW == rc) {
- // First collect the column names
- int num_columns = sqlite3_column_count(prepared_statement);
- std::vector<std::string> colNames;
- colNames.reserve(num_columns);
- for (int i = 0; i < num_columns; i++) {
- colNames.push_back(sqlite3_column_name(prepared_statement, i));
- }
-
- do {
- RowTyped row;
- for (int i = 0; i < num_columns; i++) {
- switch (sqlite3_column_type(prepared_statement, i)) {
- case SQLITE_INTEGER:
- row[colNames[i]] = static_cast<long long>(
- sqlite3_column_int64(prepared_statement, i));
- break;
- case SQLITE_FLOAT:
- row[colNames[i]] = sqlite3_column_double(prepared_statement, i);
- break;
- case SQLITE_NULL:
- row[colNames[i]] = "";
- break;
- default:
- // Everything else (SQLITE_TEXT, SQLITE3_TEXT, SQLITE_BLOB) is
- // obtained/conveyed as text/string
- row[colNames[i]] = std::string(reinterpret_cast<const char*>(
- sqlite3_column_text(prepared_statement, i)));
- }
- }
- results.push_back(std::move(row));
- rc = sqlite3_step(prepared_statement);
- } while (SQLITE_ROW == rc);
- }
- if (rc != SQLITE_DONE) {
- return Status::failure(sqlite3_errmsg(instance->db()));
- }
-
- rc = sqlite3_finalize(prepared_statement);
- if (rc != SQLITE_OK) {
- return Status::failure(sqlite3_errmsg(instance->db()));
- }
-
- return Status::success();
+ QueryDataTyped& results,
+ const SQLiteDBInstanceRef& instance)
+{
+ // Do nothing with a null prepared_statement (eg, if the sql was just
+ // whitespace)
+ if (prepared_statement == nullptr) {
+ return Status::success();
+ }
+ int rc = sqlite3_step(prepared_statement);
+ /* if we have a result set row... */
+ if (SQLITE_ROW == rc) {
+ // First collect the column names
+ int num_columns = sqlite3_column_count(prepared_statement);
+ std::vector<std::string> colNames;
+ colNames.reserve(num_columns);
+ for (int i = 0; i < num_columns; i++) {
+ colNames.push_back(sqlite3_column_name(prepared_statement, i));
+ }
+
+ do {
+ RowTyped row;
+ for (int i = 0; i < num_columns; i++) {
+ switch (sqlite3_column_type(prepared_statement, i)) {
+ case SQLITE_INTEGER:
+ row[colNames[i]] = static_cast<long long>(
+ sqlite3_column_int64(prepared_statement, i));
+ break;
+ case SQLITE_FLOAT:
+ row[colNames[i]] = sqlite3_column_double(prepared_statement, i);
+ break;
+ case SQLITE_NULL:
+ row[colNames[i]] = "";
+ break;
+ default:
+ // Everything else (SQLITE_TEXT, SQLITE3_TEXT, SQLITE_BLOB) is
+ // obtained/conveyed as text/string
+ row[colNames[i]] = std::string(reinterpret_cast<const char*>(
+ sqlite3_column_text(prepared_statement, i)));
+ }
+ }
+ results.push_back(std::move(row));
+ rc = sqlite3_step(prepared_statement);
+ } while (SQLITE_ROW == rc);
+ }
+ if (rc != SQLITE_DONE) {
+ return Status::failure(sqlite3_errmsg(instance->db()));
+ }
+
+ rc = sqlite3_finalize(prepared_statement);
+ if (rc != SQLITE_OK) {
+ return Status::failure(sqlite3_errmsg(instance->db()));
+ }
+
+ return Status::success();
}
Status queryInternal(const std::string& query,
- QueryDataTyped& results,
- const SQLiteDBInstanceRef& instance) {
- sqlite3_stmt* prepared_statement{nullptr}; /* Statement to execute. */
-
- int rc = SQLITE_OK; /* Return Code */
- const char* leftover_sql = nullptr; /* Tail of unprocessed SQL */
- const char* sql = query.c_str(); /* SQL to be processed */
-
- /* The big while loop. One iteration per statement */
- while ((sql[0] != '\0') && (SQLITE_OK == rc)) {
- const auto lock = instance->attachLock();
-
- // Trim leading whitespace
- while (isspace(sql[0])) {
- sql++;
- }
- rc = sqlite3_prepare_v2(
- instance->db(), sql, -1, &prepared_statement, &leftover_sql);
- if (rc != SQLITE_OK) {
- Status s = Status::failure(sqlite3_errmsg(instance->db()));
- sqlite3_finalize(prepared_statement);
- return s;
- }
-
- Status s = readRows(prepared_statement, results, instance);
- if (!s.ok()) {
- return s;
- }
-
- sql = leftover_sql;
- } /* end while */
- sqlite3_db_release_memory(instance->db());
- return Status::success();
+ QueryDataTyped& results,
+ const SQLiteDBInstanceRef& instance)
+{
+ sqlite3_stmt* prepared_statement{nullptr}; /* Statement to execute. */
+
+ int rc = SQLITE_OK; /* Return Code */
+ const char* leftover_sql = nullptr; /* Tail of unprocessed SQL */
+ const char* sql = query.c_str(); /* SQL to be processed */
+
+ /* The big while loop. One iteration per statement */
+ while ((sql[0] != '\0') && (SQLITE_OK == rc)) {
+ const auto lock = instance->attachLock();
+
+ // Trim leading whitespace
+ while (isspace(sql[0])) {
+ sql++;
+ }
+ rc = sqlite3_prepare_v2(
+ instance->db(), sql, -1, &prepared_statement, &leftover_sql);
+ if (rc != SQLITE_OK) {
+ Status s = Status::failure(sqlite3_errmsg(instance->db()));
+ sqlite3_finalize(prepared_statement);
+ return s;
+ }
+
+ Status s = readRows(prepared_statement, results, instance);
+ if (!s.ok()) {
+ return s;
+ }
+
+ sql = leftover_sql;
+ } /* end while */
+ sqlite3_db_release_memory(instance->db());
+ return Status::success();
}
Status getQueryColumnsInternal(const std::string& q,
- TableColumns& columns,
- const SQLiteDBInstanceRef& instance) {
- Status status = Status();
- TableColumns results;
- {
- auto lock = instance->attachLock();
-
- // Turn the query into a prepared statement
- sqlite3_stmt* stmt{nullptr};
- auto rc = sqlite3_prepare_v2(instance->db(),
- q.c_str(),
- static_cast<int>(q.length() + 1),
- &stmt,
- nullptr);
- if (rc != SQLITE_OK || stmt == nullptr) {
- if (stmt != nullptr) {
- sqlite3_finalize(stmt);
- }
- return Status(1, sqlite3_errmsg(instance->db()));
- }
-
- // Get column count
- auto num_columns = sqlite3_column_count(stmt);
- results.reserve(num_columns);
-
- // Get column names and types
- bool unknown_type = false;
- for (int i = 0; i < num_columns; ++i) {
- auto col_name = sqlite3_column_name(stmt, i);
- auto col_type = sqlite3_column_decltype(stmt, i);
-
- if (col_name == nullptr) {
- status = Status(1, "Could not get column type");
- break;
- }
-
- if (col_type == nullptr) {
- // Types are only returned for table columns (not expressions).
- col_type = "UNKNOWN";
- unknown_type = true;
- }
- results.push_back(std::make_tuple(
- col_name, columnTypeName(col_type), ColumnOptions::DEFAULT));
- }
-
- // An unknown type means we have to parse the plan and SQLite opcodes.
- if (unknown_type) {
- QueryPlanner planner(q, instance);
- planner.applyTypes(results);
- }
- sqlite3_finalize(stmt);
- }
-
- if (status.ok()) {
- columns = std::move(results);
- }
-
- return status;
+ TableColumns& columns,
+ const SQLiteDBInstanceRef& instance)
+{
+ Status status = Status();
+ TableColumns results;
+ {
+ auto lock = instance->attachLock();
+
+ // Turn the query into a prepared statement
+ sqlite3_stmt* stmt{nullptr};
+ auto rc = sqlite3_prepare_v2(instance->db(),
+ q.c_str(),
+ static_cast<int>(q.length() + 1),
+ &stmt,
+ nullptr);
+ if (rc != SQLITE_OK || stmt == nullptr) {
+ if (stmt != nullptr) {
+ sqlite3_finalize(stmt);
+ }
+ return Status(1, sqlite3_errmsg(instance->db()));
+ }
+
+ // Get column count
+ auto num_columns = sqlite3_column_count(stmt);
+ results.reserve(num_columns);
+
+ // Get column names and types
+ bool unknown_type = false;
+ for (int i = 0; i < num_columns; ++i) {
+ auto col_name = sqlite3_column_name(stmt, i);
+ auto col_type = sqlite3_column_decltype(stmt, i);
+
+ if (col_name == nullptr) {
+ status = Status(1, "Could not get column type");
+ break;
+ }
+
+ if (col_type == nullptr) {
+ // Types are only returned for table columns (not expressions).
+ col_type = "UNKNOWN";
+ unknown_type = true;
+ }
+ results.push_back(std::make_tuple(
+ col_name, columnTypeName(col_type), ColumnOptions::DEFAULT));
+ }
+
+ // An unknown type means we have to parse the plan and SQLite opcodes.
+ if (unknown_type) {
+ QueryPlanner planner(q, instance);
+ planner.applyTypes(results);
+ }
+ sqlite3_finalize(stmt);
+ }
+
+ if (status.ok()) {
+ columns = std::move(results);
+ }
+
+ return status;
}
} // namespace osquery
* SQLiteDBInstance.
*/
class SQLiteDBInstance : private boost::noncopyable {
- public:
- SQLiteDBInstance() {
- init();
- }
- SQLiteDBInstance(sqlite3*& db, Mutex& mtx);
- ~SQLiteDBInstance();
-
- /// Check if the instance is the osquery primary.
- bool isPrimary() const {
- return primary_;
- }
-
- /// Generate a new 'transient' connection.
- void init();
-
- /**
- * @brief Accessor to the internal `sqlite3` object, do not store references
- * to the object within osquery code.
- */
- sqlite3* db() const {
- return db_;
- }
-
- /// Allow a virtual table implementation to record use/access of a table.
- void addAffectedTable(std::shared_ptr<VirtualTableContent> table);
-
- /// Clear per-query state of a table affected by the use of this instance.
- void clearAffectedTables();
-
- /// Check if a virtual table had been called already.
- bool tableCalled(VirtualTableContent const& table);
-
- /// Request that virtual tables use a warm cache for their results.
- void useCache(bool use_cache);
-
- /// Check if the query requested use of the warm query cache.
- bool useCache() const;
-
- /// Lock the database for attaching virtual tables.
- RecursiveLock attachLock() const;
-
- private:
- /// Handle the primary/forwarding requests for table attribute accesses.
- TableAttributes getAttributes() const;
-
- private:
- /// An opaque constructor only used by the DBManager.
- explicit SQLiteDBInstance(sqlite3* db)
- : primary_(true), managed_(true), db_(db) {}
-
- private:
- /// Introspection into the database pointer, primary means managed.
- bool primary_{false};
-
- /// Track whether this instance is managed internally by the DB manager.
- bool managed_{false};
-
- /// True if this query should bypass table cache.
- bool use_cache_{false};
-
- /// Either the managed primary database or an ephemeral instance.
- sqlite3* db_{nullptr};
-
- /**
- * @brief An attempted unique lock on the manager's primary database mutex.
- *
- * This lock is not always acquired. If it is then this instance has locked
- * access to the 'primary' SQLite database.
- */
- WriteLock lock_;
-
- /**
- * @brief A mutex protecting access to this instance's SQLite database.
- *
- * Attaching, and other access, can occur async from the registry APIs.
- *
- * If a database is primary then the static attach mutex is used.
- */
- mutable RecursiveMutex attach_mutex_;
-
- /// See attach_mutex_ but used for the primary database.
- static RecursiveMutex kPrimaryAttachMutex;
-
- /// Vector of tables that need their constraints cleared after execution.
- std::map<std::string, std::shared_ptr<VirtualTableContent>> affected_tables_;
-
- private:
- friend class SQLiteDBManager;
- friend class SQLInternal;
-
- private:
- FRIEND_TEST(SQLiteUtilTests, test_affected_tables);
+public:
+ SQLiteDBInstance()
+ {
+ init();
+ }
+ SQLiteDBInstance(sqlite3*& db, Mutex& mtx);
+ ~SQLiteDBInstance();
+
+ /// Check if the instance is the osquery primary.
+ bool isPrimary() const
+ {
+ return primary_;
+ }
+
+ /// Generate a new 'transient' connection.
+ void init();
+
+ /**
+ * @brief Accessor to the internal `sqlite3` object, do not store references
+ * to the object within osquery code.
+ */
+ sqlite3* db() const
+ {
+ return db_;
+ }
+
+ /// Allow a virtual table implementation to record use/access of a table.
+ void addAffectedTable(std::shared_ptr<VirtualTableContent> table);
+
+ /// Clear per-query state of a table affected by the use of this instance.
+ void clearAffectedTables();
+
+ /// Check if a virtual table had been called already.
+ bool tableCalled(VirtualTableContent const& table);
+
+ /// Request that virtual tables use a warm cache for their results.
+ void useCache(bool use_cache);
+
+ /// Check if the query requested use of the warm query cache.
+ bool useCache() const;
+
+ /// Lock the database for attaching virtual tables.
+ RecursiveLock attachLock() const;
+
+private:
+ /// Handle the primary/forwarding requests for table attribute accesses.
+ TableAttributes getAttributes() const;
+
+private:
+ /// An opaque constructor only used by the DBManager.
+ explicit SQLiteDBInstance(sqlite3* db)
+ : primary_(true), managed_(true), db_(db) {}
+
+private:
+ /// Introspection into the database pointer, primary means managed.
+ bool primary_{false};
+
+ /// Track whether this instance is managed internally by the DB manager.
+ bool managed_{false};
+
+ /// True if this query should bypass table cache.
+ bool use_cache_{false};
+
+ /// Either the managed primary database or an ephemeral instance.
+ sqlite3* db_{nullptr};
+
+ /**
+ * @brief An attempted unique lock on the manager's primary database mutex.
+ *
+ * This lock is not always acquired. If it is then this instance has locked
+ * access to the 'primary' SQLite database.
+ */
+ WriteLock lock_;
+
+ /**
+ * @brief A mutex protecting access to this instance's SQLite database.
+ *
+ * Attaching, and other access, can occur async from the registry APIs.
+ *
+ * If a database is primary then the static attach mutex is used.
+ */
+ mutable RecursiveMutex attach_mutex_;
+
+ /// See attach_mutex_ but used for the primary database.
+ static RecursiveMutex kPrimaryAttachMutex;
+
+ /// Vector of tables that need their constraints cleared after execution.
+ std::map<std::string, std::shared_ptr<VirtualTableContent>> affected_tables_;
+
+private:
+ friend class SQLiteDBManager;
+ friend class SQLInternal;
+
+private:
+ FRIEND_TEST(SQLiteUtilTests, test_affected_tables);
};
using SQLiteDBInstanceRef = std::shared_ptr<SQLiteDBInstance>;
* resources as well as provide optimization around resource access.
*/
class SQLiteDBManager : private boost::noncopyable {
- public:
- static SQLiteDBManager& instance() {
- static SQLiteDBManager instance;
- return instance;
- }
-
- /**
- * @brief Return a fully configured `sqlite3` database object wrapper.
- *
- * An osquery database is basically just a SQLite3 database with several
- * virtual tables attached. This method is the main abstraction for accessing
- * SQLite3 databases within osquery.
- *
- * A RAII wrapper around the `sqlite3` database will manage attaching tables
- * and freeing resources when the instance (connection per-say) goes out of
- * scope. Using the SQLiteDBManager will also try to optimize the number of
- * `sqlite3` databases in use by managing a single global instance and
- * returning resource-safe transient databases if there's access contention.
- *
- * Note: osquery::initOsquery must be called before calling `get` in order
- * for virtual tables to be registered.
- *
- * @return a SQLiteDBInstance with all virtual tables attached.
- */
- static SQLiteDBInstanceRef get() {
- return getConnection();
- }
-
- /// See `get` but always return a transient DB connection (for testing).
- static SQLiteDBInstanceRef getUnique();
-
- /**
- * @brief Reset the primary database connection.
- *
- * Over time it may be helpful to remove SQLite's arena.
- * We can periodically close and re-initialize and connect virtual tables.
- */
- static void resetPrimary();
-
- /**
- * @brief Check if `table_name` is disabled.
- *
- * Check if `table_name` is in the list of tables passed in to the
- * `--disable_tables` flag.
- *
- * @param The name of the Table to check.
- * @return If `table_name` is disabled.
- */
- static bool isDisabled(const std::string& table_name);
-
- protected:
- SQLiteDBManager();
- virtual ~SQLiteDBManager();
-
- public:
- SQLiteDBManager(SQLiteDBManager const&) = delete;
- SQLiteDBManager& operator=(SQLiteDBManager const&) = delete;
-
- private:
- /// Primary (managed) sqlite3 database.
- sqlite3* db_{nullptr};
-
- /// The primary connection maintains an opaque instance.
- SQLiteDBInstanceRef connection_{nullptr};
-
- /// Mutex and lock around sqlite3 access.
- Mutex mutex_;
-
- /// A write mutex for initializing the primary database.
- Mutex create_mutex_;
-
- /// Member variable to hold set of disabled tables.
- std::unordered_set<std::string> disabled_tables_;
-
- /// Parse a comma-delimited set of tables names, passed in as a flag.
- void setDisabledTables(const std::string& s);
-
- /// Request a connection, optionally request the primary connection.
- static SQLiteDBInstanceRef getConnection(bool primary = false);
-
- private:
- friend class SQLiteDBInstance;
- friend class SQLiteSQLPlugin;
+public:
+ static SQLiteDBManager& instance()
+ {
+ static SQLiteDBManager instance;
+ return instance;
+ }
+
+ /**
+ * @brief Return a fully configured `sqlite3` database object wrapper.
+ *
+ * An osquery database is basically just a SQLite3 database with several
+ * virtual tables attached. This method is the main abstraction for accessing
+ * SQLite3 databases within osquery.
+ *
+ * A RAII wrapper around the `sqlite3` database will manage attaching tables
+ * and freeing resources when the instance (connection per-say) goes out of
+ * scope. Using the SQLiteDBManager will also try to optimize the number of
+ * `sqlite3` databases in use by managing a single global instance and
+ * returning resource-safe transient databases if there's access contention.
+ *
+ * Note: osquery::initOsquery must be called before calling `get` in order
+ * for virtual tables to be registered.
+ *
+ * @return a SQLiteDBInstance with all virtual tables attached.
+ */
+ static SQLiteDBInstanceRef get()
+ {
+ return getConnection();
+ }
+
+ /// See `get` but always return a transient DB connection (for testing).
+ static SQLiteDBInstanceRef getUnique();
+
+ /**
+ * @brief Reset the primary database connection.
+ *
+ * Over time it may be helpful to remove SQLite's arena.
+ * We can periodically close and re-initialize and connect virtual tables.
+ */
+ static void resetPrimary();
+
+ /**
+ * @brief Check if `table_name` is disabled.
+ *
+ * Check if `table_name` is in the list of tables passed in to the
+ * `--disable_tables` flag.
+ *
+ * @param The name of the Table to check.
+ * @return If `table_name` is disabled.
+ */
+ static bool isDisabled(const std::string& table_name);
+
+protected:
+ SQLiteDBManager();
+ virtual ~SQLiteDBManager();
+
+public:
+ SQLiteDBManager(SQLiteDBManager const&) = delete;
+ SQLiteDBManager& operator=(SQLiteDBManager const&) = delete;
+
+private:
+ /// Primary (managed) sqlite3 database.
+ sqlite3* db_{nullptr};
+
+ /// The primary connection maintains an opaque instance.
+ SQLiteDBInstanceRef connection_{nullptr};
+
+ /// Mutex and lock around sqlite3 access.
+ Mutex mutex_;
+
+ /// A write mutex for initializing the primary database.
+ Mutex create_mutex_;
+
+ /// Member variable to hold set of disabled tables.
+ std::unordered_set<std::string> disabled_tables_;
+
+ /// Parse a comma-delimited set of tables names, passed in as a flag.
+ void setDisabledTables(const std::string& s);
+
+ /// Request a connection, optionally request the primary connection.
+ static SQLiteDBInstanceRef getConnection(bool primary = false);
+
+private:
+ friend class SQLiteDBInstance;
+ friend class SQLiteSQLPlugin;
};
/**
* once per new query and only when needed (aka an unusable expression).
*/
class QueryPlanner : private boost::noncopyable {
- public:
- explicit QueryPlanner(const std::string& query)
- : QueryPlanner(query, SQLiteDBManager::get()) {}
- QueryPlanner(const std::string& query, const SQLiteDBInstanceRef& instance);
- ~QueryPlanner() {}
-
- public:
- /**
- * @brief Scan the plan and program for opcodes that infer types.
- *
- * This allows column type inference based on column expressions. The query
- * column introspection may use a QueryPlanner to apply types to the unknown
- * columns (which are usually expressions).
- *
- * @param column an ordered set of columns to fill in type information.
- * @return success if all columns types were found, otherwise false.
- */
- Status applyTypes(TableColumns& columns);
-
- /// Get the list of tables filtered by this query.
- std::vector<std::string> tables() const {
- return tables_;
- }
-
- /**
- * @brief A helper structure to represent an opcode's result and type.
- *
- * An opcode can be defined by a register and type, for the sake of the
- * only known use case of resultant type determination.
- */
- struct Opcode {
- enum Register {
- P1 = 0,
- P2,
- P3,
- };
-
- Register reg;
- ColumnType type;
-
- public:
- Opcode(Register r, ColumnType t) : reg(r), type(t) {}
-
- /// Return a register as its column string name.
- static std::string regString(Register r) {
- static std::vector<std::string> regs = {"p1", "p2", "p3"};
- return regs[r];
- }
- };
-
- private:
- /// The results of EXPLAIN q.
- QueryData program_;
- /// The order of tables scanned.
- std::vector<std::string> tables_;
+public:
+ explicit QueryPlanner(const std::string& query)
+ : QueryPlanner(query, SQLiteDBManager::get()) {}
+ QueryPlanner(const std::string& query, const SQLiteDBInstanceRef& instance);
+ ~QueryPlanner() {}
+
+public:
+ /**
+ * @brief Scan the plan and program for opcodes that infer types.
+ *
+ * This allows column type inference based on column expressions. The query
+ * column introspection may use a QueryPlanner to apply types to the unknown
+ * columns (which are usually expressions).
+ *
+ * @param column an ordered set of columns to fill in type information.
+ * @return success if all columns types were found, otherwise false.
+ */
+ Status applyTypes(TableColumns& columns);
+
+ /// Get the list of tables filtered by this query.
+ std::vector<std::string> tables() const
+ {
+ return tables_;
+ }
+
+ /**
+ * @brief A helper structure to represent an opcode's result and type.
+ *
+ * An opcode can be defined by a register and type, for the sake of the
+ * only known use case of resultant type determination.
+ */
+ struct Opcode {
+ enum Register {
+ P1 = 0,
+ P2,
+ P3,
+ };
+
+ Register reg;
+ ColumnType type;
+
+ public:
+ Opcode(Register r, ColumnType t) : reg(r), type(t) {}
+
+ /// Return a register as its column string name.
+ static std::string regString(Register r)
+ {
+ static std::vector<std::string> regs = {"p1", "p2", "p3"};
+ return regs[r];
+ }
+ };
+
+private:
+ /// The results of EXPLAIN q.
+ QueryData program_;
+ /// The order of tables scanned.
+ std::vector<std::string> tables_;
};
/// Specific SQLite opcodes that change column/expression type.
* @return A status indicating SQL query results.
*/
Status queryInternal(const std::string& q,
- QueryDataTyped& results,
- const SQLiteDBInstanceRef& instance);
+ QueryDataTyped& results,
+ const SQLiteDBInstanceRef& instance);
/**
* @brief SQLite Internal: Execute a query on a specific database
* @return A status indicating SQL query results.
*/
Status queryInternal(const std::string& q,
- QueryData& results,
- const SQLiteDBInstanceRef& instance);
+ QueryData& results,
+ const SQLiteDBInstanceRef& instance);
/**
* @brief SQLite Intern: Analyze a query, providing information about the
* @return status indicating success or failure of the operation
*/
Status getQueryColumnsInternal(const std::string& q,
- TableColumns& columns,
- const SQLiteDBInstanceRef& instance);
+ TableColumns& columns,
+ const SQLiteDBInstanceRef& instance);
/**
* @brief SQLInternal: like SQL, but backed by internal calls, and deals
* with QueryDataTyped results.
*/
class SQLInternal : private only_movable {
- public:
- /**
- * @brief Instantiate an instance of the class with an internal query.
- *
- * @param query An osquery SQL query.
- * @param use_cache [optional] Set true to use the query cache.
- */
- explicit SQLInternal(const std::string& query, bool use_cache = false);
-
- public:
- /**
- * @brief Const accessor for the rows returned by the query.
- *
- * @return A QueryDataTyped object of the query results.
- */
- QueryDataTyped& rowsTyped();
-
- const Status& getStatus() const;
-
- /**
- * @brief Check if the SQL query's results use event-based tables.
- *
- * Higher level SQL facilities, like the scheduler, may act differently when
- * the results of a query (including a JOIN) are event-based. For example,
- * it does not make sense to perform set difference checks for an
- * always-append result set.
- *
- * All the tables used in the query will be checked. The TableAttributes of
- * each will be ORed and if any include EVENT_BASED, this will return true.
- */
- bool eventBased() const;
-
- /// ASCII escape the results of the query.
- void escapeResults();
-
- private:
- /// The internal member which holds the typed results of the query.
- QueryDataTyped resultsTyped_;
-
- /// The internal member which holds the status of the query.
- Status status_;
- /// Before completing the execution, store a check for EVENT_BASED.
- bool event_based_{false};
+public:
+ /**
+ * @brief Instantiate an instance of the class with an internal query.
+ *
+ * @param query An osquery SQL query.
+ * @param use_cache [optional] Set true to use the query cache.
+ */
+ explicit SQLInternal(const std::string& query, bool use_cache = false);
+
+public:
+ /**
+ * @brief Const accessor for the rows returned by the query.
+ *
+ * @return A QueryDataTyped object of the query results.
+ */
+ QueryDataTyped& rowsTyped();
+
+ const Status& getStatus() const;
+
+ /**
+ * @brief Check if the SQL query's results use event-based tables.
+ *
+ * Higher level SQL facilities, like the scheduler, may act differently when
+ * the results of a query (including a JOIN) are event-based. For example,
+ * it does not make sense to perform set difference checks for an
+ * always-append result set.
+ *
+ * All the tables used in the query will be checked. The TableAttributes of
+ * each will be ORed and if any include EVENT_BASED, this will return true.
+ */
+ bool eventBased() const;
+
+ /// ASCII escape the results of the query.
+ void escapeResults();
+
+private:
+ /// The internal member which holds the typed results of the query.
+ QueryDataTyped resultsTyped_;
+
+ /// The internal member which holds the status of the query.
+ Status status_;
+ /// Before completing the execution, store a check for EVENT_BASED.
+ bool event_based_{false};
};
/**
* @param results The TableRows data structure that will hold the returned rows
*/
Status genTableRowsForSqliteTable(const boost::filesystem::path& sqlite_db,
- const std::string& sqlite_query,
- TableRows& results,
- bool respect_locking = true);
+ const std::string& sqlite_query,
+ TableRows& results,
+ bool respect_locking = true);
} // namespace osquery
extern void escapeNonPrintableBytesEx(std::string& data);
class SQLTests : public testing::Test {
- public:
- void SetUp() override {
- registryAndPluginInit();
- }
+public:
+ void SetUp() override
+ {
+ registryAndPluginInit();
+ }
};
class TestTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const {
- return {
- std::make_tuple("test_int", INTEGER_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("test_text", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- TableRows generate(QueryContext& ctx) {
- TableRows results;
- if (ctx.constraints["test_int"].existsAndMatches("1")) {
- results.push_back(
- make_table_row({{"test_int", "1"}, {"test_text", "0"}}));
- } else {
- results.push_back(
- make_table_row({{"test_int", "0"}, {"test_text", "1"}}));
- }
-
- auto ints = ctx.constraints["test_int"].getAll<int>(EQUALS);
- for (const auto& int_match : ints) {
- results.push_back(make_table_row({{"test_int", INTEGER(int_match)}}));
- }
-
- return results;
- }
+private:
+ TableColumns columns() const
+ {
+ return {
+ std::make_tuple("test_int", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("test_text", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+ TableRows generate(QueryContext& ctx)
+ {
+ TableRows results;
+ if (ctx.constraints["test_int"].existsAndMatches("1")) {
+ results.push_back(
+ make_table_row({{"test_int", "1"}, {"test_text", "0"}}));
+ } else {
+ results.push_back(
+ make_table_row({{"test_int", "0"}, {"test_text", "1"}}));
+ }
+
+ auto ints = ctx.constraints["test_int"].getAll<int>(EQUALS);
+ for (const auto& int_match : ints) {
+ results.push_back(make_table_row({{"test_int", INTEGER(int_match)}}));
+ }
+
+ return results;
+ }
};
-TEST_F(SQLTests, test_raw_access_context) {
- auto tables = RegistryFactory::get().registry("table");
- tables->add("test", std::make_shared<TestTablePlugin>());
- auto results = SQL::selectAllFrom("test");
+TEST_F(SQLTests, test_raw_access_context)
+{
+ auto tables = RegistryFactory::get().registry("table");
+ tables->add("test", std::make_shared<TestTablePlugin>());
+ auto results = SQL::selectAllFrom("test");
- EXPECT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["test_text"], "1");
+ EXPECT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["test_text"], "1");
- results = SQL::selectAllFrom("test", "test_int", EQUALS, "1");
- EXPECT_EQ(results.size(), 2U);
+ results = SQL::selectAllFrom("test", "test_int", EQUALS, "1");
+ EXPECT_EQ(results.size(), 2U);
- results = SQL::selectAllFrom("test", "test_int", EQUALS, "2");
- EXPECT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["test_int"], "2");
+ results = SQL::selectAllFrom("test", "test_int", EQUALS, "2");
+ EXPECT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["test_int"], "2");
}
-TEST_F(SQLTests, test_sql_escape) {
- std::string input = "しかたがない";
- escapeNonPrintableBytesEx(input);
- EXPECT_EQ(input,
- "\\xE3\\x81\\x97\\xE3\\x81\\x8B\\xE3\\x81\\x9F\\xE3\\x81\\x8C\\xE3"
- "\\x81\\xAA\\xE3\\x81\\x84");
-
- input = "悪因悪果";
- escapeNonPrintableBytesEx(input);
- EXPECT_EQ(input,
- "\\xE6\\x82\\xAA\\xE5\\x9B\\xA0\\xE6\\x82\\xAA\\xE6\\x9E\\x9C");
-
- input = "モンスターハンター";
- escapeNonPrintableBytesEx(input);
- EXPECT_EQ(input,
- "\\xE3\\x83\\xA2\\xE3\\x83\\xB3\\xE3\\x82\\xB9\\xE3\\x82\\xBF\\xE3"
- "\\x83\\xBC\\xE3\\x83\\x8F\\xE3\\x83\\xB3\\xE3\\x82\\xBF\\xE3\\x83"
- "\\xBC");
-
- input = "съешь же ещё этих мягких французских булок, да выпей чаю";
- escapeNonPrintableBytesEx(input);
- EXPECT_EQ(
- input,
- "\\xD1\\x81\\xD1\\x8A\\xD0\\xB5\\xD1\\x88\\xD1\\x8C \\xD0\\xB6\\xD0\\xB5 "
- "\\xD0\\xB5\\xD1\\x89\\xD1\\x91 \\xD1\\x8D\\xD1\\x82\\xD0\\xB8\\xD1\\x85 "
- "\\xD0\\xBC\\xD1\\x8F\\xD0\\xB3\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
- "\\xD1\\x84\\xD1\\x80\\xD0\\xB0\\xD0\\xBD\\xD1\\x86\\xD1\\x83\\xD0\\xB7\\"
- "xD1\\x81\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
- "\\xD0\\xB1\\xD1\\x83\\xD0\\xBB\\xD0\\xBE\\xD0\\xBA, "
- "\\xD0\\xB4\\xD0\\xB0 \\xD0\\xB2\\xD1\\x8B\\xD0\\xBF\\xD0\\xB5\\xD0\\xB9 "
- "\\xD1\\x87\\xD0\\xB0\\xD1\\x8E");
-
- input = "The quick brown fox jumps over the lazy dog.";
- escapeNonPrintableBytesEx(input);
- EXPECT_EQ(input, "The quick brown fox jumps over the lazy dog.");
+TEST_F(SQLTests, test_sql_escape)
+{
+ std::string input = "しかたがない";
+ escapeNonPrintableBytesEx(input);
+ EXPECT_EQ(input,
+ "\\xE3\\x81\\x97\\xE3\\x81\\x8B\\xE3\\x81\\x9F\\xE3\\x81\\x8C\\xE3"
+ "\\x81\\xAA\\xE3\\x81\\x84");
+
+ input = "悪因悪果";
+ escapeNonPrintableBytesEx(input);
+ EXPECT_EQ(input,
+ "\\xE6\\x82\\xAA\\xE5\\x9B\\xA0\\xE6\\x82\\xAA\\xE6\\x9E\\x9C");
+
+ input = "モンスターハンター";
+ escapeNonPrintableBytesEx(input);
+ EXPECT_EQ(input,
+ "\\xE3\\x83\\xA2\\xE3\\x83\\xB3\\xE3\\x82\\xB9\\xE3\\x82\\xBF\\xE3"
+ "\\x83\\xBC\\xE3\\x83\\x8F\\xE3\\x83\\xB3\\xE3\\x82\\xBF\\xE3\\x83"
+ "\\xBC");
+
+ input = "съешь же ещё этих мягких французских булок, да выпей чаю";
+ escapeNonPrintableBytesEx(input);
+ EXPECT_EQ(
+ input,
+ "\\xD1\\x81\\xD1\\x8A\\xD0\\xB5\\xD1\\x88\\xD1\\x8C \\xD0\\xB6\\xD0\\xB5 "
+ "\\xD0\\xB5\\xD1\\x89\\xD1\\x91 \\xD1\\x8D\\xD1\\x82\\xD0\\xB8\\xD1\\x85 "
+ "\\xD0\\xBC\\xD1\\x8F\\xD0\\xB3\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
+ "\\xD1\\x84\\xD1\\x80\\xD0\\xB0\\xD0\\xBD\\xD1\\x86\\xD1\\x83\\xD0\\xB7\\"
+ "xD1\\x81\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
+ "\\xD0\\xB1\\xD1\\x83\\xD0\\xBB\\xD0\\xBE\\xD0\\xBA, "
+ "\\xD0\\xB4\\xD0\\xB0 \\xD0\\xB2\\xD1\\x8B\\xD0\\xBF\\xD0\\xB5\\xD0\\xB9 "
+ "\\xD1\\x87\\xD0\\xB0\\xD1\\x8E");
+
+ input = "The quick brown fox jumps over the lazy dog.";
+ escapeNonPrintableBytesEx(input);
+ EXPECT_EQ(input, "The quick brown fox jumps over the lazy dog.");
}
}
namespace osquery {
-QueryDataTyped getTestDBExpectedResults() {
- QueryDataTyped d;
- RowTyped row1;
- row1["username"] = "mike";
- row1["age"] = 23LL;
- d.push_back(row1);
- RowTyped row2;
- row2["username"] = "matt";
- row2["age"] = 24LL;
- d.push_back(row2);
- return d;
+QueryDataTyped getTestDBExpectedResults()
+{
+ QueryDataTyped d;
+ RowTyped row1;
+ row1["username"] = "mike";
+ row1["age"] = 23LL;
+ d.push_back(row1);
+ RowTyped row2;
+ row2["username"] = "matt";
+ row2["age"] = 24LL;
+ d.push_back(row2);
+ return d;
}
-std::vector<std::pair<std::string, QueryDataTyped>> getTestDBResultStream() {
- std::vector<std::pair<std::string, QueryDataTyped>> results;
+std::vector<std::pair<std::string, QueryDataTyped>> getTestDBResultStream()
+{
+ std::vector<std::pair<std::string, QueryDataTyped>> results;
- std::string q2 =
- R"(INSERT INTO test_table (username, age) VALUES ("joe", 25))";
- QueryDataTyped d2;
- RowTyped row2_1;
- row2_1["username"] = "mike";
- row2_1["age"] = 23LL;
- d2.push_back(row2_1);
- RowTyped row2_2;
- row2_2["username"] = "matt";
- row2_2["age"] = 24LL;
- d2.push_back(row2_2);
- RowTyped row2_3;
- row2_3["username"] = "joe";
- row2_3["age"] = 25LL;
- d2.push_back(row2_3);
- results.push_back(std::make_pair(q2, d2));
+ std::string q2 =
+ R"(INSERT INTO test_table (username, age) VALUES ("joe", 25))";
+ QueryDataTyped d2;
+ RowTyped row2_1;
+ row2_1["username"] = "mike";
+ row2_1["age"] = 23LL;
+ d2.push_back(row2_1);
+ RowTyped row2_2;
+ row2_2["username"] = "matt";
+ row2_2["age"] = 24LL;
+ d2.push_back(row2_2);
+ RowTyped row2_3;
+ row2_3["username"] = "joe";
+ row2_3["age"] = 25LL;
+ d2.push_back(row2_3);
+ results.push_back(std::make_pair(q2, d2));
- std::string q3 = R"(UPDATE test_table SET age = 27 WHERE username = "matt")";
- QueryDataTyped d3;
- RowTyped row3_1;
- row3_1["username"] = "mike";
- row3_1["age"] = 23LL;
- d3.push_back(row3_1);
- RowTyped row3_2;
- row3_2["username"] = "matt";
- row3_2["age"] = 27LL;
- d3.push_back(row3_2);
- RowTyped row3_3;
- row3_3["username"] = "joe";
- row3_3["age"] = 25LL;
- d3.push_back(row3_3);
- results.push_back(std::make_pair(q3, d3));
+ std::string q3 = R"(UPDATE test_table SET age = 27 WHERE username = "matt")";
+ QueryDataTyped d3;
+ RowTyped row3_1;
+ row3_1["username"] = "mike";
+ row3_1["age"] = 23LL;
+ d3.push_back(row3_1);
+ RowTyped row3_2;
+ row3_2["username"] = "matt";
+ row3_2["age"] = 27LL;
+ d3.push_back(row3_2);
+ RowTyped row3_3;
+ row3_3["username"] = "joe";
+ row3_3["age"] = 25LL;
+ d3.push_back(row3_3);
+ results.push_back(std::make_pair(q3, d3));
- std::string q4 =
- R"(DELETE FROM test_table WHERE username = "matt" AND age = 27)";
- QueryDataTyped d4;
- RowTyped row4_1;
- row4_1["username"] = "mike";
- row4_1["age"] = 23LL;
- d4.push_back(row4_1);
- RowTyped row4_2;
- row4_2["username"] = "joe";
- row4_2["age"] = 25LL;
- d4.push_back(row4_2);
- results.push_back(std::make_pair(q4, d4));
+ std::string q4 =
+ R"(DELETE FROM test_table WHERE username = "matt" AND age = 27)";
+ QueryDataTyped d4;
+ RowTyped row4_1;
+ row4_1["username"] = "mike";
+ row4_1["age"] = 23LL;
+ d4.push_back(row4_1);
+ RowTyped row4_2;
+ row4_2["username"] = "joe";
+ row4_2["age"] = 25LL;
+ d4.push_back(row4_2);
+ results.push_back(std::make_pair(q4, d4));
- return results;
+ return results;
}
-ColumnNames getSerializedRowColumnNames(bool unordered_and_repeated) {
- ColumnNames cn;
- if (unordered_and_repeated) {
- cn.push_back("repeated_column");
- }
- cn.push_back("alphabetical");
- cn.push_back("foo");
- cn.push_back("meaning_of_life");
- cn.push_back("repeated_column");
- return cn;
+ColumnNames getSerializedRowColumnNames(bool unordered_and_repeated)
+{
+ ColumnNames cn;
+ if (unordered_and_repeated) {
+ cn.push_back("repeated_column");
+ }
+ cn.push_back("alphabetical");
+ cn.push_back("foo");
+ cn.push_back("meaning_of_life");
+ cn.push_back("repeated_column");
+ return cn;
}
} // namespace osquery
namespace osquery {
class SQLiteUtilTests : public testing::Test {
- public:
- void SetUp() override {
- registryAndPluginInit();
- }
+public:
+ void SetUp() override
+ {
+ registryAndPluginInit();
+ }
};
-std::shared_ptr<SQLiteDBInstance> getTestDBC() {
- auto dbc = SQLiteDBManager::getUnique();
- char* err = nullptr;
- std::vector<std::string> queries = {
- "CREATE TABLE test_table (username varchar(30) primary key, age int)",
- "INSERT INTO test_table VALUES (\"mike\", 23)",
- "INSERT INTO test_table VALUES (\"matt\", 24)"};
-
- for (auto q : queries) {
- sqlite3_exec(dbc->db(), q.c_str(), nullptr, nullptr, &err);
- if (err != nullptr) {
- throw std::domain_error(std::string("Cannot create testing DBC's db: ") +
- err);
- }
- }
-
- return dbc;
+std::shared_ptr<SQLiteDBInstance> getTestDBC()
+{
+ auto dbc = SQLiteDBManager::getUnique();
+ char* err = nullptr;
+ std::vector<std::string> queries = {
+ "CREATE TABLE test_table (username varchar(30) primary key, age int)",
+ "INSERT INTO test_table VALUES (\"mike\", 23)",
+ "INSERT INTO test_table VALUES (\"matt\", 24)"
+ };
+
+ for (auto q : queries) {
+ sqlite3_exec(dbc->db(), q.c_str(), nullptr, nullptr, &err);
+ if (err != nullptr) {
+ throw std::domain_error(std::string("Cannot create testing DBC's db: ") +
+ err);
+ }
+ }
+
+ return dbc;
}
-TEST_F(SQLiteUtilTests, test_zero_as_float_doesnt_convert_to_int) {
- auto sql = SQL("SELECT 0.0 as zero");
- EXPECT_TRUE(sql.ok());
- EXPECT_EQ(sql.rows().size(), 1U);
- Row r;
- r["zero"] = "0.0";
- EXPECT_EQ(sql.rows()[0], r);
+TEST_F(SQLiteUtilTests, test_zero_as_float_doesnt_convert_to_int)
+{
+ auto sql = SQL("SELECT 0.0 as zero");
+ EXPECT_TRUE(sql.ok());
+ EXPECT_EQ(sql.rows().size(), 1U);
+ Row r;
+ r["zero"] = "0.0";
+ EXPECT_EQ(sql.rows()[0], r);
}
-TEST_F(SQLiteUtilTests, test_precision_is_maintained) {
- auto sql = SQL("SELECT 0.123456789 as high_precision, 0.12 as low_precision");
- EXPECT_TRUE(sql.ok());
- EXPECT_EQ(sql.rows().size(), 1U);
- Row r;
- r["high_precision"] = "0.123456789";
- r["low_precision"] = "0.12";
- EXPECT_EQ(sql.rows()[0], r);
+TEST_F(SQLiteUtilTests, test_precision_is_maintained)
+{
+ auto sql = SQL("SELECT 0.123456789 as high_precision, 0.12 as low_precision");
+ EXPECT_TRUE(sql.ok());
+ EXPECT_EQ(sql.rows().size(), 1U);
+ Row r;
+ r["high_precision"] = "0.123456789";
+ r["low_precision"] = "0.12";
+ EXPECT_EQ(sql.rows()[0], r);
}
-TEST_F(SQLiteUtilTests, test_sqlite_instance_manager) {
- auto dbc1 = SQLiteDBManager::get();
- auto dbc2 = SQLiteDBManager::get();
- EXPECT_NE(dbc1->db(), dbc2->db());
- EXPECT_EQ(dbc1->db(), dbc1->db());
+TEST_F(SQLiteUtilTests, test_sqlite_instance_manager)
+{
+ auto dbc1 = SQLiteDBManager::get();
+ auto dbc2 = SQLiteDBManager::get();
+ EXPECT_NE(dbc1->db(), dbc2->db());
+ EXPECT_EQ(dbc1->db(), dbc1->db());
}
-TEST_F(SQLiteUtilTests, test_sqlite_instance) {
- // Don't do this at home kids.
- // Keep a copy of the internal DB and let the SQLiteDBInstance go oos.
- auto internal_db = SQLiteDBManager::get()->db();
- // Compare the internal DB to another request with no SQLiteDBInstances
- // in scope, meaning the primary will be returned.
- EXPECT_EQ(internal_db, SQLiteDBManager::get()->db());
+TEST_F(SQLiteUtilTests, test_sqlite_instance)
+{
+ // Don't do this at home kids.
+ // Keep a copy of the internal DB and let the SQLiteDBInstance go oos.
+ auto internal_db = SQLiteDBManager::get()->db();
+ // Compare the internal DB to another request with no SQLiteDBInstances
+ // in scope, meaning the primary will be returned.
+ EXPECT_EQ(internal_db, SQLiteDBManager::get()->db());
}
-TEST_F(SQLiteUtilTests, test_reset) {
- auto internal_db = SQLiteDBManager::get()->db();
- ASSERT_NE(nullptr, internal_db);
+TEST_F(SQLiteUtilTests, test_reset)
+{
+ auto internal_db = SQLiteDBManager::get()->db();
+ ASSERT_NE(nullptr, internal_db);
- sqlite3_exec(internal_db,
- "create view test_view as select 'test';",
- nullptr,
- nullptr,
- nullptr);
+ sqlite3_exec(internal_db,
+ "create view test_view as select 'test';",
+ nullptr,
+ nullptr,
+ nullptr);
- SQLiteDBManager::resetPrimary();
- auto instance = SQLiteDBManager::get();
+ SQLiteDBManager::resetPrimary();
+ auto instance = SQLiteDBManager::get();
- QueryDataTyped results;
- queryInternal("select * from test_view", results, instance);
+ QueryDataTyped results;
+ queryInternal("select * from test_view", results, instance);
- // Assume the internal (primary) database we reset and recreated.
- EXPECT_EQ(results.size(), 0U);
+ // Assume the internal (primary) database we reset and recreated.
+ EXPECT_EQ(results.size(), 0U);
}
-TEST_F(SQLiteUtilTests, test_direct_query_execution) {
- auto dbc = getTestDBC();
- QueryDataTyped results;
- auto status = queryInternal(kTestQuery, results, dbc);
- EXPECT_TRUE(status.ok());
- EXPECT_EQ(results, getTestDBExpectedResults());
+TEST_F(SQLiteUtilTests, test_direct_query_execution)
+{
+ auto dbc = getTestDBC();
+ QueryDataTyped results;
+ auto status = queryInternal(kTestQuery, results, dbc);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(results, getTestDBExpectedResults());
}
-TEST_F(SQLiteUtilTests, test_aggregate_query) {
- auto dbc = getTestDBC();
- QueryDataTyped results;
- auto status = queryInternal(kTestQuery, results, dbc);
- EXPECT_TRUE(status.ok());
- EXPECT_EQ(results, getTestDBExpectedResults());
+TEST_F(SQLiteUtilTests, test_aggregate_query)
+{
+ auto dbc = getTestDBC();
+ QueryDataTyped results;
+ auto status = queryInternal(kTestQuery, results, dbc);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(results, getTestDBExpectedResults());
}
-TEST_F(SQLiteUtilTests, test_no_results_query) {
- auto dbc = getTestDBC();
- QueryDataTyped results;
- auto status = queryInternal(
- "select * from test_table where username=\"A_NON_EXISTENT_NAME\"",
- results,
- dbc);
- EXPECT_TRUE(status.ok());
+TEST_F(SQLiteUtilTests, test_no_results_query)
+{
+ auto dbc = getTestDBC();
+ QueryDataTyped results;
+ auto status = queryInternal(
+ "select * from test_table where username=\"A_NON_EXISTENT_NAME\"",
+ results,
+ dbc);
+ EXPECT_TRUE(status.ok());
}
-TEST_F(SQLiteUtilTests, test_whitespace_query) {
- auto dbc = getTestDBC();
- QueryDataTyped results;
- auto status = queryInternal(" ", results, dbc);
- EXPECT_TRUE(status.ok());
+TEST_F(SQLiteUtilTests, test_whitespace_query)
+{
+ auto dbc = getTestDBC();
+ QueryDataTyped results;
+ auto status = queryInternal(" ", results, dbc);
+ EXPECT_TRUE(status.ok());
}
-TEST_F(SQLiteUtilTests, test_get_test_db_result_stream) {
- auto dbc = getTestDBC();
- auto results = getTestDBResultStream();
- for (auto r : results) {
- char* err_char = nullptr;
- sqlite3_exec(dbc->db(), (r.first).c_str(), nullptr, nullptr, &err_char);
- EXPECT_TRUE(err_char == nullptr);
- if (err_char != nullptr) {
- sqlite3_free(err_char);
- ASSERT_TRUE(false);
- }
-
- QueryDataTyped expected;
- auto status = queryInternal(kTestQuery, expected, dbc);
- EXPECT_EQ(expected, r.second);
- }
+TEST_F(SQLiteUtilTests, test_get_test_db_result_stream)
+{
+ auto dbc = getTestDBC();
+ auto results = getTestDBResultStream();
+ for (auto r : results) {
+ char* err_char = nullptr;
+ sqlite3_exec(dbc->db(), (r.first).c_str(), nullptr, nullptr, &err_char);
+ EXPECT_TRUE(err_char == nullptr);
+ if (err_char != nullptr) {
+ sqlite3_free(err_char);
+ ASSERT_TRUE(false);
+ }
+
+ QueryDataTyped expected;
+ auto status = queryInternal(kTestQuery, expected, dbc);
+ EXPECT_EQ(expected, r.second);
+ }
}
} // namespace osquery
namespace osquery {
class VirtualTableTests : public testing::Test {
- public:
- void SetUp() override {
- registryAndPluginInit();
- }
+public:
+ void SetUp() override
+ {
+ registryAndPluginInit();
+ }
};
// sample plugin used on tests
class sampleTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("foo", INTEGER_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("bar", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("foo", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("bar", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
};
-TEST_F(VirtualTableTests, test_tableplugin_columndefinition) {
- auto table = std::make_shared<sampleTablePlugin>();
- EXPECT_EQ("(`foo` INTEGER, `bar` TEXT)", table->columnDefinition(false));
+TEST_F(VirtualTableTests, test_tableplugin_columndefinition)
+{
+ auto table = std::make_shared<sampleTablePlugin>();
+ EXPECT_EQ("(`foo` INTEGER, `bar` TEXT)", table->columnDefinition(false));
}
class optionsTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple(
- "id", INTEGER_TYPE, ColumnOptions::INDEX | ColumnOptions::REQUIRED),
- std::make_tuple("username", TEXT_TYPE, ColumnOptions::OPTIMIZED),
- std::make_tuple("name", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_tableplugin_options);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple(
+ "id", INTEGER_TYPE, ColumnOptions::INDEX | ColumnOptions::REQUIRED),
+ std::make_tuple("username", TEXT_TYPE, ColumnOptions::OPTIMIZED),
+ std::make_tuple("name", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_tableplugin_options);
};
-TEST_F(VirtualTableTests, test_tableplugin_options) {
- auto table = std::make_shared<optionsTablePlugin>();
- EXPECT_EQ(ColumnOptions::INDEX | ColumnOptions::REQUIRED,
- std::get<2>(table->columns()[0]));
-
- PluginResponse response;
- PluginRequest request = {{"action", "columns"}};
- EXPECT_TRUE(table->call(request, response).ok());
- auto index_required =
- static_cast<size_t>(ColumnOptions::INDEX | ColumnOptions::REQUIRED);
- EXPECT_EQ(INTEGER(index_required), response[0]["op"]);
-
- response = table->routeInfo();
- EXPECT_EQ(INTEGER(index_required), response[0]["op"]);
-
- std::string expected_statement =
- "(`id` INTEGER, `username` TEXT, `name` TEXT, PRIMARY KEY (`id`)) "
- "WITHOUT ROWID";
- EXPECT_EQ(expected_statement, columnDefinition(response, true, false));
+TEST_F(VirtualTableTests, test_tableplugin_options)
+{
+ auto table = std::make_shared<optionsTablePlugin>();
+ EXPECT_EQ(ColumnOptions::INDEX | ColumnOptions::REQUIRED,
+ std::get<2>(table->columns()[0]));
+
+ PluginResponse response;
+ PluginRequest request = {{"action", "columns"}};
+ EXPECT_TRUE(table->call(request, response).ok());
+ auto index_required =
+ static_cast<size_t>(ColumnOptions::INDEX | ColumnOptions::REQUIRED);
+ EXPECT_EQ(INTEGER(index_required), response[0]["op"]);
+
+ response = table->routeInfo();
+ EXPECT_EQ(INTEGER(index_required), response[0]["op"]);
+
+ std::string expected_statement =
+ "(`id` INTEGER, `username` TEXT, `name` TEXT, PRIMARY KEY (`id`)) "
+ "WITHOUT ROWID";
+ EXPECT_EQ(expected_statement, columnDefinition(response, true, false));
}
class moreOptionsTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("id", INTEGER_TYPE, ColumnOptions::INDEX),
- std::make_tuple("username", TEXT_TYPE, ColumnOptions::ADDITIONAL),
- std::make_tuple("name", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_tableplugin_moreoptions);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("id", INTEGER_TYPE, ColumnOptions::INDEX),
+ std::make_tuple("username", TEXT_TYPE, ColumnOptions::ADDITIONAL),
+ std::make_tuple("name", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_tableplugin_moreoptions);
};
-TEST_F(VirtualTableTests, test_tableplugin_moreoptions) {
- auto table = std::make_shared<moreOptionsTablePlugin>();
+TEST_F(VirtualTableTests, test_tableplugin_moreoptions)
+{
+ auto table = std::make_shared<moreOptionsTablePlugin>();
- PluginResponse response;
- PluginRequest request = {{"action", "columns"}};
- EXPECT_TRUE(table->call(request, response).ok());
+ PluginResponse response;
+ PluginRequest request = {{"action", "columns"}};
+ EXPECT_TRUE(table->call(request, response).ok());
- std::string expected_statement =
- "(`id` INTEGER, `username` TEXT, `name` TEXT, PRIMARY KEY (`id`, "
- "`username`)) WITHOUT ROWID";
- EXPECT_EQ(expected_statement, columnDefinition(response, true, false));
+ std::string expected_statement =
+ "(`id` INTEGER, `username` TEXT, `name` TEXT, PRIMARY KEY (`id`, "
+ "`username`)) WITHOUT ROWID";
+ EXPECT_EQ(expected_statement, columnDefinition(response, true, false));
}
class aliasesTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("username", TEXT_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("name", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- std::vector<std::string> aliases() const override {
- return {"aliases1", "aliases2"};
- }
-
- ColumnAliasSet columnAliases() const override {
- return {
- {"username", {"user_name"}},
- {"name", {"name1", "name2"}},
- };
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_tableplugin_aliases);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("username", TEXT_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("name", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+ std::vector<std::string> aliases() const override
+ {
+ return {"aliases1", "aliases2"};
+ }
+
+ ColumnAliasSet columnAliases() const override
+ {
+ return {
+ {"username", {"user_name"}},
+ {"name", {"name1", "name2"}},
+ };
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_tableplugin_aliases);
};
-TEST_F(VirtualTableTests, test_tableplugin_aliases) {
- auto table = std::make_shared<aliasesTablePlugin>();
- std::vector<std::string> expected_aliases = {"aliases1", "aliases2"};
- EXPECT_EQ(expected_aliases, table->aliases());
-
- PluginResponse response;
- PluginRequest request = {{"action", "columns"}};
- EXPECT_TRUE(table->call(request, response).ok());
-
- auto default_option = static_cast<size_t>(ColumnOptions::DEFAULT);
- PluginResponse expected_response = {
- {{"id", "column"},
- {"name", "username"},
- {"type", "TEXT"},
- {"op", INTEGER(default_option)}},
- {{"id", "column"},
- {"name", "name"},
- {"type", "TEXT"},
- {"op", INTEGER(default_option)}},
- {{"alias", "aliases1"}, {"id", "alias"}},
- {{"alias", "aliases2"}, {"id", "alias"}},
- {{"id", "columnAlias"}, {"name", "name1"}, {"target", "name"}},
- {{"id", "columnAlias"}, {"name", "name2"}, {"target", "name"}},
- {{"id", "columnAlias"}, {"name", "user_name"}, {"target", "username"}},
- {{"attributes", "0"}, {"id", "attributes"}},
- };
- EXPECT_EQ(response, expected_response);
-
- // Compare the expected table definitions.
- std::string expected_statement =
- "(`username` TEXT, `name` TEXT, `name1` TEXT HIDDEN, `name2` TEXT HIDDEN,"
- " `user_name` TEXT HIDDEN)";
- EXPECT_EQ(expected_statement, columnDefinition(response, true, false));
- expected_statement = "(`username` TEXT, `name` TEXT)";
- EXPECT_EQ(expected_statement, columnDefinition(response, false, false));
+TEST_F(VirtualTableTests, test_tableplugin_aliases)
+{
+ auto table = std::make_shared<aliasesTablePlugin>();
+ std::vector<std::string> expected_aliases = {"aliases1", "aliases2"};
+ EXPECT_EQ(expected_aliases, table->aliases());
+
+ PluginResponse response;
+ PluginRequest request = {{"action", "columns"}};
+ EXPECT_TRUE(table->call(request, response).ok());
+
+ auto default_option = static_cast<size_t>(ColumnOptions::DEFAULT);
+ PluginResponse expected_response = {
+ { {"id", "column"},
+ {"name", "username"},
+ {"type", "TEXT"},
+ {"op", INTEGER(default_option)}
+ },
+ { {"id", "column"},
+ {"name", "name"},
+ {"type", "TEXT"},
+ {"op", INTEGER(default_option)}
+ },
+ {{"alias", "aliases1"}, {"id", "alias"}},
+ {{"alias", "aliases2"}, {"id", "alias"}},
+ {{"id", "columnAlias"}, {"name", "name1"}, {"target", "name"}},
+ {{"id", "columnAlias"}, {"name", "name2"}, {"target", "name"}},
+ {{"id", "columnAlias"}, {"name", "user_name"}, {"target", "username"}},
+ {{"attributes", "0"}, {"id", "attributes"}},
+ };
+ EXPECT_EQ(response, expected_response);
+
+ // Compare the expected table definitions.
+ std::string expected_statement =
+ "(`username` TEXT, `name` TEXT, `name1` TEXT HIDDEN, `name2` TEXT HIDDEN,"
+ " `user_name` TEXT HIDDEN)";
+ EXPECT_EQ(expected_statement, columnDefinition(response, true, false));
+ expected_statement = "(`username` TEXT, `name` TEXT)";
+ EXPECT_EQ(expected_statement, columnDefinition(response, false, false));
}
-TEST_F(VirtualTableTests, test_sqlite3_attach_vtable) {
- auto table = std::make_shared<sampleTablePlugin>();
- table->setName("sample");
-
- // Request a managed "connection".
- // This will be a single (potentially locked) instance or a transient
- // SQLite database if there is contention and a lock was not requested.
- auto dbc = SQLiteDBManager::get();
-
- // Virtual tables require the registry/plugin API to query tables.
- auto status =
- attachTableInternal("failed_sample", "(foo INTEGER)", dbc, false);
- EXPECT_EQ(status.getCode(), SQLITE_ERROR);
-
- // The table attach will complete only when the table name is registered.
- auto tables = RegistryFactory::get().registry("table");
- tables->add("sample", std::make_shared<sampleTablePlugin>());
-
- PluginResponse response;
- status = Registry::call("table", "sample", {{"action", "columns"}}, response);
- ASSERT_TRUE(status.ok());
-
- // Use the table name, plugin-generated schema to attach.
- status = attachTableInternal(
- "sample", columnDefinition(response, false, false), dbc, false);
- EXPECT_EQ(status.getCode(), SQLITE_OK);
-
- std::string const q = "SELECT sql FROM sqlite_temp_master WHERE tbl_name='sample';";
- QueryData results;
- status = queryInternal(q, results, dbc);
- EXPECT_EQ(
- "CREATE VIRTUAL TABLE sample USING sample(`foo` INTEGER, `bar` TEXT)",
- results[0]["sql"]);
+TEST_F(VirtualTableTests, test_sqlite3_attach_vtable)
+{
+ auto table = std::make_shared<sampleTablePlugin>();
+ table->setName("sample");
+
+ // Request a managed "connection".
+ // This will be a single (potentially locked) instance or a transient
+ // SQLite database if there is contention and a lock was not requested.
+ auto dbc = SQLiteDBManager::get();
+
+ // Virtual tables require the registry/plugin API to query tables.
+ auto status =
+ attachTableInternal("failed_sample", "(foo INTEGER)", dbc, false);
+ EXPECT_EQ(status.getCode(), SQLITE_ERROR);
+
+ // The table attach will complete only when the table name is registered.
+ auto tables = RegistryFactory::get().registry("table");
+ tables->add("sample", std::make_shared<sampleTablePlugin>());
+
+ PluginResponse response;
+ status = Registry::call("table", "sample", {{"action", "columns"}}, response);
+ ASSERT_TRUE(status.ok());
+
+ // Use the table name, plugin-generated schema to attach.
+ status = attachTableInternal(
+ "sample", columnDefinition(response, false, false), dbc, false);
+ EXPECT_EQ(status.getCode(), SQLITE_OK);
+
+ std::string const q = "SELECT sql FROM sqlite_temp_master WHERE tbl_name='sample';";
+ QueryData results;
+ status = queryInternal(q, results, dbc);
+ EXPECT_EQ(
+ "CREATE VIRTUAL TABLE sample USING sample(`foo` INTEGER, `bar` TEXT)",
+ results[0]["sql"]);
}
class pTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("x", INTEGER_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("y", INTEGER_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext&) override {
- TableRows tr;
- tr.push_back(make_table_row({{"x", "1"}, {"y", "2"}}));
- tr.push_back(make_table_row({{"x", "2"}, {"y", "1"}}));
- return tr;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_constraints_stacking);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("x", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("y", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext&) override
+ {
+ TableRows tr;
+ tr.push_back(make_table_row({{"x", "1"}, {"y", "2"}}));
+ tr.push_back(make_table_row({{"x", "2"}, {"y", "1"}}));
+ return tr;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_constraints_stacking);
};
class kTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("x", INTEGER_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("z", INTEGER_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext&) override {
- TableRows tr;
- tr.push_back(make_table_row({{"x", "1"}, {"z", "2"}}));
- tr.push_back(make_table_row({{"x", "2"}, {"z", "1"}}));
- return tr;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_constraints_stacking);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("x", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("z", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext&) override
+ {
+ TableRows tr;
+ tr.push_back(make_table_row({{"x", "1"}, {"z", "2"}}));
+ tr.push_back(make_table_row({{"x", "2"}, {"z", "1"}}));
+ return tr;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_constraints_stacking);
};
static QueryData makeResult(const std::string& col,
- const std::vector<std::string>& values) {
- QueryData results;
- for (const auto& value : values) {
- Row r;
- r[col] = value;
- results.push_back(r);
- }
- return results;
+ const std::vector<std::string>& values)
+{
+ QueryData results;
+ for (const auto& value : values) {
+ Row r;
+ r[col] = value;
+ results.push_back(r);
+ }
+ return results;
}
#define MP std::make_pair
-TEST_F(VirtualTableTests, test_constraints_stacking) {
- // Add two testing tables to the registry.
- auto tables = RegistryFactory::get().registry("table");
- tables->add("p", std::make_shared<pTablePlugin>());
- tables->add("k", std::make_shared<kTablePlugin>());
- auto dbc = SQLiteDBManager::getUnique();
-
- {
- // To simplify the attach, just access the column definition directly.
- auto p = std::make_shared<pTablePlugin>();
- attachTableInternal("p", p->columnDefinition(false), dbc, false);
- auto k = std::make_shared<kTablePlugin>();
- attachTableInternal("k", k->columnDefinition(false), dbc, false);
- }
-
- std::vector<std::pair<std::string, QueryData>> constraint_tests = {
- MP("select k.x from p, k", makeResult("x", {"1", "2", "1", "2"})),
- MP("select k.x from (select * from k) k2, p, k where k.x = p.x",
- makeResult("x", {"1", "1", "2", "2"})),
- MP("select k.x from (select * from k where z = 1) k2, p, k where k.x = "
- "p.x",
- makeResult("x", {"1", "2"})),
- MP("select k.x from k k1, (select * from p) p1, k where k.x = p1.x",
- makeResult("x", {"1", "1", "2", "2"})),
- MP("select k.x from (select * from p) p1, k, (select * from k) k2 where "
- "k.x = p1.x",
- makeResult("x", {"1", "1", "2", "2"})),
- MP("select k.x from (select * from p) p1, k, (select * from k where z = "
- "2) k2 where k.x = p1.x",
- makeResult("x", {"1", "2"})),
- MP("select k.x from k, (select * from p) p1, k k2, (select * from k "
- "where z = 1) k3 where k.x = p1.x",
- makeResult("x", {"1", "1", "2", "2"})),
- MP("select p.x from (select * from k where z = 1) k1, (select * from k "
- "where z != 1) k2, p where p.x = k2.x",
- makeResult("x", {"1"})),
- MP("select p.x from (select * from k, (select x as xx from k where x = "
- "1) k2 where z = 1) k1, (select * from k where z != 1) k2, p, k as k3 "
- "where p.x = k2.x",
- makeResult("x", {"1", "1"})),
- };
-
- for (const auto& test : constraint_tests) {
- QueryData results;
- queryInternal(test.first, results, dbc);
- EXPECT_EQ(results, test.second) << "Unexpected result for the query: " << test.first;
- }
-
- std::vector<QueryData> union_results = {
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1", "2"}),
- makeResult("x", {"1"}),
- makeResult("x", {"1"}),
- };
-
- size_t index = 0;
- for (const auto& test : constraint_tests) {
- QueryData results;
- queryInternal(test.first + " union " + test.first, results, dbc);
- EXPECT_EQ(results, union_results[index++]);
- }
+TEST_F(VirtualTableTests, test_constraints_stacking)
+{
+ // Add two testing tables to the registry.
+ auto tables = RegistryFactory::get().registry("table");
+ tables->add("p", std::make_shared<pTablePlugin>());
+ tables->add("k", std::make_shared<kTablePlugin>());
+ auto dbc = SQLiteDBManager::getUnique();
+
+ {
+ // To simplify the attach, just access the column definition directly.
+ auto p = std::make_shared<pTablePlugin>();
+ attachTableInternal("p", p->columnDefinition(false), dbc, false);
+ auto k = std::make_shared<kTablePlugin>();
+ attachTableInternal("k", k->columnDefinition(false), dbc, false);
+ }
+
+ std::vector<std::pair<std::string, QueryData>> constraint_tests = {
+ MP("select k.x from p, k", makeResult("x", {"1", "2", "1", "2"})),
+ MP("select k.x from (select * from k) k2, p, k where k.x = p.x",
+ makeResult("x", {"1", "1", "2", "2"})),
+ MP("select k.x from (select * from k where z = 1) k2, p, k where k.x = "
+ "p.x",
+ makeResult("x", {"1", "2"})),
+ MP("select k.x from k k1, (select * from p) p1, k where k.x = p1.x",
+ makeResult("x", {"1", "1", "2", "2"})),
+ MP("select k.x from (select * from p) p1, k, (select * from k) k2 where "
+ "k.x = p1.x",
+ makeResult("x", {"1", "1", "2", "2"})),
+ MP("select k.x from (select * from p) p1, k, (select * from k where z = "
+ "2) k2 where k.x = p1.x",
+ makeResult("x", {"1", "2"})),
+ MP("select k.x from k, (select * from p) p1, k k2, (select * from k "
+ "where z = 1) k3 where k.x = p1.x",
+ makeResult("x", {"1", "1", "2", "2"})),
+ MP("select p.x from (select * from k where z = 1) k1, (select * from k "
+ "where z != 1) k2, p where p.x = k2.x",
+ makeResult("x", {"1"})),
+ MP("select p.x from (select * from k, (select x as xx from k where x = "
+ "1) k2 where z = 1) k1, (select * from k where z != 1) k2, p, k as k3 "
+ "where p.x = k2.x",
+ makeResult("x", {"1", "1"})),
+ };
+
+ for (const auto& test : constraint_tests) {
+ QueryData results;
+ queryInternal(test.first, results, dbc);
+ EXPECT_EQ(results, test.second) << "Unexpected result for the query: " << test.first;
+ }
+
+ std::vector<QueryData> union_results = {
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1", "2"}),
+ makeResult("x", {"1"}),
+ makeResult("x", {"1"}),
+ };
+
+ size_t index = 0;
+ for (const auto& test : constraint_tests) {
+ QueryData results;
+ queryInternal(test.first + " union " + test.first, results, dbc);
+ EXPECT_EQ(results, union_results[index++]);
+ }
}
class jsonTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("data", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext&) override {
- TableRows results;
- results.push_back(make_table_row({{"data", "{\"test\": 1}"}}));
- return results;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_json_extract);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("data", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext&) override
+ {
+ TableRows results;
+ results.push_back(make_table_row({{"data", "{\"test\": 1}"}}));
+ return results;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_json_extract);
};
-TEST_F(VirtualTableTests, test_json_extract) {
- // Get a database connection.
- auto tables = RegistryFactory::get().registry("table");
- auto json = std::make_shared<jsonTablePlugin>();
- tables->add("json", json);
-
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal("json", json->columnDefinition(false), dbc, false);
-
- QueryData results;
- // Run a query with a join within.
- std::string statement =
- "SELECT JSON_EXTRACT(data, '$.test') AS test FROM json;";
- auto status = queryInternal(statement, results, dbc);
- ASSERT_TRUE(status.ok());
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["test"], "1");
+TEST_F(VirtualTableTests, test_json_extract)
+{
+ // Get a database connection.
+ auto tables = RegistryFactory::get().registry("table");
+ auto json = std::make_shared<jsonTablePlugin>();
+ tables->add("json", json);
+
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal("json", json->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ // Run a query with a join within.
+ std::string statement =
+ "SELECT JSON_EXTRACT(data, '$.test') AS test FROM json;";
+ auto status = queryInternal(statement, results, dbc);
+ ASSERT_TRUE(status.ok());
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["test"], "1");
}
-TEST_F(VirtualTableTests, test_null_values) {
- auto dbc = SQLiteDBManager::getUnique();
-
- std::string statement = "SELECT NULL as null_value;";
- {
- QueryData results;
- auto status = queryInternal(statement, results, dbc);
- ASSERT_TRUE(status.ok());
- EXPECT_EQ(results[0]["null_value"], "");
- }
-
- // Try INTEGER.
- {
- QueryData results;
- statement = "SELECT CAST(NULL as INTEGER) as null_value;";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results[0]["null_value"], "");
- }
-
- // BIGINT.
- {
- QueryData results;
- statement = "SELECT CAST(NULL as BIGINT) as null_value;";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results[0]["null_value"], "");
- }
-
- // Try DOUBLE.
- {
- QueryData results;
- statement = "SELECT CAST(NULL as DOUBLE) as null_value;";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results[0]["null_value"], "");
- }
+TEST_F(VirtualTableTests, test_null_values)
+{
+ auto dbc = SQLiteDBManager::getUnique();
+
+ std::string statement = "SELECT NULL as null_value;";
+ {
+ QueryData results;
+ auto status = queryInternal(statement, results, dbc);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(results[0]["null_value"], "");
+ }
+
+ // Try INTEGER.
+ {
+ QueryData results;
+ statement = "SELECT CAST(NULL as INTEGER) as null_value;";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results[0]["null_value"], "");
+ }
+
+ // BIGINT.
+ {
+ QueryData results;
+ statement = "SELECT CAST(NULL as BIGINT) as null_value;";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results[0]["null_value"], "");
+ }
+
+ // Try DOUBLE.
+ {
+ QueryData results;
+ statement = "SELECT CAST(NULL as DOUBLE) as null_value;";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results[0]["null_value"], "");
+ }
}
class cacheTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("data", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- TableRows results;
- if (context.isCached("awesome_data")) {
- // There is cache entry for awesome data.
- results.push_back(make_table_row({{"data", "more_awesome_data"}}));
- } else {
- auto tr = make_table_row({{"data", "awesome_data"}});
- context.setCache("awesome_data", static_cast<TableRowHolder&&>(tr));
- results.push_back(std::move(tr));
- }
- return results;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_table_cache);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("data", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ TableRows results;
+ if (context.isCached("awesome_data")) {
+ // There is cache entry for awesome data.
+ results.push_back(make_table_row({{"data", "more_awesome_data"}}));
+ } else {
+ auto tr = make_table_row({{"data", "awesome_data"}});
+ context.setCache("awesome_data", static_cast < TableRowHolder && >(tr));
+ results.push_back(std::move(tr));
+ }
+ return results;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_table_cache);
};
-TEST_F(VirtualTableTests, test_table_cache) {
- // Get a database connection.
- auto tables = RegistryFactory::get().registry("table");
- auto cache = std::make_shared<cacheTablePlugin>();
- tables->add("cache", cache);
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal("cache", cache->columnDefinition(false), dbc, false);
-
- QueryData results;
- // Run a query with a join within.
- std::string statement = "SELECT c2.data as data FROM cache c1, cache c2;";
- auto status = queryInternal(statement, results, dbc);
- dbc->clearAffectedTables();
- ASSERT_TRUE(status.ok());
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["data"], "more_awesome_data");
-
- // Run the query again, the virtual table cache should have been expired.
- results.clear();
- statement = "SELECT data from cache c1";
- queryInternal(statement, results, dbc);
- ASSERT_EQ(results.size(), 1U);
- ASSERT_EQ(results[0]["data"], "awesome_data");
+TEST_F(VirtualTableTests, test_table_cache)
+{
+ // Get a database connection.
+ auto tables = RegistryFactory::get().registry("table");
+ auto cache = std::make_shared<cacheTablePlugin>();
+ tables->add("cache", cache);
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal("cache", cache->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ // Run a query with a join within.
+ std::string statement = "SELECT c2.data as data FROM cache c1, cache c2;";
+ auto status = queryInternal(statement, results, dbc);
+ dbc->clearAffectedTables();
+ ASSERT_TRUE(status.ok());
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["data"], "more_awesome_data");
+
+ // Run the query again, the virtual table cache should have been expired.
+ results.clear();
+ statement = "SELECT data from cache c1";
+ queryInternal(statement, results, dbc);
+ ASSERT_EQ(results.size(), 1U);
+ ASSERT_EQ(results[0]["data"], "awesome_data");
}
class tableCacheTablePlugin : public TablePlugin {
- public:
- TableColumns columns() const override {
- return {
- std::make_tuple("i", TEXT_TYPE, ColumnOptions::INDEX),
- std::make_tuple("d", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- TableAttributes attributes() const override {
- return TableAttributes::CACHEABLE;
- }
-
- TableRows generate(QueryContext& ctx) override {
- if (isCached(60, ctx)) {
- return getCache();
- }
-
- generates_++;
- auto r = make_table_row();
- r["i"] = "1";
- TableRows result;
- result.push_back(std::move(r));
- setCache(60, 1, ctx, result);
- return result;
- }
-
- size_t generates_{0};
+public:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("i", TEXT_TYPE, ColumnOptions::INDEX),
+ std::make_tuple("d", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+ TableAttributes attributes() const override
+ {
+ return TableAttributes::CACHEABLE;
+ }
+
+ TableRows generate(QueryContext& ctx) override
+ {
+ if (isCached(60, ctx)) {
+ return getCache();
+ }
+
+ generates_++;
+ auto r = make_table_row();
+ r["i"] = "1";
+ TableRows result;
+ result.push_back(std::move(r));
+ setCache(60, 1, ctx, result);
+ return result;
+ }
+
+ size_t generates_{0};
};
-TEST_F(VirtualTableTests, test_table_results_cache) {
- // Get a database connection.
- auto tables = RegistryFactory::get().registry("table");
- auto cache = std::make_shared<tableCacheTablePlugin>();
- tables->add("table_cache", cache);
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal(
- "table_cache", cache->columnDefinition(false), dbc, false);
-
- QueryData results;
- std::string statement = "SELECT * from table_cache;";
- auto status = queryInternal(statement, results, dbc);
- dbc->clearAffectedTables();
-
- ASSERT_TRUE(status.ok());
- EXPECT_EQ(results.size(), 1U);
- EXPECT_EQ(cache->generates_, 1U);
-
- // Run the query again, the virtual table cache was not requested.
- results.clear();
- statement = "SELECT * from table_cache;";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results.size(), 1U);
-
- // The table should have used the cache.
- EXPECT_EQ(cache->generates_, 2U);
-
- // Now request that caching be used.
- dbc->useCache(true);
-
- // Run the query again, the virtual table cache will be populated.
- results.clear();
- statement = "SELECT * from table_cache;";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results.size(), 1U);
- EXPECT_EQ(cache->generates_, 3U);
-
- // Run the query again, the virtual table cache will be returned.
- results.clear();
- statement = "SELECT * from table_cache;";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results.size(), 1U);
- EXPECT_EQ(cache->generates_, 3U);
-
- // Once last time with constraints that invalidate the cache results.
- results.clear();
- statement = "SELECT * from table_cache where i = '1';";
- queryInternal(statement, results, dbc);
- EXPECT_EQ(results.size(), 1U);
-
- // The table should NOT have used the cache.
- EXPECT_EQ(cache->generates_, 4U);
+TEST_F(VirtualTableTests, test_table_results_cache)
+{
+ // Get a database connection.
+ auto tables = RegistryFactory::get().registry("table");
+ auto cache = std::make_shared<tableCacheTablePlugin>();
+ tables->add("table_cache", cache);
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal(
+ "table_cache", cache->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ std::string statement = "SELECT * from table_cache;";
+ auto status = queryInternal(statement, results, dbc);
+ dbc->clearAffectedTables();
+
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(results.size(), 1U);
+ EXPECT_EQ(cache->generates_, 1U);
+
+ // Run the query again, the virtual table cache was not requested.
+ results.clear();
+ statement = "SELECT * from table_cache;";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results.size(), 1U);
+
+ // The table should have used the cache.
+ EXPECT_EQ(cache->generates_, 2U);
+
+ // Now request that caching be used.
+ dbc->useCache(true);
+
+ // Run the query again, the virtual table cache will be populated.
+ results.clear();
+ statement = "SELECT * from table_cache;";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results.size(), 1U);
+ EXPECT_EQ(cache->generates_, 3U);
+
+ // Run the query again, the virtual table cache will be returned.
+ results.clear();
+ statement = "SELECT * from table_cache;";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results.size(), 1U);
+ EXPECT_EQ(cache->generates_, 3U);
+
+ // Once last time with constraints that invalidate the cache results.
+ results.clear();
+ statement = "SELECT * from table_cache where i = '1';";
+ queryInternal(statement, results, dbc);
+ EXPECT_EQ(results.size(), 1U);
+
+ // The table should NOT have used the cache.
+ EXPECT_EQ(cache->generates_, 4U);
}
class likeTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("i", TEXT_TYPE, ColumnOptions::INDEX),
- std::make_tuple("op", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- TableRows results;
-
- // To test, we'll move all predicate constraints into the result set.
- // First we'll move constrains for the column `i` using operands =, LIKE.
- auto i = context.constraints["i"].getAll(EQUALS);
- for (const auto& constraint : i) {
- auto r = make_table_row();
- r["i"] = constraint;
- r["op"] = "EQUALS";
- results.push_back(std::move(r));
- }
-
- i = context.constraints["i"].getAll(LIKE);
- for (const auto& constraint : i) {
- auto r = make_table_row();
- r["i"] = constraint;
- r["op"] = "LIKE";
- results.push_back(std::move(r));
- }
-
- return results;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_like_constraints);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("i", TEXT_TYPE, ColumnOptions::INDEX),
+ std::make_tuple("op", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ TableRows results;
+
+ // To test, we'll move all predicate constraints into the result set.
+ // First we'll move constrains for the column `i` using operands =, LIKE.
+ auto i = context.constraints["i"].getAll(EQUALS);
+ for (const auto& constraint : i) {
+ auto r = make_table_row();
+ r["i"] = constraint;
+ r["op"] = "EQUALS";
+ results.push_back(std::move(r));
+ }
+
+ i = context.constraints["i"].getAll(LIKE);
+ for (const auto& constraint : i) {
+ auto r = make_table_row();
+ r["i"] = constraint;
+ r["op"] = "LIKE";
+ results.push_back(std::move(r));
+ }
+
+ return results;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_like_constraints);
};
-TEST_F(VirtualTableTests, test_like_constraints) {
- auto table = std::make_shared<likeTablePlugin>();
- auto table_registry = RegistryFactory::get().registry("table");
- table_registry->add("like_table", table);
-
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal("like_table", table->columnDefinition(false), dbc, false);
-
- // Base case, without constrains this table has no results.
- QueryData results;
- queryInternal("SELECT * FROM like_table", results, dbc);
- dbc->clearAffectedTables();
- ASSERT_EQ(results.size(), 0U);
-
- // A single EQUAL constraint's value is emitted.
- queryInternal("SELECT * FROM like_table WHERE i = '1'", results, dbc);
- dbc->clearAffectedTables();
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["i"], "1");
- EXPECT_EQ(results[0]["op"], "EQUALS");
-
- // When using OR, both values should be added.
- results.clear();
- queryInternal(
- "SELECT * FROM like_table WHERE i = '1' OR i = '2'", results, dbc);
- dbc->clearAffectedTables();
- ASSERT_EQ(results.size(), 2U);
- EXPECT_EQ(results[0]["i"], "1");
- EXPECT_EQ(results[0]["op"], "EQUALS");
- EXPECT_EQ(results[1]["i"], "2");
- EXPECT_EQ(results[1]["op"], "EQUALS");
-
- // When using a LIKE, the value (with substitution character) is emitted.
- results.clear();
- queryInternal(
- "SELECT * FROM like_table WHERE i LIKE '/test/1/%'", results, dbc);
- dbc->clearAffectedTables();
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["i"], "/test/1/%");
- EXPECT_EQ(results[0]["op"], "LIKE");
-
- // As with EQUAL, multiple LIKEs mean multiple values.
- results.clear();
- queryInternal(
- "SELECT * FROM like_table WHERE i LIKE '/test/1/%' OR i LIKE '/test/2/%'",
- results,
- dbc);
- dbc->clearAffectedTables();
- ASSERT_EQ(results.size(), 2U);
- EXPECT_EQ(results[0]["i"], "/test/1/%");
- EXPECT_EQ(results[0]["op"], "LIKE");
- EXPECT_EQ(results[1]["i"], "/test/2/%");
- EXPECT_EQ(results[1]["op"], "LIKE");
-
- // As with EQUAL, multiple LIKEs mean multiple values.
- results.clear();
- queryInternal(
- "SELECT * FROM like_table WHERE i LIKE '/home/%/downloads' OR i LIKE "
- "'/home/%/documents'",
- results,
- dbc);
- dbc->clearAffectedTables();
- ASSERT_EQ(results.size(), 2U);
- EXPECT_EQ(results[0]["i"], "/home/%/downloads");
- EXPECT_EQ(results[0]["op"], "LIKE");
- EXPECT_EQ(results[1]["i"], "/home/%/documents");
- EXPECT_EQ(results[1]["op"], "LIKE");
+TEST_F(VirtualTableTests, test_like_constraints)
+{
+ auto table = std::make_shared<likeTablePlugin>();
+ auto table_registry = RegistryFactory::get().registry("table");
+ table_registry->add("like_table", table);
+
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal("like_table", table->columnDefinition(false), dbc, false);
+
+ // Base case, without constrains this table has no results.
+ QueryData results;
+ queryInternal("SELECT * FROM like_table", results, dbc);
+ dbc->clearAffectedTables();
+ ASSERT_EQ(results.size(), 0U);
+
+ // A single EQUAL constraint's value is emitted.
+ queryInternal("SELECT * FROM like_table WHERE i = '1'", results, dbc);
+ dbc->clearAffectedTables();
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["i"], "1");
+ EXPECT_EQ(results[0]["op"], "EQUALS");
+
+ // When using OR, both values should be added.
+ results.clear();
+ queryInternal(
+ "SELECT * FROM like_table WHERE i = '1' OR i = '2'", results, dbc);
+ dbc->clearAffectedTables();
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0]["i"], "1");
+ EXPECT_EQ(results[0]["op"], "EQUALS");
+ EXPECT_EQ(results[1]["i"], "2");
+ EXPECT_EQ(results[1]["op"], "EQUALS");
+
+ // When using a LIKE, the value (with substitution character) is emitted.
+ results.clear();
+ queryInternal(
+ "SELECT * FROM like_table WHERE i LIKE '/test/1/%'", results, dbc);
+ dbc->clearAffectedTables();
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["i"], "/test/1/%");
+ EXPECT_EQ(results[0]["op"], "LIKE");
+
+ // As with EQUAL, multiple LIKEs mean multiple values.
+ results.clear();
+ queryInternal(
+ "SELECT * FROM like_table WHERE i LIKE '/test/1/%' OR i LIKE '/test/2/%'",
+ results,
+ dbc);
+ dbc->clearAffectedTables();
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0]["i"], "/test/1/%");
+ EXPECT_EQ(results[0]["op"], "LIKE");
+ EXPECT_EQ(results[1]["i"], "/test/2/%");
+ EXPECT_EQ(results[1]["op"], "LIKE");
+
+ // As with EQUAL, multiple LIKEs mean multiple values.
+ results.clear();
+ queryInternal(
+ "SELECT * FROM like_table WHERE i LIKE '/home/%/downloads' OR i LIKE "
+ "'/home/%/documents'",
+ results,
+ dbc);
+ dbc->clearAffectedTables();
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0]["i"], "/home/%/downloads");
+ EXPECT_EQ(results[0]["op"], "LIKE");
+ EXPECT_EQ(results[1]["i"], "/home/%/documents");
+ EXPECT_EQ(results[1]["op"], "LIKE");
}
class indexIOptimizedTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("i", INTEGER_TYPE, ColumnOptions::INDEX),
- std::make_tuple("j", INTEGER_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("text", INTEGER_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- scans++;
-
- TableRows results;
- auto indexes = context.constraints["i"].getAll<int>(EQUALS);
- for (const auto& i : indexes) {
- results.push_back(make_table_row(
- {{"i", INTEGER(i)}, {"j", INTEGER(i * 10)}, {"text", "none"}}));
- }
- if (indexes.empty()) {
- for (size_t i = 0; i < 100; i++) {
- results.push_back(make_table_row(
- {{"i", INTEGER(i)}, {"j", INTEGER(i * 10)}, {"text", "some"}}));
- }
- }
- return results;
- }
-
- // Here the goal is to expect/assume the number of scans.
- size_t scans{0};
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("i", INTEGER_TYPE, ColumnOptions::INDEX),
+ std::make_tuple("j", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("text", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ scans++;
+
+ TableRows results;
+ auto indexes = context.constraints["i"].getAll<int>(EQUALS);
+ for (const auto& i : indexes) {
+ results.push_back(make_table_row(
+ {{"i", INTEGER(i)}, {"j", INTEGER(i * 10)}, {"text", "none"}}));
+ }
+ if (indexes.empty()) {
+ for (size_t i = 0; i < 100; i++) {
+ results.push_back(make_table_row(
+ {{"i", INTEGER(i)}, {"j", INTEGER(i * 10)}, {"text", "some"}}));
+ }
+ }
+ return results;
+ }
+
+ // Here the goal is to expect/assume the number of scans.
+ size_t scans{0};
};
class indexJOptimizedTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("j", INTEGER_TYPE, ColumnOptions::INDEX),
- std::make_tuple("text", INTEGER_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- scans++;
-
- TableRows results;
- auto indexes = context.constraints["j"].getAll<int>(EQUALS);
- for (const auto& j : indexes) {
- results.push_back(make_table_row({{"j", INTEGER(j)}, {"text", "none"}}));
- }
- if (indexes.empty()) {
- for (size_t j = 0; j < 100; j++) {
- results.push_back(
- make_table_row({{"j", INTEGER(j)}, {"text", "some"}}));
- }
- }
- return results;
- }
-
- // Here the goal is to expect/assume the number of scans.
- size_t scans{0};
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("j", INTEGER_TYPE, ColumnOptions::INDEX),
+ std::make_tuple("text", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ scans++;
+
+ TableRows results;
+ auto indexes = context.constraints["j"].getAll<int>(EQUALS);
+ for (const auto& j : indexes) {
+ results.push_back(make_table_row({{"j", INTEGER(j)}, {"text", "none"}}));
+ }
+ if (indexes.empty()) {
+ for (size_t j = 0; j < 100; j++) {
+ results.push_back(
+ make_table_row({{"j", INTEGER(j)}, {"text", "some"}}));
+ }
+ }
+ return results;
+ }
+
+ // Here the goal is to expect/assume the number of scans.
+ size_t scans{0};
};
class defaultScanTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("i", INTEGER_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("text", INTEGER_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- scans++;
-
- TableRows results;
- for (size_t i = 0; i < 10; i++) {
- results.push_back(make_table_row({{"i", INTEGER(i)}, {"text", "some"}}));
- }
- return results;
- }
-
- // Here the goal is to expect/assume the number of scans.
- size_t scans{0};
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("i", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("text", INTEGER_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ scans++;
+
+ TableRows results;
+ for (size_t i = 0; i < 10; i++) {
+ results.push_back(make_table_row({{"i", INTEGER(i)}, {"text", "some"}}));
+ }
+ return results;
+ }
+
+ // Here the goal is to expect/assume the number of scans.
+ size_t scans{0};
};
class colsUsedTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("col1", TEXT_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("col2", TEXT_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("col3", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- ColumnAliasSet columnAliases() const override {
- return {
- {"col2", {"aliasToCol2"}},
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- auto r = make_table_row();
- if (context.isColumnUsed("col1")) {
- r["col1"] = "value1";
- }
- if (context.isColumnUsed("col2")) {
- r["col2"] = "value2";
- }
- if (context.isColumnUsed("col3")) {
- r["col3"] = "value3";
- }
- TableRows result;
- result.push_back(std::move(r));
- return result;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_used_columns);
- FRIEND_TEST(VirtualTableTests, test_used_columns_with_alias);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("col1", TEXT_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("col2", TEXT_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("col3", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+ ColumnAliasSet columnAliases() const override
+ {
+ return {
+ {"col2", {"aliasToCol2"}},
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ auto r = make_table_row();
+ if (context.isColumnUsed("col1")) {
+ r["col1"] = "value1";
+ }
+ if (context.isColumnUsed("col2")) {
+ r["col2"] = "value2";
+ }
+ if (context.isColumnUsed("col3")) {
+ r["col3"] = "value3";
+ }
+ TableRows result;
+ result.push_back(std::move(r));
+ return result;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_used_columns);
+ FRIEND_TEST(VirtualTableTests, test_used_columns_with_alias);
};
-TEST_F(VirtualTableTests, test_used_columns) {
- // Add testing table to the registry.
- auto tables = RegistryFactory::get().registry("table");
- auto colsUsed = std::make_shared<colsUsedTablePlugin>();
- tables->add("colsUsed1", colsUsed);
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal(
- "colsUsed1", colsUsed->columnDefinition(false), dbc, false);
-
- QueryData results;
- auto status = queryInternal("SELECT col1, col3 FROM colsUsed1", results, dbc);
- EXPECT_TRUE(status.ok());
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["col1"], "value1");
- EXPECT_EQ(results[0].find("col2"), results[0].end());
- EXPECT_EQ(results[0]["col3"], "value3");
- EXPECT_EQ(results[0].find("aliasToCol2"), results[0].end());
+TEST_F(VirtualTableTests, test_used_columns)
+{
+ // Add testing table to the registry.
+ auto tables = RegistryFactory::get().registry("table");
+ auto colsUsed = std::make_shared<colsUsedTablePlugin>();
+ tables->add("colsUsed1", colsUsed);
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal(
+ "colsUsed1", colsUsed->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ auto status = queryInternal("SELECT col1, col3 FROM colsUsed1", results, dbc);
+ EXPECT_TRUE(status.ok());
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["col1"], "value1");
+ EXPECT_EQ(results[0].find("col2"), results[0].end());
+ EXPECT_EQ(results[0]["col3"], "value3");
+ EXPECT_EQ(results[0].find("aliasToCol2"), results[0].end());
}
-TEST_F(VirtualTableTests, test_used_columns_with_alias) {
- // Add testing table to the registry.
- auto tables = RegistryFactory::get().registry("table");
- auto colsUsed = std::make_shared<colsUsedTablePlugin>();
- tables->add("colsUsed2", colsUsed);
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal(
- "colsUsed2", colsUsed->columnDefinition(false), dbc, false);
-
- QueryData results;
- auto status =
- queryInternal("SELECT aliasToCol2 FROM colsUsed2", results, dbc);
- EXPECT_TRUE(status.ok());
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0].find("col1"), results[0].end());
- EXPECT_EQ(results[0].find("col2"), results[0].end());
- EXPECT_EQ(results[0].find("col3"), results[0].end());
- EXPECT_EQ(results[0]["aliasToCol2"], "value2");
+TEST_F(VirtualTableTests, test_used_columns_with_alias)
+{
+ // Add testing table to the registry.
+ auto tables = RegistryFactory::get().registry("table");
+ auto colsUsed = std::make_shared<colsUsedTablePlugin>();
+ tables->add("colsUsed2", colsUsed);
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal(
+ "colsUsed2", colsUsed->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ auto status =
+ queryInternal("SELECT aliasToCol2 FROM colsUsed2", results, dbc);
+ EXPECT_TRUE(status.ok());
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0].find("col1"), results[0].end());
+ EXPECT_EQ(results[0].find("col2"), results[0].end());
+ EXPECT_EQ(results[0].find("col3"), results[0].end());
+ EXPECT_EQ(results[0]["aliasToCol2"], "value2");
}
class colsUsedBitsetTablePlugin : public TablePlugin {
- private:
- TableColumns columns() const override {
- return {
- std::make_tuple("col1", TEXT_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("col2", TEXT_TYPE, ColumnOptions::DEFAULT),
- std::make_tuple("col3", TEXT_TYPE, ColumnOptions::DEFAULT),
- };
- }
-
- ColumnAliasSet columnAliases() const override {
- return {
- {"col2", {"aliasToCol2"}},
- };
- }
-
- public:
- TableRows generate(QueryContext& context) override {
- TableRows results;
- auto r = make_table_row();
- if (context.isAnyColumnUsed(UsedColumnsBitset(0x1))) {
- r["col1"] = "value1";
- }
- if (context.isAnyColumnUsed(UsedColumnsBitset(0x2))) {
- r["col2"] = "value2";
- }
- if (context.isAnyColumnUsed(UsedColumnsBitset(0x4))) {
- r["col3"] = "value3";
- }
- results.push_back(std::move(r));
- return results;
- }
-
- private:
- FRIEND_TEST(VirtualTableTests, test_used_columns_bitset);
- FRIEND_TEST(VirtualTableTests, test_used_columns_bitset_with_alias);
+private:
+ TableColumns columns() const override
+ {
+ return {
+ std::make_tuple("col1", TEXT_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("col2", TEXT_TYPE, ColumnOptions::DEFAULT),
+ std::make_tuple("col3", TEXT_TYPE, ColumnOptions::DEFAULT),
+ };
+ }
+
+ ColumnAliasSet columnAliases() const override
+ {
+ return {
+ {"col2", {"aliasToCol2"}},
+ };
+ }
+
+public:
+ TableRows generate(QueryContext& context) override
+ {
+ TableRows results;
+ auto r = make_table_row();
+ if (context.isAnyColumnUsed(UsedColumnsBitset(0x1))) {
+ r["col1"] = "value1";
+ }
+ if (context.isAnyColumnUsed(UsedColumnsBitset(0x2))) {
+ r["col2"] = "value2";
+ }
+ if (context.isAnyColumnUsed(UsedColumnsBitset(0x4))) {
+ r["col3"] = "value3";
+ }
+ results.push_back(std::move(r));
+ return results;
+ }
+
+private:
+ FRIEND_TEST(VirtualTableTests, test_used_columns_bitset);
+ FRIEND_TEST(VirtualTableTests, test_used_columns_bitset_with_alias);
};
-TEST_F(VirtualTableTests, test_used_columns_bitset) {
- // Add testing table to the registry.
- auto tables = RegistryFactory::get().registry("table");
- auto colsUsed = std::make_shared<colsUsedBitsetTablePlugin>();
- tables->add("colsUsedBitset1", colsUsed);
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal(
- "colsUsedBitset1", colsUsed->columnDefinition(false), dbc, false);
-
- QueryData results;
- auto status =
- queryInternal("SELECT col1, col3 FROM colsUsedBitset1", results, dbc);
- EXPECT_TRUE(status.ok());
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0]["col1"], "value1");
- EXPECT_EQ(results[0].find("col2"), results[0].end());
- EXPECT_EQ(results[0]["col3"], "value3");
- EXPECT_EQ(results[0].find("aliasToCol2"), results[0].end());
+TEST_F(VirtualTableTests, test_used_columns_bitset)
+{
+ // Add testing table to the registry.
+ auto tables = RegistryFactory::get().registry("table");
+ auto colsUsed = std::make_shared<colsUsedBitsetTablePlugin>();
+ tables->add("colsUsedBitset1", colsUsed);
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal(
+ "colsUsedBitset1", colsUsed->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ auto status =
+ queryInternal("SELECT col1, col3 FROM colsUsedBitset1", results, dbc);
+ EXPECT_TRUE(status.ok());
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0]["col1"], "value1");
+ EXPECT_EQ(results[0].find("col2"), results[0].end());
+ EXPECT_EQ(results[0]["col3"], "value3");
+ EXPECT_EQ(results[0].find("aliasToCol2"), results[0].end());
}
-TEST_F(VirtualTableTests, test_used_columns_bitset_with_alias) {
- // Add testing table to the registry.
- auto tables = RegistryFactory::get().registry("table");
- auto colsUsed = std::make_shared<colsUsedBitsetTablePlugin>();
- tables->add("colsUsedBitset2", colsUsed);
- auto dbc = SQLiteDBManager::getUnique();
- attachTableInternal(
- "colsUsedBitset2", colsUsed->columnDefinition(false), dbc, false);
-
- QueryData results;
- auto status =
- queryInternal("SELECT aliasToCol2 FROM colsUsedBitset2", results, dbc);
- EXPECT_TRUE(status.ok());
- ASSERT_EQ(results.size(), 1U);
- EXPECT_EQ(results[0].find("col1"), results[0].end());
- EXPECT_EQ(results[0].find("col2"), results[0].end());
- EXPECT_EQ(results[0].find("col3"), results[0].end());
- EXPECT_EQ(results[0]["aliasToCol2"], "value2");
+TEST_F(VirtualTableTests, test_used_columns_bitset_with_alias)
+{
+ // Add testing table to the registry.
+ auto tables = RegistryFactory::get().registry("table");
+ auto colsUsed = std::make_shared<colsUsedBitsetTablePlugin>();
+ tables->add("colsUsedBitset2", colsUsed);
+ auto dbc = SQLiteDBManager::getUnique();
+ attachTableInternal(
+ "colsUsedBitset2", colsUsed->columnDefinition(false), dbc, false);
+
+ QueryData results;
+ auto status =
+ queryInternal("SELECT aliasToCol2 FROM colsUsedBitset2", results, dbc);
+ EXPECT_TRUE(status.ok());
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0].find("col1"), results[0].end());
+ EXPECT_EQ(results[0].find("col2"), results[0].end());
+ EXPECT_EQ(results[0].find("col3"), results[0].end());
+ EXPECT_EQ(results[0]["aliasToCol2"], "value2");
}
} // namespace osquery
namespace osquery {
-const char* getSystemVFS(bool respect_locking) {
- return "unix-none";
+const char* getSystemVFS(bool respect_locking)
+{
+ return "unix-none";
}
Status genSqliteTableRow(sqlite3_stmt* stmt,
- TableRows& qd,
- const fs::path& sqlite_db) {
- auto r = make_table_row();
- for (int i = 0; i < sqlite3_column_count(stmt); ++i) {
- auto column_name = std::string(sqlite3_column_name(stmt, i));
- auto column_type = sqlite3_column_type(stmt, i);
- switch (column_type) {
- case SQLITE_TEXT: {
- auto text_value = sqlite3_column_text(stmt, i);
- if (text_value != nullptr) {
- r[column_name] = std::string(reinterpret_cast<const char*>(text_value));
- }
- break;
- }
- case SQLITE_FLOAT: {
- auto float_value = sqlite3_column_double(stmt, i);
- r[column_name] = DOUBLE(float_value);
- break;
- }
- case SQLITE_INTEGER: {
- auto int_value = sqlite3_column_int(stmt, i);
- r[column_name] = INTEGER(int_value);
- break;
- }
- }
- }
- if (r.count("path") > 0) {
- WARN(OSQUERY) << "Row contains a path key, refusing to overwrite";
- } else {
- r["path"] = sqlite_db.string();
- }
- qd.push_back(std::move(r));
- return Status::success();
+ TableRows& qd,
+ const fs::path& sqlite_db)
+{
+ auto r = make_table_row();
+ for (int i = 0; i < sqlite3_column_count(stmt); ++i) {
+ auto column_name = std::string(sqlite3_column_name(stmt, i));
+ auto column_type = sqlite3_column_type(stmt, i);
+ switch (column_type) {
+ case SQLITE_TEXT: {
+ auto text_value = sqlite3_column_text(stmt, i);
+ if (text_value != nullptr) {
+ r[column_name] = std::string(reinterpret_cast<const char*>(text_value));
+ }
+ break;
+ }
+ case SQLITE_FLOAT: {
+ auto float_value = sqlite3_column_double(stmt, i);
+ r[column_name] = DOUBLE(float_value);
+ break;
+ }
+ case SQLITE_INTEGER: {
+ auto int_value = sqlite3_column_int(stmt, i);
+ r[column_name] = INTEGER(int_value);
+ break;
+ }
+ }
+ }
+ if (r.count("path") > 0) {
+ WARN(OSQUERY) << "Row contains a path key, refusing to overwrite";
+ } else {
+ r["path"] = sqlite_db.string();
+ }
+ qd.push_back(std::move(r));
+ return Status::success();
}
Status genTableRowsForSqliteTable(const fs::path& sqlite_db,
- const std::string& sqlite_query,
- TableRows& results,
- bool respect_locking) {
- sqlite3* db = nullptr;
- boost::system::error_code ec;
- if (sqlite_db.empty()) {
- return Status(1, "Database path does not exist");
- }
+ const std::string& sqlite_query,
+ TableRows& results,
+ bool respect_locking)
+{
+ sqlite3* db = nullptr;
+ boost::system::error_code ec;
+ if (sqlite_db.empty()) {
+ return Status(1, "Database path does not exist");
+ }
- // A tri-state determination of presence
- if (!fs::exists(sqlite_db, ec) || ec.value() != errc::success) {
- return Status(1, ec.message());
- }
+ // A tri-state determination of presence
+ if (!fs::exists(sqlite_db, ec) || ec.value() != errc::success) {
+ return Status(1, ec.message());
+ }
- auto rc = sqlite3_open_v2(
- sqlite_db.string().c_str(),
- &db,
- (SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_NOMUTEX),
- getSystemVFS(respect_locking));
- if (rc != SQLITE_OK || db == nullptr) {
- DEBUG(OSQUERY) << "Cannot open specified database: "
- << getStringForSQLiteReturnCode(rc);
- if (db != nullptr) {
- sqlite3_close(db);
- }
- return Status(1, "Could not open database");
- }
+ auto rc = sqlite3_open_v2(
+ sqlite_db.string().c_str(),
+ &db,
+ (SQLITE_OPEN_READONLY | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_NOMUTEX),
+ getSystemVFS(respect_locking));
+ if (rc != SQLITE_OK || db == nullptr) {
+ DEBUG(OSQUERY) << "Cannot open specified database: "
+ << getStringForSQLiteReturnCode(rc);
+ if (db != nullptr) {
+ sqlite3_close(db);
+ }
+ return Status(1, "Could not open database");
+ }
- sqlite3_stmt* stmt = nullptr;
- rc = sqlite3_prepare_v2(db, sqlite_query.c_str(), -1, &stmt, nullptr);
- if (rc != SQLITE_OK) {
- sqlite3_close(db);
- DEBUG(OSQUERY) << "Could not prepare database at path: " << sqlite_db;
- return Status(rc, "Could not prepare database");
- }
+ sqlite3_stmt* stmt = nullptr;
+ rc = sqlite3_prepare_v2(db, sqlite_query.c_str(), -1, &stmt, nullptr);
+ if (rc != SQLITE_OK) {
+ sqlite3_close(db);
+ DEBUG(OSQUERY) << "Could not prepare database at path: " << sqlite_db;
+ return Status(rc, "Could not prepare database");
+ }
- while ((sqlite3_step(stmt)) == SQLITE_ROW) {
- auto s = genSqliteTableRow(stmt, results, sqlite_db);
- if (!s.ok()) {
- break;
- }
- }
+ while ((sqlite3_step(stmt)) == SQLITE_ROW) {
+ auto s = genSqliteTableRow(stmt, results, sqlite_db);
+ if (!s.ok()) {
+ break;
+ }
+ }
- // Close handles and free memory
- sqlite3_finalize(stmt);
- sqlite3_close(db);
+ // Close handles and free memory
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
- return Status{};
+ return Status{};
}
} // namespace osquery
*/
static std::atomic<size_t> kConstraintIndexID{0};
-static inline std::string opString(unsigned char op) {
- switch (op) {
- case EQUALS:
- return "=";
- case GREATER_THAN:
- return ">";
- case LESS_THAN_OR_EQUALS:
- return "<=";
- case LESS_THAN:
- return "<";
- case GREATER_THAN_OR_EQUALS:
- return ">=";
- case LIKE:
- return "LIKE";
- case MATCH:
- return "MATCH";
- case GLOB:
- return "GLOB";
- case REGEXP:
- return "REGEX";
- case UNIQUE:
- return "UNIQUE";
- }
- return "?";
+static inline std::string opString(unsigned char op)
+{
+ switch (op) {
+ case EQUALS:
+ return "=";
+ case GREATER_THAN:
+ return ">";
+ case LESS_THAN_OR_EQUALS:
+ return "<=";
+ case LESS_THAN:
+ return "<";
+ case GREATER_THAN_OR_EQUALS:
+ return ">=";
+ case LIKE:
+ return "LIKE";
+ case MATCH:
+ return "MATCH";
+ case GLOB:
+ return "GLOB";
+ case REGEXP:
+ return "REGEX";
+ case UNIQUE:
+ return "UNIQUE";
+ }
+ return "?";
}
namespace {
Mutex sqlite_module_map_mutex;
bool getColumnValue(std::string& value,
- size_t index,
- size_t argc,
- sqlite3_value** argv) {
- value.clear();
-
- if (index >= argc) {
- return false;
- }
-
- auto sqlite_value = argv[index];
- switch (sqlite3_value_type(sqlite_value)) {
- case SQLITE_INTEGER: {
- auto temp = sqlite3_value_int64(sqlite_value);
- value = std::to_string(temp);
- break;
- }
-
- case SQLITE_FLOAT: {
- auto temp = sqlite3_value_double(sqlite_value);
- value = std::to_string(temp);
- break;
- }
-
- case SQLITE_BLOB:
- case SQLITE3_TEXT: {
- auto data_ptr = static_cast<const char*>(sqlite3_value_blob(sqlite_value));
- auto buffer_size = static_cast<size_t>(sqlite3_value_bytes(sqlite_value));
-
- value.assign(data_ptr, buffer_size);
- break;
- }
-
- case SQLITE_NULL: {
- break;
- }
-
- default: {
- ERROR(OSQUERY) << "Invalid column type returned by sqlite";
- return false;
- }
- }
-
- return true;
+ size_t index,
+ size_t argc,
+ sqlite3_value** argv)
+{
+ value.clear();
+
+ if (index >= argc) {
+ return false;
+ }
+
+ auto sqlite_value = argv[index];
+ switch (sqlite3_value_type(sqlite_value)) {
+ case SQLITE_INTEGER: {
+ auto temp = sqlite3_value_int64(sqlite_value);
+ value = std::to_string(temp);
+ break;
+ }
+
+ case SQLITE_FLOAT: {
+ auto temp = sqlite3_value_double(sqlite_value);
+ value = std::to_string(temp);
+ break;
+ }
+
+ case SQLITE_BLOB:
+ case SQLITE3_TEXT: {
+ auto data_ptr = static_cast<const char*>(sqlite3_value_blob(sqlite_value));
+ auto buffer_size = static_cast<size_t>(sqlite3_value_bytes(sqlite_value));
+
+ value.assign(data_ptr, buffer_size);
+ break;
+ }
+
+ case SQLITE_NULL: {
+ break;
+ }
+
+ default: {
+ ERROR(OSQUERY) << "Invalid column type returned by sqlite";
+ return false;
+ }
+ }
+
+ return true;
}
/// PATCH START //////////////////////////////////////////////////////////////
-int serializeDeleteParameters(std::string& serialized, VirtualTable* pVtab) {
+int serializeDeleteParameters(std::string& serialized, VirtualTable* pVtab)
+{
auto content = pVtab->content;
if (content->constraints.size() <= 0) {
ERROR(OSQUERY) << "Invalid constraints arguments";
int serializeUpdateParameters(std::string& serialized,
size_t argc,
sqlite3_value** argv,
- const TableColumns& columnDescriptors) {
+ const TableColumns& columnDescriptors)
+{
if (columnDescriptors.size() != argc - 2U) {
DEBUG(VIST) << "Wrong column count: " << argc - 2U
<< ". Expected: " << columnDescriptors.size();
switch (receivedValueType) {
case SQLITE_INTEGER: {
if (columnType != INTEGER_TYPE && columnType != BIGINT_TYPE &&
- columnType != UNSIGNED_BIGINT_TYPE) {
+ columnType != UNSIGNED_BIGINT_TYPE) {
return SQLITE_MISMATCH;
}
else
typeMismatch = columnType != TEXT_TYPE;
- if (typeMismatch)
+ if (typeMismatch)
return SQLITE_MISMATCH;
auto data_pointer = sqlite3_value_blob(sqlite_value);
}
void setTableErrorMessage(sqlite3_vtab* vtable,
- const std::string& error_message) {
- // We are required to always replace the pointer with memory
- // allocated with sqlite3_malloc. This buffer is freed automatically
- // by sqlite3 on exit
- //
- // Documentation: https://www.sqlite.org/vtab.html section 1.2
-
- if (vtable->zErrMsg != nullptr) {
- sqlite3_free(vtable->zErrMsg);
- }
-
- auto buffer_size = static_cast<int>(error_message.size() + 1);
-
- vtable->zErrMsg = static_cast<char*>(sqlite3_malloc(buffer_size));
- if (vtable->zErrMsg != nullptr) {
- memcpy(vtable->zErrMsg, error_message.c_str(), buffer_size);
- }
+ const std::string& error_message)
+{
+ // We are required to always replace the pointer with memory
+ // allocated with sqlite3_malloc. This buffer is freed automatically
+ // by sqlite3 on exit
+ //
+ // Documentation: https://www.sqlite.org/vtab.html section 1.2
+
+ if (vtable->zErrMsg != nullptr) {
+ sqlite3_free(vtable->zErrMsg);
+ }
+
+ auto buffer_size = static_cast<int>(error_message.size() + 1);
+
+ vtable->zErrMsg = static_cast<char*>(sqlite3_malloc(buffer_size));
+ if (vtable->zErrMsg != nullptr) {
+ memcpy(vtable->zErrMsg, error_message.c_str(), buffer_size);
+ }
}
} // namespace
-inline std::string table_doc(const std::string& name) {
- return "https://osquery.io/schema/#" + name;
+inline std::string table_doc(const std::string& name)
+{
+ return "https://osquery.io/schema/#" + name;
}
-static void plan(const std::string& output) {
+static void plan(const std::string& output)
+{
}
-int xOpen(sqlite3_vtab* tab, sqlite3_vtab_cursor** ppCursor) {
- auto* pCur = new BaseCursor;
- auto* pVtab = (VirtualTable*)tab;
- plan("Opening cursor (" + std::to_string(kPlannerCursorID) +
- ") for table: " + pVtab->content->name);
- pCur->id = kPlannerCursorID++;
- pCur->base.pVtab = tab;
- *ppCursor = (sqlite3_vtab_cursor*)pCur;
+int xOpen(sqlite3_vtab* tab, sqlite3_vtab_cursor** ppCursor)
+{
+ auto* pCur = new BaseCursor;
+ auto* pVtab = (VirtualTable*)tab;
+ plan("Opening cursor (" + std::to_string(kPlannerCursorID) +
+ ") for table: " + pVtab->content->name);
+ pCur->id = kPlannerCursorID++;
+ pCur->base.pVtab = tab;
+ *ppCursor = (sqlite3_vtab_cursor*)pCur;
- return SQLITE_OK;
+ return SQLITE_OK;
}
-int xClose(sqlite3_vtab_cursor* cur) {
- BaseCursor* pCur = (BaseCursor*)cur;
- plan("Closing cursor (" + std::to_string(pCur->id) + ")");
- delete pCur;
- return SQLITE_OK;
+int xClose(sqlite3_vtab_cursor* cur)
+{
+ BaseCursor* pCur = (BaseCursor*)cur;
+ plan("Closing cursor (" + std::to_string(pCur->id) + ")");
+ delete pCur;
+ return SQLITE_OK;
}
-int xEof(sqlite3_vtab_cursor* cur) {
- BaseCursor* pCur = (BaseCursor*)cur;
+int xEof(sqlite3_vtab_cursor* cur)
+{
+ BaseCursor* pCur = (BaseCursor*)cur;
- 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.
- return true;
- }
- return false;
+ 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.
+ return true;
+ }
+ return false;
}
-int xDestroy(sqlite3_vtab* p) {
- auto* pVtab = (VirtualTable*)p;
- delete pVtab;
- return SQLITE_OK;
+int xDestroy(sqlite3_vtab* p)
+{
+ auto* pVtab = (VirtualTable*)p;
+ delete pVtab;
+ return SQLITE_OK;
}
-int xNext(sqlite3_vtab_cursor* cur) {
- BaseCursor* pCur = (BaseCursor*)cur;
- pCur->row++;
- return SQLITE_OK;
+int xNext(sqlite3_vtab_cursor* cur)
+{
+ BaseCursor* pCur = (BaseCursor*)cur;
+ pCur->row++;
+ return SQLITE_OK;
}
-int xRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) {
- *pRowid = 0;
+int xRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid)
+{
+ *pRowid = 0;
- const BaseCursor* pCur = (BaseCursor*)cur;
- auto data_it = std::next(pCur->rows.begin(), pCur->row);
- if (data_it >= pCur->rows.end()) {
- return SQLITE_ERROR;
- }
+ const BaseCursor* pCur = (BaseCursor*)cur;
+ auto data_it = std::next(pCur->rows.begin(), pCur->row);
+ if (data_it >= pCur->rows.end()) {
+ return SQLITE_ERROR;
+ }
- // Use the rowid returned by the extension, if available; most likely, this
- // will only be used by extensions providing read/write tables
- const auto& current_row = *data_it;
- return current_row->get_rowid(pCur->row, pRowid);
+ // Use the rowid returned by the extension, if available; most likely, this
+ // will only be used by extensions providing read/write tables
+ const auto& current_row = *data_it;
+ return current_row->get_rowid(pCur->row, pRowid);
}
int xUpdate(sqlite3_vtab* p,
- int argc,
- sqlite3_value** argv,
- sqlite3_int64* pRowid) {
- auto argument_count = static_cast<size_t>(argc);
- auto* pVtab = (VirtualTable*)p;
+ int argc,
+ sqlite3_value** argv,
+ sqlite3_int64* pRowid)
+{
+ auto argument_count = static_cast<size_t>(argc);
+ auto* pVtab = (VirtualTable*)p;
- auto content = pVtab->content;
- const auto& columnDescriptors = content->columns;
+ auto content = pVtab->content;
+ const auto& columnDescriptors = content->columns;
+
+ std::string table_name = pVtab->content->name;
+
+ // The SQLite instance communicates to the TablePlugin via the context.
+ QueryContext context(content);
+ PluginRequest plugin_request;
+
+ if (argument_count == 1U) {
+ // This is a simple delete operation
+ plugin_request = {{"action", "delete"}};
+
+ auto row_to_delete = sqlite3_value_int64(argv[0]);
+ plugin_request.insert({"id", std::to_string(row_to_delete)});
+
+ /// PATCH START //////////////////////////////////////////////////////////////
+ std::string json_values;
+ serializeDeleteParameters(json_values, reinterpret_cast<VirtualTable*>(p));
+ plugin_request.insert({"json_values", json_values});
+ /// PATCH END ////////////////////////////////////////////////////////////////
+ } else if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
+ // This is an INSERT query; if the rowid has been generated for us, we'll
+ // find it inside argv[1]
+ plugin_request = {{"action", "insert"}};
+
+ // Add the values to insert; we should have a value for each column present
+ // in the table, even if the user did not specify a value (in which case
+ // we will find a nullptr)
+ std::string json_values;
+ auto serializerError = serializeUpdateParameters(
+ json_values, argument_count, argv, columnDescriptors);
+ if (serializerError != SQLITE_OK) {
+ DEBUG(OSQUERY) << "Failed to serialize the UPDATE request";
+ return serializerError;
+ }
- std::string table_name = pVtab->content->name;
+ plugin_request.insert({"json_values", json_values});
- // The SQLite instance communicates to the TablePlugin via the context.
- QueryContext context(content);
- PluginRequest plugin_request;
+ if (sqlite3_value_type(argv[1]) != SQLITE_NULL) {
+ plugin_request.insert({"auto_rowid", "true"});
- if (argument_count == 1U) {
- // This is a simple delete operation
- plugin_request = {{"action", "delete"}};
+ std::string auto_generated_rowid;
+ if (!getColumnValue(auto_generated_rowid, 1U, argument_count, argv)) {
+ DEBUG(OSQUERY) << "Failed to retrieve the column value";
+ return SQLITE_ERROR;
+ }
- auto row_to_delete = sqlite3_value_int64(argv[0]);
- plugin_request.insert({"id", std::to_string(row_to_delete)});
+ plugin_request.insert({"id", auto_generated_rowid});
-/// PATCH START //////////////////////////////////////////////////////////////
- std::string json_values;
- serializeDeleteParameters(json_values, reinterpret_cast<VirtualTable*>(p));
- plugin_request.insert({"json_values", json_values});
-/// PATCH END ////////////////////////////////////////////////////////////////
- } else if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
- // This is an INSERT query; if the rowid has been generated for us, we'll
- // find it inside argv[1]
- plugin_request = {{"action", "insert"}};
-
- // Add the values to insert; we should have a value for each column present
- // in the table, even if the user did not specify a value (in which case
- // we will find a nullptr)
- std::string json_values;
- auto serializerError = serializeUpdateParameters(
- json_values, argument_count, argv, columnDescriptors);
- if (serializerError != SQLITE_OK) {
- DEBUG(OSQUERY) << "Failed to serialize the UPDATE request";
- return serializerError;
+ } else {
+ plugin_request.insert({"auto_rowid", "false"});
+ }
+
+ } else if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
+ // This is an UPDATE query; we have to update the rowid value in some
+ // cases (if argv[1] is populated)
+ plugin_request = {{"action", "update"}};
+
+ std::string current_rowid;
+ if (!getColumnValue(current_rowid, 0U, argument_count, argv)) {
+ DEBUG(OSQUERY) << "Failed to retrieve the column value";
+ return SQLITE_ERROR;
+ }
+
+ plugin_request.insert({"id", current_rowid});
+
+ // Get the new rowid, if any
+ if (sqlite3_value_type(argv[1]) == SQLITE_INTEGER) {
+ std::string new_rowid;
+ if (!getColumnValue(new_rowid, 1U, argument_count, argv)) {
+ DEBUG(OSQUERY) << "Failed to retrieve the column value";
+ return SQLITE_ERROR;
+ }
+
+ if (new_rowid != plugin_request.at("id")) {
+ plugin_request.insert({"new_id", new_rowid});
+ }
+ }
+
+ // Get the values to update
+ std::string json_values;
+ auto serializerError = serializeUpdateParameters(
+ json_values, argument_count, argv, columnDescriptors);
+ if (serializerError != SQLITE_OK) {
+ DEBUG(OSQUERY) << "Failed to serialize the UPDATE request";
+ return serializerError;
+ }
+
+ plugin_request.insert({"json_values", json_values});
+
+ } else {
+ DEBUG(OSQUERY) << "Invalid xUpdate call";
+ return SQLITE_ERROR;
}
- plugin_request.insert({"json_values", json_values});
-
- if (sqlite3_value_type(argv[1]) != SQLITE_NULL) {
- plugin_request.insert({"auto_rowid", "true"});
-
- std::string auto_generated_rowid;
- if (!getColumnValue(auto_generated_rowid, 1U, argument_count, argv)) {
- DEBUG(OSQUERY) << "Failed to retrieve the column value";
- return SQLITE_ERROR;
- }
-
- plugin_request.insert({"id", auto_generated_rowid});
-
- } else {
- plugin_request.insert({"auto_rowid", "false"});
- }
-
- } else if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
- // This is an UPDATE query; we have to update the rowid value in some
- // cases (if argv[1] is populated)
- plugin_request = {{"action", "update"}};
-
- std::string current_rowid;
- if (!getColumnValue(current_rowid, 0U, argument_count, argv)) {
- DEBUG(OSQUERY) << "Failed to retrieve the column value";
- return SQLITE_ERROR;
- }
-
- plugin_request.insert({"id", current_rowid});
-
- // Get the new rowid, if any
- if (sqlite3_value_type(argv[1]) == SQLITE_INTEGER) {
- std::string new_rowid;
- if (!getColumnValue(new_rowid, 1U, argument_count, argv)) {
- DEBUG(OSQUERY) << "Failed to retrieve the column value";
- return SQLITE_ERROR;
- }
-
- if (new_rowid != plugin_request.at("id")) {
- plugin_request.insert({"new_id", new_rowid});
- }
- }
-
- // Get the values to update
- std::string json_values;
- auto serializerError = serializeUpdateParameters(
- json_values, argument_count, argv, columnDescriptors);
- if (serializerError != SQLITE_OK) {
- DEBUG(OSQUERY) << "Failed to serialize the UPDATE request";
- return serializerError;
+ TablePlugin::setRequestFromContext(context, plugin_request);
+
+ // Forward the query to the table extension
+ PluginResponse response_list;
+ Registry::call("table", table_name, plugin_request, response_list);
+
+ // Validate the response
+ if (response_list.size() != 1) {
+ DEBUG(OSQUERY) << "Invalid response from the extension table";
+ return SQLITE_ERROR;
+ }
+
+ const auto& response = response_list.at(0);
+ if (response.count("status") == 0) {
+ DEBUG(OSQUERY) << "Invalid response from the extension table; the status field is "
+ "missing";
+
+ return SQLITE_ERROR;
+ }
+
+ const auto& status_value = response.at("status");
+ if (status_value == "readonly") {
+ auto error_message =
+ "table " + pVtab->content->name + " may not be modified";
+
+ setTableErrorMessage(p, error_message);
+ return SQLITE_READONLY;
+
+ } else if (status_value == "failure") {
+ auto custom_error_message_it = response.find("message");
+ if (custom_error_message_it == response.end()) {
+ return SQLITE_ERROR;
+ }
+
+ const auto& custom_error_message = custom_error_message_it->second;
+ setTableErrorMessage(p, custom_error_message);
+ return SQLITE_ERROR;
+
+ } else if (status_value == "constraint") {
+ return SQLITE_CONSTRAINT;
+
+ } else if (status_value != "success") {
+ DEBUG(OSQUERY) << "Invalid response from the extension table; the status field "
+ "could not be recognized";
+
+ return SQLITE_ERROR;
}
- plugin_request.insert({"json_values", json_values});
-
- } else {
- DEBUG(OSQUERY) << "Invalid xUpdate call";
- return SQLITE_ERROR;
- }
-
- TablePlugin::setRequestFromContext(context, plugin_request);
-
- // Forward the query to the table extension
- PluginResponse response_list;
- Registry::call("table", table_name, plugin_request, response_list);
-
- // Validate the response
- if (response_list.size() != 1) {
- DEBUG(OSQUERY) << "Invalid response from the extension table";
- return SQLITE_ERROR;
- }
-
- const auto& response = response_list.at(0);
- if (response.count("status") == 0) {
- DEBUG(OSQUERY) << "Invalid response from the extension table; the status field is "
- "missing";
-
- return SQLITE_ERROR;
- }
-
- const auto& status_value = response.at("status");
- if (status_value == "readonly") {
- auto error_message =
- "table " + pVtab->content->name + " may not be modified";
-
- setTableErrorMessage(p, error_message);
- return SQLITE_READONLY;
-
- } else if (status_value == "failure") {
- auto custom_error_message_it = response.find("message");
- if (custom_error_message_it == response.end()) {
- return SQLITE_ERROR;
- }
-
- const auto& custom_error_message = custom_error_message_it->second;
- setTableErrorMessage(p, custom_error_message);
- return SQLITE_ERROR;
-
- } else if (status_value == "constraint") {
- return SQLITE_CONSTRAINT;
-
- } else if (status_value != "success") {
- DEBUG(OSQUERY) << "Invalid response from the extension table; the status field "
- "could not be recognized";
-
- return SQLITE_ERROR;
- }
-
-/*
- // INSERT actions must always return a valid rowid to sqlite
- if (plugin_request.at("action") == "insert") {
- std::string rowid;
-
- if (plugin_request.at("auto_rowid") == "true") {
- if (!getColumnValue(rowid, 1U, argument_count, argv)) {
- DEBUG(OSQUERY) << "Failed to retrieve the rowid value";
- return SQLITE_ERROR;
- }
-
- } else {
- auto id_it = response.find("id");
- if (id_it == response.end()) {
- DEBUG(OSQUERY) << "The plugin did not return a row id";
- return SQLITE_ERROR;
- }
-
- rowid = id_it->second;
- }
-
- auto exp = tryTo<long long>(rowid);
- if (exp.isError()) {
- DEBUG(OSQUERY) << "The plugin did not return a valid row id";
- return SQLITE_ERROR;
- }
- *pRowid = exp.take();
- }
-*/
- return SQLITE_OK;
+ /*
+ // INSERT actions must always return a valid rowid to sqlite
+ if (plugin_request.at("action") == "insert") {
+ std::string rowid;
+
+ if (plugin_request.at("auto_rowid") == "true") {
+ if (!getColumnValue(rowid, 1U, argument_count, argv)) {
+ DEBUG(OSQUERY) << "Failed to retrieve the rowid value";
+ return SQLITE_ERROR;
+ }
+
+ } else {
+ auto id_it = response.find("id");
+ if (id_it == response.end()) {
+ DEBUG(OSQUERY) << "The plugin did not return a row id";
+ return SQLITE_ERROR;
+ }
+
+ rowid = id_it->second;
+ }
+
+ auto exp = tryTo<long long>(rowid);
+ if (exp.isError()) {
+ DEBUG(OSQUERY) << "The plugin did not return a valid row id";
+ return SQLITE_ERROR;
+ }
+ *pRowid = exp.take();
+ }
+ */
+ return SQLITE_OK;
}
int xCreate(sqlite3* db,
- void* pAux,
- int argc,
- const char* const* argv,
- sqlite3_vtab** ppVtab,
- char** pzErr) {
- auto* pVtab = new VirtualTable;
- if (argc == 0 || argv[0] == nullptr) {
- delete pVtab;
- return SQLITE_NOMEM;
- }
-
- memset(pVtab, 0, sizeof(VirtualTable));
- pVtab->content = std::make_shared<VirtualTableContent>();
- pVtab->instance = (SQLiteDBInstance*)pAux;
-
- // Create a TablePlugin Registry call, expect column details as the response.
- PluginResponse response;
- pVtab->content->name = std::string(argv[0]);
- const auto& name = pVtab->content->name;
-
- // Get the table column information.
- auto status =
- Registry::call("table", name, {{"action", "columns"}}, response);
- if (!status.ok() || response.size() == 0) {
- delete pVtab;
- return SQLITE_ERROR;
- }
-
- bool is_extension = false;
-
- // Generate an SQL create table statement from the retrieved column details.
- // This call to columnDefinition requests column aliases (as HIDDEN columns).
- auto statement =
- "CREATE TABLE " + name + columnDefinition(response, true, is_extension);
-
- int rc = sqlite3_declare_vtab(db, statement.c_str());
- if (rc != SQLITE_OK || !status.ok() || response.size() == 0) {
- ERROR(OSQUERY) << "Error creating virtual table: " << name << " (" << rc
- << "): " << getStringForSQLiteReturnCode(rc);
-
- DEBUG(OSQUERY) << "Cannot create virtual table using: " << statement;
- delete pVtab;
- return (rc != SQLITE_OK) ? rc : SQLITE_ERROR;
- }
-
- // Tables may request aliases as views.
- std::set<std::string> views;
-
- // Keep a local copy of the column details in the VirtualTableContent struct.
- // This allows introspection into the column type without additional calls.
- for (const auto& column : response) {
- auto cid = column.find("id");
- if (cid == column.end()) {
- // This does not define a column type.
- continue;
- }
-
- auto cname = column.find("name");
- auto ctype = column.find("type");
- if (cid->second == "column" && cname != column.end() &&
- ctype != column.end()) {
- // This is a malformed column definition.
- // Populate the virtual table specific persistent column information.
- auto options = ColumnOptions::DEFAULT;
- auto cop = column.find("op");
- if (cop != column.end()) {
- auto op = tryTo<long>(cop->second);
- if (op) {
- options = static_cast<ColumnOptions>(op.take());
- }
- }
-
- pVtab->content->columns.push_back(std::make_tuple(
- cname->second, columnTypeName(ctype->second), options));
- } else if (cid->second == "alias") {
- // Create associated views for table aliases.
- auto calias = column.find("alias");
- if (calias != column.end()) {
- views.insert(calias->second);
- }
- } else if (cid->second == "columnAlias" && cname != column.end()) {
- auto ctarget = column.find("target");
- if (ctarget == column.end()) {
- continue;
- }
-
- // Record the column in the set of columns.
- // This is required because SQLITE uses indexes to identify columns.
- // Use an UNKNOWN_TYPE as a pseudo-mask, since the type does not matter.
- pVtab->content->columns.push_back(
- std::make_tuple(cname->second, UNKNOWN_TYPE, ColumnOptions::HIDDEN));
- // Record a mapping of the requested column alias name.
- size_t target_index = 0;
- for (size_t i = 0; i < pVtab->content->columns.size(); i++) {
- const auto& target_column = pVtab->content->columns[i];
- if (std::get<0>(target_column) == ctarget->second) {
- target_index = i;
- break;
- }
- }
- pVtab->content->aliases[cname->second] = target_index;
- } else if (cid->second == "attributes") {
- auto cattr = column.find("attributes");
- // Store the attributes locally so they may be passed to the SQL object.
- if (cattr != column.end()) {
- auto attr = tryTo<long>(cattr->second);
- if (attr) {
- pVtab->content->attributes =
- static_cast<TableAttributes>(attr.take());
- }
- }
- }
- }
-
- // Create the requested 'aliases'.
- for (const auto& view : views) {
- statement = "CREATE VIEW " + view + " AS SELECT * FROM " + name;
- sqlite3_exec(db, statement.c_str(), nullptr, nullptr, nullptr);
- }
-
- *ppVtab = (sqlite3_vtab*)pVtab;
- return rc;
+ void* pAux,
+ int argc,
+ const char* const* argv,
+ sqlite3_vtab** ppVtab,
+ char** pzErr)
+{
+ auto* pVtab = new VirtualTable;
+ if (argc == 0 || argv[0] == nullptr) {
+ delete pVtab;
+ return SQLITE_NOMEM;
+ }
+
+ memset(pVtab, 0, sizeof(VirtualTable));
+ pVtab->content = std::make_shared<VirtualTableContent>();
+ pVtab->instance = (SQLiteDBInstance*)pAux;
+
+ // Create a TablePlugin Registry call, expect column details as the response.
+ PluginResponse response;
+ pVtab->content->name = std::string(argv[0]);
+ const auto& name = pVtab->content->name;
+
+ // Get the table column information.
+ auto status =
+ Registry::call("table", name, {{"action", "columns"}}, response);
+ if (!status.ok() || response.size() == 0) {
+ delete pVtab;
+ return SQLITE_ERROR;
+ }
+
+ bool is_extension = false;
+
+ // Generate an SQL create table statement from the retrieved column details.
+ // This call to columnDefinition requests column aliases (as HIDDEN columns).
+ auto statement =
+ "CREATE TABLE " + name + columnDefinition(response, true, is_extension);
+
+ int rc = sqlite3_declare_vtab(db, statement.c_str());
+ if (rc != SQLITE_OK || !status.ok() || response.size() == 0) {
+ ERROR(OSQUERY) << "Error creating virtual table: " << name << " (" << rc
+ << "): " << getStringForSQLiteReturnCode(rc);
+
+ DEBUG(OSQUERY) << "Cannot create virtual table using: " << statement;
+ delete pVtab;
+ return (rc != SQLITE_OK) ? rc : SQLITE_ERROR;
+ }
+
+ // Tables may request aliases as views.
+ std::set<std::string> views;
+
+ // Keep a local copy of the column details in the VirtualTableContent struct.
+ // This allows introspection into the column type without additional calls.
+ for (const auto& column : response) {
+ auto cid = column.find("id");
+ if (cid == column.end()) {
+ // This does not define a column type.
+ continue;
+ }
+
+ auto cname = column.find("name");
+ auto ctype = column.find("type");
+ if (cid->second == "column" && cname != column.end() &&
+ ctype != column.end()) {
+ // This is a malformed column definition.
+ // Populate the virtual table specific persistent column information.
+ auto options = ColumnOptions::DEFAULT;
+ auto cop = column.find("op");
+ if (cop != column.end()) {
+ auto op = tryTo<long>(cop->second);
+ if (op) {
+ options = static_cast<ColumnOptions>(op.take());
+ }
+ }
+
+ pVtab->content->columns.push_back(std::make_tuple(
+ cname->second, columnTypeName(ctype->second), options));
+ } else if (cid->second == "alias") {
+ // Create associated views for table aliases.
+ auto calias = column.find("alias");
+ if (calias != column.end()) {
+ views.insert(calias->second);
+ }
+ } else if (cid->second == "columnAlias" && cname != column.end()) {
+ auto ctarget = column.find("target");
+ if (ctarget == column.end()) {
+ continue;
+ }
+
+ // Record the column in the set of columns.
+ // This is required because SQLITE uses indexes to identify columns.
+ // Use an UNKNOWN_TYPE as a pseudo-mask, since the type does not matter.
+ pVtab->content->columns.push_back(
+ std::make_tuple(cname->second, UNKNOWN_TYPE, ColumnOptions::HIDDEN));
+ // Record a mapping of the requested column alias name.
+ size_t target_index = 0;
+ for (size_t i = 0; i < pVtab->content->columns.size(); i++) {
+ const auto& target_column = pVtab->content->columns[i];
+ if (std::get<0>(target_column) == ctarget->second) {
+ target_index = i;
+ break;
+ }
+ }
+ pVtab->content->aliases[cname->second] = target_index;
+ } else if (cid->second == "attributes") {
+ auto cattr = column.find("attributes");
+ // Store the attributes locally so they may be passed to the SQL object.
+ if (cattr != column.end()) {
+ auto attr = tryTo<long>(cattr->second);
+ if (attr) {
+ pVtab->content->attributes =
+ static_cast<TableAttributes>(attr.take());
+ }
+ }
+ }
+ }
+
+ // Create the requested 'aliases'.
+ for (const auto& view : views) {
+ statement = "CREATE VIEW " + view + " AS SELECT * FROM " + name;
+ sqlite3_exec(db, statement.c_str(), nullptr, nullptr, nullptr);
+ }
+
+ *ppVtab = (sqlite3_vtab*)pVtab;
+ return rc;
}
-int xColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col) {
- BaseCursor* pCur = (BaseCursor*)cur;
- const auto* pVtab = (VirtualTable*)cur->pVtab;
- if (col >= static_cast<int>(pVtab->content->columns.size())) {
- // Requested column index greater than column set size.
- return SQLITE_ERROR;
- }
+int xColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
+{
+ BaseCursor* pCur = (BaseCursor*)cur;
+ const auto* pVtab = (VirtualTable*)cur->pVtab;
+ if (col >= static_cast<int>(pVtab->content->columns.size())) {
+ // Requested column index greater than column set size.
+ return SQLITE_ERROR;
+ }
- TableRowHolder& row = pCur->rows[pCur->row];
- return row->get_column(ctx, cur->pVtab, col);
+ TableRowHolder& row = pCur->rows[pCur->row];
+ return row->get_column(ctx, cur->pVtab, col);
}
-static inline bool sensibleComparison(ColumnType type, unsigned char op) {
- if (type == TEXT_TYPE) {
- if (op == GREATER_THAN || op == GREATER_THAN_OR_EQUALS || op == LESS_THAN ||
- op == LESS_THAN_OR_EQUALS) {
- return false;
- }
- }
- return true;
+static inline bool sensibleComparison(ColumnType type, unsigned char op)
+{
+ if (type == TEXT_TYPE) {
+ if (op == GREATER_THAN || op == GREATER_THAN_OR_EQUALS || op == LESS_THAN ||
+ op == LESS_THAN_OR_EQUALS) {
+ return false;
+ }
+ }
+ return true;
}
-static int xBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) {
- auto* pVtab = (VirtualTable*)tab;
- const auto& columns = pVtab->content->columns;
-
- ConstraintSet constraints;
- // Keep track of the index used for each valid constraint.
- // Expect this index to correspond with argv within xFilter.
- size_t expr_index = 0;
- // If any constraints are unusable increment the cost of the index.
- double cost = 1;
-
- // Tables may have requirements or use indexes.
- bool required_satisfied = false;
- bool index_used = false;
-
- // Expressions operating on the same virtual table are loosely identified by
- // the consecutive sets of terms each of the constraint sets are applied onto.
- // Subsequent attempts from failed (unusable) constraints replace the set,
- // while new sets of terms append.
- if (pIdxInfo->nConstraint > 0) {
- for (size_t i = 0; i < static_cast<size_t>(pIdxInfo->nConstraint); ++i) {
- // Record the term index (this index exists across all expressions).
- const auto& constraint_info = pIdxInfo->aConstraint[i];
+static int xBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo)
+{
+ auto* pVtab = (VirtualTable*)tab;
+ const auto& columns = pVtab->content->columns;
+
+ ConstraintSet constraints;
+ // Keep track of the index used for each valid constraint.
+ // Expect this index to correspond with argv within xFilter.
+ size_t expr_index = 0;
+ // If any constraints are unusable increment the cost of the index.
+ double cost = 1;
+
+ // Tables may have requirements or use indexes.
+ bool required_satisfied = false;
+ bool index_used = false;
+
+ // Expressions operating on the same virtual table are loosely identified by
+ // the consecutive sets of terms each of the constraint sets are applied onto.
+ // Subsequent attempts from failed (unusable) constraints replace the set,
+ // while new sets of terms append.
+ if (pIdxInfo->nConstraint > 0) {
+ for (size_t i = 0; i < static_cast<size_t>(pIdxInfo->nConstraint); ++i) {
+ // Record the term index (this index exists across all expressions).
+ const auto& constraint_info = pIdxInfo->aConstraint[i];
#if defined(DEBUG)
- plan("Evaluating constraints for table: " + pVtab->content->name +
- " [index=" + std::to_string(i) +
- " column=" + std::to_string(constraint_info.iColumn) +
- " term=" + std::to_string((int)constraint_info.iTermOffset) +
- " usable=" + std::to_string((int)constraint_info.usable) + "]");
+ plan("Evaluating constraints for table: " + pVtab->content->name +
+ " [index=" + std::to_string(i) +
+ " column=" + std::to_string(constraint_info.iColumn) +
+ " term=" + std::to_string((int)constraint_info.iTermOffset) +
+ " usable=" + std::to_string((int)constraint_info.usable) + "]");
#endif
- if (!constraint_info.usable) {
- // A higher cost less priority, prefer more usable query constraints.
- cost += 10;
- continue;
- }
-
- // Lookup the column name given an index into the table column set.
- if (constraint_info.iColumn < 0 ||
- static_cast<size_t>(constraint_info.iColumn) >=
- pVtab->content->columns.size()) {
- cost += 10;
- continue;
- }
- const auto& name = std::get<0>(columns[constraint_info.iColumn]);
- const auto& type = std::get<1>(columns[constraint_info.iColumn]);
- if (!sensibleComparison(type, constraint_info.op)) {
- cost += 10;
- continue;
- }
-
- // Check if this constraint is on an index or required column.
- const auto& options = std::get<2>(columns[constraint_info.iColumn]);
- if (options & ColumnOptions::REQUIRED) {
- index_used = true;
- required_satisfied = true;
- } else if (options & (ColumnOptions::INDEX | ColumnOptions::ADDITIONAL)) {
- index_used = true;
- }
-
- // Save a pair of the name and the constraint operator.
- // Use this constraint during xFilter by performing a scan and column
- // name lookup through out all cursor constraint lists.
- constraints.push_back(
- std::make_pair(name, Constraint(constraint_info.op)));
- pIdxInfo->aConstraintUsage[i].argvIndex = static_cast<int>(++expr_index);
+ if (!constraint_info.usable) {
+ // A higher cost less priority, prefer more usable query constraints.
+ cost += 10;
+ continue;
+ }
+
+ // Lookup the column name given an index into the table column set.
+ if (constraint_info.iColumn < 0 ||
+ static_cast<size_t>(constraint_info.iColumn) >=
+ pVtab->content->columns.size()) {
+ cost += 10;
+ continue;
+ }
+ const auto& name = std::get<0>(columns[constraint_info.iColumn]);
+ const auto& type = std::get<1>(columns[constraint_info.iColumn]);
+ if (!sensibleComparison(type, constraint_info.op)) {
+ cost += 10;
+ continue;
+ }
+
+ // Check if this constraint is on an index or required column.
+ const auto& options = std::get<2>(columns[constraint_info.iColumn]);
+ if (options & ColumnOptions::REQUIRED) {
+ index_used = true;
+ required_satisfied = true;
+ } else if (options & (ColumnOptions::INDEX | ColumnOptions::ADDITIONAL)) {
+ index_used = true;
+ }
+
+ // Save a pair of the name and the constraint operator.
+ // Use this constraint during xFilter by performing a scan and column
+ // name lookup through out all cursor constraint lists.
+ constraints.push_back(
+ std::make_pair(name, Constraint(constraint_info.op)));
+ pIdxInfo->aConstraintUsage[i].argvIndex = static_cast<int>(++expr_index);
#if defined(DEBUG)
- plan("Adding constraint for table: " + pVtab->content->name +
- " [column=" + name + " arg_index=" + std::to_string(expr_index) +
- " op=" + std::to_string(constraint_info.op) + "]");
+ plan("Adding constraint for table: " + pVtab->content->name +
+ " [column=" + name + " arg_index=" + std::to_string(expr_index) +
+ " op=" + std::to_string(constraint_info.op) + "]");
#endif
- }
- }
-
- // Check the table for a required column.
- for (const auto& column : columns) {
- auto& options = std::get<2>(column);
- if ((options & ColumnOptions::REQUIRED) && !required_satisfied) {
- // A column is marked required, but no constraint satisfies.
- return SQLITE_CONSTRAINT;
- }
- }
-
- if (!index_used) {
- // A column is marked index, but no index constraint was provided.
- cost += 200;
- }
-
- UsedColumns colsUsed;
- UsedColumnsBitset colsUsedBitset(pIdxInfo->colUsed);
- if (colsUsedBitset.any()) {
- for (size_t i = 0; i < columns.size(); i++) {
- // Check whether the column is used. colUsed has one bit for each of the
- // first 63 columns, and the 64th bit indicates that at least one other
- // column is used.
- auto bit = i < 63 ? i : 63U;
- if (colsUsedBitset[bit]) {
- auto column_name = std::get<0>(columns[i]);
-
- if (pVtab->content->aliases.count(column_name)) {
- colsUsedBitset.reset(bit);
- auto real_column_index = pVtab->content->aliases[column_name];
- bit = real_column_index < 63 ? real_column_index : 63U;
- colsUsedBitset.set(bit);
- column_name = std::get<0>(columns[real_column_index]);
- }
- colsUsed.insert(column_name);
- }
- }
- }
-
- pIdxInfo->idxNum = static_cast<int>(kConstraintIndexID++);
+ }
+ }
+
+ // Check the table for a required column.
+ for (const auto& column : columns) {
+ auto& options = std::get<2>(column);
+ if ((options & ColumnOptions::REQUIRED) && !required_satisfied) {
+ // A column is marked required, but no constraint satisfies.
+ return SQLITE_CONSTRAINT;
+ }
+ }
+
+ if (!index_used) {
+ // A column is marked index, but no index constraint was provided.
+ cost += 200;
+ }
+
+ UsedColumns colsUsed;
+ UsedColumnsBitset colsUsedBitset(pIdxInfo->colUsed);
+ if (colsUsedBitset.any()) {
+ for (size_t i = 0; i < columns.size(); i++) {
+ // Check whether the column is used. colUsed has one bit for each of the
+ // first 63 columns, and the 64th bit indicates that at least one other
+ // column is used.
+ auto bit = i < 63 ? i : 63U;
+ if (colsUsedBitset[bit]) {
+ auto column_name = std::get<0>(columns[i]);
+
+ if (pVtab->content->aliases.count(column_name)) {
+ colsUsedBitset.reset(bit);
+ auto real_column_index = pVtab->content->aliases[column_name];
+ bit = real_column_index < 63 ? real_column_index : 63U;
+ colsUsedBitset.set(bit);
+ column_name = std::get<0>(columns[real_column_index]);
+ }
+ colsUsed.insert(column_name);
+ }
+ }
+ }
+
+ pIdxInfo->idxNum = static_cast<int>(kConstraintIndexID++);
#if defined(DEBUG)
- plan("Recording constraint set for table: " + pVtab->content->name +
- " [cost=" + std::to_string(cost) +
- " size=" + std::to_string(constraints.size()) +
- " idx=" + std::to_string(pIdxInfo->idxNum) + "]");
+ plan("Recording constraint set for table: " + pVtab->content->name +
+ " [cost=" + std::to_string(cost) +
+ " size=" + std::to_string(constraints.size()) +
+ " idx=" + std::to_string(pIdxInfo->idxNum) + "]");
#endif
- // Add the constraint set to the table's tracked constraints.
- pVtab->content->constraints[pIdxInfo->idxNum] = std::move(constraints);
- pVtab->content->colsUsed[pIdxInfo->idxNum] = std::move(colsUsed);
- pVtab->content->colsUsedBitsets[pIdxInfo->idxNum] = colsUsedBitset;
- pIdxInfo->estimatedCost = cost;
- return SQLITE_OK;
+ // Add the constraint set to the table's tracked constraints.
+ pVtab->content->constraints[pIdxInfo->idxNum] = std::move(constraints);
+ pVtab->content->colsUsed[pIdxInfo->idxNum] = std::move(colsUsed);
+ pVtab->content->colsUsedBitsets[pIdxInfo->idxNum] = colsUsedBitset;
+ pIdxInfo->estimatedCost = cost;
+ return SQLITE_OK;
}
static int xFilter(sqlite3_vtab_cursor* pVtabCursor,
- int idxNum,
- const char* idxStr,
- int argc,
- sqlite3_value** argv) {
- BaseCursor* pCur = (BaseCursor*)pVtabCursor;
- auto* pVtab = (VirtualTable*)pVtabCursor->pVtab;
- auto content = pVtab->content;
- pVtab->instance->addAffectedTable(content);
-
- pCur->row = 0;
- pCur->n = 0;
- QueryContext context(content);
-
- // The SQLite instance communicates to the TablePlugin via the context.
- context.useCache(pVtab->instance->useCache());
-
- // Track required columns, this is different than the requirements check
- // that occurs within BestIndex because this scan includes a cursor.
- // For each cursor used, if a requirement exists, we need to scan the
- // selected set of constraints for a match.
- bool required_satisfied = true;
-
- // The specialized table attribute USER_BASED imposes a special requirement
- // for UID. This may be represented in the requirements, but otherwise
- // would benefit from specific notification to the caller.
- bool user_based_satisfied = !(
- (content->attributes & TableAttributes::USER_BASED) > 0);
-
- // For event-based tables, help the caller if events are disabled.
- bool events_satisfied =
- ((content->attributes & TableAttributes::EVENT_BASED) == 0);
-
- std::map<std::string, ColumnOptions> options;
- for (size_t i = 0; i < content->columns.size(); ++i) {
- // Set the column affinity for each optional constraint list.
- // There is a separate list for each column name.
- auto column_name = std::get<0>(content->columns[i]);
- context.constraints[column_name].affinity =
- std::get<1>(content->columns[i]);
- // Save the column options for comparison within constraints enumeration.
- options[column_name] = std::get<2>(content->columns[i]);
- if (options[column_name] & ColumnOptions::REQUIRED) {
- required_satisfied = false;
- }
- }
-
-// Filtering between cursors happens iteratively, not consecutively.
-// If there are multiple sets of constraints, they apply to each cursor.
+ int idxNum,
+ const char* idxStr,
+ int argc,
+ sqlite3_value** argv)
+{
+ BaseCursor* pCur = (BaseCursor*)pVtabCursor;
+ auto* pVtab = (VirtualTable*)pVtabCursor->pVtab;
+ auto content = pVtab->content;
+ pVtab->instance->addAffectedTable(content);
+
+ pCur->row = 0;
+ pCur->n = 0;
+ QueryContext context(content);
+
+ // The SQLite instance communicates to the TablePlugin via the context.
+ context.useCache(pVtab->instance->useCache());
+
+ // Track required columns, this is different than the requirements check
+ // that occurs within BestIndex because this scan includes a cursor.
+ // For each cursor used, if a requirement exists, we need to scan the
+ // selected set of constraints for a match.
+ bool required_satisfied = true;
+
+ // The specialized table attribute USER_BASED imposes a special requirement
+ // for UID. This may be represented in the requirements, but otherwise
+ // would benefit from specific notification to the caller.
+ bool user_based_satisfied = !(
+ (content->attributes & TableAttributes::USER_BASED) > 0);
+
+ // For event-based tables, help the caller if events are disabled.
+ bool events_satisfied =
+ ((content->attributes & TableAttributes::EVENT_BASED) == 0);
+
+ std::map<std::string, ColumnOptions> options;
+ for (size_t i = 0; i < content->columns.size(); ++i) {
+ // Set the column affinity for each optional constraint list.
+ // There is a separate list for each column name.
+ auto column_name = std::get<0>(content->columns[i]);
+ context.constraints[column_name].affinity =
+ std::get<1>(content->columns[i]);
+ // Save the column options for comparison within constraints enumeration.
+ options[column_name] = std::get<2>(content->columns[i]);
+ if (options[column_name] & ColumnOptions::REQUIRED) {
+ required_satisfied = false;
+ }
+ }
+
+ // Filtering between cursors happens iteratively, not consecutively.
+ // If there are multiple sets of constraints, they apply to each cursor.
#if defined(DEBUG)
- plan("Filtering called for table: " + content->name +
- " [constraint_count=" + std::to_string(content->constraints.size()) +
- " argc=" + std::to_string(argc) + " idx=" + std::to_string(idxNum) +
- "]");
+ plan("Filtering called for table: " + content->name +
+ " [constraint_count=" + std::to_string(content->constraints.size()) +
+ " argc=" + std::to_string(argc) + " idx=" + std::to_string(idxNum) +
+ "]");
#endif
- // Iterate over every argument to xFilter, filling in constraint values.
- if (content->constraints.size() > 0) {
- auto& constraints = content->constraints[idxNum];
- if (argc > 0) {
- for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
- auto expr = (const char*)sqlite3_value_text(argv[i]);
- if (expr == nullptr || expr[0] == 0) {
- // SQLite did not expose the expression value.
- continue;
- }
- // Set the expression from SQLite's now-populated argv.
- auto& constraint = constraints[i];
- constraint.second.expr = std::string(expr);
- plan("Adding constraint to cursor (" + std::to_string(pCur->id) +
- "): " + constraint.first + " " + opString(constraint.second.op) +
- " " + constraint.second.expr);
- // Add the constraint to the column-sorted query request map.
- context.constraints[constraint.first].add(constraint.second);
- }
- } else if (constraints.size() > 0) {
- // Constraints failed.
- }
-
- // 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.
- required_satisfied = true;
- }
-
- if (!user_based_satisfied &&
- (constraint.first == "uid" || constraint.first == "username")) {
- // UID was required and exists in the constraints.
- user_based_satisfied = true;
- }
- }
- }
-
- if (!content->colsUsedBitsets.empty()) {
- context.colsUsedBitset = content->colsUsedBitsets[idxNum];
- } else {
- // Unspecified; have to assume all columns are used
- context.colsUsedBitset->set();
- }
- if (content->colsUsed.size() > 0) {
- context.colsUsed = content->colsUsed[idxNum];
- }
-
- if (!user_based_satisfied) {
- WARN(OSQUERY) << "The " << pVtab->content->name
- << " table returns data based on the current user by default, "
- "consider JOINing against the users table";
- } else if (!required_satisfied) {
- WARN(OSQUERY)
- << "Table " << pVtab->content->name
- << " was queried without a required column in the WHERE clause";
- } else if (!events_satisfied) {
- WARN(OSQUERY) << "Table " << pVtab->content->name
- << " is event-based but events are disabled";
- }
-
- // Reset the virtual table contents.
- pCur->rows.clear();
- options.clear();
-
- // Generate the row data set.
- plan("Scanning rows for cursor (" + std::to_string(pCur->id) + ")");
- 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);
- pCur->rows = table->generate(context);
- } else {
- PluginRequest request = {{"action", "generate"}};
- TablePlugin::setRequestFromContext(context, request);
- QueryData qd;
- Registry::call("table", pVtab->content->name, request, qd);
- pCur->rows = tableRowsFromQueryData(std::move(qd));
- }
-
- // Set the number of rows.
- pCur->n = pCur->rows.size();
- return SQLITE_OK;
+ // Iterate over every argument to xFilter, filling in constraint values.
+ if (content->constraints.size() > 0) {
+ auto& constraints = content->constraints[idxNum];
+ if (argc > 0) {
+ for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
+ auto expr = (const char*)sqlite3_value_text(argv[i]);
+ if (expr == nullptr || expr[0] == 0) {
+ // SQLite did not expose the expression value.
+ continue;
+ }
+ // Set the expression from SQLite's now-populated argv.
+ auto& constraint = constraints[i];
+ constraint.second.expr = std::string(expr);
+ plan("Adding constraint to cursor (" + std::to_string(pCur->id) +
+ "): " + constraint.first + " " + opString(constraint.second.op) +
+ " " + constraint.second.expr);
+ // Add the constraint to the column-sorted query request map.
+ context.constraints[constraint.first].add(constraint.second);
+ }
+ } else if (constraints.size() > 0) {
+ // Constraints failed.
+ }
+
+ // 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.
+ required_satisfied = true;
+ }
+
+ if (!user_based_satisfied &&
+ (constraint.first == "uid" || constraint.first == "username")) {
+ // UID was required and exists in the constraints.
+ user_based_satisfied = true;
+ }
+ }
+ }
+
+ if (!content->colsUsedBitsets.empty()) {
+ context.colsUsedBitset = content->colsUsedBitsets[idxNum];
+ } else {
+ // Unspecified; have to assume all columns are used
+ context.colsUsedBitset->set();
+ }
+ if (content->colsUsed.size() > 0) {
+ context.colsUsed = content->colsUsed[idxNum];
+ }
+
+ if (!user_based_satisfied) {
+ WARN(OSQUERY) << "The " << pVtab->content->name
+ << " table returns data based on the current user by default, "
+ "consider JOINing against the users table";
+ } else if (!required_satisfied) {
+ WARN(OSQUERY)
+ << "Table " << pVtab->content->name
+ << " was queried without a required column in the WHERE clause";
+ } else if (!events_satisfied) {
+ WARN(OSQUERY) << "Table " << pVtab->content->name
+ << " is event-based but events are disabled";
+ }
+
+ // Reset the virtual table contents.
+ pCur->rows.clear();
+ options.clear();
+
+ // Generate the row data set.
+ plan("Scanning rows for cursor (" + std::to_string(pCur->id) + ")");
+ 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);
+ pCur->rows = table->generate(context);
+ } else {
+ PluginRequest request = {{"action", "generate"}};
+ TablePlugin::setRequestFromContext(context, request);
+ QueryData qd;
+ Registry::call("table", pVtab->content->name, request, qd);
+ pCur->rows = tableRowsFromQueryData(std::move(qd));
+ }
+
+ // Set the number of rows.
+ pCur->n = pCur->rows.size();
+ return SQLITE_OK;
}
struct sqlite3_module* getVirtualTableModule(const std::string& table_name,
- bool extension) {
-// FIXME
-// UpgradeLock lock(sqlite_module_map_mutex);
-
- if (sqlite_module_map.find(table_name) != sqlite_module_map.end()) {
- return &sqlite_module_map[table_name];
- }
-
-// WriteUpgradeLock wlock(lock);
-
- sqlite_module_map[table_name] = {};
- sqlite_module_map[table_name].xCreate = tables::sqlite::xCreate;
- sqlite_module_map[table_name].xConnect = tables::sqlite::xCreate;
- sqlite_module_map[table_name].xBestIndex = tables::sqlite::xBestIndex;
- sqlite_module_map[table_name].xDisconnect = tables::sqlite::xDestroy;
- sqlite_module_map[table_name].xDestroy = tables::sqlite::xDestroy;
- sqlite_module_map[table_name].xOpen = tables::sqlite::xOpen;
- sqlite_module_map[table_name].xClose = tables::sqlite::xClose;
- sqlite_module_map[table_name].xFilter = tables::sqlite::xFilter;
- sqlite_module_map[table_name].xNext = tables::sqlite::xNext;
- sqlite_module_map[table_name].xEof = tables::sqlite::xEof;
- sqlite_module_map[table_name].xColumn = tables::sqlite::xColumn;
- sqlite_module_map[table_name].xRowid = tables::sqlite::xRowid;
- sqlite_module_map[table_name].xUpdate = tables::sqlite::xUpdate;
-
- // Allow the table to receive INSERT/UPDATE/DROP events if it is
- // implemented from an extension and is overwriting the right methods
- // in the TablePlugin class
-
- return &sqlite_module_map[table_name];
+ bool extension)
+{
+ // FIXME
+ // UpgradeLock lock(sqlite_module_map_mutex);
+
+ if (sqlite_module_map.find(table_name) != sqlite_module_map.end()) {
+ return &sqlite_module_map[table_name];
+ }
+
+ // WriteUpgradeLock wlock(lock);
+
+ sqlite_module_map[table_name] = {};
+ sqlite_module_map[table_name].xCreate = tables::sqlite::xCreate;
+ sqlite_module_map[table_name].xConnect = tables::sqlite::xCreate;
+ sqlite_module_map[table_name].xBestIndex = tables::sqlite::xBestIndex;
+ sqlite_module_map[table_name].xDisconnect = tables::sqlite::xDestroy;
+ sqlite_module_map[table_name].xDestroy = tables::sqlite::xDestroy;
+ sqlite_module_map[table_name].xOpen = tables::sqlite::xOpen;
+ sqlite_module_map[table_name].xClose = tables::sqlite::xClose;
+ sqlite_module_map[table_name].xFilter = tables::sqlite::xFilter;
+ sqlite_module_map[table_name].xNext = tables::sqlite::xNext;
+ sqlite_module_map[table_name].xEof = tables::sqlite::xEof;
+ sqlite_module_map[table_name].xColumn = tables::sqlite::xColumn;
+ sqlite_module_map[table_name].xRowid = tables::sqlite::xRowid;
+ sqlite_module_map[table_name].xUpdate = tables::sqlite::xUpdate;
+
+ // Allow the table to receive INSERT/UPDATE/DROP events if it is
+ // implemented from an extension and is overwriting the right methods
+ // in the TablePlugin class
+
+ return &sqlite_module_map[table_name];
}
} // namespace sqlite
} // namespace tables
Status attachTableInternal(const std::string& name,
- const std::string& statement,
- const SQLiteDBInstanceRef& instance,
- bool is_extension) {
- if (SQLiteDBManager::isDisabled(name)) {
- DEBUG(OSQUERY) << "Table " << name << " is disabled, not attaching";
- return Status(0, getStringForSQLiteReturnCode(0));
- }
-
- struct sqlite3_module* module =
- tables::sqlite::getVirtualTableModule(name, is_extension);
- if (module == nullptr) {
- DEBUG(OSQUERY) << "Failed to retrieve the virtual table module for \"" << name
- << "\"";
- return Status(1);
- }
-
- // Note, if the clientData API is used then this will save a registry call
- // within xCreate.
- auto lock(instance->attachLock());
-
- int rc = sqlite3_create_module(
- instance->db(), name.c_str(), module, (void*)&(*instance));
-
- if (rc == SQLITE_OK || rc == SQLITE_MISUSE) {
- auto format =
- "CREATE VIRTUAL TABLE temp." + name + " USING " + name + statement;
-
- rc =
- sqlite3_exec(instance->db(), format.c_str(), nullptr, nullptr, nullptr);
-
- } else {
- ERROR(OSQUERY) << "Error attaching table: " << name << " (" << rc << ")";
- }
-
- return Status(rc, getStringForSQLiteReturnCode(rc));
+ const std::string& statement,
+ const SQLiteDBInstanceRef& instance,
+ bool is_extension)
+{
+ if (SQLiteDBManager::isDisabled(name)) {
+ DEBUG(OSQUERY) << "Table " << name << " is disabled, not attaching";
+ return Status(0, getStringForSQLiteReturnCode(0));
+ }
+
+ struct sqlite3_module* module =
+ tables::sqlite::getVirtualTableModule(name, is_extension);
+ if (module == nullptr) {
+ DEBUG(OSQUERY) << "Failed to retrieve the virtual table module for \"" << name
+ << "\"";
+ return Status(1);
+ }
+
+ // Note, if the clientData API is used then this will save a registry call
+ // within xCreate.
+ auto lock(instance->attachLock());
+
+ int rc = sqlite3_create_module(
+ instance->db(), name.c_str(), module, (void*) & (*instance));
+
+ if (rc == SQLITE_OK || rc == SQLITE_MISUSE) {
+ auto format =
+ "CREATE VIRTUAL TABLE temp." + name + " USING " + name + statement;
+
+ rc =
+ sqlite3_exec(instance->db(), format.c_str(), nullptr, nullptr, nullptr);
+
+ } else {
+ ERROR(OSQUERY) << "Error attaching table: " << name << " (" << rc << ")";
+ }
+
+ return Status(rc, getStringForSQLiteReturnCode(rc));
}
Status detachTableInternal(const std::string& name,
- const SQLiteDBInstanceRef& instance) {
- auto lock(instance->attachLock());
- auto format = "DROP TABLE IF EXISTS temp." + name;
- int rc = sqlite3_exec(instance->db(), format.c_str(), nullptr, nullptr, 0);
- if (rc != SQLITE_OK) {
- ERROR(OSQUERY) << "Error detaching table: " << name << " (" << rc << ")";
- }
-
- return Status(rc, getStringForSQLiteReturnCode(rc));
+ const SQLiteDBInstanceRef& instance)
+{
+ auto lock(instance->attachLock());
+ auto format = "DROP TABLE IF EXISTS temp." + name;
+ int rc = sqlite3_exec(instance->db(), format.c_str(), nullptr, nullptr, 0);
+ if (rc != SQLITE_OK) {
+ ERROR(OSQUERY) << "Error detaching table: " << name << " (" << rc << ")";
+ }
+
+ return Status(rc, getStringForSQLiteReturnCode(rc));
}
Status attachFunctionInternal(
- const std::string& name,
- std::function<
- void(sqlite3_context* context, int argc, sqlite3_value** argv)> func) {
- // Hold the manager connection instance again in callbacks.
- auto dbc = SQLiteDBManager::get();
- // Add some shell-specific functions to the instance.
- auto lock(dbc->attachLock());
- int rc = sqlite3_create_function(
- dbc->db(),
- name.c_str(),
- 0,
- SQLITE_UTF8,
- nullptr,
- *func.target<void (*)(sqlite3_context*, int, sqlite3_value**)>(),
- nullptr,
- nullptr);
- return Status(rc);
+ const std::string& name,
+ std::function <
+ void(sqlite3_context* context, int argc, sqlite3_value** argv) > func)
+{
+ // Hold the manager connection instance again in callbacks.
+ auto dbc = SQLiteDBManager::get();
+ // Add some shell-specific functions to the instance.
+ auto lock(dbc->attachLock());
+ int rc = sqlite3_create_function(
+ dbc->db(),
+ name.c_str(),
+ 0,
+ SQLITE_UTF8,
+ nullptr,
+ *func.target<void (*)(sqlite3_context*, int, sqlite3_value**)>(),
+ nullptr,
+ nullptr);
+ return Status(rc);
}
-void attachVirtualTables(const SQLiteDBInstanceRef& instance) {
- PluginResponse response;
- bool is_extension = false;
-
- for (const auto& name : RegistryFactory::get().names("table")) {
- // Column information is nice for virtual table create call.
- auto status =
- Registry::call("table", name, {{"action", "columns"}}, response);
- if (status.ok()) {
- auto statement = columnDefinition(response, true, is_extension);
- attachTableInternal(name, statement, instance, is_extension);
- }
- }
+void attachVirtualTables(const SQLiteDBInstanceRef& instance)
+{
+ PluginResponse response;
+ bool is_extension = false;
+
+ for (const auto& name : RegistryFactory::get().names("table")) {
+ // Column information is nice for virtual table create call.
+ auto status =
+ Registry::call("table", name, {{"action", "columns"}}, response);
+ if (status.ok()) {
+ auto statement = columnDefinition(response, true, is_extension);
+ attachTableInternal(name, statement, instance, is_extension);
+ }
+ }
}
} // namespace osquery
* Only used in the SQLite virtual table module methods.
*/
struct BaseCursor : private boost::noncopyable {
- public:
- /// SQLite virtual table cursor.
- sqlite3_vtab_cursor base;
+public:
+ /// SQLite virtual table cursor.
+ sqlite3_vtab_cursor base;
- /// Track cursors for optional planner output.
- size_t id{0};
+ /// Track cursors for optional planner output.
+ size_t id{0};
- /// Table data generated from last access.
- TableRows rows;
+ /// Table data generated from last access.
+ TableRows rows;
- /// Results of current call.
- TableRowHolder current;
+ /// Results of current call.
+ TableRowHolder current;
- /// Current cursor position.
- size_t row{0};
+ /// Current cursor position.
+ size_t row{0};
- /// Total number of rows.
- size_t n{0};
+ /// Total number of rows.
+ size_t n{0};
};
/**
* This adds each table plugin class to the state tracking in SQLite.
*/
struct VirtualTable : private boost::noncopyable {
- /// The SQLite-provided virtual table structure.
- sqlite3_vtab base;
+ /// The SQLite-provided virtual table structure.
+ sqlite3_vtab base;
- /// Added structure: A content structure with metadata about the table.
- std::shared_ptr<VirtualTableContent> content;
+ /// Added structure: A content structure with metadata about the table.
+ std::shared_ptr<VirtualTableContent> content;
- /// Added structure: The thread-local DB instance associated with the query.
- SQLiteDBInstance* instance{nullptr};
+ /// Added structure: The thread-local DB instance associated with the query.
+ SQLiteDBInstance* instance{nullptr};
};
/// Attach a table plugin name to an in-memory SQLite database.
Status attachTableInternal(const std::string& name,
- const std::string& statement,
- const SQLiteDBInstanceRef& instance,
- bool is_extension);
+ const std::string& statement,
+ const SQLiteDBInstanceRef& instance,
+ bool is_extension);
/// Detach (drop) a table.
Status detachTableInternal(const std::string& name,
- const SQLiteDBInstanceRef& instance);
+ const SQLiteDBInstanceRef& instance);
Status attachFunctionInternal(
- const std::string& name,
- std::function<
- void(sqlite3_context* context, int argc, sqlite3_value** argv)> func);
+ const std::string& name,
+ std::function <
+ void(sqlite3_context* context, int argc, sqlite3_value** argv) > func);
/// Attach all table plugins to an in-memory SQLite database.
void attachVirtualTables(const SQLiteDBInstanceRef& instance);
} // namespace
-std::string decode(std::string encoded) {
- boost::erase_all(encoded, "\r\n");
- boost::erase_all(encoded, "\n");
- boost::trim_right_if(encoded, boost::is_any_of("="));
+std::string decode(std::string encoded)
+{
+ boost::erase_all(encoded, "\r\n");
+ boost::erase_all(encoded, "\n");
+ boost::trim_right_if(encoded, boost::is_any_of("="));
- if (encoded.empty()) {
- return encoded;
- }
+ if (encoded.empty()) {
+ return encoded;
+ }
- try {
- return std::string(base64_dec(encoded.data()),
- base64_dec(encoded.data() + encoded.size()));
- } catch (const boost::archive::iterators::dataflow_exception& e) {
- INFO(OSQUERY) << "Could not base64 decode string: " << e.what();
- return "";
- }
+ try {
+ return std::string(base64_dec(encoded.data()),
+ base64_dec(encoded.data() + encoded.size()));
+ } catch (const boost::archive::iterators::dataflow_exception& e) {
+ INFO(OSQUERY) << "Could not base64 decode string: " << e.what();
+ return "";
+ }
}
-std::string encode(const std::string& unencoded) {
- if (unencoded.empty()) {
- return unencoded;
- }
+std::string encode(const std::string& unencoded)
+{
+ if (unencoded.empty()) {
+ return unencoded;
+ }
- size_t writePaddChars = (3U - unencoded.length() % 3U) % 3U;
- try {
- auto encoded =
- std::string(it_base64(unencoded.begin()), it_base64(unencoded.end()));
- encoded.append(std::string(writePaddChars, '='));
- return encoded;
- } catch (const boost::archive::iterators::dataflow_exception& e) {
- INFO(OSQUERY) << "Could not base64 decode string: " << e.what();
- return "";
- }
+ size_t writePaddChars = (3U - unencoded.length() % 3U) % 3U;
+ try {
+ auto encoded =
+ std::string(it_base64(unencoded.begin()), it_base64(unencoded.end()));
+ encoded.append(std::string(writePaddChars, '='));
+ return encoded;
+ } catch (const boost::archive::iterators::dataflow_exception& e) {
+ INFO(OSQUERY) << "Could not base64 decode string: " << e.what();
+ return "";
+ }
}
} // namespace base64
namespace osquery {
-bool isPrintable(const std::string& check) {
- for (const unsigned char ch : check) {
- if (ch >= 0x7F || ch <= 0x1F) {
- return false;
- }
- }
- return true;
+bool isPrintable(const std::string& check)
+{
+ for (const unsigned char ch : check) {
+ if (ch >= 0x7F || ch <= 0x1F) {
+ return false;
+ }
+ }
+ return true;
}
-size_t utf8StringSize(const std::string& str) {
- size_t res = 0;
- std::string::const_iterator it = str.begin();
- for (; it != str.end(); incUtf8StringIterator(it, str.end())) {
- res++;
- }
+size_t utf8StringSize(const std::string& str)
+{
+ size_t res = 0;
+ std::string::const_iterator it = str.begin();
+ for (; it != str.end(); incUtf8StringIterator(it, str.end())) {
+ res++;
+ }
- return res;
+ return res;
}
-std::string unescapeUnicode(const std::string& escaped) {
- if (escaped.size() < 6) {
- return escaped;
- }
+std::string unescapeUnicode(const std::string& escaped)
+{
+ if (escaped.size() < 6) {
+ return escaped;
+ }
- std::string unescaped;
- unescaped.reserve(escaped.size());
- for (size_t i = 0; i < escaped.size(); ++i) {
- if (i < escaped.size() - 5 && '\\' == escaped[i] && 'u' == escaped[i + 1]) {
- // Assume 2-byte wide unicode.
- auto const exp = tryTo<long>(escaped.substr(i + 2, 4), 16);
- if (exp.isError()) {
- WARN(OSQUERY) << "Unescaping a string with length: " << escaped.size()
- << " failed at: " << i;
- return "";
- }
- long const value = exp.get();
- if (value < 255) {
- unescaped += static_cast<char>(value);
- i += 5;
- continue;
- }
- } else if (i < escaped.size() - 1 && '\\' == escaped[i] &&
- '\\' == escaped[i + 1]) {
- // In the case of \\users 'sers' is not a unicode character
- // If we see \\ we should skip them and we do this by adding
- // an extra jump forward.
- unescaped += escaped[i];
- ++i;
- }
- unescaped += escaped[i];
- }
- return unescaped;
+ std::string unescaped;
+ unescaped.reserve(escaped.size());
+ for (size_t i = 0; i < escaped.size(); ++i) {
+ if (i < escaped.size() - 5 && '\\' == escaped[i] && 'u' == escaped[i + 1]) {
+ // Assume 2-byte wide unicode.
+ auto const exp = tryTo<long>(escaped.substr(i + 2, 4), 16);
+ if (exp.isError()) {
+ WARN(OSQUERY) << "Unescaping a string with length: " << escaped.size()
+ << " failed at: " << i;
+ return "";
+ }
+ long const value = exp.get();
+ if (value < 255) {
+ unescaped += static_cast<char>(value);
+ i += 5;
+ continue;
+ }
+ } else if (i < escaped.size() - 1 && '\\' == escaped[i] &&
+ '\\' == escaped[i + 1]) {
+ // In the case of \\users 'sers' is not a unicode character
+ // If we see \\ we should skip them and we do this by adding
+ // an extra jump forward.
+ unescaped += escaped[i];
+ ++i;
+ }
+ unescaped += escaped[i];
+ }
+ return unescaped;
}
} // namespace osquery
* @brief In-line helper function for use with utf8StringSize
*/
template <typename _Iterator1, typename _Iterator2>
-size_t incUtf8StringIterator(_Iterator1& it, const _Iterator2& last) {
- if (it == last) {
- return 0;
- }
-
- size_t res = 1;
- for (++it; last != it; ++it, ++res) {
- unsigned char c = *it;
- if (!(c & 0x80) || ((c & 0xC0) == 0xC0)) {
- break;
- }
- }
-
- return res;
+size_t incUtf8StringIterator(_Iterator1& it, const _Iterator2& last)
+{
+ if (it == last) {
+ return 0;
+ }
+
+ size_t res = 1;
+ for (++it; last != it; ++it, ++res) {
+ unsigned char c = *it;
+ if (!(c & 0x80) || ((c & 0xC0) == 0xC0)) {
+ break;
+ }
+ }
+
+ return res;
}
/**
/* We do this so that we get '0.0' from double 0.0 instead of '0'
*/
class CastVisitor : public boost::static_visitor<std::string> {
- public:
- std::string operator()(const long long& i) const {
- return std::to_string(i);
- }
+public:
+ std::string operator()(const long long& i) const
+ {
+ return std::to_string(i);
+ }
- std::string operator()(const double& d) const {
- std::string s{boost::lexical_cast<std::string>(d)};
- if (s.find('.') == std::string::npos) {
- s += ".0";
- }
- return s;
- }
+ std::string operator()(const double& d) const
+ {
+ std::string s{boost::lexical_cast<std::string>(d)};
+ if (s.find('.') == std::string::npos) {
+ s += ".0";
+ }
+ return s;
+ }
- std::string operator()(const std::string& str) const {
- return str;
- }
+ std::string operator()(const std::string& str) const
+ {
+ return str;
+ }
};
inline std::string castVariant(
- const boost::variant<long long, double, std::string>& var) {
- static const CastVisitor visitor;
- return boost::apply_visitor(visitor, var);
+ const boost::variant<long long, double, std::string>& var)
+{
+ static const CastVisitor visitor;
+ return boost::apply_visitor(visitor, var);
}
} // namespace osquery
* @return the joined string.
*/
template <typename SequenceType>
-inline std::string join(const SequenceType& s, const std::string& tok) {
- return boost::algorithm::join(s, tok);
+inline std::string join(const SequenceType& s, const std::string& tok)
+{
+ return boost::algorithm::join(s, tok);
}
} // namespace osquery
namespace osquery {
-std::vector<std::string> split(const std::string& s, const std::string& delim) {
- std::vector<std::string> elems;
- boost::split(elems, s, boost::is_any_of(delim));
- auto start =
- std::remove_if(elems.begin(), elems.end(), [](const std::string& t) {
- return t.size() == 0;
- });
- elems.erase(start, elems.end());
- for (auto& each : elems) {
- boost::algorithm::trim(each);
- }
- return elems;
+std::vector<std::string> split(const std::string& s, const std::string& delim)
+{
+ std::vector<std::string> elems;
+ boost::split(elems, s, boost::is_any_of(delim));
+ auto start =
+ std::remove_if(elems.begin(), elems.end(), [](const std::string & t) {
+ return t.size() == 0;
+ });
+ elems.erase(start, elems.end());
+ for (auto& each : elems) {
+ boost::algorithm::trim(each);
+ }
+ return elems;
}
std::vector<std::string> split(const std::string& s,
- char delim,
- size_t occurrences) {
- auto delims = std::string(1, delim);
- // Split the string normally with the required delimiter.
- auto content = split(s, delims);
- // While the result split exceeds the number of requested occurrences, join.
- std::vector<std::string> accumulator;
- std::vector<std::string> elems;
- for (size_t i = 0; i < content.size(); i++) {
- if (i < occurrences) {
- elems.push_back(content.at(i));
- } else {
- accumulator.push_back(content.at(i));
- }
- }
- // Join the optional accumulator.
- if (accumulator.size() > 0) {
- elems.push_back(boost::algorithm::join(accumulator, delims));
- }
- return elems;
+ char delim,
+ size_t occurrences)
+{
+ auto delims = std::string(1, delim);
+ // Split the string normally with the required delimiter.
+ auto content = split(s, delims);
+ // While the result split exceeds the number of requested occurrences, join.
+ std::vector<std::string> accumulator;
+ std::vector<std::string> elems;
+ for (size_t i = 0; i < content.size(); i++) {
+ if (i < occurrences) {
+ elems.push_back(content.at(i));
+ } else {
+ accumulator.push_back(content.at(i));
+ }
+ }
+ // Join the optional accumulator.
+ if (accumulator.size() > 0) {
+ elems.push_back(boost::algorithm::join(accumulator, delims));
+ }
+ return elems;
}
}
* @return a vector of strings split by delim.
*/
std::vector<std::string> split(const std::string& s,
- const std::string& delim = "\t ");
+ const std::string& delim = "\t ");
/**
* @brief Split a given string based on an delimiter.
* @return a vector of strings split by delim for occurrences.
*/
std::vector<std::string> split(const std::string& s,
- char delim,
- size_t occurrences);
+ char delim,
+ size_t occurrences);
}
#include <osquery/utils/conversions/join.h>
-namespace osquery{
+namespace osquery {
class ConversionsTests : public testing::Test {};
-TEST_F(ConversionsTests, test_join) {
- std::vector<std::string> content = {
- "one", "two", "three",
- };
- EXPECT_EQ(join(content, ", "), "one, two, three");
+TEST_F(ConversionsTests, test_join)
+{
+ std::vector<std::string> content = {
+ "one", "two", "three",
+ };
+ EXPECT_EQ(join(content, ", "), "one, two, three");
}
}
class ConversionsTests : public testing::Test {};
struct SplitStringTestData {
- std::string test_string;
- std::string delim;
- std::vector<std::string> test_vector;
+ std::string test_string;
+ std::string delim;
+ std::vector<std::string> test_vector;
};
-std::vector<SplitStringTestData> generateSplitStringTestData() {
- SplitStringTestData s1;
- s1.test_string = "a b\tc";
- s1.test_vector = {"a", "b", "c"};
+std::vector<SplitStringTestData> generateSplitStringTestData()
+{
+ SplitStringTestData s1;
+ s1.test_string = "a b\tc";
+ s1.test_vector = {"a", "b", "c"};
- SplitStringTestData s2;
- s2.test_string = " a b c";
- s2.test_vector = {"a", "b", "c"};
+ SplitStringTestData s2;
+ s2.test_string = " a b c";
+ s2.test_vector = {"a", "b", "c"};
- SplitStringTestData s3;
- s3.test_string = " a b c";
- s3.test_vector = {"a", "b", "c"};
+ SplitStringTestData s3;
+ s3.test_string = " a b c";
+ s3.test_vector = {"a", "b", "c"};
- return {s1, s2, s3};
+ return {s1, s2, s3};
}
-TEST_F(ConversionsTests, test_split) {
- for (const auto& i : generateSplitStringTestData()) {
- EXPECT_EQ(split(i.test_string), i.test_vector);
- }
+TEST_F(ConversionsTests, test_split)
+{
+ for (const auto& i : generateSplitStringTestData()) {
+ EXPECT_EQ(split(i.test_string), i.test_vector);
+ }
}
-TEST_F(ConversionsTests, test_split_occurrences) {
- std::string content = "T: 'S:S'";
- std::vector<std::string> expected = {
- "T", "'S:S'",
- };
- EXPECT_EQ(split(content, ':', 1), expected);
+TEST_F(ConversionsTests, test_split_occurrences)
+{
+ std::string content = "T: 'S:S'";
+ std::vector<std::string> expected = {
+ "T", "'S:S'",
+ };
+ EXPECT_EQ(split(content, ':', 1), expected);
}
} // namespace osquery
class NonFailingConversionsTests : public testing::Test {};
enum class TestGreenColor {
- Green,
- Pine,
- Fern,
- Olive,
+ Green,
+ Pine,
+ Fern,
+ Olive,
};
-TEST_F(NonFailingConversionsTests, to_string_from_enum_class) {
- EXPECT_NE(std::string::npos,
- to<std::string>(TestGreenColor::Green).find("TestGreenColor[0]"));
- EXPECT_NE(std::string::npos,
- to<std::string>(TestGreenColor::Pine).find("TestGreenColor[1]"));
- EXPECT_NE(std::string::npos,
- to<std::string>(TestGreenColor::Fern).find("TestGreenColor[2]"));
+TEST_F(NonFailingConversionsTests, to_string_from_enum_class)
+{
+ EXPECT_NE(std::string::npos,
+ to<std::string>(TestGreenColor::Green).find("TestGreenColor[0]"));
+ EXPECT_NE(std::string::npos,
+ to<std::string>(TestGreenColor::Pine).find("TestGreenColor[1]"));
+ EXPECT_NE(std::string::npos,
+ to<std::string>(TestGreenColor::Fern).find("TestGreenColor[2]"));
}
enum class TestOrangeColor {
- Orange,
- Fire,
- Clay,
- Cider,
+ Orange,
+ Fire,
+ Clay,
+ Cider,
};
-TEST_F(NonFailingConversionsTests, to_string_from_old_enum) {
- EXPECT_NE(
- std::string::npos,
- to<std::string>(TestOrangeColor::Orange).find("TestOrangeColor[0]"));
- EXPECT_NE(std::string::npos,
- to<std::string>(TestOrangeColor::Fire).find("TestOrangeColor[1]"));
- EXPECT_NE(std::string::npos,
- to<std::string>(TestOrangeColor::Clay).find("TestOrangeColor[2]"));
- EXPECT_NE(std::string::npos,
- to<std::string>(TestOrangeColor::Cider).find("TestOrangeColor[3]"));
+TEST_F(NonFailingConversionsTests, to_string_from_old_enum)
+{
+ EXPECT_NE(
+ std::string::npos,
+ to<std::string>(TestOrangeColor::Orange).find("TestOrangeColor[0]"));
+ EXPECT_NE(std::string::npos,
+ to<std::string>(TestOrangeColor::Fire).find("TestOrangeColor[1]"));
+ EXPECT_NE(std::string::npos,
+ to<std::string>(TestOrangeColor::Clay).find("TestOrangeColor[2]"));
+ EXPECT_NE(std::string::npos,
+ to<std::string>(TestOrangeColor::Cider).find("TestOrangeColor[3]"));
}
} // namespace osquery
class ConversionsTests : public testing::Test {};
-TEST_F(ConversionsTests, tryTo_same_type) {
- class First {};
- // rvalue
- auto ret0 = tryTo<First>(First{});
- ASSERT_FALSE(ret0.isError());
-
- auto test_lvalue = First{};
- auto ret1 = tryTo<First>(test_lvalue);
- ASSERT_FALSE(ret1.isError());
-
- const auto const_test_lvalue = First{};
- auto ret2 = tryTo<First>(const_test_lvalue);
- ASSERT_FALSE(ret2.isError());
+TEST_F(ConversionsTests, tryTo_same_type)
+{
+ class First {};
+ // rvalue
+ auto ret0 = tryTo<First>(First{});
+ ASSERT_FALSE(ret0.isError());
+
+ auto test_lvalue = First{};
+ auto ret1 = tryTo<First>(test_lvalue);
+ ASSERT_FALSE(ret1.isError());
+
+ const auto const_test_lvalue = First{};
+ auto ret2 = tryTo<First>(const_test_lvalue);
+ ASSERT_FALSE(ret2.isError());
}
template <typename ValueType, typename StrType>
-void testTryToForRvalue(ValueType value, const StrType& str) {
- auto ret = tryTo<ValueType>(StrType{str});
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), value);
+void testTryToForRvalue(ValueType value, const StrType& str)
+{
+ auto ret = tryTo<ValueType>(StrType{str});
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), value);
}
template <typename ValueType, typename StrType>
-void testTryToForLValue(ValueType value, StrType str) {
- auto ret = tryTo<ValueType>(str);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), value);
+void testTryToForLValue(ValueType value, StrType str)
+{
+ auto ret = tryTo<ValueType>(str);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), value);
}
template <typename ValueType, typename StrType>
-void testTryToForConstLValue(ValueType value, const StrType str) {
- auto ret = tryTo<ValueType>(str);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), value);
+void testTryToForConstLValue(ValueType value, const StrType str)
+{
+ auto ret = tryTo<ValueType>(str);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), value);
}
template <typename ValueType, typename StrType>
-void testTryToForString(ValueType value, const StrType str) {
- testTryToForRvalue(value, str);
- testTryToForLValue(value, str);
- testTryToForConstLValue(value, str);
+void testTryToForString(ValueType value, const StrType str)
+{
+ testTryToForRvalue(value, str);
+ testTryToForLValue(value, str);
+ testTryToForConstLValue(value, str);
}
template <typename ValueType>
-void testTryToForValue(ValueType value) {
- testTryToForString(value, std::to_string(value));
- testTryToForString(value, std::to_wstring(value));
+void testTryToForValue(ValueType value)
+{
+ testTryToForString(value, std::to_string(value));
+ testTryToForString(value, std::to_wstring(value));
}
template <typename IntType>
-void testTryToForUnsignedInt() {
- testTryToForValue<IntType>(119);
- testTryToForValue<IntType>(std::numeric_limits<IntType>::max());
- testTryToForValue<IntType>(std::numeric_limits<IntType>::min());
- testTryToForValue<IntType>(std::numeric_limits<IntType>::lowest());
- {
- auto ret = tryTo<IntType>(std::string{"0xfb"}, 16);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 251);
- }
- {
- auto ret = tryTo<IntType>(std::string{"FB"}, 16);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 251);
- }
- {
- auto ret = tryTo<IntType>(std::string{"0xFb"}, 16);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 251);
- }
- {
- auto ret = tryTo<IntType>(std::string{"E1bC2"}, 16);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 924610);
- }
- {
- auto ret = tryTo<IntType>(std::string{"10101"}, 2);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 21);
- }
- {
- auto ret = tryTo<IntType>(std::string{"035"}, 8);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 29);
- }
- {
- auto ret = tryTo<IntType>(std::string{"47"}, 8);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 39);
- }
- {
- auto ret = tryTo<IntType>(std::string{"+15"});
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 15);
- }
- {
- auto ret = tryTo<IntType>(std::string{"+1A"}, 16);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), 26);
- }
- // failure tests
- {
- auto ret = tryTo<IntType>(std::string{""});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"x"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"xor"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{".1"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"(10)"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"O"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"lO0"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"IV"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"s1"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"u1"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"#12"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"%99"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"*483"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"/488"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"\\493"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"+ 19"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string(2, '\0'));
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
+void testTryToForUnsignedInt()
+{
+ testTryToForValue<IntType>(119);
+ testTryToForValue<IntType>(std::numeric_limits<IntType>::max());
+ testTryToForValue<IntType>(std::numeric_limits<IntType>::min());
+ testTryToForValue<IntType>(std::numeric_limits<IntType>::lowest());
+ {
+ auto ret = tryTo<IntType>(std::string{"0xfb"}, 16);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 251);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"FB"}, 16);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 251);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"0xFb"}, 16);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 251);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"E1bC2"}, 16);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 924610);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"10101"}, 2);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 21);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"035"}, 8);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 29);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"47"}, 8);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 39);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"+15"});
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 15);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"+1A"}, 16);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), 26);
+ }
+ // failure tests
+ {
+ auto ret = tryTo<IntType>(std::string{""});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"x"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"xor"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{".1"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"(10)"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"O"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"lO0"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"IV"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"s1"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"u1"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"#12"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"%99"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"*483"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"/488"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"\\493"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"+ 19"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string(2, '\0'));
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
}
template <typename IntType>
-void testTryToForSignedInt() {
- testTryToForUnsignedInt<IntType>();
- testTryToForValue<int>(-126);
- {
- auto ret = tryTo<IntType>(std::string{"-7A"}, 16);
- ASSERT_FALSE(ret.isError());
- ASSERT_EQ(ret.get(), -122);
- }
- // failure tests
- {
- auto ret = tryTo<IntType>(std::string{"--14779"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"+-1813"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
- {
- auto ret = tryTo<IntType>(std::string{"- 3"});
- ASSERT_TRUE(ret.isError());
- ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
- }
+void testTryToForSignedInt()
+{
+ testTryToForUnsignedInt<IntType>();
+ testTryToForValue<int>(-126);
+ {
+ auto ret = tryTo<IntType>(std::string{"-7A"}, 16);
+ ASSERT_FALSE(ret.isError());
+ ASSERT_EQ(ret.get(), -122);
+ }
+ // failure tests
+ {
+ auto ret = tryTo<IntType>(std::string{"--14779"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"+-1813"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
+ {
+ auto ret = tryTo<IntType>(std::string{"- 3"});
+ ASSERT_TRUE(ret.isError());
+ ASSERT_EQ(ret.getErrorCode(), ConversionError::InvalidArgument);
+ }
}
-TEST_F(ConversionsTests, try_i_to_string_and_back) {
- testTryToForSignedInt<int>();
+TEST_F(ConversionsTests, try_i_to_string_and_back)
+{
+ testTryToForSignedInt<int>();
}
-TEST_F(ConversionsTests, try_l_to_string_and_back) {
- testTryToForSignedInt<long>();
+TEST_F(ConversionsTests, try_l_to_string_and_back)
+{
+ testTryToForSignedInt<long>();
}
-TEST_F(ConversionsTests, try_ll_to_string_and_back) {
- testTryToForSignedInt<long long>();
+TEST_F(ConversionsTests, try_ll_to_string_and_back)
+{
+ testTryToForSignedInt<long long>();
}
-TEST_F(ConversionsTests, try_i32_to_string_and_back) {
- testTryToForSignedInt<std::int32_t>();
+TEST_F(ConversionsTests, try_i32_to_string_and_back)
+{
+ testTryToForSignedInt<std::int32_t>();
}
-TEST_F(ConversionsTests, try_i64_to_string_and_back) {
- testTryToForSignedInt<std::int64_t>();
+TEST_F(ConversionsTests, try_i64_to_string_and_back)
+{
+ testTryToForSignedInt<std::int64_t>();
}
-TEST_F(ConversionsTests, try_imax_to_string_and_back) {
- testTryToForSignedInt<std::intmax_t>();
+TEST_F(ConversionsTests, try_imax_to_string_and_back)
+{
+ testTryToForSignedInt<std::intmax_t>();
}
-TEST_F(ConversionsTests, try_u_to_string_and_back) {
- testTryToForUnsignedInt<unsigned>();
+TEST_F(ConversionsTests, try_u_to_string_and_back)
+{
+ testTryToForUnsignedInt<unsigned>();
}
-TEST_F(ConversionsTests, try_ul_to_string_and_back) {
- testTryToForUnsignedInt<unsigned long>();
+TEST_F(ConversionsTests, try_ul_to_string_and_back)
+{
+ testTryToForUnsignedInt<unsigned long>();
}
-TEST_F(ConversionsTests, try_ull_to_string_and_back) {
- testTryToForUnsignedInt<unsigned long long>();
+TEST_F(ConversionsTests, try_ull_to_string_and_back)
+{
+ testTryToForUnsignedInt<unsigned long long>();
}
-TEST_F(ConversionsTests, try_u32_to_string_and_back) {
- testTryToForUnsignedInt<std::uint32_t>();
+TEST_F(ConversionsTests, try_u32_to_string_and_back)
+{
+ testTryToForUnsignedInt<std::uint32_t>();
}
-TEST_F(ConversionsTests, try_u64_to_string_and_back) {
- testTryToForUnsignedInt<std::uint64_t>();
+TEST_F(ConversionsTests, try_u64_to_string_and_back)
+{
+ testTryToForUnsignedInt<std::uint64_t>();
}
-TEST_F(ConversionsTests, try_umax_to_string_and_back) {
- testTryToForUnsignedInt<std::uintmax_t>();
+TEST_F(ConversionsTests, try_umax_to_string_and_back)
+{
+ testTryToForUnsignedInt<std::uintmax_t>();
}
-TEST_F(ConversionsTests, try_size_t_to_string_and_back) {
- testTryToForUnsignedInt<std::size_t>();
+TEST_F(ConversionsTests, try_size_t_to_string_and_back)
+{
+ testTryToForUnsignedInt<std::size_t>();
}
-TEST_F(ConversionsTests, tryTo_string_to_boolean_valid_args) {
- const auto test_table = std::unordered_map<std::string, bool>{
- {"1", true}, {"0", false}, {"y", true},
- {"n", false}, {"yes", true}, {"yEs", true},
- {"Yes", true}, {"no", false}, {"No", false},
- {"t", true}, {"T", true}, {"f", false},
- {"F", false}, {"true", true}, {"True", true},
- {"tRUE", true}, {"false", false}, {"fALse", false},
- {"ok", true}, {"OK", true}, {"Ok", true},
- {"enable", true}, {"Enable", true}, {"ENABLE", true},
- {"disable", false}, {"Disable", false}, {"DISABLE", false},
- };
- for (const auto& argAndAnswer : test_table) {
- auto exp = tryTo<bool>(argAndAnswer.first);
- ASSERT_FALSE(exp.isError());
- EXPECT_EQ(argAndAnswer.second, exp.get());
- }
+TEST_F(ConversionsTests, tryTo_string_to_boolean_valid_args)
+{
+ const auto test_table = std::unordered_map<std::string, bool> {
+ {"1", true}, {"0", false}, {"y", true},
+ {"n", false}, {"yes", true}, {"yEs", true},
+ {"Yes", true}, {"no", false}, {"No", false},
+ {"t", true}, {"T", true}, {"f", false},
+ {"F", false}, {"true", true}, {"True", true},
+ {"tRUE", true}, {"false", false}, {"fALse", false},
+ {"ok", true}, {"OK", true}, {"Ok", true},
+ {"enable", true}, {"Enable", true}, {"ENABLE", true},
+ {"disable", false}, {"Disable", false}, {"DISABLE", false},
+ };
+ for (const auto& argAndAnswer : test_table) {
+ auto exp = tryTo<bool>(argAndAnswer.first);
+ ASSERT_FALSE(exp.isError());
+ EXPECT_EQ(argAndAnswer.second, exp.get());
+ }
}
-TEST_F(ConversionsTests, tryTo_string_to_boolean_invalid_args) {
- const auto test_table = std::vector<std::string>{
- "", "\0", "\n", "\x06", "\x15", "\x27", "ADS",
- "7251", "20.09", "M0V+K7V", "+", "-", ".", "@",
- "1.0", "11", "00", " 0", "1 ", "2", "10",
- "100%", "_0", "1_", "1.", "2.", "E", "a",
- "b", "d", "e", "o", "p", "uh", "nix",
- "nixie", "nixy", "nixey", "nay", "nah", "no way", "veto",
- "yea", "yeah", "yep", "okey", "aye", "roger", "uh-huh",
- "righto", "yup", "yuppers", "ja", "surely", "amen", "totally",
- "sure", "yessir", "true.", "tru", "tr", "tr.", "ff",
- "yy", "nn", "nope", "null", "nil", "dis", "able",
- "pos", "neg", "ack", "ACK", "NAK", "enabled", "disabled",
- "valid", "invalid", "void", "allow", "permit", "positive", "negative",
- };
- for (const auto& wrong : test_table) {
- auto exp = tryTo<bool>(wrong);
- ASSERT_TRUE(exp.isError());
- EXPECT_EQ(ConversionError::InvalidArgument, exp.getErrorCode());
- }
+TEST_F(ConversionsTests, tryTo_string_to_boolean_invalid_args)
+{
+ const auto test_table = std::vector<std::string> {
+ "", "\0", "\n", "\x06", "\x15", "\x27", "ADS",
+ "7251", "20.09", "M0V+K7V", "+", "-", ".", "@",
+ "1.0", "11", "00", " 0", "1 ", "2", "10",
+ "100%", "_0", "1_", "1.", "2.", "E", "a",
+ "b", "d", "e", "o", "p", "uh", "nix",
+ "nixie", "nixy", "nixey", "nay", "nah", "no way", "veto",
+ "yea", "yeah", "yep", "okey", "aye", "roger", "uh-huh",
+ "righto", "yup", "yuppers", "ja", "surely", "amen", "totally",
+ "sure", "yessir", "true.", "tru", "tr", "tr.", "ff",
+ "yy", "nn", "nope", "null", "nil", "dis", "able",
+ "pos", "neg", "ack", "ACK", "NAK", "enabled", "disabled",
+ "valid", "invalid", "void", "allow", "permit", "positive", "negative",
+ };
+ for (const auto& wrong : test_table) {
+ auto exp = tryTo<bool>(wrong);
+ ASSERT_TRUE(exp.isError());
+ EXPECT_EQ(ConversionError::InvalidArgument, exp.getErrorCode());
+ }
}
} // namespace osquery
* to<std::string>(En::First) -> "En::First[1]"
*/
template <typename ToType, typename FromType>
-inline typename std::enable_if<std::is_enum<FromType>::value &&
- std::is_same<ToType, std::string>::value,
- ToType>::type
-to(FromType from) noexcept {
- auto str = ToType{boost::core::demangle(typeid(from).name())};
- str.append("[");
- str.append(std::to_string(
- static_cast<typename std::underlying_type<FromType>::type>(from)));
- str.append("]");
- return str;
+inline typename std::enable_if < std::is_enum<FromType>::value&&
+std::is_same<ToType, std::string>::value,
+ ToType >::type
+ to(FromType from) noexcept
+{
+ auto str = ToType{boost::core::demangle(typeid(from).name())};
+ str.append("[");
+ str.append(std::to_string(
+ static_cast<typename std::underlying_type<FromType>::type>(from)));
+ str.append("]");
+ return str;
}
} // namespace osquery
namespace impl {
-Expected<bool, ConversionError> stringToBool(std::string from) {
- static const auto table = std::unordered_map<std::string, bool>{
- {"1", true},
- {"0", false},
- {"y", true},
- {"yes", true},
- {"n", false},
- {"no", false},
- {"t", true},
- {"true", true},
- {"f", false},
- {"false", false},
- {"ok", true},
- {"disable", false},
- {"enable", true},
- };
- using CharType = std::string::value_type;
- // Classic locale could be used here because all available string
- // representations of boolean have ascii encoding. It must be a bit faster.
- static const auto& ctype =
- std::use_facet<std::ctype<CharType>>(std::locale::classic());
- for (auto& ch : from) {
- ch = ctype.tolower(ch);
- }
- const auto it = table.find(from);
- if (it == table.end()) {
- return createError(ConversionError::InvalidArgument)
- << "Wrong string representation of boolean "
- << boost::io::quoted(from);
- }
- return it->second;
+Expected<bool, ConversionError> stringToBool(std::string from)
+{
+ static const auto table = std::unordered_map<std::string, bool> {
+ {"1", true},
+ {"0", false},
+ {"y", true},
+ {"yes", true},
+ {"n", false},
+ {"no", false},
+ {"t", true},
+ {"true", true},
+ {"f", false},
+ {"false", false},
+ {"ok", true},
+ {"disable", false},
+ {"enable", true},
+ };
+ using CharType = std::string::value_type;
+ // Classic locale could be used here because all available string
+ // representations of boolean have ascii encoding. It must be a bit faster.
+ static const auto& ctype =
+ std::use_facet<std::ctype<CharType>>(std::locale::classic());
+ for (auto& ch : from) {
+ ch = ctype.tolower(ch);
+ }
+ const auto it = table.find(from);
+ if (it == table.end()) {
+ return createError(ConversionError::InvalidArgument)
+ << "Wrong string representation of boolean "
+ << boost::io::quoted(from);
+ }
+ return it->second;
}
} // namespace impl
namespace osquery {
enum class ConversionError {
- InvalidArgument,
- OutOfRange,
- Unknown,
+ InvalidArgument,
+ OutOfRange,
+ Unknown,
};
template <typename ToType, typename FromType>
-inline typename std::enable_if<
- std::is_same<ToType,
- typename std::remove_cv<typename std::remove_reference<
- FromType>::type>::type>::value,
- Expected<ToType, ConversionError>>::type
-tryTo(FromType&& from) {
- return std::forward<FromType>(from);
+inline typename std::enable_if <
+std::is_same<ToType,
+ typename std::remove_cv<typename std::remove_reference<
+ FromType>::type>::type>::value,
+ Expected<ToType, ConversionError >>::type
+ tryTo(FromType&& from)
+{
+ return std::forward<FromType>(from);
}
namespace impl {
template <typename Type>
struct IsStlString {
- static constexpr bool value = std::is_same<Type, std::string>::value ||
- std::is_same<Type, std::wstring>::value;
+ static constexpr bool value = std::is_same<Type, std::string>::value ||
+ std::is_same<Type, std::wstring>::value;
};
template <typename Type>
struct IsInteger {
- static constexpr bool value =
- std::is_integral<Type>::value && !std::is_same<Type, bool>::value;
+ static constexpr bool value =
+ std::is_integral<Type>::value && !std::is_same<Type, bool>::value;
};
-template <typename FromType,
- typename ToType,
- typename IntType,
- typename =
- typename std::enable_if<std::is_same<ToType, IntType>::value &&
- IsStlString<FromType>::value,
- IntType>::type>
+template < typename FromType,
+ typename ToType,
+ typename IntType,
+ typename =
+ typename std::enable_if < std::is_same<ToType, IntType>::value &&
+ IsStlString<FromType>::value,
+ IntType >::type >
struct IsConversionFromStringToIntEnabledFor {
- using type = IntType;
+ using type = IntType;
};
template <typename ToType, typename FromType>
inline
- typename IsConversionFromStringToIntEnabledFor<FromType, ToType, int>::type
- throwingStringToInt(const FromType& from, const int base) {
- auto pos = std::size_t{};
- return std::stoi(from, &pos, base);
+typename IsConversionFromStringToIntEnabledFor<FromType, ToType, int>::type
+throwingStringToInt(const FromType& from, const int base)
+{
+ auto pos = std::size_t{};
+ return std::stoi(from, &pos, base);
}
template <typename ToType, typename FromType>
inline typename IsConversionFromStringToIntEnabledFor<FromType,
- ToType,
- long int>::type
-throwingStringToInt(const FromType& from, const int base) {
- auto pos = std::size_t{};
- return std::stol(from, &pos, base);
+ ToType,
+ long int>::type
+ throwingStringToInt(const FromType& from, const int base)
+{
+ auto pos = std::size_t{};
+ return std::stol(from, &pos, base);
}
template <typename ToType, typename FromType>
inline typename IsConversionFromStringToIntEnabledFor<FromType,
- ToType,
- long long int>::type
-throwingStringToInt(const FromType& from, const int base) {
- auto pos = std::size_t{};
- return std::stoll(from, &pos, base);
+ ToType,
+ long long int>::type
+ throwingStringToInt(const FromType& from, const int base)
+{
+ auto pos = std::size_t{};
+ return std::stoll(from, &pos, base);
}
template <typename ToType, typename FromType>
inline typename IsConversionFromStringToIntEnabledFor<FromType,
- ToType,
- unsigned int>::type
-throwingStringToInt(const FromType& from, const int base) {
- auto pos = std::size_t{};
- return std::stoul(from, &pos, base);
+ ToType,
+ unsigned int>::type
+ throwingStringToInt(const FromType& from, const int base)
+{
+ auto pos = std::size_t{};
+ return std::stoul(from, &pos, base);
}
template <typename ToType, typename FromType>
inline typename IsConversionFromStringToIntEnabledFor<FromType,
- ToType,
- unsigned long int>::type
-throwingStringToInt(const FromType& from, const int base) {
- auto pos = std::size_t{};
- return std::stoul(from, &pos, base);
+ ToType,
+ unsigned long int>::type
+ throwingStringToInt(const FromType& from, const int base)
+{
+ auto pos = std::size_t{};
+ return std::stoul(from, &pos, base);
}
template <typename ToType, typename FromType>
inline
- typename IsConversionFromStringToIntEnabledFor<FromType,
- ToType,
- unsigned long long int>::type
- throwingStringToInt(const FromType& from, const int base) {
- auto pos = std::size_t{};
- return std::stoull(from, &pos, base);
+typename IsConversionFromStringToIntEnabledFor<FromType,
+ ToType,
+ unsigned long long int>::type
+ throwingStringToInt(const FromType& from, const int base)
+{
+ auto pos = std::size_t{};
+ return std::stoull(from, &pos, base);
}
Expected<bool, ConversionError> stringToBool(std::string from);
* Template tryTo for [w]string to integer conversion
*/
template <typename ToType, typename FromType>
-inline typename std::enable_if<impl::IsInteger<ToType>::value &&
- impl::IsStlString<FromType>::value,
- Expected<ToType, ConversionError>>::type
-tryTo(const FromType& from, const int base = 10) noexcept {
- try {
- return impl::throwingStringToInt<ToType>(from, base);
- } catch (const std::invalid_argument& ia) {
- return createError(ConversionError::InvalidArgument)
- << "If no conversion could be performed. " << ia.what();
- } catch (const std::out_of_range& oor) {
- return createError(ConversionError::OutOfRange)
- << "Value read is out of the range of representable values by an "
- "int. "
- << oor.what();
- } catch (...) {
- return createError(ConversionError::Unknown)
- << "Unknown error during conversion "
- << boost::core::demangle(typeid(FromType).name()) << " to "
- << boost::core::demangle(typeid(ToType).name()) << " base " << base;
- }
+inline typename std::enable_if < impl::IsInteger<ToType>::value&&
+impl::IsStlString<FromType>::value,
+ Expected<ToType, ConversionError >>::type
+ tryTo(const FromType& from, const int base = 10) noexcept
+{
+ try {
+ return impl::throwingStringToInt<ToType>(from, base);
+ } catch (const std::invalid_argument& ia) {
+ return createError(ConversionError::InvalidArgument)
+ << "If no conversion could be performed. " << ia.what();
+ } catch (const std::out_of_range& oor) {
+ return createError(ConversionError::OutOfRange)
+ << "Value read is out of the range of representable values by an "
+ "int. "
+ << oor.what();
+ } catch (...) {
+ return createError(ConversionError::Unknown)
+ << "Unknown error during conversion "
+ << boost::core::demangle(typeid(FromType).name()) << " to "
+ << boost::core::demangle(typeid(ToType).name()) << " base " << base;
+ }
}
/**
*/
template <typename ToType>
inline typename std::enable_if<std::is_same<ToType, bool>::value,
- Expected<ToType, ConversionError>>::type
-tryTo(std::string from) {
- return impl::stringToBool(std::move(from));
+ Expected<ToType, ConversionError>>::type
+ tryTo(std::string from)
+{
+ return impl::stringToBool(std::move(from));
}
-inline size_t operator"" _sz(unsigned long long int x) {
- return x;
+inline size_t operator"" _sz(unsigned long long int x)
+{
+ return x;
}
}
/**
* Use it for unconditional abort with message only in debug mode
*/
-inline void fail(const char* msg) {
+inline void fail(const char* msg)
+{
#ifndef NDEBUG
- std::cerr << "Failure in debug mode: \"" << msg << "\"\n";
- assert(false && "Failure in debug mode");
+ std::cerr << "Failure in debug mode: \"" << msg << "\"\n";
+ assert(false && "Failure in debug mode");
#endif
- boost::ignore_unused(msg);
+ boost::ignore_unused(msg);
}
/**
* See examples of usage in tests osquery/debug/tests/debug_only_tests.cpp
*/
template <typename FunctionType>
-inline void verify(FunctionType checker, const char* msg) {
+inline void verify(FunctionType checker, const char* msg)
+{
#ifndef NDEBUG
- if (!checker()) {
- fail(msg);
- }
+ if (!checker()) {
+ fail(msg);
+ }
#endif
- boost::ignore_unused(checker);
- boost::ignore_unused(msg);
+ boost::ignore_unused(checker);
+ boost::ignore_unused(msg);
}
/**
* Pretty much the same as verify, but for the simple boolean condition
*/
-inline void verifyTrue(bool expected_true, const char* msg) {
+inline void verifyTrue(bool expected_true, const char* msg)
+{
#ifndef NDEBUG
- if (!expected_true) {
- fail(msg);
- }
+ if (!expected_true) {
+ fail(msg);
+ }
#endif
- boost::ignore_unused(expected_true);
- boost::ignore_unused(msg);
+ boost::ignore_unused(expected_true);
+ boost::ignore_unused(msg);
}
/**
*/
template <typename VarType>
class Var final {
- public:
- explicit Var()
+public:
+ explicit Var()
#ifndef NDEBUG
- : value_(VarType{})
+ : value_(VarType {})
#endif
- {
- }
+ {
+ }
- Var(VarType value)
+ Var(VarType value)
#ifndef NDEBUG
- : value_(std::move(value))
+ : value_(std::move(value))
#endif
- {
- boost::ignore_unused(value);
- }
+ {
+ boost::ignore_unused(value);
+ }
- inline void verify(const char* msg) const {
+ inline void verify(const char* msg) const
+ {
#ifndef NDEBUG
- if (!value_) {
- fail(msg);
- }
+ if (!value_) {
+ fail(msg);
+ }
#endif
- boost::ignore_unused(msg);
- }
+ boost::ignore_unused(msg);
+ }
- template <typename FunctionType>
- inline void verify(FunctionType checker, const char* msg) const {
+ template <typename FunctionType>
+ inline void verify(FunctionType checker, const char* msg) const
+ {
#ifndef NDEBUG
- if (!checker(value_)) {
- fail(msg);
- }
+ if (!checker(value_)) {
+ fail(msg);
+ }
#endif
- boost::ignore_unused(checker);
- boost::ignore_unused(msg);
- }
+ boost::ignore_unused(checker);
+ boost::ignore_unused(msg);
+ }
- inline void verifyEqual(const VarType& other, const char* msg) const {
+ inline void verifyEqual(const VarType& other, const char* msg) const
+ {
#ifndef NDEBUG
- if (value_ != other) {
- fail(msg);
- }
+ if (value_ != other) {
+ fail(msg);
+ }
#endif
- boost::ignore_unused(other);
- boost::ignore_unused(msg);
- }
+ boost::ignore_unused(other);
+ boost::ignore_unused(msg);
+ }
- inline void set(const VarType& newValue) const {
+ inline void set(const VarType& newValue) const
+ {
#ifndef NDEBUG
- value_ = newValue;
+ value_ = newValue;
#endif
- boost::ignore_unused(newValue);
- }
+ boost::ignore_unused(newValue);
+ }
- template <typename FunctionType>
- inline void update(FunctionType modifier) const {
+ template <typename FunctionType>
+ inline void update(FunctionType modifier) const
+ {
#ifndef NDEBUG
- value_ = modifier(value_);
+ value_ = modifier(value_);
#endif
- boost::ignore_unused(modifier);
- }
+ boost::ignore_unused(modifier);
+ }
#ifndef NDEBUG
- mutable VarType value_;
+ mutable VarType value_;
#endif
};
} // namespace
-GTEST_TEST(DebugOnly, fail) {
+GTEST_TEST(DebugOnly, fail)
+{
#ifndef NDEBUG
- ASSERT_DEATH(debug_only::fail("This code should fail"),
- "debug.*This code should fail");
+ ASSERT_DEATH(debug_only::fail("This code should fail"),
+ "debug.*This code should fail");
#endif
}
-GTEST_TEST(DebugOnly, verify) {
+GTEST_TEST(DebugOnly, verify)
+{
#ifndef NDEBUG
- ASSERT_DEATH(
- debug_only::verify([]() { return false; },
- "This code should fail because lambda return false"),
- "debug.*This code should fail because lambda return false");
+ ASSERT_DEATH(
+ debug_only::verify([]() {
+ return false;
+ },
+ "This code should fail because lambda return false"),
+ "debug.*This code should fail because lambda return false");
#endif
}
-GTEST_TEST(DebugOnly, verifyTrue) {
- debug_only::verifyTrue(true, "This code must not fail");
+GTEST_TEST(DebugOnly, verifyTrue)
+{
+ debug_only::verifyTrue(true, "This code must not fail");
#ifndef NDEBUG
- ASSERT_DEATH(debug_only::verifyTrue(false, "This code should fail"),
- "debug.*This code should fail");
+ ASSERT_DEATH(debug_only::verifyTrue(false, "This code should fail"),
+ "debug.*This code should fail");
#endif
}
-GTEST_TEST(DebugOnly, verify_do_nothing_in_non_debug_mode) {
+GTEST_TEST(DebugOnly, verify_do_nothing_in_non_debug_mode)
+{
#ifdef NDEBUG
- // This code will be compiled and run only in release mode according to macro
- // conditions around. That means code inside lambda is not going to be run.
- // Let's check it here.
- debug_only::verify(
- []() {
- EXPECT_TRUE(false);
- return false;
- },
- "This check should not fail");
+ // This code will be compiled and run only in release mode according to macro
+ // conditions around. That means code inside lambda is not going to be run.
+ // Let's check it here.
+ debug_only::verify(
+ []() {
+ EXPECT_TRUE(false);
+ return false;
+ },
+ "This check should not fail");
#endif
}
-GTEST_TEST(DebugOnlyVar, size) {
+GTEST_TEST(DebugOnlyVar, size)
+{
#ifndef NDEBUG
- ASSERT_EQ(sizeof(long long), sizeof(debug_only::Var<long long>));
+ ASSERT_EQ(sizeof(long long), sizeof(debug_only::Var<long long>));
#else
- ASSERT_EQ(sizeof(TestEmptyClass), sizeof(debug_only::Var<long long>));
+ ASSERT_EQ(sizeof(TestEmptyClass), sizeof(debug_only::Var<long long>));
#endif
}
-GTEST_TEST(DebugOnlyVar, verify) {
- auto var = debug_only::Var<int>{0};
- var.verify([](auto v) { return v == 0; }, "This should be fine");
- var.verifyEqual(0, "This also should be fine");
+GTEST_TEST(DebugOnlyVar, verify)
+{
+ auto var = debug_only::Var<int> {0};
+ var.verify([](auto v) {
+ return v == 0;
+ }, "This should be fine");
+ var.verifyEqual(0, "This also should be fine");
#ifndef NDEBUG
- ASSERT_DEATH(var.verify([](auto v) { return v == 9; },
- "There is some funny joke supposed to be here"),
- "debug.*There is some funny joke supposed to be here");
- ASSERT_DEATH(var.verifyEqual(12, "One more hilarious joke, have a fun"),
- "debug.*One more hilarious joke, have a fun");
- ASSERT_DEATH(var.verify("And one more, don't worry this is the last one"),
- "debug.*And one more, don't worry this is the last one");
+ ASSERT_DEATH(var.verify([](auto v) {
+ return v == 9;
+ },
+ "There is some funny joke supposed to be here"),
+ "debug.*There is some funny joke supposed to be here");
+ ASSERT_DEATH(var.verifyEqual(12, "One more hilarious joke, have a fun"),
+ "debug.*One more hilarious joke, have a fun");
+ ASSERT_DEATH(var.verify("And one more, don't worry this is the last one"),
+ "debug.*And one more, don't worry this is the last one");
#endif
}
-GTEST_TEST(DebugOnlyVar, implicit_constructor) {
- // object can be created from underlying value
- debug_only::Var<int> dbg_var = 12;
- dbg_var.verifyEqual(12, "This check should not fail");
+GTEST_TEST(DebugOnlyVar, implicit_constructor)
+{
+ // object can be created from underlying value
+ debug_only::Var<int> dbg_var = 12;
+ dbg_var.verifyEqual(12, "This check should not fail");
}
-GTEST_TEST(DebugOnlyVar, set) {
- auto var = debug_only::Var<int>{12};
- var.verify(
- [](auto value) {
- EXPECT_EQ(12, value);
- return true;
- },
- "This check should not fail");
- var.set(291);
- var.verify(
- [](auto value) {
- EXPECT_EQ(291, value);
- return true;
- },
- "This check should not fail");
+GTEST_TEST(DebugOnlyVar, set)
+{
+ auto var = debug_only::Var<int> {12};
+ var.verify(
+ [](auto value) {
+ EXPECT_EQ(12, value);
+ return true;
+ },
+ "This check should not fail");
+ var.set(291);
+ var.verify(
+ [](auto value) {
+ EXPECT_EQ(291, value);
+ return true;
+ },
+ "This check should not fail");
}
-GTEST_TEST(DebugOnlyVar, update) {
- auto var = debug_only::Var<int>{12};
- var.verify(
- [](auto value) {
- EXPECT_EQ(12, value);
- return true;
- },
- "This check should not fail");
- // where
- var.update([](auto old) { return old + 17; });
- // let's verify update was successful
- var.verify(
- [](auto value) {
- EXPECT_EQ(12 + 17, value);
- return true;
- },
- "This check should not fail");
+GTEST_TEST(DebugOnlyVar, update)
+{
+ auto var = debug_only::Var<int> {12};
+ var.verify(
+ [](auto value) {
+ EXPECT_EQ(12, value);
+ return true;
+ },
+ "This check should not fail");
+ // where
+ var.update([](auto old) {
+ return old + 17;
+ });
+ // let's verify update was successful
+ var.verify(
+ [](auto value) {
+ EXPECT_EQ(12 + 17, value);
+ return true;
+ },
+ "This check should not fail");
}
-GTEST_TEST(DebugOnlyVar, verify_in_non_debug_mode_should_not_be_run) {
- auto var = debug_only::Var<int>{12};
- var.verify(
- [](auto value) {
- EXPECT_EQ(12, value);
- return true;
- },
- "This check should not fail");
+GTEST_TEST(DebugOnlyVar, verify_in_non_debug_mode_should_not_be_run)
+{
+ auto var = debug_only::Var<int> {12};
+ var.verify(
+ [](auto value) {
+ EXPECT_EQ(12, value);
+ return true;
+ },
+ "This check should not fail");
#ifdef NDEBUG
- // This code will be compiled and run only in release mode according to macro
- // conditions around. That means code inside lambda is not going to be run.
- // Let's check it here.
- var.verify(
- [](auto value) {
- boost::ignore_unused(value);
- EXPECT_TRUE(false);
- return false;
- },
- "This check should not fail");
+ // This code will be compiled and run only in release mode according to macro
+ // conditions around. That means code inside lambda is not going to be run.
+ // Let's check it here.
+ var.verify(
+ [](auto value) {
+ boost::ignore_unused(value);
+ EXPECT_TRUE(false);
+ return false;
+ },
+ "This check should not fail");
#endif
}
struct Gun {
- explicit Gun(int bullets) : bullets_(bullets) {}
-
- void shot() {
- dbg.update([this](auto v) { return bullets_ == 0 ? v + 1 : v; });
- --bullets_;
- }
-
- int bullets_;
- debug_only::Var<int> dbg = 0;
+ explicit Gun(int bullets) : bullets_(bullets) {}
+
+ void shot()
+ {
+ dbg.update([this](auto v) {
+ return bullets_ == 0 ? v + 1 : v;
+ });
+ --bullets_;
+ }
+
+ int bullets_;
+ debug_only::Var<int> dbg = 0;
};
-GTEST_TEST(DebugOnlyVar, example_debug_check_watchdog) {
- auto gun = Gun(2);
- gun.shot();
- gun.shot();
- gun.dbg.verify([](auto v) { return v == 0; },
- "There is not supposed to have a failure, just an example");
+GTEST_TEST(DebugOnlyVar, example_debug_check_watchdog)
+{
+ auto gun = Gun(2);
+ gun.shot();
+ gun.shot();
+ gun.dbg.verify([](auto v) {
+ return v == 0;
+ },
+ "There is not supposed to have a failure, just an example");
}
-GTEST_TEST(DebugOnlyVar, example_debug_check_return_value) {
- auto test_function = [](int i) { return std::to_string(i); };
- debug_only::Var<std::string> dbg = test_function(11);
- dbg.verify([](const auto& str) { return !str.empty(); },
- "The return value is not supposed to be empty string. But for "
- "performance reasons let's check it only in debug mode.");
+GTEST_TEST(DebugOnlyVar, example_debug_check_return_value)
+{
+ auto test_function = [](int i) {
+ return std::to_string(i);
+ };
+ debug_only::Var<std::string> dbg = test_function(11);
+ dbg.verify([](const auto & str) {
+ return !str.empty();
+ },
+ "The return value is not supposed to be empty string. But for "
+ "performance reasons let's check it only in debug mode.");
}
} // namespace osquery
namespace osquery {
class ErrorBase {
- public:
- virtual std::string getNonRecursiveMessage() const = 0;
- virtual std::string getMessage() const = 0;
- virtual ~ErrorBase() = default;
- ErrorBase() = default;
- ErrorBase(const ErrorBase& other) = default;
+public:
+ virtual std::string getNonRecursiveMessage() const = 0;
+ virtual std::string getMessage() const = 0;
+ virtual ~ErrorBase() = default;
+ ErrorBase() = default;
+ ErrorBase(const ErrorBase& other) = default;
};
template <typename ErrorCodeEnumType>
class Error final : public ErrorBase {
- public:
- using SelfType = Error<ErrorCodeEnumType>;
-
- explicit Error(ErrorCodeEnumType error_code,
- std::string message,
- std::unique_ptr<ErrorBase> underlying_error = nullptr)
- : errorCode_(error_code),
- message_(std::move(message)),
- underlyingError_(std::move(underlying_error)) {}
-
- explicit Error(ErrorCodeEnumType error_code,
- std::unique_ptr<ErrorBase> underlying_error = nullptr)
- : errorCode_(error_code), underlyingError_(std::move(underlying_error)) {}
-
- virtual ~Error() = default;
-
- Error(Error&& other) = default;
- Error(const Error& other) = delete;
-
- Error& operator=(Error&& other) = default;
- Error& operator=(const Error& other) = delete;
-
- ErrorCodeEnumType getErrorCode() const {
- return errorCode_;
- }
-
- bool hasUnderlyingError() const {
- return underlyingError_ != nullptr;
- }
-
- const ErrorBase& getUnderlyingError() const {
- return *underlyingError_;
- }
-
- std::unique_ptr<ErrorBase> takeUnderlyingError() const {
- return std::move(underlyingError_);
- }
-
- std::string getNonRecursiveMessage() const override {
- std::string full_message = to<std::string>(errorCode_);
- if (message_.size() > 0) {
- full_message += " (" + message_ + ")";
- }
- return full_message;
- }
-
- std::string getMessage() const override {
- std::string full_message = getNonRecursiveMessage();
- if (underlyingError_) {
- full_message += " <- " + underlyingError_->getMessage();
- }
- return full_message;
- }
-
- void appendToMessage(const std::string& text) {
- message_.append(text);
- }
-
- private:
- ErrorCodeEnumType errorCode_;
- std::string message_;
- std::unique_ptr<ErrorBase> underlyingError_;
+public:
+ using SelfType = Error<ErrorCodeEnumType>;
+
+ explicit Error(ErrorCodeEnumType error_code,
+ std::string message,
+ std::unique_ptr<ErrorBase> underlying_error = nullptr)
+ : errorCode_(error_code),
+ message_(std::move(message)),
+ underlyingError_(std::move(underlying_error)) {}
+
+ explicit Error(ErrorCodeEnumType error_code,
+ std::unique_ptr<ErrorBase> underlying_error = nullptr)
+ : errorCode_(error_code), underlyingError_(std::move(underlying_error)) {}
+
+ virtual ~Error() = default;
+
+ Error(Error&& other) = default;
+ Error(const Error& other) = delete;
+
+ Error& operator=(Error&& other) = default;
+ Error& operator=(const Error& other) = delete;
+
+ ErrorCodeEnumType getErrorCode() const
+ {
+ return errorCode_;
+ }
+
+ bool hasUnderlyingError() const
+ {
+ return underlyingError_ != nullptr;
+ }
+
+ const ErrorBase& getUnderlyingError() const
+ {
+ return *underlyingError_;
+ }
+
+ std::unique_ptr<ErrorBase> takeUnderlyingError() const
+ {
+ return std::move(underlyingError_);
+ }
+
+ std::string getNonRecursiveMessage() const override
+ {
+ std::string full_message = to<std::string>(errorCode_);
+ if (message_.size() > 0) {
+ full_message += " (" + message_ + ")";
+ }
+ return full_message;
+ }
+
+ std::string getMessage() const override
+ {
+ std::string full_message = getNonRecursiveMessage();
+ if (underlyingError_) {
+ full_message += " <- " + underlyingError_->getMessage();
+ }
+ return full_message;
+ }
+
+ void appendToMessage(const std::string& text)
+ {
+ message_.append(text);
+ }
+
+private:
+ ErrorCodeEnumType errorCode_;
+ std::string message_;
+ std::unique_ptr<ErrorBase> underlyingError_;
};
template <class T>
-inline bool operator==(const Error<T>& lhs, const Error<T>& rhs) {
- return lhs.getErrorCode() == rhs.getErrorCode();
+inline bool operator==(const Error<T>& lhs, const Error<T>& rhs)
+{
+ return lhs.getErrorCode() == rhs.getErrorCode();
}
template <class T>
-inline bool operator==(const Error<T>* lhs, const T rhs) {
- return lhs->getErrorCode() == rhs;
+inline bool operator==(const Error<T>* lhs, const T rhs)
+{
+ return lhs->getErrorCode() == rhs;
}
template <class T>
-inline bool operator==(const Error<T>& lhs, const T rhs) {
- return lhs.getErrorCode() == rhs;
+inline bool operator==(const Error<T>& lhs, const T rhs)
+{
+ return lhs.getErrorCode() == rhs;
}
template <class T>
-inline bool operator==(const ErrorBase& lhs, const T rhs) {
- try {
- const Error<T>& casted_lhs = dynamic_cast<const Error<T>&>(lhs);
- return casted_lhs == rhs;
- } catch (std::bad_cast _) {
- return false;
- }
+inline bool operator==(const ErrorBase& lhs, const T rhs)
+{
+ try {
+ const Error<T>& casted_lhs = dynamic_cast<const Error<T>&>(lhs);
+ return casted_lhs == rhs;
+ } catch (std::bad_cast _) {
+ return false;
+ }
}
-inline std::ostream& operator<<(std::ostream& out, const ErrorBase& error) {
- out << error.getMessage();
- return out;
+inline std::ostream& operator<<(std::ostream& out, const ErrorBase& error)
+{
+ out << error.getMessage();
+ return out;
}
template <typename ErrorCodeEnumType, typename OtherErrorCodeEnumType>
OSQUERY_NODISCARD Error<ErrorCodeEnumType> createError(
- ErrorCodeEnumType error_code,
- Error<OtherErrorCodeEnumType> underlying_error) {
- return Error<ErrorCodeEnumType>(
- error_code,
- std::make_unique<Error<OtherErrorCodeEnumType>>(
- std::move(underlying_error)));
+ ErrorCodeEnumType error_code,
+ Error<OtherErrorCodeEnumType> underlying_error)
+{
+ return Error<ErrorCodeEnumType>(
+ error_code,
+ std::make_unique<Error<OtherErrorCodeEnumType>>(
+ std::move(underlying_error)));
}
template <typename ErrorCodeEnumType>
OSQUERY_NODISCARD Error<ErrorCodeEnumType> createError(
- ErrorCodeEnumType error_code) {
- return Error<ErrorCodeEnumType>(error_code);
+ ErrorCodeEnumType error_code)
+{
+ return Error<ErrorCodeEnumType>(error_code);
}
template <typename ErrorType,
- typename ValueType,
- typename = typename std::enable_if<
- std::is_base_of<ErrorBase, ErrorType>::value>::type>
-inline ErrorType operator<<(ErrorType&& error, const ValueType& value) {
- std::ostringstream ostr{};
- ostr << value;
- error.appendToMessage(ostr.str());
- return std::forward<ErrorType>(error);
+ typename ValueType,
+ typename = typename std::enable_if<
+ std::is_base_of<ErrorBase, ErrorType>::value>::type>
+inline ErrorType operator<<(ErrorType && error, const ValueType& value)
+{
+ std::ostringstream ostr{};
+ ostr << value;
+ error.appendToMessage(ostr.str());
+ return std::forward<ErrorType>(error);
}
} // namespace osquery
#include <osquery/utils/error/error.h>
enum class TestError {
- SomeError = 1,
- AnotherError = 2,
- MusicError,
+ SomeError = 1,
+ AnotherError = 2,
+ MusicError,
};
-GTEST_TEST(ErrorTest, initialization) {
- auto error = osquery::Error<TestError>(TestError::SomeError, "TestMessage");
- EXPECT_FALSE(error.hasUnderlyingError());
- EXPECT_TRUE(error == TestError::SomeError);
+GTEST_TEST(ErrorTest, initialization)
+{
+ auto error = osquery::Error<TestError>(TestError::SomeError, "TestMessage");
+ EXPECT_FALSE(error.hasUnderlyingError());
+ EXPECT_TRUE(error == TestError::SomeError);
- auto shortMsg = error.getNonRecursiveMessage();
- EXPECT_NE(std::string::npos, shortMsg.find("TestError[1]"));
+ auto shortMsg = error.getNonRecursiveMessage();
+ EXPECT_NE(std::string::npos, shortMsg.find("TestError[1]"));
- auto fullMsg = error.getMessage();
- EXPECT_NE(std::string::npos, fullMsg.find("TestError[1]"));
- EXPECT_NE(std::string::npos, fullMsg.find("TestMessage"));
+ auto fullMsg = error.getMessage();
+ EXPECT_NE(std::string::npos, fullMsg.find("TestError[1]"));
+ EXPECT_NE(std::string::npos, fullMsg.find("TestMessage"));
}
-GTEST_TEST(ErrorTest, recursive) {
- auto orignalError = std::make_unique<osquery::Error<TestError>>(
- TestError::SomeError, "SuperTestMessage");
- auto error = osquery::Error<TestError>(
- TestError::AnotherError, "TestMessage", std::move(orignalError));
- EXPECT_TRUE(error.hasUnderlyingError());
-
- auto shortMsg = error.getNonRecursiveMessage();
- EXPECT_EQ(std::string::npos, shortMsg.find("TestError[1]"));
- EXPECT_NE(std::string::npos, shortMsg.find("TestError[2]"));
-
- auto fullMsg = error.getMessage();
- EXPECT_NE(std::string::npos, fullMsg.find("TestError[1]"));
- EXPECT_NE(std::string::npos, fullMsg.find("SuperTestMessage"));
- EXPECT_NE(std::string::npos, fullMsg.find("TestError[2]"));
- EXPECT_NE(std::string::npos, fullMsg.find("TestMessage"));
+GTEST_TEST(ErrorTest, recursive)
+{
+ auto orignalError = std::make_unique<osquery::Error<TestError>>(
+ TestError::SomeError, "SuperTestMessage");
+ auto error = osquery::Error<TestError>(
+ TestError::AnotherError, "TestMessage", std::move(orignalError));
+ EXPECT_TRUE(error.hasUnderlyingError());
+
+ auto shortMsg = error.getNonRecursiveMessage();
+ EXPECT_EQ(std::string::npos, shortMsg.find("TestError[1]"));
+ EXPECT_NE(std::string::npos, shortMsg.find("TestError[2]"));
+
+ auto fullMsg = error.getMessage();
+ EXPECT_NE(std::string::npos, fullMsg.find("TestError[1]"));
+ EXPECT_NE(std::string::npos, fullMsg.find("SuperTestMessage"));
+ EXPECT_NE(std::string::npos, fullMsg.find("TestError[2]"));
+ EXPECT_NE(std::string::npos, fullMsg.find("TestMessage"));
}
-bool stringContains(const std::string& where, const std::string& what) {
- return boost::contains(where, what);
+bool stringContains(const std::string& where, const std::string& what)
+{
+ return boost::contains(where, what);
};
-GTEST_TEST(ErrorTest, createErrorSimple) {
- const auto msg = std::string{
- "\"!ab#c$d%e&f'g(h)i*j+k,l-m.n/o\" this is not a human readable text"};
- auto err = osquery::createError(TestError::AnotherError) << msg;
- EXPECT_EQ(TestError::AnotherError, err.getErrorCode());
- EXPECT_FALSE(err.hasUnderlyingError());
-
- auto shortMsg = err.getMessage();
- EXPECT_PRED2(stringContains, shortMsg, "TestError");
- EXPECT_PRED2(stringContains, shortMsg, msg);
+GTEST_TEST(ErrorTest, createErrorSimple)
+{
+ const auto msg = std::string{
+ "\"!ab#c$d%e&f'g(h)i*j+k,l-m.n/o\" this is not a human readable text"};
+ auto err = osquery::createError(TestError::AnotherError) << msg;
+ EXPECT_EQ(TestError::AnotherError, err.getErrorCode());
+ EXPECT_FALSE(err.hasUnderlyingError());
+
+ auto shortMsg = err.getMessage();
+ EXPECT_PRED2(stringContains, shortMsg, "TestError");
+ EXPECT_PRED2(stringContains, shortMsg, msg);
}
-GTEST_TEST(ErrorTest, createErrorFromOtherError) {
- const auto firstMsg = std::string{"2018-06-28 08:13 451014"};
-
- auto firstErr = osquery::createError(TestError::SomeError) << firstMsg;
- EXPECT_EQ(TestError::SomeError, firstErr.getErrorCode());
- EXPECT_FALSE(firstErr.hasUnderlyingError());
-
- EXPECT_PRED2(stringContains, firstErr.getMessage(), firstMsg);
-
- const auto secondMsg = std::string{"what's wrong with the first message?!"};
- auto secondErr =
- osquery::createError(TestError::AnotherError, std::move(firstErr))
- << secondMsg;
- EXPECT_EQ(TestError::AnotherError, secondErr.getErrorCode());
- EXPECT_TRUE(secondErr.hasUnderlyingError());
- auto secondShortMsg = secondErr.getMessage();
- EXPECT_PRED2(stringContains, secondShortMsg, "TestError");
- EXPECT_PRED2(stringContains, secondShortMsg, firstMsg);
- EXPECT_PRED2(stringContains, secondShortMsg, secondMsg);
+GTEST_TEST(ErrorTest, createErrorFromOtherError)
+{
+ const auto firstMsg = std::string{"2018-06-28 08:13 451014"};
+
+ auto firstErr = osquery::createError(TestError::SomeError) << firstMsg;
+ EXPECT_EQ(TestError::SomeError, firstErr.getErrorCode());
+ EXPECT_FALSE(firstErr.hasUnderlyingError());
+
+ EXPECT_PRED2(stringContains, firstErr.getMessage(), firstMsg);
+
+ const auto secondMsg = std::string{"what's wrong with the first message?!"};
+ auto secondErr =
+ osquery::createError(TestError::AnotherError, std::move(firstErr))
+ << secondMsg;
+ EXPECT_EQ(TestError::AnotherError, secondErr.getErrorCode());
+ EXPECT_TRUE(secondErr.hasUnderlyingError());
+ auto secondShortMsg = secondErr.getMessage();
+ EXPECT_PRED2(stringContains, secondShortMsg, "TestError");
+ EXPECT_PRED2(stringContains, secondShortMsg, firstMsg);
+ EXPECT_PRED2(stringContains, secondShortMsg, secondMsg);
}
-GTEST_TEST(ErrorTest, createErrorAndStreamToIt) {
- const auto a4 = std::string{"A4"};
- const auto err = osquery::createError(TestError::MusicError)
- << "Do" << '-' << "Re"
- << "-Mi"
- << "-Fa"
- << "-Sol"
- << "-La"
- << "-Si La" << boost::io::quoted(a4) << ' ' << 440 << " Hz";
- EXPECT_EQ(TestError::MusicError, err.getErrorCode());
- auto fullMsg = err.getMessage();
- EXPECT_PRED2(
- stringContains, fullMsg, "Do-Re-Mi-Fa-Sol-La-Si La\"A4\" 440 Hz");
+GTEST_TEST(ErrorTest, createErrorAndStreamToIt)
+{
+ const auto a4 = std::string{"A4"};
+ const auto err = osquery::createError(TestError::MusicError)
+ << "Do" << '-' << "Re"
+ << "-Mi"
+ << "-Fa"
+ << "-Sol"
+ << "-La"
+ << "-Si La" << boost::io::quoted(a4) << ' ' << 440 << " Hz";
+ EXPECT_EQ(TestError::MusicError, err.getErrorCode());
+ auto fullMsg = err.getMessage();
+ EXPECT_PRED2(
+ stringContains, fullMsg, "Do-Re-Mi-Fa-Sol-La-Si La\"A4\" 440 Hz");
}
template <typename ValueType_, typename ErrorCodeEnumType>
class Expected final {
- public:
- using ValueType = ValueType_;
- using ErrorType = Error<ErrorCodeEnumType>;
- using SelfType = Expected<ValueType, ErrorCodeEnumType>;
-
- static_assert(
- !std::is_pointer<ValueType>::value,
- "Please do not use raw pointers as expected value, "
- "use smart pointers instead. See CppCoreGuidelines for explanation. "
- "https://github.com/isocpp/CppCoreGuidelines/blob/master/"
- "CppCoreGuidelines.md#Rf-unique_ptr");
- static_assert(!std::is_reference<ValueType>::value,
- "Expected does not support reference as a value type");
- static_assert(std::is_enum<ErrorCodeEnumType>::value,
- "ErrorCodeEnumType template parameter must be enum");
-
- public:
- Expected(ValueType value) : object_{std::move(value)} {}
-
- Expected(ErrorType error) : object_{std::move(error)} {}
-
- explicit Expected(ErrorCodeEnumType code, std::string message)
- : object_{ErrorType(code, message)} {}
-
- Expected() = delete;
- Expected(ErrorBase* error) = delete;
-
- Expected(Expected&& other)
- : object_(std::move(other.object_)), errorChecked_(other.errorChecked_) {
- other.errorChecked_.set(true);
- }
-
- Expected& operator=(Expected&& other) {
- if (this != &other) {
- errorChecked_.verify("Expected was not checked before assigning");
-
- object_ = std::move(other.object_);
- errorChecked_ = other.errorChecked_;
- other.errorChecked_.set(true);
- }
- return *this;
- }
-
- Expected(const Expected&) = delete;
- Expected& operator=(const Expected& other) = delete;
-
- ~Expected() {
- errorChecked_.verify("Expected was not checked before destruction");
- }
-
- static SelfType success(ValueType value) {
- return SelfType{std::move(value)};
- }
-
- static SelfType failure(std::string message) {
- auto defaultCode = ErrorCodeEnumType{};
- return SelfType(defaultCode, std::move(message));
- }
-
- static SelfType failure(ErrorCodeEnumType code, std::string message) {
- return SelfType(code, std::move(message));
- }
-
- ErrorType takeError() && = delete;
- ErrorType takeError() & {
- verifyIsError();
- return std::move(boost::get<ErrorType>(object_));
- }
-
- const ErrorType& getError() const&& = delete;
- const ErrorType& getError() const& {
- verifyIsError();
- return boost::get<ErrorType>(object_);
- }
-
- ErrorCodeEnumType getErrorCode() const&& = delete;
- ErrorCodeEnumType getErrorCode() const& {
- return getError().getErrorCode();
- }
-
- bool isError() const noexcept {
- errorChecked_.set(true);
- return object_.which() == kErrorType_;
- }
-
- void ignoreResult() const noexcept {
- errorChecked_.set(true);
- }
-
- bool isValue() const noexcept {
- return !isError();
- }
-
- explicit operator bool() const noexcept {
- return isValue();
- }
-
- ValueType& get() && = delete;
- ValueType& get() & {
- verifyIsValue();
- return boost::get<ValueType>(object_);
- }
-
- const ValueType& get() const&& = delete;
- const ValueType& get() const& {
- verifyIsValue();
- return boost::get<ValueType>(object_);
- }
-
- ValueType take() && = delete;
- ValueType take() & {
- return std::move(get());
- }
-
- template <typename ValueTypeUniversal = ValueType>
- typename std::enable_if<
- std::is_same<typename std::decay<ValueTypeUniversal>::type,
- ValueType>::value,
- ValueType>::type
- takeOr(ValueTypeUniversal&& defaultValue) {
- if (isError()) {
- return std::forward<ValueTypeUniversal>(defaultValue);
- }
- return std::move(get());
- }
-
- ValueType* operator->() && = delete;
- ValueType* operator->() & {
- return &get();
- }
-
- const ValueType* operator->() const&& = delete;
- const ValueType* operator->() const& {
- return &get();
- }
-
- ValueType& operator*() && = delete;
- ValueType& operator*() & {
- return get();
- }
-
- const ValueType& operator*() const&& = delete;
- const ValueType& operator*() const& {
- return get();
- }
-
- private:
- inline void verifyIsError() const {
- debug_only::verify([this]() { return object_.which() == kErrorType_; },
- "Do not try to get error from Expected with value");
- }
-
- inline void verifyIsValue() const {
- debug_only::verify([this]() { return object_.which() == kValueType_; },
- "Do not try to get value from Expected with error");
- }
-
- private:
- boost::variant<ValueType, ErrorType> object_;
- enum ETypeId {
- kValueType_ = 0,
- kErrorType_ = 1,
- };
- debug_only::Var<bool> errorChecked_ = false;
+public:
+ using ValueType = ValueType_;
+ using ErrorType = Error<ErrorCodeEnumType>;
+ using SelfType = Expected<ValueType, ErrorCodeEnumType>;
+
+ static_assert(
+ !std::is_pointer<ValueType>::value,
+ "Please do not use raw pointers as expected value, "
+ "use smart pointers instead. See CppCoreGuidelines for explanation. "
+ "https://github.com/isocpp/CppCoreGuidelines/blob/master/"
+ "CppCoreGuidelines.md#Rf-unique_ptr");
+ static_assert(!std::is_reference<ValueType>::value,
+ "Expected does not support reference as a value type");
+ static_assert(std::is_enum<ErrorCodeEnumType>::value,
+ "ErrorCodeEnumType template parameter must be enum");
+
+public:
+ Expected(ValueType value) : object_{std::move(value)} {}
+
+ Expected(ErrorType error) : object_{std::move(error)} {}
+
+ explicit Expected(ErrorCodeEnumType code, std::string message)
+ : object_{ErrorType(code, message)} {}
+
+ Expected() = delete;
+ Expected(ErrorBase* error) = delete;
+
+ Expected(Expected&& other)
+ : object_(std::move(other.object_)), errorChecked_(other.errorChecked_)
+ {
+ other.errorChecked_.set(true);
+ }
+
+ Expected& operator=(Expected&& other)
+ {
+ if (this != &other) {
+ errorChecked_.verify("Expected was not checked before assigning");
+
+ object_ = std::move(other.object_);
+ errorChecked_ = other.errorChecked_;
+ other.errorChecked_.set(true);
+ }
+ return *this;
+ }
+
+ Expected(const Expected&) = delete;
+ Expected& operator=(const Expected& other) = delete;
+
+ ~Expected()
+ {
+ errorChecked_.verify("Expected was not checked before destruction");
+ }
+
+ static SelfType success(ValueType value)
+ {
+ return SelfType{std::move(value)};
+ }
+
+ static SelfType failure(std::string message)
+ {
+ auto defaultCode = ErrorCodeEnumType{};
+ return SelfType(defaultCode, std::move(message));
+ }
+
+ static SelfType failure(ErrorCodeEnumType code, std::string message)
+ {
+ return SelfType(code, std::move(message));
+ }
+
+ ErrorType takeError()&& = delete;
+ ErrorType takeError()& {
+ verifyIsError();
+ return std::move(boost::get<ErrorType>(object_));
+ }
+
+ const ErrorType& getError() const&& = delete;
+ const ErrorType& getError() const&
+ {
+ verifyIsError();
+ return boost::get<ErrorType>(object_);
+ }
+
+ ErrorCodeEnumType getErrorCode() const&& = delete;
+ ErrorCodeEnumType getErrorCode() const&
+ {
+ return getError().getErrorCode();
+ }
+
+ bool isError() const noexcept
+ {
+ errorChecked_.set(true);
+ return object_.which() == kErrorType_;
+ }
+
+ void ignoreResult() const noexcept
+ {
+ errorChecked_.set(true);
+ }
+
+ bool isValue() const noexcept
+ {
+ return !isError();
+ }
+
+ explicit operator bool() const noexcept
+ {
+ return isValue();
+ }
+
+ ValueType& get()&& = delete;
+ ValueType& get()& {
+ verifyIsValue();
+ return boost::get<ValueType>(object_);
+ }
+
+ const ValueType& get() const&& = delete;
+ const ValueType& get() const&
+ {
+ verifyIsValue();
+ return boost::get<ValueType>(object_);
+ }
+
+ ValueType take()&& = delete;
+ ValueType take()& {
+ return std::move(get());
+ }
+
+ template <typename ValueTypeUniversal = ValueType>
+ typename std::enable_if <
+ std::is_same<typename std::decay<ValueTypeUniversal>::type,
+ ValueType>::value,
+ ValueType >::type
+ takeOr(ValueTypeUniversal && defaultValue)
+ {
+ if (isError()) {
+ return std::forward<ValueTypeUniversal>(defaultValue);
+ }
+ return std::move(get());
+ }
+
+ ValueType* operator->()&& = delete;
+ ValueType* operator->()& {
+ return &get();
+ }
+
+ const ValueType* operator->() const&& = delete;
+ const ValueType* operator->() const&
+ {
+ return &get();
+ }
+
+ ValueType& operator*()&& = delete;
+ ValueType& operator*()& {
+ return get();
+ }
+
+ const ValueType& operator*() const&& = delete;
+ const ValueType& operator*() const&
+ {
+ return get();
+ }
+
+private:
+ inline void verifyIsError() const
+ {
+ debug_only::verify([this]() {
+ return object_.which() == kErrorType_;
+ },
+ "Do not try to get error from Expected with value");
+ }
+
+ inline void verifyIsValue() const
+ {
+ debug_only::verify([this]() {
+ return object_.which() == kValueType_;
+ },
+ "Do not try to get value from Expected with error");
+ }
+
+private:
+ boost::variant<ValueType, ErrorType> object_;
+ enum ETypeId {
+ kValueType_ = 0,
+ kErrorType_ = 1,
+ };
+ debug_only::Var<bool> errorChecked_ = false;
};
template <typename ValueType, typename ErrorCodeEnumType>
namespace osquery {
enum class TestError {
- Some,
- Another,
- Semantic,
- Logical,
- Runtime,
+ Some,
+ Another,
+ Semantic,
+ Logical,
+ Runtime,
};
-GTEST_TEST(ExpectedTest, success_contructor_initialization) {
- Expected<std::string, TestError> value = std::string("Test");
- EXPECT_TRUE(value);
- EXPECT_TRUE(value.isValue());
- EXPECT_FALSE(value.isError());
- EXPECT_EQ(value.get(), "Test");
+GTEST_TEST(ExpectedTest, success_contructor_initialization)
+{
+ Expected<std::string, TestError> value = std::string("Test");
+ EXPECT_TRUE(value);
+ EXPECT_TRUE(value.isValue());
+ EXPECT_FALSE(value.isError());
+ EXPECT_EQ(value.get(), "Test");
}
-GTEST_TEST(ExpectedTest, failure_error_contructor_initialization) {
- Expected<std::string, TestError> error =
- Error<TestError>(TestError::Some, "Please try again");
- EXPECT_FALSE(error);
- EXPECT_FALSE(error.isValue());
- EXPECT_TRUE(error.isError());
- EXPECT_EQ(error.getErrorCode(), TestError::Some);
+GTEST_TEST(ExpectedTest, failure_error_contructor_initialization)
+{
+ Expected<std::string, TestError> error =
+ Error<TestError>(TestError::Some, "Please try again");
+ EXPECT_FALSE(error);
+ EXPECT_FALSE(error.isValue());
+ EXPECT_TRUE(error.isError());
+ EXPECT_EQ(error.getErrorCode(), TestError::Some);
}
-bool stringContains(const std::string& what, const std::string& where) {
- return boost::contains(what, where);
+bool stringContains(const std::string& what, const std::string& where)
+{
+ return boost::contains(what, where);
};
-GTEST_TEST(ExpectedTest, failure_error_str_contructor_initialization) {
- const auto msg =
- std::string{"\"#$%&'()*+,-./089:;<[=]>\" is it a valid error message?"};
- auto expected = Expected<std::string, TestError>::failure(msg);
- EXPECT_FALSE(expected);
- EXPECT_TRUE(expected.isError());
- EXPECT_EQ(expected.getErrorCode(), TestError::Some);
- auto fullMsg = expected.getError().getMessage();
- EXPECT_PRED2(stringContains, fullMsg, msg);
+GTEST_TEST(ExpectedTest, failure_error_str_contructor_initialization)
+{
+ const auto msg =
+ std::string{"\"#$%&'()*+,-./089:;<[=]>\" is it a valid error message?"};
+ auto expected = Expected<std::string, TestError>::failure(msg);
+ EXPECT_FALSE(expected);
+ EXPECT_TRUE(expected.isError());
+ EXPECT_EQ(expected.getErrorCode(), TestError::Some);
+ auto fullMsg = expected.getError().getMessage();
+ EXPECT_PRED2(stringContains, fullMsg, msg);
}
-osquery::ExpectedUnique<std::string, TestError> testFunction() {
- return std::make_unique<std::string>("Test");
+osquery::ExpectedUnique<std::string, TestError> testFunction()
+{
+ return std::make_unique<std::string>("Test");
}
-GTEST_TEST(ExpectedTest, ExpectedSharedTestFunction) {
- osquery::Expected<std::shared_ptr<std::string>, TestError> sharedPointer =
- std::make_shared<std::string>("Test");
- EXPECT_TRUE(sharedPointer);
- EXPECT_EQ(**sharedPointer, "Test");
-
- osquery::ExpectedShared<std::string, TestError> sharedPointer2 =
- std::make_shared<std::string>("Test");
- EXPECT_TRUE(sharedPointer2);
- EXPECT_EQ(**sharedPointer2, "Test");
+GTEST_TEST(ExpectedTest, ExpectedSharedTestFunction)
+{
+ osquery::Expected<std::shared_ptr<std::string>, TestError> sharedPointer =
+ std::make_shared<std::string>("Test");
+ EXPECT_TRUE(sharedPointer);
+ EXPECT_EQ(**sharedPointer, "Test");
+
+ osquery::ExpectedShared<std::string, TestError> sharedPointer2 =
+ std::make_shared<std::string>("Test");
+ EXPECT_TRUE(sharedPointer2);
+ EXPECT_EQ(**sharedPointer2, "Test");
}
-GTEST_TEST(ExpectedTest, ExpectedUniqueTestFunction) {
- auto uniquePointer = testFunction();
- EXPECT_TRUE(uniquePointer);
- EXPECT_EQ(**uniquePointer, "Test");
+GTEST_TEST(ExpectedTest, ExpectedUniqueTestFunction)
+{
+ auto uniquePointer = testFunction();
+ EXPECT_TRUE(uniquePointer);
+ EXPECT_EQ(**uniquePointer, "Test");
}
-GTEST_TEST(ExpectedTest, ExpectedSharedWithError) {
- osquery::ExpectedShared<std::string, TestError> error =
- Error<TestError>(TestError::Another, "Some message");
- EXPECT_FALSE(error);
- EXPECT_EQ(error.getErrorCode(), TestError::Another);
+GTEST_TEST(ExpectedTest, ExpectedSharedWithError)
+{
+ osquery::ExpectedShared<std::string, TestError> error =
+ Error<TestError>(TestError::Another, "Some message");
+ EXPECT_FALSE(error);
+ EXPECT_EQ(error.getErrorCode(), TestError::Another);
}
-GTEST_TEST(ExpectedTest, ExpectedOptional) {
- boost::optional<std::string> optional = std::string("123");
- osquery::Expected<boost::optional<std::string>, TestError> optionalExpected =
- optional;
- EXPECT_TRUE(optionalExpected);
- EXPECT_EQ(**optionalExpected, "123");
+GTEST_TEST(ExpectedTest, ExpectedOptional)
+{
+ boost::optional<std::string> optional = std::string("123");
+ osquery::Expected<boost::optional<std::string>, TestError> optionalExpected =
+ optional;
+ EXPECT_TRUE(optionalExpected);
+ EXPECT_EQ(**optionalExpected, "123");
}
template <typename ValueType>
using LocalExpected = Expected<ValueType, TestError>;
-GTEST_TEST(ExpectedTest, createError_example) {
- auto giveMeDozen = [](bool valid) -> LocalExpected<int> {
- if (valid) {
- return 50011971;
- }
- return createError(TestError::Logical)
- << "an error message is supposed to be here";
- };
- auto v = giveMeDozen(true);
- EXPECT_TRUE(v);
- ASSERT_FALSE(v.isError());
- EXPECT_EQ(*v, 50011971);
-
- auto errV = giveMeDozen(false);
- EXPECT_FALSE(errV);
- ASSERT_TRUE(errV.isError());
- EXPECT_EQ(errV.getErrorCode(), TestError::Logical);
+GTEST_TEST(ExpectedTest, createError_example)
+{
+ auto giveMeDozen = [](bool valid) -> LocalExpected<int> {
+ if (valid)
+ {
+ return 50011971;
+ }
+ return createError(TestError::Logical)
+ << "an error message is supposed to be here";
+ };
+ auto v = giveMeDozen(true);
+ EXPECT_TRUE(v);
+ ASSERT_FALSE(v.isError());
+ EXPECT_EQ(*v, 50011971);
+
+ auto errV = giveMeDozen(false);
+ EXPECT_FALSE(errV);
+ ASSERT_TRUE(errV.isError());
+ EXPECT_EQ(errV.getErrorCode(), TestError::Logical);
}
-GTEST_TEST(ExpectedTest, ExpectedSuccess_example) {
- auto giveMeStatus = [](bool valid) -> ExpectedSuccess<TestError> {
- if (valid) {
- return Success{};
- }
- return Error<TestError>(TestError::Runtime,
- "an error message is supposed to be here");
- };
- auto s = giveMeStatus(true);
- EXPECT_TRUE(s);
- ASSERT_FALSE(s.isError());
-
- auto errS = giveMeStatus(false);
- EXPECT_FALSE(errS);
- ASSERT_TRUE(errS.isError());
+GTEST_TEST(ExpectedTest, ExpectedSuccess_example)
+{
+ auto giveMeStatus = [](bool valid) -> ExpectedSuccess<TestError> {
+ if (valid)
+ {
+ return Success{};
+ }
+ return Error<TestError>(TestError::Runtime,
+ "an error message is supposed to be here");
+ };
+ auto s = giveMeStatus(true);
+ EXPECT_TRUE(s);
+ ASSERT_FALSE(s.isError());
+
+ auto errS = giveMeStatus(false);
+ EXPECT_FALSE(errS);
+ ASSERT_TRUE(errS.isError());
}
-GTEST_TEST(ExpectedTest, nested_errors_example) {
- const auto msg = std::string{"Write a good error message"};
- auto firstFailureSource = [&msg]() -> Expected<std::vector<int>, TestError> {
- return createError(TestError::Semantic) << msg;
- };
- auto giveMeNestedError = [&]() -> Expected<std::vector<int>, TestError> {
- auto ret = firstFailureSource();
- ret.isError();
- return createError(TestError::Runtime, ret.takeError()) << msg;
- };
- auto ret = giveMeNestedError();
- EXPECT_FALSE(ret);
- ASSERT_TRUE(ret.isError());
- EXPECT_EQ(ret.getErrorCode(), TestError::Runtime);
- ASSERT_TRUE(ret.getError().hasUnderlyingError());
- EXPECT_PRED2(stringContains, ret.getError().getMessage(), msg);
+GTEST_TEST(ExpectedTest, nested_errors_example)
+{
+ const auto msg = std::string{"Write a good error message"};
+ auto firstFailureSource = [&msg]() -> Expected<std::vector<int>, TestError> {
+ return createError(TestError::Semantic) << msg;
+ };
+ auto giveMeNestedError = [&]() -> Expected<std::vector<int>, TestError> {
+ auto ret = firstFailureSource();
+ ret.isError();
+ return createError(TestError::Runtime, ret.takeError()) << msg;
+ };
+ auto ret = giveMeNestedError();
+ EXPECT_FALSE(ret);
+ ASSERT_TRUE(ret.isError());
+ EXPECT_EQ(ret.getErrorCode(), TestError::Runtime);
+ ASSERT_TRUE(ret.getError().hasUnderlyingError());
+ EXPECT_PRED2(stringContains, ret.getError().getMessage(), msg);
}
-GTEST_TEST(ExpectedTest, error_handling_example) {
- auto failureSource = []() -> Expected<std::vector<int>, TestError> {
- return createError(TestError::Runtime) << "Test error message ()*+,-.";
- };
- auto ret = failureSource();
- if (ret.isError()) {
- switch (ret.getErrorCode()) {
- case TestError::Some:
- case TestError::Another:
- case TestError::Semantic:
- case TestError::Logical:
- FAIL() << "There is must be a Runtime type of error";
- case TestError::Runtime:
- SUCCEED();
- }
- } else {
- FAIL() << "There is must be an error";
- }
+GTEST_TEST(ExpectedTest, error_handling_example)
+{
+ auto failureSource = []() -> Expected<std::vector<int>, TestError> {
+ return createError(TestError::Runtime) << "Test error message ()*+,-.";
+ };
+ auto ret = failureSource();
+ if (ret.isError()) {
+ switch (ret.getErrorCode()) {
+ case TestError::Some:
+ case TestError::Another:
+ case TestError::Semantic:
+ case TestError::Logical:
+ FAIL() << "There is must be a Runtime type of error";
+ case TestError::Runtime:
+ SUCCEED();
+ }
+ } else {
+ FAIL() << "There is must be an error";
+ }
}
-GTEST_TEST(ExpectedTest, expected_was_not_checked_before_destruction_failure) {
- auto action = []() { auto expected = ExpectedSuccess<TestError>{Success()}; };
+GTEST_TEST(ExpectedTest, expected_was_not_checked_before_destruction_failure)
+{
+ auto action = []() {
+ auto expected = ExpectedSuccess<TestError> {Success()};
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Expected was not checked before destruction");
+ ASSERT_DEATH(action(), "Expected was not checked before destruction");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, expected_was_not_checked_before_assigning_failure) {
- auto action = []() {
- auto expected = ExpectedSuccess<TestError>{Success()};
- expected = ExpectedSuccess<TestError>{Success()};
- expected.isValue();
- };
+GTEST_TEST(ExpectedTest, expected_was_not_checked_before_assigning_failure)
+{
+ auto action = []() {
+ auto expected = ExpectedSuccess<TestError> {Success()};
+ expected = ExpectedSuccess<TestError> {Success()};
+ expected.isValue();
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Expected was not checked before assigning");
+ ASSERT_DEATH(action(), "Expected was not checked before assigning");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, expected_move_is_safe) {
- auto expected = ExpectedSuccess<TestError>{Success()};
- expected.isValue();
- expected = ExpectedSuccess<TestError>{Success()};
- expected.isValue();
+GTEST_TEST(ExpectedTest, expected_move_is_safe)
+{
+ auto expected = ExpectedSuccess<TestError> {Success()};
+ expected.isValue();
+ expected = ExpectedSuccess<TestError> {Success()};
+ expected.isValue();
}
-GTEST_TEST(ExpectedTest, get_value_from_expected_with_error) {
- auto action = []() {
- auto expected = Expected<int, TestError>(TestError::Logical,
- "Test error message @#$0k+Qh");
- auto value = expected.get();
- boost::ignore_unused(value);
- };
+GTEST_TEST(ExpectedTest, get_value_from_expected_with_error)
+{
+ auto action = []() {
+ auto expected = Expected<int, TestError>(TestError::Logical,
+ "Test error message @#$0k+Qh");
+ auto value = expected.get();
+ boost::ignore_unused(value);
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Do not try to get value from Expected with error");
+ ASSERT_DEATH(action(), "Do not try to get value from Expected with error");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, const_get_value_from_expected_with_error) {
- auto action = []() {
- const auto expected = Expected<int, TestError>(
- TestError::Semantic, "Test error message {}()[].");
- auto value = expected.get();
- boost::ignore_unused(value);
- };
+GTEST_TEST(ExpectedTest, const_get_value_from_expected_with_error)
+{
+ auto action = []() {
+ const auto expected = Expected<int, TestError>(
+ TestError::Semantic, "Test error message {}()[].");
+ auto value = expected.get();
+ boost::ignore_unused(value);
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Do not try to get value from Expected with error");
+ ASSERT_DEATH(action(), "Do not try to get value from Expected with error");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, take_value_from_expected_with_error) {
- auto action = []() {
- auto expected = Expected<int, TestError>(TestError::Semantic,
- "Test error message !&^?<>.");
- auto value = expected.take();
- boost::ignore_unused(value);
- };
+GTEST_TEST(ExpectedTest, take_value_from_expected_with_error)
+{
+ auto action = []() {
+ auto expected = Expected<int, TestError>(TestError::Semantic,
+ "Test error message !&^?<>.");
+ auto value = expected.take();
+ boost::ignore_unused(value);
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Do not try to get value from Expected with error");
+ ASSERT_DEATH(action(), "Do not try to get value from Expected with error");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, get_error_from_expected_with_value) {
- auto action = []() {
- auto expected = Expected<int, TestError>(228);
- const auto& error = expected.getError();
- boost::ignore_unused(error);
- };
+GTEST_TEST(ExpectedTest, get_error_from_expected_with_value)
+{
+ auto action = []() {
+ auto expected = Expected<int, TestError>(228);
+ const auto& error = expected.getError();
+ boost::ignore_unused(error);
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Do not try to get error from Expected with value");
+ ASSERT_DEATH(action(), "Do not try to get error from Expected with value");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, take_error_from_expected_with_value) {
- auto action = []() {
- auto expected = Expected<int, TestError>(228);
- return expected.takeError();
- };
+GTEST_TEST(ExpectedTest, take_error_from_expected_with_value)
+{
+ auto action = []() {
+ auto expected = Expected<int, TestError>(228);
+ return expected.takeError();
+ };
#ifndef NDEBUG
- ASSERT_DEATH(action(), "Do not try to get error from Expected with value");
+ ASSERT_DEATH(action(), "Do not try to get error from Expected with value");
#else
- boost::ignore_unused(action);
+ boost::ignore_unused(action);
#endif
}
-GTEST_TEST(ExpectedTest, value__takeOr) {
- const auto text = std::string{"some text"};
- auto callable = [&text]() -> Expected<std::string, TestError> {
- return text;
- };
- auto expected = callable();
- EXPECT_EQ(expected ? expected.take() : std::string{"default text"}, text);
+GTEST_TEST(ExpectedTest, value__takeOr)
+{
+ const auto text = std::string{"some text"};
+ auto callable = [&text]() -> Expected<std::string, TestError> {
+ return text;
+ };
+ auto expected = callable();
+ EXPECT_EQ(expected ? expected.take() : std::string{"default text"}, text);
- EXPECT_EQ(callable().takeOr(std::string{"default text"}), text);
+ EXPECT_EQ(callable().takeOr(std::string{"default text"}), text);
}
-GTEST_TEST(ExpectedTest, error__takeOr) {
- auto expected =
- Expected<std::string, TestError>(TestError::Semantic, "error message");
- EXPECT_EQ(expected.takeOr(std::string{"default text"}), "default text");
+GTEST_TEST(ExpectedTest, error__takeOr)
+{
+ auto expected =
+ Expected<std::string, TestError>(TestError::Semantic, "error message");
+ EXPECT_EQ(expected.takeOr(std::string{"default text"}), "default text");
}
-GTEST_TEST(ExpectedTest, error__takeOr_with_user_defined_class) {
- class SomeTestClass {
- public:
- explicit SomeTestClass(const std::string& prefix, const std::string& sufix)
- : text{prefix + " - " + sufix} {}
-
- std::string text;
- };
- auto callable = []() -> Expected<SomeTestClass, TestError> {
- return createError(TestError::Semantic) << "error message";
- };
- EXPECT_EQ(callable().takeOr(SomeTestClass("427 BC", "347 BC")).text,
- "427 BC - 347 BC");
+GTEST_TEST(ExpectedTest, error__takeOr_with_user_defined_class)
+{
+ class SomeTestClass {
+ public:
+ explicit SomeTestClass(const std::string& prefix, const std::string& sufix)
+ : text{prefix + " - " + sufix} {}
+
+ std::string text;
+ };
+ auto callable = []() -> Expected<SomeTestClass, TestError> {
+ return createError(TestError::Semantic) << "error message";
+ };
+ EXPECT_EQ(callable().takeOr(SomeTestClass("427 BC", "347 BC")).text,
+ "427 BC - 347 BC");
}
-GTEST_TEST(ExpectedTest, value_takeOr_with_rvalue_as_an_argument) {
- auto value = int{312};
- auto callable = []() -> Expected<int, TestError> { return 306; };
- value = callable().takeOr(value);
- EXPECT_EQ(value, 306);
+GTEST_TEST(ExpectedTest, value_takeOr_with_rvalue_as_an_argument)
+{
+ auto value = int{312};
+ auto callable = []() -> Expected<int, TestError> { return 306; };
+ value = callable().takeOr(value);
+ EXPECT_EQ(value, 306);
}
-GTEST_TEST(ExpectedTest, error_takeOr_with_rvalue_as_an_argument) {
- auto value = int{312};
- auto callable = []() -> Expected<int, TestError> {
- return createError(TestError::Logical) << "error message";
- };
- value = callable().takeOr(value);
- EXPECT_EQ(value, 312);
+GTEST_TEST(ExpectedTest, error_takeOr_with_rvalue_as_an_argument)
+{
+ auto value = int{312};
+ auto callable = []() -> Expected<int, TestError> {
+ return createError(TestError::Logical) << "error message";
+ };
+ value = callable().takeOr(value);
+ EXPECT_EQ(value, 312);
}
} // namespace osquery
* their's as default.
*/
class only_movable {
- protected:
- /// Boilerplate self default constructor.
- only_movable() = default;
+protected:
+ /// Boilerplate self default constructor.
+ only_movable() = default;
- /// Boilerplate self destructor.
- ~only_movable() = default;
+ /// Boilerplate self destructor.
+ ~only_movable() = default;
- /// Boilerplate move constructor.
- only_movable(only_movable&&) noexcept = default;
+ /// Boilerplate move constructor.
+ only_movable(only_movable&&) noexcept = default;
- /// Boilerplate move assignment.
- only_movable& operator=(only_movable&&) = default;
+ /// Boilerplate move assignment.
+ only_movable& operator=(only_movable&&) = default;
- public:
- /// Important, a private copy constructor prevents copying.
- only_movable(const only_movable&) = delete;
+public:
+ /// Important, a private copy constructor prevents copying.
+ only_movable(const only_movable&) = delete;
- /// Important, a private copy assignment constructor prevents copying.
- only_movable& operator=(const only_movable&) = delete;
+ /// Important, a private copy assignment constructor prevents copying.
+ only_movable& operator=(const only_movable&) = delete;
};
} // namespace osquery
constexpr int Status::kSuccessCode;
-Status Status::failure(int code, std::string message) {
- assert(code != Status::kSuccessCode &&
- "Using 'failure' to create Status object with a kSuccessCode");
- return Status(code, std::move(message));
+Status Status::failure(int code, std::string message)
+{
+ assert(code != Status::kSuccessCode &&
+ "Using 'failure' to create Status object with a kSuccessCode");
+ return Status(code, std::move(message));
}
-::std::ostream& operator<<(::std::ostream& os, const Status& s) {
- return os << "Status(" << s.getCode() << R"(, ")" << s.getMessage()
- << R"("))";
+::std::ostream& operator<<(::std::ostream& os, const Status& s)
+{
+ return os << "Status(" << s.getCode() << R"(, ")" << s.getMessage()
+ << R"("))";
}
} // namespace osquery
*/
class Status {
- public:
- static constexpr int kSuccessCode = 0;
- /**
- * @brief Default constructor
- *
- * Note that the default constructor initialized an osquery::Status instance
- * to a state such that a successful operation is indicated.
- */
- explicit Status(int c = Status::kSuccessCode) : code_(c), message_("OK") {}
-
- /**
- * @brief A constructor which can be used to concisely express the status of
- * an operation.
- *
- * @param c a status code. The idiom is that a zero status code indicates a
- * successful operation and a non-zero status code indicates a failed
- * operation.
- * @param m a message indicating some extra detail regarding the operation.
- * If all operations were successful, this message should be "OK".
- * Otherwise, it doesn't matter what the string is, as long as both the
- * setter and caller agree.
- */
- Status(int c, std::string m) : code_(c), message_(std::move(m)) {}
-
- Status(const ErrorBase& error) : code_(1), message_(error.getMessage()) {}
-
- public:
- /**
- * @brief A getter for the status code property
- *
- * @return an integer representing the status code of the operation.
- */
- int getCode() const {
- return code_;
- }
-
- /**
- * @brief A getter for the message property
- *
- * @return a string representing arbitrary additional information about the
- * success or failure of an operation. On successful operations, the idiom
- * is for the message to be "OK"
- */
- std::string getMessage() const {
- return message_;
- }
-
- /**
- * @brief A convenience method to check if the return code is
- * Status::kSuccessCode
- *
- * @code{.cpp}
- * auto s = doSomething();
- * if (s.ok()) {
- * LOG(INFO) << "doing work";
- * } else {
- * LOG(ERROR) << s.toString();
- * }
- * @endcode
- *
- * @return a boolean which is true if the status code is Status::kSuccessCode,
- * false otherwise.
- */
- bool ok() const {
- return getCode() == Status::kSuccessCode;
- }
-
- /**
- * @brief A synonym for osquery::Status::getMessage()
- *
- * @see getMessage()
- */
- std::string toString() const {
- return getMessage();
- }
- std::string what() const {
- return getMessage();
- }
-
- /**
- * @brief implicit conversion to bool
- *
- * Allows easy use of Status in an if statement, as below:
- *
- * @code{.cpp}
- * if (doSomethingThatReturnsStatus()) {
- * LOG(INFO) << "Success!";
- * }
- * @endcode
- */
- /* explicit */ explicit operator bool() const {
- return ok();
- }
-
- static Status success() {
- return Status(kSuccessCode);
- }
-
- static Status failure(std::string message) {
- return Status(1, std::move(message));
- }
-
- static Status failure(int code, std::string message);
-
- // Below operator implementations useful for testing with gtest
-
- // Enables use of gtest (ASSERT|EXPECT)_EQ
- bool operator==(const Status& rhs) const {
- return (code_ == rhs.getCode()) && (message_ == rhs.getMessage());
- }
-
- // Enables use of gtest (ASSERT|EXPECT)_NE
- bool operator!=(const Status& rhs) const {
- return !operator==(rhs);
- }
-
- // Enables pretty-printing in gtest (ASSERT|EXPECT)_(EQ|NE)
- friend ::std::ostream& operator<<(::std::ostream& os, const Status& s);
-
- private:
- /// the internal storage of the status code
- int code_;
-
- /// the internal storage of the status message
- std::string message_;
+public:
+ static constexpr int kSuccessCode = 0;
+ /**
+ * @brief Default constructor
+ *
+ * Note that the default constructor initialized an osquery::Status instance
+ * to a state such that a successful operation is indicated.
+ */
+ explicit Status(int c = Status::kSuccessCode) : code_(c), message_("OK") {}
+
+ /**
+ * @brief A constructor which can be used to concisely express the status of
+ * an operation.
+ *
+ * @param c a status code. The idiom is that a zero status code indicates a
+ * successful operation and a non-zero status code indicates a failed
+ * operation.
+ * @param m a message indicating some extra detail regarding the operation.
+ * If all operations were successful, this message should be "OK".
+ * Otherwise, it doesn't matter what the string is, as long as both the
+ * setter and caller agree.
+ */
+ Status(int c, std::string m) : code_(c), message_(std::move(m)) {}
+
+ Status(const ErrorBase& error) : code_(1), message_(error.getMessage()) {}
+
+public:
+ /**
+ * @brief A getter for the status code property
+ *
+ * @return an integer representing the status code of the operation.
+ */
+ int getCode() const
+ {
+ return code_;
+ }
+
+ /**
+ * @brief A getter for the message property
+ *
+ * @return a string representing arbitrary additional information about the
+ * success or failure of an operation. On successful operations, the idiom
+ * is for the message to be "OK"
+ */
+ std::string getMessage() const
+ {
+ return message_;
+ }
+
+ /**
+ * @brief A convenience method to check if the return code is
+ * Status::kSuccessCode
+ *
+ * @code{.cpp}
+ * auto s = doSomething();
+ * if (s.ok()) {
+ * LOG(INFO) << "doing work";
+ * } else {
+ * LOG(ERROR) << s.toString();
+ * }
+ * @endcode
+ *
+ * @return a boolean which is true if the status code is Status::kSuccessCode,
+ * false otherwise.
+ */
+ bool ok() const
+ {
+ return getCode() == Status::kSuccessCode;
+ }
+
+ /**
+ * @brief A synonym for osquery::Status::getMessage()
+ *
+ * @see getMessage()
+ */
+ std::string toString() const
+ {
+ return getMessage();
+ }
+ std::string what() const
+ {
+ return getMessage();
+ }
+
+ /**
+ * @brief implicit conversion to bool
+ *
+ * Allows easy use of Status in an if statement, as below:
+ *
+ * @code{.cpp}
+ * if (doSomethingThatReturnsStatus()) {
+ * LOG(INFO) << "Success!";
+ * }
+ * @endcode
+ */
+ /* explicit */ explicit operator bool() const
+ {
+ return ok();
+ }
+
+ static Status success()
+ {
+ return Status(kSuccessCode);
+ }
+
+ static Status failure(std::string message)
+ {
+ return Status(1, std::move(message));
+ }
+
+ static Status failure(int code, std::string message);
+
+ // Below operator implementations useful for testing with gtest
+
+ // Enables use of gtest (ASSERT|EXPECT)_EQ
+ bool operator==(const Status& rhs) const
+ {
+ return (code_ == rhs.getCode()) && (message_ == rhs.getMessage());
+ }
+
+ // Enables use of gtest (ASSERT|EXPECT)_NE
+ bool operator!=(const Status& rhs) const
+ {
+ return !operator==(rhs);
+ }
+
+ // Enables pretty-printing in gtest (ASSERT|EXPECT)_(EQ|NE)
+ friend ::std::ostream& operator<<(::std::ostream& os, const Status& s);
+
+private:
+ /// the internal storage of the status code
+ int code_;
+
+ /// the internal storage of the status message
+ std::string message_;
};
::std::ostream& operator<<(::std::ostream& os, const Status& s);
template <typename ToType, typename ValueType, typename ErrorCodeEnumType>
inline
- typename std::enable_if<std::is_same<ToType, Status>::value, Status>::type
- to(const Expected<ValueType, ErrorCodeEnumType>& expected) {
- return expected ? Status::success()
- : Status::failure(expected.getError().getMessage());
+typename std::enable_if<std::is_same<ToType, Status>::value, Status>::type
+to(const Expected<ValueType, ErrorCodeEnumType>& expected)
+{
+ return expected ? Status::success()
+ : Status::failure(expected.getError().getMessage());
}
} // namespace osquery
class StatusTests : public testing::Test {};
-TEST_F(StatusTests, test_constructor) {
- auto s = Status(5, "message");
- EXPECT_EQ(s.getCode(), 5);
- EXPECT_EQ(s.getMessage(), "message");
+TEST_F(StatusTests, test_constructor)
+{
+ auto s = Status(5, "message");
+ EXPECT_EQ(s.getCode(), 5);
+ EXPECT_EQ(s.getMessage(), "message");
}
-TEST_F(StatusTests, test_constructor_2) {
- Status s;
- EXPECT_EQ(s.getCode(), 0);
- EXPECT_EQ(s.getMessage(), "OK");
+TEST_F(StatusTests, test_constructor_2)
+{
+ Status s;
+ EXPECT_EQ(s.getCode(), 0);
+ EXPECT_EQ(s.getMessage(), "OK");
}
-TEST_F(StatusTests, test_ok) {
- auto s1 = Status(5, "message");
- EXPECT_FALSE(s1.ok());
- auto s2 = Status(0, "message");
- EXPECT_TRUE(s2.ok());
+TEST_F(StatusTests, test_ok)
+{
+ auto s1 = Status(5, "message");
+ EXPECT_FALSE(s1.ok());
+ auto s2 = Status(0, "message");
+ EXPECT_TRUE(s2.ok());
}
-TEST_F(StatusTests, test_to_string) {
- auto s = Status(0, "foobar");
- EXPECT_EQ(s.toString(), "foobar");
+TEST_F(StatusTests, test_to_string)
+{
+ auto s = Status(0, "foobar");
+ EXPECT_EQ(s.toString(), "foobar");
}
-TEST_F(StatusTests, test_default_constructor) {
- auto s = Status{};
- EXPECT_TRUE(s.ok());
+TEST_F(StatusTests, test_default_constructor)
+{
+ auto s = Status{};
+ EXPECT_TRUE(s.ok());
}
-TEST_F(StatusTests, test_success_code) {
- auto s = Status(Status::kSuccessCode);
- EXPECT_TRUE(s.ok());
+TEST_F(StatusTests, test_success_code)
+{
+ auto s = Status(Status::kSuccessCode);
+ EXPECT_TRUE(s.ok());
}
-TEST_F(StatusTests, test_success) {
- auto s = Status::success();
- EXPECT_TRUE(s.ok());
+TEST_F(StatusTests, test_success)
+{
+ auto s = Status::success();
+ EXPECT_TRUE(s.ok());
}
-TEST_F(StatusTests, test_failure_single_arg) {
- auto s = Status::failure("Some proper error message.");
- EXPECT_EQ(s.toString(), "Some proper error message.");
- EXPECT_FALSE(s.ok());
+TEST_F(StatusTests, test_failure_single_arg)
+{
+ auto s = Status::failure("Some proper error message.");
+ EXPECT_EQ(s.toString(), "Some proper error message.");
+ EXPECT_FALSE(s.ok());
}
-TEST_F(StatusTests, test_failure_double_arg) {
- auto s = Status::failure(105, "One more proper error message!");
- EXPECT_EQ(s.toString(), "One more proper error message!");
- EXPECT_FALSE(s.ok());
+TEST_F(StatusTests, test_failure_double_arg)
+{
+ auto s = Status::failure(105, "One more proper error message!");
+ EXPECT_EQ(s.toString(), "One more proper error message!");
+ EXPECT_FALSE(s.ok());
}
-TEST_F(StatusTests, test_failure_with_success_code) {
+TEST_F(StatusTests, test_failure_with_success_code)
+{
#ifndef NDEBUG
- ASSERT_DEATH(Status::failure(Status::kSuccessCode, "message"),
- "Using 'failure' to create Status object with a kSuccessCode");
+ ASSERT_DEATH(Status::failure(Status::kSuccessCode, "message"),
+ "Using 'failure' to create Status object with a kSuccessCode");
#endif
}
namespace {
enum class TestError {
- Semantic = 1,
+ Semantic = 1,
};
-bool stringContains(const std::string& where, const std::string& what) {
- return boost::contains(where, what);
+bool stringContains(const std::string& where, const std::string& what)
+{
+ return boost::contains(where, what);
};
} // namespace
-TEST_F(StatusTests, test_expected_to_status_failure) {
- const auto expected = Expected<std::string, TestError>(
- TestError::Semantic, "The ultimate failure reason");
- auto s = to<Status>(expected);
- EXPECT_FALSE(s.ok());
- EXPECT_PRED2(stringContains, s.toString(), "The ultimate failure reason");
+TEST_F(StatusTests, test_expected_to_status_failure)
+{
+ const auto expected = Expected<std::string, TestError>(
+ TestError::Semantic, "The ultimate failure reason");
+ auto s = to<Status>(expected);
+ EXPECT_FALSE(s.ok());
+ EXPECT_PRED2(stringContains, s.toString(), "The ultimate failure reason");
}
-TEST_F(StatusTests, test_expected_to_status_success) {
- const auto expected =
- Expected<std::string, TestError>("This is not a failure");
- auto s = to<Status>(expected);
- EXPECT_TRUE(s.ok());
- EXPECT_EQ(s, Status::success());
+TEST_F(StatusTests, test_expected_to_status_success)
+{
+ const auto expected =
+ Expected<std::string, TestError>("This is not a failure");
+ auto s = to<Status>(expected);
+ EXPECT_TRUE(s.ok());
+ EXPECT_EQ(s, Status::success());
}
}
namespace osquery {
-bool setEnvVar(const std::string& name, const std::string& value) {
- auto ret = ::setenv(name.c_str(), value.c_str(), 1);
- return (ret == 0);
+bool setEnvVar(const std::string& name, const std::string& value)
+{
+ auto ret = ::setenv(name.c_str(), value.c_str(), 1);
+ return (ret == 0);
}
-bool unsetEnvVar(const std::string& name) {
- auto ret = ::unsetenv(name.c_str());
- return (ret == 0);
+bool unsetEnvVar(const std::string& name)
+{
+ auto ret = ::unsetenv(name.c_str());
+ return (ret == 0);
}
-boost::optional<std::string> getEnvVar(const std::string& name) {
- char* value = ::getenv(name.c_str());
- if (value) {
- return std::string(value);
- }
- return boost::none;
+boost::optional<std::string> getEnvVar(const std::string& name)
+{
+ char* value = ::getenv(name.c_str());
+ if (value) {
+ return std::string(value);
+ }
+ return boost::none;
}
} // namespace osquery
namespace osquery {
-std::string platformStrerr(int errnum) {
- return ::strerror(errnum);
+std::string platformStrerr(int errnum)
+{
+ return ::strerror(errnum);
}
namespace impl {
-PosixError toPosixSystemError(int from_errno) {
- static auto const table = std::unordered_map<int, PosixError>{
- {EPERM, PosixError::PERM}, {ENOENT, PosixError::NOENT},
- {ESRCH, PosixError::SRCH}, {EINTR, PosixError::INTR},
- {EIO, PosixError::IO}, {ENXIO, PosixError::NXIO},
- {E2BIG, PosixError::T_BIG}, {ENOEXEC, PosixError::NOEXEC},
- {EBADF, PosixError::BADF}, {ECHILD, PosixError::CHILD},
- {EAGAIN, PosixError::AGAIN}, {ENOMEM, PosixError::NOMEM},
- {EACCES, PosixError::ACCES}, {EFAULT, PosixError::FAULT},
- {ENOTBLK, PosixError::NOTBLK}, {EBUSY, PosixError::BUSY},
- {EEXIST, PosixError::EXIST}, {EXDEV, PosixError::XDEV},
- {ENODEV, PosixError::NODEV}, {ENOTDIR, PosixError::NOTDIR},
- {EISDIR, PosixError::ISDIR}, {EINVAL, PosixError::INVAL},
- {ENFILE, PosixError::NFILE}, {EMFILE, PosixError::MFILE},
- {ENOTTY, PosixError::NOTTY}, {ETXTBSY, PosixError::TXTBSY},
- {EFBIG, PosixError::FBIG}, {ENOSPC, PosixError::NOSPC},
- {ESPIPE, PosixError::SPIPE}, {EROFS, PosixError::ROFS},
- {EMLINK, PosixError::MLINK}, {EPIPE, PosixError::PIPE},
- {EDOM, PosixError::DOM}, {ERANGE, PosixError::RANGE},
- };
- auto const it = table.find(from_errno);
- if (it == table.end()) {
- return PosixError::Unknown;
- }
- return it->second;
+PosixError toPosixSystemError(int from_errno)
+{
+ static auto const table = std::unordered_map<int, PosixError> {
+ {EPERM, PosixError::PERM}, {ENOENT, PosixError::NOENT},
+ {ESRCH, PosixError::SRCH}, {EINTR, PosixError::INTR},
+ {EIO, PosixError::IO}, {ENXIO, PosixError::NXIO},
+ {E2BIG, PosixError::T_BIG}, {ENOEXEC, PosixError::NOEXEC},
+ {EBADF, PosixError::BADF}, {ECHILD, PosixError::CHILD},
+ {EAGAIN, PosixError::AGAIN}, {ENOMEM, PosixError::NOMEM},
+ {EACCES, PosixError::ACCES}, {EFAULT, PosixError::FAULT},
+ {ENOTBLK, PosixError::NOTBLK}, {EBUSY, PosixError::BUSY},
+ {EEXIST, PosixError::EXIST}, {EXDEV, PosixError::XDEV},
+ {ENODEV, PosixError::NODEV}, {ENOTDIR, PosixError::NOTDIR},
+ {EISDIR, PosixError::ISDIR}, {EINVAL, PosixError::INVAL},
+ {ENFILE, PosixError::NFILE}, {EMFILE, PosixError::MFILE},
+ {ENOTTY, PosixError::NOTTY}, {ETXTBSY, PosixError::TXTBSY},
+ {EFBIG, PosixError::FBIG}, {ENOSPC, PosixError::NOSPC},
+ {ESPIPE, PosixError::SPIPE}, {EROFS, PosixError::ROFS},
+ {EMLINK, PosixError::MLINK}, {EPIPE, PosixError::PIPE},
+ {EDOM, PosixError::DOM}, {ERANGE, PosixError::RANGE},
+ };
+ auto const it = table.find(from_errno);
+ if (it == table.end()) {
+ return PosixError::Unknown;
+ }
+ return it->second;
}
} // namespace impl
namespace osquery {
enum class PosixError {
- Unknown = 0,
- PERM = EPERM,
- NOENT = ENOENT,
- SRCH = ESRCH,
- INTR = EINTR,
- IO = EIO,
- NXIO = ENXIO,
- T_BIG = E2BIG,
- NOEXEC = ENOEXEC,
- BADF = EBADF,
- CHILD = ECHILD,
- AGAIN = EAGAIN,
- NOMEM = ENOMEM,
- ACCES = EACCES,
- FAULT = EFAULT,
- NOTBLK = ENOTBLK,
- BUSY = EBUSY,
- EXIST = EEXIST,
- XDEV = EXDEV,
- NODEV = ENODEV,
- NOTDIR = ENOTDIR,
- ISDIR = EISDIR,
- INVAL = EINVAL,
- NFILE = ENFILE,
- MFILE = EMFILE,
- NOTTY = ENOTTY,
- TXTBSY = ETXTBSY,
- FBIG = EFBIG,
- NOSPC = ENOSPC,
- SPIPE = ESPIPE,
- ROFS = EROFS,
- MLINK = EMLINK,
- PIPE = EPIPE,
- DOM = EDOM,
- RANGE = ERANGE,
+ Unknown = 0,
+ PERM = EPERM,
+ NOENT = ENOENT,
+ SRCH = ESRCH,
+ INTR = EINTR,
+ IO = EIO,
+ NXIO = ENXIO,
+ T_BIG = E2BIG,
+ NOEXEC = ENOEXEC,
+ BADF = EBADF,
+ CHILD = ECHILD,
+ AGAIN = EAGAIN,
+ NOMEM = ENOMEM,
+ ACCES = EACCES,
+ FAULT = EFAULT,
+ NOTBLK = ENOTBLK,
+ BUSY = EBUSY,
+ EXIST = EEXIST,
+ XDEV = EXDEV,
+ NODEV = ENODEV,
+ NOTDIR = ENOTDIR,
+ ISDIR = EISDIR,
+ INVAL = EINVAL,
+ NFILE = ENFILE,
+ MFILE = EMFILE,
+ NOTTY = ENOTTY,
+ TXTBSY = ETXTBSY,
+ FBIG = EFBIG,
+ NOSPC = ENOSPC,
+ SPIPE = ESPIPE,
+ ROFS = EROFS,
+ MLINK = EMLINK,
+ PIPE = EPIPE,
+ DOM = EDOM,
+ RANGE = ERANGE,
};
namespace impl {
template <typename ToType>
inline typename std::enable_if<std::is_same<ToType, PosixError>::value,
- PosixError>::type
-to(int from_errno) {
- return impl::toPosixSystemError(from_errno);
+ PosixError>::type
+ to(int from_errno)
+{
+ return impl::toPosixSystemError(from_errno);
}
} // namespace osquery
namespace osquery {
-const std::string canonicalize_file_name(const char* name) {
+const std::string canonicalize_file_name(const char* name)
+{
#ifdef PATH_MAX
- // On supported platforms where PATH_MAX is defined we can pass null
- // as buffer, and allow libc to alloced space
- // On centos/ubuntu libc will use realloc so we will be able to resolve
- // any path
- // On darwin libc will allocate PATH_MAX buffer for us
- char* resolved = realpath(name, nullptr);
- std::string result = (resolved == nullptr) ? name : resolved;
- free(resolved);
+ // On supported platforms where PATH_MAX is defined we can pass null
+ // as buffer, and allow libc to alloced space
+ // On centos/ubuntu libc will use realloc so we will be able to resolve
+ // any path
+ // On darwin libc will allocate PATH_MAX buffer for us
+ char* resolved = realpath(name, nullptr);
+ std::string result = (resolved == nullptr) ? name : resolved;
+ free(resolved);
#else
#warning PATH_MAX is undefined, please read comment below
- // PATH_MAX is not defined, very likely it's not officially supported
- // os, our best guess is _PC_PATH_MAX if available
- // In case of failure fallback to "safe" buffer of 8K
+ // PATH_MAX is not defined, very likely it's not officially supported
+ // os, our best guess is _PC_PATH_MAX if available
+ // In case of failure fallback to "safe" buffer of 8K
- long int path_max = pathconf(name, _PC_PATH_MAX);
- if (path_max <= 0) {
- path_max = 8 * 1024;
- }
- char* buffer = static_cast<char*>(malloc(path_max));
- char* resolved = realpath(name, buffer);
- std::string result = (resolved == nullptr) ? name : resolved;
- free(buffer);
+ long int path_max = pathconf(name, _PC_PATH_MAX);
+ if (path_max <= 0) {
+ path_max = 8 * 1024;
+ }
+ char* buffer = static_cast<char*>(malloc(path_max));
+ char* resolved = realpath(name, buffer);
+ std::string result = (resolved == nullptr) ? name : resolved;
+ free(buffer);
#endif
- return result;
+ return result;
}
} // namespace osquery
class PosixErrnoTests : public testing::Test {};
-TEST_F(PosixErrnoTests, to) {
- EXPECT_EQ(PosixError::Unknown, to<PosixError>(0));
- EXPECT_EQ(PosixError::Unknown, to<PosixError>(-1));
- EXPECT_EQ(PosixError::Unknown, to<PosixError>(98765));
- EXPECT_EQ(PosixError::Unknown, to<PosixError>(987654));
-
- EXPECT_EQ(PosixError::PIPE, to<PosixError>(EPIPE));
- EXPECT_EQ(PosixError::DOM, to<PosixError>(EDOM));
- EXPECT_EQ(PosixError::RANGE, to<PosixError>(ERANGE));
- EXPECT_EQ(PosixError::T_BIG, to<PosixError>(E2BIG));
+TEST_F(PosixErrnoTests, to)
+{
+ EXPECT_EQ(PosixError::Unknown, to<PosixError>(0));
+ EXPECT_EQ(PosixError::Unknown, to<PosixError>(-1));
+ EXPECT_EQ(PosixError::Unknown, to<PosixError>(98765));
+ EXPECT_EQ(PosixError::Unknown, to<PosixError>(987654));
+
+ EXPECT_EQ(PosixError::PIPE, to<PosixError>(EPIPE));
+ EXPECT_EQ(PosixError::DOM, to<PosixError>(EDOM));
+ EXPECT_EQ(PosixError::RANGE, to<PosixError>(ERANGE));
+ EXPECT_EQ(PosixError::T_BIG, to<PosixError>(E2BIG));
}
} // namespace
namespace osquery {
-std::string platformAsctime(const struct tm* timeptr) {
- if (timeptr == nullptr) {
- return "";
- }
+std::string platformAsctime(const struct tm* timeptr)
+{
+ if (timeptr == nullptr) {
+ return "";
+ }
- // Manual says at least 26 characters.
- char buffer[32] = {0};
- return ::asctime_r(timeptr, buffer);
+ // Manual says at least 26 characters.
+ char buffer[32] = {0};
+ return ::asctime_r(timeptr, buffer);
}
} // namespace osquery
class TimeTests : public testing::Test {};
-TEST_F(TimeTests, test_asciitime) {
- const std::time_t epoch = 1491518994;
- const std::string expected = "Thu Apr 6 22:49:54 2017 UTC";
+TEST_F(TimeTests, test_asciitime)
+{
+ const std::time_t epoch = 1491518994;
+ const std::string expected = "Thu Apr 6 22:49:54 2017 UTC";
- auto const result = std::gmtime(&epoch);
+ auto const result = std::gmtime(&epoch);
- EXPECT_EQ(expected, toAsciiTime(result));
+ EXPECT_EQ(expected, toAsciiTime(result));
}
-TEST_F(TimeTests, test_asciitimeutc) {
- const std::time_t epoch = 1491518994;
- const std::string expected = "Thu Apr 6 22:49:54 2017 UTC";
+TEST_F(TimeTests, test_asciitimeutc)
+{
+ const std::time_t epoch = 1491518994;
+ const std::string expected = "Thu Apr 6 22:49:54 2017 UTC";
- auto const result = std::localtime(&epoch);
+ auto const result = std::localtime(&epoch);
- EXPECT_EQ(expected, toAsciiTimeUTC(result));
+ EXPECT_EQ(expected, toAsciiTimeUTC(result));
}
}
namespace osquery {
-std::string toAsciiTime(const struct tm* tm_time) {
- if (tm_time == nullptr) {
- return "";
- }
-
- auto time_str = platformAsctime(tm_time);
- boost::algorithm::trim(time_str);
- return time_str + " UTC";
+std::string toAsciiTime(const struct tm* tm_time)
+{
+ if (tm_time == nullptr) {
+ return "";
+ }
+
+ auto time_str = platformAsctime(tm_time);
+ boost::algorithm::trim(time_str);
+ return time_str + " UTC";
}
-std::string toAsciiTimeUTC(const struct tm* tm_time) {
- size_t epoch = toUnixTime(tm_time);
- struct tm tptr;
+std::string toAsciiTimeUTC(const struct tm* tm_time)
+{
+ size_t epoch = toUnixTime(tm_time);
+ struct tm tptr;
- std::memset(&tptr, 0, sizeof(tptr));
+ std::memset(&tptr, 0, sizeof(tptr));
- if (epoch == (size_t)-1) {
- return "";
- }
+ if (epoch == (size_t) -1) {
+ return "";
+ }
#ifdef OSQUERY_WINDOWS
- _gmtime64_s(&tptr, (time_t*)&epoch);
+ _gmtime64_s(&tptr, (time_t*)&epoch);
#else
- gmtime_r((time_t*)&epoch, &tptr);
+ gmtime_r((time_t*)&epoch, &tptr);
#endif
- return toAsciiTime(&tptr);
+ return toAsciiTime(&tptr);
}
-std::string getAsciiTime() {
- auto result = std::time(nullptr);
+std::string getAsciiTime()
+{
+ auto result = std::time(nullptr);
- struct tm now;
+ struct tm now;
#ifdef OSQUERY_WINDOWS
- _gmtime64_s(&now, &result);
+ _gmtime64_s(&now, &result);
#else
- gmtime_r(&result, &now);
+ gmtime_r(&result, &now);
#endif
- return toAsciiTime(&now);
+ return toAsciiTime(&now);
}
-size_t toUnixTime(const struct tm* tm_time) {
- struct tm result;
- std::memset(&result, 0, sizeof(result));
+size_t toUnixTime(const struct tm* tm_time)
+{
+ struct tm result;
+ std::memset(&result, 0, sizeof(result));
- std::memcpy(&result, tm_time, sizeof(result));
- return mktime(&result);
+ std::memcpy(&result, tm_time, sizeof(result));
+ return mktime(&result);
}
-size_t getUnixTime() {
- std::time_t ut = std::time(nullptr);
- return ut < 0 ? 0 : ut;
+size_t getUnixTime()
+{
+ std::time_t ut = std::time(nullptr);
+ return ut < 0 ? 0 : ut;
}
} // namespace osquery