osquery-1/osquery/dispatcher/scheduler.cpp
Teddy Reed 48cb4d555d Add systemLog API (#2229)
This includes a minor SDK refactor as it move quite a few specialized
functions and facilities from core.h into system.h. There was a breaking point
for needing to frequently update core includes.

The new logger systemLog function allows a call site to bypass logging config
and write a line to the OS logger (aka syslog).
2016-07-07 15:16:28 -07:00

157 lines
5.3 KiB
C++

/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include <ctime>
#include <osquery/config.h>
#include <osquery/core.h>
#include <osquery/database.h>
#include <osquery/flags.h>
#include <osquery/logger.h>
#include <osquery/system.h>
#include "osquery/config/parsers/decorators.h"
#include "osquery/core/process.h"
#include "osquery/database/query.h"
#include "osquery/dispatcher/scheduler.h"
#include "osquery/sql/sqlite_util.h"
namespace osquery {
FLAG(bool, enable_monitor, false, "Enable the schedule monitor");
FLAG(uint64, schedule_timeout, 0, "Limit the schedule, 0 for no limit")
inline SQL monitor(const std::string& name, const ScheduledQuery& query) {
// Snapshot the performance and times for the worker before running.
auto pid = std::to_string(PlatformProcess::getCurrentProcess()->pid());
auto r0 = SQL::selectAllFrom("processes", "pid", EQUALS, pid);
auto t0 = getUnixTime();
Config::getInstance().recordQueryStart(name);
auto sql = SQLInternal(query.query);
// Snapshot the performance after, and compare.
auto t1 = getUnixTime();
auto r1 = SQL::selectAllFrom("processes", "pid", EQUALS, pid);
if (r0.size() > 0 && r1.size() > 0) {
// Calculate a size as the expected byte output of results.
// This does not dedup result differentials and is not aware of snapshots.
size_t size = 0;
for (const auto& row : sql.rows()) {
for (const auto& column : row) {
size += column.first.size();
size += column.second.size();
}
}
// Always called while processes table is working.
Config::getInstance().recordQueryPerformance(name, t1 - t0, size, r0[0],
r1[0]);
}
return sql;
}
inline void launchQuery(const std::string& name, const ScheduledQuery& query) {
// Execute the scheduled query and create a named query object.
VLOG(1) << "Executing query: " << query.query;
runDecorators(DECORATE_ALWAYS);
auto sql =
(FLAGS_enable_monitor) ? monitor(name, query) : SQLInternal(query.query);
if (!sql.ok()) {
LOG(ERROR) << "Error executing query (" << query.query
<< "): " << sql.getMessageString();
return;
}
// Fill in a host identifier fields based on configuration or availability.
std::string ident = getHostIdentifier();
// A query log item contains an optional set of differential results or
// a copy of the most-recent execution alongside some query metadata.
QueryLogItem item;
item.name = name;
item.identifier = ident;
item.time = osquery::getUnixTime();
item.calendar_time = osquery::getAsciiTime();
getDecorations(item.decorations);
if (query.options.count("snapshot") && query.options.at("snapshot")) {
// This is a snapshot query, emit results with a differential or state.
item.snapshot_results = std::move(sql.rows());
logSnapshotQuery(item);
return;
}
// Create a database-backed set of query results.
auto dbQuery = Query(name, query);
// Comparisons and stores must include escaped data.
sql.escapeResults();
DiffResults diff_results;
// Add this execution's set of results to the database-tracked named query.
// We can then ask for a differential from the last time this named query
// was executed by exact matching each row.
auto status = dbQuery.addNewResults(sql.rows(), diff_results);
if (!status.ok()) {
std::string line = "Error adding new results to database: " + status.what();
LOG(ERROR) << line;
// If the database is not available then the daemon cannot continue.
Initializer::requestShutdown(EXIT_CATASTROPHIC, line);
}
if (diff_results.added.size() == 0 && diff_results.removed.size() == 0) {
// No diff results or events to emit.
return;
}
VLOG(1) << "Found results for query (" << name << ") for host: " << ident;
item.results = diff_results;
if (query.options.count("removed") && !query.options.at("removed")) {
item.results.removed.clear();
}
status = logQueryLogItem(item);
if (!status.ok()) {
LOG(ERROR) << "Error logging the results of query (" << query.query
<< "): " << status.toString();
}
}
void SchedulerRunner::start() {
// Start the counter at the second.
auto i = osquery::getUnixTime();
for (; (timeout_ == 0) || (i <= timeout_); ++i) {
Config::getInstance().scheduledQueries(
([&i](const std::string& name, const ScheduledQuery& query) {
if (query.splayed_interval > 0 && i % query.splayed_interval == 0) {
TablePlugin::kCacheInterval = query.splayed_interval;
TablePlugin::kCacheStep = i;
launchQuery(name, query);
}
}));
// Configuration decorators run on 60 second intervals only.
if (i % 60 == 0) {
runDecorators(DECORATE_INTERVAL, i);
}
// Put the thread into an interruptible sleep without a config instance.
pauseMilli(interval_ * 1000);
if (interrupted()) {
break;
}
}
}
void startScheduler() { startScheduler(FLAGS_schedule_timeout, 1); }
void startScheduler(unsigned long int timeout, size_t interval) {
Dispatcher::addService(std::make_shared<SchedulerRunner>(timeout, interval));
}
}