2017-09-26 13:49:16 +00:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
2019-06-06 05:28:23 +00:00
|
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
2017-09-26 13:49:16 +00:00
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
2020-08-12 08:22:23 +00:00
|
|
|
#include <stdio.h>
|
2017-09-26 13:49:16 +00:00
|
|
|
|
2018-07-20 08:48:33 +00:00
|
|
|
#include <qse/si/App.hpp>
|
2018-08-31 09:39:12 +00:00
|
|
|
#include <qse/si/Mutex.hpp>
|
2017-09-26 13:49:16 +00:00
|
|
|
#include <qse/si/sinfo.h>
|
2018-09-17 02:51:52 +00:00
|
|
|
#include <qse/si/os.h>
|
2017-09-26 13:49:16 +00:00
|
|
|
#include "../cmn/syscall.h"
|
2019-01-21 15:12:01 +00:00
|
|
|
#include "../cmn/mem-prv.h"
|
2019-06-11 09:10:09 +00:00
|
|
|
#include "../cmn/fmt-prv.h"
|
2017-11-30 05:46:42 +00:00
|
|
|
#include <qse/cmn/mbwc.h>
|
2017-09-26 13:49:16 +00:00
|
|
|
|
|
|
|
/////////////////////////////////
|
|
|
|
QSE_BEGIN_NAMESPACE(QSE)
|
|
|
|
/////////////////////////////////
|
|
|
|
|
2018-08-31 09:39:12 +00:00
|
|
|
static Mutex g_app_mutex;
|
|
|
|
static App* g_app_top = QSE_NULL; // maintain the chain of application objects
|
|
|
|
static App* g_app_sig[QSE_NSIGS] = { QSE_NULL, };
|
2018-10-28 15:54:36 +00:00
|
|
|
static struct app_sig_t
|
2018-08-31 09:39:12 +00:00
|
|
|
{
|
2018-10-28 15:54:36 +00:00
|
|
|
app_sig_t(): sa_flags(0)
|
|
|
|
{
|
|
|
|
sigemptyset (&sa_mask);
|
|
|
|
}
|
2018-08-31 09:39:12 +00:00
|
|
|
sigset_t sa_mask;
|
|
|
|
int sa_flags;
|
2018-10-28 15:54:36 +00:00
|
|
|
} g_app_oldsi[QSE_NSIGS];
|
2018-09-10 14:15:28 +00:00
|
|
|
|
|
|
|
class SigScopedMutexLocker
|
|
|
|
{
|
|
|
|
public:
|
2020-08-31 00:42:22 +00:00
|
|
|
SigScopedMutexLocker (Mutex& mutex) QSE_CPP_NOEXCEPT: mutex(mutex)
|
2018-09-10 14:15:28 +00:00
|
|
|
{
|
|
|
|
sigset_t sigset;
|
2018-10-27 10:18:01 +00:00
|
|
|
sigfillset (&sigset);
|
2018-09-10 14:15:28 +00:00
|
|
|
::sigprocmask (SIG_BLOCK, &sigset, &this->oldsigset);
|
|
|
|
// TODO: would this work properly if this is called within a thread?
|
|
|
|
// do i need to use pthread_sigmask() conditionally?
|
|
|
|
this->mutex.lock ();
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
~SigScopedMutexLocker () QSE_CPP_NOEXCEPT
|
2018-09-10 14:15:28 +00:00
|
|
|
{
|
|
|
|
this->mutex.unlock ();
|
|
|
|
::sigprocmask (SIG_SETMASK, &this->oldsigset, QSE_NULL);
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
Mutex& mutex;
|
|
|
|
sigset_t oldsigset;
|
|
|
|
};
|
2018-08-31 09:39:12 +00:00
|
|
|
|
2019-06-11 09:10:09 +00:00
|
|
|
App::App (Mmgr* mmgr) QSE_CPP_NOEXCEPT: Mmged(mmgr), _prev_app(QSE_NULL), _next_app(QSE_NULL), _guarded_child_pid(-1), _log(this)
|
2018-08-31 09:39:12 +00:00
|
|
|
{
|
2019-06-12 07:07:18 +00:00
|
|
|
this->_cmgr = qse_getdflcmgr();
|
2019-11-10 08:19:15 +00:00
|
|
|
|
|
|
|
// instead of relying on the logger's priority masking, the class will do its own masking.
|
|
|
|
// set the prioriy mask to QSE_LOG_ALL_PRIORITIES.
|
|
|
|
qse_log_init(&this->_log.logger, this->getMmgr(), QSE_T(""), QSE_LOG_ALL_PRIORITIES, QSE_NULL);
|
2019-06-12 07:07:18 +00:00
|
|
|
|
2018-09-10 14:15:28 +00:00
|
|
|
SigScopedMutexLocker sml(g_app_mutex);
|
2018-08-31 09:39:12 +00:00
|
|
|
if (!g_app_top)
|
|
|
|
{
|
|
|
|
g_app_top = this;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_app_top->_prev_app = this;
|
|
|
|
this->_next_app = g_app_top;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
App::~App () QSE_CPP_NOEXCEPT
|
|
|
|
{
|
2018-09-10 14:15:28 +00:00
|
|
|
SigScopedMutexLocker sml(g_app_mutex);
|
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
|
|
|
{
|
2018-09-13 06:51:48 +00:00
|
|
|
// if the signal handler has not been removed before this application
|
|
|
|
// is destroyed and this is the last application subscribing to
|
|
|
|
// a signal, i arrange to set the signal handler to SIG_IGN as indicated by
|
|
|
|
// the third argument 'true' to set_signal_subscription_no_mutex().
|
|
|
|
// if the signal is not ignored, the signal received after destruction
|
|
|
|
// of this application object may lead to program crash as the handler
|
|
|
|
// is still associated with the destructed object.
|
|
|
|
|
|
|
|
// if the signal handler has been removed, the following function
|
|
|
|
// actual does nothing.
|
|
|
|
this->set_signal_subscription_no_mutex (i, SIGNAL_NEGLECTED, true);
|
2018-09-10 14:15:28 +00:00
|
|
|
}
|
2018-09-13 06:51:48 +00:00
|
|
|
|
2018-08-31 09:39:12 +00:00
|
|
|
if (this->_next_app) this->_next_app->_prev_app = this->_prev_app;
|
|
|
|
if (this->_prev_app) this->_prev_app->_next_app = this->_next_app;
|
2018-09-04 08:46:48 +00:00
|
|
|
if (this == g_app_top)
|
|
|
|
{
|
|
|
|
QSE_ASSERT (this->_prev_app == QSE_NULL);
|
|
|
|
g_app_top = this->_next_app;
|
|
|
|
}
|
2019-11-07 15:47:17 +00:00
|
|
|
|
2019-11-08 08:33:38 +00:00
|
|
|
qse_log_fini (&this->_log.logger);
|
2018-08-31 09:39:12 +00:00
|
|
|
}
|
|
|
|
|
2019-11-10 08:19:15 +00:00
|
|
|
void App::setName (const qse_char_t* name) QSE_CPP_NOEXCEPT
|
|
|
|
{
|
|
|
|
QSE::Named<32>::setName (name);
|
|
|
|
qse_log_setident (&this->_log.logger, name);
|
|
|
|
}
|
|
|
|
|
2021-12-06 16:22:10 +00:00
|
|
|
int App::daemonize (bool chdir_to_root, int fork_count, bool root_only, bool close_files) QSE_CPP_NOEXCEPT
|
2017-09-26 13:49:16 +00:00
|
|
|
{
|
2018-09-06 09:59:54 +00:00
|
|
|
if (root_only && QSE_GETEUID() != 0) return -1;
|
2017-09-26 13:49:16 +00:00
|
|
|
|
|
|
|
if (fork_count >= 1)
|
|
|
|
{
|
|
|
|
if (fork_count >= 2)
|
|
|
|
{
|
|
|
|
int n = QSE_FORK();
|
|
|
|
if (n == -1) return -1;
|
|
|
|
if (n != 0) QSE_EXIT(0);
|
|
|
|
|
|
|
|
QSE_SETSID ();
|
|
|
|
}
|
|
|
|
|
|
|
|
int n = QSE_FORK();
|
2017-09-29 00:32:31 +00:00
|
|
|
if (n == -1) return -1;
|
2017-09-26 13:49:16 +00:00
|
|
|
if (n != 0) QSE_EXIT(0); // TODO: should i call exit() in stdlib.h?
|
|
|
|
}
|
|
|
|
|
|
|
|
QSE_UMASK (0);
|
|
|
|
if (chdir_to_root) QSE_CHDIR("/"); // don't care about failure
|
|
|
|
|
2021-12-06 16:22:10 +00:00
|
|
|
if (close_files)
|
2017-09-26 13:49:16 +00:00
|
|
|
{
|
|
|
|
int keep[] = { 0, 1, 2};
|
|
|
|
|
2018-10-17 08:07:45 +00:00
|
|
|
if (qse_close_open_fds_using_proc(keep, QSE_COUNTOF(keep)) <= -1)
|
2017-09-26 13:49:16 +00:00
|
|
|
{
|
|
|
|
for (int i = qse_get_highest_fd(); i >= 3; i--) QSE_CLOSE (i);
|
|
|
|
}
|
|
|
|
|
2018-10-17 08:07:45 +00:00
|
|
|
int fd = QSE_OPEN("/dev/null", O_RDWR, 0);
|
2017-09-26 13:49:16 +00:00
|
|
|
if (fd >= 0)
|
|
|
|
{
|
|
|
|
if (fd != 0) QSE_DUP2 (fd, 0);
|
|
|
|
if (fd != 1) QSE_DUP2 (fd, 1);
|
|
|
|
if (fd != 2) QSE_DUP2 (fd, 2);
|
|
|
|
if (fd > 2) QSE_CLOSE (fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fork_count <= 1)
|
|
|
|
{
|
|
|
|
if (fork_count <= 0)
|
|
|
|
{
|
|
|
|
// detach from the controlling terminal manually
|
|
|
|
// when no fork() is requested though setsid() is supposed to
|
|
|
|
// do this as well.
|
2021-12-06 16:03:47 +00:00
|
|
|
int fd = QSE_OPEN("/dev/tty", O_RDWR, 0);
|
2017-09-26 13:49:16 +00:00
|
|
|
if (fd >= 0)
|
|
|
|
{
|
|
|
|
QSE_IOCTL(fd, TIOCNOTTY, NULL);
|
|
|
|
QSE_CLOSE (fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// since no fork() has been executed, the process that has
|
|
|
|
// executed this process will remain as the parent. there
|
|
|
|
// will happen no orphaning of this process and take-over
|
|
|
|
// by init happen
|
|
|
|
}
|
|
|
|
|
|
|
|
QSE_SETSID (); // don't care about failure
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-20 08:48:33 +00:00
|
|
|
int App::chroot (const qse_mchar_t* mpath) QSE_CPP_NOEXCEPT
|
2017-09-29 00:32:31 +00:00
|
|
|
{
|
2018-09-04 08:46:48 +00:00
|
|
|
return QSE_CHROOT(mpath);
|
2017-09-29 00:32:31 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 08:48:33 +00:00
|
|
|
int App::chroot (const qse_wchar_t* wpath) QSE_CPP_NOEXCEPT
|
2017-09-29 00:32:31 +00:00
|
|
|
{
|
2017-10-27 06:56:31 +00:00
|
|
|
qse_mchar_t* mpath;
|
2017-11-30 05:46:42 +00:00
|
|
|
mpath = qse_wcstombsdup (wpath, QSE_NULL, this->getMmgr());
|
2017-10-27 06:56:31 +00:00
|
|
|
if (!mpath) return -1;
|
2018-09-04 08:46:48 +00:00
|
|
|
int n = App::chroot((const qse_mchar_t*)mpath);
|
2017-11-30 05:46:42 +00:00
|
|
|
this->getMmgr()->dispose (mpath);
|
2017-10-27 06:56:31 +00:00
|
|
|
return n;
|
2017-09-29 00:32:31 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 05:46:42 +00:00
|
|
|
#if 0
|
2018-07-20 08:48:33 +00:00
|
|
|
int App::switchPrivilege (int gid, int uid, bool permanently)
|
2017-09-26 13:49:16 +00:00
|
|
|
{
|
2017-11-30 05:46:42 +00:00
|
|
|
gid = QSE_GETGID();
|
|
|
|
uid = QSE_GETUID();
|
|
|
|
|
|
|
|
this->saved_egid = QSE_GETEGID();
|
|
|
|
this->saved_euid = QSE_GETEUID();
|
|
|
|
this->saved_ngroups = getgroups (QSE_COUNTOF(this->saved_groups), this->saved_groups);
|
|
|
|
|
|
|
|
if (this->saved_euid == 0) setgrops (1, gid);
|
|
|
|
|
|
|
|
setegid (gid);
|
|
|
|
//setregid (-1, gid);
|
|
|
|
|
|
|
|
if (uid != this->saved_euid)
|
|
|
|
{
|
|
|
|
seteuid (uid);
|
|
|
|
//setreuid (-1, uid);
|
|
|
|
}
|
2017-09-26 13:49:16 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 08:48:33 +00:00
|
|
|
int App::restorePrivilege ()
|
2017-11-30 05:46:42 +00:00
|
|
|
{
|
|
|
|
if (QSE_GETEUID() != this->saved_euid) seteuid (this->saved_euid);
|
|
|
|
if (QSE_GETEGID() != this->saved_egid) setegid (this->saved_egid);
|
|
|
|
if (this->saved_euid == 0) setgroups (this->saved_ngroups, this->saved_groups);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2017-09-26 13:49:16 +00:00
|
|
|
|
2018-07-20 08:48:33 +00:00
|
|
|
qse_size_t App::_sighrs[2][QSE_NSIGS] = { { 0, }, };
|
|
|
|
|
|
|
|
static void dispatch_signal (int sig)
|
|
|
|
{
|
|
|
|
((App::SignalHandler)App::_sighrs[0][sig]) (sig);
|
|
|
|
if (App::_sighrs[1][sig] && App::_sighrs[1][sig] != (qse_size_t)SIG_IGN && App::_sighrs[1][sig] != (qse_size_t)SIG_DFL)
|
|
|
|
{
|
|
|
|
((App::SignalHandler)App::_sighrs[1][sig]) (sig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dispatch_siginfo (int sig, siginfo_t* si, void* ctx)
|
|
|
|
{
|
|
|
|
((App::SignalHandler)App::_sighrs[0][sig]) (sig);
|
|
|
|
if (App::_sighrs[1][sig] && App::_sighrs[1][sig] != (qse_size_t)SIG_IGN && App::_sighrs[1][sig] != (qse_size_t)SIG_DFL)
|
|
|
|
{
|
|
|
|
((void(*)(int, siginfo_t*, void*))App::_sighrs[1][sig]) (sig, si, ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::setSignalHandler (int sig, SignalHandler sighr) QSE_CPP_NOEXCEPT
|
2018-09-04 08:46:48 +00:00
|
|
|
{
|
2018-09-10 14:15:28 +00:00
|
|
|
SigScopedMutexLocker sml(g_app_mutex);
|
2018-09-04 08:46:48 +00:00
|
|
|
return App::set_signal_handler_no_mutex (sig, sighr);
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::set_signal_handler_no_mutex (int sig, SignalHandler sighr) QSE_CPP_NOEXCEPT
|
2018-07-20 08:48:33 +00:00
|
|
|
{
|
|
|
|
if (App::_sighrs[0][sig]) return -1; // already set
|
|
|
|
|
|
|
|
struct sigaction sa, oldsa;
|
|
|
|
|
|
|
|
if (::sigaction(sig, QSE_NULL, &oldsa) == -1) return -1;
|
|
|
|
|
2019-01-21 15:12:01 +00:00
|
|
|
QSE_MEMSET (&sa, 0, QSE_SIZEOF(sa));
|
2018-07-20 08:48:33 +00:00
|
|
|
if (oldsa.sa_flags & SA_SIGINFO)
|
|
|
|
{
|
|
|
|
sa.sa_sigaction = dispatch_siginfo;
|
2019-01-21 15:12:01 +00:00
|
|
|
sa.sa_flags = SA_SIGINFO;
|
2018-07-20 08:48:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sa.sa_handler = dispatch_signal;
|
|
|
|
sa.sa_flags = 0;
|
|
|
|
}
|
2019-01-21 15:12:01 +00:00
|
|
|
//sa.sa_flags |= SA_INTERUPT;
|
|
|
|
//sa.sa_flags |= SA_RESTART;
|
|
|
|
sigfillset (&sa.sa_mask); // block all signals while the handler is being executed
|
2018-07-20 08:48:33 +00:00
|
|
|
|
|
|
|
if (::sigaction(sig, &sa, QSE_NULL) == -1) return -1;
|
|
|
|
|
|
|
|
App::_sighrs[0][sig] = (qse_size_t)sighr;
|
2019-01-21 15:12:01 +00:00
|
|
|
if (oldsa.sa_flags & SA_SIGINFO)
|
|
|
|
App::_sighrs[1][sig] = (qse_size_t)oldsa.sa_sigaction;
|
|
|
|
else
|
|
|
|
App::_sighrs[1][sig] = (qse_size_t)oldsa.sa_handler;
|
2018-08-31 09:39:12 +00:00
|
|
|
g_app_oldsi[sig].sa_mask = oldsa.sa_mask;
|
|
|
|
g_app_oldsi[sig].sa_flags = oldsa.sa_flags;
|
2018-07-20 08:48:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::unsetSignalHandler (int sig, bool ignore) QSE_CPP_NOEXCEPT
|
2018-09-04 08:46:48 +00:00
|
|
|
{
|
2018-09-10 14:15:28 +00:00
|
|
|
SigScopedMutexLocker sml(g_app_mutex);
|
|
|
|
return App::unset_signal_handler_no_mutex(sig, ignore);
|
2018-09-04 08:46:48 +00:00
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::unset_signal_handler_no_mutex(int sig, int ignore) QSE_CPP_NOEXCEPT
|
2017-09-26 13:49:16 +00:00
|
|
|
{
|
2018-07-20 08:48:33 +00:00
|
|
|
if (!App::_sighrs[0][sig]) return -1;
|
|
|
|
|
|
|
|
struct sigaction sa;
|
2017-09-26 13:49:16 +00:00
|
|
|
|
2019-01-21 15:12:01 +00:00
|
|
|
QSE_MEMSET (&sa, 0, QSE_SIZEOF(sa));
|
2018-08-31 09:39:12 +00:00
|
|
|
sa.sa_mask = g_app_oldsi[sig].sa_mask;
|
|
|
|
sa.sa_flags = g_app_oldsi[sig].sa_flags;
|
2018-09-10 14:15:28 +00:00
|
|
|
if (ignore)
|
|
|
|
{
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
|
|
sa.sa_flags &= ~SA_SIGINFO;
|
|
|
|
}
|
|
|
|
else if (sa.sa_flags & SA_SIGINFO)
|
|
|
|
{
|
2018-07-20 08:48:33 +00:00
|
|
|
sa.sa_sigaction = (void(*)(int,siginfo_t*,void*))App::_sighrs[1][sig];
|
2018-09-10 14:15:28 +00:00
|
|
|
}
|
2018-07-20 08:48:33 +00:00
|
|
|
else
|
2018-09-09 17:22:16 +00:00
|
|
|
{
|
2018-07-20 08:48:33 +00:00
|
|
|
sa.sa_handler = (SignalHandler)App::_sighrs[1][sig];
|
2018-09-09 17:22:16 +00:00
|
|
|
}
|
2017-11-30 05:46:42 +00:00
|
|
|
|
2019-09-10 08:53:59 +00:00
|
|
|
if (::sigaction(sig, &sa, QSE_NULL) <= -1) return -1;
|
2017-09-26 13:49:16 +00:00
|
|
|
|
2018-07-20 08:48:33 +00:00
|
|
|
App::_sighrs[0][sig] = 0;
|
|
|
|
App::_sighrs[1][sig] = 0;
|
2017-09-26 13:49:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
/*static*/ void App::handle_signal (int sig) QSE_CPP_NOEXCEPT
|
2018-08-31 09:39:12 +00:00
|
|
|
{
|
2018-09-10 14:15:28 +00:00
|
|
|
// Note: i use ScopedMutexLocker in the signal handler
|
|
|
|
// whereas I use SigScopedMutexLocker in other functions.
|
|
|
|
// as SigScopedMutexLock blocks all signals, this signal handler
|
|
|
|
// is not called while those other functions are holding on to
|
|
|
|
// this mutex object.
|
|
|
|
ScopedMutexLocker sml(g_app_mutex);
|
|
|
|
|
2018-08-31 09:39:12 +00:00
|
|
|
App* app = g_app_sig[sig];
|
|
|
|
while (app)
|
|
|
|
{
|
|
|
|
App::_SigLink& sl = app->_sig[sig];
|
|
|
|
App* next = sl._next;
|
2018-09-09 17:22:16 +00:00
|
|
|
if (sl._state == App::SIGNAL_ACCEPTED)
|
2018-09-04 10:27:24 +00:00
|
|
|
{
|
|
|
|
// the actual signal handler is called with the mutex locked.
|
2018-10-28 15:49:46 +00:00
|
|
|
// it must not call acceptSingal()/discardSignal()/neglectSingal()
|
2018-09-04 10:27:24 +00:00
|
|
|
// from within the handler.
|
2018-09-06 09:59:54 +00:00
|
|
|
if (app->_guarded_child_pid >= 0) app->on_guard_signal (sig);
|
2018-09-13 06:51:48 +00:00
|
|
|
app->on_signal (sig);
|
2018-09-04 10:27:24 +00:00
|
|
|
}
|
2018-08-31 09:39:12 +00:00
|
|
|
app = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
void App::on_guard_signal (int sig) QSE_CPP_NOEXCEPT
|
2018-09-06 09:59:54 +00:00
|
|
|
{
|
|
|
|
::kill (this->_guarded_child_pid, sig);
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
App::SignalState App::getSignalSubscription (int sig) const QSE_CPP_NOEXCEPT
|
2018-08-31 09:39:12 +00:00
|
|
|
{
|
|
|
|
QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS);
|
2018-09-09 17:22:16 +00:00
|
|
|
return this->_sig[sig]._state;
|
2018-09-06 09:59:54 +00:00
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::setSignalSubscription (int sig, SignalState ss, bool ignore_if_unhandled) QSE_CPP_NOEXCEPT
|
2018-09-06 09:59:54 +00:00
|
|
|
{
|
2018-09-09 17:22:16 +00:00
|
|
|
QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS);
|
2018-09-10 14:15:28 +00:00
|
|
|
SigScopedMutexLocker sml(g_app_mutex);
|
2018-09-13 06:51:48 +00:00
|
|
|
return this->set_signal_subscription_no_mutex(sig, ss, ignore_if_unhandled);
|
2018-09-09 17:22:16 +00:00
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate, bool ignore_if_unhandled) QSE_CPP_NOEXCEPT
|
2018-09-09 17:22:16 +00:00
|
|
|
{
|
|
|
|
_SigLink& sl = this->_sig[sig];
|
2018-09-13 06:51:48 +00:00
|
|
|
|
2018-09-09 17:22:16 +00:00
|
|
|
if (QSE_UNLIKELY(sl._state == reqstate)) return 0; // no change
|
|
|
|
|
2018-09-13 06:51:48 +00:00
|
|
|
if (reqstate == SIGNAL_NEGLECTED)
|
2018-09-06 09:59:54 +00:00
|
|
|
{
|
2019-11-12 07:08:24 +00:00
|
|
|
// accepted/discarded->neglected(unhandled)
|
2018-09-09 17:22:16 +00:00
|
|
|
QSE_ASSERT (g_app_sig[sig] != QSE_NULL);
|
|
|
|
|
|
|
|
if (g_app_sig[sig] == this)
|
2018-09-06 09:59:54 +00:00
|
|
|
{
|
2018-09-09 17:22:16 +00:00
|
|
|
QSE_ASSERT (sl._prev == QSE_NULL);
|
|
|
|
if (!sl._next)
|
2018-09-06 09:59:54 +00:00
|
|
|
{
|
2018-09-13 06:51:48 +00:00
|
|
|
// this is the last application subscribing to the signal.
|
|
|
|
// let's get rid of the signal handler
|
|
|
|
if (App::unset_signal_handler_no_mutex(sig, ignore_if_unhandled) <= -1) return -1;
|
2018-09-06 09:59:54 +00:00
|
|
|
}
|
2018-09-09 17:22:16 +00:00
|
|
|
g_app_sig[sig] = sl._next;
|
2018-09-06 09:59:54 +00:00
|
|
|
}
|
2018-09-09 17:22:16 +00:00
|
|
|
|
|
|
|
if (sl._next) sl._next->_sig[sig]._prev = sl._prev;
|
|
|
|
if (sl._prev) sl._prev->_sig[sig]._next = sl._next;
|
|
|
|
sl._prev = QSE_NULL;
|
|
|
|
sl._next = QSE_NULL;
|
|
|
|
sl._state = reqstate;
|
2018-09-06 09:59:54 +00:00
|
|
|
}
|
2018-09-13 06:51:48 +00:00
|
|
|
else if (QSE_LIKELY(sl._state == SIGNAL_NEGLECTED))
|
2018-09-09 17:22:16 +00:00
|
|
|
{
|
2019-11-12 07:08:24 +00:00
|
|
|
// neglected(unhandled)->accepted/discarded
|
2018-09-09 17:22:16 +00:00
|
|
|
QSE_ASSERT (sl._prev == QSE_NULL && sl._next == QSE_NULL);
|
2018-09-06 09:59:54 +00:00
|
|
|
|
2018-09-09 17:22:16 +00:00
|
|
|
App* xapp = g_app_sig[sig];
|
|
|
|
App* xapp_xprev = QSE_NULL;
|
2018-08-31 09:39:12 +00:00
|
|
|
|
2018-09-09 17:22:16 +00:00
|
|
|
g_app_sig[sig] = this;
|
2019-11-12 07:08:24 +00:00
|
|
|
sl._state = reqstate;
|
2018-09-09 17:22:16 +00:00
|
|
|
sl._next = xapp;
|
|
|
|
if (xapp)
|
|
|
|
{
|
|
|
|
xapp_xprev = xapp->_sig[sig]._prev;
|
|
|
|
xapp->_sig[sig]._prev = this;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no application is set to accept this signal.
|
2018-09-10 14:15:28 +00:00
|
|
|
// it is the first time to set the system-level signal handler.
|
2018-09-09 17:22:16 +00:00
|
|
|
if (App::set_signal_handler_no_mutex(sig, App::handle_signal) <= -1)
|
|
|
|
{
|
|
|
|
// roll back
|
|
|
|
g_app_sig[sig] = xapp;
|
|
|
|
if (xapp) xapp->_sig[sig]._prev = xapp_xprev;
|
2018-09-13 06:51:48 +00:00
|
|
|
sl._state = SIGNAL_NEGLECTED;
|
2018-09-09 17:22:16 +00:00
|
|
|
sl._next = QSE_NULL;
|
|
|
|
QSE_ASSERT (sl._prev == QSE_NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QSE_ASSERT (sl._prev == QSE_NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-12 07:08:24 +00:00
|
|
|
// accpeted->discarded or discarded->accepted
|
2018-09-09 17:22:16 +00:00
|
|
|
QSE_ASSERT (g_app_sig[sig] != QSE_NULL);
|
|
|
|
sl._state = reqstate;
|
|
|
|
}
|
|
|
|
|
|
|
|
return reqstate;
|
2018-09-06 09:59:54 +00:00
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::acceptSignals (const SignalSet& signals) QSE_CPP_NOEXCEPT
|
2019-11-11 09:24:56 +00:00
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
|
|
|
{
|
|
|
|
if (signals.isSet(i))
|
|
|
|
{
|
|
|
|
if (this->setSignalSubscription(i, SIGNAL_ACCEPTED) <= -1) n = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::discardSignals (const SignalSet& signals) QSE_CPP_NOEXCEPT
|
2019-11-11 09:24:56 +00:00
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
|
|
|
{
|
|
|
|
if (signals.isSet(i))
|
|
|
|
{
|
|
|
|
if (this->setSignalSubscription(i, SIGNAL_DISCARDED) <= -1) n = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::neglectSignals (const SignalSet& signals, bool ignore_if_unhandled) QSE_CPP_NOEXCEPT
|
2019-11-11 09:24:56 +00:00
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
|
|
|
{
|
|
|
|
if (signals.isSet(i))
|
|
|
|
{
|
|
|
|
if (this->setSignalSubscription(i, SIGNAL_NEGLECTED, ignore_if_unhandled) <= -1) n = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::guardProcess (const SignalSet& signals, bool _guard, qse_mtime_t guard_pause_ms, const qse_mchar_t* proc_name) QSE_CPP_NOEXCEPT
|
2018-09-05 14:52:51 +00:00
|
|
|
{
|
2018-09-09 17:22:16 +00:00
|
|
|
SignalState old_ss[QSE_NSIGS];
|
2018-09-17 10:11:57 +00:00
|
|
|
int seq = 0;
|
2018-09-09 17:22:16 +00:00
|
|
|
|
2018-09-13 06:51:48 +00:00
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
|
|
|
{
|
|
|
|
if (signals.isSet(i))
|
|
|
|
{
|
|
|
|
old_ss[i] = this->getSignalSubscription(i);
|
|
|
|
this->setSignalSubscription (i, SIGNAL_ACCEPTED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 14:52:51 +00:00
|
|
|
while (1)
|
|
|
|
{
|
2018-09-17 10:11:57 +00:00
|
|
|
if (seq < QSE_TYPE_MAX(int)) ++seq;
|
|
|
|
|
2018-09-05 14:52:51 +00:00
|
|
|
pid_t pid = ::fork();
|
|
|
|
if (pid == -1) return -1;
|
2018-09-11 15:11:48 +00:00
|
|
|
if (pid == 0)
|
|
|
|
{
|
2018-09-13 06:51:48 +00:00
|
|
|
// child process
|
|
|
|
this->_guarded_child_pid = -1;
|
2018-09-05 14:52:51 +00:00
|
|
|
|
2019-06-11 09:10:09 +00:00
|
|
|
// the child process has inherited the signal handlers.
|
|
|
|
// restore the signal handlers of the child process to the original handlers.
|
2018-09-13 06:51:48 +00:00
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
2018-09-09 17:22:16 +00:00
|
|
|
{
|
2018-09-13 06:51:48 +00:00
|
|
|
if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]);
|
2018-09-09 17:22:16 +00:00
|
|
|
}
|
2018-09-13 06:51:48 +00:00
|
|
|
|
|
|
|
::setpgid (0, 0); // change the process group id.
|
|
|
|
break;
|
2018-09-09 17:22:16 +00:00
|
|
|
}
|
2018-09-06 09:59:54 +00:00
|
|
|
|
2018-09-13 06:51:48 +00:00
|
|
|
// ===============================================
|
|
|
|
// the guardian(parent) process
|
|
|
|
// ===============================================
|
2018-09-06 09:59:54 +00:00
|
|
|
this->_guarded_child_pid = pid;
|
2021-07-24 12:43:15 +00:00
|
|
|
this->on_guarded_child (pid, GUARDED_CHILD_STARTED, 0);
|
2018-09-06 09:59:54 +00:00
|
|
|
|
2018-09-05 14:52:51 +00:00
|
|
|
int status;
|
2018-09-06 09:59:54 +00:00
|
|
|
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.
|
|
|
|
// ------------------------------------------------------
|
|
|
|
}
|
|
|
|
|
2021-07-24 12:43:15 +00:00
|
|
|
this->on_guarded_child (pid, GUARDED_CHILD_EXITED, status);
|
2018-09-11 15:11:48 +00:00
|
|
|
if (WIFEXITED(status))
|
|
|
|
{
|
|
|
|
if (WEXITSTATUS(status) == 0)
|
|
|
|
{
|
2020-08-12 08:22:23 +00:00
|
|
|
exit_ok:
|
2018-09-11 15:11:48 +00:00
|
|
|
// the child has terminated normally and successfully.
|
2018-09-13 06:51:48 +00:00
|
|
|
for (int i = 0; i < QSE_NSIGS; i++)
|
|
|
|
{
|
|
|
|
if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]);
|
|
|
|
}
|
2018-09-11 15:11:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2018-09-05 14:52:51 +00:00
|
|
|
{
|
2018-09-11 15:11:48 +00:00
|
|
|
// the child process aborted or crashed.
|
|
|
|
// let's kill all other processes in the same process group as the child
|
|
|
|
// but is it safe to do this after waitpid() has been called on 'pid'?
|
|
|
|
// it should be mostly safe because most OSes doesn't reuse the same pid
|
|
|
|
// within a very short time.
|
|
|
|
::kill (-pid, SIGKILL);
|
2018-09-05 14:52:51 +00:00
|
|
|
}
|
2019-06-13 10:18:30 +00:00
|
|
|
|
2020-08-12 08:22:23 +00:00
|
|
|
if (!_guard) goto exit_ok; // no guard;
|
|
|
|
|
2019-06-13 10:18:30 +00:00
|
|
|
if (guard_pause_ms > 0) qse_msleep (guard_pause_ms);
|
2018-09-05 14:52:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: if (proc_name) qse_set_proc_name (proc_name);
|
2018-09-17 10:11:57 +00:00
|
|
|
return seq; // the caller must execute the actual work.
|
2018-09-05 14:52:51 +00:00
|
|
|
}
|
|
|
|
|
2019-06-11 09:10:09 +00:00
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
int App::put_char_to_log_buf (qse_char_t c, void* ctx) QSE_CPP_NOEXCEPT
|
2019-06-11 09:10:09 +00:00
|
|
|
{
|
|
|
|
App* app = (App*)ctx;
|
2019-11-07 15:47:17 +00:00
|
|
|
|
|
|
|
if (app->_log.len >= QSE_COUNTOF(app->_log.buf) - 1)
|
2019-06-11 09:10:09 +00:00
|
|
|
{
|
2019-11-07 15:47:17 +00:00
|
|
|
if (app->_log.buf[app->_log.len - 1] != '\n')
|
|
|
|
{
|
|
|
|
/* no line ending - append a line terminator */
|
|
|
|
app->_log.buf[app->_log.len++] = '\n';
|
|
|
|
}
|
|
|
|
|
2019-11-10 08:19:15 +00:00
|
|
|
app->log_write (app->_log.last_pri, app->_log.buf, app->_log.len);
|
2019-06-11 09:10:09 +00:00
|
|
|
app->_log.len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
app->_log.buf[app->_log.len++] = c;
|
|
|
|
if (c == QSE_T('\n'))
|
|
|
|
{
|
2019-11-10 08:19:15 +00:00
|
|
|
app->log_write (app->_log.last_pri, app->_log.buf, app->_log.len);
|
2019-06-11 09:10:09 +00:00
|
|
|
app->_log.len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-06-12 07:07:18 +00:00
|
|
|
static int wcs_to_mbs (const qse_wchar_t* wcs, qse_size_t* wcslen, qse_mchar_t* mbs, qse_size_t* mbslen, void* ctx)
|
|
|
|
{
|
|
|
|
App* app = (App*)ctx;
|
2019-11-07 08:38:01 +00:00
|
|
|
return qse_wcsntombsnwithcmgr(wcs, wcslen, mbs, mbslen, app->getCmgr());
|
2019-06-12 07:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mbs_to_wcs (const qse_mchar_t* mbs, qse_size_t* mbslen, qse_wchar_t* wcs, qse_size_t* wcslen, void* ctx)
|
|
|
|
{
|
|
|
|
App* app = (App*)ctx;
|
2019-11-07 08:38:01 +00:00
|
|
|
return qse_mbsntowcsnwithcmgr(mbs, mbslen, wcs, wcslen, app->getCmgr());
|
2019-06-12 07:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-08-31 00:42:22 +00:00
|
|
|
void App::logfmtv (qse_log_priority_flag_t pri, const qse_char_t* fmt, va_list ap) QSE_CPP_NOEXCEPT
|
2019-06-11 09:10:09 +00:00
|
|
|
{
|
|
|
|
/*if (this->threaded)*/ this->_log.mtx.lock ();
|
|
|
|
|
2019-11-10 08:19:15 +00:00
|
|
|
if (this->_log.len > 0 && this->_log.last_pri != pri)
|
2019-06-11 09:10:09 +00:00
|
|
|
{
|
|
|
|
if (this->_log.buf[this->_log.len - 1] != QSE_T('\n'))
|
|
|
|
{
|
|
|
|
// no line ending - append a line terminator
|
|
|
|
this->_log.buf[this->_log.len++] = QSE_T('\n');
|
|
|
|
}
|
2019-11-10 08:19:15 +00:00
|
|
|
this->log_write (this->_log.last_pri, this->_log.buf, this->_log.len);
|
2019-06-11 09:10:09 +00:00
|
|
|
this->_log.len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
qse_fmtout_t fo;
|
|
|
|
|
|
|
|
fo.count = 0;
|
|
|
|
fo.limit = QSE_TYPE_MAX(qse_size_t) - 1;
|
|
|
|
fo.ctx = this;
|
|
|
|
fo.put = put_char_to_log_buf;
|
2019-06-12 07:07:18 +00:00
|
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
|
|
fo.conv = wcs_to_mbs;
|
2019-06-11 09:10:09 +00:00
|
|
|
#else
|
2019-06-12 07:07:18 +00:00
|
|
|
fo.conv = mbs_to_wcs;
|
2019-06-11 09:10:09 +00:00
|
|
|
#endif
|
|
|
|
|
2019-11-10 08:19:15 +00:00
|
|
|
this->_log.last_pri = pri;
|
2019-06-11 09:10:09 +00:00
|
|
|
qse_fmtout(fmt, &fo, ap);
|
|
|
|
|
|
|
|
/*if (this->threaded)*/ this->_log.mtx.unlock ();
|
|
|
|
}
|
|
|
|
|
2019-11-07 08:38:01 +00:00
|
|
|
// default log message output implementation
|
2020-08-31 00:42:22 +00:00
|
|
|
void App::log_write (qse_log_priority_flag_t pri, const qse_char_t* msg, qse_size_t len) QSE_CPP_NOEXCEPT
|
2019-11-07 08:38:01 +00:00
|
|
|
{
|
2019-11-08 08:33:38 +00:00
|
|
|
// the last character is \n. qse_log_report() knows to terminate a line. so exclude it from reporting
|
2019-11-10 08:19:15 +00:00
|
|
|
qse_log_report (&this->_log.logger, QSE_NULL, pri, QSE_T("%.*js"), (int)(len - 1), msg);
|
2019-11-07 08:38:01 +00:00
|
|
|
}
|
|
|
|
|
2017-09-26 13:49:16 +00:00
|
|
|
/////////////////////////////////
|
|
|
|
QSE_END_NAMESPACE(QSE)
|
|
|
|
/////////////////////////////////
|