Annotation of linux/kernel/exit.c, revision 1.1.1.12

1.1.1.2   root        1: /*
                      2:  *  linux/kernel/exit.c
                      3:  *
1.1.1.11  root        4:  *  Copyright (C) 1991, 1992  Linus Torvalds
1.1.1.2   root        5:  */
                      6: 
1.1.1.4   root        7: #define DEBUG_PROC_TREE
                      8: 
1.1.1.11  root        9: #include <linux/wait.h>
                     10: #include <linux/errno.h>
                     11: #include <linux/signal.h>
1.1       root       12: #include <linux/sched.h>
                     13: #include <linux/kernel.h>
1.1.1.12! root       14: #include <linux/resource.h>
1.1.1.10  root       15: #include <linux/mm.h>
1.1       root       16: #include <linux/tty.h>
1.1.1.11  root       17: 
1.1       root       18: #include <asm/segment.h>
                     19: 
                     20: int sys_close(int fd);
1.1.1.12! root       21: void getrusage(struct task_struct *, int, struct rusage *);
1.1       root       22: 
1.1.1.8   root       23: int send_sig(long sig,struct task_struct * p,int priv)
1.1.1.5   root       24: {
                     25:        if (!p || (sig < 0) || (sig > 32))
                     26:                return -EINVAL;
1.1.1.8   root       27:        if (!priv && ((sig != SIGCONT) || (current->session != p->session)) &&
1.1.1.9   root       28:            (current->euid != p->euid) && (current->uid != p->uid) && !suser())
1.1.1.5   root       29:                return -EPERM;
                     30:        if (!sig)
                     31:                return 0;
                     32:        if ((sig == SIGKILL) || (sig == SIGCONT)) {
                     33:                if (p->state == TASK_STOPPED)
                     34:                        p->state = TASK_RUNNING;
                     35:                p->exit_code = 0;
                     36:                p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
                     37:                                (1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
                     38:        } 
                     39:        /* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
                     40:        if ((sig >= SIGSTOP) && (sig <= SIGTTOU)) 
                     41:                p->signal &= ~(1<<(SIGCONT-1));
                     42:        /* Actually deliver the signal */
                     43:        p->signal |= (1<<(sig-1));
                     44:        if (p->flags & PF_PTRACED) {
                     45:                /* save the signal number for wait. */
                     46:                p->exit_code = sig;
                     47: 
1.1.1.9   root       48:                /* we have to make sure the parent process is awake. */
1.1.1.5   root       49:                if (p->p_pptr != NULL && p->p_pptr->state == TASK_INTERRUPTIBLE)
                     50:                        p->p_pptr->state = TASK_RUNNING;
                     51: 
                     52:                /* we have to make sure that the process stops. */
                     53:                if (p->state == TASK_INTERRUPTIBLE || p->state == TASK_RUNNING)
                     54:                        p->state = TASK_STOPPED;
                     55:        }
                     56:        return 0;
                     57: }
                     58: 
1.1       root       59: void release(struct task_struct * p)
                     60: {
                     61:        int i;
                     62: 
                     63:        if (!p)
                     64:                return;
1.1.1.4   root       65:        if (p == current) {
                     66:                printk("task releasing itself\n\r");
                     67:                return;
                     68:        }
1.1       root       69:        for (i=1 ; i<NR_TASKS ; i++)
1.1.1.7   root       70:                if (task[i] == p) {
                     71:                        task[i] = NULL;
1.1.1.9   root       72:                        REMOVE_LINKS(p);
1.1.1.7   root       73:                        free_page((long) p);
1.1       root       74:                        return;
                     75:                }
                     76:        panic("trying to release non-existent task");
                     77: }
                     78: 
1.1.1.4   root       79: #ifdef DEBUG_PROC_TREE
                     80: /*
                     81:  * Check to see if a task_struct pointer is present in the task[] array
                     82:  * Return 0 if found, and 1 if not found.
                     83:  */
                     84: int bad_task_ptr(struct task_struct *p)
                     85: {
                     86:        int     i;
                     87: 
                     88:        if (!p)
                     89:                return 0;
                     90:        for (i=0 ; i<NR_TASKS ; i++)
                     91:                if (task[i] == p)
                     92:                        return 0;
                     93:        return 1;
                     94: }
                     95:        
                     96: /*
                     97:  * This routine scans the pid tree and make sure the rep invarient still
                     98:  * holds.  Used for debugging only, since it's very slow....
                     99:  *
                    100:  * It looks a lot scarier than it really is.... we're doing ænothing more
                    101:  * than verifying the doubly-linked list foundæin p_ysptr and p_osptr, 
                    102:  * and checking it corresponds with the process tree defined by p_cptr and 
                    103:  * p_pptr;
                    104:  */
                    105: void audit_ptree()
                    106: {
                    107:        int     i;
                    108: 
                    109:        for (i=1 ; i<NR_TASKS ; i++) {
                    110:                if (!task[i])
                    111:                        continue;
                    112:                if (bad_task_ptr(task[i]->p_pptr))
                    113:                        printk("Warning, pid %d's parent link is bad\n",
                    114:                                task[i]->pid);
                    115:                if (bad_task_ptr(task[i]->p_cptr))
                    116:                        printk("Warning, pid %d's child link is bad\n",
                    117:                                task[i]->pid);
                    118:                if (bad_task_ptr(task[i]->p_ysptr))
                    119:                        printk("Warning, pid %d's ys link is bad\n",
                    120:                                task[i]->pid);
                    121:                if (bad_task_ptr(task[i]->p_osptr))
                    122:                        printk("Warning, pid %d's os link is bad\n",
                    123:                                task[i]->pid);
                    124:                if (task[i]->p_pptr == task[i])
                    125:                        printk("Warning, pid %d parent link points to self\n");
                    126:                if (task[i]->p_cptr == task[i])
                    127:                        printk("Warning, pid %d child link points to self\n");
                    128:                if (task[i]->p_ysptr == task[i])
                    129:                        printk("Warning, pid %d ys link points to self\n");
                    130:                if (task[i]->p_osptr == task[i])
                    131:                        printk("Warning, pid %d os link points to self\n");
                    132:                if (task[i]->p_osptr) {
                    133:                        if (task[i]->p_pptr != task[i]->p_osptr->p_pptr)
                    134:                                printk(
                    135:                        "Warning, pid %d older sibling %d parent is %d\n",
                    136:                                task[i]->pid, task[i]->p_osptr->pid,
                    137:                                task[i]->p_osptr->p_pptr->pid);
                    138:                        if (task[i]->p_osptr->p_ysptr != task[i])
                    139:                                printk(
                    140:                "Warning, pid %d older sibling %d has mismatched ys link\n",
                    141:                                task[i]->pid, task[i]->p_osptr->pid);
                    142:                }
                    143:                if (task[i]->p_ysptr) {
                    144:                        if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr)
                    145:                                printk(
                    146:                        "Warning, pid %d younger sibling %d parent is %d\n",
                    147:                                task[i]->pid, task[i]->p_osptr->pid,
                    148:                                task[i]->p_osptr->p_pptr->pid);
                    149:                        if (task[i]->p_ysptr->p_osptr != task[i])
                    150:                                printk(
                    151:                "Warning, pid %d younger sibling %d has mismatched os link\n",
                    152:                                task[i]->pid, task[i]->p_ysptr->pid);
                    153:                }
                    154:                if (task[i]->p_cptr) {
                    155:                        if (task[i]->p_cptr->p_pptr != task[i])
                    156:                                printk(
                    157:                        "Warning, pid %d youngest child %d has mismatched parent link\n",
                    158:                                task[i]->pid, task[i]->p_cptr->pid);
                    159:                        if (task[i]->p_cptr->p_ysptr)
                    160:                                printk(
                    161:                        "Warning, pid %d youngest child %d has non-NULL ys link\n",
                    162:                                task[i]->pid, task[i]->p_cptr->pid);
                    163:                }
                    164:        }
                    165: }
                    166: #endif /* DEBUG_PROC_TREE */
                    167: 
