osquery-1/osquery/core/init.cpp

306 lines
9.8 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
2015-01-02 05:55:10 +00:00
* 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 <syslog.h>
#include <time.h>
#include <boost/algorithm/string/trim.hpp>
#include <boost/filesystem.hpp>
2015-01-02 05:55:10 +00:00
#include <osquery/config.h>
#include <osquery/core.h>
2015-01-30 18:44:25 +00:00
#include <osquery/events.h>
2015-03-03 23:03:14 +00:00
#include <osquery/extensions.h>
#include <osquery/flags.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/registry.h>
2015-03-03 23:03:14 +00:00
#include "osquery/core/watcher.h"
2015-03-03 23:03:14 +00:00
namespace osquery {
2014-09-02 00:13:04 +00:00
2015-03-03 23:03:14 +00:00
#define DESCRIPTION \
"osquery %s, your OS as a high-performance relational database\n"
#define EPILOG "\nosquery project page <http://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"
#define CONFIG_ERROR \
"You are using default configurations for osqueryd for one or more of the " \
"following\n" \
"flags: pidfile, db_path.\n\n" \
"These options create files in /var/osquery but it looks like that path " \
"has not\n" \
"been created. Please consider explicitly defining those " \
"options as a different \n" \
"path. Additionally, review the \"using osqueryd\" wiki page:\n" \
" - https://github.com/facebook/osquery/wiki/using-osqueryd\n\n";
CLI_FLAG(bool,
config_check,
false,
"Check the format of an osquery config and exit");
#ifndef __APPLE__
2015-03-03 23:03:14 +00:00
CLI_FLAG(bool, daemonize, false, "Run as daemon (osqueryd only)");
#endif
namespace fs = boost::filesystem;
void printUsage(const std::string& binary, int tool) {
// Parse help options before gflags. Only display osquery-related options.
2015-03-03 23:03:14 +00:00
fprintf(stdout, DESCRIPTION, OSQUERY_VERSION);
if (tool == OSQUERY_TOOL_SHELL) {
// The shell allows a caller to run a single SQL statement and exit.
2015-03-03 23:03:14 +00:00
fprintf(stdout, USAGE, binary.c_str(), "[SQL STATEMENT]");
} else {
2015-03-03 23:03:14 +00:00
fprintf(stdout, USAGE, binary.c_str(), "");
}
if (tool == OSQUERY_EXTENSION) {
2015-03-03 23:03:14 +00:00
fprintf(stdout, OPTIONS_CLI, " extension");
Flag::printFlags(false, true);
} else {
2015-03-03 23:03:14 +00:00
fprintf(stdout, OPTIONS_CLI, "");
Flag::printFlags(false, false, true);
fprintf(stdout, OPTIONS);
Flag::printFlags();
}
if (tool == OSQUERY_TOOL_SHELL) {
// Print shell flags.
2015-03-03 23:03:14 +00:00
fprintf(stdout, OPTIONS_SHELL);
2015-02-17 00:26:06 +00:00
Flag::printFlags(true);
}
2015-03-03 23:03:14 +00:00
fprintf(stdout, EPILOG);
}
2015-03-19 03:47:35 +00:00
Initializer::Initializer(int& argc, char**& argv, ToolType tool)
: argc_(&argc),
argv_(&argv),
2015-03-03 23:03:14 +00:00
tool_(tool),
binary_(fs::path(std::string(argv[0])).filename().string()) {
2015-02-24 11:47:12 +00:00
std::srand(time(nullptr));
2015-01-02 05:55:10 +00:00
// osquery implements a custom help/usage output.
2015-03-19 03:47:35 +00:00
std::string first_arg = (*argc_ > 1) ? std::string((*argv_)[1]) : "";
2014-11-09 04:27:28 +00:00
if ((first_arg == "--help" || first_arg == "-h" || first_arg == "-help") &&
tool != OSQUERY_TOOL_TEST) {
2015-03-03 23:03:14 +00:00
printUsage(binary_, tool_);
::exit(0);
}
// To change the default config plugin, compile osquery with
// -DOSQUERY_DEFAULT_CONFIG_PLUGIN=<new_default_plugin>
#ifdef OSQUERY_DEFAULT_CONFIG_PLUGIN
FLAGS_config_plugin = STR(OSQUERY_DEFAULT_CONFIG_PLUGIN);
#endif
2014-10-27 01:39:03 +00:00
// To change the default logger plugin, compile osquery with
// -DOSQUERY_DEFAULT_LOGGER_PLUGIN=<new_default_plugin>
2015-02-16 02:15:06 +00:00
#ifdef OSQUERY_DEFAULT_LOGGER_PLUGIN
FLAGS_logger_plugin = STR(OSQUERY_DEFAULT_LOGGER_PLUGIN);
#endif
if (tool == OSQUERY_TOOL_SHELL) {
// The shell is transient, rewrite config-loaded paths.
osquery::FLAGS_disable_logging = true;
// Get the caller's home dir for temporary storage/state management.
2015-03-19 03:47:35 +00:00
auto homedir = osqueryHomeDirectory();
if (osquery::pathExists(homedir).ok() ||
boost::filesystem::create_directory(homedir)) {
osquery::FLAGS_database_path = homedir + "/shell.db";
osquery::FLAGS_extensions_socket = homedir + "/shell.em";
}
}
2014-11-09 00:55:19 +00:00
// Set version string from CMake build
2015-02-17 00:26:06 +00:00
GFLAGS_NAMESPACE::SetVersionString(OSQUERY_VERSION);
2014-11-09 00:55:19 +00:00
// Let gflags parse the non-help options/flags.
2015-03-19 03:47:35 +00:00
GFLAGS_NAMESPACE::ParseCommandLineFlags(
argc_, argv_, (tool == OSQUERY_TOOL_SHELL));
2014-10-27 01:39:03 +00:00
2015-03-03 23:03:14 +00:00
// If the caller is checking configuration, disable the watchdog/worker.
if (FLAGS_config_check) {
FLAGS_disable_watchdog = true;
}
2015-02-16 02:15:06 +00:00
2015-03-03 23:03:14 +00:00
// Initialize the status and results logger.
initStatusLogger(binary_);
if (tool != OSQUERY_EXTENSION) {
VLOG(1) << "osquery initialized [version=" << OSQUERY_VERSION << "]";
} else {
VLOG(1) << "osquery extension initialized [sdk=" << OSQUERY_SDK_VERSION
<< "]";
}
}
2015-03-03 23:03:14 +00:00
void Initializer::initDaemon() {
#ifndef __APPLE__
// OSX uses launchd to daemonize.
if (osquery::FLAGS_daemonize) {
if (daemon(0, 0) == -1) {
::exit(EXIT_FAILURE);
}
}
#endif
// Print the version to SYSLOG.
2015-03-03 23:03:14 +00:00
syslog(
LOG_NOTICE, "%s started [version=%s]", binary_.c_str(), OSQUERY_VERSION);
// check if /var/osquery exists
if ((Flag::isDefault("pidfile") || Flag::isDefault("db_path")) &&
!isDirectory("/var/osquery")) {
2015-03-03 23:03:14 +00:00
std::cerr << CONFIG_ERROR
}
// Create a process mutex around the daemon.
auto pid_status = createPidFile();
if (!pid_status.ok()) {
2015-03-03 23:03:14 +00:00
LOG(ERROR) << binary_ << " initialize failed: " << pid_status.toString();
::exit(EXIT_FAILURE);
}
2015-03-03 23:03:14 +00:00
}
void Initializer::initWatcher() {
// The watcher takes a list of paths to autoload extensions from.
loadExtensions();
2015-03-03 23:03:14 +00:00
// Add a watcher service thread to start/watch an optional worker and set
// of optional extensions in the autoload paths.
if (Watcher::hasManagedExtensions() || !FLAGS_disable_watchdog) {
2015-03-19 03:47:35 +00:00
Dispatcher::getInstance().addService(std::make_shared<WatcherRunner>(
*argc_, *argv_, !FLAGS_disable_watchdog));
}
2015-03-03 23:03:14 +00:00
// 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.
if (!FLAGS_disable_watchdog) {
Dispatcher::joinServices();
2015-03-03 23:03:14 +00:00
// Executation should never reach this point.
::exit(EXIT_FAILURE);
}
}
void Initializer::initWorker(const std::string& name) {
// Set the worker's process name.
2015-03-19 03:47:35 +00:00
size_t name_size = strlen((*argv_)[0]);
for (int i = 0; i < *argc_; i++) {
if ((*argv_)[i] != nullptr) {
memset((*argv_)[i], 0, strlen((*argv_)[i]));
}
}
2015-03-19 03:47:35 +00:00
strncpy((*argv_)[0], name.c_str(), name_size);
2015-03-03 23:03:14 +00:00
// Start a watcher watcher thread to exit the process if the watcher exits.
Dispatcher::getInstance().addService(
std::make_shared<WatcherWatcherRunner>(getppid()));
}
2015-03-04 16:45:21 +00:00
void Initializer::initWorkerWatcher(const std::string& name) {
if (isWorker()) {
initWorker(name);
} else {
// The watcher will forever monitor and spawn additional workers.
initWatcher();
}
}
bool Initializer::isWorker() { return (getenv("OSQUERY_WORKER") != nullptr); }
void Initializer::initConfigLogger() {
// Use a delay, meaning the amount of milliseconds waited for extensions.
size_t delay = 0;
// The timeout is the maximum time in seconds to wait for extensions.
size_t timeout = atoi(FLAGS_extensions_timeout.c_str());
while (!Registry::setActive("config", FLAGS_config_plugin)) {
// If there is at least 1 autoloaded extension, it may broadcast a route
// to the active config plugin.
if (!Watcher::hasManagedExtensions() || delay > timeout * 1000) {
LOG(ERROR) << "Config plugin not found: " << FLAGS_config_plugin;
::exit(EXIT_CATASTROPHIC);
}
::usleep(kExtensionInitializeMLatency * 1000);
delay += kExtensionInitializeMLatency;
}
// Try the same wait for a logger pluing too.
while (!Registry::setActive("logger", FLAGS_logger_plugin)) {
if (!Watcher::hasManagedExtensions() || delay > timeout * 1000) {
LOG(ERROR) << "Logger plugin not found: " << FLAGS_logger_plugin;
::exit(EXIT_CATASTROPHIC);
}
::usleep(kExtensionInitializeMLatency * 1000);
delay += kExtensionInitializeMLatency;
}
}
void Initializer::start() {
// Load registry/extension modules before extensions.
osquery::loadModules();
2015-03-03 23:03:14 +00:00
// Bind to an extensions socket and wait for registry additions.
osquery::startExtensionManager();
// Then set the config/logger plugins, which use a single/active plugin.
initConfigLogger();
// Run the setup for all lazy registries (tables, SQL).
Registry::setUp();
2015-03-03 23:03:14 +00:00
if (FLAGS_config_check) {
// The initiator requested an initialization and config check.
auto s = Config::checkConfig();
if (!s.ok()) {
std::cerr << "Error reading config: " << s.toString() << "\n";
}
// A configuration check exits the application.
::exit(s.getCode());
}
// Load the osquery config using the default/active config plugin.
Config::load();
2015-03-03 23:03:14 +00:00
// Check the backing store by allocating and exiting on error.
if (!DBHandle::checkDB()) {
LOG(ERROR) << binary_ << " initialize failed: Could not create DB handle";
if (isWorker()) {
::exit(EXIT_CATASTROPHIC);
} else {
::exit(EXIT_FAILURE);
}
}
// Initialize the status and result plugin logger.
initLogger(binary_);
// Start event threads.
osquery::attachEvents();
osquery::EventFactory::delay();
}
void Initializer::shutdown() {
// End any event type run loops.
EventFactory::end();
// Hopefully release memory used by global string constructors in gflags.
2015-02-17 00:26:06 +00:00
GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
}
2014-08-15 07:25:30 +00:00
}