root/signalwrappers.cpp
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- bannedSignalNumber
- patchBSDMask
- patchBSDUserMask
- patchPOSIXMask
- patchPOSIXUserMaskWork
- patchPOSIXUserMask
- patchPOSIXUserMaskMT
- signal
- sigaction
- rt_sigaction
- sigvec
- sigblock
- sigsetmask
- siggetmask
- sigprocmask
- rt_sigprocmask
- sigsuspend
- sighold
- sigignore
- sigrelse
- __sigpause
- sigpause
- pthread_sigmask
- sigwait
- sigwaitinfo
- sigtimedwait
1 /****************************************************************************
2 * Copyright (C) 2006-2013 by Jason Ansel, Kapil Arya, and Gene Cooperman *
3 * jansel@csail.mit.edu, kapil@ccs.neu.edu, gene@ccs.neu.edu *
4 * *
5 * This file is part of DMTCP. *
6 * *
7 * DMTCP is free software: you can redistribute it and/or *
8 * modify it under the terms of the GNU Lesser General Public License as *
9 * published by the Free Software Foundation, either version 3 of the *
10 * License, or (at your option) any later version. *
11 * *
12 * DMTCP is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU Lesser General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with DMTCP:dmtcp/src. If not, see *
19 * <http://www.gnu.org/licenses/>. *
20 ****************************************************************************/
21
22 #include "dmtcpworker.h"
23 #include "mtcpinterface.h"
24 #include "syscallwrappers.h"
25 #include "../jalib/jassert.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30
31 #ifndef EXTERNC
32 #define EXTERNC extern "C"
33 #endif
34
35 using namespace dmtcp;
36
37 //gah!!! signals API is redundant
38
39 static bool checkpointSignalBlockedForProcess = false;
40 static __thread bool checkpointSignalBlockedForThread = false;
41 static int stopSignal = -1;
42
43
44 static int bannedSignalNumber()
45 {
46 if (stopSignal == -1) {
47 stopSignal = DmtcpWorker::determineCkptSignal();
48
49 // On some systems, the ckpt-signal may be blocked by default. Unblock it
50 // now.
51 sigset_t set;
52 sigemptyset(&set);
53 sigaddset(&set, stopSignal);
54 JASSERT(_real_pthread_sigmask(SIG_UNBLOCK, &set, NULL) == 0)
55 (strerror(_real_pthread_sigmask(SIG_UNBLOCK, &set, NULL))) (stopSignal);
56 }
57 return stopSignal;
58 }
59
60 static int patchBSDMask(int mask)
61 {
62 const int allowedMask = ~sigmask(bannedSignalNumber());
63 return mask & allowedMask;
64 }
65
66 static inline void patchBSDUserMask(int how, const int mask, int *oldmask)
67 {
68 const int bannedMask = sigmask(bannedSignalNumber());
69 if (checkpointSignalBlockedForProcess == true) {
70 *oldmask |= bannedMask;
71 } else {
72 *oldmask &= ~bannedMask;
73 }
74
75 if (how == SIG_BLOCK && (mask & bannedMask)) {
76 checkpointSignalBlockedForProcess = true;
77 } else if (how == SIG_SETMASK) {
78 checkpointSignalBlockedForProcess = ((mask & bannedMask) != 0);
79 }
80 }
81
82 static inline sigset_t patchPOSIXMask(const sigset_t* mask)
83 {
84 JASSERT(mask != NULL);
85 sigset_t t = *mask;
86
87 sigdelset(&t, bannedSignalNumber());
88 return t;
89 }
90
91 static inline void patchPOSIXUserMaskWork(int how, const sigset_t *set,
92 sigset_t *oldset,
93 bool *checkpointSignalBlocked)
94 {
95 if (oldset != NULL) {
96 if (*checkpointSignalBlocked == true) {
97 sigaddset(oldset, bannedSignalNumber());
98 } else {
99 sigdelset(oldset, bannedSignalNumber());
100 }
101 }
102
103 if (set != NULL) {
104 int bannedSignaIsMember = sigismember(set, bannedSignalNumber());
105 if (how == SIG_BLOCK && bannedSignaIsMember) {
106 *checkpointSignalBlocked = true;
107 } else if (how == SIG_UNBLOCK && bannedSignaIsMember) {
108 *checkpointSignalBlocked = false;
109 } else if (how == SIG_SETMASK) {
110 *checkpointSignalBlocked = bannedSignaIsMember;
111 }
112 }
113 }
114
115 static inline void patchPOSIXUserMask(int how, const sigset_t *set, sigset_t *oldset)
116 {
117 patchPOSIXUserMaskWork(how, set, oldset, &checkpointSignalBlockedForProcess);
118 }
119
120 /* Multi-threaded version of the above function */
121 static inline void patchPOSIXUserMaskMT(int how, const sigset_t *set, sigset_t *oldset)
122 {
123 patchPOSIXUserMaskWork(how, set, oldset, &checkpointSignalBlockedForThread);
124 }
125
126
127 //set the handler
128 EXTERNC sighandler_t signal(int signum, sighandler_t handler)
129 {
130 if(signum == bannedSignalNumber()) {
131 return SIG_IGN;
132 }
133 return _real_signal( signum, handler );
134 }
135
136
137 EXTERNC int sigaction(int signum, const struct sigaction *act,
138 struct sigaction *oldact)
139 {
140 if(signum == bannedSignalNumber() && act != NULL) {
141 JWARNING("Application trying to use DMTCP's signal for it's own use.\n"
142 " You should employ a different signal by setting the\n"
143 " environment variable DMTCP_SIGCKPT to the number\n"
144 " of the signal that DMTCP should use for checkpointing.")
145 (stopSignal);
146 act = NULL;
147 }
148 return _real_sigaction( signum, act, oldact);
149 }
150
151 EXTERNC int rt_sigaction(int signum, const struct sigaction *act,
152 struct sigaction *oldact)
153 {
154 return sigaction (signum, act, oldact);
155 //if(signum == bannedSignalNumber()) {
156 // act = NULL;
157 //}
158 //return _real_rt_sigaction( signum, act, oldact);
159 }
160
161 #if !__GLIBC_PREREQ(2,21)
162 EXTERNC int sigvec(int signum, const struct sigvec *vec, struct sigvec *ovec)
163 {
164 if(signum == bannedSignalNumber()) {
165 vec = NULL;
166 }
167 return _real_sigvec( signum, vec, ovec );
168 }
169 #endif
170
171 //set the mask
172 EXTERNC int sigblock(int mask)
173 {
174 int oldmask = _real_sigblock( patchBSDMask(mask) );
175 patchBSDUserMask(SIG_BLOCK, mask, &oldmask);
176 return oldmask;
177 }
178
179 EXTERNC int sigsetmask(int mask)
180 {
181 int oldmask = _real_sigsetmask( patchBSDMask(mask) );
182 patchBSDUserMask(SIG_SETMASK, mask, &oldmask);
183 return oldmask;
184 }
185
186 EXTERNC int siggetmask(void)
187 {
188 int oldmask = _real_siggetmask();
189 patchBSDUserMask(SIG_BLOCK, 0, &oldmask);
190 return oldmask;
191 }
192
193 EXTERNC int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
194 {
195 sigset_t tmp;
196 const sigset_t *orig = set;
197 if (set != NULL) {
198 tmp = patchPOSIXMask(set);
199 set = &tmp;
200 }
201
202 int ret = _real_sigprocmask( how, set, oldset );
203
204 if (ret != -1) {
205 patchPOSIXUserMask(how, orig, oldset);
206 }
207 return ret;
208 }
209
210 EXTERNC int rt_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
211 {
212 return sigprocmask(how, set, oldset);
213 // sigset_t tmp;
214 // const sigset_t *orig = set;
215 // if (set != NULL) {
216 // tmp = patchPOSIXMask(set);
217 // set = &tmp;
218 // }
219 //
220 // int ret = _real_rt_sigprocmask( how, set, oldset );
221 //
222 // if (ret != -1) {
223 // patchPOSIXUserMask(how, orig, oldset);
224 // }
225 // return ret;
226 }
227
228 EXTERNC int sigsuspend(const sigset_t *mask)
229 {
230 sigset_t tmp;
231 if (mask != NULL) {
232 tmp = patchPOSIXMask(mask);
233 mask = &tmp;
234 }
235
236 int ret = _real_sigsuspend(mask);
237 return ret;
238 }
239
240 /* FIXME: Reverify the logic of the following four wrappers:
241 * sighold, sigignore, sigrelse, sigpause
242 * These are deprecated according to manpage.
243 */
244 EXTERNC int sighold(int sig)
245 {
246 if (sig == bannedSignalNumber()) {
247 return 0;
248 }
249 return _real_sighold(sig);
250 }
251
252 EXTERNC int sigignore(int sig)
253 {
254 if (sig == bannedSignalNumber()) {
255 return 0;
256 }
257 return _real_sigignore(sig);
258 }
259
260 EXTERNC int sigrelse(int sig)
261 {
262 if (sig == bannedSignalNumber()) {
263 return 0;
264 }
265 return _real_sigrelse(sig);
266 }
267
268 // signal.h can define sigpause as a macro expanding into __sigpause
269 // That takes an extra arg to handle sigmask (BSD) or signal (System V)
270 // So, we wrap both version.
271 EXTERNC int __sigpause(int __sig_or_mask, int __is_sig)
272 {
273 JWARNING(false)
274 .Text("This function is deprecated. Use sigsuspend instead." \
275 " The DMTCP wrappers for this function may not be fully tested");
276 return _real__sigpause(__sig_or_mask, __is_sig);
277 }
278
279 // Remove any possible macro expansion from signal.h
280 // sigpause must not be invoked after this in this file.
281 #undef sigpause
282 EXTERNC int sigpause(int sig)
283 {
284 JWARNING(false)
285 .Text("This function is deprecated. Use sigsuspend instead." \
286 " The DMTCP wrappers for this function may not be fully tested");
287 return _real_sigpause(sig);
288 }
289
290 /*
291 * This wrapper should be thread safe so we use the multithreaded version of
292 * patchPOSIXUserMask function. This will declare the static variables with
293 * __thread to make them thread local.
294 */
295 EXTERNC int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldmask)
296 {
297 const sigset_t *orig = set;
298 sigset_t tmp;
299 if (set != NULL) {
300 tmp = patchPOSIXMask(set);
301 set = &tmp;
302 }
303
304 int ret = _real_pthread_sigmask( how, set, oldmask );
305
306 if (ret != -1) {
307 patchPOSIXUserMaskMT(how, orig, oldmask);
308 }
309
310 return ret;
311 }
312
313 /*
314 * TODO: man page says that sigwait is implemented via sigtimedwait, however
315 * sigtimedwait can return EINTR (acc. to man page) whereas sigwait won't.
316 * Should we make the wrappers for sigwait/sigtimedwait homogeneous??
317 * -- Kapil
318 */
319 EXTERNC int sigwait(const sigset_t *set, int *sig)
320 {
321 sigset_t tmp;
322 if (set != NULL) {
323 tmp = patchPOSIXMask(set);
324 set = &tmp;
325 }
326
327 int ret = _real_sigwait( set, sig );
328
329 return ret;
330 }
331
332 /*
333 * In sigwaitinfo and sigtimedwait, it is not possible to differentiate between
334 * a DMTCP_SIGCKPT and any other signal (that is outside the given signal set)
335 * that might have occurred while executing the system call. These system call
336 * will return -1 with errno set to EINTR.
337 * To deal with the situation, we do not remove the DMTCP_SIGCKPT from the
338 * signal set (if it is present); instead, we check the return value and if it
339 * turns out to be DMTCP_SIGCKPT, we raise the signal once again for this
340 * thread.
341 * Also note that once sigwaitinfo/sigtimedwait returns DMTCP_SIGCKPT, we won't
342 * be receiving another DMTCP_SIGCKPT until we have called _real_tkill due to
343 * obvious reasons so I believe it is safe to call _real_gettid() here.
344 * -- Kapil
345 *
346 * Update:
347 * Another way to write this wrapper would be to remove the STOPSIGNAL from the
348 * user supplied 'set' and then call sigwaitinfo and then we won't need to
349 * raise the STOPSIGNAL ourselves. However, there is a catch. sigwaitinfo will
350 * return 'EINTR' if the wait was interrupted by a signal handler (STOPSIGNAL
351 * in our case), thus we can either call sigwaitinfo again or return the error
352 * to the user code; I would like to do the former.
353 * -- Kapil
354 */
355 EXTERNC int sigwaitinfo(const sigset_t *set, siginfo_t *info)
356 {
357 int ret;
358 while ( 1 ) {
359 ret = _real_sigwaitinfo( set, info );
360 if ( ret != bannedSignalNumber() ) {
361 break;
362 }
363 raise(bannedSignalNumber());
364 }
365 return ret;
366 }
367
368 EXTERNC int sigtimedwait(const sigset_t *set, siginfo_t *info,
369 const struct timespec *timeout)
370 {
371 int ret;
372 while ( 1 ) {
373 ret = _real_sigtimedwait( set, info, timeout );
374 if ( ret != bannedSignalNumber() ) {
375 break;
376 }
377 raise(bannedSignalNumber());
378 }
379 return ret;
380 }