1.1.1.8   root      168: /*
                    169:  * This checks not only the pgrp, but falls back on the pid if no
                    170:  * satisfactory prgp is found. I dunno - gdb doesn't work correctly
                    171:  * without this...
                    172:  */
1.1.1.4   root      173: int session_of_pgrp(int pgrp)
1.1.1.2   root      174: {
1.1.1.4   root      175:        struct task_struct **p;
1.1.1.8   root      176:        int fallback;
1.1.1.4   root      177: 
1.1.1.8   root      178:        fallback = -1;
                    179:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
                    180:                if (!*p || (*p)->session <= 0)
                    181:                        continue;
1.1.1.4   root      182:                if ((*p)->pgrp == pgrp)
1.1.1.8   root      183:                        return (*p)->session;
                    184:                if ((*p)->pid == pgrp)
                    185:                        fallback = (*p)->session;
                    186:        }
                    187:        return fallback;
1.1.1.4   root      188: }
                    189: 
                    190: int kill_pg(int pgrp, int sig, int priv)
                    191: {
                    192:        struct task_struct **p;
                    193:        int err,retval = -ESRCH;
                    194:        int found = 0;
                    195: 
1.1.1.5   root      196:        if (sig<0 || sig>32 || pgrp<=0)
1.1.1.4   root      197:                return -EINVAL;
                    198:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
1.1.1.8   root      199:                if (*p && (*p)->pgrp == pgrp) {
1.1.1.4   root      200:                        if (sig && (err = send_sig(sig,*p,priv)))
                    201:                                retval = err;
                    202:                        else
                    203:                                found++;
                    204:                }
                    205:        return(found ? 0 : retval);
                    206: }
                    207: 
1.1.1.12! root      208: /* This routine is used by vhangup.  It send's sigkill to everything
        !           209:    waiting on a particular wait_queue.  It assumes root privledges.
        !           210:    We don't want to destroy the wait queue here, because the caller
        !           211:    should call wake_up immediately after calling kill_wait. */
        !           212: 
        !           213: void
        !           214: kill_wait (struct wait_queue **q, int sig)
        !           215: {
        !           216:    struct wait_queue *next;
        !           217:    struct wait_queue *tmp;
        !           218:    struct task_struct *p;
        !           219:    
        !           220:    if (!q || !(next = *q))
        !           221:      return;
        !           222:    do { 
        !           223:       tmp = next;
        !           224:       next = tmp->next;
        !           225:       if (p = tmp->task)
        !           226:        {
        !           227:           send_sig (sig, p , 1);
        !           228:        }
        !           229:    } while (next && next != *q);
        !           230: }
        !           231: 
1.1.1.4   root      232: int kill_proc(int pid, int sig, int priv)
                    233: {
                    234:        struct task_struct **p;
                    235: 
1.1.1.5   root      236:        if (sig<0 || sig>32)
1.1.1.4   root      237:                return -EINVAL;
                    238:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
1.1.1.8   root      239:                if (*p && (*p)->pid == pid)
1.1.1.4   root      240:                        return(sig ? send_sig(sig,*p,priv) : 0);
                    241:        return(-ESRCH);
1.1.1.2   root      242: }
                    243: 
                    244: /*
1.1.1.4   root      245:  * POSIX specifies that kill(-1,sig) is unspecified, but what we have
                    246:  * is probably wrong.  Should make it like BSD or SYSV.
1.1.1.2   root      247:  */
                    248: int sys_kill(int pid,int sig)
1.1       root      249: {
                    250:        struct task_struct **p = NR_TASKS + task;
1.1.1.7   root      251:        int err, retval = 0, count = 0;
1.1       root      252: 
1.1.1.4   root      253:        if (!pid)
1.1.1.7   root      254:                return(kill_pg(current->pgrp,sig,0));
1.1.1.4   root      255:        if (pid == -1) {
                    256:                while (--p > &FIRST_TASK)
1.1.1.8   root      257:                        if (*p && (*p)->pid > 1 && *p != current) {
1.1.1.7   root      258:                                ++count;
                    259:                                if ((err = send_sig(sig,*p,0)) != -EPERM)
                    260:                                        retval = err;
                    261:                        }
                    262:                return(count ? retval : -ESRCH);
1.1.1.4   root      263:        }
                    264:        if (pid < 0) 
                    265:                return(kill_pg(-pid,sig,0));
                    266:        /* Normal kill */
                    267:        return(kill_proc(pid,sig,0));
1.1       root      268: }
                    269: 
1.1.1.4   root      270: /*
                    271:  * Determine if a process group is "orphaned", according to the POSIX
                    272:  * definition in 2.2.2.52.  Orphaned process groups are not to be affected
                    273:  * by terminal-generated stop signals.  Newly orphaned process groups are 
                    274:  * to receive a SIGHUP and a SIGCONT.
                    275:  * 
                    276:  * "I ask you, have you ever known what it is to be an orphan?"
                    277:  */
                    278: int is_orphaned_pgrp(int pgrp)
1.1       root      279: {
1.1.1.4   root      280:        struct task_struct **p;
1.1.1.2   root      281: 
1.1.1.4   root      282:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
                    283:                if (!(*p) ||
                    284:                    ((*p)->pgrp != pgrp) || 
                    285:                    ((*p)->state == TASK_ZOMBIE) ||
                    286:                    ((*p)->p_pptr->pid == 1))
                    287:                        continue;
                    288:                if (((*p)->p_pptr->pgrp != pgrp) &&
                    289:                    ((*p)->p_pptr->session == (*p)->session))
                    290:                        return 0;
                    291:        }
                    292:        return(1);      /* (sighing) "Often!" */
1.1       root      293: }
                    294: 
1.1.1.4   root      295: static int has_stopped_jobs(int pgrp)
1.1       root      296: {
1.1.1.4   root      297:        struct task_struct ** p;
                    298: 
                    299:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
1.1.1.8   root      300:                if (!*p || (*p)->pgrp != pgrp)
1.1.1.4   root      301:                        continue;
                    302:                if ((*p)->state == TASK_STOPPED)
                    303:                        return(1);
                    304:        }
                    305:        return(0);
                    306: }
                    307: 
1.1.1.9   root      308: static void forget_original_parent(struct task_struct * father)
                    309: {
                    310:        struct task_struct ** p;
                    311: 
                    312:        for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
                    313:                if (*p && (*p)->p_opptr == father)
1.1.1.10  root      314:                        if (task[1])
                    315:                                (*p)->p_opptr = task[1];
                    316:                        else
                    317:                                (*p)->p_opptr = task[0];
1.1.1.9   root      318: }
                    319: 
1.1.1.4   root      320: volatile void do_exit(long code)
                    321: {
                    322:        struct task_struct *p;
1.1       root      323:        int i;
                    324: 
1.1.1.10  root      325: fake_volatile:
1.1.1.12! root      326:        free_page_tables(current);
1.1       root      327:        for (i=0 ; i<NR_OPEN ; i++)
                    328:                if (current->filp[i])
                    329:                        sys_close(i);
1.1.1.9   root      330:        forget_original_parent(current);
1.1       root      331:        iput(current->pwd);
1.1.1.4   root      332:        current->pwd = NULL;
1.1       root      333:        iput(current->root);
1.1.1.4   root      334:        current->root = NULL;
1.1.1.3   root      335:        iput(current->executable);
1.1.1.4   root      336:        current->executable = NULL;
1.1.1.8   root      337:        for (i=0; i < current->numlibraries; i++) {
                    338:                iput(current->libraries[i].library);
                    339:                current->libraries[i].library = NULL;
                    340:        }       
1.1.1.2   root      341:        current->state = TASK_ZOMBIE;
                    342:        current->exit_code = code;
1.1.1.7   root      343:        current->rss = 0;
1.1.1.4   root      344:        /* 
                    345:         * Check to see if any process groups have become orphaned
                    346:         * as a result of our exiting, and if they have any stopped
                    347:         * jobs, send them a SIGUP and then a SIGCONT.  (POSIX 3.2.2.2)
                    348:         *
                    349:         * Case i: Our father is in a different pgrp than we are
                    350:         * and we were the only connection outside, so our pgrp
                    351:         * is about to become orphaned.
                    352:         */
                    353:        if ((current->p_pptr->pgrp != current->pgrp) &&
                    354:            (current->p_pptr->session == current->session) &&
                    355:            is_orphaned_pgrp(current->pgrp) &&
                    356:            has_stopped_jobs(current->pgrp)) {
                    357:                kill_pg(current->pgrp,SIGHUP,1);
                    358:                kill_pg(current->pgrp,SIGCONT,1);
                    359:        }
                    360:        /* Let father know we died */
1.1.1.5   root      361:        send_sig (SIGCHLD, current->p_pptr, 1);
1.1.1.4   root      362:        
                    363:        /*
                    364:         * This loop does two things:
                    365:         * 
                    366:         * A.  Make init inherit all the child processes
                    367:         * B.  Check to see if any process groups have become orphaned
                    368:         *      as a result of our exiting, and if they have any stopped
1.1.1.9   root      369:         *      jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
1.1.1.4   root      370:         */
1.1.1.7   root      371:        while (p = current->p_cptr) {
                    372:                current->p_cptr = p->p_osptr;
                    373:                p->p_ysptr = NULL;
1.1.1.9   root      374:                p->flags &= ~PF_PTRACED;
1.1.1.10  root      375:                if (task[1])
                    376:                        p->p_pptr = task[1];
                    377:                else
                    378:                        p->p_pptr = task[0];
1.1.1.9   root      379:                p->p_osptr = p->p_pptr->p_cptr;
                    380:                p->p_osptr->p_ysptr = p;
                    381:                p->p_pptr->p_cptr = p;
1.1.1.7   root      382:                if (p->state == TASK_ZOMBIE)
1.1.1.10  root      383:                        send_sig(SIGCHLD,p->p_pptr,1);
1.1.1.7   root      384:                /*
                    385:                 * process group orphan check
                    386:                 * Case ii: Our child is in a different pgrp 
                    387:                 * than we are, and it was the only connection
                    388:                 * outside, so the child pgrp is now orphaned.
                    389:                 */
                    390:                if ((p->pgrp != current->pgrp) &&
                    391:                    (p->session == current->session) &&
                    392:                    is_orphaned_pgrp(p->pgrp) &&
                    393:                    has_stopped_jobs(p->pgrp)) {
                    394:                        kill_pg(p->pgrp,SIGHUP,1);
                    395:                        kill_pg(p->pgrp,SIGCONT,1);
1.1.1.4   root      396:                }
                    397:        }
                    398:        if (current->leader) {
                    399:                struct task_struct **p;
                    400:                struct tty_struct *tty;
                    401: 
                    402:                if (current->tty >= 0) {
                    403:                        tty = TTY_TABLE(current->tty);
1.1.1.6   root      404:                        if (tty->pgrp > 0)
1.1.1.4   root      405:                                kill_pg(tty->pgrp, SIGHUP, 1);
1.1.1.6   root      406:                        tty->pgrp = -1;
1.1.1.4   root      407:                        tty->session = 0;
                    408:                }
                    409:                for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
1.1.1.8   root      410:                        if (*p && (*p)->session == current->session)
1.1.1.4   root      411:                                (*p)->tty = -1;
                    412:        }
                    413:        if (last_task_used_math == current)
                    414:                last_task_used_math = NULL;
                    415: #ifdef DEBUG_PROC_TREE
                    416:        audit_ptree();
                    417: #endif
1.1       root      418:        schedule();
1.1.1.10  root      419: /*
                    420:  * In order to get rid of the "volatile function does return" message
                    421:  * I did this little loop that confuses gcc to think do_exit really
                    422:  * is volatile. In fact it's schedule() that is volatile in some
                    423:  * circumstances: when current->state = ZOMBIE, schedule() never
                    424:  * returns.
                    425:  *
                    426:  * In fact the natural way to do all this is to have the label and the
                    427:  * goto right after each other, but I put the fake_volatile label at
                    428:  * the start of the function just in case something /really/ bad
                    429:  * happens, and the schedule returns. This way we can try again. I'm
                    430:  * not paranoid: it's just that everybody is out to get me.
                    431:  */
                    432:        goto fake_volatile;
1.1       root      433: }
                    434: 
                    435: int sys_exit(int error_code)
                    436: {
1.1.1.4   root      437:        do_exit((error_code&0xff)<<8);
1.1       root      438: }
                    439: 
