improving signal handling of the App class
This commit is contained in:
parent
e8804b20ec
commit
5f29d1670f
@ -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
|
||||
|
@ -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,20 +437,36 @@ 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;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user