mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 18:08:53 +00:00
64b98263aa
Summary: Somehow, still looking for how, D14577520 broke communication between osqueryi/osqueryd -S and extension. Revert. Reviewed By: akindyakov Differential Revision: D14620885 fbshipit-source-id: 98392f6e1e5da1b0ea68ee54dc00b3bbcd687315
779 lines
23 KiB
C++
779 lines
23 KiB
C++
/**
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed in accordance with the terms specified in
|
|
* the LICENSE file found in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <random>
|
|
#include <thread>
|
|
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <osquery/utils/system/system.h>
|
|
|
|
#include <WbemIdl.h>
|
|
#include <signal.h>
|
|
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifndef WIN32
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include "osquery/utils/config/default_paths.h"
|
|
#include "osquery/utils/info/platform_type.h"
|
|
#include <osquery/config/config.h>
|
|
#include <osquery/core.h>
|
|
#include <osquery/data_logger.h>
|
|
#include <osquery/dispatcher.h>
|
|
#include <osquery/events.h>
|
|
#include <osquery/extensions.h>
|
|
#include <osquery/filesystem/filesystem.h>
|
|
#include <osquery/flags.h>
|
|
#include <osquery/killswitch.h>
|
|
#include <osquery/numeric_monitoring.h>
|
|
#include <osquery/process/process.h>
|
|
#include <osquery/registry.h>
|
|
#include <osquery/utils/info/version.h>
|
|
#include <osquery/utils/system/time.h>
|
|
|
|
#include "osquery/core/watcher.h"
|
|
|
|
#ifdef __linux__
|
|
#include <sys/syscall.h>
|
|
|
|
/*
|
|
* These are the io priority groups as implemented by CFQ. RT is the realtime
|
|
* class, it always gets premium service. BE is the best-effort scheduling
|
|
* class, the default for any process. IDLE is the idle scheduling class, it
|
|
* is only served when no one else is using the disk.
|
|
*/
|
|
enum {
|
|
IOPRIO_CLASS_NONE,
|
|
IOPRIO_CLASS_RT,
|
|
IOPRIO_CLASS_BE,
|
|
IOPRIO_CLASS_IDLE,
|
|
};
|
|
|
|
/*
|
|
* 8 best effort priority levels are supported
|
|
*/
|
|
#define IOPRIO_BE_NR (8)
|
|
|
|
enum {
|
|
IOPRIO_WHO_PROCESS = 1,
|
|
IOPRIO_WHO_PGRP,
|
|
IOPRIO_WHO_USER,
|
|
};
|
|
#endif
|
|
|
|
#define DESCRIPTION \
|
|
"osquery %s, your OS as a high-performance relational database\n"
|
|
#define EPILOG "\nosquery project page <https://osquery.io>.\n"
|
|
#define OPTIONS \
|
|
"\nosquery configuration options (set by config or CLI flags):\n\n"
|
|
#define OPTIONS_SHELL "\nosquery shell-only CLI flags:\n\n"
|
|
#define OPTIONS_CLI "osquery%s command line flags:\n\n"
|
|
#define USAGE "Usage: %s [OPTION]... %s\n\n"
|
|
|
|
namespace osquery {
|
|
CLI_FLAG(uint64, alarm_timeout, 4, "Seconds to wait for a graceful shutdown");
|
|
CLI_FLAG(bool,
|
|
enable_signal_handler,
|
|
false,
|
|
"Enable custom osquery signals handler instead of default one");
|
|
}
|
|
|
|
namespace {
|
|
extern "C" {
|
|
static inline bool hasWorkerVariable() {
|
|
return ::osquery::getEnvVar("OSQUERY_WORKER").is_initialized();
|
|
}
|
|
|
|
volatile std::sig_atomic_t kHandledSignal{0};
|
|
|
|
static inline bool hasWorker() {
|
|
return (osquery::Watcher::get().isWorkerValid());
|
|
}
|
|
|
|
void signalHandler(int num) {
|
|
// Inform exit status of main threads blocked by service joins.
|
|
if (kHandledSignal == 0) {
|
|
kHandledSignal = num;
|
|
// If no part of osquery requested an interruption then the exit 'wanted'
|
|
// code becomes the signal number.
|
|
if (num != SIGUSR1 && osquery::kExitCode == 0) {
|
|
// The only exception is SIGUSR1 which is used to signal the main thread
|
|
// to interrupt dispatched services.
|
|
osquery::kExitCode = 128 + num;
|
|
}
|
|
|
|
// Handle signals based on a tri-state (worker, watcher, neither).
|
|
if (num == SIGHUP) {
|
|
if (!hasWorker() || hasWorkerVariable()) {
|
|
// Reload configuration.
|
|
}
|
|
} else if (num == SIGTERM || num == SIGINT || num == SIGABRT ||
|
|
num == SIGUSR1) {
|
|
#ifndef WIN32
|
|
// Time to stop, set an upper bound time constraint on how long threads
|
|
// have to terminate (join). Publishers may be in 20ms or similar sleeps.
|
|
alarm(osquery::FLAGS_alarm_timeout);
|
|
|
|
// Allow the OS to auto-reap our child processes.
|
|
std::signal(SIGCHLD, SIG_IGN);
|
|
#endif
|
|
|
|
// Restore the default signal handler.
|
|
std::signal(num, SIG_DFL);
|
|
|
|
// The watcher waits for the worker to die.
|
|
if (hasWorker()) {
|
|
// Bind the fate of the worker to this watcher.
|
|
osquery::Watcher::get().bindFates();
|
|
} else {
|
|
// Otherwise the worker or non-watched process joins.
|
|
// Stop thrift services/clients/and their thread pools.
|
|
osquery::Dispatcher::stopServices();
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef WIN32
|
|
if (num == SIGALRM) {
|
|
// Restore the default signal handler for SIGALRM.
|
|
std::signal(SIGALRM, SIG_DFL);
|
|
|
|
// Took too long to stop.
|
|
VLOG(1) << "Cannot stop event publisher threads or services";
|
|
raise((kHandledSignal != 0) ? kHandledSignal : SIGALRM);
|
|
}
|
|
#endif
|
|
|
|
if (hasWorker()) {
|
|
// The signal should be proliferated through the process group.
|
|
// Otherwise the watcher could 'forward' the signal to workers and
|
|
// managed extension processes.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
using chrono_clock = std::chrono::high_resolution_clock;
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
DECLARE_string(flagfile);
|
|
|
|
namespace osquery {
|
|
|
|
DECLARE_string(config_plugin);
|
|
DECLARE_string(logger_plugin);
|
|
DECLARE_string(numeric_monitoring_plugins);
|
|
DECLARE_string(distributed_plugin);
|
|
DECLARE_string(killswitch_plugin);
|
|
DECLARE_bool(config_check);
|
|
DECLARE_bool(config_dump);
|
|
DECLARE_bool(database_dump);
|
|
DECLARE_string(database_path);
|
|
DECLARE_bool(disable_distributed);
|
|
DECLARE_bool(disable_database);
|
|
DECLARE_bool(disable_events);
|
|
DECLARE_bool(disable_logging);
|
|
DECLARE_bool(enable_killswitch);
|
|
DECLARE_bool(enable_numeric_monitoring);
|
|
|
|
CLI_FLAG(bool, S, false, "Run as a shell process");
|
|
CLI_FLAG(bool, D, false, "Run as a daemon process");
|
|
CLI_FLAG(bool, daemonize, false, "Attempt to daemonize (POSIX only)");
|
|
|
|
FLAG(bool, ephemeral, false, "Skip pidfile and database state checks");
|
|
|
|
ToolType kToolType{ToolType::UNKNOWN};
|
|
|
|
/// The saved exit code from a thread's request to stop the process.
|
|
volatile std::sig_atomic_t kExitCode{0};
|
|
|
|
/// The saved thread ID for shutdown to short-circuit raising a signal.
|
|
static std::thread::id kMainThreadId;
|
|
|
|
/// Legacy thread ID to ensure that the windows service waits before exiting
|
|
unsigned long kLegacyThreadId;
|
|
|
|
/// When no flagfile is provided via CLI, attempt to read flag 'defaults'.
|
|
const std::string kBackupDefaultFlagfile{OSQUERY_HOME "osquery.flags.default"};
|
|
|
|
const size_t kDatabaseMaxRetryCount{25};
|
|
const size_t kDatabaseRetryDelay{200};
|
|
std::function<void()> Initializer::shutdown_{nullptr};
|
|
RecursiveMutex Initializer::shutdown_mutex_;
|
|
|
|
namespace {
|
|
|
|
void initWorkDirectories() {
|
|
if (!FLAGS_disable_database) {
|
|
auto const recursive = true;
|
|
auto const ignore_existence = true;
|
|
auto const status = createDirectory(
|
|
boost::filesystem::path(FLAGS_database_path).parent_path(),
|
|
recursive,
|
|
ignore_existence);
|
|
if (!status.ok()) {
|
|
LOG(ERROR) << "Could not initialize db directory: " << status.what();
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
static inline void printUsage(const std::string& binary, ToolType tool) {
|
|
// Parse help options before gflags. Only display osquery-related options.
|
|
fprintf(stdout, DESCRIPTION, kVersion.c_str());
|
|
if (tool == ToolType::SHELL) {
|
|
// The shell allows a caller to run a single SQL statement and exit.
|
|
fprintf(stdout, USAGE, binary.c_str(), "[SQL STATEMENT]");
|
|
} else {
|
|
fprintf(stdout, USAGE, binary.c_str(), "");
|
|
}
|
|
|
|
if (tool == ToolType::EXTENSION) {
|
|
fprintf(stdout, OPTIONS_CLI, " extension");
|
|
Flag::printFlags(false, true);
|
|
} else {
|
|
fprintf(stdout, OPTIONS_CLI, "");
|
|
Flag::printFlags(false, false, true);
|
|
fprintf(stdout, OPTIONS);
|
|
Flag::printFlags();
|
|
}
|
|
|
|
if (tool == ToolType::SHELL) {
|
|
// Print shell flags.
|
|
fprintf(stdout, OPTIONS_SHELL);
|
|
Flag::printFlags(true);
|
|
}
|
|
|
|
fprintf(stdout, EPILOG);
|
|
}
|
|
|
|
Initializer::Initializer(int& argc,
|
|
char**& argv,
|
|
ToolType tool,
|
|
bool const init_glog)
|
|
: argc_(&argc), argv_(&argv) {
|
|
// Initialize random number generated based on time.
|
|
std::srand(static_cast<unsigned int>(
|
|
chrono_clock::now().time_since_epoch().count()));
|
|
// The config holds the initialization time for easy access.
|
|
Config::setStartTime(getUnixTime());
|
|
|
|
// osquery can function as the daemon or shell depending on argv[0].
|
|
if (tool == ToolType::SHELL_DAEMON) {
|
|
if (fs::path(argv[0]).filename().string().find("osqueryd") !=
|
|
std::string::npos) {
|
|
kToolType = ToolType::DAEMON;
|
|
binary_ = "osqueryd";
|
|
} else {
|
|
kToolType = ToolType::SHELL;
|
|
binary_ = "osqueryi";
|
|
}
|
|
} else {
|
|
// Set the tool type to allow runtime decisions based on daemon, shell, etc.
|
|
kToolType = tool;
|
|
}
|
|
|
|
// The 'main' thread is that which executes the initializer.
|
|
kMainThreadId = std::this_thread::get_id();
|
|
|
|
// Maintain a legacy thread id for Windows service stops.
|
|
kLegacyThreadId = platformGetTid();
|
|
|
|
#ifndef WIN32
|
|
// Set the max number of open files.
|
|
struct rlimit nofiles;
|
|
if (getrlimit(RLIMIT_NOFILE, &nofiles) == 0) {
|
|
if (nofiles.rlim_cur < 1024 || nofiles.rlim_max < 1024) {
|
|
nofiles.rlim_cur = (nofiles.rlim_cur < 1024) ? 1024 : nofiles.rlim_cur;
|
|
nofiles.rlim_max = (nofiles.rlim_max < 1024) ? 1024 : nofiles.rlim_max;
|
|
setrlimit(RLIMIT_NOFILE, &nofiles);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Flag::create("logtostderr",
|
|
{"Log messages to stderr in addition to the logger plugin(s)",
|
|
false,
|
|
false,
|
|
true,
|
|
false});
|
|
Flag::create("stderrthreshold",
|
|
{"Stderr log level threshold", false, false, true, false});
|
|
|
|
// osquery implements a custom help/usage output.
|
|
bool default_flags = true;
|
|
for (int i = 1; i < *argc_; i++) {
|
|
auto help = std::string((*argv_)[i]);
|
|
if (help == "-S" || help == "--S") {
|
|
kToolType = ToolType::SHELL;
|
|
binary_ = "osqueryi";
|
|
} else if (help == "-D" || help == "--D") {
|
|
kToolType = ToolType::DAEMON;
|
|
binary_ = "osqueryd";
|
|
} else if ((help == "--help" || help == "-help" || help == "--h" ||
|
|
help == "-h") &&
|
|
tool != ToolType::TEST) {
|
|
printUsage(binary_, kToolType);
|
|
shutdown();
|
|
}
|
|
if (help.find("--flagfile") == 0) {
|
|
default_flags = false;
|
|
}
|
|
}
|
|
|
|
if (isShell()) {
|
|
// The shell is transient, rewrite config-loaded paths.
|
|
FLAGS_disable_logging = true;
|
|
// The shell never will not fork a worker.
|
|
FLAGS_disable_watchdog = true;
|
|
FLAGS_disable_events = true;
|
|
}
|
|
|
|
if (default_flags && isReadable(kBackupDefaultFlagfile)) {
|
|
// No flagfile was set (daemons and services always set a flagfile).
|
|
FLAGS_flagfile = kBackupDefaultFlagfile;
|
|
} else {
|
|
// No flagfile was set, but no default flags exist.
|
|
default_flags = false;
|
|
}
|
|
|
|
// Set version string from CMake build
|
|
GFLAGS_NAMESPACE::SetVersionString(kVersion.c_str());
|
|
|
|
// Let gflags parse the non-help options/flags.
|
|
GFLAGS_NAMESPACE::ParseCommandLineFlags(argc_, argv_, isShell());
|
|
|
|
// Initialize registries and plugins
|
|
registryAndPluginInit();
|
|
|
|
if (isShell() || FLAGS_ephemeral) {
|
|
if (Flag::isDefault("database_path") &&
|
|
Flag::isDefault("disable_database")) {
|
|
// The shell should not use a database by default, but should use the DB
|
|
// specified by database_path if it is set
|
|
FLAGS_disable_database = true;
|
|
}
|
|
}
|
|
|
|
if (isShell()) {
|
|
// Initialize the shell after setting modified defaults and parsing flags.
|
|
initShell();
|
|
}
|
|
if (isDaemon()) {
|
|
initWorkDirectories();
|
|
}
|
|
|
|
if (FLAGS_enable_signal_handler) {
|
|
std::signal(SIGABRT, signalHandler);
|
|
std::signal(SIGUSR1, signalHandler);
|
|
|
|
// 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.
|
|
if (!isPlatform(PlatformType::TYPE_WINDOWS)) {
|
|
std::signal(SIGTERM, signalHandler);
|
|
std::signal(SIGINT, signalHandler);
|
|
std::signal(SIGHUP, signalHandler);
|
|
std::signal(SIGALRM, signalHandler);
|
|
}
|
|
}
|
|
|
|
// If the caller is checking configuration, disable the watchdog/worker.
|
|
if (FLAGS_config_check) {
|
|
FLAGS_disable_watchdog = true;
|
|
}
|
|
|
|
if (isWatcher()) {
|
|
FLAGS_disable_database = true;
|
|
FLAGS_disable_logging = true;
|
|
}
|
|
|
|
// Initialize the status and results logger.
|
|
initStatusLogger(binary_, init_glog);
|
|
if (kToolType != ToolType::EXTENSION) {
|
|
if (isWorker()) {
|
|
VLOG(1) << "osquery worker initialized [watcher="
|
|
<< PlatformProcess::getLauncherProcess()->pid() << "]";
|
|
} else {
|
|
VLOG(1) << "osquery initialized [version=" << kVersion << "]";
|
|
}
|
|
} else {
|
|
VLOG(1) << "osquery extension initialized [sdk=" << kVersion << "]";
|
|
}
|
|
|
|
if (default_flags) {
|
|
VLOG(1) << "Using default flagfile: " << kBackupDefaultFlagfile;
|
|
}
|
|
|
|
// Initialize the COM libs
|
|
platformSetup();
|
|
}
|
|
|
|
void Initializer::initDaemon() const {
|
|
if (isWorker() || !isDaemon()) {
|
|
// The worker process (child) will not daemonize.
|
|
return;
|
|
}
|
|
|
|
if (FLAGS_config_check) {
|
|
// No need to daemonize, emit log lines, or create process mutexes.
|
|
return;
|
|
}
|
|
|
|
#if !defined(__APPLE__) && !defined(WIN32)
|
|
// OS X uses launchd to daemonize.
|
|
if (osquery::FLAGS_daemonize) {
|
|
if (daemon(0, 0) == -1) {
|
|
shutdown(EXIT_FAILURE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Print the version to the OS system log.
|
|
systemLog(binary_ + " started [version=" + kVersion + "]");
|
|
|
|
if (!FLAGS_ephemeral) {
|
|
// Create a process mutex around the daemon.
|
|
auto pid_status = createPidFile();
|
|
if (!pid_status.ok()) {
|
|
LOG(ERROR) << binary_ << " initialize failed: " << pid_status.toString();
|
|
shutdown(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// Nice ourselves if using a watchdog and the level is not too permissive.
|
|
if (!FLAGS_disable_watchdog && FLAGS_watchdog_level >= 0) {
|
|
// Set CPU scheduling I/O limits.
|
|
// On windows these values are inherited so no further calls are needed.
|
|
setToBackgroundPriority();
|
|
|
|
#ifdef __linux__
|
|
// Using: ioprio_set(IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
|
|
syscall(SYS_ioprio_set, IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
|
|
#elif defined(__APPLE__)
|
|
setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Initializer::initShell() const {
|
|
// Get the caller's home dir for temporary storage/state management.
|
|
auto homedir = osqueryHomeDirectory();
|
|
if (osquery::pathExists(homedir).ok()) {
|
|
// Only apply user/shell-specific paths if not overridden by CLI flag.
|
|
if (Flag::isDefault("database_path")) {
|
|
osquery::FLAGS_database_path =
|
|
(fs::path(homedir) / "shell.db").make_preferred().string();
|
|
}
|
|
initShellSocket(homedir);
|
|
} else {
|
|
fprintf(
|
|
stderr, "Cannot access or create osquery home: %s", homedir.c_str());
|
|
FLAGS_disable_extensions = true;
|
|
FLAGS_disable_database = true;
|
|
}
|
|
|
|
if (Flag::isDefault("hash_delay")) {
|
|
// The hash_delay is designed for daemons only.
|
|
Flag::updateValue("hash_delay", "0");
|
|
}
|
|
}
|
|
|
|
void Initializer::initWatcher() const {
|
|
// The watcher should not log into or use a persistent database.
|
|
if (isWatcher()) {
|
|
DatabasePlugin::setAllowOpen(true);
|
|
DatabasePlugin::initPlugin();
|
|
}
|
|
|
|
// The watcher takes a list of paths to autoload extensions from.
|
|
// The loadExtensions call will populate the watcher's list of extensions.
|
|
osquery::loadExtensions();
|
|
|
|
// Add a watcher service thread to start/watch an optional worker and list
|
|
// of optional extensions from the autoload paths.
|
|
if (Watcher::get().hasManagedExtensions() || !FLAGS_disable_watchdog) {
|
|
Dispatcher::addService(
|
|
std::make_shared<WatcherRunner>(*argc_, *argv_, isWatcher()));
|
|
}
|
|
|
|
if (isWatcher()) {
|
|
if (shutdown_ != nullptr) {
|
|
shutdown_();
|
|
shutdown_ = nullptr;
|
|
}
|
|
|
|
// If there are no autoloaded extensions, the watcher service will end,
|
|
// otherwise it will continue as a background thread and respawn them.
|
|
// If the watcher is also a worker watchdog it will do nothing but monitor
|
|
// the extensions and worker process.
|
|
Dispatcher::joinServices();
|
|
// Execution should only reach this point if a signal was handled by the
|
|
// worker and watcher.
|
|
auto retcode = 0;
|
|
if (kHandledSignal > 0) {
|
|
retcode = 128 + kHandledSignal;
|
|
} else if (Watcher::get().getWorkerStatus() >= 0) {
|
|
retcode = Watcher::get().getWorkerStatus();
|
|
} else {
|
|
retcode = EXIT_FAILURE;
|
|
}
|
|
requestShutdown(retcode);
|
|
}
|
|
}
|
|
|
|
void Initializer::initWorker(const std::string& name) const {
|
|
// Clear worker's arguments.
|
|
auto original_name = std::string((*argv_)[0]);
|
|
for (int i = 1; i < *argc_; i++) {
|
|
if ((*argv_)[i] != nullptr) {
|
|
memset((*argv_)[i], '\0', strlen((*argv_)[i]));
|
|
}
|
|
}
|
|
|
|
// 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>(
|
|
PlatformProcess::getLauncherProcess()));
|
|
}
|
|
|
|
void Initializer::initWorkerWatcher(const std::string& name) const {
|
|
if (isWorker()) {
|
|
initWorker(name);
|
|
} else {
|
|
// The watcher will forever monitor and spawn additional workers.
|
|
// This initialize will handle work for processes without watchdogs too.
|
|
initWatcher();
|
|
}
|
|
}
|
|
|
|
bool Initializer::isWorker() {
|
|
return hasWorkerVariable();
|
|
}
|
|
|
|
bool Initializer::isWatcher() {
|
|
return !FLAGS_disable_watchdog && !isWorker();
|
|
}
|
|
|
|
void Initializer::initActivePlugin(const std::string& type,
|
|
const std::string& name) const {
|
|
auto status = applyExtensionDelay(([type, name](bool& stop) {
|
|
auto rs = RegistryFactory::get().setActive(type, name);
|
|
if (rs.ok()) {
|
|
// The plugin was found, and is now active.
|
|
return rs;
|
|
}
|
|
|
|
if (!Watcher::get().hasManagedExtensions()) {
|
|
// The plugin must be local, and is not active, problem.
|
|
stop = true;
|
|
}
|
|
return rs;
|
|
}));
|
|
|
|
if (!status.ok()) {
|
|
LOG(ERROR) << "Cannot activate " << name << " " << type
|
|
<< " plugin: " << status.getMessage();
|
|
requestShutdown(EXIT_CATASTROPHIC);
|
|
}
|
|
}
|
|
|
|
void Initializer::installShutdown(std::function<void()>& handler) {
|
|
RecursiveLock lock(shutdown_mutex_);
|
|
shutdown_ = std::move(handler);
|
|
}
|
|
|
|
void Initializer::start() const {
|
|
// Pre-extension manager initialization options checking.
|
|
// If the shell or daemon does not need extensions and it will exit quickly,
|
|
// prefer to disable the extension manager.
|
|
if ((FLAGS_config_check || FLAGS_config_dump) &&
|
|
!Watcher::get().hasManagedExtensions()) {
|
|
FLAGS_disable_extensions = true;
|
|
}
|
|
|
|
// A watcher should not need access to the backing store.
|
|
// If there are spurious access then warning logs will be emitted since the
|
|
// set-allow-open will never be called.
|
|
if (!isWatcher()) {
|
|
DatabasePlugin::setAllowOpen(true);
|
|
// A daemon must always have R/W access to the database.
|
|
DatabasePlugin::setRequireWrite(isDaemon());
|
|
|
|
for (size_t i = 1; i <= kDatabaseMaxRetryCount; i++) {
|
|
if (DatabasePlugin::initPlugin().ok()) {
|
|
break;
|
|
}
|
|
|
|
if (i == kDatabaseMaxRetryCount) {
|
|
LOG(ERROR) << RLOG(1629) << binary_
|
|
<< " initialize failed: Could not initialize database";
|
|
auto retcode = (isWorker()) ? EXIT_CATASTROPHIC : EXIT_FAILURE;
|
|
requestShutdown(retcode);
|
|
}
|
|
|
|
sleepFor(kDatabaseRetryDelay);
|
|
}
|
|
|
|
// Ensure the database results version is up to date before proceeding
|
|
if (!upgradeDatabase()) {
|
|
LOG(ERROR) << "Failed to upgrade database";
|
|
auto retcode = (isWorker()) ? EXIT_CATASTROPHIC : EXIT_FAILURE;
|
|
requestShutdown(retcode);
|
|
}
|
|
}
|
|
|
|
// Bind to an extensions socket and wait for registry additions.
|
|
// After starting the extension manager, osquery MUST shutdown using the
|
|
// internal 'shutdown' method.
|
|
auto s = osquery::startExtensionManager();
|
|
if (!s.ok()) {
|
|
auto severity = (Watcher::get().hasManagedExtensions()) ? google::GLOG_ERROR
|
|
: google::GLOG_INFO;
|
|
if (severity == google::GLOG_INFO) {
|
|
VLOG(1) << "Cannot start extension manager: " + s.getMessage();
|
|
} else {
|
|
google::LogMessage(__FILE__, __LINE__, severity).stream()
|
|
<< "Cannot start extension manager: " + s.getMessage();
|
|
}
|
|
}
|
|
|
|
// Then set the config plugin, which uses a single/active plugin.
|
|
initActivePlugin("config", FLAGS_config_plugin);
|
|
|
|
// Run the setup for all lazy registries (tables, SQL).
|
|
Registry::setUp();
|
|
|
|
if (FLAGS_config_check) {
|
|
// The initiator requested an initialization and config check.
|
|
s = Config::get().load();
|
|
if (!s.ok()) {
|
|
std::cerr << "Error reading config: " << s.toString() << "\n";
|
|
}
|
|
// A configuration check exits the application.
|
|
// Make sure to request a shutdown as plugins may have created services.
|
|
requestShutdown(s.getCode());
|
|
}
|
|
|
|
if (FLAGS_database_dump) {
|
|
dumpDatabase();
|
|
requestShutdown();
|
|
}
|
|
|
|
// Load the osquery config using the default/active config plugin.
|
|
s = Config::get().load();
|
|
if (!s.ok()) {
|
|
auto message = "Error reading config: " + s.toString();
|
|
if (isDaemon()) {
|
|
LOG(WARNING) << message;
|
|
} else {
|
|
VLOG(1) << message;
|
|
}
|
|
}
|
|
|
|
// Initialize the status and result plugin logger.
|
|
if (!FLAGS_disable_logging) {
|
|
initActivePlugin("logger", FLAGS_logger_plugin);
|
|
initLogger(binary_);
|
|
}
|
|
|
|
// Initialize the distributed plugin, if necessary
|
|
if (!FLAGS_disable_distributed) {
|
|
initActivePlugin("distributed", FLAGS_distributed_plugin);
|
|
}
|
|
|
|
if (FLAGS_enable_killswitch) {
|
|
initActivePlugin("killswitch", FLAGS_killswitch_plugin);
|
|
}
|
|
if (FLAGS_enable_numeric_monitoring) {
|
|
initActivePlugin(monitoring::registryName(),
|
|
FLAGS_numeric_monitoring_plugins);
|
|
}
|
|
|
|
if (Killswitch::get().isAppStartMonitorEnabled()) {
|
|
monitoring::record("osquery.start", 1, monitoring::PreAggregationType::Sum);
|
|
}
|
|
|
|
// Start event threads.
|
|
osquery::attachEvents();
|
|
EventFactory::delay();
|
|
}
|
|
|
|
void Initializer::waitForShutdown() {
|
|
{
|
|
RecursiveLock lock(shutdown_mutex_);
|
|
if (shutdown_ != nullptr) {
|
|
// Copy the callable, then remove it, prevent callable recursion.
|
|
auto shutdown = shutdown_;
|
|
shutdown_ = nullptr;
|
|
|
|
// Call the shutdown callable.
|
|
shutdown();
|
|
}
|
|
}
|
|
|
|
// Attempt to be the only place in code where a join is attempted.
|
|
Dispatcher::joinServices();
|
|
// End any event type run loops.
|
|
EventFactory::end(true);
|
|
|
|
// Hopefully release memory used by global string constructors in gflags.
|
|
GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
|
|
DatabasePlugin::shutdown();
|
|
|
|
auto excode = (kExitCode != 0) ? kExitCode : EXIT_SUCCESS;
|
|
if (isWatcher()) {
|
|
platformMainThreadExit(excode);
|
|
}
|
|
exit(excode);
|
|
}
|
|
|
|
void Initializer::requestShutdown(int retcode) {
|
|
if (kExitCode == 0) {
|
|
kExitCode = retcode;
|
|
}
|
|
|
|
// Stop thrift services/clients/and their thread pools.
|
|
if (std::this_thread::get_id() != kMainThreadId &&
|
|
FLAGS_enable_signal_handler) {
|
|
raise(SIGUSR1);
|
|
} else {
|
|
// The main thread is requesting a shutdown, meaning in almost every case
|
|
// it is NOT waiting for a shutdown.
|
|
// Exceptions include: tight request / wait in an exception handler or
|
|
// custom signal handling.
|
|
Dispatcher::stopServices();
|
|
waitForShutdown();
|
|
}
|
|
}
|
|
|
|
void Initializer::requestShutdown(int retcode, const std::string& system_log) {
|
|
systemLog(system_log);
|
|
requestShutdown(retcode);
|
|
}
|
|
|
|
void Initializer::shutdown(int retcode) {
|
|
platformTeardown();
|
|
::exit(retcode);
|
|
}
|
|
}
|