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"); } if (request.at("action") == "generate") { // "generate" runs the table implementation using a PluginRequest with // optional serialized QueryContext and returns the QueryData results as // the PluginRequest data. QueryContext context; if (request.count("context") > 0) { setContextFromRequest(request, context); } response = generate(context); } else if (request.at("action") == "columns") { // "columns" returns a PluginRequest filled with column information // such as name and type. const auto& column_list = columns(); for (const auto& column : column_list) { response.push_back( {{"name", column.first}, {"type", columnTypeName(column.second)}}); } } else if (request.at("action") == "definition") { response.push_back({{"definition", columnDefinition()}}); } else { return Status(1, "Unknown table plugin action: " + request.at("action")); } return Status(0, "OK"); }
std::string columnDefinition(const PluginResponse& response) { TableColumns columns; for (const auto& column : response) { columns.push_back( make_pair(column.at("name"), columnTypeName(column.at("type")))); } return columnDefinition(columns); }
PluginResponse TablePlugin::routeInfo() const { // Route info consists of only the serialized column information. PluginResponse response; for (const auto& column : columns()) { response.push_back( {{"name", column.first}, {"type", columnTypeName(column.second)}}); } return response; }
void ConstraintList::unserialize(const boost::property_tree::ptree& tree) { // Iterate through the list of operand/expressions, then set the constraint // type affinity. for (const auto& list : tree.get_child("list")) { Constraint constraint(list.second.get<unsigned char>("op")); constraint.expr = list.second.get<std::string>("expr"); constraints_.push_back(constraint); } affinity = columnTypeName(tree.get<std::string>("affinity", "UNKNOWN")); }
void ConstraintList::serialize(boost::property_tree::ptree& tree) const { boost::property_tree::ptree expressions; for (const auto& constraint : constraints_) { boost::property_tree::ptree child; child.put("op", constraint.op); child.put("expr", constraint.expr); expressions.push_back(std::make_pair("", child)); } tree.add_child("list", expressions); tree.put("affinity", columnTypeName(affinity)); }
std::string columnDefinition(const TableColumns& columns) { std::string statement = "("; for (size_t i = 0; i < columns.size(); ++i) { statement += "`" + columns.at(i).first + "` " + columnTypeName(columns.at(i).second); if (i < columns.size() - 1) { statement += ", "; } } return statement += ")"; }
Status getQueryColumnsInternal(const std::string& q, TableColumns& columns, sqlite3* db) { // Turn the query into a prepared statement sqlite3_stmt* stmt{nullptr}; auto rc = sqlite3_prepare_v2( 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(db)); } // Get column count auto num_columns = sqlite3_column_count(stmt); TableColumns results; results.reserve(num_columns); // Get column names and types Status status = Status(); 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, db); planner.applyTypes(results); } if (status.ok()) { columns = std::move(results); } sqlite3_finalize(stmt); return status; }
void ExtensionManagerHandler::getQueryColumns(ExtensionResponse& _return, const std::string& sql) { TableColumns columns; auto status = osquery::getQueryColumns(sql, columns); _return.status.code = status.getCode(); _return.status.message = status.getMessage(); _return.status.uuid = uuid_; if (status.ok()) { for (const auto& col : columns) { _return.response.push_back({{col.first, columnTypeName(col.second)}}); } } }
int xCreate(sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr) { auto *pVtab = new VirtualTable; if (!pVtab || argc == 0 || argv[0] == nullptr) { delete pVtab; return SQLITE_NOMEM; } memset(pVtab, 0, sizeof(VirtualTable)); pVtab->content = new 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]); // Get the table column information. auto status = Registry::call( "table", pVtab->content->name, {{"action", "columns"}}, response); if (!status.ok() || response.size() == 0) { delete pVtab->content; delete pVtab; return SQLITE_ERROR; } // Generate an SQL create table statement from the retrieved column details. auto statement = "CREATE TABLE " + pVtab->content->name + columnDefinition(response); int rc = sqlite3_declare_vtab(db, statement.c_str()); if (rc != SQLITE_OK || !status.ok() || response.size() == 0) { delete pVtab->content; delete pVtab; return (rc != SQLITE_OK) ? rc : SQLITE_ERROR; } // 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) { pVtab->content->columns.push_back( std::make_pair(column.at("name"), columnTypeName(column.at("type")))); } *ppVtab = (sqlite3_vtab *)pVtab; return rc; }