initial config overriding on runtime

pull/1319/head
jeff 4 years ago
parent e3bfe76f98
commit 5391e6a66a

@ -66,6 +66,10 @@ namespace llarp
void
Configure(Config conf);
/// handle SIGHUP
void
Reload();
bool
IsUp() const;
@ -92,7 +96,7 @@ namespace llarp
makeRouter(llarp_ev_loop_ptr __netloop, std::shared_ptr<Logic> logic);
protected:
std::unique_ptr<Config> config = nullptr;
std::shared_ptr<Config> config = nullptr;
private:
void

@ -547,6 +547,18 @@ namespace llarp
"logging", "file", false, DefaultLogFile, AssignmentAcceptor(m_logFile));
}
void
Config::Save() const
{
m_Parser.Save();
}
void
Config::Override(std::string section, std::string key, std::string value)
{
m_Parser.AddOverride(std::move(section), std::move(key), std::move(value));
}
bool
Config::Load(const fs::path fname, bool isRelay, fs::path defaultDataDir)
{
@ -559,14 +571,13 @@ namespace llarp
ConfigDefinition conf;
initializeConfig(conf, params);
addBackwardsCompatibleConfigOptions(conf);
ConfigParser parser;
if (!parser.LoadFile(fname))
m_Parser.Clear();
if (!m_Parser.LoadFile(fname))
{
return false;
}
parser.IterAll([&](std::string_view section, const SectionValues_t& values) {
m_Parser.IterAll([&](std::string_view section, const SectionValues_t& values) {
for (const auto& pair : values)
{
conf.addConfigValue(section, pair.first, pair.second);

@ -224,6 +224,15 @@ namespace llarp
std::string
generateBaseRouterConfig(fs::path defaultDataDir);
void
Save() const;
void
Override(std::string section, std::string key, std::string value);
private:
ConfigParser m_Parser;
};
void

@ -40,6 +40,7 @@ namespace llarp
void
ConfigParser::Clear()
{
m_Overrides.clear();
m_Config.clear();
m_Data.clear();
}
@ -162,4 +163,32 @@ namespace llarp
return false;
return visit(itr->second);
}
void
ConfigParser::AddOverride(std::string section, std::string key, std::string value)
{
m_Overrides[section].emplace(key, value);
}
void
ConfigParser::Save() const
{
// if we have no overrides keep the config the same on disk
if (m_Overrides.empty())
return;
std::ofstream ofs(m_FileName);
// write existing config data
ofs.write(m_Data.data(), m_Data.size());
// write overrides
ofs << std::endl << std::endl << "# overrides" << std::endl;
for (const auto& [section, values] : m_Overrides)
{
ofs << std::endl << "[" << section << "]" << std::endl;
for (const auto& [key, value] : values)
{
ofs << key << "=" << value << std::endl;
}
}
}
} // namespace llarp

@ -40,12 +40,21 @@ namespace llarp
bool
VisitSection(const char* name, std::function<bool(const SectionValues_t&)> visit) const;
/// add a config option that is appended at the end of the config buffer with no comments
void
AddOverride(std::string section, std::string key, std::string value);
/// save config and any overrides to the file it was loaded from
void
Save() const;
private:
bool
Parse();
std::vector<char> m_Data;
Config_impl_t m_Config;
Config_impl_t m_Overrides;
fs::path m_FileName;
};

@ -33,7 +33,7 @@ namespace llarp
if (nullptr != config.get())
throw std::runtime_error("Config already exists");
config = std::make_unique<Config>(std::move(conf));
config = std::make_shared<Config>(std::move(conf));
logic = std::make_shared<Logic>();
@ -85,7 +85,7 @@ namespace llarp
nodedb = std::make_unique<llarp_nodedb>(
nodedb_dir, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); });
if (!router->Configure(*config.get(), opts.isRouter, nodedb.get()))
if (!router->Configure(config, opts.isRouter, nodedb.get()))
throw std::runtime_error("Failed to configure router");
// must be done after router is made so we can use its disk io worker
@ -157,8 +157,17 @@ namespace llarp
{
SigINT();
}
// TODO: Hot reloading would be kewl
// (it used to exist here, but wasn't maintained)
#ifndef _WIN32
if (sig == SIGHUP)
{
Reload();
}
#endif
}
void
Context::Reload()
{
}
void
@ -182,7 +191,7 @@ namespace llarp
Context::Close()
{
llarp::LogDebug("free config");
config.release();
config.reset();
llarp::LogDebug("free nodedb");
nodedb.release();

@ -1,6 +1,7 @@
#ifndef LLARP_ABSTRACT_ROUTER_HPP
#define LLARP_ABSTRACT_ROUTER_HPP
#include <config/config.hpp>
#include <config/key_manager.hpp>
#include <memory>
#include <util/types.hpp>
@ -134,6 +135,12 @@ namespace llarp
/// call function in disk io thread
virtual void QueueDiskIO(std::function<void(void)>) = 0;
virtual std::shared_ptr<Config>
GetConfig() const
{
return nullptr;
}
virtual service::Context&
hiddenServiceContext() = 0;
@ -159,7 +166,7 @@ namespace llarp
Sign(Signature& sig, const llarp_buffer_t& buf) const = 0;
virtual bool
Configure(const Config& conf, bool isRouter, llarp_nodedb* nodedb) = 0;
Configure(std::shared_ptr<Config> conf, bool isRouter, llarp_nodedb* nodedb) = 0;
virtual bool
IsServiceNode() const = 0;

@ -262,8 +262,10 @@ namespace llarp
}
bool
Router::Configure(const Config& conf, bool isRouter, llarp_nodedb* nodedb)
Router::Configure(std::shared_ptr<Config> c, bool isRouter, llarp_nodedb* nodedb)
{
m_Config = c;
auto& conf = *m_Config;
whitelistRouters = conf.lokid.whitelistRouters;
if (whitelistRouters)
lokidRPCAddr = lokimq::address(conf.lokid.lokidRPCAddr);

@ -353,7 +353,7 @@ namespace llarp
Close();
bool
Configure(const Config& conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override;
Configure(std::shared_ptr<Config> conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override;
bool
StartRpcServer() override;
@ -499,6 +499,14 @@ namespace llarp
void
AfterStopIssued();
std::shared_ptr<Config> m_Config;
std::shared_ptr<Config>
GetConfig() const override
{
return m_Config;
}
private:
std::atomic<bool> _stopping;
std::atomic<bool> _running;

@ -110,131 +110,175 @@ namespace llarp::rpc
auto ftr = result.get_future();
msg.send_reply(CreateJSONResponse(ftr.get()));
})
.add_request_command("exit", [&](lokimq::Message& msg) {
HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) {
if (r->IsServiceNode())
{
reply(CreateJSONError("not supported"));
return;
}
std::optional<service::Address> exit;
IPRange range;
bool map = true;
const auto exit_itr = obj.find("exit");
if (exit_itr != obj.end())
{
service::Address addr;
if (not addr.FromString(exit_itr->get<std::string>()))
{
reply(CreateJSONError("invalid exit address"));
return;
}
exit = addr;
}
.add_request_command(
"exit",
[&](lokimq::Message& msg) {
HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) {
if (r->IsServiceNode())
{
reply(CreateJSONError("not supported"));
return;
}
std::optional<service::Address> exit;
IPRange range;
bool map = true;
const auto exit_itr = obj.find("exit");
if (exit_itr != obj.end())
{
service::Address addr;
if (not addr.FromString(exit_itr->get<std::string>()))
{
reply(CreateJSONError("invalid exit address"));
return;
}
exit = addr;
}
const auto unmap_itr = obj.find("unmap");
if (unmap_itr != obj.end() and unmap_itr->get<bool>())
{
map = false;
}
const auto unmap_itr = obj.find("unmap");
if (unmap_itr != obj.end() and unmap_itr->get<bool>())
{
map = false;
}
const auto range_itr = obj.find("range");
if (range_itr == obj.end())
{
range.FromString("0.0.0.0/0");
}
else if (not range.FromString(range_itr->get<std::string>()))
{
reply(CreateJSONError("invalid ip range"));
return;
}
std::optional<std::string> token;
const auto token_itr = obj.find("token");
if (token_itr != obj.end())
{
token = token_itr->get<std::string>();
}
const auto range_itr = obj.find("range");
if (range_itr == obj.end())
{
range.FromString("0.0.0.0/0");
}
else if (not range.FromString(range_itr->get<std::string>()))
{
reply(CreateJSONError("invalid ip range"));
return;
}
std::optional<std::string> token;
const auto token_itr = obj.find("token");
if (token_itr != obj.end())
{
token = token_itr->get<std::string>();
}
std::string endpoint = "default";
const auto endpoint_itr = obj.find("endpoint");
if (endpoint_itr != obj.end())
std::string endpoint = "default";
const auto endpoint_itr = obj.find("endpoint");
if (endpoint_itr != obj.end())
{
endpoint = endpoint_itr->get<std::string>();
}
LogicCall(r->logic(), [map, exit, range, token, endpoint, r, reply]() {
auto ep = r->hiddenServiceContext().GetEndpointByName(endpoint);
if (ep == nullptr)
{
reply(CreateJSONError("no endpoint with name " + endpoint));
return;
}
if (map and exit.has_value())
{
const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName());
if (gateways.empty())
{
reply(CreateJSONError("no gateway found"));
return;
}
ep->MapExitRange(range, *exit);
if (token.has_value())
{
ep->SetAuthInfoForEndpoint(*exit, service::AuthInfo{*token});
}
ep->EnsurePathToService(
*exit,
[r, gateway = gateways[0], reply, ep](auto, service::OutboundContext* ctx) {
if (ctx == nullptr)
{
reply(CreateJSONError("could not find exit"));
return;
}
std::vector<std::string> firsthops;
r->ForEachPeer(
[&firsthops](const auto* link, bool) {
firsthops.emplace_back(link->GetRemoteEndpoint().toHost());
},
false);
for (const auto& hop : firsthops)
{
net::AddRoute(hop, gateway);
}
net::AddDefaultRouteViaInterface(ep->GetIfName());
r->SetDownHook([firsthops, gateway]() {
for (const auto& hop : firsthops)
{
net::DelRoute(hop, gateway);
}
});
reply(CreateJSONResponse("OK"));
},
5s);
return;
}
else if (map and not exit.has_value())
{
reply(CreateJSONError("no exit address provided"));
return;
}
else if (not map)
{
const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName());
if (gateways.empty())
{
reply(CreateJSONError("no gateway found"));
return;
}
net::DelDefaultRouteViaInterface(ep->GetIfName());
r->ForEachPeer(
[gateway = gateways[0]](const auto* link, bool) {
net::DelRoute(link->GetRemoteEndpoint().toHost(), gateway);
},
false);
ep->UnmapExitRange(range);
}
reply(CreateJSONResponse("OK"));
});
});
})
.add_request_command("config", [&](lokimq::Message& msg) {
HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) {
{
endpoint = endpoint_itr->get<std::string>();
}
LogicCall(r->logic(), [map, exit, range, token, endpoint, r, reply]() {
auto ep = r->hiddenServiceContext().GetEndpointByName(endpoint);
if (ep == nullptr)
{
reply(CreateJSONError("no endpoint with name " + endpoint));
return;
}
if (map and exit.has_value())
const auto itr = obj.find("override");
if (itr != obj.end())
{
const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName());
if (gateways.empty())
if (not itr->is_object())
{
reply(CreateJSONError("no gateway found"));
reply(CreateJSONError(stringify("override is not an object")));
return;
}
ep->MapExitRange(range, *exit);
if (token.has_value())
for (const auto& [section, value] : itr->items())
{
ep->SetAuthInfoForEndpoint(*exit, service::AuthInfo{*token});
if (not value.is_object())
{
reply(CreateJSONError(
stringify("failed to set [", section, "] section is not an object")));
return;
}
for (const auto& [key, value] : value.items())
{
if (not value.is_string())
{
reply(CreateJSONError(stringify(
"failed to set [", section, "]:", key, " value is not a string")));
return;
}
r->GetConfig()->Override(section, key, value.get<std::string>());
}
}
ep->EnsurePathToService(
*exit,
[r, gateway = gateways[0], reply, ep](auto, service::OutboundContext* ctx) {
if (ctx == nullptr)
{
reply(CreateJSONError("could not find exit"));
return;
}
std::vector<std::string> firsthops;
r->ForEachPeer(
[&firsthops](const auto* link, bool) {
firsthops.emplace_back(link->GetRemoteEndpoint().toHost());
},
false);
for (const auto& hop : firsthops)
{
net::AddRoute(hop, gateway);
}
net::AddDefaultRouteViaInterface(ep->GetIfName());
r->SetDownHook([firsthops, gateway]() {
for (const auto& hop : firsthops)
{
net::DelRoute(hop, gateway);
}
});
reply(CreateJSONResponse("OK"));
},
5s);
return;
}
else if (map and not exit.has_value())
{
reply(CreateJSONError("no exit address provided"));
return;
}
else if (not map)
}
{
const auto itr = obj.find("reload");
if (itr != obj.end() and itr->get<bool>())
{
const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName());
if (gateways.empty())
{
reply(CreateJSONError("no gateway found"));
return;
}
net::DelDefaultRouteViaInterface(ep->GetIfName());
r->ForEachPeer(
[gateway = gateways[0]](const auto* link, bool) {
net::DelRoute(link->GetRemoteEndpoint().toHost(), gateway);
},
false);
ep->UnmapExitRange(range);
r->QueueDiskIO([conf = r->GetConfig()]() { conf->Save(); });
}
reply(CreateJSONResponse("OK"));
});
}
reply(CreateJSONResponse("OK"));
});
});
}

Loading…
Cancel
Save