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 <fmt/format.h>
#include <libtransmission/transmission.h>
#include <libtransmission/error.h>
#include <libtransmission/utils.h>
#include "daemon.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) 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)); 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 #ifdef HAVE_SYS_SIGNALFD_H
static int sigfd = -1; static void handle_signals(evutil_socket_t fd, short /*what*/, void* arg)
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*/)
{ {
struct signalfd_siginfo fdsi; struct signalfd_siginfo fdsi;
auto* const daemon = static_cast<tr_daemon*>(arg);
if (read(fd, &fdsi, sizeof(fdsi)) != sizeof(fdsi)) if (read(fd, &fdsi, sizeof(fdsi)) != sizeof(fdsi))
assert("Error reading signal descriptor" && 0); assert("Error reading signal descriptor" && 0);
else 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 = {}; sigset_t mask = {};
struct event* sigev = nullptr; struct event* sigev = nullptr;
struct event_base* base = static_cast<struct event_base*>(arg); struct event_base* base = ev_base_;
sigemptyset(&mask); sigemptyset(&mask);
sigaddset(&mask, SIGINT); sigaddset(&mask, SIGINT);
@ -79,21 +66,21 @@ static bool setup_signals(void* arg)
if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0)
return false; return false;
sigfd = signalfd(-1, &mask, 0); sigfd_ = signalfd(-1, &mask, 0);
if (sigfd < 0) if (sigfd_ < 0)
return false; 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) if (sigev == nullptr)
{ {
close(sigfd); close(sigfd_);
return false; return false;
} }
if (event_add(sigev, nullptr) < 0) if (event_add(sigev, nullptr) < 0)
{ {
event_del(sigev); event_del(sigev);
close(sigfd); close(sigfd_);
return false; return false;
} }
@ -102,19 +89,19 @@ static bool setup_signals(void* arg)
#else /* no signalfd API, use evsignal */ #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) if (sigev == nullptr)
return false; return false;
@ -128,20 +115,16 @@ static bool setup_signal(struct event_base* base, int sig, void (*callback)(evut
return true; 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(ev_base_, SIGHUP, reconfigureMarshall, this) && setup_signal(ev_base_, SIGINT, stopMarshall, this) &&
setup_signal(ev_base_, SIGTERM, stopMarshall, this);
return (setup_signal(base, SIGHUP, reconfigure) && setup_signal(base, SIGINT, stop) && setup_signal(base, SIGTERM, stop));
} }
#endif /* HAVE_SYS_SIGNALFD_H */ #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; *exit_code = 1;
if (!foreground) if (!foreground)
@ -198,7 +181,7 @@ bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exi
#endif #endif
} }
*exit_code = cb->on_start(cb_arg, setup_signals, foreground); *exit_code = start(foreground);
return true; return true;
} }

View File

@ -4,23 +4,14 @@
// License text can be found in the licenses/ folder. // License text can be found in the licenses/ folder.
#include <process.h> /* _beginthreadex() */ #include <process.h> /* _beginthreadex() */
#include <windows.h> #include <windows.h>
#include <algorithm> /* std::max() */
#include <fmt/format.h> #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" #include "daemon.h"
/***
****
***/
#ifndef SERVICE_ACCEPT_PRESHUTDOWN #ifndef SERVICE_ACCEPT_PRESHUTDOWN
#define SERVICE_ACCEPT_PRESHUTDOWN 0x00000100 #define SERVICE_ACCEPT_PRESHUTDOWN 0x00000100
#endif #endif
@ -28,20 +19,17 @@
#define SERVICE_CONTROL_PRESHUTDOWN 0x0000000F #define SERVICE_CONTROL_PRESHUTDOWN 0x0000000F
#endif #endif
static dtr_callbacks const* callbacks = nullptr;
static void* callback_arg = nullptr;
static LPCWSTR const service_name = L"TransmissionDaemon"; 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 SERVICE_STATUS_HANDLE status_handle = nullptr;
static DWORD current_state = SERVICE_STOPPED; static DWORD current_state = SERVICE_STOPPED;
static HANDLE service_thread = nullptr; static HANDLE service_thread = nullptr;
static HANDLE service_stop_thread = nullptr; static HANDLE service_stop_thread = nullptr;
/***
****
***/
static void set_system_error(tr_error** error, DWORD code, char const* message) static void set_system_error(tr_error** error, DWORD code, char const* message)
{ {
auto const system_message = tr_win32_format_message(code); 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) 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); auto const system_message = tr_win32_format_message(code);
tr_logAddMessage( tr_logAddMessage(file, line, level, "tr_daemon", fmt::format("[tr_daemon] {} ({:#x}): {}", message, code, system_message));
file,
line,
level,
"dtr_daemon",
fmt::format("[dtr_daemon] {} ({:#x}): {}", message, code, system_message));
} }
#define log_system_error(level, code, 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) } while (0)
/***
****
***/
static BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/) static BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/)
{ {
callbacks->on_stop(callback_arg); daemon->stop();
return TRUE; 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) static unsigned int __stdcall service_stop_thread_main(void* param)
{ {
callbacks->on_stop(callback_arg); daemon->stop();
DWORD const sleep_time = 500; DWORD const sleep_time = 500;
DWORD wait_time = (DWORD)(UINT_PTR)param; 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) for (DWORD checkpoint = 2; WaitForSingleObject(service_thread, sleep_time) == WAIT_TIMEOUT; ++checkpoint)
{ {
wait_time = wait_time >= sleep_time ? wait_time - sleep_time : 0; 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; return 0;
@ -158,7 +135,7 @@ static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/
return NO_ERROR; return NO_ERROR;
case SERVICE_CONTROL_PARAMCHANGE: case SERVICE_CONTROL_PARAMCHANGE:
callbacks->on_reconfigure(callback_arg); daemon->reconfigure();
return NO_ERROR; return NO_ERROR;
case SERVICE_CONTROL_INTERROGATE: 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*/) 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*/) 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); update_service_status(SERVICE_STOPPED, NO_ERROR, exit_code, 0, 0);
} }
/*** bool tr_daemon::setup_signals()
****
***/
bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exit_code, tr_error** error)
{ {
callbacks = cb; return true;
callback_arg = cb_arg; }
bool tr_daemon::spawn(bool foreground, int* exit_code, tr_error** error)
{
daemon = this;
*exit_code = 1; *exit_code = 1;
@ -238,7 +215,7 @@ bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exi
return false; return false;
} }
*exit_code = cb->on_start(cb_arg, nullptr, true); *exit_code = start(true);
} }
else else
{ {

View File

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

View File

@ -5,13 +5,59 @@
#pragma once #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); public:
void (*on_stop)(void* arg); tr_daemon() = default;
void (*on_reconfigure)(void* arg);
} dtr_callbacks;
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();
};