Linux0.96c/kernel/signal.c
/*
* 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 */
}