You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
4.1 KiB
198 lines
4.1 KiB
/*
|
|
* linux/kernel/exit.c
|
|
*
|
|
* (C) 1991 Linus Torvalds
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/tty.h>
|
|
#include <asm/segment.h>
|
|
|
|
int sys_pause(void);
|
|
int sys_close(int fd);
|
|
|
|
void release(struct task_struct * p)
|
|
{
|
|
int i;
|
|
|
|
if (!p)
|
|
return;
|
|
for (i=1 ; i<NR_TASKS ; i++)
|
|
if (task[i]==p) {
|
|
task[i]=NULL;
|
|
free_page((long)p);
|
|
schedule();
|
|
return;
|
|
}
|
|
panic("trying to release non-existent task");
|
|
}
|
|
|
|
static inline int send_sig(long sig,struct task_struct * p,int priv)
|
|
{
|
|
if (!p || sig<1 || sig>32)
|
|
return -EINVAL;
|
|
if (priv || (current->euid==p->euid) || suser())
|
|
p->signal |= (1<<(sig-1));
|
|
else
|
|
return -EPERM;
|
|
return 0;
|
|
}
|
|
|
|
static void kill_session(void)
|
|
{
|
|
struct task_struct **p = NR_TASKS + task;
|
|
|
|
while (--p > &FIRST_TASK) {
|
|
if (*p && (*p)->session == current->session)
|
|
(*p)->signal |= 1<<(SIGHUP-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* XXX need to check permissions needed to send signals to process
|
|
* groups, etc. etc. kill() permissions semantics are tricky!
|
|
*/
|
|
int sys_kill(int pid,int sig)
|
|
{
|
|
struct task_struct **p = NR_TASKS + task;
|
|
int err, retval = 0;
|
|
|
|
if (!pid) while (--p > &FIRST_TASK) {
|
|
if (*p && (*p)->pgrp == current->pid)
|
|
if (err=send_sig(sig,*p,1))
|
|
retval = err;
|
|
} else if (pid>0) while (--p > &FIRST_TASK) {
|
|
if (*p && (*p)->pid == pid)
|
|
if (err=send_sig(sig,*p,0))
|
|
retval = err;
|
|
} else if (pid == -1) while (--p > &FIRST_TASK)
|
|
if (err = send_sig(sig,*p,0))
|
|
retval = err;
|
|
else while (--p > &FIRST_TASK)
|
|
if (*p && (*p)->pgrp == -pid)
|
|
if (err = send_sig(sig,*p,0))
|
|
retval = err;
|
|
return retval;
|
|
}
|
|
|
|
static void tell_father(int pid)
|
|
{
|
|
int i;
|
|
|
|
if (pid)
|
|
for (i=0;i<NR_TASKS;i++) {
|
|
if (!task[i])
|
|
continue;
|
|
if (task[i]->pid != pid)
|
|
continue;
|
|
task[i]->signal |= (1<<(SIGCHLD-1));
|
|
return;
|
|
}
|
|
/* if we don't find any fathers, we just release ourselves */
|
|
/* This is not really OK. Must change it to make father 1 */
|
|
printk("BAD BAD - no father found\n\r");
|
|
release(current);
|
|
}
|
|
|
|
int do_exit(long code)
|
|
{
|
|
int i;
|
|
|
|
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
|
|
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
|
|
for (i=0 ; i<NR_TASKS ; i++)
|
|
if (task[i] && task[i]->father == current->pid) {
|
|
task[i]->father = 1;
|
|
if (task[i]->state == TASK_ZOMBIE)
|
|
/* assumption task[1] is always init */
|
|
(void) send_sig(SIGCHLD, task[1], 1);
|
|
}
|
|
for (i=0 ; i<NR_OPEN ; i++)
|
|
if (current->filp[i])
|
|
sys_close(i);
|
|
iput(current->pwd);
|
|
current->pwd=NULL;
|
|
iput(current->root);
|
|
current->root=NULL;
|
|
iput(current->executable);
|
|
current->executable=NULL;
|
|
if (current->leader && current->tty >= 0)
|
|
tty_table[current->tty].pgrp = 0;
|
|
if (last_task_used_math == current)
|
|
last_task_used_math = NULL;
|
|
if (current->leader)
|
|
kill_session();
|
|
current->state = TASK_ZOMBIE;
|
|
current->exit_code = code;
|
|
tell_father(current->father);
|
|
schedule();
|
|
return (-1); /* just to suppress warnings */
|
|
}
|
|
|
|
int sys_exit(int error_code)
|
|
{
|
|
return do_exit((error_code&0xff)<<8);
|
|
}
|
|
|
|
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
|
|
{
|
|
int flag, code;
|
|
struct task_struct ** p;
|
|
|
|
verify_area(stat_addr,4);
|
|
repeat:
|
|
flag=0;
|
|
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
|
|
if (!*p || *p == current)
|
|
continue;
|
|
if ((*p)->father != current->pid)
|
|
continue;
|
|
if (pid>0) {
|
|
if ((*p)->pid != pid)
|
|
continue;
|
|
} else if (!pid) {
|
|
if ((*p)->pgrp != current->pgrp)
|
|
continue;
|
|
} else if (pid != -1) {
|
|
if ((*p)->pgrp != -pid)
|
|
continue;
|
|
}
|
|
switch ((*p)->state) {
|
|
case TASK_STOPPED:
|
|
if (!(options & WUNTRACED))
|
|
continue;
|
|
put_fs_long(0x7f,stat_addr);
|
|
return (*p)->pid;
|
|
case TASK_ZOMBIE:
|
|
current->cutime += (*p)->utime;
|
|
current->cstime += (*p)->stime;
|
|
flag = (*p)->pid;
|
|
code = (*p)->exit_code;
|
|
release(*p);
|
|
put_fs_long(code,stat_addr);
|
|
return flag;
|
|
default:
|
|
flag=1;
|
|
continue;
|
|
}
|
|
}
|
|
if (flag) {
|
|
if (options & WNOHANG)
|
|
return 0;
|
|
current->state=TASK_INTERRUPTIBLE;
|
|
schedule();
|
|
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
|
|
goto repeat;
|
|
else
|
|
return -EINTR;
|
|
}
|
|
return -ECHILD;
|
|
}
|
|
|
|
|