1.1.1.12! root      440: int sys_wait4(pid_t pid,unsigned long * stat_addr, int options, struct rusage * ru)
1.1       root      441: {
1.1.1.4   root      442:        int flag;
                    443:        struct task_struct *p;
                    444:        unsigned long oldblocked;
1.1       root      445: 
1.1.1.5   root      446:        if (stat_addr)
                    447:                verify_area(stat_addr,4);
1.1       root      448: repeat:
1.1.1.11  root      449:        current->signal &= ~(1<<(SIGCHLD-1));
1.1.1.2   root      450:        flag=0;
1.1.1.9   root      451:        for (p = current->p_cptr ; p ; p = p->p_osptr) {
1.1.1.2   root      452:                if (pid>0) {
1.1.1.4   root      453:                        if (p->pid != pid)
1.1.1.2   root      454:                                continue;
                    455:                } else if (!pid) {
1.1.1.4   root      456:                        if (p->pgrp != current->pgrp)
1.1.1.2   root      457:                                continue;
                    458:                } else if (pid != -1) {
1.1.1.4   root      459:                        if (p->pgrp != -pid)
1.1.1.2   root      460:                                continue;
                    461:                }
1.1.1.4   root      462:                switch (p->state) {
1.1.1.2   root      463:                        case TASK_STOPPED:
1.1.1.8   root      464:                                if (!p->exit_code)
                    465:                                        continue;
                    466:                                if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
1.1.1.2   root      467:                                        continue;
1.1.1.5   root      468:                                if (stat_addr)
                    469:                                        put_fs_long((p->exit_code << 8) | 0x7f,
                    470:                                                stat_addr);
1.1.1.4   root      471:                                p->exit_code = 0;
1.1.1.12! root      472:                                if (ru != NULL)
        !           473:                                        getrusage(p, RUSAGE_BOTH, ru);
1.1.1.4   root      474:                                return p->pid;
1.1.1.2   root      475:                        case TASK_ZOMBIE:
1.1.1.7   root      476:                                current->cutime += p->utime + p->cutime;
                    477:                                current->cstime += p->stime + p->cstime;
                    478:                                current->cmin_flt += p->min_flt + p->cmin_flt;
                    479:                                current->cmaj_flt += p->maj_flt + p->cmaj_flt;
1.1.1.12! root      480:                                if (ru != NULL)
        !           481:                                        getrusage(p, RUSAGE_BOTH, ru);
1.1.1.4   root      482:                                flag = p->pid;
1.1.1.5   root      483:                                if (stat_addr)
                    484:                                        put_fs_long(p->exit_code, stat_addr);
1.1.1.9   root      485:                                if (p->p_opptr != p->p_pptr) {
                    486:                                        REMOVE_LINKS(p);
                    487:                                        p->p_pptr = p->p_opptr;
                    488:                                        SET_LINKS(p);
                    489:                                        send_sig(SIGCHLD,p->p_pptr,1);
                    490:                                } else
                    491:                                        release(p);
1.1.1.4   root      492: #ifdef DEBUG_PROC_TREE
                    493:                                audit_ptree();
                    494: #endif
1.1.1.2   root      495:                                return flag;
                    496:                        default:
1.1       root      497:                                flag=1;
1.1.1.2   root      498:                                continue;
                    499:                }
                    500:        }
1.1       root      501:        if (flag) {
                    502:                if (options & WNOHANG)
                    503:                        return 0;
1.1.1.2   root      504:                current->state=TASK_INTERRUPTIBLE;
1.1.1.4   root      505:                oldblocked = current->blocked;
                    506:                current->blocked &= ~(1<<(SIGCHLD-1));
1.1.1.2   root      507:                schedule();
1.1.1.4   root      508:                current->blocked = oldblocked;
                    509:                if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1))))
                    510:                        return -ERESTARTSYS;
1.1       root      511:                else
1.1.1.4   root      512:                        goto repeat;
1.1       root      513:        }
                    514:        return -ECHILD;
                    515: }
1.1.1.12! root      516: 
        !           517: /*
        !           518:  * sys_waitpid() remains for compatibility. waitpid() should be
        !           519:  * implemented by calling sys_wait4() from libc.a.
        !           520:  */
        !           521: int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
        !           522: {
        !           523:        return sys_wait4(pid, stat_addr, options, NULL);
        !           524: }

unix.superglobalmegacorp.com