improving signal handling of the App class

This commit is contained in:
hyung-hwan 2018-09-06 09:59:54 +00:00
parent e8804b20ec
commit 5f29d1670f
3 changed files with 111 additions and 38 deletions

View File

@ -41,20 +41,22 @@ public:
App (Mmgr* mmgr) QSE_CPP_NOEXCEPT;
virtual ~App () QSE_CPP_NOEXCEPT;
int daemonize (bool chdir_to_root = true, int fork_count = 1) QSE_CPP_NOEXCEPT;
int daemonize (bool chdir_to_root = true, int fork_count = 1, bool root_only = false) QSE_CPP_NOEXCEPT;
int chroot (const qse_mchar_t* mpath) QSE_CPP_NOEXCEPT;
int chroot (const qse_wchar_t* wpath) QSE_CPP_NOEXCEPT;
#if 0
int switchUser (uid_t uid, gid_t gid, bool permanently) QSE_CPP_NOEXCEPT;
int switchUser (qse_uid_t uid, qse_gid_t gid, bool permanently) QSE_CPP_NOEXCEPT;
int restoreUser () QSE_CPP_NOEXCEPT;
#endif
virtual void on_signal (int sig) { }
int subscribeToSignal (int sig, bool accept);
int subscribeToAllSignals (bool accept);
void unsubscribeFromSignal (int sig);
void unsubscribeFromAllSignals ();
typedef void (*SignalHandler) (int sig);
static qse_size_t _sighrs[2][QSE_NSIGS];
@ -70,17 +72,13 @@ public:
int guardProcess (const qse_mchar_t* proc_name);
protected:
bool _root_only;
private:
App* _prev_app;
App* _next_app;
public:
struct _SigLink
{
enum
enum State
{
UNHANDLED,
ACCEPTED,
@ -90,17 +88,23 @@ public:
_SigLink(): _prev(QSE_NULL), _next(QSE_NULL), _state(UNHANDLED) {}
App* _prev;
App* _next;
int _state;
State _state;
};
_SigLink _sig[QSE_NSIGS];
long int _guarded_child_pid;
protected:
static int set_signal_handler_no_mutex (int sig, SignalHandler sighr);
static int unset_signal_handler_no_mutex (int sig);
int subscribe_to_signal_no_mutex (int sig, _SigLink::State reqstate);
void unsubscribe_from_signal_no_mutex (int sig);
void unsubscribe_from_all_signals_no_mutex ();
void on_guard_signal (int sig);
static void handle_signal (int sig);
};
// functor as a template parameter

View File

