summaryrefslogtreecommitdiff
path: root/libpthread/linuxthreads/signals.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpthread/linuxthreads/signals.c')
-rw-r--r--libpthread/linuxthreads/signals.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/libpthread/linuxthreads/signals.c b/libpthread/linuxthreads/signals.c
new file mode 100644
index 000000000..2a14c8c0d
--- /dev/null
+++ b/libpthread/linuxthreads/signals.c
@@ -0,0 +1,215 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix */
+/* threads for Linux. */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU Library General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU Library General Public License for more details. */
+
+/* Handling of signals */
+
+#include <errno.h>
+#include <signal.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include <ucontext.h>
+
+/* mods for uClibc: __libc_sigaction is not in any standard headers */
+extern int __libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact);
+
+int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
+{
+ sigset_t mask;
+
+ if (newmask != NULL) {
+ mask = *newmask;
+ /* Don't allow __pthread_sig_restart to be unmasked.
+ Don't allow __pthread_sig_cancel to be masked. */
+ switch(how) {
+ case SIG_SETMASK:
+ sigaddset(&mask, __pthread_sig_restart);
+ sigdelset(&mask, __pthread_sig_cancel);
+ if (__pthread_sig_debug > 0)
+ sigdelset(&mask, __pthread_sig_debug);
+ break;
+ case SIG_BLOCK:
+ sigdelset(&mask, __pthread_sig_cancel);
+ if (__pthread_sig_debug > 0)
+ sigdelset(&mask, __pthread_sig_debug);
+ break;
+ case SIG_UNBLOCK:
+ sigdelset(&mask, __pthread_sig_restart);
+ break;
+ }
+ newmask = &mask;
+ }
+ if (sigprocmask(how, newmask, oldmask) == -1)
+ return errno;
+ else
+ return 0;
+}
+
+int pthread_kill(pthread_t thread, int signo)
+{
+ pthread_handle handle = thread_handle(thread);
+ int pid;
+
+ __pthread_lock(&handle->h_lock, NULL);
+ if (invalid_handle(handle, thread)) {
+ __pthread_unlock(&handle->h_lock);
+ return ESRCH;
+ }
+ pid = handle->h_descr->p_pid;
+ __pthread_unlock(&handle->h_lock);
+ if (kill(pid, signo) == -1)
+ return errno;
+ else
+ return 0;
+}
+
+union sighandler __sighandler[NSIG] =
+ { [1 ... NSIG - 1] = { (arch_sighandler_t) SIG_ERR } };
+
+/* The wrapper around sigaction. Install our own signal handler
+ around the signal. */
+int __pthread_sigaction(int sig, const struct sigaction * act,
+ struct sigaction * oact)
+{
+ struct sigaction newact;
+ struct sigaction *newactp;
+ __sighandler_t old = SIG_DFL;
+
+ if (sig == __pthread_sig_restart ||
+ sig == __pthread_sig_cancel ||
+ (sig == __pthread_sig_debug && __pthread_sig_debug > 0))
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+ if (sig > 0 && sig < NSIG)
+ old = (__sighandler_t) __sighandler[sig].old;
+ if (act)
+ {
+ newact = *act;
+ if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL
+ && sig > 0 && sig < NSIG)
+ {
+ if (act->sa_flags & SA_SIGINFO)
+ newact.sa_handler = (__sighandler_t) __pthread_sighandler_rt;
+ else
+ newact.sa_handler = (__sighandler_t) __pthread_sighandler;
+ if (old == SIG_IGN || old == SIG_DFL || old == SIG_ERR)
+ __sighandler[sig].old = (arch_sighandler_t) act->sa_handler;
+ }
+ newactp = &newact;
+ }
+ else
+ newactp = NULL;
+ if (__libc_sigaction(sig, newactp, oact) == -1)
+ {
+ if (act)
+ __sighandler[sig].old = (arch_sighandler_t) old;
+ return -1;
+ }
+ if (sig > 0 && sig < NSIG)
+ {
+ if (oact != NULL
+ /* We may have inherited SIG_IGN from the parent, so return the
+ kernel's idea of the signal handler the first time
+ through. */
+ && old != SIG_ERR)
+ oact->sa_handler = old;
+ if (act)
+ /* For the assignment it does not matter whether it's a normal
+ or real-time signal. */
+ __sighandler[sig].old = (arch_sighandler_t) act->sa_handler;
+ }
+ return 0;
+}
+#ifdef SHARED
+strong_alias(__pthread_sigaction, __sigaction)
+strong_alias(__pthread_sigaction, sigaction)
+#endif
+
+/* sigwait -- synchronously wait for a signal */
+int __pthread_sigwait(const sigset_t * set, int * sig)
+{
+ volatile pthread_descr self = thread_self();
+ sigset_t mask;
+ int s;
+ sigjmp_buf jmpbuf;
+ struct sigaction sa;
+
+ /* Get ready to block all signals except those in set
+ and the cancellation signal.
+ Also check that handlers are installed on all signals in set,
+ and if not, install our dummy handler. This is conformant to
+ POSIX: "The effect of sigwait() on the signal actions for the
+ signals in set is unspecified." */
+ sigfillset(&mask);
+ sigdelset(&mask, __pthread_sig_cancel);
+ for (s = 1; s < NSIG; s++) {
+ if (sigismember(set, s) &&
+ s != __pthread_sig_restart &&
+ s != __pthread_sig_cancel &&
+ s != __pthread_sig_debug) {
+ sigdelset(&mask, s);
+ if (__sighandler[s].old == (arch_sighandler_t) SIG_ERR ||
+ __sighandler[s].old == (arch_sighandler_t) SIG_DFL ||
+ __sighandler[s].old == (arch_sighandler_t) SIG_IGN) {
+ sa.sa_handler = __pthread_null_sighandler;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(s, &sa, NULL);
+ }
+ }
+ }
+ /* Test for cancellation */
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
+ if (! (THREAD_GETMEM(self, p_canceled)
+ && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) {
+ /* Reset the signal count */
+ THREAD_SETMEM(self, p_signal, 0);
+ /* Say we're in sigwait */
+ THREAD_SETMEM(self, p_sigwaiting, 1);
+ /* Unblock the signals and wait for them */
+ sigsuspend(&mask);
+ }
+ }
+ THREAD_SETMEM(self, p_cancel_jmp, NULL);
+ /* The signals are now reblocked. Check for cancellation */
+ pthread_testcancel();
+ /* We should have self->p_signal != 0 and equal to the signal received */
+ *sig = THREAD_GETMEM(self, p_signal);
+ return 0;
+}
+#ifdef SHARED
+strong_alias (__pthread_sigwait, sigwait)
+#endif
+
+/* Redefine raise() to send signal to calling thread only,
+ as per POSIX 1003.1c */
+int __pthread_raise (int sig)
+{
+ int retcode = pthread_kill(pthread_self(), sig);
+ if (retcode == 0)
+ return 0;
+ else {
+ errno = retcode;
+ return -1;
+ }
+}
+#ifdef SHARED
+strong_alias (__pthread_raise, raise)
+#endif
+
+/* This files handles cancellation internally. */
+LIBC_CANCEL_HANDLED ();