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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | /* * linux/kernel/signal.c * * (C) 1991 Linus Torvalds */ #include <linux/sched.h> #include <linux/kernel.h> #include <asm/segment.h> #include <signal.h> #include <sys/wait.h> #include <sys/ptrace.h> #include <errno.h> extern int core_dump(long signr,struct pt_regs * regs); int sys_sgetmask() { return current->blocked; } int sys_ssetmask(int newmask) { int old=current->blocked; current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1)); return old; } int sys_sigpending(sigset_t *set) { /* fill in "set" with signals pending but blocked. */ verify_area(set,4); put_fs_long(current->blocked & current->signal, (unsigned long *)set); return 0; } /* atomically swap in the new signal mask, and wait for a signal. * * we need to play some games with syscall restarting. We get help * from the syscall library interface. Note that we need to coordinate * the calling convention with the libc routine. * * "set" is just the sigmask as described in 1003.1-1988, 3.3.7. * It is assumed that sigset_t can be passed as a 32 bit quantity. * * "restart" holds a restart indication. If it's non-zero, then we * install the old mask, and return normally. If it's zero, we store * the current mask in old_mask and block until a signal comes in. */ int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set) { extern int sys_pause(void); if (restart) { /* we're restarting */ current->blocked = old_mask; return -EINTR; } /* we're not restarting. do the work */ *(&restart) = 1; *(&old_mask) = current->blocked; current->blocked = set; (void) sys_pause(); /* return after a signal arrives */ return -ERESTARTNOINTR; /* handle the signal, and come back */ } static inline void save_old(char * from,char * to) { int i; verify_area(to, sizeof(struct sigaction)); for (i=0 ; i< sizeof(struct sigaction) ; i++) { put_fs_byte(*from,to); from++; to++; } } static inline void get_new(char * from,char * to) { int i; for (i=0 ; i< sizeof(struct sigaction) ; i++) *(to++) = get_fs_byte(from++); } int sys_signal(int signum, long handler, long restorer) { struct sigaction tmp; if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP) return -EINVAL; tmp.sa_handler = (void (*)(int)) handler; tmp.sa_mask = 0; tmp.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT; tmp.sa_restorer = (void (*)(void)) restorer; handler = (long) current->sigaction[signum-1].sa_handler; current->sigaction[signum-1] = tmp; return handler; } int sys_sigaction(int signum, const struct sigaction * action, struct sigaction * oldaction) { struct sigaction tmp; if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP) return -EINVAL; tmp = current->sigaction[signum-1]; get_new((char *) action, (char *) (signum-1+current->sigaction)); if (oldaction) save_old((char *) &tmp,(char *) oldaction); if (current->sigaction[signum-1].sa_flags & SA_NOMASK) current->sigaction[signum-1].sa_mask = 0; else current->sigaction[signum-1].sa_mask |= (1<<(signum-1)); return 0; } extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); int do_signal(long signr,struct pt_regs * regs) { unsigned long sa_handler; long old_eip = regs->eip; struct sigaction * sa = current->sigaction + signr - 1; int longs; unsigned long * tmp_esp; #ifdef notdef printk("pid: %d, signr: %x, eax=%d, oeax = %d, int=%d\n", current->pid, signr, regs->eax, regs->orig_eax, sa->sa_flags & SA_INTERRUPT); #endif sa_handler = (unsigned long) sa->sa_handler; if ((regs->orig_eax != -1) && ((regs->eax == -ERESTARTSYS) || (regs->eax == -ERESTARTNOINTR))) { if ((sa_handler > 1) && (regs->eax == -ERESTARTSYS) && (sa->sa_flags & SA_INTERRUPT)) regs->eax = -EINTR; else { regs->eax = regs->orig_eax; regs->eip = old_eip -= 2; } } if (sa_handler==1) { /* check for SIGCHLD: it's special */ if (signr == SIGCHLD) while (sys_waitpid(-1,NULL,WNOHANG) > 0) /* nothing */; return(1); /* Ignore, see if there are more signals... */ } if (!sa_handler) { switch (signr) { case SIGCONT: case SIGCHLD: case SIGWINCH: return(1); /* Ignore, ... */ case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: current->state = TASK_STOPPED; current->exit_code = signr; if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) send_sig(SIGCHLD, current->p_pptr, 1); return(1); /* Reschedule another event */ case SIGQUIT: case SIGILL: case SIGTRAP: case SIGIOT: case SIGFPE: case SIGSEGV: if (core_dump(signr,regs)) do_exit(signr|0x80); /* fall through */ default: do_exit(signr); } } /* * OK, we're invoking a handler */ if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; regs->eip = sa_handler; longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4); regs->esp -= longs; tmp_esp = (unsigned long *) regs->esp; verify_area(tmp_esp,longs); put_fs_long((long) sa->sa_restorer,tmp_esp++); put_fs_long(signr,tmp_esp++); if (!(sa->sa_flags & SA_NOMASK)) put_fs_long(current->blocked,tmp_esp++); put_fs_long(regs->eax,tmp_esp++); put_fs_long(regs->ecx,tmp_esp++); put_fs_long(regs->edx,tmp_esp++); put_fs_long(regs->eflags,tmp_esp++); put_fs_long(old_eip,tmp_esp++); current->blocked |= sa->sa_mask; /* force a supervisor-mode page-in of the signal handler to reduce races */ __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler)); return(0); /* Continue, execute handler */ } |