root/kernel/signal.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sys_sgetmask
  2. sys_ssetmask
  3. sys_sigpending
  4. sys_sigsuspend
  5. check_pending
  6. sys_signal
  7. sys_sigaction
  8. do_signal

   1 /*
   2  *  linux/kernel/signal.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 #include <linux/sched.h>
   8 #include <linux/kernel.h>
   9 #include <linux/signal.h>
  10 #include <linux/errno.h>
  11 #include <linux/wait.h>
  12 #include <linux/ptrace.h>
  13 
  14 #include <asm/segment.h>
  15 
  16 #define _S(nr) (1<<((nr)-1))
  17 
  18 #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
  19 
  20 extern int core_dump(long signr,struct pt_regs * regs);
  21 
  22 int sys_sgetmask(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  23 {
  24         return current->blocked;
  25 }
  26 
  27 int sys_ssetmask(int newmask)
     /* [previous][next][first][last][top][bottom][index][help] */
  28 {
  29         int old=current->blocked;
  30 
  31         current->blocked = newmask & _BLOCKABLE;
  32         return old;
  33 }
  34 
  35 int sys_sigpending(sigset_t *set)
     /* [previous][next][first][last][top][bottom][index][help] */
  36 {
  37         int error;
  38         /* fill in "set" with signals pending but blocked. */
  39         error = verify_area(VERIFY_WRITE, set, 4);
  40         if (!error)
  41                 put_fs_long(current->blocked & current->signal, (unsigned long *)set);
  42         return error;
  43 }
  44 
  45 /* atomically swap in the new signal mask, and wait for a signal.
  46  *
  47  * we need to play some games with syscall restarting.  We get help
  48  * from the syscall library interface.  Note that we need to coordinate
  49  * the calling convention with the libc routine.
  50  *
  51  * "set" is just the sigmask as described in 1003.1-1988, 3.3.7.
  52  *      It is assumed that sigset_t can be passed as a 32 bit quantity.
  53  *
  54  * "restart" holds a restart indication.  If it's 1, then we 
  55  *      install the old mask, and return normally.  If it's zero, we store 
  56  *      the current mask in old_mask and block until a signal comes in.
  57  *      If it's 2, then it's a signal we must handle but not return from.
  58  *
  59  * We are careful to prevent a rouge restart from user space from fooling
  60  * us into blocking SIGKILL or SIGSTOP.
  61  */
  62 int sys_sigsuspend(volatile int restart, volatile unsigned long old_mask, unsigned long set)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64         extern int sys_pause(void);
  65 
  66         switch (restart) {
  67         case 0:
  68                 /* we're not restarting.  do the work */
  69                 restart = 1;
  70                 old_mask = current->blocked;
  71                 current->blocked = set & _BLOCKABLE;
  72                 break;
  73         case 1:
  74                 /* we're restarting to restore and exit */
  75                 current->blocked = old_mask & _BLOCKABLE;
  76                 return -EINTR;
  77         case 2:
  78                 /* we're restarting but staying paused */
  79                 restart = 1;
  80                 break;
  81         }
  82         /* pause returns after a signal arrives */
  83         if (sys_pause() == -ERESTARTSYS)
  84                 restart = 2;
  85         return -ERESTARTNOINTR;         /* handle the signal, and come back */
  86 }
  87 
  88 /*
  89  * POSIX 3.3.1.3:
  90  *  "Setting a signal action to SIG_IGN for a signal that is pending
  91  *   shall cause the pending signal to be discarded, whether or not
  92  *   it is blocked" (but SIGCHLD is unspecified: linux leaves it alone).
  93  *
  94  *  "Setting a signal action to SIG_DFL for a signal that is pending
  95  *   and whose default action is to ignore the signal (for example,
  96  *   SIGCHLD), shall cause the pending signal to be discarded, whether
  97  *   or not it is blocked"
  98  *
  99  * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal
 100  * isn't actually ignored, but does automatic child reaping, while
 101  * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
 102  */
 103 static void check_pending(int signum)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105         struct sigaction *p;
 106 
 107         p = signum - 1 + current->sigaction;
 108         if (p->sa_handler == SIG_IGN) {
 109                 if (signum == SIGCHLD)
 110                         return;
 111                 current->signal &= ~_S(signum);
 112                 return;
 113         }
 114         if (p->sa_handler == SIG_DFL) {
 115                 if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH)
 116                         return;
 117                 current->signal &= ~_S(signum);
 118                 return;
 119         }       
 120 }
 121 
 122 int sys_signal(int signum, long handler, long restorer)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124         struct sigaction tmp;
 125 
 126         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 127                 return -EINVAL;
 128         tmp.sa_handler = (void (*)(int)) handler;
 129         tmp.sa_mask = 0;
 130         tmp.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT;
 131         tmp.sa_restorer = (void (*)(void)) restorer;
 132         handler = (long) current->sigaction[signum-1].sa_handler;
 133         current->sigaction[signum-1] = tmp;
 134         check_pending(signum);
 135         return handler;
 136 }
 137 
 138 int sys_sigaction(int signum, const struct sigaction * action,
     /* [previous][next][first][last][top][bottom][index][help] */
 139         struct sigaction * oldaction)
 140 {
 141         struct sigaction new, *p;
 142 
 143         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 144                 return -EINVAL;
 145         p = signum - 1 + current->sigaction;
 146         if (action) {
 147                 memcpy_fromfs(&new, action, sizeof(struct sigaction));
 148                 if (new.sa_flags & SA_NOMASK)
 149                         new.sa_mask = 0;
 150                 else {
 151                         new.sa_mask |= _S(signum);
 152                         new.sa_mask &= _BLOCKABLE;
 153                 }
 154         }
 155         if (oldaction) {
 156                 if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
 157                         memcpy_tofs(oldaction, p, sizeof(struct sigaction));
 158         }
 159         if (action) {
 160                 *p = new;
 161                 check_pending(signum);
 162         }
 163         return 0;
 164 }
 165 
 166 extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
 167 
 168 /*
 169  * Note that 'init' is a special process: it doesn't get signals it doesn't
 170  * want to handle. Thus you cannot kill init even with a SIGKILL even by
 171  * mistake.
 172  */
 173 void do_signal(struct pt_regs * regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 174 {
 175         unsigned long signr;
 176         unsigned long sa_handler;
 177         long old_eip = regs->eip;
 178         struct sigaction * sa;
 179         int longs;
 180         unsigned long * tmp_esp;
 181 
 182         if (regs->orig_eax >= 0 && regs->eax == -ERESTARTNOINTR) {
 183                 regs->eax = regs->orig_eax;
 184                 regs->eip = old_eip -= 2;
 185         }
 186         signr = current->signal & ~current->blocked;
 187         do {
 188                 __asm__("bsf %2,%1\n\t"
 189                         "btrl %1,%0"
 190                         :"=m" (current->signal),"=r" (signr)
 191                         :"1" (signr));
 192                 sa = current->sigaction + signr;
 193                 signr++;
 194                 sa_handler = (unsigned long) sa->sa_handler;
 195                 if (sa_handler==1) {
 196 /* check for SIGCHLD: it's special */
 197                         if (signr == SIGCHLD)
 198                                 while (sys_waitpid(-1,NULL,WNOHANG) > 0)
 199                                         /* nothing */;
 200                         continue;
 201                 }
 202                 if (!sa_handler) {
 203                         if (current->pid == 1)
 204                                 continue;
 205                         switch (signr) {
 206                         case SIGCONT:
 207                         case SIGCHLD:
 208                         case SIGWINCH:
 209                                 continue;
 210 
 211                         case SIGSTOP:
 212                         case SIGTSTP:
 213                         case SIGTTIN:
 214                         case SIGTTOU:
 215                                 current->state = TASK_STOPPED;
 216                                 current->exit_code = signr;
 217                                 if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & 
 218                                                 SA_NOCLDSTOP))
 219                                         send_sig(SIGCHLD, current->p_pptr, 1);
 220                                 schedule();
 221                                 continue;
 222 
 223                         case SIGQUIT:
 224                         case SIGILL:
 225                         case SIGTRAP:
 226                         case SIGIOT:
 227                         case SIGFPE:
 228                         case SIGSEGV:
 229                                 if (core_dump(signr,regs))
 230                                         signr |= 0x80;
 231                                 /* fall through */
 232                         default:
 233                                 current->signal |= _S(signr & 0x7f);
 234                                 do_exit(signr);
 235                         }
 236                 }
 237                 /*
 238                  * OK, we're invoking a handler 
 239                  */
 240                 if (regs->orig_eax >= 0 && regs->eax == -ERESTARTSYS) {
 241                         if (sa->sa_flags & SA_INTERRUPT)
 242                                 regs->eax = -EINTR;
 243                         else {
 244                                 regs->eax = regs->orig_eax;
 245                                 regs->eip = old_eip -= 2;
 246                         }
 247                 }
 248                 if (sa->sa_flags & SA_ONESHOT)
 249                         sa->sa_handler = NULL;
 250                 regs->eip = sa_handler;
 251                 longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4);
 252                 regs->esp -= longs;
 253                 tmp_esp = (unsigned long *) regs->esp;
 254                 verify_area(VERIFY_WRITE,tmp_esp,longs);
 255                 put_fs_long((long) sa->sa_restorer,tmp_esp++);
 256                 put_fs_long(signr,tmp_esp++);
 257                 if (!(sa->sa_flags & SA_NOMASK))
 258                         put_fs_long(current->blocked,tmp_esp++);
 259                 put_fs_long(regs->eax,tmp_esp++);
 260                 put_fs_long(regs->ecx,tmp_esp++);
 261                 put_fs_long(regs->edx,tmp_esp++);
 262                 put_fs_long(regs->eflags,tmp_esp++);
 263                 put_fs_long(old_eip,tmp_esp++);
 264                 current->blocked |= sa->sa_mask;
 265 /* force a supervisor-mode page-in of the signal handler to reduce races */
 266                 __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
 267                 return;
 268         } while ((signr = current->signal & ~current->blocked));
 269         if (regs->orig_eax >= 0 && regs->eax == -ERESTARTSYS) {
 270                 regs->eax = regs->orig_eax;
 271                 regs->eip = old_eip -= 2;
 272         }
 273 }

/* [previous][next][first][last][top][bottom][index][help] */