Process Operations - osquery/core Integration (#2087)

This integrates the process abstraction operations within osquery core.
This commit is contained in:
yying 2016-05-13 22:47:45 -04:00 committed by Teddy Reed
parent 484cf9c919
commit 15d1455957
9 changed files with 190 additions and 133 deletions

View File

@ -14,9 +14,12 @@
#include <thread>
#include <stdio.h>
#include <syslog.h>
#include <time.h>
#ifndef WIN32
#include <syslog.h>
#include <unistd.h>
#endif
#include <boost/filesystem.hpp>
@ -31,6 +34,7 @@
#include <osquery/registry.h>
#include "osquery/core/watcher.h"
#include "osquery/core/process.h"
#if defined(__linux__) || defined(__FreeBSD__)
#include <sys/resource.h>
@ -97,13 +101,16 @@ enum {
namespace {
extern "C" {
static inline bool hasWorkerVariable() {
return (getenv("OSQUERY_WORKER") != nullptr);
return ::osquery::getEnvVar("OSQUERY_WORKER").is_initialized();
}
volatile std::sig_atomic_t kHandledSignal{0};
static inline bool isWatcher() { return (osquery::Watcher::getWorker() > 0); }
static inline bool isWatcher() {
return (osquery::Watcher::getWorker().isValid());
}
#ifndef WIN32
void signalHandler(int num) {
// Inform exit status of main threads blocked by service joins.
if (kHandledSignal == 0) {
@ -157,6 +164,7 @@ void signalHandler(int num) {
// managed extension processes.
}
}
#endif
}
}
@ -164,7 +172,7 @@ namespace osquery {
using chrono_clock = std::chrono::high_resolution_clock;
#ifndef __APPLE__
#if !defined(__APPLE__) && !defined(WIN32)
CLI_FLAG(bool, daemonize, false, "Run as daemon (osqueryd only)");
#endif
@ -226,7 +234,11 @@ Initializer::Initializer(int& argc, char**& argv, ToolType tool)
try {
boost::filesystem::path::codecvt();
} catch (const std::runtime_error& e) {
#ifdef WIN32
setlocale(LC_ALL, "C");
#else
setenv("LC_ALL", "C", 1);
#endif
}
// osquery implements a custom help/usage output.
@ -285,6 +297,7 @@ Initializer::Initializer(int& argc, char**& argv, ToolType tool)
}
}
#ifndef WIN32
// All tools handle the same set of signals.
// If a daemon process is a watchdog the signal is passed to the worker,
// unless the worker has not yet started.
@ -294,6 +307,7 @@ Initializer::Initializer(int& argc, char**& argv, ToolType tool)
std::signal(SIGHUP, signalHandler);
std::signal(SIGALRM, signalHandler);
std::signal(SIGUSR1, signalHandler);
#endif
// If the caller is checking configuration, disable the watchdog/worker.
if (FLAGS_config_check) {
@ -304,7 +318,8 @@ Initializer::Initializer(int& argc, char**& argv, ToolType tool)
initStatusLogger(binary_);
if (tool != OSQUERY_EXTENSION) {
if (isWorker()) {
VLOG(1) << "osquery worker initialized [watcher=" << getppid() << "]";
VLOG(1) << "osquery worker initialized [watcher="
<< PlatformProcess::getLauncherProcess()->pid() << "]";
} else {
VLOG(1) << "osquery initialized [version=" << kVersion << "]";
}
@ -319,7 +334,7 @@ void Initializer::initDaemon() const {
return;
}
#ifndef __APPLE__
#if !defined(__APPLE__) && !defined(WIN32)
// OS X uses launchd to daemonize.
if (osquery::FLAGS_daemonize) {
if (daemon(0, 0) == -1) {
@ -328,9 +343,11 @@ void Initializer::initDaemon() const {
}
#endif
#ifndef WIN32
// Print the version to SYSLOG.
syslog(
LOG_NOTICE, "%s started [version=%s]", binary_.c_str(), kVersion.c_str());
#endif
// Check if /var/osquery exists
if ((Flag::isDefault("pidfile") || Flag::isDefault("database_path")) &&
@ -350,7 +367,8 @@ void Initializer::initDaemon() const {
FLAGS_watchdog_level >= WATCHDOG_LEVEL_DEFAULT &&
FLAGS_watchdog_level != WATCHDOG_LEVEL_DEBUG) {
// Set CPU scheduling I/O limits.
setpriority(PRIO_PGRP, 0, 10);
setToBackgroundPriority();
#ifdef __linux__
// Using: ioprio_set(IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
syscall(SYS_ioprio_set, IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
@ -413,7 +431,8 @@ void Initializer::initWorker(const std::string& name) const {
// Start a 'watcher watcher' thread to exit the process if the watcher exits.
// In this case the parent process is called the 'watcher' process.
Dispatcher::addService(std::make_shared<WatcherWatcherRunner>(getppid()));
Dispatcher::addService(std::make_shared<WatcherWatcherRunner>(
PlatformProcess::getLauncherProcess()));
}
void Initializer::initWorkerWatcher(const std::string& name) const {
@ -452,7 +471,7 @@ void Initializer::initActivePlugin(const std::string& type,
}
// The plugin is not local and is not active, wait and retry.
delay += kExtensionInitializeLatencyUS;
::usleep(kExtensionInitializeLatencyUS);
sleepFor(kExtensionInitializeLatencyUS);
} while (delay < timeout);
LOG(ERROR) << "Cannot activate " << name << " " << type
@ -531,12 +550,14 @@ void Initializer::start() const {
}
initLogger(binary_);
#ifndef WIN32
// Initialize the distributed plugin, if necessary
if (!FLAGS_disable_distributed) {
if (Registry::exists("distributed", FLAGS_distributed_plugin)) {
initActivePlugin("distributed", FLAGS_distributed_plugin);
}
}
#endif
// Start event threads.
osquery::attachEvents();
@ -559,7 +580,9 @@ void Initializer::requestShutdown(int retcode) {
// Stop thrift services/clients/and their thread pools.
kExitCode = retcode;
if (std::this_thread::get_id() != kMainThreadId) {
#ifndef WIN32
raise(SIGUSR1);
#endif
} else {
// The main thread is requesting a shutdown, meaning in almost every case
// it is NOT waiting for a shutdown.
@ -572,3 +595,4 @@ void Initializer::requestShutdown(int retcode) {
void Initializer::shutdown(int retcode) { ::exit(retcode); }
}

View File

@ -56,13 +56,13 @@ std::shared_ptr<PlatformProcess> PlatformProcess::getLauncherProcess() {
}
std::shared_ptr<PlatformProcess> PlatformProcess::launchWorker(
const std::string& exec_path, const std::string& name) {
const std::string& exec_path, int argc, char** argv) {
auto worker_pid = ::fork();
if (worker_pid < 0) {
return std::shared_ptr<PlatformProcess>();
} else if (worker_pid == 0) {
setEnvVar("OSQUERY_WORKER", std::to_string(::getpid()).c_str());
::execle(exec_path.c_str(), name.c_str(), nullptr, ::environ);
::execve(exec_path.c_str(), argv, ::environ);
// Code should never reach this point
LOG(ERROR) << "osqueryd could not start worker process";

View File

@ -27,6 +27,7 @@
namespace osquery {
#ifdef WIN32
/// Unfortunately, pid_t is not defined in Windows, however, DWORD is the
/// most appropriate alternative since process ID on Windows are stored in
/// a DWORD.
@ -57,8 +58,7 @@ enum ProcessState {
* @brief Platform-agnostic process object.
*
* PlatformProcess is a specialized, platform-agnostic class that handles the
* process operation needs
* of osquery.
* process operation needs of osquery.
*/
class PlatformProcess : private boost::noncopyable {
public:
@ -101,11 +101,12 @@ class PlatformProcess : private boost::noncopyable {
/**
* @brief Creates a new worker process.
*
* Launches a worker process given a worker executable path and a worker name.
* Any double quotes in the worker name will be stripped out.
* Launches a worker process given a worker executable path, number of
* arguments, and an array of arguments. All double quotes within each entry
* in the array of arguments will be supplanted with a preceding blackslash.
*/
static std::shared_ptr<PlatformProcess> launchWorker(
const std::string& exec_path, const std::string& name);
const std::string& exec_path, int argc, char** argv);
/**
* @brief Creates a new extension process.

View File

@ -34,8 +34,10 @@ extern const char *kOsqueryTestModuleName;
/// These are the expected arguments for our test worker process.
extern const char *kExpectedWorkerArgs[];
extern const size_t kExpectedWorkerArgsCount;
/// These are the expected arguments for our test extensions process.
extern const char *kExpectedExtensionArgs[];
extern const size_t kExpectedExtensionArgsCount;
}

View File

@ -105,16 +105,20 @@ TEST_F(ProcessTests, test_getpid) {
TEST_F(ProcessTests, test_envVar) {
auto val = getEnvVar("GTEST_OSQUERY");
EXPECT_FALSE(val);
EXPECT_FALSE(val.is_initialized());
EXPECT_TRUE(setEnvVar("GTEST_OSQUERY", "true"));
val = getEnvVar("GTEST_OSQUERY");
EXPECT_FALSE(!val);
EXPECT_TRUE(val.is_initialized());
EXPECT_EQ(*val, "true");
EXPECT_TRUE(unsetEnvVar("GTEST_OSQUERY"));
val = getEnvVar("GTEST_OSQUERY");
EXPECT_FALSE(val);
EXPECT_FALSE(val.is_initialized());
}
TEST_F(ProcessTests, test_launchExtension) {
@ -136,9 +140,23 @@ TEST_F(ProcessTests, test_launchExtension) {
TEST_F(ProcessTests, test_launchWorker) {
{
std::vector<char *> argv;
for (size_t i = 0; i < kExpectedWorkerArgsCount; i++) {
char *entry = new char[strlen(kExpectedWorkerArgs[i]) + 1];
EXPECT_NE(entry, nullptr);
memset(entry, '\0', strlen(kExpectedWorkerArgs[i]) + 1);
memcpy(entry, kExpectedWorkerArgs[i], strlen(kExpectedWorkerArgs[i]));
argv.push_back(entry);
}
argv.push_back(nullptr);
std::shared_ptr<osquery::PlatformProcess> process =
osquery::PlatformProcess::launchWorker(kProcessTestExecPath.c_str(),
kExpectedWorkerArgs[0]);
osquery::PlatformProcess::launchWorker(
kProcessTestExecPath.c_str(), kExpectedWorkerArgsCount, &argv[0]);
for (size_t i = 0; i < argv.size(); i++) {
delete argv[i];
}
EXPECT_TRUE(process.get());
int code = 0;
@ -164,19 +182,6 @@ TEST_F(ProcessTests, test_launchExtensionQuotes) {
EXPECT_EQ(code, EXTENSION_SUCCESS_CODE);
}
}
TEST_F(ProcessTests, test_launchWorkerQuotes) {
{
std::shared_ptr<osquery::PlatformProcess> process =
osquery::PlatformProcess::launchWorker(kProcessTestExecPath.c_str(),
"worker\"-test");
EXPECT_TRUE(process.get());
int code = 0;
EXPECT_TRUE(getProcessExitCode(*process, code));
EXPECT_EQ(code, WORKER_SUCCESS_CODE);
}
}
#endif
}

View File

@ -12,7 +12,10 @@
#include <math.h>
#include <signal.h>
#ifndef WIN32
#include <sys/wait.h>
#endif
#include <boost/filesystem.hpp>
@ -21,6 +24,7 @@
#include <osquery/sql.h>
#include "osquery/core/watcher.h"
#include "osquery/core/process.h"
extern char** environ;
@ -70,9 +74,9 @@ void Watcher::resetExtensionCounters(const std::string& extension,
state.last_respawn_time = respawn_time;
}
std::string Watcher::getExtensionPath(pid_t child) {
std::string Watcher::getExtensionPath(const PlatformProcess& child) {
for (const auto& extension : extensions()) {
if (extension.second == child) {
if (*extension.second == child) {
return extension.first;
}
}
@ -85,8 +89,8 @@ void Watcher::removeExtensionPath(const std::string& extension) {
instance().extension_states_.erase(extension);
}
PerformanceState& Watcher::getState(pid_t child) {
if (child == instance().worker_) {
PerformanceState& Watcher::getState(const PlatformProcess& child) {
if (child == *instance().worker_) {
return instance().state_;
} else {
return instance().extension_states_[getExtensionPath(child)];
@ -97,13 +101,14 @@ PerformanceState& Watcher::getState(const std::string& extension) {
return instance().extension_states_[extension];
}
void Watcher::setExtension(const std::string& extension, pid_t child) {
void Watcher::setExtension(const std::string& extension,
const std::shared_ptr<PlatformProcess>& child) {
WatcherLocker locker;
instance().extensions_[extension] = child;
}
void Watcher::reset(pid_t child) {
if (child == instance().worker_) {
void Watcher::reset(const PlatformProcess& child) {
if (child == *instance().worker_) {
instance().worker_ = 0;
resetWorkerCounters(0);
return;
@ -111,15 +116,15 @@ void Watcher::reset(pid_t child) {
// If it was not the worker pid then find the extension name to reset.
for (const auto& extension : extensions()) {
if (extension.second == child) {
setExtension(extension.first, 0);
if (*extension.second == child) {
setExtension(extension.first, std::make_shared<PlatformProcess>());
resetExtensionCounters(extension.first, 0);
}
}
}
void Watcher::addExtensionPath(const std::string& path) {
setExtension(path, 0);
setExtension(path, std::make_shared<PlatformProcess>());
resetExtensionCounters(path, 0);
}
@ -132,7 +137,7 @@ bool Watcher::hasManagedExtensions() {
// Setting this counter to 0 will prevent the worker from waiting for missing
// dependent config plugins. Otherwise, its existence, will cause a worker to
// wait for missing plugins to broadcast from managed extensions.
return (getenv("OSQUERY_EXTENSIONS") != nullptr);
return getEnvVar("OSQUERY_EXTENSIONS").is_initialized();
}
bool WatcherRunner::ok() {
@ -142,7 +147,7 @@ bool WatcherRunner::ok() {
return false;
}
// Watcher is OK to run if a worker or at least one extension exists.
return (Watcher::getWorker() >= 0 || Watcher::hasManagedExtensions());
return (Watcher::getWorker().isValid() || Watcher::hasManagedExtensions());
}
void WatcherRunner::start() {
@ -163,7 +168,7 @@ void WatcherRunner::start() {
// Loop over every managed extension and check sanity.
std::vector<std::string> failing_extensions;
for (const auto& extension : Watcher::extensions()) {
if (!watch(extension.second)) {
if (!watch(*extension.second)) {
if (!createExtension(extension.first)) {
failing_extensions.push_back(extension.first);
}
@ -177,18 +182,19 @@ void WatcherRunner::start() {
} while (!interrupted() && ok());
}
bool WatcherRunner::watch(pid_t child) {
bool WatcherRunner::watch(const PlatformProcess& child) const {
int status = 0;
pid_t result = waitpid(child, &status, WNOHANG);
ProcessState result = checkChildProcessStatus(child, status);
if (Watcher::fatesBound()) {
// A signal was handled while the watcher was watching.
return false;
}
if (child == 0 || result < 0) {
if (!child.isValid() || result == PROCESS_ERROR) {
// Worker does not exist or never existed.
return false;
} else if (result == 0) {
} else if (result == PROCESS_STILL_ALIVE) {
// If the inspect finds problems it will stop/restart the worker.
if (!isChildSane(child)) {
stopChild(child);
@ -197,22 +203,24 @@ bool WatcherRunner::watch(pid_t child) {
return true;
}
if (WIFEXITED(status)) {
if (result == PROCESS_EXITED) {
// If the worker process existed, store the exit code.
Watcher::instance().worker_status_ = WEXITSTATUS(status);
Watcher::instance().worker_status_ = status;
}
return true;
}
void WatcherRunner::stopChild(pid_t child) {
kill(child, SIGKILL);
void WatcherRunner::stopChild(const PlatformProcess& child) const {
child.kill();
// Clean up the defunct (zombie) process.
waitpid(-1, 0, WNOHANG);
cleanupDefunctProcesses();
}
bool WatcherRunner::isChildSane(pid_t child) {
auto rows = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child));
bool WatcherRunner::isChildSane(const PlatformProcess& child) const {
auto rows =
SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child.pid()));
if (rows.size() == 0) {
// Could not find worker process?
return false;
@ -270,7 +278,7 @@ bool WatcherRunner::isChildSane(pid_t child) {
// Only make a decision about the child sanity if it is still the watcher's
// child. It's possible for the child to die, and its pid reused.
if (parent != getpid()) {
if (parent != PlatformProcess::getCurrentProcess()->pid()) {
// The child's parent is not the watcher.
Watcher::reset(child);
// Do not stop or call the child insane, since it is not our child.
@ -279,13 +287,13 @@ bool WatcherRunner::isChildSane(pid_t child) {
if (sustained_latency > 0 &&
sustained_latency * iv >= getWorkerLimit(LATENCY_LIMIT)) {
LOG(WARNING) << "osqueryd worker (" << child
LOG(WARNING) << "osqueryd worker (" << child.pid()
<< ") system performance limits exceeded";
return false;
}
// Check if the private memory exceeds a memory limit.
if (footprint > 0 && footprint > getWorkerLimit(MEMORY_LIMIT) * 1024 * 1024) {
LOG(WARNING) << "osqueryd worker (" << child
LOG(WARNING) << "osqueryd worker (" << child.pid()
<< ") memory limits exceeded: " << footprint;
return false;
}
@ -295,6 +303,7 @@ bool WatcherRunner::isChildSane(pid_t child) {
if (use_worker_) {
relayStatusLogs();
}
return true;
}
@ -315,7 +324,11 @@ void WatcherRunner::createWorker() {
}
// Get the path of the current process.
auto qd = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(getpid()));
auto qd =
SQL::selectAllFrom("processes",
"pid",
EQUALS,
INTEGER(PlatformProcess::getCurrentProcess()->pid()));
if (qd.size() != 1 || qd[0].count("path") == 0 || qd[0]["path"].size() == 0) {
LOG(ERROR) << "osquery watcher cannot determine process path for worker";
Initializer::requestShutdown(EXIT_FAILURE);
@ -325,7 +338,7 @@ void WatcherRunner::createWorker() {
// Set an environment signaling to potential plugin-dependent workers to wait
// for extensions to broadcast.
if (Watcher::hasManagedExtensions()) {
setenv("OSQUERY_EXTENSIONS", "true", 1);
setEnvVar("OSQUERY_EXTENSIONS", "true");
}
// Get the complete path of the osquery process binary.
@ -339,26 +352,18 @@ void WatcherRunner::createWorker() {
return;
}
auto worker_pid = fork();
if (worker_pid < 0) {
auto worker = PlatformProcess::launchWorker(exec_path.string(), argc_, argv_);
if (worker == nullptr) {
// Unrecoverable error, cannot create a worker process.
LOG(ERROR) << "osqueryd could not create a worker process";
Initializer::shutdown(EXIT_FAILURE);
return;
} else if (worker_pid == 0) {
// This is the new worker process, no watching needed.
setenv("OSQUERY_WORKER", std::to_string(getpid()).c_str(), 1);
execve(exec_path.string().c_str(), argv_, environ);
// Code should never reach this point.
LOG(ERROR) << "osqueryd could not start worker process";
Initializer::shutdown(EXIT_CATASTROPHIC);
return;
}
Watcher::setWorker(worker_pid);
Watcher::setWorker(worker);
Watcher::resetWorkerCounters(getUnixTime());
VLOG(1) << "osqueryd watcher (" << getpid() << ") executing worker ("
<< worker_pid << ")";
VLOG(1) << "osqueryd watcher (" << PlatformProcess::getCurrentProcess()->pid()
<< ") executing worker (" << worker->pid() << ")";
}
bool WatcherRunner::createExtension(const std::string& extension) {
@ -382,44 +387,34 @@ bool WatcherRunner::createExtension(const std::string& extension) {
return false;
}
auto ext_pid = fork();
if (ext_pid < 0) {
auto ext_process =
PlatformProcess::launchExtension(exec_path.string(),
extension,
Flag::getValue("extensions_socket"),
Flag::getValue("extensions_timeout"),
Flag::getValue("extensions_interval"),
Flag::getValue("verbose"));
if (ext_process == nullptr) {
// Unrecoverable error, cannot create an extension process.
LOG(ERROR) << "Cannot create extension process: " << extension;
Initializer::shutdown(EXIT_FAILURE);
} else if (ext_pid == 0) {
// Pass the current extension socket and a set timeout to the extension.
setenv("OSQUERY_EXTENSION", std::to_string(getpid()).c_str(), 1);
// Execute extension with very specific arguments.
execle(exec_path.string().c_str(),
("osquery extension: " + extension).c_str(),
"--socket",
Flag::getValue("extensions_socket").c_str(),
"--timeout",
Flag::getValue("extensions_timeout").c_str(),
"--interval",
Flag::getValue("extensions_interval").c_str(),
(Flag::getValue("verbose") == "true") ? "--verbose" : (char*)nullptr,
(char*)nullptr,
environ);
// Code should never reach this point.
VLOG(1) << "Could not start extension process: " << extension;
Initializer::shutdown(EXIT_FAILURE);
}
Watcher::setExtension(extension, ext_pid);
Watcher::setExtension(extension, ext_process);
Watcher::resetExtensionCounters(extension, getUnixTime());
VLOG(1) << "Created and monitoring extension child (" << ext_pid
VLOG(1) << "Created and monitoring extension child (" << ext_process->pid()
<< "): " << extension;
return true;
}
void WatcherWatcherRunner::start() {
while (!interrupted()) {
if (getppid() != watcher_) {
if (isLauncherProcessDead(*watcher_)) {
// Watcher died, the worker must follow.
VLOG(1) << "osqueryd worker (" << getpid()
<< ") detected killed watcher (" << watcher_ << ")";
VLOG(1) << "osqueryd worker ("
<< PlatformProcess::getCurrentProcess()->pid()
<< ") detected killed watcher (" << watcher_->pid() << ")";
// The watcher watcher is a thread. Do not join services after removing.
Initializer::requestShutdown();
break;
@ -443,3 +438,4 @@ size_t getWorkerLimit(WatchdogLimitType name, int level) {
return kWatchdogLimits.at(name).at(level);
}
}

View File

@ -13,13 +13,17 @@
#include <atomic>
#include <string>
#ifndef WIN32
#include <unistd.h>
#endif
#include <boost/noncopyable.hpp>
#include <osquery/dispatcher.h>
#include <osquery/flags.h>
#include "osquery/core/process.h"
/// Define a special debug/testing watchdog level.
#define WATCHDOG_LEVEL_DEBUG 3
/// Define the default watchdog level, level below are considered permissive.
@ -27,6 +31,8 @@
namespace osquery {
using ExtensionMap = std::map<std::string, std::shared_ptr<PlatformProcess>>;
DECLARE_bool(disable_watchdog);
DECLARE_int32(watchdog_level);
@ -113,12 +119,10 @@ class Watcher : private boost::noncopyable {
static void unlock() { instance().lock_.unlock(); }
/// Accessor for autoloadable extension paths.
static const std::map<std::string, pid_t>& extensions() {
return instance().extensions_;
}
static const ExtensionMap& extensions() { return instance().extensions_; }
/// Lookup extension path from pid.
static std::string getExtensionPath(pid_t child);
static std::string getExtensionPath(const PlatformProcess& child);
/// Remove an autoloadable extension path.
static void removeExtensionPath(const std::string& extension);
@ -127,20 +131,23 @@ class Watcher : private boost::noncopyable {
static void addExtensionPath(const std::string& path);
/// Get state information for a worker or extension child.
static PerformanceState& getState(pid_t child);
static PerformanceState& getState(const PlatformProcess& child);
static PerformanceState& getState(const std::string& extension);
/// Accessor for the worker process.
static pid_t getWorker() { return instance().worker_; }
static PlatformProcess& getWorker() { return *instance().worker_; }
/// Setter for worker process.
static void setWorker(pid_t child) { instance().worker_ = child; }
static void setWorker(const std::shared_ptr<PlatformProcess>& child) {
instance().worker_ = child;
}
/// Setter for an extension process.
static void setExtension(const std::string& extension, pid_t child);
static void setExtension(const std::string& extension,
const std::shared_ptr<PlatformProcess>& child);
/// Reset pid and performance counters for a worker or extension process.
static void reset(pid_t child);
static void reset(const PlatformProcess& child);
/// Count the number of worker restarts.
static size_t workerRestartCount() { return instance().worker_restarts_; }
@ -166,7 +173,9 @@ class Watcher : private boost::noncopyable {
private:
/// Do not request the lock until extensions are used.
Watcher()
: worker_(-1), worker_restarts_(0), lock_(mutex_, std::defer_lock) {}
: worker_(std::make_shared<PlatformProcess>()),
worker_restarts_(0),
lock_(mutex_, std::defer_lock) {}
Watcher(Watcher const&);
void operator=(Watcher const&);
@ -185,13 +194,13 @@ class Watcher : private boost::noncopyable {
private:
/// Keep the single worker process/thread ID for inspection.
std::atomic<int> worker_{-1};
std::shared_ptr<PlatformProcess> worker_;
/// Number of worker restarts NOT induced by a watchdog process.
size_t worker_restarts_{0};
/// Keep a list of resolved extension paths and their managed pids.
std::map<std::string, pid_t> extensions_;
ExtensionMap extensions_;
/// Paths to autoload extensions.
std::vector<std::string> extensions_paths_;
@ -255,9 +264,9 @@ class WatcherRunner : public InternalRunnable {
/// Boilerplate function to sleep for some configured latency
bool ok();
/// Begin the worker-watcher process.
bool watch(pid_t child);
bool watch(const PlatformProcess& child) const;
/// Inspect into the memory, CPU, and other worker/extension process states.
bool isChildSane(pid_t child);
bool isChildSane(const PlatformProcess& child) const;
private:
/// Fork and execute a worker process.
@ -265,7 +274,7 @@ class WatcherRunner : public InternalRunnable {
/// Fork an extension process.
bool createExtension(const std::string& extension);
/// If a worker/extension has otherwise gone insane, stop it.
void stopChild(pid_t child);
void stopChild(const PlatformProcess& child) const;
private:
/// Keep the invocation daemon's argc to iterate through argv.
@ -279,16 +288,18 @@ class WatcherRunner : public InternalRunnable {
/// The WatcherWatcher is spawned within the worker and watches the watcher.
class WatcherWatcherRunner : public InternalRunnable {
public:
explicit WatcherWatcherRunner(pid_t watcher) : watcher_(watcher) {}
explicit WatcherWatcherRunner(const std::shared_ptr<PlatformProcess>& watcher)
: watcher_(watcher) {}
/// Runnable thread's entry point.
void start();
private:
/// Parent, or watchdog, process ID.
pid_t watcher_{-1};
std::shared_ptr<PlatformProcess> watcher_;
};
/// Get a performance limit by name and optional level.
size_t getWorkerLimit(WatchdogLimitType limit, int level = -1);
}

View File

@ -79,7 +79,7 @@ std::shared_ptr<PlatformProcess> PlatformProcess::getCurrentProcess() {
HANDLE handle =
::OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
if (handle == NULL) {
return std::shared_ptr<PlatformProcess>();
return std::make_shared<PlatformProcess>();
}
return std::make_shared<PlatformProcess>(handle);
@ -88,7 +88,7 @@ std::shared_ptr<PlatformProcess> PlatformProcess::getCurrentProcess() {
std::shared_ptr<PlatformProcess> PlatformProcess::getLauncherProcess() {
auto launcher_handle = getEnvVar("OSQUERY_LAUNCHER");
if (!launcher_handle) {
return std::shared_ptr<PlatformProcess>();
return std::make_shared<PlatformProcess>();
}
// Convert the environment variable into a HANDLE (the value from environment
@ -101,26 +101,27 @@ std::shared_ptr<PlatformProcess> PlatformProcess::getLauncherProcess() {
std::stoull(*launcher_handle, nullptr, 16)));
}
catch (std::invalid_argument e) {
return std::shared_ptr<PlatformProcess>();
return std::make_shared<PlatformProcess>();
}
catch (std::out_of_range e) {
return std::shared_ptr<PlatformProcess>();
return std::make_shared<PlatformProcess>();
}
if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
return std::shared_ptr<PlatformProcess>();
return std::make_shared<PlatformProcess>();
}
return std::make_shared<PlatformProcess>(handle);
}
std::shared_ptr<PlatformProcess> PlatformProcess::launchWorker(
const std::string &exec_path, const std::string &name) {
const std::string &exec_path, int argc, char **argv) {
::STARTUPINFOA si = {0};
::PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
std::stringstream argv_stream;
std::stringstream handle_stream;
// The HANDLE exposed to the child process is currently limited to only having
@ -158,15 +159,29 @@ std::shared_ptr<PlatformProcess> PlatformProcess::launchWorker(
// Since Windows does not accept a char * array for arguments, we have to
// build one as a string. Therefore, we need to make sure that special
// characters are not present that would obstruct the parsing of arguments.
// For now, we strip out all double quotes.
// For now, we strip out all double quotes. If the an entry in argv has
// spaces, we will put double-quotes around the entry.
//
// NOTE: This is extremely naive and will break the moment complexities are
// involved... Windows command line argument parsing is extremely
// nitpicky and is different in behavior than POSIX argv parsing.
//
// We don't directly use argv.c_str() as the value for lpCommandLine in
// CreateProcess since that argument requires a modifiable buffer. So,
// instead, we off-load the contents of argv into a vector which will have its
// backing memory as modifiable.
auto argv =
std::string("\"") + boost::replace_all_copy(name, "\" ", " ") + "\"";
std::vector<char> mutable_argv(argv.begin(), argv.end());
for (size_t i = 0; i < argc; i++) {
std::string component(argv[i]);
if (component.find(" ") != std::string::npos) {
boost::replace_all(component, "\"", "\\\"");
argv_stream << "\"" << component << "\" ";
} else {
argv_stream << component << " ";
}
}
std::string cmdline = argv_stream.str();
std::vector<char> mutable_argv(cmdline.begin(), cmdline.end());
mutable_argv.push_back('\0');
BOOL status = ::CreateProcessA(exec_path.c_str(),

View File

@ -35,13 +35,18 @@ std::string kProcessTestExecPath;
const char *kOsqueryTestModuleName = "osquery_tests.exe";
/// These are the expected arguments for our test worker process.
const char *kExpectedWorkerArgs[] = {"worker-test"};
const char *kExpectedWorkerArgs[] = {"worker-test", "--socket",
"fake-socket", nullptr};
const size_t kExpectedWorkerArgsCount =
(sizeof(osquery::kExpectedWorkerArgs) / sizeof(char *)) - 1;
/// These are the expected arguments for our test extensions process.
const char *kExpectedExtensionArgs[] = {
"osquery extension: extension-test", "--socket", "socket-name",
"--timeout", "100", "--interval",
"5", "--verbose"};
"osquery extension: extension-test", "--socket", "socket-name",
"--timeout", "100", "--interval",
"5", "--verbose", nullptr};
const size_t kExpectedExtensionArgsCount =
(sizeof(osquery::kExpectedExtensionArgs) / sizeof(char *)) - 1;
static bool compareArguments(char *result[],
unsigned int result_nelms,
@ -69,8 +74,7 @@ int workerMain(int argc, char *argv[]) {
if (!osquery::compareArguments(argv,
argc,
osquery::kExpectedWorkerArgs,
sizeof(osquery::kExpectedWorkerArgs) /
sizeof(const char *))) {
osquery::kExpectedWorkerArgsCount)) {
return ERROR_COMPARE_ARGUMENT;
}
@ -106,8 +110,7 @@ int extensionMain(int argc, char *argv[]) {
if (!osquery::compareArguments(argv,
argc,
osquery::kExpectedExtensionArgs,
sizeof(osquery::kExpectedExtensionArgs) /
sizeof(const char *))) {
osquery::kExpectedExtensionArgsCount)) {
return ERROR_COMPARE_ARGUMENT;
}
return EXTENSION_SUCCESS_CODE;