port fix of install_signal_handler from current libi2ncommon to compat code
[libasyncio] / utils / i2ncommon / signalfunc.cpp
CommitLineData
8d528bce
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
20/** @file
21 * @brief implementation of wrapper and tools for signal related stuff.
22 *
4f10e2ed 23 * @copyright © Copyright 2007-2009 by Intra2net AG
8d528bce
TJ
24 *
25 * opensource@intra2net.com
26 */
27
28#include "signalfunc.hpp"
29
30#include <algorithm>
31
32#include <unistd.h>
33#include <signal.h>
34#include <errno.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <dirent.h>
38#include <errno.h>
39#include <pwd.h>
40#include <grp.h>
41
4f10e2ed 42#include "containerfunc.hpp"
8d528bce
TJ
43
44
45namespace I2n
46{
47namespace SystemTools
48{
49
50
51namespace
52{
53
54/**
55 * helper for using sigaddset() as unary function within (STL) algorithms.
56 */
57struct SigAddSet
58{
59 sigset_t *m_set;
60 SigAddSet(sigset_t *set) : m_set(set) {}
61 int operator() (int sig) { return sigaddset(m_set,sig); }
62}; // eo struct SigAddSet
63
64
65
66/**
67 * blocks the given signals while existing.
68 *
69 * This class is the real (internal) implementation of the @a SignalBlocker .
70 *
71 * @internal This internal implementation is used to avoid including signal.h within the header file.
72 * This way, we can keep the header clean; and hide all the internals in the implementation.
73 */
74class _ScopedSignalBlocker : public SystemTools::Detail::SObject
75{
76 public:
77 _ScopedSignalBlocker(const std::vector<SystemTools::Signal>& sigs);
78 ~_ScopedSignalBlocker();
79
80 bool successful() const {return m_set; }
81
82 protected:
83 sigset_t m_sigset[1];
84 sigset_t m_oldsigset[1];
85 bool m_set;
86}; // eo _ScopedSignalBlocker
87
88
89/**
90 * Blocks the given signals.
91 *
92 * Constructs a sigset from the passed signals; and calls sigprocmask to block them.
93 * In case of interruption with EINTR during the call, the call is repeated some times
94 * to get the desired signals blocked.
95 *
96 * @param sigs the vector with the siganls which should be blocked.
97 */
98_ScopedSignalBlocker::_ScopedSignalBlocker(const std::vector<SystemTools::Signal>& sigs)
99{
100 sigemptyset(m_sigset);
101
102 // fill in signals
103 std::for_each(sigs.begin(), sigs.end(), SigAddSet(m_sigset) );
104
105 int res;
106 for(int cnt=1000; cnt-->0;)
107 {
108 res= sigprocmask(SIG_BLOCK,m_sigset, m_oldsigset);
109 if (!res || errno!=EINTR) break;
110 }
111 m_set= (res==0);
112} // eo _ScopedSignalBlocker::_ScopedSignalBlocker()
113
114
115/**
116 * Unblocks the signals by restoring the original block mask.
117 */
118_ScopedSignalBlocker::~_ScopedSignalBlocker()
119{
120 int res;
121 if (!m_set) return;
122 for(int cnt=1000; cnt-->0;)
123 {
124 res= sigprocmask(SIG_SETMASK, m_oldsigset, NULL);
125 if (!res || errno!=EINTR) break;
126 }
127} // eo _ScopedSignalBlocker::~_ScopedSignalBlocker()
128
129
130
131} // eo namespace <anonymous>
132
133
134/*************************************************************************\
135\*************************************************************************/
136
137
138
139/*
140 * Signal
141 */
142/// @cond
143
144const int Signal::VOID= 0;
145
146// helper macro to import the signal nums:
147#define IMPORT(sig) const int Signal::sig = SIG ## sig
148
149IMPORT(HUP);
150IMPORT(INT);
151IMPORT(QUIT);
152IMPORT(ILL);
153IMPORT(TRAP);
154IMPORT(ABRT);
155IMPORT(IOT);
156IMPORT(BUS);
157IMPORT(FPE);
158IMPORT(KILL);
159IMPORT(USR1);
160IMPORT(SEGV);
161IMPORT(USR2);
162IMPORT(PIPE);
163IMPORT(ALRM);
164IMPORT(TERM);
165IMPORT(STKFLT);
166IMPORT(CLD);
167IMPORT(CHLD);
168IMPORT(CONT);
169IMPORT(STOP);
170IMPORT(TSTP);
171IMPORT(TTIN);
172IMPORT(TTOU);
173IMPORT(URG);
174IMPORT(XCPU);
175IMPORT(XFSZ);
176IMPORT(VTALRM);
177IMPORT(PROF);
178IMPORT(WINCH);
179IMPORT(POLL);
180IMPORT(IO);
181IMPORT(PWR);
182IMPORT(SYS);
183
184// remove helper macro
185#undef IMPORT
186
187/// @endcond
188
189int Signal::RT(int num)
190{
191 return SIGRTMIN + num;
192} // eo Signal::RT
193
194
195/*
196 * SignalCode
197 */
198
199
200/// @cond
201
202// basically: define apropriate CODE macro and copy the CODE() parts from hpp:
203
204#undef CODE
205#define CODE(name) const int SignalCode::name = SI_ ## name
206 CODE(USER); CODE(QUEUE); CODE(TIMER); CODE(MESGQ);
207 CODE(ASYNCIO);
208#undef SIGIO
209 CODE(SIGIO);
210#undef CODE
211#define CODE(name) const int SignalCode::ILL::name = ILL_ ## name
212 CODE(ILLOPC); CODE(ILLOPN); CODE(ILLADR); CODE(ILLTRP);
213 CODE(PRVOPC); CODE(PRVREG); CODE(COPROC); CODE(BADSTK);
214#undef CODE
215#define CODE(name) const int SignalCode::FPE::name = FPE_ ## name
216 CODE(INTDIV); CODE(INTOVF); CODE(FLTDIV); CODE(FLTOVF);
217 CODE(FLTUND); CODE(FLTRES); CODE(FLTINV); CODE(FLTSUB);
218#undef CODE
219#define CODE(name) const int SignalCode::SEGV::name = SEGV_ ## name
220 CODE(MAPERR); CODE(ACCERR);
221#undef CODE
222#define CODE(name) const int SignalCode::BUS::name = BUS_ ## name
223 CODE(ADRALN); CODE(ADRERR); CODE(OBJERR);
224#undef CODE
225#define CODE(name) const int SignalCode::TRAP::name = TRAP_ ## name
226 CODE(BRKPT); CODE(TRACE);
227#undef CODE
228#define CODE(name) const int SignalCode::CHLD::name = CLD_ ## name
229 CODE(EXITED); CODE(KILLED); CODE(DUMPED); CODE(TRAPPED);
230 CODE(STOPPED); CODE(CONTINUED);
231#undef CODE
232#define CODE(name) const int SignalCode::POLL::name = POLL_ ## name
233 CODE(IN); CODE(OUT); CODE(MSG); CODE(ERR); CODE(PRI); CODE(HUP);
234#undef CODE
235/// @endcond
236
237/*
238 * ScopedSignalBlocker
239 */
240
241
242/**
243 * Blocks the given signal.
244 * @param sig the signal which should be blocked.
245 */
246ScopedSignalBlocker::ScopedSignalBlocker(Signal sig)
247{
248 Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig) );
249} // eo ScopedSignalBlocker::ScopedSignalBlocker
250
251
252ScopedSignalBlocker::ScopedSignalBlocker(Signal sig1, Signal sig2)
253{
254 Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig1)(sig2) );
255} // eo ScopedSignalBlocker::ScopedSignalBlocker
256
257
258ScopedSignalBlocker::ScopedSignalBlocker(Signal sig1, Signal sig2, Signal sig3)
259{
260 Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig1)(sig2)(sig3) );
261} // eo ScopedSignalBlocker::ScopedSignalBlocker
262
263
264/**
265 * Blocks the given signals.
266 * @param sigs vector with the signals which should be blocked.
267 */
268ScopedSignalBlocker::ScopedSignalBlocker(const std::vector<Signal>& sigs)
269{
270 Implementation = new _ScopedSignalBlocker( sigs );
271} // eo ScopedSignalBlocker::ScopedSignalBlocker
272
273
274/**
275 * Unblocks the signals by restoring the previous blocking list.
276 */
277ScopedSignalBlocker::~ScopedSignalBlocker()
278{
279 if (Implementation)
280 {
281 delete Implementation;
282 Implementation= NULL;
283 }
284} // eo ScopedSignalBlocker::ScopedSignalBlocker
285
286
287
288/*
289** signal handling
290*/
291
292typedef std::map< int, struct sigaction > SignalActionMap;
293
294namespace {
295
296SignalActionMap original_signal_action;
297
298} // eo namespace <anonymous>
299
300
301/**
302 * @brief installs a new signal action.
4f10e2ed 303 * @param sig the signal
8d528bce
TJ
304 * @param new_action the new signal action.
305 * @return @a true iff the new signal action was succesfully installed.
306 *
307 * Remembers the original value of the signal handler for later restoring.
308 */
309bool install_signal_handler(
310 Signal sig,
311 struct sigaction& new_action
312)
313{
314 struct sigaction old_action[1];
315 int signum= sig;
316 int res= ::sigaction(signum, &new_action, old_action);
317 if (0 == res)
318 {
319 SignalActionMap::iterator it= original_signal_action.find(signum);
320 if (it == original_signal_action.end())
321 {
322 original_signal_action[signum] = *old_action;
323 }
324 }
325 else if (res > 0)
326 {
327 // some glibc's seem to return the errno instead of storing
328 // it in the appropriate var... *sigh*
329 errno = res;
330 }
331 return (0 == res);
332} // eo install_signal_handler(int,sigaction&)
333
334
335/**
336 * @brief installs a simple signal handler.
337 * @param sig the signal.
338 * @param handler pointer to the signal handler.
339 * @return @a true iff the handler was successfully installed.
340 *
341 * Remembers the original value of the signal handler for later restoring.
342 */
343bool install_signal_handler(
344 Signal sig,
345 void(*handler)(int)
346)
347{
348 struct sigaction new_action;
349 new_action.sa_handler= handler;
350 sigemptyset( &new_action.sa_mask );
351 new_action.sa_flags= 0;
352 new_action.sa_restorer= NULL;
353 return install_signal_handler(sig, new_action);
354} // eo install_signal_handler(signum,void(*)(int))
355
356
357
358/**
359 * @brief installs a signal action handler.
360 * @param sig the signal
361 * @param handler pointer to the signal action handler.
362 * @return @a true iff the action handler was successfully installed.
363 *
364 * Remembers the original value of the signal handler for later restoring.
365 */
366bool install_signal_handler(
367 Signal sig,
368 void(*handler)(int,struct siginfo*,void*)
369)
370{
371 struct sigaction new_action;
2daf78cd 372 new_action.sa_sigaction= (void (*)(int, siginfo_t*, void*))(handler);
8d528bce
TJ
373 sigemptyset( &new_action.sa_mask );
374 new_action.sa_flags= SA_SIGINFO;
375 new_action.sa_restorer= NULL;
376 return install_signal_handler(sig, new_action);
377} // eo install_signal_handler(signum,void(*)(int,siginfo_t*,void*))
378
379
380/**
381 * @brief ignores a signal.
382 * @param sig the signal
383 * @return @a true iff the ignore handler was successfully installed.
384 *
385 * Remembers the original value of the signal handler for later restoring.
386 */
387bool ignore_signal(Signal sig)
388{
389 return install_signal_handler(sig, SIG_IGN );
390} // eo ignore_signal(Signal)
391
392
393/**
394 * @brief enables the default signal handler.
395 * @param sig the signal
396 * @return @a true iff the default handler was successfully installed.
397 *
398 * Remembers the original value of the signal handler for later restoring.
399 */
400bool install_default_signal_handler(Signal sig)
401{
402 return install_signal_handler(sig, SIG_DFL );
403} // eo install_default_signal_handler(Signal)
404
405
406/**
407 * @brief restores a signal handle to its original value.
408 * @param sig the signal.
409 * @return @a true iff the handler was sucessfully restored.
410 */
411bool restore_signal_handler(Signal sig)
412{
413 int signum= sig;
414 SignalActionMap::iterator it= original_signal_action.find(signum);
415 int res= -1;
416 if (it != original_signal_action.end())
417 {
418 res= ::sigaction(signum, &(it->second), NULL);
419 if (0 == res)
420 {
421 original_signal_action.erase(it);
422 }
423 }
424 return (0 == res);
425} // eo restore_signal_handler
426
427
428
429/**
430 * @brief convenience function; send's a signal to a process.
431 * @param pid PID of the process which should recive the signal.
432 * @param signal the signal to send.
433 * @return @a true iff sending of the signal succeeded.
434 */
435bool send_signal( pid_t pid, Signal signal)
436{
437 return ::kill(pid, signal.Value) == 0;
438} // eo send_signal(pid_t,Signal)
439
440
441
442} // eo namespace SystemTools
443} // eo namespace I2n