Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | /* * 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 <linux/smp.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) #ifndef __alpha__ /* * This call isn't used by all ports, in particular, the Alpha * uses osf_sigprocmask instead. Maybe it should be moved into * arch-dependent dir? * * 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 int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset) { sigset_t old_set = current->blocked; if (set) { sigset_t new_set; int error = get_user(new_set, set); if (error) return error; new_set &= _BLOCKABLE; switch (how) { default: return -EINVAL; case SIG_BLOCK: new_set |= old_set; break; case SIG_UNBLOCK: new_set = old_set & ~new_set; break; case SIG_SETMASK: break; } current->blocked = new_set; } if (oset) { int error = put_user(old_set, oset); if (error) return error; } return 0; } /* * For backwards compatibility? Functionality superseded by sigprocmask. */ asmlinkage int sys_sgetmask(void) { int ret; /* SMP safe */ ret = current->blocked; return ret; } asmlinkage int sys_ssetmask(int newmask) { int old; lock_kernel(); old = current->blocked; current->blocked = newmask & _BLOCKABLE; unlock_kernel(); return old; } #endif asmlinkage int sys_sigpending(sigset_t *set) { int ret; /* fill in "set" with signals pending but blocked. */ lock_kernel(); ret = put_user(current->blocked & current->signal, set); unlock_kernel(); return ret; } /* * 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.. */ inline void check_pending(int signum) { struct sigaction *p; p = signum - 1 + current->sig->action; if (p->sa_handler == SIG_IGN) { current->signal &= ~_S(signum); return; } if (p->sa_handler == SIG_DFL) { if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH) return; current->signal &= ~_S(signum); return; } } #ifndef __alpha__ /* * For backwards compatibility? Functionality superseded by sigaction. */ asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler) { unsigned long err; struct sigaction tmp; lock_kernel(); err = -EINVAL; if (signum<1 || signum>32) goto out; if (signum==SIGKILL || signum==SIGSTOP) goto out; if (handler != SIG_DFL && handler != SIG_IGN) { err = verify_area(VERIFY_READ, handler, 1); if (err) goto out; } memset(&tmp, 0, sizeof(tmp)); tmp.sa_handler = handler; tmp.sa_flags = SA_ONESHOT | SA_NOMASK; handler = current->sig->action[signum-1].sa_handler; current->sig->action[signum-1] = tmp; check_pending(signum); err = (unsigned long) handler; out: unlock_kernel(); return err; } #endif #ifndef __sparc__ 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; p = signum - 1 + current->sig->action; if (action) { if (copy_from_user(&new_sa, action, sizeof(struct sigaction))) return -EFAULT; if (signum==SIGKILL || signum==SIGSTOP) return -EINVAL; } if (oldaction) { if (copy_to_user(oldaction, p, sizeof(struct sigaction))) return -EFAULT; } if (action) { *p = new_sa; check_pending(signum); } return 0; } #endif |