@ -30,6 +30,7 @@
#include "../cmn/syscall.h"
#include <qse/cmn/mbwc.h>
extern "C" { int printf (const char* fmt, ...); }
/////////////////////////////////
QSE_BEGIN_NAMESPACE(QSE)
/////////////////////////////////
@ -43,7 +44,7 @@ static struct
int sa_flags;
} g_app_oldsi[QSE_NSIGS] = { { 0, 0 }, };
App::App (Mmgr* mmgr) QSE_CPP_NOEXCEPT: Mmged(mmgr), _root_only(false), _prev_app(QSE_NULL), _next_app(QSE_NULL)
App::App (Mmgr* mmgr) QSE_CPP_NOEXCEPT: Mmged(mmgr), _prev_app(QSE_NULL), _next_app(QSE_NULL), _guarded_child_pid(-1)
{
ScopedMutexLocker sml(g_app_mutex);
if (!g_app_top)
@ -70,9 +71,9 @@ App::~App () QSE_CPP_NOEXCEPT
}
}
int App::daemonize (bool chdir_to_root, int fork_count) QSE_CPP_NOEXCEPT
int App::daemonize (bool chdir_to_root, int fork_count, bool root_only) QSE_CPP_NOEXCEPT
{
if (this->_root_only && QSE_GETEUID() != 0) return -1;
if (root_only && QSE_GETEUID() != 0) return -1;
if (fork_count >= 1)
{
@ -269,7 +270,8 @@ int App::unset_signal_handler_no_mutex(int sig)
return 0;
}
static void handle_signal (int sig)
/*static*/ void App::handle_signal (int sig)
{
ScopedMutexLocker sml(g_app_mutex);
App* app = g_app_sig[sig];
@ -282,18 +284,75 @@ static void handle_signal (int sig)
// the actual signal handler is called with the mutex locked.
// it must not call subscribeToSingal() or unsubscribeFromSingal()
// from within the handler.
app->on_signal (sig);
if (app->_guarded_child_pid >= 0) app->on_guard_signal (sig);
else app->on_signal (sig);
}
app = next;
}
}
void App::on_guard_signal (int sig)
{
printf ("relaying %d to %d\n", sig, this->_guarded_child_pid);
::kill (this->_guarded_child_pid, sig);
}
int App::subscribeToSignal (int sig, bool accept)
{
QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS);
int reqstate = accept? _SigLink::ACCEPTED: _SigLink::IGNORED;
_SigLink::State reqstate = accept? _SigLink::ACCEPTED: _SigLink::IGNORED;
ScopedMutexLocker sml(g_app_mutex);
return this->subscribe_to_signal_no_mutex(sig, reqstate);
}
int App::subscribeToAllSignals (bool accept)
{
_SigLink::State reqstate = accept? _SigLink::ACCEPTED: _SigLink::IGNORED;
_SigLink::State _old_state[QSE_NSIGS];
ScopedMutexLocker sml(g_app_mutex);
for (int i = 0; i < QSE_NSIGS; i++)
{
_old_state[i] = this->_sig[i]._state;
if (this->subscribe_to_signal_no_mutex(i, reqstate) <= -1)
{
// roll back on a best-efforts basis.
while (i > 0)
{
--i;
switch (_old_state[i])
{
case _SigLink::UNHANDLED:
this->unsubscribe_from_signal_no_mutex (i);
break;
case _SigLink::ACCEPTED:
case _SigLink::IGNORED:
this->subscribe_to_signal_no_mutex (i, _old_state[i]);
break;
}
}
return -1;
}
}
return 0;
}
void App::unsubscribeFromSignal (int sig)
{
QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS);
ScopedMutexLocker sml(g_app_mutex);
this->unsubscribe_from_signal_no_mutex (sig);
}
void App::unsubscribeFromAllSignals ()
{
ScopedMutexLocker sml(g_app_mutex);
this->unsubscribe_from_all_signals_no_mutex ();
}
int App::subscribe_to_signal_no_mutex (int sig, _SigLink::State reqstate)
{
if (sig == SIGKILL || sig == SIGSTOP) return 0;
_SigLink& sl = this->_sig[sig];
if (QSE_LIKELY(sl._state == _SigLink::UNHANDLED))
@ -315,7 +374,7 @@ int App::subscribeToSignal (int sig, bool accept)
{
// no application is set to accept this signal.
// this is the first time to
if (App::set_signal_handler_no_mutex(sig, handle_signal) <= -1)
if (App::set_signal_handler_no_mutex(sig, App::handle_signal) <= -1)
{
// roll back
g_app_sig[sig] = xapp;
@ -323,6 +382,7 @@ int App::subscribeToSignal (int sig, bool accept)
sl._state = _SigLink::UNHANDLED;
sl._next = QSE_NULL;
QSE_ASSERT (sl._prev == QSE_NULL);
if (errno == EINVAL) return 0; /// dirty hack???
return -1;
}
}
@ -339,15 +399,10 @@ int App::subscribeToSignal (int sig, bool accept)
return 0;
}
void App::unsubscribeFromSignal (int sig)
{
QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS);
ScopedMutexLocker sml(g_app_mutex);
this->unsubscribe_from_signal_no_mutex (sig);
}
void App::unsubscribe_from_signal_no_mutex (int sig)
{
if (sig == SIGKILL || sig == SIGSTOP) return;
_SigLink& sl = this->_sig[sig];
if (QSE_UNLIKELY(sl._state == _SigLink::UNHANDLED))
{
@ -382,17 +437,33 @@ void App::unsubscribe_from_all_signals_no_mutex()
}
}
int App::guardProcess (const qse_mchar_t* proc_name)
int App::guardProcess (const qse_mchar_t* proc_name, /* TODO: get the list of signals to relay??? */)
{
// TODO: enhance it
while (1)
{
pid_t pid = ::fork();
if (pid == -1) return -1;
if (pid == 0) break; // child
int _SigLink::State old_sig_states[QSE_NSIGS];
this->subscribeToAllSignals(true);
this->_guarded_child_pid = pid;
int status;
while (::waitpid(pid, &status, 0) != pid);
while (::waitpid(pid, &status, 0) != pid)
{
// if there is a signal sent to this guarding process,
// the signal should be relayed to the child process
// via on_guard_signal().
// ------------------------------------------------------
// do nothing inside this loop block.
// ------------------------------------------------------
}
this->_guarded_child_pid = -1;
// TODO: restore signal handlers to the previous states
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
{
return 0;

View File

@ -93,26 +93,24 @@ static int test1()
QSE::HeapMmgr heap_mmgr (QSE::Mmgr::getDFL(), 30000);
MyApp app (&heap_mmgr);
MyApp app2 (&heap_mmgr);
MyApp app3 (&heap_mmgr);
MyApp app4 (&heap_mmgr);
//MyApp app2 (&heap_mmgr);
//MyApp app3 (&heap_mmgr);
//MyApp app4 (&heap_mmgr);
app.subscribeToSignal (SIGINT, true);
app.subscribeToSignal (SIGTERM, true);
app4.subscribeToSignal (SIGINT, true);
app3.subscribeToSignal (SIGINT, true);
app2.subscribeToSignal (SIGINT, true);
//app4.subscribeToSignal (SIGINT, true);
//app3.subscribeToSignal (SIGINT, true);
//app2.subscribeToSignal (SIGINT, true);
int n = app.run();
app.subscribeToSignal (SIGTERM, false);
app.subscribeToSignal (SIGINT, false);
app4.unsubscribeFromSignal (SIGINT);
app3.unsubscribeFromSignal (SIGINT);
app2.unsubscribeFromSignal (SIGINT);
//app4.unsubscribeFromSignal (SIGINT);
//app3.unsubscribeFromSignal (SIGINT);
//app2.unsubscribeFromSignal (SIGINT);
return n;
}