File indexing completed on 2024-06-02 04:06:47

0001 /*
0002  * Copyright (C) 2006  Justin Karneges
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * either version 2
0008    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public
0016  * License along with this library; if not, write to the Free Software
0017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0018  * 02110-1301  USA
0019  *
0020  */
0021 
0022 #include "processquit.h"
0023 
0024 #ifndef NO_IRISNET
0025 # include "irisnetglobal_p.h"
0026 #endif
0027 
0028 #ifdef QT_GUI_LIB
0029 # include <QApplication>
0030 #endif
0031 
0032 #ifdef Q_OS_WIN
0033 # include <windows.h>
0034 #endif
0035 
0036 #ifdef Q_OS_UNIX
0037 # include <signal.h>
0038 # include <unistd.h>
0039 #endif
0040 
0041 namespace {
0042 
0043 // safeobj stuff, from qca
0044 
0045 void releaseAndDeleteLater(QObject *owner, QObject *obj)
0046 {
0047     obj->disconnect(owner);
0048     obj->setParent(0);
0049     obj->deleteLater();
0050 }
0051 
0052 class SafeSocketNotifier : public QObject
0053 {
0054     Q_OBJECT
0055 public:
0056     SafeSocketNotifier(int socket, QSocketNotifier::Type type,
0057         QObject *parent = 0) :
0058         QObject(parent)
0059     {
0060         sn = new QSocketNotifier(socket, type, this);
0061         connect(sn, &QSocketNotifier::activated, this, &SafeSocketNotifier::activated);
0062     }
0063 
0064     ~SafeSocketNotifier() override
0065     {
0066         sn->setEnabled(false);
0067         releaseAndDeleteLater(this, sn);
0068     }
0069 
0070     bool isEnabled() const             { return sn->isEnabled(); }
0071     int socket() const                 { return sn->socket(); }
0072     QSocketNotifier::Type type() const { return sn->type(); }
0073 
0074 public slots:
0075     void setEnabled(bool enable)       { sn->setEnabled(enable); }
0076 
0077 signals:
0078     void activated(int socket);
0079 
0080 private:
0081     QSocketNotifier *sn;
0082 };
0083 
0084 }
0085 
0086 #ifndef NO_IRISNET
0087 namespace XMPP {
0088 #endif
0089 
0090 Q_GLOBAL_STATIC(QMutex, pq_mutex)
0091 static ProcessQuit *g_pq = 0;
0092 
0093 inline bool is_gui_app()
0094 {
0095 #ifdef QT_GUI_LIB
0096         #pragma message("Port to Qt5")
0097     //QT5 return (QApplication::type() != QApplication::Tty);
0098         return false;
0099 #else
0100     return false;
0101 #endif
0102 }
0103 
0104 class ProcessQuit::Private : public QObject
0105 {
0106     Q_OBJECT
0107 public:
0108     ProcessQuit *q;
0109 
0110     bool done;
0111 #ifdef Q_OS_WIN
0112     bool use_handler;
0113 #endif
0114 #ifdef Q_OS_UNIX
0115     int sig_pipe[2];
0116     SafeSocketNotifier *sig_notifier;
0117 #endif
0118 
0119     Private(ProcessQuit *_q) : QObject(_q), q(_q)
0120     {
0121         done = false;
0122 #ifdef Q_OS_WIN
0123         use_handler = !is_gui_app();
0124         if(use_handler)
0125             SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, true);
0126 #endif
0127 #ifdef Q_OS_UNIX
0128         pipe(sig_pipe);
0129         sig_notifier = new SafeSocketNotifier(sig_pipe[0], QSocketNotifier::Read, this);
0130         connect(sig_notifier, &SafeSocketNotifier::activated, this, &Private::sig_activated);
0131         unixWatchAdd(SIGINT);
0132         unixWatchAdd(SIGHUP);
0133         unixWatchAdd(SIGTERM);
0134 #endif
0135     }
0136 
0137     ~Private() override
0138     {
0139 #ifdef Q_OS_WIN
0140         if(use_handler)
0141             SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, false);
0142 #endif
0143 #ifdef Q_OS_UNIX
0144         unixWatchRemove(SIGINT);
0145         unixWatchRemove(SIGHUP);
0146         unixWatchRemove(SIGTERM);
0147         delete sig_notifier;
0148         close(sig_pipe[0]);
0149         close(sig_pipe[1]);
0150 #endif
0151     }
0152 
0153 #ifdef Q_OS_WIN
0154     static BOOL winHandler(DWORD ctrlType)
0155     {
0156         Q_UNUSED(ctrlType);
0157         QMetaObject::invokeMethod(g_pq->d, "ctrl_ready", Qt::QueuedConnection);
0158         return true;
0159     }
0160 #endif
0161 
0162 #ifdef Q_OS_UNIX
0163     static void unixHandler(int sig)
0164     {
0165         Q_UNUSED(sig);
0166         unsigned char c = 0;
0167         ::write(g_pq->d->sig_pipe[1], &c, 1);
0168     }
0169 
0170     void unixWatchAdd(int sig)
0171     {
0172         struct sigaction sa;
0173         sigaction(sig, NULL, &sa);
0174         // if the signal is ignored, don't take it over.  this is
0175         //   recommended by the glibc manual
0176         if(sa.sa_handler == SIG_IGN)
0177             return;
0178         sigemptyset(&(sa.sa_mask));
0179         sa.sa_flags = 0;
0180         sa.sa_handler = unixHandler;
0181         sigaction(sig, &sa, 0);
0182     }
0183 
0184     void unixWatchRemove(int sig)
0185     {
0186         struct sigaction sa;
0187         sigaction(sig, NULL, &sa);
0188         // ignored means we skipped it earlier, so we should
0189         //   skip it again
0190         if(sa.sa_handler == SIG_IGN)
0191             return;
0192         sigemptyset(&(sa.sa_mask));
0193         sa.sa_flags = 0;
0194         sa.sa_handler = SIG_DFL;
0195         sigaction(sig, &sa, 0);
0196     }
0197 #endif
0198 
0199 public slots:
0200     void ctrl_ready()
0201     {
0202 #ifdef Q_OS_WIN
0203         do_emit();
0204 #endif
0205     }
0206 
0207     void sig_activated(int)
0208     {
0209 #ifdef Q_OS_UNIX
0210         unsigned char c;
0211         ::read(sig_pipe[0], &c, 1);
0212         do_emit();
0213 #endif
0214     }
0215 
0216 private:
0217     void do_emit()
0218     {
0219         // only signal once
0220         if(!done)
0221         {
0222             done = true;
0223             emit q->quit();
0224         }
0225     }
0226 };
0227 
0228 ProcessQuit::ProcessQuit(QObject *parent)
0229 :QObject(parent)
0230 {
0231     d = new Private(this);
0232 }
0233 
0234 ProcessQuit::~ProcessQuit()
0235 {
0236     delete d;
0237 }
0238 
0239 ProcessQuit *ProcessQuit::instance()
0240 {
0241     QMutexLocker locker(pq_mutex());
0242     if(!g_pq)
0243     {
0244         g_pq = new ProcessQuit;
0245         g_pq->moveToThread(QCoreApplication::instance()->thread());
0246 #ifndef NO_IRISNET
0247         irisNetAddPostRoutine(cleanup);
0248 #endif
0249     }
0250     return g_pq;
0251 }
0252 
0253 void ProcessQuit::reset()
0254 {
0255     QMutexLocker locker(pq_mutex());
0256     if(g_pq)
0257         g_pq->d->done = false;
0258 }
0259 
0260 void ProcessQuit::cleanup()
0261 {
0262     delete g_pq;
0263     g_pq = 0;
0264 }
0265 
0266 #ifndef NO_IRISNET
0267 }
0268 #endif
0269 
0270 #include "processquit.moc"