refactor: add more C++ glue for the daemon (#3815)

Get rid of C-style callbacks on void pointers and convert (well, the
most of) daemon code to (somewhat more) real C++, adjust related bits.
This commit is contained in:
Dmitry Antipov 2022-09-13 09:37:12 +03:00 committed by GitHub
parent 3c8d8488ea
commit 161330ae16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 138 additions and 200 deletions

View File

@ -19,15 +19,8 @@
#include <fmt/format.h>
#include <libtransmission/transmission.h>
#include <libtransmission/error.h>
#include <libtransmission/utils.h>
#include "daemon.h"
static dtr_callbacks const* callbacks = nullptr;
static void* callback_arg = nullptr;
static void set_system_error(tr_error** error, int code, std::string_view message)
{
tr_error_set(error, code, fmt::format(FMT_STRING("{:s}: {:s} ({:d}"), message, tr_strerror(code), code));
@ -35,41 +28,35 @@ static void set_system_error(tr_error** error, int code, std::string_view messag
#ifdef HAVE_SYS_SIGNALFD_H
static int sigfd = -1;
static void handle_signal(int sig)
{
switch (sig)
{
case SIGHUP:
callbacks->on_reconfigure(callback_arg);
break;
case SIGINT:
case SIGTERM:
callbacks->on_stop(callback_arg);
break;
default:
assert("Unexpected signal" && 0);
}
}
static void handle_signals(evutil_socket_t fd, short /*what*/, void* /*arg*/)
static void handle_signals(evutil_socket_t fd, short /*what*/, void* arg)
{
struct signalfd_siginfo fdsi;
auto* const daemon = static_cast<tr_daemon*>(arg);
if (read(fd, &fdsi, sizeof(fdsi)) != sizeof(fdsi))
assert("Error reading signal descriptor" && 0);
else
handle_signal(fdsi.ssi_signo);
{
switch (fdsi.ssi_signo)
{
case SIGHUP:
daemon->reconfigure();
break;
case SIGINT:
case SIGTERM:
daemon->stop();
break;
default:
assert("Unexpected signal" && 0);
}
}
}
static bool setup_signals(void* arg)
bool tr_daemon::setup_signals()
{
sigset_t mask = {};
struct event* sigev = nullptr;
struct event_base* base = static_cast<struct event_base*>(arg);
struct event_base* base = ev_base_;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
@ -79,21 +66,21 @@ static bool setup_signals(void* arg)
if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0)
return false;
sigfd = signalfd(-1, &mask, 0);
if (sigfd < 0)
sigfd_ = signalfd(-1, &mask, 0);
if (sigfd_ < 0)
return false;
sigev = event_new(base, sigfd, EV_READ | EV_PERSIST, handle_signals, nullptr);
sigev = event_new(base, sigfd_, EV_READ | EV_PERSIST, handle_signals, this);
if (sigev == nullptr)
{
close(sigfd);
close(sigfd_);
return false;
}
if (event_add(sigev, nullptr) < 0)
{
event_del(sigev);
close(sigfd);
close(sigfd_);
return false;
}
@ -102,19 +89,19 @@ static bool setup_signals(void* arg)
#else /* no signalfd API, use evsignal */
static void reconfigure(evutil_socket_t /*fd*/, short /*events*/, void* /*arg*/)
static void reconfigureMarshall(evutil_socket_t /*fd*/, short /*events*/, void* arg)
{
callbacks->on_reconfigure(callback_arg);
static_cast<tr_daemon*>(arg)->reconfigure();
}
static void stop(evutil_socket_t /*fd*/, short /*events*/, void* /*arg*/)
static void stopMarshall(evutil_socket_t /*fd*/, short /*events*/, void* arg)
{
callbacks->on_stop(callback_arg);
static_cast<tr_daemon*>(arg)->stop();
}
static bool setup_signal(struct event_base* base, int sig, void (*callback)(evutil_socket_t, short, void*))
static bool setup_signal(struct event_base* base, int sig, void (*callback)(evutil_socket_t, short, void*), void* arg)
{
struct event* sigev = evsignal_new(base, sig, callback, nullptr);
struct event* sigev = evsignal_new(base, sig, callback, arg);
if (sigev == nullptr)
return false;
@ -128,20 +115,16 @@ static bool setup_signal(struct event_base* base, int sig, void (*callback)(evut
return true;
}
static bool setup_signals(void* arg)
bool tr_daemon::setup_signals()
{
struct event_base* base = static_cast<struct event_base*>(arg);
return (setup_signal(base, SIGHUP, reconfigure) && setup_signal(base, SIGINT, stop) && setup_signal(base, SIGTERM, stop));
return setup_signal(ev_base_, SIGHUP, reconfigureMarshall, this) && setup_signal(ev_base_, SIGINT, stopMarshall, this) &&
setup_signal(ev_base_, SIGTERM, stopMarshall, this);
}
#endif /* HAVE_SYS_SIGNALFD_H */
bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exit_code, tr_error** error)
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error** error)
{
callbacks = cb;
callback_arg = cb_arg;
*exit_code = 1;
if (!foreground)
@ -198,7 +181,7 @@ bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exi
#endif
}
*exit_code = cb->on_start(cb_arg, setup_signals, foreground);
*exit_code = start(foreground);
return true;
}

View File

@ -4,23 +4,14 @@
// License text can be found in the licenses/ folder.
#include <process.h> /* _beginthreadex() */
#include <windows.h>
#include <algorithm> /* std::max() */
#include <fmt/format.h>
#include <libtransmission/transmission.h>
#include <libtransmission/error.h>
#include <libtransmission/log.h>
#include <libtransmission/tr-macros.h>
#include <libtransmission/utils.h>
#include "daemon.h"
/***
****
***/
#ifndef SERVICE_ACCEPT_PRESHUTDOWN
#define SERVICE_ACCEPT_PRESHUTDOWN 0x00000100
#endif
@ -28,20 +19,17 @@
#define SERVICE_CONTROL_PRESHUTDOWN 0x0000000F
#endif
static dtr_callbacks const* callbacks = nullptr;
static void* callback_arg = nullptr;
static LPCWSTR const service_name = L"TransmissionDaemon";
// If we can get rid of this global variable...
static tr_daemon* daemon;
// ...these becomes a good candidates for being converted to 'class tr_daemon' members.
static SERVICE_STATUS_HANDLE status_handle = nullptr;
static DWORD current_state = SERVICE_STOPPED;
static HANDLE service_thread = nullptr;
static HANDLE service_stop_thread = nullptr;
/***
****
***/
static void set_system_error(tr_error** error, DWORD code, char const* message)
{
auto const system_message = tr_win32_format_message(code);
@ -51,12 +39,7 @@ static void set_system_error(tr_error** error, DWORD code, char const* message)
static void do_log_system_error(char const* file, int line, tr_log_level level, DWORD code, char const* message)
{
auto const system_message = tr_win32_format_message(code);
tr_logAddMessage(
file,
line,
level,
"dtr_daemon",
fmt::format("[dtr_daemon] {} ({:#x}): {}", message, code, system_message));
tr_logAddMessage(file, line, level, "tr_daemon", fmt::format("[tr_daemon] {} ({:#x}): {}", message, code, system_message));
}
#define log_system_error(level, code, message) \
@ -70,13 +53,9 @@ static void do_log_system_error(char const* file, int line, tr_log_level level,
} \
} while (0)
/***
****
***/
static BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/)
{
callbacks->on_stop(callback_arg);
daemon->stop();
return TRUE;
}
@ -108,11 +87,9 @@ static void update_service_status(
}
}
#define TR_MAX(a, b) (((a) > (b)) ? (a) : (b))
static unsigned int __stdcall service_stop_thread_main(void* param)
{
callbacks->on_stop(callback_arg);
daemon->stop();
DWORD const sleep_time = 500;
DWORD wait_time = (DWORD)(UINT_PTR)param;
@ -120,7 +97,7 @@ static unsigned int __stdcall service_stop_thread_main(void* param)
for (DWORD checkpoint = 2; WaitForSingleObject(service_thread, sleep_time) == WAIT_TIMEOUT; ++checkpoint)
{
wait_time = wait_time >= sleep_time ? wait_time - sleep_time : 0;
update_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0, checkpoint, TR_MAX(wait_time, sleep_time * 2));
update_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0, checkpoint, std::max(wait_time, sleep_time * 2));
}
return 0;
@ -158,7 +135,7 @@ static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/
return NO_ERROR;
case SERVICE_CONTROL_PARAMCHANGE:
callbacks->on_reconfigure(callback_arg);
daemon->reconfigure();
return NO_ERROR;
case SERVICE_CONTROL_INTERROGATE:
@ -171,7 +148,7 @@ static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/
static unsigned int __stdcall service_thread_main(void* /*context*/)
{
return callbacks->on_start(callback_arg, nullptr, false);
return daemon->start(false);
}
static VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
@ -219,14 +196,14 @@ static VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
update_service_status(SERVICE_STOPPED, NO_ERROR, exit_code, 0, 0);
}
/***
****
***/
bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exit_code, tr_error** error)
bool tr_daemon::setup_signals()
{
callbacks = cb;
callback_arg = cb_arg;
return true;
}
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error** error)
{
daemon = this;
*exit_code = 1;
@ -238,7 +215,7 @@ bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exi
return false;
}
*exit_code = cb->on_start(cb_arg, nullptr, true);
*exit_code = start(true);
}
else
{

View File

@ -9,8 +9,6 @@
#include <cstdlib> /* atoi */
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#ifdef HAVE_SYSLOG
#include <syslog.h>
@ -26,22 +24,15 @@
#include <fmt/core.h>
#include <libtransmission/transmission.h>
#include "daemon.h"
#include <libtransmission/error.h>
#include <libtransmission/file.h>
#include <libtransmission/log.h>
#include <libtransmission/timer-ev.h>
#include <libtransmission/tr-getopt.h>
#include <libtransmission/tr-macros.h>
#include <libtransmission/tr-strbuf.h>
#include <libtransmission/utils.h>
#include <libtransmission/variant.h>
#include <libtransmission/version.h>
#include <libtransmission/watchdir.h>
#include "daemon.h"
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
@ -60,47 +51,8 @@ static void sd_notifyf(int /*status*/, char const* /*fmt*/, ...)
#endif
using namespace std::literals;
using libtransmission::Watchdir;
class Daemon
{
public:
struct event_base* ev_base_ = nullptr;
tr_sys_file_t logfile_ = TR_BAD_SYS_FILE;
private:
bool seen_hup_ = false;
bool paused_ = false;
std::string config_dir_;
char const* log_file_name_ = nullptr;
tr_session* my_session_ = nullptr;
tr_variant settings_ = {};
tr_quark key_pidfile_ = tr_quark_new("pidfile"sv);
tr_quark key_watch_dir_force_generic_ = tr_quark_new("watch-dir-force-generic"sv);
public:
Daemon() = default;
~Daemon()
{
tr_variantClear(&settings_);
}
bool init(int argc, char* argv[], bool* foreground, int* ret);
bool parseArgs(int argc, char const** argv, bool* dump_settings, bool* foreground, int* exit_code);
bool reopenLogFile(char const* filename);
int start(bool (*setupsigfn)(void*), bool foreground);
void periodicUpdate();
void reportStatus();
void reconfigure();
void stop();
};
/***
****
***/
static char constexpr MyName[] = "transmission-daemon";
static char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
" https://transmissionbt.com/\n"
@ -201,7 +153,7 @@ static auto constexpr Options = std::array<tr_option, 45>{
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
bool Daemon::reopenLogFile(char const* filename)
bool tr_daemon::reopen_log_file(char const* filename)
{
tr_error* error = nullptr;
tr_sys_file_t const old_log_file = logfile_;
@ -426,7 +378,7 @@ static void pumpLogMessages(tr_sys_file_t file)
tr_logFreeQueue(list);
}
void Daemon::reportStatus(void)
void tr_daemon::report_status(void)
{
double const up = tr_sessionGetRawSpeed_KBps(my_session_, TR_UP);
double const dn = tr_sessionGetRawSpeed_KBps(my_session_, TR_DOWN);
@ -441,15 +393,15 @@ void Daemon::reportStatus(void)
}
}
void Daemon::periodicUpdate(void)
void tr_daemon::periodic_update(void)
{
pumpLogMessages(logfile_);
reportStatus();
report_status();
}
static void periodicUpdate(evutil_socket_t /*fd*/, short /*what*/, void* arg)
static void periodic_update(evutil_socket_t /*fd*/, short /*what*/, void* arg)
{
static_cast<Daemon*>(arg)->periodicUpdate();
static_cast<tr_daemon*>(arg)->periodic_update();
}
static tr_rpc_callback_status on_rpc_callback(
@ -458,17 +410,14 @@ static tr_rpc_callback_status on_rpc_callback(
tr_torrent* /*tor*/,
void* arg)
{
Daemon* daemon = static_cast<Daemon*>(arg);
if (type == TR_RPC_SESSION_CLOSE)
{
event_base_loopexit(daemon->ev_base_, nullptr);
static_cast<tr_daemon*>(arg)->stop();
}
return TR_RPC_OK;
}
bool Daemon::parseArgs(int argc, char const** argv, bool* dump_settings, bool* foreground, int* exit_code)
bool tr_daemon::parse_args(int argc, char const** argv, bool* dump_settings, bool* foreground, int* exit_code)
{
int c;
char const* optstr;
@ -523,7 +472,7 @@ bool Daemon::parseArgs(int argc, char const** argv, bool* dump_settings, bool* f
break;
case 'e':
if (reopenLogFile(optstr))
if (reopen_log_file(optstr))
{
log_file_name_ = optstr;
}
@ -687,12 +636,7 @@ bool Daemon::parseArgs(int argc, char const** argv, bool* dump_settings, bool* f
return true;
}
static void daemon_reconfigure(void* arg)
{
static_cast<Daemon*>(arg)->reconfigure();
}
void Daemon::reconfigure(void)
void tr_daemon::reconfigure(void)
{
if (my_session_ == nullptr)
{
@ -707,7 +651,7 @@ void Daemon::reconfigure(void)
/* reopen the logfile to allow for log rotation */
if (log_file_name_ != nullptr)
{
reopenLogFile(log_file_name_);
reopen_log_file(log_file_name_);
}
configDir = tr_sessionGetConfigDir(my_session_);
@ -721,17 +665,12 @@ void Daemon::reconfigure(void)
}
}
static void daemon_stop(void* arg)
{
static_cast<Daemon*>(arg)->stop();
}
void Daemon::stop(void)
void tr_daemon::stop(void)
{
event_base_loopexit(ev_base_, nullptr);
}
int Daemon::start(bool (*setupsigfn)(void*), bool foreground)
int tr_daemon::start([[maybe_unused]] bool foreground)
{
bool boolVal;
bool pidfile_created = false;
@ -748,7 +687,7 @@ int Daemon::start(bool (*setupsigfn)(void*), bool foreground)
/* setup event state */
ev_base_ = event_base_new();
if (ev_base_ == nullptr || (setupsigfn ? setupsigfn(ev_base_) : true) == false)
if (ev_base_ == nullptr || setup_signals() == false)
{
auto const error_code = errno;
auto const errmsg = fmt::format(
@ -809,7 +748,7 @@ int Daemon::start(bool (*setupsigfn)(void*), bool foreground)
/* If we got a SIGHUP during startup, process that now. */
if (seen_hup_)
{
daemon_reconfigure(this);
reconfigure();
}
/* maybe add a watchdir */
@ -860,7 +799,7 @@ int Daemon::start(bool (*setupsigfn)(void*), bool foreground)
/* Create new timer event to report daemon status */
{
constexpr auto one_sec = timeval{ 1, 0 }; // 1 second
status_ev = event_new(ev_base_, -1, EV_PERSIST, &::periodicUpdate, this);
status_ev = event_new(ev_base_, -1, EV_PERSIST, &::periodic_update, this);
if (status_ev == nullptr)
{
@ -937,12 +876,7 @@ CLEANUP:
return 0;
}
static int daemon_start(void* varg, bool (*setupsigfn)(void*), bool foreground)
{
return static_cast<Daemon*>(varg)->start(setupsigfn, foreground);
}
bool Daemon::init(int argc, char* argv[], bool* foreground, int* ret)
bool tr_daemon::init(int argc, char* argv[], bool* foreground, int* ret)
{
config_dir_ = getConfigDir(argc, (char const* const*)argv);
@ -956,7 +890,7 @@ bool Daemon::init(int argc, char* argv[], bool* foreground, int* ret)
*ret = 0;
/* overwrite settings from the command line */
if (!parseArgs(argc, (char const**)argv, &dumpSettings, foreground, ret))
if (!parse_args(argc, (char const**)argv, &dumpSettings, foreground, ret))
{
goto EXIT_EARLY;
}
@ -987,29 +921,27 @@ EXIT_EARLY:
return false;
}
void tr_daemon::handle_error(tr_error* error)
{
auto const errmsg = fmt::format(FMT_STRING("Couldn't daemonize: {:s} ({:d})"), error->message, error->code);
printMessage(logfile_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
tr_error_free(error);
}
int tr_main(int argc, char* argv[])
{
Daemon daemon;
bool foreground;
int ret;
tr_daemon daemon;
bool foreground;
tr_error* error = nullptr;
if (!daemon.init(argc, argv, &foreground, &ret))
{
return ret;
}
auto constexpr cb = dtr_callbacks{
&daemon_start,
&daemon_stop,
&daemon_reconfigure,
};
if (tr_error* error = nullptr; !dtr_daemon(&cb, &daemon, foreground, &ret, &error))
if (!daemon.spawn(foreground, &ret, &error))
{
auto const errmsg = fmt::format(FMT_STRING("Couldn't daemonize: {:s} ({:d})"), error->message, error->code);
printMessage(daemon.logfile_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
tr_error_free(error);
daemon.handle_error(error);
}
return ret;
}

View File

@ -5,13 +5,59 @@
#pragma once
struct tr_error;
#include <string>
#include <string_view>
typedef struct dtr_callbacks
#include <libtransmission/transmission.h>
#include <libtransmission/variant.h>
#include <libtransmission/error.h>
#include <libtransmission/utils.h>
#include <libtransmission/file.h>
#include <libtransmission/log.h>
using namespace std::literals;
class tr_daemon
{
int (*on_start)(void* arg, bool (*setupsigfn)(void*), bool foreground);
void (*on_stop)(void* arg);
void (*on_reconfigure)(void* arg);
} dtr_callbacks;
public:
tr_daemon() = default;
bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exit_code, struct tr_error** error);
~tr_daemon()
{
#ifdef HAVE_SYS_SIGNALFD_H
if (sigfd_ != -1)
{
close(sigfd_);
}
#endif /* signalfd API */
tr_variantClear(&settings_);
}
bool spawn(bool foreground, int* exit_code, tr_error** error);
bool init(int argc, char* argv[], bool* foreground, int* ret);
void handle_error(tr_error*);
int start(bool foreground);
void periodic_update();
void reconfigure();
void stop();
private:
#ifdef HAVE_SYS_SIGNALFD_H
int sigfd_ = -1;
#endif /* signalfd API */
bool paused_ = false;
bool seen_hup_ = false;
std::string config_dir_;
tr_variant settings_ = {};
tr_session* my_session_ = nullptr;
char const* log_file_name_ = nullptr;
struct event_base* ev_base_ = nullptr;
tr_sys_file_t logfile_ = TR_BAD_SYS_FILE;
tr_quark key_pidfile_ = tr_quark_new("pidfile"sv);
tr_quark key_watch_dir_force_generic_ = tr_quark_new("watch-dir-force-generic"sv);
bool parse_args(int argc, char const** argv, bool* dump_settings, bool* foreground, int* exit_code);
bool reopen_log_file(char const* filename);
bool setup_signals();
void report_status();
};