daemon: use evsignal-based signal handling on POSIX systems (#3782)

If signalfd(2) interface is not available (i.e. everywhere
except Linux), use evsignal interface to manage signals.
This commit is contained in:
Dmitry Antipov 2022-09-08 12:23:54 +03:00 committed by GitHub
parent 9fb590d3f5
commit 828d008f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 22 additions and 111 deletions

View File

@ -7,9 +7,9 @@
#include <errno.h> #include <errno.h>
#include <pthread.h> #include <pthread.h>
#ifdef HAVE_SYS_SIGNALFD_H #ifdef HAVE_SYS_SIGNALFD_H
#include <event2/event.h>
#include <sys/signalfd.h> #include <sys/signalfd.h>
#endif /* signalfd API */ #endif /* signalfd API */
#include <event2/event.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h> /* abort(), daemon(), exit() */ #include <stdlib.h> /* abort(), daemon(), exit() */
#include <fcntl.h> /* open() */ #include <fcntl.h> /* open() */
@ -25,33 +25,17 @@
#include "daemon.h" #include "daemon.h"
using namespace std::literals;
/***
****
***/
static dtr_callbacks const* callbacks = nullptr; static dtr_callbacks const* callbacks = nullptr;
static void* callback_arg = nullptr; static void* callback_arg = nullptr;
#ifdef HAVE_SYS_SIGNALFD_H
static int sigfd = -1;
#else
static int signal_pipe[2];
#endif /* signalfd API */
/***
****
***/
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));
} }
/*** #ifdef HAVE_SYS_SIGNALFD_H
****
***/ static int sigfd = -1;
static void handle_signal(int sig) static void handle_signal(int sig)
{ {
@ -71,8 +55,6 @@ static void handle_signal(int sig)
} }
} }
#ifdef HAVE_SYS_SIGNALFD_H
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; struct signalfd_siginfo fdsi;
@ -118,92 +100,42 @@ static bool setup_signals(void* arg)
return true; return true;
} }
#else /* no signalfd API */ #else /* no signalfd API, use evsignal */
static void send_signal_to_pipe(int sig) static void reconfigure(evutil_socket_t /*fd*/, short /*events*/, void* /*arg*/)
{ {
int const old_errno = errno; callbacks->on_reconfigure(callback_arg);
if (write(signal_pipe[1], &sig, sizeof(sig)) == -1)
{
abort();
}
errno = old_errno;
} }
static void* signal_handler_thread_main(void* /*arg*/) static void stop(evutil_socket_t /*fd*/, short /*events*/, void* /*arg*/)
{ {
int sig; callbacks->on_stop(callback_arg);
while (read(signal_pipe[0], &sig, sizeof(sig)) == sizeof(sig) && sig != 0)
{
handle_signal(sig);
}
return nullptr;
} }
static bool create_signal_pipe(tr_error** error) static bool setup_signal(struct event_base* base, int sig, void (*callback)(evutil_socket_t, short, void*))
{ {
if (pipe(signal_pipe) == -1) struct event* sigev = evsignal_new(base, sig, callback, nullptr);
if (sigev == nullptr)
return false;
if (evsignal_add(sigev, nullptr) < 0)
{ {
set_system_error(error, errno, "pipe() failed"); event_free(sigev);
return false; return false;
} }
return true; return true;
} }
static void destroy_signal_pipe(void) static bool setup_signals(void* arg)
{ {
close(signal_pipe[0]); struct event_base* base = static_cast<struct event_base*>(arg);
close(signal_pipe[1]);
return (setup_signal(base, SIGHUP, reconfigure) && setup_signal(base, SIGINT, stop) && setup_signal(base, SIGTERM, stop));
} }
static bool create_signal_handler_thread(pthread_t* thread, tr_error** error) #endif /* HAVE_SYS_SIGNALFD_H */
{
if (!create_signal_pipe(error))
{
return false;
}
if ((errno = pthread_create(thread, nullptr, &signal_handler_thread_main, nullptr)) != 0)
{
set_system_error(error, errno, "pthread_create() failed");
destroy_signal_pipe();
return false;
}
return true;
}
static void destroy_signal_handler_thread(pthread_t thread)
{
send_signal_to_pipe(0);
pthread_join(thread, nullptr);
destroy_signal_pipe();
}
static bool setup_signal_handler(int sig, tr_error** error)
{
assert(sig != 0);
if (signal(sig, &send_signal_to_pipe) == SIG_ERR)
{
set_system_error(error, errno, "signal() failed");
return false;
}
return true;
}
#endif /* signalfd API */
/***
****
***/
bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exit_code, tr_error** error) bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exit_code, tr_error** error)
{ {
@ -266,28 +198,7 @@ bool dtr_daemon(dtr_callbacks const* cb, void* cb_arg, bool foreground, int* exi
#endif #endif
} }
#ifndef HAVE_SYS_SIGNALFD_H
pthread_t signal_thread;
if (!create_signal_handler_thread(&signal_thread, error))
{
return false;
}
if (!setup_signal_handler(SIGINT, error) || !setup_signal_handler(SIGTERM, error) || !setup_signal_handler(SIGHUP, error))
{
destroy_signal_handler_thread(signal_thread);
return false;
}
*exit_code = cb->on_start(cb_arg, nullptr, foreground);
#else
*exit_code = cb->on_start(cb_arg, setup_signals, foreground); *exit_code = cb->on_start(cb_arg, setup_signals, foreground);
#endif /* signalfd API */
#ifndef HAVE_SYS_SIGNALFD_H
destroy_signal_handler_thread(signal_thread);
#endif /* no signalfd API */
return true; return true;
} }