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"