version 1.1.1.1, 2018/04/24 18:00:41
|
version 1.1.1.6, 2018/04/24 18:05:29
|
Line 1
|
Line 1
|
|
/* |
|
* linux/kernel/exit.c |
|
* |
|
* (C) 1991 Linus Torvalds |
|
*/ |
|
|
|
#define DEBUG_PROC_TREE |
|
|
#include <errno.h> |
#include <errno.h> |
#include <signal.h> |
#include <signal.h> |
#include <sys/wait.h> |
#include <sys/wait.h> |
Line 7
|
Line 15
|
#include <linux/tty.h> |
#include <linux/tty.h> |
#include <asm/segment.h> |
#include <asm/segment.h> |
|
|
int sys_pause(void); |
|
int sys_close(int fd); |
int sys_close(int fd); |
|
|
|
inline int send_sig(long sig,struct task_struct * p,int priv) |
|
{ |
|
if (!p || (sig < 0) || (sig > 32)) |
|
return -EINVAL; |
|
if (!priv && (current->euid!=p->euid) && !suser()) |
|
return -EPERM; |
|
if (!sig) |
|
return 0; |
|
if ((sig == SIGKILL) || (sig == SIGCONT)) { |
|
if (p->state == TASK_STOPPED) |
|
p->state = TASK_RUNNING; |
|
p->exit_code = 0; |
|
p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) | |
|
(1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) ); |
|
} |
|
/* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */ |
|
if ((sig >= SIGSTOP) && (sig <= SIGTTOU)) |
|
p->signal &= ~(1<<(SIGCONT-1)); |
|
/* Actually deliver the signal */ |
|
p->signal |= (1<<(sig-1)); |
|
if (p->flags & PF_PTRACED) { |
|
/* save the signal number for wait. */ |
|
p->exit_code = sig; |
|
|
|
/* we have to make sure the parent is awake. */ |
|
if (p->p_pptr != NULL && p->p_pptr->state == TASK_INTERRUPTIBLE) |
|
p->p_pptr->state = TASK_RUNNING; |
|
|
|
/* we have to make sure that the process stops. */ |
|
if (p->state == TASK_INTERRUPTIBLE || p->state == TASK_RUNNING) |
|
p->state = TASK_STOPPED; |
|
|
|
if (p == current) |
|
schedule(); |
|
} |
|
return 0; |
|
} |
|
|
void release(struct task_struct * p) |
void release(struct task_struct * p) |
{ |
{ |
int i; |
int i; |
|
|
if (!p) |
if (!p) |
return; |
return; |
|
if (p == current) { |
|
printk("task releasing itself\n\r"); |
|
return; |
|
} |
for (i=1 ; i<NR_TASKS ; i++) |
for (i=1 ; i<NR_TASKS ; i++) |
if (task[i]==p) { |
if (task[i]==p) { |
task[i]=NULL; |
task[i]=NULL; |
|
/* Update links */ |
|
if (p->p_osptr) |
|
p->p_osptr->p_ysptr = p->p_ysptr; |
|
if (p->p_ysptr) |
|
p->p_ysptr->p_osptr = p->p_osptr; |
|
else |
|
p->p_pptr->p_cptr = p->p_osptr; |
free_page((long)p); |
free_page((long)p); |
schedule(); |
schedule(); |
return; |
return; |
Line 26 void release(struct task_struct * p)
|
Line 82 void release(struct task_struct * p)
|
panic("trying to release non-existent task"); |
panic("trying to release non-existent task"); |
} |
} |
|
|
static inline void send_sig(long sig,struct task_struct * p,int priv) |
#ifdef DEBUG_PROC_TREE |
|
/* |
|
* Check to see if a task_struct pointer is present in the task[] array |
|
* Return 0 if found, and 1 if not found. |
|
*/ |
|
int bad_task_ptr(struct task_struct *p) |
{ |
{ |
if (!p || sig<1 || sig>32) |
int i; |
return; |
|
if (priv || |
if (!p) |
current->uid==p->uid || |
return 0; |
current->euid==p->uid || |
for (i=0 ; i<NR_TASKS ; i++) |
current->uid==p->euid || |
if (task[i] == p) |
current->euid==p->euid) |
return 0; |
p->signal |= (1<<(sig-1)); |
return 1; |
} |
} |
|
|
|
/* |
|
* This routine scans the pid tree and make sure the rep invarient still |
|
* holds. Used for debugging only, since it's very slow.... |
|
* |
|
* It looks a lot scarier than it really is.... we're doing ænothing more |
|
* than verifying the doubly-linked list foundæin p_ysptr and p_osptr, |
|
* and checking it corresponds with the process tree defined by p_cptr and |
|
* p_pptr; |
|
*/ |
|
void audit_ptree() |
|
{ |
|
int i; |
|
|
|
for (i=1 ; i<NR_TASKS ; i++) { |
|
if (!task[i]) |
|
continue; |
|
if (bad_task_ptr(task[i]->p_pptr)) |
|
printk("Warning, pid %d's parent link is bad\n", |
|
task[i]->pid); |
|
if (bad_task_ptr(task[i]->p_cptr)) |
|
printk("Warning, pid %d's child link is bad\n", |
|
task[i]->pid); |
|
if (bad_task_ptr(task[i]->p_ysptr)) |
|
printk("Warning, pid %d's ys link is bad\n", |
|
task[i]->pid); |
|
if (bad_task_ptr(task[i]->p_osptr)) |
|
printk("Warning, pid %d's os link is bad\n", |
|
task[i]->pid); |
|
if (task[i]->p_pptr == task[i]) |
|
printk("Warning, pid %d parent link points to self\n"); |
|
if (task[i]->p_cptr == task[i]) |
|
printk("Warning, pid %d child link points to self\n"); |
|
if (task[i]->p_ysptr == task[i]) |
|
printk("Warning, pid %d ys link points to self\n"); |
|
if (task[i]->p_osptr == task[i]) |
|
printk("Warning, pid %d os link points to self\n"); |
|
if (task[i]->p_osptr) { |
|
if (task[i]->p_pptr != task[i]->p_osptr->p_pptr) |
|
printk( |
|
"Warning, pid %d older sibling %d parent is %d\n", |
|
task[i]->pid, task[i]->p_osptr->pid, |
|
task[i]->p_osptr->p_pptr->pid); |
|
if (task[i]->p_osptr->p_ysptr != task[i]) |
|
printk( |
|
"Warning, pid %d older sibling %d has mismatched ys link\n", |
|
task[i]->pid, task[i]->p_osptr->pid); |
|
} |
|
if (task[i]->p_ysptr) { |
|
if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr) |
|
printk( |
|
"Warning, pid %d younger sibling %d parent is %d\n", |
|
task[i]->pid, task[i]->p_osptr->pid, |
|
task[i]->p_osptr->p_pptr->pid); |
|
if (task[i]->p_ysptr->p_osptr != task[i]) |
|
printk( |
|
"Warning, pid %d younger sibling %d has mismatched os link\n", |
|
task[i]->pid, task[i]->p_ysptr->pid); |
|
} |
|
if (task[i]->p_cptr) { |
|
if (task[i]->p_cptr->p_pptr != task[i]) |
|
printk( |
|
"Warning, pid %d youngest child %d has mismatched parent link\n", |
|
task[i]->pid, task[i]->p_cptr->pid); |
|
if (task[i]->p_cptr->p_ysptr) |
|
printk( |
|
"Warning, pid %d youngest child %d has non-NULL ys link\n", |
|
task[i]->pid, task[i]->p_cptr->pid); |
|
} |
|
} |
|
} |
|
#endif /* DEBUG_PROC_TREE */ |
|
|
void do_kill(long pid,long sig,int priv) |
int session_of_pgrp(int pgrp) |
{ |
{ |
struct task_struct **p = NR_TASKS + task; |
struct task_struct **p; |
|
|
|
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) |
|
if ((*p)->pgrp == pgrp) |
|
return((*p)->session); |
|
return -1; |
|
} |
|
|
|
int kill_pg(int pgrp, int sig, int priv) |
|
{ |
|
struct task_struct **p; |
|
int err,retval = -ESRCH; |
|
int found = 0; |
|
|
|
if (sig<0 || sig>32 || pgrp<=0) |
|
return -EINVAL; |
|
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) |
|
if ((*p)->pgrp == pgrp) { |
|
if (sig && (err = send_sig(sig,*p,priv))) |
|
retval = err; |
|
else |
|
found++; |
|
} |
|
return(found ? 0 : retval); |
|
} |
|
|
|
int kill_proc(int pid, int sig, int priv) |
|
{ |
|
struct task_struct **p; |
|
|
if (!pid) while (--p > &FIRST_TASK) { |
if (sig<0 || sig>32) |
if (*p && (*p)->pgrp == current->pid) |
return -EINVAL; |
send_sig(sig,*p,priv); |
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) |
} else if (pid>0) while (--p > &FIRST_TASK) { |
if ((*p)->pid == pid) |
if (*p && (*p)->pid == pid) |
return(sig ? send_sig(sig,*p,priv) : 0); |
send_sig(sig,*p,priv); |
return(-ESRCH); |
} else if (pid == -1) while (--p > &FIRST_TASK) |
|
send_sig(sig,*p,priv); |
|
else while (--p > &FIRST_TASK) |
|
if (*p && (*p)->pgrp == -pid) |
|
send_sig(sig,*p,priv); |
|
} |
} |
|
|
|
/* |
|
* POSIX specifies that kill(-1,sig) is unspecified, but what we have |
|
* is probably wrong. Should make it like BSD or SYSV. |
|
*/ |
int sys_kill(int pid,int sig) |
int sys_kill(int pid,int sig) |
{ |
{ |
do_kill(pid,sig,!(current->uid || current->euid)); |
struct task_struct **p = NR_TASKS + task; |
return 0; |
int err, retval = 0; |
|
|
|
if (!pid) |
|
return(kill_pg(current->pid,sig,0)); |
|
if (pid == -1) { |
|
while (--p > &FIRST_TASK) |
|
if (err = send_sig(sig,*p,0)) |
|
retval = err; |
|
return(retval); |
|
} |
|
if (pid < 0) |
|
return(kill_pg(-pid,sig,0)); |
|
/* Normal kill */ |
|
return(kill_proc(pid,sig,0)); |
|
} |
|
|
|
/* |
|
* Determine if a process group is "orphaned", according to the POSIX |
|
* definition in 2.2.2.52. Orphaned process groups are not to be affected |
|
* by terminal-generated stop signals. Newly orphaned process groups are |
|
* to receive a SIGHUP and a SIGCONT. |
|
* |
|
* "I ask you, have you ever known what it is to be an orphan?" |
|
*/ |
|
int is_orphaned_pgrp(int pgrp) |
|
{ |
|
struct task_struct **p; |
|
|
|
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { |
|
if (!(*p) || |
|
((*p)->pgrp != pgrp) || |
|
((*p)->state == TASK_ZOMBIE) || |
|
((*p)->p_pptr->pid == 1)) |
|
continue; |
|
if (((*p)->p_pptr->pgrp != pgrp) && |
|
((*p)->p_pptr->session == (*p)->session)) |
|
return 0; |
|
} |
|
return(1); /* (sighing) "Often!" */ |
|
} |
|
|
|
static int has_stopped_jobs(int pgrp) |
|
{ |
|
struct task_struct ** p; |
|
|
|
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { |
|
if ((*p)->pgrp != pgrp) |
|
continue; |
|
if ((*p)->state == TASK_STOPPED) |
|
return(1); |
|
} |
|
return(0); |
} |
} |
|
|
int do_exit(long code) |
volatile void do_exit(long code) |
{ |
{ |
|
struct task_struct *p; |
int i; |
int i; |
|
|
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); |
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); |
free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); |
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 = 0; |
|
for (i=0 ; i<NR_OPEN ; i++) |
for (i=0 ; i<NR_OPEN ; i++) |
if (current->filp[i]) |
if (current->filp[i]) |
sys_close(i); |
sys_close(i); |
iput(current->pwd); |
iput(current->pwd); |
current->pwd=NULL; |
current->pwd = NULL; |
iput(current->root); |
iput(current->root); |
current->root=NULL; |
current->root = NULL; |
if (current->leader && current->tty >= 0) |
iput(current->executable); |
tty_table[current->tty].pgrp = 0; |
current->executable = NULL; |
|
iput(current->library); |
|
current->library = NULL; |
|
current->state = TASK_ZOMBIE; |
|
current->exit_code = code; |
|
/* |
|
* Check to see if any process groups have become orphaned |
|
* as a result of our exiting, and if they have any stopped |
|
* jobs, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2) |
|
* |
|
* Case i: Our father is in a different pgrp than we are |
|
* and we were the only connection outside, so our pgrp |
|
* is about to become orphaned. |
|
*/ |
|
if ((current->p_pptr->pgrp != current->pgrp) && |
|
(current->p_pptr->session == current->session) && |
|
is_orphaned_pgrp(current->pgrp) && |
|
has_stopped_jobs(current->pgrp)) { |
|
kill_pg(current->pgrp,SIGHUP,1); |
|
kill_pg(current->pgrp,SIGCONT,1); |
|
} |
|
/* Let father know we died */ |
|
send_sig (SIGCHLD, current->p_pptr, 1); |
|
|
|
/* |
|
* This loop does two things: |
|
* |
|
* A. Make init inherit all the child processes |
|
* B. Check to see if any process groups have become orphaned |
|
* as a result of our exiting, and if they have any stopped |
|
* jons, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2) |
|
*/ |
|
if (p = current->p_cptr) { |
|
while (1) { |
|
p->flags &= ~PF_PTRACED; |
|
p->p_pptr = task[1]; |
|
if (p->state == TASK_ZOMBIE) |
|
task[1]->signal |= (1<<(SIGCHLD-1)); |
|
/* |
|
* process group orphan check |
|
* Case ii: Our child is in a different pgrp |
|
* than we are, and it was the only connection |
|
* outside, so the child pgrp is now orphaned. |
|
*/ |
|
if ((p->pgrp != current->pgrp) && |
|
(p->session == current->session) && |
|
is_orphaned_pgrp(p->pgrp) && |
|
has_stopped_jobs(p->pgrp)) { |
|
kill_pg(p->pgrp,SIGHUP,1); |
|
kill_pg(p->pgrp,SIGCONT,1); |
|
} |
|
if (p->p_osptr) { |
|
p = p->p_osptr; |
|
continue; |
|
} |
|
/* |
|
* This is it; link everything into init's children |
|
* and leave |
|
*/ |
|
p->p_osptr = task[1]->p_cptr; |
|
task[1]->p_cptr->p_ysptr = p; |
|
task[1]->p_cptr = current->p_cptr; |
|
current->p_cptr = 0; |
|
break; |
|
} |
|
} |
|
if (current->leader) { |
|
struct task_struct **p; |
|
struct tty_struct *tty; |
|
|
|
if (current->tty >= 0) { |
|
tty = TTY_TABLE(current->tty); |
|
if (tty->pgrp > 0) |
|
kill_pg(tty->pgrp, SIGHUP, 1); |
|
tty->pgrp = -1; |
|
tty->session = 0; |
|
} |
|
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) |
|
if ((*p)->session == current->session) |
|
(*p)->tty = -1; |
|
} |
if (last_task_used_math == current) |
if (last_task_used_math == current) |
last_task_used_math = NULL; |
last_task_used_math = NULL; |
if (current->father) { |
#ifdef DEBUG_PROC_TREE |
current->state = TASK_ZOMBIE; |
audit_ptree(); |
do_kill(current->father,SIGCHLD,1); |
#endif |
current->exit_code = code; |
|
} else |
|
release(current); |
|
schedule(); |
schedule(); |
return (-1); /* just to suppress warnings */ |
|
} |
} |
|
|
int sys_exit(int error_code) |
int sys_exit(int error_code) |
{ |
{ |
return do_exit((error_code&0xff)<<8); |
do_exit((error_code&0xff)<<8); |
} |
} |
|
|
int sys_waitpid(pid_t pid,int * stat_addr, int options) |
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) |
{ |
{ |
int flag=0; |
int flag; |
struct task_struct ** p; |
struct task_struct *p; |
|
unsigned long oldblocked; |
|
|
verify_area(stat_addr,4); |
if (stat_addr) |
|
verify_area(stat_addr,4); |
repeat: |
repeat: |
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) |
flag=0; |
if (*p && *p != current && |
for (p = current->p_cptr ; p ; p = p->p_osptr) { |
(pid==-1 || (*p)->pid==pid || |
if (pid>0) { |
(pid==0 && (*p)->pgrp==current->pgrp) || |
if (p->pid != pid) |
(pid<0 && (*p)->pgrp==-pid))) |
continue; |
if ((*p)->father == current->pid) { |
} 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) || |
|
!p->exit_code) |
|
continue; |
|
if (stat_addr) |
|
put_fs_long((p->exit_code << 8) | 0x7f, |
|
stat_addr); |
|
p->exit_code = 0; |
|
return p->pid; |
|
case TASK_ZOMBIE: |
|
current->cutime += p->utime; |
|
current->cstime += p->stime; |
|
flag = p->pid; |
|
if (stat_addr) |
|
put_fs_long(p->exit_code, stat_addr); |
|
release(p); |
|
#ifdef DEBUG_PROC_TREE |
|
audit_ptree(); |
|
#endif |
|
return flag; |
|
default: |
flag=1; |
flag=1; |
if ((*p)->state==TASK_ZOMBIE) { |
continue; |
put_fs_long((*p)->exit_code, |
} |
(unsigned long *) stat_addr); |
} |
current->cutime += (*p)->utime; |
|
current->cstime += (*p)->stime; |
|
flag = (*p)->pid; |
|
release(*p); |
|
return flag; |
|
} |
|
} |
|
if (flag) { |
if (flag) { |
if (options & WNOHANG) |
if (options & WNOHANG) |
return 0; |
return 0; |
sys_pause(); |
current->state=TASK_INTERRUPTIBLE; |
if (!(current->signal &= ~(1<<(SIGCHLD-1)))) |
oldblocked = current->blocked; |
goto repeat; |
current->blocked &= ~(1<<(SIGCHLD-1)); |
|
schedule(); |
|
current->blocked = oldblocked; |
|
if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1)))) |
|
return -ERESTARTSYS; |
else |
else |
return -EINTR; |
goto repeat; |
} |
} |
return -ECHILD; |
return -ECHILD; |
} |
} |