2014-12-18 18:50:47 +00:00
|
|
|
/*
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2014-12-18 18:50:47 +00:00
|
|
|
* 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
|
2014-12-18 18:50:47 +00:00
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
2014-08-05 23:13:55 +00:00
|
|
|
|
2015-06-04 01:09:32 +00:00
|
|
|
#include <chrono>
|
2016-03-11 08:30:20 +00:00
|
|
|
#include <iostream>
|
2015-06-04 01:09:32 +00:00
|
|
|
#include <random>
|
2016-03-19 02:37:11 +00:00
|
|
|
#include <thread>
|
2015-06-04 01:09:32 +00:00
|
|
|
|
2015-04-11 00:37:49 +00:00
|
|
|
#include <stdio.h>
|
2016-02-23 05:40:00 +00:00
|
|
|
#include <syslog.h>
|
2015-02-23 05:56:52 +00:00
|
|
|
#include <time.h>
|
2015-04-11 00:37:49 +00:00
|
|
|
#include <unistd.h>
|
2014-12-16 20:04:43 +00:00
|
|
|
|
2015-03-15 05:43:21 +00:00
|
|
|
#include <boost/filesystem.hpp>
|
2015-03-13 15:11:08 +00:00
|
|
|
|
2015-01-02 05:55:10 +00:00
|
|
|
#include <osquery/config.h>
|
2014-12-03 23:14:02 +00:00
|
|
|
#include <osquery/core.h>
|
2016-03-21 22:27:51 +00:00
|
|
|
#include <osquery/dispatcher.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>
|
2014-12-03 23:14:02 +00:00
|
|
|
#include <osquery/filesystem.h>
|
2016-02-23 05:40:00 +00:00
|
|
|
#include <osquery/flags.h>
|
2015-01-12 20:53:05 +00:00
|
|
|
#include <osquery/logger.h>
|
2014-12-03 23:14:02 +00:00
|
|
|
#include <osquery/registry.h>
|
2014-08-05 23:13:55 +00:00
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
#include "osquery/core/watcher.h"
|
2014-08-05 23:13:55 +00:00
|
|
|
|
2015-07-03 23:47:47 +00:00
|
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
2015-06-05 18:20:24 +00:00
|
|
|
#include <sys/resource.h>
|
2015-07-03 23:47:47 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __linux__
|
2015-06-05 18:20:24 +00:00
|
|
|
#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
|
|
|
|
*/
|
2015-12-04 23:41:40 +00:00
|
|
|
#define IOPRIO_BE_NR (8)
|
2015-06-05 18:20:24 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
IOPRIO_WHO_PROCESS = 1,
|
|
|
|
IOPRIO_WHO_PGRP,
|
|
|
|
IOPRIO_WHO_USER,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2016-02-09 05:50:08 +00:00
|
|
|
#ifdef __linux__
|
|
|
|
#define OSQUERY_HOME "/etc/osquery"
|
|
|
|
#else
|
|
|
|
#define OSQUERY_HOME "/var/osquery"
|
|
|
|
#endif
|
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
#define DESCRIPTION \
|
|
|
|
"osquery %s, your OS as a high-performance relational database\n"
|
2015-04-30 08:41:01 +00:00
|
|
|
#define EPILOG "\nosquery project page <https://osquery.io>.\n"
|
2015-03-03 23:03:14 +00:00
|
|
|
#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" \
|
2016-02-09 05:50:08 +00:00
|
|
|
"These options create files in " OSQUERY_HOME \
|
|
|
|
" but it looks like that path " \
|
2015-03-03 23:03:14 +00:00
|
|
|
"has not\n" \
|
|
|
|
"been created. Please consider explicitly defining those " \
|
|
|
|
"options as a different \n" \
|
|
|
|
"path. Additionally, review the \"using osqueryd\" wiki page:\n" \
|
2015-04-30 08:41:01 +00:00
|
|
|
" - https://osquery.readthedocs.org/en/latest/introduction/using-osqueryd/" \
|
|
|
|
"\n\n";
|
2015-03-03 23:03:14 +00:00
|
|
|
|
2015-09-22 05:02:27 +00:00
|
|
|
/// Seconds to alarm and quit for non-responsive event loops.
|
|
|
|
#define SIGNAL_ALARM_TIMEOUT 4
|
|
|
|
|
2015-09-02 23:53:29 +00:00
|
|
|
namespace {
|
|
|
|
extern "C" {
|
|
|
|
static inline bool hasWorkerVariable() {
|
|
|
|
return (getenv("OSQUERY_WORKER") != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
volatile std::sig_atomic_t kHandledSignal{0};
|
|
|
|
|
2016-03-05 17:29:51 +00:00
|
|
|
static inline bool isWatcher() { return (osquery::Watcher::getWorker() > 0); }
|
|
|
|
|
2016-03-19 02:37:11 +00:00
|
|
|
void signalHandler(int num) {
|
2015-09-02 23:53:29 +00:00
|
|
|
// Inform exit status of main threads blocked by service joins.
|
2015-09-22 05:02:27 +00:00
|
|
|
if (kHandledSignal == 0) {
|
2016-03-19 02:37:11 +00:00
|
|
|
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;
|
2015-09-02 23:53:29 +00:00
|
|
|
}
|
2016-03-19 02:37:11 +00:00
|
|
|
|
|
|
|
// Handle signals based on a tri-state (worker, watcher, neither).
|
|
|
|
if (num == SIGHUP) {
|
|
|
|
if (!isWatcher() || hasWorkerVariable()) {
|
|
|
|
// Reload configuration.
|
|
|
|
}
|
|
|
|
} else if (num == SIGTERM || num == SIGINT || num == SIGABRT ||
|
|
|
|
num == SIGUSR1) {
|
|
|
|
// 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(SIGNAL_ALARM_TIMEOUT);
|
|
|
|
|
|
|
|
// Restore the default signal handler.
|
|
|
|
std::signal(num, SIG_DFL);
|
|
|
|
|
|
|
|
// The watcher waits for the worker to die.
|
|
|
|
if (isWatcher()) {
|
|
|
|
// Bind the fate of the worker to this watcher.
|
|
|
|
osquery::Watcher::bindFates();
|
|
|
|
} else {
|
|
|
|
// Otherwise the worker or non-watched process joins.
|
|
|
|
// Stop thrift services/clients/and their thread pools.
|
|
|
|
osquery::Dispatcher::stopServices();
|
|
|
|
}
|
2015-09-02 23:53:29 +00:00
|
|
|
}
|
2016-03-19 02:37:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (num == SIGALRM) {
|
2015-09-22 05:02:27 +00:00
|
|
|
// Restore the default signal handler for SIGALRM.
|
|
|
|
std::signal(SIGALRM, SIG_DFL);
|
|
|
|
|
2015-09-02 23:53:29 +00:00
|
|
|
// Took too long to stop.
|
2016-03-19 02:37:11 +00:00
|
|
|
VLOG(1) << "Cannot stop event publisher threads or services";
|
2015-09-22 05:02:27 +00:00
|
|
|
raise((kHandledSignal != 0) ? kHandledSignal : SIGALRM);
|
2015-09-02 23:53:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-05 17:29:51 +00:00
|
|
|
if (isWatcher()) {
|
2015-09-02 23:53:29 +00:00
|
|
|
// The signal should be proliferated through the process group.
|
|
|
|
// Otherwise the watcher could 'forward' the signal to workers and
|
|
|
|
// managed extension processes.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
|
2015-11-27 00:35:22 +00:00
|
|
|
using chrono_clock = std::chrono::high_resolution_clock;
|
2015-06-04 01:09:32 +00:00
|
|
|
|
2015-02-06 17:42:03 +00:00
|
|
|
#ifndef __APPLE__
|
2015-03-03 23:03:14 +00:00
|
|
|
CLI_FLAG(bool, daemonize, false, "Run as daemon (osqueryd only)");
|
2015-02-06 17:42:03 +00:00
|
|
|
#endif
|
|
|
|
|
2015-09-07 18:09:06 +00:00
|
|
|
DECLARE_string(distributed_plugin);
|
2015-10-02 18:33:50 +00:00
|
|
|
DECLARE_bool(disable_distributed);
|
2015-12-04 23:41:40 +00:00
|
|
|
DECLARE_string(config_plugin);
|
|
|
|
DECLARE_bool(config_check);
|
2016-03-05 17:29:51 +00:00
|
|
|
DECLARE_bool(config_dump);
|
2015-12-04 23:41:40 +00:00
|
|
|
DECLARE_bool(database_dump);
|
2016-03-05 17:29:51 +00:00
|
|
|
DECLARE_string(database_path);
|
2015-09-07 18:09:06 +00:00
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
ToolType kToolType = OSQUERY_TOOL_UNKNOWN;
|
|
|
|
|
2016-03-19 02:37:11 +00:00
|
|
|
volatile std::sig_atomic_t kExitCode{0};
|
|
|
|
|
|
|
|
/// The saved thread ID for shutdown to short-circuit raising a signal.
|
|
|
|
static std::thread::id kMainThreadId;
|
|
|
|
|
2014-12-16 20:04:43 +00:00
|
|
|
void printUsage(const std::string& binary, int tool) {
|
|
|
|
// Parse help options before gflags. Only display osquery-related options.
|
2015-06-12 01:13:37 +00:00
|
|
|
fprintf(stdout, DESCRIPTION, kVersion.c_str());
|
2014-12-16 20:04:43 +00:00
|
|
|
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]");
|
2014-12-16 20:04:43 +00:00
|
|
|
} else {
|
2015-03-03 23:03:14 +00:00
|
|
|
fprintf(stdout, USAGE, binary.c_str(), "");
|
2015-02-25 04:29:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tool == OSQUERY_EXTENSION) {
|
2015-03-03 23:03:14 +00:00
|
|
|
fprintf(stdout, OPTIONS_CLI, " extension");
|
2015-02-25 04:29:57 +00:00
|
|
|
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);
|
2015-02-25 04:29:57 +00:00
|
|
|
Flag::printFlags();
|
|
|
|
}
|
2014-12-16 20:04:43 +00:00
|
|
|
|
|
|
|
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);
|
2014-12-16 20:04:43 +00:00
|
|
|
}
|
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
fprintf(stdout, EPILOG);
|
2014-09-09 22:35:34 +00:00
|
|
|
}
|
|
|
|
|
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),
|
2015-11-27 00:35:22 +00:00
|
|
|
binary_((tool == OSQUERY_TOOL_DAEMON) ? "osqueryd" : "osqueryi") {
|
2015-06-04 01:09:32 +00:00
|
|
|
std::srand(chrono_clock::now().time_since_epoch().count());
|
2016-03-19 02:37:11 +00:00
|
|
|
// The 'main' thread is that which executes the initializer.
|
|
|
|
kMainThreadId = std::this_thread::get_id();
|
2014-10-27 18:55:28 +00:00
|
|
|
|
2015-10-13 16:55:44 +00:00
|
|
|
// Handled boost filesystem locale problems fixes in 1.56.
|
|
|
|
// See issue #1559 for the discussion and upstream boost patch.
|
|
|
|
try {
|
|
|
|
boost::filesystem::path::codecvt();
|
2015-11-27 00:35:22 +00:00
|
|
|
} catch (const std::runtime_error& e) {
|
2015-10-13 16:55:44 +00:00
|
|
|
setenv("LC_ALL", "C", 1);
|
|
|
|
}
|
|
|
|
|
2015-01-02 05:55:10 +00:00
|
|
|
// osquery implements a custom help/usage output.
|
2015-04-30 01:53:25 +00:00
|
|
|
for (int i = 1; i < *argc_; i++) {
|
|
|
|
auto help = std::string((*argv_)[i]);
|
|
|
|
if ((help == "--help" || help == "-help" || help == "--h" ||
|
|
|
|
help == "-h") &&
|
|
|
|
tool != OSQUERY_TOOL_TEST) {
|
|
|
|
printUsage(binary_, tool_);
|
2016-03-19 02:37:11 +00:00
|
|
|
shutdown();
|
2015-04-30 01:53:25 +00:00
|
|
|
}
|
2014-09-09 22:35:34 +00:00
|
|
|
}
|
|
|
|
|
2015-02-24 21:19:37 +00:00
|
|
|
// To change the default config plugin, compile osquery with
|
|
|
|
// -DOSQUERY_DEFAULT_CONFIG_PLUGIN=<new_default_plugin>
|
2015-01-26 18:44:27 +00:00
|
|
|
#ifdef OSQUERY_DEFAULT_CONFIG_PLUGIN
|
2015-02-05 00:54:44 +00:00
|
|
|
FLAGS_config_plugin = STR(OSQUERY_DEFAULT_CONFIG_PLUGIN);
|
2015-01-26 18:44:27 +00:00
|
|
|
#endif
|
2014-10-27 01:39:03 +00:00
|
|
|
|
2015-02-24 21:19:37 +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
|
|
|
|
|
2015-04-11 00:37:49 +00:00
|
|
|
// Set version string from CMake build
|
2015-06-12 01:13:37 +00:00
|
|
|
GFLAGS_NAMESPACE::SetVersionString(kVersion.c_str());
|
2015-04-11 00:37:49 +00:00
|
|
|
|
|
|
|
// Let gflags parse the non-help options/flags.
|
|
|
|
GFLAGS_NAMESPACE::ParseCommandLineFlags(
|
|
|
|
argc_, argv_, (tool == OSQUERY_TOOL_SHELL));
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
// Set the tool type to allow runtime decisions based on daemon, shell, etc.
|
|
|
|
kToolType = tool;
|
2015-03-15 05:43:21 +00:00
|
|
|
if (tool == OSQUERY_TOOL_SHELL) {
|
|
|
|
// The shell is transient, rewrite config-loaded paths.
|
2015-04-27 23:40:05 +00:00
|
|
|
FLAGS_disable_logging = true;
|
2016-02-09 05:50:08 +00:00
|
|
|
// The shell never will not fork a worker.
|
|
|
|
FLAGS_disable_watchdog = true;
|
2015-03-15 05:43:21 +00:00
|
|
|
// Get the caller's home dir for temporary storage/state management.
|
2015-03-19 03:47:35 +00:00
|
|
|
auto homedir = osqueryHomeDirectory();
|
2016-02-04 21:43:24 +00:00
|
|
|
boost::system::error_code ec;
|
2015-03-15 05:43:21 +00:00
|
|
|
if (osquery::pathExists(homedir).ok() ||
|
2016-02-04 21:43:24 +00:00
|
|
|
boost::filesystem::create_directory(homedir, ec)) {
|
2015-06-02 04:49:21 +00:00
|
|
|
// Only apply user/shell-specific paths if not overridden by CLI flag.
|
|
|
|
if (Flag::isDefault("database_path")) {
|
|
|
|
osquery::FLAGS_database_path = homedir + "/shell.db";
|
|
|
|
}
|
2015-06-22 07:55:17 +00:00
|
|
|
if (Flag::isDefault("extensions_socket")) {
|
2015-06-02 04:49:21 +00:00
|
|
|
osquery::FLAGS_extensions_socket = homedir + "/shell.em";
|
|
|
|
}
|
2016-02-04 21:43:24 +00:00
|
|
|
} else {
|
|
|
|
LOG(INFO) << "Cannot access or create osquery home directory";
|
|
|
|
FLAGS_disable_extensions = true;
|
|
|
|
FLAGS_database_path = "/dev/null";
|
2015-03-15 05:43:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-02 23:53:29 +00:00
|
|
|
// 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.
|
|
|
|
std::signal(SIGTERM, signalHandler);
|
|
|
|
std::signal(SIGABRT, signalHandler);
|
|
|
|
std::signal(SIGINT, signalHandler);
|
|
|
|
std::signal(SIGHUP, signalHandler);
|
2015-09-22 05:02:27 +00:00
|
|
|
std::signal(SIGALRM, signalHandler);
|
2016-03-19 02:37:11 +00:00
|
|
|
std::signal(SIGUSR1, signalHandler);
|
2015-09-02 23:53:29 +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-06 17:42:03 +00:00
|
|
|
}
|
2015-02-16 02:15:06 +00:00
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
// Initialize the status and results logger.
|
|
|
|
initStatusLogger(binary_);
|
2015-03-13 15:11:08 +00:00
|
|
|
if (tool != OSQUERY_EXTENSION) {
|
2015-04-30 01:53:25 +00:00
|
|
|
if (isWorker()) {
|
2015-11-02 08:23:11 +00:00
|
|
|
VLOG(1) << "osquery worker initialized [watcher=" << getppid() << "]";
|
2015-04-30 01:53:25 +00:00
|
|
|
} else {
|
2015-06-12 01:13:37 +00:00
|
|
|
VLOG(1) << "osquery initialized [version=" << kVersion << "]";
|
2015-04-30 01:53:25 +00:00
|
|
|
}
|
2015-03-13 15:11:08 +00:00
|
|
|
} else {
|
2015-06-12 01:13:37 +00:00
|
|
|
VLOG(1) << "osquery extension initialized [sdk=" << kSDKVersion << "]";
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
2015-02-06 17:42:03 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
void Initializer::initDaemon() const {
|
2015-04-02 20:31:51 +00:00
|
|
|
if (FLAGS_config_check) {
|
|
|
|
// No need to daemonize, emit log lines, or create process mutexes.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-06 17:42:03 +00:00
|
|
|
#ifndef __APPLE__
|
2015-07-01 22:26:26 +00:00
|
|
|
// OS X uses launchd to daemonize.
|
2015-02-06 17:42:03 +00:00
|
|
|
if (osquery::FLAGS_daemonize) {
|
|
|
|
if (daemon(0, 0) == -1) {
|
2016-03-19 02:37:11 +00:00
|
|
|
shutdown(EXIT_FAILURE);
|
2015-02-06 17:42:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Print the version to SYSLOG.
|
2015-03-03 23:03:14 +00:00
|
|
|
syslog(
|
2015-06-12 01:13:37 +00:00
|
|
|
LOG_NOTICE, "%s started [version=%s]", binary_.c_str(), kVersion.c_str());
|
2015-02-06 17:42:03 +00:00
|
|
|
|
2015-04-24 08:44:41 +00:00
|
|
|
// Check if /var/osquery exists
|
|
|
|
if ((Flag::isDefault("pidfile") || Flag::isDefault("database_path")) &&
|
2016-02-09 05:50:08 +00:00
|
|
|
!isDirectory(OSQUERY_HOME)) {
|
2015-09-02 23:53:29 +00:00
|
|
|
std::cerr << CONFIG_ERROR;
|
2015-02-24 21:19:37 +00:00
|
|
|
}
|
|
|
|
|
2015-02-06 17:42:03 +00:00
|
|
|
// 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();
|
2016-03-19 02:37:11 +00:00
|
|
|
shutdown(EXIT_FAILURE);
|
2015-02-06 17:42:03 +00:00
|
|
|
}
|
2015-06-05 18:20:24 +00:00
|
|
|
|
|
|
|
// Nice ourselves if using a watchdog and the level is not too permissive.
|
|
|
|
if (!FLAGS_disable_watchdog &&
|
|
|
|
FLAGS_watchdog_level >= WATCHDOG_LEVEL_DEFAULT &&
|
|
|
|
FLAGS_watchdog_level != WATCHDOG_LEVEL_DEBUG) {
|
2015-07-01 22:26:26 +00:00
|
|
|
// Set CPU scheduling I/O limits.
|
2015-06-05 18:20:24 +00:00
|
|
|
setpriority(PRIO_PGRP, 0, 10);
|
|
|
|
#ifdef __linux__
|
|
|
|
// Using: ioprio_set(IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
|
|
|
|
syscall(SYS_ioprio_set, IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
|
2015-07-03 23:47:47 +00:00
|
|
|
#elif defined(__APPLE__)
|
2015-06-05 18:20:24 +00:00
|
|
|
setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
|
|
|
|
#endif
|
|
|
|
}
|
2015-03-03 23:03:14 +00:00
|
|
|
}
|
2015-02-06 17:42:03 +00:00
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
void Initializer::initWatcher() const {
|
2015-03-13 15:11:08 +00:00
|
|
|
// The watcher takes a list of paths to autoload extensions from.
|
2016-02-09 05:50:08 +00:00
|
|
|
// The loadExtensions call will populate the watcher's list of extensions.
|
2015-04-24 08:44:41 +00:00
|
|
|
osquery::loadExtensions();
|
2015-03-03 23:03:14 +00:00
|
|
|
|
2016-02-09 05:50:08 +00:00
|
|
|
// Add a watcher service thread to start/watch an optional worker and list
|
|
|
|
// of optional extensions from the autoload paths.
|
2015-03-17 07:29:23 +00:00
|
|
|
if (Watcher::hasManagedExtensions() || !FLAGS_disable_watchdog) {
|
2015-05-04 03:02:01 +00:00
|
|
|
Dispatcher::addService(std::make_shared<WatcherRunner>(
|
2015-03-19 03:47:35 +00:00
|
|
|
*argc_, *argv_, !FLAGS_disable_watchdog));
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
2015-03-03 23:03:14 +00:00
|
|
|
|
2015-03-13 15:11:08 +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-09-02 23:53:29 +00:00
|
|
|
// Execution should only reach this point if a signal was handled by the
|
|
|
|
// worker and watcher.
|
2016-02-05 03:12:48 +00:00
|
|
|
auto retcode = 0;
|
|
|
|
if (kHandledSignal > 0) {
|
|
|
|
retcode = 128 + kHandledSignal;
|
|
|
|
} else if (Watcher::getWorkerStatus() >= 0) {
|
|
|
|
retcode = Watcher::getWorkerStatus();
|
|
|
|
} else {
|
|
|
|
retcode = EXIT_FAILURE;
|
|
|
|
}
|
2016-03-19 02:37:11 +00:00
|
|
|
requestShutdown(retcode);
|
2015-02-06 17:42:03 +00:00
|
|
|
}
|
2014-08-05 23:13:55 +00:00
|
|
|
}
|
2015-02-01 08:35:44 +00:00
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
void Initializer::initWorker(const std::string& name) const {
|
2015-05-04 03:02:01 +00:00
|
|
|
// Clear worker's arguments.
|
2015-05-04 21:15:19 +00:00
|
|
|
size_t name_size = strlen((*argv_)[0]);
|
2015-06-12 03:20:53 +00:00
|
|
|
auto original_name = std::string((*argv_)[0]);
|
2015-03-19 03:47:35 +00:00
|
|
|
for (int i = 0; i < *argc_; i++) {
|
|
|
|
if ((*argv_)[i] != nullptr) {
|
2015-06-12 03:20:53 +00:00
|
|
|
memset((*argv_)[i], ' ', strlen((*argv_)[i]));
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-04 03:02:01 +00:00
|
|
|
|
|
|
|
// Set the worker's process name.
|
2015-06-12 03:20:53 +00:00
|
|
|
if (name.size() < name_size) {
|
2015-05-04 03:02:01 +00:00
|
|
|
std::copy(name.begin(), name.end(), (*argv_)[0]);
|
2015-06-12 03:20:53 +00:00
|
|
|
(*argv_)[0][name.size()] = '\0';
|
|
|
|
} else {
|
|
|
|
std::copy(original_name.begin(), original_name.end(), (*argv_)[0]);
|
|
|
|
(*argv_)[0][original_name.size()] = '\0';
|
2015-05-04 03:02:01 +00:00
|
|
|
}
|
2015-03-03 23:03:14 +00:00
|
|
|
|
2016-02-09 05:50:08 +00:00
|
|
|
// Start a 'watcher watcher' thread to exit the process if the watcher exits.
|
|
|
|
// In this case the parent process is called the 'watcher' process.
|
2015-05-04 03:02:01 +00:00
|
|
|
Dispatcher::addService(std::make_shared<WatcherWatcherRunner>(getppid()));
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
2015-03-04 16:45:21 +00:00
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
void Initializer::initWorkerWatcher(const std::string& name) const {
|
2015-03-13 15:11:08 +00:00
|
|
|
if (isWorker()) {
|
|
|
|
initWorker(name);
|
|
|
|
} else {
|
|
|
|
// The watcher will forever monitor and spawn additional workers.
|
|
|
|
initWatcher();
|
2015-03-08 21:52:13 +00:00
|
|
|
}
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
|
|
|
|
2015-09-02 23:53:29 +00:00
|
|
|
bool Initializer::isWorker() { return hasWorkerVariable(); }
|
2015-03-08 21:52:13 +00:00
|
|
|
|
2015-04-30 01:53:25 +00:00
|
|
|
void Initializer::initActivePlugin(const std::string& type,
|
2016-02-05 03:12:48 +00:00
|
|
|
const std::string& name) const {
|
2015-03-13 15:11:08 +00:00
|
|
|
// Use a delay, meaning the amount of milliseconds waited for extensions.
|
|
|
|
size_t delay = 0;
|
2015-06-25 08:35:51 +00:00
|
|
|
// The timeout is the maximum microseconds in seconds to wait for extensions.
|
|
|
|
size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000;
|
|
|
|
if (timeout < kExtensionInitializeLatencyUS * 10) {
|
|
|
|
timeout = kExtensionInitializeLatencyUS * 10;
|
|
|
|
}
|
2016-02-09 05:50:08 +00:00
|
|
|
|
2016-03-05 17:29:51 +00:00
|
|
|
// Attempt to set the request plugin as active.
|
|
|
|
Status status;
|
|
|
|
do {
|
|
|
|
status = Registry::setActive(type, name);
|
|
|
|
if (status.ok()) {
|
|
|
|
// The plugin was found, and is not active.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Watcher::hasManagedExtensions()) {
|
|
|
|
// The plugin was found locally, and is not active, problem.
|
|
|
|
break;
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
2016-03-05 17:29:51 +00:00
|
|
|
// The plugin is not local and is not active, wait and retry.
|
2015-06-25 08:35:51 +00:00
|
|
|
delay += kExtensionInitializeLatencyUS;
|
|
|
|
::usleep(kExtensionInitializeLatencyUS);
|
2016-03-05 17:29:51 +00:00
|
|
|
} while (delay < timeout);
|
|
|
|
|
|
|
|
LOG(ERROR) << "Cannot activate " << name << " " << type
|
|
|
|
<< " plugin: " << status.getMessage();
|
2016-03-19 02:37:11 +00:00
|
|
|
requestShutdown(EXIT_CATASTROPHIC);
|
2015-03-13 15:11:08 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 03:12:48 +00:00
|
|
|
void Initializer::start() const {
|
2015-03-13 15:11:08 +00:00
|
|
|
// Load registry/extension modules before extensions.
|
|
|
|
osquery::loadModules();
|
2015-03-08 21:52:13 +00:00
|
|
|
|
2015-05-11 08:21:57 +00:00
|
|
|
// Pre-extension manager initialization options checking.
|
2016-03-05 17:29:51 +00:00
|
|
|
// 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::hasManagedExtensions()) {
|
2015-05-11 08:21:57 +00:00
|
|
|
FLAGS_disable_extensions = true;
|
|
|
|
}
|
|
|
|
|
2016-03-05 17:29:51 +00:00
|
|
|
// 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(tool_ == OSQUERY_TOOL_DAEMON);
|
|
|
|
if (!DatabasePlugin::initPlugin()) {
|
|
|
|
LOG(ERROR) << RLOG(1629) << binary_
|
|
|
|
<< " initialize failed: Could not initialize database";
|
|
|
|
auto retcode = (isWorker()) ? EXIT_CATASTROPHIC : EXIT_FAILURE;
|
2016-03-19 02:37:11 +00:00
|
|
|
requestShutdown(retcode);
|
2016-03-05 17:29:51 +00:00
|
|
|
}
|
2015-06-02 04:49:21 +00:00
|
|
|
}
|
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
// Bind to an extensions socket and wait for registry additions.
|
2016-02-05 03:12:48 +00:00
|
|
|
// After starting the extension manager, osquery MUST shutdown using the
|
|
|
|
// internal 'shutdown' method.
|
2015-03-03 23:03:14 +00:00
|
|
|
osquery::startExtensionManager();
|
|
|
|
|
2015-04-30 01:53:25 +00:00
|
|
|
// Then set the config plugin, which uses a single/active plugin.
|
|
|
|
initActivePlugin("config", FLAGS_config_plugin);
|
2015-03-13 15:11:08 +00:00
|
|
|
|
|
|
|
// 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.
|
[fix #1390] query pack re-org
This commit contains the features specified in #1390 as well as a
refactoring of the general osquery configuration code.
The API for the config plugins hasn't changed, although now there's a
`genPack` method that config plugins can implement. If a plugin doesn't
implement `genPack`, then the map<string, string> format cannot be used.
The default config plugin, the filesystem plugin, now implements
`genPack`, so existing query packs code will continue to work as it
always has.
Now many other config plugins can implement custom pack handling for
what makes sense in their context. `genPacks` is not a pure virtual, so
it doesn't have to be implemented in your plugin if you don't want to
use it. Also, more importantly, all config plugins can use the standard
inline pack format if they want to use query packs. Which is awesome.
For more information, refer to #1390, the documentation and the doxygen
comments included with this pull requests, as well as the following
example config which is now supported, regardless of what config plugin
you're using:
```json
{
"options": {
"enable_monitor": "true"
},
"packs": {
"core_os_monitoring": {
"version": "1.4.5",
"discovery": [
"select pid from processes where name like '%osqueryd%';"
],
"queries": {
"kernel_modules": {
"query": "SELECT name, size FROM kernel_modules;",
"interval": 600
},
"system_controls": {
"query": "SELECT * FROM system_controls;",
"interval": 600,
"snapshot": true,
},
"usb_devices": {
"query": "SELECT * FROM usb_devices;",
"interval": 600
}
}
},
"osquery_internal_info": {
"version": "1.4.5",
"discovery": [
"select pid from processes where name like '%osqueryd%';"
],
"queries": {
"info": {
"query": "select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid;",
"interval": 60,
"snapshot": true
},
"registry": {
"query": "SELECT * FROM osquery_registry;",
"interval": 600,
"snapshot": true
},
"schedule": {
"query": "select name, interval, executions, output_size, wall_time, (user_time/executions) as avg_user_time, (system_time/executions) as avg_system_time, average_memory from osquery_schedule;",
"interval": 60,
"snapshot": true
}
}
}
}
}
```
The `osquery_packs` table was modified to remove the superfluous
columns which could already have been found in `osquery_schedule`. Two
more columns were added in their place, representing stats about pack's
discovery query execution history.
Notably, the internal API for the `osquery::Config` class has changed
rather dramatically as apart of the refactoring. We think this is an
improvement. While strictly adhering to the osquery config plugin
interface will have avoided any compatibility errors, advanced users may
notice compilation errors if they access config data directly. All
internal users of the config have obviously been updated. Yet another
reason to merge your code into mainline; we update it for you when we
refactor!
2015-08-19 20:27:49 +00:00
|
|
|
auto s = Config::getInstance().load();
|
2015-03-03 23:03:14 +00:00
|
|
|
if (!s.ok()) {
|
|
|
|
std::cerr << "Error reading config: " << s.toString() << "\n";
|
|
|
|
}
|
|
|
|
// A configuration check exits the application.
|
2016-03-19 02:37:11 +00:00
|
|
|
// Make sure to request a shutdown as plugins may have created services.
|
|
|
|
requestShutdown(s.getCode());
|
2015-03-03 23:03:14 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 23:41:40 +00:00
|
|
|
if (FLAGS_database_dump) {
|
|
|
|
dumpDatabase();
|
2016-03-19 02:37:11 +00:00
|
|
|
requestShutdown();
|
2015-12-04 23:41:40 +00:00
|
|
|
}
|
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
// Load the osquery config using the default/active config plugin.
|
2015-09-03 18:43:36 +00:00
|
|
|
auto s = Config::getInstance().load();
|
|
|
|
if (!s.ok()) {
|
2015-09-16 17:53:28 +00:00
|
|
|
auto message = "Error reading config: " + s.toString();
|
|
|
|
if (tool_ == OSQUERY_TOOL_DAEMON) {
|
2015-09-17 06:31:07 +00:00
|
|
|
LOG(WARNING) << message;
|
|
|
|
} else {
|
|
|
|
LOG(INFO) << message;
|
2015-09-16 17:53:28 +00:00
|
|
|
}
|
2015-09-17 06:31:07 +00:00
|
|
|
}
|
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
// Initialize the status and result plugin logger.
|
2016-02-09 05:50:08 +00:00
|
|
|
if (!FLAGS_disable_logging) {
|
|
|
|
initActivePlugin("logger", FLAGS_logger_plugin);
|
|
|
|
}
|
2015-03-03 23:03:14 +00:00
|
|
|
initLogger(binary_);
|
|
|
|
|
2015-09-07 18:09:06 +00:00
|
|
|
// Initialize the distributed plugin, if necessary
|
2015-10-02 18:33:50 +00:00
|
|
|
if (!FLAGS_disable_distributed) {
|
2015-09-07 18:09:06 +00:00
|
|
|
if (Registry::exists("distributed", FLAGS_distributed_plugin)) {
|
|
|
|
initActivePlugin("distributed", FLAGS_distributed_plugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
// Start event threads.
|
|
|
|
osquery::attachEvents();
|
2015-04-24 08:44:41 +00:00
|
|
|
EventFactory::delay();
|
2015-03-03 23:03:14 +00:00
|
|
|
}
|
|
|
|
|
2016-03-19 02:37:11 +00:00
|
|
|
void Initializer::waitForShutdown() {
|
|
|
|
// Attempt to be the only place in code where a join is attempted.
|
2016-02-05 03:12:48 +00:00
|
|
|
Dispatcher::joinServices();
|
2016-03-19 02:37:11 +00:00
|
|
|
// End any event type run loops.
|
|
|
|
EventFactory::end(true);
|
2015-02-01 08:35:44 +00:00
|
|
|
|
|
|
|
// Hopefully release memory used by global string constructors in gflags.
|
2015-02-17 00:26:06 +00:00
|
|
|
GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
|
2016-03-05 17:29:51 +00:00
|
|
|
DatabasePlugin::shutdown();
|
2016-03-19 02:37:11 +00:00
|
|
|
::exit((kExitCode != 0) ? kExitCode : EXIT_SUCCESS);
|
2015-02-01 08:35:44 +00:00
|
|
|
}
|
2016-03-19 02:37:11 +00:00
|
|
|
|
|
|
|
void Initializer::requestShutdown(int retcode) {
|
|
|
|
// Stop thrift services/clients/and their thread pools.
|
|
|
|
kExitCode = retcode;
|
|
|
|
if (std::this_thread::get_id() != kMainThreadId) {
|
|
|
|
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::shutdown(int retcode) { ::exit(retcode); }
|
2014-08-15 07:25:30 +00:00
|
|
|
}
|