mirror of https://github.com/oxen-io/lokinet
Merge remote-tracking branch 'origin/stable' into debian/sid
commit
8ec6407841
@ -0,0 +1,33 @@
|
|||||||
|
commit 56d816014d5e8a7eb055169c7e13a303dad5e50f
|
||||||
|
Author: Jason Rhinelander <jason@imaginary.ca>
|
||||||
|
Date: Mon Oct 31 22:07:03 2022 -0300
|
||||||
|
|
||||||
|
Set tube->ev_listen to NULL to prevent double unregister
|
||||||
|
|
||||||
|
On windows when using threaded mode (i.e. `ub_ctx_async(ctx, 1)`)
|
||||||
|
tube_remove_bg_listen gets called twice: once when the thread does its
|
||||||
|
own cleanup, then again in `tube_delete()`. Because `ev_listen` doesn't
|
||||||
|
get cleared, however, we end we calling ub_winsock_unregister_wsaevent
|
||||||
|
with a freed pointer.
|
||||||
|
|
||||||
|
This doesn't always manifest because, apparently, for various compilers
|
||||||
|
and settings that memory *might* be overwritten in which case the
|
||||||
|
additional check for ev->magic will prevent anything actually happening,
|
||||||
|
but in my case under mingw32 that doesn't happen and we end up
|
||||||
|
eventually crashing.
|
||||||
|
|
||||||
|
This fixes the crash by properly NULLing the pointer so that the second
|
||||||
|
ub_winsock_unregister_wsaevent(...) becomes a no-op.
|
||||||
|
|
||||||
|
diff --git a/util/tube.c b/util/tube.c
|
||||||
|
index 43455fee..a92dfa77 100644
|
||||||
|
--- a/util/tube.c
|
||||||
|
+++ b/util/tube.c
|
||||||
|
@@ -570,6 +570,7 @@ void tube_remove_bg_listen(struct tube* tube)
|
||||||
|
{
|
||||||
|
verbose(VERB_ALGO, "tube remove_bg_listen");
|
||||||
|
ub_winsock_unregister_wsaevent(tube->ev_listen);
|
||||||
|
+ tube->ev_listen = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tube_remove_bg_write(struct tube* tube)
|
@ -1,8 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# create signed release tarball with submodules bundled
|
# create signed release tarball with submodules bundled
|
||||||
|
# usage: ./contrib/tarball.sh [keyid]
|
||||||
#
|
#
|
||||||
repo=$(readlink -e $(dirname $0)/..)
|
repo=$(readlink -e $(dirname $0)/..)
|
||||||
branch=$(test -e $repo/.git/ && git rev-parse --abbrev-ref HEAD)
|
branch=$(test -e $repo/.git/ && git rev-parse --abbrev-ref HEAD)
|
||||||
out="lokinet-$(git describe --exact-match --tags $(git log -n1 --pretty='%h') 2> /dev/null || ( echo -n $branch- && git rev-parse --short HEAD)).tar.xz"
|
out="lokinet-$(git describe --exact-match --tags $(git log -n1 --pretty='%h') 2> /dev/null || ( echo -n $branch- && git rev-parse --short HEAD)).tar.xz"
|
||||||
git-archive-all -C $repo --force-submodules $out && rm -f $out.sig && (gpg --sign --detach $out &> /dev/null && gpg --verify $out.sig)
|
git-archive-all -C $repo --force-submodules $out && rm -f $out.sig && (gpg -u ${1:-jeff@lokinet.io} --sign --detach $out &> /dev/null && gpg --verify $out.sig)
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 7b0f1aacdf79b558adfc39dc9cccb7e348aeec03
|
Subproject commit 37f274e86fea7fe0fcc472727398d118a6917854
|
@ -1,240 +0,0 @@
|
|||||||
#include "unbound_resolver.hpp"
|
|
||||||
|
|
||||||
#include "server.hpp"
|
|
||||||
#include <llarp/constants/apple.hpp>
|
|
||||||
#include <llarp/constants/platform.hpp>
|
|
||||||
#include <llarp/util/buffer.hpp>
|
|
||||||
#include <sstream>
|
|
||||||
#include <llarp/util/str.hpp>
|
|
||||||
|
|
||||||
#include <unbound.h>
|
|
||||||
|
|
||||||
namespace llarp::dns
|
|
||||||
{
|
|
||||||
static auto logcat = log::Cat("dns");
|
|
||||||
|
|
||||||
struct PendingUnboundLookup
|
|
||||||
{
|
|
||||||
std::weak_ptr<UnboundResolver> resolver;
|
|
||||||
Message msg;
|
|
||||||
SockAddr resolverAddr;
|
|
||||||
SockAddr askerAddr;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboundResolver::Stop()
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboundResolver::Reset()
|
|
||||||
{
|
|
||||||
started = false;
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (runner.joinable())
|
|
||||||
{
|
|
||||||
runner.join();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (udp)
|
|
||||||
{
|
|
||||||
udp->close();
|
|
||||||
}
|
|
||||||
udp.reset();
|
|
||||||
#endif
|
|
||||||
if (unboundContext)
|
|
||||||
{
|
|
||||||
ub_ctx_delete(unboundContext);
|
|
||||||
}
|
|
||||||
unboundContext = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboundResolver::UnboundResolver(EventLoop_ptr _loop, ReplyFunction reply, FailFunction fail)
|
|
||||||
: unboundContext{nullptr}
|
|
||||||
, started{false}
|
|
||||||
, replyFunc{_loop->make_caller(std::move(reply))}
|
|
||||||
, failFunc{_loop->make_caller(std::move(fail))}
|
|
||||||
{
|
|
||||||
#ifndef _WIN32
|
|
||||||
loop = _loop->MaybeGetUVWLoop();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// static callback
|
|
||||||
void
|
|
||||||
UnboundResolver::Callback(void* data, int err, ub_result* result)
|
|
||||||
{
|
|
||||||
std::unique_ptr<PendingUnboundLookup> lookup{static_cast<PendingUnboundLookup*>(data)};
|
|
||||||
|
|
||||||
auto this_ptr = lookup->resolver.lock();
|
|
||||||
if (not this_ptr)
|
|
||||||
return; // resolver is gone, so we don't reply.
|
|
||||||
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
Message& msg = lookup->msg;
|
|
||||||
msg.AddServFail();
|
|
||||||
this_ptr->failFunc(lookup->askerAddr, lookup->resolverAddr, msg);
|
|
||||||
ub_resolve_free(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OwnedBuffer pkt{(size_t)result->answer_len};
|
|
||||||
std::memcpy(pkt.buf.get(), result->answer_packet, pkt.sz);
|
|
||||||
llarp_buffer_t buf(pkt);
|
|
||||||
|
|
||||||
MessageHeader hdr;
|
|
||||||
hdr.Decode(&buf);
|
|
||||||
hdr.id = lookup->msg.hdr_id;
|
|
||||||
|
|
||||||
buf.cur = buf.base;
|
|
||||||
hdr.Encode(&buf);
|
|
||||||
|
|
||||||
this_ptr->replyFunc(lookup->askerAddr, lookup->resolverAddr, std::move(pkt));
|
|
||||||
|
|
||||||
ub_resolve_free(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
UnboundResolver::Init()
|
|
||||||
{
|
|
||||||
if (started)
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
unboundContext = ub_ctx_create();
|
|
||||||
|
|
||||||
if (not unboundContext)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable ip6 for upstream dns
|
|
||||||
ub_ctx_set_option(unboundContext, "prefer-ip6", "0");
|
|
||||||
// enable async
|
|
||||||
ub_ctx_async(unboundContext, 1);
|
|
||||||
#ifdef _WIN32
|
|
||||||
runner = std::thread{[&]() {
|
|
||||||
while (started)
|
|
||||||
{
|
|
||||||
if (unboundContext)
|
|
||||||
ub_wait(unboundContext);
|
|
||||||
std::this_thread::sleep_for(25ms);
|
|
||||||
}
|
|
||||||
if (unboundContext)
|
|
||||||
ub_process(unboundContext);
|
|
||||||
}};
|
|
||||||
#else
|
|
||||||
if (auto loop_ptr = loop.lock())
|
|
||||||
{
|
|
||||||
udp = loop_ptr->resource<uvw::PollHandle>(ub_fd(unboundContext));
|
|
||||||
udp->on<uvw::PollEvent>([ptr = weak_from_this()](auto&, auto&) {
|
|
||||||
if (auto self = ptr.lock())
|
|
||||||
{
|
|
||||||
if (self->unboundContext)
|
|
||||||
{
|
|
||||||
ub_process(self->unboundContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
udp->start(uvw::PollHandle::Event::READABLE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
started = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
UnboundResolver::AddUpstreamResolver(const SockAddr& upstreamResolver)
|
|
||||||
{
|
|
||||||
const auto hoststr = upstreamResolver.hostString();
|
|
||||||
std::string upstream = hoststr;
|
|
||||||
|
|
||||||
const auto port = upstreamResolver.getPort();
|
|
||||||
if (port != 53)
|
|
||||||
{
|
|
||||||
upstream += '@';
|
|
||||||
upstream += std::to_string(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info("Adding upstream resolver ", upstream);
|
|
||||||
if (ub_ctx_set_fwd(unboundContext, upstream.c_str()) != 0)
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (platform::is_apple)
|
|
||||||
{
|
|
||||||
// On Apple, when we turn on exit mode, we can't directly connect to upstream from here
|
|
||||||
// because, from within the network extension, macOS ignores setting the tunnel as the default
|
|
||||||
// route and would leak all DNS; instead we have to bounce things through the objective C
|
|
||||||
// trampoline code so that it can call into Apple's special snowflake API to set up a socket
|
|
||||||
// that has the magic Apple snowflake sauce added on top so that it actually routes through
|
|
||||||
// the tunnel instead of around it.
|
|
||||||
//
|
|
||||||
// This behaviour is all carefully and explicitly documented by Apple with plenty of examples
|
|
||||||
// and other exposition, of course, just like all of their wonderful new APIs to reinvent
|
|
||||||
// standard unix interfaces.
|
|
||||||
if (hoststr == "127.0.0.1" && port == apple::dns_trampoline_port)
|
|
||||||
{
|
|
||||||
// Not at all clear why this is needed but without it we get "send failed: Can't assign
|
|
||||||
// requested address" when unbound tries to connect to the localhost address using a source
|
|
||||||
// address of 0.0.0.0. Yay apple.
|
|
||||||
ub_ctx_set_option(unboundContext, "outgoing-interface:", "127.0.0.1");
|
|
||||||
|
|
||||||
// The trampoline expects just a single source port (and sends everything back to it)
|
|
||||||
ub_ctx_set_option(unboundContext, "outgoing-range:", "1");
|
|
||||||
ub_ctx_set_option(unboundContext, "outgoing-port-avoid:", "0-65535");
|
|
||||||
ub_ctx_set_option(
|
|
||||||
unboundContext,
|
|
||||||
"outgoing-port-permit:",
|
|
||||||
std::to_string(apple::dns_trampoline_source_port).c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboundResolver::AddHostsFile(const fs::path& file)
|
|
||||||
{
|
|
||||||
log::debug(logcat, "adding hosts file {}", file);
|
|
||||||
const auto str = file.u8string();
|
|
||||||
if (auto ret = ub_ctx_hosts(unboundContext, str.c_str()))
|
|
||||||
throw std::runtime_error{
|
|
||||||
fmt::format("Failed to add host file {}: {}", file, ub_strerror(ret))};
|
|
||||||
log::info(logcat, "added hosts file {}", file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboundResolver::Lookup(SockAddr to, SockAddr from, Message msg)
|
|
||||||
{
|
|
||||||
if (not unboundContext)
|
|
||||||
{
|
|
||||||
msg.AddServFail();
|
|
||||||
failFunc(from, to, std::move(msg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& q = msg.questions[0];
|
|
||||||
auto* lookup = new PendingUnboundLookup{weak_from_this(), msg, to, from};
|
|
||||||
int err = ub_resolve_async(
|
|
||||||
unboundContext,
|
|
||||||
q.Name().c_str(),
|
|
||||||
q.qtype,
|
|
||||||
q.qclass,
|
|
||||||
(void*)lookup,
|
|
||||||
&UnboundResolver::Callback,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
msg.AddServFail();
|
|
||||||
failFunc(from, to, std::move(msg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace llarp::dns
|
|
@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#include <llarp/ev/ev.hpp>
|
|
||||||
#include <llarp/util/fs.hpp>
|
|
||||||
|
|
||||||
#include "message.hpp"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <thread>
|
|
||||||
#else
|
|
||||||
#include <uvw.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
struct ub_ctx;
|
|
||||||
struct ub_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace llarp::dns
|
|
||||||
{
|
|
||||||
using ReplyFunction =
|
|
||||||
std::function<void(const SockAddr& reply_to, const SockAddr& from_resolver, OwnedBuffer buf)>;
|
|
||||||
using FailFunction =
|
|
||||||
std::function<void(const SockAddr& reply_to, const SockAddr& from_resolver, Message msg)>;
|
|
||||||
|
|
||||||
class UnboundResolver : public std::enable_shared_from_this<UnboundResolver>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
ub_ctx* unboundContext;
|
|
||||||
|
|
||||||
std::atomic<bool> started;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::thread runner;
|
|
||||||
#else
|
|
||||||
std::weak_ptr<uvw::Loop> loop;
|
|
||||||
std::shared_ptr<uvw::PollHandle> udp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ReplyFunction replyFunc;
|
|
||||||
FailFunction failFunc;
|
|
||||||
void
|
|
||||||
Reset();
|
|
||||||
|
|
||||||
public:
|
|
||||||
UnboundResolver(EventLoop_ptr loop, ReplyFunction replyFunc, FailFunction failFunc);
|
|
||||||
|
|
||||||
static void
|
|
||||||
Callback(void* data, int err, ub_result* result);
|
|
||||||
|
|
||||||
// stop resolver thread
|
|
||||||
void
|
|
||||||
Stop();
|
|
||||||
|
|
||||||
// upstream resolver IP can be IPv4 or IPv6
|
|
||||||
bool
|
|
||||||
Init();
|
|
||||||
|
|
||||||
bool
|
|
||||||
AddUpstreamResolver(const SockAddr& upstreamResolverIP);
|
|
||||||
|
|
||||||
void
|
|
||||||
AddHostsFile(const fs::path& file);
|
|
||||||
|
|
||||||
void
|
|
||||||
Lookup(SockAddr to, SockAddr from, Message msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace llarp::dns
|
|
@ -1,51 +0,0 @@
|
|||||||
#include "win32_platform.hpp"
|
|
||||||
#include <llarp/net/net.hpp>
|
|
||||||
|
|
||||||
namespace llarp::dns::win32
|
|
||||||
{
|
|
||||||
void
|
|
||||||
Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
// clear any previous dns settings
|
|
||||||
m_UndoDNS.clear();
|
|
||||||
|
|
||||||
auto interfaces = m_Loop->Net_ptr()->AllNetworkInterfaces();
|
|
||||||
// remove dns
|
|
||||||
{
|
|
||||||
std::vector<llarp::win32::OneShotExec> jobs;
|
|
||||||
for (const auto& ent : interfaces)
|
|
||||||
{
|
|
||||||
if (ent.index == index)
|
|
||||||
continue;
|
|
||||||
jobs.emplace_back(
|
|
||||||
"netsh.exe", fmt::format("interface ipv4 delete dns \"{}\" all", ent.name));
|
|
||||||
jobs.emplace_back(
|
|
||||||
"netsh.exe", fmt::format("interface ipv6 delete dns \"{}\" all", ent.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add new dns
|
|
||||||
{
|
|
||||||
std::vector<llarp::win32::OneShotExec> jobs;
|
|
||||||
for (const auto& ent : interfaces)
|
|
||||||
{
|
|
||||||
if (ent.index == index)
|
|
||||||
continue;
|
|
||||||
jobs.emplace_back(
|
|
||||||
"netsh.exe",
|
|
||||||
fmt::format("interface ipv4 add dns \"{}\" {} validate=no", ent.name, dns.asIPv4()));
|
|
||||||
jobs.emplace_back(
|
|
||||||
"netsh.exe",
|
|
||||||
fmt::format("interface ipv6 add dns \"{}\" {} validate=no", ent.name, dns.asIPv6()));
|
|
||||||
m_UndoDNS.emplace_back("netsh.exe", fmt::format("", index));
|
|
||||||
}
|
|
||||||
m_UndoDNS.emplace_back("netsh.exe", "winsock reset");
|
|
||||||
}
|
|
||||||
// flush dns
|
|
||||||
llarp::win32::Exec("ipconfig.exe", "/flushdns");
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace llarp::dns::win32
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "platform.hpp"
|
|
||||||
|
|
||||||
namespace llarp::dns
|
|
||||||
{
|
|
||||||
// TODO: implement me
|
|
||||||
using Win32_Platform_t = Null_Platform;
|
|
||||||
} // namespace llarp::dns
|
|
@ -0,0 +1,59 @@
|
|||||||
|
#include <llarp/util/service_manager.hpp>
|
||||||
|
|
||||||
|
#include <systemd/sd-daemon.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <llarp.hpp>
|
||||||
|
#include <llarp/router/router.hpp>
|
||||||
|
#include <llarp/util/logging.hpp>
|
||||||
|
|
||||||
|
namespace llarp::sys
|
||||||
|
{
|
||||||
|
class SD_Manager : public I_SystemLayerManager
|
||||||
|
{
|
||||||
|
llarp::sys::ServiceState m_State{ServiceState::Initial};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// change our state and report it to the system layer
|
||||||
|
void
|
||||||
|
we_changed_our_state(ServiceState st) override
|
||||||
|
{
|
||||||
|
m_State = st;
|
||||||
|
report_changed_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
report_changed_state() override
|
||||||
|
{
|
||||||
|
if (m_State == ServiceState::Running)
|
||||||
|
{
|
||||||
|
::sd_notify(0, "READY=1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_State == ServiceState::Stopping)
|
||||||
|
{
|
||||||
|
::sd_notify(0, "STOPPING=1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
report_periodic_stats() override
|
||||||
|
{
|
||||||
|
if (m_Context and m_Context->router and not m_disable)
|
||||||
|
{
|
||||||
|
auto status = fmt::format("WATCHDOG=1\nSTATUS={}", m_Context->router->status_line());
|
||||||
|
::sd_notify(0, status.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
system_changed_our_state(ServiceState) override
|
||||||
|
{
|
||||||
|
// not applicable on systemd
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SD_Manager _manager{};
|
||||||
|
I_SystemLayerManager* const service_manager = &_manager;
|
||||||
|
|
||||||
|
} // namespace llarp::sys
|
@ -0,0 +1,7 @@
|
|||||||
|
#include "service_manager.hpp"
|
||||||
|
|
||||||
|
namespace llarp::sys
|
||||||
|
{
|
||||||
|
NOP_SystemLayerHandler _manager{};
|
||||||
|
I_SystemLayerManager* const service_manager = &_manager;
|
||||||
|
} // namespace llarp::sys
|
@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace llarp
|
||||||
|
{
|
||||||
|
struct Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace llarp::sys
|
||||||
|
{
|
||||||
|
|
||||||
|
// what state lokinet will report we are in to the system layer
|
||||||
|
enum class ServiceState
|
||||||
|
{
|
||||||
|
Initial,
|
||||||
|
Starting,
|
||||||
|
Running,
|
||||||
|
Stopping,
|
||||||
|
Stopped,
|
||||||
|
HardStop,
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// interface type for interacting with the os dependant system layer
|
||||||
|
class I_SystemLayerManager
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool m_disable{false};
|
||||||
|
llarp::Context* m_Context{nullptr};
|
||||||
|
|
||||||
|
/// change our state and report it to the system layer
|
||||||
|
virtual void
|
||||||
|
we_changed_our_state(ServiceState st) = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~I_SystemLayerManager() = default;
|
||||||
|
|
||||||
|
/// disable all reporting to system layer
|
||||||
|
inline void
|
||||||
|
disable()
|
||||||
|
{
|
||||||
|
m_disable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// give our current lokinet context to the system layer manager
|
||||||
|
inline void
|
||||||
|
give_context(llarp::Context* ctx)
|
||||||
|
{
|
||||||
|
m_Context = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// system told us to enter this state
|
||||||
|
virtual void
|
||||||
|
system_changed_our_state(ServiceState st) = 0;
|
||||||
|
|
||||||
|
/// report our current state to the system layer
|
||||||
|
virtual void
|
||||||
|
report_changed_state() = 0;
|
||||||
|
|
||||||
|
/// report our stats on each timer tick
|
||||||
|
virtual void
|
||||||
|
report_periodic_stats(){};
|
||||||
|
|
||||||
|
void
|
||||||
|
starting()
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
we_changed_our_state(ServiceState::Starting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ready()
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
we_changed_our_state(ServiceState::Running);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
stopping()
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
we_changed_our_state(ServiceState::Stopping);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
stopped()
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
we_changed_our_state(ServiceState::Stopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
failed()
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
we_changed_our_state(ServiceState::Failed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern I_SystemLayerManager* const service_manager;
|
||||||
|
|
||||||
|
class NOP_SystemLayerHandler : public I_SystemLayerManager
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void
|
||||||
|
we_changed_our_state(ServiceState) override
|
||||||
|
{}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
report_changed_state() override{};
|
||||||
|
void system_changed_our_state(ServiceState) override{};
|
||||||
|
};
|
||||||
|
} // namespace llarp::sys
|
@ -0,0 +1,112 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <llarp.hpp>
|
||||||
|
#include <llarp/util/logging.hpp>
|
||||||
|
#include "service_manager.hpp"
|
||||||
|
#include <dbghelp.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <csignal>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace llarp::sys
|
||||||
|
{
|
||||||
|
|
||||||
|
static auto logcat = log::Cat("svc");
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
std::optional<DWORD>
|
||||||
|
to_win32_state(ServiceState st)
|
||||||
|
{
|
||||||
|
switch (st)
|
||||||
|
{
|
||||||
|
case ServiceState::Starting:
|
||||||
|
return SERVICE_START_PENDING;
|
||||||
|
case ServiceState::Running:
|
||||||
|
return SERVICE_RUNNING;
|
||||||
|
case ServiceState::Stopping:
|
||||||
|
return SERVICE_STOP_PENDING;
|
||||||
|
case ServiceState::Stopped:
|
||||||
|
return SERVICE_STOPPED;
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SVC_Manager::SVC_Manager()
|
||||||
|
{
|
||||||
|
_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVC_Manager::system_changed_our_state(ServiceState st)
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
if (st == ServiceState::Stopping)
|
||||||
|
{
|
||||||
|
we_changed_our_state(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVC_Manager::report_changed_state()
|
||||||
|
{
|
||||||
|
if (m_disable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
log::debug(
|
||||||
|
logcat,
|
||||||
|
"Reporting Windows service status '{}', exit code {}, wait hint {}, dwCP {}, dwCA {}",
|
||||||
|
_status.dwCurrentState == SERVICE_START_PENDING ? "start pending"
|
||||||
|
: _status.dwCurrentState == SERVICE_RUNNING ? "running"
|
||||||
|
: _status.dwCurrentState == SERVICE_STOPPED ? "stopped"
|
||||||
|
: _status.dwCurrentState == SERVICE_STOP_PENDING
|
||||||
|
? "stop pending"
|
||||||
|
: fmt::format("unknown: {}", _status.dwCurrentState),
|
||||||
|
_status.dwWin32ExitCode,
|
||||||
|
_status.dwWaitHint,
|
||||||
|
_status.dwCheckPoint,
|
||||||
|
_status.dwControlsAccepted);
|
||||||
|
|
||||||
|
SetServiceStatus(handle, &_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVC_Manager::we_changed_our_state(ServiceState st)
|
||||||
|
{
|
||||||
|
if (st == ServiceState::Failed)
|
||||||
|
{
|
||||||
|
_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
|
||||||
|
_status.dwServiceSpecificExitCode = 2; // TODO: propagate more info ?
|
||||||
|
report_changed_state();
|
||||||
|
}
|
||||||
|
else if (auto maybe_state = to_win32_state(st))
|
||||||
|
{
|
||||||
|
auto new_state = *maybe_state;
|
||||||
|
_status.dwWin32ExitCode = NO_ERROR;
|
||||||
|
_status.dwCurrentState = new_state;
|
||||||
|
_status.dwControlsAccepted = st == ServiceState::Running ? SERVICE_ACCEPT_STOP : 0;
|
||||||
|
_status.dwWaitHint =
|
||||||
|
std::chrono::milliseconds{
|
||||||
|
st == ServiceState::Starting ? StartupTimeout
|
||||||
|
: st == ServiceState::Stopping ? StopTimeout
|
||||||
|
: 0s}
|
||||||
|
.count();
|
||||||
|
// dwCheckPoint gets incremented during a start/stop to tell windows "we're still
|
||||||
|
// starting/stopping" and to reset its must-be-hung timer. We increment it here so that this
|
||||||
|
// can be called multiple times to tells Windows something is happening.
|
||||||
|
if (st == ServiceState::Starting or st == ServiceState::Stopping)
|
||||||
|
_status.dwCheckPoint++;
|
||||||
|
else
|
||||||
|
_status.dwCheckPoint = 0;
|
||||||
|
|
||||||
|
report_changed_state();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SVC_Manager _manager{};
|
||||||
|
I_SystemLayerManager* const service_manager = &_manager;
|
||||||
|
} // namespace llarp::sys
|
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <chrono>
|
||||||
|
#include <llarp/util/service_manager.hpp>
|
||||||
|
#include <llarp/util/types.hpp>
|
||||||
|
|
||||||
|
namespace llarp::sys
|
||||||
|
{
|
||||||
|
|
||||||
|
class SVC_Manager : public I_SystemLayerManager
|
||||||
|
{
|
||||||
|
SERVICE_STATUS _status;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SERVICE_STATUS_HANDLE handle;
|
||||||
|
|
||||||
|
// How long we tell Windows to give us to startup before assuming we have stalled/hung. The
|
||||||
|
// biggest potential time here is wintun, which if it is going to fail appears to take around
|
||||||
|
// 15s before doing so.
|
||||||
|
static constexpr auto StartupTimeout = 17s;
|
||||||
|
|
||||||
|
// How long we tell Windows to give us to fully stop before killing us.
|
||||||
|
static constexpr auto StopTimeout = 5s;
|
||||||
|
|
||||||
|
SVC_Manager();
|
||||||
|
|
||||||
|
void
|
||||||
|
system_changed_our_state(ServiceState st) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
report_changed_state() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
we_changed_our_state(ServiceState st) override;
|
||||||
|
};
|
||||||
|
} // namespace llarp::sys
|
Loading…
Reference in New Issue