From 5f29d1670f24df4b5b7d36c06d89a6a8ecf72027 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Thu, 6 Sep 2018 09:59:54 +0000 Subject: [PATCH] improving signal handling of the App class --- qse/include/qse/si/App.hpp | 20 ++++--- qse/lib/si/App.cpp | 109 +++++++++++++++++++++++++++++------- qse/samples/si/tcpsvr01.cpp | 20 +++---- 3 files changed, 111 insertions(+), 38 deletions(-) diff --git a/qse/include/qse/si/App.hpp b/qse/include/qse/si/App.hpp index ba04c568..cda3be54 100644 --- a/qse/include/qse/si/App.hpp +++ b/qse/include/qse/si/App.hpp @@ -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 diff --git a/qse/lib/si/App.cpp b/qse/lib/si/App.cpp index e662480d..ab764657 100644 --- a/qse/lib/si/App.cpp +++ b/qse/lib/si/App.cpp @@ -30,6 +30,7 @@ #include "../cmn/syscall.h" #include +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; } } diff --git a/qse/samples/si/tcpsvr01.cpp b/qse/samples/si/tcpsvr01.cpp index 66e8b1f7..1802c2b3 100644 --- a/qse/samples/si/tcpsvr01.cpp +++ b/qse/samples/si/tcpsvr01.cpp @@ -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; }