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:
parent
9fb590d3f5
commit
828d008f82
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue