📄 kernel-signal.c
字号:
return retval;
}
inline int
kill_proc_info(int sig, struct siginfo *info, pid_t pid)
{
int error;
struct task_struct *p;
read_lock(&tasklist_lock);
p = find_task_by_pid(pid);
error = -ESRCH;
if (p)
error = send_sig_info(sig, info, p);
read_unlock(&tasklist_lock);
return error;
}
/*
* kill_something_info() interprets pid in interesting ways just like kill(2).
*
* POSIX specifies that kill(-1,sig) is unspecified, but what we have
* is probably wrong. Should make it like BSD or SYSV.
*/
static int kill_something_info(int sig, struct siginfo *info, int pid)
{
if (!pid) {
return kill_pg_info(sig, info, current->pgrp);
} else if (pid == -1) {
int retval = 0, count = 0;
struct task_struct * p;
read_lock(&tasklist_lock);
for_each_task(p) {
if (p->pid > 1 && p != current) {
int err = send_sig_info(sig, info, p);
++count;
if (err != -EPERM)
retval = err;
}
}
read_unlock(&tasklist_lock);
return count ? retval : -ESRCH;
} else if (pid < 0) {
return kill_pg_info(sig, info, -pid);
} else {
return kill_proc_info(sig, info, pid);
}
}
/*
* These are for backward compatibility with the rest of the kernel source.
*/
int
send_sig(int sig, struct task_struct *p, int priv)
{
return send_sig_info(sig, (void*)(long)(priv != 0), p);
}
void
force_sig(int sig, struct task_struct *p)
{
force_sig_info(sig, (void*)1L, p);
}
int
kill_pg(pid_t pgrp, int sig, int priv)
{
return kill_pg_info(sig, (void *)(long)(priv != 0), pgrp);
}
int
kill_sl(pid_t sess, int sig, int priv)
{
return kill_sl_info(sig, (void *)(long)(priv != 0), sess);
}
int
kill_proc(pid_t pid, int sig, int priv)
{
return kill_proc_info(sig, (void *)(long)(priv != 0), pid);
}
/*
* Joy. Or not. Pthread wants us to wake up every thread
* in our parent group.
*/
static void wake_up_parent(struct task_struct *parent)
{
struct task_struct *tsk = parent;
do {
wake_up_interruptible(&tsk->wait_chldexit);
tsk = next_thread(tsk);
} while (tsk != parent);
}
/*
* Let a parent know about a status change of a child.
*/
void do_notify_parent(struct task_struct *tsk, int sig)
{
struct siginfo info;
int why, status;
info.si_signo = sig;
info.si_errno = 0;
info.si_pid = tsk->pid;
info.si_uid = tsk->uid;
/* FIXME: find out whether or not this is supposed to be c*time. */
info.si_utime = tsk->times.tms_utime;
info.si_stime = tsk->times.tms_stime;
status = tsk->exit_code & 0x7f;
why = SI_KERNEL; /* shouldn't happen */
switch (tsk->state) {
case TASK_STOPPED:
/* FIXME -- can we deduce CLD_TRAPPED or CLD_CONTINUED? */
if (tsk->ptrace & PT_PTRACED)
why = CLD_TRAPPED;
else
why = CLD_STOPPED;
break;
default:
if (tsk->exit_code & 0x80)
why = CLD_DUMPED;
else if (tsk->exit_code & 0x7f)
why = CLD_KILLED;
else {
why = CLD_EXITED;
status = tsk->exit_code >> 8;
}
break;
}
info.si_code = why;
info.si_status = status;
send_sig_info(sig, &info, tsk->p_pptr);
wake_up_parent(tsk->p_pptr);
}
/*
* We need the tasklist lock because it's the only
* thing that protects out "parent" pointer.
*
* exit.c calls "do_notify_parent()" directly, because
* it already has the tasklist lock.
*/
void
notify_parent(struct task_struct *tsk, int sig)
{
read_lock(&tasklist_lock);
do_notify_parent(tsk, sig);
read_unlock(&tasklist_lock);
}
EXPORT_SYMBOL(dequeue_signal);
EXPORT_SYMBOL(flush_signals);
EXPORT_SYMBOL(force_sig);
EXPORT_SYMBOL(force_sig_info);
EXPORT_SYMBOL(kill_pg);
EXPORT_SYMBOL(kill_pg_info);
EXPORT_SYMBOL(kill_proc);
EXPORT_SYMBOL(kill_proc_info);
EXPORT_SYMBOL(kill_sl);
EXPORT_SYMBOL(kill_sl_info);
EXPORT_SYMBOL(notify_parent);
EXPORT_SYMBOL(recalc_sigpending);
EXPORT_SYMBOL(send_sig);
EXPORT_SYMBOL(send_sig_info);
EXPORT_SYMBOL(block_all_signals);
EXPORT_SYMBOL(unblock_all_signals);
/*
* System call entry points.
*/
/*
* We don't need to get the kernel lock - this is all local to this
* particular thread.. (and that's good, because this is _heavily_
* used by various programs)
*/
asmlinkage long
sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize)
{
int error = -EINVAL;
sigset_t old_set, new_set;
/* XXX: Don't preclude handling different sized sigset_t's. */
if (sigsetsize != sizeof(sigset_t))
goto out;
if (set) {
error = -EFAULT;
if (copy_from_user(&new_set, set, sizeof(*set)))
goto out;
sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
spin_lock_irq(¤t->sigmask_lock);
old_set = current->blocked;
error = 0;
switch (how) {
default:
error = -EINVAL;
break;
case SIG_BLOCK:
sigorsets(&new_set, &old_set, &new_set);
break;
case SIG_UNBLOCK:
signandsets(&new_set, &old_set, &new_set);
break;
case SIG_SETMASK:
break;
}
current->blocked = new_set;
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
if (error)
goto out;
if (oset)
goto set_old;
} else if (oset) {
spin_lock_irq(¤t->sigmask_lock);
old_set = current->blocked;
spin_unlock_irq(¤t->sigmask_lock);
set_old:
error = -EFAULT;
if (copy_to_user(oset, &old_set, sizeof(*oset)))
goto out;
}
error = 0;
out:
return error;
}
long do_sigpending(void *set, unsigned long sigsetsize)
{
long error = -EINVAL;
sigset_t pending;
if (sigsetsize > sizeof(sigset_t))
goto out;
spin_lock_irq(¤t->sigmask_lock);
sigandsets(&pending, ¤t->blocked, ¤t->pending.signal);
spin_unlock_irq(¤t->sigmask_lock);
error = -EFAULT;
if (!copy_to_user(set, &pending, sigsetsize))
error = 0;
out:
return error;
}
asmlinkage long
sys_rt_sigpending(sigset_t *set, size_t sigsetsize)
{
return do_sigpending(set, sigsetsize);
}
asmlinkage long
sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo,
const struct timespec *uts, size_t sigsetsize)
{
int ret, sig;
sigset_t these;
struct timespec ts;
siginfo_t info;
long timeout = 0;
/* XXX: Don't preclude handling different sized sigset_t's. */
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
if (copy_from_user(&these, uthese, sizeof(these)))
return -EFAULT;
/*
* Invert the set of allowed signals to get those we
* want to block.
*/
sigdelsetmask(&these, sigmask(SIGKILL)|sigmask(SIGSTOP));
signotset(&these);
if (uts) {
if (copy_from_user(&ts, uts, sizeof(ts)))
return -EFAULT;
if (ts.tv_nsec >= 1000000000L || ts.tv_nsec < 0
|| ts.tv_sec < 0)
return -EINVAL;
}
spin_lock_irq(¤t->sigmask_lock);
sig = dequeue_signal(&these, &info);
if (!sig) {
timeout = MAX_SCHEDULE_TIMEOUT;
if (uts)
timeout = (timespec_to_jiffies(&ts)
+ (ts.tv_sec || ts.tv_nsec));
if (timeout) {
/* None ready -- temporarily unblock those we're
* interested while we are sleeping in so that we'll
* be awakened when they arrive. */
sigset_t oldblocked = current->blocked;
sigandsets(¤t->blocked, ¤t->blocked, &these);
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
current->state = TASK_INTERRUPTIBLE;
timeout = schedule_timeout(timeout);
spin_lock_irq(¤t->sigmask_lock);
sig = dequeue_signal(&these, &info);
current->blocked = oldblocked;
recalc_sigpending(current);
}
}
spin_unlock_irq(¤t->sigmask_lock);
if (sig) {
ret = sig;
if (uinfo) {
if (copy_siginfo_to_user(uinfo, &info))
ret = -EFAULT;
}
} else {
ret = -EAGAIN;
if (timeout)
ret = -EINTR;
}
return ret;
}
asmlinkage long
sys_kill(int pid, int sig)
{
struct siginfo info;
info.si_signo = sig;
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = current->pid;
info.si_uid = current->uid;
return kill_something_info(sig, &info, pid);
}
asmlinkage long
sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo)
{
siginfo_t info;
if (copy_from_user(&info, uinfo, sizeof(siginfo_t)))
return -EFAULT;
/* Not even root can pretend to send signals from the kernel.
Nor can they impersonate a kill(), which adds source info. */
if (info.si_code >= 0)
return -EPERM;
info.si_signo = sig;
/* POSIX.1b doesn't mention process groups. */
return kill_proc_info(sig, &info, pid);
}
int
do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
{
struct k_sigaction *k;
if (sig < 1 || sig > _NSIG ||
(act && (sig == SIGKILL || sig == SIGSTOP)))
return -EINVAL;
k = ¤t->sig->action[sig-1];
spin_lock(¤t->sig->siglock);
if (oact)
*oact = *k;
if (act) {
*k = *act;
sigdelsetmask(&k->sa.sa_mask, sigmask(SIGKILL) | sigmask(SIGSTOP));
/*
* POSIX 3.3.1.3:
* "Setting a signal action to SIG_IGN for a signal that is
* pending shall cause the pending signal to be discarded,
* whether or not it is blocked."
*
* "Setting a signal action to SIG_DFL for a signal that is
* pending and whose default action is to ignore the signal
* (for example, SIGCHLD), shall cause the pending signal to
* be discarded, whether or not it is blocked"
*
* Note the silly behaviour of SIGCHLD: SIG_IGN means that the
* signal isn't actually ignored, but does automatic child
* reaping, while SIG_DFL is explicitly said by POSIX to force
* the signal to be ignored.
*/
if (k->sa.sa_handler == SIG_IGN
|| (k->sa.sa_handler == SIG_DFL
&& (sig == SIGCONT ||
sig == SIGCHLD ||
sig == SIGWINCH))) {
spin_lock_irq(¤t->sigmask_lock);
if (rm_sig_from_queue(sig, current))
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
}
}
spin_unlock(¤t->sig->siglock);
return 0;
}
int
do_sigaltstack (const stack_t *uss, stack_t *uoss, unsigned long sp)
{
stack_t oss;
int error;
if (uoss) {
oss.ss_sp = (void *) current->sas_ss_sp;
oss.ss_size = current->sas_ss_size;
oss.ss_flags = sas_ss_flags(sp);
}
if (uss) {
void *ss_sp;
size_t ss_size;
int ss_flags;
error = -EFAULT;
if (verify_area(VERIFY_READ, uss, sizeof(*uss))
|| __get_user(ss_sp, &uss->ss_sp)
|| __get_user(ss_flags, &uss->ss_flags)
|| __get_user(ss_size, &uss->ss_size))
goto out;
error = -EPERM;
if (on_sig_stack (sp))
goto out;
error = -EINVAL;
/*
*
* Note - this code used to test ss_flags incorrectly
* old code may have been written using ss_flags==0
* to mean ss_flags==SS_ONSTACK (as this was the only
* way that worked) - this fix preserves that older
* mechanism
*/
if (ss_flags != SS_DISABLE && ss_flags != SS_ONSTACK && ss_flags != 0)
goto out;
if (ss_flags == SS_DISABLE) {
ss_size = 0;
ss_sp = NULL;
} else {
error = -ENOMEM;
if (ss_size < MINSIGSTKSZ)
goto out;
}
current->sas_ss_sp = (unsigned long) ss_sp;
current->sas_ss_size = ss_size;
}
if (uoss) {
error = -EFAULT;
if (copy_to_user(uoss, &oss, sizeof(oss)))
goto out;
}
error = 0;
out:
return error;
}
asmlinkage long
sys_sigpending(old_sigset_t *set)
{
return do_sigpending(set, sizeof(*set));
}
#if !defined(__alpha__)
/* Alpha has its own versions with special arguments. */
asmlinkage long
sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset)
{
int error;
old_sigset_t old_set, new_set;
if (set) {
error = -EFAULT;
if (copy_from_user(&new_set, set, sizeof(*set)))
goto out;
new_set &= ~(sigmask(SIGKILL)|sigmask(SIGSTOP));
spin_lock_irq(¤t->sigmask_lock);
old_set = current->blocked.sig[0];
error = 0;
switch (how) {
default:
error = -EINVAL;
break;
case SIG_BLOCK:
sigaddsetmask(¤t->blocked, new_set);
break;
case SIG_UNBLOCK:
sigdelsetmask(¤t->blocked, new_set);
break;
case SIG_SETMASK:
current->blocked.sig[0] = new_set;
break;
}
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
if (error)
goto out;
if (oset)
goto set_old;
} else if (oset) {
old_set = current->blocked.sig[0];
set_old:
error = -EFAULT;
if (copy_to_user(oset, &old_set, sizeof(*oset)))
goto out;
}
error = 0;
out:
return error;
}
#ifndef __sparc__
asmlinkage long
sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact,
size_t sigsetsize)
{
struct k_sigaction new_sa, old_sa;
int ret = -EINVAL;
/* XXX: Don't preclude handling different sized sigset_t's. */
if (sigsetsize != sizeof(sigset_t))
goto out;
if (act) {
if (copy_from_user(&new_sa.sa, act, sizeof(new_sa.sa)))
return -EFAULT;
}
ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL);
if (!ret && oact) {
if (copy_to_user(oact, &old_sa.sa, sizeof(old_sa.sa)))
return -EFAULT;
}
out:
return ret;
}
#endif /* __sparc__ */
#endif
#if !defined(__alpha__) && !defined(__ia64__)
/*
* For backwards compatibility. Functionality superseded by sigprocmask.
*/
asmlinkage long
sys_sgetmask(void)
{
/* SMP safe */
return current->blocked.sig[0];
}
asmlinkage long
sys_ssetmask(int newmask)
{
int old;
spin_lock_irq(¤t->sigmask_lock);
old = current->blocked.sig[0];
siginitset(¤t->blocked, newmask & ~(sigmask(SIGKILL)|
sigmask(SIGSTOP)));
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
return old;
}
#endif /* !defined(__alpha__) */
#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__)
/*
* For backwards compatibility. Functionality superseded by sigaction.
*/
asmlinkage unsigned long
sys_signal(int sig, __sighandler_t handler)
{
struct k_sigaction new_sa, old_sa;
int ret;
new_sa.sa.sa_handler = handler;
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
ret = do_sigaction(sig, &new_sa, &old_sa);
return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}
#endif /* !alpha && !__ia64__ && !defined(__mips__) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -