summaryrefslogtreecommitdiff
path: root/src/signal/sigaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/signal/sigaction.c')
-rw-r--r--src/signal/sigaction.c47
1 files changed, 26 insertions, 21 deletions
diff --git a/src/signal/sigaction.c b/src/signal/sigaction.c
index af47195e..e45308fa 100644
--- a/src/signal/sigaction.c
+++ b/src/signal/sigaction.c
@@ -7,12 +7,6 @@
#include "lock.h"
#include "ksigaction.h"
-volatile int dummy_lock[1] = { 0 };
-
-extern hidden volatile int __abort_lock[1];
-
-weak_alias(dummy_lock, __abort_lock);
-
static int unmask_done;
static unsigned long handler_set[_NSIG/(8*sizeof(long))];
@@ -21,10 +15,11 @@ void __get_handler_set(sigset_t *set)
memcpy(set, handler_set, sizeof handler_set);
}
+volatile int __eintr_valid_flag;
+
int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
{
struct k_sigaction ksa, ksa_old;
- unsigned long set[_NSIG/(8*sizeof(long))];
if (sa) {
if ((uintptr_t)sa->sa_handler > 1UL) {
a_or_l(handler_set+(sig-1)/(8*sizeof(long)),
@@ -43,25 +38,20 @@ int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigact
SIGPT_SET, 0, _NSIG/8);
unmask_done = 1;
}
- }
- /* Changing the disposition of SIGABRT to anything but
- * SIG_DFL requires a lock, so that it cannot be changed
- * while abort is terminating the process after simply
- * calling raise(SIGABRT) failed to do so. */
- if (sa->sa_handler != SIG_DFL && sig == SIGABRT) {
- __block_all_sigs(&set);
- LOCK(__abort_lock);
+
+ if (!(sa->sa_flags & SA_RESTART)) {
+ a_store(&__eintr_valid_flag, 1);
+ }
}
ksa.handler = sa->sa_handler;
- ksa.flags = sa->sa_flags | SA_RESTORER;
+ ksa.flags = sa->sa_flags;
+#ifdef SA_RESTORER
+ ksa.flags |= SA_RESTORER;
ksa.restorer = (sa->sa_flags & SA_SIGINFO) ? __restore_rt : __restore;
+#endif
memcpy(&ksa.mask, &sa->sa_mask, _NSIG/8);
}
int r = __syscall(SYS_rt_sigaction, sig, sa?&ksa:0, old?&ksa_old:0, _NSIG/8);
- if (sig == SIGABRT && sa && sa->sa_handler != SIG_DFL) {
- UNLOCK(__abort_lock);
- __restore_sigs(&set);
- }
if (old && !r) {
old->sa_handler = ksa_old.handler;
old->sa_flags = ksa_old.flags;
@@ -72,11 +62,26 @@ int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigact
int __sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
{
+ unsigned long set[_NSIG/(8*sizeof(long))];
+
if (sig-32U < 3 || sig-1U >= _NSIG-1) {
errno = EINVAL;
return -1;
}
- return __libc_sigaction(sig, sa, old);
+
+ /* Doing anything with the disposition of SIGABRT requires a lock,
+ * so that it cannot be changed while abort is terminating the
+ * process and so any change made by abort can't be observed. */
+ if (sig == SIGABRT) {
+ __block_all_sigs(&set);
+ LOCK(__abort_lock);
+ }
+ int r = __libc_sigaction(sig, sa, old);
+ if (sig == SIGABRT) {
+ UNLOCK(__abort_lock);
+ __restore_sigs(&set);
+ }
+ return r;
}
weak_alias(__sigaction, sigaction);