diff --git a/daemon/daemon-posix.cc b/daemon/daemon-posix.cc index 3ba41e48b..cb87a06be 100644 --- a/daemon/daemon-posix.cc +++ b/daemon/daemon-posix.cc @@ -19,15 +19,8 @@ #include -#include -#include -#include - #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(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(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(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(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(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; } diff --git a/daemon/daemon-win32.cc b/daemon/daemon-win32.cc index 7392b0e46..22656048e 100644 --- a/daemon/daemon-win32.cc +++ b/daemon/daemon-win32.cc @@ -4,23 +4,14 @@ // License text can be found in the licenses/ folder. #include /* _beginthreadex() */ - #include +#include /* std::max() */ + #include -#include -#include -#include -#include -#include - #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 { diff --git a/daemon/daemon.cc b/daemon/daemon.cc index 7c164b2f6..bebcb5390 100644 --- a/daemon/daemon.cc +++ b/daemon/daemon.cc @@ -9,8 +9,6 @@ #include /* atoi */ #include #include -#include -#include #ifdef HAVE_SYSLOG #include @@ -26,22 +24,15 @@ #include -#include +#include "daemon.h" -#include -#include -#include #include #include #include #include -#include -#include #include #include -#include "daemon.h" - #ifdef USE_SYSTEMD #include @@ -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{ { 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(arg)->periodicUpdate(); + static_cast(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(arg); - if (type == TR_RPC_SESSION_CLOSE) { - event_base_loopexit(daemon->ev_base_, nullptr); + static_cast(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(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(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(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; } diff --git a/daemon/daemon.h b/daemon/daemon.h index e598f90e7..375535910 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -5,13 +5,59 @@ #pragma once -struct tr_error; +#include +#include -typedef struct dtr_callbacks +#include +#include +#include +#include +#include +#include + +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(); +};