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 <pthread.h>
#ifdef HAVE_SYS_SIGNALFD_H
#include <event2/event.h>
#include <sys/signalfd.h>
#endif /* signalfd API */
#include <event2/event.h>
#include <signal.h>
#include <stdlib.h> /* abort(), daemon(), exit() */
#include <fcntl.h> /* open() */
@ -25,33 +25,17 @@
#include "daemon.h"
using namespace std::literals;
/***
****
***/
static dtr_callbacks const* callbacks = 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)
{
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)
{
@ -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*/)
{
struct signalfd_siginfo fdsi;
@ -118,92 +100,42 @@ static bool setup_signals(void* arg)
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;
if (write(signal_pipe[1], &sig, sizeof(sig)) == -1)
{
abort();
}
errno = old_errno;
callbacks->on_reconfigure(callback_arg);
}
static void* signal_handler_thread_main(void* /*arg*/)
static void stop(evutil_socket_t /*fd*/, short /*events*/, void* /*arg*/)
{
int sig;
while (read(signal_pipe[0], &sig, sizeof(sig)) == sizeof(sig) && sig != 0)
{
handle_signal(sig);
}
return nullptr;
callbacks->on_stop(callback_arg);
}
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 true;
}
static void destroy_signal_pipe(void)
static bool setup_signals(void* arg)
{
close(signal_pipe[0]);
close(signal_pipe[1]);
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));
}
static bool create_signal_handler_thread(pthread_t* thread, tr_error** error)
{
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 */
/***
****
***/
#endif /* HAVE_SYS_SIGNALFD_H */
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
}
#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);
#endif /* signalfd API */
#ifndef HAVE_SYS_SIGNALFD_H
destroy_signal_handler_thread(signal_thread);
#endif /* no signalfd API */
return true;
}