home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/kernel/signal.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/signal.h>
- #include <linux/errno.h>
- #include <linux/wait.h>
- #include <linux/ptrace.h>
- #include <linux/unistd.h>
- #include <linux/mm.h>
-
- #include <asm/segment.h>
-
- #define _S(nr) (1<<((nr)-1))
-
- #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
-
- asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)
- {
- sigset_t new_set, old_set = current->blocked;
- int error;
-
- if (set) {
- error = verify_area(VERIFY_READ, set, sizeof(sigset_t));
- if (error)
- return error;
- new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE;
- switch (how) {
- case SIG_BLOCK:
- current->blocked |= new_set;
- break;
- case SIG_UNBLOCK:
- current->blocked &= ~new_set;
- break;
- case SIG_SETMASK:
- current->blocked = new_set;
- break;
- default:
- return -EINVAL;
- }
- }
- if (oset) {
- error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t));
- if (error)
- return error;
- put_fs_long(old_set, (unsigned long *) oset);
- }
- return 0;
- }
-
- asmlinkage int sys_sgetmask(void)
- {
- return current->blocked;
- }
-
- asmlinkage int sys_ssetmask(int newmask)
- {
- int old=current->blocked;
-
- current->blocked = newmask & _BLOCKABLE;
- return old;
- }
-
- asmlinkage int sys_sigpending(sigset_t *set)
- {
- int error;
- /* fill in "set" with signals pending but blocked. */
- error = verify_area(VERIFY_WRITE, set, 4);
- if (!error)
- put_fs_long(current->blocked & current->signal, (unsigned long *)set);
- return error;
- }
-
- /*
- * 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" (but SIGCHLD is unspecified: linux leaves it alone).
- *
- * "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..
- */
- static void check_pending(int signum)
- {
- struct sigaction *p;
-
- p = signum - 1 + current->sigaction;
- if (p->sa_handler == SIG_IGN) {
- if (signum == SIGCHLD)
- return;
- current->signal &= ~_S(signum);
- return;
- }
- if (p->sa_handler == SIG_DFL) {
- if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH)
- return;
- current->signal &= ~_S(signum);
- return;
- }
- }
-
- asmlinkage unsigned long sys_signal(int signum, void (*handler)(int))
- {
- int err;
- struct sigaction tmp;
-
- if (signum<1 || signum>32)
- return -EINVAL;
- if (signum==SIGKILL || signum==SIGSTOP)
- return -EINVAL;
- if (handler != SIG_DFL && handler != SIG_IGN) {
- err = verify_area(VERIFY_READ, handler, 1);
- if (err)
- return err;
- }
- tmp.sa_handler = handler;
- tmp.sa_mask = 0;
- tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
- tmp.sa_restorer = NULL;
- handler = current->sigaction[signum-1].sa_handler;
- current->sigaction[signum-1] = tmp;
- check_pending(signum);
- return (unsigned long) handler;
- }
-
- asmlinkage int sys_sigaction(int signum, const struct sigaction * action,
- struct sigaction * oldaction)
- {
- struct sigaction new_sa, *p;
-
- if (signum<1 || signum>32)
- return -EINVAL;
- if (signum==SIGKILL || signum==SIGSTOP)
- return -EINVAL;
- p = signum - 1 + current->sigaction;
- if (action) {
- int err = verify_area(VERIFY_READ, action, sizeof(*action));
- if (err)
- return err;
- memcpy_fromfs(&new_sa, action, sizeof(struct sigaction));
- if (new_sa.sa_flags & SA_NOMASK)
- new_sa.sa_mask = 0;
- else {
- new_sa.sa_mask |= _S(signum);
- new_sa.sa_mask &= _BLOCKABLE;
- }
- if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) {
- err = verify_area(VERIFY_READ, new_sa.sa_handler, 1);
- if (err)
- return err;
- }
- }
- if (oldaction) {
- int err = verify_area(VERIFY_WRITE, oldaction, sizeof(*oldaction));
- if (err)
- return err;
- memcpy_tofs(oldaction, p, sizeof(struct sigaction));
- }
- if (action) {
- *p = new_sa;
- check_pending(signum);
- }
- return 0;
- }
-