Commit | Line | Data |
---|---|---|
f3311b30 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 | */ | |
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 | ||
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 | } | |
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 | */ | |
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; | |
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 | */ | |
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 |