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