Commit | Line | Data |
---|---|---|
8d528bce TJ |
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 | * | |
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 | ||
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. | |
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 | */ | |
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 |