mirror of https://github.com/oxen-io/lokinet
system layer manager (llarp::sys::service_manager)
the win32 and sd_notify components provided a disjointed set of similar high level functionality so we consolidate these duplicate code paths into one that has the same lifecycle regardless of platform to reduce complexity of this feature. this new component is responsible for reporting state changes to the system layer and optionally propagating state change to lokinet requested by the system layer (used by windows service).pull/2045/head
parent
a7f3c3595b
commit
4103908a8d
@ -0,0 +1,60 @@
|
||||
#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
|
||||
{
|
||||
assert(m_State != st);
|
||||
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,85 @@
|
||||
#include <windows.h>
|
||||
#include <llarp.hpp>
|
||||
#include "service_manager.hpp"
|
||||
#include <dbghelp.h>
|
||||
#include <cassert>
|
||||
#include <csignal>
|
||||
#include <optional>
|
||||
|
||||
namespace llarp::sys
|
||||
{
|
||||
|
||||
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);
|
||||
m_Context->HandleSignal(SIGINT);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SVC_Manager::report_changed_state()
|
||||
{
|
||||
if (m_disable)
|
||||
return;
|
||||
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;
|
||||
assert(_status.dwCurrentState != new_state);
|
||||
_status.dwCurrentState = new_state;
|
||||
// tell windows it takes 5s at most to start or stop
|
||||
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,24 @@
|
||||
#pragma once
|
||||
#include <llarp/util/service_manager.hpp>
|
||||
namespace llarp::sys
|
||||
{
|
||||
|
||||
class SVC_Manager : public I_SystemLayerManager
|
||||
{
|
||||
SERVICE_STATUS _status;
|
||||
|
||||
public:
|
||||
SERVICE_STATUS_HANDLE handle;
|
||||
|
||||
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