/* * Copyright (C) 2006 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "processquit.h" #ifndef NO_IRISNET # include "irisnetglobal_p.h" #endif #ifdef QT_GUI_LIB # include #endif #ifdef Q_OS_WIN # include #endif #ifdef Q_OS_UNIX # include # include #endif #ifndef NO_IRISNET namespace XMPP { #endif Q_GLOBAL_STATIC(QMutex, pq_mutex) static ProcessQuit *g_pq = 0; inline bool is_gui_app() { #ifdef QT_GUI_LIB return (QApplication::type() != QApplication::Tty); #else return false; #endif } class ProcessQuit::Private : public QObject { Q_OBJECT public: ProcessQuit *q; bool done; #ifdef Q_OS_WIN bool use_handler; #endif #ifdef Q_OS_UNIX int sig_pipe[2]; QSocketNotifier *sig_notifier; #endif Private(ProcessQuit *_q) : QObject(_q), q(_q) { done = false; #ifdef Q_OS_WIN use_handler = !is_gui_app(); if(use_handler) SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, TRUE); #endif #ifdef Q_OS_UNIX pipe(sig_pipe); sig_notifier = new QSocketNotifier(sig_pipe[0], QSocketNotifier::Read, this); connect(sig_notifier, SIGNAL(activated(int)), SLOT(sig_activated(int))); unixWatchAdd(SIGINT); unixWatchAdd(SIGHUP); unixWatchAdd(SIGTERM); #endif } ~Private() { #ifdef Q_OS_WIN if(use_handler) SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, FALSE); #endif #ifdef Q_OS_UNIX unixWatchRemove(SIGINT); unixWatchRemove(SIGHUP); unixWatchRemove(SIGTERM); delete sig_notifier; close(sig_pipe[0]); close(sig_pipe[1]); #endif } #ifdef Q_OS_WIN static BOOL winHandler(DWORD ctrlType) { Q_UNUSED(ctrlType); QMetaObject::invokeMethod(g_pq->d, "ctrl_ready", Qt::QueuedConnection); return TRUE; } #endif #ifdef Q_OS_UNIX static void unixHandler(int sig) { Q_UNUSED(sig); unsigned char c = 0; ::write(g_pq->d->sig_pipe[1], &c, 1); } void unixWatchAdd(int sig) { struct sigaction sa; sigaction(sig, NULL, &sa); // if the signal is ignored, don't take it over. this is // recommended by the glibc manual if(sa.sa_handler == SIG_IGN) return; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; sa.sa_handler = unixHandler; sigaction(sig, &sa, 0); } void unixWatchRemove(int sig) { struct sigaction sa; sigaction(sig, NULL, &sa); // ignored means we skipped it earlier, so we should // skip it again if(sa.sa_handler == SIG_IGN) return; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; sa.sa_handler = SIG_DFL; sigaction(sig, &sa, 0); } #endif public slots: void ctrl_ready() { #ifdef Q_OS_WIN do_emit(); #endif } void sig_activated(int) { #ifdef Q_OS_UNIX unsigned char c; ::read(sig_pipe[0], &c, 1); do_emit(); #endif } private: void do_emit() { // only signal once if(!done) { done = true; emit q->quit(); } } }; ProcessQuit::ProcessQuit(QObject *parent) :QObject(parent) { d = new Private(this); } ProcessQuit::~ProcessQuit() { delete d; } ProcessQuit *ProcessQuit::instance() { QMutexLocker locker(pq_mutex()); if(!g_pq) { g_pq = new ProcessQuit; g_pq->moveToThread(QCoreApplication::instance()->thread()); #ifndef NO_IRISNET irisNetAddPostRoutine(cleanup); #endif } return g_pq; } void ProcessQuit::reset() { QMutexLocker locker(pq_mutex()); if(g_pq) g_pq->d->done = false; } void ProcessQuit::cleanup() { delete g_pq; g_pq = 0; } #ifndef NO_IRISNET } #endif #include "processquit.moc"