Increase version to 2.9
[libi2ncommon] / utils / signalfunc.cpp
CommitLineData
f3311b30
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*/
e525f445
RP
20/** @file
21 * @brief implementation of wrapper and tools for signal related stuff.
22 *
23 * @copyright © Copyright 2007-2008 by Intra2net AG
e525f445
RP
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
43namespace I2n
44{
45namespace SystemTools
46{
47
48
49namespace
50{
51
52/**
53 * helper for using sigaddset() as unary function within (STL) algorithms.
54 */
55struct 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 */
72class _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
142const int Signal::VOID= 0;
143
144// helper macro to import the signal nums:
145#define IMPORT(sig) const int Signal::sig = SIG ## sig
146
147IMPORT(HUP);
148IMPORT(INT);
149IMPORT(QUIT);
150IMPORT(ILL);
151IMPORT(TRAP);
152IMPORT(ABRT);
153IMPORT(IOT);
154IMPORT(BUS);
155IMPORT(FPE);
156IMPORT(KILL);
157IMPORT(USR1);
158IMPORT(SEGV);
159IMPORT(USR2);
160IMPORT(PIPE);
161IMPORT(ALRM);
162IMPORT(TERM);
163IMPORT(STKFLT);
164IMPORT(CLD);
165IMPORT(CHLD);
166IMPORT(CONT);
167IMPORT(STOP);
168IMPORT(TSTP);
169IMPORT(TTIN);
170IMPORT(TTOU);
171IMPORT(URG);
172IMPORT(XCPU);
173IMPORT(XFSZ);
174IMPORT(VTALRM);
175IMPORT(PROF);
176IMPORT(WINCH);
177IMPORT(POLL);
178IMPORT(IO);
179IMPORT(PWR);
180IMPORT(SYS);
181
182// remove helper macro
183#undef IMPORT
184
185/// @endcond
186
187int 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 */
244ScopedSignalBlocker::ScopedSignalBlocker(Signal sig)
245{
246 Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig) );
247} // eo ScopedSignalBlocker::ScopedSignalBlocker
248
249
250ScopedSignalBlocker::ScopedSignalBlocker(Signal sig1, Signal sig2)
251{
252 Implementation = new _ScopedSignalBlocker( TransientPushBackFiller< Signal, std::vector >()(sig1)(sig2) );
253} // eo ScopedSignalBlocker::ScopedSignalBlocker
254
255
256ScopedSignalBlocker::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 */
266ScopedSignalBlocker::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 */
275ScopedSignalBlocker::~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
290typedef std::map< int, struct sigaction > SignalActionMap;
291
292namespace {
293
294SignalActionMap 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 */
307bool 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 }
f5284bf7
RP
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 }
e525f445
RP
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 */
341bool 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 */
364bool install_signal_handler(
365 Signal sig,
366 void(*handler)(int,struct siginfo*,void*)
367)
368{
369 struct sigaction new_action;
a9948c5d 370 new_action.sa_sigaction= (void (*)(int, siginfo_t*, void*))(handler);
e525f445
RP
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 */
385bool 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 */
398bool 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 */
409bool 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 */
433bool 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