/* * linux/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds * * 1997-11-02 Modified for POSIX.1b signals by Richard Henderson * * 2003-06-02 Jim Houston - Concurrent Computer Corp. * Changes to use preallocated sigqueue structures * to allow signals to be sent reliably. */ #include <linux/config.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/tty.h>#include <linux/binfmts.h>#include <linux/security.h>#include <linux/syscalls.h>#include <linux/ptrace.h>#include <linux/posix-timers.h>#include <linux/signal.h>#include <linux/audit.h>#include <asm/param.h>#include <asm/uaccess.h>#include <asm/unistd.h>#include <asm/siginfo.h> /* * SLAB caches for signal bits. */ static kmem_cache_t *sigqueue_cachep; /* * In POSIX a signal is sent either to a specific thread (Linux task) * or to the process as a whole (Linux thread group). How the signal * is sent determines whether it's to one thread or the whole group, * which determines which signal mask(s) are involved in blocking it * from being delivered until later. When the signal is delivered, * either it's caught or ignored by a user handler or it has a default * effect that applies to the whole thread group (POSIX process). * * The possible effects an unblocked signal set to SIG_DFL can have are: * ignore - Nothing Happens * terminate - kill the process, i.e. all threads in the group, * similar to exit_group. The group leader (only) reports * WIFSIGNALED status to its parent. * coredump - write a core dump file describing all threads using * the same mm and then kill all those threads * stop - stop all the threads in the group, i.e. TASK_STOPPED state * * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. * Other signals when not blocked and set to SIG_DFL behaves as follows. * The job control signals also have other special effects. * * +--------------------+------------------+ * | POSIX signal | default action | * +--------------------+------------------+ * | SIGHUP | terminate | * | SIGINT | terminate | * | SIGQUIT | coredump | * | SIGILL | coredump | * | SIGTRAP | coredump | * | SIGABRT/SIGIOT | coredump | * | SIGBUS | coredump | * | SIGFPE | coredump | * | SIGKILL | terminate(+) | * | SIGUSR1 | terminate | * | SIGSEGV | coredump | * | SIGUSR2 | terminate | * | SIGPIPE | terminate | * | SIGALRM | terminate | * | SIGTERM | terminate | * | SIGCHLD | ignore | * | SIGCONT | ignore(*) | * | SIGSTOP | stop(*)(+) | * | SIGTSTP | stop(*) | * | SIGTTIN | stop(*) | * | SIGTTOU | stop(*) | * | SIGURG | ignore | * | SIGXCPU | coredump | * | SIGXFSZ | coredump | * | SIGVTALRM | terminate | * | SIGPROF | terminate | * | SIGPOLL/SIGIO | terminate | * | SIGSYS/SIGUNUSED | coredump | * | SIGSTKFLT | terminate | * | SIGWINCH | ignore | * | SIGPWR | terminate | * | SIGRTMIN-SIGRTMAX | terminate | * +--------------------+------------------+ * | non-POSIX signal | default action | * +--------------------+------------------+ * | SIGEMT | coredump | * +--------------------+------------------+ * * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". * (*) Special job control effects: * When SIGCONT is sent, it resumes the process (all threads in the group) * from TASK_STOPPED state and also clears any pending/queued stop signals * (any of those marked with "stop(*)"). This happens regardless of blocking, * catching, or ignoring SIGCONT. When any stop signal is sent, it clears * any pending/queued SIGCONT signals; this happens regardless of blocking, * catching, or ignored the stop signal, though (except for SIGSTOP) the * default action of stopping the process may happen later or never. */ #ifdef SIGEMT#define M_SIGEMT M(SIGEMT)#else#define M_SIGEMT 0#endif #if SIGRTMIN > BITS_PER_LONG#define M(sig) (1ULL << ((sig)-1))#else#define M(sig) (1UL << ((sig)-1))#endif#define T(sig, mask) (M(sig) & (mask)) #define SIG_KERNEL_ONLY_MASK (\ M(SIGKILL) | M(SIGSTOP) ) #define SIG_KERNEL_STOP_MASK (\ M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) ) #define SIG_KERNEL_COREDUMP_MASK (\ M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \ M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \ M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT ) #define SIG_KERNEL_IGNORE_MASK (\ M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) ) #define sig_kernel_only(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK))#define sig_kernel_coredump(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK))#define sig_kernel_ignore(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK))#define sig_kernel_stop(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK)) #define sig_user_defined(t, signr) \ (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) && \ ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN)) #define sig_fatal(t, signr) \ (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL) static int sig_ignored(struct task_struct *t, int sig){ void __user * handler; /* * Tracers always want to know about signals.. */ if (t->ptrace & PT_PTRACED) return 0; /* * Blocked signals are never ignored, since the * signal handler may change by the time it is * unblocked. */ if (sigismember(&t->blocked, sig)) return 0; /* Is it explicitly or implicitly ignored? */ handler = t->sighand->action[sig-1].sa.sa_handler; return handler == SIG_IGN || (handler == SIG_DFL && sig_kernel_ignore(sig));} /* * Re-calculate pending state from the set of locally pending * signals, globally pending signals, and blocked signals. */static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked){ unsigned long ready; long i; switch (_NSIG_WORDS) { default: for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;) ready |= signal->sig[i] &~ blocked->sig[i]; break; case 4: ready = signal->sig[3] &~ blocked->sig[3]; ready |= signal->sig[2] &~ blocked->sig[2]; ready |= signal->sig[1] &~ blocked->sig[1]; ready |= signal->sig[0] &~ blocked->sig[0]; break; case 2: ready = signal->sig[1] &~ blocked->sig[1]; ready |= signal->sig[0] &~ blocked->sig[0]; break; case 1: ready = signal->sig[0] &~ blocked->sig[0]; } return ready != 0;} #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) fastcall void recalc_sigpending_tsk(struct task_struct *t){ if (t->signal->group_stop_count > 0 || (freezing(t)) || PENDING(&t->pending, &t->blocked) || PENDING(&t->signal->shared_pending, &t->blocked)) set_tsk_thread_flag(t, TIF_SIGPENDING); else clear_tsk_thread_flag(t, TIF_SIGPENDING);} void recalc_sigpending(void){ recalc_sigpending_tsk(current);} /* Given the mask, find the first available signal that should be serviced. */ static intnext_signal(struct sigpending *pending, sigset_t *mask){ unsigned long i, *s, *m, x; int sig = 0; s = pending->signal.sig; m = mask->sig; switch (_NSIG_WORDS) { default: for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m) if ((x = *s &~ *m) != 0) { sig = ffz(~x) + i*_NSIG_BPW + 1; break; } break; case 2: if ((x = s[0] &~ m[0]) != 0) sig = 1; else if ((x = s[1] &~ m[1]) != 0) sig = _NSIG_BPW + 1; else break; sig += ffz(~x); break; case 1: if ((x = *s &~ *m) != 0) sig = ffz(~x) + 1; break; } return sig;} static struct sigqueue *__sigqueue_alloc(struct task_struct *t, unsigned int __nocast flags, int override_rlimit){ struct sigqueue *q = NULL; atomic_inc(&t->user->sigpending); if (override_rlimit || atomic_read(&t->user->sigpending) <= t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) q = kmem_cache_alloc(sigqueue_cachep, flags); if (unlikely(q == NULL)) { atomic_dec(&t->user->sigpending); } else { INIT_LIST_HEAD(&q->list); q->flags = 0; q->lock = NULL; q->user = get_uid(t->user); } return(q);} static inline void __sigqueue_free(struct sigqueue *q){ if (q->flags & SIGQUEUE_PREALLOC) return; atomic_dec(&q->user->sigpending); free_uid(q->user); kmem_cache_free(sigqueue_cachep, q);} static void flush_sigqueue(struct sigpending *queue){ struct sigqueue *q; sigemptyset(&queue->signal); while (!list_empty(&queue->list)) { q = list_entry(queue->list.next, struct sigqueue , list); list_del_init(&q->list); __sigqueue_free(q); }} /* * Flush all pending signals for a task. */ voidflush_signals(struct task_struct *t){ unsigned long flags; spin_lock_irqsave(&t->sighand->siglock, flags); clear_tsk_thread_flag(t,TIF_SIGPENDING); flush_sigqueue(&t->pending); flush_sigqueue(&t->signal->shared_pending); spin_unlock_irqrestore(&t->sighand->siglock, flags);} /* * This function expects the tasklist_lock write-locked. */void __exit_sighand(struct task_struct *tsk){ struct sighand_struct * sighand = tsk->sighand; /* Ok, we're done with the signal handlers */ tsk->sighand = NULL; if (atomic_dec_and_test(&sighand->count)) kmem_cache_free(sighand_cachep, sighand);} void exit_sighand(struct task_struct *tsk){ write_lock_irq(&tasklist_lock); __exit_sighand(tsk); write_unlock_irq(&tasklist_lock);} /* * This function expects the tasklist_lock write-locked. */void __exit_signal(struct task_struct *tsk){ struct signal_struct * sig = tsk->signal; struct sighand_struct * sighand = tsk->sighand; if (!sig) BUG(); if (!atomic_read(&sig->count)) BUG(); spin_lock(&sighand->siglock); posix_cpu_timers_exit(tsk); if (atomic_dec_and_test(&sig->count)) { posix_cpu_timers_exit_group(tsk); if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); tsk->signal = NULL; spin_unlock(&sighand->siglock); flush_sigqueue(&sig->shared_pending); } else { /* * If there is any task waiting for the group exit * then notify it: */ if (sig->group_exit_task && atomic_read(&sig->count) == sig->notify_count) { wake_up_process(sig->group_exit_task); sig->group_exit_task = NULL; } if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); tsk->signal = NULL; /* * Accumulate here the counters for all threads but the * group leader as they die, so they can be added into * the process-wide totals when those are taken. * The group leader stays around as a zombie as long * as there are other threads. When it gets reaped, * the exit.c code will add its counts into these totals. * We won't ever get here for the group leader, since it * will have been the last reference on the signal_struct. */ sig->utime = cputime_add(sig->utime, tsk->utime); sig->stime = cputime_add(sig->stime, tsk->stime); sig->min_flt += tsk->min_flt; sig->maj_flt += tsk->maj_flt; sig->nvcsw += tsk->nvcsw; sig->nivcsw += tsk->nivcsw; sig->sched_time += tsk->sched_time; spin_unlock(&sighand->siglock); sig = NULL; /* Marker for below. */ } clear_tsk_thread_flag(tsk,TIF_SIGPENDING); flush_sigqueue(&tsk->pending); if (sig) { /* * We are cleaning up the signal_struct here. We delayed * calling exit_itimers until after flush_sigqueue, just in * case our thread-local pending queue contained a queued * timer signal that would have been cleared in * exit_itimers. When that called sigqueue_free, it would * attempt to re-take the tasklist_lock and deadlock. This * can never happen if we ensure that all queues the * timer's signal might be queued on have been flushed * first. The shared_pending queue, and our own pending * queue are the only queues the timer could be on, since * there are no other threads left in the group and timer * signals are constrained to threads inside the group. */ exit_itimers(sig); exit_thread_group_keys(sig); kmem_cache_free(signal_cachep, sig); }} void exit_signal(struct task_struct *tsk){ write_lock_irq(&tasklist_lock); __exit_signal(tsk); write_unlock_irq(&tasklist_lock);} /* * Flush all handlers for a task. */ voidflush_signal_handlers(struct task_struct *t, int force_default){ int i; struct k_sigaction *ka = &t->sighand->action[0]; for (i = _NSIG ; i != 0 ; i--) { if (force_default || ka->sa.sa_handler != SIG_IGN) ka->sa.sa_handler = SIG_DFL; ka->sa.sa_flags = 0; sigemptyset(&ka->sa.sa_mask); ka++; }} /* Notify the system that a driver wants to block all signals for this * process, and wants to be notified if any signals at all were to be * sent/acted upon. If the notifier routine returns non-zero, then the * signal will be acted upon after all. If the notifier routine returns 0, * then then signal will be blocked. Only one block per process is * allowed. priv is a pointer to private data that the notifier routine * can use to determine if the signal should be blocked or not. */ voidblock_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask){ unsigned long flags; spin_lock_irqsave(¤t->sighand->siglock, flags); current->notifier_mask = mask; current->notifier_data = priv; current->notifier = notifier; spin_unlock_irqrestore(¤t->sighand->siglock, flags);} /* Notify the system that blocking has ended. */ voidunblock_all_signals(void){ unsigned long flags; spin_lock_irqsave(¤t->sighand->siglock, flags); current->notifier = NULL; current->notifier_data = NULL; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags);} static inline int collect_signal(int sig, struct sigpending *list, siginfo_t *info){ struct sigqueue *q, *first = NULL; int still_pending = 0; if (unlikely(!sigismember(&list->signal, sig))) return 0; /* * Collect the siginfo appropriate to this signal. Check if * there is another siginfo for the same signal. */ list_for_each_entry(q, &list->list, list) { if (q->info.si_signo == sig) { if (first) { still_pending = 1; break; } first = q; } } if (first) {Lines 1–500 of 2673. Load more
/* * linux/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds * * 1997-11-02 Modified for POSIX.1b signals by Richard Henderson * * 2003-06-02 Jim Houston - Concurrent Computer Corp. * Changes to use preallocated sigqueue structures * to allow signals to be sent reliably. */ #include <linux/config.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/smp_lock.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/fs.h> #include <linux/tty.h> #include <linux/binfmts.h> #include <linux/security.h> #include <linux/syscalls.h> #include <linux/ptrace.h> #include <linux/posix-timers.h> #include <linux/signal.h> #include <linux/audit.h> #include <asm/param.h> #include <asm/uaccess.h> #include <asm/unistd.h> #include <asm/siginfo.h> /* * SLAB caches for signal bits. */ static kmem_cache_t *sigqueue_cachep; /* * In POSIX a signal is sent either to a specific thread (Linux task) * or to the process as a whole (Linux thread group). How the signal * is sent determines whether it's to one thread or the whole group, * which determines which signal mask(s) are involved in blocking it * from being delivered until later. When the signal is delivered, * either it's caught or ignored by a user handler or it has a default * effect that applies to the whole thread group (POSIX process). * * The possible effects an unblocked signal set to SIG_DFL can have are: * ignore - Nothing Happens * terminate - kill the process, i.e. all threads in the group, * similar to exit_group. The group leader (only) reports * WIFSIGNALED status to its parent. * coredump - write a core dump file describing all threads using * the same mm and then kill all those threads * stop - stop all the threads in the group, i.e. TASK_STOPPED state * * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. * Other signals when not blocked and set to SIG_DFL behaves as follows. * The job control signals also have other special effects. * * +--------------------+------------------+ * | POSIX signal | default action | * +--------------------+------------------+ * | SIGHUP | terminate | * | SIGINT | terminate | * | SIGQUIT | coredump | * | SIGILL | coredump | * | SIGTRAP | coredump | * | SIGABRT/SIGIOT | coredump | * | SIGBUS | coredump | * | SIGFPE | coredump | * | SIGKILL | terminate(+) | * | SIGUSR1 | terminate | * | SIGSEGV | coredump | * | SIGUSR2 | terminate | * | SIGPIPE | terminate | * | SIGALRM | terminate | * | SIGTERM | terminate | * | SIGCHLD | ignore | * | SIGCONT | ignore(*) | * | SIGSTOP | stop(*)(+) | * | SIGTSTP | stop(*) | * | SIGTTIN | stop(*) | * | SIGTTOU | stop(*) | * | SIGURG | ignore | * | SIGXCPU | coredump | * | SIGXFSZ | coredump | * | SIGVTALRM | terminate | * | SIGPROF | terminate | * | SIGPOLL/SIGIO | terminate | * | SIGSYS/SIGUNUSED | coredump | * | SIGSTKFLT | terminate | * | SIGWINCH | ignore | * | SIGPWR | terminate | * | SIGRTMIN-SIGRTMAX | terminate | * +--------------------+------------------+ * | non-POSIX signal | default action | * +--------------------+------------------+ * | SIGEMT | coredump | * +--------------------+------------------+ * * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". * (*) Special job control effects: * When SIGCONT is sent, it resumes the process (all threads in the group) * from TASK_STOPPED state and also clears any pending/queued stop signals * (any of those marked with "stop(*)"). This happens regardless of blocking, * catching, or ignoring SIGCONT. When any stop signal is sent, it clears * any pending/queued SIGCONT signals; this happens regardless of blocking, * catching, or ignored the stop signal, though (except for SIGSTOP) the * default action of stopping the process may happen later or never. */ #ifdef SIGEMT #define M_SIGEMT M(SIGEMT) #else #define M_SIGEMT 0 #endif #if SIGRTMIN > BITS_PER_LONG #define M(sig) (1ULL << ((sig)-1)) #else #define M(sig) (1UL << ((sig)-1)) #endif #define T(sig, mask) (M(sig) & (mask)) #define SIG_KERNEL_ONLY_MASK (\ M(SIGKILL) | M(SIGSTOP) ) #define SIG_KERNEL_STOP_MASK (\ M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) ) #define SIG_KERNEL_COREDUMP_MASK (\ M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \ M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \ M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT ) #define SIG_KERNEL_IGNORE_MASK (\ M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) ) #define sig_kernel_only(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK)) #define sig_kernel_coredump(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK)) #define sig_kernel_ignore(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK)) #define sig_kernel_stop(sig) \ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK)) #define sig_user_defined(t, signr) \ (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) && \ ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN)) #define sig_fatal(t, signr) \ (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL) static int sig_ignored(struct task_struct *t, int sig) { void __user * handler; /* * Tracers always want to know about signals.. */ if (t->ptrace & PT_PTRACED) return 0; /* * Blocked signals are never ignored, since the * signal handler may change by the time it is * unblocked. */ if (sigismember(&t->blocked, sig)) return 0; /* Is it explicitly or implicitly ignored? */ handler = t->sighand->action[sig-1].sa.sa_handler; return handler == SIG_IGN || (handler == SIG_DFL && sig_kernel_ignore(sig)); } /* * Re-calculate pending state from the set of locally pending * signals, globally pending signals, and blocked signals. */ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) { unsigned long ready; long i; switch (_NSIG_WORDS) { default: for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;) ready |= signal->sig[i] &~ blocked->sig[i]; break; case 4: ready = signal->sig[3] &~ blocked->sig[3]; ready |= signal->sig[2] &~ blocked->sig[2]; ready |= signal->sig[1] &~ blocked->sig[1]; ready |= signal->sig[0] &~ blocked->sig[0]; break; case 2: ready = signal->sig[1] &~ blocked->sig[1]; ready |= signal->sig[0] &~ blocked->sig[0]; break; case 1: ready = signal->sig[0] &~ blocked->sig[0]; } return ready != 0; } #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) fastcall void recalc_sigpending_tsk(struct task_struct *t) { if (t->signal->group_stop_count > 0 || (freezing(t)) || PENDING(&t->pending, &t->blocked) || PENDING(&t->signal->shared_pending, &t->blocked)) set_tsk_thread_flag(t, TIF_SIGPENDING); else clear_tsk_thread_flag(t, TIF_SIGPENDING); } void recalc_sigpending(void) { recalc_sigpending_tsk(current); } /* Given the mask, find the first available signal that should be serviced. */ static int next_signal(struct sigpending *pending, sigset_t *mask) { unsigned long i, *s, *m, x; int sig = 0; s = pending->signal.sig; m = mask->sig; switch (_NSIG_WORDS) { default: for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m) if ((x = *s &~ *m) != 0) { sig = ffz(~x) + i*_NSIG_BPW + 1; break; } break; case 2: if ((x = s[0] &~ m[0]) != 0) sig = 1; else if ((x = s[1] &~ m[1]) != 0) sig = _NSIG_BPW + 1; else break; sig += ffz(~x); break; case 1: if ((x = *s &~ *m) != 0) sig = ffz(~x) + 1; break; } return sig; } static struct sigqueue *__sigqueue_alloc(struct task_struct *t, unsigned int __nocast flags, int override_rlimit) { struct sigqueue *q = NULL; atomic_inc(&t->user->sigpending); if (override_rlimit || atomic_read(&t->user->sigpending) <= t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) q = kmem_cache_alloc(sigqueue_cachep, flags); if (unlikely(q == NULL)) { atomic_dec(&t->user->sigpending); } else { INIT_LIST_HEAD(&q->list); q->flags = 0; q->lock = NULL; q->user = get_uid(t->user); } return(q); } static inline void __sigqueue_free(struct sigqueue *q) { if (q->flags & SIGQUEUE_PREALLOC) return; atomic_dec(&q->user->sigpending); free_uid(q->user); kmem_cache_free(sigqueue_cachep, q); } static void flush_sigqueue(struct sigpending *queue) { struct sigqueue *q; sigemptyset(&queue->signal); while (!list_empty(&queue->list)) { q = list_entry(queue->list.next, struct sigqueue , list); list_del_init(&q->list); __sigqueue_free(q); } } /* * Flush all pending signals for a task. */ void flush_signals(struct task_struct *t) { unsigned long flags; spin_lock_irqsave(&t->sighand->siglock, flags); clear_tsk_thread_flag(t,TIF_SIGPENDING); flush_sigqueue(&t->pending); flush_sigqueue(&t->signal->shared_pending); spin_unlock_irqrestore(&t->sighand->siglock, flags); } /* * This function expects the tasklist_lock write-locked. */ void __exit_sighand(struct task_struct *tsk) { struct sighand_struct * sighand = tsk->sighand; /* Ok, we're done with the signal handlers */ tsk->sighand = NULL; if (atomic_dec_and_test(&sighand->count)) kmem_cache_free(sighand_cachep, sighand); } void exit_sighand(struct task_struct *tsk) { write_lock_irq(&tasklist_lock); __exit_sighand(tsk); write_unlock_irq(&tasklist_lock); } /* * This function expects the tasklist_lock write-locked. */ void __exit_signal(struct task_struct *tsk) { struct signal_struct * sig = tsk->signal; struct sighand_struct * sighand = tsk->sighand; if (!sig) BUG(); if (!atomic_read(&sig->count)) BUG(); spin_lock(&sighand->siglock); posix_cpu_timers_exit(tsk); if (atomic_dec_and_test(&sig->count)) { posix_cpu_timers_exit_group(tsk); if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); tsk->signal = NULL; spin_unlock(&sighand->siglock); flush_sigqueue(&sig->shared_pending); } else { /* * If there is any task waiting for the group exit * then notify it: */ if (sig->group_exit_task && atomic_read(&sig->count) == sig->notify_count) { wake_up_process(sig->group_exit_task); sig->group_exit_task = NULL; } if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); tsk->signal = NULL; /* * Accumulate here the counters for all threads but the * group leader as they die, so they can be added into * the process-wide totals when those are taken. * The group leader stays around as a zombie as long * as there are other threads. When it gets reaped, * the exit.c code will add its counts into these totals. * We won't ever get here for the group leader, since it * will have been the last reference on the signal_struct. */ sig->utime = cputime_add(sig->utime, tsk->utime); sig->stime = cputime_add(sig->stime, tsk->stime); sig->min_flt += tsk->min_flt; sig->maj_flt += tsk->maj_flt; sig->nvcsw += tsk->nvcsw; sig->nivcsw += tsk->nivcsw; sig->sched_time += tsk->sched_time; spin_unlock(&sighand->siglock); sig = NULL; /* Marker for below. */ } clear_tsk_thread_flag(tsk,TIF_SIGPENDING); flush_sigqueue(&tsk->pending); if (sig) { /* * We are cleaning up the signal_struct here. We delayed * calling exit_itimers until after flush_sigqueue, just in * case our thread-local pending queue contained a queued * timer signal that would have been cleared in * exit_itimers. When that called sigqueue_free, it would * attempt to re-take the tasklist_lock and deadlock. This * can never happen if we ensure that all queues the * timer's signal might be queued on have been flushed * first. The shared_pending queue, and our own pending * queue are the only queues the timer could be on, since * there are no other threads left in the group and timer * signals are constrained to threads inside the group. */ exit_itimers(sig); exit_thread_group_keys(sig); kmem_cache_free(signal_cachep, sig); } } void exit_signal(struct task_struct *tsk) { write_lock_irq(&tasklist_lock); __exit_signal(tsk); write_unlock_irq(&tasklist_lock); } /* * Flush all handlers for a task. */ void flush_signal_handlers(struct task_struct *t, int force_default) { int i; struct k_sigaction *ka = &t->sighand->action[0]; for (i = _NSIG ; i != 0 ; i--) { if (force_default || ka->sa.sa_handler != SIG_IGN) ka->sa.sa_handler = SIG_DFL; ka->sa.sa_flags = 0; sigemptyset(&ka->sa.sa_mask); ka++; } } /* Notify the system that a driver wants to block all signals for this * process, and wants to be notified if any signals at all were to be * sent/acted upon. If the notifier routine returns non-zero, then the * signal will be acted upon after all. If the notifier routine returns 0, * then then signal will be blocked. Only one block per process is * allowed. priv is a pointer to private data that the notifier routine * can use to determine if the signal should be blocked or not. */ void block_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask) { unsigned long flags; spin_lock_irqsave(¤t->sighand->siglock, flags); current->notifier_mask = mask; current->notifier_data = priv; current->notifier = notifier; spin_unlock_irqrestore(¤t->sighand->siglock, flags); } /* Notify the system that blocking has ended. */ void unblock_all_signals(void) { unsigned long flags; spin_lock_irqsave(¤t->sighand->siglock, flags); current->notifier = NULL; current->notifier_data = NULL; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); } static inline int collect_signal(int sig, struct sigpending *list, siginfo_t *info) { struct sigqueue *q, *first = NULL; int still_pending = 0; if (unlikely(!sigismember(&list->signal, sig))) return 0; /* * Collect the siginfo appropriate to this signal. Check if * there is another siginfo for the same signal. */ list_for_each_entry(q, &list->list, list) { if (q->info.si_signo == sig) { if (first) { still_pending = 1; break; } first = q; } } if (first) {