root/kernel/exit.c~

/* [previous][next][first][last][top][bottom][index][help] */
/*
 *  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 void send_sig(long sig,struct task_struct * p,int priv)
{
        if (!p || sig<1 || sig>32)
                return;
        if (priv ||
                current->uid==p->uid ||
                current->euid==p->uid ||
                current->uid==p->euid ||
                current->euid==p->euid)
                p->signal |= (1<<(sig-1));
}

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);
        }
}

void do_kill(long pid,long sig,int priv)
{
        struct task_struct **p = NR_TASKS + task;

        if (!pid) while (--p > &FIRST_TASK) {
                if (*p && (*p)->pgrp == current->pid)
                        send_sig(sig,*p,priv);
        } else if (pid>0) while (--p > &FIRST_TASK) {
                if (*p && (*p)->pid == pid)
                        send_sig(sig,*p,priv);
        } 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);
}

int sys_kill(int pid,int sig)
{
        do_kill(pid,sig,!(current->uid || current->euid));
        return 0;
}

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 */
        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 = 0;
        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;
        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;
        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;
                                put_fs_long((*p)->exit_code,stat_addr);
                                release(*p);
                                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;
}



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