8cad55e8c807ea6f3e47461c4f6a226caf3c8907
[libasyncio] / i2ncommon / signalfunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on 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  *
23  * @copyright © Copyright 2007-2008 by Intra2net AG
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
42 #include <containerfunc.hpp>
43
44
45 namespace I2n
46 {
47 namespace SystemTools
48 {
49
50
51 namespace
52 {
53
54 /**
55  * helper for using sigaddset() as unary function within (STL) algorithms.
56  */
57 struct 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  */
74 class _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
144 const int Signal::VOID= 0;
145
146 // helper macro to import the signal nums:
147 #define IMPORT(sig) const int Signal::sig = SIG ## sig
148
149 IMPORT(HUP);
150 IMPORT(INT);
151 IMPORT(QUIT);
152 IMPORT(ILL);
153 IMPORT(TRAP);
154 IMPORT(ABRT);
155 IMPORT(IOT);
156 IMPORT(BUS);
157 IMPORT(FPE);
158 IMPORT(KILL);
159 IMPORT(USR1);
160 IMPORT(SEGV);
161 IMPORT(USR2);
162 IMPORT(PIPE);
163 IMPORT(ALRM);
164 IMPORT(TERM);
165 IMPORT(STKFLT);
166 IMPORT(CLD);
167 IMPORT(CHLD);
168 IMPORT(CONT);
169 IMPORT(STOP);
170 IMPORT(TSTP);
171 IMPORT(TTIN);
172 IMPORT(TTOU);
173 IMPORT(URG);
174 IMPORT(XCPU);
175 IMPORT(XFSZ);
176 IMPORT(VTALRM);
177 IMPORT(PROF);
178 IMPORT(WINCH);
179 IMPORT(POLL);
180 IMPORT(IO);
181 IMPORT(PWR);
182 IMPORT(SYS);
183
184 // remove helper macro
185 #undef IMPORT
186
187 /// @endcond
188
189 int 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  */
246 ScopedSignalBlocker::ScopedSignalBlocker(Signal sig)
247 {
248     Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig) );
249 } // eo ScopedSignalBlocker::ScopedSignalBlocker
250
251
252 ScopedSignalBlocker::ScopedSignalBlocker(Signal sig1, Signal sig2)
253 {
254     Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig1)(sig2) );
255 } // eo ScopedSignalBlocker::ScopedSignalBlocker
256
257
258 ScopedSignalBlocker::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  */
268 ScopedSignalBlocker::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  */
277 ScopedSignalBlocker::~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
292 typedef std::map< int, struct sigaction > SignalActionMap;
293
294 namespace {
295
296 SignalActionMap original_signal_action;
297
298 } // eo namespace <anonymous>
299
300
301 /**
302  * @brief installs a new signal action.
303  * @param sig the signal 
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  */
309 bool 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  */
343 bool 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  */
366 bool install_signal_handler(
367     Signal sig,
368     void(*handler)(int,struct siginfo*,void*)
369 )
370 {
371     struct sigaction new_action;
372     new_action.sa_sigaction= handler;
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  */
387 bool 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  */
400 bool 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  */
411 bool 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  */
435 bool 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