2015-02-04 03:55:16 +00:00
|
|
|
/*
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2015-02-04 03:55:16 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2015-02-12 06:07:25 +00:00
|
|
|
#include <csignal>
|
2015-02-10 05:57:45 +00:00
|
|
|
|
2015-03-04 16:45:21 +00:00
|
|
|
#include <boost/algorithm/string/trim.hpp>
|
|
|
|
|
2016-03-19 02:37:11 +00:00
|
|
|
#include <osquery/core.h>
|
2015-02-04 03:55:16 +00:00
|
|
|
#include <osquery/filesystem.h>
|
|
|
|
#include <osquery/logger.h>
|
2015-02-19 23:19:00 +00:00
|
|
|
#include <osquery/registry.h>
|
2015-02-18 02:42:09 +00:00
|
|
|
#include <osquery/sql.h>
|
2016-07-07 22:16:28 +00:00
|
|
|
#include <osquery/system.h>
|
2015-02-04 03:55:16 +00:00
|
|
|
|
2016-02-12 17:39:20 +00:00
|
|
|
#include "osquery/core/conversions.h"
|
2016-05-17 19:39:11 +00:00
|
|
|
#include "osquery/core/process.h"
|
2015-03-17 16:49:30 +00:00
|
|
|
#include "osquery/core/watcher.h"
|
2016-03-12 09:23:09 +00:00
|
|
|
#include "osquery/extensions/interface.h"
|
2015-02-04 03:55:16 +00:00
|
|
|
|
|
|
|
using namespace osquery::extensions;
|
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
2015-02-04 03:55:16 +00:00
|
|
|
namespace osquery {
|
2015-02-05 00:54:44 +00:00
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
// Millisecond latency between initalizing manager pings.
|
2015-06-25 08:35:51 +00:00
|
|
|
const size_t kExtensionInitializeLatencyUS = 20000;
|
2015-02-19 01:19:45 +00:00
|
|
|
|
2015-03-05 19:28:27 +00:00
|
|
|
#ifdef __APPLE__
|
2016-06-08 00:14:17 +00:00
|
|
|
#define MODULE_EXTENSION ".dylib"
|
2016-07-01 21:56:07 +00:00
|
|
|
#elif defined(WIN32)
|
|
|
|
#define MODULE_EXTENSION ".dll"
|
2015-03-05 19:28:27 +00:00
|
|
|
#else
|
2016-06-08 00:14:17 +00:00
|
|
|
#define MODULE_EXTENSION ".so"
|
2015-03-05 19:28:27 +00:00
|
|
|
#endif
|
2016-06-08 00:14:17 +00:00
|
|
|
|
|
|
|
enum ExtenableTypes {
|
|
|
|
EXTENSION = 1,
|
|
|
|
MODULE = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
const std::map<ExtenableTypes, std::string> kExtendables = {
|
|
|
|
{EXTENSION, ".ext"}, {MODULE, MODULE_EXTENSION},
|
|
|
|
};
|
2015-03-05 19:28:27 +00:00
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
CLI_FLAG(bool, disable_extensions, false, "Disable extension API");
|
2015-02-05 00:54:44 +00:00
|
|
|
|
2015-03-03 23:03:14 +00:00
|
|
|
CLI_FLAG(string,
|
|
|
|
extensions_socket,
|
2016-07-22 20:29:37 +00:00
|
|
|
OSQUERY_DB_HOME "/osquery.em",
|
2015-03-03 23:03:14 +00:00
|
|
|
"Path to the extensions UNIX domain socket")
|
|
|
|
|
|
|
|
CLI_FLAG(string,
|
|
|
|
extensions_autoload,
|
2016-07-22 20:29:37 +00:00
|
|
|
OSQUERY_HOME "/extensions.load",
|
2015-03-17 16:49:30 +00:00
|
|
|
"Optional path to a list of autoloaded & managed extensions")
|
2015-02-05 00:54:44 +00:00
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
CLI_FLAG(string,
|
|
|
|
extensions_timeout,
|
2015-03-14 01:18:18 +00:00
|
|
|
"3",
|
2015-03-13 15:11:08 +00:00
|
|
|
"Seconds to wait for autoloaded extensions");
|
|
|
|
|
2015-03-14 01:18:18 +00:00
|
|
|
CLI_FLAG(string,
|
|
|
|
extensions_interval,
|
|
|
|
"3",
|
|
|
|
"Seconds delay between connectivity checks")
|
|
|
|
|
2015-03-04 16:45:21 +00:00
|
|
|
CLI_FLAG(string,
|
|
|
|
modules_autoload,
|
2016-07-22 20:29:37 +00:00
|
|
|
OSQUERY_HOME "/modules.load",
|
2015-03-17 16:49:30 +00:00
|
|
|
"Optional path to a list of autoloaded registry modules")
|
2015-03-04 16:45:21 +00:00
|
|
|
|
2015-05-04 03:02:01 +00:00
|
|
|
/**
|
|
|
|
* @brief Alias the extensions_socket (used by core) to a simple 'socket'.
|
|
|
|
*
|
|
|
|
* Extension binaries will more commonly set the path to an extension manager
|
|
|
|
* socket. Alias the long switch name to 'socket' for an easier UX.
|
|
|
|
*
|
|
|
|
* We include timeout and interval, where the 'extensions_' prefix is removed
|
|
|
|
* in the alias since we are already within the context of an extension.
|
|
|
|
*/
|
2015-03-15 01:46:03 +00:00
|
|
|
EXTENSION_FLAG_ALIAS(socket, extensions_socket);
|
|
|
|
EXTENSION_FLAG_ALIAS(timeout, extensions_timeout);
|
|
|
|
EXTENSION_FLAG_ALIAS(interval, extensions_interval);
|
2015-05-04 03:02:01 +00:00
|
|
|
|
2015-05-06 00:09:07 +00:00
|
|
|
void ExtensionWatcher::start() {
|
2015-02-04 03:55:16 +00:00
|
|
|
// Watch the manager, if the socket is removed then the extension will die.
|
2016-02-09 05:50:08 +00:00
|
|
|
// A check for sane paths and activity is applied before the watcher
|
|
|
|
// service is added and started.
|
2016-03-12 09:23:09 +00:00
|
|
|
while (!interrupted()) {
|
2015-02-19 01:19:45 +00:00
|
|
|
watch();
|
2016-03-12 09:23:09 +00:00
|
|
|
pauseMilli(interval_);
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-04 03:55:16 +00:00
|
|
|
|
2016-07-06 19:23:24 +00:00
|
|
|
void ExtensionManagerWatcher::start() {
|
|
|
|
// Watch each extension.
|
|
|
|
while (!interrupted()) {
|
|
|
|
watch();
|
|
|
|
pauseMilli(interval_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// When interrupted, request each extension tear down.
|
|
|
|
const auto uuids = Registry::routeUUIDs();
|
|
|
|
for (const auto& uuid : uuids) {
|
|
|
|
try {
|
|
|
|
auto path = getExtensionSocket(uuid);
|
|
|
|
auto client = EXClient(path);
|
|
|
|
client.get()->shutdown();
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
VLOG(1) << "Extension UUID " << uuid << " shutdown request failed";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 01:19:45 +00:00
|
|
|
void ExtensionWatcher::exitFatal(int return_code) {
|
|
|
|
// Exit the extension.
|
2016-03-19 02:37:11 +00:00
|
|
|
// We will save the wanted return code and raise an interrupt.
|
|
|
|
// This interrupt will be handled by the main thread then join the watchers.
|
|
|
|
Initializer::requestShutdown(return_code);
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
2015-02-04 03:55:16 +00:00
|
|
|
|
2015-02-19 01:19:45 +00:00
|
|
|
void ExtensionWatcher::watch() {
|
2016-02-09 05:50:08 +00:00
|
|
|
// Attempt to ping the extension core.
|
|
|
|
// This does NOT use pingExtension to avoid the latency checks applied.
|
2015-02-04 03:55:16 +00:00
|
|
|
ExtensionStatus status;
|
2016-02-09 05:50:08 +00:00
|
|
|
bool core_sane = true;
|
|
|
|
if (isWritable(path_)) {
|
|
|
|
try {
|
|
|
|
auto client = EXManagerClient(path_);
|
|
|
|
// Ping the extension manager until it goes down.
|
|
|
|
client.get()->ping(status);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
core_sane = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// The previously-writable extension socket is not usable.
|
|
|
|
core_sane = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!core_sane) {
|
|
|
|
LOG(INFO) << "Extension watcher ending: osquery core has gone away";
|
2015-02-19 01:19:45 +00:00
|
|
|
exitFatal(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status.code != ExtensionCode::EXT_SUCCESS && fatal_) {
|
2016-02-09 05:50:08 +00:00
|
|
|
// The core may be healthy but return a failed ping status.
|
2015-02-19 01:19:45 +00:00
|
|
|
exitFatal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExtensionManagerWatcher::watch() {
|
|
|
|
// Watch the set of extensions, if the socket is removed then the extension
|
|
|
|
// will be deregistered.
|
|
|
|
const auto uuids = Registry::routeUUIDs();
|
|
|
|
|
|
|
|
ExtensionStatus status;
|
|
|
|
for (const auto& uuid : uuids) {
|
2016-03-20 23:05:13 +00:00
|
|
|
auto path = getExtensionSocket(uuid);
|
|
|
|
if (isWritable(path)) {
|
|
|
|
try {
|
|
|
|
auto client = EXClient(path);
|
|
|
|
// Ping the extension until it goes down.
|
|
|
|
client.get()->ping(status);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
failures_[uuid] += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Immediate fail non-writable paths.
|
|
|
|
failures_[uuid] = 3;
|
2015-02-23 05:56:52 +00:00
|
|
|
continue;
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
|
|
|
|
2015-06-27 21:17:25 +00:00
|
|
|
if (status.code != ExtensionCode::EXT_SUCCESS) {
|
|
|
|
LOG(INFO) << "Extension UUID " << uuid << " ping failed";
|
|
|
|
failures_[uuid] += 1;
|
|
|
|
} else {
|
|
|
|
failures_[uuid] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& uuid : failures_) {
|
|
|
|
if (uuid.second >= 3) {
|
|
|
|
LOG(INFO) << "Extension UUID " << uuid.first << " has gone away";
|
|
|
|
Registry::removeBroadcast(uuid.first);
|
|
|
|
failures_[uuid.first] = 0;
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 08:45:45 +00:00
|
|
|
Status socketWritable(const fs::path& path) {
|
2015-03-24 22:48:53 +00:00
|
|
|
if (pathExists(path).ok()) {
|
|
|
|
if (!isWritable(path).ok()) {
|
|
|
|
return Status(1, "Cannot write extension socket: " + path.string());
|
|
|
|
}
|
|
|
|
|
2016-06-08 00:14:17 +00:00
|
|
|
if (!osquery::remove(path).ok()) {
|
2015-03-24 22:48:53 +00:00
|
|
|
return Status(1, "Cannot remove extension socket: " + path.string());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!pathExists(path.parent_path()).ok()) {
|
|
|
|
return Status(1, "Extension socket directory missing: " + path.string());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isWritable(path.parent_path()).ok()) {
|
2015-11-02 08:45:45 +00:00
|
|
|
return Status(1, "Cannot create extension socket: " + path.string());
|
2015-03-24 22:48:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
2015-03-04 16:45:21 +00:00
|
|
|
void loadExtensions() {
|
2015-03-25 00:44:49 +00:00
|
|
|
// Disabling extensions will disable autoloading.
|
|
|
|
if (FLAGS_disable_extensions) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-09 05:50:08 +00:00
|
|
|
// Optionally autoload extensions, sanitize the binary path and inform
|
|
|
|
// the osquery watcher to execute the extension when started.
|
2016-07-25 18:58:55 +00:00
|
|
|
auto status = loadExtensions(
|
|
|
|
fs::path(FLAGS_extensions_autoload).make_preferred().string());
|
2015-03-03 23:03:14 +00:00
|
|
|
if (!status.ok()) {
|
2015-03-19 03:47:35 +00:00
|
|
|
VLOG(1) << "Could not autoload extensions: " << status.what();
|
2015-03-03 23:03:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-04 16:45:21 +00:00
|
|
|
void loadModules() {
|
2016-07-25 18:58:55 +00:00
|
|
|
auto status =
|
|
|
|
loadModules(fs::path(FLAGS_modules_autoload).make_preferred().string());
|
2015-03-04 16:45:21 +00:00
|
|
|
if (!status.ok()) {
|
2015-06-02 04:49:21 +00:00
|
|
|
VLOG(1) << "Could not autoload modules: " << status.what();
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-08 00:14:17 +00:00
|
|
|
static bool isFileSafe(std::string& path, ExtenableTypes type) {
|
2016-03-18 02:11:18 +00:00
|
|
|
boost::trim(path);
|
2016-06-08 00:14:17 +00:00
|
|
|
// A 'type name' may be used in verbose log output.
|
|
|
|
std::string type_name = ((type == EXTENSION) ? "extension" : "module");
|
2016-03-18 02:11:18 +00:00
|
|
|
if (path.size() == 0 || path[0] == '#' || path[0] == ';') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resolve acceptable extension binaries from autoload paths.
|
|
|
|
if (isDirectory(path).ok()) {
|
2016-06-08 00:14:17 +00:00
|
|
|
VLOG(1) << "Cannot autoload " << type_name << " from directory: " << path;
|
2016-03-18 02:11:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// The extendables will force an appropriate file path extension.
|
2016-06-08 00:14:17 +00:00
|
|
|
auto& ext = kExtendables.at(type);
|
2016-03-18 02:11:18 +00:00
|
|
|
|
|
|
|
// Only autoload file which were safe at the time of discovery.
|
|
|
|
// If the binary later becomes unsafe (permissions change) then it will fail
|
|
|
|
// to reload if a reload is ever needed.
|
|
|
|
fs::path extendable(path);
|
|
|
|
// Set the output sanitized path.
|
|
|
|
path = extendable.string();
|
|
|
|
if (!safePermissions(extendable.parent_path().string(), path, true)) {
|
2016-06-08 00:14:17 +00:00
|
|
|
LOG(WARNING) << "Will not autoload " << type_name
|
|
|
|
<< " with unsafe directory permissions: " << path;
|
2016-03-18 02:11:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (extendable.extension().string() != ext) {
|
2016-06-08 00:14:17 +00:00
|
|
|
LOG(WARNING) << "Will not autoload " << type_name << " not ending in '"
|
|
|
|
<< ext << "': " << path;
|
2016-03-18 02:11:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-06-08 00:14:17 +00:00
|
|
|
VLOG(1) << "Found autoloadable " << type_name << ": " << path;
|
2016-03-18 02:11:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-17 16:49:30 +00:00
|
|
|
Status loadExtensions(const std::string& loadfile) {
|
|
|
|
std::string autoload_paths;
|
|
|
|
if (readFile(loadfile, autoload_paths).ok()) {
|
|
|
|
for (auto& path : osquery::split(autoload_paths, "\n")) {
|
2016-06-08 00:14:17 +00:00
|
|
|
if (isFileSafe(path, EXTENSION)) {
|
2016-02-09 05:50:08 +00:00
|
|
|
// After the path is sanitized the watcher becomes responsible for
|
|
|
|
// forking and executing the extension binary.
|
2015-03-17 16:49:30 +00:00
|
|
|
Watcher::addExtensionPath(path);
|
2015-03-05 19:28:27 +00:00
|
|
|
}
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
2015-03-17 16:49:30 +00:00
|
|
|
return Status(0, "OK");
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
2015-06-02 04:49:21 +00:00
|
|
|
return Status(1, "Failed reading: " + loadfile);
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
|
|
|
|
2015-03-17 16:49:30 +00:00
|
|
|
Status loadModules(const std::string& loadfile) {
|
2015-03-04 16:45:21 +00:00
|
|
|
// Split the search path for modules using a ':' delimiter.
|
2016-03-18 02:11:18 +00:00
|
|
|
bool all_loaded = true;
|
2015-03-17 16:49:30 +00:00
|
|
|
std::string autoload_paths;
|
|
|
|
if (readFile(loadfile, autoload_paths).ok()) {
|
2016-03-18 02:11:18 +00:00
|
|
|
for (auto& path : osquery::split(autoload_paths, "\n")) {
|
2016-06-08 00:14:17 +00:00
|
|
|
if (isFileSafe(path, MODULE)) {
|
2016-03-18 02:11:18 +00:00
|
|
|
RegistryModuleLoader loader(path);
|
|
|
|
loader.init();
|
|
|
|
} else {
|
|
|
|
all_loaded = false;
|
2015-03-17 16:49:30 +00:00
|
|
|
}
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
2015-03-17 16:49:30 +00:00
|
|
|
// Return an aggregate failure if any load fails (invalid search path).
|
2016-03-18 02:11:18 +00:00
|
|
|
return Status((all_loaded) ? 0 : 1);
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
2015-06-02 04:49:21 +00:00
|
|
|
return Status(1, "Failed reading: " + loadfile);
|
2015-03-04 16:45:21 +00:00
|
|
|
}
|
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
Status extensionPathActive(const std::string& path, bool use_timeout = false) {
|
|
|
|
// Make sure the extension manager path exists, and is writable.
|
|
|
|
size_t delay = 0;
|
2015-06-25 08:35:51 +00:00
|
|
|
// The timeout is given in seconds, but checked interval is microseconds.
|
|
|
|
size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000;
|
|
|
|
if (timeout < kExtensionInitializeLatencyUS * 10) {
|
|
|
|
timeout = kExtensionInitializeLatencyUS * 10;
|
|
|
|
}
|
2015-03-13 15:11:08 +00:00
|
|
|
do {
|
|
|
|
if (pathExists(path) && isWritable(path)) {
|
|
|
|
try {
|
|
|
|
auto client = EXManagerClient(path);
|
|
|
|
return Status(0, "OK");
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
// Path might exist without a connected extension or extension manager.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Only check active once if this check does not allow a timeout.
|
|
|
|
if (!use_timeout || timeout == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Increase the total wait detail.
|
2015-06-25 08:35:51 +00:00
|
|
|
delay += kExtensionInitializeLatencyUS;
|
2016-05-17 19:39:11 +00:00
|
|
|
sleepFor(kExtensionInitializeLatencyUS / 1000);
|
2015-06-25 08:35:51 +00:00
|
|
|
} while (delay < timeout);
|
2015-03-13 15:11:08 +00:00
|
|
|
return Status(1, "Extension socket not available: " + path);
|
|
|
|
}
|
|
|
|
|
2015-02-19 01:19:45 +00:00
|
|
|
Status startExtension(const std::string& name, const std::string& version) {
|
|
|
|
return startExtension(name, version, "0.0.0");
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
2015-02-19 01:19:45 +00:00
|
|
|
Status startExtension(const std::string& name,
|
|
|
|
const std::string& version,
|
|
|
|
const std::string& min_sdk_version) {
|
2015-03-30 06:51:52 +00:00
|
|
|
Registry::setExternal();
|
2015-06-25 08:35:51 +00:00
|
|
|
// Latency converted to milliseconds, used as a thread interruptible.
|
2015-03-14 01:18:18 +00:00
|
|
|
auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
|
|
|
|
auto status = startExtensionWatcher(FLAGS_extensions_socket, latency, true);
|
2015-02-19 01:19:45 +00:00
|
|
|
if (!status.ok()) {
|
|
|
|
// If the threaded watcher fails to start, fail the extension.
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = startExtension(
|
|
|
|
FLAGS_extensions_socket, name, version, min_sdk_version, kSDKVersion);
|
|
|
|
if (!status.ok()) {
|
|
|
|
// If the extension failed to start then the EM is most likely unavailable.
|
|
|
|
return status;
|
|
|
|
}
|
2016-03-19 02:37:11 +00:00
|
|
|
return Status(0);
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Status startExtension(const std::string& manager_path,
|
|
|
|
const std::string& name,
|
|
|
|
const std::string& version,
|
2015-02-19 01:19:45 +00:00
|
|
|
const std::string& min_sdk_version,
|
2015-02-04 03:55:16 +00:00
|
|
|
const std::string& sdk_version) {
|
|
|
|
// Make sure the extension manager path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(manager_path, true);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The Registry broadcast is used as the ExtensionRegistry.
|
|
|
|
auto broadcast = Registry::getBroadcast();
|
2015-03-13 15:11:08 +00:00
|
|
|
// The extension will register and provide name, version, sdk details.
|
2015-02-04 03:55:16 +00:00
|
|
|
InternalExtensionInfo info;
|
|
|
|
info.name = name;
|
|
|
|
info.version = version;
|
|
|
|
info.sdk_version = sdk_version;
|
2015-02-19 01:19:45 +00:00
|
|
|
info.min_sdk_version = min_sdk_version;
|
2015-02-04 03:55:16 +00:00
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
// If registration is successful, we will also request the manager's options.
|
|
|
|
InternalOptionList options;
|
2015-02-06 17:42:03 +00:00
|
|
|
// Register the extension's registry broadcast with the manager.
|
2015-03-13 15:11:08 +00:00
|
|
|
ExtensionStatus ext_status;
|
2015-02-04 03:55:16 +00:00
|
|
|
try {
|
2015-02-19 01:19:45 +00:00
|
|
|
auto client = EXManagerClient(manager_path);
|
2015-03-13 15:11:08 +00:00
|
|
|
client.get()->registerExtension(ext_status, info, broadcast);
|
|
|
|
// The main reason for a failed registry is a duplicate extension name
|
|
|
|
// (the extension process is already running), or the extension broadcasts
|
|
|
|
// a duplicate registry item.
|
|
|
|
if (ext_status.code != ExtensionCode::EXT_SUCCESS) {
|
|
|
|
return Status(ext_status.code, ext_status.message);
|
|
|
|
}
|
|
|
|
// Request the core options, mainly to set the active registry plugins for
|
|
|
|
// logger and config.
|
|
|
|
client.get()->options(options);
|
|
|
|
} catch (const std::exception& e) {
|
2015-02-04 03:55:16 +00:00
|
|
|
return Status(1, "Extension register failed: " + std::string(e.what()));
|
|
|
|
}
|
|
|
|
|
2015-02-19 01:19:45 +00:00
|
|
|
// Now that the uuid is known, try to clean up stale socket paths.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto extension_path = getExtensionSocket(ext_status.uuid, manager_path);
|
2015-03-24 22:48:53 +00:00
|
|
|
status = socketWritable(extension_path);
|
|
|
|
if (!status) {
|
|
|
|
return status;
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
|
|
|
|
2015-03-13 15:11:08 +00:00
|
|
|
// Set the active config and logger plugins. The core will arbitrate if the
|
|
|
|
// plugins are not available in the extension's local registry.
|
|
|
|
Registry::setActive("config", options["config_plugin"].value);
|
|
|
|
Registry::setActive("logger", options["logger_plugin"].value);
|
|
|
|
// Set up all lazy registry plugins and the active config/logger plugin.
|
|
|
|
Registry::setUp();
|
|
|
|
|
2015-02-04 03:55:16 +00:00
|
|
|
// Start the extension's Thrift server
|
2015-05-04 03:02:01 +00:00
|
|
|
Dispatcher::addService(
|
2015-03-13 15:11:08 +00:00
|
|
|
std::make_shared<ExtensionRunner>(manager_path, ext_status.uuid));
|
|
|
|
VLOG(1) << "Extension (" << name << ", " << ext_status.uuid << ", " << version
|
2015-02-19 01:19:45 +00:00
|
|
|
<< ", " << sdk_version << ") registered";
|
2015-03-13 15:11:08 +00:00
|
|
|
return Status(0, std::to_string(ext_status.uuid));
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
2015-02-19 01:19:45 +00:00
|
|
|
Status queryExternal(const std::string& manager_path,
|
|
|
|
const std::string& query,
|
|
|
|
QueryData& results) {
|
|
|
|
// Make sure the extension path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(manager_path);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ExtensionResponse response;
|
|
|
|
try {
|
|
|
|
auto client = EXManagerClient(manager_path);
|
|
|
|
client.get()->query(response, query);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
return Status(1, "Extension call failed: " + std::string(e.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& row : response.response) {
|
|
|
|
results.push_back(row);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status(response.status.code, response.status.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status queryExternal(const std::string& query, QueryData& results) {
|
|
|
|
return queryExternal(FLAGS_extensions_socket, query, results);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status getQueryColumnsExternal(const std::string& manager_path,
|
|
|
|
const std::string& query,
|
2015-05-20 20:27:53 +00:00
|
|
|
TableColumns& columns) {
|
2015-02-19 01:19:45 +00:00
|
|
|
// Make sure the extension path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(manager_path);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ExtensionResponse response;
|
|
|
|
try {
|
|
|
|
auto client = EXManagerClient(manager_path);
|
|
|
|
client.get()->getQueryColumns(response, query);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
return Status(1, "Extension call failed: " + std::string(e.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Translate response map: {string: string} to a vector: pair(name, type).
|
|
|
|
for (const auto& column : response.response) {
|
2015-11-09 09:19:45 +00:00
|
|
|
for (const auto& col : column) {
|
2016-08-31 22:32:20 +00:00
|
|
|
columns.push_back(std::make_tuple(
|
|
|
|
col.first, columnTypeName(col.second), ColumnOptions::DEFAULT));
|
2015-02-19 01:19:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status(response.status.code, response.status.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status getQueryColumnsExternal(const std::string& query,
|
2015-05-20 20:27:53 +00:00
|
|
|
TableColumns& columns) {
|
2015-02-19 01:19:45 +00:00
|
|
|
return getQueryColumnsExternal(FLAGS_extensions_socket, query, columns);
|
|
|
|
}
|
|
|
|
|
2015-02-10 20:00:03 +00:00
|
|
|
Status pingExtension(const std::string& path) {
|
|
|
|
if (FLAGS_disable_extensions) {
|
|
|
|
return Status(1, "Extensions disabled");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the extension path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(path);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-10 20:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ExtensionStatus ext_status;
|
|
|
|
try {
|
2015-02-19 01:19:45 +00:00
|
|
|
auto client = EXClient(path);
|
|
|
|
client.get()->ping(ext_status);
|
2015-02-10 20:00:03 +00:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
return Status(1, "Extension call failed: " + std::string(e.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status(ext_status.code, ext_status.message);
|
|
|
|
}
|
|
|
|
|
2015-02-10 20:50:07 +00:00
|
|
|
Status getExtensions(ExtensionList& extensions) {
|
|
|
|
if (FLAGS_disable_extensions) {
|
|
|
|
return Status(1, "Extensions disabled");
|
|
|
|
}
|
|
|
|
return getExtensions(FLAGS_extensions_socket, extensions);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status getExtensions(const std::string& manager_path,
|
|
|
|
ExtensionList& extensions) {
|
|
|
|
// Make sure the extension path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(manager_path);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-10 20:50:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
InternalExtensionList ext_list;
|
|
|
|
try {
|
2015-02-19 01:19:45 +00:00
|
|
|
auto client = EXManagerClient(manager_path);
|
|
|
|
client.get()->extensions(ext_list);
|
2015-02-10 20:50:07 +00:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
return Status(1, "Extension call failed: " + std::string(e.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the extension manager to the list called (core).
|
2015-06-12 01:13:37 +00:00
|
|
|
extensions[0] = {"core", kVersion, "0.0.0", kSDKVersion};
|
2015-02-10 20:50:07 +00:00
|
|
|
|
|
|
|
// Convert from Thrift-internal list type to RouteUUID/ExtenionInfo type.
|
2015-02-19 23:19:00 +00:00
|
|
|
for (const auto& ext : ext_list) {
|
2016-08-31 22:32:20 +00:00
|
|
|
extensions[ext.first] = {ext.second.name,
|
|
|
|
ext.second.version,
|
2015-02-19 23:19:00 +00:00
|
|
|
ext.second.min_sdk_version,
|
|
|
|
ext.second.sdk_version};
|
2015-02-10 20:50:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
2015-02-04 03:55:16 +00:00
|
|
|
Status callExtension(const RouteUUID uuid,
|
|
|
|
const std::string& registry,
|
|
|
|
const std::string& item,
|
|
|
|
const PluginRequest& request,
|
|
|
|
PluginResponse& response) {
|
2015-02-10 20:00:03 +00:00
|
|
|
if (FLAGS_disable_extensions) {
|
|
|
|
return Status(1, "Extensions disabled");
|
|
|
|
}
|
2015-02-10 20:50:07 +00:00
|
|
|
return callExtension(
|
|
|
|
getExtensionSocket(uuid), registry, item, request, response);
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Status callExtension(const std::string& extension_path,
|
|
|
|
const std::string& registry,
|
|
|
|
const std::string& item,
|
|
|
|
const PluginRequest& request,
|
|
|
|
PluginResponse& response) {
|
|
|
|
// Make sure the extension manager path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(extension_path);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ExtensionResponse ext_response;
|
|
|
|
try {
|
2015-02-19 01:19:45 +00:00
|
|
|
auto client = EXClient(extension_path);
|
|
|
|
client.get()->call(ext_response, registry, item, request);
|
2015-12-08 07:08:00 +00:00
|
|
|
} catch (const std::exception& e) {
|
2015-02-04 03:55:16 +00:00
|
|
|
return Status(1, "Extension call failed: " + std::string(e.what()));
|
|
|
|
}
|
|
|
|
|
2015-02-10 20:50:07 +00:00
|
|
|
// Convert from Thrift-internal list type to PluginResponse type.
|
2015-02-04 03:55:16 +00:00
|
|
|
if (ext_response.status.code == ExtensionCode::EXT_SUCCESS) {
|
|
|
|
for (const auto& item : ext_response.response) {
|
|
|
|
response.push_back(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Status(ext_response.status.code, ext_response.status.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status startExtensionWatcher(const std::string& manager_path,
|
|
|
|
size_t interval,
|
|
|
|
bool fatal) {
|
|
|
|
// Make sure the extension manager path exists, and is writable.
|
2015-03-13 15:11:08 +00:00
|
|
|
auto status = extensionPathActive(manager_path, true);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start a extension manager watcher, if the manager dies, so should we.
|
2015-05-04 03:02:01 +00:00
|
|
|
Dispatcher::addService(
|
2015-02-04 03:55:16 +00:00
|
|
|
std::make_shared<ExtensionWatcher>(manager_path, interval, fatal));
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
|
|
|
Status startExtensionManager() {
|
2015-02-10 20:00:03 +00:00
|
|
|
if (FLAGS_disable_extensions) {
|
|
|
|
return Status(1, "Extensions disabled");
|
|
|
|
}
|
2016-07-28 23:04:34 +00:00
|
|
|
return startExtensionManager(
|
|
|
|
fs::path(FLAGS_extensions_socket).make_preferred().string());
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Status startExtensionManager(const std::string& manager_path) {
|
|
|
|
// Check if the socket location exists.
|
2015-03-24 22:48:53 +00:00
|
|
|
auto status = socketWritable(manager_path);
|
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
2015-02-04 03:55:16 +00:00
|
|
|
}
|
|
|
|
|
2015-06-25 08:35:51 +00:00
|
|
|
// Seconds converted to milliseconds, used as a thread interruptible.
|
2015-03-14 01:18:18 +00:00
|
|
|
auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
|
2015-02-19 01:19:45 +00:00
|
|
|
// Start a extension manager watcher, if the manager dies, so should we.
|
2015-05-04 03:02:01 +00:00
|
|
|
Dispatcher::addService(
|
2015-03-14 01:18:18 +00:00
|
|
|
std::make_shared<ExtensionManagerWatcher>(manager_path, latency));
|
2015-02-19 01:19:45 +00:00
|
|
|
|
2015-02-04 03:55:16 +00:00
|
|
|
// Start the extension manager thread.
|
2015-05-04 03:02:01 +00:00
|
|
|
Dispatcher::addService(
|
2015-02-04 03:55:16 +00:00
|
|
|
std::make_shared<ExtensionManagerRunner>(manager_path));
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
}
|