├── LICENCE ├── README.md ├── example.cpp ├── sigwatch.cpp ├── sigwatch.h └── sigwatch.pro /LICENCE: -------------------------------------------------------------------------------- 1 | Unix signal watcher for Qt. 2 | 3 | Copyright (C) 2014 Simon Knopp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Unix Signal Watcher For Qt 2 | ========================== 3 | 4 | Author: Simon Knopp 5 | Licence: [MIT](http://opensource.org/licenses/MIT) 6 | 7 | 8 | ## Summary 9 | 10 | When writing a Qt application, as with any application, it is sometimes useful 11 | to handle Unix signals. Of course, Qt already incorporates the notion of 12 | signals, so it would be nice if Unix signals could be mapped to Qt signals. Then 13 | we could write handlers for Unix signals and connect them up in the same way as 14 | normal Qt slots. 15 | 16 | The class described below does just this. It is heavily based on [this 17 | example](http://qt-project.org/doc/qt-5.0/qtdoc/unix-signals.html) in the Qt 18 | documentation, but it encapsulates that functionality in a generic re-usable 19 | class. 20 | 21 | ## Interface 22 | 23 | The interface is simple: you call `watchForSignal()` with the signals you're 24 | interested in, and `connect()` your handlers to `SIGNAL(unixSignal(int))`. 25 | 26 | ``` c++ 27 | class UnixSignalWatcher : public QObject 28 | { 29 | Q_OBJECT 30 | public: 31 | explicit UnixSignalWatcher(QObject *parent = 0); 32 | ~UnixSignalWatcher(); 33 | 34 | void watchForSignal(int signal); 35 | 36 | signals: 37 | void unixSignal(int signal); 38 | }; 39 | ``` 40 | 41 | ## Example usage 42 | 43 | Let's look at an example program. 44 | 45 | ``` c++ 46 | #include 47 | #include 48 | #include "sigwatch.h" 49 | 50 | int main(int argc, char *argv[]) 51 | { 52 | QCoreApplication app(argc, argv); 53 | qDebug() << "Hello from process" << QCoreApplication::applicationPid(); 54 | 55 | UnixSignalWatcher sigwatch; 56 | sigwatch.watchForSignal(SIGINT); 57 | sigwatch.watchForSignal(SIGTERM); 58 | QObject::connect(&sigwatch, SIGNAL(unixSignal(int)), &app, SLOT(quit())); 59 | 60 | int exitcode = app.exec(); 61 | qDebug() << "Goodbye"; 62 | return exitcode; 63 | } 64 | ``` 65 | 66 | This simply registers signal handlers for `SIGINT` and `SIGTERM` and then idles 67 | forever. If you run it (`qmake && make && ./sigwatch-demo`) you'll see a 68 | greeting and the pid of the process: 69 | 70 | Hello from process 6811 71 | 72 | Press `^C` to send `SIGINT`. The `UnixSignalWatcher` will handle the signal, 73 | which in turn is connected to `QCoreApplication::quit()`, so the event loop 74 | exits and the farewell message is printed. 75 | 76 | ^CCaught signal: Interrupt 77 | Goodbye 78 | 79 | Similarly, you could use `kill` to send `SIGTERM`. 80 | 81 | $ ./sigwatch-demo & 82 | Hello from process 6848 83 | $ kill 6848 84 | Caught signal: Terminated 85 | Goodbye 86 | 87 | If you send a signal that does not have a handler, though, you won't see the 88 | farewell message. For instance: 89 | 90 | $ ./sigwatch-demo 91 | Hello from process 6906 92 | $ kill -SIGABRT 6906 93 | Aborted (core dumped) 94 | 95 | ## Compatibility 96 | 97 | Tested with Qt 4.6 and 5.2 on Linux. 98 | 99 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sigwatch.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QCoreApplication app(argc, argv); 8 | qDebug() << "Hello from process" << QCoreApplication::applicationPid(); 9 | 10 | UnixSignalWatcher sigwatch; 11 | sigwatch.watchForSignal(SIGINT); 12 | sigwatch.watchForSignal(SIGTERM); 13 | QObject::connect(&sigwatch, SIGNAL(unixSignal(int)), &app, SLOT(quit())); 14 | 15 | int exitcode = app.exec(); 16 | qDebug() << "Goodbye"; 17 | return exitcode; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /sigwatch.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Unix signal watcher for Qt. 3 | * 4 | * Copyright (C) 2014 Simon Knopp 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "sigwatch.h" 32 | 33 | 34 | /*! 35 | * \brief The UnixSignalWatcherPrivate class implements the back-end signal 36 | * handling for the UnixSignalWatcher. 37 | * 38 | * \see http://qt-project.org/doc/qt-5.0/qtdoc/unix-signals.html 39 | */ 40 | class UnixSignalWatcherPrivate : public QObject 41 | { 42 | UnixSignalWatcher * const q_ptr; 43 | Q_DECLARE_PUBLIC(UnixSignalWatcher) 44 | 45 | public: 46 | UnixSignalWatcherPrivate(UnixSignalWatcher *q); 47 | ~UnixSignalWatcherPrivate(); 48 | 49 | void watchForSignal(int signal); 50 | static void signalHandler(int signal); 51 | 52 | void _q_onNotify(int sockfd); 53 | 54 | private: 55 | static int sockpair[2]; 56 | QSocketNotifier *notifier; 57 | QList watchedSignals; 58 | }; 59 | 60 | 61 | int UnixSignalWatcherPrivate::sockpair[2]; 62 | 63 | UnixSignalWatcherPrivate::UnixSignalWatcherPrivate(UnixSignalWatcher *q) : 64 | q_ptr(q) 65 | { 66 | // Create socket pair 67 | if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair)) { 68 | qDebug() << "UnixSignalWatcher: socketpair: " << ::strerror(errno); 69 | return; 70 | } 71 | 72 | // Create a notifier for the read end of the pair 73 | notifier = new QSocketNotifier(sockpair[1], QSocketNotifier::Read); 74 | QObject::connect(notifier, SIGNAL(activated(int)), q, SLOT(_q_onNotify(int))); 75 | notifier->setEnabled(true); 76 | } 77 | 78 | UnixSignalWatcherPrivate::~UnixSignalWatcherPrivate() 79 | { 80 | delete notifier; 81 | } 82 | 83 | /*! 84 | * Registers a handler for the given Unix \a signal. The handler will write to 85 | * a socket pair, the other end of which is connected to a QSocketNotifier. 86 | * This provides a way to break out of the asynchronous context from which the 87 | * signal handler is called and back into the Qt event loop. 88 | */ 89 | void UnixSignalWatcherPrivate::watchForSignal(int signal) 90 | { 91 | if (watchedSignals.contains(signal)) { 92 | qDebug() << "Already watching for signal" << signal; 93 | return; 94 | } 95 | 96 | // Register a sigaction which will write to the socket pair 97 | struct sigaction sigact; 98 | sigact.sa_handler = UnixSignalWatcherPrivate::signalHandler; 99 | sigact.sa_flags = 0; 100 | ::sigemptyset(&sigact.sa_mask); 101 | sigact.sa_flags |= SA_RESTART; 102 | if (::sigaction(signal, &sigact, NULL)) { 103 | qDebug() << "UnixSignalWatcher: sigaction: " << ::strerror(errno); 104 | return; 105 | } 106 | 107 | watchedSignals.append(signal); 108 | } 109 | 110 | /*! 111 | * Called when a Unix \a signal is received. Write to the socket to wake up the 112 | * QSocketNotifier. 113 | */ 114 | void UnixSignalWatcherPrivate::signalHandler(int signal) 115 | { 116 | ssize_t nBytes = ::write(sockpair[0], &signal, sizeof(signal)); 117 | Q_UNUSED(nBytes); 118 | } 119 | 120 | /*! 121 | * Called when the signal handler has written to the socket pair. Emits the Unix 122 | * signal as a Qt signal. 123 | */ 124 | void UnixSignalWatcherPrivate::_q_onNotify(int sockfd) 125 | { 126 | Q_Q(UnixSignalWatcher); 127 | 128 | int signal; 129 | ssize_t nBytes = ::read(sockfd, &signal, sizeof(signal)); 130 | Q_UNUSED(nBytes); 131 | qDebug() << "Caught signal:" << ::strsignal(signal); 132 | emit q->unixSignal(signal); 133 | } 134 | 135 | 136 | /*! 137 | * Create a new UnixSignalWatcher as a child of the given \a parent. 138 | */ 139 | UnixSignalWatcher::UnixSignalWatcher(QObject *parent) : 140 | QObject(parent), 141 | d_ptr(new UnixSignalWatcherPrivate(this)) 142 | { 143 | } 144 | 145 | /*! 146 | * Destroy this UnixSignalWatcher. 147 | */ 148 | UnixSignalWatcher::~UnixSignalWatcher() 149 | { 150 | delete d_ptr; 151 | } 152 | 153 | /*! 154 | * Register a signal handler for the given \a signal. 155 | * 156 | * After calling this method you can \c connect() to the unixSignal() Qt signal 157 | * to be notified when the Unix signal is received. 158 | */ 159 | void UnixSignalWatcher::watchForSignal(int signal) 160 | { 161 | Q_D(UnixSignalWatcher); 162 | d->watchForSignal(signal); 163 | } 164 | 165 | /*! 166 | * \fn void UnixSignalWatcher::unixSignal(int signal) 167 | * Emitted when the given Unix \a signal is received. 168 | * 169 | * watchForSignal() must be called for each Unix signal that you want to receive 170 | * via the unixSignal() Qt signal. If a watcher is watching multiple signals, 171 | * unixSignal() will be emitted whenever *any* of the watched Unix signals are 172 | * received, and the \a signal argument can be inspected to find out which one 173 | * was actually received. 174 | */ 175 | 176 | #include "moc_sigwatch.cpp" 177 | -------------------------------------------------------------------------------- /sigwatch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Unix signal watcher for Qt. 3 | * 4 | * Copyright (C) 2014 Simon Knopp 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef SIGWATCH_H 26 | #define SIGWATCH_H 27 | 28 | #include 29 | #include 30 | 31 | class UnixSignalWatcherPrivate; 32 | 33 | 34 | /*! 35 | * \brief The UnixSignalWatcher class converts Unix signals to Qt signals. 36 | * 37 | * To watch for a given signal, e.g. \c SIGINT, call \c watchForSignal(SIGINT) 38 | * and \c connect() your handler to unixSignal(). 39 | */ 40 | 41 | class UnixSignalWatcher : public QObject 42 | { 43 | Q_OBJECT 44 | public: 45 | explicit UnixSignalWatcher(QObject *parent = 0); 46 | ~UnixSignalWatcher(); 47 | 48 | void watchForSignal(int signal); 49 | 50 | signals: 51 | void unixSignal(int signal); 52 | 53 | private: 54 | UnixSignalWatcherPrivate * const d_ptr; 55 | Q_DECLARE_PRIVATE(UnixSignalWatcher) 56 | Q_PRIVATE_SLOT(d_func(), void _q_onNotify(int)) 57 | }; 58 | 59 | #endif // SIGWATCH_H 60 | -------------------------------------------------------------------------------- /sigwatch.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT = core 3 | 4 | TARGET = sigwatch-demo 5 | 6 | SOURCES += example.cpp \ 7 | sigwatch.cpp 8 | 9 | HEADERS += sigwatch.h 10 | 11 | --------------------------------------------------------------------------------